[debugger] Parse C-style casts.

Adds C-style cast support to the parser.

Since symbol lookup is not yet hooked up to the parser, this is not
currently usable. It will be hooked up in a follow-up.

This leaves some refactoring needs for the existing casting code: (a)
the existing reinterpret_cast support (which is special-cased as a
function call) should generate the new CastExprNode, and (b) the
existing "Coerce" function should be folded into the new general-purpose
casting function. These changes are noted in the code.

Change-Id: I62ac8753ef25886497bdfbe300f15c195a8b0489
diff --git a/garnet/bin/zxdb/expr/cast.cc b/garnet/bin/zxdb/expr/cast.cc
index 478f7fe..0afcf85 100644
--- a/garnet/bin/zxdb/expr/cast.cc
+++ b/garnet/bin/zxdb/expr/cast.cc
@@ -260,6 +260,54 @@
 
 }  // namespace
 
+const char* CastTypeToString(CastType type) {
+  switch (type) {
+    case CastType::kImplicit:
+      return "implicit";
+    case CastType::kC:
+      return "C";
+    case CastType::kReinterpret:
+      return "reinterpret_cast";
+    case CastType::kStatic:
+      return "static_cast";
+  }
+  return "<invalid>";
+}
+
+Err CastExprValue(CastType cast_type, const ExprValue& source,
+                  const fxl::RefPtr<Type>& dest_type, ExprValue* result) {
+  switch (cast_type) {
+    case CastType::kImplicit:
+      return CoerceValueTo(source, dest_type, ExprValueSource(), result);
+    case CastType::kC: {
+      // A C-style cast can do the following things.
+      //  - const_cast
+      //  - static_cast
+      //  - static_cast followed by a const_cast
+      //  - reinterpret_cast
+      //  - reinterpret_cast followed by a const_cast
+      //
+      // Since the debugger ignores const in debugging, this ends up being
+      // a static cast falling back to a reinterpret cast.
+      //
+      // TODO(DX-1178) this should be a static cast when it exists. Currently
+      // "coerce" implements the things we're willing to implicitly cast
+      // and doesn't handle things like derived type conversions.
+      if (!CoerceValueTo(source, dest_type, ExprValueSource(), result)
+               .has_error())
+        return Err();
+      return ReinterpretCast(source, dest_type, result);
+    }
+    case CastType::kReinterpret:
+      return ReinterpretCast(source, dest_type, result);
+    case CastType::kStatic:
+      // TODO(DX-1178) write a real static cast.
+      return CoerceValueTo(source, dest_type, ExprValueSource(), result);
+  }
+  FXL_NOTREACHED();
+  return Err("Internal error.");
+}
+
 Err CoerceValueTo(const ExprValue& source, const fxl::RefPtr<Type>& dest_type,
                   const ExprValueSource& dest_source, ExprValue* result) {
   // There are several fundamental types of things that can be casted:
diff --git a/garnet/bin/zxdb/expr/cast.h b/garnet/bin/zxdb/expr/cast.h
index 76ec1a1..c9d9889 100644
--- a/garnet/bin/zxdb/expr/cast.h
+++ b/garnet/bin/zxdb/expr/cast.h
@@ -13,6 +13,21 @@
 class Type;
 class ExprValueSource;
 
+enum class CastType {
+  kImplicit,  // Things like double d = (float)f;
+  kC,         // C-style cast: (int)foo;
+  kReinterpret,
+  kStatic
+  // We don't bother implementing const_cast and dynamic_cast yet because
+  // they're less useful in a debugger.
+};
+
+const char* CastTypeToString(CastType);
+
+// TODO(brettw) replace the other cast calls with calls to this one:
+Err CastExprValue(CastType cast_type, const ExprValue& source,
+                  const fxl::RefPtr<Type>& dest_type, ExprValue* result);
+
 // Attempts to convert the "source" value to the given type. This attempts
 // to be as permissive as possible. In a debugger context, people want to be
 // able to make arbitrary binary assignments without being told to do an
@@ -26,6 +41,13 @@
 //
 // This does not implement static-cast-like conversions of derived classes
 // where the cast involves adjusting the value of the pointer.
+//
+// The dest_source will be set as the "source" of the result ExprValue. When
+// generating temporaries, this should be a default-constructed
+// ExprValueSource, but this is useful when doing implicit casts for assignment
+// where the destination location is given.
+//
+// TODO(brettw) this should be renamed "ImplicitCast".
 Err CoerceValueTo(const ExprValue& source, const fxl::RefPtr<Type>& dest_type,
                   const ExprValueSource& dest_source, ExprValue* result);
 
diff --git a/garnet/bin/zxdb/expr/expr_node.cc b/garnet/bin/zxdb/expr/expr_node.cc
index e59a976..0c41864 100644
--- a/garnet/bin/zxdb/expr/expr_node.cc
+++ b/garnet/bin/zxdb/expr/expr_node.cc
@@ -228,6 +228,29 @@
   right_->Print(out, indent + 1);
 }
 
