Add a new attribute 'enable_if' which can be used to control overload resolution based on the values of the function arguments at the call site.

llvm-svn: 198996
This commit is contained in:
Nick Lewycky 2014-01-11 02:50:57 +00:00
parent 5641233047
commit 35a6ef4c35
17 changed files with 677 additions and 29 deletions

View File

@ -1316,6 +1316,8 @@ parameters of protocol-qualified type.
Query the presence of this new mangling with
``__has_feature(objc_protocol_qualifier_mangling)``.
.. _langext-overloading:
Function Overloading in C
=========================
@ -1398,6 +1400,84 @@ caveats to this use of name mangling:
Query for this feature with ``__has_extension(attribute_overloadable)``.
Controlling Overload Resolution
===============================
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);
int isdigit(int c) __attribute__((enable_if(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 to be displayed in diagnostics. The
expression is part of the function signature for the purposes of determining
whether it is a redeclaration (following the rules used when determining
whether a C++ template specialization is ODR-equivalent), but is not part of
the type.
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.
Because the enable_if expression is an unevaluated context, there are no global
state changes, nor the ability to pass information from the enable_if
expression to the function body. For 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))),
"chosen when the buffer size is known but 'maxlen' is not")))
{
return strnlen_chk(s, maxlen, __builtin_object_size(s, 0));
}
Multiple enable_if attributes may be applied to a single declaration. In this
case, the enable_if expressions are evaluated from left to right in the
following manner. First, the candidates whose enable_if expressions evaluate to
false or cannot be evaluated are discarded. If the remaining candidates do not
share ODR-equivalent enable_if expressions, the overload resolution is
ambiguous. Otherwise, enable_if overload resolution continues with the next
enable_if attribute on the candidates that have not been discarded and have
remaining enable_if attributes. In this way, we pick the most specific
overload out of a number of viable overloads using enable_if.
.. code-block:: c++
void f() __attribute__((enable_if(true, ""))); // #1
void f() __attribute__((enable_if(true, ""))) __attribute__((enable_if(true, ""))); // #2
void g(int i, int j) __attribute__((enable_if(i, ""))); // #1
void g(int i, int j) __attribute__((enable_if(j, ""))) __attribute__((enable_if(true))); // #2
In this example, a call to f() is always resolved to #2, as the first enable_if
expression is ODR-equivalent for both declarations, but #1 does not have another
enable_if expression to continue evaluating, so the next round of evaluation has
only a single candidate. In a call to g(1, 1), the call is ambiguous even though
#2 has more enable_if attributes, because the first enable_if expressions are
not ODR-equivalent.
Query for this feature with ``__has_attribute(enable_if)``.
Initializer lists for complex numbers in C
==========================================

View File

