george.burgess.iv updated this revision to Diff 52619.
george.burgess.iv marked 4 inline comments as done.
george.burgess.iv added a comment.

Addressed most feedback; see comments. :)


http://reviews.llvm.org/D18425

Files:
  include/clang/Sema/Sema.h
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaOverload.cpp
  test/SemaCXX/enable_if.cpp

Index: test/SemaCXX/enable_if.cpp
===================================================================
--- test/SemaCXX/enable_if.cpp
+++ test/SemaCXX/enable_if.cpp
@@ -133,13 +133,18 @@
   int t1 = y.h(1, 2);  // expected-error{{no matching member function for call to 'h'}}
 }
 
-// FIXME: issue an error (without instantiation) because ::h(T()) is not
-// convertible to bool, because return types aren't overloadable.
+void ovlH(int);
+int ovlH(double);
 void h(int);
-template <typename T> void outer() {
-  void local_function() __attribute__((enable_if(::h(T()), "")));
+template <typename T> int outer() {
+  void local_function() __attribute__((enable_if(::h(T()), ""))); // expected-error{{value of type 'void' is not contextually convertible to 'bool'}}
+  void local_function2() __attribute__((enable_if(::ovlH(T()), ""))); // expected-error{{value of type 'void' is not contextually convertible to 'bool'}}
   local_function();
-};
+  local_function2();
+  return 0;
+}
+
+int runOuter = outer<int>(); // expected-note{{in instantiation of function template specialization 'outer<int>'}}
 
 namespace PR20988 {
   struct Integer {
@@ -191,11 +196,11 @@
   int ovlConflict(int m) __attribute__((enable_if(true, "")));
   int ovlConflict(int m) __attribute__((enable_if(1, "")));
   void test3() {
-    int (*p)(int) = ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
-    int (*p2)(int) = &ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
+    int (*p)(int) = ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@196{{candidate function}} expected-note@197{{candidate function}}
+    int (*p2)(int) = &ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@196{{candidate function}} expected-note@197{{candidate function}}
     int (*a)(int);
-    a = ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
-    a = &ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
+    a = ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@196{{candidate function}} expected-note@197{{candidate function}}
+    a = &ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@196{{candidate function}} expected-note@197{{candidate function}}
   }
 
   template <typename T>
@@ -213,11 +218,11 @@
   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@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@214{{candidate function made ineligible by enable_if}}
+    int (*p)(int) = templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note@219{{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@219{{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@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@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@219{{candidate function made ineligible by enable_if}}
+    a = &templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@219{{candidate function made ineligible by enable_if}}
   }
 
   template <typename T>
@@ -227,21 +232,21 @@
   template <typename T>
   T templatedConflict(T m) __attribute__((enable_if(1, ""))) { return T(); }
   void test6() {
-    int (*p)(int) = templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@224{{candidate function made ineligible by enable_if}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
-    int (*p0)(int) = &templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@224{{candidate function made ineligible by enable_if}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
+    int (*p)(int) = templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@229{{candidate function made ineligible by enable_if}} expected-note@231{{candidate function}} expected-note@233{{candidate function}}
+    int (*p0)(int) = &templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@229{{candidate function made ineligible by enable_if}} expected-note@231{{candidate function}} expected-note@233{{candidate function}}
     int (*a)(int);
-    a = templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
-    a = &templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
+    a = templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@231{{candidate function}} expected-note@233{{candidate function}}
+    a = &templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@231{{candidate function}} expected-note@233{{candidate function}}
   }
 
   int ovlNoCandidate(int m) __attribute__((enable_if(false, "")));
   int ovlNoCandidate(int m) __attribute__((enable_if(0, "")));
   void test7() {
-    int (*p)(int) = ovlNoCandidate; // expected-error{{address of overloaded function 'ovlNoCandidate' does not match required type}} expected-note@237{{made ineligible by enable_if}} expected-note@238{{made ineligible by enable_if}}
-    int (*p2)(int) = &ovlNoCandidate; // expected-error{{address of overloaded function 'ovlNoCandidate' does not match required type}} expected-note@237{{made ineligible by enable_if}} expected-note@238{{made ineligible by enable_if}}
+    int (*p)(int) = ovlNoCandidate; // expected-error{{address of overloaded function 'ovlNoCandidate' does not match required type}} expected-note@242{{made ineligible by enable_if}} expected-note@243{{made ineligible by enable_if}}
+    int (*p2)(int) = &ovlNoCandidate; // expected-error{{address of overloaded function 'ovlNoCandidate' does not match required type}} expected-note@242{{made ineligible by enable_if}} expected-note@243{{made ineligible by enable_if}}
     int (*a)(int);
-    a = ovlNoCandidate; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@237{{made ineligible by enable_if}} expected-note@238{{made ineligible by enable_if}}
-    a = &ovlNoCandidate; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@237{{made ineligible by enable_if}} expected-note@238{{made ineligible by enable_if}}
+    a = ovlNoCandidate; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@242{{made ineligible by enable_if}} expected-note@243{{made ineligible by enable_if}}
+    a = &ovlNoCandidate; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@242{{made ineligible by enable_if}} expected-note@243{{made ineligible by enable_if}}
   }
 
   int noOvlNoCandidate(int m) __attribute__((enable_if(false, "")));
@@ -386,3 +391,47 @@
   f.bar(1, 2); // expected-error{{too many arguments}}
 }
 }
+
+namespace value_dependent {
+// Ensure that enable_if works well with dependent expressions...
+
+constexpr int noOverload(int N) __attribute__((enable_if(!N, ""))) { return 0; } //expected-note 2 {{candidate disabled}}
+
+template <int N> constexpr int callNoOverload() { return noOverload(N); }
+
+static_assert(callNoOverload<0>() == 0, "");
+constexpr int Fail = callNoOverload<1>(); // expected-error@-3{{no matching function for call to 'noOverload'}} expected-error{{constexpr variable 'Fail' must be initialized by a constant expression}} expected-note{{in instantiation of function}}
+
+// Prefixing noOverload with its namespace means we get to skip ADL, so we won't
+// go into overload resolution for this.
+template <int N> constexpr int directCallNoOverload() {
+  return ::value_dependent::noOverload(N);
+}
+
+static_assert(directCallNoOverload<0>() == 0, "");
+constexpr int FailAgain = directCallNoOverload<1>(); // expected-error@-4{{no matching function for call to 'noOverload'}} expected-note{{in instantiation of function}}
+
+constexpr int overloaded(int N) __attribute__((enable_if(N == 0, ""))) {
+  return 1;
+}
+
+constexpr int overloaded(int N) __attribute__((enable_if(N == 1, ""))) {
+  return 2;
+}
+
+constexpr int overloaded(int N) { return 4; }
+
+template <int N> constexpr int callIt() { return overloaded(N); }
+
+static_assert(callIt<0>() == 1, "");
+static_assert(callIt<1>() == 2, "");
+static_assert(callIt<2>() == 4, "");
+
+template <int N>
+constexpr int directlyDepends() __attribute__((enable_if(N, ""))) {
+  return N;
+}
+
+static_assert(directlyDepends<1>() == 1, "");
+constexpr int FailYetAgain = directlyDepends<0>(); // expected-error{{no matching function for call to 'directlyDepends'}} expected-note@-5{{candidate disabled}}
+}
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -5691,6 +5691,33 @@
   return false;
 }
 
+namespace {
+/// In overload resolution, we need to know if any enable_if candidates couldn't
+/// be evaluated due to value-dependent values. So, we pack that into the
+/// FailedAttr pointer, rather than recomputing it.
+struct EnableIfFailureResults {
+  EnableIfAttr *FailedAttr;
+  bool ValueDependent;
+};
+}
+
+static EnableIfFailureResults unpackEnableIfFailure(void *Opaque) {
+  llvm::PointerIntPair<EnableIfAttr*, 1> Pair;
+  Pair.setFromOpaqueValue(Opaque);
+  return {Pair.getPointer(), bool(Pair.getInt())};
+}
+
+static void *packedCheckEnableIf(Sema &S, FunctionDecl *Fn,
+                                 ArrayRef<Expr *> Args,
+                                 bool MissingImplicitThis = false) {
+  bool ValDep = false;
+  EnableIfAttr *EIA = S.CheckEnableIf(Fn, Args, MissingImplicitThis, &ValDep);
+  // ValDep is meaningless if we didn't fail.
+  if (!EIA)
+    return nullptr;
+  return llvm::PointerIntPair<EnableIfAttr *, 1>(EIA, ValDep).getOpaqueValue();
+}
+
 /// AddOverloadCandidate - Adds the given function to the set of
 /// candidate functions, using the given function call arguments.  If
 /// @p SuppressUserConversions, then don't allow user-defined
@@ -5848,10 +5875,10 @@
     }
   }
 
-  if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) {
+  if (void *Failed = packedCheckEnableIf(*this, Function, Args)) {
     Candidate.Viable = false;
     Candidate.FailureKind = ovl_fail_enable_if;
-    Candidate.DeductionFailure.Data = FailedAttr;
+    Candidate.DeductionFailure.Data = Failed;
     return;
   }
 }
@@ -5962,7 +5989,10 @@
 }
 
 EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
