diff options
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 25 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 11 | ||||
-rw-r--r-- | test/Sema/knr-variadic-def.c | 29 |
3 files changed, 51 insertions, 14 deletions
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9c650e61e7..959154c4ec 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -765,6 +765,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { // 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 a variadic prototype is followed by a non-variadic K&R definition, + // the K&R definition becomes variadic. This is sort of an edge case, but + // it's legal per the standard depending on how you read C99 6.7.5.3p15 and + // C99 6.9.1p8. if (!getLangOptions().CPlusPlus && Old->hasPrototype() && !New->hasPrototype() && New->getType()->getAsFunctionProtoType() && @@ -777,12 +782,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { = New->getType()->getAsFunctionProtoType(); // Determine whether this is the GNU C extension. - bool GNUCompatible = - Context.typesAreCompatible(OldProto->getResultType(), - NewProto->getResultType()) && - (OldProto->isVariadic() == NewProto->isVariadic()); + QualType MergedReturn = Context.mergeTypes(OldProto->getResultType(), + NewProto->getResultType()); + bool LooseCompatible = !MergedReturn.isNull(); for (unsigned Idx = 0, End = Old->getNumParams(); - GNUCompatible && Idx != End; ++Idx) { + LooseCompatible && Idx != End; ++Idx) { ParmVarDecl *OldParm = Old->getParamDecl(Idx); ParmVarDecl *NewParm = New->getParamDecl(Idx); if (Context.typesAreCompatible(OldParm->getType(), @@ -795,10 +799,10 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { Warnings.push_back(Warn); ArgTypes.push_back(NewParm->getType()); } else - GNUCompatible = false; + LooseCompatible = false; } - if (GNUCompatible) { + if (LooseCompatible) { for (unsigned Warn = 0; Warn < Warnings.size(); ++Warn) { Diag(Warnings[Warn].NewParm->getLocation(), diag::ext_param_promoted_not_compatible_with_prototype) @@ -808,10 +812,9 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { diag::note_previous_declaration); } - New->setType(Context.getFunctionType(NewProto->getResultType(), - &ArgTypes[0], ArgTypes.size(), - NewProto->isVariadic(), - NewProto->getTypeQuals())); + New->setType(Context.getFunctionType(MergedReturn, &ArgTypes[0], + ArgTypes.size(), + OldProto->isVariadic(), 0)); return MergeCompatibleFunctionDecls(New, Old); } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index f4bfefca13..ee5132a7d8 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2670,9 +2670,14 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, // Check if we have too few/too many template arguments, based // on our knowledge of the function definition. const FunctionDecl *Def = 0; - if (FDecl->getBody(Context, Def) && NumArgs != Def->param_size()) - Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments) - << (NumArgs > Def->param_size()) << FDecl << Fn->getSourceRange(); + if (FDecl->getBody(Context, Def) && NumArgs != Def->param_size()) { + const FunctionProtoType *Proto = + Def->getType()->getAsFunctionProtoType(); + if (!Proto || !(Proto->isVariadic() && NumArgs >= Def->param_size())) { + Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments) + << (NumArgs > Def->param_size()) << FDecl << Fn->getSourceRange(); + } + } } // Promote the arguments (C99 6.5.2.2p6). diff --git a/test/Sema/knr-variadic-def.c b/test/Sema/knr-variadic-def.c new file mode 100644 index 0000000000..070ba071ca --- /dev/null +++ b/test/Sema/knr-variadic-def.c @@ -0,0 +1,29 @@ +// RUN: clang-cc -fsyntax-only -verify -pedantic %s +// PR4287 + +#include <stdarg.h> +char *foo = "test"; +int test(char*,...); + +int test(fmt) + char*fmt; +{ + va_list ap; + char*a; + int x; + + va_start(ap,fmt); + a=va_arg(ap,char*); + x=(a!=foo); + va_end(ap); + return x; +} + +void exit(); + +int main(argc,argv) + int argc;char**argv; +{ + exit(test("",foo)); +} + |