The attached patch adds a new attribute named "enable_if" which takes an expression and a string. At the call site, the arguments are substituted into the enable_if expression (which is written in terms of the parameters) to determine whether this function is a viable candidate.

Please review!

Nick
Index: include/clang/AST/Expr.h
===================================================================
--- include/clang/AST/Expr.h	(revision 191171)
+++ include/clang/AST/Expr.h	(working copy)
@@ -601,6 +601,14 @@
                              const VarDecl *VD,
                              SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
 
+  /// EvaluateWithSubstitution - Evaluate an expression as if from the context
+  /// of a call to the given function with the given arguments. Returns true
+  /// if the expression could be folded to a constant, even if the evaluation
+  /// had side-effects or some subexpression could not be evaluated.
+  bool EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
+                                FunctionDecl *Callee,
+                                llvm::ArrayRef<const Expr*> Args) const;
+
   /// \brief Enumeration used to describe the kind of Null pointer constant
   /// returned from \c isNullPointerConstant().
   enum NullPointerConstantKind {
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td	(revision 191171)
+++ include/clang/Basic/Attr.td	(working copy)
@@ -360,6 +360,12 @@
   let Args = [IntArgument<"Priority", 1>];
 }
 
+def EnableIf : InheritableAttr {
+  let Spellings = [GNU<"enable_if">];
+  let Args = [ExprArgument<"Cond">, StringArgument<"Message">];
+  let TemplateDependent = 1;
+}
+
 def ExtVectorType : Attr {
   let Spellings = [GNU<"ext_vector_type">];
   let Args = [ExprArgument<"NumElements">];
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 191171)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -2397,6 +2397,10 @@
   "previous overload of function is here">;
 def err_attribute_overloadable_no_prototype : Error<
   "'overloadable' function %0 must have a prototype">;
+def err_attribute_enable_if_not_function : Error<
+  "'enable_if' attribute can only be applied to a function">;
+def err_attribute_enable_if_not_string : Error<
+  "second argument to 'enable_if' attribute must be a string literal">;
 def warn_ns_attribute_wrong_return_type : Warning<
   "%0 attribute only applies to %select{functions|methods|properties}1 that "
   "return %select{an Objective-C object|a pointer|a non-retainable pointer}2">,
@@ -2541,6 +2545,8 @@
     "candidate template ignored: substitution failure%0%1">;
 def note_ovl_candidate_disabled_by_enable_if : Note<
     "candidate template ignored: disabled by %0%1">;
+def note_ovl_candidate_disabled_by_enable_if_attr : Note<
+    "candidate ignored: %0">;
 def note_ovl_candidate_failed_overload_resolution : Note<
     "candidate template ignored: couldn't resolve reference to overloaded "
     "function %0">;
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h	(revision 191171)
+++ include/clang/Parse/Parser.h	(working copy)
@@ -1887,7 +1887,7 @@
     if (Tok.is(tok::kw___attribute)) {
       ParsedAttributes attrs(AttrFactory);
       SourceLocation endLoc;
-      ParseGNUAttributes(attrs, &endLoc, LateAttrs);
+      ParseGNUAttributes(attrs, &endLoc, LateAttrs, &D);
       D.takeAttributes(attrs, endLoc);
     }
   }
@@ -1899,14 +1899,16 @@
   }
   void ParseGNUAttributes(ParsedAttributes &attrs,
                           SourceLocation *endLoc = 0,
-                          LateParsedAttrList *LateAttrs = 0);
+                          LateParsedAttrList *LateAttrs = 0,
+                          Declarator *D = 0);
   void ParseGNUAttributeArgs(IdentifierInfo *AttrName,
                              SourceLocation AttrNameLoc,
                              ParsedAttributes &Attrs,
                              SourceLocation *EndLoc,
                              IdentifierInfo *ScopeName,
                              SourceLocation ScopeLoc,
-                             AttributeList::Syntax Syntax);
+                             AttributeList::Syntax Syntax,
+                             Declarator *D);
   IdentifierLoc *ParseIdentifierLoc();
 
   void MaybeParseCXX11Attributes(Declarator &D) {
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h	(revision 191171)
+++ include/clang/Sema/Overload.h	(working copy)
@@ -579,7 +579,11 @@
     /// (CUDA) This candidate was not viable because the callee
     /// was not accessible from the caller's target (i.e. host->device,
     /// global->host, device->host).
-    ovl_fail_bad_target
+    ovl_fail_bad_target,
+
+    /// This candidate function was not viable because an enable_if
+    /// attribute disabled it.
+    ovl_fail_enable_if
   };
 
   /// OverloadCandidate - A single candidate in an overload set (C++ 13.3).
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 191171)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -101,6 +101,7 @@
   class DependentDiagnostic;
   class DesignatedInitExpr;
   class Designation;
