Skip to content

Commit 56bacf4

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

21 files changed

+289
-57
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13303,6 +13303,7 @@ class Sema final : public SemaBase {
1330313303
///
1330413304
/// \param SkipForSpecialization when specified, any template specializations
1330513305
/// in a traversal would be ignored.
13306+
///
1330613307
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
1330713308
/// when encountering a specialized member function template, rather than
1330813309
/// returning immediately.
@@ -13314,6 +13315,17 @@ class Sema final : public SemaBase {
1331413315
bool SkipForSpecialization = false,
1331513316
bool ForDefaultArgumentSubstitution = false);
1331613317

13318+
/// Apart from storing the result to \p Result, this behaves the same as
13319+
/// another overload.
13320+
void getTemplateInstantiationArgs(
13321+
MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
13322+
const DeclContext *DC = nullptr, bool Final = false,
13323+
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
13324+
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
13325+
bool ForConstraintInstantiation = false,
13326+
bool SkipForSpecialization = false,
13327+
bool ForDefaultArgumentSubstitution = false);
13328+
1331713329
/// RAII object to handle the state changes required to synthesize
1331813330
/// a function body.
1331913331
class SynthesizedFunctionScope {
@@ -13590,7 +13602,7 @@ class Sema final : public SemaBase {
1359013602
ExprResult
1359113603
SubstConstraintExpr(Expr *E,
1359213604
const MultiLevelTemplateArgumentList &TemplateArgs);
13593-
// Unlike the above, this does not evaluates constraints.
13605+
// Unlike the above, this does not evaluate constraints.
1359413606
ExprResult SubstConstraintExprWithoutSatisfaction(
1359513607
Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
1359613608

@@ -14732,10 +14744,10 @@ class Sema final : public SemaBase {
1473214744
const MultiLevelTemplateArgumentList &TemplateArgs,
1473314745
SourceRange TemplateIDRange);
1473414746

14735-
bool CheckInstantiatedFunctionTemplateConstraints(
14736-
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
14737-
ArrayRef<TemplateArgument> TemplateArgs,
14738-
ConstraintSatisfaction &Satisfaction);
14747+
bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
14748+
FunctionDecl *Decl,
14749+
ArrayRef<TemplateArgument> TemplateArgs,
14750+
ConstraintSatisfaction &Satisfaction);
1473914751

1474014752
/// \brief Emit diagnostics explaining why a constraint expression was deemed
1474114753
/// 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
@@ -792,7 +792,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
792792
bool ForOverloadResolution) {
793793
// Don't check constraints if the function is dependent. Also don't check if
794794
// this is a function template specialization, as the call to
795-
// CheckinstantiatedFunctionTemplateConstraints after this will check it
795+
// CheckFunctionTemplateConstraints after this will check it
796796
// better.
797797
if (FD->isDependentContext() ||
798798
FD->getTemplatedKind() ==
@@ -1060,12 +1060,55 @@ bool Sema::EnsureTemplateArgumentListConstraints(
10601060
return false;
10611061
}
10621062

1063-
bool Sema::CheckInstantiatedFunctionTemplateConstraints(
1063+
static bool CheckFunctionConstraintsWithoutInstantiation(
1064+
Sema &SemaRef, SourceLocation PointOfInstantiation,
1065+
FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
1066+
ConstraintSatisfaction &Satisfaction) {
1067+
SmallVector<const Expr *, 3> TemplateAC;
1068+
Template->getAssociatedConstraints(TemplateAC);
1069+
if (TemplateAC.empty()) {
1070+
Satisfaction.IsSatisfied = true;
1071+
return false;
1072+
}
1073+
1074+
LocalInstantiationScope Scope(SemaRef);
1075+
1076+
FunctionDecl *FD = Template->getTemplatedDecl();
1077+
// Collect the list of template arguments relative to the 'primary'
1078+
// template. We need the entire list, since the constraint is completely
1079+
// uninstantiated at this point.
1080+
1081+
// FIXME: Add TemplateArgs through the 'Innermost' parameter once
1082+
// the refactoring of getTemplateInstantiationArgs() relands.
1083+
MultiLevelTemplateArgumentList MLTAL;
1084+
MLTAL.addOuterTemplateArguments(Template, std::nullopt, /*Final=*/false);
1085+
SemaRef.getTemplateInstantiationArgs(
1086+
MLTAL, /*D=*/FD, FD,
1087+
/*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
1088+
/*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
1089+
MLTAL.replaceInnermostTemplateArguments(Template, TemplateArgs);
1090+
1091+
Sema::ContextRAII SavedContext(SemaRef, FD);
1092+
std::optional<Sema::CXXThisScopeRAII> ThisScope;
1093+
if (auto *Method = dyn_cast<CXXMethodDecl>(FD))
1094+
ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(),
1095+
/*ThisQuals=*/Method->getMethodQualifiers());
1096+
return SemaRef.CheckConstraintSatisfaction(
1097+
Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
1098+
}
1099+
1100+
bool Sema::CheckFunctionTemplateConstraints(
10641101
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
10651102
ArrayRef<TemplateArgument> TemplateArgs,
10661103
ConstraintSatisfaction &Satisfaction) {
10671104
// In most cases we're not going to have constraints, so check for that first.
10681105
FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
1106+
1107+
if (!Template)
1108+
return ::CheckFunctionConstraintsWithoutInstantiation(
1109+
*this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(),
1110+
TemplateArgs, Satisfaction);
1111+
10691112
// Note - code synthesis context for the constraints check is created
10701113
// inside CheckConstraintsSatisfaction.
10711114
SmallVector<AssociatedConstraint, 3> TemplateAC;

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3991,18 +3991,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
39913991
Result != TemplateDeductionResult::Success)
39923992
return Result;
39933993

3994-
// C++ [temp.deduct.call]p10: [DR1391]
3995-
// If deduction succeeds for all parameters that contain
3996-
// template-parameters that participate in template argument deduction,
3997-
// and all template arguments are explicitly specified, deduced, or
3998-
// obtained from default template arguments, remaining parameters are then
3999-
// compared with the corresponding arguments. For each remaining parameter
4000-
// P with a type that was non-dependent before substitution of any
4001-
// explicitly-specified template arguments, if the corresponding argument
4002-
// A cannot be implicitly converted to P, deduction fails.
4003-
if (CheckNonDependent())
4004-
return TemplateDeductionResult::NonDependentConversionFailure;
4005-
40063994
// Form the template argument list from the deduced template arguments.
40073995
TemplateArgumentList *SugaredDeducedArgumentList =
40083996
TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted);
@@ -4017,6 +4005,39 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40174005
Owner = FunctionTemplate->getLexicalDeclContext();
40184006
FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
40194007

4008+
// C++20 [temp.deduct.general]p5: [CWG2369]
4009+
// If the function template has associated constraints, those constraints
4010+
// are checked for satisfaction. If the constraints are not satisfied, type
4011+
// deduction fails.
4012+
//
4013+
// FIXME: We haven't implemented CWG2369 for lambdas yet, because we need
4014+
// to figure out how to instantiate lambda captures to the scope without
4015+
// first instantiating the lambda.
4016+
bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
4017+
if (!IsLambda && !IsIncomplete) {
4018+
if (CheckFunctionTemplateConstraints(
4019+
Info.getLocation(),
4020+
FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
4021+
CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction))
4022+
return TemplateDeductionResult::MiscellaneousDeductionFailure;
4023+
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
4024+
Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy(
4025+
Context, CTAI.CanonicalConverted));
4026+
return TemplateDeductionResult::ConstraintsNotSatisfied;
4027+
}
4028+
}
4029+
// C++ [temp.deduct.call]p10: [CWG1391]
4030+
// If deduction succeeds for all parameters that contain
4031+
// template-parameters that participate in template argument deduction,
4032+
// and all template arguments are explicitly specified, deduced, or
4033+
// obtained from default template arguments, remaining parameters are then
4034+
// compared with the corresponding arguments. For each remaining parameter
4035+
// P with a type that was non-dependent before substitution of any
4036+
// explicitly-specified template arguments, if the corresponding argument
4037+
// A cannot be implicitly converted to P, deduction fails.
4038+
if (CheckNonDependent())
4039+
return TemplateDeductionResult::NonDependentConversionFailure;
4040+
40204041
MultiLevelTemplateArgumentList SubstArgs(
40214042
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
40224043
/*Final=*/false);
@@ -4051,8 +4072,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40514072
// ([temp.constr.decl]), those constraints are checked for satisfaction
40524073
// ([temp.constr.constr]). If the constraints are not satisfied, type
40534074
// deduction fails.
4054-
if (!IsIncomplete) {
4055-
if (CheckInstantiatedFunctionTemplateConstraints(
4075+
if (IsLambda && !IsIncomplete) {
4076+
if (CheckFunctionTemplateConstraints(
40564077
Info.getLocation(), Specialization, CTAI.CanonicalConverted,
40574078
Info.AssociatedConstraintsSatisfaction))
40584079
return TemplateDeductionResult::MiscellaneousDeductionFailure;

clang/lib/Sema/SemaTemplateDeductionGuide.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -955,10 +955,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
955955
Context.getTrivialTypeSourceInfo(
956956
Context.getDeducedTemplateSpecializationType(
957957
TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
958-
/*IsDependent=*/true)), // template specialization type whose
959-
// arguments will be deduced.
958+
/*IsDependent=*/true),
959+
AliasTemplate->getLocation()), // template specialization type whose
960+
// arguments will be deduced.
960961
Context.getTrivialTypeSourceInfo(
961-
ReturnType), // type from which template arguments are deduced.
962+
ReturnType, AliasTemplate->getLocation()), // type from which template
963+
// arguments are deduced.
962964
};
963965
return TypeTraitExpr::Create(
964966
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;
@@ -534,14 +549,12 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
534549
}
535550

536551
if (R.IsDone)
537-
return Result;
552+
return;
538553
if (R.ClearRelativeToPrimary)
539554
RelativeToPrimary = false;
540555
assert(R.NextDecl);
541556
CurDecl = R.NextDecl;
542557
}
543-
544-
return Result;
545558
}
546559

547560
bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
@@ -1374,6 +1387,19 @@ namespace {
13741387
// Whether an incomplete substituion should be treated as an error.
13751388
bool BailOutOnIncomplete;
13761389

1390+
private:
1391+
bool isSubstitutingConstraints() const {
1392+
return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) {
1393+
return Context.Kind ==
1394+
Sema::CodeSynthesisContext::ConstraintSubstitution;
1395+
});
1396+
}
1397+
1398+
// CWG2770: Function parameters should be instantiated when they are
1399+
// needed by a satisfaction check of an atomic constraint or
1400+
// (recursively) by another function parameter.
1401+
bool maybeInstantiateFunctionParameterToScope(ParmVarDecl *OldParm);
1402+
13771403
public:
13781404
typedef TreeTransform<TemplateInstantiator> inherited;
13791405

