From b0b8d5b064c35e4f0b66e2538c4172afa9665dbe Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 17 May 2024 15:28:48 +0200 Subject: [PATCH 1/4] [clang] CTAD alias: fix transformation for require-clause expr Part2. In the https://github.com/llvm/llvm-project/pull/90961 fix, we miss a case where the undeduced template parameters of the underlying deduction guide is not transformed, which leaves incorrect depth/index information, and causes crash when evaluating the constraints. This patch fix this missing case. Fixes #92596 Fixes #92212 --- clang/lib/Sema/SemaTemplate.cpp | 32 ++++++++++++++++---- clang/test/AST/ast-dump-ctad-alias.cpp | 25 +++++++++++++++ clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 25 +++++++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 6879a9a274b5c..08c6858f7e776 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2743,6 +2743,7 @@ Expr * buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, TypeAliasTemplateDecl *AliasTemplate, ArrayRef DeduceResults, + unsigned UndeducedTemplateParameterStartIndex, Expr *IsDeducible) { Expr *RC = F->getTemplateParameters()->getRequiresClause(); if (!RC) @@ -2803,8 +2804,22 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { const auto &D = DeduceResults[Index]; - if (D.isNull()) + if (D.isNull()) { // non-deduced template parameters of f + auto TP = F->getTemplateParameters()->getParam(Index); + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(TemplateArgsForBuildingRC); + // Rebuild the template parameter with updated depth and index. + NamedDecl *NewParam = transformTemplateParameter( + SemaRef, F->getDeclContext(), TP, Args, + /*NewIndex=*/UndeducedTemplateParameterStartIndex++, + getTemplateParameterDepth(TP) + AdjustDepth); + + assert(TemplateArgsForBuildingRC[Index].isNull()); + TemplateArgsForBuildingRC[Index] = Context.getCanonicalTemplateArgument( + Context.getInjectedTemplateArg(NewParam)); continue; + } TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{}); TemplateArgumentLoc Output; @@ -2820,9 +2835,11 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, MultiLevelTemplateArgumentList ArgsForBuildingRC; ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC); - // For 2), if the underlying F is instantiated from a member template, we need - // the entire template argument list, as the constraint AST in the - // require-clause of F remains completely uninstantiated. + // For 2), if the underlying function template F is nested in a class template + // (either instantiated from an explicitly-written deduction guide, or + // synthesized from a constructor), we need the entire template argument list, + // as the constraint AST in the require-clause of F remains completely + // uninstantiated. // // For example: // template // depth 0 @@ -2845,7 +2862,8 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, // We add the outer template arguments which is [int] to the multi-level arg // list to ensure that the occurrence U in `C` will be replaced with int // during the substitution. - if (F->getInstantiatedFromMemberTemplate()) { + if (F->getLexicalDeclContext()->getDeclKind() == + clang::Decl::ClassTemplateSpecialization) { auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs( F, F->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, @@ -3063,6 +3081,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, Context.getInjectedTemplateArg(NewParam)); TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument; } + unsigned UndeducedTemplateParameterStartIndex = FPrimeTemplateParams.size(); // ...followed by the template parameters of f that were not deduced // (including their default template arguments) for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) { @@ -3132,7 +3151,8 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, Expr *IsDeducible = buildIsDeducibleConstraint( SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams); Expr *RequiresClause = buildAssociatedConstraints( - SemaRef, F, AliasTemplate, DeduceResults, IsDeducible); + SemaRef, F, AliasTemplate, DeduceResults, + UndeducedTemplateParameterStartIndex, IsDeducible); auto *FPrimeTemplateParamList = TemplateParameterList::Create( Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(), diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp index a4b6f06547443..b9101d88cf8ac 100644 --- a/clang/test/AST/ast-dump-ctad-alias.cpp +++ b/clang/test/AST/ast-dump-ctad-alias.cpp @@ -53,6 +53,31 @@ Out2::AInner t(1.0); // CHECK-NEXT: | | `-BuiltinType {{.*}} 'double' // CHECK-NEXT: | `-ParmVarDecl {{.*}} 'double' +// GH92596 +template +struct Out3 { + template + struct Foo { + // Deduction guide: Foo(T1, T2, V) -> Foo; + template requires Concept // V in require clause of Foo deduction guide: depth 1, index: 2 + Foo(V, T1); + }; +}; +template +using AFoo3 = Out3::Foo; +AFoo3 afoo3{0, 1}; +// Verify occurrence V in the require-clause is transformed (depth: 1 => 0, index: 2 => 1) correctly. + +// CHECK: FunctionTemplateDecl {{.*}} implicit +// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 0 T3 +// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 1 V +// CHECK-NEXT: |-BinaryOperator {{.*}} '' '&&' +// CHECK-NEXT: | |-UnresolvedLookupExpr {{.*}} '' lvalue (no ADL) = 'Concept' +// CHECK-NEXT: | | |-TemplateArgument type 'int' +// CHECK-NEXT: | | | `-BuiltinType {{.*}} 'int' +// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-0-1' +// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent depth 0 index 1 + template struct Foo { Foo(T1...); diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 3dafe823fbbaa..aa68a088d4f7b 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -414,4 +414,29 @@ struct A1 { template using AFoo = A1::A2::Foo; AFoo case3(1); + +// Case4: crashes on the constexpr evaluator due to the mixed-up index for the +// template parameters `V`. +template +struct Case4 { + template requires C + Case4(V, T); +}; + +template +using ACase4 = Case4; +ACase4 case4{0, 1}; + } // namespace test24 + +namespace GH92212 { +template +struct A{ + template requires __is_same(V, int) + A(V); +}; + +template +using AA = A; +AA a{0}; +} From 2447bf860b74119f90639838c3e95eefbd12972b Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Thu, 27 Jun 2024 10:20:57 +0200 Subject: [PATCH 2/4] Address review comments. --- clang/lib/Sema/SemaTemplate.cpp | 25 ++++++++++++++----------- clang/test/AST/ast-dump-ctad-alias.cpp | 4 +++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 08c6858f7e776..62a41b29364cf 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2743,7 +2743,7 @@ Expr * buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, TypeAliasTemplateDecl *AliasTemplate, ArrayRef DeduceResults, - unsigned UndeducedTemplateParameterStartIndex, + unsigned FirstUndeducedParamIdx, Expr *IsDeducible) { Expr *RC = F->getTemplateParameters()->getRequiresClause(); if (!RC) @@ -2805,16 +2805,16 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { const auto &D = DeduceResults[Index]; if (D.isNull()) { // non-deduced template parameters of f - auto TP = F->getTemplateParameters()->getParam(Index); + NamedDecl* TP = F->getTemplateParameters()->getParam(Index); MultiLevelTemplateArgumentList Args; Args.setKind(TemplateSubstitutionKind::Rewrite); Args.addOuterTemplateArguments(TemplateArgsForBuildingRC); // Rebuild the template parameter with updated depth and index. NamedDecl *NewParam = transformTemplateParameter( SemaRef, F->getDeclContext(), TP, Args, - /*NewIndex=*/UndeducedTemplateParameterStartIndex++, + /*NewIndex=*/FirstUndeducedParamIdx, getTemplateParameterDepth(TP) + AdjustDepth); - + FirstUndeducedParamIdx += 1; assert(TemplateArgsForBuildingRC[Index].isNull()); TemplateArgsForBuildingRC[Index] = Context.getCanonicalTemplateArgument( Context.getInjectedTemplateArg(NewParam)); @@ -2835,11 +2835,9 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, MultiLevelTemplateArgumentList ArgsForBuildingRC; ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC); - // For 2), if the underlying function template F is nested in a class template - // (either instantiated from an explicitly-written deduction guide, or - // synthesized from a constructor), we need the entire template argument list, - // as the constraint AST in the require-clause of F remains completely - // uninstantiated. + // For 2), if the underlying deduction guide F is nested in a class template, + // we need the entire template argument list, as the constraint AST in the + // require-clause of F remains completely uninstantiated. // // For example: // template // depth 0 @@ -2862,6 +2860,11 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, // We add the outer template arguments which is [int] to the multi-level arg // list to ensure that the occurrence U in `C` will be replaced with int // during the substitution. + // + // NOTE: The underlying deduction guide F is instantiated -- either from an + // explicitly-written deduction guide member, or from a constructor. + // getInstantiatedFromMemberTemplate() can only handle the former case, so we + // check the DeclContext kind. if (F->getLexicalDeclContext()->getDeclKind() == clang::Decl::ClassTemplateSpecialization) { auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs( @@ -3081,7 +3084,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, Context.getInjectedTemplateArg(NewParam)); TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument; } - unsigned UndeducedTemplateParameterStartIndex = FPrimeTemplateParams.size(); + unsigned FirstUndeducedParamIdx = FPrimeTemplateParams.size(); // ...followed by the template parameters of f that were not deduced // (including their default template arguments) for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) { @@ -3152,7 +3155,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams); Expr *RequiresClause = buildAssociatedConstraints( SemaRef, F, AliasTemplate, DeduceResults, - UndeducedTemplateParameterStartIndex, IsDeducible); + FirstUndeducedParamIdx, IsDeducible); auto *FPrimeTemplateParamList = TemplateParameterList::Create( Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(), diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp index b9101d88cf8ac..6f07a62e9a069 100644 --- a/clang/test/AST/ast-dump-ctad-alias.cpp +++ b/clang/test/AST/ast-dump-ctad-alias.cpp @@ -58,7 +58,9 @@ template struct Out3 { template struct Foo { - // Deduction guide: Foo(T1, T2, V) -> Foo; + // Deduction guide: + // template + // Foo(V, T1) -> Foo; template requires Concept // V in require clause of Foo deduction guide: depth 1, index: 2 Foo(V, T1); }; From 251f179e1fc654381b90a2746799c044344499f3 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Thu, 4 Jul 2024 17:58:47 +0200 Subject: [PATCH 3/4] Rebase and update the test --- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index aa68a088d4f7b..a369ce687a728 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -266,7 +266,7 @@ template using Bar = Foo; // expected-note {{could not match 'Foo' against 'int'}} \ // expected-note {{implicit deduction guide declared as 'template requires __is_deducible(test18::Bar, Foo) Bar(Foo) -> Foo'}} \ // expected-note {{candidate template ignored: constraints not satisfied}} \ - // expected-note {{implicit deduction guide declared as 'template requires False && __is_deducible(test18::Bar, Foo) Bar(type-parameter-0-0) -> Foo'}} \ + // expected-note {{implicit deduction guide declared as 'template requires False && __is_deducible(test18::Bar, Foo) Bar(type-parameter-0-0) -> Foo'}} \ // expected-note {{candidate function template not viable}} \ // expected-note {{implicit deduction guide declared as 'template requires __is_deducible(test18::Bar, Foo) Bar() -> Foo'}} From 284d1171ad24154ecd847d32b63ec59d7aabbdde Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Thu, 4 Jul 2024 18:10:41 +0200 Subject: [PATCH 4/4] clang-format --- clang/lib/Sema/SemaTemplate.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 62a41b29364cf..07b3f793b3a29 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2743,8 +2743,7 @@ Expr * buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, TypeAliasTemplateDecl *AliasTemplate, ArrayRef DeduceResults, - unsigned FirstUndeducedParamIdx, - Expr *IsDeducible) { + unsigned FirstUndeducedParamIdx, Expr *IsDeducible) { Expr *RC = F->getTemplateParameters()->getRequiresClause(); if (!RC) return IsDeducible; @@ -2805,7 +2804,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { const auto &D = DeduceResults[Index]; if (D.isNull()) { // non-deduced template parameters of f - NamedDecl* TP = F->getTemplateParameters()->getParam(Index); + NamedDecl *TP = F->getTemplateParameters()->getParam(Index); MultiLevelTemplateArgumentList Args; Args.setKind(TemplateSubstitutionKind::Rewrite); Args.addOuterTemplateArguments(TemplateArgsForBuildingRC); @@ -3153,9 +3152,9 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, Expr *IsDeducible = buildIsDeducibleConstraint( SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams); - Expr *RequiresClause = buildAssociatedConstraints( - SemaRef, F, AliasTemplate, DeduceResults, - FirstUndeducedParamIdx, IsDeducible); + Expr *RequiresClause = + buildAssociatedConstraints(SemaRef, F, AliasTemplate, DeduceResults, + FirstUndeducedParamIdx, IsDeducible); auto *FPrimeTemplateParamList = TemplateParameterList::Create( Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),