Skip to content

Commit 1deb74f

Browse files
Younan Zhangzyn0217
authored andcommitted
[Clang][Sema] Retain the expanding index for unevaluated type constraints
1 parent c28e268 commit 1deb74f

File tree

5 files changed

+177
-7
lines changed

5 files changed

+177
-7
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11252,6 +11252,7 @@ class Sema final : public SemaBase {
1125211252
ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
1125311253
const TemplateArgumentListInfo *TemplateArgs,
1125411254
TemplateTypeParmDecl *ConstrainedParameter,
11255+
QualType ConstrainedType,
1125511256
SourceLocation EllipsisLoc);
1125611257

1125711258
bool AttachTypeConstraint(AutoTypeLoc TL,

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,7 +1134,8 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
11341134
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(),
11351135
ConceptName, CD, /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : CD,
11361136
TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr,
1137-
ConstrainedParameter, EllipsisLoc);
1137+
ConstrainedParameter, Context.getTypeDeclType(ConstrainedParameter),
1138+
EllipsisLoc);
11381139
}
11391140

11401141
template <typename ArgumentLocAppender>
@@ -1191,6 +1192,7 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
11911192
ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
11921193
const TemplateArgumentListInfo *TemplateArgs,
11931194
TemplateTypeParmDecl *ConstrainedParameter,
1195+
QualType ConstrainedType,
11941196
SourceLocation EllipsisLoc) {
11951197
// C++2a [temp.param]p4:
11961198
// [...] If Q is of the form C<A1, ..., An>, then let E' be
@@ -1199,7 +1201,7 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
11991201
TemplateArgs ? ASTTemplateArgumentListInfo::Create(Context,
12001202
*TemplateArgs) : nullptr;
12011203

1202-
QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0);
1204+
QualType ParamAsArgument = ConstrainedType;
12031205

12041206
ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint(
12051207
*this, NS, NameInfo, NamedConcept, FoundDecl,

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,21 @@ namespace {
16461646
SubstTemplateTypeParmPackTypeLoc TL,
16471647
bool SuppressObjCLifetime);
16481648

1649+
QualType
1650+
TransformSubstTemplateTypeParmType(TypeLocBuilder &TLB,
1651+
SubstTemplateTypeParmTypeLoc TL) {
1652+
if (SemaRef.CodeSynthesisContexts.back().Kind !=
1653+
Sema::CodeSynthesisContext::ConstraintSubstitution)
1654+
return inherited::TransformSubstTemplateTypeParmType(TLB, TL);
1655+
1656+
auto PackIndex = TL.getTypePtr()->getPackIndex();
1657+
std::optional<Sema::ArgumentPackSubstitutionIndexRAII> SubstIndex;
1658+
if (SemaRef.ArgumentPackSubstitutionIndex == -1 && PackIndex)
1659+
SubstIndex.emplace(SemaRef, *PackIndex);
1660+
1661+
return inherited::TransformSubstTemplateTypeParmType(TLB, TL);
1662+
}
1663+
16491664
CXXRecordDecl::LambdaDependencyKind
16501665
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
16511666
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(getSema());
@@ -3056,6 +3071,46 @@ namespace {
30563071

30573072
} // namespace
30583073

3074+
namespace {
3075+
3076+
struct ExpandPackedTypeConstraints
3077+
: TreeTransform<ExpandPackedTypeConstraints> {
3078+
3079+
using inherited = TreeTransform<ExpandPackedTypeConstraints>;
3080+
3081+
ExpandPackedTypeConstraints(Sema &SemaRef) : inherited(SemaRef) {}
3082+
3083+
using inherited::TransformTemplateTypeParmType;
3084+
3085+
QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
3086+
TemplateTypeParmTypeLoc TL, bool) {
3087+
const TemplateTypeParmType *T = TL.getTypePtr();
3088+
if (!T->isParameterPack()) {
3089+
TemplateTypeParmTypeLoc NewTL =
3090+
TLB.push<TemplateTypeParmTypeLoc>(TL.getType());
3091+
NewTL.setNameLoc(TL.getNameLoc());
3092+
return TL.getType();
3093+
}
3094+
3095+
assert(SemaRef.ArgumentPackSubstitutionIndex != -1);
3096+
3097+
QualType Result = SemaRef.Context.getSubstTemplateTypeParmType(
3098+
TL.getType(), T->getDecl(), T->getIndex(),
3099+
SemaRef.ArgumentPackSubstitutionIndex);
3100+
SubstTemplateTypeParmTypeLoc NewTL =
3101+
TLB.push<SubstTemplateTypeParmTypeLoc>(Result);
3102+
NewTL.setNameLoc(TL.getNameLoc());
3103+
return Result;
3104+
}
3105+
3106+
bool SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
3107+
TemplateArgumentListInfo &Out) {
3108+
return inherited::TransformTemplateArguments(Args.begin(), Args.end(), Out);
3109+
}
3110+
};
3111+
3112+
} // namespace
3113+
30593114
bool Sema::SubstTypeConstraint(
30603115
TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
30613116
const MultiLevelTemplateArgumentList &TemplateArgs,
@@ -3064,9 +3119,62 @@ bool Sema::SubstTypeConstraint(
30643119
TC->getTemplateArgsAsWritten();
30653120

30663121
if (!EvaluateConstraints) {
3067-
Inst->setTypeConstraint(TC->getConceptReference(),
3068-
TC->getImmediatelyDeclaredConstraint());
3069-
return false;
3122+
bool ShouldExpandExplicitTemplateArgs =
3123+
TemplArgInfo && ArgumentPackSubstitutionIndex != -1 &&
3124+
llvm::any_of(TemplArgInfo->arguments(), [](auto &Arg) {
3125+
return Arg.getArgument().containsUnexpandedParameterPack();
3126+
});
3127+
3128+
// We want to transform the packs into Subst* nodes for type constraints
3129+
// inside a pack expansion. For example,
3130+
//
3131+
// template <class... Ts> void foo() {
3132+
// bar([](C<Ts> auto value) {}...);
3133+
// }
3134+
//
3135+
// As we expand Ts in the process of instantiating foo(), and retain
3136+
// the original template depths of Ts until the constraint evaluation, we
3137+
// would otherwise have no chance to expand Ts by the time of evaluating
3138+
// C<auto, Ts>.
3139+
//
3140+
// So we form a Subst* node for Ts along with a proper substitution index
3141+
// here, and substitute the node with a complete MLTAL later in evaluation.
3142+
if (ShouldExpandExplicitTemplateArgs) {
3143+
TemplateArgumentListInfo InstArgs;
3144+
InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc);
3145+
InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc);
3146+
if (ExpandPackedTypeConstraints(*this).SubstTemplateArguments(
3147+
TemplArgInfo->arguments(), InstArgs))
3148+
return true;
3149+
3150+
// The type of the original parameter.
3151+
auto *ConstraintExpr = TC->getImmediatelyDeclaredConstraint();
3152+
QualType ConstrainedType;
3153+
3154+
if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr)) {
3155+
assert(FE->getLHS());
3156+
ConstraintExpr = FE->getLHS();
3157+
}
3158+
auto *CSE = cast<ConceptSpecializationExpr>(ConstraintExpr);
3159+
assert(!CSE->getTemplateArguments().empty() &&
3160+
"Empty template arguments?");
3161+
ConstrainedType = CSE->getTemplateArguments()[0].getAsType();
3162+
assert(!ConstrainedType.isNull() &&
3163+
"Failed to extract the original ConstrainedType?");
3164+
3165+
return AttachTypeConstraint(
3166+
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
3167+
TC->getNamedConcept(),
3168+
/*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs,
3169+
Inst, ConstrainedType,
3170+
Inst->isParameterPack()
3171+
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
3172+
->getEllipsisLoc()
3173+
: SourceLocation());
3174+
}
3175+
Inst->setTypeConstraint(TC->getConceptReference(),
3176+
TC->getImmediatelyDeclaredConstraint());
3177+
return false;
30703178
}
30713179

30723180
TemplateArgumentListInfo InstArgs;
@@ -3082,6 +3190,7 @@ bool Sema::SubstTypeConstraint(
30823190
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
30833191
TC->getNamedConcept(),
30843192
/*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs, Inst,
3193+
Context.getTypeDeclType(Inst),
30853194
Inst->isParameterPack()
30863195
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
30873196
->getEllipsisLoc()

clang/lib/Sema/SemaType.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3035,7 +3035,9 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
30353035
AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(),
30363036
AutoLoc.getNamedConcept(), /*FoundDecl=*/AutoLoc.getFoundDecl(),
30373037
AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr,
3038-
InventedTemplateParam, D.getEllipsisLoc());
3038+
InventedTemplateParam,
3039+
S.Context.getTypeDeclType(InventedTemplateParam),
3040+
D.getEllipsisLoc());
30393041
}
30403042
} else {
30413043
// The 'auto' appears in the decl-specifiers; we've not finished forming
@@ -3072,7 +3074,9 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
30723074
/*FoundDecl=*/
30733075
USD ? cast<NamedDecl>(USD) : CD,
30743076
TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr,
3075-
InventedTemplateParam, D.getEllipsisLoc());
3077+
InventedTemplateParam,
3078+
S.Context.getTypeDeclType(InventedTemplateParam),
3079+
D.getEllipsisLoc());
30763080
}
30773081
}
30783082
}

