Skip to content

[lldb] Implement support for constrained generics in generic expression #10586

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

Open
wants to merge 1 commit into
base: stable/20240723
Choose a base branch
from
Open
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 @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> instead of Array<Int>). 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); }
Expand All @@ -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";
}
Expand All @@ -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) {}
Expand All @@ -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;
}

Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,7 @@ AddArchetypeTypeAliases(std::unique_ptr<SwiftASTManipulator> &code_manipulator,
llvm::SmallDenseMap<llvm::StringRef, MetadataPointerInfo>
visible_metadata_pointers;
for (auto &variable : code_manipulator->GetVariableInfo()) {
if (!variable.IsMetadataPointer())
if (!variable.IsMetadataPointer() && !variable.IsWitnessTable())
continue;

llvm::StringRef type_name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -30,8 +32,11 @@ using namespace lldb_private;

namespace lldb_private {
std::optional<std::pair<unsigned, unsigned>>
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('_');
Expand Down Expand Up @@ -165,6 +170,127 @@ static llvm::Expected<llvm::SmallVector<MetadataInfo>> CollectMetadataInfos(
}
return metadata_info;
}

/// Returns a map from the index and depth to the archetype name, for example,
/// given: struct S<T, U> {} This function returns {{0, 0} -> T, {0, 1} -> U}.
static llvm::DenseMap<std::pair<unsigned, unsigned>, llvm::StringRef>
MakeIndexAndDepthToArchetypeMap(
llvm::ArrayRef<swift::Requirement> requirements) {
llvm::DenseMap<std::pair<unsigned, unsigned>, llvm::StringRef> map;
for (auto &req : requirements) {
if (req.getKind() != swift::RequirementKind::Conformance)
continue;
auto type = req.getFirstType();
auto *generic_type =
llvm::dyn_cast<swift::GenericTypeParamType>(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<llvm::SmallVector<ParsedWitnessTable>> ParseWitnessInfos(
llvm::ArrayRef<SwiftASTManipulator::VariableInfo> local_variables,
llvm::ArrayRef<swift::Requirement> requirements) {
llvm::SmallVector<ParsedWitnessTable> 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<ParsedWitnessTable> 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<swift::GenericTypeParamType>(type.getPointer()))
name = generic_type->getName().str();
else if (auto *dependent =
llvm::dyn_cast<swift::DependentMemberType>(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
Expand Down Expand Up @@ -208,10 +334,25 @@ static llvm::Expected<CallsAndArgs> 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<swift::NominalOrBoundGenericNominalType>(
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<std::pair<unsigned, unsigned>, llvm::SmallString<4>> subs;
std::string generic_params;
Expand All @@ -230,11 +371,17 @@ static llvm::Expected<CallsAndArgs> 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);
Expand All @@ -250,6 +397,8 @@ static llvm::Expected<CallsAndArgs> 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);
Expand All @@ -259,6 +408,8 @@ static llvm::Expected<CallsAndArgs> 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;
Expand Down Expand Up @@ -291,6 +442,12 @@ static llvm::Expected<CallsAndArgs> 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 << ")";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ namespace lldb_private {

/// Parse a name such as "$τ_0_0".
std::optional<std::pair<unsigned, unsigned>>
ParseSwiftGenericParameter(llvm::StringRef name);
ParseSwiftGenericParameter(llvm::StringRef name,
bool expect_dollar_prefix = true);

class SwiftExpressionSourceCode : public ExpressionSourceCode {
public:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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);

Expand Down Expand Up @@ -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();
}
Expand Down
Loading