aboutsummaryrefslogtreecommitdiff
path: root/clang
diff options
context:
space:
mode:
authorHaojian Wu <hokein.wu@gmail.com>2024-07-04 18:45:33 +0200
committerGitHub <noreply@github.com>2024-07-04 18:45:33 +0200
commit834ecc8b2ad44859f5bcaac9f92178e469390a51 (patch)
tree0b62dd9036e1a8b6731b6bea00714c54518e2ad6 /clang
parent0ccec541d526a1be84fdfcf8f42553965dadff78 (diff)
downloadllvm-834ecc8b2ad44859f5bcaac9f92178e469390a51.zip
llvm-834ecc8b2ad44859f5bcaac9f92178e469390a51.tar.gz
llvm-834ecc8b2ad44859f5bcaac9f92178e469390a51.tar.bz2
[clang] CTAD alias: fix transformation for require-clause expr Part2. (#93533)
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 are not transformed, which leaves incorrect depth/index information, and causes crashes when evaluating constraints. This patch fix this missing case. Fixes #92596 Fixes #92212
Diffstat (limited to 'clang')
-rw-r--r--clang/lib/Sema/SemaTemplate.cpp36
-rw-r--r--clang/test/AST/ast-dump-ctad-alias.cpp27
-rw-r--r--clang/test/SemaCXX/cxx20-ctad-type-alias.cpp27
3 files changed, 82 insertions, 8 deletions
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 6879a9a..07b3f79 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<DeducedTemplateArgument> DeduceResults,
- Expr *IsDeducible) {
+ unsigned FirstUndeducedParamIdx, Expr *IsDeducible) {
Expr *RC = F->getTemplateParameters()->getRequiresClause();
if (!RC)
return IsDeducible;
@@ -2803,8 +2803,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
+ 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=*/FirstUndeducedParamIdx,
+ getTemplateParameterDepth(TP) + AdjustDepth);
+ FirstUndeducedParamIdx += 1;
+ assert(TemplateArgsForBuildingRC[Index].isNull());
+ TemplateArgsForBuildingRC[Index] = Context.getCanonicalTemplateArgument(
+ Context.getInjectedTemplateArg(NewParam));
continue;
+ }
TemplateArgumentLoc Input =
SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{});
TemplateArgumentLoc Output;
@@ -2820,8 +2834,8 @@ 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
+ // 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:
@@ -2845,7 +2859,13 @@ 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<U>` will be replaced with int
// during the substitution.
- if (F->getInstantiatedFromMemberTemplate()) {
+ //
+ // 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(
F, F->getLexicalDeclContext(),
/*Final=*/false, /*Innermost=*/std::nullopt,
@@ -3063,6 +3083,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
Context.getInjectedTemplateArg(NewParam));
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) {
@@ -3131,8 +3152,9 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
Expr *IsDeducible = buildIsDeducibleConstraint(
SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
- Expr *RequiresClause = buildAssociatedConstraints(
- SemaRef, F, AliasTemplate, DeduceResults, IsDeducible);
+ Expr *RequiresClause =
+ buildAssociatedConstraints(SemaRef, F, AliasTemplate, DeduceResults,
+ 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 a4b6f06..6f07a62 100644
--- a/clang/test/AST/ast-dump-ctad-alias.cpp
+++ b/clang/test/AST/ast-dump-ctad-alias.cpp
@@ -53,6 +53,33 @@ Out2<double>::AInner t(1.0);
// CHECK-NEXT: | | `-BuiltinType {{.*}} 'double'
// CHECK-NEXT: | `-ParmVarDecl {{.*}} 'double'
+// GH92596
+template <typename T0>
+struct Out3 {
+ template<class T1, typename T2>
+ struct Foo {
+ // Deduction guide:
+ // template <typename T1, typename T2, typename V>
+ // Foo(V, T1) -> Foo<T1, T2>;
+ template<class V> requires Concept<T0, V> // V in require clause of Foo deduction guide: depth 1, index: 2
+ Foo(V, T1);
+ };
+};
+template<class T3>
+using AFoo3 = Out3<int>::Foo<T3, T3>;
+AFoo3 afoo3{0, 1};
+// Verify occurrence V in the require-clause is transformed (depth: 1 => 0, index: 2 => 1) correctly.
+
+// CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for AFoo3>
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 0 T3
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 1 V
+// CHECK-NEXT: |-BinaryOperator {{.*}} '<dependent type>' '&&'
+// CHECK-NEXT: | |-UnresolvedLookupExpr {{.*}} '<dependent type>' 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 <typename... T1>
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 3dafe82..a369ce6 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -266,7 +266,7 @@ template <typename U>
using Bar = Foo<U>; // expected-note {{could not match 'Foo<type-parameter-0-0>' against 'int'}} \
// expected-note {{implicit deduction guide declared as 'template <typename U> requires __is_deducible(test18::Bar, Foo<type-parameter-0-0>) Bar(Foo<type-parameter-0-0>) -> Foo<type-parameter-0-0>'}} \
// expected-note {{candidate template ignored: constraints not satisfied}} \
- // expected-note {{implicit deduction guide declared as 'template <typename T> requires False<T> && __is_deducible(test18::Bar, Foo<int>) Bar(type-parameter-0-0) -> Foo<int>'}} \
+ // expected-note {{implicit deduction guide declared as 'template <typename T> requires False<type-parameter-0-0> && __is_deducible(test18::Bar, Foo<int>) Bar(type-parameter-0-0) -> Foo<int>'}} \
// expected-note {{candidate function template not viable}} \
// expected-note {{implicit deduction guide declared as 'template <typename U> requires __is_deducible(test18::Bar, Foo<type-parameter-0-0>) Bar() -> Foo<type-parameter-0-0>'}}
@@ -414,4 +414,29 @@ struct A1 {
template <typename U>
using AFoo = A1<int>::A2<int>::Foo<U>;
AFoo case3(1);
+
+// Case4: crashes on the constexpr evaluator due to the mixed-up index for the
+// template parameters `V`.
+template<class T, typename T2>
+struct Case4 {
+ template<class V> requires C<V>
+ Case4(V, T);
+};
+
+template<class T2>
+using ACase4 = Case4<T2, T2>;
+ACase4 case4{0, 1};
+
} // namespace test24
+
+namespace GH92212 {
+template<typename T, typename...Us>
+struct A{
+ template<typename V> requires __is_same(V, int)
+ A(V);
+};
+
+template<typename...TS>
+using AA = A<int, TS...>;
+AA a{0};
+}