diff --git a/lldb/include/lldb/Symbol/ClangASTContext.h b/lldb/include/lldb/Symbol/ClangASTContext.h --- a/lldb/include/lldb/Symbol/ClangASTContext.h +++ b/lldb/include/lldb/Symbol/ClangASTContext.h @@ -261,7 +261,8 @@ CompilerType CreateRecordType(clang::DeclContext *decl_ctx, lldb::AccessType access_type, const char *name, int kind, lldb::LanguageType language, - ClangASTMetadata *metadata = nullptr); + ClangASTMetadata *metadata = nullptr, + bool exports_symbols = false); class TemplateParameterInfos { public: diff --git a/lldb/packages/Python/lldbsuite/test/python_api/type/TestTypeList.py b/lldb/packages/Python/lldbsuite/test/python_api/type/TestTypeList.py --- a/lldb/packages/Python/lldbsuite/test/python_api/type/TestTypeList.py +++ b/lldb/packages/Python/lldbsuite/test/python_api/type/TestTypeList.py @@ -73,13 +73,17 @@ self.assertTrue(enum_member) self.DebugSBType(enum_member.type) elif field.name == "my_type_is_nameless": - self.assertTrue( + self.assertFalse( field.type.IsAnonymousType(), - "my_type_is_nameless has an anonymous type") + "my_type_is_nameless is not an anonymous type") elif field.name == "my_type_is_named": self.assertFalse( field.type.IsAnonymousType(), "my_type_is_named has a named type") + elif field.name == None: + self.assertTrue( + field.type.IsAnonymousType(), + "Nameless type is not anonymous") # Pass an empty string. LLDB should not crash. :-) fuzz_types = target.FindTypes(None) diff --git a/lldb/packages/Python/lldbsuite/test/python_api/type/main.cpp b/lldb/packages/Python/lldbsuite/test/python_api/type/main.cpp --- a/lldb/packages/Python/lldbsuite/test/python_api/type/main.cpp +++ b/lldb/packages/Python/lldbsuite/test/python_api/type/main.cpp @@ -15,6 +15,14 @@ TASK_TYPE_1, TASK_TYPE_2 } type; + // This struct is anonymous b/c it does not have a name + // and it is not unnamed class. + // Anonymous classes are a GNU extension. + struct { + int y; + }; + // This struct is an unnamed class see [class.pre]p1 + // http://eel.is/c++draft/class#pre-1.sentence-6 struct { int x; } my_type_is_nameless; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -181,6 +181,7 @@ bool is_scoped_enum = false; bool is_vector = false; bool is_virtual = false; + bool exports_symbols = false; clang::StorageClass storage = clang::SC_None; const char *mangled_name = nullptr; lldb_private::ConstString name; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -347,6 +347,9 @@ case DW_AT_GNU_vector: is_vector = form_value.Boolean(); break; + case DW_AT_export_symbols: + exports_symbols = form_value.Boolean(); + break; } } } @@ -1546,7 +1549,7 @@ clang_type_was_created = true; clang_type = m_ast.CreateRecordType( decl_ctx, attrs.accessibility, attrs.name.GetCString(), tag_decl_kind, - attrs.class_language, &metadata); + attrs.class_language, &metadata, attrs.exports_symbols); } } diff --git a/lldb/source/Symbol/ClangASTContext.cpp b/lldb/source/Symbol/ClangASTContext.cpp --- a/lldb/source/Symbol/ClangASTContext.cpp +++ b/lldb/source/Symbol/ClangASTContext.cpp @@ -1347,11 +1347,9 @@ #pragma mark Structure, Unions, Classes -CompilerType ClangASTContext::CreateRecordType(DeclContext *decl_ctx, - AccessType access_type, - const char *name, int kind, - LanguageType language, - ClangASTMetadata *metadata) { +CompilerType ClangASTContext::CreateRecordType( + DeclContext *decl_ctx, AccessType access_type, const char *name, int kind, + LanguageType language, ClangASTMetadata *metadata, bool exports_symbols) { ASTContext *ast = getASTContext(); assert(ast != nullptr); @@ -1402,10 +1400,7 @@ // Anonymous classes is a GNU/MSVC extension that clang supports. It // requires the anonymous class be embedded within a class. So the new // heuristic verifies this condition. - // - // FIXME: An unnamed class within a class is also wrongly recognized as an - // anonymous struct. - if (isa(decl_ctx)) + if (isa(decl_ctx) && exports_symbols) decl->setAnonymousStructOrUnion(true); } @@ -8989,7 +8984,7 @@ TypeSP type = type_list.GetTypeAtIndex(i); if (!symbol_name.empty()) - if (symbol_name.compare(type->GetName().GetStringRef()) != 0) + if (symbol_name != type->GetName().GetStringRef()) continue; s << type->GetName().AsCString() << "\n"; diff --git a/lldb/test/Shell/SymbolFile/DWARF/anon_class_w_and_wo_export_symbols.ll b/lldb/test/Shell/SymbolFile/DWARF/anon_class_w_and_wo_export_symbols.ll new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/DWARF/anon_class_w_and_wo_export_symbols.ll @@ -0,0 +1,77 @@ +; This test verifies that we do the right thing with DIFlagExportSymbols which is the new +; behavioir and without the DIFlagExportSymbols which is the old behavior for the given +; definitions below. +; +;``` +; struct A{ +; struct { +; int x; +; }; +; struct { +; int y; +; }; +; struct { +; int z; +; } unnamed; +; } a; +;``` +; +; RUN: %clang++ -g -c -o %t.o %s +; RUN: lldb-test symbols -dump-clang-ast %t.o | FileCheck %s +; RUN: llvm-dwarfdump %t.o | FileCheck %s --check-prefix DWARFDUMP + +%struct.A = type { %struct.anon, %struct.anon.0, %struct.anon.1 } +%struct.anon = type { i32 } +%struct.anon.0 = type { i32 } +%struct.anon.1 = type { i32 } + +@a = global %struct.A zeroinitializer, align 4, !dbg !0 + +!llvm.module.flags = !{!21, !22} +!llvm.dbg.cu = !{!2} +!llvm.ident = !{!23} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 11, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 10.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "anon_old_new.cpp", directory: "/dir") +!4 = !{} +!5 = !{!0} +!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !3, line: 1, size: 96, flags: DIFlagTypePassByValue, elements: !7, identifier: "_ZTS1A") +; CHECK: struct A definition +!7 = !{!8, !13, !17} +!8 = !DIDerivedType(tag: DW_TAG_member, scope: !6, file: !3, line: 2, baseType: !9, size: 32) +!9 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !6, file: !3, line: 2, size: 32, flags: DIFlagExportSymbols | DIFlagTypePassByValue, elements: !10, identifier: "_ZTSN1AUt_E") +; Correctly identify an anonymous class with DIFlagExportSymbols +; CHECK: struct definition +; CHECK: DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal +!10 = !{!11} +!11 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !9, file: !3, line: 3, baseType: !12, size: 32) +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !DIDerivedType(tag: DW_TAG_member, scope: !6, file: !3, line: 5, baseType: !14, size: 32, offset: 32) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !6, file: !3, line: 5, size: 32, flags: DIFlagTypePassByValue, elements: !15, identifier: "_ZTSN1AUt0_E") +; Correctly identify an anonymous class without DIFlagExportSymbols +; This works b/c we have additional checks when we fields to A. +; CHECK: struct definition +; CHECK: DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal +!15 = !{!16} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !14, file: !3, line: 6, baseType: !12, size: 32) +!17 = !DIDerivedType(tag: DW_TAG_member, name: "unnamed", scope: !6, file: !3, line: 10, baseType: !18, size: 32, offset: 64) +!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !6, file: !3, line: 8, size: 32, flags: DIFlagTypePassByValue, elements: !19, identifier: "_ZTSN1AUt1_E") +; Correctly identify an unamed class +; CHECK: struct definition +; CHECK: DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal +!19 = !{!20} +!20 = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: !18, file: !3, line: 9, baseType: !12, size: 32) +!21 = !{i32 2, !"Dwarf Version", i32 4} +!22 = !{i32 2, !"Debug Info Version", i32 3} +!23 = !{!"clang version 10.0.0"} + +; DWARFDUMP: DW_TAG_structure_type +; DWARFDUMP: DW_AT_name ("A") +; +; DWARFDUMP: DW_TAG_structure_type +; DWARFDUMP: DW_AT_export_symbols (true) +; +; DWARFDUMP: DW_TAG_structure_type +; DWARFDUMP-NOT: DW_AT_export_symbols (true) diff --git a/lldb/test/Shell/SymbolFile/DWARF/clang-ast-from-dwarf-unamed-and-anon-structs.cpp b/lldb/test/Shell/SymbolFile/DWARF/clang-ast-from-dwarf-unamed-and-anon-structs.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/DWARF/clang-ast-from-dwarf-unamed-and-anon-structs.cpp @@ -0,0 +1,19 @@ +// Test to verify we are corectly generating anonymous flags when parsing +// anonymous class and unnamed structs from DWARF to the a clang AST node. + +// RUN: %clang++ -g -c -o %t.o %s +// RUN: lldb-test symbols -dump-clang-ast %t.o | FileCheck %s + +struct A { + struct { + int x; + }; + struct { + int y; + } C; +} a; + +// CHECK: A::(anonymous struct) +// CHECK: |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal +// CHECK: A::(anonymous struct) +// CHECK: |-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal