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