https://github.com/bassiounix updated https://github.com/llvm/llvm-project/pull/193056
>From 39b87cfe469336770e1d94e6bea5f50f6fae75d4 Mon Sep 17 00:00:00 2001 From: bassiounix <[email protected]> Date: Mon, 20 Apr 2026 20:50:53 +0200 Subject: [PATCH 1/7] [Clang][C++20] Add full support to deduction guides --- clang/lib/Frontend/InitPreprocessor.cpp | 3 +-- clang/test/Lexer/cxx-features.cpp | 3 +-- clang/www/cxx_status.html | 8 +------- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 1ccd74314f373..0a5ab5c21cce6 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -686,7 +686,6 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_noexcept_function_type", "201510L"); Builder.defineMacro("__cpp_capture_star_this", "201603L"); Builder.defineMacro("__cpp_if_constexpr", "201606L"); - Builder.defineMacro("__cpp_deduction_guides", "201703L"); // (not latest) Builder.defineMacro("__cpp_template_auto", "201606L"); // (old name) Builder.defineMacro("__cpp_namespace_attributes", "201411L"); Builder.defineMacro("__cpp_enumerator_attributes", "201411L"); @@ -708,7 +707,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, // C++20 features. if (LangOpts.CPlusPlus20) { Builder.defineMacro("__cpp_aggregate_paren_init", "201902L"); - + Builder.defineMacro("__cpp_deduction_guides", "201907L"); Builder.defineMacro("__cpp_concepts", "202002"); Builder.defineMacro("__cpp_conditional_explicit", "201806L"); Builder.defineMacro("__cpp_consteval", "202211L"); diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index 8eb9ea032879c..f8440ba6c641e 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -193,8 +193,7 @@ #error "wrong value for __cpp_if_constexpr" #endif -#if check(deduction_guides, 0, 0, 0, 201703, 201703, 201703, 201703) -// FIXME: 201907 in C++20 +#if check(deduction_guides, 0, 0, 0, 201907, 201907, 201907, 201907) #error "wrong value for __cpp_deduction_guides" #endif diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 2c834b07f9a8f..eac8689da39fb 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1030,13 +1030,7 @@ <h2 id="cxx20">C++20 implementation status</h2> <tr> <td>Class template argument deduction for alias templates</td> <td><a href="https://wg21.link/p1814r0">P1814R0</a></td> - <td class="partial" align="center"> - <details> - <summary>Clang 19 (Partial)</summary> - This feature has been initially completed, but the feature macro - __cpp_deduction_guides has not been updated. - </details> - </td> + <td class="full" align="center">Clang 22</td> </tr> <tr> <td>Permit conversions to arrays of unknown bound</td> >From d86ad85b0e6a6b9c167fd04b00fe5f291ff30958 Mon Sep 17 00:00:00 2001 From: bassiounix <[email protected]> Date: Mon, 20 Apr 2026 22:35:16 +0200 Subject: [PATCH 2/7] fix build --- clang/test/Lexer/cxx-features.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index f8440ba6c641e..c99b0eca3af22 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -94,6 +94,10 @@ #error "wrong value for __cpp_aggregate_paren_init" #endif +#if check(deduction_guides, 0, 0, 0, 0, 201907, 201907, 201907) +#error "wrong value for __cpp_deduction_guides" +#endif + #if defined(CHAR8_T) ? check(char8_t, 202207, 202207, 202207, 202207, 202207, 202207, 202207) : \ defined(NO_CHAR8_T) ? check(char8_t, 0, 0, 0, 0, 0, 0, 0) : \ check(char8_t, 0, 0, 0, 0, 202207, 202207, 202207) @@ -193,10 +197,6 @@ #error "wrong value for __cpp_if_constexpr" #endif -#if check(deduction_guides, 0, 0, 0, 201907, 201907, 201907, 201907) -#error "wrong value for __cpp_deduction_guides" -#endif - #if check(nontype_template_parameter_auto, 0, 0, 0, 201606, 201606, 201606, 201606) #error "wrong value for __cpp_nontype_template_parameter_auto" #endif >From 971ce4bf50a10750dd894cef32c489b037d2e8d6 Mon Sep 17 00:00:00 2001 From: bassiounix <[email protected]> Date: Tue, 21 Apr 2026 15:42:00 +0200 Subject: [PATCH 3/7] enable ctad for both c++17 and c++20 --- clang/lib/Frontend/InitPreprocessor.cpp | 1 + clang/test/Lexer/cxx-features.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 0a5ab5c21cce6..0bab87c8a8d1b 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -686,6 +686,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_noexcept_function_type", "201510L"); Builder.defineMacro("__cpp_capture_star_this", "201603L"); Builder.defineMacro("__cpp_if_constexpr", "201606L"); + Builder.defineMacro("__cpp_deduction_guides", "201703L"); Builder.defineMacro("__cpp_template_auto", "201606L"); // (old name) Builder.defineMacro("__cpp_namespace_attributes", "201411L"); Builder.defineMacro("__cpp_enumerator_attributes", "201411L"); diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index c99b0eca3af22..494b86d024b30 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -94,7 +94,7 @@ #error "wrong value for __cpp_aggregate_paren_init" #endif -#if check(deduction_guides, 0, 0, 0, 0, 201907, 201907, 201907) +#if check(deduction_guides, 0, 0, 0, 201703, 201907, 201907, 201907) #error "wrong value for __cpp_deduction_guides" #endif >From df7eb6223031877af5628b9bcc0cd54ba9629b83 Mon Sep 17 00:00:00 2001 From: bassiounix <[email protected]> Date: Tue, 21 Apr 2026 15:42:17 +0200 Subject: [PATCH 4/7] increase clang support version --- clang/www/cxx_status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index eac8689da39fb..0581370ea751b 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1030,7 +1030,7 @@ <h2 id="cxx20">C++20 implementation status</h2> <tr> <td>Class template argument deduction for alias templates</td> <td><a href="https://wg21.link/p1814r0">P1814R0</a></td> - <td class="full" align="center">Clang 22</td> + <td class="full" align="center">Clang 23</td> </tr> <tr> <td>Permit conversions to arrays of unknown bound</td> >From e9bfd0893afd468860cee6ae5fddde9bfdff526e Mon Sep 17 00:00:00 2001 From: bassiounix <[email protected]> Date: Wed, 2 Sep 2026 22:22:34 +0300 Subject: [PATCH 5/7] Fix GH125821 --- clang/docs/ReleaseNotes.md | 7 ++++ clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 35 ++++++++++++++++--- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 4 +-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index cd7f40aec112d..79386c1c8924f 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -470,6 +470,13 @@ features cannot lower the translation-unit ABI level; - Fixed an issue where `__typeof__` incorrectly rejected cv-qualified function types. +- Class template argument deduction through an alias template now works when + the right-hand side of the alias names another alias template that cannot + have deduction guides of its own, such as an identity alias + (`template <class T> using Identity = T;`). The deduction guides are now + derived from the first template in the chain that can have them, per the + equivalence rule of `[temp.alias]p2`, matching GCC. (#GH125821) + - Fixed a bug where top-level CV qualifiers (such as ``const``) were dropped from pointers modified by Microsoft pointer attributes (like ``__ptr32`` and ``__ptr64``) and WebAssembly's ``__funcref``. - Fixed an issue where we tried to compare invalid NTTPs for variable declarations, which ended up in hitting an assertion with a constrained non-plain-auto NTTP, which we don't quite implement yet. (#GH208658) diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 63d6759bc5490..e133ae0b1e9b0 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -1177,13 +1177,40 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) { auto RhsType = AliasTemplate->getTemplatedDecl()->getUnderlyingType(); TemplateDecl *Template = nullptr; llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs; - if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) { + const auto *TST = RhsType->getAs<TemplateSpecializationType>(); + + // The RHS of the alias may name another alias template that can never have + // deduction guides of its own, because its defining-type-id is not of the + // form + // [typename] [nested-name-specifier] [template] simple-template-id + // as required by [over.match.class.deduct]p3. e.g. + // template <typename T> + // using Identity = T; + // template <typename T> + // using C = Identity<Foo<T>>; + // Per [temp.alias]p2, Identity<Foo<T>> is equivalent to Foo<T>, so step + // through such aliases and derive the deduction guides from the first + // template that can actually have them (GH125821). + while (TST) { + auto *RhsAlias = dyn_cast_or_null<TypeAliasTemplateDecl>( + TST->getTemplateName().getAsTemplateDecl()); + if (!RhsAlias || getRHSTemplateDeclAndArgs(SemaRef, RhsAlias).first) + break; + RhsType = TST->desugar(); + TST = RhsType->getAs<TemplateSpecializationType>(); + } + + if (TST) { // Cases where the RHS of the alias is dependent. e.g. // template<typename T> // using AliasFoo1 = Foo<T>; // a class/type alias template specialization - Template = TST->getTemplateName().getAsTemplateDecl(); - AliasRhsTemplateArgs = - TST->getAsNonAliasTemplateSpecializationType()->template_arguments(); + // The RHS may not desugar to a template specialization at all (e.g. an + // alias of the form 'T*' whose specialization ends up being a pointer); + // in that case, there is no template to derive the guides from. + if (const auto *RhsTST = TST->getAsNonAliasTemplateSpecializationType()) { + Template = TST->getTemplateName().getAsTemplateDecl(); + AliasRhsTemplateArgs = RhsTST->template_arguments(); + } } else if (const auto *RT = RhsType->getAs<RecordType>()) { // Cases where template arguments in the RHS of the alias are not // dependent. e.g. diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 78911cbaed67b..e0f59be663b2f 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -553,7 +553,6 @@ void foo() { test<{1, 2, 3}>(); } } // namespace GH113518 -// FIXME: This is accepted by GCC: https://gcc.godbolt.org/z/f3rMfbacz namespace GH125821 { template<typename T> struct A { A(T){} }; @@ -564,7 +563,8 @@ using Proxy = T; template<typename T> using C = Proxy< A<T> >; -C test{ 42 }; // expected-error {{no viable constructor or deduction guide for deduction of template arguments}} +C test{ 42 }; +static_assert(__is_same(decltype(test), A<int>)); } // namespace GH125821 >From 83b7daebf896606b9bb518591e94a97160b0626a Mon Sep 17 00:00:00 2001 From: bassiounix <[email protected]> Date: Wed, 2 Sep 2026 22:25:02 +0300 Subject: [PATCH 6/7] pump up clang version --- clang/www/cxx_status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 6c045937add3e..9617a8f833fe1 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1154,7 +1154,7 @@ <h2 id="cxx20">C++20 implementation status</h2> <tr> <td>Class template argument deduction for alias templates</td> <td><a href="https://wg21.link/p1814r0">P1814R0</a></td> - <td class="full" align="center">Clang 23</td> + <td class="unreleased" align="center">Clang 24</td> </tr> <tr> <td>Permit conversions to arrays of unknown bound</td> >From b306f8160976b89f0bd2d8eaf0f42fa6d0b5bd88 Mon Sep 17 00:00:00 2001 From: bassiounix <[email protected]> Date: Thu, 3 Sep 2026 06:03:55 +0300 Subject: [PATCH 7/7] Allow default arguments deduction from the return type of the underlying deduction guide. This change ensures that template parameters that cannot be deduced from constructor arguments receive appropriate default values. --- clang/docs/ReleaseNotes.md | 15 +- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 454 ++++++++++++++++-- clang/test/AST/ast-dump-ctad-alias.cpp | 30 ++ clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 102 ++++ 4 files changed, 560 insertions(+), 41 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 79386c1c8924f..21578b5eb87e1 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -73,7 +73,7 @@ features cannot lower the translation-unit ABI level; - On SPARC, a `_Complex` value with an integer element type is now passed and returned packed into the one or two integer registers it fits in, matching GCC. Clang previously passed such a value indirectly and returned it with one part - per register. + per register. `-fclang-abi-compat=23` restores the previous behavior. (#GH212340) - On SPARC64, a `_Complex char` or `_Complex short` is now @@ -119,7 +119,7 @@ features cannot lower the translation-unit ABI level; - `CompletionString.availability` now returns instances of `AvailabilityKind`. As a result, the `__str__` representation of its return values changed. - Like other libclang enums, it now follows the `CompletionChunkKind.VARIANT_NAME` scheme instead of `VariantName`. + Like other libclang enums, it now follows the `CompletionChunkKind.VARIANT_NAME` scheme instead of `VariantName`. ### OpenCL Potentially Breaking Changes @@ -477,6 +477,13 @@ features cannot lower the translation-unit ABI level; derived from the first template in the chain that can have them, per the equivalence rule of `[temp.alias]p2`, matching GCC. (#GH125821) +- Class template argument deduction through an alias template now works when + a template parameter of the alias only appears in the synthesized deduction + guide through default template arguments. Such a template parameter now gets a default + template argument deduced from the return type of the underlying deduction + guide (here `Key` becomes the iterator's value type), instead of being + undeducible. + - Fixed a bug where top-level CV qualifiers (such as ``const``) were dropped from pointers modified by Microsoft pointer attributes (like ``__ptr32`` and ``__ptr64``) and WebAssembly's ``__funcref``. - Fixed an issue where we tried to compare invalid NTTPs for variable declarations, which ended up in hitting an assertion with a constrained non-plain-auto NTTP, which we don't quite implement yet. (#GH208658) @@ -491,9 +498,9 @@ features cannot lower the translation-unit ABI level; producing a spurious "no matching function" error with no candidate notes. (#GH210822) -- Fixed a crash when module directive export module foo not following a +- Fixed a crash when module directive export module foo not following a semicolon and there are no rest pp-tokens in current module file. (#GH187771) - + - Fixed a crash when a lambda parameter pack was given a default argument that is a pack expansion referencing an enclosing function's parameter pack (e.g. `[](Types... = args...) {}`). Clang now diagnoses the illegal default diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index e133ae0b1e9b0..05b99c9374148 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -956,19 +956,61 @@ llvm::DenseSet<const NamedDecl *> getSourceDeductionGuides(DeclarationName Name, return Result; } +// Marker for a template parameter that doesn't appear in the synthesized +// deduction guide f' of an alias template. +constexpr unsigned InvalidFPrimeIndex = -1; + +// A template parameter of the synthesized deduction guide f' of an alias +// template A, before it is created. +struct FPrimeTemplateParamRef { + // Whether this is a template parameter of A, as opposed to a non-deduced + // template parameter of the underlying deduction guide f. + bool IsAliasParam; + // The index of the template parameter in the template parameter list of A or + // f, respectively. + unsigned Index; + // For a non-deduced template parameter of f: the deduced template parameters + // of f that it refers to. + llvm::SmallBitVector DeducedFParamsUsed; +}; + +static bool hasDefaultArgument(const NamedDecl *Param) { + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) + return TTP->hasDefaultArgument(); + if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) + return NTTP->hasDefaultArgument(); + return cast<TemplateTemplateParmDecl>(Param)->hasDefaultArgument(); +} + +static void setDefaultArgument(ASTContext &Context, NamedDecl *Param, + const TemplateArgumentLoc &DefArg) { + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) + TTP->setDefaultArgument(Context, DefArg); + else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) + NTTP->setDefaultArgument(Context, DefArg); + else + cast<TemplateTemplateParmDecl>(Param)->setDefaultArgument(Context, DefArg); +} + // Build the associated constraints for the alias deduction guides. // C++ [over.match.class.deduct]p3.3: // The associated constraints ([temp.constr.decl]) are the conjunction of the // associated constraints of g and a constraint that is satisfied if and only // if the arguments of A are deducible (see below) from the return type. // +// AliasParamFPrimeIndex and FParamFPrimeIndex give the index in f' of the +// template parameters of the alias template and of the non-deduced template +// parameters of F, respectively (InvalidFPrimeIndex for those not in f'). +// // The return result is expected to be the require-clause for the synthesized // alias deduction guide. Expr * buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, TypeAliasTemplateDecl *AliasTemplate, ArrayRef<DeducedTemplateArgument> DeduceResults, - unsigned FirstUndeducedParamIdx, Expr *IsDeducible) { + ArrayRef<unsigned> AliasParamFPrimeIndex, + ArrayRef<unsigned> FParamFPrimeIndex, + Expr *IsDeducible) { Expr *RC = F->getTemplateParameters()->getRequiresClause(); if (!RC) return IsDeducible; @@ -1002,15 +1044,19 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, // build template arguments refer to them. SmallVector<TemplateArgument> AdjustedAliasTemplateArgs; - for (auto *TP : *AliasTemplate->getTemplateParameters()) { + for (auto [Index, TP] : + llvm::enumerate(*AliasTemplate->getTemplateParameters())) { // Rebuild any internal references to earlier parameters and reindex - // as we go. + // as we go. Template parameters of the alias that don't appear in f' are + // not referred to by the deduced template arguments; keep their index. MultiLevelTemplateArgumentList Args; Args.setKind(TemplateSubstitutionKind::Rewrite); Args.addOuterTemplateArguments(AdjustedAliasTemplateArgs); + unsigned NewIndex = AliasParamFPrimeIndex[Index] != InvalidFPrimeIndex + ? AliasParamFPrimeIndex[Index] + : Index; NamedDecl *NewParam = transformTemplateParameter( - SemaRef, AliasTemplate->getDeclContext(), TP, Args, - /*NewIndex=*/AdjustedAliasTemplateArgs.size(), + SemaRef, AliasTemplate->getDeclContext(), TP, Args, NewIndex, getDepthAndIndex(TP).first + AdjustDepth); TemplateArgument NewTemplateArgument = @@ -1036,9 +1082,8 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, // Rebuild the template parameter with updated depth and index. NamedDecl *NewParam = transformTemplateParameter(SemaRef, F->getDeclContext(), TP, Args, - /*NewIndex=*/FirstUndeducedParamIdx, + /*NewIndex=*/FParamFPrimeIndex[Index], getDepthAndIndex(TP).first + AdjustDepth); - FirstUndeducedParamIdx += 1; assert(TemplateArgsForBuildingRC[Index].isNull()); TemplateArgsForBuildingRC[Index] = Context.getInjectedTemplateArg(NewParam); @@ -1237,6 +1282,255 @@ bool IsNonDeducedArgument(const TemplateArgument &TA) { llvm::any_of(TA.pack_elements(), IsNonDeducedArgument)); } +// Synthesize default template arguments for the template parameters of the +// alias template A that appear in the deduction guide f' without having a +// default template argument. +// +// Such a template parameter cannot be deduced from the function parameters of +// f' if it only appears in f' through the default template arguments of other +// template parameters of A, e.g. +// +// template <class Key, class Hash = std::hash<Key>> +// using MySet = std::unordered_set<Key, Hash>; +// +// with the deduction guide +// +// template <class It, class H = std::hash<iter_value_t<It>>> +// unordered_set(It, It, H = H()) -> unordered_set<iter_value_t<It>, H>; +// +// Deducing the return type of the guide from the defining-type-id of MySet +// gives H = Hash, so Hash and, through its default template argument, Key are +// template parameters of f': +// +// template <class Key, class Hash = std::hash<Key>, class It> +// MySet(It, It, Hash) -> unordered_set<iter_value_t<It>, Hash>; +// +// and `MySet(first, last)` fails, as Key cannot be deduced. However, Key +// corresponds to `iter_value_t<It>` in the return type of the guide: deducing +// the template arguments of A from the return type of f gives +// Key = iter_value_t<It>, which we use as the default template argument of Key +// in f' (see orderFPrimeTemplateParameters for the resulting order): +// +// template <class It, class Key = iter_value_t<It>, +// class Hash = std::hash<Key>> +// MySet(It, It, Hash) -> unordered_set<iter_value_t<It>, Hash>; +// +// The result has an entry for each template parameter of A, which is null for +// those that don't get a synthesized default template argument. The synthesized +// default template arguments refer to the (non-deduced) template parameters of +// f. +static SmallVector<TemplateArgument> synthesizeDefaultArgumentsForFPrime( + Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, + FunctionTemplateDecl *F, ArrayRef<TemplateArgument> AliasRhsTemplateArgs, + ArrayRef<TemplateArgument> FReturnTemplateArgs, + ArrayRef<DeducedTemplateArgument> DeduceResults, + ArrayRef<unsigned> AliasParamsInFPrime, SourceLocation Loc) { + TemplateParameterList *AliasParams = AliasTemplate->getTemplateParameters(); + TemplateParameterList *FParams = F->getTemplateParameters(); + SmallVector<TemplateArgument> Result(AliasParams->size()); + + auto NeedsDefaultArgument = [&](unsigned Index) { + NamedDecl *Param = AliasParams->getParam(Index); + return !Param->isTemplateParameterPack() && !hasDefaultArgument(Param); + }; + + if (llvm::none_of(AliasParamsInFPrime, NeedsDefaultArgument)) + return Result; + + // Deduce the template arguments of A from the return type of f, the reverse + // of the deduction of the template arguments of f from the defining-type-id + // of A. + sema::TemplateDeductionInfo Info(Loc, AliasParams->getDepth()); + SmallVector<DeducedTemplateArgument> Deduced(AliasParams->size()); + SemaRef.DeduceTemplateArguments(AliasParams, AliasRhsTemplateArgs, + FReturnTemplateArgs, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false); + + for (unsigned Index : AliasParamsInFPrime) { + if (!NeedsDefaultArgument(Index)) + continue; + const TemplateArgument &D = Deduced[Index]; + + if (D.isNull() || D.isPackExpansion()) + continue; + + NamedDecl *Param = AliasParams->getParam(Index); + + bool KindMatches = [&] { + switch (D.getKind()) { + case TemplateArgument::Type: + return isa<TemplateTypeParmDecl>(Param); + case TemplateArgument::Template: + return isa<TemplateTemplateParmDecl>(Param); + case TemplateArgument::Expression: + return isa<NonTypeTemplateParmDecl>(Param); + default: + return false; + } + }(); + + if (!KindMatches) + continue; + + // The deduced argument may only refer to the non-deduced template + // parameters of f. The deduced ones are replaced in f' by the template + // parameters of A they were deduced to, whose default template arguments + // may in turn refer to this template parameter. + llvm::SmallBitVector UsedFParams(FParams->size()); + SemaRef.MarkUsedTemplateParameters(D, /*OnlyDeduced=*/false, + FParams->getDepth(), UsedFParams); + if (llvm::any_of(UsedFParams.set_bits(), [&](unsigned FIndex) { + return !IsNonDeducedArgument(DeduceResults[FIndex]); + })) + continue; + Result[Index] = D; + } + return Result; +} + +// Determine the order of the template parameters of the deduction guide f' of +// the alias template A when some of them got a synthesized default template +// argument (see synthesizeDefaultArgumentsForFPrime). +// +// A default template argument can only refer to preceding template parameters. +// The order given by the standard (the template parameters of A, followed by +// the non-deduced template parameters of f) doesn't satisfy that for the +// synthesized default template arguments, which refer to non-deduced template +// parameters of f. Instead, order the template parameters of f' such that each +// of them follows the ones its default template argument, its type (for a +// non-type template parameter) and its type-constraint refer to, staying as +// close to the standard's order as possible. +// +// Returns false if there is no such order. +static bool orderFPrimeTemplateParameters( + Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, + FunctionTemplateDecl *F, ArrayRef<DeducedTemplateArgument> DeduceResults, + ArrayRef<unsigned> AliasParamsInFPrime, + ArrayRef<unsigned> NonDeducedFParams, + ArrayRef<TemplateArgument> SynthesizedDefaultArgs, + SmallVectorImpl<FPrimeTemplateParamRef> &Order) { + TemplateParameterList *AliasParams = AliasTemplate->getTemplateParameters(); + TemplateParameterList *FParams = F->getTemplateParameters(); + + // The template parameters of f' in the standard's order, and the position + // of each template parameter of A / non-deduced template parameter of f in + // that list. + SmallVector<FPrimeTemplateParamRef> Params; + SmallVector<unsigned> AliasParamPos(AliasParams->size(), InvalidFPrimeIndex); + SmallVector<unsigned> FParamPos(FParams->size(), InvalidFPrimeIndex); + + for (unsigned Index : AliasParamsInFPrime) { + AliasParamPos[Index] = Params.size(); + Params.push_back( + {/*IsAliasParam=*/true, Index, llvm::SmallBitVector(FParams->size())}); + } + + for (unsigned Index : NonDeducedFParams) { + FParamPos[Index] = Params.size(); + Params.push_back( + {/*IsAliasParam=*/false, Index, llvm::SmallBitVector(FParams->size())}); + } + + unsigned NumParams = Params.size(); + + // Mark the template parameters (of the list containing Param, at the given + // depth) that the default template argument, the type and the + // type-constraint of Param refer to. + auto MarkReferencedTemplateParams = [&](NamedDecl *Param, unsigned Depth, + llvm::SmallBitVector &Used) { + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) { + if (TTP->hasDefaultArgument()) + SemaRef.MarkUsedTemplateParameters( + TTP->getDefaultArgument().getArgument(), /*OnlyDeduced=*/false, + Depth, Used); + if (const TypeConstraint *TC = TTP->getTypeConstraint()) + if (const Expr *E = TC->getImmediatelyDeclaredConstraint()) + SemaRef.MarkUsedTemplateParameters(E, /*OnlyDeduced=*/false, Depth, + Used); + } else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) { + if (NTTP->hasDefaultArgument()) + SemaRef.MarkUsedTemplateParameters( + NTTP->getDefaultArgument().getArgument(), /*OnlyDeduced=*/false, + Depth, Used); + SemaRef.MarkUsedTemplateParameters(TemplateArgument(NTTP->getType()), + /*OnlyDeduced=*/false, Depth, Used); + } else { + auto *TTP = cast<TemplateTemplateParmDecl>(Param); + if (TTP->hasDefaultArgument()) + SemaRef.MarkUsedTemplateParameters( + TTP->getDefaultArgument().getArgument(), /*OnlyDeduced=*/false, + Depth, Used); + } + }; + + // Deps[I] holds the positions of the template parameters that must precede + // the I-th template parameter. + SmallVector<llvm::SmallBitVector> Deps(NumParams, + llvm::SmallBitVector(NumParams)); + for (auto [Pos, P] : llvm::enumerate(Params)) { + if (P.IsAliasParam) { + llvm::SmallBitVector UsedAliasParams(AliasParams->size()); + MarkReferencedTemplateParams(AliasParams->getParam(P.Index), + AliasParams->getDepth(), UsedAliasParams); + for (unsigned Index : UsedAliasParams.set_bits()) + if (Index != P.Index && AliasParamPos[Index] != InvalidFPrimeIndex) + Deps[Pos].set(AliasParamPos[Index]); + // The synthesized default template argument refers to non-deduced + // template parameters of f. + if (!SynthesizedDefaultArgs[P.Index].isNull()) { + llvm::SmallBitVector UsedFParams(FParams->size()); + SemaRef.MarkUsedTemplateParameters(SynthesizedDefaultArgs[P.Index], + /*OnlyDeduced=*/false, + FParams->getDepth(), UsedFParams); + for (unsigned Index : UsedFParams.set_bits()) { + assert(FParamPos[Index] != InvalidFPrimeIndex && + "synthesized default argument refers to a deduced parameter"); + Deps[Pos].set(FParamPos[Index]); + } + } + continue; + } + + llvm::SmallBitVector UsedFParams(FParams->size()); + MarkReferencedTemplateParams(FParams->getParam(P.Index), + FParams->getDepth(), UsedFParams); + for (unsigned Index : UsedFParams.set_bits()) { + if (Index == P.Index) + continue; + if (FParamPos[Index] != InvalidFPrimeIndex) { + Deps[Pos].set(FParamPos[Index]); + continue; + } + // A deduced template parameter of f, which is replaced in f' by the + // template parameters of A that its deduced argument refers to. + P.DeducedFParamsUsed.set(Index); + const TemplateArgument &DeducedArg = DeduceResults[Index]; + llvm::SmallBitVector UsedAliasParams(AliasParams->size()); + SemaRef.MarkUsedTemplateParameters(DeducedArg, /*OnlyDeduced=*/false, + AliasParams->getDepth(), + UsedAliasParams); + for (unsigned AliasIndex : UsedAliasParams.set_bits()) + if (AliasParamPos[AliasIndex] != InvalidFPrimeIndex) + Deps[Pos].set(AliasParamPos[AliasIndex]); + } + } + + // Repeatedly pick the first template parameter all of whose dependencies + // have been placed. + llvm::SmallBitVector Placed(NumParams); + while (Order.size() < NumParams) { + unsigned Next = NumParams; + for (unsigned Pos = 0; Pos != NumParams && Next == NumParams; ++Pos) + if (!Placed[Pos] && !Deps[Pos].test(Placed)) + Next = Pos; + if (Next == NumParams) // The dependencies are circular. + return false; + Placed.set(Next); + Order.push_back(std::move(Params[Next])); + } + return true; +} + // Build deduction guides for a type alias template from the given underlying // source deduction guide. CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( @@ -1349,26 +1643,59 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( SmallVector<TemplateArgument> TemplateArgsForBuildingFPrime( F->getTemplateParameters()->size()); + TemplateParameterList *AliasParams = AliasTemplate->getTemplateParameters(); + TemplateParameterList *FParams = F->getTemplateParameters(); + // Create a template parameter list for the synthesized deduction guide f'. // // C++ [over.match.class.deduct]p3.2: // If f is a function template, f' is a function template whose template // parameter list consists of all the template parameters of A // (including their default template arguments) that appear in the above - // deductions or (recursively) in their default template arguments + // deductions or (recursively) in their default template arguments, + // followed by the template parameters of f that were not deduced + // (including their default template arguments) SmallVector<NamedDecl *> FPrimeTemplateParams; // Store template arguments that refer to the newly-created template // parameters, used for building `TemplateArgsForBuildingFPrime`. SmallVector<TemplateArgument, 16> TransformedDeducedAliasArgs( - AliasTemplate->getTemplateParameters()->size()); + AliasParams->size()); + // The index in f' of the template parameters of A, and of the non-deduced + // template parameters of f, that appear in f'. + SmallVector<unsigned> AliasParamFPrimeIndex(AliasParams->size(), + InvalidFPrimeIndex); + SmallVector<unsigned> FParamFPrimeIndex(FParams->size(), InvalidFPrimeIndex); + + // Template parameters of A that appear in f' without a default template + // argument, and that cannot be deduced from the function parameters of f', + // get a default template argument synthesized from the return type of f. + SmallVector<TemplateArgument> SynthesizedDefaultArgs = + synthesizeDefaultArgumentsForFPrime( + SemaRef, AliasTemplate, F, AliasRhsTemplateArgs, FReturnTemplateArgs, + DeduceResults, DeducedAliasTemplateParams, Loc); + // Those refer to template parameters of f, which the standard's order places + // after the template parameters of A; reorder the template parameters of f' + // so that default template arguments only refer to preceding template + // parameters. If that is not possible, don't synthesize any. + SmallVector<FPrimeTemplateParamRef> FPrimeParamOrder; + if (llvm::any_of(SynthesizedDefaultArgs, + [](const TemplateArgument &TA) { return !TA.isNull(); }) && + !orderFPrimeTemplateParameters( + SemaRef, AliasTemplate, F, DeduceResults, DeducedAliasTemplateParams, + NonDeducedTemplateParamsInFIndex, SynthesizedDefaultArgs, + FPrimeParamOrder)) { + llvm::fill(SynthesizedDefaultArgs, TemplateArgument()); + FPrimeParamOrder.clear(); + } + // We might be already within a pack expansion, but rewriting template // parameters is independent of that. (We may or may not expand new packs // when rewriting. So clear the state) Sema::ArgPackSubstIndexRAII PackSubstReset(SemaRef, std::nullopt); - for (unsigned AliasTemplateParamIdx : DeducedAliasTemplateParams) { - auto *TP = - AliasTemplate->getTemplateParameters()->getParam(AliasTemplateParamIdx); + // Add the template parameter of A at the given index to f'. + auto AddAliasTemplateParam = [&](unsigned AliasTemplateParamIdx) -> bool { + auto *TP = AliasParams->getParam(AliasTemplateParamIdx); // Rebuild any internal references to earlier parameters and reindex as // we go. MultiLevelTemplateArgumentList Args; @@ -1377,13 +1704,31 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( NamedDecl *NewParam = transformTemplateParameter( SemaRef, AliasTemplate->getDeclContext(), TP, Args, /*NewIndex=*/FPrimeTemplateParams.size(), getDepthAndIndex(TP).first); + if (const TemplateArgument &Default = + SynthesizedDefaultArgs[AliasTemplateParamIdx]; + !Default.isNull()) { + // The synthesized default template argument refers to template + // parameters of f; rewrite it in terms of the corresponding + // (already created) template parameters of f'. + QualType NTTPType; + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(NewParam)) + NTTPType = NTTP->getType(); + MultiLevelTemplateArgumentList FArgs; + FArgs.setKind(TemplateSubstitutionKind::Rewrite); + FArgs.addOuterTemplateArguments(TemplateArgsForBuildingFPrime); + TemplateArgumentLoc Output; + if (SemaRef.SubstTemplateArgument( + SemaRef.getTrivialTemplateArgumentLoc(Default, NTTPType, Loc), + FArgs, Output, Loc, NewParam->getDeclName())) + return false; + setDefaultArgument(Context, NewParam, Output); + } + AliasParamFPrimeIndex[AliasTemplateParamIdx] = FPrimeTemplateParams.size(); FPrimeTemplateParams.push_back(NewParam); - - TemplateArgument NewTemplateArgument = + TransformedDeducedAliasArgs[AliasTemplateParamIdx] = Context.getInjectedTemplateArg(NewParam); - TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument; - } - unsigned FirstUndeducedParamIdx = FPrimeTemplateParams.size(); + return true; + }; // To form a deduction guide f' from f, we leverage clang's instantiation // mechanism, we construct a template argument list where the template @@ -1400,21 +1745,21 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( // // Note: the non-deduced template arguments of `f` might refer to arguments // deduced in 1), as in a type constraint. - MultiLevelTemplateArgumentList Args; - Args.setKind(TemplateSubstitutionKind::Rewrite); - Args.addOuterTemplateArguments(TransformedDeducedAliasArgs); - for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { + // + // Substitute the template parameters of f' into the deduced template + // argument for the template parameter of f at the given index (1). The + // template parameters of A it refers to must have been added to f' already. + auto SubstDeducedTemplateArg = [&](unsigned Index) -> bool { const auto &D = DeduceResults[Index]; - auto *TP = F->getTemplateParameters()->getParam(Index); - if (IsNonDeducedArgument(D)) { - // 2): Non-deduced template parameters would be substituted later. - continue; - } + auto *TP = FParams->getParam(Index); + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(TransformedDeducedAliasArgs); TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{}); TemplateArgumentListInfo Output; if (SemaRef.SubstTemplateArguments(Input, Args, Output)) - return nullptr; + return false; assert(TemplateArgsForBuildingFPrime[Index].isNull() && "InstantiatedArgs must be null before setting"); // CheckTemplateArgument is necessary for NTTP initializations. @@ -1428,7 +1773,7 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( TP, TA, F, F->getLocation(), F->getLocation(), /*ArgumentPackIndex=*/-1, CTAI, Sema::CheckTemplateArgumentKind::CTAK_Specified)) - return nullptr; + return false; if (Input.getArgument().getKind() == TemplateArgument::Pack) { // We will substitute the non-deduced template arguments with these // transformed (unpacked at this point) arguments, where that substitution @@ -1439,13 +1784,12 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( assert(Output.arguments().size() == 1); TemplateArgsForBuildingFPrime[Index] = CTAI.SugaredConverted[0]; } - } + return true; + }; - // Case 2) - // ...followed by the template parameters of f that were not deduced - // (including their default template arguments) - for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) { - auto *TP = F->getTemplateParameters()->getParam(FTemplateParamIdx); + // Add the non-deduced template parameter of f at the given index to f' (2). + auto AddFTemplateParam = [&](unsigned FTemplateParamIdx) { + auto *TP = FParams->getParam(FTemplateParamIdx); MultiLevelTemplateArgumentList Args; Args.setKind(TemplateSubstitutionKind::Rewrite); // We take a shortcut here, it is ok to reuse the @@ -1454,12 +1798,48 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( NamedDecl *NewParam = transformTemplateParameter( SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size(), getDepthAndIndex(TP).first); + FParamFPrimeIndex[FTemplateParamIdx] = FPrimeTemplateParams.size(); FPrimeTemplateParams.push_back(NewParam); assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() && "The argument must be null before setting"); TemplateArgsForBuildingFPrime[FTemplateParamIdx] = Context.getInjectedTemplateArg(NewParam); + }; + + if (FPrimeParamOrder.empty()) { + // The standard's order: the template parameters of A that appear in the + // deductions, followed by the non-deduced template parameters of f. + for (unsigned AliasTemplateParamIdx : DeducedAliasTemplateParams) + if (!AddAliasTemplateParam(AliasTemplateParamIdx)) + return nullptr; + for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) + if (!IsNonDeducedArgument(DeduceResults[Index]) && + !SubstDeducedTemplateArg(Index)) + return nullptr; + for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) + AddFTemplateParam(FTemplateParamIdx); + } else { + for (const FPrimeTemplateParamRef &P : FPrimeParamOrder) { + if (P.IsAliasParam) { + if (!AddAliasTemplateParam(P.Index)) + return nullptr; + continue; + } + // The template parameters of A that the deduced template arguments + // referred to by this template parameter of f refer to have been added + // already, so we can substitute them now. + for (unsigned Index : P.DeducedFParamsUsed.set_bits()) + if (TemplateArgsForBuildingFPrime[Index].isNull() && + !SubstDeducedTemplateArg(Index)) + return nullptr; + AddFTemplateParam(P.Index); + } + for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) + if (!IsNonDeducedArgument(DeduceResults[Index]) && + TemplateArgsForBuildingFPrime[Index].isNull() && + !SubstDeducedTemplateArg(Index)) + return nullptr; } auto *TemplateArgListForBuildingFPrime = @@ -1472,9 +1852,9 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( Expr *IsDeducible = buildIsDeducibleConstraint( SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams); - Expr *RequiresClause = - buildAssociatedConstraints(SemaRef, F, AliasTemplate, DeduceResults, - FirstUndeducedParamIdx, IsDeducible); + Expr *RequiresClause = buildAssociatedConstraints( + SemaRef, F, AliasTemplate, DeduceResults, AliasParamFPrimeIndex, + FParamFPrimeIndex, IsDeducible); TemplateParameterList *FPrimeTemplateParamList = nullptr; if (!FPrimeTemplateParams.empty()) diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp index 4ca84dc2d1828..2704bab0910bc 100644 --- a/clang/test/AST/ast-dump-ctad-alias.cpp +++ b/clang/test/AST/ast-dump-ctad-alias.cpp @@ -202,3 +202,33 @@ void foo() { // CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'U' } // namespace GH124715 + +// A template parameter of the alias template that cannot be deduced from the +// constructor arguments gets a default template argument deduced from the +// return type of the underlying deduction guide ('Key' below), and the template +// parameters of the synthesized guide are ordered so that default template +// arguments only refer to preceding ones ('It' comes first). +namespace synthesized_default_args { +template <class T> struct hash {}; +template <class It> struct iter_traits { using value_type = typename It::value_type; }; +struct Iter { using value_type = int; }; +template <class Key, class Hash = hash<Key>> struct Set { template <class It> Set(It, It); }; +template <class It, class Hash = hash<typename iter_traits<It>::value_type>> +Set(It, It, Hash = Hash()) -> Set<typename iter_traits<It>::value_type, Hash>; +template <class Key, class Hash = hash<Key>> using MySet = Set<Key, Hash>; +void f(Iter b, Iter e) { MySet s(b, e); } + +// CHECK: <deduction guide for MySet> 'auto (synthesized_default_args::Set<Key, Hash>) -> synthesized_default_args::Set<Key, Hash>' +// CHECK: `-FunctionTemplateDecl {{.*}} <deduction guide for MySet> +// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 0 It +// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 1 Key +// CHECK-NEXT: | `-TemplateArgument type 'typename iter_traits<It>::value_type':'synthesized_default_args::iter_traits<type-parameter-0-0>::value_type' +// CHECK-NEXT: | `-DependentNameType {{.*}} 'typename iter_traits<It>::value_type' dependent +// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 2 Hash +// CHECK-NEXT: | `-TemplateArgument type 'hash<Key>':'synthesized_default_args::hash<type-parameter-0-1>' +// CHECK: |-CXXDeductionGuideDecl {{.*}} <deduction guide for MySet> 'auto (It, It, Hash) -> Set<typename iter_traits<It>::value_type, Hash>' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} 'It' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} 'It' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} 'Hash' +// CHECK-NEXT: `-CXXDeductionGuideDecl {{.*}} used <deduction guide for MySet> 'auto (synthesized_default_args::Iter, synthesized_default_args::Iter, synthesized_default_args::hash<int>) -> Set<typename iter_traits<synthesized_default_args::Iter>::value_type, synthesized_default_args::hash<int>>' implicit_instantiation +} // namespace synthesized_default_args diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index e0f59be663b2f..fe7012e1a87ab 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -629,3 +629,105 @@ template <typename T> using S3 = S2<T>; // expected-note {{candidate function no // expected-note {{cannot deduce template arguments for 'GH190517::S3' from 'GH190517::S1<char>'}} S3 foo(42); // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'S3'}} } + +// Template parameters of the alias template that appear in a synthesized +// deduction guide only through the default template arguments of other +// template parameters cannot be deduced from the constructor arguments. They +// get a default template argument deduced from the return type of the +// underlying deduction guide instead, and the template parameters of the +// synthesized guide are ordered so that default template arguments only refer +// to preceding ones. +namespace synthesized_default_args { +template <class T> struct hash {}; +template <class T> struct alloc {}; +template <class It> struct iter_traits { using value_type = typename It::value_type; }; +struct Iter { using value_type = int; }; + +template <class Key, class Hash = hash<Key>, class Alloc = alloc<Key>> +struct Set { + Set(); + template <class It> Set(It, It); + template <class It> Set(It, It, Hash); +}; +template <class It, + class Hash = hash<typename iter_traits<It>::value_type>, + class Alloc = alloc<typename iter_traits<It>::value_type>> +Set(It, It, Hash = Hash(), Alloc = Alloc()) + -> Set<typename iter_traits<It>::value_type, Hash, Alloc>; + +// Like std::unordered_set: the alias merely renames the class template. +template <class Key, class Hash = hash<Key>, class Alloc = alloc<Key>> +using MySet = Set<Key, Hash, Alloc>; +// The alias has fewer template parameters than the class template. +template <class Key, class Hash = hash<Key>> +using MySet2 = Set<Key, Hash>; +// The alias has a different default template argument, which wins. +template <class Key, class Hash = hash<Key*>, class Alloc = alloc<Key>> +using MySet3 = Set<Key, Hash, Alloc>; // #MySet3 + +void f(Iter b, Iter e) { + MySet s1(b, e); + static_assert(__is_same(decltype(s1), Set<int, hash<int>, alloc<int>>)); + MySet s2(b, e, hash<long>()); + static_assert(__is_same(decltype(s2), Set<int, hash<long>, alloc<int>>)); + MySet2 s3(b, e); + static_assert(__is_same(decltype(s3), Set<int, hash<int>, alloc<int>>)); + MySet3 s4(b, e); + static_assert(__is_same(decltype(s4), Set<int, hash<int*>, alloc<int>>)); + MySet s5 = s1; + static_assert(__is_same(decltype(s5), decltype(s1))); + + // The non-deduced template parameter 'It' of the underlying guide now comes + // first, followed by 'Key' with its synthesized default template argument. + MySet3 s6(b, e, 1, 2, 3); // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'MySet3'}} + // expected-note@#MySet3 {{implicit deduction guide declared as 'template <class It, class Key = typename iter_traits<It>::value_type, class Hash = hash<Key *>, class Alloc = alloc<Key>> requires __is_deducible(synthesized_default_args::MySet3, Set<typename iter_traits<It>::value_type, Hash, Alloc>) MySet3(It, It, Hash, Alloc) -> Set<typename iter_traits<It>::value_type, Hash, Alloc>'}} + // expected-note@#MySet3 4 {{implicit deduction guide declared as}} + // expected-note@#MySet3 {{requires at most 4 arguments, but 5 were provided}} + // expected-note@#MySet3 {{requires 3 arguments, but 5 were provided}} + // expected-note@#MySet3 {{requires 2 arguments, but 5 were provided}} + // expected-note@#MySet3 {{requires 1 argument, but 5 were provided}} + // expected-note@#MySet3 {{requires 0 arguments, but 5 were provided}} +} + +// A template parameter of the alias that is deducible from some constructor +// arguments only. +template <class T, class A = alloc<T>> struct Vec { + template <class It> Vec(It, It); + template <class It> Vec(It, It, A); +}; +template <class It, class A = alloc<typename iter_traits<It>::value_type>> +Vec(It, It, A = A()) -> Vec<typename iter_traits<It>::value_type, A>; +template <class T> using MyVec = Vec<T, hash<T>>; + +void g(Iter b, Iter e) { + MyVec v1(b, e); + static_assert(__is_same(decltype(v1), Vec<int, hash<int>>)); + MyVec v2(b, e, hash<int>()); + static_assert(__is_same(decltype(v2), Vec<int, hash<int>>)); +} + +// The underlying guide is constrained, and the alias is a member of a class +// template. +template <class T> concept Any = true; +template <class Key, class Hash = hash<Key>> struct CSet { + CSet(); + template <class It> CSet(It, It); +}; +template <class It, class Hash = hash<typename iter_traits<It>::value_type>> + requires Any<It> && Any<Hash> +CSet(It, It, Hash = Hash()) -> CSet<typename iter_traits<It>::value_type, Hash>; + +template <class U> struct Outer { + template <class Key, class Hash = hash<Key>> using MyCSet = CSet<Key, Hash>; +}; + +void h(Iter b, Iter e) { + Outer<long>::MyCSet s1(b, e); + static_assert(__is_same(decltype(s1), CSet<int, hash<int>>)); +} +template <class U> void h2(Iter b, Iter e) { + typename Outer<U>::MyCSet s(b, e); + static_assert(__is_same(decltype(s), CSet<int, hash<int>>)); +} +template void h2<char>(Iter, Iter); +} // namespace synthesized_default_args _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
