Skip to content

Add SwiftLanguageRuntime support for implicit enum members. #8146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,11 @@ class SwiftLanguageRuntimeStub {
return {};
}

std::pair<bool, llvm::Optional<size_t>> GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) {
std::pair<SwiftLanguageRuntime::LookupResult, llvm::Optional<size_t>>
GetIndexOfChildMemberWithName(CompilerType type, llvm::StringRef name,
ExecutionContext *exe_ctx,
bool omit_empty_base_classes,
std::vector<uint32_t> &child_indexes) {
STUB_LOG();
return {};
}
Expand Down Expand Up @@ -2427,7 +2429,7 @@ llvm::Optional<std::string> SwiftLanguageRuntime::GetEnumCaseName(
FORWARD(GetEnumCaseName, type, data, exe_ctx);
}

std::pair<bool, llvm::Optional<size_t>>
std::pair<SwiftLanguageRuntime::LookupResult, llvm::Optional<size_t>>
SwiftLanguageRuntime::GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) {
Expand Down
19 changes: 16 additions & 3 deletions lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,17 @@ class SwiftLanguageRuntime : public LanguageRuntime {
const DataExtractor &data,
ExecutionContext *exe_ctx);

enum LookupResult {
/// Failed due to missing reflection meatadata or unimplemented
/// functionality. Should retry with SwiftASTContext.
eError = 0,
/// Success.
eFound,
/// Found complete type info, lookup unsuccessful.
/// Do not waste time retrying.
eNotFound
};

/// Behaves like the CompilerType::GetIndexOfChildMemberWithName()
/// except for the more nuanced return value.
///
Expand All @@ -333,9 +344,11 @@ class SwiftLanguageRuntime : public LanguageRuntime {
/// don't have an index.
///
/// \returns {true, {num_idexes}} on success.
std::pair<bool, llvm::Optional<size_t>> GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes);
std::pair<LookupResult, llvm::Optional<size_t>>
GetIndexOfChildMemberWithName(CompilerType type, llvm::StringRef name,
ExecutionContext *exe_ctx,
bool omit_empty_base_classes,
std::vector<uint32_t> &child_indexes);

/// Ask Remote Mirrors about a child of a composite type.
CompilerType GetChildCompilerTypeAtIndex(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,20 @@ GetExistentialSyntheticChildren(std::shared_ptr<TypeSystemSwiftTypeRef> ts,
assert(children.size());
return children;
}

/// Log the fact that a type kind is not supported.
void LogUnimplementedTypeKind(const char *function, CompilerType type) {
// When running the test suite assert that all cases are covered.
LLDB_LOG(GetLog(LLDBLog::Types), "{0}: unimplemented type info in {1}",
type.GetMangledTypeName(), function);
#ifndef NDEBUG
llvm::dbgs() << function << ": unimplemented type info in"
<< type.GetMangledTypeName() << "\n";
if (ModuleList::GetGlobalModuleListProperties().GetSwiftValidateTypeSystem())
assert(false && "not implemented");
#endif
}

} // namespace

llvm::Optional<unsigned>
Expand Down Expand Up @@ -729,9 +743,7 @@ SwiftLanguageRuntimeImpl::GetNumChildren(CompilerType type,
return {};
}

// FIXME: Implement more cases.
LLDB_LOG(GetLog(LLDBLog::Types), "{0}: unimplemented type info",
type.GetMangledTypeName());
LogUnimplementedTypeKind(__FUNCTION__, type);
return {};
}

Expand Down Expand Up @@ -800,26 +812,34 @@ SwiftLanguageRuntimeImpl::GetNumFields(CompilerType type,
}
}
default:
// FIXME: Implement more cases.
LogUnimplementedTypeKind(__FUNCTION__, type);
return {};
}
}

