diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index ca9c00a1473bbd..d454a7ff2f8cf4 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -162,6 +162,7 @@ LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P20 LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") LANGOPT(RelaxedTemplateTemplateArgs, 1, 1, "C++17 relaxed matching of template template arguments") LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features") +LANGOPT(RetainSubstTemplateTypeParmTypeAstNodes, 1, 0, "retain SubstTemplateTypeParmType nodes in the AST's representation of alias template specializations") LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") LANGOPT(PointerAuthCalls , 1, 0, "function pointer authentication") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 75320cafaefa5f..6df3a6a5943a97 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3455,6 +3455,12 @@ defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-arg PosFlag, NegFlag, BothFlags<[], [ClangOption], " C++17 relaxed template template argument matching">>; +defm retain_subst_template_type_parm_type_ast_nodes : BoolFOption<"retain-subst-template-type-parm-type-ast-nodes", + LangOpts<"RetainSubstTemplateTypeParmTypeAstNodes">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [], " retain SubstTemplateTypeParmType nodes in the AST's representation" + " of alias template specializations">>; defm sized_deallocation : BoolFOption<"sized-deallocation", LangOpts<"SizedDeallocation">, Default, PosFlag, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 29e7978ba5b1f8..876921a6b311d4 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3332,10 +3332,16 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, if (Pattern->isInvalidDecl()) return QualType(); - // Only substitute for the innermost template argument list. + // Only substitute for the innermost template argument list. NOTE: Some + // external resugarers rely on leaving a Subst* node here. Make the + // substitution non-final in that case. Note that these external resugarers + // will still miss some information in this representation, because we don't + // provide enough context in the Subst* nodes in order to tell different + // template type alias specializations apart. MultiLevelTemplateArgumentList TemplateArgLists; - TemplateArgLists.addOuterTemplateArguments(Template, SugaredConverted, - /*Final=*/true); + TemplateArgLists.addOuterTemplateArguments( + Template, SugaredConverted, + /*Final=*/!getLangOpts().RetainSubstTemplateTypeParmTypeAstNodes); TemplateArgLists.addOuterRetainedLevels( AliasTemplate->getTemplateParameters()->getDepth()); diff --git a/clang/test/AST/ast-dump-retain-subst-template-type-parm-type-ast-nodes.cpp b/clang/test/AST/ast-dump-retain-subst-template-type-parm-type-ast-nodes.cpp new file mode 100644 index 00000000000000..97dc983e2436cb --- /dev/null +++ b/clang/test/AST/ast-dump-retain-subst-template-type-parm-type-ast-nodes.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -fretain-subst-template-type-parm-type-ast-nodes -ast-dump -ast-dump-filter=dump %s | FileCheck -strict-whitespace %s + +namespace t1 { +template using X = T; +using dump = X; + +// CHECK-LABEL: Dumping t1::dump: +// CHECK-NEXT: TypeAliasDecl +// CHECK-NEXT: `-ElaboratedType +// CHECK-NEXT: `-TemplateSpecializationType +// CHECK-NEXT: |-name: 'X':'t1::X' qualified +// CHECK-NEXT: | `-TypeAliasTemplateDecl +// CHECK-NEXT: |-TemplateArgument +// CHECK-NEXT: | `-BuiltinType {{.+}} 'int' +// CHECK-NEXT: `-SubstTemplateTypeParmType 0x{{[0-9a-f]+}} 'int' sugar class depth 0 index 0 T +// CHECK-NEXT: |-TypeAliasTemplate {{.+}} 'X' +// CHECK-NEXT: `-BuiltinType {{.+}} 'int' +} // namespace t1