r250090 - [Sema] Make `&function_with_enable_if_attrs` an error

George Burgess IV via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 12 12:57:04 PDT 2015


Author: gbiv
Date: Mon Oct 12 14:57:04 2015
New Revision: 250090

URL: http://llvm.org/viewvc/llvm-project?rev=250090&view=rev
Log:
[Sema] Make `&function_with_enable_if_attrs` an error

This fixes a bug where one can take the address of a conditionally
enabled function to drop its enable_if guards. For example:

  int foo(int a) __attribute__((enable_if(a > 0, "")));
  int (*p)(int) = &foo;
  int result = p(-1); // compilation succeeds; calls foo(-1)

Overloading logic has been updated to reflect this change, as well.

Functions with enable_if attributes that are always true are still
allowed to have their address taken.

Differential Revision: http://reviews.llvm.org/D13607

Added:
    cfe/trunk/test/CodeGen/enable_if.c
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCast.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/test/Sema/enable_if.c
    cfe/trunk/test/SemaCXX/enable_if.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=250090&r1=250089&r2=250090&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Oct 12 14:57:04 2015
@@ -1496,7 +1496,8 @@ def err_init_conversion_failed : Error<
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
   "volatile and restrict|const, volatile, and restrict}5 vs "
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
-  "volatile and restrict|const, volatile, and restrict}6)}4">;
+  "volatile and restrict|const, volatile, and restrict}6)"
+  "|: cannot take the address of a potentially disabled function}4">;
 
 def err_lvalue_to_rvalue_ref : Error<"rvalue reference %diff{to type $ cannot "
   "bind to lvalue of type $|cannot bind to incompatible lvalue}0,1">;
@@ -2967,7 +2968,8 @@ def note_ovl_candidate : Note<"candidate
     "%select{none|const|restrict|const and restrict|volatile|const and volatile"
     "|volatile and restrict|const, volatile, and restrict}3 but found "
     "%select{none|const|restrict|const and restrict|volatile|const and volatile"
-    "|volatile and restrict|const, volatile, and restrict}4)}2">;
+    "|volatile and restrict|const, volatile, and restrict}4)"
+    "| made ineligible by enable_if}2">;
 
 def note_ovl_candidate_inherited_constructor : Note<"inherited from here">;
 def note_ovl_candidate_illegal_constructor : Note<
@@ -5662,7 +5664,8 @@ def note_hidden_overloaded_virtual_decla
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
   "volatile and restrict|const, volatile, and restrict}2 vs "
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
-  "volatile and restrict|const, volatile, and restrict}3)}1">;
+  "volatile and restrict|const, volatile, and restrict}3)"
+  "|: mismatch in enable_if attributes}1">;
 def warn_using_directive_in_header : Warning<
   "using namespace directive in global context in header">,
   InGroup<HeaderHygiene>, DefaultIgnore;
@@ -5899,7 +5902,8 @@ def err_typecheck_convert_incompatible :
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
   "volatile and restrict|const, volatile, and restrict}5 vs "
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
-  "volatile and restrict|const, volatile, and restrict}6)}4">;
+  "volatile and restrict|const, volatile, and restrict}6)"
+  "|: cannot take the address of a potentially disabled function}4">;
 def err_typecheck_missing_return_type_incompatible : Error<
   "%diff{return type $ must match previous return type $|"
   "return type must match previous return type}0,1 when %select{block "

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=250090&r1=250089&r2=250090&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Oct 12 14:57:04 2015
@@ -2440,11 +2440,13 @@ public:
                                             bool PartialOverloading = false);
 
   // Emit as a 'note' the specific overload candidate
-  void NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType = QualType());
+  void NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType = QualType(),
+                             bool TakingAddress = false);
 
