llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: None (cmtice)

<details>
<summary>Changes</summary>

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.

---

Patch is 24.86 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/170332.diff


7 Files Affected:

- (modified) lldb/include/lldb/ValueObject/DILAST.h (+7) 
- (modified) lldb/include/lldb/ValueObject/DILEval.h (+5) 
- (modified) lldb/source/ValueObject/DILEval.cpp (+261-4) 
- (modified) 
lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
 (+1) 
- (added) lldb/test/API/commands/frame/var-dil/expr/Casts/Makefile (+6) 
- (added) 
lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py (+213) 
- (added) lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp (+54) 


``````````diff
diff --git a/lldb/include/lldb/ValueObject/DILAST.h 
b/lldb/include/lldb/ValueObject/DILAST.h
index 9fda0c798ec4e..3a18172852b5e 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -46,6 +46,13 @@ enum class CastKind {
   eNone,        ///< Type promotion casting
 };
 
+/// Promotions allowed for type casts in DIL.
+enum CastPromoKind {
+  eArithmetic,
+  ePointer,
+  eNone,
+};
+
 /// Forward declaration, for use in DIL AST nodes. Definition is at the very
 /// end of this file.
 class Visitor;
diff --git a/lldb/include/lldb/ValueObject/DILEval.h 
b/lldb/include/lldb/ValueObject/DILEval.h
index 2db45a7c37314..8df12ce450db7 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -71,6 +71,11 @@ class Interpreter : Visitor {
                   std::shared_ptr<ExecutionContextScope> ctx,
                   const IntegerLiteralNode *literal);
 
+  llvm::Expected<CompilerType>
+  VerifyCastType(lldb::ValueObjectSP &operand, CompilerType &op_type,
+                 CompilerType target_type, CastPromoKind &promo_kind,
+                 CastKind &cast_kind, int location);
+
   // Used by the interpreter to create objects, perform casts, etc.
   lldb::TargetSP m_target;
   llvm::StringRef m_expr;
