Skip to content

Commit 96eced6

Browse files
authored
[Clang] Implement CWG2369 "Ordering between constraints and substitution" (llvm#102857)
This patch partially implements CWG2369 for non-lambda-constrained functions. Lambdas are left intact at this point because we need extra work to correctly instantiate captures before the function instantiation. As a premise of CWG2369, this patch also implements CWG2770 to ensure the function parameters are instantiated on demand. Closes llvm#54440
1 parent 2d9d291 commit 96eced6

22 files changed

+288
-62
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13055,6 +13055,7 @@ class Sema final : public SemaBase {
1305513055
///
1305613056
/// \param SkipForSpecialization when specified, any template specializations
1305713057
/// in a traversal would be ignored.
13058+
///
1305813059
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
1305913060
/// when encountering a specialized member function template, rather than
1306013061
/// returning immediately.
@@ -13066,6 +13067,17 @@ class Sema final : public SemaBase {
1306613067
bool SkipForSpecialization = false,
1306713068
bool ForDefaultArgumentSubstitution = false);
1306813069

13070+
/// Apart from storing the result to \p Result, this behaves the same as
13071+
/// another overload.
13072+
void getTemplateInstantiationArgs(
13073+
MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
13074+
const DeclContext *DC = nullptr, bool Final = false,
13075+
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
13076+
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
13077+
bool ForConstraintInstantiation = false,
13078+
bool SkipForSpecialization = false,
13079+
bool ForDefaultArgumentSubstitution = false);
13080+
1306913081
/// RAII object to handle the state changes required to synthesize
1307013082
/// a function body.
1307113083
class SynthesizedFunctionScope {
@@ -13335,7 +13347,7 @@ class Sema final : public SemaBase {
1333513347
ExprResult
1333613348
SubstConstraintExpr(Expr *E,
1333713349
const MultiLevelTemplateArgumentList &TemplateArgs);
13338-
// Unlike the above, this does not evaluates constraints.
13350+
// Unlike the above, this does not evaluate constraints.
1333913351
ExprResult SubstConstraintExprWithoutSatisfaction(
1334013352
Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
1334113353

@@ -14456,10 +14468,10 @@ class Sema final : public SemaBase {
1445614468
const MultiLevelTemplateArgumentList &TemplateArgs,
1445714469
SourceRange TemplateIDRange);
1445814470

14459-
bool CheckInstantiatedFunctionTemplateConstraints(
14460-
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
14461-
ArrayRef<TemplateArgument> TemplateArgs,
14462-
ConstraintSatisfaction &Satisfaction);
14471+
bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
14472+
FunctionDecl *Decl,
14473+
ArrayRef<TemplateArgument> TemplateArgs,
14474+
ConstraintSatisfaction &Satisfaction);
1446314475

1446414476
/// \brief Emit diagnostics explaining why a constraint expression was deemed
1446514477
/// 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() ==
@@ -1111,12 +1111,55 @@ bool Sema::EnsureTemplateArgumentListConstraints(
11111111
return false;
11121112
}
11131113

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

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3936,18 +3936,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
39363936
Result != TemplateDeductionResult::Success)
39373937
return Result;
39383938

3939-
// C++ [temp.deduct.call]p10: [DR1391]
3940-
// If deduction succeeds for all parameters that contain
3941-
// template-parameters that participate in template argument deduction,
3942-
// and all template arguments are explicitly specified, deduced, or
3943-
// obtained from default template arguments, remaining parameters are then
3944-
// compared with the corresponding arguments. For each remaining parameter
3945-
// P with a type that was non-dependent before substitution of any
3946-
// explicitly-specified template arguments, if the corresponding argument
3947-
// A cannot be implicitly converted to P, deduction fails.
3948-
if (CheckNonDependent())
3949-
return TemplateDeductionResult::NonDependentConversionFailure;
3950-
39513939
// Form the template argument list from the deduced template arguments.
39523940
TemplateArgumentList *SugaredDeducedArgumentList =
39533941
TemplateArgumentList::CreateCopy(Context, SugaredBuilder);
@@ -3977,6 +3965,39 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
39773965
FD = const_cast<FunctionDecl *>(FDFriend);
39783966
Owner = FD->getLexicalDeclContext();
39793967
}
3968+
// C++20 [temp.deduct.general]p5: [CWG2369]
3969+
// If the function template has associated constraints, those constraints
3970+
// are checked for satisfaction. If the constraints are not satisfied, type
3971+
// deduction fails.
3972+
//
3973+
// FIXME: We haven't implemented CWG2369 for lambdas yet, because we need
3974+
// to figure out how to instantiate lambda captures to the scope without
3975+
// first instantiating the lambda.
3976+
bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
3977+
if (!IsLambda && !IsIncomplete) {
3978+
if (CheckFunctionTemplateConstraints(
3979+
Info.getLocation(),
3980+
FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
3981+
CanonicalBuilder, Info.AssociatedConstraintsSatisfaction))
3982+
return TemplateDeductionResult::MiscellaneousDeductionFailure;
3983+
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
3984+
Info.reset(Info.takeSugared(),
3985+
TemplateArgumentList::CreateCopy(Context, CanonicalBuilder));
3986+
return TemplateDeductionResult::ConstraintsNotSatisfied;
3987+
}
3988+
}
3989+
// C++ [temp.deduct.call]p10: [CWG1391]
3990+
// If deduction succeeds for all parameters that contain
3991+
// template-parameters that participate in template argument deduction,
3992+
// and all template arguments are explicitly specified, deduced, or
3993+
// obtained from default template arguments, remaining parameters are then
3994+
// compared with the corresponding arguments. For each remaining parameter
3995+
// P with a type that was non-dependent before substitution of any
3996+
// explicitly-specified template arguments, if the corresponding argument
3997+
// A cannot be implicitly converted to P, deduction fails.
3998+
if (CheckNonDependent())
3999+
return TemplateDeductionResult::NonDependentConversionFailure;
4000+
39804001
MultiLevelTemplateArgumentList SubstArgs(
39814002
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
39824003
/*Final=*/false);
@@ -4011,8 +4032,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40114032
// ([temp.constr.decl]), those constraints are checked for satisfaction
40124033
// ([temp.constr.constr]). If the constraints are not satisfied, type
40134034
// deduction fails.
4014-
if (!IsIncomplete) {
4015-
if (CheckInstantiatedFunctionTemplateConstraints(
4035+
if (IsLambda && !IsIncomplete) {
4036+
if (CheckFunctionTemplateConstraints(
40164037
Info.getLocation(), Specialization, CanonicalBuilder,
40174038
Info.AssociatedConstraintsSatisfaction))
40184039
return TemplateDeductionResult::MiscellaneousDeductionFailure;

clang/lib/Sema/SemaTemplateDeductionGuide.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -902,10 +902,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
902902
Context.getTrivialTypeSourceInfo(
903903
Context.getDeducedTemplateSpecializationType(
904904
TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
905-
/*IsDependent=*/true)), // template specialization type whose
906-
// arguments will be deduced.
905+
/*IsDependent=*/true),
906+
AliasTemplate->getLocation()), // template specialization type whose
907+
// arguments will be deduced.
907908
Context.getTrivialTypeSourceInfo(
908-
ReturnType), // type from which template arguments are deduced.
909+
ReturnType, AliasTemplate->getLocation()), // type from which template
910+
// arguments are deduced.
909911
};
910912
return TypeTraitExpr::Create(
911913
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 {
@@ -1349,6 +1362,19 @@ namespace {
13491362
// Whether an incomplete substituion should be treated as an error.
13501363
bool BailOutOnIncomplete;
13511364

1365+
private:
1366+
bool isSubstitutingConstraints() const {
1367+
return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) {
1368+
return Context.Kind ==
1369+
Sema::CodeSynthesisContext::ConstraintSubstitution;
1370+
});
1371+
}
1372+
1373+
// CWG2770: Function parameters should be instantiated when they are
1374+
// needed by a satisfaction check of an atomic constraint or
1375+
// (recursively) by another function parameter.
1376+
bool maybeInstantiateFunctionParameterToScope(ParmVarDecl *OldParm);
1377+
13521378
public:
13531379
typedef TreeTransform<TemplateInstantiator> inherited;
13541380

@@ -1405,12 +1431,19 @@ namespace {
14051431
ArrayRef<UnexpandedParameterPack> Unexpanded,
14061432
bool &ShouldExpand, bool &RetainExpansion,
14071433
std::optional<unsigned> &NumExpansions) {
1408-
return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
1409-
PatternRange, Unexpanded,
1410-
TemplateArgs,
1411-
ShouldExpand,
1412-
RetainExpansion,
1413-
NumExpansions);
1434+
if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
1435+
for (UnexpandedParameterPack ParmPack : Unexpanded) {
1436+
NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
1437+
if (!isa_and_present<ParmVarDecl>(VD))
1438+
continue;
1439+
if (maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(VD)))
1440+
return true;
1441+
}
1442+
}
1443+
1444+
return getSema().CheckParameterPacksForExpansion(
1445+
EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
1446+
RetainExpansion, NumExpansions);
14141447
}
14151448

