george.burgess.iv created this revision.
george.burgess.iv added a reviewer: rsmith.
george.burgess.iv added a subscriber: cfe-commits.

Test case:

```
int foo(int A) __attribute__((enable_if(A == 0, "")));
template <int A> int bar() { return foo(A); }

int G = bar<1>(); // calls foo(1), which should be a compile-time error, but 
isn't.
```

We get around this by making `CheckEnableIf` fail all value dependent 
`enable_if` conditions, and report whether the condition may have failed due to 
a dependent value. If we fail due for this reason during overload resolution, 
we hand back an unresolved, type-dependent call expression, because the 
following code is perfectly legal:

```
int ThisIsABadIdea(int A) __attribute__((enable_if(A == 1, "")));
double ThisIsABadIdea(int A) __attribute__((enable_if(A == 2, "")));
```

...But if we're not in overload resolution, we can get away with just marking 
the expression as value dependent, because we know our target. :)

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, "")));
@@ -346,3 +351,48 @@
   auto CRef = (NoMatchTy)&foo; // expected-error{{address of overloaded function 'foo' does not match required type 'void ()'}}
 }
 }
+
+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
@@ -5684,6 +5684,33 @@
   return false;
 }
 
+namespace {
+/// In overload resolution, we need to know if any enable_if candidates failed
+/// 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);
+  // ValueDep 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
@@ -5841,10 +5868,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;
   }
 }
@@ -5955,7 +5982,11 @@
 }
 
 EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
-                                  bool MissingImplicitThis) {
+                                  bool MissingImplicitThis,
+                                  bool *FailedDueToValueDependentExpr) {
+  if (FailedDueToValueDependentExpr)
+    *FailedDueToValueDependentExpr = false;
+
   auto EnableIfAttrs = getOrderedEnableIfAttrs(Function);
   if (EnableIfAttrs.empty())
     return nullptr;
@@ -6016,18 +6047,26 @@
 
   for (auto *EIA : EnableIfAttrs) {
     APValue Result;
+
     if (EIA->getCond()->isValueDependent()) {
-      // Don't even try now, we'll examine it after instantiation.
-      continue;
+      if (FailedDueToValueDependentExpr)
+        *FailedDueToValueDependentExpr = true;
+      return EIA;
     }
 
     if (!EIA->getCond()->EvaluateWithSubstitution(
             Result, Context, Function, llvm::makeArrayRef(ConvertedArgs))) {
-      if (!ContainsValueDependentExpr)
-        return EIA;
-    } else if (!Result.isInt() || !Result.getInt().getBoolValue()) {
+      // FailedDueToValueDependentExpr 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 (FailedDueToValueDependentExpr)
+        *FailedDueToValueDependentExpr = ContainsValueDependentExpr;
       return EIA;
     }
+
+    if (!Result.isInt() || !Result.getInt().getBoolValue())
+      return EIA;
   }
   return nullptr;
 }
@@ -6224,10 +6263,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;
   }
 }
@@ -6533,10 +6572,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;
   }
 }
@@ -6685,10 +6724,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;
   }
 }
@@ -9609,7 +9648,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)
@@ -11376,8 +11416,24 @@
                              &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 = buildDependentCallExpr(ExecConfig, Fn, Args, 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
@@ -5044,6 +5044,17 @@
   return OverloadDecl;
 }
 
+CallExpr *Sema::buildDependentCallExpr(Expr *ExecConfig, Expr *Fn,
+                                       MultiExprArg ArgExprs,
+                                       SourceLocation RParenLoc) {
+  if (ExecConfig)
+    return new (Context)
+        CUDAKernelCallExpr(Context, Fn, cast<CallExpr>(ExecConfig), ArgExprs,
+                           Context.DependentTy, VK_RValue, RParenLoc);
+  return new (Context) CallExpr(Context, Fn, ArgExprs, Context.DependentTy,
+                                VK_RValue, RParenLoc);
+}
+
 /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments.
 /// This provides the location of the left/right parens and a list of comma
 /// locations.
@@ -5083,22 +5094,8 @@
     // 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;
-    else if (Expr::hasAnyTypeDependentArguments(ArgExprs))
-      Dependent = true;
-
-    if (Dependent) {
-      if (ExecConfig) {
-        return new (Context) CUDAKernelCallExpr(
-            Context, Fn, cast<CallExpr>(ExecConfig), ArgExprs,
-            Context.DependentTy, VK_RValue, RParenLoc);
-      } else {
-        return new (Context) CallExpr(
-            Context, Fn, ArgExprs, Context.DependentTy, VK_RValue, RParenLoc);
-      }
-    }
+    if (Fn->isTypeDependent() || Expr::hasAnyTypeDependentArguments(ArgExprs))
+      return buildDependentCallExpr(ExecConfig, Fn, ArgExprs, RParenLoc);
 
     // Determine whether this is a call to an object (C++ [over.call.object]).
     if (Fn->getType()->isRecordType())
@@ -5169,28 +5166,45 @@
   } else if (isa<MemberExpr>(NakedFn))
     NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl();
 
+  bool MakeCallValueDependent = false;
   if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NDecl)) {
     if (CallingNDeclIndirectly &&
         !checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true,
                                            Fn->getLocStart()))
       return ExprError();
 
     if (FD->hasAttr<EnableIfAttr>()) {
-      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 FailedByValueDependentExpr = false;
+      if (const EnableIfAttr *Attr =
+              CheckEnableIf(FD, ArgExprs, true, &FailedByValueDependentExpr)) {
+        if (FailedByValueDependentExpr) {
+          // 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 value
+          // dependent.
+          MakeCallValueDependent = 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 (MakeCallValueDependent) {
+    BuiltCall.get()->setValueDependent(true);
+    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
@@ -2480,8 +2480,13 @@
 
   /// Check the enable_if expressions on the given function. Returns the first
   /// failing attribute, or NULL if they were all successful.
+  ///
+  /// If `FailedDueToValueDependentExpr` is non-null,
+  /// *FailedDueToValueDependentExpr will report whether the check may have
+  /// failed because a value dependent expression was present.
   EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
-                              bool MissingImplicitThis = false);
+                              bool MissingImplicitThis = false,
+                              bool *FailedDueToValueDependentExpr = nullptr);
 
   /// Returns whether the given function's address can be taken or not,
   /// optionally emitting a diagnostic if the address can't be taken.
@@ -3970,6 +3975,12 @@
 private:
   static BinaryOperatorKind ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind);
 
+  /// \brief Build dependent call expression. This is needed for attributes that
+  /// may interfere with overload resolution.
+  CallExpr *buildDependentCallExpr(Expr *ExecConfig, Expr *Fn,
+                                   MultiExprArg ArgExprs,
+                                   SourceLocation RParenLoc);
+
 public:
   ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc,
                         tok::TokenKind Kind, Expr *LHSExpr, Expr *RHSExpr);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to