Skip to content

Commit a144bf2

Browse files
authored
[Clang] Fix handling of brace ellison when building deduction guides (#94889)
Fixes two issues in two ways: 1) The `braced-init-list` consisted of `initializer-list` and `designated-initializer-list`, and thus the designated initializer is subject to [over.match.class.deduct]p1.8, which means the brace elision is also applicable on it for CTAD deduction guides. 2) When forming a deduction guide where the brace elision is applicable, we should also consider the presence of braces within the initializer. For example, given template <class T, class U> struct X { T t[2]; U u[3]; }; X x = {{1, 2}, 3, 4, 5}; we should establish such deduction guide AFAIU: `X(T (&&)[2], U, U, U) -> X<T, U>`. Fixes #64625 Fixes #83368
1 parent 3475116 commit a144bf2

File tree

3 files changed

+158
-4
lines changed

3 files changed

+158
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ Bug Fixes to C++ Support
846846
- Fix a crash caused by improper use of ``__array_extent``. (#GH80474)
847847
- Fixed several bugs in capturing variables within unevaluated contexts. (#GH63845), (#GH67260), (#GH69307),
848848
(#GH88081), (#GH89496), (#GH90669) and (#GH91633).
849+
- Fixed handling of brace ellison when building deduction guides. (#GH64625), (#GH83368).
849850

850851
Bug Fixes to AST Handling
851852
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaInit.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ class InitListChecker {
513513
: InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true,
514514
/*TreatUnavailableAsInvalid=*/false,
515515
/*InOverloadResolution=*/false,
516-
&AggrDeductionCandidateParamTypes){};
516+
&AggrDeductionCandidateParamTypes) {}
517517

518518
bool HadError() { return hadError; }
519519

@@ -1443,7 +1443,21 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
14431443
// dependent non-array type or an array type with a value-dependent
14441444
// bound
14451445
assert(AggrDeductionCandidateParamTypes);
1446-
if (!isa_and_nonnull<ConstantArrayType>(
1446+
1447+
// In the presence of a braced-init-list within the initializer, we should
1448+
// not perform brace-elision, even if brace elision would otherwise be
1449+
// applicable. For example, given:
1450+
//
1451+
// template <class T> struct Foo {
1452+
// T t[2];
1453+
// };
1454+
//
1455+
// Foo t = {{1, 2}};
1456+
//
1457+
// we don't want the (T, T) but rather (T [2]) in terms of the initializer
1458+
// {{1, 2}}.
1459+
if (isa<InitListExpr, DesignatedInitExpr>(expr) ||
1460+
!isa_and_present<ConstantArrayType>(
14471461
SemaRef.Context.getAsArrayType(ElemType))) {
14481462
++Index;
14491463
AggrDeductionCandidateParamTypes->push_back(ElemType);
@@ -10940,14 +10954,14 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
1094010954
// if e_i is of array type and x_i is a braced-init-list, T_i is an
1094110955
// rvalue reference to the declared type of e_i and
1094210956
// C++ [over.match.class.deduct]p1.9:
10943-
// if e_i is of array type and x_i is a bstring-literal, T_i is an
10957+
// if e_i is of array type and x_i is a string-literal, T_i is an
1094410958
// lvalue reference to the const-qualified declared type of e_i and
1094510959
// C++ [over.match.class.deduct]p1.10:
1094610960
// otherwise, T_i is the declared type of e_i
1094710961
for (int I = 0, E = ListInit->getNumInits();
1094810962
I < E && !isa<PackExpansionType>(ElementTypes[I]); ++I)
1094910963
if (ElementTypes[I]->isArrayType()) {
10950-
if (isa<InitListExpr>(ListInit->getInit(I)))
10964+
if (isa<InitListExpr, DesignatedInitExpr>(ListInit->getInit(I)))
1095110965
ElementTypes[I] = Context.getRValueReferenceType(ElementTypes[I]);
1095210966
else if (isa<StringLiteral>(
1095310967
ListInit->getInit(I)->IgnoreParenImpCasts()))

clang/test/SemaTemplate/deduction-guide.cpp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,142 @@ namespace TTP {
335335
// CHECK-NEXT: `-TemplateArgument type 'T':'type-parameter-0-0'{{$}}
336336
// CHECK-NEXT: `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0{{$}}
337337
// CHECK-NEXT: `-TemplateTypeParm {{.+}} 'T'{{$}}
338+
339+
namespace GH64625 {
340+
341+
template <class T> struct X {
342+
T t[2];
343+
};
344+
345+
X x = {{1, 2}};
346+
347+
// CHECK-LABEL: Dumping GH64625::<deduction guide for X>:
348+
// CHECK-NEXT: FunctionTemplateDecl {{.+}} <{{.+}}:[[#@LINE - 7]]:1, col:27> col:27 implicit <deduction guide for X>
349+
// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} <col:11, col:17> col:17 referenced class depth 0 index 0 T
350+
// CHECK: |-CXXDeductionGuideDecl {{.+}} <col:27> col:27 implicit <deduction guide for X> 'auto (T (&&)[2]) -> X<T>' aggregate
351+
// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:27> col:27 'T (&&)[2]'
352+
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:27> col:27 implicit used <deduction guide for X> 'auto (int (&&)[2]) -> GH64625::X<int>' implicit_instantiation aggregate
353+
// CHECK-NEXT: |-TemplateArgument type 'int'
354+
// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
355+
// CHECK-NEXT: `-ParmVarDecl {{.+}} <col:27> col:27 'int (&&)[2]'
356+
// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T (&&)[2]) -> X<T>' dependent trailing_return
357+
// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'X<T>' dependent
358+
// CHECK-NEXT: | `-CXXRecord {{.+}} 'X'
359+
// CHECK-NEXT: `-RValueReferenceType {{.+}} 'T (&&)[2]' dependent
360+
// CHECK-NEXT: `-ConstantArrayType {{.+}} 'T[2]' dependent 2
361+
// CHECK-NEXT: `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
362+
// CHECK-NEXT: `-TemplateTypeParm {{.+}} 'T'
363+
364+
template <class T, class U> struct TwoArrays {
365+
T t[2];
366+
U u[3];
367+
};
368+
369+
TwoArrays ta = {{1, 2}, {3, 4, 5}};
370+
// CHECK-LABEL: Dumping GH64625::<deduction guide for TwoArrays>:
371+
// CHECK-NEXT: FunctionTemplateDecl {{.+}} <{{.+}}:[[#@LINE - 7]]:1, col:36> col:36 implicit <deduction guide for TwoArrays>
372+
// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} <col:11, col:17> col:17 referenced class depth 0 index 0 T
373+
// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} <col:20, col:26> col:26 referenced class depth 0 index 1 U
374+
// CHECK: |-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit <deduction guide for TwoArrays> 'auto (T (&&)[2], U (&&)[3]) -> TwoArrays<T, U>' aggregate
375+
// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'T (&&)[2]'
376+
// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:36> col:36 'U (&&)[3]'
377+
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit used <deduction guide for TwoArrays> 'auto (int (&&)[2], int (&&)[3]) -> GH64625::TwoArrays<int, int>' implicit_instantiation aggregate
378+
// CHECK-NEXT: |-TemplateArgument type 'int'
379+
// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
380+
// CHECK-NEXT: |-TemplateArgument type 'int'
381+
// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
382+
// CHECK-NEXT: |-ParmVarDecl {{.+}} <col:36> col:36 'int (&&)[2]'
383+
// CHECK-NEXT: `-ParmVarDecl {{.+}} <col:36> col:36 'int (&&)[3]'
384+
// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T (&&)[2], U (&&)[3]) -> TwoArrays<T, U>' dependent trailing_return
385+
// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'TwoArrays<T, U>' dependent
386+
// CHECK-NEXT: | `-CXXRecord {{.+}} 'TwoArrays'
387+
// CHECK-NEXT: |-RValueReferenceType {{.+}} 'T (&&)[2]' dependent
388+
// CHECK-NEXT: | `-ConstantArrayType {{.+}} 'T[2]' dependent 2
389+
// CHECK-NEXT: | `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
390+
// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
391+
// CHECK-NEXT: `-RValueReferenceType {{.+}} 'U (&&)[3]' dependent
392+
// CHECK-NEXT: `-ConstantArrayType {{.+}} 'U[3]' dependent 3
393+
// CHECK-NEXT: `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
394+
// CHECK-NEXT: `-TemplateTypeParm {{.+}} 'U'
395+
396+
TwoArrays tb = {1, 2, {3, 4, 5}};
397+
// CHECK: |-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit <deduction guide for TwoArrays> 'auto (T, T, U (&&)[3]) -> TwoArrays<T, U>' aggregate
398+
// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'T'
399+
// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'T'
400+
// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:36> col:36 'U (&&)[3]'
401+
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit used <deduction guide for TwoArrays> 'auto (int, int, int (&&)[3]) -> GH64625::TwoArrays<int, int>' implicit_instantiation aggregate
402+
// CHECK-NEXT: |-TemplateArgument type 'int'
403+
// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
404+
// CHECK-NEXT: |-TemplateArgument type 'int'
405+
// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
406+
// CHECK-NEXT: |-ParmVarDecl {{.+}} <col:36> col:36 'int'
407+
// CHECK-NEXT: |-ParmVarDecl {{.+}} <col:36> col:36 'int'
408+
// CHECK-NEXT: `-ParmVarDecl {{.+}} <col:36> col:36 'int (&&)[3]'
409+
// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T, T, U (&&)[3]) -> TwoArrays<T, U>' dependent trailing_return
410+
// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'TwoArrays<T, U>' dependent
411+
// CHECK-NEXT: | `-CXXRecord {{.+}} 'TwoArrays'
412+
// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
413+
// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
414+
// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
415+
// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
416+
// CHECK-NEXT: `-RValueReferenceType {{.+}} 'U (&&)[3]' dependent
417+
// CHECK-NEXT: `-ConstantArrayType {{.+}} 'U[3]' dependent 3
418+
// CHECK-NEXT: `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
419+
// CHECK-NEXT: `-TemplateTypeParm {{.+}} 'U'
420+
421+
TwoArrays tc = {{1, 2}, 3, 4, 5};
422+
// CHECK: |-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit <deduction guide for TwoArrays> 'auto (T (&&)[2], U, U, U) -> TwoArrays<T, U>' aggregate
423+
// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'T (&&)[2]'
424+
// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'U'
425+
// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'U'
426+
// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:36> col:36 'U'
427+
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit used <deduction guide for TwoArrays> 'auto (int (&&)[2], int, int, int) -> GH64625::TwoArrays<int, int>' implicit_instantiation aggregate
428+
// CHECK-NEXT: |-TemplateArgument type 'int'
429+
// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
430+
// CHECK-NEXT: |-TemplateArgument type 'int'
431+
// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
432+
// CHECK-NEXT: |-ParmVarDecl {{.+}} <col:36> col:36 'int (&&)[2]'
433+
// CHECK-NEXT: |-ParmVarDecl {{.+}} <col:36> col:36 'int'
434+
// CHECK-NEXT: |-ParmVarDecl {{.+}} <col:36> col:36 'int'
435+
// CHECK-NEXT: `-ParmVarDecl {{.+}} <col:36> col:36 'int'
436+
// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T (&&)[2], U, U, U) -> TwoArrays<T, U>' dependent trailing_return
437+
// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'TwoArrays<T, U>' dependent
438+
// CHECK-NEXT: | `-CXXRecord {{.+}} 'TwoArrays'
439+
// CHECK-NEXT: |-RValueReferenceType {{.+}} 'T (&&)[2]' dependent
440+
// CHECK-NEXT: | `-ConstantArrayType {{.+}} 'T[2]' dependent 2
441+
// CHECK-NEXT: | `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
442+
// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
443+
// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
444+
// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'U'
445+
// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
446+
// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'U'
447+
// CHECK-NEXT: `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
448+
// CHECK-NEXT: `-TemplateTypeParm {{.+}} 'U'
449+
450+
} // namespace GH64625
451+
452+
namespace GH83368 {
453+
454+
template <int N> struct A {
455+
int f1[N];
456+
};
457+
458+
A a{.f1 = {1}};
459+
460+
// CHECK-LABEL: Dumping GH83368::<deduction guide for A>:
461+
// CHECK-NEXT: FunctionTemplateDecl 0x{{.+}} <{{.+}}:[[#@LINE - 7]]:1, col:25> col:25 implicit <deduction guide for A>
462+
// CHECK-NEXT: |-NonTypeTemplateParmDecl {{.+}} <col:11, col:15> col:15 referenced 'int' depth 0 index 0 N
463+
// CHECK: |-CXXDeductionGuideDecl {{.+}} <col:25> col:25 implicit <deduction guide for A> 'auto (int (&&)[N]) -> A<N>' aggregate
464+
// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:25> col:25 'int (&&)[N]'
465+
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:25> col:25 implicit used <deduction guide for A> 'auto (int (&&)[1]) -> GH83368::A<1>' implicit_instantiation aggregate
466+
// CHECK-NEXT: |-TemplateArgument integral '1'
467+
// CHECK-NEXT: `-ParmVarDecl {{.+}} <col:25> col:25 'int (&&)[1]'
468+
// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (int (&&)[N]) -> A<N>' dependent trailing_return
469+
// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'A<N>' dependent
470+
// CHECK-NEXT: | `-CXXRecord {{.+}} 'A'
471+
// CHECK-NEXT: `-RValueReferenceType {{.+}} 'int (&&)[N]' dependent
472+
// CHECK-NEXT: `-DependentSizedArrayType {{.+}} 'int[N]' dependent
473+
// CHECK-NEXT: |-BuiltinType {{.+}} 'int'
474+
// CHECK-NEXT: `-DeclRefExpr {{.+}} <col:10> 'int' NonTypeTemplateParm {{.+}} 'N' 'int'
475+
476+
} // namespace GH83368

0 commit comments

Comments
 (0)