-  // Emit as a series of 'note's all template and non-templates
-  // identified by the expression Expr
-  void NoteAllOverloadCandidates(Expr* E, QualType DestType = QualType());
+  // Emit as a series of 'note's all template and non-templates identified by
+  // the expression Expr
+  void NoteAllOverloadCandidates(Expr *E, QualType DestType = QualType(),
+                                 bool TakingAddress = false);
 
   /// Check the enable_if expressions on the given function. Returns the first
   /// failing attribute, or NULL if they were all successful.

Modified: cfe/trunk/lib/Sema/SemaCast.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCast.cpp?rev=250090&r1=250089&r2=250090&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCast.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCast.cpp Mon Oct 12 14:57:04 2015
@@ -2236,6 +2236,16 @@ void CastOperation::CheckCStyleCast() {
     return;
   }
 
+  // Overloads are allowed with C extensions, so we need to support them.
+  if (SrcExpr.get()->getType() == Self.Context.OverloadTy) {
+    DeclAccessPair DAP;
+    if (FunctionDecl *FD = Self.ResolveAddressOfOverloadedFunction(
+            SrcExpr.get(), DestType, /*Complain=*/true, DAP))
+      SrcExpr = Self.FixOverloadedFunctionReference(SrcExpr.get(), DAP, FD);
+    else
+      return;
+    assert(SrcExpr.isUsable());
+  }
   SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get());
   if (SrcExpr.isInvalid())
     return;

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=250090&r1=250089&r2=250090&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon Oct 12 14:57:04 2015
@@ -7274,6 +7274,15 @@ Sema::CheckSingleAssignmentConstraints(Q
     // structures.
     // FIXME: We also fall through for atomics; not sure what should
     // happen there, though.
+  } else if (RHS.get()->getType() == Context.OverloadTy) {
+    // As a set of extensions to C, we support overloading on functions. These
+    // functions need to be resolved here.
+    DeclAccessPair DAP;
+    if (FunctionDecl *FD = ResolveAddressOfOverloadedFunction(
+            RHS.get(), LHSType, /*Complain=*/false, DAP))
+      RHS = FixOverloadedFunctionReference(RHS.get(), DAP, FD);
+    else
+      return Incompatible;
   }
 
   // C99 6.5.16.1p1: the left operand is a pointer and the right is
