OSDN Git Service

compiler: Make sure we produce an error for a call to a non-function.
[pf3gnuchains/gcc-fork.git] / gcc / go / gofrontend / expressions.cc
index 728a2ac..e16cd84 100644 (file)
@@ -89,10 +89,11 @@ Expression::do_traverse(Traverse*)
 // expression is being discarded.  By default, we give an error.
 // Expressions with side effects override.
 
-void
+bool
 Expression::do_discarding_value()
 {
   this->unused_value_error();
+  return false;
 }
 
 // This virtual function is called to export expressions.  This will
@@ -109,7 +110,7 @@ Expression::do_export(Export*) const
 void
 Expression::unused_value_error()
 {
-  error_at(this->location(), "value computed is not used");
+  this->report_error(_("value computed is not used"));
 }
 
 // Note that this expression is an error.  This is called by children
@@ -301,19 +302,25 @@ Expression::convert_type_to_interface(Translate_context* context,
       // object type: a list of function pointers for each interface
       // method.
       Named_type* rhs_named_type = rhs_type->named_type();
+      Struct_type* rhs_struct_type = rhs_type->struct_type();
       bool is_pointer = false;
-      if (rhs_named_type == NULL)
+      if (rhs_named_type == NULL && rhs_struct_type == NULL)
        {
          rhs_named_type = rhs_type->deref()->named_type();
+         rhs_struct_type = rhs_type->deref()->struct_type();
          is_pointer = true;
        }
       tree method_table;
-      if (rhs_named_type == NULL)
-       method_table = null_pointer_node;
-      else
+      if (rhs_named_type != NULL)
        method_table =
          rhs_named_type->interface_method_table(gogo, lhs_interface_type,
                                                 is_pointer);
+      else if (rhs_struct_type != NULL)
+       method_table =
+         rhs_struct_type->interface_method_table(gogo, lhs_interface_type,
+                                                 is_pointer);
+      else
+       method_table = null_pointer_node;
       first_field_value = fold_convert_loc(location.gcc_location(),
                                            const_ptr_type_node, method_table);
     }
@@ -783,9 +790,9 @@ class Error_expression : public Expression
     return true;
   }
 
-  void
+  bool
   do_discarding_value()
-  { }
+  { return true; }
 
   Type*
   do_type()
@@ -1146,9 +1153,9 @@ class Sink_expression : public Expression
   { }
 
  protected:
-  void
+  bool
   do_discarding_value()
-  { }
+  { return true; }
 
   Type*
   do_type();
@@ -5084,7 +5091,7 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*,
                                                     &right_nc, location,
                                                     &result))
              return this;
-           return Expression::make_cast(Type::lookup_bool_type(),
+           return Expression::make_cast(Type::make_boolean_type(),
                                         Expression::make_boolean(result,
                                                                  location),
                                         location);
@@ -5115,10 +5122,7 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*,
            {
              int cmp = left_string.compare(right_string);
              bool r = Binary_expression::cmp_to_bool(op, cmp);
-             return Expression::make_cast(Type::lookup_bool_type(),
-                                          Expression::make_boolean(r,
-                                                                   location),
-                                          location);
+             return Expression::make_boolean(r, location);
            }
        }
     }
@@ -5187,6 +5191,9 @@ Binary_expression::lower_struct_comparison(Gogo* gogo,
        pf != fields->end();
        ++pf, ++field_index)
     {
+      if (Gogo::is_sink_name(pf->field_name()))
+       continue;
+
       if (field_index > 0)
        {
          if (left_temp == NULL)
@@ -5317,13 +5324,19 @@ Binary_expression::do_numeric_constant_value(Numeric_constant* nc) const
 
 // Note that the value is being discarded.
 
-void
+bool
 Binary_expression::do_discarding_value()
 {
   if (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_ANDAND)
-    this->right_->discarding_value();
+    {
+      this->right_->discarding_value();
+      return true;
+    }
   else
-    this->unused_value_error();
+    {
+      this->unused_value_error();
+      return false;
+    }
 }
 
 // Get type.
@@ -5336,15 +5349,15 @@ Binary_expression::do_type()
 
   switch (this->op_)
     {
-    case OPERATOR_OROR:
-    case OPERATOR_ANDAND:
     case OPERATOR_EQEQ:
     case OPERATOR_NOTEQ:
     case OPERATOR_LT:
     case OPERATOR_LE:
     case OPERATOR_GT:
     case OPERATOR_GE:
-      return Type::lookup_bool_type();
+      if (this->type_ == NULL)
+       this->type_ = Type::make_boolean_type();
+      return this->type_;
 
     case OPERATOR_PLUS:
     case OPERATOR_MINUS:
@@ -5355,6 +5368,8 @@ Binary_expression::do_type()
     case OPERATOR_MOD:
     case OPERATOR_AND:
     case OPERATOR_BITCLEAR:
+    case OPERATOR_OROR:
+    case OPERATOR_ANDAND:
       {
        Type* type;
        if (!Binary_expression::operation_type(this->op_,
@@ -5451,7 +5466,8 @@ Binary_expression::do_determine_type(const Type_context* context)
          && (this->left_->type()->integer_type() == NULL
              || (subcontext.type->integer_type() == NULL
                  && subcontext.type->float_type() == NULL
-                 && subcontext.type->complex_type() == NULL)))
+                 && subcontext.type->complex_type() == NULL
+                 && subcontext.type->interface_type() == NULL)))
        this->report_error(("invalid context-determined non-integer type "
                            "for shift operand"));
 
@@ -5462,6 +5478,16 @@ Binary_expression::do_determine_type(const Type_context* context)
     }
 
   this->right_->determine_type(&subcontext);
+
+  if (is_comparison)
+    {
+      if (this->type_ != NULL && !this->type_->is_abstract())
+       ;
+      else if (context->type != NULL && context->type->is_boolean_type())
+       this->type_ = context->type;
+      else if (!context->may_be_abstract)
+       this->type_ = Type::lookup_bool_type();
+    }
 }
 
 // Report an error if the binary operator OP does not support TYPE.
