Skip to content

[Clang] Correct the order of substituted arguments in CTAD alias guides #123022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,8 @@ Bug Fixes to C++ Support
- Clang now identifies unexpanded parameter packs within the type constraint on a non-type template parameter. (#GH88866)
- Fixed an issue while resolving type of expression indexing into a pack of values of non-dependent type (#GH121242)
- Fixed a crash when __PRETTY_FUNCTION__ or __FUNCSIG__ (clang-cl) appears in the trailing return type of the lambda (#GH121274)
- Fixed a crash caused by the incorrect construction of template arguments for CTAD alias guides when type
constraints are applied. (#GH122134)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
61 changes: 30 additions & 31 deletions clang/lib/Sema/SemaTemplateDeductionGuide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
F->getTemplateParameters()->size());

// FIXME: DeduceTemplateArguments stops immediately at the first
// non-deducible template argument. However, this doesn't seem to casue
// non-deducible template argument. However, this doesn't seem to cause
// issues for practice cases, we probably need to extend it to continue
// performing deduction for rest of arguments to align with the C++
// standard.
Expand Down Expand Up @@ -1053,25 +1053,6 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument;
}
unsigned FirstUndeducedParamIdx = FPrimeTemplateParams.size();
// ...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);
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
// We take a shortcut here, it is ok to reuse the
// TemplateArgsForBuildingFPrime.
Args.addOuterTemplateArguments(TemplateArgsForBuildingFPrime);
NamedDecl *NewParam = transformTemplateParameter(
SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size(),
getDepthAndIndex(TP).first);
FPrimeTemplateParams.push_back(NewParam);

assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() &&
"The argument must be null before setting");
TemplateArgsForBuildingFPrime[FTemplateParamIdx] =
Context.getInjectedTemplateArg(NewParam);
}

// To form a deduction guide f' from f, we leverage clang's instantiation
// mechanism, we construct a template argument list where the template
Expand All @@ -1080,24 +1061,21 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
// f, this ensures all template parameter occurrences are updated
// correctly.
//
// The template argument list is formed from the `DeducedArgs`, two parts:
// 1) appeared template parameters of alias: transfrom the deduced
// template argument;
// 2) non-deduced template parameters of f: rebuild a
// template argument;
// The template argument list is formed, in order, from
// 1) For the template parameters of the alias, the corresponding deduced
// template arguments
// 2) For the non-deduced template parameters of f. the
// (rebuilt) template arguments corresponding.
//
// 2) has been built already (when rebuilding the new template
// parameters), we now perform 1).
// 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) {
const auto &D = DeduceResults[Index];
if (D.isNull()) {
// 2): Non-deduced template parameter has been built already.
assert(!TemplateArgsForBuildingFPrime[Index].isNull() &&
"template arguments for non-deduced template parameters should "
"be been set!");
// 2): Non-deduced template parameters would be substituted later.
continue;
}
TemplateArgumentLoc Input =
Expand All @@ -1110,6 +1088,27 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
}
}

// 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);
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
// We take a shortcut here, it is ok to reuse the
// TemplateArgsForBuildingFPrime.
Args.addOuterTemplateArguments(TemplateArgsForBuildingFPrime);
NamedDecl *NewParam = transformTemplateParameter(
SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size(),
getDepthAndIndex(TP).first);
FPrimeTemplateParams.push_back(NewParam);

assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() &&
"The argument must be null before setting");
TemplateArgsForBuildingFPrime[FTemplateParamIdx] =
Context.getInjectedTemplateArg(NewParam);
}

auto *TemplateArgListForBuildingFPrime =
TemplateArgumentList::CreateCopy(Context, TemplateArgsForBuildingFPrime);
// Form the f' by substituting the template arguments into f.
Expand Down
50 changes: 50 additions & 0 deletions clang/test/SemaTemplate/deduction-guide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,3 +478,53 @@ A a{.f1 = {1}};
// CHECK-NEXT: `-DeclRefExpr {{.+}} <col:10> 'int' NonTypeTemplateParm {{.+}} 'N' 'int'

} // namespace GH83368

namespace GH122134 {

template <class, class>
concept Constraint = true;

template <class T, int> struct Struct {
Struct(Constraint<T> auto) {}
};

template <int N = 0> using Test = Struct<int, N>;

Test test(42);

// CHECK-LABEL: Dumping GH122134::<deduction guide for Test>:
// CHECK-NEXT: FunctionTemplateDecl {{.*}} implicit <deduction guide for Test>
// CHECK-NEXT: |-NonTypeTemplateParmDecl {{.*}} 'int' depth 0 index 0 N
// CHECK-NEXT: | `-TemplateArgument {{.*}} expr '0'
// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} Concept {{.*}} 'Constraint' depth 0 index 1 auto:1
// CHECK-NEXT: | `-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'Constraint'
// CHECK-NEXT: | |-ImplicitConceptSpecializationDecl {{.*}}
// CHECK-NEXT: | | |-TemplateArgument type 'type-parameter-0-1'
// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent depth 0 index 1
// CHECK-NEXT: | | `-TemplateArgument type 'int'
// CHECK-NEXT: | | `-BuiltinType {{.*}} 'int'
// CHECK-NEXT: | |-TemplateArgument {{.*}} type 'auto:1':'type-parameter-0-1'
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'auto:1' dependent depth 0 index 1
// CHECK-NEXT: | | `-TemplateTypeParm {{.*}} 'auto:1'
// CHECK-NEXT: | `-TemplateArgument {{.*}} type 'int'
// CHECK-NEXT: | `-BuiltinType {{.*}} 'int'
// CHECK-NEXT: |-TypeTraitExpr {{.*}} 'bool' __is_deducible
// CHECK-NEXT: | |-DeducedTemplateSpecializationType {{.*}} 'GH122134::Test' dependent
// CHECK-NEXT: | | `-name: 'GH122134::Test'
// CHECK-NEXT: | | `-TypeAliasTemplateDecl {{.*}} Test
// CHECK-NEXT: | `-TemplateSpecializationType {{.*}} 'Struct<int, N>' dependent
// CHECK-NEXT: | |-name: 'Struct':'GH122134::Struct' qualified
// CHECK-NEXT: | | `-ClassTemplateDecl {{.*}} Struct
// CHECK-NEXT: | |-TemplateArgument type 'int'
// CHECK-NEXT: | | `-SubstTemplateTypeParmType {{.*}} 'int' sugar class depth 0 index 0 T
// CHECK-NEXT: | | |-FunctionTemplate {{.*}} '<deduction guide for Struct>'
// CHECK-NEXT: | | `-BuiltinType {{.*}} 'int'
// CHECK-NEXT: | `-TemplateArgument expr 'N'
// CHECK-NEXT: | `-SubstNonTypeTemplateParmExpr {{.*}} 'int'
// CHECK-NEXT: | |-NonTypeTemplateParmDecl {{.*}} 'int' depth 0 index 1
// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'N' 'int'
// CHECK-NEXT: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for Test> 'auto (auto:1) -> Struct<int, N>'
// CHECK-NEXT: | `-ParmVarDecl {{.*}} 'auto:1'

} // namespace GH122134
Loading