Index: include/clang/AST/PrettyPrinter.h =================================================================== --- include/clang/AST/PrettyPrinter.h +++ include/clang/AST/PrettyPrinter.h @@ -50,7 +50,8 @@ UseVoidForZeroParams(!LO.CPlusPlus), TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), - IncludeNewlines(true), MSVCFormatting(false) { } + IncludeNewlines(true), MSVCFormatting(false), + PrintTemplateTypesWithTypedefs(LO.EmitTypedefNamesInTemplateTypes) { } /// \brief Adjust this printing policy for cases where it's known that /// we're printing C++ code (for instance, if AST dumping reaches a @@ -200,6 +201,9 @@ /// prints anonymous namespaces as `anonymous namespace' and does not insert /// spaces after template arguments. bool MSVCFormatting : 1; + + /// \brief When true, print template type arguments without removing typedefs. + unsigned PrintTemplateTypesWithTypedefs : 1; }; } // end namespace clang Index: include/clang/AST/TemplateBase.h =================================================================== --- include/clang/AST/TemplateBase.h +++ include/clang/AST/TemplateBase.h @@ -106,6 +106,9 @@ struct TV { unsigned Kind; uintptr_t V; + // This is the type that most closely resembles what is in the source. + // At the moment it is retaining typedefs, but not decltype or typeof. + uintptr_t DisplayType; }; union { struct DA DeclArg; @@ -119,12 +122,21 @@ public: /// \brief Construct an empty, invalid template argument. - constexpr TemplateArgument() : TypeOrValue({Null, 0}) {} + constexpr TemplateArgument() : TypeOrValue({Null, 0, 0}) {} /// \brief Construct a template type argument. TemplateArgument(QualType T, bool isNullPtr = false) { TypeOrValue.Kind = isNullPtr ? NullPtr : Type; + TypeOrValue.V = TypeOrValue.DisplayType = + reinterpret_cast(T.getAsOpaquePtr()); + } + + /// Construct a template type argument and record the display type. + TemplateArgument(QualType T, QualType DisplayType, bool isNullPtr = false) { + TypeOrValue.Kind = isNullPtr ? NullPtr : Type; TypeOrValue.V = reinterpret_cast(T.getAsOpaquePtr()); + TypeOrValue.DisplayType = + reinterpret_cast(DisplayType.getAsOpaquePtr()); } /// \brief Construct a template argument that refers to a @@ -237,6 +249,12 @@ return QualType::getFromOpaquePtr(reinterpret_cast(TypeOrValue.V)); } + /// Retrieve the display type for a type template argument. + QualType getAsDisplayType() const { + assert(getKind() == Type && "Unexpected kind"); + return QualType::getFromOpaquePtr(reinterpret_cast(TypeOrValue.DisplayType)); + } + /// \brief Retrieve the declaration for a declaration non-type /// template argument. ValueDecl *getAsDecl() const { Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -843,6 +843,10 @@ QualType getCanonicalType() const; + /// Returns the type that should be used in contexts that require + /// representation that closely resembles what is written in the source. + QualType getDisplayType(const ASTContext &Context) const; + /// \brief Return this type with all of the instance-specific qualifiers /// removed, but without removing any qualifiers that may have been applied /// through typedefs. Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -260,6 +260,11 @@ LANGOPT(ApplePragmaPack, 1, 0, "Apple gcc-compatible #pragma pack handling") +// Emit template type names with typedef names in the template parameters, +// when recording them in DWARF, rather than the canonical type. +LANGOPT(EmitTypedefNamesInTemplateTypes, 1, 0, + "emit typedef names in template types (DWARF)") + LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST") LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan " Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1381,6 +1381,14 @@ HelpText<"Emit macro debug information">; def fno_debug_macro : Flag<["-"], "fno-debug-macro">, Group, Flags<[CoreOption]>, HelpText<"Do not emit macro debug information">; +def femit_typedefs_in_template_types : + Flag<["-"], "femit-typedefs-in-template-types">, Group, + Flags<[CC1Option, HelpHidden]>, + HelpText<"Preserve typedef names in debug info for template types">; +def fno_emit_typedefs_in_template_types : + Flag<["-"], "fno-emit-typedefs-in-template-types">, Group, + Flags<[CC1Option, HelpHidden]>, + HelpText<"Use canonical type names in debug info for template types">; def fstrict_aliasing : Flag<["-"], "fstrict-aliasing">, Group, Flags<[DriverOption, CoreOption]>; def fstrict_enums : Flag<["-"], "fstrict-enums">, Group, Flags<[CC1Option]>, Index: lib/AST/TemplateBase.cpp =================================================================== --- lib/AST/TemplateBase.cpp +++ lib/AST/TemplateBase.cpp @@ -386,7 +386,10 @@ case Type: { PrintingPolicy SubPolicy(Policy); SubPolicy.SuppressStrongLifetime = true; - getAsType().print(Out, SubPolicy); + if (SubPolicy.PrintTemplateTypesWithTypedefs) + getAsDisplayType().print(Out, SubPolicy); + else + getAsType().print(Out, SubPolicy); break; } Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -255,6 +255,24 @@ } } +// Returns a type that removes decltype and typeof but preserves typedef types +// as well as all qualifers that are not provided by the typedef types +// themselves. The goal is to provide a type that looks reasonably close to +// what is in the source. +QualType QualType::getDisplayType(const ASTContext &Context) const { + QualifierCollector Qs; + + QualType Cur = *this; + while (true) { + // Collect the qualifiers in Qs as we are desugaring the type + const Type *CurTy = Qs.strip(Cur); + QualType Desugar = Cur.getSingleStepDesugaredType(Context); + if (Cur == Desugar || CurTy->getTypeClass() == Type::Typedef) + return Qs.apply(Context, CurTy); + Cur = Desugar; + } +} + SplitQualType QualType::getSplitUnqualifiedTypeImpl(QualType type) { SplitQualType split = type.split(); Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -2833,6 +2833,12 @@ CmdArgs.push_back("-generate-type-units"); } + // PS4 prefers to keep typedef names in template parameters. + if (Args.hasFlag(options::OPT_femit_typedefs_in_template_types, + options::OPT_fno_emit_typedefs_in_template_types, + DebuggerTuning == llvm::DebuggerKind::SCE)) + CmdArgs.push_back("-femit-typedefs-in-template-types"); + RenderDebugInfoCompressionArgs(Args, CmdArgs, D); bool UseSeparateSections = isUseSeparateSections(Triple); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2386,6 +2386,11 @@ case 3: Opts.setStackProtector(LangOptions::SSPReq); break; } + // Whether to emit template type names while preserving typedef names in the + // template parameter types. + Opts.EmitTypedefNamesInTemplateTypes = + Args.hasArg(OPT_femit_typedefs_in_template_types); + // Parse -fsanitize= arguments. parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ), Diags, Opts.Sanitize); Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -4165,6 +4165,8 @@ if (CheckTemplateArgument(Param, TSI)) return true; + QualType DisplayArgType = ArgType.getDisplayType(Context); + // Add the converted template type argument. ArgType = Context.getCanonicalType(ArgType); @@ -4179,7 +4181,7 @@ ArgType = Context.getQualifiedType(ArgType, Qs); } - Converted.push_back(TemplateArgument(ArgType)); + Converted.push_back(TemplateArgument(ArgType, DisplayArgType)); return false; } Index: test/CodeGen/debug-template-types.cpp =================================================================== --- test/CodeGen/debug-template-types.cpp +++ test/CodeGen/debug-template-types.cpp @@ -0,0 +1,94 @@ +// RUN: %clang_cc1 -debug-info-kind=standalone -fgnu-keywords -std=c++11 -femit-typedefs-in-template-types -emit-llvm %s -o - | FileCheck %s + +// With -femit-typedefs-in-template-types, template parameter type names should +// not be stripped of typedef names. This test tries some more complicated +// cases to check that we don't drop qualifiers and attach them correctly to +// the typename. + + +class A +{ +public: + int i; + A(){} +}; + +typedef A myA; + +myA a1; + +volatile decltype(a1) a; + +template +class B +{ + T t; +}; + +// See that we collect all the qualifiers while finding the topmost typedef +B b; +// CHECK-DAG: !DICompositeType(tag: DW_TAG_class_type, name: "B" + +class C +{ +public: + C(){} + C(volatile C&){} +}; + +typedef volatile C myC; + +const myC c; + +template +class D +{ + T t; +}; + +// See that we don't add the typedef's qualifiers to the type name +D d; +// CHECK-DAG: !DICompositeType(tag: DW_TAG_class_type, name: "D" + + +typedef int func(); +typedef float myFloat; + +template +class F +{ + void foo(T...); +}; + +// See that we deal with parameter packs +F f; +// CHECK-DAG: !DICompositeType(tag: DW_TAG_class_type, name: "F" + + +template +class G +{ + T t; + S s; +}; + +// See that we display parameters that are template types and default +// parameters correctly +G> g; +// CHECK-DAG: !DICompositeType(tag: DW_TAG_class_type, name: "G, myA>" + +template +T foo(T t) +{ + return t; +} + +template <> +const myC foo(const myC) +{ + myC c; + return c; +} + +// See that we do display the typedef names for function types also +// CHECK-DAG: !DISubprogram(name: "foo" Index: test/Driver/clang_f_opts.c =================================================================== --- test/Driver/clang_f_opts.c +++ test/Driver/clang_f_opts.c @@ -496,3 +496,10 @@ // RUN: %clang -### -S -fno-allow-editor-placeholders %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ALLOW-PLACEHOLDERS %s // CHECK-ALLOW-PLACEHOLDERS: -fallow-editor-placeholders // CHECK-NO-ALLOW-PLACEHOLDERS-NOT: -fallow-editor-placeholders + +// RUN: %clang -### -femit-typedefs-in-template-types %s 2>&1 | FileCheck -check-prefix=CHECK-EMIT-TYPEDEF-NAMES %s +// RUN: %clang -### -fno-emit-typedefs-in-template-types %s 2>&1 | FileCheck -check-prefix=CHECK-NO-EMIT-TYPEDEF-NAMES %s +// RUN: %clang -### -gsce %s 2>&1 | FileCheck -check-prefix=CHECK-EMIT-TYPEDEF-NAMES %s +// RUN: %clang -### -ggdb %s 2>&1 | FileCheck -check-prefix=CHECK-NO-EMIT-TYPEDEF-NAMES %s +// CHECK-EMIT-TYPEDEF-NAMES: -femit-typedefs-in-template-types +// CHECK-NO-EMIT-TYPEDEF-NAMES-NOT: -femit-typedefs-in-template-types Index: test/Index/print-type.cpp =================================================================== --- test/Index/print-type.cpp +++ test/Index/print-type.cpp @@ -119,7 +119,9 @@ // CHECK: TemplateRef=Baz:9:8 [type=] [typekind=Invalid] [isPOD=0] // CHECK: IntegerLiteral= [type=int] [typekind=Int] [isPOD=1] // CHECK: TemplateRef=Foo:4:8 [type=] [typekind=Invalid] [isPOD=0] -// CHECK: FieldDecl=qux:29:38 (Definition) [type=Qux, outer::inner::Bar::FooType>] [typekind=Unexposed] [templateargs/4= [type=int] [typekind=Int] [type=char *] [typekind=Pointer] [type=Foo] [typekind=Unexposed] [type=outer::inner::Bar::FooType] [typekind=Typedef]] [canonicaltype=outer::Qux, int>] [canonicaltypekind=Record] [canonicaltemplateargs/4= [type=int] [typekind=Int] [type=char *] [typekind=Pointer] [type=outer::Foo] [typekind=Record] [type=int] [typekind=Int]] [isPOD=1] +// The expression in the next line is for -femit-typedefs-in-template-types +// which is the default on PS4. +// CHECK: FieldDecl=qux:29:38 (Definition) [type=Qux, outer::inner::Bar::FooType>] [typekind=Unexposed] [templateargs/4= [type=int] [typekind=Int] [type=char *] [typekind=Pointer] [type=Foo] [typekind=Unexposed] [type=outer::inner::Bar::FooType] [typekind=Typedef]] [canonicaltype=outer::Qux, {{int|outer::inner::Bar::FooType}}>] [canonicaltypekind=Record] [canonicaltemplateargs/4= [type=int] [typekind=Int] [type=char *] [typekind=Pointer] [type=outer::Foo] [typekind=Record] [type=int] [typekind=Int]] [isPOD=1] // CHECK: TemplateRef=Qux:12:8 [type=] [typekind=Invalid] [isPOD=0] // CHECK: TemplateRef=Foo:4:8 [type=] [typekind=Invalid] [isPOD=0] // CHECK: FunctionTemplate=tbar:36:3 [type=T (int)] [typekind=FunctionProto] [canonicaltype=type-parameter-0-0 (int)] [canonicaltypekind=FunctionProto] [resulttype=T] [resulttypekind=Unexposed] [isPOD=0]