From: John McCall Date: Thu, 4 Feb 2010 05:44:44 +0000 (+0000) Subject: Allow calling convention attributes to apply to types. Patch by Chip Davis! X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f82b4e85b1219295cad4b5851b035575bc293010;p=clang Allow calling convention attributes to apply to types. Patch by Chip Davis! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95291 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index f53fd92106..fa5d3e105f 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -1766,10 +1766,11 @@ public: QualType desugar() const { return QualType(this, 0); } void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getResultType(), getNoReturnAttr()); + Profile(ID, getResultType(), getNoReturnAttr(), getCallConv()); } static void Profile(llvm::FoldingSetNodeID &ID, QualType ResultType, - bool NoReturn) { + bool NoReturn, CallingConv CallConv) { + ID.AddInteger(CallConv); ID.AddInteger(NoReturn); ID.AddPointer(ResultType.getAsOpaquePtr()); } @@ -1892,7 +1893,7 @@ public: bool isVariadic, unsigned TypeQuals, bool hasExceptionSpec, bool anyExceptionSpec, unsigned NumExceptions, exception_iterator Exs, - bool NoReturn); + bool NoReturn, CallingConv CallConv); }; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index d6c9041e3e..52c5cbccde 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1716,7 +1716,7 @@ QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, bool NoReturn, // Unique functions, to guarantee there is only one function of a particular // structure. llvm::FoldingSetNodeID ID; - FunctionNoProtoType::Profile(ID, ResultTy, NoReturn); + FunctionNoProtoType::Profile(ID, ResultTy, NoReturn, CallConv); void *InsertPos = 0; if (FunctionNoProtoType *FT = @@ -1736,7 +1736,7 @@ QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, bool NoReturn, } FunctionNoProtoType *New = new (*this, TypeAlignment) - FunctionNoProtoType(ResultTy, Canonical, NoReturn); + FunctionNoProtoType(ResultTy, Canonical, NoReturn, CallConv); Types.push_back(New); FunctionNoProtoTypes.InsertNode(New, InsertPos); return QualType(New, 0); @@ -1755,7 +1755,7 @@ QualType ASTContext::getFunctionType(QualType ResultTy,const QualType *ArgArray, llvm::FoldingSetNodeID ID; FunctionProtoType::Profile(ID, ResultTy, ArgArray, NumArgs, isVariadic, TypeQuals, hasExceptionSpec, hasAnyExceptionSpec, - NumExs, ExArray, NoReturn); + NumExs, ExArray, NoReturn, CallConv); void *InsertPos = 0; if (FunctionProtoType *FTP = diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index b3010d5e7b..4e74759676 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -815,7 +815,8 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, unsigned NumArgs, bool isVariadic, unsigned TypeQuals, bool hasExceptionSpec, bool anyExceptionSpec, unsigned NumExceptions, - exception_iterator Exs, bool NoReturn) { + exception_iterator Exs, bool NoReturn, + CallingConv CallConv) { ID.AddPointer(Result.getAsOpaquePtr()); for (unsigned i = 0; i != NumArgs; ++i) ID.AddPointer(ArgTys[i].getAsOpaquePtr()); @@ -828,12 +829,14 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, ID.AddPointer(Exs[i].getAsOpaquePtr()); } ID.AddInteger(NoReturn); + ID.AddInteger(CallConv); } void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getResultType(), arg_type_begin(), NumArgs, isVariadic(), getTypeQuals(), hasExceptionSpec(), hasAnyExceptionSpec(), - getNumExceptions(), exception_begin(), getNoReturnAttr()); + getNumExceptions(), exception_begin(), getNoReturnAttr(), + getCallConv()); } void ObjCObjectPointerType::Profile(llvm::FoldingSetNodeID &ID, diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 00b74bc21a..597ab37d2b 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -271,6 +271,19 @@ void TypePrinter::PrintFunctionProto(const FunctionProtoType *T, S += ")"; + switch(T->getCallConv()) { + case CC_Default: + default: break; + case CC_C: + S += " __attribute__((cdecl))"; + break; + case CC_X86StdCall: + S += " __attribute__((stdcall))"; + break; + case CC_X86FastCall: + S += " __attribute__((fastcall))"; + break; + } if (T->getNoReturnAttr()) S += " __attribute__((noreturn))"; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 12ce174308..19de095b2a 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -560,7 +560,9 @@ public: // Type Analysis / Processing: SemaType.cpp. // QualType adjustParameterType(QualType T); - void ProcessTypeAttributeList(QualType &Result, const AttributeList *AL); + void ProcessTypeAttributeList(QualType &Result, const AttributeList *AL, + bool HandleCallConvAttributes = false, + bool HandleOnlyCallConv = false); QualType BuildPointerType(QualType T, unsigned Quals, SourceLocation Loc, DeclarationName Entity); QualType BuildReferenceType(QualType T, bool LValueRef, unsigned Quals, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 80831f42c8..2e3a4352f2 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -901,6 +901,14 @@ static Sema::CXXSpecialMember getSpecialMember(ASTContext &Ctx, return Sema::CXXCopyAssignment; } +static const char* getCallConvName(CallingConv CC) { + switch (CC) { + default: return "cdecl"; + case CC_X86StdCall: return "stdcall"; + case CC_X86FastCall: return "fastcall"; + } +} + /// MergeFunctionDecl - We just parsed a function 'New' from /// declarator D which has the same name and scope as a previous /// declaration 'Old'. Figure out how to resolve this situation, @@ -958,6 +966,33 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { return true; } + // If a function is first declared with a calling convention, but is + // later declared or defined without one, the second decl assumes the + // calling convention of the first. + // + // For the new decl, we have to look at the NON-canonical type to tell the + // difference between a function that really doesn't have a calling + // convention and one that is declared cdecl. That's because in + // canonicalization (see ASTContext.cpp), cdecl is canonicalized away + // because it is the default calling convention. + // + // Note also that we DO NOT return at this point, because we still have + // other tests to run. + const FunctionType *OldType = OldQType->getAs(); + const FunctionType *NewType = New->getType()->getAs(); + if (OldType->getCallConv() != CC_Default && + NewType->getCallConv() == CC_Default) { + NewQType = Context.getCallConvType(NewQType, OldType->getCallConv()); + New->setType(NewQType); + NewQType = Context.getCanonicalType(NewQType); + } else if (OldType->getCallConv() != NewType->getCallConv()) { + // Calling conventions really aren't compatible, so complain. + Diag(New->getLocation(), diag::err_attributes_are_not_compatible) + << getCallConvName(NewType->getCallConv()) + << getCallConvName(OldType->getCallConv()); + return true; + } + if (getLangOptions().CPlusPlus) { // (C++98 13.1p2): // Certain function declarations cannot be overloaded: diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index a391a0eaed..5e7788ef40 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -948,6 +948,12 @@ static void HandleCDeclAttr(Decl *d, const AttributeList &Attr, Sema &S) { } // Attribute can be applied only to functions. + // If we try to apply it to a function pointer, don't warn, but don't + // do anything, either. All the function-pointer stuff is handled in + // SemaType.cpp. + ValueDecl *VD = dyn_cast(d); + if (VD && VD->getType()->isFunctionPointerType()) + return; if (!isa(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << 0 /*function*/; @@ -980,6 +986,11 @@ static void HandleStdCallAttr(Decl *d, const AttributeList &Attr, Sema &S) { } // Attribute can be applied only to functions. + // If we try to apply it to a function pointer, don't warn, but don't + // do anything, either. + ValueDecl *VD = dyn_cast(d); + if (VD && VD->getType()->isFunctionPointerType()) + return; if (!isa(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << 0 /*function*/; @@ -1019,6 +1030,11 @@ static void HandleFastCallAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } + // If we try to apply it to a function pointer, don't warn, but don't + // do anything, either. + ValueDecl *VD = dyn_cast(d); + if (VD && VD->getType()->isFunctionPointerType()) + return; if (!isa(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << 0 /*function*/; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 906576115a..8833792494 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1241,7 +1241,7 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // See if there are any attributes on this declarator chunk. if (const AttributeList *AL = DeclType.getAttrs()) - ProcessTypeAttributeList(T, AL); + ProcessTypeAttributeList(T, AL, true); } if (getLangOptions().CPlusPlus && T->isFunctionType()) { @@ -1274,7 +1274,10 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // If there were any type attributes applied to the decl itself (not the // type, apply the type attribute to the type!) if (const AttributeList *Attrs = D.getAttributes()) - ProcessTypeAttributeList(T, Attrs); + ProcessTypeAttributeList(T, Attrs, true); + // Also look in the decl spec. + if (const AttributeList *Attrs = D.getDeclSpec().getAttributes()) + ProcessTypeAttributeList(T, Attrs, true, true); if (TInfo) { if (D.isInvalidType()) @@ -1612,6 +1615,36 @@ static void HandleAddressSpaceTypeAttribute(QualType &Type, Type = S.Context.getAddrSpaceQualType(Type, ASIdx); } +/// HandleCDeclTypeAttribute - Process the cdecl attribute on the +/// specified type. The attribute contains 0 arguments. +static void HandleCDeclTypeAttribute(QualType &Type, + const AttributeList &Attr, Sema &S) { + if (Attr.getNumArgs() != 0) + return; + + // We only apply this to a pointer to function. + if (!Type->isFunctionPointerType() + && !Type->isFunctionType()) + return; + + Type = S.Context.getCallConvType(Type, CC_C); +} + +/// HandleFastCallTypeAttribute - Process the fastcall attribute on the +/// specified type. The attribute contains 0 arguments. +static void HandleFastCallTypeAttribute(QualType &Type, + const AttributeList &Attr, Sema &S) { + if (Attr.getNumArgs() != 0) + return; + + // We only apply this to a pointer to function. + if (!Type->isFunctionPointerType() + && !Type->isFunctionType()) + return; + + Type = S.Context.getCallConvType(Type, CC_X86FastCall); +} + /// HandleObjCGCTypeAttribute - Process an objc's gc attribute on the /// specified type. The attribute contains 1 argument, weak or strong. static void HandleObjCGCTypeAttribute(QualType &Type, @@ -1661,6 +1694,21 @@ static void HandleNoReturnTypeAttribute(QualType &Type, Type = S.Context.getNoReturnType(Type); } +/// HandleStdCallTypeAttribute - Process the stdcall attribute on the +/// specified type. The attribute contains 0 arguments. +static void HandleStdCallTypeAttribute(QualType &Type, + const AttributeList &Attr, Sema &S) { + if (Attr.getNumArgs() != 0) + return; + + // We only apply this to a pointer to function. + if (!Type->isFunctionPointerType() + && !Type->isFunctionType()) + return; + + Type = S.Context.getCallConvType(Type, CC_X86StdCall); +} + /// HandleVectorSizeAttribute - this attribute is only applicable to integral /// and float scalars, although arrays, pointers, and function return values are /// allowed in conjunction with this construct. Aggregates with this attribute @@ -1708,7 +1756,12 @@ static void HandleVectorSizeAttr(QualType& CurType, const AttributeList &Attr, S CurType = S.Context.getVectorType(CurType, vectorSize/typeSize); } -void Sema::ProcessTypeAttributeList(QualType &Result, const AttributeList *AL) { +void Sema::ProcessTypeAttributeList(QualType &Result, const AttributeList *AL, + bool HandleCallConvAttributes, + bool HandleOnlyCallConv) { + if(HandleOnlyCallConv) + assert(HandleCallConvAttributes && "Can't not handle call-conv attributes" + " while only handling them!"); // Scan through and apply attributes to this type where it makes sense. Some // attributes (such as __address_space__, __vector_size__, etc) apply to the // type, but others can be present in the type specifiers even though they @@ -1719,16 +1772,32 @@ void Sema::ProcessTypeAttributeList(QualType &Result, const AttributeList *AL) { switch (AL->getKind()) { default: break; case AttributeList::AT_address_space: - HandleAddressSpaceTypeAttribute(Result, *AL, *this); + if (!HandleOnlyCallConv) + HandleAddressSpaceTypeAttribute(Result, *AL, *this); + break; + case AttributeList::AT_cdecl: + if (HandleCallConvAttributes) + HandleCDeclTypeAttribute(Result, *AL, *this); + break; + case AttributeList::AT_fastcall: + if (HandleCallConvAttributes) + HandleFastCallTypeAttribute(Result, *AL, *this); break; case AttributeList::AT_objc_gc: - HandleObjCGCTypeAttribute(Result, *AL, *this); + if (!HandleOnlyCallConv) + HandleObjCGCTypeAttribute(Result, *AL, *this); break; case AttributeList::AT_noreturn: - HandleNoReturnTypeAttribute(Result, *AL, *this); + if (!HandleOnlyCallConv) + HandleNoReturnTypeAttribute(Result, *AL, *this); + break; + case AttributeList::AT_stdcall: + if (HandleCallConvAttributes) + HandleStdCallTypeAttribute(Result, *AL, *this); break; case AttributeList::AT_vector_size: - HandleVectorSizeAttr(Result, *AL, *this); + if (!HandleOnlyCallConv) + HandleVectorSizeAttr(Result, *AL, *this); break; } } diff --git a/test/Sema/callingconv.c b/test/Sema/callingconv.c index a32a495308..0337c069f1 100644 --- a/test/Sema/callingconv.c +++ b/test/Sema/callingconv.c @@ -21,3 +21,15 @@ void __attribute__((fastcall)) test2(int a, ...) { // expected-error {{variadic void __attribute__((cdecl)) ctest0() {} void __attribute__((cdecl(1))) ctest1(float x) {} // expected-error {{attribute requires 0 argument(s)}} + +void (__attribute__((fastcall)) *pfoo)(float*) = foo; + +void (__attribute__((stdcall)) *pbar)(float*) = bar; + +void (__attribute__((cdecl)) *ptest1)(void) = test1; // expected-warning {{incompatible pointer types}} + +void (*pctest0)() = ctest0; + +void ctest2() {} +void (__attribute__((cdecl)) *pctest2)() = ctest2; +