-                                  bool MissingImplicitThis) {
+                                  bool MissingImplicitThis, bool *Dependent) {
+  if (Dependent)
+    *Dependent = false;
+
   auto EnableIfAttrs = getOrderedEnableIfAttrs(Function);
   if (EnableIfAttrs.empty())
     return nullptr;
@@ -5992,6 +6022,8 @@
       break;
     }
 
+    // Some args may be ignored by enable_if conditions, so we shouldn't
+    // immediately fail when we see a value-dependent argument.
     ContainsValueDependentExpr |= R.get()->isValueDependent();
     ConvertedArgs.push_back(R.get());
   }
@@ -6024,17 +6056,24 @@
   for (auto *EIA : EnableIfAttrs) {
     APValue Result;
     if (EIA->getCond()->isValueDependent()) {
-      // Don't even try now, we'll examine it after instantiation.
-      continue;
+      if (Dependent)
+        *Dependent = true;
+      return EIA;
     }
 
     if (!EIA->getCond()->EvaluateWithSubstitution(
             Result, Context, Function, llvm::makeArrayRef(ConvertedArgs))) {
-      if (!ContainsValueDependentExpr)
-        return EIA;
-    } else if (!Result.isInt() || !Result.getInt().getBoolValue()) {
+      // Dependent is a best effort check that notes that we *may* have failed
+      // due to a value dependent expression. There's no cheap way to figure out
+      // if this is true, so set it in all cases where it may apply. If we're
+      // wrong, we'll find that out after instantiation.
+      if (Dependent)
+        *Dependent = ContainsValueDependentExpr;
       return EIA;
     }
+
+    if (!Result.isInt() || !Result.getInt().getBoolValue())
+      return EIA;
   }
   return nullptr;
 }