diff --git a/lldb/source/ValueObject/DILEval.cpp 
b/lldb/source/ValueObject/DILEval.cpp
index dc0d93d242739..453152554ac62 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -21,6 +21,42 @@
 
 namespace lldb_private::dil {
 
+lldb::ValueObjectSP
+GetDynamicOrSyntheticValue(lldb::ValueObjectSP in_valobj_sp,
+                           lldb::DynamicValueType use_dynamic,
+                           bool use_synthetic) {
+  Status error;
+  if (!in_valobj_sp) {
+    error = Status("invalid value object");
+    return in_valobj_sp;
+  }
+  lldb::ValueObjectSP value_sp = in_valobj_sp;
+  Target *target = value_sp->GetTargetSP().get();
+  // If this ValueObject holds an error, then it is valuable for that.
+  if (value_sp->GetError().Fail())
+    return value_sp;
+
+  if (!target)
+    return lldb::ValueObjectSP();
+
+  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;
+  }
+
+  if (!value_sp)
+    error = Status("invalid value object");
+
+  return value_sp;
+}
+
 static llvm::Expected<lldb::TypeSystemSP>
 GetTypeSystemFromCU(std::shared_ptr<ExecutionContextScope> ctx) {
   auto stack_frame = ctx->CalculateStackFrame();
@@ -740,16 +776,237 @@ Interpreter::Visit(const BooleanLiteralNode *node) {
   return ValueObject::CreateValueObjectFromBool(m_target, value, "result");
 }
 
+llvm::Expected<CompilerType> Interpreter::VerifyCastType(
+    lldb::ValueObjectSP &operand, CompilerType &op_type,
+    CompilerType target_type, CastPromoKind &promo_kind,
+    CastKind &cast_kind, int location) {
+
+  promo_kind = CastPromoKind::eNone;
+  if (op_type.IsReferenceType())
+    op_type = op_type.GetNonReferenceType();
+  if (target_type.IsScalarType()) {
+    if (op_type.IsArrayType()) {
+      // Do array-to-pointer conversion.
+      CompilerType deref_type =
+          op_type.IsReferenceType() ? op_type.GetNonReferenceType() : op_type;
+      CompilerType result_type =
+          deref_type.GetArrayElementType(nullptr).GetPointerType();
+      uint64_t addr = operand->GetLoadAddress();
+      llvm::StringRef name = operand->GetName().GetStringRef();
+      operand = ValueObject::CreateValueObjectFromAddress(
+          name, addr, m_exe_ctx_scope, result_type, /*do_deref=*/false);
+      op_type = result_type;
+    }
+
+    if (op_type.IsPointerType() || op_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",
+            op_type.TypeDescription(), target_type.TypeDescription());
+        return llvm::make_error<DILDiagnosticError>(
+            m_expr, std::move(errMsg), location,
+            op_type.TypeDescription().length());
+      }
+      // Casting pointer to bool is valid. 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.value();
+        type_byte_size = *temp;
+      if (auto temp = op_type.GetByteSize(m_exe_ctx_scope.get()))
+        // rhs_type_byte_size = temp.value();
+        rhs_type_byte_size = *temp;
+      if (!target_type.IsBoolean() && 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,
+            op_type.TypeDescription().length());
+      }
+    } else if (!op_type.IsScalarType() && !op_type.IsEnumerationType()) {
+      // Otherwise accept only arithmetic types and enums.
+      std::string errMsg = llvm::formatv(
+          "cannot convert {0} to {1} without a conversion operator",
+          op_type.TypeDescription(), target_type.TypeDescription());
+
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, std::move(errMsg), location,
+          op_type.TypeDescription().length());
+    }
+    promo_kind = CastPromoKind::eArithmetic;
+  } else if (target_type.IsEnumerationType()) {
+    // Cast to enum type.
+    if (!op_type.IsScalarType() && !op_type.IsEnumerationType()) {
+      std::string errMsg = llvm::formatv(
+          "Cast from {0} to {1} is not allowed",
+          op_type.TypeDescription(), target_type.TypeDescription());
+
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, std::move(errMsg), location,
+          op_type.TypeDescription().length());
+    }
+    cast_kind = CastKind::eEnumeration;
+
+  } else if (target_type.IsPointerType()) {
+    if (!op_type.IsInteger() && !op_type.IsEnumerationType() &&
+        !op_type.IsArrayType() && !op_type.IsPointerType() &&
+        !op_type.IsNullPtrType()) {
+      std::string errMsg = llvm::formatv(
+          "cannot cast from type {0} to pointer type {1}",
+          op_type.TypeDescription(), target_type.TypeDescription());
+
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, std::move(errMsg), location,
+          op_type.TypeDescription().length());
+    }
+    promo_kind = CastPromoKind::ePointer;
+
+  } else if (target_type.IsNullPtrType()) {
+    // Cast to nullptr type.
+    bool is_signed;
+    if (!target_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",
+          op_type.TypeDescription(), target_type.TypeDescription());
+
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, std::move(errMsg), location,
+          op_type.TypeDescription().length());
+    }
+    cast_kind = CastKind::eNullptr;
+
+  } else if (target_type.IsReferenceType()) {
+    // Cast to a reference type.
+    cast_kind = CastKind::eReference;
+  } else {
+    // Unsupported cast.
+    std::string errMsg =
+        llvm::formatv("casting of {0} to {1} is not implemented yet",
+                      op_type.TypeDescription(), 
target_type.TypeDescription());
+    return llvm::make_error<DILDiagnosticError>(
+        m_expr, std::move(errMsg), location,
+        op_type.TypeDescription().length());
+  }
+
+  return target_type;
+}
+
 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();
