[cfe-commits] r66301 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.def lib/Sema/SemaDecl.cpp test/Sema/function-redecl.c test/Sema/knr-def-call.c

Douglas Gregor dgregor at apple.com
Fri Mar 6 14:43:54 PST 2009


Author: dgregor
Date: Fri Mar  6 16:43:54 2009
New Revision: 66301

URL: http://llvm.org/viewvc/llvm-project?rev=66301&view=rev
Log:
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.

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

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def?rev=66301&r1=66300&r2=66301&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def Fri Mar  6 16:43:54 2009
@@ -470,6 +470,9 @@
 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,

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=66301&r1=66300&r2=66301&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Mar  6 16:43:54 2009
@@ -510,6 +510,14 @@
   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 @@
   // 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 @@
       }
 
       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

Modified: cfe/trunk/test/Sema/function-redecl.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/function-redecl.c?rev=66301&r1=66300&r2=66301&view=diff

==============================================================================
--- cfe/trunk/test/Sema/function-redecl.c (original)
+++ cfe/trunk/test/Sema/function-redecl.c Fri Mar  6 16:43:54 2009
@@ -93,3 +93,16 @@
 }
 
 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;
+}

Modified: cfe/trunk/test/Sema/knr-def-call.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/knr-def-call.c?rev=66301&r1=66300&r2=66301&view=diff

==============================================================================
--- cfe/trunk/test/Sema/knr-def-call.c (original)
+++ cfe/trunk/test/Sema/knr-def-call.c Fri Mar  6 16:43:54 2009
@@ -8,7 +8,7 @@
 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





More information about the cfe-commits mailing list