compiler: cast comparison function result to expected bool type
authorIan Lance Taylor <iant@golang.org>
Thu, 3 Dec 2020 02:11:00 +0000 (18:11 -0800)
committerIan Lance Taylor <iant@golang.org>
Thu, 3 Dec 2020 16:27:41 +0000 (08:27 -0800)
Otherwise cases like
    type mybool bool
    var b mybool = [10]string{} == [10]string{}
get an incorrect type checking error.

Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/274446

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h

index f55daf7..cd1a396 100644 (file)
@@ -1,4 +1,4 @@
-5364d15082de77d2759a01f254208d4cb4f579e3
+b3a0b068f7fa2d65ba781271b2c0479d103b7d7b
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 50574c2..ebe1b36 100644 (file)
@@ -6287,8 +6287,21 @@ Binary_expression::lower_array_comparison(Gogo* gogo,
   args->push_back(this->operand_address(inserter, this->left_));
   args->push_back(this->operand_address(inserter, this->right_));
 
-  Expression* ret = Expression::make_call(func, args, false, loc);
-
+  Call_expression* ce = Expression::make_call(func, args, false, loc);
+
+  // Record that this is a call to a generated equality function.  We
+  // need to do this because a comparison returns an abstract boolean
+  // type, but the function necessarily returns "bool".  The
+  // difference shows up in code like
+  //     type mybool bool
+  //     var b mybool = [10]string{} == [10]string{}
+  // The comparison function returns "bool", but since a comparison
+  // has an abstract boolean type we need an implicit conversion to
+  // "mybool".  The implicit conversion is inserted in
+  // Call_expression::do_flatten.
+  ce->set_is_equal_function();
+
+  Expression* ret = ce;
   if (this->op_ == OPERATOR_NOTEQ)
     ret = Expression::make_unary(OPERATOR_NOT, ret, loc);
 
@@ -11163,6 +11176,13 @@ Call_expression::do_flatten(Gogo* gogo, Named_object*,
         return ret;
     }
 
+  // Add an implicit conversion to a boolean type, if needed.  See the
+  // comment in Binary_expression::lower_array_comparison.
+  if (this->is_equal_function_
+      && this->type_ != NULL
+      && this->type_ != Type::lookup_bool_type())
+    return Expression::make_cast(this->type_, this, this->location());
+
   return this;
 }
 
@@ -11938,7 +11958,7 @@ Call_expression::do_type()
 // parameter types to set the types of the arguments.
 
 void
-Call_expression::do_determine_type(const Type_context*)
+Call_expression::do_determine_type(const Type_context* context)
 {
   if (!this->determining_types())
     return;
@@ -11985,6 +12005,22 @@ Call_expression::do_determine_type(const Type_context*)
            (*pa)->determine_type_no_context();
        }
     }
+
+  // If this is a call to a generated equality function, we determine
+  // the type based on the context.  See the comment in
+  // Binary_expression::lower_array_comparison.
+  if (this->is_equal_function_
+      && !context->may_be_abstract
+      && context->type != NULL
+      && context->type->is_boolean_type()
+      && context->type != Type::lookup_bool_type())
+    {
+      go_assert(this->type_ == NULL
+               || this->type_ == Type::lookup_bool_type()
+               || this->type_ == context->type
+               || this->type_->is_error());
+      this->type_ = context->type;
+    }
 }
 
 // Called when determining types for a Call_expression.  Return true
index d297523..259eeb6 100644 (file)
@@ -2326,8 +2326,8 @@ class Call_expression : public Expression
       fn_(fn), args_(args), type_(NULL), call_(NULL), call_temp_(NULL)
     , expected_result_count_(0), is_varargs_(is_varargs),
       varargs_are_lowered_(false), types_are_determined_(false),
-      is_deferred_(false), is_concurrent_(false), issued_error_(false),
-      is_multi_value_arg_(false), is_flattened_(false)
+      is_deferred_(false), is_concurrent_(false), is_equal_function_(false),
+      issued_error_(false), is_multi_value_arg_(false), is_flattened_(false)
   { }
 
   // The function to call.
@@ -2408,6 +2408,11 @@ class Call_expression : public Expression
   set_is_concurrent()
   { this->is_concurrent_ = true; }
 
+  // Note that this is a call to a generated equality function.
+  void
+  set_is_equal_function()
+  { this->is_equal_function_ = true; }
+
   // We have found an error with this call expression; return true if
   // we should report it.
   bool
@@ -2545,6 +2550,8 @@ class Call_expression : public Expression
   bool is_deferred_;
   // True if the call is an argument to a go statement.
   bool is_concurrent_;
+  // True if this is a call to a generated equality function.
+  bool is_equal_function_;
   // True if we reported an error about a mismatch between call
   // results and uses.  This is to avoid producing multiple errors
   // when there are multiple Call_result_expressions.