@@ -1430,12 +1456,19 @@ namespace {
14301456
ArrayRef<UnexpandedParameterPack> Unexpanded,
14311457
bool &ShouldExpand, bool &RetainExpansion,
14321458
UnsignedOrNone &NumExpansions) {
1433-
return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
1434-
PatternRange, Unexpanded,
1435-
TemplateArgs,
1436-
ShouldExpand,
1437-
RetainExpansion,
1438-
NumExpansions);
1459+
if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
1460+
for (UnexpandedParameterPack ParmPack : Unexpanded) {
1461+
NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
1462+
if (!isa_and_present<ParmVarDecl>(VD))
1463+
continue;
1464+
if (maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(VD)))
1465+
return true;
1466+
}
1467+
}
1468+
1469+
return getSema().CheckParameterPacksForExpansion(
1470+
EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
1471+
RetainExpansion, NumExpansions);
14391472
}
14401473

14411474
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
@@ -1919,9 +1952,62 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
19191952
// template parameter.
19201953
}
19211954

1955+
if (SemaRef.CurrentInstantiationScope) {
1956+
if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
1957+
maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
1958+
return nullptr;
1959+
}
1960+
19221961
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
19231962
}
19241963

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

4488-
44894574
llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
4490-
LocalInstantiationScope::findInstantiationOf(const Decl *D) {
4575+
LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) {
44914576
D = getCanonicalParmVarDecl(D);
44924577
for (LocalInstantiationScope *Current = this; Current;
44934578
Current = Current->Outer) {
@@ -4512,6 +4597,14 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) {
45124597
break;
45134598
}
45144599

4600+
return nullptr;
4601+
}
4602+
4603+
llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
4604+
LocalInstantiationScope::findInstantiationOf(const Decl *D) {
4605+
auto *Result = findInstantiationUnsafe(D);
4606+
if (Result)
4607+
return Result;
45154608
// If we're performing a partial substitution during template argument
45164609
// deduction, we may not have values for template parameters yet.
45174610
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)