+  class EnableIfAttr;
   class EnumConstantDecl;
   class Expr;
   class ExtVectorType;
@@ -2180,6 +2181,11 @@
   // identified by the expression Expr
   void NoteAllOverloadCandidates(Expr* E, QualType DestType = QualType());
 
+  // Check the enable_if expressions on the given function. Returns one of the
+  // failing attributes, or NULL if they were all successful.
+  EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
+                              bool MissingImplicitThis = false);
+
   // [PossiblyAFunctionType]  -->   [Return]
   // NonFunctionType --> NonFunctionType
   // R (A) --> R(A)
Index: include/clang/Sema/TemplateDeduction.h
===================================================================
--- include/clang/Sema/TemplateDeduction.h	(revision 191171)
+++ include/clang/Sema/TemplateDeduction.h	(working copy)
@@ -19,6 +19,7 @@
 
 namespace clang {
 
+class EnableIfAttr;
 class TemplateArgumentList;
 class Sema;
 
@@ -208,6 +209,10 @@
   /// if any.
   Expr *getExpr();
 
+  /// When FailureKind is ovl_fail_enable_if, the attribute which caused
+  /// this function to not be viable.
+  EnableIfAttr *EnableIfAttribute;
+
   /// \brief Free any memory associated with this deduction failure.
   void Destroy();
 };
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp	(revision 191171)
+++ lib/AST/ExprConstant.cpp	(working copy)
@@ -459,15 +459,21 @@
 
     bool IntOverflowCheckMode;
 
+    /// Some expressions, such as __builtin_object_size(ptr) can be retried in
+    /// the optimizer if we don't solve them here. If set to true, always fold
+    /// immediately since the optimizer will not get a chance to look at it.
+    bool UnevaluatedContext;
+
     EvalInfo(const ASTContext &C, Expr::EvalStatus &S,
-             bool OverflowCheckMode = false)
+             bool OverflowCheckMode = false, bool UnevaluatedContext = false)
       : Ctx(const_cast<ASTContext&>(C)), EvalStatus(S), CurrentCall(0),
         CallStackDepth(0), NextCallIndex(1),
         StepsLeft(getLangOpts().ConstexprStepLimit),
         BottomFrame(*this, SourceLocation(), 0, 0, 0),
         EvaluatingDecl((const ValueDecl*)0), EvaluatingDeclValue(0),
         HasActiveDiagnostic(false), CheckingPotentialConstantExpression(false),
-        IntOverflowCheckMode(OverflowCheckMode) {}
+        IntOverflowCheckMode(OverflowCheckMode),
+        UnevaluatedContext(UnevaluatedContext) {}
 
     void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
       EvaluatingDecl = Base;
@@ -976,6 +982,7 @@
 static bool EvaluateInPlace(APValue &Result, EvalInfo &Info,
                             const LValue &This, const Expr *E,
                             bool AllowNonLiteralTypes = false);
+static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result);
 static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info);
 static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info);
 static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
@@ -1803,7 +1810,7 @@
       return false;
     }
     Result = &Frame->Arguments[PVD->getFunctionScopeIndex()];
-    return true;
+    return !Result->isUninit();
   }
 
   // If this is a local variable, dig out its value.
@@ -5789,7 +5796,7 @@
 
 /// EvaluateBuiltinConstantP - Evaluate __builtin_constant_p as similarly to
 /// GCC as we can manage.