@@ -11986,7 +11995,7 @@ bool Sema::DiagnoseAssignmentResult(Assi
     
   if (SecondType == Context.OverloadTy)
     NoteAllOverloadCandidates(OverloadExpr::find(SrcExpr).Expression,
-                              FirstType);
+                              FirstType, /*TakingAddress=*/true);
 
   if (CheckInferredResultType)
     EmitRelatedResultTypeNote(SrcExpr);

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=250090&r1=250089&r2=250090&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Mon Oct 12 14:57:04 2015
@@ -6599,6 +6599,8 @@ InitializationSequence::Perform(Sema &S,
 
     case SK_CAssignment: {
       QualType SourceType = CurInit.get()->getType();
+      // Save off the initial CurInit in case we need to emit a diagnostic
+      ExprResult InitialCurInit = CurInit;
       ExprResult Result = CurInit;
       Sema::AssignConvertType ConvTy =
         S.CheckSingleAssignmentConstraints(Step->Type, Result, true,
@@ -6621,7 +6623,7 @@ InitializationSequence::Perform(Sema &S,
       bool Complained;
       if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(),
                                      Step->Type, SourceType,
-                                     CurInit.get(),
+                                     InitialCurInit.get(),
                                      getAssignmentAction(Entity, true),
                                      &Complained)) {
         PrintInitLocationNote(S, Entity);

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=250090&r1=250089&r2=250090&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Mon Oct 12 14:57:04 2015
@@ -2508,7 +2508,8 @@ enum {
   ft_parameter_arity,
   ft_parameter_mismatch,
   ft_return_type,
-  ft_qualifer_mismatch
+  ft_qualifer_mismatch,
+  ft_addr_enable_if
 };
 
 /// HandleFunctionTypeMismatch - Gives diagnostic information for differeing
@@ -8683,20 +8684,37 @@ void MaybeEmitInheritedConstructorNote(S
 
 } // end anonymous namespace
 
+static bool isFunctionAlwaysEnabled(const ASTContext &Ctx,
+                                    const FunctionDecl *FD) {
+  for (auto *EnableIf : FD->specific_attrs<EnableIfAttr>()) {
+    bool AlwaysTrue;
+    if (!EnableIf->getCond()->EvaluateAsBooleanCondition(AlwaysTrue, Ctx))
+      return false;
+    if (!AlwaysTrue)
+      return false;
+  }
+  return true;
+}
+
 // Notes the location of an overload candidate.
-void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType) {
+void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType,
+                                 bool TakingAddress) {
   std::string FnDesc;
   OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Fn, FnDesc);
   PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
                              << (unsigned) K << FnDesc;
-  HandleFunctionTypeMismatch(PD, Fn->getType(), DestType);
+  if (TakingAddress && !isFunctionAlwaysEnabled(Context, Fn))
+    PD << ft_addr_enable_if;
+  else
+    HandleFunctionTypeMismatch(PD, Fn->getType(), DestType);
   Diag(Fn->getLocation(), PD);
   MaybeEmitInheritedConstructorNote(*this, Fn);
 }
 
 // Notes the location of all overload candidates designated through
 // OverloadedExpr
-void Sema::NoteAllOverloadCandidates(Expr* OverloadedExpr, QualType DestType) {
+void Sema::NoteAllOverloadCandidates(Expr *OverloadedExpr, QualType DestType,
+                                     bool TakingAddress) {
   assert(OverloadedExpr->getType() == Context.OverloadTy);
 
   OverloadExpr::FindResult Ovl = OverloadExpr::find(OverloadedExpr);
@@ -8707,10 +8725,11 @@ void Sema::NoteAllOverloadCandidates(Exp
        I != IEnd; ++I) {
     if (FunctionTemplateDecl *FunTmpl = 
                 dyn_cast<FunctionTemplateDecl>((*I)->getUnderlyingDecl()) ) {
-      NoteOverloadCandidate(FunTmpl->getTemplatedDecl(), DestType);
+      NoteOverloadCandidate(FunTmpl->getTemplatedDecl(), DestType,
+                            TakingAddress);
     } else if (FunctionDecl *Fun 
                       = dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()) ) {
-      NoteOverloadCandidate(Fun, DestType);
+      NoteOverloadCandidate(Fun, DestType, TakingAddress);
     }
   }
 }
@@ -10035,7 +10054,12 @@ private:
     Specialization = cast<FunctionDecl>(Specialization->getCanonicalDecl());
     assert(S.isSameOrCompatibleFunctionType(
               Context.getCanonicalType(Specialization->getType()),
-              Context.getCanonicalType(TargetFunctionType)));
+              Context.getCanonicalType(TargetFunctionType)) ||
+           (!S.getLangOpts().CPlusPlus && TargetType->isVoidPointerType()));
+
+    if (!isFunctionAlwaysEnabled(S.Context, Specialization))
+      return false;
+
     Matches.push_back(std::make_pair(CurAccessFunPair, Specialization));
     return true;
   }
@@ -10066,13 +10090,17 @@ private:
         return false;
       }
 
+      if (!isFunctionAlwaysEnabled(S.Context, FunDecl))
+        return false;
+
       QualType ResultTy;
       if (Context.hasSameUnqualifiedType(TargetFunctionType, 
                                          FunDecl->getType()) ||
           S.IsNoReturnConversion(FunDecl->getType(), TargetFunctionType,
-                                 ResultTy)) {
-        Matches.push_back(std::make_pair(CurAccessFunPair,
-          cast<FunctionDecl>(FunDecl->getCanonicalDecl())));
+                                 ResultTy) ||
+          (!S.getLangOpts().CPlusPlus && TargetType->isVoidPointerType())) {
+        Matches.push_back(std::make_pair(
+            CurAccessFunPair, cast<FunctionDecl>(FunDecl->getCanonicalDecl())));
         FoundNonTemplateFunction = true;
         return true;
       }
