Skip to content
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
4 changes: 3 additions & 1 deletion lldb/include/lldb/ValueObject/DILAST.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ enum class UnaryOpKind {

/// The type casts allowed by DIL.
enum class CastKind {
eArithmetic, ///< Casting to a scalar.
eEnumeration, ///< Casting from a scalar to an enumeration type
eNullptr, ///< Casting to a nullptr type
ePointer, ///< Casting to a pointer type.
eReference, ///< Casting to a reference type
eNone, ///< Type promotion casting
eNone, ///< Invalid promotion type (results in error).
};

/// Forward declaration, for use in DIL AST nodes. Definition is at the very
Expand Down
16 changes: 16 additions & 0 deletions lldb/include/lldb/ValueObject/DILEval.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ class Interpreter : Visitor {
std::shared_ptr<ExecutionContextScope> ctx,
const IntegerLiteralNode *literal);

/// A helper function for VerifyCastType (below). This performs
/// arithmetic-specific checks. It should only be called if the target_type
/// is a scalar type.
llvm::Expected<CastKind> VerifyArithmeticCast(CompilerType source_type,
CompilerType target_type,
int location);

/// As a preparation for type casting, compare the requested 'target' type
/// of the cast with the type of the operand to be cast. If the cast is
/// allowed, return the appropriate CastKind for the cast; otherwise return
/// an error.
llvm::Expected<CastKind> VerifyCastType(lldb::ValueObjectSP operand,
CompilerType source_type,
CompilerType target_type,
int location);

// Used by the interpreter to create objects, perform casts, etc.
lldb::TargetSP m_target;
llvm::StringRef m_expr;
Expand Down
232 changes: 224 additions & 8 deletions lldb/source/ValueObject/DILEval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@

namespace lldb_private::dil {

lldb::ValueObjectSP
GetDynamicOrSyntheticValue(lldb::ValueObjectSP value_sp,
lldb::DynamicValueType use_dynamic,
bool use_synthetic) {
if (!value_sp)
return nullptr;

if (use_dynamic != lldb::eNoDynamicValues) {
lldb::ValueObjectSP dynamic_sp = value_sp->GetDynamicValue(use_dynamic);
if (dynamic_sp)
value_sp = dynamic_sp;
}

if (use_synthetic) {
lldb::ValueObjectSP synthetic_sp = value_sp->GetSyntheticValue();
if (synthetic_sp)
value_sp = synthetic_sp;
}

return value_sp;
}

static llvm::Expected<lldb::TypeSystemSP>
GetTypeSystemFromCU(std::shared_ptr<ExecutionContextScope> ctx) {
auto stack_frame = ctx->CalculateStackFrame();
Expand All @@ -42,13 +64,14 @@ static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
return CompilerType();
}

static lldb::ValueObjectSP
ArrayToPointerConversion(ValueObject &valobj, ExecutionContextScope &ctx) {
static lldb::ValueObjectSP ArrayToPointerConversion(ValueObject &valobj,
ExecutionContextScope &ctx,
llvm::StringRef name) {
uint64_t addr = valobj.GetLoadAddress();
ExecutionContext exe_ctx;
ctx.CalculateExecutionContext(exe_ctx);
return ValueObject::CreateValueObjectFromAddress(
"result", addr, exe_ctx,
name, addr, exe_ctx,
valobj.GetCompilerType().GetArrayElementType(&ctx).GetPointerType(),
/* do_deref */ false);
}
Expand Down Expand Up @@ -100,7 +123,7 @@ Interpreter::UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location) {
}

if (in_type.IsArrayType())
valobj = ArrayToPointerConversion(*valobj, *m_exe_ctx_scope);
valobj = ArrayToPointerConversion(*valobj, *m_exe_ctx_scope, "result");

if (valobj->GetCompilerType().IsInteger() ||
valobj->GetCompilerType().IsUnscopedEnumerationType()) {
Expand Down Expand Up @@ -740,16 +763,209 @@ Interpreter::Visit(const BooleanLiteralNode *node) {
return ValueObject::CreateValueObjectFromBool(m_target, value, "result");
}

llvm::Expected<CastKind>
Interpreter::VerifyArithmeticCast(CompilerType source_type,
CompilerType target_type, int location) {
if (source_type.IsPointerType() || source_type.IsNullPtrType()) {
// Cast from pointer to float/double is not allowed.
if (target_type.IsFloat()) {
std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed",
source_type.TypeDescription(),
target_type.TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
source_type.TypeDescription().length());
}

// Casting from pointer to bool is always valid.
if (target_type.IsBoolean())
return CastKind::eArithmetic;

// Otherwise check if the result type is at least as big as the pointer
// size.
uint64_t type_byte_size = 0;
uint64_t rhs_type_byte_size = 0;
if (auto temp = target_type.GetByteSize(m_exe_ctx_scope.get())) {
type_byte_size = *temp;
} else {
std::string errMsg = llvm::formatv("unable to get byte size for type {0}",
target_type.TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
target_type.TypeDescription().length());
}

if (auto temp = source_type.GetByteSize(m_exe_ctx_scope.get())) {
rhs_type_byte_size = *temp;
} else {
std::string errMsg = llvm::formatv("unable to get byte size for type {0}",
source_type.TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
source_type.TypeDescription().length());
}

if (type_byte_size < rhs_type_byte_size) {
std::string errMsg = llvm::formatv(
"cast from pointer to smaller type {0} loses information",
target_type.TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
source_type.TypeDescription().length());
}
} else if (!source_type.IsScalarType() && !source_type.IsEnumerationType()) {
// Otherwise accept only arithmetic types and enums.
std::string errMsg = llvm::formatv("cannot convert {0} to {1}",
source_type.TypeDescription(),
target_type.TypeDescription());

return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
source_type.TypeDescription().length());
}
return CastKind::eArithmetic;
}

llvm::Expected<CastKind>
Interpreter::VerifyCastType(lldb::ValueObjectSP operand,
CompilerType source_type, CompilerType target_type,
int location) {

if (target_type.IsScalarType())
return VerifyArithmeticCast(source_type, target_type, location);

if (target_type.IsEnumerationType()) {
// Cast to enum type.
if (!source_type.IsScalarType() && !source_type.IsEnumerationType()) {
std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed",
source_type.TypeDescription(),
target_type.TypeDescription());

return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
source_type.TypeDescription().length());
}
return CastKind::eEnumeration;
}

if (target_type.IsPointerType()) {
if (!source_type.IsInteger() && !source_type.IsEnumerationType() &&
!source_type.IsArrayType() && !source_type.IsPointerType() &&
!source_type.IsNullPtrType()) {
std::string errMsg = llvm::formatv(
"cannot cast from type {0} to pointer type {1}",
source_type.TypeDescription(), target_type.TypeDescription());

return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
source_type.TypeDescription().length());
}
return CastKind::ePointer;
}

if (target_type.IsNullPtrType()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have tests for the nullptr case? I'm tempted to suggest to not support casting to nullptr_t at all for now because it's not a cast the C++ allows (i.e., I'm pretty sure you can't cast to std::nullptr_t). What are the use-cases you had in mind?

I'd suggest removing this entire block.

// Cast to nullptr type.
bool is_signed;
if (!source_type.IsNullPtrType() &&
(!operand->IsIntegerType(is_signed) ||
(is_signed && operand->GetValueAsSigned(0) != 0) ||
(!is_signed && operand->GetValueAsUnsigned(0) != 0))) {
std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed",
source_type.TypeDescription(),
target_type.TypeDescription());
Comment on lines +867 to +875
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move the IsNullPtrType into a separate if-block for readability:

Suggested change
// Cast to nullptr type.
bool is_signed;
if (!source_type.IsNullPtrType() &&
(!operand->IsIntegerType(is_signed) ||
(is_signed && operand->GetValueAsSigned(0) != 0) ||
(!is_signed && operand->GetValueAsUnsigned(0) != 0))) {
std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed",
source_type.TypeDescription(),
target_type.TypeDescription());
// Cast to nullptr type.
if (source_type.IsNullPtrType())
return CastKind::eNullptr;
bool is_signed;
if (!operand->IsIntegerType(is_signed) ||
(is_signed && operand->GetValueAsSigned(0) != 0) ||
(!is_signed && operand->GetValueAsUnsigned(0) != 0)) {
std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed",
source_type.TypeDescription(),
target_type.TypeDescription());


return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
source_type.TypeDescription().length());
}
return CastKind::eNullptr;
}

if (target_type.IsReferenceType())
return CastKind::eReference;

// Unsupported cast.
std::string errMsg = llvm::formatv(
"casting of {0} to {1} is not implemented yet",
source_type.TypeDescription(), target_type.TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, std::move(errMsg), location,
source_type.TypeDescription().length());
}

llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const CastNode *node) {
auto operand_or_err = Evaluate(node->GetOperand());
if (!operand_or_err)
return operand_or_err;

lldb::ValueObjectSP operand = *operand_or_err;
// Don't actually do the cast for now -- that code will be added later.
// For now just return an error message.
return llvm::make_error<DILDiagnosticError>(
m_expr, "Type casting is not supported here.", node->GetLocation());
CompilerType op_type = operand->GetCompilerType();
CompilerType target_type = node->GetType();

if (op_type.IsReferenceType())
op_type = op_type.GetNonReferenceType();
if (target_type.IsScalarType() && op_type.IsArrayType()) {
operand = ArrayToPointerConversion(*operand, *m_exe_ctx_scope,
operand->GetName().GetStringRef());
op_type = operand->GetCompilerType();
}
auto type_or_err =
VerifyCastType(operand, op_type, target_type, node->GetLocation());
if (!type_or_err)
return type_or_err.takeError();

CastKind cast_kind = *type_or_err;
if (operand->GetCompilerType().IsReferenceType()) {
Status error;
operand = operand->Dereference(error);
if (error.Fail())
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
node->GetLocation());
}

switch (cast_kind) {
case CastKind::eEnumeration: {
if (op_type.IsFloat() || op_type.IsInteger() || op_type.IsEnumerationType())
return operand->CastToEnumType(target_type);
break;
}
case CastKind::eNullptr: {
return ValueObject::CreateValueObjectFromNullptr(m_target, target_type,
"result");
}
case CastKind::eReference: {
lldb::ValueObjectSP operand_sp(
GetDynamicOrSyntheticValue(operand, m_use_dynamic, m_use_synthetic));
return lldb::ValueObjectSP(
operand_sp->Cast(target_type.GetNonReferenceType()));
Comment on lines +939 to +940
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we be creating a reference type? Why the target_type.GetNonReferenceType?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I don't get the non-reference type, then I end up with strange (and/or incorrect) results. Things like:

(lldb) v &(int&)arr_1d
(int &) &arr_1d = 0x00007fffffffddc0
notice the odd "int &
" type

or
(lldb) v (int&)arr_1d
(int &) arr_1d = 0x0000000200000001 (&arr_1d = <read memory from 0x200000001 failed (0 of 4 bytes read)>)

versus when I get the non-reference type:
(lldb) v (int&)arr_1d
(int) arr_1d = 1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

versus when I get the non-reference type:
(lldb) v (int&)arr_1d
(int) arr_1d = 1

I would've expected (int&) arr_1d = 1 here. Is there a way to do that?

Here is how we currently present reference types (this is using the DIL-based frame var):

(lldb) v b
(int &) b = 0x000000016fdfe8dc (&b = 5)

I.e., the type itself is (int &). So we print the memory location it references and the value of the referenced object.

So I think the DIL cast should be consistent with it. In your example, I would expect:

(lldb) v (int&)arr_1d
(int &) arr_1d = 0x0000000200000001 (&arr_1d = 1)

Do we understand why the memory location in your example fails to be read?

}
case CastKind::eArithmetic: {
if (op_type.IsPointerType() || op_type.IsNullPtrType() ||
op_type.IsScalarType() || op_type.IsEnumerationType())
return operand->CastToBasicType(target_type);
break;
}
case CastKind::ePointer: {
uint64_t addr = op_type.IsArrayType()
? operand->GetLoadAddress()
: (op_type.IsSigned() ? operand->GetValueAsSigned(0)
: operand->GetValueAsUnsigned(0));
llvm::StringRef name = "result";
ExecutionContext exe_ctx(m_target.get(), false);
return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx,
target_type,
/* do_deref */ false);
}
case CastKind::eNone: {
return lldb::ValueObjectSP();
}
} // switch

std::string errMsg =
llvm::formatv("unable to cast from '{0}' to '{1}'",
op_type.TypeDescription(), target_type.TypeDescription());
return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
node->GetLocation());
}

} // namespace lldb_private::dil
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ def test_frame_var(self):
self.expect_var_path("a", value="1")
self.expect_var_path("b", value="2")
self.expect_var_path("c", value="'\\xfd'")
self.expect_var_path("(int)c", value="-3")
self.expect_var_path("s", value="4")
3 changes: 3 additions & 0 deletions lldb/test/API/commands/frame/var-dil/expr/Casts/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Loading
Loading