Skip to content

Commit 30e75b8

Browse files
committed
Reapply "[Clang] Implement CWG2369 "Ordering between constraints and substitution"" (llvm#122130)
This reverts commit 3972ed5.
1 parent cd57c95 commit 30e75b8

22 files changed

+291
-64
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13081,6 +13081,7 @@ class Sema final : public SemaBase {
1308113081
///
1308213082
/// \param SkipForSpecialization when specified, any template specializations
1308313083
/// in a traversal would be ignored.
13084+
///
1308413085
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
1308513086
/// when encountering a specialized member function template, rather than
1308613087
/// returning immediately.
@@ -13092,6 +13093,17 @@ class Sema final : public SemaBase {
1309213093
bool SkipForSpecialization = false,
1309313094
bool ForDefaultArgumentSubstitution = false);
1309413095

13096+
/// Apart from storing the result to \p Result, this behaves the same as
13097+
/// another overload.
13098+
void getTemplateInstantiationArgs(
13099+
MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
13100+
const DeclContext *DC = nullptr, bool Final = false,
13101+
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
13102+
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
13103+
bool ForConstraintInstantiation = false,
13104+
bool SkipForSpecialization = false,
13105+
bool ForDefaultArgumentSubstitution = false);
13106+
1309513107
/// RAII object to handle the state changes required to synthesize
1309613108
/// a function body.
1309713109
class SynthesizedFunctionScope {
@@ -13363,7 +13375,7 @@ class Sema final : public SemaBase {
1336313375
ExprResult
1336413376
SubstConstraintExpr(Expr *E,
1336513377
const MultiLevelTemplateArgumentList &TemplateArgs);
13366-
// Unlike the above, this does not evaluates constraints.
13378+
// Unlike the above, this does not evaluate constraints.
1336713379
ExprResult SubstConstraintExprWithoutSatisfaction(
1336813380
Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
1336913381

@@ -14492,10 +14504,10 @@ class Sema final : public SemaBase {
1449214504
const MultiLevelTemplateArgumentList &TemplateArgs,
1449314505
SourceRange TemplateIDRange);
1449414506

14495-
bool CheckInstantiatedFunctionTemplateConstraints(
14496-
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
14497-
ArrayRef<TemplateArgument> TemplateArgs,
14498-
ConstraintSatisfaction &Satisfaction);
14507+
bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
14508+
FunctionDecl *Decl,
14509+
ArrayRef<TemplateArgument> TemplateArgs,
14510+
ConstraintSatisfaction &Satisfaction);
1449914511

1450014512
/// \brief Emit diagnostics explaining why a constraint expression was deemed
1450114513
/// unsatisfied.

clang/include/clang/Sema/Template.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,12 @@ enum class TemplateSubstitutionKind : char {
522522
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
523523
findInstantiationOf(const Decl *D);
524524

525+
/// Similar to \p findInstantiationOf(), but it wouldn't assert if the
526+
/// instantiation was not found within the current instantiation scope. This
527+
/// is helpful for on-demand declaration instantiation.
528+
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
529+
findInstantiationUnsafe(const Decl *D);
530+
525531
void InstantiatedLocal(const Decl *D, Decl *Inst);
526532
void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst);
527533
void MakeInstantiatedLocalArgPack(const Decl *D);

clang/lib/Sema/SemaConcept.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
846846
bool ForOverloadResolution) {
847847
// Don't check constraints if the function is dependent. Also don't check if
848848
// this is a function template specialization, as the call to
849-
// CheckinstantiatedFunctionTemplateConstraints after this will check it
849+
// CheckFunctionTemplateConstraints after this will check it
850850
// better.
851851
if (FD->isDependentContext() ||
852852
FD->getTemplatedKind() ==
@@ -1114,12 +1114,55 @@ bool Sema::EnsureTemplateArgumentListConstraints(
11141114
return false;
11151115
}
11161116

1117-
bool Sema::CheckInstantiatedFunctionTemplateConstraints(
1117+
static bool CheckFunctionConstraintsWithoutInstantiation(
1118+
Sema &SemaRef, SourceLocation PointOfInstantiation,
1119+
FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
1120+
ConstraintSatisfaction &Satisfaction) {
1121+
SmallVector<const Expr *, 3> TemplateAC;
1122+
Template->getAssociatedConstraints(TemplateAC);
1123+
if (TemplateAC.empty()) {
1124+
Satisfaction.IsSatisfied = true;
1125+
return false;
1126+
}
1127+
1128+
LocalInstantiationScope Scope(SemaRef);
1129+
1130+
FunctionDecl *FD = Template->getTemplatedDecl();
1131+
// Collect the list of template arguments relative to the 'primary'
1132+
// template. We need the entire list, since the constraint is completely
1133+
// uninstantiated at this point.
1134+
1135+
// FIXME: Add TemplateArgs through the 'Innermost' parameter once
1136+
// the refactoring of getTemplateInstantiationArgs() relands.
1137+
MultiLevelTemplateArgumentList MLTAL;
1138+
MLTAL.addOuterTemplateArguments(Template, std::nullopt, /*Final=*/false);
1139+
SemaRef.getTemplateInstantiationArgs(
1140+
MLTAL, /*D=*/FD, FD,
1141+
/*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
1142+
/*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
1143+
MLTAL.replaceInnermostTemplateArguments(Template, TemplateArgs);
1144+
1145+
Sema::ContextRAII SavedContext(SemaRef, FD);
1146+
std::optional<Sema::CXXThisScopeRAII> ThisScope;
1147+
if (auto *Method = dyn_cast<CXXMethodDecl>(FD))
1148+
ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(),
1149+
/*ThisQuals=*/Method->getMethodQualifiers());
1150+
return SemaRef.CheckConstraintSatisfaction(
1151+
Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
1152+
}
1153+
1154+
bool Sema::CheckFunctionTemplateConstraints(
11181155
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
11191156
ArrayRef<TemplateArgument> TemplateArgs,
11201157
ConstraintSatisfaction &Satisfaction) {
11211158
// In most cases we're not going to have constraints, so check for that first.
11221159
FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
1160+
1161+
if (!Template)
1162+
return ::CheckFunctionConstraintsWithoutInstantiation(
1163+
*this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(),
1164+
TemplateArgs, Satisfaction);
1165+
11231166
// Note - code synthesis context for the constraints check is created
11241167
// inside CheckConstraintsSatisfaction.
11251168
SmallVector<const Expr *, 3> TemplateAC;

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4053,18 +4053,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40534053
Result != TemplateDeductionResult::Success)
40544054
return Result;
40554055

4056-
// C++ [temp.deduct.call]p10: [DR1391]
4057-
// If deduction succeeds for all parameters that contain
4058-
// template-parameters that participate in template argument deduction,
4059-
// and all template arguments are explicitly specified, deduced, or
4060-
// obtained from default template arguments, remaining parameters are then
4061-
// compared with the corresponding arguments. For each remaining parameter
4062-
// P with a type that was non-dependent before substitution of any
4063-
// explicitly-specified template arguments, if the corresponding argument
4064-
// A cannot be implicitly converted to P, deduction fails.
4065-
if (CheckNonDependent())
4066-
return TemplateDeductionResult::NonDependentConversionFailure;
4067-
40684056
// Form the template argument list from the deduced template arguments.
40694057
TemplateArgumentList *SugaredDeducedArgumentList =
40704058
TemplateArgumentList::CreateCopy(Context, SugaredBuilder);
@@ -4094,6 +4082,39 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40944082
FD = const_cast<FunctionDecl *>(FDFriend);
40954083
Owner = FD->getLexicalDeclContext();
40964084
}
4085+
// C++20 [temp.deduct.general]p5: [CWG2369]
4086+
// If the function template has associated constraints, those constraints
4087+
// are checked for satisfaction. If the constraints are not satisfied, type
4088+
// deduction fails.
4089+
//
4090+
// FIXME: We haven't implemented CWG2369 for lambdas yet, because we need
4091+
// to figure out how to instantiate lambda captures to the scope without
4092+
// first instantiating the lambda.
4093+
bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
4094+
if (!IsLambda && !IsIncomplete) {
4095+
if (CheckFunctionTemplateConstraints(
4096+
Info.getLocation(),
4097+
FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
4098+
CanonicalBuilder, Info.AssociatedConstraintsSatisfaction))
4099+
return TemplateDeductionResult::MiscellaneousDeductionFailure;
4100+
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
4101+
Info.reset(Info.takeSugared(),
4102+
TemplateArgumentList::CreateCopy(Context, CanonicalBuilder));
4103+
return TemplateDeductionResult::ConstraintsNotSatisfied;
4104+
}
4105+
}
4106+
// C++ [temp.deduct.call]p10: [CWG1391]
4107+
// If deduction succeeds for all parameters that contain
4108+
// template-parameters that participate in template argument deduction,
4109+
// and all template arguments are explicitly specified, deduced, or
4110+
// obtained from default template arguments, remaining parameters are then
4111+
// compared with the corresponding arguments. For each remaining parameter
4112+
// P with a type that was non-dependent before substitution of any
4113+
// explicitly-specified template arguments, if the corresponding argument
4114+
// A cannot be implicitly converted to P, deduction fails.
4115+
if (CheckNonDependent())
4116+
return TemplateDeductionResult::NonDependentConversionFailure;
4117+
40974118
MultiLevelTemplateArgumentList SubstArgs(
40984119
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
40994120
/*Final=*/false);
@@ -4128,8 +4149,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
41284149
// ([temp.constr.decl]), those constraints are checked for satisfaction
41294150
// ([temp.constr.constr]). If the constraints are not satisfied, type
41304151
// deduction fails.
4131-
if (!IsIncomplete) {
4132-
if (CheckInstantiatedFunctionTemplateConstraints(
4152+
if (IsLambda && !IsIncomplete) {
4153+
if (CheckFunctionTemplateConstraints(
41334154
Info.getLocation(), Specialization, CanonicalBuilder,
41344155
Info.AssociatedConstraintsSatisfaction))
41354156
return TemplateDeductionResult::MiscellaneousDeductionFailure;

clang/lib/Sema/SemaTemplateDeductionGuide.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -942,10 +942,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
942942
Context.getTrivialTypeSourceInfo(
943943
Context.getDeducedTemplateSpecializationType(
944944
TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
945-
/*IsDependent=*/true)), // template specialization type whose
946-
// arguments will be deduced.
945+
/*IsDependent=*/true),
946+
AliasTemplate->getLocation()), // template specialization type whose
947+
// arguments will be deduced.
947948
Context.getTrivialTypeSourceInfo(
948-
ReturnType), // type from which template arguments are deduced.
949+
ReturnType, AliasTemplate->getLocation()), // type from which template
950+
// arguments are deduced.
949951
};
950952
return TypeTraitExpr::Create(
951953
Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 104 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,21 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
475475
assert((ND || DC) && "Can't find arguments for a decl if one isn't provided");
476476
// Accumulate the set of template argument lists in this structure.
477477
MultiLevelTemplateArgumentList Result;
478+
getTemplateInstantiationArgs(
479+
Result, ND, DC, Final, Innermost, RelativeToPrimary, Pattern,
480+
ForConstraintInstantiation, SkipForSpecialization,
481+
ForDefaultArgumentSubstitution);
482+
return Result;
483+
}
484+
485+
void Sema::getTemplateInstantiationArgs(
486+
MultiLevelTemplateArgumentList &Result, const NamedDecl *ND,
487+
const DeclContext *DC, bool Final,
488+
std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary,
489+
const FunctionDecl *Pattern, bool ForConstraintInstantiation,
490+
bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) {
491+
assert((ND || DC) && "Can't find arguments for a decl if one isn't provided");
492+
// Accumulate the set of template argument lists in this structure.
478493

479494
using namespace TemplateInstArgsHelpers;
480495
const Decl *CurDecl = ND;
@@ -535,14 +550,12 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
535550
}
536551

537552
if (R.IsDone)
538-
return Result;
553+
return;
539554
if (R.ClearRelativeToPrimary)
540555
RelativeToPrimary = false;
541556
assert(R.NextDecl);
542557
CurDecl = R.NextDecl;
543558
}
544-
545-
return Result;
546559
}
547560

548561
bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
@@ -1364,6 +1377,19 @@ namespace {
13641377
// Whether an incomplete substituion should be treated as an error.
13651378
bool BailOutOnIncomplete;
13661379

1380+
private:
1381+
bool isSubstitutingConstraints() const {
1382+
return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) {
1383+
return Context.Kind ==
1384+
Sema::CodeSynthesisContext::ConstraintSubstitution;
1385+
});
1386+
}
1387+
1388+
// CWG2770: Function parameters should be instantiated when they are
1389+
// needed by a satisfaction check of an atomic constraint or
1390+
// (recursively) by another function parameter.
1391+
bool maybeInstantiateFunctionParameterToScope(ParmVarDecl *OldParm);
1392+
13671393
public:
13681394
typedef TreeTransform<TemplateInstantiator> inherited;
13691395

@@ -1420,12 +1446,19 @@ namespace {
14201446
ArrayRef<UnexpandedParameterPack> Unexpanded,
14211447
bool &ShouldExpand, bool &RetainExpansion,
14221448
std::optional<unsigned> &NumExpansions) {
1423-
return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
1424-
PatternRange, Unexpanded,
1425-
TemplateArgs,
1426-
ShouldExpand,
1427-
RetainExpansion,
1428-
NumExpansions);
1449+
if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
1450+
for (UnexpandedParameterPack ParmPack : Unexpanded) {
1451+
NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
1452+
if (!isa_and_present<ParmVarDecl>(VD))
1453+
continue;
1454+
if (maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(VD)))
1455+
return true;
1456+
}
1457+
}
1458+
1459+
return getSema().CheckParameterPacksForExpansion(
1460+
EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
1461+
RetainExpansion, NumExpansions);
14291462
}
14301463

