diff options
| author | Douglas Gregor <dgregor@apple.com> | 2009-03-06 22:43:54 +0000 |
|---|---|---|
| committer | Douglas Gregor <dgregor@apple.com> | 2009-03-06 22:43:54 +0000 |
| commit | a74a29771f5746bcfe4eee77617185080982f7be (patch) | |
| tree | 465ab5cc663bdb8c4f0bda133b3541d1a8323f3f | |
| parent | d39e0974fe640dd552e27e10ee20a9cd91bd6752 (diff) | |
| download | bcm5719-llvm-a74a29771f5746bcfe4eee77617185080982f7be.tar.gz bcm5719-llvm-a74a29771f5746bcfe4eee77617185080982f7be.zip | |
Implement GNU C semantics for K&R function definitions that follow a
prototype of the same function, where the promoted parameter types in
the K&R definition are not compatible with the types in the
prototype. Fixes PR2821.
llvm-svn: 66301
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.def | 3 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 72 | ||||
| -rw-r--r-- | clang/test/Sema/function-redecl.c | 13 | ||||
| -rw-r--r-- | clang/test/Sema/knr-def-call.c | 2 |
4 files changed, 87 insertions, 3 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.def b/clang/include/clang/Basic/DiagnosticSemaKinds.def index 26a6ab7821e..953d3e3429f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.def +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.def @@ -470,6 +470,9 @@ DIAG(err_param_default_argument_references_this, ERROR, DIAG(err_param_default_argument_nonfunc, ERROR, "default arguments can only be specified for parameters in a function" " declaration") +DIAG(ext_param_promoted_not_compatible_with_prototype, EXTWARN, + "promoted type %0 of K&R function parameter is not compatible with the " + "parameter type %1 declared in a previous prototype") // C++ Overloading Semantic Analysis. DIAG(err_ovl_diff_return_type, ERROR, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index b25ba431496..50113ee4b7f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -510,6 +510,14 @@ static void MergeAttributes(Decl *New, Decl *Old, ASTContext &C) { Old->invalidateAttrs(); } +/// Used in MergeFunctionDecl to keep track of function parameters in +/// C. +struct GNUCompatibleParamWarning { + ParmVarDecl *OldParm; + ParmVarDecl *NewParm; + QualType PromotedType; +}; + /// 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, @@ -617,10 +625,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { // duplicate function decls like "void f(int); void f(enum X);" properly. if (!getLangOptions().CPlusPlus && Context.typesAreCompatible(OldQType, NewQType)) { + const FunctionType *OldFuncType = OldQType->getAsFunctionType(); const FunctionType *NewFuncType = NewQType->getAsFunctionType(); const FunctionProtoType *OldProto = 0; if (isa<FunctionNoProtoType>(NewFuncType) && - (OldProto = OldQType->getAsFunctionProtoType())) { + (OldProto = dyn_cast<FunctionProtoType>(OldFuncType))) { // The old declaration provided a function prototype, but the // new declaration does not. Merge in the prototype. llvm::SmallVector<QualType, 16> ParamTypes(OldProto->arg_type_begin(), @@ -647,10 +656,69 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { } New->setParams(Context, &Params[0], Params.size()); + } + + return MergeCompatibleFunctionDecls(New, Old); + } + // GNU C permits a K&R definition to follow a prototype declaration + // if the declared types of the parameters in the K&R definition + // match the types in the prototype declaration, even when the + // promoted types of the parameters from the K&R definition differ + // from the types in the prototype. GCC then keeps the types from + // the prototype. + if (!getLangOptions().CPlusPlus && + !getLangOptions().NoExtensions && + Old->hasPrototype() && !New->hasPrototype() && + New->getType()->getAsFunctionProtoType() && + Old->getNumParams() == New->getNumParams()) { + llvm::SmallVector<QualType, 16> ArgTypes; + llvm::SmallVector<GNUCompatibleParamWarning, 16> Warnings; + const FunctionProtoType *OldProto + = Old->getType()->getAsFunctionProtoType(); + const FunctionProtoType *NewProto + = New->getType()->getAsFunctionProtoType(); + + // Determine whether this is the GNU C extension. + bool GNUCompatible = + Context.typesAreCompatible(OldProto->getResultType(), + NewProto->getResultType()) && + (OldProto->isVariadic() == NewProto->isVariadic()); + for (unsigned Idx = 0, End = Old->getNumParams(); + GNUCompatible && Idx != End; ++Idx) { + ParmVarDecl *OldParm = Old->getParamDecl(Idx); + ParmVarDecl *NewParm = New->getParamDecl(Idx); + if (Context.typesAreCompatible(OldParm->getType(), + NewProto->getArgType(Idx))) { + ArgTypes.push_back(NewParm->getType()); + } else if (Context.typesAreCompatible(OldParm->getType(), + NewParm->getType())) { + GNUCompatibleParamWarning Warn + = { OldParm, NewParm, NewProto->getArgType(Idx) }; + Warnings.push_back(Warn); + ArgTypes.push_back(NewParm->getType()); + } else + GNUCompatible = false; + } + + if (GNUCompatible) { + for (unsigned Warn = 0; Warn < Warnings.size(); ++Warn) { + Diag(Warnings[Warn].NewParm->getLocation(), + diag::ext_param_promoted_not_compatible_with_prototype) + << Warnings[Warn].PromotedType + << Warnings[Warn].OldParm->getType(); + Diag(Warnings[Warn].OldParm->getLocation(), + diag::note_previous_declaration); + } + + New->setType(Context.getFunctionType(NewProto->getResultType(), + &ArgTypes[0], ArgTypes.size(), + NewProto->isVariadic(), + NewProto->getTypeQuals())); + return MergeCompatibleFunctionDecls(New, Old); } - return MergeCompatibleFunctionDecls(New, Old); + // Fall through to diagnose conflicting types. } // A function that has already been declared has been redeclared or defined diff --git a/clang/test/Sema/function-redecl.c b/clang/test/Sema/function-redecl.c index 4ba97c2a8fd..999aa491894 100644 --- a/clang/test/Sema/function-redecl.c +++ b/clang/test/Sema/function-redecl.c @@ -93,3 +93,16 @@ void outer_test3() { } static float outer8(float); // okay + +enum e { e1, e2 }; + +// GNU extension: prototypes and K&R function definitions +int isroot(short x, // expected-note{{previous declaration is here}} + enum e); + +int isroot(x, y) + short x; // expected-warning{{promoted type 'int' of K&R function parameter is not compatible with the parameter type 'short' declared in a previous prototype}} + unsigned int y; +{ + return x == 1; +} diff --git a/clang/test/Sema/knr-def-call.c b/clang/test/Sema/knr-def-call.c index 706f21418ee..83a0c5e24cb 100644 --- a/clang/test/Sema/knr-def-call.c +++ b/clang/test/Sema/knr-def-call.c @@ -8,7 +8,7 @@ void f1(a, b) int a, b; {} void t1(void) { f1(1, 2, 3); } void f2(float); // expected-note{{previous declaration is here}} -void f2(x) float x; { } // expected-error{{conflicting types for 'f2'}} +void f2(x) float x; { } // expected-warning{{promoted type 'double' of K&R function parameter is not compatible with the parameter type 'float' declared in a previous prototype}} typedef void (*f3)(void); f3 t3(int b) { return b? f0 : f1; } // okay |