-static bool EvaluateBuiltinConstantP(ASTContext &Ctx, const Expr *Arg) {
+static bool EvaluateBuiltinConstantP(EvalInfo &Info, const Expr *Arg) {
   QualType ArgType = Arg->getType();
 
   // __builtin_constant_p always has one operand. The rules which gcc follows
@@ -5803,26 +5810,35 @@
   //
   // Otherwise, it returns 0.
   //
+  // In the former case, when building with -O, GCC will sometimes return 1 if
+  // a constant numeric value is used as an argument to an inline function, and
+  // the corresponding parameter is passed to __builtin_constant_p. In the
+  // latter case, it never will. We pretend -O is always specified when checking
+  // constexpr function parameters.
+  //
   // FIXME: GCC also intends to return 1 for literals of aggregate types, but
   // its support for this does not currently work.
   if (ArgType->isIntegralOrEnumerationType()) {
-    Expr::EvalResult Result;
-    if (!Arg->EvaluateAsRValue(Result, Ctx) || Result.HasSideEffects)
+    llvm::SmallVector<PartialDiagnosticAt, 8> Diag;
+    SpeculativeEvaluationRAII Speculate(Info, &Diag);
+
+    Info.EvalStatus.HasSideEffects = false;
+    APValue Result;
+    if (!EvaluateAsRValue(Info, Arg, Result) || Info.EvalStatus.HasSideEffects)
       return false;
 
-    APValue &V = Result.Val;
-    if (V.getKind() == APValue::Int)
+    if (Result.getKind() == APValue::Int)
       return true;
 
-    return EvaluateBuiltinConstantPForLValue(V);
+    return EvaluateBuiltinConstantPForLValue(Result);
   } else if (ArgType->isFloatingType() || ArgType->isAnyComplexType()) {
-    return Arg->isEvaluatable(Ctx);
+    return Arg->isEvaluatable(Info.Ctx);
   } else if (ArgType->isPointerType() || Arg->isGLValue()) {
     LValue LV;
     Expr::EvalStatus Status;
-    EvalInfo Info(Ctx, Status);
-    if ((Arg->isGLValue() ? EvaluateLValue(Arg, LV, Info)
-                          : EvaluatePointer(Arg, LV, Info)) &&
+    EvalInfo InnerInfo(Info.Ctx, Status);
+    if ((Arg->isGLValue() ? EvaluateLValue(Arg, LV, InnerInfo)
+                          : EvaluatePointer(Arg, LV, InnerInfo)) &&
         !Status.HasSideEffects)
       return EvaluateBuiltinConstantPForLValue(LV);
   }
@@ -5898,7 +5914,7 @@
 
     // Expression had no side effects, but we couldn't statically determine the
     // size of the referenced object.
-    return Error(E);
+    return Info.UnevaluatedContext ? Success(-1ULL, E) : Error(E);
   }
 
   case Builtin::BI__builtin_bswap16:
@@ -5931,7 +5947,7 @@
   }
 
   case Builtin::BI__builtin_constant_p:
-    return Success(EvaluateBuiltinConstantP(Info.Ctx, E->getArg(0)), E);
+    return Success(EvaluateBuiltinConstantP(Info, E->getArg(0)), E);
 
   case Builtin::BI__builtin_ctz:
   case Builtin::BI__builtin_ctzl:
@@ -8519,6 +8535,25 @@
   return IsConstExpr;
 }
 
+bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
+                                    FunctionDecl *Callee,
+                                    llvm::ArrayRef<const Expr*> Args) const {
+  Expr::EvalStatus Status;
+  EvalInfo Info(Ctx, Status, false, true);
+
+  ArgVector ArgValues(Args.size());
+  for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
+       I != E; ++I)
+    if (!Evaluate(ArgValues[I - Args.begin()], Info, *I))
+      // If evaluation fails, throw away the argument entirely.
+      ArgValues[I - Args.begin()] = APValue();
+
+  // Build fake call to Callee.
+  CallStackFrame Frame(Info, Callee->getLocation(), Callee, /*This*/0,
+                       ArgValues.data());
+  return Evaluate(Value, Info, this);
+}
+
 bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
                                    SmallVectorImpl<
                                      PartialDiagnosticAt> &Diags) {
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp	(revision 191171)
+++ lib/Parse/ParseDecl.cpp	(working copy)
@@ -117,7 +117,8 @@
 /// We follow the C++ model, but don't allow junk after the identifier.
 void Parser::ParseGNUAttributes(ParsedAttributes &attrs,
                                 SourceLocation *endLoc,
-                                LateParsedAttrList *LateAttrs) {
+                                LateParsedAttrList *LateAttrs,
+                                Declarator *D) {
   assert(Tok.is(tok::kw___attribute) && "Not a GNU attribute list!");
 
   while (Tok.is(tok::kw___attribute)) {
@@ -164,7 +165,7 @@
           LA->Toks.push_back(Eof);
         } else {
           ParseGNUAttributeArgs(AttrName, AttrNameLoc, attrs, endLoc,
-                                0, SourceLocation(), AttributeList::AS_GNU);
+                                0, SourceLocation(), AttributeList::AS_GNU, D);
         }
       } else {
         attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, 0,
@@ -206,7 +207,8 @@
                                    SourceLocation *EndLoc,
                                    IdentifierInfo *ScopeName,
                                    SourceLocation ScopeLoc,
-                                   AttributeList::Syntax Syntax) {
+                                   AttributeList::Syntax Syntax,
+                                   Declarator *D) {
 
   assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
 
@@ -226,6 +228,21 @@
     ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
     return;
   }
+  // These may refer to the function arguments, but need to be parsed early to
+  // participate in determining whether it's a redeclaration.
+  llvm::OwningPtr<ParseScope> PrototypeScope;
+  if (AttrName->isStr("enable_if") && D && D->isFunctionDeclarator()) {
+    DeclaratorChunk::FunctionTypeInfo FTI = D->getFunctionTypeInfo();
+    PrototypeScope.reset(new ParseScope(this, Scope::FunctionPrototypeScope |
+                                        Scope::FunctionDeclarationScope |
+                                        Scope::DeclScope));
+    for (unsigned i = 0; i != FTI.NumArgs; ++i) {
+      ParmVarDecl *Param = cast<ParmVarDecl>(FTI.ArgInfo[i].Param);
+      getCurScope()->AddDecl(Param);
+      if (Param->getDeclName())
+        Actions.IdResolver.AddDecl(Param);
+    }
+  }
 
   ConsumeParen(); // ignore the left paren loc for now
 
@@ -1085,7 +1102,7 @@
         Actions.ActOnReenterFunctionContext(Actions.CurScope, D);
 
       ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc,