14311464
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
@@ -1900,9 +1933,62 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
19001933
// template parameter.
19011934
}
19021935

1936+
if (SemaRef.CurrentInstantiationScope) {
1937+
if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
1938+
maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
1939+
return nullptr;
1940+
}
1941+
19031942
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
19041943
}
19051944

1945+
bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope(
1946+
ParmVarDecl *OldParm) {
1947+
if (SemaRef.CurrentInstantiationScope->findInstantiationUnsafe(OldParm))
1948+
return false;
1949+
// We're instantiating a function parameter whose associated function template
1950+
// has not been instantiated at this point for constraint evaluation, so make
1951+
// sure the instantiated parameters are owned by a function declaration such
1952+
// that they can be correctly 'captured' in tryCaptureVariable().
1953+
Sema::ContextRAII Context(SemaRef, OldParm->getDeclContext());
1954+
1955+
if (!OldParm->isParameterPack())
1956+
return !TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0,
1957+
/*NumExpansions=*/std::nullopt,
1958+
/*ExpectParameterPack=*/false);
1959+
1960+
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
1961+
1962+
// Find the parameter packs that could be expanded.
1963+
TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc();
1964+
PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>();
1965+
TypeLoc Pattern = ExpansionTL.getPatternLoc();
1966+
SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded);
1967+
assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
1968+
1969+
bool ShouldExpand = false;
1970+
bool RetainExpansion = false;
1971+
std::optional<unsigned> OrigNumExpansions =
1972+
ExpansionTL.getTypePtr()->getNumExpansions();
1973+
std::optional<unsigned> NumExpansions = OrigNumExpansions;
1974+
if (TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(),
1975+
Pattern.getSourceRange(), Unexpanded,
1976+
ShouldExpand, RetainExpansion, NumExpansions))
1977+
return true;
1978+
1979+
assert(ShouldExpand && !RetainExpansion &&
1980+
"Shouldn't preserve pack expansion when evaluating constraints");
1981+
ExpandingFunctionParameterPack(OldParm);
1982+
for (unsigned I = 0; I != *NumExpansions; ++I) {
1983+
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
1984+
if (!TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0,
1985+
/*NumExpansions=*/OrigNumExpansions,
1986+
/*ExpectParameterPack=*/false))
1987+
return true;
1988+
}
1989+
return false;
1990+
}
1991+
19061992
Decl *TemplateInstantiator::TransformDefinition(SourceLocation Loc, Decl *D) {
19071993
Decl *Inst = getSema().SubstDecl(D, getSema().CurContext, TemplateArgs);
19081994
if (!Inst)
@@ -4584,9 +4670,8 @@ static const Decl *getCanonicalParmVarDecl(const Decl *D) {
45844670
return D;
45854671
}
45864672

4587-
45884673
llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
4589-
LocalInstantiationScope::findInstantiationOf(const Decl *D) {
4674+
LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) {
45904675
D = getCanonicalParmVarDecl(D);
45914676
for (LocalInstantiationScope *Current = this; Current;
45924677
Current = Current->Outer) {
@@ -4611,6 +4696,14 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) {
46114696
break;
46124697
}
46134698

4699+
return nullptr;
4700+
}
4701+
4702+
llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
4703+
LocalInstantiationScope::findInstantiationOf(const Decl *D) {
4704+
auto *Result = findInstantiationUnsafe(D);
4705+
if (Result)
4706+
return Result;
46144707
// If we're performing a partial substitution during template argument
46154708
// deduction, we may not have values for template parameters yet.
46164709
if (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) ||

clang/lib/Sema/TreeTransform.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ class TreeTransform {
714714
/// variables vector are acceptable.
715715
///
716716
/// LastParamTransformed, if non-null, will be set to the index of the last
717-
/// parameter on which transfromation was started. In the event of an error,
717+
/// parameter on which transformation was started. In the event of an error,
718718
/// this will contain the parameter which failed to instantiate.
719719
///
720720
/// Return true on error.

0 commit comments

Comments
 (0)