give better diagnostics for converting between function pointer and void*.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@45556 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/Sema/Sema.h b/Sema/Sema.h
index 7faac13..33fd345 100644
--- a/Sema/Sema.h
+++ b/Sema/Sema.h
@@ -612,6 +612,7 @@
     Incompatible,
     PointerFromInt, 
     IntFromPointer,
+    FunctionVoidPointer,
     IncompatiblePointer,
     CompatiblePointerDiscardsQualifiers
   };
diff --git a/Sema/SemaDecl.cpp b/Sema/SemaDecl.cpp
index 5a58839..1165e52 100644
--- a/Sema/SemaDecl.cpp
+++ b/Sema/SemaDecl.cpp
@@ -394,6 +394,11 @@
          DeclType.getAsString(), rhsType.getAsString(), 
          Init->getSourceRange());
     break;
+  case FunctionVoidPointer:
+    Diag(Init->getLocStart(), diag::ext_typecheck_assign_pointer_void_func, 
+         DeclType.getAsString(), rhsType.getAsString(), 
+         Init->getSourceRange());
+    break;
   case IncompatiblePointer:
     Diag(Init->getLocStart(), diag::ext_typecheck_assign_incompatible_pointer,
          DeclType.getAsString(), rhsType.getAsString(), 
diff --git a/Sema/SemaExpr.cpp b/Sema/SemaExpr.cpp
index 1ec60d9..68c301a 100644
--- a/Sema/SemaExpr.cpp
+++ b/Sema/SemaExpr.cpp
@@ -644,6 +644,11 @@
              LHSType.getAsString(), RHSType.getAsString(),
              Fn->getSourceRange(), Arg->getSourceRange());
         break;
+      case FunctionVoidPointer:
+        Diag(Loc, diag::ext_typecheck_passing_pointer_void_func, 
+             LHSType.getAsString(), RHSType.getAsString(),
+             Fn->getSourceRange(), Arg->getSourceRange());
+        break;
       case IncompatiblePointer:
         Diag(Loc, diag::ext_typecheck_passing_incompatible_pointer, 
              RHSType.getAsString(), LHSType.getAsString(),
@@ -1075,17 +1080,29 @@
   // C99 6.5.16.1p1 (constraint 4): If one operand is a pointer to an object or 
   // incomplete type and the other is a pointer to a qualified or unqualified 
   // version of void...
-  if (lhptee.getUnqualifiedType()->isVoidType() &&
-      (rhptee->isObjectType() || rhptee->isIncompleteType()))
-    ;
-  else if (rhptee.getUnqualifiedType()->isVoidType() &&
-      (lhptee->isObjectType() || lhptee->isIncompleteType()))
-    ;
+  if (lhptee->isVoidType()) {
+    if (rhptee->isObjectType() || rhptee->isIncompleteType())
+      return r;
+    
+    // As an extension, we allow cast to/from void* to function pointer.
+    if (rhptee->isFunctionType())
+      return FunctionVoidPointer;
+  }
+  
+  if (rhptee->isVoidType()) {
+    if (lhptee->isObjectType() || lhptee->isIncompleteType())
+      return r;
+
+    // As an extension, we allow cast to/from void* to function pointer.
+    if (lhptee->isFunctionType())
+      return FunctionVoidPointer;
+  }
+  
   // C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or 
   // unqualified versions of compatible types, ...
-  else if (!Context.typesAreCompatible(lhptee.getUnqualifiedType(), 
-                                       rhptee.getUnqualifiedType()))
-    r = IncompatiblePointer; // this "trumps" PointerAssignDiscardsQualifiers
+  if (!Context.typesAreCompatible(lhptee.getUnqualifiedType(), 
+                                  rhptee.getUnqualifiedType()))
+    return IncompatiblePointer; // this "trumps" PointerAssignDiscardsQualifiers
   return r;
 }
 
@@ -1543,6 +1560,11 @@
          lhsType.getAsString(), rhsType.getAsString(),
          lex->getSourceRange(), rex->getSourceRange());
     break;
+  case FunctionVoidPointer:
+    Diag(loc, diag::ext_typecheck_assign_pointer_void_func, 
+         lhsType.getAsString(), rhsType.getAsString(),
+         lex->getSourceRange(), rex->getSourceRange());
+    break;
   case IncompatiblePointer:
     Diag(loc, diag::ext_typecheck_assign_incompatible_pointer,
          lhsType.getAsString(), rhsType.getAsString(),
@@ -2226,6 +2248,11 @@
            rhsType.getAsString(), lhsType.getAsString(),
            argExpr->getSourceRange());
       break;
