diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp index 6cdd41b0219c1..4d416786d8e75 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp @@ -879,8 +879,8 @@ swift::FuncDecl *SwiftASTManipulator::GetFunctionToInjectVariableInto( // pointers in the wrapper, so we can pass them as opaque pointers in the // trampoline function later on. if (!ShouldBindGenericTypes(m_bind_generic_types) && - (variable.IsMetadataPointer() || variable.IsPackCount() || - variable.IsUnboundPack())) + (variable.IsMetadataPointer() || variable.IsWitnessTable() || + variable.IsPackCount() || variable.IsUnboundPack())) return m_entrypoint_decl; return m_function_decl; diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.h b/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.h index 999fd6c77bcd8..789f20b186f5a 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.h +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.h @@ -103,6 +103,11 @@ class SwiftASTManipulatorBase { struct VariableInfo { CompilerType GetType() const { return m_type; } + /// The unbound swift::Type. Unbound means that the generic type parameters + /// are not substituted (for example, Array instead of Array). Only + /// valid the the variable that represents self when evaluating the + /// expression without the generic types. + const swift::Type &GetUnboundType() const { return m_unbound_type; } swift::Identifier GetName() const { return m_name; } VariableMetadata *GetMetadata() const { return m_metadata.get(); } void TakeMetadata(VariableMetadata *vmd) { m_metadata.reset(vmd); } @@ -113,6 +118,7 @@ class SwiftASTManipulatorBase { bool IsOutermostMetadataPointer() const { return m_name.str().starts_with("$τ_0_"); } + bool IsWitnessTable() const { return m_name.str().starts_with("$WT"); } bool IsSelf() const { return m_name.str() == "$__lldb_injected_self"; } @@ -125,14 +131,16 @@ class SwiftASTManipulatorBase { VariableInfo(CompilerType type, swift::Identifier name, VariableMetadataSP metadata, swift::VarDecl::Introducer introducer, - bool is_capture_list = false, bool is_unbound_pack = false) - : m_type(type), m_name(name), m_metadata(metadata), - m_var_introducer(introducer), m_is_capture_list(is_capture_list), + bool is_capture_list = false, bool is_unbound_pack = false, + swift::Type unbound_type = {}) + : m_type(type), m_unbound_type(unbound_type), m_name(name), + m_metadata(metadata), m_var_introducer(introducer), + m_is_capture_list(is_capture_list), m_is_unbound_pack(is_unbound_pack) {} VariableInfo(const VariableInfo &other) - : m_type(other.m_type), m_name(other.m_name), - m_metadata(other.m_metadata), m_decl(other.m_decl), - m_var_introducer(other.m_var_introducer), + : m_type(other.m_type), m_unbound_type(other.m_unbound_type), + m_name(other.m_name), m_metadata(other.m_metadata), + m_decl(other.m_decl), m_var_introducer(other.m_var_introducer), m_lookup_error(other.m_lookup_error.Clone()), m_is_capture_list(other.m_is_capture_list), m_is_unbound_pack(other.m_is_unbound_pack) {} @@ -150,6 +158,7 @@ class SwiftASTManipulatorBase { m_lookup_error = other.m_lookup_error.Clone(); m_is_capture_list = other.m_is_capture_list; m_is_unbound_pack = other.m_is_unbound_pack; + m_unbound_type = other.m_unbound_type; return *this; } @@ -165,6 +174,7 @@ class SwiftASTManipulatorBase { protected: CompilerType m_type; + swift::Type m_unbound_type; swift::Identifier m_name; VariableMetadataSP m_metadata; swift::VarDecl *m_decl = nullptr; diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp index 53aaac127329c..5a08bee499085 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp @@ -1182,7 +1182,7 @@ AddArchetypeTypeAliases(std::unique_ptr &code_manipulator, llvm::SmallDenseMap visible_metadata_pointers; for (auto &variable : code_manipulator->GetVariableInfo()) { - if (!variable.IsMetadataPointer()) + if (!variable.IsMetadataPointer() && !variable.IsWitnessTable()) continue; llvm::StringRef type_name; diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp index 952d8ee005f77..42bc728f04043 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp @@ -18,6 +18,8 @@ #include "lldb/Target/Target.h" #include "lldb/Utility/StreamString.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/ASTMangler.h" #include "swift/Basic/LangOptions.h" #include "swift/Demangling/Demangle.h" #include "swift/Demangling/Demangler.h" @@ -30,8 +32,11 @@ using namespace lldb_private; namespace lldb_private { std::optional> -ParseSwiftGenericParameter(llvm::StringRef name) { - if (!name.consume_front("$τ_")) +ParseSwiftGenericParameter(llvm::StringRef name, bool expect_dollar_prefix) { + if (expect_dollar_prefix && !name.consume_front("$")) + return {}; + + if (!name.consume_front("τ_")) return {}; auto pair = name.split('_'); @@ -165,6 +170,127 @@ static llvm::Expected> CollectMetadataInfos( } return metadata_info; } + +/// Returns a map from the index and depth to the archetype name, for example, +/// given: struct S {} This function returns {{0, 0} -> T, {0, 1} -> U}. +static llvm::DenseMap, llvm::StringRef> +MakeIndexAndDepthToArchetypeMap( + llvm::ArrayRef requirements) { + llvm::DenseMap, llvm::StringRef> map; + for (auto &req : requirements) { + if (req.getKind() != swift::RequirementKind::Conformance) + continue; + auto type = req.getFirstType(); + auto *generic_type = + llvm::dyn_cast(type.getPointer()); + if (!generic_type) + continue; + + unsigned depth = generic_type->getDepth(); + unsigned index = generic_type->getIndex(); + auto name = generic_type->getName().str(); + map.insert({{depth, index}, name}); + } + return map; +} + +struct ParsedWitnessTable { + /// The full name of the variable in debug info. For example: + /// $WTτ_0_0$SubType$$MangledProtocol. + llvm::StringRef full_name; + /// The archetype name, for example T.SubType + std::string archetype_name; + /// The mangled protocol name. + llvm::StringRef mangled_protocol_name; + /// The "display" protocol name. + std::string protocol_name; + ParsedWitnessTable(llvm::StringRef full_name, std::string archetype_name, + llvm::StringRef mangled_protocol_name, + std::string protocol_name) + : full_name(full_name), archetype_name(archetype_name), + mangled_protocol_name(mangled_protocol_name), + protocol_name(protocol_name) {} +}; + +/// Parses the witness table artificial variables. +static llvm::Expected> ParseWitnessInfos( + llvm::ArrayRef local_variables, + llvm::ArrayRef requirements) { + llvm::SmallVector witness_tables; + auto indexes_to_archetype = MakeIndexAndDepthToArchetypeMap(requirements); + for (auto &local_variable : local_variables) { + if (!local_variable.IsWitnessTable()) + continue; + + // Full name looks something like "$WTτ_0_0$SubType$$MangledProtocol.". + auto full_name = local_variable.GetName().str(); + auto [metadata_name, mangled_protocol_name] = full_name.split("$$"); + + if (metadata_name.empty() || mangled_protocol_name.empty() || + !SwiftLanguageRuntime::IsSwiftMangledName(mangled_protocol_name)) + return llvm::createStringError( + "malformed witness table name in debug info"); + + metadata_name = metadata_name.drop_front(StringRef("$WT").size()); + auto [front, back] = metadata_name.split('$'); + auto maybe_depth_and_index = + ParseSwiftGenericParameter(front, /*expect_dollar_prefix*/ false); + if (!maybe_depth_and_index) + return llvm::createStringError( + "malformed witness table name in debug info"); + + auto [depth, index] = *maybe_depth_and_index; + + auto it = indexes_to_archetype.find({depth, index}); + if (it == indexes_to_archetype.end()) + return llvm::createStringError( + "malformed witness table name in debug info"); + + std::string archetype_name = it->getSecond().str(); + if (!back.empty()) + archetype_name += "." + back.str(); + std::replace(archetype_name.begin(), archetype_name.end(), '$', '.'); + auto protocol_name = + swift::Demangle::demangleSymbolAsString(mangled_protocol_name); + witness_tables.emplace_back(full_name, archetype_name, + mangled_protocol_name, protocol_name); + } + + // Order the witness tables according to the requirements, otherwise we risk + // passing the witness table pointers in the wrong order when generating the + // expression. + llvm::SmallVector ordered_witness_tables; + for (auto &Requirement : requirements) { + if (Requirement.getKind() != swift::RequirementKind::Conformance) + continue; + swift::ProtocolDecl *ProtocolDecl = Requirement.getProtocolDecl(); + auto protocol_type = ProtocolDecl->getDeclaredType(); + auto type = Requirement.getFirstType(); + auto &ast_ctx = type->getASTContext(); + swift::Mangle::ASTMangler mangler(ast_ctx, true); + std::string mangled_protocol_name = + mangler.mangleTypeForDebugger(protocol_type, nullptr); + std::string name; + if (auto *generic_type = + llvm::dyn_cast(type.getPointer())) + name = generic_type->getName().str(); + else if (auto *dependent = + llvm::dyn_cast(type.getPointer())) + name = dependent->getString(); + + for (auto &parsed_witness_table : witness_tables) { + if (name == parsed_witness_table.archetype_name && + mangled_protocol_name == parsed_witness_table.mangled_protocol_name) { + ordered_witness_tables.emplace_back(std::move(parsed_witness_table)); + } + } + } + assert(ordered_witness_tables.size() == witness_tables.size() && + "Ordered witness table size does not match"); + + return ordered_witness_tables; +} + /// Constructs the signatures for the expression evaluation functions based on /// the metadata variables in scope and any variadic functiontion parameters. /// For every outermost metadata pointer in scope ($τ_0_0, $τ_0_1, etc), we want @@ -208,10 +334,25 @@ static llvm::Expected MakeGenericSignaturesAndCalls( return llvm::createStringError(llvm::errc::not_supported, "Inconsistent generic signature"); - auto maybe_metadata_infos = CollectMetadataInfos(metadata_variables, generic_sig); - if (!maybe_metadata_infos) - return maybe_metadata_infos.takeError(); - auto metadata_infos = *maybe_metadata_infos; + auto self = llvm::find_if( + local_variables, [](const SwiftASTManipulator::VariableInfo &variable) { + return variable.IsSelf(); + }); + if (!self || !self->GetUnboundType()) + return llvm::createStringError("\"self\" not found"); + auto bound_generic = llvm::cast( + self->GetUnboundType().getPointer()); + auto decl = bound_generic->getDecl(); + auto requirements = decl->getGenericRequirements(); + auto witness_infos = ParseWitnessInfos(local_variables, requirements); + if (!witness_infos) + return witness_infos.takeError(); + + auto metatada_infos_or_err = + CollectMetadataInfos(metadata_variables, generic_sig); + if (!metatada_infos_or_err) + return metatada_infos_or_err.takeError(); + auto metadata_infos = *metatada_infos_or_err; llvm::SmallDenseMap, llvm::SmallString<4>> subs; std::string generic_params; @@ -230,11 +371,17 @@ static llvm::Expected MakeGenericSignaturesAndCalls( s_generic_params << sig_archetype_name << ","; subs.insert({{depth, index}, sig_archetype_name}); } + std::string type_constraints; + llvm::raw_string_ostream s_type_constraints(type_constraints); + for (auto &wi : *witness_infos) + s_type_constraints << wi.archetype_name << ": " << wi.protocol_name << ","; if (!generic_params.empty()) generic_params.pop_back(); if (!generic_params_no_packs.empty()) generic_params_no_packs.pop_back(); + if (!type_constraints.empty()) + type_constraints.pop_back(); std::string user_expr; llvm::raw_string_ostream user_expr_stream(user_expr); @@ -250,6 +397,8 @@ static llvm::Expected MakeGenericSignaturesAndCalls( << *pack_type; } user_expr_stream << ")"; + if (!type_constraints.empty()) + user_expr_stream << "where " << type_constraints; std::string trampoline; llvm::raw_string_ostream trampoline_stream(trampoline); @@ -259,6 +408,8 @@ static llvm::Expected MakeGenericSignaturesAndCalls( if (needs_object_ptr) trampoline_stream << ", _ $__lldb_injected_self: inout $__lldb_context"; trampoline_stream << ")"; + if (!type_constraints.empty()) + trampoline_stream << "where " << type_constraints; std::string sink; std::string call; @@ -291,6 +442,12 @@ static llvm::Expected MakeGenericSignaturesAndCalls( sink_stream << ", _: $__lldb_builtin_ptr_t"; call_stream << ", " << var->GetName().str(); } + + for (auto &wi : *witness_infos) { + sink_stream << ", _: $__lldb_builtin_ptr_t"; + call_stream << ", " << wi.full_name; + } + sink_stream << ")"; call_stream << ")"; diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.h b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.h index 38e2273ab217e..664c3b21f5348 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.h +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.h @@ -19,7 +19,8 @@ namespace lldb_private { /// Parse a name such as "$τ_0_0". std::optional> -ParseSwiftGenericParameter(llvm::StringRef name); +ParseSwiftGenericParameter(llvm::StringRef name, + bool expect_dollar_prefix = true); class SwiftExpressionSourceCode : public ExpressionSourceCode { public: diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp index 4f4a7625e8121..4cfc450a8ab97 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp @@ -330,6 +330,7 @@ static llvm::Error AddVariableInfo( return llvm::Error::success(); CompilerType target_type; + swift::Type unbound_type; bool should_not_bind_generic_types = !SwiftASTManipulator::ShouldBindGenericTypes(bind_generic_types); bool is_unbound_pack = @@ -341,11 +342,17 @@ static llvm::Error AddVariableInfo( // opaque pointer type. This is necessary because we don't bind the generic // parameters, and we can't have a type with unbound generics in a non-generic // function. - if (should_not_bind_generic_types && is_self) + if (should_not_bind_generic_types && is_self) { target_type = ast_context.GetBuiltinRawPointerType(); - else if (is_unbound_pack) + CompilerType var_type = SwiftExpressionParser::ResolveVariable( + variable_sp, stack_frame_sp, runtime, use_dynamic, bind_generic_types); + if (auto unbound_type_or_err = ast_context.GetSwiftType(var_type)) + unbound_type = *unbound_type_or_err; + else + return unbound_type_or_err.takeError(); + } else if (is_unbound_pack) { target_type = variable_sp->GetType()->GetForwardCompilerType(); - else { + } else { CompilerType var_type = SwiftExpressionParser::ResolveVariable( variable_sp, stack_frame_sp, runtime, use_dynamic, bind_generic_types); @@ -436,7 +443,7 @@ static llvm::Error AddVariableInfo( metadata_sp, variable_sp->IsConstant() ? swift::VarDecl::Introducer::Let : swift::VarDecl::Introducer::Var, - false, is_unbound_pack); + false, is_unbound_pack, unbound_type); processed_variables.insert(overridden_name); return llvm::Error::success(); } diff --git a/lldb/test/API/lang/swift/private_generic_type/Private.swift b/lldb/test/API/lang/swift/private_generic_type/Private.swift index d38c9598256b6..d8b829fadc104 100644 --- a/lldb/test/API/lang/swift/private_generic_type/Private.swift +++ b/lldb/test/API/lang/swift/private_generic_type/Private.swift @@ -9,6 +9,43 @@ private class InvisibleClass { public var someNumber = 42 } +private class PImpl: P { + typealias Element = PImpl2 + + private let str = "This is PImpl" + func getElement() -> Element { + PImpl2() + } + func protocolFunc() -> String { + "Hello from PImpl" + } + static func staticProtocolFunc() -> String{ + "Hello from static PImpl" + } +} + +private class PImpl2: P { + typealias Element = P2Impl + + func getElement() -> Element { + return P2Impl() + } + + func protocolFunc() -> String { + "Hello from PImpl2" + } + + static func staticProtocolFunc() -> String{ + "Hello from static PImpl2" + } +} + +private class P2Impl: P2 { + func protocolFunc2() -> String { + "Hello from P2Impl" + } +} + public func privateDoIt() { let structWrapper = StructWrapper(InvisibleStruct()) structWrapper.foo() @@ -34,4 +71,9 @@ public func privateDoIt() { let nestedParameters = Nested.Parameters(InvisibleClass(), InvisibleStruct()) nestedParameters.foo() + + let pHolder = PHolder(t: PImpl()) + pHolder.foo() + + } diff --git a/lldb/test/API/lang/swift/private_generic_type/Public.swift b/lldb/test/API/lang/swift/private_generic_type/Public.swift index f67618eabbe46..f6cdd9c5417e8 100644 --- a/lldb/test/API/lang/swift/private_generic_type/Public.swift +++ b/lldb/test/API/lang/swift/private_generic_type/Public.swift @@ -94,3 +94,28 @@ public struct Nested { } } + +public protocol P { + associatedtype Element + func getElement() -> Element + func protocolFunc() -> String + static func staticProtocolFunc() -> String +} + +public protocol P2 { + func protocolFunc2() -> String +} + + +public struct PHolder where T: P, T.Element: P, T.Element.Element: P2 { + let t: T + + public init(t: T) { + self.t = t + } + + public func foo() { + print(self) // break here for constrained protocol + } +} + diff --git a/lldb/test/API/lang/swift/private_generic_type/TestSwiftPrivateGenericType.py b/lldb/test/API/lang/swift/private_generic_type/TestSwiftPrivateGenericType.py index 5c2283f22e202..ec503e8d0ec67 100644 --- a/lldb/test/API/lang/swift/private_generic_type/TestSwiftPrivateGenericType.py +++ b/lldb/test/API/lang/swift/private_generic_type/TestSwiftPrivateGenericType.py @@ -170,3 +170,31 @@ def test_private_generic_type(self): self.expect("expr --bind-generic-types auto -- self", substrs=["Couldn't realize Swift AST type of self."], error=True) + + breakpoint = target.BreakpointCreateBySourceRegex( + 'break here for constrained protocol', lldb.SBFileSpec('Public.swift'), None) + lldbutil.continue_to_breakpoint(process, breakpoint) + self.expect("expr --bind-generic-types true -- self", + substrs=["Couldn't realize Swift AST type of self."], + error=True) + self.expect("expr --bind-generic-types false -- self", + substrs=["Public.PHolder", + 'str = "This is PImpl"']) + self.expect("expr --bind-generic-types auto -- self", + substrs=["Public.PHolder", + 'str = "This is PImpl"']) + self.expect("expr -- self", + substrs=["Public.PHolder", + 'str = "This is PImpl"']) + self.expect("expr --bind-generic-types false -- t", + substrs=["Private.PImpl", + 'str = "This is PImpl"']) + + self.expect("expr --bind-generic-types false -- t.protocolFunc()", + substrs=["Hello from PImpl"]) + self.expect("expr --bind-generic-types false -- t.getElement().protocolFunc()", + substrs=["Hello from PImpl2"]) + self.expect("expr --bind-generic-types false -- t.getElement().getElement().protocolFunc2()", + substrs=["Hello from P2Impl"]) + +