+void CastExprNode::Eval(fxl::RefPtr<ExprEvalContext> context,
+                        EvalCallback cb) const {
+  from_->Eval(context, [cast_type = cast_type_, to_type = to_type_->type(),
+                        cb = std::move(cb)](const Err& err, ExprValue value) {
+    if (err.has_error()) {
+      cb(err, value);
+    } else {
+      ExprValue cast_result;
+      Err cast_err = CastExprValue(cast_type, value, to_type, &cast_result);
+      if (cast_err.has_error())
+        cb(cast_err, ExprValue());
+      else
+        cb(Err(), std::move(cast_result));
+    }
+  });
+}
+
+void CastExprNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "CAST(" << CastTypeToString(cast_type_) << ")\n";
+  to_type_->Print(out, indent + 1);
+  from_->Print(out, indent + 1);
+}
+
 void DereferenceExprNode::Eval(fxl::RefPtr<ExprEvalContext> context,
                                EvalCallback cb) const {
   expr_->EvalFollowReferences(
@@ -243,6 +266,8 @@
 
 void FunctionCallExprNode::Eval(fxl::RefPtr<ExprEvalContext> context,
                                 EvalCallback cb) const {
+  // TODO(brettw) parse reinterpret_cast as a cast instead.
+
   // Handle reinterpret_cast calls since this is currently the only function
   // that can be called (this will need to be enhanced significantly when we
   // add more).
diff --git a/garnet/bin/zxdb/expr/expr_node.h b/garnet/bin/zxdb/expr/expr_node.h
index f1eb8c9..a51d405 100644
--- a/garnet/bin/zxdb/expr/expr_node.h
+++ b/garnet/bin/zxdb/expr/expr_node.h
@@ -8,6 +8,7 @@
 #include <iosfwd>
 #include <memory>
 
+#include "garnet/bin/zxdb/expr/cast.h"
 #include "garnet/bin/zxdb/expr/expr_token.h"
 #include "garnet/bin/zxdb/expr/expr_value.h"
 #include "garnet/bin/zxdb/expr/identifier.h"
@@ -24,6 +25,7 @@
 class AddressOfExprNode;
 class ArrayAccessExprNode;
 class BinaryOpExprNode;
+class CastExprNode;
 class DereferenceExprNode;
 class FunctionCallExprNode;
 class IdentifierExprNode;
@@ -43,6 +45,7 @@
   virtual const AddressOfExprNode* AsAddressOf() const { return nullptr; }
   virtual const ArrayAccessExprNode* AsArrayAccess() const { return nullptr; }
   virtual const BinaryOpExprNode* AsBinaryOp() const { return nullptr; }
+  virtual const CastExprNode* AsCast() const { return nullptr; }
   virtual const DereferenceExprNode* AsDereference() const { return nullptr; }
   virtual const FunctionCallExprNode* AsFunctionCall() const { return nullptr; }
   virtual const IdentifierExprNode* AsIdentifier() const { return nullptr; }
@@ -167,6 +170,31 @@
   fxl::RefPtr<ExprNode> right_;
 };
 
+// Implements all types of casts.
+class CastExprNode : public ExprNode {
+ public:
+  const CastExprNode* AsCast() const override { return this; }
+  void Eval(fxl::RefPtr<ExprEvalContext> context,
+            EvalCallback cb) const override;
+  void Print(std::ostream& out, int indent) const override;
+
+ private:
+  FRIEND_REF_COUNTED_THREAD_SAFE(CastExprNode);
+  FRIEND_MAKE_REF_COUNTED(CastExprNode);
+
+  CastExprNode();
+  CastExprNode(CastType cast_type, fxl::RefPtr<TypeExprNode> to_type,
+               fxl::RefPtr<ExprNode> from)
+      : cast_type_(cast_type),
+        to_type_(std::move(to_type)),
+        from_(std::move(from)) {}
+  ~CastExprNode() override = default;
+
+  CastType cast_type_;
+  fxl::RefPtr<TypeExprNode> to_type_;
+  fxl::RefPtr<ExprNode> from_;
+};
+
 // Implements dereferencing a pointer ("*" in C).
 class DereferenceExprNode : public ExprNode {
  public:
diff --git a/garnet/bin/zxdb/expr/expr_parser.cc b/garnet/bin/zxdb/expr/expr_parser.cc
index f279de0..f325a97e 100644
--- a/garnet/bin/zxdb/expr/expr_parser.cc
+++ b/garnet/bin/zxdb/expr/expr_parser.cc
@@ -596,6 +596,18 @@
     Consume(ExprToken::kRightParen, token, "Expected ')' to match.");
   if (has_error())
     return nullptr;
+
+  if (const TypeExprNode* type_expr = expr->AsType()) {
+    // Convert "(TypeName)..." into a cast.
+    auto cast_expr = ParseExpression(kPrecedenceCallAccess);
+    if (has_error())
+      return nullptr;
+
+    fxl::RefPtr<TypeExprNode> type_ref(const_cast<TypeExprNode*>(type_expr));
+    return fxl::MakeRefCounted<CastExprNode>(CastType::kC, std::move(type_ref),
+                                             std::move(cast_expr));
+  }
+
   return expr;
 }
 
diff --git a/garnet/bin/zxdb/expr/expr_parser_unittest.cc b/garnet/bin/zxdb/expr/expr_parser_unittest.cc
index 2569f49..f2195fc 100644
--- a/garnet/bin/zxdb/expr/expr_parser_unittest.cc
+++ b/garnet/bin/zxdb/expr/expr_parser_unittest.cc
@@ -558,4 +558,26 @@
       GetParseString("sizeof(foo)", &TestLookupName));
 }
 
+TEST_F(ExprParserTest, Casts) {
+  EXPECT_EQ(
+      "CAST(C)\n"
+      " TYPE(Type)\n"
+      " IDENTIFIER(\"a\")\n",
+      GetParseString("(Type)(a)", &TestLookupName));
+
+  EXPECT_EQ(
+      "BINARY_OP(&&)\n"
+      " CAST(C)\n"
+      "  TYPE(Type*)\n"
+      "  IDENTIFIER(\"a\")\n"
+      " IDENTIFIER(\"b\")\n",
+      GetParseString("(Type*)a && b", &TestLookupName));
+
+  // Looks like a cast but it's not a type.
+  auto result = Parse("(NotType)a", &TestLookupName);
+  EXPECT_FALSE(result);
+  EXPECT_EQ("Unexpected input, did you forget an operator?",
+            parser().err().msg());
+}
+
 }  // namespace zxdb