@ -508,6 +508,16 @@ public:
SmallVectorImpl<
PartialDiagnosticAt> &Diags);
/// isPotentialConstantExprUnevaluted - Return true if this expression might
/// be usable in a constant expression in C++11 in an unevaluated context, 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 isPotentialConstantExprUnevaluated(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;
@ -600,6 +610,14 @@ public:
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, inside an
/// unevaluated context. Returns true if the expression could be folded to a
/// constant.
bool EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
const 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 {

View File

@ -470,6 +470,13 @@ def Destructor : InheritableAttr {
let Subjects = SubjectList<[Function]>;
}
def EnableIf : InheritableAttr {
let Spellings = [GNU<"enable_if">];
let Subjects = SubjectList<[Function]>;
let Args = [ExprArgument<"Cond">, StringArgument<"Message">];
let TemplateDependent = 1;
}
def ExtVectorType : Attr {
let Spellings = [GNU<"ext_vector_type">];
let Subjects = SubjectList<[TypedefName], ErrorDiag>;

View File

@ -1737,6 +1737,8 @@ def err_constexpr_local_var_no_init : Error<
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_enable_if_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<
@ -2586,6 +2588,8 @@ def note_ovl_candidate_substitution_failure : Note<
"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 disabled: %0">;
def note_ovl_candidate_failed_overload_resolution : Note<
"candidate template ignored: couldn't resolve reference to overloaded "
"function %0">;

View File

@ -1968,7 +1968,7 @@ private:
if (Tok.is(tok::kw___attribute)) {
ParsedAttributes attrs(AttrFactory);
SourceLocation endLoc;
ParseGNUAttributes(attrs, &endLoc, LateAttrs);
ParseGNUAttributes(attrs, &endLoc, LateAttrs, &D);
D.takeAttributes(attrs, endLoc);
}
}
@ -1980,14 +1980,16 @@ private:
}
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) {

View File

@ -579,7 +579,11 @@ namespace clang {
/// (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).

View File

@ -101,6 +101,7 @@ namespace clang {
class DependentDiagnostic;
class DesignatedInitExpr;
class Designation;
class EnableIfAttr;
class EnumConstantDecl;
class Expr;
class ExtVectorType;
@ -2200,6 +2201,11 @@ public:
// identified by the expression Expr
void NoteAllOverloadCandidates(Expr* E, QualType DestType = QualType());
/// Check the enable_if expressions on the given function. Returns the first
/// failing attribute, 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)
@ -4770,6 +4776,7 @@ public:
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);

View File

@ -474,13 +474,30 @@ namespace {
/// Evaluate in any way we know how. Don't worry about side-effects that
/// can't be modeled.
EM_IgnoreSideEffects
EM_IgnoreSideEffects,
/// Evaluate as a constant expression. Stop if we find that the expression
/// is not a constant expression. Some expressions can be retried in the
/// optimizer if we don't constant fold them here, but in an unevaluated
/// context we try to fold them immediately since the optimizer never
/// gets a chance to look at it.
EM_ConstantExpressionUnevaluated,
/// Evaluate as a potential constant expression. Keep going if we hit a
/// construct that we can't evaluate yet (because we don't yet know the
/// value of something) but stop if we hit something that could never be
/// a constant expression. Some expressions can be retried in the
/// optimizer if we don't constant fold them here, but in an unevaluated
/// context we try to fold them immediately since the optimizer never
/// gets a chance to look at it.
EM_PotentialConstantExpressionUnevaluated
} EvalMode;
/// Are we checking whether the expression is a potential constant
/// expression?
bool checkingPotentialConstantExpression() const {
return EvalMode == EM_PotentialConstantExpression;
return EvalMode == EM_PotentialConstantExpression ||
EvalMode == EM_PotentialConstantExpressionUnevaluated;
}
/// Are we checking an expression for overflow?
@ -573,6 +590,8 @@ namespace {
// some later problem.
case EM_ConstantExpression:
case EM_PotentialConstantExpression:
case EM_ConstantExpressionUnevaluated:
case EM_PotentialConstantExpressionUnevaluated:
HasActiveDiagnostic = false;
return OptionalDiagnostic();
}
@ -644,11 +663,13 @@ namespace {
bool keepEvaluatingAfterSideEffect() {
switch (EvalMode) {
case EM_PotentialConstantExpression:
case EM_PotentialConstantExpressionUnevaluated:
case EM_EvaluateForOverflow:
case EM_IgnoreSideEffects:
return true;
case EM_ConstantExpression:
case EM_ConstantExpressionUnevaluated:
case EM_ConstantFold:
return false;
}
@ -670,10 +691,12 @@ namespace {
switch (EvalMode) {
case EM_PotentialConstantExpression:
case EM_PotentialConstantExpressionUnevaluated:
case EM_EvaluateForOverflow:
return true;
case EM_ConstantExpression:
case EM_ConstantExpressionUnevaluated:
case EM_ConstantFold:
case EM_IgnoreSideEffects:
return false;
@ -696,7 +719,9 @@ namespace {
Info.EvalStatus.Diag->empty() &&
!Info.EvalStatus.HasSideEffects),
OldMode(Info.EvalMode) {
if (Enabled && Info.EvalMode == EvalInfo::EM_ConstantExpression)
if (Enabled &&
(Info.EvalMode == EvalInfo::EM_ConstantExpression ||
Info.EvalMode == EvalInfo::EM_ConstantExpressionUnevaluated))
Info.EvalMode = EvalInfo::EM_ConstantFold;
}
void keepDiagnostics() { Enabled = false; }
@ -5985,7 +6010,17 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) {
// Expression had no side effects, but we couldn't statically determine the
// size of the referenced object.
return Error(E);
switch (Info.EvalMode) {
case EvalInfo::EM_ConstantExpression:
case EvalInfo::EM_PotentialConstantExpression:
case EvalInfo::EM_ConstantFold:
case EvalInfo::EM_EvaluateForOverflow:
case EvalInfo::EM_IgnoreSideEffects:
return Error(E);
case EvalInfo::EM_ConstantExpressionUnevaluated:
case EvalInfo::EM_PotentialConstantExpressionUnevaluated:
return Success(-1ULL, E);
}
}
case Builtin::BI__builtin_bswap16:
@ -8656,6 +8691,28 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,
return IsConstExpr;
}
bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
const FunctionDecl *Callee,
llvm::ArrayRef<const Expr*> Args) const {
Expr::EvalStatus Status;
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpressionUnevaluated);
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) {
@ -8696,3 +8753,27 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
return Diags.empty();
}
bool Expr::isPotentialConstantExprUnevaluated(Expr *E,
const FunctionDecl *FD,
SmallVectorImpl<
PartialDiagnosticAt> &Diags) {
Expr::EvalStatus Status;
Status.Diag = &Diags;
EvalInfo Info(FD->getASTContext(), Status,
EvalInfo::EM_PotentialConstantExpressionUnevaluated);
// Fabricate a call stack frame to give the arguments a plausible cover story.
ArrayRef<const Expr*> Args;
ArgVector ArgValues(0);
bool Success = EvaluateArgs(Args, ArgValues, Info);
(void)Success;
assert(Success &&
"Failed to set up arguments for potential constant evaluation");
CallStackFrame Frame(Info, SourceLocation(), FD, 0, ArgValues.data());
APValue ResultScratch;
Evaluate(ResultScratch, Info, E);
return Diags.empty();
}

View File

@ -117,7 +117,8 @@ static bool isAttributeLateParsed(const IdentifierInfo &II) {
/// 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)) {
@ -153,7 +154,7 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs,
// Handle "parameterized" attributes
if (!LateAttrs || !isAttributeLateParsed(*AttrName)) {
ParseGNUAttributeArgs(AttrName, AttrNameLoc, attrs, endLoc, 0,
SourceLocation(), AttributeList::AS_GNU);
SourceLocation(), AttributeList::AS_GNU, D);
continue;
}
@ -258,7 +259,8 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
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 '('");
@ -272,23 +274,38 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
return;
}
if (AttrKind == AttributeList::AT_ObjCBridgeRelated) {
ParseObjCBridgeRelatedAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
return;
}
// Type safety attributes have their own grammar.
if (AttrKind == AttributeList::AT_TypeTagForDatatype) {
ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
return;
}
// Some attributes expect solely a type parameter.
if (attributeIsTypeArgAttr(*AttrName)) {
ParseAttributeWithTypeArg(*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);
}
}
// Ignore the left paren location for now.
ConsumeParen();
@ -1156,7 +1173,7 @@ void Parser::ParseLexedAttribute(LateParsedAttribute &LA,
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();
@ -1169,7 +1186,7 @@ void Parser::ParseLexedAttribute(LateParsedAttribute &LA,
// 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();

View File

@ -3243,7 +3243,7 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
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)

View File

@ -827,6 +827,34 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D,
Attr.getAttributeSpellingListIndex()));
}
static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
Expr *Cond = Attr.getArgAsExpr(0);
if (!Cond->isTypeDependent()) {
ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
if (Converted.isInvalid())
return;
Cond = Converted.take();
}
StringRef Msg;
if (!S.checkStringLiteralArgumentAttr(Attr, 1, Msg))
return;
SmallVector<PartialDiagnosticAt, 8> Diags;
if (!Cond->isValueDependent() &&
!Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D),
Diags)) {
S.Diag(Attr.getLoc(), diag::err_enable_if_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, Cond, Msg,
Attr.getAttributeSpellingListIndex()));
}
static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) {
ConsumableAttr::ConsumedState DefaultState;
@ -3990,6 +4018,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
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;

View File

@ -6079,6 +6079,18 @@ void Sema::ActOnFinishDelayedMemberDeclarations(Scope *S, Decl *RecordD) {
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

View File

@ -4474,6 +4474,21 @@ Sema::ActOnCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc,
else if (isa<MemberExpr>(NakedFn))
NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl();
if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NDecl)) {
if (FD->hasAttr<EnableIfAttr>()) {
if (const EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) {
Diag(Fn->getLocStart(),
isa<CXXMethodDecl>(FD) ?
diag::err_ovl_no_viable_member_function_in_call :
diag::err_ovl_no_viable_function_in_call)
<< FD << FD->getSourceRange();
Diag(FD->getLocation(),
diag::note_ovl_candidate_disabled_by_enable_if_attr)
<< Attr->getCond()->getSourceRange() << Attr->getMessage();
}
}
}
return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
ExecConfig, IsExecConfig);
}