+  CastKind cast_kind = CastKind::eNone;
+  CastPromoKind promo_kind = CastPromoKind::eNone;
+
+  auto type_or_err =
+      VerifyCastType(operand, op_type, node->GetType(), promo_kind,
+                     cast_kind, node->GetLocation());
+  if (!type_or_err)
+    return type_or_err.takeError();
+
+  CompilerType target_type = *type_or_err;
+  if (op_type.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 (!target_type.IsEnumerationType()) {
+      std::string errMsg = "invalid ast: target type should be an 
enumeration.";
+      return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                  node->GetLocation());
+    }
+    if (op_type.IsFloat())
+      return operand->CastToEnumType(target_type);
+
+    if (op_type.IsInteger() || op_type.IsEnumerationType())
+      return operand->CastToEnumType(target_type);
+
+    std::string errMsg =
+        "invalid ast: operand is not convertible to enumeration type";
+    return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                node->GetLocation());
+    // return error.ToError();
+  }
+  case CastKind::eNullptr: {
+    if (target_type.GetCanonicalType().GetBasicTypeEnumeration() !=
+        lldb::eBasicTypeNullPtr) {
+      std::string errMsg = "invalid ast: target type should be a nullptr_t.";
+      return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                  node->GetLocation());
+    }
+    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()));
+  }
+  case CastKind::eNone: {
+    switch (promo_kind) {
+    case CastPromoKind::eArithmetic: {
+      if (target_type.GetCanonicalType().GetBasicTypeEnumeration() ==
+          lldb::eBasicTypeInvalid) {
+        std::string errMsg = "invalid ast: target type should be a basic 
type.";
+        return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                    node->GetLocation());
+      }
+      // Pick an appropriate cast.
+      if (op_type.IsPointerType() || op_type.IsNullPtrType()) {
+        return operand->CastToBasicType(target_type);
+      }
+      if (op_type.IsScalarType()) {
+        return operand->CastToBasicType(target_type);
+      }
+      if (op_type.IsEnumerationType()) {
+        return operand->CastToBasicType(target_type);
+      }
+      std::string errMsg =
+          "invalid ast: operand is not convertible to arithmetic type";
+      return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                  node->GetLocation());
+    }
+    case CastPromoKind::ePointer: {
+      if (!target_type.IsPointerType()) {
+        std::string errMsg = "invalid ast: target type should be a pointer";
+        return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                    node->GetLocation());
+      }
+      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 CastPromoKind::eNone: {
+      return lldb::ValueObjectSP();
+    }
+    } // switch promo_kind
+  } // case CastKind::eNone
+  } // switch cast_kind
+  std::string errMsg = "invalid ast: unexpected c-style cast kind";
+  return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                              node->GetLocation());
 }
 
 } // namespace lldb_private::dil
diff --git 
a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
 