-                            0, SourceLocation(), AttributeList::AS_GNU);
+                            0, SourceLocation(), AttributeList::AS_GNU, 0);
 
       if (HasFunScope) {
         Actions.ActOnExitFunctionContext();
@@ -1098,7 +1115,7 @@
       // If there are multiple decls, then the decl cannot be within the
       // function scope.
       ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc,
-                            0, SourceLocation(), AttributeList::AS_GNU);
+                            0, SourceLocation(), AttributeList::AS_GNU, 0);
     }
   } else {
     Diag(Tok, diag::warn_attribute_no_decl) << LA.AttrName.getName();
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp	(revision 191171)
+++ lib/Parse/ParseDeclCXX.cpp	(working copy)
@@ -3228,7 +3228,7 @@
     if (Tok.is(tok::l_paren)) {
       if (ScopeName && ScopeName->getName() == "gnu") {
         ParseGNUAttributeArgs(AttrName, AttrLoc, attrs, endLoc,
-                              ScopeName, ScopeLoc, AttributeList::AS_CXX11);
+                              ScopeName, ScopeLoc, AttributeList::AS_CXX11, 0);
         AttrParsed = true;
       } else {
         if (StandardAttr)
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp	(revision 191171)
+++ lib/Parse/ParseExpr.cpp	(working copy)
@@ -816,7 +816,7 @@
     if (getLangOpts().ObjC1 && 
         ((Tok.is(tok::identifier) && !InMessageExpression) || 
          Tok.is(tok::code_completion))) {
-      const Token& Next = NextToken();
+      const Token &Next = NextToken();
       if (Tok.is(tok::code_completion) || 
           Next.is(tok::colon) || Next.is(tok::r_square))
         if (ParsedType Typ = Actions.getTypeName(II, ILoc, getCurScope()))
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp	(revision 191171)
+++ lib/Sema/SemaChecking.cpp	(working copy)
@@ -754,6 +754,22 @@
       CheckArgumentWithTypeTag(*i, Args.data());
     }
   }
+
+  if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(FDecl)) {
+    if (FD->hasAttr<EnableIfAttr>()) {
+      ArrayRef<Expr *> NonConstArgs =
+        llvm::makeArrayRef<Expr*>(const_cast<Expr**>(Args.data()), Args.size());
+      if (EnableIfAttr *Attr = CheckEnableIf(FD, NonConstArgs, true)) {
+        Diag(Loc, IsMemberFunction ?
+                      diag::err_ovl_no_viable_member_function_in_call :
+                      diag::err_ovl_no_viable_function_in_call)
+          << FD->getDeclName() << Range;
+        Diag(FD->getLocation(),
+             diag::note_ovl_candidate_disabled_by_enable_if_attr)
+            << Attr->getCond()->getSourceRange() << Attr->getMessage();
+      }
+    }
+  }
 }
 
 /// CheckConstructorCall - Check a constructor call for correctness and safety
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp	(revision 191171)
+++ lib/Sema/SemaDeclAttr.cpp	(working copy)
@@ -982,6 +982,33 @@
                                Attr.getAttributeSpellingListIndex()));
 }
 
+static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_enable_if_not_function);
+    return;
+  }
+
+  if (!checkAttributeNumArgs(S, Attr, 2))
+    return;
+
+  Expr *Cond = Attr.getArgAsExpr(0);
+  Expr *Message = Attr.getArgAsExpr(1);
+
+  StringLiteral *MsgStr = dyn_cast<StringLiteral>(Message);
+  if (!MsgStr || !MsgStr->isAscii()) {
+    S.Diag(Message->getExprLoc(), diag::err_attribute_enable_if_not_string);
+    return;
+  }
+
+  ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
+  if (Converted.isInvalid())
+    return;
+
+  D->addAttr(::new (S.Context) EnableIfAttr(Attr.getRange(), S.Context,
+                                            Converted.take(),
+                                            MsgStr->getString()));
+}
+
 static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) {
   ConsumableAttr::ConsumedState DefaultState;
 
@@ -4518,6 +4545,7 @@
     handleAttrWithMessage<DeprecatedAttr>(S, D, Attr);
     break;
   case AttributeList::AT_Destructor:  handleDestructorAttr  (S, D, Attr); break;
+  case AttributeList::AT_EnableIf:    handleEnableIfAttr    (S, D, Attr); break;
   case AttributeList::AT_ExtVectorType:
     handleExtVectorTypeAttr(S, scope, D, Attr);
     break;
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp	(revision 191171)
+++ lib/Sema/SemaOverload.cpp	(working copy)
@@ -1007,8 +1007,8 @@
       isa<FunctionNoProtoType>(NewQType.getTypePtr()))
     return false;
 
-  const FunctionProtoType* OldType = cast<FunctionProtoType>(OldQType);
-  const FunctionProtoType* NewType = cast<FunctionProtoType>(NewQType);
+  const FunctionProtoType *OldType = cast<FunctionProtoType>(OldQType);
+  const FunctionProtoType *NewType = cast<FunctionProtoType>(NewQType);
 
   // The signature of a function includes the types of its
   // parameters (C++ 1.3.10), which includes the presence or absence
@@ -1079,6 +1079,21 @@
       return true;
   }
 