@@ -6231,10 +6270,10 @@
     }
   }
 
-  if (EnableIfAttr *FailedAttr = CheckEnableIf(Method, Args, true)) {
+  if (void *Failed = packedCheckEnableIf(*this, Method, Args, true)) {
     Candidate.Viable = false;
     Candidate.FailureKind = ovl_fail_enable_if;
-    Candidate.DeductionFailure.Data = FailedAttr;
+    Candidate.DeductionFailure.Data = Failed;
     return;
   }
 }
@@ -6540,10 +6579,10 @@
            "Can only end up with a standard conversion sequence or failure");
   }
 
-  if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) {
+  if (void *Failed = packedCheckEnableIf(*this, Conversion, None)) {
     Candidate.Viable = false;
     Candidate.FailureKind = ovl_fail_enable_if;
-    Candidate.DeductionFailure.Data = FailedAttr;
+    Candidate.DeductionFailure.Data = Failed;
     return;
   }
 }
@@ -6692,10 +6731,10 @@
     }
   }
 
-  if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) {
+  if (void *Failed = packedCheckEnableIf(*this, Conversion, None)) {
     Candidate.Viable = false;
     Candidate.FailureKind = ovl_fail_enable_if;
-    Candidate.DeductionFailure.Data = FailedAttr;
+    Candidate.DeductionFailure.Data = Failed;
     return;
   }
 }
@@ -9616,7 +9655,8 @@
 
 static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) {
   FunctionDecl *Callee = Cand->Function;
-  EnableIfAttr *Attr = static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data);
+  void *PackedFailure = Cand->DeductionFailure.Data;
+  EnableIfAttr *Attr = unpackEnableIfFailure(PackedFailure).FailedAttr;
 
   S.Diag(Callee->getLocation(),
          diag::note_ovl_candidate_disabled_by_enable_if_attr)
@@ -11382,8 +11422,32 @@
                              &result))
     return result;
 