14161449
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
@@ -1911,9 +1944,62 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
19111944
// template parameter.
19121945
}
19131946

1947+
if (SemaRef.CurrentInstantiationScope) {
1948+
if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
1949+
maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
1950+
return nullptr;
1951+
}
1952+
19141953
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
19151954
}
19161955

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

4594-
45954680
llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
4596-
LocalInstantiationScope::findInstantiationOf(const Decl *D) {
4681+
LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) {
45974682
D = getCanonicalParmVarDecl(D);
45984683
for (LocalInstantiationScope *Current = this; Current;
45994684
Current = Current->Outer) {
@@ -4618,6 +4703,14 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) {
46184703
break;
46194704
}
46204705

4706+
return nullptr;
4707+
}
4708+
4709+
llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
4710+
LocalInstantiationScope::findInstantiationOf(const Decl *D) {
4711+
auto *Result = findInstantiationUnsafe(D);
4712+
if (Result)
4713+
return Result;
46214714
// If we're performing a partial substitution during template argument
46224715
// deduction, we may not have values for template parameters yet.
46234716
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
@@ -713,7 +713,7 @@ class TreeTransform {
713713
/// variables vector are acceptable.
714714
///
715715
/// LastParamTransformed, if non-null, will be set to the index of the last
716-
/// parameter on which transfromation was started. In the event of an error,
716+
/// parameter on which transformation was started. In the event of an error,
717717
/// this will contain the parameter which failed to instantiate.
718718
///
719719
/// Return true on error.

0 commit comments

Comments
 (0)