+  // enable_if attributes are an order-sensitive part of the signature.
+  for (specific_attr_iterator<EnableIfAttr>
+         NewI = New->specific_attr_begin<EnableIfAttr>(),
+         NewE = New->specific_attr_end<EnableIfAttr>(),
+         OldI = Old->specific_attr_begin<EnableIfAttr>(),
+         OldE = Old->specific_attr_end<EnableIfAttr>();
+       NewI != NewE && OldI != OldE; ++NewI, ++OldI) {
+    if (NewI == NewE || OldI == OldE)
+      return true;
+    llvm::FoldingSetNodeID NewID, OldID;
+    NewI->getCond()->Profile(NewID, Context, true);
+    OldI->getCond()->Profile(OldID, Context, true);
+    return !(NewID == OldID);
+  }
+
   // The signatures match; this is not an overload.
   return false;
 }
@@ -5412,11 +5427,11 @@
 Sema::AddOverloadCandidate(FunctionDecl *Function,
                            DeclAccessPair FoundDecl,
                            ArrayRef<Expr *> Args,
-                           OverloadCandidateSet& CandidateSet,
+                           OverloadCandidateSet &CandidateSet,
                            bool SuppressUserConversions,
                            bool PartialOverloading,
                            bool AllowExplicit) {
-  const FunctionProtoType* Proto
+  const FunctionProtoType *Proto
     = dyn_cast<FunctionProtoType>(Function->getType()->getAs<FunctionType>());
   assert(Proto && "Functions without a prototype cannot be overloaded");
   assert(!Function->getDescribedFunctionTemplate() &&
@@ -5520,7 +5535,7 @@
       if (Candidate.Conversions[ArgIdx].isBad()) {
         Candidate.Viable = false;
         Candidate.FailureKind = ovl_fail_bad_conversion;
-        break;
+        return;
       }
     } else {
       // (C++ 13.3.2p2): For the purposes of overload resolution, any
@@ -5529,8 +5544,66 @@
       Candidate.Conversions[ArgIdx].setEllipsis();
     }
   }
+
+  if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) {
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_enable_if;
+    Candidate.DeductionFailure.EnableIfAttribute = FailedAttr;
+  }
 }
 
+EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
+                                  bool MissingImplicitThis) {
+                                  
+  for (specific_attr_iterator<EnableIfAttr>
+         I = Function->specific_attr_begin<EnableIfAttr>(),
+         E = Function->specific_attr_end<EnableIfAttr>(); I != E; ++I) {
+    APValue Result;
+
+    SFINAETrap Trap(*this);
+
+    // Convert the arguments.
+    SmallVector<Expr *, 16> Params;
+    bool InitializationFailed = false;
+    for (unsigned i = 0, e = Args.size(); i != e; ++i) {
+      if (i == 0 && !MissingImplicitThis && isa<CXXMethodDecl>(Function) &&
+          !cast<CXXMethodDecl>(Function)->isStatic()) {
+        CXXMethodDecl *Method = cast<CXXMethodDecl>(Function);
+        ExprResult R =
+          PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/0,
+                                              Method, Method);
+        if (R.isInvalid()) {
+          InitializationFailed = true;
+          break;
+        }
+        Params.push_back(R.take());
+      } else {
+        ExprResult R =
+          PerformCopyInitialization(InitializedEntity::InitializeParameter(
+                                                  Context,
+                                                  Function->getParamDecl(i)),
+                                    SourceLocation(),
+                                    Args[i]);
+        if (R.isInvalid()) {
+          InitializationFailed = true;
+          break;
+        }
+        Params.push_back(R.take());
+      }
+    }
+
+    if (InitializationFailed || Trap.hasErrorOccurred() ||
+        !(*I)->getCond()->EvaluateWithSubstitution(
+            Result, Context, Function,
+            llvm::ArrayRef<const Expr*>(Params.data(), Params.size())) ||
+        !Result.isInt() || !Result.getInt().getBoolValue()) {
+      // FIXME: Produce a different error if EvaluateWithSubstitution failed.
+      return *I;
+    }
+  }
+  return 0;
+}
+
 /// \brief Add all of the function declarations in the given function set to
 /// the overload candidate set.
 void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
@@ -5610,9 +5683,9 @@
                          CXXRecordDecl *ActingContext, QualType ObjectType,
                          Expr::Classification ObjectClassification,
                          ArrayRef<Expr *> Args,
