<div dir="ltr">On 10 January 2014 13:42, Aaron Ballman <span dir="ltr"><<a href="mailto:aaron@aaronballman.com" target="_blank">aaron@aaronballman.com</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote">


<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div><div>On Fri, Jan 10, 2014 at 3:43 PM, Nick Lewycky <<a href="mailto:nicholas@mxc.ca" target="_blank">nicholas@mxc.ca</a>> wrote:<br>



> Aaron Ballman wrote:<br>
>><br>
>> This is fantastic, thank you for taking this on! Some minor comments<br>
>> and observations below.<br>
><br>
><br>
> Thank you for the thorough review! I haven't been able to fix everything you<br>
> pointed out, but I'm attaching an updated patch anyways, with FIXME's in it.<br>
><br>
> The two issues unfixed are:<br>
>  - Why does C++11 syntax think there's only one argument?<br>
>  - Why is potential-constexpr evaluation failing on VarTemplateDecls?<br>
><br>
>>> Index: docs/LanguageExtensions.rst<br>
>>> ===================================================================<br>
>>> --- docs/LanguageExtensions.rst (revision 198923)<br>
>>> +++ docs/LanguageExtensions.rst (working copy)<br>
>>> @@ -1316,6 +1316,8 @@<br>
>>>   Query the presence of this new mangling with<br>
>>>   ``__has_feature(objc_protocol_qualifier_mangling)``.<br>
>>><br>
>>> +.. _langext-overloading:<br>
>>> +<br>
>>>   Function Overloading in C<br>
>>>   =========================<br>
>>><br>
>>> @@ -1398,6 +1400,84 @@<br>
>>><br>
>>>   Query for this feature with<br>
>>> ``__has_extension(attribute_overloadable)``.<br>
>>><br>
>>> +Controlling Overload Resolution<br>
>>> +===============================<br>
>>> +<br>
>>> +Clang introduces the ``enable_if`` attribute, which can be placed on<br>
>>> function<br>
>>> +declarations to control which overload is selected based on the values<br>
>>> of the<br>
>>> +function's arguments. When combined with the<br>
>>> +:ref:``overloadable<langext-overloading>`` attribute, this feature is<br>
>>> also<br>
>>> +available in C.<br>
>>> +<br>
>>> +.. code-block:: c++<br>
>>> +<br>
>>> +  int isdigit(int c);<br>
>>> +  int isdigit(int c) __attribute__((enable_if(c>= -1&&  c<= 255, "'c'<br>
>>> must have the value of an unsigned char or EOF")));<br>
>>><br>
>>> +<br>
>>> +  void foo(char c) {<br>
>>> +    isdigit(c);<br>
>>> +    isdigit(10);<br>
>>> +    isdigit(-10);  // results in a compile-time error.<br>
>>> +  }<br>
>>> +<br>
>>> +The enable_if attribute takes two arguments, the first is an expression<br>
>>> written<br>
>>> +in terms of the function parameters, the second is a string explaining<br>
>>> why this<br>
>>> +overload candidate could not be selected to be displayed in diagnostics.<br>
>>> The<br>
>>> +expression is part of the function signature for the purposes of<br>
>>> determining<br>
>>> +whether it is a redeclaration (following the rules used when determining<br>
>>> +whether a C++ template specialization is ODR-equivalent), but is not<br>
>>> part of<br>
>>> +the type.<br>
>>> +<br>
>>> +An enable_if expression will be evaluated by substituting the values of<br>
>>> the<br>
>>> +parameters from the call site into the arguments in the expression and<br>
>>> +determining whether the result is true. If the result is false or could<br>
>>> not be<br>
>>> +determined through constant expression evaluation, then this overload<br>
>>> will not<br>
>>> +be chosen and the reason supplied in the string will be given to the<br>
>>> user if<br>
>>> +their code does not compile as a result.<br>
>>> +<br>
>>> +Because the enable_if expression is an unevaluated context, there are no<br>
>>> global<br>
>>> +state changes, nor the ability to pass information from the enable_if<br>
>>> +expression to the function body. As a worked example, suppose we want<br>
>>> calls to<br>
>><br>
>><br>
>> "As a worked example" reads a bit funny to me, is it a typo?<br>
><br>
><br>
> Hm, it's a phrase I use to mean "an example that's been worked out to show<br>
> the reader how to do it" but that doesn't appear to be common usage. Fixed<br>
> to simply use "For example".<br>
><br>
><br>
>>> +strnlen(strbuf, maxlen) to resolve to strnlen_chk(strbuf, maxlen, size<br>
>>> of<br>
>>> +strbuf) only if the size of strbuf can be determined:<br>
>>> +<br>
>>> +.. code-block:: c++<br>
>>> +<br>
>>> +  __attribute__((always_inline))<br>
>>> +  static inline size_t strnlen(const char *s, size_t maxlen)<br>
>>> +    __attribute__((overloadable))<br>
>>> +    __attribute__((enable_if(__builtin_object_size(s, 0) != -1))),<br>
>>> +                             "chosen when the buffer size is known but<br>
>>> 'maxlen' is not")))<br>
>>> +  {<br>
>>> +    return strnlen_chk(s, maxlen, __builtin_object_size(s, 0));<br>
>>> +  }<br>
>>> +<br>
>>> +Multiple enable_if attributes may be applied to a single declaration. In<br>
>>> this<br>
>>> +case, the enable_if expressions are evaluated from left to right in the<br>
>>> +following manner. First the candidates whose enable_if expressions<br>
>>> evaluate to<br>
>><br>
>><br>
>> "First" should be followed by a comma. Wow, that's nitpicky. ;-)<br>
><br>
><br>
> Done. :)<br>
><br>
>>> +false or cannot be evaluated are discarded. If the remaining candidates<br>
>>> do not<br>
>>> +share ODR-equivalent enable_if expressions, the overload resolution is<br>
>>> +ambiguous. Otherwise, enable_if overload resolution continues with the<br>
>>> next<br>
>>> +enable_if attribute on the candidates that have not been discarded and<br>
>>> have<br>
>>> +remaining enable_if attributes. In this way, we pick the most specific<br>
>>> +overload out of a number of viable overloads using enable_if.<br>
>>> +<br>
>>> +.. code-block:: c++<br>
>>> +  void f() __attribute__((enable_if(true, "")));  // #1<br>
>>> +  void f() __attribute__((enable_if(true, "")))<br>
>>> __attribute__((enable_if(true, "")));  // #2<br>
>>> +<br>
>>> +  void g(int i, int j) __attribute__((enable_if(i, "")));  // #1<br>
>>> +  void g(int i, int j) __attribute__((enable_if(j, "")))<br>
>>> __attribute__((enable_if(true)));  // #2<br>
>>> +<br>
>>> +In this example, a call to f() is always resolved to #2, as the first<br>
>>> enable_if<br>
>>> +expression is ODR-equivalent for both declarations, but #1 does not have<br>
>>> another<br>
>>> +enable_if expression to continue evaluating, so the next round of<br>
>>> evaluation has<br>
>>> +only a single candidate. In a call to g(1, 1), the call is ambiguous<br>
>>> even though<br>
>>> +#2 has more enable_if attributes, because the first enable_if<br>
>>> expressions are<br>
>>> +not ODR-equivalent.<br>
>>> +<br>
>>> +Query for this feature with ``__has_attribute(enable_if)``.<br>
>>> +<br>
>>>   Initializer lists for complex numbers in C<br>
>>>   ==========================================<br>
>>><br>
>>> Index: include/clang/AST/Expr.h<br>
>>> ===================================================================<br>
>>> --- include/clang/AST/Expr.h (revision 198923)<br>
>>> +++ include/clang/AST/Expr.h (working copy)<br>
>>> @@ -508,6 +508,16 @@<br>
>>>                                         SmallVectorImpl<<br>
>>>                                           PartialDiagnosticAt>  &Diags);<br>
>>><br>
>>> +  /// isPotentialConstantExprUnevaluted - Return true if this expression<br>
>>> might<br>
>>> +  /// be usable in a constant expression in C++11 in an unevaluated<br>
>>> context, if<br>
>>> +  /// it were in function FD marked constexpr. Return false if the<br>
>>> function can<br>
>>> +  /// never produce a constant expression, along with diagnostics<br>
>>> describing<br>
>>> +  /// why not.<br>
>>> +  static bool isPotentialConstantExprUnevaluated(Expr *E,<br>
>>> +                                                 const FunctionDecl *FD,<br>
>>> +                                                 SmallVectorImpl<<br>
>>> +                                                   PartialDiagnosticAt><br>
>>> &Diags);<br>
>>> +<br>
>>>     /// isConstantInitializer - Returns true if this expression can be<br>
>>> emitted to<br>
>>>     /// IR as a constant, and thus can be used as a constant initializer<br>
>>> in C.<br>
>>>     bool isConstantInitializer(ASTContext&Ctx, bool ForRef) const;<br>
>>><br>
>>> @@ -600,6 +610,14 @@<br>
>>>                                const VarDecl *VD,<br>
>>>                                SmallVectorImpl<PartialDiagnosticAt><br>
>>> &Notes) const;<br>
>>><br>
>>> +  /// EvaluateWithSubstitution - Evaluate an expression as if from the<br>
>>> context<br>
>>> +  /// of a call to the given function with the given arguments, inside<br>
>>> an<br>
>>> +  /// unevaluated context. Returns true if the expression could be<br>
>>> folded to a<br>
>>> +  /// constant.<br>
>>> +  bool EvaluateWithSubstitution(APValue&Value, ASTContext&Ctx,<br>
>>> +                                FunctionDecl *Callee,<br>
>>> +                                llvm::ArrayRef<const Expr*>  Args)<br>
>>> const;<br>
>>> +<br>
>>>     /// \brief Enumeration used to describe the kind of Null pointer<br>
>>> constant<br>
>>>     /// returned from \c isNullPointerConstant().<br>
>>>     enum NullPointerConstantKind {<br>
>>> Index: include/clang/Basic/Attr.td<br>
>>> ===================================================================<br>
>>> --- include/clang/Basic/Attr.td (revision 198923)<br>
>>> +++ include/clang/Basic/Attr.td (working copy)<br>
>>> @@ -470,6 +470,13 @@<br>
>>>     let Subjects = SubjectList<[Function]>;<br>
>>>   }<br>
>>><br>
>>> +def EnableIf : InheritableAttr {<br>
>>> +  let Spellings = [GNU<"enable_if">];<br>
>><br>
>><br>
>> Since this is also supported in C++, could we have a CXX11<"clang",<br>
>> "enable_if">  spelling as well?<br>
><br>
><br>
> I tried adding that, but it doesn't seem work out of the box:<br>
><br>
>   void f(int n) [[clang::enable_if((n > 5), "chosen when 'n' is greater than<br>
> five")]];<br>
><br>
> produces "error: expected expression" while:<br>
><br>
>   [[clang::enable_if((n > 5), "chosen when 'n' is greater than five")]] void<br>
> f(int n);<br>
><br>
> produces "error: 'enable_if' attribute requires exactly 2 arguments"!<br>
><br>
> That's going to take some debugging. I've added a testcase for this<br>
> commented out with a FIXME on it.<br>
<br>
</div></div>I can save you the debugging -- I totally forgot that C++11 attribute<br>
parsing doesn't support arguments. I'll put that on my list of things<br>
to look into sometime in the near-ish future though. Thanks for the<br>
reminder!  :-)<br></blockquote><div><br></div><div>Hah! Thanks. I'm going to pull the spelling out of Attrs.td and leave the FIXME in the testcase.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">

<div>
>>> +  let Subjects = SubjectList<[Function]>;<br>
>><br>
>><br>
>> Do you want this to apply to FunctionTemplate and perhaps ObjCMethod as<br>
>> well?<br>
><br>
><br>
> To quote Richard Smith, "this [attribute] should apply to the FunctionDecl<br>
> within a FunctionTemplateDecl, not to the template itself, right?" -<br>
> <a href="http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130923/089332.html" target="_blank">http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130923/089332.html</a><br>
<br>
</div>Hmmm, I may have to educate myself on this point a bit more then.<br>
<div><div><br>
><br>
> I'm not sure about ObjCMethod. I'm leaning towards not supporting it simply<br>
> because I don't know enough objc to write any testcases. I don't have any<br>
> ideological opposition here.<br>
><br>
><br>
>>> +  let Args = [ExprArgument<"Cond">, StringArgument<"Message">];<br>
>>> +  let TemplateDependent = 1;<br>
>><br>
>><br>
>> You *may* have to set LateParsed = 1 here as well, as the expressions<br>
>> are template dependant. I'm not 100% certain though.<br>
><br>
><br>
> Uh oh. I musn't set LateParsed = 1 here, because we need to parse it before<br>
> we can decide whether it's a redeclaration. Late parsed is too late.<br>
><br>
> Unfortunately, enable_if on a templated function looks pretty broken, see my<br>
> comments at the end. At the moment, I don't think that LateParsed = 0 is the<br>
> problem, but until I finish debugging it I can't be sure.<br>
><br>
><br>
>>> +}<br>
>>> +<br>
>>>   def ExtVectorType : Attr {<br>
>>>     let Spellings = [GNU<"ext_vector_type">];<br>
>>>     let Subjects = SubjectList<[TypedefName], ErrorDiag>;<br>
>>> Index: include/clang/Basic/DiagnosticSemaKinds.td<br>
>>> ===================================================================<br>
>>> --- include/clang/Basic/DiagnosticSemaKinds.td (revision 198923)<br>
>>> +++ include/clang/Basic/DiagnosticSemaKinds.td (working copy)<br>
>>> @@ -1739,6 +1739,8 @@<br>
>>>   def ext_constexpr_function_never_constant_expr : ExtWarn<<br>
>>>     "constexpr %select{function|constructor}0 never produces a"<br>
>>>     "constant expression">, InGroup<DiagGroup<"invalid-constexpr">>,<br>
>>> DefaultError;<br>
>>> +def err_enable_if_never_constant_expr : Error<<br>
>>> +  "enable_if attribute expression never produces a constant<br>
>>> expression">;<br>
>><br>
>><br>
>> I'd prefer the diagnostic to have enable_if in single quotes (it's<br>
>> more consistent with other attribute diagnostics then).<br>
><br>
><br>
> Done.<br>
><br>
><br>
>>>   def err_constexpr_body_no_return : Error<<br>
>>>     "no return statement in constexpr function">;<br>
>>>   def warn_cxx11_compat_constexpr_body_no_return : Warning<<br>
>>> @@ -2588,6 +2590,8 @@<br>
>>>       "candidate template ignored: substitution failure%0%1">;<br>
>>>   def note_ovl_candidate_disabled_by_enable_if : Note<<br>
>>>       "candidate template ignored: disabled by %0%1">;<br>
>>> +def note_ovl_candidate_disabled_by_enable_if_attr : Note<<br>
>>> +    "candidate disabled: %0">;<br>
>>>   def note_ovl_candidate_failed_overload_resolution : Note<<br>
>>>       "candidate template ignored: couldn't resolve reference to<br>
>>> overloaded"<br>
>>>       "function %0">;<br>
>>> Index: include/clang/Parse/Parser.h<br>
>>> ===================================================================<br>
>>> --- include/clang/Parse/Parser.h (revision 198923)<br>
>>> +++ include/clang/Parse/Parser.h (working copy)<br>
>>> @@ -1972,7 +1972,7 @@<br>
>>>       if (Tok.is(tok::kw___attribute)) {<br>
>>>         ParsedAttributes attrs(AttrFactory);<br>
>>>         SourceLocation endLoc;<br>
>>> -      ParseGNUAttributes(attrs,&endLoc, LateAttrs);<br>
>>> +      ParseGNUAttributes(attrs,&endLoc, LateAttrs,&D);<br>
>>>         D.takeAttributes(attrs, endLoc);<br>
>>>       }<br>
>>>     }<br>
>>> @@ -1984,14 +1984,16 @@<br>
>>>     }<br>
>>>     void ParseGNUAttributes(ParsedAttributes&attrs,<br>
>>>                             SourceLocation *endLoc = 0,<br>
>>> -                          LateParsedAttrList *LateAttrs = 0);<br>
>>> +                          LateParsedAttrList *LateAttrs = 0,<br>
>>> +                          Declarator *D = 0);<br>
>>>     void ParseGNUAttributeArgs(IdentifierInfo *AttrName,<br>
>>>                                SourceLocation AttrNameLoc,<br>
>>>                                ParsedAttributes&Attrs,<br>
>>>                                SourceLocation *EndLoc,<br>
>>>                                IdentifierInfo *ScopeName,<br>
>>>                                SourceLocation ScopeLoc,<br>
>>> -                             AttributeList::Syntax Syntax);<br>
>>> +                             AttributeList::Syntax Syntax,<br>
>>> +                             Declarator *D);<br>
>>>     IdentifierLoc *ParseIdentifierLoc();<br>
>>><br>
>>>     void MaybeParseCXX11Attributes(Declarator&D) {<br>
>>> Index: include/clang/Sema/Overload.h<br>
>>> ===================================================================<br>
>>> --- include/clang/Sema/Overload.h (revision 198923)<br>
>>> +++ include/clang/Sema/Overload.h (working copy)<br>
>>> @@ -579,7 +579,11 @@<br>
>>>       /// (CUDA) This candidate was not viable because the callee<br>
>>>       /// was not accessible from the caller's target (i.e. host->device,<br>
>>>       /// global->host, device->host).<br>
>>> -    ovl_fail_bad_target<br>
>>> +    ovl_fail_bad_target,<br>
>>> +<br>
>>> +    /// This candidate function was not viable because an enable_if<br>
>>> +    /// attribute disabled it.<br>
>>> +    ovl_fail_enable_if<br>
>>>     };<br>
>>><br>
>>>     /// OverloadCandidate - A single candidate in an overload set (C++<br>
>>> 13.3).<br>
>>> Index: include/clang/Sema/Sema.h<br>
>>> ===================================================================<br>
>>> --- include/clang/Sema/Sema.h (revision 198923)<br>
>>> +++ include/clang/Sema/Sema.h (working copy)<br>
>>> @@ -101,6 +101,7 @@<br>
>>>     class DependentDiagnostic;<br>
>>>     class DesignatedInitExpr;<br>
>>>     class Designation;<br>
>>> +  class EnableIfAttr;<br>
>>>     class EnumConstantDecl;<br>
>>>     class Expr;<br>
>>>     class ExtVectorType;<br>
>>> @@ -2200,6 +2201,11 @@<br>
>>>     // identified by the expression Expr<br>
>>>     void NoteAllOverloadCandidates(Expr* E, QualType DestType =<br>
>>> QualType());<br>
>>><br>
>>> +  /// Check the enable_if expressions on the given function. Returns the<br>
>>> first<br>
>>> +  /// failing attribute, or NULL if they were all successful.<br>
>>> +  EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *><br>
>>> Args,<br>
>>> +                              bool MissingImplicitThis = false);<br>
>>> +<br>
>><br>
>><br>
>> A bit of const love here would not make me sad, but isn't strictly<br>
>> required.<br>
><br>
><br>
> I tried making using ArrayRef<const Expr *> here but ended up pulling a very<br>
> long string (or else using bad const_casts). It's not worth it.<br>
<br>
</div></div>I was thinking more for the returned Attrm the given FunctionDecl and<br>
the CheckEnableIf function itself (if possible).<br></blockquote><div><br></div><div>The returned Attr* can't be (without const_casts) because it's a container whose member is copied into DeductionFailureInfo::Data. Making that const breaks other users who expect to stick a non-const Expr* in there and get one back out. No dice.</div>


