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.cpp2000
1 files changed, 1303 insertions, 697 deletions
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index dc6d232..8413090 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -12,9 +12,11 @@
#include "clang/Sema/SemaConcept.h"
#include "TreeTransform.h"
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprConcepts.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/OperatorPrecedence.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
@@ -27,7 +29,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/StringExtras.h"
-#include <optional>
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
using namespace sema;
@@ -85,7 +87,7 @@ public:
OK_Ordinary, Loc, FPOptionsOverride{});
}
};
-}
+} // namespace
bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
Token NextToken, bool *PossibleNonPrimary,
@@ -146,14 +148,14 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {
Diag(ConstraintExpression->getExprLoc(),
- diag::err_non_bool_atomic_constraint) << Type
- << ConstraintExpression->getSourceRange();
+ diag::err_non_bool_atomic_constraint)
+ << Type << ConstraintExpression->getSourceRange();
CheckForNonPrimary();
return false;
}
if (PossibleNonPrimary)
- *PossibleNonPrimary = false;
+ *PossibleNonPrimary = false;
return true;
}
@@ -164,52 +166,315 @@ struct SatisfactionStackRAII {
SatisfactionStackRAII(Sema &SemaRef, const NamedDecl *ND,
const llvm::FoldingSetNodeID &FSNID)
: SemaRef(SemaRef) {
- if (ND) {
+ if (ND) {
SemaRef.PushSatisfactionStackEntry(ND, FSNID);
Inserted = true;
- }
+ }
}
~SatisfactionStackRAII() {
- if (Inserted)
- SemaRef.PopSatisfactionStackEntry();
+ if (Inserted)
+ SemaRef.PopSatisfactionStackEntry();
}
};
} // namespace
-static bool
-DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
- const NamedDecl *Templ, const Expr *E,
- const MultiLevelTemplateArgumentList &MLTAL) {
+static bool DiagRecursiveConstraintEval(
+ Sema &S, llvm::FoldingSetNodeID &ID, const NamedDecl *Templ, const Expr *E,
+ const MultiLevelTemplateArgumentList *MLTAL = nullptr) {
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 (MLTAL) {
+ for (const auto &List : *MLTAL)
+ for (const auto &TemplateArg : List.Args)
+ S.Context.getCanonicalTemplateArgument(TemplateArg)
+ .Profile(ID, S.Context);
+ }
if (S.SatisfactionStackContains(Templ, ID)) {
S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self)
<< 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) {
+// Figure out the to-translation-unit depth for this function declaration for
+// the purpose of seeing if they differ by constraints. This isn't the same as
+// getTemplateDepth, because it includes already instantiated parents.
+static unsigned
+CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
+ bool SkipForSpecialization = false) {
+ MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+ ND, ND->getLexicalDeclContext(), /*Final=*/false,
+ /*Innermost=*/std::nullopt,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true, SkipForSpecialization);
+ return MLTAL.getNumLevels();
+}
+
+namespace {
+class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
+ unsigned TemplateDepth = 0;
+
+public:
+ using inherited = TreeTransform<AdjustConstraintDepth>;
+ AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
+ : inherited(SemaRef), TemplateDepth(TemplateDepth) {}
+
+ using inherited::TransformTemplateTypeParmType;
+ QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
+ TemplateTypeParmTypeLoc TL, bool) {
+ const TemplateTypeParmType *T = TL.getTypePtr();
+
+ TemplateTypeParmDecl *NewTTPDecl = nullptr;
+ if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
+ NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
+ TransformDecl(TL.getNameLoc(), OldTTPDecl));
+
+ QualType Result = getSema().Context.getTemplateTypeParmType(
+ T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
+ NewTTPDecl);
+ TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
+ NewTL.setNameLoc(TL.getNameLoc());
+ return Result;
+ }
+
+ bool AlreadyTransformed(QualType T) {
+ if (T.isNull())
+ return true;
+
+ if (T->isInstantiationDependentType() || T->isVariablyModifiedType() ||
+ T->containsUnexpandedParameterPack())
+ return false;
+ return true;
+ }
+};
+} // namespace
+
+namespace {
+
+// FIXME: Convert it to DynamicRecursiveASTVisitor
+class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
+ using inherited = RecursiveASTVisitor<HashParameterMapping>;
+ friend inherited;
+
+ Sema &SemaRef;
+ const MultiLevelTemplateArgumentList &TemplateArgs;
+ llvm::FoldingSetNodeID &ID;
+ llvm::SmallVector<TemplateArgument, 10> UsedTemplateArgs;
+
+ UnsignedOrNone OuterPackSubstIndex;
+
+ TemplateArgument getPackSubstitutedTemplateArgument(TemplateArgument Arg) {
+ assert(*SemaRef.ArgPackSubstIndex < Arg.pack_size());
+ Arg = Arg.pack_begin()[*SemaRef.ArgPackSubstIndex];
+ if (Arg.isPackExpansion())
+ Arg = Arg.getPackExpansionPattern();
+ return Arg;
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+
+public:
+ HashParameterMapping(Sema &SemaRef,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ llvm::FoldingSetNodeID &ID,
+ UnsignedOrNone OuterPackSubstIndex)
+ : SemaRef(SemaRef), TemplateArgs(TemplateArgs), ID(ID),
+ OuterPackSubstIndex(OuterPackSubstIndex) {}
+
+ bool VisitTemplateTypeParmType(TemplateTypeParmType *T) {
+ // A lambda expression can introduce template parameters that don't have
+ // corresponding template arguments yet.
+ if (T->getDepth() >= TemplateArgs.getNumLevels())
+ return true;
+
+ TemplateArgument Arg = TemplateArgs(T->getDepth(), T->getIndex());
+
+ if (T->isParameterPack() && SemaRef.ArgPackSubstIndex) {
+ assert(Arg.getKind() == TemplateArgument::Pack &&
+ "Missing argument pack");
+
+ Arg = getPackSubstitutedTemplateArgument(Arg);
+ }
+
+ UsedTemplateArgs.push_back(
+ SemaRef.Context.getCanonicalTemplateArgument(Arg));
+ return true;
+ }
+
+ bool VisitDeclRefExpr(DeclRefExpr *E) {
+ NamedDecl *D = E->getDecl();
+ NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D);
+ if (!NTTP)
+ return TraverseDecl(D);
+
+ TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
+ if (NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex) {
+ assert(Arg.getKind() == TemplateArgument::Pack &&
+ "Missing argument pack");
+ Arg = getPackSubstitutedTemplateArgument(Arg);
+ }
+
+ UsedTemplateArgs.push_back(
+ SemaRef.Context.getCanonicalTemplateArgument(Arg));
+ return true;
+ }
+
+ bool VisitTypedefType(TypedefType *TT) {
+ return inherited::TraverseType(TT->desugar());
+ }
+
+ bool TraverseDecl(Decl *D) {
+ if (auto *VD = dyn_cast<ValueDecl>(D))
+ return TraverseType(VD->getType());
+
+ return inherited::TraverseDecl(D);
+ }
+
+ bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
+ // We don't care about TypeLocs. So traverse Types instead.
+ return TraverseType(TL.getType(), TraverseQualifier);
+ }
+
+ bool TraverseTagType(const TagType *T, bool TraverseQualifier) {
+ // T's parent can be dependent while T doesn't have any template arguments.
+ // We should have already traversed its qualifier.
+ // FIXME: Add an assert to catch cases where we failed to profile the
+ // concept. assert(!T->isDependentType() && "We missed a case in profiling
+ // concepts!");
+ return true;
+ }
+
+ bool TraverseInjectedClassNameType(InjectedClassNameType *T,
+ bool TraverseQualifier) {
+ return TraverseTemplateArguments(T->getTemplateArgs(SemaRef.Context));
+ }
+
+ bool TraverseTemplateArgument(const TemplateArgument &Arg) {
+ if (!Arg.containsUnexpandedParameterPack() || Arg.isPackExpansion()) {
+ // Act as if we are fully expanding this pack, if it is a PackExpansion.
+ Sema::ArgPackSubstIndexRAII _1(SemaRef, std::nullopt);
+ llvm::SaveAndRestore<UnsignedOrNone> _2(OuterPackSubstIndex,
+ std::nullopt);
+ return inherited::TraverseTemplateArgument(Arg);
+ }
+
+ Sema::ArgPackSubstIndexRAII _1(SemaRef, OuterPackSubstIndex);
+ return inherited::TraverseTemplateArgument(Arg);
+ }
+
+ void VisitConstraint(const NormalizedConstraintWithParamMapping &Constraint) {
+ if (!Constraint.hasParameterMapping()) {
+ for (const auto &List : TemplateArgs)
+ for (const TemplateArgument &Arg : List.Args)
+ SemaRef.Context.getCanonicalTemplateArgument(Arg).Profile(
+ ID, SemaRef.Context);
+ return;
+ }
+
+ llvm::ArrayRef<TemplateArgumentLoc> Mapping =
+ Constraint.getParameterMapping();
+ for (auto &ArgLoc : Mapping) {
+ TemplateArgument Canonical =
+ SemaRef.Context.getCanonicalTemplateArgument(ArgLoc.getArgument());
+ // We don't want sugars to impede the profile of cache.
+ UsedTemplateArgs.push_back(Canonical);
+ TraverseTemplateArgument(Canonical);
+ }
+
+ for (auto &Used : UsedTemplateArgs) {
+ llvm::FoldingSetNodeID R;
+ Used.Profile(R, SemaRef.Context);
+ ID.AddNodeID(R);
+ }
+ }
+};
+
+class ConstraintSatisfactionChecker {
+ Sema &S;
+ const NamedDecl *Template;
+ SourceLocation TemplateNameLoc;
+ UnsignedOrNone PackSubstitutionIndex;
+
+ ConstraintSatisfaction &Satisfaction;
+
+private:
+ ExprResult
+ EvaluateAtomicConstraint(const Expr *AtomicExpr,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ UnsignedOrNone EvaluateFoldExpandedConstraintSize(
+ const FoldExpandedConstraint &FE,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ // XXX: It is SLOW! Use it very carefully.
+ std::optional<MultiLevelTemplateArgumentList> SubstitutionInTemplateArguments(
+ const NormalizedConstraintWithParamMapping &Constraint,
+ MultiLevelTemplateArgumentList MLTAL,
+ llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost);
+
+ ExprResult EvaluateSlow(const AtomicConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult Evaluate(const AtomicConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult EvaluateSlow(const FoldExpandedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult Evaluate(const FoldExpandedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult EvaluateSlow(const ConceptIdConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ unsigned int Size);
+
+ ExprResult Evaluate(const ConceptIdConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult Evaluate(const CompoundConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+public:
+ ConstraintSatisfactionChecker(Sema &SemaRef, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc,
+ UnsignedOrNone PackSubstitutionIndex,
+ ConstraintSatisfaction &Satisfaction)
+ : S(SemaRef), Template(Template), TemplateNameLoc(TemplateNameLoc),
+ PackSubstitutionIndex(PackSubstitutionIndex),
+ Satisfaction(Satisfaction) {}
+
+ ExprResult Evaluate(const NormalizedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+};
+
+StringRef allocateStringFromConceptDiagnostic(const Sema &S,
+ const PartialDiagnostic Diag) {
+ SmallString<128> DiagString;
+ DiagString = ": ";
+ Diag.EmitToString(S.getDiagnostics(), DiagString);
+ return S.getASTContext().backupStr(DiagString);
+}
+
+} // namespace
+
+ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
+ const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL) {
EnterExpressionEvaluationContext ConstantEvaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
Sema::ReuseLambdaContextDecl);
+ llvm::FoldingSetNodeID ID;
+ if (Template &&
+ DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, &MLTAL)) {
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = true;
+ return ExprEmpty();
+ }
+ SatisfactionStackRAII StackRAII(S, Template, ID);
+
// Atomic constraint - substitute arguments and check satisfaction.
- ExprResult SubstitutedExpression;
+ ExprResult SubstitutedExpression = const_cast<Expr *>(AtomicExpr);
{
TemplateDeductionInfo Info(TemplateNameLoc);
Sema::InstantiatingTemplate Inst(
@@ -220,16 +485,6 @@ static ExprResult EvaluateAtomicConstraint(
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 =
@@ -247,21 +502,16 @@ static ExprResult EvaluateAtomicConstraint(
PartialDiagnosticAt SubstDiag{SourceLocation(),
PartialDiagnostic::NullDiagnostic()};
Info.takeSFINAEDiagnostic(SubstDiag);
- // FIXME: Concepts: This is an unfortunate consequence of there
+ // FIXME: 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)});
+ new (S.Context) ConstraintSubstitutionDiagnostic{
+ SubstDiag.first,
+ allocateStringFromConceptDiagnostic(S, SubstDiag.second)});
Satisfaction.IsSatisfied = false;
return ExprEmpty();
}
@@ -289,216 +539,94 @@ static ExprResult EvaluateAtomicConstraint(
return SubstitutedExpression;
}
-static UnsignedOrNone 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();
+std::optional<MultiLevelTemplateArgumentList>
+ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
+ const NormalizedConstraintWithParamMapping &Constraint,
+ MultiLevelTemplateArgumentList MLTAL,
+ llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) {
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
- assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
- bool Expand = true;
- bool RetainExpansion = false;
- UnsignedOrNone NumExpansions = FE->getNumExpansions();
- if (S.CheckParameterPacksForExpansion(
- FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
- /*FailOnPackProducingTemplates=*/true, Expand, RetainExpansion,
- NumExpansions) ||
- !Expand || RetainExpansion)
- return std::nullopt;
+ if (!Constraint.hasParameterMapping())
+ return std::move(MLTAL);
- 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);
+ TemplateDeductionInfo Info(Constraint.getBeginLoc());
+ Sema::InstantiatingTemplate Inst(
+ S, Constraint.getBeginLoc(),
+ Sema::InstantiatingTemplate::ConstraintSubstitution{},
+ // FIXME: improve const-correctness of InstantiatingTemplate
+ const_cast<NamedDecl *>(Template), Info, Constraint.getSourceRange());
+ if (Inst.isInvalid())
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, Template, TemplateNameLoc, MLTAL, Satisfaction);
-
- if (LHSRes.isInvalid())
- return ExprError();
-
- bool IsLHSSatisfied = Satisfaction.IsSatisfied;
-
- if (Op == clang::OO_PipePipe && IsLHSSatisfied)
- // [temp.constr.op] p3
- // A disjunction is a constraint taking two operands. To determine if
- // a disjunction is satisfied, the satisfaction of the first operand
- // is checked. If that is satisfied, the disjunction is satisfied.
- // Otherwise, the disjunction is satisfied if and only if the second
- // operand is satisfied.
- // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
- return LHSRes;
-
- if (Op == clang::OO_AmpAmp && !IsLHSSatisfied)
- // [temp.constr.op] p2
- // A conjunction is a constraint taking two operands. To determine if
- // a conjunction is satisfied, the satisfaction of the first operand
- // is checked. If that is not satisfied, the conjunction is not
- // satisfied. Otherwise, the conjunction is satisfied if and only if
- // the second operand is satisfied.
- // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
- return LHSRes;
-
- ExprResult RHSRes = calculateConstraintSatisfaction(
- S, RHS, Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (RHSRes.isInvalid())
- return ExprError();
- bool IsRHSSatisfied = Satisfaction.IsSatisfied;
- // Current implementation adds diagnostic information about the falsity
- // of each false atomic constraint expression when it evaluates them.
- // When the evaluation results to `false || true`, the information
- // generated during the evaluation of left-hand side is meaningless
- // because the whole expression evaluates to true.
- // The following code removes the irrelevant diagnostic information.
- // FIXME: We should probably delay the addition of diagnostic information
- // until we know the entire expression is false.
- if (Op == clang::OO_PipePipe && IsRHSSatisfied) {
- auto EffectiveDetailEnd = Satisfaction.Details.begin();
- std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
- Satisfaction.Details.erase(EffectiveDetailEnd, Satisfaction.Details.end());
- }
-
- if (!LHSRes.isUsable() || !RHSRes.isUsable())
- return ExprEmpty();
-
- return BinaryOperator::Create(S.Context, LHSRes.get(), RHSRes.get(),
- BinaryOperator::getOverloadedOpcode(Op),
- S.Context.BoolTy, VK_PRValue, OK_Ordinary,
- LHS->getBeginLoc(), FPOptionsOverride{});
-}
-
-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(), Template,
- TemplateNameLoc, MLTAL, Satisfaction);
- if (Out.isInvalid())
- return ExprError();
+ Sema::SFINAETrap Trap(S);
- // If the first clause of a conjunction is not satisfied,
- // or if the first clause of a disjection is satisfied,
- // we have established satisfaction of the whole constraint
- // and we should not continue further.
- if (Conjunction != Satisfaction.IsSatisfied)
- return Out;
- }
- UnsignedOrNone NumExpansions = EvaluateFoldExpandedConstraintSize(
- S, FE, Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (!NumExpansions)
- return ExprError();
- for (unsigned I = 0; I < *NumExpansions; I++) {
- Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
- ExprResult Res = calculateConstraintSatisfaction(
- S, FE->getPattern(), Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (Res.isInvalid())
- return ExprError();
- bool IsRHSSatisfied = Satisfaction.IsSatisfied;
- if (!Conjunction && IsRHSSatisfied) {
- auto EffectiveDetailEnd = Satisfaction.Details.begin();
- std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
- Satisfaction.Details.erase(EffectiveDetailEnd,
- Satisfaction.Details.end());
- }
- if (Out.isUnset())
- Out = Res;
- else if (!Res.isUnset()) {
- Out = BinaryOperator::Create(
- S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
- VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
- }
- if (Conjunction != IsRHSSatisfied)
- return Out;
+ TemplateArgumentListInfo SubstArgs;
+ Sema::ArgPackSubstIndexRAII SubstIndex(
+ S, Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex);
+
+ if (S.SubstTemplateArgumentsInParameterMapping(
+ Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL,
+ SubstArgs, /*BuildPackExpansionTypes=*/true)) {
+ Satisfaction.IsSatisfied = false;
+ return std::nullopt;
}
- if (FE->isRightFold() && FE->getInit()) {
- ExprResult Res = calculateConstraintSatisfaction(
- S, FE->getInit(), Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (Out.isInvalid())
- return ExprError();
-
- if (Out.isUnset())
- Out = Res;
- else if (!Res.isUnset()) {
- Out = BinaryOperator::Create(
- S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
- VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
+ Sema::CheckTemplateArgumentInfo CTAI;
+ auto *TD = const_cast<TemplateDecl *>(
+ cast<TemplateDecl>(Constraint.getConstraintDecl()));
+ if (S.CheckTemplateArgumentList(TD, Constraint.getUsedTemplateParamList(),
+ TD->getLocation(), SubstArgs,
+ /*DefaultArguments=*/{},
+ /*PartialTemplateArgs=*/false, CTAI))
+ return std::nullopt;
+ const NormalizedConstraint::OccurenceList &Used =
+ Constraint.mappingOccurenceList();
+ SubstitutedOuterMost =
+ llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
+ unsigned Offset = 0;
+ for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) {
+ TemplateArgument Arg;
+ if (Used[I])
+ Arg = S.Context.getCanonicalTemplateArgument(
+ CTAI.SugaredConverted[MappedIndex++]);
+ if (I < SubstitutedOuterMost.size()) {
+ SubstitutedOuterMost[I] = Arg;
+ Offset = I + 1;
+ } else {
+ SubstitutedOuterMost.push_back(Arg);
+ Offset = SubstitutedOuterMost.size();
}
}
+ if (Offset < SubstitutedOuterMost.size())
+ SubstitutedOuterMost.erase(SubstitutedOuterMost.begin() + Offset);
- if (Out.isUnset()) {
- Satisfaction.IsSatisfied = Conjunction;
- Out = S.BuildEmptyCXXFoldExpr(FE->getBeginLoc(), FE->getOperator());
- }
- return Out;
+ MLTAL.replaceOutermostTemplateArguments(
+ const_cast<NamedDecl *>(Constraint.getConstraintDecl()),
+ SubstitutedOuterMost);
+ return std::move(MLTAL);
}
-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(), Template, TemplateNameLoc,
- MLTAL, Satisfaction);
+ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
+ const AtomicConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
- 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(), 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, Template, TemplateNameLoc,
- MLTAL, Satisfaction);
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost);
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ return ExprEmpty();
}
- // FIXME: We should not treat ConceptSpecializationExpr as atomic constraints.
-
- // An atomic constraint expression
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex);
ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint(
- S, ConstraintExpr, Template, TemplateNameLoc, MLTAL, Satisfaction);
+ Constraint.getConstraintExpr(), *SubstitutedArgs);
if (SubstitutedAtomicExpr.isInvalid())
return ExprError();
- if (!SubstitutedAtomicExpr.isUsable())
+ if (SubstitutedAtomicExpr.isUnset())
// Evaluator has decided satisfaction without yielding an expression.
return ExprEmpty();
@@ -512,16 +640,16 @@ static ExprResult calculateConstraintSatisfaction(
Satisfaction.ContainsErrors = true;
PartialDiagnostic Msg = S.PDiag(diag::note_constraint_references_error);
- SmallString<128> DiagString;
- DiagString = ": ";
- Msg.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{
+ new (S.Context) ConstraintSubstitutionDiagnostic{
SubstitutedAtomicExpr.get()->getBeginLoc(),
- StringRef(Mem, MessageSize)});
+ allocateStringFromConceptDiagnostic(S, Msg)});
+ return SubstitutedAtomicExpr;
+ }
+
+ if (SubstitutedAtomicExpr.get()->isValueDependent()) {
+ Satisfaction.IsSatisfied = true;
+ Satisfaction.ContainsErrors = false;
return SubstitutedAtomicExpr;
}
@@ -552,21 +680,384 @@ static ExprResult calculateConstraintSatisfaction(
return SubstitutedAtomicExpr;
}
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
- const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
- ConstraintSatisfaction &Satisfaction) {
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const AtomicConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ unsigned Size = Satisfaction.Details.size();
+ llvm::FoldingSetNodeID ID;
+ UnsignedOrNone OuterPackSubstIndex =
+ Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex;
+
+ ID.AddPointer(Constraint.getConstraintExpr());
+ ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
+ HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex)
+ .VisitConstraint(Constraint);
+
+ if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
+ Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
+
+ auto &Cached = Iter->second.Satisfaction;
+ Satisfaction.ContainsErrors = Cached.ContainsErrors;
+ Satisfaction.IsSatisfied = Cached.IsSatisfied;
+ Satisfaction.Details.insert(Satisfaction.Details.begin() + Size,
+ Cached.Details.begin(), Cached.Details.end());
+ return Iter->second.SubstExpr;
+ }
+
+ ExprResult E = EvaluateSlow(Constraint, MLTAL);
+
+ UnsubstitutedConstraintSatisfactionCacheResult Cache;
+ Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors;
+ Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied;
+ std::copy(Satisfaction.Details.begin() + Size, Satisfaction.Details.end(),
+ std::back_inserter(Cache.Satisfaction.Details));
+ Cache.SubstExpr = E;
+ S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)});
+
+ return E;
+}
+
+UnsignedOrNone
+ConstraintSatisfactionChecker::EvaluateFoldExpandedConstraintSize(
+ const FoldExpandedConstraint &FE,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ // We should ignore errors in the presence of packs of different size.
+ Sema::SFINAETrap Trap(S);
+
+ Expr *Pattern = const_cast<Expr *>(FE.getPattern());
+
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+ bool Expand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions(std::nullopt);
+ if (S.CheckParameterPacksForExpansion(
+ Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
+ /*FailOnPackProducingTemplates=*/false, Expand, RetainExpansion,
+ NumExpansions) ||
+ !Expand || RetainExpansion)
+ return std::nullopt;
+
+ if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) {
+ S.Diag(Pattern->getExprLoc(),
+ clang::diag::err_fold_expression_limit_exceeded)
+ << *NumExpansions << S.getLangOpts().BracketDepth
+ << Pattern->getSourceRange();
+ S.Diag(Pattern->getExprLoc(), diag::note_bracket_depth);
+ return std::nullopt;
+ }
+ return NumExpansions;
+}
- return calculateConstraintSatisfaction(S, ConstraintExpr, Template,
- TemplateNameLoc, MLTAL, Satisfaction);
+ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
+ const FoldExpandedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ bool Conjunction = Constraint.getFoldOperator() ==
+ FoldExpandedConstraint::FoldOperatorKind::And;
+ unsigned EffectiveDetailEndIndex = Satisfaction.Details.size();
+
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ // FIXME: Is PackSubstitutionIndex correct?
+ llvm::SaveAndRestore _(PackSubstitutionIndex, S.ArgPackSubstIndex);
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(
+ static_cast<const NormalizedConstraintWithParamMapping &>(Constraint),
+ MLTAL, SubstitutedOuterMost);
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ return ExprError();
+ }
+
+ ExprResult Out;
+ UnsignedOrNone NumExpansions =
+ EvaluateFoldExpandedConstraintSize(Constraint, *SubstitutedArgs);
+ if (!NumExpansions)
+ return ExprEmpty();
+
+ if (*NumExpansions == 0) {
+ Satisfaction.IsSatisfied = Conjunction;
+ return ExprEmpty();
+ }
+
+ for (unsigned I = 0; I < *NumExpansions; I++) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = false;
+ ExprResult Expr =
+ ConstraintSatisfactionChecker(S, Template, TemplateNameLoc,
+ UnsignedOrNone(I), Satisfaction)
+ .Evaluate(Constraint.getNormalizedPattern(), *SubstitutedArgs);
+ if (Expr.isUsable()) {
+ if (Out.isUnset())
+ Out = Expr;
+ else
+ Out = BinaryOperator::Create(S.Context, Out.get(), Expr.get(),
+ Conjunction ? BinaryOperatorKind::BO_LAnd
+ : BinaryOperatorKind::BO_LOr,
+ S.Context.BoolTy, VK_PRValue, OK_Ordinary,
+ Constraint.getBeginLoc(),
+ FPOptionsOverride{});
+ } else {
+ assert(!Satisfaction.IsSatisfied);
+ }
+ if (!Conjunction && Satisfaction.IsSatisfied) {
+ Satisfaction.Details.erase(Satisfaction.Details.begin() +
+ EffectiveDetailEndIndex,
+ Satisfaction.Details.end());
+ break;
+ }
+ if (Satisfaction.IsSatisfied != Conjunction)
+ return Out;
+ }
+
+ return Out;
+}
+
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const FoldExpandedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ llvm::FoldingSetNodeID ID;
+ ID.AddPointer(Constraint.getPattern());
+ HashParameterMapping(S, MLTAL, ID, std::nullopt).VisitConstraint(Constraint);
+
+ if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
+ Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
+
+ auto &Cached = Iter->second.Satisfaction;
+ Satisfaction.ContainsErrors = Cached.ContainsErrors;
+ Satisfaction.IsSatisfied = Cached.IsSatisfied;
+ Satisfaction.Details.insert(Satisfaction.Details.end(),
+ Cached.Details.begin(), Cached.Details.end());
+ return Iter->second.SubstExpr;
+ }
+
+ unsigned Size = Satisfaction.Details.size();
+
+ ExprResult E = EvaluateSlow(Constraint, MLTAL);
+ UnsubstitutedConstraintSatisfactionCacheResult Cache;
+ Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors;
+ Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied;
+ std::copy(Satisfaction.Details.begin() + Size, Satisfaction.Details.end(),
+ std::back_inserter(Cache.Satisfaction.Details));
+ Cache.SubstExpr = E;
+ S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)});
+ return E;
+}
+
+ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
+ const ConceptIdConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL, unsigned Size) {
+ const ConceptReference *ConceptId = Constraint.getConceptId();
+
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost);
+
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ // FIXME: diagnostics?
+ return ExprError();
+ }
+
+ Sema::SFINAETrap Trap(S);
+ Sema::ArgPackSubstIndexRAII SubstIndex(
+ S, Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex);
+
+ const ASTTemplateArgumentListInfo *Ori =
+ ConceptId->getTemplateArgsAsWritten();
+ TemplateDeductionInfo Info(TemplateNameLoc);
+ Sema::InstantiatingTemplate _(
+ S, TemplateNameLoc, Sema::InstantiatingTemplate::ConstraintSubstitution{},
+ const_cast<NamedDecl *>(Template), Info, Constraint.getSourceRange());
+
+ TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc);
+ if (S.SubstTemplateArguments(Ori->arguments(), *SubstitutedArgs, OutArgs) ||
+ Trap.hasErrorOccurred()) {
+ Satisfaction.IsSatisfied = false;
+ if (!Trap.hasErrorOccurred())
+ return ExprError();
+
+ PartialDiagnosticAt SubstDiag{SourceLocation(),
+ PartialDiagnostic::NullDiagnostic()};
+ Info.takeSFINAEDiagnostic(SubstDiag);
+ // FIXME: 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.
+ Satisfaction.Details.insert(
+ Satisfaction.Details.begin() + Size,
+ new (S.Context) ConstraintSubstitutionDiagnostic{
+ SubstDiag.first,
+ allocateStringFromConceptDiagnostic(S, SubstDiag.second)});
+ return ExprError();
+ }
+
+ CXXScopeSpec SS;
+ SS.Adopt(ConceptId->getNestedNameSpecifierLoc());
+
+ ExprResult SubstitutedConceptId = S.CheckConceptTemplateId(
+ SS, ConceptId->getTemplateKWLoc(), ConceptId->getConceptNameInfo(),
+ ConceptId->getFoundDecl(), ConceptId->getNamedConcept(), &OutArgs,
+ /*DoCheckConstraintSatisfaction=*/false);
+
+ if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
+ return ExprError();
+
+ if (Size != Satisfaction.Details.size()) {
+ Satisfaction.Details.insert(
+ Satisfaction.Details.begin() + Size,
+ UnsatisfiedConstraintRecord(
+ SubstitutedConceptId.getAs<ConceptSpecializationExpr>()
+ ->getConceptReference()));
+ }
+ return SubstitutedConceptId;
+}
+
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const ConceptIdConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ const ConceptReference *ConceptId = Constraint.getConceptId();
+
+ UnsignedOrNone OuterPackSubstIndex =
+ Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex;
+
+ Sema::InstantiatingTemplate _(S, ConceptId->getBeginLoc(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{},
+ ConceptId->getNamedConcept(),
+ MLTAL.getInnermost(),
+ Constraint.getSourceRange());
+
+ unsigned Size = Satisfaction.Details.size();
+
+ ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL);
+
+ if (!E.isUsable()) {
+ Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, ConceptId);
+ return E;
+ }
+
+ // ConceptIdConstraint is only relevant for diagnostics,
+ // so if the normalized constraint is satisfied, we should not
+ // substitute into the constraint.
+ if (Satisfaction.IsSatisfied)
+ return E;
+
+ llvm::FoldingSetNodeID ID;
+ ID.AddPointer(Constraint.getConceptId());
+ ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
+ HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex)
+ .VisitConstraint(Constraint);
+
+ if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
+ Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
+
+ auto &Cached = Iter->second.Satisfaction;
+ Satisfaction.ContainsErrors = Cached.ContainsErrors;
+ Satisfaction.IsSatisfied = Cached.IsSatisfied;
+ Satisfaction.Details.insert(Satisfaction.Details.begin() + Size,
+ Cached.Details.begin(), Cached.Details.end());
+ return Iter->second.SubstExpr;
+ }
+
+ ExprResult CE = EvaluateSlow(Constraint, MLTAL, Size);
+ if (CE.isInvalid())
+ return E;
+ UnsubstitutedConstraintSatisfactionCacheResult Cache;
+ Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors;
+ Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied;
+ std::copy(Satisfaction.Details.begin() + Size, Satisfaction.Details.end(),
+ std::back_inserter(Cache.Satisfaction.Details));
+ Cache.SubstExpr = CE;
+ S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)});
+ return CE;
+}
+
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const CompoundConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ unsigned EffectiveDetailEndIndex = Satisfaction.Details.size();
+
+ bool Conjunction =
+ Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction;
+
+ ExprResult LHS = Evaluate(Constraint.getLHS(), MLTAL);
+
+ if (Conjunction && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
+ return LHS;
+
+ if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied &&
+ !Satisfaction.ContainsErrors)
+ return LHS;
+
+ Satisfaction.ContainsErrors = false;
+ Satisfaction.IsSatisfied = false;
+
+ ExprResult RHS = Evaluate(Constraint.getRHS(), MLTAL);
+
+ if (RHS.isUsable() && Satisfaction.IsSatisfied &&
+ !Satisfaction.ContainsErrors)
+ Satisfaction.Details.erase(Satisfaction.Details.begin() +
+ EffectiveDetailEndIndex,
+ Satisfaction.Details.end());
+
+ if (!LHS.isUsable())
+ return RHS;
+
+ if (!RHS.isUsable())
+ return LHS;
+
+ return BinaryOperator::Create(S.Context, LHS.get(), RHS.get(),
+ Conjunction ? BinaryOperatorKind::BO_LAnd
+ : BinaryOperatorKind::BO_LOr,
+ S.Context.BoolTy, VK_PRValue, OK_Ordinary,
+ Constraint.getBeginLoc(), FPOptionsOverride{});
+}
+
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const NormalizedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+ switch (Constraint.getKind()) {
+ case NormalizedConstraint::ConstraintKind::Atomic:
+ return Evaluate(static_cast<const AtomicConstraint &>(Constraint), MLTAL);
+
+ case NormalizedConstraint::ConstraintKind::FoldExpanded:
+ return Evaluate(static_cast<const FoldExpandedConstraint &>(Constraint),
+ MLTAL);
+
+ case NormalizedConstraint::ConstraintKind::ConceptId:
+ return Evaluate(static_cast<const ConceptIdConstraint &>(Constraint),
+ MLTAL);
+
+ case NormalizedConstraint::ConstraintKind::Compound:
+ return Evaluate(static_cast<const CompoundConstraint &>(Constraint), MLTAL);
+ }
}
static bool CheckConstraintSatisfaction(
Sema &S, const NamedDecl *Template,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
- llvm::SmallVectorImpl<Expr *> &Converted,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
- SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
+ SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
+ Expr **ConvertedExpr, const ConceptReference *TopLevelConceptId = nullptr) {
+
+ if (ConvertedExpr)
+ *ConvertedExpr = nullptr;
+
if (AssociatedConstraints.empty()) {
Satisfaction.IsSatisfied = true;
return false;
@@ -578,57 +1069,60 @@ static bool CheckConstraintSatisfaction(
return false;
}
- ArrayRef<TemplateArgument> TemplateArgs =
- TemplateArgsLists.getNumSubstitutedLevels() > 0
- ? TemplateArgsLists.getOutermost()
- : ArrayRef<TemplateArgument>{};
- Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
- Sema::InstantiatingTemplate::ConstraintsCheck{},
- const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
- if (Inst.isInvalid())
+ llvm::ArrayRef<TemplateArgument> Args;
+ if (TemplateArgsLists.getNumLevels() != 0)
+ Args = TemplateArgsLists.getInnermost();
+
+ std::optional<Sema::InstantiatingTemplate> SynthesisContext;
+ if (!TopLevelConceptId) {
+ SynthesisContext.emplace(S, TemplateIDRange.getBegin(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{},
+ const_cast<NamedDecl *>(Template), Args,
+ TemplateIDRange);
+ }
+
+ const NormalizedConstraint *C =
+ S.getNormalizedAssociatedConstraints(Template, AssociatedConstraints);
+ if (!C) {
+ Satisfaction.IsSatisfied = false;
return true;
+ }
- for (const AssociatedConstraint &AC : AssociatedConstraints) {
- if (AC.isNull())
- return true;
+ if (TopLevelConceptId)
+ C = ConceptIdConstraint::Create(S.getASTContext(), TopLevelConceptId,
+ const_cast<NormalizedConstraint *>(C),
+ Template, /*CSE=*/nullptr,
+ S.ArgPackSubstIndex);
- Sema::ArgPackSubstIndexRAII _(S, AC.ArgPackSubstIndex);
- ExprResult Res = calculateConstraintSatisfaction(
- S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
- AC.ConstraintExpr, Satisfaction);
- if (Res.isInvalid())
- return true;
+ ExprResult Res =
+ ConstraintSatisfactionChecker(S, Template, TemplateIDRange.getBegin(),
+ S.ArgPackSubstIndex, Satisfaction)
+ .Evaluate(*C, TemplateArgsLists);
+
+ if (Res.isInvalid())
+ return true;
+
+ if (Res.isUsable() && ConvertedExpr)
+ *ConvertedExpr = Res.get();
- Converted.push_back(Res.get());
- if (!Satisfaction.IsSatisfied) {
- // Backfill the 'converted' list with nulls so we can keep the Converted
- // and unconverted lists in sync.
- Converted.append(AssociatedConstraints.size() - Converted.size(),
- nullptr);
- // [temp.constr.op] p2
- // [...] To determine if a conjunction is satisfied, the satisfaction
- // of the first operand is checked. If that is not satisfied, the
- // conjunction is not satisfied. [...]
- return false;
- }
- }
return false;
}
bool Sema::CheckConstraintSatisfaction(
- const NamedDecl *Template,
+ ConstrainedDeclOrNestedRequirement Entity,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
- llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
- SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
+ SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction,
+ const ConceptReference *TopLevelConceptId, Expr **ConvertedExpr) {
if (AssociatedConstraints.empty()) {
OutSatisfaction.IsSatisfied = true;
return false;
}
+ const auto *Template = Entity.dyn_cast<const NamedDecl *>();
if (!Template) {
return ::CheckConstraintSatisfaction(
- *this, nullptr, AssociatedConstraints, ConvertedConstraints,
- TemplateArgsLists, TemplateIDRange, OutSatisfaction);
+ *this, nullptr, AssociatedConstraints, TemplateArgsLists,
+ TemplateIDRange, OutSatisfaction, ConvertedExpr, TopLevelConceptId);
}
// Invalid templates could make their way here. Substituting them could result
// in dependent expressions.
@@ -643,10 +1137,15 @@ bool Sema::CheckConstraintSatisfaction(
// here.
llvm::SmallVector<TemplateArgument, 4> FlattenedArgs;
for (auto List : TemplateArgsLists)
- llvm::append_range(FlattenedArgs, List.Args);
+ for (const TemplateArgument &Arg : List.Args)
+ FlattenedArgs.emplace_back(Context.getCanonicalTemplateArgument(Arg));
+
+ const NamedDecl *Owner = Template;
+ if (TopLevelConceptId)
+ Owner = TopLevelConceptId->getNamedConcept();
llvm::FoldingSetNodeID ID;
- ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
+ ConstraintSatisfaction::Profile(ID, Context, Owner, FlattenedArgs);
void *InsertPos;
if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
OutSatisfaction = *Cached;
@@ -654,11 +1153,11 @@ bool Sema::CheckConstraintSatisfaction(
}
auto Satisfaction =
- std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
- if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints,
- ConvertedConstraints, TemplateArgsLists,
- TemplateIDRange, *Satisfaction)) {
- OutSatisfaction = *Satisfaction;
+ std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs);
+ if (::CheckConstraintSatisfaction(
+ *this, Template, AssociatedConstraints, TemplateArgsLists,
+ TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) {
+ OutSatisfaction = std::move(*Satisfaction);
return true;
}
@@ -688,14 +1187,18 @@ bool Sema::CheckConstraintSatisfaction(
const ConceptSpecializationExpr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
+ llvm::SmallVector<AssociatedConstraint, 1> Constraints;
+ Constraints.emplace_back(
+ ConstraintExpr->getNamedConcept()->getConstraintExpr());
+
MultiLevelTemplateArgumentList MLTAL(ConstraintExpr->getNamedConcept(),
ConstraintExpr->getTemplateArguments(),
true);
- return calculateConstraintSatisfaction(
- *this, ConstraintExpr, ConstraintExpr->getNamedConcept(),
- ConstraintExpr->getConceptNameLoc(), MLTAL, Satisfaction)
- .isInvalid();
+ return CheckConstraintSatisfaction(
+ ConstraintExpr->getNamedConcept(), Constraints, MLTAL,
+ ConstraintExpr->getSourceRange(), Satisfaction,
+ ConstraintExpr->getConceptReference());
}
bool Sema::SetupConstraintScope(
@@ -854,50 +1357,6 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
Satisfaction);
}
-
-// Figure out the to-translation-unit depth for this function declaration for
-// the purpose of seeing if they differ by constraints. This isn't the same as
-// getTemplateDepth, because it includes already instantiated parents.
-static unsigned
-CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
- bool SkipForSpecialization = false) {
- MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
- ND, ND->getLexicalDeclContext(), /*Final=*/false,
- /*Innermost=*/std::nullopt,
- /*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/true, SkipForSpecialization);
- return MLTAL.getNumLevels();
-}
-
-namespace {
- class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
- unsigned TemplateDepth = 0;
- public:
- using inherited = TreeTransform<AdjustConstraintDepth>;
- AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
- : inherited(SemaRef), TemplateDepth(TemplateDepth) {}
-
- using inherited::TransformTemplateTypeParmType;
- QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
- TemplateTypeParmTypeLoc TL, bool) {
- const TemplateTypeParmType *T = TL.getTypePtr();
-
- TemplateTypeParmDecl *NewTTPDecl = nullptr;
- if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
- NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
- TransformDecl(TL.getNameLoc(), OldTTPDecl));
-
- QualType Result = getSema().Context.getTemplateTypeParmType(
- T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
- NewTTPDecl);
- TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
- NewTL.setNameLoc(TL.getNameLoc());
- return Result;
- }
- };
-} // namespace
-
static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo,
const Expr *ConstrExpr) {
@@ -1161,73 +1620,61 @@ bool Sema::CheckFunctionTemplateConstraints(
static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::ExprRequirement *Req,
bool First) {
- assert(!Req->isSatisfied()
- && "Diagnose() can only be used on an unsatisfied requirement");
+ assert(!Req->isSatisfied() &&
+ "Diagnose() can only be used on an unsatisfied requirement");
switch (Req->getSatisfactionStatus()) {
- case concepts::ExprRequirement::SS_Dependent:
- llvm_unreachable("Diagnosing a dependent requirement");
- break;
- case concepts::ExprRequirement::SS_ExprSubstitutionFailure: {
- auto *SubstDiag = Req->getExprSubstitutionDiagnostic();
- if (!SubstDiag->DiagMessage.empty())
- S.Diag(SubstDiag->DiagLoc,
- diag::note_expr_requirement_expr_substitution_error)
- << (int)First << SubstDiag->SubstitutedEntity
- << SubstDiag->DiagMessage;
- else
- S.Diag(SubstDiag->DiagLoc,
- diag::note_expr_requirement_expr_unknown_substitution_error)
- << (int)First << SubstDiag->SubstitutedEntity;
- break;
- }
- case concepts::ExprRequirement::SS_NoexceptNotMet:
- S.Diag(Req->getNoexceptLoc(),
- diag::note_expr_requirement_noexcept_not_met)
- << (int)First << Req->getExpr();
- break;
- case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: {
- auto *SubstDiag =
- Req->getReturnTypeRequirement().getSubstitutionDiagnostic();
- if (!SubstDiag->DiagMessage.empty())
- S.Diag(SubstDiag->DiagLoc,
- diag::note_expr_requirement_type_requirement_substitution_error)
- << (int)First << SubstDiag->SubstitutedEntity
- << SubstDiag->DiagMessage;
- else
- S.Diag(SubstDiag->DiagLoc,
- diag::note_expr_requirement_type_requirement_unknown_substitution_error)
- << (int)First << SubstDiag->SubstitutedEntity;
- break;
- }
- case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: {
- ConceptSpecializationExpr *ConstraintExpr =
- Req->getReturnTypeRequirementSubstitutedConstraintExpr();
- if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
- // A simple case - expr type is the type being constrained and the concept
- // was not provided arguments.
- Expr *e = Req->getExpr();
- S.Diag(e->getBeginLoc(),
- diag::note_expr_requirement_constraints_not_satisfied_simple)
- << (int)First << S.Context.getReferenceQualifiedType(e)
- << ConstraintExpr->getNamedConcept();
- } else {
- S.Diag(ConstraintExpr->getBeginLoc(),
- diag::note_expr_requirement_constraints_not_satisfied)
- << (int)First << ConstraintExpr;
- }
- S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction());
- break;
- }
- case concepts::ExprRequirement::SS_Satisfied:
- llvm_unreachable("We checked this above");
+ case concepts::ExprRequirement::SS_Dependent:
+ llvm_unreachable("Diagnosing a dependent requirement");
+ break;
+ case concepts::ExprRequirement::SS_ExprSubstitutionFailure: {
+ auto *SubstDiag = Req->getExprSubstitutionDiagnostic();
+ if (!SubstDiag->DiagMessage.empty())
+ S.Diag(SubstDiag->DiagLoc,
+ diag::note_expr_requirement_expr_substitution_error)
+ << (int)First << SubstDiag->SubstitutedEntity
+ << SubstDiag->DiagMessage;
+ else
+ S.Diag(SubstDiag->DiagLoc,
+ diag::note_expr_requirement_expr_unknown_substitution_error)
+ << (int)First << SubstDiag->SubstitutedEntity;
+ break;
+ }
+ case concepts::ExprRequirement::SS_NoexceptNotMet:
+ S.Diag(Req->getNoexceptLoc(), diag::note_expr_requirement_noexcept_not_met)
+ << (int)First << Req->getExpr();
+ break;
+ case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: {
+ auto *SubstDiag =
+ Req->getReturnTypeRequirement().getSubstitutionDiagnostic();
+ if (!SubstDiag->DiagMessage.empty())
+ S.Diag(SubstDiag->DiagLoc,
+ diag::note_expr_requirement_type_requirement_substitution_error)
+ << (int)First << SubstDiag->SubstitutedEntity
+ << SubstDiag->DiagMessage;
+ else
+ S.Diag(
+ SubstDiag->DiagLoc,
+ diag::
+ note_expr_requirement_type_requirement_unknown_substitution_error)
+ << (int)First << SubstDiag->SubstitutedEntity;
+ break;
+ }
+ case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: {
+ ConceptSpecializationExpr *ConstraintExpr =
+ Req->getReturnTypeRequirementSubstitutedConstraintExpr();
+ S.DiagnoseUnsatisfiedConstraint(ConstraintExpr);
+ break;
+ }
+ case concepts::ExprRequirement::SS_Satisfied:
+ llvm_unreachable("We checked this above");
}
}
static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::TypeRequirement *Req,
bool First) {
- assert(!Req->isSatisfied()
- && "Diagnose() can only be used on an unsatisfied requirement");
+ assert(!Req->isSatisfied() &&
+ "Diagnose() can only be used on an unsatisfied requirement");
switch (Req->getSatisfactionStatus()) {
case concepts::TypeRequirement::SS_Dependent:
llvm_unreachable("Diagnosing a dependent requirement");
@@ -1235,9 +1682,9 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
case concepts::TypeRequirement::SS_SubstitutionFailure: {
auto *SubstDiag = Req->getSubstitutionDiagnostic();
if (!SubstDiag->DiagMessage.empty())
- S.Diag(SubstDiag->DiagLoc,
- diag::note_type_requirement_substitution_error) << (int)First
- << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage;
+ S.Diag(SubstDiag->DiagLoc, diag::note_type_requirement_substitution_error)
+ << (int)First << SubstDiag->SubstitutedEntity
+ << SubstDiag->DiagMessage;
else
S.Diag(SubstDiag->DiagLoc,
diag::note_type_requirement_unknown_substitution_error)
@@ -1249,31 +1696,53 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
return;
}
}
-static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
- Expr *SubstExpr,
- bool First = true);
+
+static void diagnoseUnsatisfiedConceptIdExpr(Sema &S,
+ const ConceptReference *Concept,
+ SourceLocation Loc, bool First) {
+ if (Concept->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
+ S.Diag(
+ Loc,
+ diag::
+ note_single_arg_concept_specialization_constraint_evaluated_to_false)
+ << (int)First
+ << Concept->getTemplateArgsAsWritten()->arguments()[0].getArgument()
+ << Concept->getNamedConcept();
+ } else {
+ S.Diag(Loc, diag::note_concept_specialization_constraint_evaluated_to_false)
+ << (int)First << Concept;
+ }
+}
+
+static void diagnoseUnsatisfiedConstraintExpr(
+ Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc,
+ bool First, concepts::NestedRequirement *Req = nullptr);
+
+static void DiagnoseUnsatisfiedConstraint(
+ Sema &S, ArrayRef<UnsatisfiedConstraintRecord> Records, SourceLocation Loc,
+ bool First = true, concepts::NestedRequirement *Req = nullptr) {
+ for (auto &Record : Records) {
+ diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req);
+ Loc = {};
+ First = isa<const ConceptReference *>(Record);
+ }
+}
static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::NestedRequirement *Req,
bool First) {
- using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
- for (auto &Record : Req->getConstraintSatisfaction()) {
- if (auto *SubstDiag = Record.dyn_cast<SubstitutionDiagnostic *>())
- S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error)
- << (int)First << Req->getInvalidConstraintEntity()
- << SubstDiag->second;
- else
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, Record.dyn_cast<Expr *>(),
- First);
- First = false;
- }
+ DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(),
+ Req->hasInvalidConstraint()
+ ? SourceLocation()
+ : Req->getConstraintExpr()->getExprLoc(),
+ First, Req);
}
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
- Expr *SubstExpr,
+ const Expr *SubstExpr,
bool First) {
SubstExpr = SubstExpr->IgnoreParenImpCasts();
- if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
+ if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
switch (BO->getOpcode()) {
// These two cases will in practice only be reached when using fold
// expressions with || and &&, since otherwise the || and && will have been
@@ -1319,7 +1788,7 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context,
Expr::SE_NoSideEffects,
/*InConstantContext=*/true);
- if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) {
+ if (!SimplifiedLHS.Diag && !SimplifiedRHS.Diag) {
S.Diag(SubstExpr->getBeginLoc(),
diag::note_atomic_constraint_evaluated_to_false_elaborated)
<< (int)First << SubstExpr
@@ -1334,22 +1803,6 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
default:
break;
}
- } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
- if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
- S.Diag(
- CSE->getSourceRange().getBegin(),
- diag::
- note_single_arg_concept_specialization_constraint_evaluated_to_false)
- << (int)First
- << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
- << CSE->getNamedConcept();
- } else {
- S.Diag(SubstExpr->getSourceRange().getBegin(),
- diag::note_concept_specialization_constraint_evaluated_to_false)
- << (int)First << CSE;
- }
- S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
- return;
} else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
// FIXME: RequiresExpr should store dependent diagnostics.
for (concepts::Requirement *Req : RE->getRequirements())
@@ -1364,6 +1817,10 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
break;
}
return;
+ } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
+ // Drill down concept ids treated as atomic constraints
+ S.DiagnoseUnsatisfiedConstraint(CSE, First);
+ return;
} else if (auto *TTE = dyn_cast<TypeTraitExpr>(SubstExpr);
TTE && TTE->getTrait() == clang::TypeTrait::BTT_IsDeducible) {
assert(TTE->getNumArgs() == 2);
@@ -1379,216 +1836,332 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
S.DiagnoseTypeTraitDetails(SubstExpr);
}
-template <typename SubstitutionDiagnostic>
static void diagnoseUnsatisfiedConstraintExpr(
- Sema &S, const llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> &Record,
- bool First = true) {
- if (auto *Diag = Record.template dyn_cast<SubstitutionDiagnostic *>()) {
- S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
- << Diag->second;
+ Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc,
+ bool First, concepts::NestedRequirement *Req) {
+ if (auto *Diag =
+ Record
+ .template dyn_cast<const ConstraintSubstitutionDiagnostic *>()) {
+ if (Req)
+ S.Diag(Diag->first, diag::note_nested_requirement_substitution_error)
+ << (int)First << Req->getInvalidConstraintEntity() << Diag->second;
+ else
+ S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
+ << Diag->second;
return;
}
-
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, cast<Expr *>(Record), First);
+ if (const auto *Concept = dyn_cast<const ConceptReference *>(Record)) {
+ if (Loc.isInvalid())
+ Loc = Concept->getBeginLoc();
+ diagnoseUnsatisfiedConceptIdExpr(S, Concept, Loc, First);
+ return;
+ }
+ diagnoseWellFormedUnsatisfiedConstraintExpr(
+ S, cast<const class Expr *>(Record), First);
}
-void
-Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction,
- bool First) {
+void Sema::DiagnoseUnsatisfiedConstraint(
+ const ConstraintSatisfaction &Satisfaction, SourceLocation Loc,
+ bool First) {
+
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
- for (auto &Record : Satisfaction.Details) {
- diagnoseUnsatisfiedConstraintExpr(*this, Record, First);
- First = false;
- }
+ ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.Details, Loc, First);
}
void Sema::DiagnoseUnsatisfiedConstraint(
- const ASTConstraintSatisfaction &Satisfaction,
- bool First) {
+ const ConceptSpecializationExpr *ConstraintExpr, bool First) {
+
+ const ASTConstraintSatisfaction &Satisfaction =
+ ConstraintExpr->getSatisfaction();
+
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
- for (auto &Record : Satisfaction) {
- diagnoseUnsatisfiedConstraintExpr(*this, Record, First);
- First = false;
- }
+
+ ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(),
+ ConstraintExpr->getBeginLoc(), First);
}
-const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
- const NamedDecl *ConstrainedDecl,
- ArrayRef<AssociatedConstraint> AssociatedConstraints) {
- // In case the ConstrainedDecl comes from modules, it is necessary to use
- // the canonical decl to avoid different atomic constraints with the 'same'
- // declarations.
- ConstrainedDecl = cast<NamedDecl>(ConstrainedDecl->getCanonicalDecl());
+namespace {
- auto CacheEntry = NormalizationCache.find(ConstrainedDecl);
- if (CacheEntry == NormalizationCache.end()) {
- auto Normalized = NormalizedConstraint::fromAssociatedConstraints(
- *this, ConstrainedDecl, AssociatedConstraints);
- CacheEntry =
- NormalizationCache
- .try_emplace(ConstrainedDecl,
- Normalized
- ? new (Context) NormalizedConstraint(
- std::move(*Normalized))
- : nullptr)
- .first;
- }
- return CacheEntry->second;
-}
+class SubstituteParameterMappings {
+ Sema &SemaRef;
-const NormalizedConstraint *clang::getNormalizedAssociatedConstraints(
- Sema &S, const NamedDecl *ConstrainedDecl,
- ArrayRef<AssociatedConstraint> AssociatedConstraints) {
- return S.getNormalizedAssociatedConstraints(ConstrainedDecl,
- AssociatedConstraints);
-}
+ const MultiLevelTemplateArgumentList *MLTAL;
+ const ASTTemplateArgumentListInfo *ArgsAsWritten;
-static bool
-substituteParameterMappings(Sema &S, NormalizedConstraint &N,
- ConceptDecl *Concept,
- const MultiLevelTemplateArgumentList &MLTAL,
- const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+ bool InFoldExpr;
- if (N.isCompound()) {
- if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
- ArgsAsWritten))
- return true;
- return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
- ArgsAsWritten);
- }
+ SubstituteParameterMappings(Sema &SemaRef,
+ const MultiLevelTemplateArgumentList *MLTAL,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ bool InFoldExpr)
+ : SemaRef(SemaRef), MLTAL(MLTAL), ArgsAsWritten(ArgsAsWritten),
+ InFoldExpr(InFoldExpr) {}
+
+ void buildParameterMapping(NormalizedConstraintWithParamMapping &N);
+
+ bool substitute(NormalizedConstraintWithParamMapping &N);
+
+ bool substitute(ConceptIdConstraint &CC);
+
+public:
+ SubstituteParameterMappings(Sema &SemaRef, bool InFoldExpr = false)
+ : SemaRef(SemaRef), MLTAL(nullptr), ArgsAsWritten(nullptr),
+ InFoldExpr(InFoldExpr) {}
+
+ bool substitute(NormalizedConstraint &N);
+};
- if (N.isFoldExpanded()) {
- Sema::ArgPackSubstIndexRAII _(S, std::nullopt);
- return substituteParameterMappings(
- S, N.getFoldExpandedConstraint()->Constraint, Concept, MLTAL,
- ArgsAsWritten);
+void SubstituteParameterMappings::buildParameterMapping(
+ NormalizedConstraintWithParamMapping &N) {
+ TemplateParameterList *TemplateParams =
+ cast<TemplateDecl>(N.getConstraintDecl())->getTemplateParameters();
+
+ llvm::SmallBitVector OccurringIndices(TemplateParams->size());
+ llvm::SmallBitVector OccurringIndicesForSubsumption(TemplateParams->size());
+
+ if (N.getKind() == NormalizedConstraint::ConstraintKind::Atomic) {
+ SemaRef.MarkUsedTemplateParameters(
+ static_cast<AtomicConstraint &>(N).getConstraintExpr(),
+ /*OnlyDeduced=*/false,
+ /*Depth=*/0, OccurringIndices);
+
+ SemaRef.MarkUsedTemplateParametersForSubsumptionParameterMapping(
+ static_cast<AtomicConstraint &>(N).getConstraintExpr(),
+ /*Depth=*/0, OccurringIndicesForSubsumption);
+
+ } else if (N.getKind() ==
+ NormalizedConstraint::ConstraintKind::FoldExpanded) {
+ SemaRef.MarkUsedTemplateParameters(
+ static_cast<FoldExpandedConstraint &>(N).getPattern(),
+ /*OnlyDeduced=*/false,
+ /*Depth=*/0, OccurringIndices);
+ } else if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) {
+ auto *Args = static_cast<ConceptIdConstraint &>(N)
+ .getConceptId()
+ ->getTemplateArgsAsWritten();
+ if (Args)
+ SemaRef.MarkUsedTemplateParameters(Args->arguments(),
+ /*Depth=*/0, OccurringIndices);
}
+ TemplateArgumentLoc *TempArgs =
+ new (SemaRef.Context) TemplateArgumentLoc[OccurringIndices.count()];
+ llvm::SmallVector<NamedDecl *> UsedParams;
+ for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) {
+ SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I
+ ? ArgsAsWritten->arguments()[I].getLocation()
+ : SourceLocation();
+ // FIXME: Investigate why we couldn't always preserve the SourceLoc. We
+ // can't assert Loc.isValid() now.
+ if (OccurringIndices[I]) {
+ NamedDecl *Param = TemplateParams->begin()[I];
+ new (&(TempArgs)[J]) TemplateArgumentLoc(
+ SemaRef.getIdentityTemplateArgumentLoc(Param, Loc));
+ UsedParams.push_back(Param);
+ J++;
+ }
+ }
+ auto *UsedList = TemplateParameterList::Create(
+ SemaRef.Context, TemplateParams->getTemplateLoc(),
+ TemplateParams->getLAngleLoc(), UsedParams,
+ /*RAngleLoc=*/SourceLocation(),
+ /*RequiresClause=*/nullptr);
+ unsigned Size = OccurringIndices.count();
+ N.updateParameterMapping(
+ std::move(OccurringIndices), std::move(OccurringIndicesForSubsumption),
+ MutableArrayRef<TemplateArgumentLoc>{TempArgs, Size}, UsedList);
+}
- TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
+bool SubstituteParameterMappings::substitute(
+ NormalizedConstraintWithParamMapping &N) {
+ if (!N.hasParameterMapping())
+ buildParameterMapping(N);
- AtomicConstraint &Atomic = *N.getAtomicConstraint();
- TemplateArgumentListInfo SubstArgs;
- if (!Atomic.ParameterMapping) {
- llvm::SmallBitVector OccurringIndices(TemplateParams->size());
- S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
- /*Depth=*/0, OccurringIndices);
- TemplateArgumentLoc *TempArgs =
- new (S.Context) TemplateArgumentLoc[OccurringIndices.count()];
- for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
- if (OccurringIndices[I])
- new (&(TempArgs)[J++])
- TemplateArgumentLoc(S.getIdentityTemplateArgumentLoc(
- TemplateParams->begin()[I],
- // Here we assume we do not support things like
- // template<typename A, typename B>
- // concept C = ...;
- //
- // template<typename... Ts> requires C<Ts...>
- // struct S { };
- // The above currently yields a diagnostic.
- // We still might have default arguments for concept parameters.
- ArgsAsWritten->NumTemplateArgs > I
- ? ArgsAsWritten->arguments()[I].getLocation()
- : SourceLocation()));
- Atomic.ParameterMapping.emplace(TempArgs, OccurringIndices.count());
- }
- SourceLocation InstLocBegin =
- ArgsAsWritten->arguments().empty()
- ? ArgsAsWritten->getLAngleLoc()
- : ArgsAsWritten->arguments().front().getSourceRange().getBegin();
- SourceLocation InstLocEnd =
- ArgsAsWritten->arguments().empty()
- ? ArgsAsWritten->getRAngleLoc()
- : ArgsAsWritten->arguments().front().getSourceRange().getEnd();
+ SourceLocation InstLocBegin, InstLocEnd;
+ llvm::ArrayRef Arguments = ArgsAsWritten->arguments();
+ if (Arguments.empty()) {
+ InstLocBegin = ArgsAsWritten->getLAngleLoc();
+ InstLocEnd = ArgsAsWritten->getRAngleLoc();
+ } else {
+ auto SR = Arguments[0].getSourceRange();
+ InstLocBegin = SR.getBegin();
+ InstLocEnd = SR.getEnd();
+ }
Sema::InstantiatingTemplate Inst(
- S, InstLocBegin,
+ SemaRef, InstLocBegin,
Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
- const_cast<NamedDecl *>(Atomic.ConstraintDecl),
+ const_cast<NamedDecl *>(N.getConstraintDecl()),
{InstLocBegin, InstLocEnd});
if (Inst.isInvalid())
return true;
- if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
+
+ // TransformTemplateArguments is unable to preserve the source location of a
+ // pack. The SourceLocation is necessary for the instantiation location.
+ // FIXME: The BaseLoc will be used as the location of the pack expansion,
+ // which is wrong.
+ TemplateArgumentListInfo SubstArgs;
+ if (SemaRef.SubstTemplateArgumentsInParameterMapping(
+ N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs,
+ /*BuildPackExpansionTypes=*/!InFoldExpr))
+ return true;
+ Sema::CheckTemplateArgumentInfo CTAI;
+ auto *TD =
+ const_cast<TemplateDecl *>(cast<TemplateDecl>(N.getConstraintDecl()));
+ if (SemaRef.CheckTemplateArgumentList(TD, N.getUsedTemplateParamList(),
+ TD->getLocation(), SubstArgs,
+ /*DefaultArguments=*/{},
+ /*PartialTemplateArgs=*/false, CTAI))
return true;
TemplateArgumentLoc *TempArgs =
- new (S.Context) TemplateArgumentLoc[SubstArgs.size()];
- std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(),
- TempArgs);
- Atomic.ParameterMapping.emplace(TempArgs, SubstArgs.size());
+ new (SemaRef.Context) TemplateArgumentLoc[CTAI.SugaredConverted.size()];
+
+ for (unsigned I = 0; I < CTAI.SugaredConverted.size(); ++I) {
+ SourceLocation Loc;
+ // If this is an empty pack, we have no corresponding SubstArgs.
+ if (I < SubstArgs.size())
+ Loc = SubstArgs.arguments()[I].getLocation();
+
+ TempArgs[I] = SemaRef.getTrivialTemplateArgumentLoc(
+ CTAI.SugaredConverted[I], QualType(), Loc);
+ }
+
+ MutableArrayRef<TemplateArgumentLoc> Mapping(TempArgs,
+ CTAI.SugaredConverted.size());
+ N.updateParameterMapping(N.mappingOccurenceList(),
+ N.mappingOccurenceListForSubsumption(), Mapping,
+ N.getUsedTemplateParamList());
return false;
}
-static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
- const ConceptSpecializationExpr *CSE) {
- MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
- CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(),
- /*Final=*/false, CSE->getTemplateArguments(),
- /*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/true);
+bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) {
+ assert(CC.getConstraintDecl() && MLTAL && ArgsAsWritten);
- return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
- CSE->getTemplateArgsAsWritten());
-}
+ if (substitute(static_cast<NormalizedConstraintWithParamMapping &>(CC)))
+ return true;
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
- NormalizedConstraint LHS,
- NormalizedConstraint RHS,
- CompoundConstraintKind Kind)
- : Constraint{CompoundConstraint{
- new(C) NormalizedConstraintPair{std::move(LHS), std::move(RHS)},
- Kind}} {}
-
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
- const NormalizedConstraint &Other) {
- if (Other.isAtomic()) {
- Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
- } else if (Other.isFoldExpanded()) {
- Constraint = new (C) FoldExpandedConstraint(
- Other.getFoldExpandedConstraint()->Kind,
- NormalizedConstraint(C, Other.getFoldExpandedConstraint()->Constraint),
- Other.getFoldExpandedConstraint()->Pattern);
+ auto *CSE = CC.getConceptSpecializationExpr();
+ assert(CSE);
+ assert(!CC.getBeginLoc().isInvalid());
+
+ SourceLocation InstLocBegin, InstLocEnd;
+ if (llvm::ArrayRef Arguments = ArgsAsWritten->arguments();
+ Arguments.empty()) {
+ InstLocBegin = ArgsAsWritten->getLAngleLoc();
+ InstLocEnd = ArgsAsWritten->getRAngleLoc();
} else {
- Constraint = CompoundConstraint(
- new (C)
- NormalizedConstraintPair{NormalizedConstraint(C, Other.getLHS()),
- NormalizedConstraint(C, Other.getRHS())},
- Other.getCompoundKind());
+ auto SR = Arguments[0].getSourceRange();
+ InstLocBegin = SR.getBegin();
+ InstLocEnd = SR.getEnd();
}
-}
+ // This is useful for name lookup across modules; see Sema::getLookupModules.
+ Sema::InstantiatingTemplate Inst(
+ SemaRef, InstLocBegin,
+ Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
+ const_cast<NamedDecl *>(CC.getConstraintDecl()),
+ {InstLocBegin, InstLocEnd});
+ if (Inst.isInvalid())
+ return true;
-NormalizedConstraint &NormalizedConstraint::getLHS() const {
- assert(isCompound() && "getLHS called on a non-compound constraint.");
- return cast<CompoundConstraint>(Constraint).getPointer()->LHS;
+ TemplateArgumentListInfo Out;
+ // TransformTemplateArguments is unable to preserve the source location of a
+ // pack. The SourceLocation is necessary for the instantiation location.
+ // FIXME: The BaseLoc will be used as the location of the pack expansion,
+ // which is wrong.
+ const ASTTemplateArgumentListInfo *ArgsAsWritten =
+ CSE->getTemplateArgsAsWritten();
+ if (SemaRef.SubstTemplateArgumentsInParameterMapping(
+ ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out,
+ /*BuildPackExpansionTypes=*/!InFoldExpr))
+ return true;
+ Sema::CheckTemplateArgumentInfo CTAI;
+ if (SemaRef.CheckTemplateArgumentList(CSE->getNamedConcept(),
+ CSE->getConceptNameInfo().getLoc(), Out,
+ /*DefaultArgs=*/{},
+ /*PartialTemplateArgs=*/false, CTAI,
+ /*UpdateArgsWithConversions=*/false))
+ return true;
+ auto TemplateArgs = *MLTAL;
+ TemplateArgs.replaceOutermostTemplateArguments(
+ TemplateArgs.getAssociatedDecl(0).first, CTAI.SugaredConverted);
+ return SubstituteParameterMappings(SemaRef, &TemplateArgs, ArgsAsWritten,
+ InFoldExpr)
+ .substitute(CC.getNormalizedConstraint());
}
-NormalizedConstraint &NormalizedConstraint::getRHS() const {
- assert(isCompound() && "getRHS called on a non-compound constraint.");
- return cast<CompoundConstraint>(Constraint).getPointer()->RHS;
+bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
+ switch (N.getKind()) {
+ case NormalizedConstraint::ConstraintKind::Atomic: {
+ if (!MLTAL) {
+ assert(!ArgsAsWritten);
+ return false;
+ }
+ return substitute(static_cast<NormalizedConstraintWithParamMapping &>(N));
+ }
+ case NormalizedConstraint::ConstraintKind::FoldExpanded: {
+ auto &FE = static_cast<FoldExpandedConstraint &>(N);
+ if (!MLTAL) {
+ llvm::SaveAndRestore _1(InFoldExpr, true);
+ assert(!ArgsAsWritten);
+ return substitute(FE.getNormalizedPattern());
+ }
+ Sema::ArgPackSubstIndexRAII _(SemaRef, std::nullopt);
+ substitute(static_cast<NormalizedConstraintWithParamMapping &>(FE));
+ return SubstituteParameterMappings(SemaRef, /*InFoldExpr=*/true)
+ .substitute(FE.getNormalizedPattern());
+ }
+ case NormalizedConstraint::ConstraintKind::ConceptId: {
+ auto &CC = static_cast<ConceptIdConstraint &>(N);
+ if (MLTAL) {
+ assert(ArgsAsWritten);
+ return substitute(CC);
+ }
+ assert(!ArgsAsWritten);
+ const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr();
+ ConceptDecl *Concept = CSE->getNamedConcept();
+ MultiLevelTemplateArgumentList MLTAL = SemaRef.getTemplateInstantiationArgs(
+ Concept, Concept->getLexicalDeclContext(),
+ /*Final=*/true, CSE->getTemplateArguments(),
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true);
+
+ return SubstituteParameterMappings(
+ SemaRef, &MLTAL, CSE->getTemplateArgsAsWritten(), InFoldExpr)
+ .substitute(CC.getNormalizedConstraint());
+ }
+ case NormalizedConstraint::ConstraintKind::Compound: {
+ auto &Compound = static_cast<CompoundConstraint &>(N);
+ if (substitute(Compound.getLHS()))
+ return true;
+ return substitute(Compound.getRHS());
+ }
+ }
}
-std::optional<NormalizedConstraint>
-NormalizedConstraint::fromAssociatedConstraints(
+} // namespace
+
+NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints(
Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) {
assert(ACs.size() != 0);
- auto Conjunction = fromConstraintExpr(S, D, ACs[0].ConstraintExpr);
+ auto *Conjunction =
+ fromConstraintExpr(S, D, ACs[0].ConstraintExpr, ACs[0].ArgPackSubstIndex);
if (!Conjunction)
- return std::nullopt;
+ return nullptr;
for (unsigned I = 1; I < ACs.size(); ++I) {
- auto Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr);
+ auto *Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr,
+ ACs[I].ArgPackSubstIndex);
if (!Next)
- return std::nullopt;
- *Conjunction = NormalizedConstraint(S.Context, std::move(*Conjunction),
- std::move(*Next), CCK_Conjunction);
+ return nullptr;
+ Conjunction = CompoundConstraint::CreateConjunction(S.getASTContext(),
+ Conjunction, Next);
}
return Conjunction;
}
-std::optional<NormalizedConstraint>
-NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
- const Expr *E) {
+NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
+ Sema &S, const NamedDecl *D, const Expr *E, UnsignedOrNone SubstIndex) {
assert(E != nullptr);
// C++ [temp.constr.normal]p1.1
@@ -1597,23 +2170,29 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// [...]
E = E->IgnoreParenImpCasts();
+ llvm::FoldingSetNodeID ID;
+ if (D && DiagRecursiveConstraintEval(S, ID, D, E)) {
+ return nullptr;
+ }
+ SatisfactionStackRAII StackRAII(S, D, ID);
+
// C++2a [temp.param]p4:
// [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
// Fold expression is considered atomic constraints per current wording.
// See http://cplusplus.github.io/concepts-ts/ts-active.html#28
if (LogicalBinOp BO = E) {
- auto LHS = fromConstraintExpr(S, D, BO.getLHS());
+ auto *LHS = fromConstraintExpr(S, D, BO.getLHS(), SubstIndex);
if (!LHS)
- return std::nullopt;
- auto RHS = fromConstraintExpr(S, D, BO.getRHS());
+ return nullptr;
+ auto *RHS = fromConstraintExpr(S, D, BO.getRHS(), SubstIndex);
if (!RHS)
- return std::nullopt;
+ return nullptr;
- return NormalizedConstraint(S.Context, std::move(*LHS), std::move(*RHS),
- BO.isAnd() ? CCK_Conjunction : CCK_Disjunction);
+ return CompoundConstraint::Create(
+ S.Context, LHS, BO.isAnd() ? CCK_Conjunction : CCK_Disjunction, RHS);
} else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) {
- const NormalizedConstraint *SubNF;
+ NormalizedConstraint *SubNF;
{
Sema::InstantiatingTemplate Inst(
S, CSE->getExprLoc(),
@@ -1621,7 +2200,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// FIXME: improve const-correctness of InstantiatingTemplate
const_cast<NamedDecl *>(D), CSE->getSourceRange());
if (Inst.isInvalid())
- return std::nullopt;
+ return nullptr;
// C++ [temp.constr.normal]p1.1
// [...]
// The normal form of an id-expression of the form C<A1, A2, ..., AN>,
@@ -1631,20 +2210,21 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// constraint. If any such substitution results in an invalid type or
// expression, the program is ill-formed; no diagnostic is required.
// [...]
- ConceptDecl *CD = CSE->getNamedConcept();
- SubNF = S.getNormalizedAssociatedConstraints(
- CD, AssociatedConstraint(CD->getConstraintExpr()));
+
+ // Use canonical declarations to merge ConceptDecls across
+ // different modules.
+ ConceptDecl *CD = CSE->getNamedConcept()->getCanonicalDecl();
+ SubNF = NormalizedConstraint::fromAssociatedConstraints(
+ S, CD, AssociatedConstraint(CD->getConstraintExpr(), SubstIndex));
+
if (!SubNF)
- return std::nullopt;
+ return nullptr;
}
- std::optional<NormalizedConstraint> New;
- New.emplace(S.Context, *SubNF);
-
- if (substituteParameterMappings(S, *New, CSE))
- return std::nullopt;
+ return ConceptIdConstraint::Create(S.getASTContext(),
+ CSE->getConceptReference(), SubNF, D,
+ CSE, SubstIndex);
- return New;
} else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
FE && S.getLangOpts().CPlusPlus26 &&
(FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
@@ -1658,31 +2238,61 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
: FoldExpandedConstraint::FoldOperatorKind::Or;
if (FE->getInit()) {
- auto LHS = fromConstraintExpr(S, D, FE->getLHS());
- auto RHS = fromConstraintExpr(S, D, FE->getRHS());
+ auto *LHS = fromConstraintExpr(S, D, FE->getLHS(), SubstIndex);
+ auto *RHS = fromConstraintExpr(S, D, FE->getRHS(), SubstIndex);
if (!LHS || !RHS)
- return std::nullopt;
+ return nullptr;
if (FE->isRightFold())
- RHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*RHS), FE->getPattern()}};
+ LHS = FoldExpandedConstraint::Create(S.getASTContext(),
+ FE->getPattern(), D, Kind, LHS);
else
- LHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*LHS), FE->getPattern()}};
-
- return NormalizedConstraint(
- S.Context, std::move(*LHS), std::move(*RHS),
- FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
- : CCK_Disjunction);
+ RHS = FoldExpandedConstraint::Create(S.getASTContext(),
+ FE->getPattern(), D, Kind, RHS);
+
+ return CompoundConstraint::Create(
+ S.getASTContext(), LHS,
+ (FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
+ : CCK_Disjunction),
+ RHS);
}
- auto Sub = fromConstraintExpr(S, D, FE->getPattern());
+ auto *Sub = fromConstraintExpr(S, D, FE->getPattern(), SubstIndex);
if (!Sub)
- return std::nullopt;
- return NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*Sub), FE->getPattern()}};
+ return nullptr;
+ return FoldExpandedConstraint::Create(S.getASTContext(), FE->getPattern(),
+ D, Kind, Sub);
}
+ return AtomicConstraint::Create(S.getASTContext(), E, D, SubstIndex);
+}
- return NormalizedConstraint{new (S.Context) AtomicConstraint(E, D)};
+const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
+ ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq,
+ ArrayRef<AssociatedConstraint> AssociatedConstraints) {
+ if (!ConstrainedDeclOrNestedReq) {
+ auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
+ *this, nullptr, AssociatedConstraints);
+ if (!Normalized ||
+ SubstituteParameterMappings(*this).substitute(*Normalized))
+ return nullptr;
+
+ return Normalized;
+ }
+
+ // FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement!
+ const NamedDecl *ND =
+ ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>();
+ auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq);
+ if (CacheEntry == NormalizationCache.end()) {
+ auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
+ *this, ND, AssociatedConstraints);
+ CacheEntry =
+ NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
+ .first;
+ if (!Normalized ||
+ SubstituteParameterMappings(*this).substitute(*Normalized))
+ return nullptr;
+ }
+ return CacheEntry->second;
}
bool FoldExpandedConstraint::AreCompatibleForSubsumption(
@@ -1693,8 +2303,10 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption(
// if their respective constraints both contain an equivalent unexpanded pack.
llvm::SmallVector<UnexpandedParameterPack> APacks, BPacks;
- Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.Pattern), APacks);
- Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks);
+ Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.getPattern()),
+ APacks);
+ Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.getPattern()),
+ BPacks);
for (const UnexpandedParameterPack &APack : APacks) {
auto ADI = getDepthAndIndex(APack);
@@ -1788,7 +2400,7 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
const AtomicConstraint &B) {
if (!A.hasMatchingParameterMapping(Context, B))
return false;
- const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr;
+ const Expr *EA = A.getConstraintExpr(), *EB = B.getConstraintExpr();
if (EA == EB)
return true;
@@ -1841,24 +2453,6 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
return true;
}
-NormalizedConstraint::CompoundConstraintKind
-NormalizedConstraint::getCompoundKind() const {
- assert(isCompound() && "getCompoundKind on a non-compound constraint..");
- return cast<CompoundConstraint>(Constraint).getInt();
-}
-
-AtomicConstraint *NormalizedConstraint::getAtomicConstraint() const {
- assert(isAtomic() && "getAtomicConstraint called on non-atomic constraint.");
- return cast<AtomicConstraint *>(Constraint);
-}
-
-FoldExpandedConstraint *
-NormalizedConstraint::getFoldExpandedConstraint() const {
- assert(isFoldExpanded() &&
- "getFoldExpandedConstraint called on non-fold-expanded constraint.");
- return cast<FoldExpandedConstraint *>(Constraint);
-}
-
//
//
// ------------------------ Subsumption -----------------------------------
@@ -1874,8 +2468,8 @@ uint16_t SubsumptionChecker::getNewLiteralId() {
return NextID++;
}
-auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
- auto &Elems = AtomicMap[Ori->ConstraintExpr];
+auto SubsumptionChecker::find(const AtomicConstraint *Ori) -> Literal {
+ auto &Elems = AtomicMap[Ori->getConstraintExpr()];
// C++ [temp.constr.order] p2
// - an atomic constraint A subsumes another atomic constraint B
// if and only if the A and B are identical [...]
@@ -1891,13 +2485,16 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
// subsumes another, their literal will be the same
llvm::FoldingSetNodeID ID;
- const auto &Mapping = Ori->ParameterMapping;
- ID.AddBoolean(Mapping.has_value());
- if (Mapping) {
- for (const TemplateArgumentLoc &TAL : *Mapping) {
- SemaRef.getASTContext()
- .getCanonicalTemplateArgument(TAL.getArgument())
- .Profile(ID, SemaRef.getASTContext());
+ ID.AddBoolean(Ori->hasParameterMapping());
+ if (Ori->hasParameterMapping()) {
+ const auto &Mapping = Ori->getParameterMapping();
+ const NormalizedConstraint::OccurenceList &Indexes =
+ Ori->mappingOccurenceListForSubsumption();
+ for (auto [Idx, TAL] : llvm::enumerate(Mapping)) {
+ if (Indexes[Idx])
+ SemaRef.getASTContext()
+ .getCanonicalTemplateArgument(TAL.getArgument())
+ .Profile(ID, SemaRef.getASTContext());
}
}
auto It = Elems.find(ID);
@@ -1912,11 +2509,11 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
return It->getSecond().ID;
}
-auto SubsumptionChecker::find(FoldExpandedConstraint *Ori) -> Literal {
- auto &Elems = FoldMap[Ori->Pattern];
+auto SubsumptionChecker::find(const FoldExpandedConstraint *Ori) -> Literal {
+ auto &Elems = FoldMap[Ori->getPattern()];
FoldExpendedConstraintKey K;
- K.Kind = Ori->Kind;
+ K.Kind = Ori->getFoldOperator();
auto It = llvm::find_if(Elems, [&K](const FoldExpendedConstraintKey &Other) {
return K.Kind == Other.Kind;
@@ -1960,38 +2557,47 @@ FormulaType SubsumptionChecker::Normalize(const NormalizedConstraint &NC) {
AddUniqueClauseToFormula(Res, std::move(C));
};
- if (NC.isAtomic())
- return {{find(NC.getAtomicConstraint())}};
+ switch (NC.getKind()) {
- if (NC.isFoldExpanded())
- return {{find(NC.getFoldExpandedConstraint())}};
+ case NormalizedConstraint::ConstraintKind::Atomic:
+ return {{find(&static_cast<const AtomicConstraint &>(NC))}};
- FormulaType Left, Right;
- SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] {
- Left = Normalize<FormulaType>(NC.getLHS());
- Right = Normalize<FormulaType>(NC.getRHS());
- });
+ case NormalizedConstraint::ConstraintKind::FoldExpanded:
+ return {{find(&static_cast<const FoldExpandedConstraint &>(NC))}};
- if (NC.getCompoundKind() == FormulaType::Kind) {
- auto SizeLeft = Left.size();
- Res = std::move(Left);
- Res.reserve(SizeLeft + Right.size());
- std::for_each(std::make_move_iterator(Right.begin()),
- std::make_move_iterator(Right.end()), Add);
- return Res;
- }
+ case NormalizedConstraint::ConstraintKind::ConceptId:
+ return Normalize<FormulaType>(
+ static_cast<const ConceptIdConstraint &>(NC).getNormalizedConstraint());
+
+ case NormalizedConstraint::ConstraintKind::Compound: {
+ const auto &Compound = static_cast<const CompoundConstraint &>(NC);
+ FormulaType Left, Right;
+ SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] {
+ Left = Normalize<FormulaType>(Compound.getLHS());
+ Right = Normalize<FormulaType>(Compound.getRHS());
+ });
+
+ if (Compound.getCompoundKind() == FormulaType::Kind) {
+ Res = std::move(Left);
+ Res.reserve(Left.size() + Right.size());
+ std::for_each(std::make_move_iterator(Right.begin()),
+ std::make_move_iterator(Right.end()), Add);
+ return Res;
+ }
- Res.reserve(Left.size() * Right.size());
- for (const auto &LTransform : Left) {
- for (const auto &RTransform : Right) {
- Clause Combined;
- Combined.reserve(LTransform.size() + RTransform.size());
- llvm::append_range(Combined, LTransform);
- llvm::append_range(Combined, RTransform);
- Add(std::move(Combined));
+ Res.reserve(Left.size() * Right.size());
+ for (const auto &LTransform : Left) {
+ for (const auto &RTransform : Right) {
+ Clause Combined;
+ Combined.reserve(LTransform.size() + RTransform.size());
+ llvm::copy(LTransform, std::back_inserter(Combined));
+ llvm::copy(RTransform, std::back_inserter(Combined));
+ Add(std::move(Combined));
+ }
}
+ return Res;
+ }
}
- return Res;
}
void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) {
@@ -2006,12 +2612,12 @@ std::optional<bool> SubsumptionChecker::Subsumes(
const NamedDecl *DP, ArrayRef<AssociatedConstraint> P, const NamedDecl *DQ,
ArrayRef<AssociatedConstraint> Q) {
const NormalizedConstraint *PNormalized =
- getNormalizedAssociatedConstraints(SemaRef, DP, P);
+ SemaRef.getNormalizedAssociatedConstraints(DP, P);
if (!PNormalized)
return std::nullopt;
const NormalizedConstraint *QNormalized =
- getNormalizedAssociatedConstraints(SemaRef, DQ, Q);
+ SemaRef.getNormalizedAssociatedConstraints(DQ, Q);
if (!QNormalized)
return std::nullopt;
@@ -2061,9 +2667,9 @@ bool SubsumptionChecker::Subsumes(const FoldExpandedConstraint *A,
// constraint B if they are compatible for subsumption, have the same
// fold-operator, and the constraint of A subsumes that of B.
bool DoesSubsume =
- A->Kind == B->Kind &&
+ A->getFoldOperator() == B->getFoldOperator() &&
FoldExpandedConstraint::AreCompatibleForSubsumption(*A, *B) &&
- Subsumes(&A->Constraint, &B->Constraint);
+ Subsumes(&A->getNormalizedPattern(), &B->getNormalizedPattern());
It = FoldSubsumptionCache.try_emplace(std::move(Key), DoesSubsume).first;
}
return It->second;