Skip to content

Commit b46fcb9

Browse files
authoredJan 23, 2025
[Clang] Implement CWG 2628 "Implicit deduction guides should propagate constraints" (#111143)
Closes llvm/llvm-project#98592
1 parent 3ef90f8 commit b46fcb9

File tree

5 files changed

+227
-30
lines changed

5 files changed

+227
-30
lines changed
 

‎clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,9 @@ Resolutions to C++ Defect Reports
363363
- Clang now allows trailing requires clause on explicit deduction guides.
364364
(`CWG2707: Deduction guides cannot have a trailing requires-clause <https://cplusplus.github.io/CWG/issues/2707.html>`_).
365365

366+
- Respect constructor constraints during CTAD.
367+
(`CWG2628: Implicit deduction guides should propagate constraints <https://cplusplus.github.io/CWG/issues/2628.html>`_).
368+
366369
- Clang now diagnoses a space in the first production of a ``literal-operator-id``
367370
by default.
368371
(`CWG2521: User-defined literals and reserved identifiers <https://cplusplus.github.io/CWG/issues/2521.html>`_).

‎clang/lib/Sema/SemaTemplateDeductionGuide.cpp

+55-15
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,14 @@ class ExtractTypeForDeductionGuide
194194
// A deduction guide can be either a template or a non-template function
195195
// declaration. If \p TemplateParams is null, a non-template function
196196
// declaration will be created.
197-
NamedDecl *buildDeductionGuide(
198-
Sema &SemaRef, TemplateDecl *OriginalTemplate,
199-
TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor,
200-
ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart,
201-
SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
202-
llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}) {
197+
NamedDecl *
198+
buildDeductionGuide(Sema &SemaRef, TemplateDecl *OriginalTemplate,
199+
TemplateParameterList *TemplateParams,
200+
CXXConstructorDecl *Ctor, ExplicitSpecifier ES,
201+
TypeSourceInfo *TInfo, SourceLocation LocStart,
202+
SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
203+
llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {},
204+
Expr *FunctionTrailingRC = nullptr) {
203205
DeclContext *DC = OriginalTemplate->getDeclContext();
204206
auto DeductionGuideName =
205207
SemaRef.Context.DeclarationNames.getCXXDeductionGuideName(
@@ -210,9 +212,9 @@ NamedDecl *buildDeductionGuide(
210212
TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams();
211213

212214
// Build the implicit deduction guide template.
213-
auto *Guide =
214-
CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name,
215-
TInfo->getType(), TInfo, LocEnd, Ctor);
215+
auto *Guide = CXXDeductionGuideDecl::Create(
216+
SemaRef.Context, DC, LocStart, ES, Name, TInfo->getType(), TInfo, LocEnd,
217+
Ctor, DeductionCandidate::Normal, FunctionTrailingRC);
216218
Guide->setImplicit(IsImplicit);
217219
Guide->setParams(Params);
218220

@@ -354,10 +356,11 @@ struct ConvertConstructorToDeductionGuideTransform {
354356
// template arguments) of the constructor, if any.
355357
TemplateParameterList *TemplateParams =
356358
SemaRef.GetTemplateParameterList(Template);
359+
SmallVector<TemplateArgument, 16> Depth1Args;
360+
Expr *OuterRC = TemplateParams->getRequiresClause();
357361
if (FTD) {
358362
TemplateParameterList *InnerParams = FTD->getTemplateParameters();
359363
SmallVector<NamedDecl *, 16> AllParams;
360-
SmallVector<TemplateArgument, 16> Depth1Args;
361364
AllParams.reserve(TemplateParams->size() + InnerParams->size());
362365
AllParams.insert(AllParams.begin(), TemplateParams->begin(),
363366
TemplateParams->end());
@@ -390,7 +393,7 @@ struct ConvertConstructorToDeductionGuideTransform {
390393
/*EvaluateConstraint=*/false);
391394
}
392395

393-
assert(NewParam->getTemplateDepth() == 0 &&
396+
assert(getDepthAndIndex(NewParam).first == 0 &&
394397
"Unexpected template parameter depth");
395398

396399
AllParams.push_back(NewParam);
@@ -406,10 +409,11 @@ struct ConvertConstructorToDeductionGuideTransform {
406409
Args.addOuterRetainedLevel();
407410
if (NestedPattern)
408411
Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());
409-
ExprResult E = SemaRef.SubstExpr(InnerRC, Args);
410-
if (E.isInvalid())
412+
ExprResult E =
413+
SemaRef.SubstConstraintExprWithoutSatisfaction(InnerRC, Args);
414+
if (!E.isUsable())
411415
return nullptr;
412-
RequiresClause = E.getAs<Expr>();
416+
RequiresClause = E.get();
413417
}
414418

415419
TemplateParams = TemplateParameterList::Create(
@@ -445,10 +449,46 @@ struct ConvertConstructorToDeductionGuideTransform {
445449
return nullptr;
446450
TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType);
447451

452+
// At this point, the function parameters are already 'instantiated' in the
453+
// current scope. Substitute into the constructor's trailing
454+
// requires-clause, if any.
455+
Expr *FunctionTrailingRC = nullptr;
456+
if (Expr *RC = CD->getTrailingRequiresClause()) {
457+
MultiLevelTemplateArgumentList Args;
458+
Args.setKind(TemplateSubstitutionKind::Rewrite);
459+
Args.addOuterTemplateArguments(Depth1Args);
460+
Args.addOuterRetainedLevel();
461+
if (NestedPattern)
462+
Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());
463+
ExprResult E = SemaRef.SubstConstraintExprWithoutSatisfaction(RC, Args);
464+
if (!E.isUsable())
465+
return nullptr;
466+
FunctionTrailingRC = E.get();
467+
}
468+
469+
// C++ [over.match.class.deduct]p1:
470+
// If C is defined, for each constructor of C, a function template with
471+
// the following properties:
472+
// [...]
473+
// - The associated constraints are the conjunction of the associated
474+
// constraints of C and the associated constraints of the constructor, if
475+
// any.
476+
if (OuterRC) {
477+
// The outer template parameters are not transformed, so their
478+
// associated constraints don't need substitution.
479+
if (!FunctionTrailingRC)
480+
FunctionTrailingRC = OuterRC;
481+
else
482+
FunctionTrailingRC = BinaryOperator::Create(
483+
SemaRef.Context, /*lhs=*/OuterRC, /*rhs=*/FunctionTrailingRC,
484+
BO_LAnd, SemaRef.Context.BoolTy, VK_PRValue, OK_Ordinary,
485+
TemplateParams->getTemplateLoc(), FPOptionsOverride());
486+
}
487+
448488
return buildDeductionGuide(
449489
SemaRef, Template, TemplateParams, CD, CD->getExplicitSpecifier(),
450490
NewTInfo, CD->getBeginLoc(), CD->getLocation(), CD->getEndLoc(),
451-
/*IsImplicit=*/true, MaterializedTypedefs);
491+
/*IsImplicit=*/true, MaterializedTypedefs, FunctionTrailingRC);
452492
}
453493

454494
/// Build a deduction guide with the specified parameter types.

‎clang/test/CXX/drs/cwg26xx.cpp

+5-14
Original file line numberDiff line numberDiff line change
@@ -132,27 +132,18 @@ struct E {
132132
#endif
133133
} // namespace cwg2627
134134

135-
namespace cwg2628 { // cwg2628: no
136-
// this was reverted for the 16.x release
137-
// due to regressions, see the issue for more details:
138-
// https://github.com/llvm/llvm-project/issues/60777
135+
namespace cwg2628 { // cwg2628: 20
139136
#if __cplusplus >= 202002L
140137
template <bool A = false, bool B = false>
141138
struct foo {
142-
// The expected notes below should be removed when cwg2628 is fully implemented again
143-
constexpr foo() requires (!A && !B) = delete; // #cwg2628-ctor-1
144-
constexpr foo() requires (A || B) = delete; // #cwg2628-ctor-2
139+
constexpr foo() requires (!A && !B) = delete; // #cwg2628-ctor
140+
constexpr foo() requires (A || B) = delete;
145141
};
146142

147143
void f() {
148-
// The FIXME's below should be the expected errors when cwg2628 is
149-
// fully implemented again.
150144
foo fooable; // #cwg2628-fooable
151-
// since-cxx20-error@-1 {{ambiguous deduction for template arguments of 'foo'}}
152-
// since-cxx20-note@#cwg2628-ctor-1 {{candidate function [with A = false, B = false]}}
153-
// since-cxx20-note@#cwg2628-ctor-2 {{candidate function [with A = false, B = false]}}
154-
// FIXME-since-cxx20-error@#cwg2628-fooable {{call to deleted}}
155-
// FIXME-since-cxx20-note@#cwg2628-ctor {{marked deleted here}}
145+
// since-cxx20-error@#cwg2628-fooable {{call to deleted}}
146+
// since-cxx20-note@#cwg2628-ctor {{marked deleted here}}
156147
}
157148
#endif
158149
} // namespace cwg2628

‎clang/test/SemaTemplate/deduction-guide.cpp

+163
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,169 @@ A a{.f1 = {1}};
479479

480480
} // namespace GH83368
481481

482+
namespace GH60777 {
483+
484+
template <typename... Ts> constexpr bool True() { return true; }
485+
486+
template <typename T>
487+
requires(sizeof(T) > 1)
488+
struct A {
489+
template <typename... Ts>
490+
requires(sizeof...(Ts) == 0)
491+
A(T val, Ts... tail)
492+
requires(True<Ts...>())
493+
{}
494+
};
495+
496+
A a(42);
497+
498+
// `requires (sizeof(T) > 1)` goes into the deduction guide together with
499+
// `requires (True<Ts...>())`, while `requires(sizeof...(Ts) == 0)` goes into
500+
// the template parameter list of the synthesized declaration.
501+
502+
// CHECK-LABEL: Dumping GH60777::<deduction guide for A>:
503+
// CHECK-NEXT: FunctionTemplateDecl 0x{{.+}} <{{.+}}> {{.+}} implicit <deduction guide for A>
504+
// CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:20 referenced typename depth 0 index 0 T
505+
// CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:25 typename depth 0 index 1 ... Ts
506+
// CHECK-NEXT: |-ParenExpr 0x{{.+}} <{{.+}}> 'bool'
507+
// CHECK-NEXT: | `-BinaryOperator 0x{{.+}} <{{.+}}> 'bool' '=='
508+
// CHECK-NEXT: | |-SizeOfPackExpr {{.+}} Ts
509+
// CHECK-NEXT: | | `-TemplateArgument type 'Ts...':'type-parameter-0-1...'
510+
// CHECK-NEXT: | | `-PackExpansionType 0x{{.+}} 'Ts...' dependent
511+
// CHECK-NEXT: | | `-TemplateTypeParmType 0x{{.+}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack
512+
// CHECK-NEXT: | | `-TemplateTypeParm 0x{{.+}} 'Ts'
513+
// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <IntegralCast>
514+
// CHECK-NEXT: | `-IntegerLiteral 0x{{.+}} <{{.+}}> 'int' 0
515+
// CHECK-NEXT: |-CXXDeductionGuideDecl 0x{{.+}} <{{.+}}> line:{{.+}} implicit <deduction guide for A> 'auto (T, Ts...) -> A<T>'
516+
// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <{{.+}}> col:{{.+}} val 'T'
517+
// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <{{.+}}> col:{{.+}} tail 'Ts...' pack
518+
// CHECK-NEXT: | `-BinaryOperator 0x{{.+}} <{{.+}}> 'bool' '&&'
519+
// CHECK-NEXT: | |-ParenExpr 0x{{.+}} <{{.+}}> 'bool'
520+
// CHECK-NEXT: | | `-BinaryOperator 0x{{.+}} <{{.+}}> 'bool' '>'
521+
// CHECK-NEXT: | | |-UnaryExprOrTypeTraitExpr {{.+}} sizeof 'T'
522+
// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <IntegralCast>
523+
// CHECK-NEXT: | | `-IntegerLiteral 0x{{.+}} <{{.+}}> 'int' 1
524+
// CHECK-NEXT: | `-ParenExpr 0x{{.+}} <{{.+}}> '<dependent type>'
525+
// CHECK-NEXT: | `-CallExpr 0x{{.+}} <{{.+}}> '<dependent type>'
526+
// CHECK-NEXT: | `-UnresolvedLookupExpr 0x{{.+}} <col:14, col:24> '<dependent type>' {{.+}}
527+
// CHECK-NEXT: | `-TemplateArgument type 'Ts...':'type-parameter-0-1...'
528+
// CHECK-NEXT: | `-PackExpansionType 0x{{.+}} 'Ts...' dependent
529+
// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack
530+
// CHECK-NEXT: | `-TemplateTypeParm 0x{{.+}} 'Ts'
531+
532+
template <typename T>
533+
struct B {
534+
template <typename... Ts>
535+
B(T val, Ts... tail)
536+
requires(True<tail...>())
537+
{}
538+
};
539+
540+
B b(42, 43);
541+
// expected-error@-1 {{no viable constructor}} \
542+
// expected-note@-6 {{constraints not satisfied}} \
543+
// expected-note@-5 {{because substituted constraint expression is ill-formed}} \
544+
// expected-note@-6 {{implicit deduction guide declared as 'template <typename T, typename ...Ts> B(T val, Ts ...tail) -> B<T> requires (True<tail...>())'}} \
545+
// expected-note@-8 {{function template not viable}} \
546+
// expected-note@-8 {{implicit deduction guide declared as 'template <typename T> B(B<T>) -> B<T>'}}
547+
548+
} // namespace GH60777
549+
550+
// Examples from @hokein.
551+
namespace GH98592 {
552+
553+
template <class T> concept True = true;
554+
double arr3[3];
555+
556+
template <class T>
557+
struct X {
558+
const int size;
559+
template <class U>
560+
constexpr X(T, U(&)[3]) requires True<T> : size(sizeof(T)) {}
561+
};
562+
563+
template <typename T, typename U>
564+
X(T, U (&)[3]) -> X<U>;
565+
566+
constexpr X x(3, arr3);
567+
568+
// The synthesized deduction guide is more constrained than the explicit one.
569+
static_assert(x.size == 4);
570+
571+
// CHECK-LABEL: Dumping GH98592::<deduction guide for X>:
572+
// CHECK-NEXT: FunctionTemplateDecl 0x{{.+}} <{{.+}}> col:13 implicit <deduction guide for X>
573+
// CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:17 referenced class depth 0 index 0 T
574+
// CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:19 class depth 0 index 1 U
575+
// CHECK-NEXT: |-CXXDeductionGuideDecl 0x{{.+}} <{{.+}}> col:13 implicit <deduction guide for X> 'auto (T, U (&)[3]) -> X<T>'
576+
// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <col:15> col:16 'T'
577+
// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'U (&)[3]'
578+
// CHECK-NEXT: | `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True'
579+
// CHECK-NEXT: | |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
580+
// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-0-0'
581+
// CHECK-NEXT: | | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
582+
// CHECK-NEXT: | `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
583+
// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
584+
// CHECK-NEXT: | `-TemplateTypeParm 0x{{.+}} 'T'
585+
// 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
586+
// CHECK-NEXT: |-TemplateArgument type 'int'
587+
// CHECK-NEXT: | `-BuiltinType 0x{{.+}} 'int'
588+
// CHECK-NEXT: |-TemplateArgument type 'double'
589+
// CHECK-NEXT: | `-BuiltinType 0x{{.+}} 'double'
590+
// CHECK-NEXT: |-ParmVarDecl 0x{{.+}} <col:15> col:16 'int'
591+
// CHECK-NEXT: |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'double (&)[3]'
592+
// CHECK-NEXT: `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True'
593+
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
594+
// CHECK-NEXT: | `-TemplateArgument type 'type-parameter-0-0'
595+
// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
596+
// CHECK-NEXT: `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
597+
// CHECK-NEXT: `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
598+
// CHECK-NEXT: `-TemplateTypeParm 0x{{.+}} 'T'
599+
600+
template <class T> requires True<T> struct Y {
601+
const int size;
602+
template <class U>
603+
constexpr Y(T, U(&)[3]) : size(sizeof(T)) {}
604+
};
605+
606+
template <typename T, typename U> Y(T, U (&)[3]) -> Y<U>;
607+
608+
constexpr Y y(3, arr3);
609+
610+
// Likewise, the synthesized deduction guide should be preferred
611+
// according to [over.match.class.deduct]p1.
612+
static_assert(y.size == 4);
613+
614+
// Dumping GH98592::<deduction guide for Y>:
615+
// FunctionTemplateDecl 0x{{.+}} <{{.+}}> col:13 implicit <deduction guide for Y>
616+
// |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:17 referenced class depth 0 index 0 T
617+
// |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:19 class depth 0 index 1 U
618+
// |-CXXDeductionGuideDecl 0x{{.+}} <{{.+}}> col:13 implicit <deduction guide for Y> 'auto (T, U (&)[3]) -> Y<T>'
619+
// | |-ParmVarDecl 0x{{.+}} <col:15> col:16 'T'
620+
// | |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'U (&)[3]'
621+
// | `-ConceptSpecializationExpr 0x{{.+}} <{{.+}}> 'bool' Concept 0x{{.+}} 'True'
622+
// | |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
623+
// | | `-TemplateArgument type 'type-parameter-0-0'
624+
// | | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
625+
// | `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
626+
// | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
627+
// | `-TemplateTypeParm 0x{{.+}} 'T'
628+
// `-CXXDeductionGuideDecl 0x{{.+}} <{{.+}}> col:13 implicit used <deduction guide for Y> 'auto (int, double (&)[3]) -> GH98592::Y<int>' implicit_instantiation
629+
// |-TemplateArgument type 'int'
630+
// | `-BuiltinType 0x{{.+}} 'int'
631+
// |-TemplateArgument type 'double'
632+
// | `-BuiltinType 0x{{.+}} 'double'
633+
// |-ParmVarDecl 0x{{.+}} <col:15> col:16 'int'
634+
// |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'double (&)[3]'
635+
// `-ConceptSpecializationExpr 0x{{.+}} <{{.+}}> 'bool' Concept 0x{{.+}} 'True'
636+
// |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
637+
// | `-TemplateArgument type 'type-parameter-0-0'
638+
// | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
639+
// `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
640+
// `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
641+
// `-TemplateTypeParm 0x{{.+}} 'T'
642+
643+
} // namespce GH98592
644+
482645
namespace GH122134 {
483646

484647
template <class, class>

‎clang/www/cxx_dr_status.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -15611,7 +15611,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1561115611
<td><a href="https://cplusplus.github.io/CWG/issues/2628.html">2628</a></td>
1561215612
<td>DRWP</td>
1561315613
<td>Implicit deduction guides should propagate constraints</td>
15614-
<td class="none" align="center">No</td>
15614+
<td class="unreleased" align="center">Clang 20</td>
1561515615
</tr>
1561615616
<tr id="2629">
1561715617
<td><a href="https://cplusplus.github.io/CWG/issues/2629.html">2629</a></td>

0 commit comments

Comments
 (0)
Please sign in to comment.