-                         OverloadCandidateSet& CandidateSet,
+                         OverloadCandidateSet &CandidateSet,
                          bool SuppressUserConversions) {
-  const FunctionProtoType* Proto
+  const FunctionProtoType *Proto
     = dyn_cast<FunctionProtoType>(Method->getType()->getAs<FunctionType>());
   assert(Proto && "Methods without a prototype cannot be overloaded");
   assert(!isa<CXXConstructorDecl>(Method) &&
@@ -5656,6 +5729,13 @@
     return;
   }
 
+  if (EnableIfAttr *FailedAttr = CheckEnableIf(Method, Args, true)) {
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_enable_if;
+    Candidate.DeductionFailure.EnableIfAttribute = FailedAttr;
+    return;
+  }
+
   Candidate.Viable = true;
 
   if (Method->isStatic() || ObjectType.isNull())
@@ -8722,6 +8802,15 @@
       << (unsigned) FnKind << CalleeTarget << CallerTarget;
 }
 
+void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) {
+  FunctionDecl *Callee = Cand->Function;
+  EnableIfAttr *Attr = Cand->DeductionFailure.EnableIfAttribute;
+
+  S.Diag(Callee->getLocation(),
+         diag::note_ovl_candidate_disabled_by_enable_if_attr)
+      << Attr->getCond()->getSourceRange() << Attr->getMessage();
+}
+
 /// Generates a 'note' diagnostic for an overload candidate.  We've
 /// already generated a primary error at the call site.
 ///
@@ -8785,6 +8874,9 @@
 
   case ovl_fail_bad_target:
     return DiagnoseBadTarget(S, Cand);
+
+  case ovl_fail_enable_if:
+    return DiagnoseFailedEnableIfAttr(S, Cand);
   }
 }
 
@@ -11007,7 +11099,7 @@
         << qualsString
         << (qualsString.find(' ') == std::string::npos ? 1 : 2);
     }
-              
+
     CXXMemberCallExpr *call
       = new (Context) CXXMemberCallExpr(Context, MemExprE, Args,
                                         resultType, valueKind, RParenLoc);
