https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/111143
Closes https://github.com/llvm/llvm-project/issues/98592 >From 567177e658ede96a59a22af14c9472e232391487 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 4 Oct 2024 20:22:59 +0800 Subject: [PATCH] [Clang] Implement CWG 2628 "Implicit deduction guides should propagate constraints" --- clang/docs/ReleaseNotes.rst | 3 + clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 70 ++++++-- clang/test/CXX/drs/cwg26xx.cpp | 19 +- clang/test/SemaTemplate/deduction-guide.cpp | 163 ++++++++++++++++++ clang/www/cxx_dr_status.html | 2 +- 5 files changed, 227 insertions(+), 30 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6e7a5fb76b602b..a88c00ea038091 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -215,6 +215,9 @@ Resolutions to C++ Defect Reports - Clang now allows trailing requires clause on explicit deduction guides. (`CWG2707: Deduction guides cannot have a trailing requires-clause <https://cplusplus.github.io/CWG/issues/2707.html>`_). +- Respect constructor constraints during CTAD. + (`CWG2628: Implicit deduction guides should propagate constraints <https://cplusplus.github.io/CWG/issues/2628.html>`_). + C Language Changes ------------------ diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 545da21183c3c4..dc9eb1b83b03fe 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -195,12 +195,14 @@ class ExtractTypeForDeductionGuide // A deduction guide can be either a template or a non-template function // declaration. If \p TemplateParams is null, a non-template function // declaration will be created. -NamedDecl *buildDeductionGuide( - Sema &SemaRef, TemplateDecl *OriginalTemplate, - TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor, - ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart, - SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit, - llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}) { +NamedDecl * +buildDeductionGuide(Sema &SemaRef, TemplateDecl *OriginalTemplate, + TemplateParameterList *TemplateParams, + CXXConstructorDecl *Ctor, ExplicitSpecifier ES, + TypeSourceInfo *TInfo, SourceLocation LocStart, + SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit, + llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}, + Expr *FunctionTrailingRC = nullptr) { DeclContext *DC = OriginalTemplate->getDeclContext(); auto DeductionGuideName = SemaRef.Context.DeclarationNames.getCXXDeductionGuideName( @@ -211,9 +213,9 @@ NamedDecl *buildDeductionGuide( TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(); // Build the implicit deduction guide template. - auto *Guide = - CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name, - TInfo->getType(), TInfo, LocEnd, Ctor); + auto *Guide = CXXDeductionGuideDecl::Create( + SemaRef.Context, DC, LocStart, ES, Name, TInfo->getType(), TInfo, LocEnd, + Ctor, DeductionCandidate::Normal, FunctionTrailingRC); Guide->setImplicit(IsImplicit); Guide->setParams(Params); @@ -355,10 +357,11 @@ struct ConvertConstructorToDeductionGuideTransform { // template arguments) of the constructor, if any. TemplateParameterList *TemplateParams = SemaRef.GetTemplateParameterList(Template); + SmallVector<TemplateArgument, 16> Depth1Args; + Expr *OuterRC = TemplateParams->getRequiresClause(); if (FTD) { TemplateParameterList *InnerParams = FTD->getTemplateParameters(); SmallVector<NamedDecl *, 16> AllParams; - SmallVector<TemplateArgument, 16> Depth1Args; AllParams.reserve(TemplateParams->size() + InnerParams->size()); AllParams.insert(AllParams.begin(), TemplateParams->begin(), TemplateParams->end()); @@ -391,7 +394,7 @@ struct ConvertConstructorToDeductionGuideTransform { /*EvaluateConstraint=*/false); } - assert(NewParam->getTemplateDepth() == 0 && + assert(getDepthAndIndex(NewParam).first == 0 && "Unexpected template parameter depth"); AllParams.push_back(NewParam); @@ -407,10 +410,11 @@ struct ConvertConstructorToDeductionGuideTransform { Args.addOuterRetainedLevel(); if (NestedPattern) Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth()); - ExprResult E = SemaRef.SubstExpr(InnerRC, Args); - if (E.isInvalid()) + ExprResult E = + SemaRef.SubstConstraintExprWithoutSatisfaction(InnerRC, Args); + if (!E.isUsable()) return nullptr; - RequiresClause = E.getAs<Expr>(); + RequiresClause = E.get(); } TemplateParams = TemplateParameterList::Create( @@ -446,10 +450,46 @@ struct ConvertConstructorToDeductionGuideTransform { return nullptr; TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType); + // At this point, the function parameters are already 'instantiated' in the + // current scope. Substitute into the constructor's trailing + // requires-clause, if any. + Expr *FunctionTrailingRC = nullptr; + if (Expr *RC = CD->getTrailingRequiresClause()) { + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(Depth1Args); + Args.addOuterRetainedLevel(); + if (NestedPattern) + Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth()); + ExprResult E = SemaRef.SubstConstraintExprWithoutSatisfaction(RC, Args); + if (!E.isUsable()) + return nullptr; + FunctionTrailingRC = E.get(); + } + + // C++ [over.match.class.deduct]p1: + // If C is defined, for each constructor of C, a function template with + // the following properties: + // [...] + // - The associated constraints are the conjunction of the associated + // constraints of C and the associated constraints of the constructor, if + // any. + if (OuterRC) { + // The outer template parameters are not transformed, so their + // associated constraints don't need substitution. + if (!FunctionTrailingRC) + FunctionTrailingRC = OuterRC; + else + FunctionTrailingRC = BinaryOperator::Create( + SemaRef.Context, /*lhs=*/OuterRC, /*rhs=*/FunctionTrailingRC, + BO_LAnd, SemaRef.Context.BoolTy, VK_PRValue, OK_Ordinary, + TemplateParams->getTemplateLoc(), FPOptionsOverride()); + } + return buildDeductionGuide( SemaRef, Template, TemplateParams, CD, CD->getExplicitSpecifier(), NewTInfo, CD->getBeginLoc(), CD->getLocation(), CD->getEndLoc(), - /*IsImplicit=*/true, MaterializedTypedefs); + /*IsImplicit=*/true, MaterializedTypedefs, FunctionTrailingRC); } /// Build a deduction guide with the specified parameter types. diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp index 63a954c803b77a..0d297b33e88bd9 100644 --- a/clang/test/CXX/drs/cwg26xx.cpp +++ b/clang/test/CXX/drs/cwg26xx.cpp @@ -132,27 +132,18 @@ struct E { #endif } // namespace cwg2627 -namespace cwg2628 { // cwg2628: no - // this was reverted for the 16.x release - // due to regressions, see the issue for more details: - // https://github.com/llvm/llvm-project/issues/60777 +namespace cwg2628 { // cwg2628: 20 #if __cplusplus >= 202002L template <bool A = false, bool B = false> struct foo { - // The expected notes below should be removed when cwg2628 is fully implemented again - constexpr foo() requires (!A && !B) = delete; // #cwg2628-ctor-1 - constexpr foo() requires (A || B) = delete; // #cwg2628-ctor-2 + constexpr foo() requires (!A && !B) = delete; // #cwg2628-ctor + constexpr foo() requires (A || B) = delete; }; void f() { - // The FIXME's below should be the expected errors when cwg2628 is - // fully implemented again. foo fooable; // #cwg2628-fooable - // since-cxx20-error@-1 {{ambiguous deduction for template arguments of 'foo'}} - // since-cxx20-note@#cwg2628-ctor-1 {{candidate function [with A = false, B = false]}} - // since-cxx20-note@#cwg2628-ctor-2 {{candidate function [with A = false, B = false]}} - // FIXME-since-cxx20-error@#cwg2628-fooable {{call to deleted}} - // FIXME-since-cxx20-note@#cwg2628-ctor {{marked deleted here}} + // since-cxx20-error@#cwg2628-fooable {{call to deleted}} + // since-cxx20-note@#cwg2628-ctor {{marked deleted here}} } #endif } diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp index d03c783313dd71..1e7b4d80be53f1 100644 --- a/clang/test/SemaTemplate/deduction-guide.cpp +++ b/clang/test/SemaTemplate/deduction-guide.cpp @@ -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@-1 {{no viable constructor}} \ +// expected-note@-6 {{constraints not satisfied}} \ +// expected-note@-5 {{because substituted constraint expression is ill-formed}} \ +// expected-note@-6 {{implicit deduction guide declared as 'template <typename T, typename ...Ts> B(T val, Ts ...tail) -> B<T> requires (True<tail...>())'}} \ +// expected-note@-8 {{function template not viable}} \ +// expected-note@-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); + +// Dumping GH98592::<deduction guide for Y>: +// FunctionTemplateDecl 0x{{.+}} <{{.+}}> col:13 implicit <deduction guide for Y> +// |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:17 referenced class depth 0 index 0 T +// |-TemplateTypeParmDecl 0x{{.+}} <{{.+}}> col:19 class depth 0 index 1 U +// |-CXXDeductionGuideDecl 0x{{.+}} <{{.+}}> col:13 implicit <deduction guide for Y> 'auto (T, U (&)[3]) -> Y<T>' +// | |-ParmVarDecl 0x{{.+}} <col:15> col:16 'T' +// | |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'U (&)[3]' +// | `-ConceptSpecializationExpr 0x{{.+}} <{{.+}}> 'bool' Concept 0x{{.+}} 'True' +// | |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28 +// | | `-TemplateArgument type 'type-parameter-0-0' +// | | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0 +// | `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0' +// | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0 +// | `-TemplateTypeParm 0x{{.+}} 'T' +// `-CXXDeductionGuideDecl 0x{{.+}} <{{.+}}> col:13 implicit used <deduction guide for Y> 'auto (int, double (&)[3]) -> GH98592::Y<int>' implicit_instantiation +// |-TemplateArgument type 'int' +// | `-BuiltinType 0x{{.+}} 'int' +// |-TemplateArgument type 'double' +// | `-BuiltinType 0x{{.+}} 'double' +// |-ParmVarDecl 0x{{.+}} <col:15> col:16 'int' +// |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'double (&)[3]' +// `-ConceptSpecializationExpr 0x{{.+}} <{{.+}}> 'bool' Concept 0x{{.+}} 'True' +// |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28 +// | `-TemplateArgument type 'type-parameter-0-0' +// | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0 +// `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0' +// `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0 +// `-TemplateTypeParm 0x{{.+}} 'T' + +} // namespce GH98592 diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index ba63106ccc3875..185fb6a94f55a6 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -15615,7 +15615,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2628.html">2628</a></td> <td>DRWP</td> <td>Implicit deduction guides should propagate constraints</td> - <td class="none" align="center">No</td> + <td class="unreleased" align="center">Clang 20</td> </tr> <tr id="2629"> <td><a href="https://cplusplus.github.io/CWG/issues/2629.html">2629</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits