diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp index 4eea3687adc1b..021858308f27e 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp @@ -815,6 +815,10 @@ SwiftLanguageRuntime::GetNumChildren(CompilerType type, return llvm::createStringError("could not typeref for " + type.GetMangledTypeName().GetString()); + // Indirect enums. + if (type.GetTypeInfo() & lldb::eTypeIsEnumeration) + return 1; + // Existentials. if (size_t n = GetExistentialSyntheticChildren(ts, tr, ti).size()) return n; @@ -1029,6 +1033,8 @@ SwiftLanguageRuntime::GetIndexOfChildMemberWithName( auto ts = type.GetTypeSystem().dyn_cast_or_null<TypeSystemSwiftTypeRef>(); if (!ts) return {SwiftLanguageRuntime::eError, {}}; + if (!exe_ctx) + return {SwiftLanguageRuntime::eError, {}}; using namespace swift::reflection; // Try the static type metadata. @@ -1086,6 +1092,29 @@ SwiftLanguageRuntime::GetIndexOfChildMemberWithName( exe_ctx, omit_empty_base_classes, child_indexes); case ReferenceKind::Strong: { + // Indirect enums. + if (type.GetTypeInfo() & lldb::eTypeIsEnumeration) { + const swift::reflection::TypeRef *tr = nullptr; + auto ti_or_err = GetSwiftRuntimeTypeInfo( + type, exe_ctx->GetBestExecutionContextScope(), &tr); + if (!ti_or_err) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Types), ti_or_err.takeError(), + "Could not get enum type info: {0}"); + return {SwiftLanguageRuntime::eError, {}}; + } + auto *eti = llvm::dyn_cast<EnumTypeInfo>(&*ti_or_err); + if (!eti) { + // This is probably a generic single-payload enum. + // Let's pretend we found it. + LLDB_LOG(GetLog(LLDBLog::Types), + "Presuming generic single-payload enum."); + child_indexes.push_back(0); + return {SwiftLanguageRuntime::eFound, child_indexes.size()}; + } + return findFieldWithName(eti->getCases(), tr, name, true, + child_indexes); + } + ThreadSafeReflectionContext reflection_ctx = GetReflectionContext(); if (!reflection_ctx) return {SwiftLanguageRuntime::eError, {}}; @@ -1197,6 +1226,47 @@ llvm::Expected<CompilerType> SwiftLanguageRuntime::GetChildCompilerTypeAtIndex( return pack_element_type; } + auto get_from_indirect_enum = [&]() -> llvm::Expected<CompilerType> { + ThreadSafeReflectionContext reflection_ctx = GetReflectionContext(); + if (!reflection_ctx) + return llvm::createStringError("no reflection context"); + // The indirect enum field should point to a closure context. + LLDBTypeInfoProvider tip(*this, &exe_ctx); + lldb::addr_t instance = ::MaskMaybeBridgedPointer(GetProcess(), pointer); + auto ti_or_err = reflection_ctx->GetTypeInfoFromInstance( + instance, &tip, ts->GetDescriptorFinder()); + if (!ti_or_err) { + llvm::consumeError(ti_or_err.takeError()); + return CompilerType(); + } + auto *ti = &*ti_or_err; + if (auto *rti = llvm::dyn_cast<swift::reflection::RecordTypeInfo>(ti)) { + switch (rti->getRecordKind()) { + case swift::reflection::RecordKind::ClosureContext: { + auto &field = rti->getFields()[0]; + auto *type_ref = field.TR; + language_flags |= TypeSystemSwift::LanguageFlags::eIsIndirectEnumCase; + child_byte_offset = field.Offset; + child_byte_size = field.TI.getSize(); + return GetTypeFromTypeRef(*ts, type_ref); + } + case swift::reflection::RecordKind::Tuple: { + std::vector<TypeSystemSwift::TupleElement> elts; + for (auto &field : rti->getFields()) + elts.emplace_back(ConstString(), GetTypeFromTypeRef(*ts, field.TR)); + return ts->CreateTupleType(elts); + } + default: + return llvm::createStringError( + "unexpected kind of indirect record enum"); + } + } + language_flags |= TypeSystemSwift::LanguageFlags::eIsIndirectEnumCase; + child_byte_offset = 0; + child_byte_size = ti->getSize(); + return ts->GetBuiltinRawPointerType(); + }; + // The actual conversion from the FieldInfo record. auto get_from_field_info = [&](const swift::reflection::FieldInfo &field, @@ -1225,27 +1295,16 @@ llvm::Expected<CompilerType> SwiftLanguageRuntime::GetChildCompilerTypeAtIndex( CompilerType result; if (tuple) result = tuple->element_type; - else if (is_indirect_enum) { - ThreadSafeReflectionContext reflection_ctx = GetReflectionContext(); - if (!reflection_ctx) - return llvm::createStringError("no reflection context"); - // The indirect enum field should point to a closure context. - LLDBTypeInfoProvider tip(*this, &exe_ctx); - lldb::addr_t instance = ::MaskMaybeBridgedPointer(GetProcess(), pointer); - auto ti_or_err = reflection_ctx->GetTypeInfoFromInstance( - instance, &tip, ts->GetDescriptorFinder()); - if (!ti_or_err) - return ti_or_err.takeError(); - auto *ti = &*ti_or_err; - auto *rti = llvm::dyn_cast_or_null<swift::reflection::RecordTypeInfo>(ti); - if (rti->getFields().size() < 1) - return llvm::createStringError("no fields in indirect enum"); - auto &field = rti->getFields()[0]; - auto *type_ref = field.TR; - result = GetTypeFromTypeRef(*ts, type_ref); - child_byte_offset = field.Offset; - } else + else { + if (is_indirect_enum) { + auto type_or_err = get_from_indirect_enum(); + // Only if this couldn't be resolved as an instance pointer, carry on. + if (!type_or_err || *type_or_err) + return type_or_err; + } result = GetTypeFromTypeRef(*ts, field.TR); + } + // Bug-for-bug compatibility. See comment in // SwiftASTContext::GetBitSize(). if (result.IsFunctionType()) @@ -1395,6 +1454,10 @@ llvm::Expected<CompilerType> SwiftLanguageRuntime::GetChildCompilerTypeAtIndex( if (!reflection_ctx) return llvm::createStringError("no reflection context"); + // Indirect enums. + if (type.GetTypeInfo() & lldb::eTypeIsEnumeration) + return get_from_indirect_enum(); + CompilerType instance_type = valobj->GetCompilerType(); auto instance_ts = instance_type.GetTypeSystem().dyn_cast_or_null<TypeSystemSwift>(); diff --git a/lldb/test/API/lang/swift/enums/indirect/Makefile b/lldb/test/API/lang/swift/enums/indirect/Makefile new file mode 100644 index 0000000000000..2a69023633b34 --- /dev/null +++ b/lldb/test/API/lang/swift/enums/indirect/Makefile @@ -0,0 +1,3 @@ +SWIFT_SOURCES := main.swift + +include Makefile.rules diff --git a/lldb/test/API/lang/swift/enums/indirect/TestSwiftEnumIndirect.py b/lldb/test/API/lang/swift/enums/indirect/TestSwiftEnumIndirect.py new file mode 100644 index 0000000000000..0fca490458286 --- /dev/null +++ b/lldb/test/API/lang/swift/enums/indirect/TestSwiftEnumIndirect.py @@ -0,0 +1,64 @@ +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbutil as lldbutil + +class TestCase(TestBase): + @swiftTest + def test(self): + """Test indirect enums""" + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.swift") + ) + if self.TraceOn(): + self.expect('v') + frame = thread.frames[0] + generic_s = frame.FindVariable("generic_s") + s = generic_s.GetChildMemberWithName("s") + t = s.GetChildMemberWithName("t") + lldbutil.check_variable(self, t, False, value="123") + + generic_large_s = frame.FindVariable("generic_large_s") + s = generic_s.GetChildMemberWithName("s") + t = s.GetChildMemberWithName("t") + lldbutil.check_variable(self, t, False, value="123") + + generic_c = frame.FindVariable("generic_c") + c = generic_c.GetChildMemberWithName("c") + t = c.GetChildMemberWithName("t") + lldbutil.check_variable(self, t, False, value="123") + + multi_s = frame.FindVariable("multi_s") + s = multi_s.GetChildMemberWithName("s") + t = s.GetChildMemberWithName("t") + lldbutil.check_variable(self, t, False, value="123") + + multi_c = frame.FindVariable("multi_c") + lldbutil.check_variable(self, multi_c, False, value="c") + + tuple_a = frame.FindVariable("tuple_a") + a = tuple_a.GetChildMemberWithName("a") + self.assertEqual(a.GetNumChildren(), 2) + lldbutil.check_variable(self, a.GetChildAtIndex(0), False, value="23") + lldbutil.check_variable(self, a.GetChildAtIndex(1), False, summary='"hello"') + tuple_b = frame.FindVariable("tuple_b") + b = tuple_b.GetChildMemberWithName("b") + self.assertEqual(b.GetNumChildren(), 1) + lldbutil.check_variable(self, b.GetChildAtIndex(0), False, value="42") + tuple_c = frame.FindVariable("tuple_c") + c = tuple_c.GetChildMemberWithName("c") + lldbutil.check_variable(self, c, False, value="32") + tuple_d = frame.FindVariable("tuple_d") + lldbutil.check_variable(self, tuple_d, False, value="d") + + tree = frame.FindVariable("tree") + n0 = tree.GetChildMemberWithName("node") + n1 = n0.GetChildAtIndex(0) + n2 = n1.GetChildAtIndex(0) + l0 = n2.GetChildAtIndex(0) + lldbutil.check_variable(self, l0.GetChildAtIndex(0), False, value="1") + l1 = n2.GetChildAtIndex(1) + lldbutil.check_variable(self, l1.GetChildAtIndex(0), False, value="2") + l2 = n0.GetChildAtIndex(1) + lldbutil.check_variable(self, l2.GetChildAtIndex(0), False, value="3") diff --git a/lldb/test/API/lang/swift/enums/indirect/main.swift b/lldb/test/API/lang/swift/enums/indirect/main.swift new file mode 100644 index 0000000000000..b8ba92f9a530c --- /dev/null +++ b/lldb/test/API/lang/swift/enums/indirect/main.swift @@ -0,0 +1,61 @@ +struct S<T> { + let t: T +} + +enum EGenericS<T> { + indirect case s(S<T>) +} + +class C<T> { + init(t: T) { self.t = t } + let t: T +} + +struct LargeS<T> { + let t: T + let space = (0, 1, 2, 3, 4, 5, 6, 7) +} + +enum EGenericLargeS<T> { + indirect case s(LargeS<T>) +} + +enum EGenericC<T> { + indirect case c(C<T>) +} + +enum EGenericMulti<T> { + indirect case s(S<T>) + indirect case c(C<T>) +} + +enum ETuple { + indirect case a(Int, String) + indirect case b(Int) + case c(Int) + case d +} + +enum ETree<T> { + indirect case node(ETree<T>, ETree<T>) + case leaf(T) +} + +func main() { + let generic_s = EGenericS<Int>.s(S(t: 123)) + let generic_large_s = EGenericLargeS<Int>.s(LargeS(t: 123)) + let generic_c = EGenericC<Int>.c(C(t: 123)) + let multi_s = EGenericMulti<Int>.s(S(t: 123)) + let multi_c = EGenericMulti<Int>.c(C(t: 123)) + let tuple_a = ETuple.a(23, "hello") + let tuple_b = ETuple.b(42) + let tuple_c = ETuple.c(32) + let tuple_d = ETuple.d + let tree = ETree<Int>.node( + ETree<Int>.node(ETree<Int>.leaf(1), + ETree<Int>.leaf(2)), + ETree<Int>.leaf(3)) + print("break here") +} + +main()