forked from OSchip/llvm-project
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:
parent
5641233047
commit
35a6ef4c35
|
@ -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
|
||||
==========================================
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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}}
|
||||
}
|
Loading…
Reference in New Issue