Skip to content

Commit

Permalink
Use the dynamic type of self in the expression evaluator.
Browse files Browse the repository at this point in the history
This allows LLDB to resolve generic self types even if no type
parameters are present (for example, because they were optimized out)
because class objects and other entities carry all their dynamic type
information in the *object*.

rdar://problem/69020595
  • Loading branch information
adrian-prantl committed Oct 14, 2020
1 parent e161d5c commit eaceb46
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 40 deletions.
1 change: 1 addition & 0 deletions lldb/include/lldb/Symbol/Type.h
Expand Up @@ -56,6 +56,7 @@ class SymbolFileType : public std::enable_shared_from_this<SymbolFileType>,
Type *operator->() { return GetType(); }

Type *GetType();
SymbolFile &GetSymbolFile() const { return m_symbol_file; }

protected:
SymbolFile &m_symbol_file;
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Symbol/Variable.h
Expand Up @@ -65,6 +65,8 @@ class Variable : public UserID, public std::enable_shared_from_this<Variable> {

lldb::ValueType GetScope() const { return m_scope; }

const RangeList &GetScopeRange() const { return m_scope_range; }

bool IsExternal() const { return m_external; }

bool IsArtificial() const { return m_artificial; }
Expand Down
Expand Up @@ -15,6 +15,7 @@
#include "Plugins/TypeSystem/Swift/SwiftASTContext.h"
#include "lldb/Expression/ExpressionParser.h"
#include "lldb/Expression/ExpressionSourceCode.h"
#include "lldb/Target/SwiftLanguageRuntime.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Log.h"
Expand Down Expand Up @@ -1021,11 +1022,6 @@ GetPatternBindingForVarDecl(swift::VarDecl *var_decl,
return pattern_binding;
}

static inline swift::Type GetSwiftType(CompilerType type) {
return swift::Type(
reinterpret_cast<swift::TypeBase *>(type.GetOpaqueQualType()));
}

bool SwiftASTManipulator::AddExternalVariables(
llvm::MutableArrayRef<VariableInfo> variables) {
if (!IsValid())
Expand Down
Expand Up @@ -479,7 +479,7 @@ AddRequiredAliases(Block *block, lldb::StackFrameSP &stack_frame_sp,
if (stack_frame_sp) {
lldb::ValueObjectSP valobj_sp =
stack_frame_sp->GetValueObjectForFrameVariable(self_var_sp,
lldb::eNoDynamicValues);
lldb::eDynamicCanRunTarget);

if (valobj_sp)
self_type = valobj_sp->GetCompilerType();
Expand Down Expand Up @@ -604,7 +604,7 @@ static void AddVariableInfo(
if (stack_frame_sp) {
lldb::ValueObjectSP valobj_sp =
stack_frame_sp->GetValueObjectForFrameVariable(variable_sp,
lldb::eNoDynamicValues);
lldb::eDynamicCanRunTarget);

if (!valobj_sp || valobj_sp->GetError().Fail()) {
// Ignore the variable if we couldn't find its corresponding
Expand All @@ -628,12 +628,6 @@ static void AddVariableInfo(
if (!target_type.IsValid())
return;

// Resolve all archetypes in the variable type.
if (stack_frame_sp)
if (language_runtime)
target_type = language_runtime->BindGenericTypeParameters(*stack_frame_sp,
target_type);

// If we couldn't fully realize the type, then we aren't going
// to get very far making a local out of it, so discard it here.
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TYPES |
Expand All @@ -642,7 +636,7 @@ static void AddVariableInfo(
if (log)
log->Printf("Discarding local %s because we couldn't fully realize it, "
"our best attempt was: %s.",
name_cstr, target_type.GetTypeName().AsCString("<unknown>"));
name_cstr, target_type.GetDisplayTypeName().AsCString("<unknown>"));
return;
}

Expand All @@ -656,8 +650,27 @@ static void AddVariableInfo(
static_cast<void *>(swift_type.getPointer()),
static_cast<void *>(ast_context.GetASTContext()), s.c_str());
}
// A one-off clone of variable_sp with the type replaced by target_type.
auto patched_variable_sp = std::make_shared<lldb_private::Variable>(
0, variable_sp->GetName().GetCString(), "",
std::make_shared<lldb_private::SymbolFileType>(
*variable_sp->GetType()->GetSymbolFile(),
std::make_shared<lldb_private::Type>(
0, variable_sp->GetType()->GetSymbolFile(),
variable_sp->GetType()->GetName(), llvm::None,
variable_sp->GetType()->GetSymbolContextScope(), LLDB_INVALID_UID,
Type::eEncodingIsUID, variable_sp->GetType()->GetDeclaration(),
target_type, lldb_private::Type::ResolveState::Full,
variable_sp->GetType()->GetPayload())),
variable_sp->GetScope(), variable_sp->GetSymbolContextScope(),
variable_sp->GetScopeRange(),
const_cast<lldb_private::Declaration *>(&variable_sp->GetDeclaration()),
variable_sp->LocationExpression(), variable_sp->IsExternal(),
variable_sp->IsArtificial(),
variable_sp->GetLocationIsConstantValueData(),
variable_sp->IsStaticMember(), variable_sp->IsConstant());
SwiftASTManipulatorBase::VariableMetadataSP metadata_sp(
new VariableMetadataVariable(variable_sp));
new VariableMetadataVariable(patched_variable_sp));
SwiftASTManipulator::VariableInfo variable_info(
target_type, ast_context.GetASTContext()->getIdentifier(overridden_name),
metadata_sp,
Expand Down Expand Up @@ -917,6 +930,23 @@ CreateMainFile(SwiftASTContextForExpressions &swift_ast_context,
return {buffer_id, filename.str()};
}

/// Determine whether this type was defined inside an LLDB expression.
template <typename TypeType> bool FromLLDBModuleImpl(TypeType *type) {
if (auto *decl = type->getDecl())
if (auto *module = decl->getModuleContext())
return module->getName().str().startswith("__lldb_expr_");
return false;
};

/// Determine whether this type was defined inside an LLDB expression.
static bool FromLLDBModule(swift::TypeBase *type) {
if (auto *type_alias = llvm::dyn_cast<swift::TypeAliasType>(type))
return FromLLDBModuleImpl(type_alias);
if (auto *nominal = llvm::dyn_cast<swift::NominalType>(type))
return FromLLDBModuleImpl(nominal);
return false;
}

/// Attempt to materialize one variable.
static llvm::Optional<SwiftExpressionParser::SILVariableInfo>
MaterializeVariable(SwiftASTManipulatorBase::VariableInfo &variable,
Expand Down Expand Up @@ -954,23 +984,7 @@ MaterializeVariable(SwiftASTManipulatorBase::VariableInfo &variable,
}
}
} else {
CompilerType actual_type(variable.GetType());
auto orig_swift_type = GetSwiftType(actual_type);
auto *swift_type = orig_swift_type->mapTypeOutOfContext().getPointer();
actual_type = ToCompilerType(swift_type);
lldb::StackFrameSP stack_frame_sp = stack_frame_wp.lock();
if (swift_type->hasTypeParameter()) {
if (stack_frame_sp && stack_frame_sp->GetThread() &&
stack_frame_sp->GetThread()->GetProcess()) {
auto *swift_runtime = SwiftLanguageRuntime::Get(
stack_frame_sp->GetThread()->GetProcess());
if (swift_runtime) {
actual_type = swift_runtime->BindGenericTypeParameters(
*stack_frame_sp, actual_type);
}
}
}

CompilerType actual_type = variable.GetType();
// Desugar '$lldb_context', etc.
auto transformed_type = GetSwiftType(actual_type).transform(
[](swift::Type t) -> swift::Type {
Expand All @@ -981,7 +995,21 @@ MaterializeVariable(SwiftASTManipulatorBase::VariableInfo &variable,
}
return t;
});
actual_type = ToCompilerType(transformed_type.getPointer());
actual_type =
ToCompilerType(transformed_type->mapTypeOutOfContext().getPointer());
// CompilerType return_ast_type =
// ToCompilerType(result_type->mapTypeOutOfContext());
auto *swift_ast_ctx =
llvm::cast<SwiftASTContext>(actual_type.GetTypeSystem());

// Currently the Swift runtime cannot resolve types that were
// defined in the expression evaluator. That's because we don't
// tell it about type metadata sections that were JIT-compiled
// by the expression evaluator. Until that is implemented, fall
// back to SwiftASTContext.
if (!FromLLDBModule(transformed_type.getPointer()))
actual_type =
swift_ast_ctx->GetTypeRefType(actual_type.GetOpaqueQualType());

if (is_result)
offset = materializer.AddResultVariable(
Expand Down Expand Up @@ -1009,9 +1037,6 @@ MaterializeVariable(SwiftASTManipulatorBase::VariableInfo &variable,
VariableMetadataVariable *variable_metadata =
static_cast<VariableMetadataVariable *>(variable.m_metadata.get());

// FIXME: It would be nice if we could do something like
// variable_metadata->m_variable_sp->SetType(variable.GetType())
// here.
offset = materializer.AddVariable(variable_metadata->m_variable_sp, error);

if (!error.Success()) {
Expand Down
58 changes: 54 additions & 4 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp
Expand Up @@ -1250,6 +1250,19 @@ bool Equivalent<llvm::Optional<uint64_t>>(llvm::Optional<uint64_t> l,
return false;
}

/// Version taylored to GetTypeBitAlign.
template <>
bool Equivalent<llvm::Optional<size_t>>(llvm::Optional<size_t> l,
llvm::Optional<size_t> r) {
if (l == r)
return true;
// Assume that any value is "better" than none.
if (l.hasValue() && !r.hasValue())
return true;
llvm::dbgs() << l << " != " << r << "\n";
return false;
}

} // namespace
#endif

Expand Down Expand Up @@ -1615,13 +1628,35 @@ TypeSystemSwiftTypeRef::GetArrayElementType(opaque_compiler_type_t type,
VALIDATE_AND_RETURN(impl, GetArrayElementType, type,
(ReconstructType(type), nullptr, exe_scope));
}

/// Determine wether this demangle tree contains an unresolved type alias.
static bool ContainsUnresolvedTypeAlias(swift::Demangle::NodePointer node) {
if (!node)
return false;

if (node->getKind() == swift::Demangle::Node::Kind::TypeAlias)
return true;

for (swift::Demangle::NodePointer child : *node)
if (ContainsUnresolvedTypeAlias(child))
return true;

return false;
}

CompilerType
TypeSystemSwiftTypeRef::GetCanonicalType(opaque_compiler_type_t type) {
auto impl = [&]() {
using namespace swift::Demangle;
Demangler dem;
NodePointer canonical =
GetCanonicalDemangleTree(m_swift_ast_context, dem, AsMangledName(type));
if (ContainsUnresolvedTypeAlias(canonical)) {
// If this is a typealias defined in the expression evaluator,
// then we don't have debug info to resolve it from.
CompilerType ast_type = ReconstructType({this, type}).GetCanonicalType();
return GetTypeFromMangledTypename(ast_type.GetMangledTypeName());
}
ConstString mangled(mangleNode(canonical));
return GetTypeFromMangledTypename(mangled);
};
Expand Down Expand Up @@ -1750,9 +1785,17 @@ TypeSystemSwiftTypeRef::GetBitSize(opaque_compiler_type_t type,
AsMangledName(type));
return {};
}
// The hot code path is to ask the Swift runtime for the size.
if (auto *runtime =
SwiftLanguageRuntime::Get(exe_scope->CalculateProcess()))
return runtime->GetBitSize({this, type}, exe_scope);
SwiftLanguageRuntime::Get(exe_scope->CalculateProcess())) {
if (auto result = runtime->GetBitSize({this, type}, exe_scope))
return result;
// If this is an expression context, perhaps the type was
// defined in the expression. In that case we don't have debug
// info for it, so defer to SwiftASTContext.
if (llvm::isa<SwiftASTContextForExpressions>(m_swift_ast_context))
return ReconstructType({this, type}).GetBitSize(exe_scope);
}

// If there is no process, we can still try to get the static size
// information out of DWARF. Because it is stored in the Type
Expand Down Expand Up @@ -2078,8 +2121,15 @@ TypeSystemSwiftTypeRef::GetTypeBitAlign(opaque_compiler_type_t type,
return {};
}
if (auto *runtime =
SwiftLanguageRuntime::Get(exe_scope->CalculateProcess()))
return runtime->GetBitAlignment({this, type}, exe_scope);
SwiftLanguageRuntime::Get(exe_scope->CalculateProcess())) {
if (auto result = runtime->GetBitAlignment({this, type}, exe_scope))
return result;
// If this is an expression context, perhaps the type was
// defined in the expression. In that case we don't have debug
// info for it, so defer to SwiftASTContext.
if (llvm::isa<SwiftASTContextForExpressions>(m_swift_ast_context))
return ReconstructType({this, type}).GetTypeBitAlign(exe_scope);
}

// If there is no process, we can still try to get the static
// alignment information out of DWARF. Because it is stored in the
Expand Down
Expand Up @@ -2141,6 +2141,9 @@ SwiftLanguageRuntimeImpl::GetTypeInfo(CompilerType type,
if (!ts)
return nullptr;

// Resolve all type aliases.
type = type.GetCanonicalType();

// Resolve all generic type parameters in the type for the current
// frame. Archetype binding has to happen in the scratch context,
// so we lock it while we are in this function.
Expand Down
4 changes: 4 additions & 0 deletions lldb/test/API/lang/swift/generic_class/Makefile
@@ -0,0 +1,4 @@
SWIFT_SOURCES := main.swift
SWIFTFLAGS_EXTRAS := -O

include Makefile.rules
21 changes: 21 additions & 0 deletions lldb/test/API/lang/swift/generic_class/TestSwiftGenericClass.py
@@ -0,0 +1,21 @@
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
import unittest2


class SwiftGenericClassTest(TestBase):

mydir = TestBase.compute_mydir(__file__)

def test(self):
"""Tests that a generic class type can be resolved from the instance metadata alone"""
self.build()
(target, process, thread, breakpoint) = lldbutil.run_to_source_breakpoint(self,
"break here", lldb.SBFileSpec("main.swift"))

self.expect("frame variable -d run self",
substrs=["a.F<Int>", "23", "42", "128", "256"])
self.expect("expr -d run -- self",
substrs=["a.F<Int>", "23", "42", "128", "256"])
25 changes: 25 additions & 0 deletions lldb/test/API/lang/swift/generic_class/main.swift
@@ -0,0 +1,25 @@
public class F<A> {
let a : A
var b = 42
var c = 128
var d = 256
public init(_ val : A) { a = val }
}

protocol P {
func method()
}
extension F : P {
@inline(never) func method() {
print("break here \(b)")
}
}

// Defeat type specialization.
@inline(never) func getF() -> P {
return F<Int>(23)
}

let obj = getF()
obj.method()

0 comments on commit eaceb46

Please sign in to comment.