@@ -10174,7 +10202,8 @@ public:
         << OvlExpr->getName() << TargetFunctionType
         << OvlExpr->getSourceRange();
     if (FailedCandidates.empty())
-      S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType);
+      S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType,
+                                  /*TakingAddress=*/true);
     else {
       // We have some deduction failure messages. Use them to diagnose
       // the function templates, and diagnose the non-template candidates
@@ -10184,7 +10213,8 @@ public:
            I != IEnd; ++I)
         if (FunctionDecl *Fun =
                 dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()))
-          S.NoteOverloadCandidate(Fun, TargetFunctionType);
+          S.NoteOverloadCandidate(Fun, TargetFunctionType,
+                                  /*TakingAddress=*/true);
       FailedCandidates.NoteCandidates(S, OvlExpr->getLocStart());
     }
   }
@@ -10222,7 +10252,8 @@ public:
     S.Diag(OvlExpr->getLocStart(), diag::err_addr_ovl_ambiguous)
       << OvlExpr->getName()
       << OvlExpr->getSourceRange();
-    S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType);
+    S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType,
+                                /*TakingAddress=*/true);
   }
 
   bool hadMultipleCandidates() const { return (OvlExpr->getNumDecls() > 1); }

Added: cfe/trunk/test/CodeGen/enable_if.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/enable_if.c?rev=250090&view=auto
==============================================================================
--- cfe/trunk/test/CodeGen/enable_if.c (added)
+++ cfe/trunk/test/CodeGen/enable_if.c Mon Oct 12 14:57:04 2015
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-pc-linux-gnu | FileCheck %s
+
+// Verifying that we do, in fact, select the correct function in the following
+// cases.
+
+void foo(int m) __attribute__((overloadable, enable_if(m > 0, "")));
+void foo(int m) __attribute__((overloadable));
+
+// CHECK-LABEL: define void @test1
+void test1() {
+  // CHECK: store void (i32)* @_Z3fooi
+  void (*p)(int) = foo;
+  // CHECK: store void (i32)* @_Z3fooi
+  void (*p2)(int) = &foo;
+  // CHECK: store void (i32)* @_Z3fooi
+  p = foo;
+  // CHECK: store void (i32)* @_Z3fooi
+  p = &foo;
+
+  // CHECK: store i8* bitcast (void (i32)* @_Z3fooi to i8*)
+  void *vp1 = (void*)&foo;
+  // CHECK: store i8* bitcast (void (i32)* @_Z3fooi to i8*)
+  void *vp2 = (void*)foo;
+  // CHECK: store i8* bitcast (void (i32)* @_Z3fooi to i8*)
+  vp1 = (void*)&foo;
+  // CHECK: store i8* bitcast (void (i32)* @_Z3fooi to i8*)
+  vp1 = (void*)foo;
+}
+
+void bar(int m) __attribute__((overloadable, enable_if(m > 0, "")));
+void bar(int m) __attribute__((overloadable, enable_if(1, "")));
+// CHECK-LABEL: define void @test2
+void test2() {
+  // CHECK: store void (i32)* @_Z3barUa9enable_ifIXLi1EEEi
+  void (*p)(int) = bar;
+  // CHECK: store void (i32)* @_Z3barUa9enable_ifIXLi1EEEi
+  void (*p2)(int) = &bar;
+  // CHECK: store void (i32)* @_Z3barUa9enable_ifIXLi1EEEi
+  p = bar;
+  // CHECK: store void (i32)* @_Z3barUa9enable_ifIXLi1EEEi
+  p = &bar;
+
+  // CHECK: store i8* bitcast (void (i32)* @_Z3barUa9enable_ifIXLi1EEEi to i8*)
+  void *vp1 = (void*)&bar;
+  // CHECK: store i8* bitcast (void (i32)* @_Z3barUa9enable_ifIXLi1EEEi to i8*)
+  void *vp2 = (void*)bar;
+  // CHECK: store i8* bitcast (void (i32)* @_Z3barUa9enable_ifIXLi1EEEi to i8*)
+  vp1 = (void*)&bar;
+  // CHECK: store i8* bitcast (void (i32)* @_Z3barUa9enable_ifIXLi1EEEi to i8*)
+  vp1 = (void*)bar;
+}