View File

@ -1008,8 +1008,8 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
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
@ -1085,6 +1085,22 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
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;
}
@ -5452,11 +5468,11 @@ void
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() &&
@ -5568,7 +5584,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
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
@ -5577,6 +5593,77 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
Candidate.Conversions[ArgIdx].setEllipsis();
}
}
if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
}
static bool IsNotEnableIfAttr(Attr *A) { return !isa<EnableIfAttr>(A); }
EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
bool MissingImplicitThis) {
// FIXME: specific_attr_iterator<EnableIfAttr> iterates in reverse order, but
// we need to find the first failing one.
if (!Function->hasAttrs())
return 0;
AttrVec Attrs = Function->getAttrs();
AttrVec::iterator E = std::remove_if(Attrs.begin(), Attrs.end(),
IsNotEnableIfAttr);
if (Attrs.begin() == E)
return 0;
std::reverse(Attrs.begin(), E);
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())
return cast<EnableIfAttr>(Attrs[0]);
for (AttrVec::iterator I = Attrs.begin(); I != E; ++I) {
APValue Result;
EnableIfAttr *EIA = cast<EnableIfAttr>(*I);
if (!EIA->getCond()->EvaluateWithSubstitution(
Result, Context, Function,
llvm::ArrayRef<const Expr*>(ConvertedArgs.data(),
ConvertedArgs.size())) ||
!Result.isInt() || !Result.getInt().getBoolValue()) {
return EIA;
}
}
return 0;
}
/// \brief Add all of the function declarations in the given function set to
@ -5658,9 +5745,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
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) &&
@ -5747,15 +5834,22 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
if (Candidate.Conversions[ArgIdx + 1].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
break;
return;
}
} else {
// (C++ 13.3.2p2): For the purposes of overload resolution, any
// argument for which there is no corresponding parameter is
// considered to ""match the ellipsis" (C+ 13.3.3.1.3).
// considered to "match the ellipsis" (C+ 13.3.3.1.3).
Candidate.Conversions[ArgIdx + 1].setEllipsis();
}
}
if (EnableIfAttr *FailedAttr = CheckEnableIf(Method, Args, true)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
}
/// \brief Add a C++ member function template as a candidate to the candidate
@ -5971,7 +6065,7 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
return;
}
// We won't go through a user-define type conversion function to convert a
// We won't go through a user-defined type conversion function to convert a
// derived to base as such conversions are given Conversion Rank. They only
// go through a copy constructor. 13.3.3.1.2-p4 [over.ics.user]
QualType FromCanon
@ -6031,6 +6125,7 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
GetConversionRank(ICS.Standard.Second) != ICR_Exact_Match) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_final_conversion_not_exact;
return;
}
// C++0x [dcl.init.ref]p5:
@ -6042,18 +6137,26 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
ICS.Standard.First == ICK_Lvalue_To_Rvalue) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_final_conversion;
return;
}
break;
case ImplicitConversionSequence::BadConversion:
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_final_conversion;
break;
return;
default:
llvm_unreachable(
"Can only end up with a standard conversion sequence or failure");
}
if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, ArrayRef<Expr*>())) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
}
/// \brief Adds a conversion function template specialization
@ -6191,7 +6294,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
if (Candidate.Conversions[ArgIdx + 1].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
break;
return;
}
} else {
// (C++ 13.3.2p2): For the purposes of overload resolution, any
@ -6200,6 +6303,13 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
Candidate.Conversions[ArgIdx + 1].setEllipsis();
}
}
if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, ArrayRef<Expr*>())) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
}
/// \brief Add overload candidates for overloaded operators that are
@ -8111,6 +8221,47 @@ isBetterOverloadCandidate(Sema &S,
}
}
// Check for enable_if value-based overload resolution.
if (Cand1.Function && Cand2.Function &&
(Cand1.Function->hasAttr<EnableIfAttr>() ||
Cand2.Function->hasAttr<EnableIfAttr>())) {
// FIXME: The next several lines are just
// specific_attr_iterator<EnableIfAttr> but going in declaration order,
// instead of reverse order which is how they're stored in the AST.
AttrVec Cand1Attrs;
AttrVec::iterator Cand1E = Cand1Attrs.end();
if (Cand1.Function->hasAttrs()) {
Cand1Attrs = Cand1.Function->getAttrs();
Cand1E = std::remove_if(Cand1Attrs.begin(), Cand1Attrs.end(),
IsNotEnableIfAttr);
std::reverse(Cand1Attrs.begin(), Cand1E);
}
AttrVec Cand2Attrs;
AttrVec::iterator Cand2E = Cand2Attrs.end();
if (Cand2.Function->hasAttrs()) {
Cand2Attrs = Cand2.Function->getAttrs();
Cand2E = std::remove_if(Cand2Attrs.begin(), Cand2Attrs.end(),
IsNotEnableIfAttr);
std::reverse(Cand2Attrs.begin(), Cand2E);
}
for (AttrVec::iterator
Cand1I = Cand1Attrs.begin(), Cand2I = Cand2Attrs.begin();
Cand1I != Cand1E || Cand2I != Cand2E; ++Cand1I, ++Cand2I) {
if (Cand1I == Cand1E)
return false;
if (Cand2I == Cand2E)
return true;
llvm::FoldingSetNodeID Cand1ID, Cand2ID;
cast<EnableIfAttr>(*Cand1I)->getCond()->Profile(Cand1ID,
S.getASTContext(), true);
cast<EnableIfAttr>(*Cand2I)->getCond()->Profile(Cand2ID,
S.getASTContext(), true);
if (!(Cand1ID == Cand2ID))
return false;
}
}
return false;
}
@ -8819,6 +8970,15 @@ void DiagnoseBadTarget(Sema &S, OverloadCandidate *Cand) {
<< (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.
///
@ -8882,6 +9042,9 @@ void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
case ovl_fail_bad_target:
return DiagnoseBadTarget(S, Cand);
case ovl_fail_enable_if:
return DiagnoseFailedEnableIfAttr(S, Cand);
}
}
@ -11107,7 +11270,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
<< qualsString
<< (qualsString.find(' ') == std::string::npos ? 1 : 2);
}
CXXMemberCallExpr *call
= new (Context) CXXMemberCallExpr(Context, MemExprE, Args,
resultType, valueKind, RParenLoc);

