From: Douglas Gregor Date: Fri, 6 Mar 2009 22:43:54 +0000 (+0000) Subject: Implement GNU C semantics for K&R function definitions that follow a X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c837656ca6d2a5b434923d7e2fd11d3a3c3bfa74;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66301 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 26a6ab7821..953d3e3429 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/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/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index b25ba43149..50113ee4b7 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/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(NewFuncType) && - (OldProto = OldQType->getAsFunctionProtoType())) { + (OldProto = dyn_cast(OldFuncType))) { // The old declaration provided a function prototype, but the // new declaration does not. Merge in the prototype. llvm::SmallVector 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 ArgTypes; + llvm::SmallVector 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/test/Sema/function-redecl.c b/test/Sema/function-redecl.c index 4ba97c2a8f..999aa49189 100644 --- a/test/Sema/function-redecl.c +++ b/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/test/Sema/knr-def-call.c b/test/Sema/knr-def-call.c index 706f21418e..83a0c5e24c 100644 --- a/test/Sema/knr-def-call.c +++ b/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