[clang] [Clang] Implement CWG 2628 "Implicit deduction guides should propagate constraints" (PR #111143)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 4 05:45:52 PDT 2024


================
@@ -478,3 +478,166 @@ A a{.f1 = {1}};
 // CHECK-NEXT:     `-DeclRefExpr {{.+}} <col:10> 'int' NonTypeTemplateParm {{.+}} 'N' 'int'
 
 } // namespace GH83368
+
+namespace GH60777 {
+
+template <typename... Ts> constexpr bool True() { return true; }
+
+template <typename T>
+  requires(sizeof(T) == 4)
+struct A {
+  template <typename... Ts>
+    requires(sizeof...(Ts) == 0)
+  A(T val, Ts... tail)
+    requires(True<Ts...>())
+  {}
+};
+
+A a(42);
+
+// `requires (sizeof(T) == 4)` goes into the deduction guide together with
+// `requires (True<Ts...>())`, while `requires(sizeof...(Ts) == 0)` goes into
+// the template parameter list of the synthesized declaration.
+
+// CHECK-LABEL: Dumping GH60777::<deduction guide for A>:
+// CHECK-NEXT: FunctionTemplateDecl 0x{{.+}} <{{.+}}> {{.+}} implicit <deduction guide for A>
+// CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:20 referenced typename depth 0 index 0 T
+// CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:25 typename depth 0 index 1 ... Ts
+// CHECK-NEXT: |-ParenExpr 0x{{.+}} <{{.+}}> 'bool'
+// CHECK-NEXT: | `-BinaryOperator 0x{{.+}} <{{.+}}> 'bool' '=='
+// CHECK-NEXT: |   |-SizeOfPackExpr 0x{{.+}} <{{.+}}> 'unsigned long' 0x{{.+}} Ts
+// CHECK-NEXT: |   | `-TemplateArgument type 'Ts...':'type-parameter-0-1...'
+// CHECK-NEXT: |   |   `-PackExpansionType 0x{{.+}} 'Ts...' dependent
+// CHECK-NEXT: |   |     `-TemplateTypeParmType 0x{{.+}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack
+// CHECK-NEXT: |   |       `-TemplateTypeParm 0x{{.+}} 'Ts'
+// CHECK-NEXT: |   `-ImplicitCastExpr 0x{{.+}} <{{.+}}> 'unsigned long' <IntegralCast>
+// CHECK-NEXT: |     `-IntegerLiteral 0x{{.+}} <{{.+}}> 'int' 0
+// CHECK-NEXT: |-CXXDeductionGuideDecl 0x{{.+}} <{{.+}}> line:{{.+}} implicit <deduction guide for A> 'auto (T, Ts...) -> A<T>'
+// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <{{.+}}> col:{{.+}} val 'T'
+// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <{{.+}}> col:{{.+}} tail 'Ts...' pack
+// CHECK-NEXT: | `-BinaryOperator 0x{{.+}} <{{.+}}> 'bool' '&&'
+// CHECK-NEXT: |   |-ParenExpr 0x{{.+}} <{{.+}}> 'bool'
+// CHECK-NEXT: |   | `-BinaryOperator 0x{{.+}} <{{.+}}> 'bool' '=='
+// CHECK-NEXT: |   |   |-UnaryExprOrTypeTraitExpr 0x{{.+}} <{{.+}}> 'unsigned long' sizeof 'T'
+// CHECK-NEXT: |   |   `-ImplicitCastExpr 0x{{.+}} <{{.+}}> 'unsigned long' <IntegralCast>
+// CHECK-NEXT: |   |     `-IntegerLiteral 0x{{.+}} <{{.+}}> 'int' 4
+// CHECK-NEXT: |   `-ParenExpr 0x{{.+}} <{{.+}}> '<dependent type>'
+// CHECK-NEXT: |     `-CallExpr 0x{{.+}} <{{.+}}> '<dependent type>'
+// CHECK-NEXT: |       `-UnresolvedLookupExpr 0x{{.+}} <col:14, col:24> '<dependent type>' lvalue (ADL) = 'True' 0x{{.+}}
+// CHECK-NEXT: |         `-TemplateArgument type 'Ts...':'type-parameter-0-1...'
+// CHECK-NEXT: |           `-PackExpansionType 0x{{.+}} 'Ts...' dependent
+// CHECK-NEXT: |             `-TemplateTypeParmType 0x{{.+}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack
+// CHECK-NEXT: |               `-TemplateTypeParm 0x{{.+}} 'Ts'
+
+template <typename T>
+struct B {
+  template <typename... Ts>
+  B(T val, Ts... tail)
+    requires(True<tail...>())
+  {}
+};
+
+B b(42, 43);
+// expected-error at -1 {{no viable constructor}} \
+//   expected-note at -6 {{constraints not satisfied}} \
+//   expected-note at -5 {{because substituted constraint expression is ill-formed}} \
+//   expected-note at -6 {{implicit deduction guide declared as 'template <typename T, typename ...Ts> B(T val, Ts ...tail) -> B<T> requires (True<tail...>())'}} \
+//   expected-note at -8 {{function template not viable}} \
+//   expected-note at -8 {{implicit deduction guide declared as 'template <typename T> B(B<T>) -> B<T>'}}
+
+} // namespace GH60777
+
+// Examples from @hokein.
+namespace GH98592 {
+
+template <class T> concept True = true;
+double arr3[3];
+
+template <class T>
+struct X {
+  const int size;
+  template <class U>
+  constexpr X(T, U(&)[3]) requires True<T> : size(sizeof(T)) {}
+};
+
+template <typename T, typename U>
+X(T, U (&)[3]) -> X<U>;
+
+constexpr X x(3, arr3);
+
+// The synthesized deduction guide is more constrained than the explicit one.
+static_assert(x.size == 4);
+
+// CHECK-LABEL: Dumping GH98592::<deduction guide for X>:
+// CHECK-NEXT: FunctionTemplateDecl 0x{{.+}} <{{.+}}> col:13 implicit <deduction guide for X>
+// CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:17 referenced class depth 0 index 0 T
+// CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:19 class depth 0 index 1 U
+// CHECK-NEXT: |-CXXDeductionGuideDecl 0x{{.+}} <{{.+}}> col:13 implicit <deduction guide for X> 'auto (T, U (&)[3]) -> X<T>'
+// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <col:15> col:16 'T'
+// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'U (&)[3]'
+// CHECK-NEXT: | `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True'
+// CHECK-NEXT: |   |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
+// CHECK-NEXT: |   | `-TemplateArgument type 'type-parameter-0-0'
+// CHECK-NEXT: |   |   `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
+// CHECK-NEXT: |   `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
+// CHECK-NEXT: |     `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT: |       `-TemplateTypeParm 0x{{.+}} 'T'
+// CHECK-NEXT: `-CXXDeductionGuideDecl 0x{{.+}} <col:3, col:63> col:13 implicit used <deduction guide for X> 'auto (int, double (&)[3]) -> GH98592::X<int>' implicit_instantiation
+// CHECK-NEXT:   |-TemplateArgument type 'int'
+// CHECK-NEXT:   | `-BuiltinType 0x{{.+}} 'int'
+// CHECK-NEXT:   |-TemplateArgument type 'double'
+// CHECK-NEXT:   | `-BuiltinType 0x{{.+}} 'double'
+// CHECK-NEXT:   |-ParmVarDecl 0x{{.+}} <col:15> col:16 'int'
+// CHECK-NEXT:   |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'double (&)[3]'
+// CHECK-NEXT:   `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True'
+// CHECK-NEXT:     |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
+// CHECK-NEXT:     | `-TemplateArgument type 'type-parameter-0-0'
+// CHECK-NEXT:     |   `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
+// CHECK-NEXT:     `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
+// CHECK-NEXT:       `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT:         `-TemplateTypeParm 0x{{.+}} 'T'
+
+template <class T> requires True<T> struct Y {
+  const int size;
+  template <class U>
+  constexpr Y(T, U(&)[3]) : size(sizeof(T)) {}
+};
+
+template <typename T, typename U> Y(T, U (&)[3]) -> Y<U>;
+
+constexpr Y y(3, arr3);
+
+// Likewise, the synthesized deduction guide should be preferred
+// according to [over.match.class.deduct]p1.
+static_assert(y.size == 4);
----------------
zyn0217 wrote:

Though been discussed in https://github.com/llvm/llvm-project/issues/98592, It's not so clear to me as to whether this case should be accepted:

While the wording from [[over.match.class.deduct]p1.1.2](https://eel.is/c++draft/over.match.class.deduct#1.1.2) suggests the constraint of the synthesized CTAD is the *conjunction* of that of C and that of the constructor, the note there also seems to imply the constraint from C should be checked before the checking of the constraint of constructor, so this example should not compile that way. (The overload resolution would pick up the explicit CTAD guide if C's required-clause were not propagated into the implicit CTAD guide.)

I still build up a && operator for the literal "conjunction" currently, but I think I can remove that if our reading is wrong.

https://github.com/llvm/llvm-project/pull/111143


More information about the cfe-commits mailing list