+  // If we had any enable_if attributes fail because they were value dependent,
+  // then we can't accurately resolve the overload now. Defer it until later.
+  if (getLangOpts().CPlusPlus)
+    for (auto I = CandidateSet.begin(), E = CandidateSet.end(); I != E; ++I)
+      if (!I->Viable && I->FailureKind == ovl_fail_enable_if &&
+          unpackEnableIfFailure(I->DeductionFailure.Data).ValueDependent) {
+        // The overload we select may change depending on the value the failing
+        // enable_if is dependent upon. So, the type of the function we're
+        // using is both type dependent and value dependent.
+        CallExpr *CE;
+        if (ExecConfig)
+          CE = new (Context)
+              CUDAKernelCallExpr(Context, Fn, cast<CallExpr>(ExecConfig), Args,
+                                 Context.DependentTy, VK_RValue, RParenLoc);
+        else
+          CE = new (Context) CallExpr(Context, Fn, Args, Context.DependentTy,
+                                      VK_RValue, RParenLoc);
+
+        CE->setInstantiationDependent(true);
+        CE->setValueDependent(true);
+        CE->setTypeDependent(true);
+        return CE;
+      }
+
   // If the user handed us something like `(&Foo)(Bar)`, we need to ensure that
-  // functions that aren't addressible are considered unviable.
+  // functions that aren't addressable are considered unviable.
   if (CalleesAddressIsTaken)
     markUnaddressableCandidatesUnviable(*this, CandidateSet);
 
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -5089,8 +5089,6 @@
 
     // Determine whether this is a dependent call inside a C++ template,
     // in which case we won't do any semantic analysis now.
-    // FIXME: Will need to cache the results of name lookup (including ADL) in
-    // Fn.
     bool Dependent = false;
     if (Fn->isTypeDependent())
       Dependent = true;
@@ -5177,6 +5175,7 @@
   } else if (isa<MemberExpr>(NakedFn))
     NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl();
 
+  bool MakeCallInstantiationDependent = false;
   if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NDecl)) {
     if (CallingNDeclIndirectly &&
         !checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true,
@@ -5191,21 +5190,35 @@
     // already emitted an error about the bad call.
     if (FD->hasAttr<EnableIfAttr>() &&
         isNumberOfArgsValidForCall(*this, FD, ArgExprs.size())) {
-      if (const EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) {
-        Diag(Fn->getLocStart(),
-             isa<CXXMethodDecl>(FD) ?
-                 diag::err_ovl_no_viable_member_function_in_call :
-                 diag::err_ovl_no_viable_function_in_call)
-          << FD << FD->getSourceRange();
-        Diag(FD->getLocation(),
-             diag::note_ovl_candidate_disabled_by_enable_if_attr)
-            << Attr->getCond()->getSourceRange() << Attr->getMessage();
+      bool DependentExpr;
+      if (const EnableIfAttr *Attr =
+              CheckEnableIf(FD, ArgExprs, true, &DependentExpr)) {
+        if (DependentExpr) {
+          // We can build this as usual, but we need to check the enable_if
+          // condition when we have all of the necessary values, so we need to
+          // ensure the resultant function is properly marked as instantiation
+          // dependent.
+          MakeCallInstantiationDependent = true;
+        } else {
+          Diag(Fn->getLocStart(),
+               isa<CXXMethodDecl>(FD) ?
+                   diag::err_ovl_no_viable_member_function_in_call :
+                   diag::err_ovl_no_viable_function_in_call)
+            << FD << FD->getSourceRange();
+          Diag(FD->getLocation(),
+               diag::note_ovl_candidate_disabled_by_enable_if_attr)
+              << Attr->getCond()->getSourceRange() << Attr->getMessage();
+        }
       }
     }
   }
 
-  return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
-                               ExecConfig, IsExecConfig);
+  ExprResult BuiltCall = BuildResolvedCallExpr(
+      Fn, NDecl, LParenLoc, ArgExprs, RParenLoc, ExecConfig, IsExecConfig);
+
+  if (MakeCallInstantiationDependent)
+    BuiltCall.get()->setInstantiationDependent(true);
+  return BuiltCall;
 }
 
 /// ActOnAsTypeExpr - create a new asType (bitcast) from the arguments.
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -2482,8 +2482,13 @@
 
   /// Check the enable_if expressions on the given function. Returns the first
   /// failing attribute, or NULL if they were all successful.
+  ///
+  /// If `Dependent` is non-NULL, this function will set it to true if the
+  /// attribute it's handing back couldn't be evaluated properly due to a
+  /// value-dependent expression.
   EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
-                              bool MissingImplicitThis = false);
+                              bool MissingImplicitThis = false,
+                              bool *Dependent = nullptr);
 
   /// Returns whether the given function's address can be taken or not,
   /// optionally emitting a diagnostic if the address can't be taken.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to