Author: Younan Zhang Date: 2024-07-10T12:51:27+08:00 New Revision: 869ac4064861b7e644ae52a72fb80efacfd86462
URL: https://github.com/llvm/llvm-project/commit/869ac4064861b7e644ae52a72fb80efacfd86462 DIFF: https://github.com/llvm/llvm-project/commit/869ac4064861b7e644ae52a72fb80efacfd86462.diff LOG: [Clang][Sema] Substitute for the type aliases inside of a CTAD guide (#94740) Similar to the approach of handling nested class templates when building a CTAD guide, we substitute the template parameters of a type alias declaration with the instantiating template arguments in order to ensure the guide eventually doesn't reference any outer template parameters. For example, ```cpp template <class T> struct Outer { using Alias = S<T>; template <class U> struct Inner { Inner(Alias); }; }; ``` we used to retain the reference to T accidentally because the TreeTransform does nothing on type alias Decls by default. Fixes https://github.com/llvm/llvm-project/issues/94614 Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Sema/Template.h clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/test/SemaTemplate/nested-deduction-guides.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 2a2bff082ea7f..cd3a4c2b1be1a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1000,6 +1000,7 @@ Bug Fixes to C++ Support evaluated to an integer. (#GH96670). - Fixed a bug where references to lambda capture inside a ``noexcept`` specifier were not correctly instantiated. (#GH95735). +- Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index ce44aca797b0f..0340c23fd170d 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -711,6 +711,7 @@ enum class TemplateSubstitutionKind : char { VarTemplateSpecializationDecl *PrevDecl = nullptr); Decl *InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias); + Decl *InstantiateTypeAliasTemplateDecl(TypeAliasTemplateDecl *D); ClassTemplatePartialSpecializationDecl * InstantiateClassTemplatePartialSpecialization( ClassTemplateDecl *ClassTemplate, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 07b3f793b3a29..726de9172e1cf 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2178,23 +2178,110 @@ namespace { class ExtractTypeForDeductionGuide : public TreeTransform<ExtractTypeForDeductionGuide> { llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs; + ClassTemplateDecl *NestedPattern; + const MultiLevelTemplateArgumentList *OuterInstantiationArgs; + std::optional<TemplateDeclInstantiator> TypedefNameInstantiator; 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) { + if (OuterInstantiationArgs) + TypedefNameInstantiator.emplace( + SemaRef, SemaRef.getASTContext().getTranslationUnitDecl(), + *OuterInstantiationArgs); + } 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->Equals(TargetDC)) + return true; + while (DC->isRecord()) { + 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 = + TypedefNameInstantiator->InstantiateTypeAliasTemplateDecl(TATD); + 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 && + TL.getTypePtr()->isInstantiationDependentType()) { + Decl = cast_if_present<TypedefNameDecl>( + TypedefNameInstantiator->InstantiateTypedefNameDecl( + OrigDecl, /*IsTypeAlias=*/isa<TypeAliasDecl>(OrigDecl))); + if (!Decl) + return QualType(); + MaterializedTypedefs.push_back(Decl); + } else if (InDependentContext) { TypeLocBuilder InnerTLB; QualType Transformed = TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc()); @@ -2541,8 +2628,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(); } @@ -2550,7 +2638,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()); @@ -2594,7 +2683,8 @@ struct ConvertConstructorToDeductionGuideTransform { ParmVarDecl *transformFunctionTypeParam( ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args, - llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) { + llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs, + bool TransformingOuterPatterns) { TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo(); TypeSourceInfo *NewDI; if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) { @@ -2617,7 +2707,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/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 2e90f0c215b8d..88f6af80cbc55 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1096,8 +1096,8 @@ Decl *TemplateDeclInstantiator::VisitTypeAliasDecl(TypeAliasDecl *D) { return Typedef; } -Decl * -TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { +Decl *TemplateDeclInstantiator::InstantiateTypeAliasTemplateDecl( + TypeAliasTemplateDecl *D) { // Create a local instantiation scope for this type alias template, which // will contain the instantiations of the template parameters. LocalInstantiationScope Scope(SemaRef); @@ -1143,7 +1143,14 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { if (!PrevAliasTemplate) Inst->setInstantiatedFromMemberTemplate(D); - Owner->addDecl(Inst); + return Inst; +} + +Decl * +TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { + Decl *Inst = InstantiateTypeAliasTemplateDecl(D); + if (Inst) + Owner->addDecl(Inst); return Inst; } diff --git a/clang/test/SemaTemplate/nested-deduction-guides.cpp b/clang/test/SemaTemplate/nested-deduction-guides.cpp index 38410b93ead3b..30ede69e96e3f 100644 --- a/clang/test/SemaTemplate/nested-deduction-guides.cpp +++ b/clang/test/SemaTemplate/nested-deduction-guides.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -std=c++17 -verify %s -// expected-no-diagnostics template<typename T> struct A { template<typename U> struct B { @@ -16,3 +15,76 @@ 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, 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, typename 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); + +// Make sure we don't accidentally inject the TypedefNameDecl into the TU. +Trouble should_not_be_in_the_tu_decl; // expected-error {{unknown type name 'Trouble'}} + +} // namespace GH94614 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits