aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaConcept.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema/SemaConcept.cpp')
-rw-r--r--clang/lib/Sema/SemaConcept.cpp410
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);
}