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 f53fd92..fa5d3e1 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -1766,10 +1766,11 @@
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 @@
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 d6c9041..52c5cbc 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -1716,7 +1716,7 @@
// 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 @@
}
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 @@
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 b3010d5..4e74759 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -815,7 +815,8 @@
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 @@
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 00b74bc..597ab37 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -271,6 +271,19 @@
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 12ce174..19de095 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -560,7 +560,9 @@
// 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 80831f4..2e3a435 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -901,6 +901,14 @@
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 @@
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<FunctionType>();
+ const FunctionType *NewType = New->getType()->getAs<FunctionType>();
+ 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 a391a0e..5e7788e 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -948,6 +948,12 @@
}
// 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<ValueDecl>(d);
+ if (VD && VD->getType()->isFunctionPointerType())
+ return;
if (!isa<FunctionDecl>(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
<< Attr.getName() << 0 /*function*/;
@@ -980,6 +986,11 @@
}
// 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<ValueDecl>(d);
+ if (VD && VD->getType()->isFunctionPointerType())
+ return;
if (!isa<FunctionDecl>(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
<< Attr.getName() << 0 /*function*/;
@@ -1019,6 +1030,11 @@
return;
}
+ // If we try to apply it to a function pointer, don't warn, but don't
+ // do anything, either.
+ ValueDecl *VD = dyn_cast<ValueDecl>(d);
+ if (VD && VD->getType()->isFunctionPointerType())
+ return;
if (!isa<FunctionDecl>(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 9065761..8833792 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -1241,7 +1241,7 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 a32a495..0337c06 100644
--- a/test/Sema/callingconv.c
+++ b/test/Sema/callingconv.c
@@ -21,3 +21,15 @@
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;
+