diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 386652d2efa9e..79c3df2e43eef 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -468,6 +468,7 @@ CODEGENOPT(CtorDtorReturnThis, 1, 0) /// Enables emitting Import Call sections on supported targets that can be used /// by the Windows kernel to enable import call optimization. CODEGENOPT(ImportCallOptimization, 1, 0) +CODEGENOPT(DebugTemplateParameterAsType, 1, 0) /// FIXME: Make DebugOptions its own top-level .def file. #include "DebugOptions.def" diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index d8123cc39fdc9..32a88d42dde49 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -9028,3 +9028,8 @@ def wasm_opt : Flag<["--"], "wasm-opt">, Group, HelpText<"Enable the wasm-opt optimizer (default)">, MarshallingInfoNegativeFlag>; +defm debug_template_parameter_as_type + : BoolFOption<"debug-template-parameter-as-type", + CodeGenOpts<"DebugTemplateParameterAsType">, + DefaultFalse, PosFlag, NegFlag, + BothFlags<[], [CC1Option]>>; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index db595796c067e..28b52894f1244 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1003,6 +1003,17 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { return DBuilder.createBasicType(BTName, Size, Encoding); } +llvm::DIType *CGDebugInfo::CreateType(const SubstTemplateTypeParmType *Ty, + llvm::DIFile *U) { + llvm::DIType *debugType = getOrCreateType(Ty->getReplacementType(), U); + llvm::DIScope *Scope = static_cast(U); + if (auto *Tty = + dyn_cast(Ty->getAssociatedDecl())) + Scope = CGDebugInfo::getOrCreateType(QualType(Tty->getTypeForDecl(), 0), U); + return DBuilder.createTemplateTypeParameterAsType( + Scope, Ty->getReplacedParameter()->getName(), debugType); +} + llvm::DIType *CGDebugInfo::CreateType(const BitIntType *Ty) { StringRef Name = Ty->isUnsigned() ? "unsigned _BitInt" : "_BitInt"; @@ -1240,8 +1251,9 @@ CGDebugInfo::getOrCreateRecordFwdDecl(const RecordType *Ty, Identifier); if (CGM.getCodeGenOpts().DebugFwdTemplateParams) if (auto *TSpecial = dyn_cast(RD)) - DBuilder.replaceArrays(RetTy, llvm::DINodeArray(), - CollectCXXTemplateParams(TSpecial, DefUnit)); + DBuilder.replaceArrays( + RetTy, llvm::DINodeArray(), + CollectCXXTemplateParams(TSpecial, DefUnit, RetTy)); ReplaceMap.emplace_back( std::piecewise_construct, std::make_tuple(Ty), std::make_tuple(static_cast(RetTy))); @@ -2275,12 +2287,14 @@ void CGDebugInfo::CollectCXXBasesAux( llvm::DINodeArray CGDebugInfo::CollectTemplateParams(std::optional OArgs, - llvm::DIFile *Unit) { + llvm::DIFile *Unit, + llvm::DICompositeType *RealDecl) { if (!OArgs) return llvm::DINodeArray(); TemplateArgs &Args = *OArgs; SmallVector TemplateParams; for (unsigned i = 0, e = Args.Args.size(); i != e; ++i) { + const TemplateArgument &TA = Args.Args[i]; StringRef Name; const bool defaultParameter = TA.getIsDefaulted(); @@ -2289,10 +2303,19 @@ CGDebugInfo::CollectTemplateParams(std::optional OArgs, switch (TA.getKind()) { case TemplateArgument::Type: { - llvm::DIType *TTy = getOrCreateType(TA.getAsType(), Unit); - TemplateParams.push_back(DBuilder.createTemplateTypeParameter( - TheCU, Name, TTy, defaultParameter)); - + if (CGM.getCodeGenOpts().DebugTemplateParameterAsType) { + llvm::DIType *debugType = getOrCreateType(TA.getAsType(), Unit); + llvm::DIScope *Scope = RealDecl != nullptr + ? static_cast(RealDecl) + : static_cast(Unit); + llvm::DIType *TemplateType = + DBuilder.createTemplateTypeParameterAsType(Scope, Name, debugType); + TemplateParams.push_back(TemplateType); + } else { + llvm::DIType *TTy = getOrCreateType(TA.getAsType(), Unit); + TemplateParams.push_back(DBuilder.createTemplateTypeParameter( + TheCU, Name, TTy, defaultParameter)); + } } break; case TemplateArgument::Integral: { llvm::DIType *TTy = getOrCreateType(TA.getIntegralType(), Unit); @@ -2457,9 +2480,10 @@ llvm::DINodeArray CGDebugInfo::CollectVarTemplateParams(const VarDecl *VL, return CollectTemplateParams(GetTemplateArgs(VL), Unit); } -llvm::DINodeArray CGDebugInfo::CollectCXXTemplateParams(const RecordDecl *RD, - llvm::DIFile *Unit) { - return CollectTemplateParams(GetTemplateArgs(RD), Unit); +llvm::DINodeArray +CGDebugInfo::CollectCXXTemplateParams(const RecordDecl *RD, llvm::DIFile *Unit, + llvm::DICompositeType *RealDecl) { + return CollectTemplateParams(GetTemplateArgs(RD), Unit, RealDecl); } llvm::DINodeArray CGDebugInfo::CollectBTFDeclTagAnnotations(const Decl *D) { @@ -3611,7 +3635,8 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor( /*Scope=*/TrapSP, /*InlinedAt=*/TrapLocation); } -static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) { +static QualType UnwrapTypeForDebugInfo(QualType T, const CodeGenModule &CGM) { + const ASTContext &C = CGM.getContext(); Qualifiers Quals; do { Qualifiers InnerQuals = T.getLocalQualifiers(); @@ -3664,6 +3689,8 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) { T = cast(T)->getUnderlyingType(); break; case Type::SubstTemplateTypeParm: + if (CGM.getCodeGenOpts().DebugTemplateParameterAsType) + return C.getQualifiedType(T.getTypePtr(), Quals); T = cast(T)->getReplacementType(); break; case Type::Auto: @@ -3690,7 +3717,7 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) { } llvm::DIType *CGDebugInfo::getTypeOrNull(QualType Ty) { - assert(Ty == UnwrapTypeForDebugInfo(Ty, CGM.getContext())); + assert(Ty == UnwrapTypeForDebugInfo(Ty, CGM)); auto It = TypeCache.find(Ty.getAsOpaquePtr()); if (It != TypeCache.end()) { // Verify that the debug info still exists. @@ -3729,7 +3756,7 @@ llvm::DIType *CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile *Unit) { }); // Unwrap the type as needed for debug information. - Ty = UnwrapTypeForDebugInfo(Ty, CGM.getContext()); + Ty = UnwrapTypeForDebugInfo(Ty, CGM); if (auto *T = getTypeOrNull(Ty)) return T; @@ -3866,6 +3893,9 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { case Type::Decltype: case Type::PackIndexing: case Type::UnaryTransform: + if (Ty->getTypeClass() == Type::SubstTemplateTypeParm && + CGM.getCodeGenOpts().DebugTemplateParameterAsType) + return CreateType(cast(Ty), Unit); break; } @@ -3990,8 +4020,9 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) { TypeCache[QualType(Ty, 0).getAsOpaquePtr()].reset(RealDecl); if (const auto *TSpecial = dyn_cast(RD)) - DBuilder.replaceArrays(RealDecl, llvm::DINodeArray(), - CollectCXXTemplateParams(TSpecial, DefUnit)); + DBuilder.replaceArrays( + RealDecl, llvm::DINodeArray(), + CollectCXXTemplateParams(TSpecial, DefUnit, RealDecl)); return RealDecl; } diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 38f73eca561b7..daf256852b079 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -228,6 +228,8 @@ class CGDebugInfo { llvm::DIType *CreateType(const MemberPointerType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const AtomicType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const PipeType *Ty, llvm::DIFile *F); + llvm::DIType *CreateType(const SubstTemplateTypeParmType *Ty, + llvm::DIFile *F); /// Get enumeration type. llvm::DIType *CreateEnumType(const EnumType *Ty); llvm::DIType *CreateTypeDefinition(const EnumType *Ty); @@ -303,8 +305,9 @@ class CGDebugInfo { llvm::ArrayRef Args; }; /// A helper function to collect template parameters. - llvm::DINodeArray CollectTemplateParams(std::optional Args, - llvm::DIFile *Unit); + llvm::DINodeArray + CollectTemplateParams(std::optional Args, llvm::DIFile *Unit, + llvm::DICompositeType *RealDecl = nullptr); /// A helper function to collect debug info for function template /// parameters. llvm::DINodeArray CollectFunctionTemplateParams(const FunctionDecl *FD, @@ -321,8 +324,9 @@ class CGDebugInfo { /// A helper function to collect debug info for template /// parameters. - llvm::DINodeArray CollectCXXTemplateParams(const RecordDecl *TS, - llvm::DIFile *F); + llvm::DINodeArray + CollectCXXTemplateParams(const RecordDecl *TS, llvm::DIFile *F, + llvm::DICompositeType *RealDecl = nullptr); /// A helper function to collect debug info for btf_decl_tag annotations. llvm::DINodeArray CollectBTFDeclTagAnnotations(const Decl *D); diff --git a/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp b/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp new file mode 100644 index 0000000000000..f7d624af1a557 --- /dev/null +++ b/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited -fdebug-template-parameter-as-type -triple x86_64-apple-darwin %s -o - | FileCheck %s + + +template +struct TClass { + TClass(); + void foo(); + T val_; + int val2_; +}; + +template +void TClass::foo() { + T tVar = 1; + T* pT = &tVar; + tVar++; +} + +template +T bar(T tp) { + return tp; +} + +int main () { + TClass a; + a.val2_ = 3; + a.foo(); + auto A = bar(42); + TClass b; + return 0; +} + +// CHECK: [[INT:![0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TClass" +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val_",{{.*}}baseType: [[TPARAM:![0-9]+]] +// CHECK: [[TPARAM]] = !DIDerivedType(tag: DW_TAG_template_type_parameter, name: "T", {{.*}}baseType: [[INT]]) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val2_",{{.*}}baseType: [[INT]] + +// CHECK: !DILocalVariable(name: "A",{{.*}}type: [[TPARAM3:![0-9]+]]) +// CHECK: [[TPARAM3]] = !DIDerivedType(tag: DW_TAG_template_type_parameter, name: "T", {{.*}}baseType: [[INT]]) + +// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TClass" +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val_",{{.*}}baseType: [[TPARAM2:![0-9]+]] +// CHECK: [[TPARAM2]] = !DIDerivedType(tag: DW_TAG_template_type_parameter, name: "T", {{.*}}baseType: [[DOUBLE:![0-9]+]]) +// CHECK: [[DOUBLE]] = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) + +// CHECK: distinct !DISubprogram(name: "foo" +// CHECK: !DILocalVariable(name: "tVar",{{.*}}type: [[TPARAM]]) +// CHECK: !DILocalVariable(name: "pT",{{.*}}type: [[TPTR:![0-9]+]] +// CHECK: [[TPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[TPARAM]] + +// CHECK: distinct !DISubprogram(name: "bar" +// CHECK: !DILocalVariable(name: "tp",{{.*}}type: [[TPARAM3]]) diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h index 6c479415b9ed2..bb9b9490fdcdd 100644 --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -550,6 +550,12 @@ namespace llvm { StringRef Name, DIType *Ty, bool IsDefault); + /// \param Scope Scope in which this type is defined. + /// \param Name Type parameter name. + /// \param Ty Parameter type. + DIDerivedType *createTemplateTypeParameterAsType(DIScope *Scope, + StringRef Name, + DIType *Ty); /// Create debugging information for template /// value parameter. diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index 0a8a1ad38c959..8557d833dfb0e 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -520,6 +520,8 @@ void DwarfUnit::addTemplateParams(DIE &Buffer, DINodeArray TParams) { constructTemplateTypeParameterDIE(Buffer, TTP); else if (auto *TVP = dyn_cast(Element)) constructTemplateValueParameterDIE(Buffer, TVP); + else if (auto *TDT = dyn_cast(Element)) + createTypeDIE(TDT->getScope(), Buffer, TDT); } } diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index 8f9462ab46d88..fc50077ba8736 100644 --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -458,6 +458,15 @@ DIBuilder::createObjCProperty(StringRef Name, DIFile *File, unsigned LineNumber, SetterName, PropertyAttributes, Ty); } +DIDerivedType *DIBuilder::createTemplateTypeParameterAsType(DIScope *Context, + StringRef Name, + DIType *Ty) { + return DIDerivedType::get(VMContext, dwarf::DW_TAG_template_type_parameter, + Name, nullptr, 0, Context, Ty, 0, 0, 0, + std::nullopt, std::nullopt, DINode::FlagZero, + nullptr); +} + DITemplateTypeParameter * DIBuilder::createTemplateTypeParameter(DIScope *Context, StringRef Name, DIType *Ty, bool isDefault) { diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 8432779c107de..669c3370d9fc4 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1241,6 +1241,7 @@ void Verifier::visitDIDerivedType(const DIDerivedType &N) { N.getTag() == dwarf::DW_TAG_inheritance || N.getTag() == dwarf::DW_TAG_friend || N.getTag() == dwarf::DW_TAG_set_type || + N.getTag() == dwarf::DW_TAG_template_type_parameter || N.getTag() == dwarf::DW_TAG_template_alias, "invalid tag", &N); if (N.getTag() == dwarf::DW_TAG_ptr_to_member_type) { @@ -1288,8 +1289,11 @@ void Verifier::visitTemplateParams(const MDNode &N, const Metadata &RawParams) { auto *Params = dyn_cast(&RawParams); CheckDI(Params, "invalid template params", &N, &RawParams); for (Metadata *Op : Params->operands()) { - CheckDI(Op && isa(Op), "invalid template parameter", - &N, Params, Op); + CheckDI(((Op) && (isa(Op))) || + ((isa(Op)) && + (dyn_cast(Op)->getTag() == + dwarf::DW_TAG_template_type_parameter)), + "invalid template parameter", &N, Params, Op); } } diff --git a/llvm/test/DebugInfo/X86/template-as-type-param.ll b/llvm/test/DebugInfo/X86/template-as-type-param.ll new file mode 100644 index 0000000000000..25eaeb92fec3e --- /dev/null +++ b/llvm/test/DebugInfo/X86/template-as-type-param.ll @@ -0,0 +1,119 @@ +; RUN: llc -mtriple=x86_64-unknown-linux-gnu %s -o %t -filetype=obj +; RUN: llvm-dwarfdump %t | FileCheck %s +;Source code for the IR below: +;template +;struct A +;{ +; A () : val_ (), val2_ () { } +; T val_; +; int val2_; +;}; + +;int main (void) +;{ +; A a; +; a.val2_ = 3; +; return 0; +;} + +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("A") +; CHECK-NEXT: DW_AT_byte_size (0x08) +; CHECK-NEXT: DW_AT_decl_file ("test.cpp") +; CHECK-NEXT: DW_AT_decl_line (2) + +; CHECK-NOT: NULL + +; CHECK:[[TEMPLATE:0x[0-9a-f]*]]: DW_TAG_template_type_parameter +; CHECK-NEXT: DW_AT_type {{.*}} "int" +; CHECK-NEXT: DW_AT_name ("T") + +; CHECK: DW_TAG_member +; CHECK-NEXT: DW_AT_name ("val_") +; CHECK-NEXT: DW_AT_type ([[TEMPLATE]] "T") + +; CHECK: DW_TAG_member +; CHECK-NEXT: DW_AT_name ("val2_") +; CHECK-NEXT: DW_AT_type {{.*}} "int" +; ModuleID = 'test.cpp' +source_filename = "test.cpp" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.A = type { i32, i32 } + +$_ZN1AIiEC2Ev = comdat any + +; Function Attrs: mustprogress noinline norecurse optnone uwtable +define dso_local noundef i32 @main() #0 !dbg !22 { +entry: + %retval = alloca i32, align 4 + %a = alloca %struct.A, align 4 + store i32 0, ptr %retval, align 4 + #dbg_declare(ptr %a, !26, !DIExpression(), !27) + call void @_ZN1AIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %a), !dbg !27 + %val2_ = getelementptr inbounds nuw %struct.A, ptr %a, i32 0, i32 1, !dbg !28 + store i32 3, ptr %val2_, align 4, !dbg !29 + ret i32 0, !dbg !30 +} + +; Function Attrs: mustprogress noinline nounwind optnone uwtable +define linkonce_odr dso_local void @_ZN1AIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this) unnamed_addr #1 comdat align 2 !dbg !31 { +entry: + %this.addr = alloca ptr, align 8 + store ptr %this, ptr %this.addr, align 8 + #dbg_declare(ptr %this.addr, !32, !DIExpression(), !34) + %this1 = load ptr, ptr %this.addr, align 8 + %val_ = getelementptr inbounds nuw %struct.A, ptr %this1, i32 0, i32 0, !dbg !35 + store i32 0, ptr %val_, align 4, !dbg !35 + %val2_ = getelementptr inbounds nuw %struct.A, ptr %this1, i32 0, i32 1, !dbg !36 + store i32 0, ptr %val2_, align 4, !dbg !36 + ret void, !dbg !37 +} + +attributes #0 = { mustprogress noinline norecurse optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { mustprogress noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!14, !15, !16, !17, !18, !19, !20} +!llvm.ident = !{!21} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "", checksumkind: CSK_MD5, checksum: "451371997e00e9e85d610a4e9d44a9b5") +!2 = !{!3} +!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !1, line: 2, size: 64, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !4, templateParams: !12, identifier: "_ZTS1AIiE") +!4 = !{!5, !7, !8} +!5 = !DIDerivedType(tag: DW_TAG_member, name: "val_", scope: !3, file: !1, line: 4, baseType: !38, size: 32) +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !DIDerivedType(tag: DW_TAG_member, name: "val2_", scope: !3, file: !1, line: 5, baseType: !6, size: 32, offset: 32) +!8 = !DISubprogram(name: "A", scope: !3, file: !1, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0) +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11} +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!12 = !{!38} +!13 = !DITemplateTypeParameter(name: "T", type: !6) +!14 = !{i32 7, !"Dwarf Version", i32 5} +!15 = !{i32 2, !"Debug Info Version", i32 3} +!16 = !{i32 1, !"wchar_size", i32 4} +!17 = !{i32 8, !"PIC Level", i32 2} +!18 = !{i32 7, !"PIE Level", i32 2} +!19 = !{i32 7, !"uwtable", i32 2} +!20 = !{i32 7, !"frame-pointer", i32 2} +!21 = !{!"clang"} +!22 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 7, type: !23, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !25) +!23 = !DISubroutineType(types: !24) +!24 = !{!6} +!25 = !{} +!26 = !DILocalVariable(name: "a", scope: !22, file: !1, line: 8, type: !3) +!27 = !DILocation(line: 8, column: 8, scope: !22) +!28 = !DILocation(line: 9, column: 3, scope: !22) +!29 = !DILocation(line: 9, column: 9, scope: !22) +!30 = !DILocation(line: 10, column: 1, scope: !22) +!31 = distinct !DISubprogram(name: "A", linkageName: "_ZN1AIiEC2Ev", scope: !3, file: !1, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !8, retainedNodes: !25) +!32 = !DILocalVariable(name: "this", arg: 1, scope: !31, type: !33, flags: DIFlagArtificial | DIFlagObjectPointer) +!33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64) +!34 = !DILocation(line: 0, scope: !31) +!35 = !DILocation(line: 3, column: 8, scope: !31) +!36 = !DILocation(line: 3, column: 17, scope: !31) +!37 = !DILocation(line: 3, column: 28, scope: !31) +!38 = !DIDerivedType(tag: DW_TAG_template_type_parameter,name:"T",scope:!3, baseType: !6)