diff options
Diffstat (limited to 'clang/lib/Sema/SemaConcept.cpp')
-rw-r--r-- | clang/lib/Sema/SemaConcept.cpp | 410 |
1 files changed, 199 insertions, 211 deletions
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index e6117f9..5e1cd62 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -176,22 +176,167 @@ struct SatisfactionStackRAII { }; } // namespace -template <typename ConstraintEvaluator> -static ExprResult -calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, - ConstraintSatisfaction &Satisfaction, - const ConstraintEvaluator &Evaluator); - -template <typename ConstraintEvaluator> -static ExprResult -calculateConstraintSatisfaction(Sema &S, const Expr *LHS, - OverloadedOperatorKind Op, const Expr *RHS, - ConstraintSatisfaction &Satisfaction, - const ConstraintEvaluator &Evaluator) { +static bool +DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID, + const NamedDecl *Templ, const Expr *E, + const MultiLevelTemplateArgumentList &MLTAL) { + E->Profile(ID, S.Context, /*Canonical=*/true); + for (const auto &List : MLTAL) + for (const auto &TemplateArg : List.Args) + TemplateArg.Profile(ID, S.Context); + + // Note that we have to do this with our own collection, because there are + // times where a constraint-expression check can cause us to need to evaluate + // other constriants that are unrelated, such as when evaluating a recovery + // expression, or when trying to determine the constexpr-ness of special + // members. Otherwise we could just use the + // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function. + if (S.SatisfactionStackContains(Templ, ID)) { + S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self) + << const_cast<Expr *>(E) << E->getSourceRange(); + return true; + } + + return false; +} + +static ExprResult EvaluateAtomicConstraint( + Sema &S, const Expr *AtomicExpr, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction) { + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated, + Sema::ReuseLambdaContextDecl); + + // Atomic constraint - substitute arguments and check satisfaction. + ExprResult SubstitutedExpression; + { + TemplateDeductionInfo Info(TemplateNameLoc); + Sema::InstantiatingTemplate Inst( + S, AtomicExpr->getBeginLoc(), + Sema::InstantiatingTemplate::ConstraintSubstitution{}, + // FIXME: improve const-correctness of InstantiatingTemplate + const_cast<NamedDecl *>(Template), Info, AtomicExpr->getSourceRange()); + if (Inst.isInvalid()) + return ExprError(); + + llvm::FoldingSetNodeID ID; + if (Template && + DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) { + Satisfaction.IsSatisfied = false; + Satisfaction.ContainsErrors = true; + return ExprEmpty(); + } + + SatisfactionStackRAII StackRAII(S, Template, ID); + + // We do not want error diagnostics escaping here. + Sema::SFINAETrap Trap(S); + SubstitutedExpression = + S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL); + + if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) { + // C++2a [temp.constr.atomic]p1 + // ...If substitution results in an invalid type or expression, the + // constraint is not satisfied. + if (!Trap.hasErrorOccurred()) + // A non-SFINAE error has occurred as a result of this + // substitution. + return ExprError(); + + PartialDiagnosticAt SubstDiag{SourceLocation(), + PartialDiagnostic::NullDiagnostic()}; + Info.takeSFINAEDiagnostic(SubstDiag); + // FIXME: Concepts: This is an unfortunate consequence of there + // being no serialization code for PartialDiagnostics and the fact + // that serializing them would likely take a lot more storage than + // just storing them as strings. We would still like, in the + // future, to serialize the proper PartialDiagnostic as serializing + // it as a string defeats the purpose of the diagnostic mechanism. + SmallString<128> DiagString; + DiagString = ": "; + SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString); + unsigned MessageSize = DiagString.size(); + char *Mem = new (S.Context) char[MessageSize]; + memcpy(Mem, DiagString.c_str(), MessageSize); + Satisfaction.Details.emplace_back( + new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ + SubstDiag.first, StringRef(Mem, MessageSize)}); + Satisfaction.IsSatisfied = false; + return ExprEmpty(); + } + } + + if (!S.CheckConstraintExpression(SubstitutedExpression.get())) + return ExprError(); + + // [temp.constr.atomic]p3: To determine if an atomic constraint is + // satisfied, the parameter mapping and template arguments are first + // substituted into its expression. If substitution results in an + // invalid type or expression, the constraint is not satisfied. + // Otherwise, the lvalue-to-rvalue conversion is performed if necessary, + // and E shall be a constant expression of type bool. + // + // Perform the L to R Value conversion if necessary. We do so for all + // non-PRValue categories, else we fail to extend the lifetime of + // temporaries, and that fails the constant expression check. + if (!SubstitutedExpression.get()->isPRValue()) + SubstitutedExpression = ImplicitCastExpr::Create( + S.Context, SubstitutedExpression.get()->getType(), CK_LValueToRValue, + SubstitutedExpression.get(), + /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride()); + + return SubstitutedExpression; +} + +std::optional<unsigned> static EvaluateFoldExpandedConstraintSize( + Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction) { + + // We should ignore errors in the presence of packs of different size. + Sema::SFINAETrap Trap(S); + + Expr *Pattern = FE->getPattern(); + + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + S.collectUnexpandedParameterPacks(Pattern, Unexpanded); + assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); + bool Expand = true; + bool RetainExpansion = false; + std::optional<unsigned> OrigNumExpansions = FE->getNumExpansions(), + NumExpansions = OrigNumExpansions; + if (S.CheckParameterPacksForExpansion( + FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL, + Expand, RetainExpansion, NumExpansions) || + !Expand || RetainExpansion) + return std::nullopt; + + if (NumExpansions && S.getLangOpts().BracketDepth < NumExpansions) { + S.Diag(FE->getEllipsisLoc(), + clang::diag::err_fold_expression_limit_exceeded) + << *NumExpansions << S.getLangOpts().BracketDepth + << FE->getSourceRange(); + S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth); + return std::nullopt; + } + return NumExpansions; +} + +static ExprResult calculateConstraintSatisfaction( + Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction); + +static ExprResult calculateConstraintSatisfaction( + Sema &S, const Expr *LHS, OverloadedOperatorKind Op, const Expr *RHS, + const NamedDecl *Template, SourceLocation TemplateNameLoc, + const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction) { size_t EffectiveDetailEndIndex = Satisfaction.Details.size(); - ExprResult LHSRes = - calculateConstraintSatisfaction(S, LHS, Satisfaction, Evaluator); + ExprResult LHSRes = calculateConstraintSatisfaction( + S, LHS, Template, TemplateNameLoc, MLTAL, Satisfaction); if (LHSRes.isInvalid()) return ExprError(); @@ -218,8 +363,8 @@ calculateConstraintSatisfaction(Sema &S, const Expr *LHS, // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp. return LHSRes; - ExprResult RHSRes = - calculateConstraintSatisfaction(S, RHS, Satisfaction, Evaluator); + ExprResult RHSRes = calculateConstraintSatisfaction( + S, RHS, Template, TemplateNameLoc, MLTAL, Satisfaction); if (RHSRes.isInvalid()) return ExprError(); @@ -247,18 +392,17 @@ calculateConstraintSatisfaction(Sema &S, const Expr *LHS, LHS->getBeginLoc(), FPOptionsOverride{}); } -template <typename ConstraintEvaluator> -static ExprResult -calculateConstraintSatisfaction(Sema &S, const CXXFoldExpr *FE, - ConstraintSatisfaction &Satisfaction, - const ConstraintEvaluator &Evaluator) { +static ExprResult calculateConstraintSatisfaction( + Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction) { bool Conjunction = FE->getOperator() == BinaryOperatorKind::BO_LAnd; size_t EffectiveDetailEndIndex = Satisfaction.Details.size(); ExprResult Out; if (FE->isLeftFold() && FE->getInit()) { - Out = calculateConstraintSatisfaction(S, FE->getInit(), Satisfaction, - Evaluator); + Out = calculateConstraintSatisfaction(S, FE->getInit(), Template, + TemplateNameLoc, MLTAL, Satisfaction); if (Out.isInvalid()) return ExprError(); @@ -269,14 +413,14 @@ calculateConstraintSatisfaction(Sema &S, const CXXFoldExpr *FE, if (Conjunction != Satisfaction.IsSatisfied) return Out; } - std::optional<unsigned> NumExpansions = - Evaluator.EvaluateFoldExpandedConstraintSize(FE); + std::optional<unsigned> NumExpansions = EvaluateFoldExpandedConstraintSize( + S, FE, Template, TemplateNameLoc, MLTAL, Satisfaction); if (!NumExpansions) return ExprError(); for (unsigned I = 0; I < *NumExpansions; I++) { Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(S, I); - ExprResult Res = calculateConstraintSatisfaction(S, FE->getPattern(), - Satisfaction, Evaluator); + ExprResult Res = calculateConstraintSatisfaction( + S, FE->getPattern(), Template, TemplateNameLoc, MLTAL, Satisfaction); if (Res.isInvalid()) return ExprError(); bool IsRHSSatisfied = Satisfaction.IsSatisfied; @@ -298,8 +442,8 @@ calculateConstraintSatisfaction(Sema &S, const CXXFoldExpr *FE, } if (FE->isRightFold() && FE->getInit()) { - ExprResult Res = calculateConstraintSatisfaction(S, FE->getInit(), - Satisfaction, Evaluator); + ExprResult Res = calculateConstraintSatisfaction( + S, FE->getInit(), Template, TemplateNameLoc, MLTAL, Satisfaction); if (Out.isInvalid()) return ExprError(); @@ -319,34 +463,37 @@ calculateConstraintSatisfaction(Sema &S, const CXXFoldExpr *FE, return Out; } -template <typename ConstraintEvaluator> -static ExprResult -calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, - ConstraintSatisfaction &Satisfaction, - const ConstraintEvaluator &Evaluator) { +static ExprResult calculateConstraintSatisfaction( + Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction) { ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts(); if (LogicalBinOp BO = ConstraintExpr) return calculateConstraintSatisfaction( - S, BO.getLHS(), BO.getOp(), BO.getRHS(), Satisfaction, Evaluator); + S, BO.getLHS(), BO.getOp(), BO.getRHS(), Template, TemplateNameLoc, + MLTAL, Satisfaction); if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) { // These aren't evaluated, so we don't care about cleanups, so we can just // evaluate these as if the cleanups didn't exist. - return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction, - Evaluator); + return calculateConstraintSatisfaction( + S, C->getSubExpr(), Template, TemplateNameLoc, MLTAL, Satisfaction); } if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr); FE && S.getLangOpts().CPlusPlus26 && (FE->getOperator() == BinaryOperatorKind::BO_LAnd || FE->getOperator() == BinaryOperatorKind::BO_LOr)) { - return calculateConstraintSatisfaction(S, FE, Satisfaction, Evaluator); + return calculateConstraintSatisfaction(S, FE, Template, TemplateNameLoc, + MLTAL, Satisfaction); } + // FIXME: We should not treat ConceptSpecializationExpr as atomic constraints. + // An atomic constraint expression - ExprResult SubstitutedAtomicExpr = - Evaluator.EvaluateAtomicConstraint(ConstraintExpr); + ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint( + S, ConstraintExpr, Template, TemplateNameLoc, MLTAL, Satisfaction); if (SubstitutedAtomicExpr.isInvalid()) return ExprError(); @@ -405,165 +552,13 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, return SubstitutedAtomicExpr; } -static bool -DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID, - const NamedDecl *Templ, const Expr *E, - const MultiLevelTemplateArgumentList &MLTAL) { - E->Profile(ID, S.Context, /*Canonical=*/true); - for (const auto &List : MLTAL) - for (const auto &TemplateArg : List.Args) - TemplateArg.Profile(ID, S.Context); - - // Note that we have to do this with our own collection, because there are - // times where a constraint-expression check can cause us to need to evaluate - // other constriants that are unrelated, such as when evaluating a recovery - // expression, or when trying to determine the constexpr-ness of special - // members. Otherwise we could just use the - // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function. - if (S.SatisfactionStackContains(Templ, ID)) { - S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self) - << const_cast<Expr *>(E) << E->getSourceRange(); - return true; - } - - return false; -} - static ExprResult calculateConstraintSatisfaction( Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { - struct ConstraintEvaluator { - Sema &S; - const NamedDecl *Template; - SourceLocation TemplateNameLoc; - const MultiLevelTemplateArgumentList &MLTAL; - ConstraintSatisfaction &Satisfaction; - - ExprResult EvaluateAtomicConstraint(const Expr *AtomicExpr) const { - EnterExpressionEvaluationContext ConstantEvaluated( - S, Sema::ExpressionEvaluationContext::ConstantEvaluated, - Sema::ReuseLambdaContextDecl); - - // Atomic constraint - substitute arguments and check satisfaction. - ExprResult SubstitutedExpression; - { - TemplateDeductionInfo Info(TemplateNameLoc); - Sema::InstantiatingTemplate Inst( - S, AtomicExpr->getBeginLoc(), - Sema::InstantiatingTemplate::ConstraintSubstitution{}, - // FIXME: improve const-correctness of InstantiatingTemplate - const_cast<NamedDecl *>(Template), Info, - AtomicExpr->getSourceRange()); - if (Inst.isInvalid()) - return ExprError(); - - llvm::FoldingSetNodeID ID; - if (Template && - DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) { - Satisfaction.IsSatisfied = false; - Satisfaction.ContainsErrors = true; - return ExprEmpty(); - } - - SatisfactionStackRAII StackRAII(S, Template, ID); - - // We do not want error diagnostics escaping here. - Sema::SFINAETrap Trap(S); - SubstitutedExpression = - S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL); - - if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) { - // C++2a [temp.constr.atomic]p1 - // ...If substitution results in an invalid type or expression, the - // constraint is not satisfied. - if (!Trap.hasErrorOccurred()) - // A non-SFINAE error has occurred as a result of this - // substitution. - return ExprError(); - - PartialDiagnosticAt SubstDiag{SourceLocation(), - PartialDiagnostic::NullDiagnostic()}; - Info.takeSFINAEDiagnostic(SubstDiag); - // FIXME: Concepts: This is an unfortunate consequence of there - // being no serialization code for PartialDiagnostics and the fact - // that serializing them would likely take a lot more storage than - // just storing them as strings. We would still like, in the - // future, to serialize the proper PartialDiagnostic as serializing - // it as a string defeats the purpose of the diagnostic mechanism. - SmallString<128> DiagString; - DiagString = ": "; - SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString); - unsigned MessageSize = DiagString.size(); - char *Mem = new (S.Context) char[MessageSize]; - memcpy(Mem, DiagString.c_str(), MessageSize); - Satisfaction.Details.emplace_back( - new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ - SubstDiag.first, StringRef(Mem, MessageSize)}); - Satisfaction.IsSatisfied = false; - return ExprEmpty(); - } - } - - if (!S.CheckConstraintExpression(SubstitutedExpression.get())) - return ExprError(); - - // [temp.constr.atomic]p3: To determine if an atomic constraint is - // satisfied, the parameter mapping and template arguments are first - // substituted into its expression. If substitution results in an - // invalid type or expression, the constraint is not satisfied. - // Otherwise, the lvalue-to-rvalue conversion is performed if necessary, - // and E shall be a constant expression of type bool. - // - // Perform the L to R Value conversion if necessary. We do so for all - // non-PRValue categories, else we fail to extend the lifetime of - // temporaries, and that fails the constant expression check. - if (!SubstitutedExpression.get()->isPRValue()) - SubstitutedExpression = ImplicitCastExpr::Create( - S.Context, SubstitutedExpression.get()->getType(), - CK_LValueToRValue, SubstitutedExpression.get(), - /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride()); - - return SubstitutedExpression; - } - - std::optional<unsigned> - EvaluateFoldExpandedConstraintSize(const CXXFoldExpr *FE) const { - - // We should ignore errors in the presence of packs of different size. - Sema::SFINAETrap Trap(S); - - Expr *Pattern = FE->getPattern(); - - SmallVector<UnexpandedParameterPack, 2> Unexpanded; - S.collectUnexpandedParameterPacks(Pattern, Unexpanded); - assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); - bool Expand = true; - bool RetainExpansion = false; - std::optional<unsigned> OrigNumExpansions = FE->getNumExpansions(), - NumExpansions = OrigNumExpansions; - if (S.CheckParameterPacksForExpansion( - FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, - MLTAL, Expand, RetainExpansion, NumExpansions) || - !Expand || RetainExpansion) - return std::nullopt; - - if (NumExpansions && S.getLangOpts().BracketDepth < NumExpansions) { - S.Diag(FE->getEllipsisLoc(), - clang::diag::err_fold_expression_limit_exceeded) - << *NumExpansions << S.getLangOpts().BracketDepth - << FE->getSourceRange(); - S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth); - return std::nullopt; - } - return NumExpansions; - } - }; - - return calculateConstraintSatisfaction( - S, ConstraintExpr, Satisfaction, - ConstraintEvaluator{S, Template, TemplateNameLoc, MLTAL, Satisfaction}); + return calculateConstraintSatisfaction(S, ConstraintExpr, Template, + TemplateNameLoc, MLTAL, Satisfaction); } static bool CheckConstraintSatisfaction( @@ -688,23 +683,17 @@ bool Sema::CheckConstraintSatisfaction( return false; } -bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, - ConstraintSatisfaction &Satisfaction) { +bool Sema::CheckConstraintSatisfaction( + const ConceptSpecializationExpr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction) { - struct ConstraintEvaluator { - Sema &S; - ExprResult EvaluateAtomicConstraint(const Expr *AtomicExpr) const { - return S.PerformContextuallyConvertToBool(const_cast<Expr *>(AtomicExpr)); - } + MultiLevelTemplateArgumentList MLTAL(ConstraintExpr->getNamedConcept(), + ConstraintExpr->getTemplateArguments(), + true); - std::optional<unsigned> - EvaluateFoldExpandedConstraintSize(const CXXFoldExpr *FE) const { - return 0; - } - }; - - return calculateConstraintSatisfaction(*this, ConstraintExpr, Satisfaction, - ConstraintEvaluator{*this}) + return calculateConstraintSatisfaction( + *this, ConstraintExpr, ConstraintExpr->getNamedConcept(), + ConstraintExpr->getConceptNameLoc(), MLTAL, Satisfaction) .isInvalid(); } @@ -1183,8 +1172,7 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( LambdaScopeForCallOperatorInstantiationRAII LambdaScope( *this, const_cast<FunctionDecl *>(Decl), *MLTAL, Scope); - llvm::SmallVector<Expr *, 1> Converted; - return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, + return CheckConstraintSatisfaction(Template, TemplateAC, *MLTAL, PointOfInstantiation, Satisfaction); } |