Modified: cfe/trunk/test/Sema/enable_if.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/enable_if.c?rev=250090&r1=250089&r2=250090&view=diff
==============================================================================
--- cfe/trunk/test/Sema/enable_if.c (original)
+++ cfe/trunk/test/Sema/enable_if.c Mon Oct 12 14:57:04 2015
@@ -91,6 +91,12 @@ void test4(int c) {
 #endif
 }
 
+void test5() {
+  int (*p1)(int) = &isdigit2;
+  int (*p2)(int) = isdigit2;
+  void *p3 = (void *)&isdigit2;
+  void *p4 = (void *)isdigit2;
+}
 
 #ifndef CODEGEN
 __attribute__((enable_if(n == 0, "chosen when 'n' is zero"))) void f1(int n); // expected-error{{use of undeclared identifier 'n'}}
@@ -109,4 +115,23 @@ void f(int n) __attribute__((enable_if(g
 const int cst = 7;
 void return_cst(void) __attribute__((overloadable)) __attribute__((enable_if(cst == 7, "chosen when 'cst' is 7")));
 void test_return_cst() { return_cst(); }
+
+void f2(void) __attribute__((overloadable)) __attribute__((enable_if(1, "always chosen")));
+void f2(void) __attribute__((overloadable)) __attribute__((enable_if(0, "never chosen")));
+void f2(void) __attribute__((overloadable));
+void test6() {
+  void (*p1)(void) = &f2; // expected-error{{initializing 'void (*)(void)' with an expression of incompatible type '<overloaded function type>'}} expected-note at 119{{candidate function}} expected-note at 120{{candidate function made ineligible by enable_if}} expected-note at 121{{candidate function}}
+  void (*p2)(void) = f2; // expected-error{{initializing 'void (*)(void)' with an expression of incompatible type '<overloaded function type>'}} expected-note at 119{{candidate function}} expected-note at 120{{candidate function made ineligible by enable_if}} expected-note at 121{{candidate function}}
+  void *p3 = (void*)&f2; // expected-error{{address of overloaded function 'f2' is ambiguous}} expected-note at 119{{candidate function}} expected-note at 120{{candidate function made ineligible by enable_if}} expected-note at 121{{candidate function}}
+  void *p4 = (void*)f2; // expected-error{{address of overloaded function 'f2' is ambiguous}} expected-note at 119{{candidate function}} expected-note at 120{{candidate function made ineligible by enable_if}} expected-note at 121{{candidate function}}
+}
+
+void f3(int m) __attribute__((overloadable)) __attribute__((enable_if(m >= 0, "positive")));
+void f3(int m) __attribute__((overloadable)) __attribute__((enable_if(m < 0, "negative")));
+void test7() {
+  void (*p1)(int) = &f3; // expected-error{{initializing 'void (*)(int)' with an expression of incompatible type '<overloaded function type>'}} expected-note at 129{{candidate function made ineligible by enable_if}} expected-note at 130{{candidate function made ineligible by enable_if}}
+  void (*p2)(int) = f3; // expected-error{{initializing 'void (*)(int)' with an expression of incompatible type '<overloaded function type>'}} expected-note at 129{{candidate function made ineligible by enable_if}} expected-note at 130{{candidate function made ineligible by enable_if}}
+  void *p3 = (void*)&f3; // expected-error{{address of overloaded function 'f3' does not match required type 'void'}} expected-note at 129{{candidate function made ineligible by enable_if}} expected-note at 130{{candidate function made ineligible by enable_if}}
+  void *p4 = (void*)f3; // expected-error{{address of overloaded function 'f3' does not match required type 'void'}} expected-note at 129{{candidate function made ineligible by enable_if}} expected-note at 130{{candidate function made ineligible by enable_if}}
+}
 #endif

Modified: cfe/trunk/test/SemaCXX/enable_if.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/enable_if.cpp?rev=250090&r1=250089&r2=250090&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/enable_if.cpp (original)
+++ cfe/trunk/test/SemaCXX/enable_if.cpp Mon Oct 12 14:57:04 2015
@@ -163,3 +163,74 @@ namespace PR20988 {
     fn3(sizeof(T) == 1);
   }
 }
+
+namespace FnPtrs {
+  int ovlFoo(int m) __attribute__((enable_if(m > 0, "")));
+  int ovlFoo(int m);
+
+  void test() {
+    // Assignment gives us a different code path than declarations, and `&foo`
+    // gives us a different code path than `foo`
+    int (*p)(int) = ovlFoo;
+    int (*p2)(int) = &ovlFoo;
+    int (*a)(int);
+    a = ovlFoo;
+    a = &ovlFoo;
+  }
+
+  int ovlBar(int) __attribute__((enable_if(true, "")));
+  int ovlBar(int m) __attribute__((enable_if(false, "")));
+  void test2() {
+    int (*p)(int) = ovlBar;
+    int (*p2)(int) = &ovlBar;
+    int (*a)(int);
+    a = ovlBar;
+    a = &ovlBar;
+  }
+
+  int ovlConflict(int m) __attribute__((enable_if(true, "")));
+  int ovlConflict(int m);
+  void test3() {
+    int (*p)(int) = ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note at 191{{candidate function}} expected-note at 192{{candidate function}}
+    int (*p2)(int) = &ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note at 191{{candidate function}} expected-note at 192{{candidate function}}
+    int (*a)(int);
+    a = ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note at 191{{candidate function}} expected-note at 192{{candidate function}}
+    a = &ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note at 191{{candidate function}} expected-note at 192{{candidate function}}
+  }
+
+  template <typename T>
+  T templated(T m) __attribute__((enable_if(true, ""))) { return T(); }
+  template <typename T>
+  T templated(T m) __attribute__((enable_if(false, ""))) { return T(); }
+  void test4() {
+    int (*p)(int) = templated<int>;
+    int (*p2)(int) = &templated<int>;
+    int (*a)(int);
+    a = templated<int>;
+    a = &templated<int>;
+  }
+
+  template <typename T>
+  T templatedBar(T m) __attribute__((enable_if(m > 0, ""))) { return T(); }
+  void test5() {
+    int (*p)(int) = templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note at 214{{candidate function made ineligible by enable_if}}
+    int (*p2)(int) = &templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note at 214{{candidate function made ineligible by enable_if}}
+    int (*a)(int);
+    a = templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note at 214{{candidate function made ineligible by enable_if}}
+    a = &templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note at 214{{candidate function made ineligible by enable_if}}
+  }
+
+  template <typename T>
+  T templatedConflict(T m) __attribute__((enable_if(false, ""))) { return T(); }
+  template <typename T>
+  T templatedConflict(T m) __attribute__((enable_if(true, ""))) { return T(); }
+  template <typename T>
+  T templatedConflict(T m) { return T(); }
+  void test6() {
+    int (*p)(int) = templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note at 224{{candidate function made ineligible by enable_if}} expected-note at 226{{candidate function}} expected-note at 228{{candidate function}}
+    int (*p0)(int) = &templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note at 224{{candidate function made ineligible by enable_if}} expected-note at 226{{candidate function}} expected-note at 228{{candidate function}}
+    int (*a)(int);
+    a = templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note at 226{{candidate function}} expected-note at 228{{candidate function}}
+    a = &templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note at 226{{candidate function}} expected-note at 228{{candidate function}}
+  }
+}




More information about the cfe-commits mailing list