mizvekov created this revision. Herald added a subscriber: kristof.beyls. mizvekov updated this revision to Diff 375937. mizvekov added a comment. mizvekov updated this revision to Diff 375943. mizvekov published this revision for review. Herald added a project: clang. Herald added a subscriber: cfe-commits.
. mizvekov added a comment. . There is a special situation with templates in local classes, as can be seen in this example with generic lambdas in function scope: template<class T1> void foo() { (void)[]<class T2>() { struct S { void bar() { (void)[]<class T3>(T2) {}; } }; }; }; template void foo<int>(); As a consequence of the resolution of DR1484, bar is instantiated during the substitution of foo, and in this context we would substitute the lambda within it with it's own parameters "injected" (turned into arguments). This can't be properly dealt with for at least a couple of reasons: - The 'TemplateTypeParm' type itself can only deal with canonical replacement types, which the injected arguments are not. - If T3 were constrained in the example above, our (non-conforming) eager substitution of type constraints would just leave that parameter dangling. Instead of substituting with injected parameters, this patch just leaves those inner levels unreplaced. Since injected arguments appear to be unused within the users of `getTemplateInstantiationArgs`, this patch just removes that support there and leaves a couple of asserts in place. Signed-off-by: Matheus Izvekov <mizve...@gmail.com> Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D110727 Files: clang/lib/Sema/SemaTemplateInstantiate.cpp clang/test/SemaTemplate/generic-lambda.cpp Index: clang/test/SemaTemplate/generic-lambda.cpp =================================================================== --- /dev/null +++ clang/test/SemaTemplate/generic-lambda.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s + +// expected-no-diagnostics + +template <class T, class U> constexpr bool is_same_v = false; +template <class T> constexpr bool is_same_v<T, T> = true; +template <class T, class U> +concept is_same = is_same_v<T, U>; + +template <class T> struct X {}; +template <class T, class U> +concept C1 = is_same<T, X<U>>; + +template <class T1> X<X<X<T1>>> t1() { + return []<class T2>(T2) -> X<X<T2>> { + struct S { + static X<X<T2>> f() { + return []<class T3>(T3) -> X<T3> { + static_assert(is_same<T2, X<T1>>); + static_assert(is_same<T3, X<T2>>); + return X<T3>(); + }(X<T2>()); + } + }; + return S::f(); + }(X<T1>()); +}; +template X<X<X<int>>> t1<int>(); + +#if 0 // FIXME: crashes +template<class T1> auto t2() { + return []<class T2>(T2) { + struct S { + static auto f() { + return []<class T3>(T3) { + static_assert(is_same<T2, X<T1>>); + static_assert(is_same<T3, X<T2>>); + return X<T3>(); + }(X<T2>()); + } + }; + return S::f(); + }(X<T1>()); +}; +template auto t2<int>(); +static_assert(is_same<decltype(t2<int>()), X<X<X<int>>>>); + +template<class T1> C1<X<X<T1>>> auto t3() { + return []<C1<T1> T2>(T2) -> C1<X<T2>> auto { + struct S { + static auto f() { + return []<C1<T2> T3>(T3) -> C1<T3> auto { + return X<T3>(); + }(X<T2>()); + } + }; + return S::f(); + }(X<T1>()); +}; +template C1<X<X<int>>> auto t3<int>(); +static_assert(is_same<decltype(t3<int>()), X<X<X<int>>>>); +#endif Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -161,10 +161,9 @@ if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) break; - } else if (FunctionTemplateDecl *FunTmpl - = Function->getDescribedFunctionTemplate()) { - // Add the "injected" template arguments. - Result.addOuterTemplateArguments(FunTmpl->getInjectedTemplateArgs()); + } else if (Function->getDescribedFunctionTemplate()) { + assert(Result.getNumSubstitutedLevels() == 0 && + "Outer template not instantiated?"); } // If this is a friend declaration and it declares an entity at @@ -180,11 +179,8 @@ } } else if (CXXRecordDecl *Rec = dyn_cast<CXXRecordDecl>(Ctx)) { if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { - QualType T = ClassTemplate->getInjectedClassNameSpecialization(); - const TemplateSpecializationType *TST = - cast<TemplateSpecializationType>(Context.getCanonicalType(T)); - Result.addOuterTemplateArguments( - llvm::makeArrayRef(TST->getArgs(), TST->getNumArgs())); + assert(Result.getNumSubstitutedLevels() == 0 && + "Outer template not instantiated?"); if (ClassTemplate->isMemberSpecialization()) break; }
Index: clang/test/SemaTemplate/generic-lambda.cpp =================================================================== --- /dev/null +++ clang/test/SemaTemplate/generic-lambda.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s + +// expected-no-diagnostics + +template <class T, class U> constexpr bool is_same_v = false; +template <class T> constexpr bool is_same_v<T, T> = true; +template <class T, class U> +concept is_same = is_same_v<T, U>; + +template <class T> struct X {}; +template <class T, class U> +concept C1 = is_same<T, X<U>>; + +template <class T1> X<X<X<T1>>> t1() { + return []<class T2>(T2) -> X<X<T2>> { + struct S { + static X<X<T2>> f() { + return []<class T3>(T3) -> X<T3> { + static_assert(is_same<T2, X<T1>>); + static_assert(is_same<T3, X<T2>>); + return X<T3>(); + }(X<T2>()); + } + }; + return S::f(); + }(X<T1>()); +}; +template X<X<X<int>>> t1<int>(); + +#if 0 // FIXME: crashes +template<class T1> auto t2() { + return []<class T2>(T2) { + struct S { + static auto f() { + return []<class T3>(T3) { + static_assert(is_same<T2, X<T1>>); + static_assert(is_same<T3, X<T2>>); + return X<T3>(); + }(X<T2>()); + } + }; + return S::f(); + }(X<T1>()); +}; +template auto t2<int>(); +static_assert(is_same<decltype(t2<int>()), X<X<X<int>>>>); + +template<class T1> C1<X<X<T1>>> auto t3() { + return []<C1<T1> T2>(T2) -> C1<X<T2>> auto { + struct S { + static auto f() { + return []<C1<T2> T3>(T3) -> C1<T3> auto { + return X<T3>(); + }(X<T2>()); + } + }; + return S::f(); + }(X<T1>()); +}; +template C1<X<X<int>>> auto t3<int>(); +static_assert(is_same<decltype(t3<int>()), X<X<X<int>>>>); +#endif Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -161,10 +161,9 @@ if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) break; - } else if (FunctionTemplateDecl *FunTmpl - = Function->getDescribedFunctionTemplate()) { - // Add the "injected" template arguments. - Result.addOuterTemplateArguments(FunTmpl->getInjectedTemplateArgs()); + } else if (Function->getDescribedFunctionTemplate()) { + assert(Result.getNumSubstitutedLevels() == 0 && + "Outer template not instantiated?"); } // If this is a friend declaration and it declares an entity at @@ -180,11 +179,8 @@ } } else if (CXXRecordDecl *Rec = dyn_cast<CXXRecordDecl>(Ctx)) { if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { - QualType T = ClassTemplate->getInjectedClassNameSpecialization(); - const TemplateSpecializationType *TST = - cast<TemplateSpecializationType>(Context.getCanonicalType(T)); - Result.addOuterTemplateArguments( - llvm::makeArrayRef(TST->getArgs(), TST->getNumArgs())); + assert(Result.getNumSubstitutedLevels() == 0 && + "Outer template not instantiated?"); if (ClassTemplate->isMemberSpecialization()) break; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits