Skip to content

Commit bb31d36

Browse files
committed
Instantiate function parameters as needed
1 parent 6b7072d commit bb31d36

File tree

6 files changed

+179
-102
lines changed

6 files changed

+179
-102
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14450,6 +14450,11 @@ class Sema final : public SemaBase {
1445014450
ArrayRef<TemplateArgument> TemplateArgs,
1445114451
ConstraintSatisfaction &Satisfaction);
1445214452

14453+
bool CheckFunctionConstraintsWithoutInstantiation(
14454+
SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template,
14455+
ArrayRef<TemplateArgument> TemplateArgs,
14456+
ConstraintSatisfaction &Satisfaction);
14457+
1445314458
/// \brief Emit diagnostics explaining why a constraint expression was deemed
1445414459
/// unsatisfied.
1445514460
/// \param First whether this is the first time an unsatisfied constraint is

clang/lib/Sema/SemaConcept.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,13 +461,17 @@ static ExprResult calculateConstraintSatisfaction(
461461
return ExprError();
462462

463463
llvm::FoldingSetNodeID ID;
464+
// llvm::errs() << "Preparing for checking " << Template << "\n";
464465
if (Template &&
465466
DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
467+
// Template->dump();
466468
Satisfaction.IsSatisfied = false;
467469
Satisfaction.ContainsErrors = true;
470+
// __builtin_trap();
468471
return ExprEmpty();
469472
}
470473

474+
// llvm::errs() << "Pushing " << Template << "\n";
471475
SatisfactionStackRAII StackRAII(S, Template, ID);
472476

473477
// We do not want error diagnostics escaping here.
@@ -1122,6 +1126,139 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
11221126
PointOfInstantiation, Satisfaction);
11231127
}
11241128

