]> granicus.if.org Git - clang/commitdiff
Implement GNU C semantics for K&R function definitions that follow a
authorDouglas Gregor <dgregor@apple.com>
Fri, 6 Mar 2009 22:43:54 +0000 (22:43 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 6 Mar 2009 22:43:54 +0000 (22:43 +0000)
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

include/clang/Basic/DiagnosticSemaKinds.def
lib/Sema/SemaDecl.cpp
test/Sema/function-redecl.c
test/Sema/knr-def-call.c

index 26a6ab7821e28e14da74ec39d335f8431d42d09f..953d3e3429fc1e6f5cd3daa34c456558d493e417 100644 (file)
@@ -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,
index b25ba431496efcb592de22ad85cc369b83bd35d2..50113ee4b7f466352303e8e7a0cb05f63b99a481 100644 (file)
@@ -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
index 4ba97c2a8fdc03e9e1d0f7bf0f4de83f9bccab3c..999aa49189485b3ad2b67fcb6c0a8f21306eba1f 100644 (file)
@@ -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;
+}
index 706f21418ee6b03f086811ac9ed17cfadaf4b53c..83a0c5e24cbc80ebfa68bd8e4fc6fdaae545528f 100644 (file)
@@ -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