Updated patch attached. Almost all review comments implemented!
Richard Smith wrote:
Hi Nick,
This looks really promising. Do you yet know whether this is enough to
implement *all* the functionality of -DFORTIFY_SOURCE?
I asked Geremy to try implementing it with the patch I posted, and he
says he has -DFORTIFY_SOURCE=1 done, and forsees no problems with
-DFORTIFY_SOURCE=2.
One big thing missing here is documentation. That should serve to both
motivate the feature (and explain why it's sufficiently valuable for us
to have and maintain this extension), and to describe how it's intended
to be used, plus what we actually guarantee in terms of its semantics.
Great point. I've started on this, but I'm not really sure yet what the
semantics ought to be ...
I'm a bit concerned that the semantics of the attribute currently
involve "try as hard as you can to fold this to a constant, and even
ignore side-effects, but if constant-folding it fails, don't choose this
overload" -- if we change how hard we're willing to try when
constant-folding in the future, it might break code relying on this
feature. How essential is it that we support conditions that have
side-effects?
On the one hand, I will not support anything that actually changes state
inside the expression. On the other hand, imagine trying to express
strlen(foo) inside an enable_if expression in C mode where you can't
write constexpr functions, loops, etc. (No, statement expressions are
not allowed inside the enable_if attribute, I tried that.)
I've asked Geremy to let me know what he's actually using in his
expressions. If he isn't using anything that isn't a language-defined
constant expression then that's the obvious way to go.
--- include/clang/Sema/TemplateDeduction.h(revision 191171)
+++ include/clang/Sema/TemplateDeduction.h(working copy)
@@ -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;
Can you reuse the existing Data pointer for this, rather than growing
DeductionFailureInfo?
Done.
--- lib/AST/ExprConstant.cpp(revision 191171)
+++ lib/AST/ExprConstant.cpp(working copy)
@@ -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;
Reusing the current 'Info' here will mean that __builtin_constant_p's
result will depend on the context as well as its operand. That may be
what you intended here, but is a big enough change to be worth
separately calling out (and separately committing). For instance:
constexpr bool f(int n) { return __builtin_constant_p(n); }
constexpr bool a = f(0);
bool b = f(0);
extern int n;
bool c = f(n);
I think, with your patch, we'll guarantee to initialize 'a' to true and
'c' to false, and 'b' may or may not be 'true'. Prior to your patch, all
three will be false. IIRC, that basically matches g++'s behavior, but
it'd be good to double-check.
All true. And it does match g++.
I am extremely nervous about changing __builtin_constant_p's behaviour,
even if the result does more closely match gcc in this particular instance.
My ideal would be to have a way to get builtin_constant_p continue to
have access to the function parameters as I need for enable_if
evaluation with no other visible changes. It looks like doing that would
require making a copy of the EvalInfo and that would break all the
CallStackFrames which hold it by reference, so I'd need to copy them
too, yuck.
What do you think I should do here? Try to land this one bit on its own?
Try to make a deep copy of the EvalInfo state? Something more clever?
You'll also need to turn off the ability to mutate local state during
the evaluation of the subexpression in C++1y mode.
Can I punt this to a future patch? It might turn out to be a small
number of lines of code, but I'm worried about the cognitive load of
adding yet another "thread foo through ExprConstant" getting into this
patch.
--- lib/Parse/ParseDecl.cpp(revision 191171)
+++ lib/Parse/ParseDecl.cpp(working copy)
@@ -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);
Filling in the scope should be done in Sema/, not in Parse/.
Done.
Can we avoid redoing the enable_if check in SemaChecking if we've
already been through overload resolution (and done the checking there)?
Done!
--- lib/Sema/SemaOverload.cpp(revision 191171)
+++ lib/Sema/SemaOverload.cpp(working copy)
@@ -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);
I don't think you meant to return here in the case where NewID == OldID.
(You're only checking the first enable_if attribute on each function.)
Whoops! I had that right previously, then broke it during self-review.
+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;
ConvertedArgs maybe? These are not parameters.
Done.
--- test/Sema/enable_if.c(revision 0)
+++ test/Sema/enable_if.c(working copy)
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 %s -verify -Wno-gcc-compat
Presumably this warning flag is for the 'attributes on function
definitions' warning? Maybe we should only provide that for
GCC-compatible attributes. We already suppress it for the thread-safety
attributes, IIRC.
Done. We now have Parser::IsAttributeSupportedByGCC.
[...]
+// This f() is never chosen because the constant evaluator fails on
non-const global, but there's no error for that.
+// FIXME: we should issue an error at the point of the enable_if
declaration
+int global;
+void f(int n) __attribute__((enable_if(global == 0, "chosen when
'global' is zero"))); // expected-note{{candidate ignored: chosen when
'global' is zero}}
You can use isPotentialConstantExpr for the interesting half of this
(where the enable_if expression is never constant). I'm not sure what
you want to do with the boring half (where the expression evaluates to
'false' -- or 'true' -- independently of the function's parameters'
values or template arguments).
Thanks for the pointer, I've implemented this.
I'm okay with expressions that evaluate to true or to false. It could be
reasonable if it's expanded from a preprocessor macro or somesuch.
It'd be nice to have warnings for "{none|two or more} of the overloads
are enabled" but I think because of the way we parse we'd get the errors
at the call-sites before we could detect this.
Nick
Index: docs/LanguageExtensions.rst
===================================================================
--- docs/LanguageExtensions.rst (revision 191815)
+++ docs/LanguageExtensions.rst (working copy)
@@ -522,6 +522,53 @@
- (id)method __attribute__((availability(macosx,introduced=10.5))); // error: this method was available via the base class in 10.4
@end
+enable_if attribute
+===================
+Clang introduces the ``enable_if`` attribute, which can be placed on function
+declarations to control which overload is selected based on the values of the
+function's arguments. When combined with the
+:ref:``overloadable<langext-overloading>`` attribute, this feature is also
+available in C.
+
+.. code-block:: c++
+
+ 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")));
+
+ void foo(char c) {
+ isdigit(c);
+ isdigit(10);
+ isdigit(-10); // results in a compile-time error.
+ }
+
+The enable_if attribute takes two arguments, the first is an expression written
+in terms of the function parameters, the second is a string explaining why this
+overload candidate could not be selected.
+
+An enable_if expression will be evaluated by substituting the values of the
+parameters from the call-site into the arguments in the expression and
+determining whether the result is true. If the result is false or could not be
+determined through constant expression evaluation, then this overload will not
+be chosen and the reason supplied in the string will be given to the user if
+their code does not compile as a result.
+
+Becuase the enable_if expression is an unevaluated context, there are no globalstate changes, nor the ability to pass information from the enable_if
+expression to the function body. As a worked example, suppose we want calls to
+strnlen(strbuf, maxlen) to resolve to strnlen_chk(strbuf, maxlen, size of strbuf) only if the size of strbuf can be determined:
+
+.. code-block:: c++
+
+ __attribute__((always_inline))
+ static inline size_t strnlen(const char *s, size_t maxlen)
+ __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));
+ }
+
+
+Query for this feature with ``__has_attribute(enable_if)``.
+
Checks for Standard Language Features
=====================================
@@ -1256,6 +1303,8 @@
``__has_attribute(ns_returns_retained)``, etc.
+.. _langext-overloading:
+
Function Overloading in C
=========================
Index: include/clang/AST/Expr.h
===================================================================
--- include/clang/AST/Expr.h (revision 191815)
+++ include/clang/AST/Expr.h (working copy)
@@ -508,6 +508,15 @@
SmallVectorImpl<
PartialDiagnosticAt> &Diags);
+ /// isPotentialConstantExpr - Return true if this expression might be usable
+ /// in a constant expression in C++11, if it were in function FD marked
+ /// constexpr. Return false if the function can never produce a constant
+ /// expression, along with diagnostics describing why not.
+ static bool isPotentialConstantExpr(Expr *E,
+ const FunctionDecl *FD,
+ SmallVectorImpl<
+ PartialDiagnosticAt> &Diags);
+
/// isConstantInitializer - Returns true if this expression can be emitted to
/// IR as a constant, and thus can be used as a constant initializer in C.
bool isConstantInitializer(ASTContext &Ctx, bool ForRef) const;
@@ -601,6 +610,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 191815)
+++ include/clang/Basic/Attr.td (working copy)
@@ -369,6 +369,13 @@
let Args = [IntArgument<"Priority", 1>];
}
+def EnableIf : InheritableAttr {
+ let Spellings = [GNU<"enable_if">];
+ let Subjects = [Function];
+ 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 191815)
+++ include/clang/Basic/DiagnosticSemaKinds.td (working copy)
@@ -1711,6 +1711,8 @@
def ext_constexpr_function_never_constant_expr : ExtWarn<
"constexpr %select{function|constructor}0 never produces a "
"constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
+def err_enableif_never_constant_expr : Error<
+ "enable_if attribute expression never produces a constant expression">;
def err_constexpr_body_no_return : Error<
"no return statement in constexpr function">;
def warn_cxx11_compat_constexpr_body_no_return : Warning<
@@ -2539,6 +2541,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 191815)
+++ 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) {
@@ -1967,6 +1969,8 @@
ParsedAttributes &attrs,
SourceLocation *endLoc);
+ bool IsAttributeSupportedByGCC(StringRef AttrName);
+
bool IsThreadSafetyAttribute(StringRef AttrName);
void ParseThreadSafetyAttribute(IdentifierInfo &AttrName,
SourceLocation AttrNameLoc,
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h (revision 191815)
+++ 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 191815)
+++ 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;
@@ -2189,6 +2190,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)
@@ -4743,6 +4749,7 @@
AttributeList *AttrList);
void ActOnFinishCXXMemberDecls();
+ void ActOnReenterCXXMethodParameter(Scope *S, ParmVarDecl *Param);
void ActOnReenterTemplateScope(Scope *S, Decl *Template);
void ActOnReenterDeclaratorTemplateScope(Scope *S, DeclaratorDecl *D);
void ActOnStartDelayedMemberDeclarations(Scope *S, Decl *Record);
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp (revision 191815)
+++ 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,8 @@
// Expression had no side effects, but we couldn't statically determine the
// size of the referenced object.
- return Error(E);
+ return (Info.CheckingPotentialConstantExpression ||
+ Info.UnevaluatedContext) ? Success(-1ULL, E) : Error(E);
}
case Builtin::BI__builtin_bswap16:
@@ -5931,7 +5948,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 +8536,28 @@
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();
+ if (Info.EvalStatus.HasSideEffects)
+ return false;
+ }
+
+ // Build fake call to Callee.
+ CallStackFrame Frame(Info, Callee->getLocation(), Callee, /*This*/0,
+ ArgValues.data());
+ return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects;
+}
+
bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
SmallVectorImpl<
PartialDiagnosticAt> &Diags) {
@@ -8559,3 +8598,24 @@
return Diags.empty();
}
+
+bool Expr::isPotentialConstantExpr(Expr *E,
+ const FunctionDecl *FD,
+ SmallVectorImpl<
+ PartialDiagnosticAt> &Diags) {
+ Expr::EvalStatus Status;
+ Status.Diag = &Diags;
+
+ EvalInfo Info(FD->getASTContext(), Status);
+ Info.CheckingPotentialConstantExpression = true;
+
+ // Fabricate a call stack frame to give the arguments a plausible cover story.
+ ArrayRef<const Expr*> Args;
+ ArgVector ArgValues(0);
+ assert(EvaluateArgs(Args, ArgValues, Info));
+ CallStackFrame Frame(Info, SourceLocation(), FD, 0, ArgValues.data());
+
+ APValue ResultScratch;
+ Evaluate(ResultScratch, Info, E);
+ return Diags.empty();
+}
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp (revision 191815)
+++ 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,19 @@
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);
+ Actions.ActOnReenterCXXMethodParameter(getCurScope(), Param);
+ }
+ }
ConsumeParen(); // ignore the left paren loc for now
@@ -1020,6 +1035,7 @@
}
+
/// \brief Parse all attributes in LAs, and attach them to Decl D.
void Parser::ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,
bool EnterScope, bool OnDefinition) {
@@ -1034,7 +1050,6 @@
LAs.clear();
}
-
/// \brief Finish parsing an attribute for which parsing was delayed.
/// This will be called at the end of parsing a class declaration
/// for each LateParsedAttribute. We consume the saved tokens and
@@ -1052,7 +1067,7 @@
// Consume the previously pushed token.
ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
- if (OnDefinition && !IsThreadSafetyAttribute(LA.AttrName.getName())) {
+ if (OnDefinition && IsAttributeSupportedByGCC(LA.AttrName.getName())) {
// FIXME: Do not warn on C++11 attributes, once we start supporting
// them here.
Diag(Tok, diag::warn_attribute_on_function_definition)
@@ -1085,7 +1100,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 +1113,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();
@@ -1120,6 +1135,33 @@
}
}
+/// \brief Wrapper around a case statement checking if AttrName is an attribute
+/// supported by GCC.
+bool Parser::IsAttributeSupportedByGCC(StringRef AttrName) {
+ return llvm::StringSwitch<bool>(AttrName)
+ .Case("guarded_by", false)
+ .Case("guarded_var", false)
+ .Case("pt_guarded_by", false)
+ .Case("pt_guarded_var", false)
+ .Case("lockable", false)
+ .Case("scoped_lockable", false)
+ .Case("no_thread_safety_analysis", false)
+ .Case("acquired_after", false)
+ .Case("acquired_before", false)
+ .Case("exclusive_lock_function", false)
+ .Case("shared_lock_function", false)
+ .Case("exclusive_trylock_function", false)
+ .Case("shared_trylock_function", false)
+ .Case("unlock_function", false)
+ .Case("lock_returned", false)
+ .Case("locks_excluded", false)
+ .Case("exclusive_locks_required", false)
+ .Case("shared_locks_required", false)
+ .Case("enable_if", false)
+ .Case("overloadable", false)
+ .Default(true);
+}
+
/// \brief Wrapper around a case statement checking if AttrName is
/// one of the thread safety attributes
bool Parser::IsThreadSafetyAttribute(StringRef AttrName) {
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp (revision 191815)
+++ 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/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp (revision 191815)
+++ lib/Parse/Parser.cpp (working copy)
@@ -978,7 +978,7 @@
if (Tok.isNot(tok::equal)) {
AttributeList *DtorAttrs = D.getAttributes();
while (DtorAttrs) {
- if (!IsThreadSafetyAttribute(DtorAttrs->getName()->getName()) &&
+ if (IsAttributeSupportedByGCC(DtorAttrs->getName()->getName()) &&
!DtorAttrs->isCXX11Attribute()) {
Diag(DtorAttrs->getLoc(), diag::warn_attribute_on_function_definition)
<< DtorAttrs->getName()->getName();
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp (revision 191815)
+++ lib/Sema/SemaDeclAttr.cpp (working copy)
@@ -981,6 +981,38 @@
Attr.getAttributeSpellingListIndex()));
}
+static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (!isa<FunctionDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type)
+ << Attr.getName() << ExpectedFunction;
+ return;
+ }
+
+ if (!checkAttributeNumArgs(S, Attr, 2))
+ return;
+
+ Expr *Cond = Attr.getArgAsExpr(0);
+ ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
+ if (Converted.isInvalid())
+ return;
+
+ StringRef Msg;
+ if (!S.checkStringLiteralArgumentAttr(Attr, 1, Msg))
+ return;
+
+ SmallVector<PartialDiagnosticAt, 8> Diags;
+ if (!Expr::isPotentialConstantExpr(Cond, cast<FunctionDecl>(D), Diags)) {
+ S.Diag(Attr.getLoc(), diag::err_enableif_never_constant_expr);
+ for (int i = 0, n = Diags.size(); i != n; ++i)
+ S.Diag(Diags[i].first, Diags[i].second);
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ EnableIfAttr(Attr.getRange(), S.Context, Converted.take(), Msg,
+ Attr.getAttributeSpellingListIndex()));
+}
+
static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) {
ConsumableAttr::ConsumedState DefaultState;
@@ -4517,6 +4549,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/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp (revision 191815)
+++ lib/Sema/SemaDeclCXX.cpp (working copy)
@@ -5977,6 +5977,18 @@
PopDeclContext();
}
+/// This is used to implement the constant expression evaluation part of the
+/// attribute enable_if extension. There is nothing in standard C++ which would
+/// require reentering parameters.
+void Sema::ActOnReenterCXXMethodParameter(Scope *S, ParmVarDecl *Param) {
+ if (!Param)
+ return;
+
+ S->AddDecl(Param);
+ if (Param->getDeclName())
+ IdResolver.AddDecl(Param);
+}
+
/// ActOnStartDelayedCXXMethodDeclaration - We have completed
/// parsing a top-level (non-nested) C++ class, and we are now
/// parsing those parts of the given Method declaration that could
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp (revision 191815)
+++ lib/Sema/SemaExpr.cpp (working copy)
@@ -4425,6 +4425,21 @@
else if (isa<MemberExpr>(NakedFn))
NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl();
+ if (FunctionDecl *FD = dyn_cast<FunctionDecl>(NDecl)) {
+ if (FD->hasAttr<EnableIfAttr>()) {
+ if (EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) {
+ Diag(Fn->getLocStart(),
+ isa<CXXMethodDecl>(FD) ?
+ diag::err_ovl_no_viable_member_function_in_call :
+ diag::err_ovl_no_viable_function_in_call)
+ << FD->getDeclName() << FD->getSourceRange();
+ Diag(FD->getLocation(),
+ diag::note_ovl_candidate_disabled_by_enable_if_attr)
+ << Attr->getCond()->getSourceRange() << Attr->getMessage();
+ }
+ }
+ }
+
return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
ExecConfig, IsExecConfig);
}
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp (revision 191815)
+++ 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,22 @@
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);
+ if (!(NewID == OldID))
+ return true;
+ }
+
// The signatures match; this is not an overload.
return false;
}
@@ -5412,11 +5428,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 +5536,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 +5545,67 @@
Candidate.Conversions[ArgIdx].setEllipsis();
}
}
+
+ if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) {
+ Candidate.Viable = false;
+ Candidate.FailureKind = ovl_fail_enable_if;
+ Candidate.DeductionFailure.Data = 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> ConvertedArgs;
+ 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;
+ }
+ ConvertedArgs.push_back(R.take());
+ } else {
+ ExprResult R =
+ PerformCopyInitialization(InitializedEntity::InitializeParameter(
+ Context,
+ Function->getParamDecl(i)),
+ SourceLocation(),
+ Args[i]);
+ if (R.isInvalid()) {
+ InitializationFailed = true;
+ break;
+ }
+ ConvertedArgs.push_back(R.take());
+ }
+ }
+
+ if (InitializationFailed || Trap.hasErrorOccurred() ||
+ !(*I)->getCond()->EvaluateWithSubstitution(
+ Result, Context, Function,
+ llvm::ArrayRef<const Expr*>(ConvertedArgs.data(),
+ ConvertedArgs.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 +5685,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 +5731,13 @@
return;
}
+ if (EnableIfAttr *FailedAttr = CheckEnableIf(Method, Args, true)) {
+ Candidate.Viable = false;
+ Candidate.FailureKind = ovl_fail_enable_if;
+ Candidate.DeductionFailure.Data = FailedAttr;
+ return;
+ }
+
Candidate.Viable = true;
if (Method->isStatic() || ObjectType.isNull())
@@ -8726,6 +8808,15 @@
<< (unsigned) FnKind << CalleeTarget << CallerTarget;
}
+void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) {
+ FunctionDecl *Callee = Cand->Function;
+ EnableIfAttr *Attr = static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data);
+
+ 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.
///
@@ -8789,6 +8880,9 @@
case ovl_fail_bad_target:
return DiagnoseBadTarget(S, Cand);
+
+ case ovl_fail_enable_if:
+ return DiagnoseFailedEnableIfAttr(S, Cand);
}
}
@@ -11011,7 +11105,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,90 @@
+// RUN: %clang_cc1 %s -verify
+// RUN: %clang_cc1 %s -DCODEGEN -emit-llvm -o - | 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")));
+
+__attribute__((always_inline))
+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__((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
+
+}
+
+#ifndef CODEGEN
+__attribute__((enable_if(n == 0, "chosen when 'n' is zero"))) void f1(int n); // expected-error{{use of undeclared identifier 'n'}}
+
+int n __attribute__((enable_if(1, "always chosen"))); // expected-error{{'enable_if' attribute only applies to functions}}
+
+void f(int n) __attribute__((enable_if("chosen when 'n' is zero", n == 0))); // expected-error{{'enable_if' attribute requires a string}}
+
+void f(int n) __attribute__((enable_if())); // expected-error{{'enable_if' attribute requires exactly 2 arguments}}
+
+void f(int n) __attribute__((enable_if(unresolvedid, "chosen when 'unresolvedid' is non-zero"))); // expected-error{{use of undeclared identifier 'unresolvedid'}}
+
+int global;
+void f(int n) __attribute__((enable_if(global == 0, "chosen when 'global' is zero"))); // expected-error{{enable_if attribute expression never produces a constant expression}} // expected-note{{subexpression not valid in a constant expression}}
+
+const int cst = 7;
+void return_cst(void) __attribute__((overloadable)) __attribute__((enable_if(cst == 7, "chosen when 'cst' is 7")));
+void test_return_cst() { return_cst(); }
+#endif
Index: test/SemaCXX/enable_if.cpp
===================================================================
--- test/SemaCXX/enable_if.cpp (revision 0)
+++ test/SemaCXX/enable_if.cpp (working copy)
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++11 -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}}
+{
+}
+
+__attribute__((deprecated)) constexpr int old() { return 0; } // expected-note2{{'old' declared here}}
+void deprec1(int i) __attribute__((enable_if(old() == 0, "chosen when old() is zero"))); // expected-warning{{'old' is deprecated}}
+void deprec2(int i) __attribute__((enable_if(old() == 0, "chosen when old() is zero"))); // expected-warning{{'old' is deprecated}}
+
+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 member function for call to 's'}}
+
+ X::s(0);
+ X::s(1); // expected-error{{no matching member function for call to 's'}}
+
+ x.conflict(5); // expected-error{{call to member function 'conflict' is ambiguous}}
+
+ deprec2(0);
+}
+
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits