Skip to content

Commit 834ecc8

Browse files
authored
[clang] CTAD alias: fix transformation for require-clause expr Part2. (#93533)
In the #90961 fix, we miss a case where the undeduced template parameters of the underlying deduction guide are not transformed, which leaves incorrect depth/index information, and causes crashes when evaluating constraints. This patch fix this missing case. Fixes #92596 Fixes #92212
1 parent 0ccec54 commit 834ecc8

File tree

3 files changed

+82
-8
lines changed

3 files changed

+82
-8
lines changed

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,7 +2743,7 @@ Expr *
27432743
buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
27442744
TypeAliasTemplateDecl *AliasTemplate,
27452745
ArrayRef<DeducedTemplateArgument> DeduceResults,
2746-
Expr *IsDeducible) {
2746+
unsigned FirstUndeducedParamIdx, Expr *IsDeducible) {
27472747
Expr *RC = F->getTemplateParameters()->getRequiresClause();
27482748
if (!RC)
27492749
return IsDeducible;
@@ -2803,8 +2803,22 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
28032803

28042804
for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
28052805
const auto &D = DeduceResults[Index];
2806-
if (D.isNull())
2806+
if (D.isNull()) { // non-deduced template parameters of f
2807+
NamedDecl *TP = F->getTemplateParameters()->getParam(Index);
2808+
MultiLevelTemplateArgumentList Args;
2809+
Args.setKind(TemplateSubstitutionKind::Rewrite);
2810+
Args.addOuterTemplateArguments(TemplateArgsForBuildingRC);
2811+
// Rebuild the template parameter with updated depth and index.
2812+
NamedDecl *NewParam = transformTemplateParameter(
2813+
SemaRef, F->getDeclContext(), TP, Args,
2814+
/*NewIndex=*/FirstUndeducedParamIdx,
2815+
getTemplateParameterDepth(TP) + AdjustDepth);
2816+
FirstUndeducedParamIdx += 1;
2817+
assert(TemplateArgsForBuildingRC[Index].isNull());
2818+
TemplateArgsForBuildingRC[Index] = Context.getCanonicalTemplateArgument(
2819+
Context.getInjectedTemplateArg(NewParam));
28072820
continue;
2821+
}
28082822
TemplateArgumentLoc Input =
28092823
SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{});
28102824
TemplateArgumentLoc Output;
@@ -2820,8 +2834,8 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
28202834
MultiLevelTemplateArgumentList ArgsForBuildingRC;
28212835
ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite);
28222836
ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC);
2823-
// For 2), if the underlying F is instantiated from a member template, we need
2824-
// the entire template argument list, as the constraint AST in the
2837+
// For 2), if the underlying deduction guide F is nested in a class template,
2838+
// we need the entire template argument list, as the constraint AST in the
28252839
// require-clause of F remains completely uninstantiated.
28262840
//
28272841
// For example:
@@ -2845,7 +2859,13 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
28452859
// We add the outer template arguments which is [int] to the multi-level arg
28462860
// list to ensure that the occurrence U in `C<U>` will be replaced with int
28472861
// during the substitution.
2848-
if (F->getInstantiatedFromMemberTemplate()) {
2862+
//
2863+
// NOTE: The underlying deduction guide F is instantiated -- either from an
2864+
// explicitly-written deduction guide member, or from a constructor.
2865+
// getInstantiatedFromMemberTemplate() can only handle the former case, so we
2866+
// check the DeclContext kind.
2867+
if (F->getLexicalDeclContext()->getDeclKind() ==
2868+
clang::Decl::ClassTemplateSpecialization) {
28492869
auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs(
28502870
F, F->getLexicalDeclContext(),
28512871
/*Final=*/false, /*Innermost=*/std::nullopt,
@@ -3063,6 +3083,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
30633083
Context.getInjectedTemplateArg(NewParam));
30643084
TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument;
30653085
}
3086+
unsigned FirstUndeducedParamIdx = FPrimeTemplateParams.size();
30663087
// ...followed by the template parameters of f that were not deduced
30673088
// (including their default template arguments)
30683089
for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) {
@@ -3131,8 +3152,9 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
31313152

31323153
Expr *IsDeducible = buildIsDeducibleConstraint(
31333154
SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
3134-
Expr *RequiresClause = buildAssociatedConstraints(
3135-
SemaRef, F, AliasTemplate, DeduceResults, IsDeducible);
3155+
Expr *RequiresClause =
3156+
buildAssociatedConstraints(SemaRef, F, AliasTemplate, DeduceResults,
3157+
FirstUndeducedParamIdx, IsDeducible);
31363158

31373159
auto *FPrimeTemplateParamList = TemplateParameterList::Create(
31383160
Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),

clang/test/AST/ast-dump-ctad-alias.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,33 @@ Out2<double>::AInner t(1.0);
5353
// CHECK-NEXT: | | `-BuiltinType {{.*}} 'double'
5454
// CHECK-NEXT: | `-ParmVarDecl {{.*}} 'double'
5555

56+
// GH92596
57+
template <typename T0>
58+
struct Out3 {
59+
template<class T1, typename T2>
60+
struct Foo {
61+
// Deduction guide:
62+
// template <typename T1, typename T2, typename V>
63+
// Foo(V, T1) -> Foo<T1, T2>;
64+
template<class V> requires Concept<T0, V> // V in require clause of Foo deduction guide: depth 1, index: 2
65+
Foo(V, T1);
66+
};
67+
};
68+
template<class T3>
69+
using AFoo3 = Out3<int>::Foo<T3, T3>;
70+
AFoo3 afoo3{0, 1};
71+
// Verify occurrence V in the require-clause is transformed (depth: 1 => 0, index: 2 => 1) correctly.
72+
73+
// CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for AFoo3>
74+
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 0 T3
75+
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 1 V
76+
// CHECK-NEXT: |-BinaryOperator {{.*}} '<dependent type>' '&&'
77+
// CHECK-NEXT: | |-UnresolvedLookupExpr {{.*}} '<dependent type>' lvalue (no ADL) = 'Concept'
78+
// CHECK-NEXT: | | |-TemplateArgument type 'int'
79+
// CHECK-NEXT: | | | `-BuiltinType {{.*}} 'int'
80+
// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-0-1'
81+
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent depth 0 index 1
82+
5683
template <typename... T1>
5784
struct Foo {
5885
Foo(T1...);

clang/test/SemaCXX/cxx20-ctad-type-alias.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ template <typename U>
266266
using Bar = Foo<U>; // expected-note {{could not match 'Foo<type-parameter-0-0>' against 'int'}} \
267267
// expected-note {{implicit deduction guide declared as 'template <typename U> requires __is_deducible(test18::Bar, Foo<type-parameter-0-0>) Bar(Foo<type-parameter-0-0>) -> Foo<type-parameter-0-0>'}} \
268268
// expected-note {{candidate template ignored: constraints not satisfied}} \
269-
// expected-note {{implicit deduction guide declared as 'template <typename T> requires False<T> && __is_deducible(test18::Bar, Foo<int>) Bar(type-parameter-0-0) -> Foo<int>'}} \
269+
// expected-note {{implicit deduction guide declared as 'template <typename T> requires False<type-parameter-0-0> && __is_deducible(test18::Bar, Foo<int>) Bar(type-parameter-0-0) -> Foo<int>'}} \
270270
// expected-note {{candidate function template not viable}} \
271271
// expected-note {{implicit deduction guide declared as 'template <typename U> requires __is_deducible(test18::Bar, Foo<type-parameter-0-0>) Bar() -> Foo<type-parameter-0-0>'}}
272272

@@ -414,4 +414,29 @@ struct A1 {
414414
template <typename U>
415415
using AFoo = A1<int>::A2<int>::Foo<U>;
416416
AFoo case3(1);
417+
418+
// Case4: crashes on the constexpr evaluator due to the mixed-up index for the
419+
// template parameters `V`.
420+
template<class T, typename T2>
421+
struct Case4 {
422+
template<class V> requires C<V>
423+
Case4(V, T);
424+
};
425+
426+
template<class T2>
427+
using ACase4 = Case4<T2, T2>;
428+
ACase4 case4{0, 1};
429+
417430
} // namespace test24
431+
432+
namespace GH92212 {
433+
template<typename T, typename...Us>
434+
struct A{
435+
template<typename V> requires __is_same(V, int)
436+
A(V);
437+
};
438+
439+
template<typename...TS>
440+
using AA = A<int, TS...>;
441+
AA a{0};
442+
}

0 commit comments

Comments
 (0)