<div><br></div><div>Making the FunctionDecl* const fails when trying to use PerformObjectArgumentInitialization / InitializeParameter which don't accept const, and I'm not threading it through there.</div><div><br>

</div><div>Qualifying the method itself const means that I 'this' is now const, and I can't change state like creating a SFINAETrap, nor can I call PerformObjectArgumentInitialization.</div><div><br></div><div>

Nick</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<div><div>><br>
>>>     // [PossiblyAFunctionType]  -->    [Return]<br>
>>>     // NonFunctionType -->  NonFunctionType<br>
>>>     // R (A) -->  R(A)<br>
>>> @@ -4776,6 +4782,7 @@<br>
>>>                                            AttributeList *AttrList);<br>
>>>     void ActOnFinishCXXMemberDecls();<br>
>>><br>
>>> +  void ActOnReenterCXXMethodParameter(Scope *S, ParmVarDecl *Param);<br>
>>>     void ActOnReenterTemplateScope(Scope *S, Decl *Template);<br>
>>>     void ActOnReenterDeclaratorTemplateScope(Scope *S, DeclaratorDecl<br>
>>> *D);<br>
>>>     void ActOnStartDelayedMemberDeclarations(Scope *S, Decl *Record);<br>
>>> Index: lib/AST/ExprConstant.cpp<br>
>>> ===================================================================<br>
>>> --- lib/AST/ExprConstant.cpp (revision 198923)<br>
>>> +++ lib/AST/ExprConstant.cpp (working copy)<br>
>>> @@ -474,13 +474,30 @@<br>
>>><br>
>>>         /// Evaluate in any way we know how. Don't worry about<br>
>>> side-effects that<br>
>>>         /// can't be modeled.<br>
>>> -      EM_IgnoreSideEffects<br>
>>> +      EM_IgnoreSideEffects,<br>
>>> +<br>
>>> +      /// Evaluate as a constant expression. Stop if we find that the<br>
>>> expression<br>
>>> +      /// is not a constant expression. Some expressions can be retried<br>
>>> in the<br>
>>> +      /// optimizer if we don't constant fold them here, but in an<br>
>>> unevaluated<br>
>>> +      /// context we try to fold them immediately since the optimizer<br>
>>> never<br>
>>> +      /// gets a chance to look at it.<br>
>>> +      EM_ConstantExpressionUnevaluated,<br>
>>> +<br>
>>> +      /// Evaluate as a potential constant expression. Keep going if we<br>
>>> hit a<br>
>>> +      /// construct that we can't evaluate yet (because we don't yet<br>
>>> know the<br>
>>> +      /// value of something) but stop if we hit something that could<br>
>>> never be<br>
>>> +      /// a constant expression. Some expressions can be retried in the<br>
>>> +      /// optimizer if we don't constant fold them here, but in an<br>
>>> unevaluated<br>
>>> +      /// context we try to fold them immediately since the optimizer<br>
>>> never<br>
>>> +      /// gets a chance to look at it.<br>
>>> +      EM_PotentialConstantExpressionUnevaluated<br>
>>>       } EvalMode;<br>
>>><br>
>>>       /// Are we checking whether the expression is a potential constant<br>
>>>       /// expression?<br>
>>>       bool checkingPotentialConstantExpression() const {<br>
>>> -      return EvalMode == EM_PotentialConstantExpression;<br>
>>> +      return EvalMode == EM_PotentialConstantExpression ||<br>
>>> +             EvalMode == EM_PotentialConstantExpressionUnevaluated;<br>
>>>       }<br>
>>><br>
>>>       /// Are we checking an expression for overflow?<br>
>>> @@ -573,6 +590,8 @@<br>
>>>               // some later problem.<br>
>>>             case EM_ConstantExpression:<br>
>>>             case EM_PotentialConstantExpression:<br>
>>> +          case EM_ConstantExpressionUnevaluated:<br>
>>> +          case EM_PotentialConstantExpressionUnevaluated:<br>
>>>               HasActiveDiagnostic = false;<br>
>>>               return OptionalDiagnostic();<br>
>>>             }<br>
>>> @@ -644,11 +663,13 @@<br>
>>>       bool keepEvaluatingAfterSideEffect() {<br>
>>>         switch (EvalMode) {<br>
>>>         case EM_PotentialConstantExpression:<br>
>>> +      case EM_PotentialConstantExpressionUnevaluated:<br>
>>>         case EM_EvaluateForOverflow:<br>
>>>         case EM_IgnoreSideEffects:<br>
>>>           return true;<br>
>>><br>
>>>         case EM_ConstantExpression:<br>
>>> +      case EM_ConstantExpressionUnevaluated:<br>
>>>         case EM_ConstantFold:<br>
>>>           return false;<br>
>>>         }<br>
>>> @@ -670,10 +691,12 @@<br>
>>><br>
>>>         switch (EvalMode) {<br>
>>>         case EM_PotentialConstantExpression:<br>
>>> +      case EM_PotentialConstantExpressionUnevaluated:<br>
>>>         case EM_EvaluateForOverflow:<br>
>>>           return true;<br>
>>><br>
>>>         case EM_ConstantExpression:<br>
>>> +      case EM_ConstantExpressionUnevaluated:<br>
>>>         case EM_ConstantFold:<br>
>>>         case EM_IgnoreSideEffects:<br>
>>>           return false;<br>
>>> @@ -696,7 +719,9 @@<br>
>>>                           Info.EvalStatus.Diag->empty()&&<br>
>>>                           !Info.EvalStatus.HasSideEffects),<br>
>>>           OldMode(Info.EvalMode) {<br>
>>> -      if (Enabled&&  Info.EvalMode == EvalInfo::EM_ConstantExpression)<br>
>>><br>
>>> +      if (Enabled&&<br>
>>> +          (Info.EvalMode == EvalInfo::EM_ConstantExpression ||<br>
>>> +           Info.EvalMode == EvalInfo::EM_ConstantExpressionUnevaluated))<br>
>>>           Info.EvalMode = EvalInfo::EM_ConstantFold;<br>
>>>       }<br>
>>>       void keepDiagnostics() { Enabled = false; }<br>
>>> @@ -5985,7 +6010,17 @@<br>
>>><br>
>>>       // Expression had no side effects, but we couldn't statically<br>
>>> determine the<br>
>>>       // size of the referenced object.<br>
>>> -    return Error(E);<br>
>>> +    switch (Info.EvalMode) {<br>
>>> +    case EvalInfo::EM_ConstantExpression:<br>
>>> +    case EvalInfo::EM_PotentialConstantExpression:<br>
>>> +    case EvalInfo::EM_ConstantFold:<br>
>>> +    case EvalInfo::EM_EvaluateForOverflow:<br>
>>> +    case EvalInfo::EM_IgnoreSideEffects:<br>
>>> +      return Error(E);<br>
>>> +    case EvalInfo::EM_ConstantExpressionUnevaluated:<br>
>>> +    case EvalInfo::EM_PotentialConstantExpressionUnevaluated:<br>
>>> +      return Success(-1ULL, E);<br>
>><br>
>><br>
>> std::numeric_limits<unsigned long long>::max()? Not heavily tied to it.<br>
><br>
><br>
> The other two callers in the file also use "Success(-1ULL,". Let me know if<br>
> you want it anyways and I'll update them too.<br>
<br>
</div></div>I don't have a strong opinion one way or the other, but I do find our<br>
usages of -1ULL a bit contradictory when I come across them. It might<br>
be done better as a style discussion of its own, instead of part of<br>
this patch, so I guess I'd say leave it as-is for now.<br>
<div><div><br>
><br>
>>> +    }<br>
>>>     }<br>
>>><br>
>>>     case Builtin::BI__builtin_bswap16:<br>
>>> @@ -8656,6 +8691,28 @@<br>
>>>     return IsConstExpr;<br>
>>>   }<br>
>>><br>
>>> +bool Expr::EvaluateWithSubstitution(APValue&Value, ASTContext&Ctx,<br>
>>> +                                    FunctionDecl *Callee,<br>
>>> +                                    llvm::ArrayRef<const Expr*>  Args)<br>
>>> const {<br>
>>> +  Expr::EvalStatus Status;<br>
>>> +  EvalInfo Info(Ctx, Status,<br>
>>> EvalInfo::EM_ConstantExpressionUnevaluated);<br>
>>> +<br>
>>> +  ArgVector ArgValues(Args.size());<br>
>>> +  for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();<br>
>>> +       I != E; ++I) {<br>
>>> +    if (!Evaluate(ArgValues[I - Args.begin()], Info, *I))<br>
>>> +      // If evaluation fails, throw away the argument entirely.<br>
>>> +      ArgValues[I - Args.begin()] = APValue();<br>
>>> +    if (Info.EvalStatus.HasSideEffects)<br>
>>> +      return false;<br>
>>> +  }<br>
>>> +<br>
>>> +  // Build fake call to Callee.<br>
>>> +  CallStackFrame Frame(Info, Callee->getLocation(), Callee, /*This*/0,<br>
>>> +                       ArgValues.data());<br>
>>> +  return Evaluate(Value, Info, this)&&  !Info.EvalStatus.HasSideEffects;<br>
>>><br>
>>> +}<br>
>>> +<br>
>>>   bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,<br>
>>>                                      SmallVectorImpl<<br>
>>>                                        PartialDiagnosticAt>  &Diags) {<br>
>>> @@ -8696,3 +8753,26 @@<br>
>>><br>
>>>     return Diags.empty();<br>
>>>   }<br>
>>> +<br>
>>> +bool Expr::isPotentialConstantExprUnevaluated(Expr *E,<br>
>>> +                                              const FunctionDecl *FD,<br>
>>> +                                              SmallVectorImpl<<br>
>>> +                                                PartialDiagnosticAt><br>
>>> &Diags) {<br>
>>> +  Expr::EvalStatus Status;<br>
>>> +  Status.Diag =&Diags;<br>
>>> +<br>
>>> +  EvalInfo Info(FD->getASTContext(), Status,<br>
>>> +                EvalInfo::EM_PotentialConstantExpressionUnevaluated);<br>
>>> +<br>
>>> +  // Fabricate a call stack frame to give the arguments a plausible<br>
>>> cover story.<br>
>>> +  ArrayRef<const Expr*>  Args;<br>
>>> +  ArgVector ArgValues(0);<br>
>>> +  bool Success = EvaluateArgs(Args, ArgValues, Info);<br>
>>> +  assert(Success&&<br>
>>> +         "Failed to set up arguments for potential constant<br>
>>> evaluation");<br>
>>> +  CallStackFrame Frame(Info, SourceLocation(), FD, 0, ArgValues.data());<br>
>>> +<br>
>>> +  APValue ResultScratch;<br>
>>> +  Evaluate(ResultScratch, Info, E);<br>
>>> +  return Diags.empty();<br>
>>> +}<br>
>>> Index: lib/Parse/ParseDecl.cpp<br>
>>> ===================================================================<br>
>>> --- lib/Parse/ParseDecl.cpp (revision 198923)<br>
>>> +++ lib/Parse/ParseDecl.cpp (working copy)<br>
>>> @@ -117,7 +117,8 @@<br>
>>>   /// We follow the C++ model, but don't allow junk after the identifier.<br>
>>>   void Parser::ParseGNUAttributes(ParsedAttributes&attrs,<br>
>>>                                   SourceLocation *endLoc,<br>
>>> -                                LateParsedAttrList *LateAttrs) {<br>
>>> +                                LateParsedAttrList *LateAttrs,<br>
>>> +                                Declarator *D) {<br>
>>>     assert(Tok.is(tok::kw___attribute)&&  "Not a GNU attribute list!");<br>
>>><br>
>>><br>
>>>     while (Tok.is(tok::kw___attribute)) {<br>
>>> @@ -153,7 +154,7 @@<br>
>>>         // Handle "parameterized" attributes<br>
>>>         if (!LateAttrs || !isAttributeLateParsed(*AttrName)) {<br>
>>>           ParseGNUAttributeArgs(AttrName, AttrNameLoc, attrs, endLoc, 0,<br>
>>> -                              SourceLocation(), AttributeList::AS_GNU);<br>
>>> +                              SourceLocation(), AttributeList::AS_GNU,<br>
>>> D);<br>
>>>           continue;<br>
>>>         }<br>
>>><br>
>>> @@ -258,7 +259,8 @@<br>
>>>                                      SourceLocation *EndLoc,<br>
>>>                                      IdentifierInfo *ScopeName,<br>
>>>                                      SourceLocation ScopeLoc,<br>
>>> -                                   AttributeList::Syntax Syntax) {<br>
>>> +                                   AttributeList::Syntax Syntax,<br>
>>> +                                   Declarator *D) {<br>
>>><br>
>>>     assert(Tok.is(tok::l_paren)&&  "Attribute arg list not starting with<br>
>>> '('");<br>
>>><br>
>>><br>
>>> @@ -278,17 +280,33 @@<br>
>>>       return;<br>
>>>     }<br>
>>><br>
>>> +<br>
>><br>
>><br>
>> Extra newline snuck in here.<br>
><br>
><br>
> Fixed.<br>
><br>
>>>     // Type safety attributes have their own grammar.<br>
>>>     if (AttrKind == AttributeList::AT_TypeTagForDatatype) {<br>
>>>       ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs,<br>
>>> EndLoc);<br>
>>>       return;<br>
>>>     }<br>
>>> +<br>
>>>     // Some attributes expect solely a type parameter.<br>
>>>     if (attributeIsTypeArgAttr(*AttrName)) {<br>
>>>       ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, EndLoc);<br>
>>>       return;<br>
>>>     }<br>
>>><br>
>>> +  // These may refer to the function arguments, but need to be parsed<br>
>>> early to<br>
>>> +  // participate in determining whether it's a redeclaration.<br>
>>> +  llvm::OwningPtr<ParseScope>  PrototypeScope;<br>
>>> +  if (AttrName->isStr("enable_if")&&  D&&  D->isFunctionDeclarator()) {<br>
>>><br>
>>> +    DeclaratorChunk::FunctionTypeInfo FTI = D->getFunctionTypeInfo();<br>
>>> +    PrototypeScope.reset(new ParseScope(this,<br>
>>> Scope::FunctionPrototypeScope |<br>
>>> +                                        Scope::FunctionDeclarationScope<br>
>>> |<br>
>>> +                                        Scope::DeclScope));<br>
>>> +    for (unsigned i = 0; i != FTI.NumArgs; ++i) {<br>
>>> +      ParmVarDecl *Param = cast<ParmVarDecl>(FTI.ArgInfo[i].Param);<br>
>>> +      Actions.ActOnReenterCXXMethodParameter(getCurScope(), Param);<br>
>>> +    }<br>
>>> +  }<br>
>>> +<br>
>>>     // Ignore the left paren location for now.<br>
>>>     ConsumeParen();<br>
>>><br>
>>> @@ -1164,7 +1182,7 @@<br>
>>>           Actions.ActOnReenterFunctionContext(Actions.CurScope, D);<br>
>>><br>
>>>         ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc,<br>
>>> Attrs,&endLoc,<br>
>>> -                            0, SourceLocation(), AttributeList::AS_GNU);<br>
>>> +                            0, SourceLocation(), AttributeList::AS_GNU,<br>
>>> 0);<br>
>>><br>
>>>         if (HasFunScope) {<br>
>>>           Actions.ActOnExitFunctionContext();<br>
>>> @@ -1177,7 +1195,7 @@<br>
>>>         // If there are multiple decls, then the decl cannot be within<br>
>>> the<br>
>>>         // function scope.<br>
>>>         ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc,<br>
>>> Attrs,&endLoc,<br>
>>> -                            0, SourceLocation(), AttributeList::AS_GNU);<br>
>>> +                            0, SourceLocation(), AttributeList::AS_GNU,<br>
>>> 0);<br>
>>>       }<br>
>>>     } else {<br>
>>>       Diag(Tok, diag::warn_attribute_no_decl)<<  LA.AttrName.getName();<br>
>>> Index: lib/Parse/ParseDeclCXX.cpp<br>
>>> ===================================================================<br>
>>> --- lib/Parse/ParseDeclCXX.cpp (revision 198923)<br>
>>> +++ lib/Parse/ParseDeclCXX.cpp (working copy)<br>
>>> @@ -3259,7 +3259,7 @@<br>
>>>       if (Tok.is(tok::l_paren)) {<br>
>>>         if (ScopeName&&  ScopeName->getName() == "gnu") {<br>
>>><br>
>>>           ParseGNUAttributeArgs(AttrName, AttrLoc, attrs, endLoc,<br>
>>> -                              ScopeName, ScopeLoc,<br>
>>> AttributeList::AS_CXX11);<br>
>>> +                              ScopeName, ScopeLoc,<br>
>>> AttributeList::AS_CXX11, 0);<br>
>>>           AttrParsed = true;<br>
>>>         } else {<br>
>>>           if (StandardAttr)<br>
>>> Index: lib/Sema/SemaDeclAttr.cpp<br>
>>> ===================================================================<br>
>>> --- lib/Sema/SemaDeclAttr.cpp (revision 198923)<br>
>>> +++ lib/Sema/SemaDeclAttr.cpp (working copy)<br>
>>> @@ -827,6 +827,30 @@<br>
>>>                                  Attr.getAttributeSpellingListIndex()));<br>
>>>   }<br>
>>><br>
>>> +static void handleEnableIfAttr(Sema&S, Decl *D, const<br>
>>> AttributeList&Attr) {<br>
>>> +  Expr *Cond = Attr.getArgAsExpr(0);<br>
>>> +  ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);<br>
>>> +  if (Converted.isInvalid())<br>
>>> +    return;<br>
>>> +<br>
>>> +  StringRef Msg;<br>
>>> +  if (!S.checkStringLiteralArgumentAttr(Attr, 1, Msg))<br>
>>> +    return;<br>
>>> +<br>
>>> +  SmallVector<PartialDiagnosticAt, 8>  Diags;<br>
>>> +  if (!Expr::isPotentialConstantExprUnevaluated(Cond,<br>
>>> cast<FunctionDecl>(D),<br>
>>> +                                                Diags)) {<br>
>>> +    S.Diag(Attr.getLoc(), diag::err_enable_if_never_constant_expr);<br>
>>> +    for (int I = 0, N = Diags.size(); I != N; ++I)<br>
>>> +      S.Diag(Diags[I].first, Diags[I].second);<br>
>>> +    return;<br>
>>> +  }<br>
>>> +<br>
>>> +  D->addAttr(::new (S.Context)<br>
>>> +             EnableIfAttr(Attr.getRange(), S.Context, Converted.take(),<br>
>>> Msg,<br>
>>> +                          Attr.getAttributeSpellingListIndex()));<br>
>>> +}<br>
>>> +<br>
>>>   static void handleConsumableAttr(Sema&S, Decl *D, const<br>
>>> AttributeList&Attr) {<br>
>>>     ConsumableAttr::ConsumedState DefaultState;<br>
>>><br>
>>> @@ -3990,6 +4014,7 @@<br>
>>>       handleAttrWithMessage<DeprecatedAttr>(S, D, Attr);<br>
>>>       break;<br>
>>>     case AttributeList::AT_Destructor:  handleDestructorAttr  (S, D,<br>
>>> Attr); break;<br>
>>> +  case AttributeList::AT_EnableIf:    handleEnableIfAttr    (S, D,<br>
>>> Attr); break;<br>
>>>     case AttributeList::AT_ExtVectorType:<br>
>>>       handleExtVectorTypeAttr(S, scope, D, Attr);<br>
>>>       break;<br>
>>> Index: lib/Sema/SemaDeclCXX.cpp<br>
>>> ===================================================================<br>
>>> --- lib/Sema/SemaDeclCXX.cpp (revision 198923)<br>
>>> +++ lib/Sema/SemaDeclCXX.cpp (working copy)<br>
>>> @@ -6079,6 +6079,18 @@<br>
>>>     PopDeclContext();<br>
>>>   }<br>
>>><br>
>>> +/// This is used to implement the constant expression evaluation part of<br>
>>> the<br>
>>> +/// attribute enable_if extension. There is nothing in standard C++<br>
>>> which would<br>
>>> +/// require reentering parameters.<br>
>>> +void Sema::ActOnReenterCXXMethodParameter(Scope *S, ParmVarDecl *Param)<br>
>>> {<br>
>>> +  if (!Param)<br>
>>> +    return;<br>
>>> +<br>
>>> +  S->AddDecl(Param);<br>
>>> +  if (Param->getDeclName())<br>
>>> +    IdResolver.AddDecl(Param);<br>
>>> +}<br>
>>> +<br>
>>>   /// ActOnStartDelayedCXXMethodDeclaration - We have completed<br>
>>>   /// parsing a top-level (non-nested) C++ class, and we are now<br>
>>>   /// parsing those parts of the given Method declaration that could<br>
>>> Index: lib/Sema/SemaExpr.cpp<br>
>>> ===================================================================<br>
>>> --- lib/Sema/SemaExpr.cpp (revision 198923)<br>
>>> +++ lib/Sema/SemaExpr.cpp (working copy)<br>
>>> @@ -4474,6 +4474,21 @@<br>
>>>     else if (isa<MemberExpr>(NakedFn))<br>
>>>       NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl();<br>
>>><br>
>>> +  if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NDecl)) {<br>
>>> +    if (FD->hasAttr<EnableIfAttr>()) {<br>
>>> +      if (EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) {<br>
>>> +        Diag(Fn->getLocStart(),<br>
>>> +             isa<CXXMethodDecl>(FD) ?<br>
>>> +                 diag::err_ovl_no_viable_member_function_in_call :<br>
>>> +                 diag::err_ovl_no_viable_function_in_call)<br>
>>> +<<  FD->getDeclName()<<  FD->getSourceRange();<br>
>><br>
>><br>
>> No need to pass getDeclName -- the diagnostics engine already<br>
>> understands NamedDecls.<br>
><br>
><br>
> Cool. Fixed!<br>
><br>
><br>
>>> +        Diag(FD->getLocation(),<br>
>>> +             diag::note_ovl_candidate_disabled_by_enable_if_attr)<br>
>>> +<<  Attr->getCond()->getSourceRange()<<  Attr->getMessage();<br>
>>> +      }<br>
>>> +    }<br>
>>> +  }<br>
>>> +<br>
>>>     return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs,<br>
>>> RParenLoc,<br>
>>>                                  ExecConfig, IsExecConfig);<br>
>>>   }<br>
>>> Index: lib/Sema/SemaOverload.cpp<br>
>>> ===================================================================<br>
>>> --- lib/Sema/SemaOverload.cpp (revision 198923)<br>
>>> +++ lib/Sema/SemaOverload.cpp (working copy)<br>
>>> @@ -1008,8 +1008,8 @@<br>
>>>         isa<FunctionNoProtoType>(NewQType.getTypePtr()))<br>
>>>       return false;<br>
>>><br>
>>> -  const FunctionProtoType* OldType = cast<FunctionProtoType>(OldQType);<br>
>>> -  const FunctionProtoType* NewType = cast<FunctionProtoType>(NewQType);<br>
>>> +  const FunctionProtoType *OldType = cast<FunctionProtoType>(OldQType);<br>
>>> +  const FunctionProtoType *NewType = cast<FunctionProtoType>(NewQType);<br>
>>><br>
>>>     // The signature of a function includes the types of its<br>
>>>     // parameters (C++ 1.3.10), which includes the presence or absence<br>
>>> @@ -1085,6 +1085,22 @@<br>
>>>         return true;<br>
>>>     }<br>
>>><br>
>>> +  // enable_if attributes are an order-sensitive part of the signature.<br>
>><br>
>><br>
>> So you are okay with them going in reverse order here?<br>
><br>
><br>
> Yes. We care that the two of them have the same enable_if attrs in the same<br>
> order, but we can check that in any order.<br>
><br>
><br>
>>> +  for (specific_attr_iterator<EnableIfAttr><br>
>>> +         NewI = New->specific_attr_begin<EnableIfAttr>(),<br>
>>> +         NewE = New->specific_attr_end<EnableIfAttr>(),<br>
>>> +         OldI = Old->specific_attr_begin<EnableIfAttr>(),<br>
>>> +         OldE = Old->specific_attr_end<EnableIfAttr>();<br>
>>> +       NewI != NewE || OldI != OldE; ++NewI, ++OldI) {<br>
>>> +    if (NewI == NewE || OldI == OldE)<br>
>>> +      return true;<br>
>>> +    llvm::FoldingSetNodeID NewID, OldID;<br>
>>> +    NewI->getCond()->Profile(NewID, Context, true);<br>
>>> +    OldI->getCond()->Profile(OldID, Context, true);<br>
>>> +    if (!(NewID == OldID))<br>
>><br>
>><br>
>> Is there an != operator for FoldingSetNodeID?<br>
><br>
><br>
> There isn't! It's on my obvious cleanups list to add one and use it here.<br>
<br>
</div></div>Thanks!<br>
<div><div><br>
><br>
>>> +      return true;<br>
>>> +  }<br>
>>> +<br>
>>>     // The signatures match; this is not an overload.<br>
>>>     return false;<br>
>>>   }<br>
>>> @@ -5452,11 +5468,11 @@<br>
>>>   Sema::AddOverloadCandidate(FunctionDecl *Function,<br>
>>>                              DeclAccessPair FoundDecl,<br>
>>>                              ArrayRef<Expr *>  Args,<br>
>>> -                           OverloadCandidateSet&  CandidateSet,<br>
>>> +                           OverloadCandidateSet&CandidateSet,<br>
>>>                              bool SuppressUserConversions,<br>
>>>                              bool PartialOverloading,<br>
>>>                              bool AllowExplicit) {<br>
>>> -  const FunctionProtoType* Proto<br>
>>> +  const FunctionProtoType *Proto<br>
>>>       =<br>
>>> dyn_cast<FunctionProtoType>(Function->getType()->getAs<FunctionType>());<br>
>>>     assert(Proto&&  "Functions without a prototype cannot be<br>
>>> overloaded");<br>
>>><br>
>>>     assert(!Function->getDescribedFunctionTemplate()&&<br>
>>> @@ -5568,7 +5584,7 @@<br>
>>>         if (Candidate.Conversions[ArgIdx].isBad()) {<br>
>>>           Candidate.Viable = false;<br>
>>>           Candidate.FailureKind = ovl_fail_bad_conversion;<br>
>>> -        break;<br>
>>> +        return;<br>
>>>         }<br>
>>>       } else {<br>
>>>         // (C++ 13.3.2p2): For the purposes of overload resolution, any<br>
>>> @@ -5577,8 +5593,78 @@<br>
>>>         Candidate.Conversions[ArgIdx].setEllipsis();<br>
>>>       }<br>
>>>     }<br>
>>> +<br>
>>> +  if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) {<br>
>>> +    Candidate.Viable = false;<br>
>>> +    Candidate.FailureKind = ovl_fail_enable_if;<br>
>>> +    Candidate.DeductionFailure.Data = FailedAttr;<br>
>>> +    return;<br>
>>> +  }<br>
>>>   }<br>
>>><br>
>>> +static bool IsNotEnableIfAttr(Attr *A) { return !isa<EnableIfAttr>(A); }<br>
>>> +<br>
>>> +EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr<br>
>>> *>  Args,<br>
>>> +                                  bool MissingImplicitThis) {<br>
>>> +  // FIXME: specific_attr_iterator<EnableIfAttr>  iterates in reverse<br>
>>> order, but<br>
>>> +  // we need to find the first failing one.<br>
>>> +  if (!Function->hasAttrs()) return 0;<br>
>><br>
>><br>
>> Return should be on a new line.<br>
><br>
><br>
> Done.<br>
><br>
><br>
>>> +  AttrVec Attrs = Function->getAttrs();<br>
>>> +  AttrVec::iterator E = std::remove_if(Attrs.begin(), Attrs.end(),<br>
>>> +                                       IsNotEnableIfAttr);<br>
>>> +  if (Attrs.begin() == E) return 0;<br>
>><br>
>><br>
>> I think it would be more clear if it were Attrs.empty().<br>
><br>
><br>
> Nope! remove_if doesn't remove elements from the container, it shuffles them<br>
> to the end and returns a new 'end' iterator marking the point at which<br>
> elements fail the predicate.<br>
><br>
><br>
>  Also a new line here.<br>
><br>
> Done.<br>
><br>
><br>
>><br>
>>> +  std::reverse(Attrs.begin(), E);<br>
>>> +<br>
>>> +  SFINAETrap Trap(*this);<br>
>>> +<br>
>>> +  // Convert the arguments.<br>
>>> +  SmallVector<Expr *, 16>  ConvertedArgs;<br>
>>> +  bool InitializationFailed = false;<br>
>>> +  for (unsigned i = 0, e = Args.size(); i != e; ++i) {<br>
>>> +    if (i == 0&&  !MissingImplicitThis&&  isa<CXXMethodDecl>(Function)&&<br>
>>> +        !cast<CXXMethodDecl>(Function)->isStatic()) {<br>
>>> +      CXXMethodDecl *Method = cast<CXXMethodDecl>(Function);<br>
>>> +      ExprResult R =<br>
>>> +        PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/0,<br>
>>> +                                            Method, Method);<br>
>>> +      if (R.isInvalid()) {<br>
>>> +        InitializationFailed = true;<br>
>>> +        break;<br>
>>> +      }<br>
>>> +      ConvertedArgs.push_back(R.take());<br>
>>> +    } else {<br>
>>> +      ExprResult R =<br>
>>> +<br>
>>> PerformCopyInitialization(InitializedEntity::InitializeParameter(<br>
>>> +                                                Context,<br>
>>> +<br>
>>> Function->getParamDecl(i)),<br>
>>> +                                  SourceLocation(),<br>
>>> +                                  Args[i]);<br>
>>> +      if (R.isInvalid()) {<br>
>>> +        InitializationFailed = true;<br>
>>> +        break;<br>
>>> +      }<br>
>>> +      ConvertedArgs.push_back(R.take());<br>
>>> +    }<br>
>>> +  }<br>
>>> +<br>
>>> +  if (InitializationFailed || Trap.hasErrorOccurred()) {<br>
>>> +    return cast<EnableIfAttr>(Attrs[0]);<br>
>>> +  }<br>
>><br>
>><br>
>> Braces are not needed here.<br>
><br>
><br>
> Done.<br>
><br>
>>> +<br>
>>> +  for (AttrVec::iterator I = Attrs.begin(); I != E; ++I) {<br>
>>> +    APValue Result;<br>
>>> +    EnableIfAttr *EIA = cast<EnableIfAttr>(*I);<br>
>>> +    if (!EIA->getCond()->EvaluateWithSubstitution(<br>
>>> +            Result, Context, Function,<br>
>>> +            llvm::ArrayRef<const Expr*>(ConvertedArgs.data(),<br>
>>> +                                        ConvertedArgs.size())) ||<br>
>>> +        !Result.isInt() || !Result.getInt().getBoolValue()) {<br>
>>> +      return EIA;<br>
>>> +    }<br>
>>> +  }<br>
>>> +  return 0;<br>
>>> +}<br>
>>> +<br>
>>>   /// \brief Add all of the function declarations in the given function<br>
>>> set to<br>
>>>   /// the overload candidate set.<br>
>>>   void Sema::AddFunctionCandidates(const UnresolvedSetImpl&Fns,<br>
>>> @@ -5658,9 +5744,9 @@<br>
>>>                            CXXRecordDecl *ActingContext, QualType<br>
>>> ObjectType,<br>
>>>                            Expr::Classification ObjectClassification,<br>
>>>                            ArrayRef<Expr *>  Args,<br>
>>> -                         OverloadCandidateSet&  CandidateSet,<br>
>>> +                         OverloadCandidateSet&CandidateSet,<br>
>>>                            bool SuppressUserConversions) {<br>
>>> -  const FunctionProtoType* Proto<br>
>>> +  const FunctionProtoType *Proto<br>
>>>       =<br>
>>> dyn_cast<FunctionProtoType>(Method->getType()->getAs<FunctionType>());<br>
>>>     assert(Proto&&  "Methods without a prototype cannot be overloaded");<br>
>>><br>
>>>     assert(!isa<CXXConstructorDecl>(Method)&&<br>
>>> @@ -5747,15 +5833,22 @@<br>
>>>         if (Candidate.Conversions[ArgIdx + 1].isBad()) {<br>
>>>           Candidate.Viable = false;<br>
>>>           Candidate.FailureKind = ovl_fail_bad_conversion;<br>
>>> -        break;<br>
>>> +        return;<br>
>>>         }<br>
>>>       } else {<br>
>>>         // (C++ 13.3.2p2): For the purposes of overload resolution, any<br>
>>>         // argument for which there is no corresponding parameter is<br>
>>> -      // considered to ""match the ellipsis" (C+ 13.3.3.1.3).<br>
>>> +      // considered to "match the ellipsis" (C+ 13.3.3.1.3).<br>
>>>         Candidate.Conversions[ArgIdx + 1].setEllipsis();<br>
>>>       }<br>
>>>     }<br>
>>> +<br>
>>> +  if (EnableIfAttr *FailedAttr = CheckEnableIf(Method, Args, true)) {<br>
>>> +    Candidate.Viable = false;<br>
>>> +    Candidate.FailureKind = ovl_fail_enable_if;<br>
>>> +    Candidate.DeductionFailure.Data = FailedAttr;<br>
>>> +    return;<br>
>>> +  }<br>
>>>   }<br>
>>><br>
>>>   /// \brief Add a C++ member function template as a candidate to the<br>
>>> candidate<br>
>>> @@ -5971,7 +6064,7 @@<br>
>>>       return;<br>
>>>     }<br>
>>><br>
>>> -  // We won't go through a user-define type conversion function to<br>
>>> convert a<br>
>>> +  // We won't go through a user-defined type conversion function to<br>
>>> convert a<br>
>>>     // derived to base as such conversions are given Conversion Rank.<br>
>>> They only<br>
>>>     // go through a copy constructor. 13.3.3.1.2-p4 [over.ics.user]<br>
>>>     QualType FromCanon<br>
>>> @@ -6031,6 +6124,7 @@<br>
>>>           GetConversionRank(ICS.Standard.Second) != ICR_Exact_Match) {<br>
>>>         Candidate.Viable = false;<br>
>>>         Candidate.FailureKind = ovl_fail_final_conversion_not_exact;<br>
>>> +      return;<br>
>>>       }<br>
>>><br>
>>>       // C++0x [dcl.init.ref]p5:<br>
>>> @@ -6042,18 +6136,26 @@<br>
>>>           ICS.Standard.First == ICK_Lvalue_To_Rvalue) {<br>
>>>         Candidate.Viable = false;<br>
>>>         Candidate.FailureKind = ovl_fail_bad_final_conversion;<br>
>>> +      return;<br>
>>>       }<br>
>>>       break;<br>
>>><br>
>>>     case ImplicitConversionSequence::BadConversion:<br>
>>>       Candidate.Viable = false;<br>
>>>       Candidate.FailureKind = ovl_fail_bad_final_conversion;<br>
>>> -    break;<br>
>>> +    return;<br>
>>><br>
>>>     default:<br>
>>>       llvm_unreachable(<br>
>>>              "Can only end up with a standard conversion sequence or<br>
>>> failure");<br>
>>>     }<br>
>>> +<br>
>>> +  if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion,<br>
>>> ArrayRef<Expr*>())) {<br>
>>> +    Candidate.Viable = false;<br>
>>> +    Candidate.FailureKind = ovl_fail_enable_if;<br>
>>> +    Candidate.DeductionFailure.Data = FailedAttr;<br>
>>> +    return;<br>
>>> +  }<br>
>>>   }<br>
>>><br>
>>>   /// \brief Adds a conversion function template specialization<br>
>>> @@ -6191,7 +6293,7 @@<br>
>>>         if (Candidate.Conversions[ArgIdx + 1].isBad()) {<br>
>>>           Candidate.Viable = false;<br>
>>>           Candidate.FailureKind = ovl_fail_bad_conversion;<br>
>>> -        break;<br>
>>> +        return;<br>
>>>         }<br>
>>>       } else {<br>
>>>         // (C++ 13.3.2p2): For the purposes of overload resolution, any<br>
>>> @@ -6200,6 +6302,13 @@<br>
>>>         Candidate.Conversions[ArgIdx + 1].setEllipsis();<br>
>>>       }<br>
>>>     }<br>
>>> +<br>
>>> +  if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion,<br>
>>> ArrayRef<Expr*>())) {<br>
>>> +    Candidate.Viable = false;<br>
>>> +    Candidate.FailureKind = ovl_fail_enable_if;<br>
>>> +    Candidate.DeductionFailure.Data = FailedAttr;<br>
>>> +    return;<br>
>>> +  }<br>
>>>   }<br>
>>><br>
>>>   /// \brief Add overload candidates for overloaded operators that are<br>
>>> @@ -8111,6 +8220,41 @@<br>
>>>       }<br>
>>>     }<br>
>>><br>
>>> +  // Check for enable_if value-based overload resolution.<br>
>>> +  if (Cand1.Function&&  Cand2.Function&&<br>
>>> +      (Cand1.Function->hasAttr<EnableIfAttr>() ||<br>
>>> +       Cand2.Function->hasAttr<EnableIfAttr>())) {<br>
>>> +    // FIXME: The next several lines are just<br>
>>> +    // specific_attr_iterator<EnableIfAttr>  but going in declaration<br>
>>> order,<br>
>>> +    // instead of reverse order which is how they're stored in the AST.<br>
>>> +    AttrVec Cand1Attrs = Cand1.Function->getAttrs();<br>
>>> +    AttrVec::iterator Cand1E = std::remove_if(Cand1Attrs.begin(),<br>
>>> +                                              Cand1Attrs.end(),<br>
>>> +                                              IsNotEnableIfAttr);<br>
>>> +    std::reverse(Cand1Attrs.begin(), Cand1E);<br>
>>> +<br>
>>> +    AttrVec Cand2Attrs = Cand2.Function->getAttrs();<br>
>>> +    AttrVec::iterator Cand2E = std::remove_if(Cand2Attrs.begin(),<br>
>>> +                                              Cand2Attrs.end(),<br>
>>> +                                              IsNotEnableIfAttr);<br>
>>> +    std::reverse(Cand2Attrs.begin(), Cand2E);<br>
>>> +    for (AttrVec::iterator<br>
>>> +         Cand1I = Cand1Attrs.begin(), Cand2I = Cand2Attrs.begin();<br>
>>> +         Cand1I != Cand1E || Cand2I != Cand2E; ++Cand1I, ++Cand2I) {<br>
>>> +      if (Cand1I == Cand1E)<br>
>>> +        return false;<br>
>>> +      if (Cand2I == Cand2E)<br>
>>> +        return true;<br>
>>> +      llvm::FoldingSetNodeID Cand1ID, Cand2ID;<br>
>>> +      cast<EnableIfAttr>(*Cand1I)->getCond()->Profile(Cand1ID,<br>
>>> +                                                      S.getASTContext(),<br>
>>> true);<br>
>>> +      cast<EnableIfAttr>(*Cand2I)->getCond()->Profile(Cand2ID,<br>
>>> +                                                      S.getASTContext(),<br>
>>> true);<br>
>>> +      if (!(Cand1ID == Cand2ID))<br>
>><br>
>><br>
>> operator != ?<br>
><br>
><br>
> Nope.<br>
><br>
>>> +        return false;<br>
>>> +    }<br>
>>> +  }<br>
>>> +<br>
>>>     return false;<br>
>>>   }<br>
>>><br>
>>> @@ -8819,6 +8963,15 @@<br>
>>>         <<  (unsigned) FnKind<<  CalleeTarget<<  CallerTarget;<br>
>>>   }<br>
>>><br>
>>> +void DiagnoseFailedEnableIfAttr(Sema&S, OverloadCandidate *Cand) {<br>
>>><br>
>>> +  FunctionDecl *Callee = Cand->Function;<br>
>>> +  EnableIfAttr *Attr =<br>
>>> static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data);<br>
>>> +<br>
>>> +  S.Diag(Callee->getLocation(),<br>
>>> +         diag::note_ovl_candidate_disabled_by_enable_if_attr)<br>
>>> +<<  Attr->getCond()->getSourceRange()<<  Attr->getMessage();<br>
>>> +}<br>
>>> +<br>
>>>   /// Generates a 'note' diagnostic for an overload candidate.  We've<br>
>>>   /// already generated a primary error at the call site.<br>
>>>   ///<br>
>>> @@ -8882,6 +9035,9 @@<br>
>>><br>
>>>     case ovl_fail_bad_target:<br>
>>>       return DiagnoseBadTarget(S, Cand);<br>
>>> +<br>
>>> +  case ovl_fail_enable_if:<br>
>>> +    return DiagnoseFailedEnableIfAttr(S, Cand);<br>
>>>     }<br>
>>>   }<br>
>>><br>
>>> @@ -11107,7 +11263,7 @@<br>
>>>           <<  qualsString<br>
>>>           <<  (qualsString.find(' ') == std::string::npos ? 1 : 2);<br>
>>>       }<br>
>>> -<br>
>>> +<br>
>>>       CXXMemberCallExpr *call<br>
>>>         = new (Context) CXXMemberCallExpr(Context, MemExprE, Args,<br>
>>>                                           resultType, valueKind,<br>
>>> RParenLoc);<br>
>>> Index: test/Sema/enable_if.c<br>
>>> ===================================================================<br>
>>> --- test/Sema/enable_if.c (revision 0)<br>
>>> +++ test/Sema/enable_if.c (working copy)<br>
>>> @@ -0,0 +1,97 @@<br>
>>> +// RUN: %clang_cc1 %s -verify -Wno-gcc-compat<br>
>>> +// RUN: %clang_cc1 %s -DCODEGEN -emit-llvm -o - -Wno-gcc-compat |<br>
>>> FileCheck %s<br>
>>> +<br>
>>> +#define O_CREAT 0x100<br>
>>> +typedef int mode_t;<br>
>>> +typedef unsigned long size_t;<br>
>>> +<br>
>>> +int open(const char *pathname, int flags)<br>
>>> __attribute__((enable_if(!(flags&  O_CREAT), "must specify mode when using<br>
>>> O_CREAT"))) __attribute__((overloadable));  // expected-note{{candidate<br>
>>> disabled: must specify mode when using O_CREAT}}<br>
>>><br>
>>> +int open(const char *pathname, int flags, mode_t mode)<br>
>>> __attribute__((overloadable));  // expected-note{{candidate function not<br>
>>> viable: requires 3 arguments, but 2 were provided}}<br>
>>> +<br>
>>> +void test1() {<br>
>>> +#ifndef CODEGEN<br>
>>> +  open("path", O_CREAT);  // expected-error{{no matching function for<br>
>>> call to 'open'}}<br>
>>> +#endif<br>
>>> +  open("path", O_CREAT, 0660);<br>
>>> +  open("path", 0);<br>
>>> +  open("path", 0, 0);<br>
>>> +}<br>
>>> +<br>
>>> +size_t __strnlen_chk(const char *s, size_t requested_amount, size_t<br>
>>> s_len);<br>
>>> +<br>
>>> +size_t strnlen(const char *s, size_t maxlen)  //<br>
>>> expected-note{{candidate function}}<br>
>>> +  __attribute__((overloadable))<br>
>>> +  __asm__("strnlen_real1");<br>
>>> +<br>
>>> +__attribute__((always_inline))<br>
>>> +inline size_t strnlen(const char *s, size_t maxlen)  //<br>
>>> expected-note{{candidate function}}<br>
>>> +  __attribute__((overloadable))<br>
>>> +  __attribute__((enable_if(__builtin_object_size(s, 0) != -1,<br>
>>> +                           "chosen when target buffer size is known")))<br>
>>> +{<br>
>>> +  return __strnlen_chk(s, maxlen, __builtin_object_size(s, 0));<br>
>>> +}<br>
>>> +<br>
>>> +size_t strnlen(const char *s, size_t maxlen)  //<br>
>>> expected-note{{candidate disabled: chosen when 'maxlen' is known to be less<br>
>>> than or equal to the buffer size}}<br>
>>> +  __attribute__((overloadable))<br>
>>> +  __attribute__((enable_if(__builtin_object_size(s, 0) != -1,<br>
>>> +                           "chosen when target buffer size is known")))<br>
>>> +  __attribute__((enable_if(maxlen<= __builtin_object_size(s, 0),<br>
>>> +                           "chosen when 'maxlen' is known to be less<br>
>>> than or equal to the buffer size")))<br>
>>> +  __asm__("strnlen_real2");<br>
>>> +<br>
>>> +size_t strnlen(const char *s, size_t maxlen)  //<br>
>>> expected-note{{candidate function has been explicitly made unavailable}}<br>
>>> +  __attribute__((overloadable))<br>
>>> +  __attribute__((enable_if(__builtin_object_size(s, 0) != -1,<br>
>>> +                           "chosen when target buffer size is known")))<br>
>>> +  __attribute__((enable_if(maxlen>  __builtin_object_size(s, 0),<br>
>>> +                           "chosen when 'maxlen' is larger than the<br>
>>> buffer size")))<br>
>>> +  __attribute__((unavailable("'maxlen' is larger than the buffer<br>
>>> size")));<br>
>>> +<br>
>>> +void test2(const char *s, int i) {<br>
>>> +// CHECK: define void @test2<br>
>>> +  const char c[123];<br>
>>> +  strnlen(s, i);<br>
>>> +// CHECK: call {{.*}}strnlen_real1<br>
>>> +  strnlen(s, 999);<br>
>>> +// CHECK: call {{.*}}strnlen_real1<br>
>>> +  strnlen(c, 1);<br>
>>> +// CHECK: call {{.*}}strnlen_real2<br>
>>> +  strnlen(c, i);<br>
>>> +// CHECK: call {{.*}}strnlen_chk<br>
>>> +#ifndef CODEGEN<br>
>>> +  strnlen(c, 999);  // expected-error{{call to unavailable function<br>
>>> 'strnlen': 'maxlen' is larger than the buffer size}}<br>
>>> +#endif<br>
>>> +}<br>
>>> +<br>
>>> +int isdigit(int c) __attribute__((overloadable));  //<br>
>>> expected-note{{candidate function}}<br>
>>> +int isdigit(int c) __attribute__((overloadable))  //<br>
>>> expected-note{{candidate function has been explicitly made unavailable}}<br>
>>> +  __attribute__((enable_if(c<= -1 || c>  255, "'c' must have the value<br>
>>> of an unsigned char or EOF")))<br>
>>> +  __attribute__((unavailable("'c' must have the value of an unsigned<br>
>>> char or EOF")));<br>
>>> +<br>
>>> +void test3(int c) {<br>
>>> +  isdigit(c);<br>
>>> +  isdigit(10);<br>
>>> +#ifndef CODEGEN<br>
>>> +  isdigit(-10);  // expected-error{{call to unavailable function<br>
>>> 'isdigit': 'c' must have the value of an unsigned char or EOF}}<br>
>>> +#endif<br>
>>> +}<br>
>>> +<br>
>>> +#ifndef CODEGEN<br>
>>> +__attribute__((enable_if(n == 0, "chosen when 'n' is zero"))) void<br>
>>> f1(int n); // expected-error{{use of undeclared identifier 'n'}}<br>
>>> +<br>
>>> +int n __attribute__((enable_if(1, "always chosen"))); //<br>
>>> expected-warning{{'enable_if' attribute only applies to functions}}<br>
>>> +<br>
>>> +void f(int n) __attribute__((enable_if("chosen when 'n' is zero", n ==<br>
>>> 0)));  // expected-error{{'enable_if' attribute requires a string}}<br>
>>> +<br>
>>> +void f(int n) __attribute__((enable_if()));  //<br>
>>> expected-error{{'enable_if' attribute requires exactly 2 arguments}}<br>
>>> +<br>
>>> +void f(int n) __attribute__((enable_if(unresolvedid, "chosen when<br>
>>> 'unresolvedid' is non-zero")));  // expected-error{{use of undeclared<br>
>>> identifier 'unresolvedid'}}<br>
>>> +<br>
>>> +int global;<br>
>>> +void f(int n) __attribute__((enable_if(global == 0, "chosen when<br>
>>> 'global' is zero")));  // expected-error{{enable_if attribute expression<br>
>>> never produces a constant expression}}  // expected-note{{subexpression not<br>
>>> valid in a constant expression}}<br>
>>> +<br>
>>> +const int cst = 7;<br>
>>> +void return_cst(void) __attribute__((overloadable))<br>
>>> __attribute__((enable_if(cst == 7, "chosen when 'cst' is 7")));<br>
>>> +void test_return_cst() { return_cst(); }<br>
>>> +#endif<br>
>>> Index: test/SemaCXX/enable_if.cpp<br>
>>> ===================================================================<br>
>>> --- test/SemaCXX/enable_if.cpp (revision 0)<br>
>>> +++ test/SemaCXX/enable_if.cpp (working copy)<br>
>>> @@ -0,0 +1,56 @@<br>
>>> +// RUN: %clang_cc1 -std=c++11 -verify -Wno-gcc-compat %s<br>
>>> +<br>
>>> +typedef int (*fp)(int);<br>
>>> +int surrogate(int);<br>
>>> +<br>
>>> +struct X {<br>
>>> +  void f(int n) __attribute__((enable_if(n == 0, "chosen when 'n' is<br>
>>> zero")));<br>
>>> +  void f(int n) __attribute__((enable_if(n == 1, "chosen when 'n' is<br>
>>> one")));  // expected-note{{member declaration nearly matches}}<br>
>>> expected-note{{candidate disabled: chosen when 'n' is one}}<br>
>>> +<br>
>>> +  static void s(int n) __attribute__((enable_if(n == 0, "chosen when 'n'<br>
>>> is zero")));  // expected-note2{{candidate disabled: chosen when 'n' is<br>
>>> zero}}<br>
>>> +<br>
>>> +  void conflict(int n) __attribute__((enable_if(n+n == 10, "chosen when<br>
>>> 'n' is five")));  // expected-note{{candidate function}}<br>
>>> +  void conflict(int n) __attribute__((enable_if(n*2 == 10, "chosen when<br>
>>> 'n' is five")));  // expected-note{{candidate function}}<br>
>>> +<br>
>>> +  operator long() __attribute__((enable_if(true, "chosen on your<br>
>>> platform")));<br>
>>> +  operator int() __attribute__((enable_if(false, "chosen on other<br>
>>> platform")));<br>
>>> +<br>
>>> +  operator fp() __attribute__((enable_if(false, "never enabled"))) {<br>
>>> return surrogate; }  // expected-note{{conversion candidate of type 'int<br>
>>> (*)(int)'}}  // FIXME: the message is not displayed<br>
>>> +};<br>
>>> +<br>
>>> +void X::f(int n) __attribute__((enable_if(n == 0, "chosen when 'n' is<br>
>>> zero")))  // expected-note{{member declaration nearly matches}}<br>
>>> expected-note{{candidate disabled: chosen when 'n' is zero}}<br>
>>> +{<br>
>>> +}<br>
>>> +<br>
>>> +void X::f(int n) __attribute__((enable_if(n == 2, "chosen when 'n' is<br>
>>> two")))  // expected-error{{out-of-line definition of 'f' does not match any<br>
>>> declaration in 'X'}} expected-note{{candidate disabled: chosen when 'n' is<br>
>>> two}}<br>
>>> +{<br>
>>> +}<br>
>>> +<br>
>>> +__attribute__((deprecated)) constexpr int old() { return 0; }  //<br>
>>> expected-note2{{'old' has been explicitly marked deprecated here}}<br>
>>> +void deprec1(int i) __attribute__((enable_if(old() == 0, "chosen when<br>
>>> old() is zero")));  // expected-warning{{'old' is deprecated}}<br>
>>> +void deprec2(int i) __attribute__((enable_if(old() == 0, "chosen when<br>
>>> old() is zero")));  // expected-warning{{'old' is deprecated}}<br>
>>> +<br>
>>> +void overloaded(int);<br>
>>> +void overloaded(long);<br>
>>> +<br>
>>> +void test() {<br>
>>> +  X x;<br>
>>> +  x.f(0);<br>
>>> +  x.f(1);<br>
>>> +  x.f(2);  // no error, suppressed by erroneous out-of-line definition<br>
>>> +  x.f(3);  // expected-error{{no matching member function for call to<br>
>>> 'f'}}<br>
>>> +<br>
>>> +  x.s(0);<br>
>>> +  x.s(1);  // expected-error{{no matching member function for call to<br>
>>> 's'}}<br>
>>> +<br>
>>> +  X::s(0);<br>
>>> +  X::s(1);  // expected-error{{no matching member function for call to<br>
>>> 's'}}<br>
>>> +<br>
>>> +  x.conflict(5);  // expected-error{{call to member function 'conflict'<br>
>>> is ambiguous}}<br>
>>> +<br>
>>> +  deprec2(0);<br>
>>> +<br>
>>> +  overloaded(x);<br>
>>> +<br>
>>> +  int i = x(1);  // expected-error{{no matching function for call to<br>
>>> object of type 'X'}}<br>
>>> +}<br>
>><br>
>><br>
>> Some tests involving templates might be kind of interesting.<br>
><br>
><br>
> Agreed! I was just wondering whether:<br>
><br>
>   template<int N> void foo() __attribute__((enable_if(N == 1)));<br>
>   template<int N> void foo() __attribute__((enable_if(N == 1)));<br>
><br>
> is considered one declaration + redeclaration, or two declarations. The<br>
> verdict? "'enable_if' attribute expression never produces a constant". Sigh.<br>
<br>
</div></div>I'll let Richard tackle this one. :-)<br>
<span><font color="#888888"><br>
~Aaron<br>
</font></span><div><div>_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@cs.uiuc.edu" target="_blank">cfe-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits</a><br>
</div></div></blockquote></div></div></div>