+    case FunctionVoidPointer:
+      Diag(l, diag::ext_typecheck_sending_pointer_void_func, 
+           rhsType.getAsString(), lhsType.getAsString(),
+           argExpr->getSourceRange());
+      break;
     case CompatiblePointerDiscardsQualifiers:
       Diag(l, diag::ext_typecheck_passing_discards_qualifiers,
            rhsType.getAsString(), lhsType.getAsString(),
diff --git a/Sema/SemaStmt.cpp b/Sema/SemaStmt.cpp
index cbb01bd..2c2dc5e 100644
--- a/Sema/SemaStmt.cpp
+++ b/Sema/SemaStmt.cpp
@@ -663,6 +663,11 @@
          lhsType.getAsString(), rhsType.getAsString(),
          RetValExp->getSourceRange());
     break;
+  case FunctionVoidPointer:
+    Diag(ReturnLoc, diag::ext_typecheck_return_pointer_void_func, 
+         lhsType.getAsString(), rhsType.getAsString(),
+         RetValExp->getSourceRange());
+    break;
   case IncompatiblePointer:
     Diag(ReturnLoc, diag::ext_typecheck_return_incompatible_pointer,
          lhsType.getAsString(), rhsType.getAsString(),
diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def
index 591e305..0f31c92 100644
--- a/include/clang/Basic/DiagnosticKinds.def
+++ b/include/clang/Basic/DiagnosticKinds.def
@@ -767,6 +767,8 @@
      "incompatible pointer types assigning '%1' to '%0'")
 DIAG(ext_typecheck_assign_discards_qualifiers, WARNING,
      "assigning '%1' to '%0' discards qualifiers")
+DIAG(ext_typecheck_assign_pointer_void_func, EXTENSION,
+     "assigning '%1' to '%0' converts between void* and function pointer")
 DIAG(err_typecheck_array_not_modifiable_lvalue, ERROR,
      "array type '%0' is not assignable")
 DIAG(err_typecheck_non_object_not_modifiable_lvalue, ERROR,
@@ -791,6 +793,9 @@
      "incompatible pointer types passing '%0' to function expecting '%1'")
 DIAG(ext_typecheck_passing_pointer_int, WARNING,
      "incompatible types passing '%1' to function expecting '%0'")
+DIAG(ext_typecheck_passing_pointer_void_func, EXTENSION,
+     "passing '%1' to function expecting '%0' converts between void*"
+     " and function pointer")
 DIAG(ext_typecheck_passing_discards_qualifiers, WARNING,
      "passing '%0' to '%1' discards qualifiers")
 DIAG(err_typecheck_sending_incompatible, ERROR,
@@ -799,6 +804,9 @@
      "incompatible pointer types passing '%0' to method expecting '%1'")
 DIAG(ext_typecheck_sending_pointer_int, WARNING,
      "incompatible types passing '%1' to method expecting '%0'")
+DIAG(ext_typecheck_sending_pointer_void_func, EXTENSION,
+     "sending '%1' to method expecting '%0' converts between void*"
+     " and function pointer")
 DIAG(err_typecheck_cond_expect_scalar, ERROR,
      "used type '%0' where arithmetic or pointer type is required")
 DIAG(err_typecheck_expect_scalar_operand, ERROR,
@@ -909,6 +917,9 @@
      "incompatible type returning '%1', expected '%0'")
 DIAG(ext_typecheck_return_pointer_int, EXTENSION,
      "incompatible type returning '%1', expected '%0'")
+DIAG(ext_typecheck_return_pointer_void_func, EXTENSION,
+     "returning '%1' to from function expecting '%0' converts between void*"
+     " and function pointer")
 DIAG(ext_typecheck_return_incompatible_pointer, EXTENSION,
      "incompatible pointer type returning '%1', expected '%0'")
 DIAG(ext_typecheck_return_discards_qualifiers, EXTENSION,
diff --git a/test/Sema/function-ptr.c b/test/Sema/function-ptr.c
new file mode 100644
index 0000000..83e4d74
--- /dev/null
+++ b/test/Sema/function-ptr.c
@@ -0,0 +1,11 @@
+// RUN: clang %s -verify -pedantic
+typedef int unary_int_func(int arg);
+unary_int_func *func;
+
+unary_int_func *set_func(void *p) {
+ func = p; // expected-warning {{converts between void* and function pointer}}
+ p = func; // expected-warning {{converts between void* and function pointer}}
+
+ return p; // expected-warning {{converts between void* and function pointer}}
+}
+