diff options
author | Nick Lewycky <nicholas@mxc.ca> | 2014-01-11 02:50:57 +0000 |
---|---|---|
committer | Nick Lewycky <nicholas@mxc.ca> | 2014-01-11 02:50:57 +0000 |
commit | 35a6ef4c35cca71540255d7531af91f14515577d (patch) | |
tree | 78e147cf9261b23f6c4657f3f90e07c62717699c /clang/lib | |
parent | 5641233047034ae122c0204cb91b6e5f2da29319 (diff) | |
download | bcm5719-llvm-35a6ef4c35cca71540255d7531af91f14515577d.tar.gz bcm5719-llvm-35a6ef4c35cca71540255d7531af91f14515577d.zip |
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
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 89 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 31 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 29 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 12 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 15 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 189 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 40 |
8 files changed, 382 insertions, 25 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index bef77681649..e836e18238c 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -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(); +} diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index d3e76c5e92d..9835e249b1f 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -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(); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 5135530cd9f..49686314804 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -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) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 945525bdd54..6555a3d27fc 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -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; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 28f038a69e3..820f57f5f0c 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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 diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ca261cd7849..f479dc8ae56 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 13337480b0a..6032ed39805 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 22f13d7d2f7..6995ae712bb 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -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 |