b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index 0f6618fe47984..85899caaa7433 100644
--- 
a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -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")
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/Makefile 
b/lldb/test/API/commands/frame/var-dil/expr/Casts/Makefile
new file mode 100644
index 0000000000000..0165eb73f3073
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/Makefile
@@ -0,0 +1,6 @@
+CXX_SOURCES := main.cpp
+#CXXFLAGS_EXTRAS := -std=c++14
+
+USE_LIBSTDCPP := 1
+
+include Makefile.rules
diff --git 
a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py 
b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
new file mode 100644
index 0000000000000..2e358ed923732
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
@@ -0,0 +1,213 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for C-Style casts.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+
+class TestFrameVarDILCast(TestBase):
+    def test_type_cast(self):
+        self.build()
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
+
+        self.runCmd("settings set target.experimental.use-DIL true")
+
+        # TestCastBUiltins
+
+        self.expect_var_path("(int)1", value="1", type="int")
+        self.expect_var_path("(long long)1", value="1", type="long long")
+        self.expect_var_path("(unsigned long)1", value="1", type="unsigned 
long")
+        self.expect_var_path("(char*)1", value="0x0000000000000001", 
type="char *")
+        self.expect_var_path(
+            "(long long**)1", value="0x0000000000000001", type="long long **"
+        )
+
+        self.expect(
+            "frame variable '(long&*)1'",
+            error=True,
+            substrs=[
+                "'type name' declared as a pointer to a reference of type 
'long &'"
+            ],
+        )
+
+        self.expect(
+            "frame variable '(long& &)1'",
+            error=True,
+            substrs=["type name declared as a reference to a reference"],
+        )
+
+        self.expect(
+            "frame variable '(long 1)1'",
+            error=True,
+            substrs=["expected 'r_paren', got: <'1' (integer_constant)>"],
+        )
+
+        # TestCastBasicType
+
+        # Test with integer literals.
+        self.expect_var_path("(char)1", type="char", value="'\\x01'")
+        self.expect_var_path("(long long)1", type="long long", value="1")
+        self.expect_var_path("(short)65534", type="short", value="-2")
+        self.expect_var_path(
+            "(unsigned short)100000", type="unsigned short", value="34464"
+        )
+        self.expect_var_path("(int)false", type="int", value="0")
+        self.expect_var_path("(int)true", type="int", value="1")
+        self.expect_var_path("(float)1", type="float", value="1")
+        self.expect_var_path("(float)1.1", type="float", value="1.10000002")
+        self.expect_var_path("(float)1.1f", type="float", value="1.10000002")
+        self.expect_var_path("(float)false", type="float", value="0")
+        self.expect_var_path("(float)true", type="float", value="1")
+        self.expect_var_path("(double)1", type="double", value="1")
+        self.expect_var_path("(double)1.1", type="double", 
value="1.1000000000000001")
+        self.expect_var_path("(double)1.1f", type="double", 
value="1.1000000238418579")
+        self.expect_var_path("(double)false", type="double", value="0")
+        self.expect_var_path("(double)true", type="double", value="1")
+        self.expect_var_path("(int)1.1", type="int", value="1")
+        self.expect_var_path("(int)1.1f", type="int", value="1")
+        self.expect_var_path("(long)1.1", type="long", value="1")
+        self.expect_var_path("(bool)0", type="bool", value="false")
+        self.expect_var_path("(bool)0.0", type="bool", value="false")
+        self.expect_var_path("(bool)0.0f", type="bool", value="false")
+        self.expect_var_path("(bool)3", type="bool", value="true")
+
+        self.expect(
+            "frame variable '&(int)1'",
+            error=True,
+            substrs=["'result' doesn't have a valid address"],
+        )
+
+        # Test with variables.
+        self.expect_var_path("(char)a", type="char", value="'\\x01'")
+        self.expect_var_path("(unsigned char)na", type="unsigned char", 
value="'\\xff'")
+        self.expect_var_path("(short)na", type="short", value="-1")
+        self.expect_var_path("(long long)a", type="long long", value="1")
+        self.expect_var_path("(float)a", type="float", value="1")
+        self.expect_var_path("(float)f", type="float", value="1.10000002")
+        self.expect_var_path("(double)f", type="double", 
value="1.1000000238418579")
+        self.expect_var_path("(int)f", type="int", value="1")
+        self.expect_var_path("(long)f", type="long", value="1")
+        self.expect_var_path("(bool)finf", type="bool", value="true")
+        self.expect_var_path("(bool)fnan", type="bool", value="true")
+        self.expect_var_path("(bool)fsnan", type="bool", value="true")
+        self.expect_var_path("(bool)fmax", type="bool", value="true")
+        self.expect_var_path("(bool)fdenorm", type="bool", value="true")
+        self.expect(
+            "frame variable '(int)ns_foo_'",
+            error=True,
+            substrs=["cannot convert 'ns::Foo' to 'int' without a conversion 
operator"],
+        )
+
+        self.expect_var_path("(int)myint_", type="int", value="1")
+        self.expect_var_path("(int)ns_myint_", type="int", value="2")
+        self.expect_var_path("(long long)myint_", type="long long", value="1")
+        self.expect_var_path("(long long)ns_myint_", type="long long", 
value="2")
+
+        # Test with pointers and arrays.
+        self.expect_var_path("(long long)ap", type="long long")
+        self.expect_var_path("(unsigned long long)vp", type="unsigned long 
long")
+        self.expect_var_path("(long long)arr", type="long long")
+        self.expect_var_path("(bool)ap", type="bool", value="true")
+        self.expect_var_path("(bool)(int*)0x00000000", type="bool", 
value="false")
+        self.expect_var_path("(bool)arr", type="bool", value="true")
+        self.expect(
+            "frame variable '(char)ap'",
+            error=True,
+            substrs=["cast from pointer to smaller type 'char' loses 
information"],
+        )
+        Is32Bit = False
+        if self.target().GetAddressByteSize() == 4:
+            Is32Bit = True
+
+...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/170332
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to