View File

@ -129,6 +129,39 @@ static void instantiateDependentAlignedAttr(
}
}
static void instantiateDependentEnableIfAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const EnableIfAttr *A, const Decl *Tmpl, Decl *New) {
Expr *Cond = 0;
{
EnterExpressionEvaluationContext Unevaluated(S, Sema::Unevaluated);
ExprResult Result = S.SubstExpr(A->getCond(), TemplateArgs);
if (Result.isInvalid())
return;
Cond = Result.takeAs<Expr>();
}
if (A->getCond()->isTypeDependent() && !Cond->isTypeDependent()) {
ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
if (Converted.isInvalid())
return;
Cond = Converted.take();
}
SmallVector<PartialDiagnosticAt, 8> Diags;
if (A->getCond()->isValueDependent() && !Cond->isValueDependent() &&
!Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(Tmpl),
Diags)) {
S.Diag(A->getLocation(), diag::err_enable_if_never_constant_expr);
for (int I = 0, N = Diags.size(); I != N; ++I)
S.Diag(Diags[I].first, Diags[I].second);
return;
}
EnableIfAttr *EIA = new (S.getASTContext()) EnableIfAttr(
A->getLocation(), S.getASTContext(), Cond, A->getMessage());
New->addAttr(EIA);
}
void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
const Decl *Tmpl, Decl *New,
LateInstantiatedAttrVec *LateAttrs,
@ -144,6 +177,13 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
continue;
}
const EnableIfAttr *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr);
if (EnableIf && EnableIf->getCond()->isValueDependent()) {
instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
New);
continue;
}
assert(!TmplAttr->isPackExpansion());
if (TmplAttr->isLateParsed() && LateAttrs) {
// Late parsed attributes must be instantiated and attached after the

View File

@ -0,0 +1,97 @@
// 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(!(flags & O_CREAT), "must specify mode when using O_CREAT"))) __attribute__((overloadable)); // expected-note{{candidate disabled: 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 function}}
__attribute__((overloadable))
__asm__("strnlen_real1");
__attribute__((always_inline))
inline size_t strnlen(const char *s, size_t maxlen) // expected-note{{candidate function}}
__attribute__((overloadable))
__attribute__((enable_if(__builtin_object_size(s, 0) != -1,
"chosen when target buffer size is known")))
{
return __strnlen_chk(s, maxlen, __builtin_object_size(s, 0));
}
size_t strnlen(const char *s, size_t maxlen) // expected-note{{candidate disabled: 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,
"chosen when target buffer size is known")))
__attribute__((enable_if(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,
"chosen when target buffer size is known")))
__attribute__((enable_if(maxlen > __builtin_object_size(s, 0),
"chosen when 'maxlen' is larger than the buffer size")))
__attribute__((unavailable("'maxlen' is larger than the buffer size")));
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__((overloadable)); // expected-note{{candidate function}}
int isdigit(int c) __attribute__((overloadable)) // expected-note{{candidate function has been explicitly made unavailable}}
__attribute__((enable_if(c <= -1 || c > 255, "'c' must have the value of an unsigned char or EOF")))
__attribute__((unavailable("'c' must have the value of an unsigned char or EOF")));
void test3(int c) {
isdigit(c);
isdigit(10);
#ifndef CODEGEN
isdigit(-10); // expected-error{{call to unavailable function 'isdigit': 'c' must have the value of an unsigned char or EOF}}
#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-warning{{'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

View File

@ -0,0 +1,72 @@
// RUN: %clang_cc1 -std=c++11 -verify -Wno-gcc-compat %s
typedef int (*fp)(int);
int surrogate(int);
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 disabled: chosen when 'n' is one}}
static void s(int n) __attribute__((enable_if(n == 0, "chosen when 'n' is zero"))); // expected-note2{{candidate disabled: 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}}
operator long() __attribute__((enable_if(true, "chosen on your platform")));
operator int() __attribute__((enable_if(false, "chosen on other platform")));
operator fp() __attribute__((enable_if(false, "never enabled"))) { return surrogate; } // expected-note{{conversion candidate of type 'int (*)(int)'}} // FIXME: the message is not displayed
};
void X::f(int n) __attribute__((enable_if(n == 0, "chosen when 'n' is zero"))) // expected-note{{member declaration nearly matches}} expected-note{{candidate disabled: 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 disabled: chosen when 'n' is two}}
{
}
__attribute__((deprecated)) constexpr int old() { return 0; } // expected-note2{{'old' has been explicitly marked deprecated 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 overloaded(int);
void overloaded(long);
struct Nothing { };
template<typename T> void typedep(T t) __attribute__((enable_if(t, ""))); // expected-note{{candidate disabled:}} expected-error{{value of type 'Nothing' is not contextually convertible to 'bool'}}
template<int N> void valuedep() __attribute__((enable_if(N == 1, "")));
// FIXME: we skip potential constant expression evaluation on value dependent
// enable-if expressions
int not_constexpr();
template<int N> void valuedep() __attribute__((enable_if(N == not_constexpr(), "")));
template <typename T> void instantiationdep() __attribute__((enable_if(sizeof(sizeof(T)) != 0, "")));
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);
overloaded(x);
int i = x(1); // expected-error{{no matching function for call to object of type 'X'}}
Nothing n;
typedep(0); // expected-error{{no matching function for call to 'typedep'}}
typedep(1);
typedep(n); // expected-note{{in instantiation of function template specialization 'typedep<Nothing>' requested here}}
}