diff options
author | George Burgess IV <george.burgess.iv@gmail.com> | 2017-01-09 04:12:14 +0000 |
---|---|---|
committer | George Burgess IV <george.burgess.iv@gmail.com> | 2017-01-09 04:12:14 +0000 |
commit | 177399e2277c3dbecfc00d83f94784cc7bfdd4d6 (patch) | |
tree | fd3bdd091ba2ff8942eec3c67d793d6afc236b67 /clang/lib | |
parent | ff567a8ba200e05cd92015403302983510959766 (diff) | |
download | bcm5719-llvm-177399e2277c3dbecfc00d83f94784cc7bfdd4d6.tar.gz bcm5719-llvm-177399e2277c3dbecfc00d83f94784cc7bfdd4d6.zip |
Add the diagnose_if attribute to clang.
`diagnose_if` can be used to have clang emit either warnings or errors
for function calls that meet user-specified conditions. For example:
```
constexpr int foo(int a)
__attribute__((diagnose_if(a > 10, "configurations with a > 10 are "
"expensive.", "warning")));
int f1 = foo(9);
int f2 = foo(10); // warning: configuration with a > 10 are expensive.
int f3 = foo(f2);
```
It currently only emits diagnostics in cases where the condition is
guaranteed to always be true. So, the following code will emit no
warnings:
```
constexpr int bar(int a) {
foo(a);
return 0;
}
constexpr int i = bar(10);
```
We hope to support optionally emitting diagnostics for cases like that
(and emitting runtime checks) in the future.
Release notes will appear shortly. :)
Differential Revision: https://reviews.llvm.org/D27424
llvm-svn: 291418
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 19 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 109 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 73 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 344 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 63 |
6 files changed, 474 insertions, 137 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 18206e5e0ee..fe77c7f6f3b 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10410,10 +10410,25 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result, bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, const FunctionDecl *Callee, - ArrayRef<const Expr*> Args) const { + ArrayRef<const Expr*> Args, + const Expr *This) const { Expr::EvalStatus Status; EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpressionUnevaluated); + LValue ThisVal; + const LValue *ThisPtr = nullptr; + if (This) { +#ifndef NDEBUG + auto *MD = dyn_cast<CXXMethodDecl>(Callee); + assert(MD && "Don't provide `this` for non-methods."); + assert(!MD->isStatic() && "Don't provide `this` for static methods."); +#endif + if (EvaluateObjectArgument(Info, This, ThisVal)) + ThisPtr = &ThisVal; + if (Info.EvalStatus.HasSideEffects) + return false; + } + ArgVector ArgValues(Args.size()); for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end(); I != E; ++I) { @@ -10426,7 +10441,7 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, } // Build fake call to Callee. - CallStackFrame Frame(Info, Callee->getLocation(), Callee, /*This*/nullptr, + CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr, ArgValues.data()); return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index f9b6a91a300..c6a5bc74145 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -32,6 +32,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaInternal.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/MathExtras.h" @@ -890,34 +891,117 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } -static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) { - S.Diag(Attr.getLoc(), diag::ext_clang_enable_if); - - Expr *Cond = Attr.getArgAsExpr(0); +static bool checkFunctionConditionAttr(Sema &S, Decl *D, + const AttributeList &Attr, + Expr *&Cond, StringRef &Msg) { + Cond = Attr.getArgAsExpr(0); if (!Cond->isTypeDependent()) { ExprResult Converted = S.PerformContextuallyConvertToBool(Cond); if (Converted.isInvalid()) - return; + return false; Cond = Converted.get(); } - StringRef Msg; if (!S.checkStringLiteralArgumentAttr(Attr, 1, Msg)) - return; + return false; + + if (Msg.empty()) + Msg = "<no message provided>"; 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); + S.Diag(Attr.getLoc(), diag::err_attr_cond_never_constant_expr) + << Attr.getName(); for (const PartialDiagnosticAt &PDiag : Diags) S.Diag(PDiag.first, PDiag.second); + return false; + } + return true; +} + +static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) { + S.Diag(Attr.getLoc(), diag::ext_clang_enable_if); + + Expr *Cond; + StringRef Msg; + if (checkFunctionConditionAttr(S, D, Attr, Cond, Msg)) + D->addAttr(::new (S.Context) + EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg, + Attr.getAttributeSpellingListIndex())); +} + +namespace { +/// Determines if a given Expr references any of the given function's +/// ParmVarDecls, or the function's implicit `this` parameter (if applicable). +class ArgumentDependenceChecker + : public RecursiveASTVisitor<ArgumentDependenceChecker> { +#ifndef NDEBUG + const CXXRecordDecl *ClassType; +#endif + llvm::SmallPtrSet<const ParmVarDecl *, 16> Parms; + bool Result; + +public: + ArgumentDependenceChecker(const FunctionDecl *FD) { +#ifndef NDEBUG + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) + ClassType = MD->getParent(); + else + ClassType = nullptr; +#endif + Parms.insert(FD->param_begin(), FD->param_end()); + } + + bool referencesArgs(Expr *E) { + Result = false; + TraverseStmt(E); + return Result; + } + + bool VisitCXXThisExpr(CXXThisExpr *E) { + assert(E->getType()->getPointeeCXXRecordDecl() == ClassType && + "`this` doesn't refer to the enclosing class?"); + Result = true; + return false; + } + + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + if (const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) + if (Parms.count(PVD)) { + Result = true; + return false; + } + return true; + } +}; +} + +static void handleDiagnoseIfAttr(Sema &S, Decl *D, const AttributeList &Attr) { + S.Diag(Attr.getLoc(), diag::ext_clang_diagnose_if); + + Expr *Cond; + StringRef Msg; + if (!checkFunctionConditionAttr(S, D, Attr, Cond, Msg)) + return; + + StringRef DiagTypeStr; + if (!S.checkStringLiteralArgumentAttr(Attr, 2, DiagTypeStr)) + return; + + DiagnoseIfAttr::DiagnosticType DiagType; + if (!DiagnoseIfAttr::ConvertStrToDiagnosticType(DiagTypeStr, DiagType)) { + S.Diag(Attr.getArgAsExpr(2)->getLocStart(), + diag::err_diagnose_if_invalid_diagnostic_type); return; } - D->addAttr(::new (S.Context) - EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg, - Attr.getAttributeSpellingListIndex())); + auto *FD = cast<FunctionDecl>(D); + bool ArgDependent = ArgumentDependenceChecker(FD).referencesArgs(Cond); + D->addAttr(::new (S.Context) DiagnoseIfAttr( + Attr.getRange(), S.Context, Cond, Msg, DiagType, ArgDependent, FD, + Attr.getAttributeSpellingListIndex())); } static void handlePassObjectSizeAttr(Sema &S, Decl *D, @@ -5682,6 +5766,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_EnableIf: handleEnableIfAttr(S, D, Attr); break; + case AttributeList::AT_DiagnoseIf: + handleDiagnoseIfAttr(S, D, Attr); + break; case AttributeList::AT_ExtVectorType: handleExtVectorTypeAttr(S, scope, D, Attr); break; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 65e8236952f..d5378b87e0c 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -342,6 +342,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, } // See if this is a deleted function. + SmallVector<DiagnoseIfAttr *, 4> DiagnoseIfWarnings; if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { if (FD->isDeleted()) { auto *Ctor = dyn_cast<CXXConstructorDecl>(FD); @@ -363,6 +364,12 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, if (getLangOpts().CUDA && !CheckCUDACall(Loc, FD)) return true; + + if (const DiagnoseIfAttr *A = + checkArgIndependentDiagnoseIf(FD, DiagnoseIfWarnings)) { + emitDiagnoseIfDiagnostic(Loc, A); + return true; + } } // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions @@ -377,6 +384,10 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, Diag(D->getLocation(), diag::note_entity_declared_at) << D; return true; } + + for (const auto *W : DiagnoseIfWarnings) + emitDiagnoseIfDiagnostic(Loc, W); + DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass, ObjCPropertyAccess); @@ -5154,12 +5165,40 @@ static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context, return OverloadDecl; } -static bool isNumberOfArgsValidForCall(Sema &S, const FunctionDecl *Callee, - std::size_t NumArgs) { - if (S.TooManyArguments(Callee->getNumParams(), NumArgs, - /*PartialOverloading=*/false)) - return Callee->isVariadic(); - return Callee->getMinRequiredArguments() <= NumArgs; +static void checkDirectCallValidity(Sema &S, const Expr *Fn, + FunctionDecl *Callee, + MultiExprArg ArgExprs) { + // `Callee` (when called with ArgExprs) may be ill-formed. enable_if (and + // similar attributes) really don't like it when functions are called with an + // invalid number of args. + if (S.TooManyArguments(Callee->getNumParams(), ArgExprs.size(), + /*PartialOverloading=*/false) && + !Callee->isVariadic()) + return; + if (Callee->getMinRequiredArguments() > ArgExprs.size()) + return; + + if (const EnableIfAttr *Attr = S.CheckEnableIf(Callee, ArgExprs, true)) { + S.Diag(Fn->getLocStart(), + isa<CXXMethodDecl>(Callee) + ? diag::err_ovl_no_viable_member_function_in_call + : diag::err_ovl_no_viable_function_in_call) + << Callee << Callee->getSourceRange(); + S.Diag(Callee->getLocation(), + diag::note_ovl_candidate_disabled_by_function_cond_attr) + << Attr->getCond()->getSourceRange() << Attr->getMessage(); + return; + } + + SmallVector<DiagnoseIfAttr *, 4> Nonfatal; + if (const DiagnoseIfAttr *Attr = S.checkArgDependentDiagnoseIf( + Callee, ArgExprs, Nonfatal, /*MissingImplicitThis=*/true)) { + S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), Attr); + return; + } + + for (const auto *W : Nonfatal) + S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), W); } /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments. @@ -5294,26 +5333,8 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, if (getLangOpts().OpenCL && checkOpenCLDisabledDecl(*FD, *Fn)) return ExprError(); - - // CheckEnableIf assumes that the we're passing in a sane number of args for - // FD, but that doesn't always hold true here. This is because, in some - // cases, we'll emit a diag about an ill-formed function call, but then - // we'll continue on as if the function call wasn't ill-formed. So, if the - // number of args looks incorrect, don't do enable_if checks; we should've - // already emitted an error about the bad call. - if (FD->hasAttr<EnableIfAttr>() && - isNumberOfArgsValidForCall(*this, FD, ArgExprs.size())) { - 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(); - } - } + + checkDirectCallValidity(*this, Fn, FD, ArgExprs); } return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc, diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 38a7b8c127c..883e2ae264e 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -2960,6 +2960,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD, if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) { if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) AddMethodCandidate(M, Cand, RD, ThisTy, Classification, + /*ThisArg=*/nullptr, llvm::makeArrayRef(&Arg, NumArgs), OCS, true); else if (CtorInfo) AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl, @@ -2972,7 +2973,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD, if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) AddMethodTemplateCandidate( Tmpl, Cand, RD, nullptr, ThisTy, Classification, - llvm::makeArrayRef(&Arg, NumArgs), OCS, true); + /*ThisArg=*/nullptr, llvm::makeArrayRef(&Arg, NumArgs), OCS, true); else if (CtorInfo) AddTemplateOverloadCandidate( CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 15503036532..c0321d36a7c 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -29,6 +29,7 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" @@ -830,12 +831,20 @@ void OverloadCandidateSet::destroyCandidates() { void OverloadCandidateSet::clear() { destroyCandidates(); - ConversionSequenceAllocator.Reset(); - NumInlineSequences = 0; + // DiagnoseIfAttrs are just pointers, so we don't need to destroy them. + SlabAllocator.Reset(); + NumInlineBytesUsed = 0; Candidates.clear(); Functions.clear(); } +DiagnoseIfAttr ** +OverloadCandidateSet::addDiagnoseIfComplaints(ArrayRef<DiagnoseIfAttr *> CA) { + auto *DIA = slabAllocate<DiagnoseIfAttr *>(CA.size()); + std::uninitialized_copy(CA.begin(), CA.end(), DIA); + return DIA; +} + namespace { class UnbridgedCastsSet { struct Entry { @@ -5814,6 +5823,28 @@ static bool IsAcceptableNonMemberOperatorCandidate(ASTContext &Context, return false; } +static void initDiagnoseIfComplaint(Sema &S, OverloadCandidateSet &CandidateSet, + OverloadCandidate &Candidate, + FunctionDecl *Function, + ArrayRef<Expr *> Args, + bool MissingImplicitThis = false, + Expr *ExplicitThis = nullptr) { + SmallVector<DiagnoseIfAttr *, 8> Results; + if (DiagnoseIfAttr *DIA = S.checkArgDependentDiagnoseIf( + Function, Args, Results, MissingImplicitThis, ExplicitThis)) { + Results.clear(); + Results.push_back(DIA); + } + + Candidate.NumTriggeredDiagnoseIfs = Results.size(); + if (Results.empty()) + Candidate.DiagnoseIfInfo = nullptr; + else if (Results.size() == 1) + Candidate.DiagnoseIfInfo = Results[0]; + else + Candidate.DiagnoseIfInfo = CandidateSet.addDiagnoseIfComplaints(Results); +} + /// AddOverloadCandidate - Adds the given function to the set of /// candidate functions, using the given function call arguments. If /// @p SuppressUserConversions, then don't allow user-defined @@ -5847,8 +5878,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, // is irrelevant. AddMethodCandidate(Method, FoundDecl, Method->getParent(), QualType(), Expr::Classification::makeSimpleLValue(), - Args, CandidateSet, SuppressUserConversions, - PartialOverloading); + /*ThisArg=*/nullptr, Args, CandidateSet, + SuppressUserConversions, PartialOverloading); return; } // We treat a constructor like a non-member function, since its object @@ -6008,6 +6039,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, Candidate.FailureKind = ovl_fail_ext_disabled; return; } + + initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Function, Args); } ObjCMethodDecl * @@ -6120,66 +6153,87 @@ getOrderedEnableIfAttrs(const FunctionDecl *Function) { return Result; } -EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args, - bool MissingImplicitThis) { - auto EnableIfAttrs = getOrderedEnableIfAttrs(Function); - if (EnableIfAttrs.empty()) - return nullptr; - - SFINAETrap Trap(*this); - SmallVector<Expr *, 16> ConvertedArgs; - bool InitializationFailed = false; +static bool +convertArgsForAvailabilityChecks(Sema &S, FunctionDecl *Function, Expr *ThisArg, + ArrayRef<Expr *> Args, Sema::SFINAETrap &Trap, + bool MissingImplicitThis, Expr *&ConvertedThis, + SmallVectorImpl<Expr *> &ConvertedArgs) { + if (ThisArg) { + CXXMethodDecl *Method = cast<CXXMethodDecl>(Function); + assert(!isa<CXXConstructorDecl>(Method) && + "Shouldn't have `this` for ctors!"); + assert(!Method->isStatic() && "Shouldn't have `this` for static methods!"); + ExprResult R = S.PerformObjectArgumentInitialization( + ThisArg, /*Qualifier=*/nullptr, Method, Method); + if (R.isInvalid()) + return false; + ConvertedThis = R.get(); + } else { + if (auto *MD = dyn_cast<CXXMethodDecl>(Function)) { + (void)MD; + assert((MissingImplicitThis || MD->isStatic() || + isa<CXXConstructorDecl>(MD)) && + "Expected `this` for non-ctor instance methods"); + } + ConvertedThis = nullptr; + } // Ignore any variadic arguments. Converting them is pointless, since the - // user can't refer to them in the enable_if condition. + // user can't refer to them in the function condition. unsigned ArgSizeNoVarargs = std::min(Function->param_size(), Args.size()); // Convert the arguments. for (unsigned I = 0; I != ArgSizeNoVarargs; ++I) { ExprResult R; - if (I == 0 && !MissingImplicitThis && isa<CXXMethodDecl>(Function) && - !cast<CXXMethodDecl>(Function)->isStatic() && - !isa<CXXConstructorDecl>(Function)) { - CXXMethodDecl *Method = cast<CXXMethodDecl>(Function); - R = PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr, - Method, Method); - } else { - R = PerformCopyInitialization(InitializedEntity::InitializeParameter( - Context, Function->getParamDecl(I)), + R = S.PerformCopyInitialization(InitializedEntity::InitializeParameter( + S.Context, Function->getParamDecl(I)), SourceLocation(), Args[I]); - } - if (R.isInvalid()) { - InitializationFailed = true; - break; - } + if (R.isInvalid()) + return false; ConvertedArgs.push_back(R.get()); } - if (InitializationFailed || Trap.hasErrorOccurred()) - return EnableIfAttrs[0]; + if (Trap.hasErrorOccurred()) + return false; // Push default arguments if needed. if (!Function->isVariadic() && Args.size() < Function->getNumParams()) { for (unsigned i = Args.size(), e = Function->getNumParams(); i != e; ++i) { ParmVarDecl *P = Function->getParamDecl(i); - ExprResult R = PerformCopyInitialization( - InitializedEntity::InitializeParameter(Context, + ExprResult R = S.PerformCopyInitialization( + InitializedEntity::InitializeParameter(S.Context, Function->getParamDecl(i)), SourceLocation(), P->hasUninstantiatedDefaultArg() ? P->getUninstantiatedDefaultArg() : P->getDefaultArg()); - if (R.isInvalid()) { - InitializationFailed = true; - break; - } + if (R.isInvalid()) + return false; ConvertedArgs.push_back(R.get()); } - if (InitializationFailed || Trap.hasErrorOccurred()) - return EnableIfAttrs[0]; + if (Trap.hasErrorOccurred()) + return false; } + return true; +} + +EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args, + bool MissingImplicitThis) { + SmallVector<EnableIfAttr *, 4> EnableIfAttrs = + getOrderedEnableIfAttrs(Function); + if (EnableIfAttrs.empty()) + return nullptr; + + SFINAETrap Trap(*this); + SmallVector<Expr *, 16> ConvertedArgs; + // FIXME: We should look into making enable_if late-parsed. + Expr *DiscardedThis; + if (!convertArgsForAvailabilityChecks( + *this, Function, /*ThisArg=*/nullptr, Args, Trap, + /*MissingImplicitThis=*/true, DiscardedThis, ConvertedArgs)) + return EnableIfAttrs[0]; for (auto *EIA : EnableIfAttrs) { APValue Result; @@ -6195,6 +6249,87 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args, return nullptr; } +static bool gatherDiagnoseIfAttrs(FunctionDecl *Function, bool ArgDependent, + SmallVectorImpl<DiagnoseIfAttr *> &Errors, + SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal) { + for (auto *DIA : Function->specific_attrs<DiagnoseIfAttr>()) + if (ArgDependent == DIA->getArgDependent()) { + if (DIA->isError()) + Errors.push_back(DIA); + else + Nonfatal.push_back(DIA); + } + + return !Errors.empty() || !Nonfatal.empty(); +} + +template <typename CheckFn> +static DiagnoseIfAttr * +checkDiagnoseIfAttrsWith(const SmallVectorImpl<DiagnoseIfAttr *> &Errors, + SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal, + CheckFn &&IsSuccessful) { + // Note that diagnose_if attributes are late-parsed, so they appear in the + // correct order (unlike enable_if attributes). + auto ErrAttr = llvm::find_if(Errors, IsSuccessful); + if (ErrAttr != Errors.end()) + return *ErrAttr; + + llvm::erase_if(Nonfatal, [&](DiagnoseIfAttr *A) { return !IsSuccessful(A); }); + return nullptr; +} + +DiagnoseIfAttr * +Sema::checkArgDependentDiagnoseIf(FunctionDecl *Function, ArrayRef<Expr *> Args, + SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal, + bool MissingImplicitThis, + Expr *ThisArg) { + SmallVector<DiagnoseIfAttr *, 4> Errors; + if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/true, Errors, Nonfatal)) + return nullptr; + + SFINAETrap Trap(*this); + SmallVector<Expr *, 16> ConvertedArgs; + Expr *ConvertedThis; + if (!convertArgsForAvailabilityChecks(*this, Function, ThisArg, Args, Trap, + MissingImplicitThis, ConvertedThis, + ConvertedArgs)) + return nullptr; + + return checkDiagnoseIfAttrsWith(Errors, Nonfatal, [&](DiagnoseIfAttr *DIA) { + APValue Result; + // It's sane to use the same ConvertedArgs for any redecl of this function, + // since EvaluateWithSubstitution only cares about the position of each + // argument in the arg list, not the ParmVarDecl* it maps to. + if (!DIA->getCond()->EvaluateWithSubstitution( + Result, Context, DIA->getParent(), ConvertedArgs, ConvertedThis)) + return false; + return Result.isInt() && Result.getInt().getBoolValue(); + }); +} + +DiagnoseIfAttr *Sema::checkArgIndependentDiagnoseIf( + FunctionDecl *Function, SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal) { + SmallVector<DiagnoseIfAttr *, 4> Errors; + if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/false, Errors, + Nonfatal)) + return nullptr; + + return checkDiagnoseIfAttrsWith(Errors, Nonfatal, [&](DiagnoseIfAttr *DIA) { + bool Result; + return DIA->getCond()->EvaluateAsBooleanCondition(Result, Context) && + Result; + }); +} + +void Sema::emitDiagnoseIfDiagnostic(SourceLocation Loc, + const DiagnoseIfAttr *DIA) { + auto Code = DIA->isError() ? diag::err_diagnose_if_succeeded + : diag::warn_diagnose_if_succeeded; + Diag(Loc, Code) << DIA->getMessage(); + Diag(DIA->getLocation(), diag::note_from_diagnose_if) + << DIA->getParent() << DIA->getCond()->getSourceRange(); +} + /// \brief Add all of the function declarations in the given function set to /// the overload candidate set. void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns, @@ -6210,7 +6345,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns, AddMethodCandidate(cast<CXXMethodDecl>(FD), F.getPair(), cast<CXXMethodDecl>(FD)->getParent(), Args[0]->getType(), Args[0]->Classify(Context), - Args.slice(1), CandidateSet, + Args[0], Args.slice(1), CandidateSet, SuppressUserConversions, PartialOverloading); else AddOverloadCandidate(FD, F.getPair(), Args, CandidateSet, @@ -6219,13 +6354,12 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns, FunctionTemplateDecl *FunTmpl = cast<FunctionTemplateDecl>(D); if (isa<CXXMethodDecl>(FunTmpl->getTemplatedDecl()) && !cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl())->isStatic()) - AddMethodTemplateCandidate(FunTmpl, F.getPair(), - cast<CXXRecordDecl>(FunTmpl->getDeclContext()), - ExplicitTemplateArgs, - Args[0]->getType(), - Args[0]->Classify(Context), Args.slice(1), - CandidateSet, SuppressUserConversions, - PartialOverloading); + AddMethodTemplateCandidate( + FunTmpl, F.getPair(), + cast<CXXRecordDecl>(FunTmpl->getDeclContext()), + ExplicitTemplateArgs, Args[0]->getType(), + Args[0]->Classify(Context), Args[0], Args.slice(1), CandidateSet, + SuppressUserConversions, PartialOverloading); else AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs, Args, @@ -6240,6 +6374,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns, void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, Expr::Classification ObjectClassification, + Expr *ThisArg, ArrayRef<Expr *> Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions) { @@ -6255,12 +6390,12 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, AddMethodTemplateCandidate(TD, FoundDecl, ActingContext, /*ExplicitArgs*/ nullptr, ObjectType, ObjectClassification, - Args, CandidateSet, + ThisArg, Args, CandidateSet, SuppressUserConversions); } else { AddMethodCandidate(cast<CXXMethodDecl>(Decl), FoundDecl, ActingContext, ObjectType, ObjectClassification, - Args, + ThisArg, Args, CandidateSet, SuppressUserConversions); } } @@ -6276,7 +6411,7 @@ void Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, QualType ObjectType, Expr::Classification ObjectClassification, - ArrayRef<Expr *> Args, + Expr *ThisArg, ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, bool PartialOverloading) { @@ -6393,6 +6528,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, Candidate.DeductionFailure.Data = FailedAttr; return; } + + initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Method, Args, + /*MissingImplicitThis=*/!ThisArg, ThisArg); } /// \brief Add a C++ member function template as a candidate to the candidate @@ -6405,6 +6543,7 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl, TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType, Expr::Classification ObjectClassification, + Expr *ThisArg, ArrayRef<Expr *> Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions, @@ -6445,8 +6584,9 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl, assert(isa<CXXMethodDecl>(Specialization) && "Specialization is not a member function?"); AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl, - ActingContext, ObjectType, ObjectClassification, Args, - CandidateSet, SuppressUserConversions, PartialOverloading); + ActingContext, ObjectType, ObjectClassification, + /*ThisArg=*/ThisArg, Args, CandidateSet, + SuppressUserConversions, PartialOverloading); } /// \brief Add a C++ function template specialization as a candidate @@ -6702,6 +6842,8 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, Candidate.DeductionFailure.Data = FailedAttr; return; } + + initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None, false, From); } /// \brief Adds a conversion function template specialization @@ -6854,6 +6996,8 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, Candidate.DeductionFailure.Data = FailedAttr; return; } + + initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None); } /// \brief Add overload candidates for overloaded operators that are @@ -6902,10 +7046,8 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op, Oper != OperEnd; ++Oper) AddMethodCandidate(Oper.getPair(), Args[0]->getType(), - Args[0]->Classify(Context), - Args.slice(1), - CandidateSet, - /* SuppressUserConversions = */ false); + Args[0]->Classify(Context), Args[0], Args.slice(1), + CandidateSet, /*SuppressUserConversions=*/false); } } @@ -8936,6 +9078,17 @@ void Sema::diagnoseEquivalentInternalLinkageDeclarations( } } +static bool isCandidateUnavailableDueToDiagnoseIf(const OverloadCandidate &OC) { + ArrayRef<DiagnoseIfAttr *> Info = OC.getDiagnoseIfInfo(); + if (!Info.empty() && Info[0]->isError()) + return true; + + assert(llvm::all_of(Info, + [](const DiagnoseIfAttr *A) { return !A->isError(); }) && + "DiagnoseIf info shouldn't have mixed warnings and errors."); + return false; +} + /// \brief Computes the best viable function (C++ 13.3.3) /// within an overload candidate set. /// @@ -9014,13 +9167,19 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc, // Best is the best viable function. if (Best->Function && (Best->Function->isDeleted() || - S.isFunctionConsideredUnavailable(Best->Function))) + S.isFunctionConsideredUnavailable(Best->Function) || + isCandidateUnavailableDueToDiagnoseIf(*Best))) return OR_Deleted; if (!EquivalentCands.empty()) S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function, EquivalentCands); + for (const auto *W : Best->getDiagnoseIfInfo()) { + assert(W->isWarning() && "Errors should've been caught earlier!"); + S.emitDiagnoseIfDiagnostic(Loc, W); + } + return OR_Success; } @@ -9861,7 +10020,7 @@ static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) { EnableIfAttr *Attr = static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data); S.Diag(Callee->getLocation(), - diag::note_ovl_candidate_disabled_by_enable_if_attr) + diag::note_ovl_candidate_disabled_by_function_cond_attr) << Attr->getCond()->getSourceRange() << Attr->getMessage(); } @@ -9891,21 +10050,28 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, FunctionDecl *Fn = Cand->Function; // Note deleted candidates, but only if they're viable. - if (Cand->Viable && (Fn->isDeleted() || - S.isFunctionConsideredUnavailable(Fn))) { - std::string FnDesc; - OverloadCandidateKind FnKind = + if (Cand->Viable) { + if (Fn->isDeleted() || S.isFunctionConsideredUnavailable(Fn)) { + std::string FnDesc; + OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc); - S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted) - << FnKind << FnDesc - << (Fn->isDeleted() ? (Fn->isDeletedAsWritten() ? 1 : 2) : 0); - MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl); - return; - } + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted) + << FnKind << FnDesc + << (Fn->isDeleted() ? (Fn->isDeletedAsWritten() ? 1 : 2) : 0); + MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl); + return; + } + if (isCandidateUnavailableDueToDiagnoseIf(*Cand)) { + auto *A = Cand->DiagnoseIfInfo.get<DiagnoseIfAttr *>(); + assert(A->isError() && "Non-error diagnose_if disables a candidate?"); + S.Diag(Cand->Function->getLocation(), + diag::note_ovl_candidate_disabled_by_function_cond_attr) + << A->getCond()->getSourceRange() << A->getMessage(); + return; + } - // We don't really have anything else to say about viable candidates. - if (Cand->Viable) { + // We don't really have anything else to say about viable candidates. S.NoteOverloadCandidate(Cand->FoundDecl, Fn); return; } @@ -12460,6 +12626,16 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, TemplateArgs = &TemplateArgsBuffer; } + // Poor-programmer's Lazy<Expr *>; isImplicitAccess requires stripping + // parens/casts, which would be nice to avoid potentially doing multiple + // times. + llvm::Optional<Expr *> UnresolvedBase; + auto GetUnresolvedBase = [&] { + if (!UnresolvedBase.hasValue()) + UnresolvedBase = + UnresExpr->isImplicitAccess() ? nullptr : UnresExpr->getBase(); + return *UnresolvedBase; + }; for (UnresolvedMemberExpr::decls_iterator I = UnresExpr->decls_begin(), E = UnresExpr->decls_end(); I != E; ++I) { @@ -12480,14 +12656,15 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, continue; AddMethodCandidate(Method, I.getPair(), ActingDC, ObjectType, - ObjectClassification, Args, CandidateSet, + ObjectClassification, + /*ThisArg=*/GetUnresolvedBase(), Args, CandidateSet, /*SuppressUserConversions=*/false); } else { - AddMethodTemplateCandidate(cast<FunctionTemplateDecl>(Func), - I.getPair(), ActingDC, TemplateArgs, - ObjectType, ObjectClassification, - Args, CandidateSet, - /*SuppressUsedConversions=*/false); + AddMethodTemplateCandidate( + cast<FunctionTemplateDecl>(Func), I.getPair(), ActingDC, + TemplateArgs, ObjectType, ObjectClassification, + /*ThisArg=*/GetUnresolvedBase(), Args, CandidateSet, + /*SuppressUsedConversions=*/false); } } @@ -12600,10 +12777,20 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, diag::err_ovl_no_viable_member_function_in_call) << Method << Method->getSourceRange(); Diag(Method->getLocation(), - diag::note_ovl_candidate_disabled_by_enable_if_attr) + diag::note_ovl_candidate_disabled_by_function_cond_attr) << Attr->getCond()->getSourceRange() << Attr->getMessage(); return ExprError(); } + + SmallVector<DiagnoseIfAttr *, 4> Nonfatal; + if (const DiagnoseIfAttr *Attr = checkArgDependentDiagnoseIf( + Method, Args, Nonfatal, false, MemE->getBase())) { + emitDiagnoseIfDiagnostic(MemE->getMemberLoc(), Attr); + return ExprError(); + } + + for (const auto *Attr : Nonfatal) + emitDiagnoseIfDiagnostic(MemE->getMemberLoc(), Attr); } if ((isa<CXXConstructorDecl>(CurContext) || @@ -12683,7 +12870,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, Oper != OperEnd; ++Oper) { AddMethodCandidate(Oper.getPair(), Object.get()->getType(), Object.get()->Classify(Context), - Args, CandidateSet, + Object.get(), Args, CandidateSet, /*SuppressUserConversions=*/ false); } @@ -12959,7 +13146,8 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc, for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end(); Oper != OperEnd; ++Oper) { AddMethodCandidate(Oper.getPair(), Base->getType(), Base->Classify(Context), - None, CandidateSet, /*SuppressUserConversions=*/false); + Base, None, CandidateSet, + /*SuppressUserConversions=*/false); } bool HadMultipleCandidates = (CandidateSet.size() > 1); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index ffe35662303..d2a5e5cb531 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -168,40 +168,59 @@ static void instantiateDependentAlignValueAttr( Aligned->getSpellingListIndex()); } -static void instantiateDependentEnableIfAttr( +static Expr *instantiateDependentFunctionAttrCondition( Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, - const EnableIfAttr *A, const Decl *Tmpl, Decl *New) { + const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) { Expr *Cond = nullptr; { - Sema::ContextRAII SwitchContext(S, cast<FunctionDecl>(New)); + Sema::ContextRAII SwitchContext(S, New); EnterExpressionEvaluationContext Unevaluated(S, Sema::ConstantEvaluated); - ExprResult Result = S.SubstExpr(A->getCond(), TemplateArgs); + ExprResult Result = S.SubstExpr(OldCond, TemplateArgs); if (Result.isInvalid()) - return; + return nullptr; Cond = Result.getAs<Expr>(); } if (!Cond->isTypeDependent()) { ExprResult Converted = S.PerformContextuallyConvertToBool(Cond); if (Converted.isInvalid()) - return; + return nullptr; Cond = Converted.get(); } SmallVector<PartialDiagnosticAt, 8> Diags; - if (A->getCond()->isValueDependent() && !Cond->isValueDependent() && - !Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(New), - 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; + if (OldCond->isValueDependent() && !Cond->isValueDependent() && + !Expr::isPotentialConstantExprUnevaluated(Cond, New, Diags)) { + S.Diag(A->getLocation(), diag::err_attr_cond_never_constant_expr) << A; + for (const auto &P : Diags) + S.Diag(P.first, P.second); + return nullptr; } + return Cond; +} - EnableIfAttr *EIA = new (S.getASTContext()) - EnableIfAttr(A->getLocation(), S.getASTContext(), Cond, - A->getMessage(), - A->getSpellingListIndex()); - New->addAttr(EIA); +static void instantiateDependentEnableIfAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const EnableIfAttr *EIA, const Decl *Tmpl, FunctionDecl *New) { + Expr *Cond = instantiateDependentFunctionAttrCondition( + S, TemplateArgs, EIA, EIA->getCond(), Tmpl, New); + + if (Cond) + New->addAttr(new (S.getASTContext()) EnableIfAttr( + EIA->getLocation(), S.getASTContext(), Cond, EIA->getMessage(), + EIA->getSpellingListIndex())); +} + +static void instantiateDependentDiagnoseIfAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const DiagnoseIfAttr *DIA, const Decl *Tmpl, FunctionDecl *New) { + Expr *Cond = instantiateDependentFunctionAttrCondition( + S, TemplateArgs, DIA, DIA->getCond(), Tmpl, New); + + if (Cond) + New->addAttr(new (S.getASTContext()) DiagnoseIfAttr( + DIA->getLocation(), S.getASTContext(), Cond, DIA->getMessage(), + DIA->getDiagnosticType(), DIA->getArgDependent(), New, + DIA->getSpellingListIndex())); } // Constructs and adds to New a new instance of CUDALaunchBoundsAttr using @@ -335,7 +354,13 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, if (const auto *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr)) { instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl, - New); + cast<FunctionDecl>(New)); + continue; + } + + if (const auto *DiagnoseIf = dyn_cast<DiagnoseIfAttr>(TmplAttr)) { + instantiateDependentDiagnoseIfAttr(*this, TemplateArgs, DiagnoseIf, Tmpl, + cast<FunctionDecl>(New)); continue; } |