static std::pair<bool, llvm::Optional<size_t>>
static std::pair<SwiftLanguageRuntime::LookupResult, llvm::Optional<size_t>>
findFieldWithName(const std::vector<swift::reflection::FieldInfo> &fields,
const swift::reflection::TypeRef *tr, llvm::StringRef name,
bool is_enum, std::vector<uint32_t> &child_indexes,
uint32_t offset = 0) {
uint32_t index = 0;
uint32_t name_as_index = 0;
bool name_is_index = false;
auto *tuple_tr = llvm::dyn_cast<swift::reflection::TupleTypeRef>(tr);
if (tuple_tr)
name_is_index = !name.getAsInteger(10, name_as_index);
bool is_nonpayload_enum_case = false;

auto it = std::find_if(fields.begin(), fields.end(), [&](const auto &field) {
if (name_is_index && name_as_index == index)
return true;

// In some situations the cached TI for a tuple type is missing the names,
// but the type_ref has them.
StringRef field_name = field.Name;
if (!field.Name.size())
if (auto *tuple_tr = llvm::dyn_cast<swift::reflection::TupleTypeRef>(tr))
if (tuple_tr->getLabels().size() > index)
field_name = tuple_tr->getLabels().at(index);
if (tuple_tr && tuple_tr->getLabels().size() > index)
field_name = tuple_tr->getLabels().at(index);
if (name != field_name) {
// A nonnull TypeRef is required for enum cases, where it represents cases
// that have a payload. In other types it will be true anyway.
Expand All @@ -831,14 +851,15 @@ findFieldWithName(const std::vector<swift::reflection::FieldInfo> &fields,
is_nonpayload_enum_case = (field.TR == nullptr);
return true;
});

// Not found.
if (it == fields.end())
return {false, {}};
return {SwiftLanguageRuntime::eNotFound, {}};
// Found, but no index to report.
if (is_nonpayload_enum_case)
return {true, {}};
return {SwiftLanguageRuntime::eFound, {}};
child_indexes.push_back(offset + index);
return {true, child_indexes.size()};
return {SwiftLanguageRuntime::eFound, child_indexes.size()};
}

llvm::Optional<std::string> SwiftLanguageRuntimeImpl::GetEnumCaseName(
Expand All @@ -859,24 +880,30 @@ llvm::Optional<std::string> SwiftLanguageRuntimeImpl::GetEnumCaseName(
if (eti->projectEnumValue(*GetMemoryReader(), addr, &case_index))
return eti->getCases()[case_index].Name;

// FIXME: Enabling this fails TestSwiftNestedCEnums.py: The test
// nests a C-style enum inside of a payload-carrying enum and most
// likely we're not stripping off the outer enum's discriminator
// before reading the value of the inner one.

// LogUnimplementedTypeKind(__FUNCTION__, type);
return {};
}

std::pair<bool, llvm::Optional<size_t>>
std::pair<SwiftLanguageRuntime::LookupResult, llvm::Optional<size_t>>
SwiftLanguageRuntimeImpl::GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) {
LLDB_SCOPED_TIMER();
auto ts = type.GetTypeSystem().dyn_cast_or_null<TypeSystemSwiftTypeRef>();
if (!ts)
return {false, {}};
return {SwiftLanguageRuntime::eError, {}};

using namespace swift::reflection;
// Try the static type metadata.
const TypeRef *tr = nullptr;
auto *ti = GetSwiftRuntimeTypeInfo(type, exe_ctx->GetFramePtr(), &tr);
if (!ti)
return {false, {}};
return {SwiftLanguageRuntime::eError, {}};
switch (ti->getKind()) {
case TypeInfoKind::Record: {
// Structs and Tuples.
Expand All @@ -886,7 +913,7 @@ SwiftLanguageRuntimeImpl::GetIndexOfChildMemberWithName(
case RecordKind::ThickFunction:
// There are two fields, `function` and `context`, but they're not exposed
// by lldb.
return {true, {0}};
return {SwiftLanguageRuntime::eFound, {0}};
case RecordKind::OpaqueExistential:
// `OpaqueExistential` is documented as:
// An existential is a three-word buffer followed by value metadata...
Expand All @@ -896,7 +923,7 @@ SwiftLanguageRuntimeImpl::GetIndexOfChildMemberWithName(
uint32_t index;
if (name.take_back().getAsInteger(10, index) && index < 3) {
child_indexes.push_back(index);
return {true, child_indexes.size()};
return {SwiftLanguageRuntime::eFound, child_indexes.size()};
}
}
return findFieldWithName(rti->getFields(), tr, name, false, child_indexes,
Expand All @@ -916,19 +943,23 @@ SwiftLanguageRuntimeImpl::GetIndexOfChildMemberWithName(
case ReferenceKind::Weak:
case ReferenceKind::Unowned:
case ReferenceKind::Unmanaged:
// Weak references are implicitly optional.
child_indexes.push_back(0);
if (name == "some")
return {SwiftLanguageRuntime::eFound, child_indexes.size()};
return GetIndexOfChildMemberWithName(GetWeakReferent(*ts, type), name,
exe_ctx, omit_empty_base_classes,
child_indexes);
case ReferenceKind::Strong: {
ThreadSafeReflectionContext reflection_ctx = GetReflectionContext();
if (!reflection_ctx)
return {false, {}};
return {SwiftLanguageRuntime::eError, {}};

size_t idx = 0;
for (auto &protocol_child : GetExistentialSyntheticChildren(ts, tr, ti)) {
if (protocol_child.name == name) {
child_indexes.push_back(idx);
return {true, child_indexes.size()};
return {SwiftLanguageRuntime::eFound, child_indexes.size()};
}
++idx;
}
Expand All @@ -941,26 +972,46 @@ SwiftLanguageRuntimeImpl::GetIndexOfChildMemberWithName(
auto *record_ti = llvm::dyn_cast_or_null<RecordTypeInfo>(
reflection_ctx->GetClassInstanceTypeInfo(
current_tr, &tip, ts->GetDescriptorFinder()));
if (!record_ti)
break;
if (!record_ti) {
child_indexes.clear();
return {SwiftLanguageRuntime::eError, {}};
}
auto *super_tr = reflection_ctx->LookupSuperclass(
current_tr, ts->GetDescriptorFinder());
uint32_t offset = super_tr ? 1 : 0;
auto found_size = findFieldWithName(record_ti->getFields(), current_tr,
name, false, child_indexes, offset);
if (found_size.first)
if (found_size.first == SwiftLanguageRuntime::eError ||
found_size.first == SwiftLanguageRuntime::eFound)
return found_size;
current_tr = super_tr;
child_indexes.push_back(0);
}
child_indexes.clear();
return {false, {}};
return {SwiftLanguageRuntime::eNotFound, {}};
}
}
}
case TypeInfoKind::Builtin: {
// Clang enums have an artificial rawValue property.
CompilerType clang_type;
if (ts->IsImportedType(type.GetOpaqueQualType(), &clang_type)) {
bool is_signed;
if (clang_type.IsEnumerationType(is_signed) && name == "rawValue") {
child_indexes.push_back(0);
return {SwiftLanguageRuntime::eFound, {1}};
}
}
// SIMD types have an artificial _value.
if (name == "_value" && TypeSystemSwiftTypeRef::IsSIMDType(type)) {
child_indexes.push_back(0);
return {SwiftLanguageRuntime::eFound, {1}};
}
return {SwiftLanguageRuntime::eNotFound, {0}};
}
default:
// FIXME: Implement more cases.
return {false, {}};
LogUnimplementedTypeKind(__FUNCTION__, type);
return {SwiftLanguageRuntime::eError, {}};
}
}

Expand Down Expand Up @@ -1267,8 +1318,14 @@ CompilerType SwiftLanguageRuntimeImpl::GetChildCompilerTypeAtIndex(
i - 1);
return {};
}
LLDB_LOG(GetLog(LLDBLog::Types), "Cannot retrieve type information for {0}",
type.GetTypeName());
if (llvm::dyn_cast_or_null<swift::reflection::BuiltinTypeInfo>(ti)) {
// Clang enums have an artificial rawValue property. We could
// consider handling them here, but
// TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex can also
// handle them and without a Process.
return {};
}
LogUnimplementedTypeKind(__FUNCTION__, type);
return {};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,11 @@ class SwiftLanguageRuntimeImpl {
const DataExtractor &data,
ExecutionContext *exe_ctx);

std::pair<bool, llvm::Optional<size_t>> GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes);
std::pair<SwiftLanguageRuntime::LookupResult, llvm::Optional<size_t>>
GetIndexOfChildMemberWithName(CompilerType type, llvm::StringRef name,
ExecutionContext *exe_ctx,
bool omit_empty_base_classes,
std::vector<uint32_t> &child_indexes);

CompilerType GetChildCompilerTypeAtIndex(
CompilerType type, size_t idx, bool transparent_pointers,
Expand Down
38 changes: 26 additions & 12 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3413,7 +3413,10 @@ size_t TypeSystemSwiftTypeRef::GetIndexOfChildMemberWithName(
auto found_numidx = runtime->GetIndexOfChildMemberWithName(
GetCanonicalType(type), name, exe_ctx, omit_empty_base_classes,
child_indexes);
if (found_numidx.first) {
// Only use the SwiftASTContext fallback if there was an
// error. If the runtime had complete type info and couldn't
// find a result, don't waste time retrying.
if (found_numidx.first != SwiftLanguageRuntime::eError) {
size_t index_size = found_numidx.second.value_or(0);
#ifndef NDEBUG
// This block is a custom VALIDATE_AND_RETURN implementation to support
Expand All @@ -3436,17 +3439,11 @@ size_t TypeSystemSwiftTypeRef::GetIndexOfChildMemberWithName(
return index_size;

auto fail = [&]() {
auto join = [](const auto &v) {
std::ostringstream buf;
buf << "{";
for (const auto &item : v)
buf << item << ",";
buf.seekp(-1, std::ios_base::end);
buf << "}";
return buf.str();
};
llvm::dbgs() << join(child_indexes)
<< " != " << join(ast_child_indexes) << "\n";
llvm::dbgs() << "{";
llvm::interleaveComma(child_indexes, llvm::dbgs());
llvm::dbgs() << "} != {";
llvm::interleaveComma(ast_child_indexes, llvm::dbgs());
llvm::dbgs() << "}\n";
llvm::dbgs() << "failing type was " << (const char *)type
<< ", member was " << name << "\n";
assert(false &&
Expand Down Expand Up @@ -4291,6 +4288,23 @@ TypeSystemSwiftTypeRef::GetTypeBitAlign(opaque_compiler_type_t type,
return {};
}

bool TypeSystemSwiftTypeRef::IsSIMDType(CompilerType type) {
using namespace swift::Demangle;
Demangler dem;
swift::Demangle::NodePointer global =
dem.demangleSymbol(type.GetMangledTypeName().GetStringRef());
using Kind = swift::Demangle::Node::Kind;
auto *simd_storage = swift_demangle::nodeAtPath(
global, {Kind::TypeMangling, Kind::Type, Kind::Structure});
if (!simd_storage || simd_storage->getNumChildren() != 2)
return false;
auto base_type = simd_storage->getFirstChild();
auto wrapper = simd_storage->getLastChild();
return wrapper && wrapper->getKind() == Kind::Identifier &&
wrapper->hasText() && wrapper->getText().starts_with("SIMD") &&
base_type && base_type->getKind() == Kind::Structure;
}

#ifndef NDEBUG
static bool IsSIMDNode(NodePointer node) {
// A SIMD vector is a clang typealias whose identifier starts with "simd_".
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift {
void SetCachedType(ConstString mangled, const lldb::TypeSP &type_sp);
bool IsImportedType(lldb::opaque_compiler_type_t type,
CompilerType *original_type) override;
/// Determine whether this is a builtin SIMD type.
static bool IsSIMDType(CompilerType type);
/// Like \p IsImportedType(), but even returns Clang types that are also Swift
/// builtins (int <-> Swift.Int) as Clang types.
CompilerType GetAsClangTypeOrNull(lldb::opaque_compiler_type_t type,
Expand Down