compiler: add Expression::is_untyped method

This method is not currently used by anything, but it will be used
by later CLs in this series.

Change-Id: Ia85e15782b0c12c9503fdf3601a9b84326bf3ea5
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/536639
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
diff --git a/go/expressions.cc b/go/expressions.cc
index 273831f..5bea623 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -201,6 +201,19 @@
   this->set_is_error();
 }
 
+// A convenience function for handling a type in do_is_untyped.  If
+// TYPE is not abstract, return false.  Otherwise set *PTYPE to TYPE
+// and return true.
+
+bool
+Expression::is_untyped_type(Type* type, Type** ptype)
+{
+  if (!type->is_abstract())
+    return false;
+  *ptype = type;
+  return true;
+}
+
 // Set types of variables and constants.  This is implemented by the
 // child class.
 
@@ -827,6 +840,10 @@
   { return true; }
 
   bool
+  do_is_untyped(Type**) const
+  { return false; }
+
+  bool
   do_numeric_constant_value(Numeric_constant* nc) const
   {
     nc->set_unsigned_long(NULL, 0);
@@ -1966,6 +1983,9 @@
   { return true; }
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_zero_value() const
   { return this->val_ == false; }
 
@@ -2023,6 +2043,15 @@
   return TRAVERSE_CONTINUE;
 }
 
+bool
+Boolean_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->type_ != NULL)
+    return Expression::is_untyped_type(this->type_, ptype);
+  *ptype = Type::make_boolean_type();
+  return true;
+}
+
 // Get the type.
 
 Type*
@@ -2096,6 +2125,15 @@
   return TRAVERSE_CONTINUE;
 }
 
+bool
+String_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->type_ != NULL)
+    return Expression::is_untyped_type(this->type_, ptype);
+  *ptype = Type::make_string_type();
+  return true;
+}
+
 // Get the type.
 
 Type*
@@ -2486,6 +2524,9 @@
   { return true; }
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_zero_value() const
   { return mpz_sgn(this->val_) == 0; }
 
@@ -2568,6 +2609,18 @@
   return true;
 }
 
+bool
+Integer_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->type_ != NULL)
+    return Expression::is_untyped_type(this->type_, ptype);
+  if (this->is_character_constant_)
+    *ptype = Type::make_abstract_character_type();
+  else
+    *ptype = Type::make_abstract_integer_type();
+  return true;
+}
+
 // Return the current type.  If we haven't set the type yet, we return
 // an abstract integer type.
 
@@ -2914,6 +2967,9 @@
   { return true; }
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_zero_value() const
   {
     return mpfr_zero_p(this->val_) != 0
@@ -2979,6 +3035,15 @@
   return TRAVERSE_CONTINUE;
 }
 
+bool
+Float_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->type_ != NULL)
+    return Expression::is_untyped_type(this->type_, ptype);
+  *ptype = Type::make_abstract_float_type();
+  return true;
+}
+
 // Return the current type.  If we haven't set the type yet, we return
 // an abstract float type.
 
@@ -3136,6 +3201,9 @@
   { return true; }
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_zero_value() const
   {
     return mpfr_zero_p(mpc_realref(this->val_)) != 0
@@ -3205,6 +3273,15 @@
   return TRAVERSE_CONTINUE;
 }
 
+bool
+Complex_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->type_ != NULL)
+    return Expression::is_untyped_type(this->type_, ptype);
+  *ptype = Type::make_abstract_complex_type();
+  return true;
+}
+
 // Return the current type.  If we haven't set the type yet, we return
 // an abstract complex type.
 
@@ -3458,6 +3535,21 @@
   return ok;
 }
 
+// Whether this is untyped.
+
+bool
+Const_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->type_ != NULL)
+    return Expression::is_untyped_type(this->type_, ptype);
+
+  Named_constant* nc = this->constant_->const_value();
+  if (nc->type() != NULL)
+    return Expression::is_untyped_type(nc->type(), ptype);
+
+  return nc->expr()->is_untyped(ptype);
+}
+
 // Return the type of the const reference.
 
 Type*
@@ -3708,6 +3800,13 @@
   { return true; }
 
   bool
+  do_untyped_type(Type** ptype) const
+  {
+    *ptype = Type::make_nil_type();
+    return true;
+  }
+
+  bool
   do_is_zero_value() const
   { return true; }
 
@@ -4774,6 +4873,14 @@
     return this->expr_->is_constant();
 }
 
+bool
+Unary_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->op_ == OPERATOR_MULT || this->op_ == OPERATOR_AND)
+    return false;
+  return this->expr_->is_untyped(ptype);
+}
+
 // Return whether a unary expression can be used as a constant
 // initializer.
 
@@ -5523,6 +5630,81 @@
   return Expression::traverse(&this->right_, traverse);
 }
 