clang/test/SemaCXX/fold_lambda_with_variadics.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,57 @@ void foo() {
179179
}
180180

181181
} // namespace GH99877
182+
183+
namespace GH101754 {
184+
185+
template <typename... Ts> struct Overloaded : Ts... {
186+
using Ts::operator()...;
187+
};
188+
189+
template <typename... Ts> Overloaded(Ts...) -> Overloaded<Ts...>;
190+
191+
template <class T, class U>
192+
concept same_as = __is_same(T, U); // #same_as
193+
194+
template <typename... Ts> constexpr auto foo() {
195+
return Overloaded{[](same_as<Ts> auto value) { return value; }...}; // #lambda
196+
}
197+
198+
static_assert(foo<int, double>()(123) == 123);
199+
static_assert(foo<int, double>()(2.718) == 2.718);
200+
201+
static_assert(foo<int, double>()('c'));
202+
// expected-error@-1 {{no matching function}}
203+
204+
// expected-note@#lambda {{constraints not satisfied}}
205+
// expected-note@#lambda {{'same_as<char, int>' evaluated to false}}
206+
// expected-note@#same_as {{evaluated to false}}
207+
208+
// expected-note@#lambda {{constraints not satisfied}}
209+
// expected-note@#lambda {{'same_as<char, double>' evaluated to false}}
210+
// expected-note@#same_as {{evaluated to false}}
211+
212+
template <class T, class U, class V>
213+
concept C = same_as<T, U> && same_as<U, V>; // #C
214+
215+
template <typename... Ts> constexpr auto bar() {
216+
return ([]<class Up>() {
217+
return Overloaded{[](C<Up, Ts> auto value) { // #bar
218+
return value;
219+
}...};
220+
}.template operator()<Ts>(), ...);
221+
}
222+
static_assert(bar<int, float>()(3.14f)); // OK, bar() returns the last overload i.e. <float>.
223+
224+
static_assert(bar<int, float>()(123));
225+
// expected-error@-1 {{no matching function}}
226+
// expected-note@#bar {{constraints not satisfied}}
227+
// expected-note@#bar {{'C<int, float, int>' evaluated to false}}
228+
// expected-note@#C {{evaluated to false}}
229+
230+
// expected-note@#bar {{constraints not satisfied}}
231+
// expected-note@#bar {{'C<int, float, float>' evaluated to false}}
232+
// expected-note@#C {{evaluated to false}}
233+
// expected-note@#same_as 2{{evaluated to false}}
234+
235+
} // namespace GH101754

0 commit comments

Comments
 (0)