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
This commit is contained in:
John McCall 2016-03-01 00:49:02 +00:00
parent 6d0c8a036e
commit 18afab762a
8 changed files with 178 additions and 79 deletions

View File

@ -2260,7 +2260,7 @@ public:
QualType mergeObjCGCQualifiers(QualType, QualType);
bool FunctionTypesMatchOnNSConsumedAttrs(
bool doFunctionTypesMatchOnExtParameterInfos(
const FunctionProtoType *FromFunctionType,
const FunctionProtoType *ToFunctionType);

View File

@ -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<Expr *const *>(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<const char *>(exception_begin());
ptr += getExceptionSpecSize();
return reinterpret_cast<const bool*>(eh_end);
return reinterpret_cast<const ExtParameterInfo *>(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<ExtParameterInfo> getExtParameterInfos() const {
assert(hasExtParameterInfos());
return ArrayRef<ExtParameterInfo>(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;
}

View File

@ -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<QualType> 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<QualType> 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;
}

View File

@ -2671,7 +2671,7 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> 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<QualType> params,
slot[0] = epi.ExceptionSpec.SourceDecl;
}
if (epi.ConsumedParameters) {
bool *consumedParams = const_cast<bool *>(getConsumedParamsBuffer());
if (epi.ExtParameterInfos) {
ExtParameterInfo *extParamInfos =
const_cast<ExtParameterInfo *>(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);

View File

@ -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;

View File

@ -3982,9 +3982,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
SmallVector<QualType, 16> ParamTys;
ParamTys.reserve(FTI.NumParams);
SmallVector<bool, 16> ConsumedParameters;
ConsumedParameters.reserve(FTI.NumParams);
bool HasAnyConsumedParameters = false;
SmallVector<FunctionProtoType::ExtParameterInfo, 16>
ExtParameterInfos(FTI.NumParams);
bool HasAnyInterestingExtParameterInfos = false;
for (unsigned i = 0, e = FTI.NumParams; i != e; ++i) {
ParmVarDecl *Param = cast<ParmVarDecl>(FTI.Params[i].Param);
@ -4042,17 +4042,16 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
}
}
if (LangOpts.ObjCAutoRefCount) {
bool Consumed = Param->hasAttr<NSConsumedAttr>();
ConsumedParameters.push_back(Consumed);
HasAnyConsumedParameters |= Consumed;
if (LangOpts.ObjCAutoRefCount && Param->hasAttr<NSConsumedAttr>()) {
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<QualType, 4> Exceptions;
SmallVector<ParsedType, 2> 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<FunctionProtoType>(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;
}
}

View File

@ -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<FunctionProtoType::ExtParameterInfo, 4> 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);
}

View File

@ -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;