@@ -5673,7 +5699,7 @@ Binary_expression::do_get_tree(Translate_context* context)
     case OPERATOR_LE:
     case OPERATOR_GT:
     case OPERATOR_GE:
-      return Expression::comparison_tree(context, this->op_,
+      return Expression::comparison_tree(context, this->type_, this->op_,
                                         this->left_->type(), left,
                                         this->right_->type(), right,
                                         this->location());
@@ -6134,8 +6160,8 @@ Expression::make_binary(Operator op, Expression* left, Expression* right,
 // Implement a comparison.
 
 tree
-Expression::comparison_tree(Translate_context* context, Operator op,
-                           Type* left_type, tree left_tree,
+Expression::comparison_tree(Translate_context* context, Type* result_type,
+                           Operator op, Type* left_type, tree left_tree,
                            Type* right_type, tree right_tree,
                            Location location)
 {
@@ -6376,7 +6402,13 @@ Expression::comparison_tree(Translate_context* context, Operator op,
   if (left_tree == error_mark_node || right_tree == error_mark_node)
     return error_mark_node;
 
-  tree ret = fold_build2(code, boolean_type_node, left_tree, right_tree);
+  tree result_type_tree;
+  if (result_type == NULL)
+    result_type_tree = boolean_type_node;
+  else
+    result_type_tree = type_to_tree(result_type->get_backend(context->gogo()));
+
+  tree ret = fold_build2(code, result_type_tree, left_tree, right_tree);
   if (CAN_HAVE_LOCATION_P(ret))
     SET_EXPR_LOCATION(ret, location.gcc_location());
   return ret;
@@ -6503,7 +6535,7 @@ class Builtin_call_expression : public Call_expression
   bool
   do_numeric_constant_value(Numeric_constant*) const;
 
-  void
+  bool
   do_discarding_value();
 
   Type*
@@ -6667,38 +6699,6 @@ Builtin_call_expression::do_set_recover_arg(Expression* arg)
   this->set_args(new_args);
 }
 
-// A traversal class which looks for a call expression.
-
-class Find_call_expression : public Traverse
-{
- public:
-  Find_call_expression()
-    : Traverse(traverse_expressions),
-      found_(false)
-  { }
-
-  int
-  expression(Expression**);
-
-  bool
-  found()
-  { return this->found_; }
-
- private:
-  bool found_;
-};
-
-int
-Find_call_expression::expression(Expression** pexpr)
-{
-  if ((*pexpr)->call_expression() != NULL)
-    {
-      this->found_ = true;
-      return TRAVERSE_EXIT;
-    }
-  return TRAVERSE_CONTINUE;
-}
-
 // Lower a builtin call expression.  This turns new and make into
 // specific expressions.  We also convert to a constant if we can.
 
@@ -6719,20 +6719,6 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function,
 
   if (this->is_constant())
     {
-      // We can only lower len and cap if there are no function calls
-      // in the arguments.  Otherwise we have to make the call.
-      if (this->code_ == BUILTIN_LEN || this->code_ == BUILTIN_CAP)
-       {
-         Expression* arg = this->one_arg();
-         if (arg != NULL && !arg->is_constant())
-           {
-             Find_call_expression find_call;
-             Expression::traverse(&arg, &find_call);
-             if (find_call.found())
-               return this;
-           }
-       }
-
       Numeric_constant nc;
       if (this->numeric_constant_value(&nc))
        return nc.expression(loc);
@@ -7049,8 +7035,42 @@ Builtin_call_expression::one_arg() const
   return args->front();
 }
 
-// Return whether this is constant: len of a string, or len or cap of
-// a fixed array, or unsafe.Sizeof, unsafe.Offsetof, unsafe.Alignof.
+// A traversal class which looks for a call or receive expression.
+
+class Find_call_expression : public Traverse
+{
+ public:
+  Find_call_expression()
+    : Traverse(traverse_expressions),
+      found_(false)
+  { }
+
+  int
+  expression(Expression**);
+
+  bool
+  found()
+  { return this->found_; }
+
+ private:
+  bool found_;
+};
+
+int
+Find_call_expression::expression(Expression** pexpr)
+{
+  if ((*pexpr)->call_expression() != NULL
+      || (*pexpr)->receive_expression() != NULL)
+    {
+      this->found_ = true;
+      return TRAVERSE_EXIT;
+    }
+  return TRAVERSE_CONTINUE;
+}
+
+// Return whether this is constant: len of a string constant, or len
+// or cap of an array, or unsafe.Sizeof, unsafe.Offsetof,
+// unsafe.Alignof.
 
 bool
 Builtin_call_expression::do_is_constant() const
@@ -7073,6 +7093,17 @@ Builtin_call_expression::do_is_constant() const
            && !arg_type->points_to()->is_slice_type())
          arg_type = arg_type->points_to();
 
+       // The len and cap functions are only constant if there are no
+       // function calls or channel operations in the arguments.
+       // Otherwise we have to make the call.
+       if (!arg->is_constant())
+         {
+           Find_call_expression find_call;
+           Expression::traverse(&arg, &find_call);
+           if (find_call.found())
+             return false;
+         }
+
        if (arg_type->array_type() != NULL
            && arg_type->array_type()->length() != NULL)
          return true;
@@ -7306,7 +7337,7 @@ Builtin_call_expression::do_numeric_constant_value(Numeric_constant* nc) const
 // discarding the value of an ordinary function call, but we do for
 // builtin functions, purely for consistency with the gc compiler.
 
-void
+bool
 Builtin_call_expression::do_discarding_value()
 {
   switch (this->code_)
@@ -7327,7 +7358,7 @@ Builtin_call_expression::do_discarding_value()
     case BUILTIN_OFFSETOF:
     case BUILTIN_SIZEOF:
       this->unused_value_error();
-      break;
+      return false;
 
     case BUILTIN_CLOSE:
     case BUILTIN_COPY:
@@ -7336,7 +7367,7 @@ Builtin_call_expression::do_discarding_value()
     case BUILTIN_PRINT:
     case BUILTIN_PRINTLN:
     case BUILTIN_RECOVER:
-      break;
+      return true;
     }
 }
 
@@ -7459,7 +7490,7 @@ Builtin_call_expression::do_determine_type(const Type_context* context)
        if (args != NULL && args->size() == 2)
          {
            Type* t1 = args->front()->type();
-           Type* t2 = args->front()->type();
+           Type* t2 = args->back()->type();
            if (!t1->is_abstract())
              arg_type = t1;
            else if (!t2->is_abstract())
@@ -8482,6 +8513,16 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
     return Expression::make_cast(this->fn_->type(), this->args_->front(),
                                 loc);
 
+  // Because do_type will return an error type and thus prevent future
+  // errors, check for that case now to ensure that the error gets
+  // reported.
+  if (this->get_function_type() == NULL)
+    {
+      if (!this->fn_->type()->is_error())
+       this->report_error(_("expected function"));
+      return Expression::make_error(loc);
+    }
+
   // Recognize a call to a builtin function.
   Func_expression* fne = this->fn_->func_expression();
   if (fne != NULL
@@ -9171,6 +9212,9 @@ Call_expression::do_get_tree(Translate_context* context)
        }
     }
 
+  if (func == NULL)
+    fn = save_expr(fn);
+
   tree ret = build_call_array(excess_type != NULL_TREE ? excess_type : rettype,
                              fn, nargs, args);
   delete[] args;
@@ -9204,6 +9248,24 @@ Call_expression::do_get_tree(Translate_context* context)
   if (this->results_ != NULL)
     ret = this->set_results(context, ret);
 
+  // We can't unwind the stack past a call to nil, so we need to
+  // insert an explicit check so that the panic can be recovered.
+  if (func == NULL)
+    {
+      tree compare = fold_build2_loc(location.gcc_location(), EQ_EXPR,
+                                    boolean_type_node, fn,
+                                    fold_convert_loc(location.gcc_location(),
+                                                     TREE_TYPE(fn),
+                                                     null_pointer_node));
+      tree crash = build3_loc(location.gcc_location(), COND_EXPR,
+                             void_type_node, compare,
+                             gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
+                                                 location),
+                             NULL_TREE);
+      ret = fold_build2_loc(location.gcc_location(), COMPOUND_EXPR,
+                           TREE_TYPE(ret), crash, ret);
+    }
+
   this->tree_ = ret;
 
   return ret;
@@ -14055,7 +14117,7 @@ Numeric_constant::check_int_type(Integer_type* type, bool issue_error,
 
 bool
 Numeric_constant::check_float_type(Float_type* type, bool issue_error,
-                                  Location location) const
+                                  Location location)
 {
   mpfr_t val;
   switch (this->classification_)
@@ -14108,6 +14170,29 @@ Numeric_constant::check_float_type(Float_type* type, bool issue_error,
        }
 
       ret = exp <= max_exp;
+
+      if (ret)
+       {
+         // Round the constant to the desired type.
+         mpfr_t t;
+         mpfr_init(t);
+         switch (type->bits())
+           {
+           case 32:
+             mpfr_set_prec(t, 24);
+             break;
+           case 64:
+             mpfr_set_prec(t, 53);
+             break;
+           default:
+             go_unreachable();
+           }
+         mpfr_set(t, val, GMP_RNDN);
+         mpfr_set(val, t, GMP_RNDN);
+         mpfr_clear(t);
+
+         this->set_float(type, val);
+       }
     }
 
   mpfr_clear(val);
@@ -14122,7 +14207,7 @@ Numeric_constant::check_float_type(Float_type* type, bool issue_error,
 
 bool
 Numeric_constant::check_complex_type(Complex_type* type, bool issue_error,
-                                    Location location) const
+                                    Location location)
 {
   if (type->is_abstract())
     return true;
@@ -14141,46 +14226,77 @@ Numeric_constant::check_complex_type(Complex_type* type, bool issue_error,
     }
 
   mpfr_t real;
+  mpfr_t imag;
   switch (this->classification_)
     {
     case NC_INT:
     case NC_RUNE:
       mpfr_init_set_z(real, this->u_.int_val, GMP_RNDN);
+      mpfr_init_set_ui(imag, 0, GMP_RNDN);
       break;
 
     case NC_FLOAT:
       mpfr_init_set(real, this->u_.float_val, GMP_RNDN);
+      mpfr_init_set_ui(imag, 0, GMP_RNDN);
       break;
 
     case NC_COMPLEX:
-      if (!mpfr_nan_p(this->u_.complex_val.imag)
-         && !mpfr_inf_p(this->u_.complex_val.imag)
-         && !mpfr_zero_p(this->u_.complex_val.imag))
-       {
-         if (mpfr_get_exp(this->u_.complex_val.imag) > max_exp)
-           {
-             if (issue_error)
-               error_at(location, "complex imaginary part overflow");
-             return false;
-           }
-       }
       mpfr_init_set(real, this->u_.complex_val.real, GMP_RNDN);
+      mpfr_init_set(imag, this->u_.complex_val.imag, GMP_RNDN);
       break;
 
     default:
       go_unreachable();
     }
 
-  bool ret;
-  if (mpfr_nan_p(real) || mpfr_inf_p(real) || mpfr_zero_p(real))
-    ret = true;
-  else
-    ret = mpfr_get_exp(real) <= max_exp;
+  bool ret = true;
+  if (!mpfr_nan_p(real)
+      && !mpfr_inf_p(real)
+      && !mpfr_zero_p(real)
+      && mpfr_get_exp(real) > max_exp)
+    {
+      if (issue_error)
+       error_at(location, "complex real part overflow");
+      ret = false;
+    }
 
-  mpfr_clear(real);
+  if (!mpfr_nan_p(imag)
+      && !mpfr_inf_p(imag)
+      && !mpfr_zero_p(imag)
+      && mpfr_get_exp(imag) > max_exp)
+    {
+      if (issue_error)
+       error_at(location, "complex imaginary part overflow");
+      ret = false;
+    }
 
-  if (!ret && issue_error)
-    error_at(location, "complex real part overflow");
+  if (ret)
+    {
+      // Round the constant to the desired type.
+      mpfr_t t;
+      mpfr_init(t);
+      switch (type->bits())
+       {
+       case 64:
+         mpfr_set_prec(t, 24);
+         break;
+       case 128:
+         mpfr_set_prec(t, 53);
+         break;
+       default:
+         go_unreachable();
+       }
+      mpfr_set(t, real, GMP_RNDN);
+      mpfr_set(real, t, GMP_RNDN);
+      mpfr_set(t, imag, GMP_RNDN);
+      mpfr_set(imag, t, GMP_RNDN);
+      mpfr_clear(t);
+
+      this->set_complex(type, real, imag);
+    }
+
+  mpfr_clear(real);
+  mpfr_clear(imag);
 
   return ret;
 }