1129+
namespace {
1130+
1131+
// We employ a TreeTransform because RAV couldn't recurse into a bunch of
1132+
// Exprs e.g. SizeOfPackExpr, CXXFoldExpr, etc.
1133+
// FIXME: Could we do the Decl instantiation as we substitute into
1134+
// the constraint expressions?
1135+
class InstantiateReferencedParameter
1136+
: public TreeTransform<InstantiateReferencedParameter> {
1137+
const MultiLevelTemplateArgumentList &TemplateArgs;
1138+
1139+
DeclContext *FunctionDC;
1140+
1141+
using inherited = TreeTransform<InstantiateReferencedParameter>;
1142+
1143+
bool instantiateParameterToScope(ParmVarDecl *OldParm,
1144+
LocalInstantiationScope &Scope) {
1145+
// Current context might have been changed by lambda expressions. So resume
1146+
// it before we substitute parameters.
1147+
Sema::ContextRAII Context(SemaRef, FunctionDC);
1148+
std::optional<unsigned> NumExpansions;
1149+
ParmVarDecl *NewParm = nullptr;
1150+
unsigned IndexAdjustment = 0;
1151+
if (OldParm->isParameterPack()) {
1152+
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
1153+
TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc();
1154+
PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>();
1155+
TypeLoc Pattern = ExpansionTL.getPatternLoc();
1156+
SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded);
1157+
1158+
assert(!Unexpanded.empty() &&
1159+
"A pack Decl doesn't contain anything unexpanded?");
1160+
1161+
bool ShouldExpand = false;
1162+
bool RetainExpansion = false;
1163+
std::optional<unsigned> OrigNumExpansions =
1164+
ExpansionTL.getTypePtr()->getNumExpansions();
1165+
NumExpansions = OrigNumExpansions;
1166+
if (SemaRef.CheckParameterPacksForExpansion(
1167+
ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(),
1168+
Unexpanded, TemplateArgs, ShouldExpand, RetainExpansion,
1169+
NumExpansions))
1170+
return true;
1171+
1172+
assert(ShouldExpand && !RetainExpansion &&
1173+
"Shouldn't retain an expansion here!");
1174+
Scope.MakeInstantiatedLocalArgPack(OldParm);
1175+
1176+
for (unsigned I = 0; I != *NumExpansions; ++I) {
1177+
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I);
1178+
ParmVarDecl *NewParm = SemaRef.SubstParmVarDecl(
1179+
OldParm, TemplateArgs, /*indexAdjustment=*/IndexAdjustment++,
1180+
NumExpansions, /*ExpectParameterPack=*/false,
1181+
/*EvaluateConstraints=*/false);
1182+
if (!NewParm)
1183+
return true;
1184+
}
1185+
1186+
return false;
1187+
}
1188+
NewParm = SemaRef.SubstParmVarDecl(OldParm, TemplateArgs,
1189+
/*indexAdjustment=*/IndexAdjustment,
1190+
std::nullopt,
1191+
/*ExpectParameterPack=*/false);
1192+
if (!NewParm)
1193+
return true;
1194+
Scope.InstantiatedLocal(OldParm, NewParm);
1195+
return false;
1196+
}
1197+
1198+
public:
1199+
InstantiateReferencedParameter(
1200+
Sema &SemaRef, const MultiLevelTemplateArgumentList &TemplateArgs,
1201+
DeclContext *FunctionDC)
1202+
: inherited(SemaRef), TemplateArgs(TemplateArgs), FunctionDC(FunctionDC) {
1203+
}
1204+
1205+
Decl *TransformDecl(SourceLocation Loc, Decl *D) {
1206+
if (auto *PVD = dyn_cast<ParmVarDecl>(D))
1207+
instantiateParameterToScope(PVD, *SemaRef.CurrentInstantiationScope);
1208+
return D;
1209+
}
1210+
1211+
void TraverseConstraintExprs(ArrayRef<const Expr *> Exprs) {
1212+
for (auto *E : Exprs)
1213+
TransformExpr(const_cast<Expr *>(E));
1214+
}
1215+
};
1216+
1217+
} // namespace
1218+
1219+
bool Sema::CheckFunctionConstraintsWithoutInstantiation(
1220+
SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template,
1221+
ArrayRef<TemplateArgument> TemplateArgs,
1222+
ConstraintSatisfaction &Satisfaction) {
1223+
FunctionDecl *FD = Template->getTemplatedDecl();
1224+
SmallVector<const Expr *, 3> TemplateAC;
1225+
Template->getAssociatedConstraints(TemplateAC);
1226+
if (TemplateAC.empty()) {
1227+
Satisfaction.IsSatisfied = true;
1228+
return false;
1229+
}
1230+
1231+
// Enter the scope of this instantiation. We don't use
1232+
// PushDeclContext because we don't have a scope.
1233+
LocalInstantiationScope Scope(*this);
1234+
1235+
// Collect the list of template arguments relative to the 'primary'
1236+
// template. We need the entire list, since the constraint is completely
1237+
// uninstantiated at this point.
1238+
DeclContext *NextDC = FD->getFriendObjectKind() ? FD->getLexicalDeclContext()
1239+
: FD->getDeclContext();
1240+
MultiLevelTemplateArgumentList MLTAL =
1241+
getTemplateInstantiationArgs(FD, NextDC,
1242+
/*Final=*/false,
1243+
/*Innermost=*/TemplateArgs,
1244+
/*RelativeToPrimary=*/true,
1245+
/*Pattern=*/nullptr,
1246+
/*ForConstraintInstantiation=*/true);
1247+
1248+
InstantiateReferencedParameter(*this, MLTAL, FD)
1249+
.TraverseConstraintExprs(TemplateAC);
1250+
1251+
Qualifiers ThisQuals;
1252+
CXXRecordDecl *Record = nullptr;
1253+
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
1254+
ThisQuals = Method->getMethodQualifiers();
1255+
Record = Method->getParent();
1256+
}
1257+
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
1258+
return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL,
1259+
PointOfInstantiation, Satisfaction);
1260+
}
1261+
11251262
static void diagnoseUnsatisfiedRequirement(Sema &S,
11261263
concepts::ExprRequirement *Req,
11271264
bool First) {

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 18 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3863,72 +3863,28 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
38633863
FD = const_cast<FunctionDecl *>(FDFriend);
38643864
Owner = FD->getLexicalDeclContext();
38653865
}
3866+
38663867
// [DR2369]
38673868
// FIXME: We have to partially instantiate lambda's captures for constraint
38683869
// evaluation.
3869-
if (!isLambdaCallOperator(FD) && !isLambdaConversionOperator(FD) &&
3870-
(!PartialOverloading ||
3871-
(CanonicalBuilder.size() ==
3872-
FunctionTemplate->getTemplateParameters()->size()))) {
3873-
FunctionTemplateDecl *Template = FunctionTemplate->getCanonicalDecl();
3874-
FunctionDecl *FD = Template->getTemplatedDecl();
3875-
SmallVector<const Expr *, 3> TemplateAC;
3876-
Template->getAssociatedConstraints(TemplateAC);
3877-
if (!TemplateAC.empty()) {
3878-
3879-
// Enter the scope of this instantiation. We don't use
3880-
// PushDeclContext because we don't have a scope.
3881-
LocalInstantiationScope Scope(*this);
3882-
3883-
// Collect the list of template arguments relative to the 'primary'
3884-
// template. We need the entire list, since the constraint is completely
3885-
// uninstantiated at this point.
3886-
3887-
MultiLevelTemplateArgumentList MLTAL(FD, SugaredBuilder, /*Final=*/false);
3888-
getTemplateInstantiationArgs(nullptr, FD->getLexicalDeclContext(),
3889-
/*Final=*/false,
3890-
/*Innermost=*/std::nullopt,
3891-
/*RelativeToPrimary=*/true,
3892-
/*Pattern=*/nullptr,
3893-
/*ForConstraintInstantiation=*/true,
3894-
/*SkipForSpecialization=*/false,
3895-
/*Merged=*/&MLTAL);
3896-
3897-
MultiLevelTemplateArgumentList JustTemplArgs(
3898-
Template, CanonicalDeducedArgumentList->asArray(),
3899-
/*Final=*/false);
3900-
if (addInstantiatedParametersToScope(nullptr, FD, Scope, JustTemplArgs))
3901-
return TemplateDeductionResult::MiscellaneousDeductionFailure;
3902-
3903-
if (FunctionTemplateDecl *FromMemTempl =
3904-
Template->getInstantiatedFromMemberTemplate()) {
3905-
while (FromMemTempl->getInstantiatedFromMemberTemplate())
3906-
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
3907-
if (addInstantiatedParametersToScope(
3908-
nullptr, FromMemTempl->getTemplatedDecl(), Scope, MLTAL))
3909-
return TemplateDeductionResult::MiscellaneousDeductionFailure;
3910-
}
3911-
3912-
Qualifiers ThisQuals;
3913-
CXXRecordDecl *Record = nullptr;
3914-
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
3915-
ThisQuals = Method->getMethodQualifiers();
3916-
Record = Method->getParent();
3917-
}
3918-
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
3919-
llvm::SmallVector<Expr *, 1> Converted;
3920-
if (CheckConstraintSatisfaction(Template, TemplateAC, MLTAL,
3921-
Info.getLocation(),
3922-
Info.AssociatedConstraintsSatisfaction))
3923-
return TemplateDeductionResult::MiscellaneousDeductionFailure;
3924-
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
3925-
Info.reset(TemplateArgumentList::CreateCopy(Context, SugaredBuilder),
3926-
Info.takeCanonical());
3927-
return TemplateDeductionResult::ConstraintsNotSatisfied;
3928-
}
3870+
bool NeedConstraintChecking =
3871+
!PartialOverloading ||
3872+
CanonicalBuilder.size() ==
3873+
FunctionTemplate->getTemplateParameters()->size();
3874+
bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
3875+
#if 1
3876+
if (!IsLambda && NeedConstraintChecking) {
3877+
if (CheckFunctionConstraintsWithoutInstantiation(
3878+
Info.getLocation(), FunctionTemplate->getCanonicalDecl(),
3879+
CanonicalBuilder, Info.AssociatedConstraintsSatisfaction))
3880+
return TemplateDeductionResult::MiscellaneousDeductionFailure;
3881+
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
3882+
Info.reset(Info.takeSugared(),
3883+
TemplateArgumentList::CreateCopy(Context, CanonicalBuilder));
3884+
return TemplateDeductionResult::ConstraintsNotSatisfied;
39293885
}
39303886
}
3931-
3887+
#endif
39323888
// C++ [temp.deduct.call]p10: [DR1391]
39333889
// If deduction succeeds for all parameters that contain
39343890
// template-parameters that participate in template argument deduction,
@@ -3975,9 +3931,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
39753931
// ([temp.constr.decl]), those constraints are checked for satisfaction
39763932
// ([temp.constr.constr]). If the constraints are not satisfied, type
39773933
// deduction fails.
3978-
if (!PartialOverloading ||
3979-
(CanonicalBuilder.size() ==
3980-
FunctionTemplate->getTemplateParameters()->size())) {
3934+
if (IsLambda && NeedConstraintChecking) {
39813935
if (CheckInstantiatedFunctionTemplateConstraints(
39823936
Info.getLocation(), Specialization, CanonicalBuilder,
39833937
Info.AssociatedConstraintsSatisfaction))

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "clang/Sema/SemaSwift.h"
3535
#include "clang/Sema/Template.h"
3636
#include "clang/Sema/TemplateInstCallback.h"
37+
#include "llvm/ADT/STLExtras.h"
3738
#include "llvm/Support/TimeProfiler.h"
3839
#include <optional>
3940

@@ -2136,7 +2137,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
21362137
return nullptr;
21372138
QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
21382139

2139-
if (TemplateParams && TemplateParams->size()) {
2140+
if (false && TemplateParams && TemplateParams->size()) {
21402141
auto *LastParam =
21412142
dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
21422143
if (LastParam && LastParam->isImplicit() &&
@@ -2548,7 +2549,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
25482549
return nullptr;
25492550
QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
25502551

2551-
if (TemplateParams && TemplateParams->size()) {
2552+
if (false && TemplateParams && TemplateParams->size()) {
25522553
auto *LastParam =
25532554
dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
25542555
if (LastParam && LastParam->isImplicit() &&
@@ -4577,17 +4578,17 @@ void Sema::addInstantiatedLocalVarsToScope(FunctionDecl *Function,
45774578
}
45784579
}
45794580

4580-
static bool addInstantiatedParametersToScope(
4581-
Sema &SemaRef, MutableArrayRef<ParmVarDecl *> InstantiatedParamDecls,
4582-
const FunctionDecl *PatternDecl, LocalInstantiationScope &Scope,
4581+
bool Sema::addInstantiatedParametersToScope(
4582+
FunctionDecl *Function, const FunctionDecl *PatternDecl,
4583+
LocalInstantiationScope &Scope,
45834584
const MultiLevelTemplateArgumentList &TemplateArgs) {
45844585
unsigned FParamIdx = 0;
45854586
for (unsigned I = 0, N = PatternDecl->getNumParams(); I != N; ++I) {
45864587
const ParmVarDecl *PatternParam = PatternDecl->getParamDecl(I);
45874588
if (!PatternParam->isParameterPack()) {
45884589
// Simple case: not a parameter pack.
4589-
assert(FParamIdx < InstantiatedParamDecls.size());
4590-
ParmVarDecl *FunctionParam = InstantiatedParamDecls[FParamIdx];
4590+
assert(FParamIdx < Function->getNumParams());
4591+
ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx);
45914592
FunctionParam->setDeclName(PatternParam->getDeclName());
45924593
// If the parameter's type is not dependent, update it to match the type
45934594
// in the pattern. They can differ in top-level cv-qualifiers, and we want
@@ -4596,9 +4597,9 @@ static bool addInstantiatedParametersToScope(
45964597
// it's instantiation-dependent.
45974598
// FIXME: Updating the type to work around this is at best fragile.
45984599
if (!PatternDecl->getType()->isDependentType()) {
4599-
QualType T = SemaRef.SubstType(PatternParam->getType(), TemplateArgs,
4600-
FunctionParam->getLocation(),
4601-
FunctionParam->getDeclName());
4600+
QualType T = SubstType(PatternParam->getType(), TemplateArgs,
4601+
FunctionParam->getLocation(),
4602+
FunctionParam->getDeclName());
46024603
if (T.isNull())
46034604
return true;
46044605
FunctionParam->setType(T);
@@ -4612,19 +4613,18 @@ static bool addInstantiatedParametersToScope(
46124613
// Expand the parameter pack.
46134614
Scope.MakeInstantiatedLocalArgPack(PatternParam);
46144615
std::optional<unsigned> NumArgumentsInExpansion =
4615-
SemaRef.getNumArgumentsInExpansion(PatternParam->getType(),
4616-
TemplateArgs);
4616+
getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs);
46174617
if (NumArgumentsInExpansion) {
46184618
QualType PatternType =
46194619
PatternParam->getType()->castAs<PackExpansionType>()->getPattern();
46204620
for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) {
4621-
ParmVarDecl *FunctionParam = InstantiatedParamDecls[FParamIdx];
4621+
ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx);
46224622
FunctionParam->setDeclName(PatternParam->getDeclName());
46234623
if (!PatternDecl->getType()->isDependentType()) {
4624-
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, Arg);
4625-
QualType T = SemaRef.SubstType(PatternType, TemplateArgs,
4626-
FunctionParam->getLocation(),
4627-
FunctionParam->getDeclName());
4624+
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(*this, Arg);
4625+
QualType T =
4626+
SubstType(PatternType, TemplateArgs, FunctionParam->getLocation(),
4627+
FunctionParam->getDeclName());
46284628
if (T.isNull())
46294629
return true;
46304630
FunctionParam->setType(T);
@@ -4639,25 +4639,6 @@ static bool addInstantiatedParametersToScope(
46394639
return false;
46404640
}
46414641

4642-
bool Sema::addInstantiatedParametersToScope(
4643-
FunctionDecl *Function, const FunctionDecl *PatternDecl,
4644-
LocalInstantiationScope &Scope,
4645-
const MultiLevelTemplateArgumentList &TemplateArgs) {
4646-
if (Function)
4647-
return ::addInstantiatedParametersToScope(*this, Function->parameters(),
4648-
PatternDecl, Scope, TemplateArgs);
4649-
FunctionTypeLoc TypeLoc = PatternDecl->getFunctionTypeLoc();
4650-
assert(!TypeLoc.isNull() && "Invalid function TypeLoc?");
4651-
SmallVector<QualType> ParamTypes;
4652-
SmallVector<ParmVarDecl *> OutParams;
4653-
Sema::ExtParameterInfoBuilder ExtParamInfos;
4654-
if (SubstParmTypes(PatternDecl->getLocation(), TypeLoc.getParams(), nullptr,
4655-
TemplateArgs, ParamTypes, &OutParams, ExtParamInfos))
4656-
return true;
4657-
return ::addInstantiatedParametersToScope(*this, OutParams, PatternDecl,
4658-
Scope, TemplateArgs);
4659-
}
4660-
46614642
bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
46624643
ParmVarDecl *Param) {
46634644
assert(Param->hasUninstantiatedDefaultArg());

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)