Index: test/Sema/enable_if.c
===================================================================
--- test/Sema/enable_if.c	(revision 0)
+++ test/Sema/enable_if.c	(working copy)
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 %s -verify -Wno-gcc-compat
+// RUN: %clang_cc1 %s -DCODEGEN -emit-llvm -o - -Wno-gcc-compat | FileCheck %s
+
+#define O_CREAT 0x100
+typedef int mode_t;
+typedef unsigned long size_t;
+
+int open(const char *pathname, int flags) __attribute__((enable_if(!__builtin_constant_p(flags) || !(flags & O_CREAT), "must specify mode when using O_CREAT"))) __attribute__((overloadable));  // expected-note{{candidate ignored: must specify mode when using O_CREAT}}
+int open(const char *pathname, int flags, mode_t mode) __attribute__((overloadable));  // expected-note{{candidate function not viable: requires 3 arguments, but 2 were provided}}
+
+void test1() {
+#ifndef CODEGEN
+  open("path", O_CREAT);  // expected-error{{no matching function for call to 'open'}}
+#endif
+  open("path", O_CREAT, 0660);
+  open("path", 0);
+  open("path", 0, 0);
+}
+
+size_t __strnlen_chk(const char *s, size_t requested_amount, size_t s_len);
+
+size_t strnlen(const char *s, size_t maxlen)  // expected-note{{candidate ignored: chosen when target buffer size is unknown}}
+  __attribute__((overloadable))
+  __attribute__((enable_if(__builtin_object_size(s, 0) == -1,
+                           "chosen when target buffer size is unknown")))
+  __asm__("__strnlen_real1");
+size_t strnlen(const char *s, size_t maxlen)  // expected-note{{candidate ignored: chosen when maxlen is known to be less than or equal to the buffer size}}
+  __attribute__((overloadable))
+  __attribute__((enable_if(__builtin_object_size(s, 0) != -1 && __builtin_constant_p(maxlen) && maxlen <= __builtin_object_size(s, 0),
+                           "chosen when maxlen is known to be less than or equal to the buffer size")))
+  __asm__("__strnlen_real2");
+size_t strnlen(const char *s, size_t maxlen)  // expected-note{{candidate function has been explicitly made unavailable}}
+  __attribute__((overloadable))
+  __attribute__((enable_if(__builtin_object_size(s, 0) != -1 && __builtin_constant_p(maxlen) && maxlen > __builtin_object_size(s, 0),
+                           "chosen when maxlen is larger than the buffer size")))
+  __attribute__((unavailable("maxlen is larger than the buffer size")));
+
+static inline size_t strnlen(const char *s, size_t maxlen)  // expected-note{{candidate ignored: chosen when the buffer size is known but maxlen is not}}
+  __attribute__((always_inline))
+  __attribute__((overloadable))
+  __attribute__((enable_if(__builtin_object_size(s, 0) != -1 && !__builtin_constant_p(maxlen),
+                           "chosen when the buffer size is known but maxlen is not")))
+{
+  return __strnlen_chk(s, maxlen, __builtin_object_size(s, 0));
+}
+
+void test2(const char *s, int i) {
+// CHECK: define void @test2
+  const char c[123];
+  strnlen(s, i);
+// CHECK: call {{.*}}strnlen_real1
+  strnlen(s, 999);
+// CHECK: call {{.*}}strnlen_real1
+  strnlen(c, 1);
+// CHECK: call {{.*}}strnlen_real2
+  strnlen(c, i);
+// CHECK: call {{.*}}strnlen_chk
+#ifndef CODEGEN
+  strnlen(c, 999); // expected-error{{call to unavailable function 'strnlen': maxlen is larger than the buffer size}}
+#endif
+}
+
+int isdigit(int c) __attribute__((enable_if( !__builtin_constant_p(c) || (c >= -1 && c <= 255) , "'c' must have the value of an unsigned char or EOF")));  // expected-note{{candidate ignored: 'c' must have the value of an unsigned char}}
+
+void test3() {
+  isdigit(10);
+#ifndef CODEGEN
+  isdigit(-10);  // expected-error{{no matching function for call to isdigit}}
+#endif
+}
Index: test/SemaCXX/enable_if.cpp
===================================================================
--- test/SemaCXX/enable_if.cpp	(revision 0)
+++ test/SemaCXX/enable_if.cpp	(working copy)
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -Wno-gcc-compat -verify %s
+
+struct X {
+  void f(int n) __attribute__((enable_if(n == 0, "chosen when n is zero")));
+  void f(int n) __attribute__((enable_if(n == 1, "chosen when n is one")));  // expected-note{{member declaration nearly matches}} expected-note{{candidate ignored: chosen when n is one}}
+
+  static void s(int n) __attribute__((enable_if(n == 0, "chosen when n is zero")));  // expected-note2{{candidate ignored: chosen when n is zero}}
+
+  void conflict(int n) __attribute__((enable_if(n+n == 10, "chosen when n is five")));  // expected-note{{candidate function}}
+  void conflict(int n) __attribute__((enable_if(n*2 == 10, "chosen when n is five")));  // expected-note{{candidate function}}
+};
+
+void X::f(int n) __attribute__((enable_if(n == 0, "chosen when n is zero")))  // expected-note{{member declaration nearly matches}} expected-note{{candidate ignored: chosen when n is zero}}
+{
+}
+
+void X::f(int n) __attribute__((enable_if(n == 2, "chosen when n is two")))  // expected-error{{out-of-line definition of 'f' does not match any declaration in 'X'}} expected-note{{candidate ignored: chosen when n is two}}
+{
+}
+
+void test() {
+  X x;
+  x.f(0);
+  x.f(1);
+  x.f(2);  // no error, suppressed by erroneous out-of-line definition
+  x.f(3);  // expected-error{{no matching member function for call to 'f'}}
+
+  x.s(0);
+  x.s(1);  // expected-error{{no matching function for call to 's'}}
+
+  X::s(0);
+  X::s(1);  // expected-error{{no matching function for call to 's'}}
+
+  x.conflict(5);  // expected-error{{call to member function 'conflict' is ambiguous}}
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to