Skip to content

Commit 539cf92

Browse files
authored
[LLDB] Add type casting to DIL, part 2 or 3 (llvm#170332)
This PR implements the actual type casting part. With this, type casting to builtin types should work. The third PR, which will be put up after this one is merged, will expand the type name parsing to allow casting to user-defined types.
1 parent 94aa4f0 commit 539cf92

7 files changed

Lines changed: 491 additions & 11 deletions

File tree

lldb/include/lldb/ValueObject/DILAST.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ enum class UnaryOpKind {
4040

4141
/// The type casts allowed by DIL.
4242
enum class CastKind {
43+
eArithmetic, ///< Casting to a scalar.
4344
eEnumeration, ///< Casting from a scalar to an enumeration type
44-
eNullptr, ///< Casting to a nullptr type
45-
eReference, ///< Casting to a reference type
46-
eNone, ///< Type promotion casting
45+
ePointer, ///< Casting to a pointer type.
46+
eNone, ///< Invalid promotion type (results in error).
4747
};
4848

4949
/// Forward declaration, for use in DIL AST nodes. Definition is at the very

lldb/include/lldb/ValueObject/DILEval.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ class Interpreter : Visitor {
7878
std::shared_ptr<ExecutionContextScope> ctx,
7979
const IntegerLiteralNode &literal);
8080

81+
/// A helper function for VerifyCastType (below). This performs
82+
/// arithmetic-specific checks. It should only be called if the target_type
83+
/// is a scalar type.
84+
llvm::Expected<CastKind> VerifyArithmeticCast(CompilerType source_type,
85+
CompilerType target_type,
86+
int location);
87+
88+
/// As a preparation for type casting, compare the requested 'target' type
89+
/// of the cast with the type of the operand to be cast. If the cast is
90+
/// allowed, return the appropriate CastKind for the cast; otherwise return
91+
/// an error.
92+
llvm::Expected<CastKind> VerifyCastType(lldb::ValueObjectSP operand,
93+
CompilerType source_type,
94+
CompilerType target_type,
95+
int location);
96+
8197
// Used by the interpreter to create objects, perform casts, etc.
8298
lldb::TargetSP m_target;
8399
llvm::StringRef m_expr;

lldb/source/ValueObject/DILEval.cpp

Lines changed: 194 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,28 @@
2121

2222
namespace lldb_private::dil {
2323

24+
lldb::ValueObjectSP
25+
GetDynamicOrSyntheticValue(lldb::ValueObjectSP value_sp,
26+
lldb::DynamicValueType use_dynamic,
27+
bool use_synthetic) {
28+
if (!value_sp)
29+
return nullptr;
30+
31+
if (use_dynamic != lldb::eNoDynamicValues) {
32+
lldb::ValueObjectSP dynamic_sp = value_sp->GetDynamicValue(use_dynamic);
33+
if (dynamic_sp)
34+
value_sp = dynamic_sp;
35+
}
36+
37+
if (use_synthetic) {
38+
lldb::ValueObjectSP synthetic_sp = value_sp->GetSyntheticValue();
39+
if (synthetic_sp)
40+
value_sp = synthetic_sp;
41+
}
42+
43+
return value_sp;
44+
}
45+
2446
static llvm::Expected<lldb::TypeSystemSP>
2547
GetTypeSystemFromCU(std::shared_ptr<ExecutionContextScope> ctx) {
2648
auto stack_frame = ctx->CalculateStackFrame();
@@ -42,13 +64,14 @@ static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
4264
return CompilerType();
4365
}
4466

45-
static lldb::ValueObjectSP
46-
ArrayToPointerConversion(ValueObject &valobj, ExecutionContextScope &ctx) {
67+
static lldb::ValueObjectSP ArrayToPointerConversion(ValueObject &valobj,
68+
ExecutionContextScope &ctx,
69+
llvm::StringRef name) {
4770
uint64_t addr = valobj.GetLoadAddress();
4871
ExecutionContext exe_ctx;
4972
ctx.CalculateExecutionContext(exe_ctx);
5073
return ValueObject::CreateValueObjectFromAddress(
51-
"result", addr, exe_ctx,
74+
name, addr, exe_ctx,
5275
valobj.GetCompilerType().GetArrayElementType(&ctx).GetPointerType(),
5376
/* do_deref */ false);
5477
}
@@ -100,7 +123,7 @@ Interpreter::UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location) {
100123
}
101124

102125
if (in_type.IsArrayType())
103-
valobj = ArrayToPointerConversion(*valobj, *m_exe_ctx_scope);
126+
valobj = ArrayToPointerConversion(*valobj, *m_exe_ctx_scope, "result");
104127

105128
if (valobj->GetCompilerType().IsInteger() ||
106129
valobj->GetCompilerType().IsUnscopedEnumerationType()) {
@@ -783,16 +806,179 @@ Interpreter::Visit(const BooleanLiteralNode &node) {
783806
return ValueObject::CreateValueObjectFromBool(m_target, value, "result");
784807
}
785808

809+
llvm::Expected<CastKind>
810+
Interpreter::VerifyArithmeticCast(CompilerType source_type,
811+
CompilerType target_type, int location) {
812+
if (source_type.IsPointerType() || source_type.IsNullPtrType()) {
813+
// Cast from pointer to float/double is not allowed.
814+
if (target_type.IsFloat()) {
815+
std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed",
816+
source_type.TypeDescription(),
817+
target_type.TypeDescription());
818+
return llvm::make_error<DILDiagnosticError>(
819+
m_expr, std::move(errMsg), location,
820+
source_type.TypeDescription().length());
821+
}
822+
823+
// Casting from pointer to bool is always valid.
824+
if (target_type.IsBoolean())
825+
return CastKind::eArithmetic;
826+
827+
// Otherwise check if the result type is at least as big as the pointer
828+
// size.
829+
uint64_t type_byte_size = 0;
830+
uint64_t rhs_type_byte_size = 0;
831+
if (auto temp = target_type.GetByteSize(m_exe_ctx_scope.get())) {
832+
type_byte_size = *temp;
833+
} else {
834+
std::string errMsg = llvm::formatv("unable to get byte size for type {0}",
835+
target_type.TypeDescription());
836+
return llvm::make_error<DILDiagnosticError>(
837+
m_expr, std::move(errMsg), location,
838+
target_type.TypeDescription().length());
839+
}
840+
841+
if (auto temp = source_type.GetByteSize(m_exe_ctx_scope.get())) {
842+
rhs_type_byte_size = *temp;
843+
} else {
844+
std::string errMsg = llvm::formatv("unable to get byte size for type {0}",
845+
source_type.TypeDescription());
846+
return llvm::make_error<DILDiagnosticError>(
847+
m_expr, std::move(errMsg), location,
848+
source_type.TypeDescription().length());
849+
}
850+
851+
if (type_byte_size < rhs_type_byte_size) {
852+
std::string errMsg = llvm::formatv(
853+
"cast from pointer to smaller type {0} loses information",
854+
target_type.TypeDescription());
855+
return llvm::make_error<DILDiagnosticError>(
856+
m_expr, std::move(errMsg), location,
857+
source_type.TypeDescription().length());
858+
}
859+
} else if (!source_type.IsScalarType() && !source_type.IsEnumerationType()) {
860+
// Otherwise accept only arithmetic types and enums.
861+
std::string errMsg = llvm::formatv("cannot convert {0} to {1}",
862+
source_type.TypeDescription(),
863+
target_type.TypeDescription());
864+
865+
return llvm::make_error<DILDiagnosticError>(
866+
m_expr, std::move(errMsg), location,
867+
source_type.TypeDescription().length());
868+
}
869+
return CastKind::eArithmetic;
870+
}
871+
872+
llvm::Expected<CastKind>
873+
Interpreter::VerifyCastType(lldb::ValueObjectSP operand,
874+
CompilerType source_type, CompilerType target_type,
875+
int location) {
876+
877+
if (target_type.IsScalarType())
878+
return VerifyArithmeticCast(source_type, target_type, location);
879+
880+
if (target_type.IsEnumerationType()) {
881+
// Cast to enum type.
882+
if (!source_type.IsScalarType() && !source_type.IsEnumerationType()) {
883+
std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed",
884+
source_type.TypeDescription(),
885+
target_type.TypeDescription());
886+
887+
return llvm::make_error<DILDiagnosticError>(
888+
m_expr, std::move(errMsg), location,
889+
source_type.TypeDescription().length());
890+
}
891+
return CastKind::eEnumeration;
892+
}
893+
894+
if (target_type.IsPointerType()) {
895+
if (!source_type.IsInteger() && !source_type.IsEnumerationType() &&
896+
!source_type.IsArrayType() && !source_type.IsPointerType() &&
897+
!source_type.IsNullPtrType()) {
898+
std::string errMsg = llvm::formatv(
899+
"cannot cast from type {0} to pointer type {1}",
900+
source_type.TypeDescription(), target_type.TypeDescription());
901+
902+
return llvm::make_error<DILDiagnosticError>(
903+
m_expr, std::move(errMsg), location,
904+
source_type.TypeDescription().length());
905+
}
906+
return CastKind::ePointer;
907+
}
908+
909+
// Unsupported cast.
910+
std::string errMsg = llvm::formatv(
911+
"casting of {0} to {1} is not implemented yet",
912+
source_type.TypeDescription(), target_type.TypeDescription());
913+
return llvm::make_error<DILDiagnosticError>(
914+
m_expr, std::move(errMsg), location,
915+
source_type.TypeDescription().length());
916+
}
917+
786918
llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const CastNode &node) {
787919
auto operand_or_err = Evaluate(node.GetOperand());
920+
788921
if (!operand_or_err)
789922
return operand_or_err;
790923

791924
lldb::ValueObjectSP operand = *operand_or_err;
792-
// Don't actually do the cast for now -- that code will be added later.
793-
// For now just return an error message.
794-
return llvm::make_error<DILDiagnosticError>(
795-
m_expr, "Type casting is not supported here.", node.GetLocation());
925+
CompilerType op_type = operand->GetCompilerType();
926+
CompilerType target_type = node.GetType();
927+
928+
if (op_type.IsReferenceType())
929+
op_type = op_type.GetNonReferenceType();
930+
if (target_type.IsScalarType() && op_type.IsArrayType()) {
931+
operand = ArrayToPointerConversion(*operand, *m_exe_ctx_scope,
932+
operand->GetName().GetStringRef());
933+
op_type = operand->GetCompilerType();
934+
}
935+
auto type_or_err =
936+
VerifyCastType(operand, op_type, target_type, node.GetLocation());
937+
if (!type_or_err)
938+
return type_or_err.takeError();
939+
940+
CastKind cast_kind = *type_or_err;
941+
if (operand->GetCompilerType().IsReferenceType()) {
942+
Status error;
943+
operand = operand->Dereference(error);
944+
if (error.Fail())
945+
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
946+
node.GetLocation());
947+
}
948+
949+
switch (cast_kind) {
950+
case CastKind::eEnumeration: {
951+
if (op_type.IsFloat() || op_type.IsInteger() || op_type.IsEnumerationType())
952+
return operand->CastToEnumType(target_type);
953+
break;
954+
}
955+
case CastKind::eArithmetic: {
956+
if (op_type.IsPointerType() || op_type.IsNullPtrType() ||
957+
op_type.IsScalarType() || op_type.IsEnumerationType())
958+
return operand->CastToBasicType(target_type);
959+
break;
960+
}
961+
case CastKind::ePointer: {
962+
uint64_t addr = op_type.IsArrayType()
963+
? operand->GetLoadAddress()
964+
: (op_type.IsSigned() ? operand->GetValueAsSigned(0)
965+
: operand->GetValueAsUnsigned(0));
966+
llvm::StringRef name = "result";
967+
ExecutionContext exe_ctx(m_target.get(), false);
968+
return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx,
969+
target_type,
970+
/* do_deref */ false);
971+
}
972+
case CastKind::eNone: {
973+
return lldb::ValueObjectSP();
974+
}
975+
} // switch
976+
977+
std::string errMsg =
978+
llvm::formatv("unable to cast from '{0}' to '{1}'",
979+
op_type.TypeDescription(), target_type.TypeDescription());
980+
return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
981+
node.GetLocation());
796982
}
797983

798984
} // namespace lldb_private::dil

lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ def test_frame_var(self):
2828
self.expect_var_path("a", value="1")
2929
self.expect_var_path("b", value="2")
3030
self.expect_var_path("c", value="'\\xfd'")
31+
self.expect_var_path("(int)c", value="-3")
3132
self.expect_var_path("s", value="4")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules

0 commit comments

Comments
 (0)