https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/94740
>From 2f60e51f2017e4448047f64983b2f22cdb67e816 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 7 Jun 2024 18:08:10 +0800 Subject: [PATCH 1/5] [Clang] Substitute for the type aliases inside of a CTAD guide --- clang/docs/ReleaseNotes.rst | 1 + clang/lib/Sema/SemaTemplate.cpp | 96 +++++++++++++++++-- .../SemaTemplate/nested-deduction-guides.cpp | 70 ++++++++++++++ 3 files changed, 160 insertions(+), 7 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0c700d23257bf..3f6d040b0ddf1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -823,6 +823,7 @@ Bug Fixes to C++ Support differering by their constraints when only one of these function was variadic. - Fix a crash when a variable is captured by a block nested inside a lambda. (Fixes #GH93625). - Fixed a type constraint substitution issue involving a generic lambda expression. (#GH93821) +- Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 40a759ea330de..1e921dd26bd7d 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2220,23 +2220,101 @@ namespace { class ExtractTypeForDeductionGuide : public TreeTransform<ExtractTypeForDeductionGuide> { llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs; + ClassTemplateDecl *NestedPattern; + const MultiLevelTemplateArgumentList *OuterInstantiationArgs; public: typedef TreeTransform<ExtractTypeForDeductionGuide> Base; ExtractTypeForDeductionGuide( Sema &SemaRef, - llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) - : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {} + llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs, + ClassTemplateDecl *NestedPattern, + const MultiLevelTemplateArgumentList *OuterInstantiationArgs) + : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs), + NestedPattern(NestedPattern), + OuterInstantiationArgs(OuterInstantiationArgs) {} TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); } + bool mightReferToOuterTemplateParameters(TypedefNameDecl *Typedef) { + if (!NestedPattern) + return false; + + static auto WalkUp = [](DeclContext *DC, DeclContext *TargetDC) { + if (DC == TargetDC) + return true; + while (!DC->isTranslationUnit()) { + if (DC->Equals(TargetDC)) + return true; + DC = DC->getParent(); + } + return false; + }; + + if (WalkUp(Typedef->getDeclContext(), NestedPattern->getTemplatedDecl())) + return true; + if (WalkUp(NestedPattern->getTemplatedDecl(), Typedef->getDeclContext())) + return true; + return false; + } + + QualType + RebuildTemplateSpecializationType(TemplateName Template, + SourceLocation TemplateNameLoc, + TemplateArgumentListInfo &TemplateArgs) { + if (!OuterInstantiationArgs || + !isa_and_present<TypeAliasTemplateDecl>(Template.getAsTemplateDecl())) + return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc, + TemplateArgs); + + auto *TATD = cast<TypeAliasTemplateDecl>(Template.getAsTemplateDecl()); + auto *Pattern = TATD; + while (Pattern->getInstantiatedFromMemberTemplate()) + Pattern = Pattern->getInstantiatedFromMemberTemplate(); + if (!mightReferToOuterTemplateParameters(Pattern->getTemplatedDecl())) + return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc, + TemplateArgs); + + Decl *NewD = SemaRef.SubstDecl( + TATD, SemaRef.getASTContext().getTranslationUnitDecl(), + *OuterInstantiationArgs); + if (!NewD) + return QualType(); + + auto *NewTATD = cast<TypeAliasTemplateDecl>(NewD); + MaterializedTypedefs.push_back(NewTATD->getTemplatedDecl()); + + return Base::RebuildTemplateSpecializationType( + TemplateName(NewTATD), TemplateNameLoc, TemplateArgs); + } + QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) { ASTContext &Context = SemaRef.getASTContext(); TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl(); TypedefNameDecl *Decl = OrigDecl; // Transform the underlying type of the typedef and clone the Decl only if // the typedef has a dependent context. - if (OrigDecl->getDeclContext()->isDependentContext()) { + bool InDependentContext = OrigDecl->getDeclContext()->isDependentContext(); + + // A typedef/alias Decl within the NestedPattern may reference the outer + // template parameters. They're substituted with corresponding instantiation + // arguments here and in RebuildTemplateSpecializationType() above. + // Otherwise, we would have a CTAD guide with "dangling" template + // parameters. + // For example, + // template <class T> struct Outer { + // using Alias = S<T>; + // template <class U> struct Inner { + // Inner(Alias); + // }; + // }; + if (OuterInstantiationArgs && InDependentContext) { + Decl = cast_if_present<TypedefNameDecl>(SemaRef.SubstDecl( + OrigDecl, Context.getTranslationUnitDecl(), *OuterInstantiationArgs)); + if (!Decl) + return QualType(); + MaterializedTypedefs.push_back(Decl); + } else if (InDependentContext) { TypeLocBuilder InnerTLB; QualType Transformed = TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc()); @@ -2577,8 +2655,9 @@ struct ConvertConstructorToDeductionGuideTransform { // defined outside of the surrounding class template. That is T in the // above example. if (NestedPattern) { - NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs, - MaterializedTypedefs); + NewParam = transformFunctionTypeParam( + NewParam, OuterInstantiationArgs, MaterializedTypedefs, + /*TransformingOuterPatterns=*/true); if (!NewParam) return QualType(); } @@ -2630,7 +2709,8 @@ struct ConvertConstructorToDeductionGuideTransform { ParmVarDecl *transformFunctionTypeParam( ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args, - llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) { + llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs, + bool TransformingOuterPatterns = false) { TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo(); TypeSourceInfo *NewDI; if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) { @@ -2653,7 +2733,9 @@ struct ConvertConstructorToDeductionGuideTransform { // members of the current instantiations with the definitions of those // typedefs, avoiding triggering instantiation of the deduced type during // deduction. - NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs) + NewDI = ExtractTypeForDeductionGuide( + SemaRef, MaterializedTypedefs, NestedPattern, + TransformingOuterPatterns ? &Args : nullptr) .transform(NewDI); // Resolving a wording defect, we also inherit default arguments from the diff --git a/clang/test/SemaTemplate/nested-deduction-guides.cpp b/clang/test/SemaTemplate/nested-deduction-guides.cpp index 38410b93ead3b..ff432173200c7 100644 --- a/clang/test/SemaTemplate/nested-deduction-guides.cpp +++ b/clang/test/SemaTemplate/nested-deduction-guides.cpp @@ -16,3 +16,73 @@ using T = A<void>::B<int>; using Copy = decltype(copy); using Copy = A<void>::B<int>; + +namespace GH94614 { + +template <class, class> struct S {}; + +struct trouble_1 { +} constexpr t1; +struct trouble_2 { +} constexpr t2; +struct trouble_3 { +} constexpr t3; +struct trouble_4 { +} constexpr t4; +struct trouble_5 { +} constexpr t5; +struct trouble_6 { +} constexpr t6; +struct trouble_7 { +} constexpr t7; +struct trouble_8 { +} constexpr t8; +struct trouble_9 { +} constexpr t9; + +template <class U, class... T> struct Unrelated { + using Trouble = S<U, T...>; + + template <class... V> using Trouble2 = S<V..., T...>; +}; + +template <class T, class U> struct Outer { + using Trouble = S<U, T>; + + template <class V> using Trouble2 = S<V, T>; + + template <class V> using Trouble3 = S<U, T>; + + template <class V> struct Inner { + template <class W> struct Paranoid { + using Trouble4 = S<W, T>; + + template <class... X> using Trouble5 = S<X..., T>; + }; + + Inner(trouble_1, V v, Trouble trouble) {} + Inner(trouble_2, V v, Trouble2<V> trouble) {} + Inner(trouble_3, V v, Trouble3<V> trouble) {} + Inner(trouble_4, V v, Unrelated<U, T>::template Trouble2<V> trouble) {} + Inner(trouble_5, V v, Unrelated<U, T>::Trouble trouble) {} + Inner(trouble_6, V v, Unrelated<V, T>::Trouble trouble) {} + Inner(trouble_7, V v, Paranoid<V>::Trouble4 trouble) {} + Inner(trouble_8, V v, Paranoid<V>::template Trouble5<V> trouble) {} + template <class W> + Inner(trouble_9, V v, W w, Paranoid<V>::template Trouble5<W> trouble) {} + }; +}; + +S<int, char> s; + +Outer<char, int>::Inner _1(t1, 42, s); +Outer<char, int>::Inner _2(t2, 42, s); +Outer<char, int>::Inner _3(t3, 42, s); +Outer<char, int>::Inner _4(t4, 42, s); +Outer<char, int>::Inner _5(t5, 42, s); +Outer<char, int>::Inner _6(t6, 42, s); +Outer<char, int>::Inner _7(t7, 42, s); +Outer<char, int>::Inner _8(t8, 42, s); +Outer<char, int>::Inner _9(t9, 42, 24, s); + +} // namespace GH94614 >From 70b754ebebd8210c036e2db0901fcfbf6db422af Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 7 Jun 2024 18:50:16 +0800 Subject: [PATCH 2/5] Add prior 'typename's --- clang/test/SemaTemplate/nested-deduction-guides.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/test/SemaTemplate/nested-deduction-guides.cpp b/clang/test/SemaTemplate/nested-deduction-guides.cpp index ff432173200c7..913042810ae82 100644 --- a/clang/test/SemaTemplate/nested-deduction-guides.cpp +++ b/clang/test/SemaTemplate/nested-deduction-guides.cpp @@ -63,13 +63,13 @@ template <class T, class U> struct Outer { Inner(trouble_1, V v, Trouble trouble) {} Inner(trouble_2, V v, Trouble2<V> trouble) {} Inner(trouble_3, V v, Trouble3<V> trouble) {} - Inner(trouble_4, V v, Unrelated<U, T>::template Trouble2<V> trouble) {} - Inner(trouble_5, V v, Unrelated<U, T>::Trouble trouble) {} - Inner(trouble_6, V v, Unrelated<V, T>::Trouble trouble) {} - Inner(trouble_7, V v, Paranoid<V>::Trouble4 trouble) {} - Inner(trouble_8, V v, Paranoid<V>::template Trouble5<V> trouble) {} + Inner(trouble_4, V v, typename Unrelated<U, T>::template Trouble2<V> trouble) {} + Inner(trouble_5, V v, typename Unrelated<U, T>::Trouble trouble) {} + Inner(trouble_6, V v, typename Unrelated<V, T>::Trouble trouble) {} + Inner(trouble_7, V v, typename Paranoid<V>::Trouble4 trouble) {} + Inner(trouble_8, V v, typename Paranoid<V>::template Trouble5<V> trouble) {} template <class W> - Inner(trouble_9, V v, W w, Paranoid<V>::template Trouble5<W> trouble) {} + Inner(trouble_9, V v, W w, typename Paranoid<V>::template Trouble5<W> trouble) {} }; }; >From f5a196a61b71ce6a002f2a25c26670d7d804cca1 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 7 Jun 2024 21:03:52 +0800 Subject: [PATCH 3/5] Give up if we exit a RecordDecl --- clang/lib/Sema/SemaTemplate.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 1e921dd26bd7d..baf2f12bcbb07 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2236,14 +2236,16 @@ class ExtractTypeForDeductionGuide TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); } + /// Returns true if it's safe to substitute \p Typedef with + /// \p OuterInstantiationArgs. bool mightReferToOuterTemplateParameters(TypedefNameDecl *Typedef) { if (!NestedPattern) return false; static auto WalkUp = [](DeclContext *DC, DeclContext *TargetDC) { - if (DC == TargetDC) + if (DC->Equals(TargetDC)) return true; - while (!DC->isTranslationUnit()) { + while (DC->isRecord()) { if (DC->Equals(TargetDC)) return true; DC = DC->getParent(); >From fe4a7cc2add1e22bce88782614d49d8ca7be6787 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 15 Jun 2024 20:29:24 +0800 Subject: [PATCH 4/5] [Clang] Instantiate local constexpr functions eagerly --- clang/docs/ReleaseNotes.rst | 1 + clang/lib/Sema/SemaExpr.cpp | 3 ++- .../SemaTemplate/instantiate-local-class.cpp | 24 +++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cc785d060901a..a5c8a00abd4c8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -848,6 +848,7 @@ Bug Fixes to C++ Support (#GH88081), (#GH89496), (#GH90669) and (#GH91633). - Fixed handling of brace ellison when building deduction guides. (#GH64625), (#GH83368). - Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614). +- Clang now instantiates local constexpr functions eagerly for constant evaluators. (#GH35052), (#GH94849) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 99a8704298314..88e9b2b00f84d 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18112,7 +18112,8 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, if (FirstInstantiation || TSK != TSK_ImplicitInstantiation || Func->isConstexpr()) { - if (isa<CXXRecordDecl>(Func->getDeclContext()) && + if (!Func->isConstexpr() && + isa<CXXRecordDecl>(Func->getDeclContext()) && cast<CXXRecordDecl>(Func->getDeclContext())->isLocalClass() && CodeSynthesisContexts.size()) PendingLocalImplicitInstantiations.push_back( diff --git a/clang/test/SemaTemplate/instantiate-local-class.cpp b/clang/test/SemaTemplate/instantiate-local-class.cpp index 47591045fd26e..7eee131e28d60 100644 --- a/clang/test/SemaTemplate/instantiate-local-class.cpp +++ b/clang/test/SemaTemplate/instantiate-local-class.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -verify -std=c++11 %s // RUN: %clang_cc1 -verify -std=c++11 -fdelayed-template-parsing %s +// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s template<typename T> void f0() { @@ -509,3 +510,26 @@ namespace LambdaInDefaultMemberInitializer { } template void f<int>(); } + +#if __cplusplus >= 201703L +namespace GH35052 { + +template <typename F> constexpr int func(F f) { + if constexpr (f(1UL)) { + return 1; + } + return 0; +} + +int main() { + auto predicate = [](auto v) /*implicit constexpr*/ -> bool { + return v == 1; + }; + + static_assert(predicate(1)); + return func(predicate); +} + +} // namespace GH35052 + +#endif >From c553fd0db2cad16b3b7b68f3459b0c26510ecd21 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 29 Jun 2024 17:47:18 +0800 Subject: [PATCH 5/5] Address review feedback --- clang/lib/Sema/SemaTemplate.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 7bc46f9efb8ca..15c8385dc1fa0 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2310,7 +2310,8 @@ class ExtractTypeForDeductionGuide // Inner(Alias); // }; // }; - if (OuterInstantiationArgs && InDependentContext) { + if (OuterInstantiationArgs && InDependentContext && + TL.getTypePtr()->isInstantiationDependentType()) { Decl = cast_if_present<TypedefNameDecl>(SemaRef.SubstDecl( OrigDecl, Context.getTranslationUnitDecl(), *OuterInstantiationArgs)); if (!Decl) @@ -2667,7 +2668,8 @@ struct ConvertConstructorToDeductionGuideTransform { // defined at the class template and the constructor. In this example, // they're U and V, respectively. NewParam = - transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs); + transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs, + /*TransformingOuterPatterns=*/false); if (!NewParam) return QualType(); ParamTypes.push_back(NewParam->getType()); @@ -2712,7 +2714,7 @@ struct ConvertConstructorToDeductionGuideTransform { ParmVarDecl *transformFunctionTypeParam( ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args, llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs, - bool TransformingOuterPatterns = false) { + bool TransformingOuterPatterns) { TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo(); TypeSourceInfo *NewDI; if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits