From: John McCall Date: Tue, 1 Mar 2016 00:49:02 +0000 (+0000) Subject: Generalize the consumed-parameter array on FunctionProtoType X-Git-Tag: llvmorg-3.9.0-rc1~12877 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=18afab762a6c22de74f1ef714178fb9d1da159b1;p=platform%2Fupstream%2Fllvm.git Generalize the consumed-parameter array on FunctionProtoType to allow arbitrary data to be associated with a parameter. Also, fix a bug where we apparently haven't been serializing this information for the last N years. llvm-svn: 262278 --- diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 438e676..fb5a64f5 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2260,7 +2260,7 @@ public: QualType mergeObjCGCQualifiers(QualType, QualType); - bool FunctionTypesMatchOnNSConsumedAttrs( + bool doFunctionTypesMatchOnExtParameterInfos( const FunctionProtoType *FromFunctionType, const FunctionProtoType *ToFunctionType); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index d63b2c4..4233425 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -3040,6 +3040,44 @@ public: /// type. class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { public: + class ExtParameterInfo { + enum { + IsConsumed = 0x01, + }; + unsigned char Data; + public: + ExtParameterInfo() : Data(0) {} + + /// Is this parameter considered "consumed" by Objective-C ARC? + /// Consumed parameters must have retainable object type. + bool isConsumed() const { + return (Data & IsConsumed); + } + ExtParameterInfo withIsConsumed(bool consumed) const { + ExtParameterInfo copy = *this; + if (consumed) { + copy.Data |= IsConsumed; + } else { + copy.Data &= ~IsConsumed; + } + return copy; + } + + unsigned char getOpaqueValue() const { return Data; } + static ExtParameterInfo getFromOpaqueValue(unsigned char data) { + ExtParameterInfo result; + result.Data = data; + return result; + } + + friend bool operator==(ExtParameterInfo lhs, ExtParameterInfo rhs) { + return lhs.Data == rhs.Data; + } + friend bool operator!=(ExtParameterInfo lhs, ExtParameterInfo rhs) { + return lhs.Data != rhs.Data; + } + }; + struct ExceptionSpecInfo { ExceptionSpecInfo() : Type(EST_None), NoexceptExpr(nullptr), @@ -3067,11 +3105,11 @@ public: struct ExtProtoInfo { ExtProtoInfo() : Variadic(false), HasTrailingReturn(false), TypeQuals(0), - RefQualifier(RQ_None), ConsumedParameters(nullptr) {} + RefQualifier(RQ_None), ExtParameterInfos(nullptr) {} ExtProtoInfo(CallingConv CC) : ExtInfo(CC), Variadic(false), HasTrailingReturn(false), TypeQuals(0), - RefQualifier(RQ_None), ConsumedParameters(nullptr) {} + RefQualifier(RQ_None), ExtParameterInfos(nullptr) {} ExtProtoInfo withExceptionSpec(const ExceptionSpecInfo &O) { ExtProtoInfo Result(*this); @@ -3085,7 +3123,7 @@ public: unsigned char TypeQuals; RefQualifierKind RefQualifier; ExceptionSpecInfo ExceptionSpec; - const bool *ConsumedParameters; + const ExtParameterInfo *ExtParameterInfos; }; private: @@ -3112,8 +3150,8 @@ private: /// The type of exception specification this function has. unsigned ExceptionSpecType : 4; - /// Whether this function has any consumed parameters. - unsigned HasAnyConsumedParams : 1; + /// Whether this function has extended parameter information. + unsigned HasExtParameterInfos : 1; /// Whether the function is variadic. unsigned Variadic : 1; @@ -3135,25 +3173,36 @@ private: // instantiate this function type's exception specification, and the function // from which it should be instantiated. - // ConsumedParameters - A variable size array, following Exceptions - // and of length NumParams, holding flags indicating which parameters - // are consumed. This only appears if HasAnyConsumedParams is true. + // ExtParameterInfos - A variable size array, following the exception + // specification and of length NumParams, holding an ExtParameterInfo + // for each of the parameters. This only appears if HasExtParameterInfos + // is true. friend class ASTContext; // ASTContext creates these. - const bool *getConsumedParamsBuffer() const { - assert(hasAnyConsumedParams()); + const ExtParameterInfo *getExtParameterInfosBuffer() const { + assert(hasExtParameterInfos()); - // Find the end of the exceptions. - Expr *const *eh_end = reinterpret_cast(exception_end()); - if (getExceptionSpecType() == EST_ComputedNoexcept) - eh_end += 1; // NoexceptExpr - // The memory layout of these types isn't handled here, so - // hopefully this is never called for them? - assert(getExceptionSpecType() != EST_Uninstantiated && - getExceptionSpecType() != EST_Unevaluated); + // Find the end of the exception specification. + const char *ptr = reinterpret_cast(exception_begin()); + ptr += getExceptionSpecSize(); - return reinterpret_cast(eh_end); + return reinterpret_cast(ptr); + } + + size_t getExceptionSpecSize() const { + switch (getExceptionSpecType()) { + case EST_None: return 0; + case EST_DynamicNone: return 0; + case EST_MSAny: return 0; + case EST_BasicNoexcept: return 0; + case EST_Unparsed: return 0; + case EST_Dynamic: return getNumExceptions() * sizeof(QualType); + case EST_ComputedNoexcept: return sizeof(Expr*); + case EST_Uninstantiated: return 2 * sizeof(FunctionDecl*); + case EST_Unevaluated: return sizeof(FunctionDecl*); + } + llvm_unreachable("bad exception specification kind"); } public: @@ -3184,8 +3233,8 @@ public: } else if (EPI.ExceptionSpec.Type == EST_Unevaluated) { EPI.ExceptionSpec.SourceDecl = getExceptionSpecDecl(); } - if (hasAnyConsumedParams()) - EPI.ConsumedParameters = getConsumedParamsBuffer(); + if (hasExtParameterInfos()) + EPI.ExtParameterInfos = getExtParameterInfosBuffer(); return EPI; } @@ -3300,11 +3349,24 @@ public: return exception_begin() + NumExceptions; } - bool hasAnyConsumedParams() const { return HasAnyConsumedParams; } + bool hasExtParameterInfos() const { return HasExtParameterInfos; } + ArrayRef getExtParameterInfos() const { + assert(hasExtParameterInfos()); + return ArrayRef(getExtParameterInfosBuffer(), + getNumParams()); + } + + ExtParameterInfo getExtParameterInfo(unsigned I) const { + assert(I < getNumParams() && "parameter index out of range"); + if (hasExtParameterInfos()) + return getExtParameterInfosBuffer()[I]; + return ExtParameterInfo(); + } + bool isParamConsumed(unsigned I) const { assert(I < getNumParams() && "parameter index out of range"); - if (hasAnyConsumedParams()) - return getConsumedParamsBuffer()[I]; + if (hasExtParameterInfos()) + return getExtParameterInfosBuffer()[I].isConsumed(); return false; } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index c6f5374..dad6d1a 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2992,13 +2992,18 @@ ASTContext::getDependentSizedExtVectorType(QualType vecType, return QualType(New, 0); } +/// \brief Determine whether \p T is canonical as the result type of a function. +static bool isCanonicalResultType(QualType T) { + return T.isCanonical() && + (T.getObjCLifetime() == Qualifiers::OCL_None || + T.getObjCLifetime() == Qualifiers::OCL_ExplicitNone); +} + /// getFunctionNoProtoType - Return a K&R style C function type like 'int()'. /// QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, const FunctionType::ExtInfo &Info) const { - const CallingConv CallConv = Info.getCC(); - // Unique functions, to guarantee there is only one function of a particular // structure. llvm::FoldingSetNodeID ID; @@ -3010,8 +3015,9 @@ ASTContext::getFunctionNoProtoType(QualType ResultTy, return QualType(FT, 0); QualType Canonical; - if (!ResultTy.isCanonical()) { - Canonical = getFunctionNoProtoType(getCanonicalType(ResultTy), Info); + if (!isCanonicalResultType(ResultTy)) { + Canonical = + getFunctionNoProtoType(getCanonicalFunctionResultType(ResultTy), Info); // Get the new insert position for the node we care about. FunctionNoProtoType *NewIP = @@ -3019,21 +3025,13 @@ ASTContext::getFunctionNoProtoType(QualType ResultTy, assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP; } - FunctionProtoType::ExtInfo newInfo = Info.withCallingConv(CallConv); FunctionNoProtoType *New = new (*this, TypeAlignment) - FunctionNoProtoType(ResultTy, Canonical, newInfo); + FunctionNoProtoType(ResultTy, Canonical, Info); Types.push_back(New); FunctionNoProtoTypes.InsertNode(New, InsertPos); return QualType(New, 0); } -/// \brief Determine whether \p T is canonical as the result type of a function. -static bool isCanonicalResultType(QualType T) { - return T.isCanonical() && - (T.getObjCLifetime() == Qualifiers::OCL_None || - T.getObjCLifetime() == Qualifiers::OCL_ExplicitNone); -} - CanQualType ASTContext::getCanonicalFunctionResultType(QualType ResultType) const { CanQualType CanResultType = getCanonicalType(ResultType); @@ -3100,12 +3098,13 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, // them for three variable size arrays at the end: // - parameter types // - exception types - // - consumed-arguments flags + // - extended parameter information // Instead of the exception types, there could be a noexcept // expression, or information used to resolve the exception // specification. size_t Size = sizeof(FunctionProtoType) + NumArgs * sizeof(QualType); + if (EPI.ExceptionSpec.Type == EST_Dynamic) { Size += EPI.ExceptionSpec.Exceptions.size() * sizeof(QualType); } else if (EPI.ExceptionSpec.Type == EST_ComputedNoexcept) { @@ -3115,8 +3114,16 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, } else if (EPI.ExceptionSpec.Type == EST_Unevaluated) { Size += sizeof(FunctionDecl*); } - if (EPI.ConsumedParameters) - Size += NumArgs * sizeof(bool); + + // Put the ExtParameterInfos last. If all were equal, it would make + // more sense to put these before the exception specification, because + // it's much easier to skip past them compared to the elaborate switch + // required to skip the exception specification. However, all is not + // equal; ExtParameterInfos are used to model very uncommon features, + // and it's better not to burden the more common paths. + if (EPI.ExtParameterInfos) { + Size += NumArgs * sizeof(FunctionProtoType::ExtParameterInfo); + } FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment); FunctionProtoType::ExtProtoInfo newEPI = EPI; @@ -7489,8 +7496,7 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lproto->getTypeQuals() != rproto->getTypeQuals()) return QualType(); - if (LangOpts.ObjCAutoRefCount && - !FunctionTypesMatchOnNSConsumedAttrs(rproto, lproto)) + if (!doFunctionTypesMatchOnExtParameterInfos(rproto, lproto)) return QualType(); // Check parameter type compatibility @@ -7878,21 +7884,26 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, llvm_unreachable("Invalid Type::Class!"); } -bool ASTContext::FunctionTypesMatchOnNSConsumedAttrs( - const FunctionProtoType *FromFunctionType, - const FunctionProtoType *ToFunctionType) { - if (FromFunctionType->hasAnyConsumedParams() != - ToFunctionType->hasAnyConsumedParams()) +bool ASTContext::doFunctionTypesMatchOnExtParameterInfos( + const FunctionProtoType *firstFnType, + const FunctionProtoType *secondFnType) { + // Fast path: if the first type doesn't have ext parameter infos, + // we match if and only if they second type also doesn't have them. + if (!firstFnType->hasExtParameterInfos()) + return !secondFnType->hasExtParameterInfos(); + + // Otherwise, we can only match if the second type has them. + if (!secondFnType->hasExtParameterInfos()) return false; - FunctionProtoType::ExtProtoInfo FromEPI = - FromFunctionType->getExtProtoInfo(); - FunctionProtoType::ExtProtoInfo ToEPI = - ToFunctionType->getExtProtoInfo(); - if (FromEPI.ConsumedParameters && ToEPI.ConsumedParameters) - for (unsigned i = 0, n = FromFunctionType->getNumParams(); i != n; ++i) { - if (FromEPI.ConsumedParameters[i] != ToEPI.ConsumedParameters[i]) - return false; - } + + auto firstEPI = firstFnType->getExtParameterInfos(); + auto secondEPI = secondFnType->getExtParameterInfos(); + assert(firstEPI.size() == secondEPI.size()); + + for (size_t i = 0, n = firstEPI.size(); i != n; ++i) { + if (firstEPI[i] != secondEPI[i]) + return false; + } return true; } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index b467dac..6cd6c2a 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2671,7 +2671,7 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, NumParams(params.size()), NumExceptions(epi.ExceptionSpec.Exceptions.size()), ExceptionSpecType(epi.ExceptionSpec.Type), - HasAnyConsumedParams(epi.ConsumedParameters != nullptr), + HasExtParameterInfos(epi.ExtParameterInfos != nullptr), Variadic(epi.Variadic), HasTrailingReturn(epi.HasTrailingReturn) { assert(NumParams == params.size() && "function has too many parameters"); @@ -2737,10 +2737,11 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, slot[0] = epi.ExceptionSpec.SourceDecl; } - if (epi.ConsumedParameters) { - bool *consumedParams = const_cast(getConsumedParamsBuffer()); + if (epi.ExtParameterInfos) { + ExtParameterInfo *extParamInfos = + const_cast(getExtParameterInfosBuffer()); for (unsigned i = 0; i != NumParams; ++i) - consumedParams[i] = epi.ConsumedParameters[i]; + extParamInfos[i] = epi.ExtParameterInfos[i]; } } @@ -2860,9 +2861,9 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, epi.ExceptionSpec.Type == EST_Unevaluated) { ID.AddPointer(epi.ExceptionSpec.SourceDecl->getCanonicalDecl()); } - if (epi.ConsumedParameters) { + if (epi.ExtParameterInfos) { for (unsigned i = 0; i != NumParams; ++i) - ID.AddBoolean(epi.ConsumedParameters[i]); + ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue()); } epi.ExtInfo.Profile(ID); ID.AddBoolean(epi.HasTrailingReturn); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index ef38ace..2ad4993 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2541,9 +2541,8 @@ bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType, // Argument types are too different. Abort. return false; } - if (LangOpts.ObjCAutoRefCount && - !Context.FunctionTypesMatchOnNSConsumedAttrs(FromFunctionType, - ToFunctionType)) + if (!Context.doFunctionTypesMatchOnExtParameterInfos(FromFunctionType, + ToFunctionType)) return false; ConvertedType = ToType; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index dbc325c..54276b6 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3982,9 +3982,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, SmallVector ParamTys; ParamTys.reserve(FTI.NumParams); - SmallVector ConsumedParameters; - ConsumedParameters.reserve(FTI.NumParams); - bool HasAnyConsumedParameters = false; + SmallVector + ExtParameterInfos(FTI.NumParams); + bool HasAnyInterestingExtParameterInfos = false; for (unsigned i = 0, e = FTI.NumParams; i != e; ++i) { ParmVarDecl *Param = cast(FTI.Params[i].Param); @@ -4042,17 +4042,16 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } - if (LangOpts.ObjCAutoRefCount) { - bool Consumed = Param->hasAttr(); - ConsumedParameters.push_back(Consumed); - HasAnyConsumedParameters |= Consumed; + if (LangOpts.ObjCAutoRefCount && Param->hasAttr()) { + ExtParameterInfos[i] = ExtParameterInfos[i].withIsConsumed(true); + HasAnyInterestingExtParameterInfos = true; } ParamTys.push_back(ParamTy); } - if (HasAnyConsumedParameters) - EPI.ConsumedParameters = ConsumedParameters.data(); + if (HasAnyInterestingExtParameterInfos) + EPI.ExtParameterInfos = ExtParameterInfos.data(); SmallVector Exceptions; SmallVector DynamicExceptions; @@ -5950,18 +5949,28 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, } } - // Diagnose use of callee-cleanup calling convention on variadic functions. + // Diagnose use of variadic functions with calling conventions that + // don't support them (e.g. because they're callee-cleanup). + // We delay warning about this on unprototyped function declarations + // until after redeclaration checking, just in case we pick up a + // prototype that way. And apparently we also "delay" warning about + // unprototyped function types in general, despite not necessarily having + // much ability to diagnose it later. if (!supportsVariadicCall(CC)) { const FunctionProtoType *FnP = dyn_cast(fn); if (FnP && FnP->isVariadic()) { unsigned DiagID = diag::err_cconv_varargs; + // stdcall and fastcall are ignored with a warning for GCC and MS // compatibility. - if (CC == CC_X86StdCall || CC == CC_X86FastCall) + bool IsInvalid = true; + if (CC == CC_X86StdCall || CC == CC_X86FastCall) { DiagID = diag::warn_cconv_varargs; + IsInvalid = false; + } S.Diag(attr.getLoc(), DiagID) << FunctionType::getNameForCallConv(CC); - attr.setInvalid(); + if (IsInvalid) attr.setInvalid(); return true; } } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 82bc92c..4eef6cf 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -5387,6 +5387,17 @@ QualType ASTReader::readTypeRecord(unsigned Index) { for (unsigned I = 0; I != NumParams; ++I) ParamTypes.push_back(readType(*Loc.F, Record, Idx)); + SmallVector ExtParameterInfos; + if (Idx != Record.size()) { + for (unsigned I = 0; I != NumParams; ++I) + ExtParameterInfos.push_back( + FunctionProtoType::ExtParameterInfo + ::getFromOpaqueValue(Record[Idx++])); + EPI.ExtParameterInfos = ExtParameterInfos.data(); + } + + assert(Idx == Record.size()); + return Context.getFunctionType(ResultType, ParamTypes, EPI); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index a88ab5f..98ed148 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -239,8 +239,14 @@ void ASTTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) { for (unsigned I = 0, N = T->getNumParams(); I != N; ++I) Writer.AddTypeRef(T->getParamType(I), Record); + if (T->hasExtParameterInfos()) { + for (unsigned I = 0, N = T->getNumParams(); I != N; ++I) + Record.push_back(T->getExtParameterInfo(I).getOpaqueValue()); + } + if (T->isVariadic() || T->hasTrailingReturn() || T->getTypeQuals() || - T->getRefQualifier() || T->getExceptionSpecType() != EST_None) + T->getRefQualifier() || T->getExceptionSpecType() != EST_None || + T->hasExtParameterInfos()) AbbrevToUse = 0; Code = TYPE_FUNCTION_PROTO;