+// Return whether a binary expression is untyped.
+
+bool
+Binary_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->type_ != NULL)
+    return Expression::is_untyped_type(this->type_, ptype);
+
+  switch (this->op_)
+    {
+    case OPERATOR_EQEQ:
+    case OPERATOR_NOTEQ:
+    case OPERATOR_LT:
+    case OPERATOR_LE:
+    case OPERATOR_GT:
+    case OPERATOR_GE:
+      // Comparisons are untyped by default.
+      *ptype = Type::make_boolean_type();
+      return true;
+
+    case OPERATOR_LSHIFT:
+    case OPERATOR_RSHIFT:
+      // A shift operation is untyped if the left hand expression is
+      // untyped.  The right hand expression is irrelevant.
+      return this->left_->is_untyped(ptype);
+
+    default:
+      break;
+    }
+
+  Type* tleft;
+  Type* tright;
+  if (!this->left_->is_untyped(&tleft)
+      || !this->right_->is_untyped(&tright))
+    return false;
+
+  // If both sides are numeric, pick a type based on the kind.
+  enum kind { INT, RUNE, FLOAT, COMPLEX };
+  enum kind kleft, kright;
+
+  if (tleft->integer_type() != NULL)
+    kleft = tleft->integer_type()->is_rune() ? RUNE : INT;
+  else if (tleft->float_type() != NULL)
+    kleft = FLOAT;
+  else if (tleft->complex_type() != NULL)
+    kleft = COMPLEX;
+  else
+    {
+      // Not numeric.  If the types are different, we will report an
+      // error later.
+      *ptype = tleft;
+      return true;
+    }
+
+  if (tright->integer_type() != NULL)
+    kright = tright->integer_type()->is_rune() ? RUNE : INT;
+  else if (tright->float_type() != NULL)
+    kright = FLOAT;
+  else if (tright->complex_type() != NULL)
+    kright = COMPLEX;
+  else
+    {
+      // Types are different.  We will report an error later.
+      *ptype = tleft;
+      return true;
+    }
+
+  if (kleft > kright)
+    *ptype = tleft;
+  else
+    *ptype = tright;
+
+  return true;
+}
+
 // Return whether this expression may be used as a static initializer.
 
 bool
@@ -7646,6 +7828,21 @@
 }
 
 bool
+String_concat_expression::do_is_untyped(Type** ptype) const
+{
+  for (Expression_list::iterator pe = this->exprs_->begin();
+       pe != this->exprs_->end();
+       ++pe)
+    {
+      if (!(*pe)->is_untyped(ptype))
+	return false;
+    }
+
+  *ptype = Type::make_string_type();
+  return true;
+}
+
+bool
 String_concat_expression::do_is_zero_value() const
 {
   for (Expression_list::const_iterator pe = this->exprs_->begin();
@@ -9671,6 +9868,47 @@
   return false;
 }
 
+// Return whether a builtin call is untyped.  Most builtin functions
+// have a known type, but complex, real, and imag can be untyped.
+
+bool
+Builtin_call_expression::do_is_untyped(Type** ptype) const
+{
+  if (this->is_error_expression())
+    return false;
+
+  switch (this->code_)
+    {
+    default:
+      return false;
+
+    case BUILTIN_COMPLEX:
+      {
+	const Expression_list* args = this->args();
+	if (args == NULL || args->size() != 2)
+	  return false;
+	Type* dummy;
+	if (!args->front()->is_untyped(&dummy)
+	    || !args->back()->is_untyped(&dummy))
+	  return false;
+	*ptype = Type::make_abstract_complex_type();
+	return true;
+      }
+
+    case BUILTIN_REAL:
+    case BUILTIN_IMAG:
+      {
+	Expression* arg = this->one_arg();
+	if (arg == NULL)
+	  return false;
+	if (!arg->is_untyped(ptype))
+	  return false;
+	*ptype = Type::make_abstract_float_type();
+	return true;
+      }
+    }
+}
+
 // Return a numeric constant if possible.
 
 bool
diff --git a/go/expressions.h b/go/expressions.h
index 2abc9cb..49140d1 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -566,6 +566,15 @@
   is_constant() const
   { return this->do_is_constant(); }
 
+  // Return whether this expression is untyped.  This isn't quite the
+  // same as is_constant with an abstract type, as 1<<val is untyped
+  // even if val is a variable.  If this returns true, it sets *PTYPE
+  // to an abstract type, which is the type the expression will have
+  // if there is no context.
+  bool
+  is_untyped(Type** ptype) const
+  { return this->do_is_untyped(ptype); }
+
   // Return whether this is the zero value of its type.
   bool
   is_zero_value() const
@@ -1169,6 +1178,11 @@
   do_is_constant() const
   { return false; }
 
+  // Return whether this expression is untyped.
+  virtual bool
+  do_is_untyped(Type**) const
+  { return false; }
+
   // Return whether this is the zero value of its type.
   virtual bool
   do_is_zero_value() const
@@ -1274,6 +1288,12 @@
   void
   report_error(const char*);
 
+  // A convenience function for handling a type in do_is_untyped.  If
+  // TYPE is not abstract, return false.  Otherwise set *PTYPE to TYPE
+  // and return true.
+  static bool
+  is_untyped_type(Type* type, Type** ptype);
+
   // Write a name to export data.
   static void
   export_name(Export_function_body* efb, const Named_object*);
@@ -1502,6 +1522,9 @@
   { return true; }
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_zero_value() const;
 
   bool
@@ -1831,6 +1854,9 @@
   { return true; }
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_zero_value() const
   { return this->val_ == ""; }
 
@@ -2137,6 +2163,9 @@
   do_is_constant() const;
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_static_initializer() const;
 
   bool
@@ -2294,6 +2323,9 @@
   { return this->left_->is_constant() && this->right_->is_constant(); }
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_static_initializer() const;
 
   bool
@@ -2416,6 +2448,9 @@
   do_is_constant() const;
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_is_zero_value() const;
 
   bool
@@ -2768,6 +2803,9 @@
   do_is_constant() const;
 
   bool
+  do_is_untyped(Type**) const;
+
+  bool
   do_numeric_constant_value(Numeric_constant*) const;
 
   bool