Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -14801,7 +14801,8 @@ class Sema final : public SemaBase {
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
const ConceptReference *TopLevelConceptId = nullptr);
const ConceptReference *TopLevelConceptId = nullptr,
Expr **ConvertedExpr = nullptr);

/// \brief Check whether the given non-dependent constraint expression is
/// satisfied. Returns false and updates Satisfaction with the satisfaction
Expand Down
204 changes: 113 additions & 91 deletions clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/WithColor.h"
#include <cstddef>
#include <optional>

Expand Down Expand Up @@ -345,7 +343,7 @@ static ExprResult EvaluateAtomicConstraint(
return SubstitutedExpression;
}

static bool calculateConstraintSatisfaction(
static ExprResult calculateConstraintSatisfaction(
Sema &S, const NormalizedConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction, UnsignedOrNone PackSubstitutionIndex);
Expand Down Expand Up @@ -417,7 +415,7 @@ SubstitutionInTemplateArguments(
return std::move(MLTAL);
}

static bool calculateConstraintSatisfaction(
static ExprResult calculateConstraintSatisfaction(
Sema &S, const AtomicConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction,
Expand Down Expand Up @@ -497,7 +495,7 @@ static bool calculateConstraintSatisfaction(
if (!Satisfaction.IsSatisfied)
Satisfaction.Details.emplace_back(SubstitutedAtomicExpr.get());

return SubstitutedAtomicExpr.isUsable();
return SubstitutedAtomicExpr;
}

static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
Expand Down Expand Up @@ -533,7 +531,7 @@ static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
return NumExpansions;
}

static bool calculateConstraintSatisfaction(
static ExprResult calculateConstraintSatisfaction(
Sema &S, const FoldExpandedConstraint &FE, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction) {
Expand Down Expand Up @@ -568,12 +566,13 @@ static bool calculateConstraintSatisfaction(
Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
Satisfaction.IsSatisfied = false;
Satisfaction.ContainsErrors = false;
bool Success = calculateConstraintSatisfaction(
// FIXME
ExprResult Expr = calculateConstraintSatisfaction(
S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
*SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
// SFINAE errors shouldn't prevent disjunction from evaluating
// FIXME: Does !Success == SFINAE errors occurred?
if (!Success && Conjunction)
if (!Expr.isUsable() && Conjunction)
return false;
if (!Conjunction && Satisfaction.IsSatisfied) {
Satisfaction.Details.erase(Satisfaction.Details.begin() +
Expand All @@ -589,25 +588,12 @@ static bool calculateConstraintSatisfaction(
return true;
}

static bool calculateConstraintSatisfaction(
static ExprResult calculateConstraintSatisfaction(
Sema &S, const ConceptIdConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction,
UnsignedOrNone PackSubstitutionIndex) {

// If the expression has been calculated, e.g. when we are in a nested
// requirement, do not compute it repeatedly.
if (auto *Expr = Constraint.getConceptSpecializationExpr();
Expr && Expr->getDependence() == ExprDependence::None) {
auto &Calculated = Expr->getSatisfaction();
Satisfaction.ContainsErrors = Calculated.ContainsErrors;
Satisfaction.IsSatisfied = Calculated.IsSatisfied;
Satisfaction.Details.insert(Satisfaction.Details.end(),
Calculated.records().begin(),
Calculated.records().end());
return !Satisfaction.ContainsErrors;
}

Sema::ContextRAII CurContext(
S, Constraint.getConceptId()->getNamedConcept()->getDeclContext(),
/*NewThisContext=*/false);
Expand All @@ -620,67 +606,73 @@ static bool calculateConstraintSatisfaction(

auto Size = Satisfaction.Details.size();

bool Ok = calculateConstraintSatisfaction(
ExprResult E = calculateConstraintSatisfaction(
S, Constraint.getNormalizedConstraint(), Template, TemplateNameLoc, MLTAL,
Satisfaction, PackSubstitutionIndex);

if (Size != Satisfaction.Details.size()) {
if (!E.isUsable())
return E;

llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
SubstitutedOuterMost,
PackSubstitutionIndex);
llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
SubstitutedOuterMost,
PackSubstitutionIndex);

if (!SubstitutedArgs)
return Ok;
if (!SubstitutedArgs)
return E.isUsable();

Sema::SFINAETrap Trap(S);
Sema::ArgPackSubstIndexRAII SubstIndex(
S, Constraint.getPackSubstitutionIndex()
? Constraint.getPackSubstitutionIndex()
: PackSubstitutionIndex);
Sema::SFINAETrap Trap(S);
Sema::ArgPackSubstIndexRAII SubstIndex(
S, Constraint.getPackSubstitutionIndex()
? Constraint.getPackSubstitutionIndex()
: PackSubstitutionIndex);

const ASTTemplateArgumentListInfo *Ori =
Constraint.getConceptId()->getTemplateArgsAsWritten();
TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc);

TemplateArgumentListInfo TransArgs(Ori->LAngleLoc, Ori->RAngleLoc);
unsigned Depth = Template && Template->getTemplateDepth()
? Template->getTemplateDepth() - 1
: 0;
AdjustConstraintDepth Adjust(S, Depth);
if (Adjust.TransformTemplateArguments(Ori->getTemplateArgs(),
Ori->NumTemplateArgs, TransArgs))
return false;

const ASTTemplateArgumentListInfo *Ori =
Constraint.getConceptId()->getTemplateArgsAsWritten();
TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc);

TemplateArgumentListInfo TransArgs(Ori->LAngleLoc, Ori->RAngleLoc);
unsigned Depth = Template && Template->getTemplateDepth()
? Template->getTemplateDepth() - 1
: 0;
AdjustConstraintDepth Adjust(S, Depth);
if (Adjust.TransformTemplateArguments(Ori->getTemplateArgs(),
Ori->NumTemplateArgs, TransArgs))
return Ok;

if (S.SubstTemplateArguments(TransArgs.arguments(), *SubstitutedArgs,
OutArgs) ||
Trap.hasErrorOccurred())
return Ok;
if (S.SubstTemplateArguments(TransArgs.arguments(), *SubstitutedArgs,
OutArgs) ||
Trap.hasErrorOccurred()) {
Satisfaction.ContainsErrors = true;
Satisfaction.IsSatisfied = false;
return false;
}

CXXScopeSpec SS;
SS.Adopt(Constraint.getConceptId()->getNestedNameSpecifierLoc());
ExprResult SubstitutedConceptId = S.CheckConceptTemplateId(
SS, Constraint.getConceptId()->getTemplateKWLoc(),
Constraint.getConceptId()->getConceptNameInfo(),
Constraint.getConceptId()->getFoundDecl(),
Constraint.getConceptId()->getNamedConcept(), &OutArgs,
/*CheckConstraintSatisfaction=*/false);
CXXScopeSpec SS;
SS.Adopt(Constraint.getConceptId()->getNestedNameSpecifierLoc());
ExprResult SubstitutedConceptId = S.CheckConceptTemplateId(
SS, Constraint.getConceptId()->getTemplateKWLoc(),
Constraint.getConceptId()->getConceptNameInfo(),
Constraint.getConceptId()->getFoundDecl(),
Constraint.getConceptId()->getNamedConcept(), &OutArgs,
/*CheckConstraintSatisfaction=*/false);

if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
return Ok;
if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
return false;

if (Size != Satisfaction.Details.size()) {

Satisfaction.Details.insert(
Satisfaction.Details.begin() + Size,
UnsatisfiedConstraintRecord(
SubstitutedConceptId.getAs<ConceptSpecializationExpr>()
->getConceptReference()));
}
return Ok;
return SubstitutedConceptId;
}

static bool calculateConstraintSatisfaction(
static ExprResult calculateConstraintSatisfaction(
Sema &S, const CompoundConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction,
Expand All @@ -691,35 +683,49 @@ static bool calculateConstraintSatisfaction(
bool Conjunction =
Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction;

bool Ok = calculateConstraintSatisfaction(
ExprResult LHS = calculateConstraintSatisfaction(
S, Constraint.getLHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);

if (Conjunction && !Ok)
if (Conjunction && !LHS.isUsable())
return false;

if (!Conjunction && Ok && Satisfaction.IsSatisfied &&
if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied &&
!Satisfaction.ContainsErrors)
return true;
return LHS;

if (Conjunction && Ok &&
if (Conjunction && LHS.isUsable() &&
(!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
return true;
return LHS;

Satisfaction.ContainsErrors = false;
Satisfaction.IsSatisfied = false;

Ok = calculateConstraintSatisfaction(S, Constraint.getRHS(), Template,
TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
if (Ok && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
ExprResult RHS = calculateConstraintSatisfaction(
S, Constraint.getRHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
if (RHS.isUsable() && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
Satisfaction.Details.erase(Satisfaction.Details.begin() +
EffectiveDetailEndIndex,
Satisfaction.Details.end());
return Ok;

if (!LHS.isUsable())
return RHS;

if (!RHS.isUsable())
return LHS;

return BinaryOperator::Create(
S.Context, LHS.get(), RHS.get(),
BinaryOperator::getOverloadedOpcode(
Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction
? OO_AmpAmp
: OO_PipePipe),
S.Context.BoolTy, VK_PRValue, OK_Ordinary, Constraint.getBeginLoc(),
FPOptionsOverride{});
}

static bool calculateConstraintSatisfaction(
static ExprResult calculateConstraintSatisfaction(
Sema &S, const NormalizedConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction,
Expand Down Expand Up @@ -753,7 +759,10 @@ static bool CheckConstraintSatisfaction(
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
const ConceptReference *TopLevelConceptId = nullptr) {
Expr **ConvertedExpr, const ConceptReference *TopLevelConceptId = nullptr) {

if (ConvertedExpr)
*ConvertedExpr = nullptr;

if (AssociatedConstraints.empty()) {
Satisfaction.IsSatisfied = true;
Expand Down Expand Up @@ -792,26 +801,31 @@ static bool CheckConstraintSatisfaction(
S.ArgPackSubstIndex);
}

return !calculateConstraintSatisfaction(
S, *C, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
Satisfaction, S.ArgPackSubstIndex);
ExprResult Res = calculateConstraintSatisfaction(
S, *C, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
Satisfaction, S.ArgPackSubstIndex);

if (ConvertedExpr)
*ConvertedExpr = Res.get();

return !Res.isUsable();
}

bool Sema::CheckConstraintSatisfaction(
ConstrainedDeclOrNestedRequirement Entity,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction,
const ConceptReference *TopLevelConceptId) {
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,
TemplateArgsLists, TemplateIDRange,
OutSatisfaction, TopLevelConceptId);
return ::CheckConstraintSatisfaction(
*this, nullptr, AssociatedConstraints, TemplateArgsLists,
TemplateIDRange, OutSatisfaction, ConvertedExpr, TopLevelConceptId);
}
// Invalid templates could make their way here. Substituting them could result
// in dependent expressions.
Expand Down Expand Up @@ -842,13 +856,15 @@ bool Sema::CheckConstraintSatisfaction(

auto Satisfaction =
std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs);
if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints,
TemplateArgsLists, TemplateIDRange,
*Satisfaction, TopLevelConceptId)) {
if (::CheckConstraintSatisfaction(
*this, Template, AssociatedConstraints, TemplateArgsLists,
TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) {
OutSatisfaction = *Satisfaction;
return true;
}

// FIXME: cache ConvertedExpr for nested requirements?

if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
// The evaluation of this constraint resulted in us trying to re-evaluate it
// recursively. This isn't really possible, except we try to form a
Expand Down Expand Up @@ -1595,7 +1611,8 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I
? ArgsAsWritten->arguments()[I].getLocation()
: SourceLocation();
// FIXME: Investigate when we couldn't preserve the SourceLoc. What shall we do??
// FIXME: Investigate when we couldn't preserve the SourceLoc. What shall
// we do??
// assert(Loc.isValid());
if (OccurringIndices[I]) {
NamedDecl *Param = TemplateParams->begin()[I];
Expand Down Expand Up @@ -1928,9 +1945,14 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq,
ArrayRef<AssociatedConstraint> AssociatedConstraints) {
if (!ConstrainedDeclOrNestedReq)
return NormalizedConstraint::fromAssociatedConstraints(
if (!ConstrainedDeclOrNestedReq) {
auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
*this, nullptr, AssociatedConstraints);
if (!Normalized || substituteParameterMappings(*this, *Normalized))
return nullptr;

return Normalized;
}

// FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement!
const NamedDecl *ND =
Expand Down
Loading