]> granicus.if.org Git - clang/commitdiff
PR4287: allow a variadic prototype to make a subsequent K&R style
authorEli Friedman <eli.friedman@gmail.com>
Mon, 1 Jun 2009 09:24:59 +0000 (09:24 +0000)
committerEli Friedman <eli.friedman@gmail.com>
Mon, 1 Jun 2009 09:24:59 +0000 (09:24 +0000)
definition variadic.  I'm not completely sure it's legal, but the
standard can be interpreted as making it legal, and gcc seems to think
it's legal, so I didn't add an extension warning.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@72689 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaDecl.cpp
lib/Sema/SemaExpr.cpp
test/Sema/knr-variadic-def.c [new file with mode: 0644]

index 9c650e61e7cfff3a954fea9412330b6c27c3b68f..959154c4ec1b55114851508321c6e2f08a477e12 100644 (file)
@@ -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);
     }
 
index f4bfefca13cf2adf8a6a48f768ddf5ef60255038..ee5132a7d8e00da6da0c0f1d3bce4c4164c8b573 100644 (file)
@@ -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 (file)
index 0000000..070ba07
--- /dev/null
@@ -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));
+}
+