diff options
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 267 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 5 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo-tree.cc | 25 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 78 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 7 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 7 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 22 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 158 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 27 |
9 files changed, 442 insertions, 154 deletions
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 2bbb11e6c3f..39baf9d1c15 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -505,6 +505,8 @@ Expression::convert_interface_to_interface(Translate_context* context, lhs_type_descriptor, TREE_TYPE(rhs_type_descriptor), rhs_type_descriptor); + if (call == error_mark_node) + return error_mark_node; // This will panic if the interface conversion fails. TREE_NOTHROW(assert_interface_decl) = 0; elt->value = fold_convert_loc(location, TREE_TYPE(field), call); @@ -535,6 +537,8 @@ Expression::convert_interface_to_interface(Translate_context* context, lhs_type_descriptor, TREE_TYPE(rhs_type_descriptor), rhs_type_descriptor); + if (call == error_mark_node) + return error_mark_node; // This will panic if the interface conversion fails. TREE_NOTHROW(convert_interface_decl) = 0; elt->value = fold_convert_loc(location, TREE_TYPE(field), call); @@ -599,6 +603,8 @@ Expression::convert_interface_to_type(Translate_context* context, rhs_type_descriptor, TREE_TYPE(rhs_inter_descriptor), rhs_inter_descriptor); + if (call == error_mark_node) + return error_mark_node; // This call will panic if the conversion is invalid. TREE_NOTHROW(check_interface_type_decl) = 0; @@ -2279,6 +2285,32 @@ Expression::make_complex(const mpfr_t* real, const mpfr_t* imag, Type* type, return new Complex_expression(real, imag, type, location); } +// Find a named object in an expression. + +class Find_named_object : public Traverse +{ + public: + Find_named_object(Named_object* no) + : Traverse(traverse_expressions), + no_(no), found_(false) + { } + + // Whether we found the object. + bool + found() const + { return this->found_; } + + protected: + int + expression(Expression**); + + private: + // The object we are looking for. + Named_object* no_; + // Whether we found it. + bool found_; +}; + // A reference to a const in an expression. class Const_expression : public Expression @@ -2286,9 +2318,13 @@ class Const_expression : public Expression public: Const_expression(Named_object* constant, source_location location) : Expression(EXPRESSION_CONST_REFERENCE, location), - constant_(constant), type_(NULL) + constant_(constant), type_(NULL), seen_(false) { } + Named_object* + named_object() + { return this->constant_; } + const std::string& name() const { return this->constant_->name(); } @@ -2344,6 +2380,9 @@ class Const_expression : public Expression // The type of this reference. This is used if the constant has an // abstract type. Type* type_; + // Used to prevent infinite recursion when a constant incorrectly + // refers to itself. + mutable bool seen_; }; // Lower a constant expression. This is where we convert the @@ -2381,6 +2420,9 @@ bool Const_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val, Type** ptype) const { + if (this->seen_) + return false; + Type* ctype; if (this->type_ != NULL) ctype = this->type_; @@ -2390,9 +2432,14 @@ Const_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val, return false; Expression* e = this->constant_->const_value()->expr(); + + this->seen_ = true; + Type* t; bool r = e->integer_constant_value(iota_is_constant, val, &t); + this->seen_ = false; + if (r && ctype != NULL && !Integer_expression::check_constant(val, ctype, this->location())) @@ -2407,6 +2454,9 @@ Const_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val, bool Const_expression::do_float_constant_value(mpfr_t val, Type** ptype) const { + if (this->seen_) + return false; + Type* ctype; if (this->type_ != NULL) ctype = this->type_; @@ -2415,9 +2465,14 @@ Const_expression::do_float_constant_value(mpfr_t val, Type** ptype) const if (ctype != NULL && ctype->float_type() == NULL) return false; + this->seen_ = true; + Type* t; bool r = this->constant_->const_value()->expr()->float_constant_value(val, &t); + + this->seen_ = false; + if (r && ctype != NULL) { if (!Float_expression::check_constant(val, ctype, this->location())) @@ -2434,6 +2489,9 @@ bool Const_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag, Type **ptype) const { + if (this->seen_) + return false; + Type* ctype; if (this->type_ != NULL) ctype = this->type_; @@ -2442,10 +2500,15 @@ Const_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag, if (ctype != NULL && ctype->complex_type() == NULL) return false; + this->seen_ = true; + Type *t; bool r = this->constant_->const_value()->expr()->complex_constant_value(real, imag, &t); + + this->seen_ = false; + if (r && ctype != NULL) { if (!Complex_expression::check_constant(real, imag, ctype, @@ -2464,13 +2527,32 @@ Const_expression::do_type() { if (this->type_ != NULL) return this->type_; + + if (this->seen_) + { + this->report_error(_("constant refers to itself")); + this->type_ = Type::make_error_type(); + return this->type_; + } + + this->seen_ = true; + Named_constant* nc = this->constant_->const_value(); Type* ret = nc->type(); + if (ret != NULL) - return ret; + { + this->seen_ = false; + return ret; + } + // During parsing, a named constant may have a NULL type, but we // must not return a NULL type here. - return nc->expr()->type(); + ret = nc->expr()->type(); + + this->seen_ = false; + + return ret; } // Set the type of the const reference. @@ -2513,6 +2595,19 @@ Const_expression::do_determine_type(const Type_context* context) void Const_expression::do_check_types(Gogo*) { + if (this->type_ != NULL && this->type_->is_error_type()) + return; + + Expression* init = this->constant_->const_value()->expr(); + Find_named_object find_named_object(this->constant_); + Expression::traverse(&init, &find_named_object); + if (find_named_object.found()) + { + this->report_error(_("constant refers to itself")); + this->type_ = Type::make_error_type(); + return; + } + if (this->type_ == NULL || this->type_->is_abstract()) return; @@ -2630,6 +2725,32 @@ Expression::make_const_reference(Named_object* constant, return new Const_expression(constant, location); } +// Find a named object in an expression. + +int +Find_named_object::expression(Expression** pexpr) +{ + switch ((*pexpr)->classification()) + { + case Expression::EXPRESSION_CONST_REFERENCE: + if (static_cast<Const_expression*>(*pexpr)->named_object() == this->no_) + break; + return TRAVERSE_CONTINUE; + case Expression::EXPRESSION_VAR_REFERENCE: + if ((*pexpr)->var_expression()->named_object() == this->no_) + break; + return TRAVERSE_CONTINUE; + case Expression::EXPRESSION_FUNC_REFERENCE: + if ((*pexpr)->func_expression()->named_object() == this->no_) + break; + return TRAVERSE_CONTINUE; + default: + return TRAVERSE_CONTINUE; + } + this->found_ = true; + return TRAVERSE_EXIT; +} + // The nil value. class Nil_expression : public Expression @@ -6012,6 +6133,8 @@ Expression::comparison_tree(Translate_context* context, Operator op, descriptor, ptr_type_node, arg); + if (left_tree == error_mark_node) + return error_mark_node; // This can panic if the type is not comparable. TREE_NOTHROW(empty_interface_value_compare_decl) = 0; } @@ -6029,6 +6152,8 @@ Expression::comparison_tree(Translate_context* context, Operator op, descriptor, ptr_type_node, arg); + if (left_tree == error_mark_node) + return error_mark_node; // This can panic if the type is not comparable. TREE_NOTHROW(interface_value_compare_decl) = 0; } @@ -6054,6 +6179,8 @@ Expression::comparison_tree(Translate_context* context, Operator op, left_tree, TREE_TYPE(right_tree), right_tree); + if (left_tree == error_mark_node) + return error_mark_node; // This can panic if the type is uncomparable. TREE_NOTHROW(empty_interface_compare_decl) = 0; } @@ -6070,6 +6197,8 @@ Expression::comparison_tree(Translate_context* context, Operator op, left_tree, TREE_TYPE(right_tree), right_tree); + if (left_tree == error_mark_node) + return error_mark_node; // This can panic if the type is uncomparable. TREE_NOTHROW(interface_compare_decl) = 0; } @@ -6109,6 +6238,9 @@ 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); if (CAN_HAVE_LOCATION_P(ret)) SET_EXPR_LOCATION(ret, location); @@ -6172,7 +6304,7 @@ Bound_method_expression::do_check_types(Gogo*) ? this->expr_type_ : this->expr_->type()); etype = etype->deref(); - if (!Type::are_identical(rtype, etype, NULL)) + if (!Type::are_identical(rtype, etype, true, NULL)) this->report_error(_("method type does not match object type")); } } @@ -6719,7 +6851,7 @@ Builtin_call_expression::do_integer_constant_value(bool iota_is_constant, if (arg == NULL) return false; Type* arg_type = arg->type(); - if (arg_type->is_error_type()) + if (arg_type->is_error_type() || arg_type->is_undefined()) return false; if (arg_type->is_abstract()) return false; @@ -6849,7 +6981,7 @@ Builtin_call_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag, bool ret = false; Type* itype; if (args->back()->float_constant_value(i, &itype) - && Type::are_identical(rtype, itype, NULL)) + && Type::are_identical(rtype, itype, false, NULL)) { mpfr_set(real, r, GMP_RNDN); mpfr_set(imag, i, GMP_RNDN); @@ -7066,7 +7198,8 @@ Builtin_call_expression::check_one_arg() return false; } if (args->front()->is_error_expression() - || args->front()->type()->is_error_type()) + || args->front()->type()->is_error_type() + || args->front()->type()->is_undefined()) { this->set_is_error(); return false; @@ -7228,7 +7361,7 @@ Builtin_call_expression::do_check_types(Gogo*) break; } - if (!Type::are_identical(e1, e2, NULL)) + if (!Type::are_identical(e1, e2, true, NULL)) this->report_error(_("element types must be the same")); } break; @@ -7282,7 +7415,7 @@ Builtin_call_expression::do_check_types(Gogo*) || args->back()->type()->is_error_type()) this->set_is_error(); else if (!Type::are_identical(args->front()->type(), - args->back()->type(), NULL)) + args->back()->type(), true, NULL)) this->report_error(_("cmplx arguments must have identical types")); else if (args->front()->type()->float_type() == NULL) this->report_error(_("cmplx arguments must have " @@ -7380,6 +7513,9 @@ Builtin_call_expression::do_get_tree(Translate_context* context) gcc_unreachable(); } + if (val_tree == error_mark_node) + return error_mark_node; + tree type_tree = Type::lookup_integer_type("int")->get_tree(gogo); if (type_tree == TREE_TYPE(val_tree)) return val_tree; @@ -7408,6 +7544,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) "__go_print_space", 0, void_type_node); + if (call == error_mark_node) + return error_mark_node; append_to_statement_list(call, &stmt_list); } @@ -7506,6 +7644,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) void_type_node, TREE_TYPE(arg), arg); + if (call == error_mark_node) + return error_mark_node; append_to_statement_list(call, &stmt_list); } } @@ -7518,6 +7658,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) "__go_print_nl", 0, void_type_node); + if (call == error_mark_node) + return error_mark_node; append_to_statement_list(call, &stmt_list); } @@ -7544,6 +7686,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) void_type_node, TREE_TYPE(arg_tree), arg_tree); + if (call == error_mark_node) + return error_mark_node; // This function will throw an exception. TREE_NOTHROW(panic_fndecl) = 0; // This function will not return. @@ -7596,6 +7740,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) 0, empty_tree); } + if (call == error_mark_node) + return error_mark_node; return fold_build3_loc(location, COND_EXPR, empty_tree, arg_tree, call, empty_nil_tree); } @@ -7665,6 +7811,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) arg1_tree = save_expr(arg1_tree); tree arg1_val = at->value_pointer_tree(gogo, arg1_tree); tree arg1_len = at->length_tree(gogo, arg1_tree); + if (arg1_val == error_mark_node || arg1_len == error_mark_node) + return error_mark_node; Type* arg2_type = arg2->type(); tree arg2_val; @@ -7682,6 +7830,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) arg2_val = String_type::bytes_tree(gogo, arg2_tree); arg2_len = String_type::length_tree(gogo, arg2_tree); } + if (arg2_val == error_mark_node || arg2_len == error_mark_node) + return error_mark_node; arg1_len = save_expr(arg1_len); arg2_len = save_expr(arg2_len); @@ -7694,6 +7844,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) Type* element_type = at->element_type(); tree element_type_tree = element_type->get_tree(gogo); + if (element_type_tree == error_mark_node) + return error_mark_node; tree element_size = TYPE_SIZE_UNIT(element_type_tree); tree bytecount = fold_convert_loc(location, TREE_TYPE(element_size), len); @@ -7702,9 +7854,23 @@ Builtin_call_expression::do_get_tree(Translate_context* context) bytecount, element_size); bytecount = fold_convert_loc(location, size_type_node, bytecount); - tree call = build_call_expr_loc(location, - built_in_decls[BUILT_IN_MEMMOVE], - 3, arg1_val, arg2_val, bytecount); + arg1_val = fold_convert_loc(location, ptr_type_node, arg1_val); + arg2_val = fold_convert_loc(location, ptr_type_node, arg2_val); + + static tree copy_fndecl; + tree call = Gogo::call_builtin(©_fndecl, + location, + "__go_copy", + 3, + void_type_node, + ptr_type_node, + arg1_val, + ptr_type_node, + arg2_val, + size_type_node, + bytecount); + if (call == error_mark_node) + return error_mark_node; return fold_build2_loc(location, COMPOUND_EXPR, TREE_TYPE(len), call, len); @@ -7717,12 +7883,29 @@ Builtin_call_expression::do_get_tree(Translate_context* context) Expression* arg1 = args->front(); Expression* arg2 = args->back(); + Array_type* at = arg1->type()->array_type(); + Type* element_type = at->element_type(); + tree arg1_tree = arg1->get_tree(context); tree arg2_tree = arg2->get_tree(context); if (arg1_tree == error_mark_node || arg2_tree == error_mark_node) return error_mark_node; - tree descriptor_tree = arg1->type()->type_descriptor_pointer(gogo); + Array_type* at2 = arg2->type()->array_type(); + arg2_tree = save_expr(arg2_tree); + tree arg2_val = at2->value_pointer_tree(gogo, arg2_tree); + tree arg2_len = at2->length_tree(gogo, arg2_tree); + if (arg2_val == error_mark_node || arg2_len == error_mark_node) + return error_mark_node; + arg2_val = fold_convert_loc(location, ptr_type_node, arg2_val); + arg2_len = fold_convert_loc(location, size_type_node, arg2_len); + + tree element_type_tree = element_type->get_tree(gogo); + if (element_type_tree == error_mark_node) + return error_mark_node; + tree element_size = TYPE_SIZE_UNIT(element_type_tree); + element_size = fold_convert_loc(location, size_type_node, + element_size); // We rebuild the decl each time since the slice types may // change. @@ -7730,14 +7913,16 @@ Builtin_call_expression::do_get_tree(Translate_context* context) return Gogo::call_builtin(&append_fndecl, location, "__go_append", - 3, + 4, TREE_TYPE(arg1_tree), - TREE_TYPE(descriptor_tree), - descriptor_tree, TREE_TYPE(arg1_tree), arg1_tree, - TREE_TYPE(arg2_tree), - arg2_tree); + ptr_type_node, + arg2_val, + size_type_node, + arg2_len, + size_type_node, + element_size); } case BUILTIN_REAL: @@ -8085,7 +8270,7 @@ Call_expression::is_compatible_varargs_argument(Named_object* function, Array_type* param_at = param_type->array_type(); if (param_at != NULL && Type::are_identical(var_at->element_type(), - param_at->element_type(), NULL)) + param_at->element_type(), true, NULL)) return true; error_at(arg->location(), "... mismatch: passing ...T as ..."); *issued_error = true; @@ -8618,6 +8803,9 @@ Call_result_expression::do_traverse(Traverse* traverse) Type* Call_result_expression::do_type() { + if (this->classification() == EXPRESSION_ERROR) + return Type::make_error_type(); + // THIS->CALL_ can be replaced with a temporary reference due to // Call_expression::do_must_eval_in_order when there is an error. Call_expression* ce = this->call_->call_expression(); @@ -8631,34 +8819,25 @@ Call_result_expression::do_type() for (unsigned int i = 0; i < this->index_; ++i) { if (pr == results->end()) - return Type::make_error_type(); + break; ++pr; } if (pr == results->end()) - return Type::make_error_type(); + { + this->report_error(_("number of results does not match " + "number of values")); + return Type::make_error_type(); + } return pr->type(); } -// Check the type. This is where we give an error if we're trying to -// extract too many values from a call. +// Check the type. Just make sure that we trigger the warning in +// do_type. void Call_result_expression::do_check_types(Gogo*) { - bool ok = true; - Call_expression* ce = this->call_->call_expression(); - if (ce != NULL) - ok = this->index_ < ce->result_count(); - else - { - // This can happen when the call returns a single value but we - // are asking for the second result. - if (this->call_->is_error_expression()) - return; - ok = false; - } - if (!ok) - this->report_error(_("number of results does not match number of values")); + this->type(); } // Determine the type. We have nothing to do here, but the 0 result @@ -9390,6 +9569,8 @@ String_index_expression::do_get_tree(Translate_context* context) start_tree, length_type, end_tree); + if (ret == error_mark_node) + return error_mark_node; // This will panic if the bounds are out of range for the // string. TREE_NOTHROW(strslice_fndecl) = 0; @@ -9559,6 +9740,8 @@ Map_index_expression::get_value_pointer(Translate_context* context, (insert ? boolean_true_node : boolean_false_node)); + if (call == error_mark_node) + return error_mark_node; // This can panic on a map of interface type if the interface holds // an uncomparable or unhashable type. TREE_NOTHROW(map_index_fndecl) = 0; @@ -9615,7 +9798,13 @@ Field_reference_expression::do_get_tree(Translate_context* context) return error_mark_node; gcc_assert(TREE_CODE(TREE_TYPE(struct_tree)) == RECORD_TYPE); tree field = TYPE_FIELDS(TREE_TYPE(struct_tree)); - gcc_assert(field != NULL_TREE); + if (field == NULL_TREE) + { + // This can happen for a type which refers to itself indirectly + // and then turns out to be erroneous. + gcc_assert(saw_errors()); + return error_mark_node; + } for (unsigned int i = this->field_index_; i > 0; --i) { field = DECL_CHAIN(field); @@ -11109,6 +11298,8 @@ Map_construction_expression::do_get_tree(Translate_context* context) TYPE_SIZE_UNIT(TREE_TYPE(val_field)), const_ptr_type_node, fold_convert(const_ptr_type_node, valaddr)); + if (call == error_mark_node) + return error_mark_node; tree ret; if (make_tmp == NULL) diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 53cca06d42e..5d7d90bdb01 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -1235,7 +1235,10 @@ class Call_expression : public Expression Expression* do_copy() { - return Expression::make_call(this->fn_->copy(), this->args_->copy(), + return Expression::make_call(this->fn_->copy(), + (this->args_ == NULL + ? NULL + : this->args_->copy()), this->is_varargs_, this->location()); } diff --git a/gcc/go/gofrontend/gogo-tree.cc b/gcc/go/gofrontend/gogo-tree.cc index 67f9949f695..e43f64d7e1a 100644 --- a/gcc/go/gofrontend/gogo-tree.cc +++ b/gcc/go/gofrontend/gogo-tree.cc @@ -341,7 +341,8 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc, void_type_node, build_pointer_type(root_list_type), build_fold_addr_expr(decl)); - append_to_statement_list(call, init_stmt_list); + if (call != error_mark_node) + append_to_statement_list(call, init_stmt_list); } // Build the decl for the initialization function. @@ -1684,7 +1685,8 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, void_type_node, ptr_type_node, this->defer_stack(end_loc)); - append_to_statement_list(call, &stmt_list); + if (call != error_mark_node) + append_to_statement_list(call, &stmt_list); tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list); tree set; @@ -1723,7 +1725,8 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, void_type_node, ptr_type_node, this->defer_stack(end_loc)); - TREE_NOTHROW(undefer_fndecl) = 0; + if (undefer_fndecl != NULL_TREE) + TREE_NOTHROW(undefer_fndecl) = 0; tree defer = Gogo::call_builtin(&check_fndecl, end_loc, @@ -2867,6 +2870,8 @@ Gogo::runtime_error(int code, source_location location) void_type_node, integer_type_node, build_int_cst(integer_type_node, code)); + if (ret == error_mark_node) + return error_mark_node; // The runtime error function panics and does not return. TREE_NOTHROW(runtime_error_fndecl) = 0; TREE_THIS_VOLATILE(runtime_error_fndecl) = 1; @@ -2904,6 +2909,8 @@ Gogo::send_on_channel(tree channel, tree val, bool blocking, bool for_select, (for_select ? boolean_true_node : boolean_false_node)); + if (ret == error_mark_node) + return error_mark_node; // This can panic if there are too many operations on a // closed channel. TREE_NOTHROW(send_small_fndecl) = 0; @@ -2922,6 +2929,8 @@ Gogo::send_on_channel(tree channel, tree val, bool blocking, bool for_select, channel, uint64_type_node, val); + if (ret == error_mark_node) + return error_mark_node; // This can panic if there are too many operations on a // closed channel. TREE_NOTHROW(send_nonblocking_small_fndecl) = 0; @@ -2967,6 +2976,8 @@ Gogo::send_on_channel(tree channel, tree val, bool blocking, bool for_select, (for_select ? boolean_true_node : boolean_false_node)); + if (call == error_mark_node) + return error_mark_node; // This can panic if there are too many operations on a // closed channel. TREE_NOTHROW(send_big_fndecl) = 0; @@ -2984,6 +2995,8 @@ Gogo::send_on_channel(tree channel, tree val, bool blocking, bool for_select, channel, ptr_type_node, val); + if (call == error_mark_node) + return error_mark_node; // This can panic if there are too many operations on a // closed channel. TREE_NOTHROW(send_nonblocking_big_fndecl) = 0; @@ -3025,6 +3038,8 @@ Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select, (for_select ? boolean_true_node : boolean_false_node)); + if (call == error_mark_node) + return error_mark_node; // This can panic if there are too many operations on a closed // channel. TREE_NOTHROW(receive_small_fndecl) = 0; @@ -3057,6 +3072,8 @@ Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select, (for_select ? boolean_true_node : boolean_false_node)); + if (call == error_mark_node) + return error_mark_node; // This can panic if there are too many operations on a closed // channel. TREE_NOTHROW(receive_big_fndecl) = 0; @@ -3114,6 +3131,8 @@ Gogo::make_trampoline(tree fnaddr, tree closure, source_location location) ptr_type_node, fold_convert_loc(location, ptr_type_node, closure)); + if (x == error_mark_node) + return error_mark_node; x = save_expr(x); diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 3a76adb7792..d8d704d956e 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -640,7 +640,7 @@ Gogo::start_function(const std::string& name, Function_type* type, } } - function->create_named_result_variables(); + function->create_named_result_variables(this); const std::string* pname; std::string nested_name; @@ -1472,7 +1472,8 @@ Check_types_traverse::constant(Named_object* named_object, bool) && !ctype->is_boolean_type() && !ctype->is_string_type()) { - error_at(constant->location(), "invalid constant type"); + if (!ctype->is_error_type()) + error_at(constant->location(), "invalid constant type"); constant->set_error(); } else if (!constant->expr()->is_constant()) @@ -2473,7 +2474,7 @@ Function::Function(Function_type* type, Function* enclosing, Block* block, // Create the named result variables. void -Function::create_named_result_variables() +Function::create_named_result_variables(Gogo* gogo) { const Typed_identifier_list* results = this->type_->results(); if (results == NULL @@ -2490,10 +2491,17 @@ Function::create_named_result_variables() p != results->end(); ++p, ++index) { - Result_variable* result = new Result_variable(p->type(), this, - index); - Named_object* no = block->bindings()->add_result_variable(p->name(), - result); + std::string name = p->name(); + if (Gogo::is_sink_name(name)) + { + static int unnamed_result_counter; + char buf[100]; + snprintf(buf, sizeof buf, "_$%d", unnamed_result_counter); + ++unnamed_result_counter; + name = gogo->pack_hidden_name(buf, false); + } + Result_variable* result = new Result_variable(p->type(), this, index); + Named_object* no = block->bindings()->add_result_variable(name, result); this->named_results_->push_back(no); } } @@ -3048,7 +3056,7 @@ Variable::Variable(Type* type, Expression* init, bool is_global, : type_(type), init_(init), preinit_(NULL), location_(location), is_global_(is_global), is_parameter_(is_parameter), is_receiver_(is_receiver), is_varargs_parameter_(false), - is_address_taken_(false), init_is_lowered_(false), + is_address_taken_(false), seen_(false), init_is_lowered_(false), type_from_init_tuple_(false), type_from_range_index_(false), type_from_range_value_(false), type_from_chan_element_(false), is_type_switch_var_(false) @@ -3082,7 +3090,18 @@ Variable::lower_init_expression(Gogo* gogo, Named_object* function) { if (this->init_ != NULL && !this->init_is_lowered_) { + if (this->seen_) + { + // We will give an error elsewhere, this is just to prevent + // an infinite loop. + return; + } + this->seen_ = true; + gogo->lower_expression(function, &this->init_); + + this->seen_ = false; + this->init_is_lowered_ = true; } } @@ -3119,7 +3138,10 @@ Variable::type_from_tuple(Expression* expr, bool report_error) const else if (expr->receive_expression() != NULL) { Expression* channel = expr->receive_expression()->channel(); - return channel->type()->channel_type()->element_type(); + Type* channel_type = channel->type(); + if (channel_type->is_error_type()) + return Type::make_error_type(); + return channel_type->channel_type()->element_type(); } else { @@ -3198,7 +3220,7 @@ Variable::type_from_chan_element(Expression* expr, bool report_error) const // with type determination, then this should be unnecessary. Type* -Variable::type() const +Variable::type() { // A variable in a type switch with a nil case will have the wrong // type here. This gets fixed up in determine_type, below. @@ -3213,14 +3235,26 @@ Variable::type() const type = NULL; } + if (this->seen_) + { + if (this->type_ == NULL || !this->type_->is_error_type()) + { + error_at(this->location_, "variable initializer refers to itself"); + this->type_ = Type::make_error_type(); + } + return this->type_; + } + + this->seen_ = true; + if (type != NULL) - return type; + ; else if (this->type_from_init_tuple_) - return this->type_from_tuple(init, false); + type = this->type_from_tuple(init, false); else if (this->type_from_range_index_ || this->type_from_range_value_) - return this->type_from_range(init, this->type_from_range_index_, false); + type = this->type_from_range(init, this->type_from_range_index_, false); else if (this->type_from_chan_element_) - return this->type_from_chan_element(init, false); + type = this->type_from_chan_element(init, false); else { gcc_assert(init != NULL); @@ -3233,9 +3267,21 @@ Variable::type() const if (type->is_void_type()) type = Type::make_error_type(); - - return type; } + + this->seen_ = false; + + return type; +} + +// Fetch the type from a const pointer, in which case it should have +// been set already. + +Type* +Variable::type() const +{ + gcc_assert(this->type_ != NULL); + return this->type_; } // Set the type if necessary. diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index d0cfa1e90e0..552a6434485 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -785,7 +785,7 @@ class Function // Create the named result variables in the outer block. void - create_named_result_variables(); + create_named_result_variables(Gogo*); // Add a new field to the closure variable. void @@ -1039,6 +1039,9 @@ class Variable // Get the type of the variable. Type* + type(); + + Type* type() const; // Return whether the type is defined yet. @@ -1258,6 +1261,8 @@ class Variable bool is_varargs_parameter_ : 1; // Whether something takes the address of this variable. bool is_address_taken_ : 1; + // True if we have seen this variable in a traversal. + bool seen_ : 1; // True if we have lowered the initialization expression. bool init_is_lowered_ : 1; // True if init is a tuple used to set the type. diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index ef63754dc8e..a0d87dc8c6a 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -2513,10 +2513,7 @@ Parse::function_lit() Function_type* type = this->signature(NULL, location); if (type == NULL) - { - this->block(); - return Expression::make_error(location); - } + type = Type::make_function_type(NULL, NULL, NULL, location); // For a function literal, the next token must be a '{'. If we // don't see that, then we may have a type expression. @@ -3982,6 +3979,8 @@ Parse::type_case_clause(Named_object* switch_no, Type_case_clauses* clauses) clauses->add(*p, true, false, NULL, location); clauses->add(types.back(), false, false, statements, location); } + else + clauses->add(Type::make_error_type(), false, false, statements, location); } // TypeSwitchCase = "case" type | "default" diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 2d17797c9dd..2c0dba0a11b 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -782,6 +782,12 @@ Tuple_assignment_statement::do_lower(Gogo*, Block* enclosing) { gcc_assert(prhs != this->rhs_->end()); + if ((*plhs)->is_error_expression() + || (*plhs)->type()->is_error_type() + || (*prhs)->is_error_expression() + || (*prhs)->type()->is_error_type()) + continue; + if ((*plhs)->is_sink_expression()) { b->add_statement(Statement::make_statement(*prhs)); @@ -802,6 +808,12 @@ Tuple_assignment_statement::do_lower(Gogo*, Block* enclosing) plhs != this->lhs_->end(); ++plhs, ++prhs) { + if ((*plhs)->is_error_expression() + || (*plhs)->type()->is_error_type() + || (*prhs)->is_error_expression() + || (*prhs)->type()->is_error_type()) + continue; + if ((*plhs)->is_sink_expression()) continue; @@ -1812,7 +1824,13 @@ Thunk_statement::simplify_statement(Gogo* gogo, Block* block) Call_expression* ce = this->call_->call_expression(); Function_type* fntype = ce->get_function_type(); - if (fntype == NULL || this->is_simple(fntype)) + if (fntype == NULL) + { + gcc_assert(saw_errors()); + this->set_is_error(); + return false; + } + if (this->is_simple(fntype)) return false; Expression* fn = ce->fn(); @@ -4210,6 +4228,8 @@ Select_clauses::get_tree(Translate_context* context, chans_arg, pointer_boolean_type_tree, is_sends_arg); + if (call == error_mark_node) + return error_mark_node; tree stmt_list = NULL_TREE; diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 1579f179aa7..af541e8eb51 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -301,16 +301,19 @@ Type::do_traverse(Traverse*) return TRAVERSE_CONTINUE; } -// Return whether two types are identical. If REASON is not NULL, -// optionally set *REASON to the reason the types are not identical. +// Return whether two types are identical. If ERRORS_ARE_IDENTICAL, +// then return true for all erroneous types; this is used to avoid +// cascading errors. If REASON is not NULL, optionally set *REASON to +// the reason the types are not identical. bool -Type::are_identical(const Type* t1, const Type* t2, std::string* reason) +Type::are_identical(const Type* t1, const Type* t2, bool errors_are_identical, + std::string* reason) { if (t1 == NULL || t2 == NULL) { - // Something is wrong. Return true to avoid cascading errors. - return true; + // Something is wrong. + return errors_are_identical ? true : t1 == t2; } // Skip defined forward declarations. @@ -320,15 +323,18 @@ Type::are_identical(const Type* t1, const Type* t2, std::string* reason) if (t1 == t2) return true; - // An undefined forward declaration is an error, so we return true - // to avoid cascading errors. + // An undefined forward declaration is an error. if (t1->forward_declaration_type() != NULL || t2->forward_declaration_type() != NULL) - return true; + return errors_are_identical; // Avoid cascading errors with error types. if (t1->is_error_type() || t2->is_error_type()) - return true; + { + if (errors_are_identical) + return true; + return t1->is_error_type() && t2->is_error_type(); + } // Get a good reason for the sink type. Note that the sink type on // the left hand side of an assignment is handled in are_assignable. @@ -368,70 +374,45 @@ Type::are_identical(const Type* t1, const Type* t2, std::string* reason) case TYPE_FUNCTION: return t1->function_type()->is_identical(t2->function_type(), false, + errors_are_identical, reason); case TYPE_POINTER: - return Type::are_identical(t1->points_to(), t2->points_to(), reason); + return Type::are_identical(t1->points_to(), t2->points_to(), + errors_are_identical, reason); case TYPE_STRUCT: - return t1->struct_type()->is_identical(t2->struct_type()); + return t1->struct_type()->is_identical(t2->struct_type(), + errors_are_identical); case TYPE_ARRAY: - return t1->array_type()->is_identical(t2->array_type()); + return t1->array_type()->is_identical(t2->array_type(), + errors_are_identical); case TYPE_MAP: - return t1->map_type()->is_identical(t2->map_type()); + return t1->map_type()->is_identical(t2->map_type(), + errors_are_identical); case TYPE_CHANNEL: - return t1->channel_type()->is_identical(t2->channel_type()); + return t1->channel_type()->is_identical(t2->channel_type(), + errors_are_identical); case TYPE_INTERFACE: - return t1->interface_type()->is_identical(t2->interface_type()); + return t1->interface_type()->is_identical(t2->interface_type(), + errors_are_identical); default: gcc_unreachable(); } } -// Return true if two types are identical when it comes to storing -// them in a hash table. This differs from Type::are_identical with -// regard to how we handle error types. We want to treat error types -// as identical to other types when it comes to reporting -// compatibility errors, but we want to treat them as different when -// it comes to storing them in a hash table. - -bool -Type::are_identical_for_hash_table(const Type* t1, const Type *t2) -{ - if (t1 == NULL || t2 == NULL) - return t1 == t2; - - t1 = t1->forwarded(); - t2 = t2->forwarded(); - - if (t1 == t2) - return true; - - // Undefined forward declarations are only equal to themselves. - if (t1->forward_declaration_type() != NULL - || t2->forward_declaration_type() != NULL) - return false; - - // The error type is only equal to the error type. - if (t1->is_error_type() || t2->is_error_type()) - return t1->is_error_type() && t2->is_error_type(); - - // Otherwise we can use the usual identity check. - return Type::are_identical(t1, t2, NULL); -} - // Return true if it's OK to have a binary operation with types LHS // and RHS. This is not used for shifts or comparisons. bool Type::are_compatible_for_binop(const Type* lhs, const Type* rhs) { - if (Type::are_identical(lhs, rhs, NULL)) + if (Type::are_identical(lhs, rhs, true, NULL)) return true; // A constant of abstract bool type may be mixed with any bool type. @@ -513,14 +494,14 @@ Type::are_assignable(const Type* lhs, const Type* rhs, std::string* reason) } // Identical types are assignable. - if (Type::are_identical(lhs, rhs, reason)) + if (Type::are_identical(lhs, rhs, true, reason)) return true; // The types are assignable if they have identical underlying types // and either LHS or RHS is not a named type. if (((lhs->named_type() != NULL && rhs->named_type() == NULL) || (rhs->named_type() != NULL && lhs->named_type() == NULL)) - && Type::are_identical(lhs->base(), rhs->base(), reason)) + && Type::are_identical(lhs->base(), rhs->base(), true, reason)) return true; // The types are assignable if LHS is an interface type and RHS @@ -547,6 +528,7 @@ Type::are_assignable(const Type* lhs, const Type* rhs, std::string* reason) && (lhs->named_type() == NULL || rhs->named_type() == NULL) && Type::are_identical(lhs->channel_type()->element_type(), rhs->channel_type()->element_type(), + true, reason)) return true; @@ -609,7 +591,7 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) // The types are convertible if they have identical underlying // types. if ((lhs->named_type() != NULL || rhs->named_type() != NULL) - && Type::are_identical(lhs->base(), rhs->base(), reason)) + && Type::are_identical(lhs->base(), rhs->base(), true, reason)) return true; // The types are convertible if they are both unnamed pointer types @@ -622,6 +604,7 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) || rhs->points_to()->named_type() != NULL) && Type::are_identical(lhs->points_to()->base(), rhs->points_to()->base(), + true, reason)) return true; @@ -2352,7 +2335,7 @@ bool Function_type::is_valid_redeclaration(const Function_type* t, std::string* reason) const { - if (!this->is_identical(t, false, reason)) + if (!this->is_identical(t, false, true, reason)) return false; // A redeclaration of a function is required to use the same names @@ -2436,6 +2419,7 @@ Function_type::is_valid_redeclaration(const Function_type* t, bool Function_type::is_identical(const Function_type* t, bool ignore_receiver, + bool errors_are_identical, std::string* reason) const { if (!ignore_receiver) @@ -2450,7 +2434,8 @@ Function_type::is_identical(const Function_type* t, bool ignore_receiver, } if (r1 != NULL) { - if (!Type::are_identical(r1->type(), r2->type(), reason)) + if (!Type::are_identical(r1->type(), r2->type(), errors_are_identical, + reason)) { if (reason != NULL && !reason->empty()) *reason = "receiver: " + *reason; @@ -2481,7 +2466,8 @@ Function_type::is_identical(const Function_type* t, bool ignore_receiver, return false; } - if (!Type::are_identical(p1->type(), p2->type(), NULL)) + if (!Type::are_identical(p1->type(), p2->type(), + errors_are_identical, NULL)) { if (reason != NULL) *reason = _("different parameter types"); @@ -2525,7 +2511,8 @@ Function_type::is_identical(const Function_type* t, bool ignore_receiver, return false; } - if (!Type::are_identical(res1->type(), res2->type(), NULL)) + if (!Type::are_identical(res1->type(), res2->type(), + errors_are_identical, NULL)) { if (reason != NULL) *reason = _("different result types"); @@ -3445,7 +3432,8 @@ Struct_type::do_has_pointer() const // Whether this type is identical to T. bool -Struct_type::is_identical(const Struct_type* t) const +Struct_type::is_identical(const Struct_type* t, + bool errors_are_identical) const { const Struct_field_list* fields1 = this->fields(); const Struct_field_list* fields2 = t->fields(); @@ -3461,7 +3449,8 @@ Struct_type::is_identical(const Struct_type* t) const if (pf1->field_name() != pf2->field_name()) return false; if (pf1->is_anonymous() != pf2->is_anonymous() - || !Type::are_identical(pf1->type(), pf2->type(), NULL)) + || !Type::are_identical(pf1->type(), pf2->type(), + errors_are_identical, NULL)) return false; if (!pf1->has_tag()) { @@ -4142,9 +4131,10 @@ Type::make_struct_type(Struct_field_list* fields, // Whether two array types are identical. bool -Array_type::is_identical(const Array_type* t) const +Array_type::is_identical(const Array_type* t, bool errors_are_identical) const { - if (!Type::are_identical(this->element_type(), t->element_type(), NULL)) + if (!Type::are_identical(this->element_type(), t->element_type(), + errors_are_identical, NULL)) return false; Expression* l1 = this->length(); @@ -4960,10 +4950,12 @@ Map_type::do_verify() // Whether two map types are identical. bool -Map_type::is_identical(const Map_type* t) const +Map_type::is_identical(const Map_type* t, bool errors_are_identical) const { - return (Type::are_identical(this->key_type(), t->key_type(), NULL) - && Type::are_identical(this->val_type(), t->val_type(), NULL)); + return (Type::are_identical(this->key_type(), t->key_type(), + errors_are_identical, NULL) + && Type::are_identical(this->val_type(), t->val_type(), + errors_are_identical, NULL)); } // Hash code. @@ -5102,6 +5094,8 @@ Map_type::do_make_expression_tree(Translate_context* context, context->gogo()->map_descriptor(this), sizetype, expr_tree); + if (ret == error_mark_node) + return error_mark_node; // This can panic if the capacity is out of range. TREE_NOTHROW(new_map_fndecl) = 0; @@ -5248,9 +5242,11 @@ Channel_type::do_hash_for_method(Gogo* gogo) const // Whether this type is the same as T. bool -Channel_type::is_identical(const Channel_type* t) const +Channel_type::is_identical(const Channel_type* t, + bool errors_are_identical) const { - if (!Type::are_identical(this->element_type(), t->element_type(), NULL)) + if (!Type::are_identical(this->element_type(), t->element_type(), + errors_are_identical, NULL)) return false; return (this->may_send_ == t->may_send_ && this->may_receive_ == t->may_receive_); @@ -5350,6 +5346,8 @@ Channel_type::do_make_expression_tree(Translate_context* context, element_size_tree, sizetype, expr_tree); + if (ret == error_mark_node) + return error_mark_node; // This can panic if the capacity is out of range. TREE_NOTHROW(new_channel_fndecl) = 0; @@ -5666,7 +5664,8 @@ Interface_type::is_unexported_method(Gogo* gogo, const std::string& name) const // Whether this type is identical with T. bool -Interface_type::is_identical(const Interface_type* t) const +Interface_type::is_identical(const Interface_type* t, + bool errors_are_identical) const { // We require the same methods with the same types. The methods // have already been sorted. @@ -5681,7 +5680,8 @@ Interface_type::is_identical(const Interface_type* t) const if (p1 == this->methods()->end()) return false; if (p1->name() != p2->name() - || !Type::are_identical(p1->type(), p2->type(), NULL)) + || !Type::are_identical(p1->type(), p2->type(), + errors_are_identical, NULL)) return false; } if (p1 != this->methods()->end()) @@ -5720,7 +5720,7 @@ Interface_type::is_compatible_for_assign(const Interface_type* t, } std::string subreason; - if (!Type::are_identical(p->type(), m->type(), &subreason)) + if (!Type::are_identical(p->type(), m->type(), true, &subreason)) { if (reason != NULL) { @@ -5855,7 +5855,7 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const Function_type* m_fn_type = m->type()->function_type(); gcc_assert(p_fn_type != NULL && m_fn_type != NULL); std::string subreason; - if (!p_fn_type->is_identical(m_fn_type, true, &subreason)) + if (!p_fn_type->is_identical(m_fn_type, true, true, &subreason)) { if (reason != NULL) { @@ -6887,11 +6887,17 @@ Named_type::do_get_tree(Gogo* gogo) break; case TYPE_FUNCTION: - // Don't recur infinitely if a function type refers to itself. - // Ideally we would build a circular data structure here, but - // GENERIC can't handle them. + // GENERIC can't handle a pointer to a function type whose + // return type is a pointer to the function type itself. It + // does into infinite loops when walking the types. if (this->seen_) - return ptr_type_node; + { + Function_type* fntype = this->type_->function_type(); + if (fntype->results() != NULL + && fntype->results()->size() == 1 + && fntype->results()->front().type()->forwarded() == this) + return ptr_type_node; + } this->seen_ = true; t = Type::get_named_type_tree(gogo, this->type_); this->seen_ = false; @@ -7568,7 +7574,7 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, const std::string& name, source_location location) { - if (type->is_error_type()) + if (type->deref()->is_error_type()) return Expression::make_error(location); const Named_type* nt = type->named_type(); @@ -7758,7 +7764,8 @@ Type::find_field_or_method(const Type* type, if (!pf->is_anonymous()) continue; - if (pf->type()->is_error_type() || pf->type()->is_undefined()) + if (pf->type()->deref()->is_error_type() + || pf->type()->deref()->is_undefined()) continue; Named_type* fnt = pf->type()->deref()->named_type(); @@ -7879,7 +7886,8 @@ Type::is_unexported_field_or_method(Gogo* gogo, const Type* type, ++pf) { if (pf->is_anonymous() - && (!pf->type()->is_error_type() && !pf->type()->is_undefined())) + && (!pf->type()->deref()->is_error_type() + && !pf->type()->deref()->is_undefined())) { Named_type* subtype = pf->type()->deref()->named_type(); gcc_assert(subtype != NULL); diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 43a2708547c..2a713b067d4 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -503,16 +503,13 @@ class Type verify() { return this->do_verify(); } - // Return true if two types are identical. If this returns false, + // Return true if two types are identical. If ERRORS_ARE_IDENTICAL, + // returns that an erroneous type is identical to any other type; + // this is used to avoid cascading errors. If this returns false, // and REASON is not NULL, it may set *REASON. static bool - are_identical(const Type* lhs, const Type* rhs, std::string* reason); - - // Return true if two types are identical when it comes to putting - // them in a hash table. This differs from are_identical only in - // how error types are handled. - static bool - are_identical_for_hash_table(const Type*, const Type*); + are_identical(const Type* lhs, const Type* rhs, bool errors_are_identical, + std::string* reason); // Return true if two types are compatible for use in a binary // operation, other than a shift, comparison, or channel send. This @@ -1110,7 +1107,7 @@ class Type_identical public: bool operator()(const Type* t1, const Type* t2) const - { return Type::are_identical_for_hash_table(t1, t2); } + { return Type::are_identical(t1, t2, false, NULL); } }; // An identifier with a type. @@ -1583,7 +1580,7 @@ class Function_type : public Type // Whether this type is the same as T. bool is_identical(const Function_type* t, bool ignore_receiver, - std::string*) const; + bool errors_are_identical, std::string*) const; // Record that this is a varargs function. void @@ -1890,7 +1887,7 @@ class Struct_type : public Type // Whether this type is identical with T. bool - is_identical(const Struct_type* t) const; + is_identical(const Struct_type* t, bool errors_are_identical) const; // Whether this struct type has any hidden fields. This returns // true if any fields have hidden names, or if any non-pointer @@ -2009,7 +2006,7 @@ class Array_type : public Type // Whether this type is identical with T. bool - is_identical(const Array_type* t) const; + is_identical(const Array_type* t, bool errors_are_identical) const; // Whether this type has any hidden fields. bool @@ -2126,7 +2123,7 @@ class Map_type : public Type // Whether this type is identical with T. bool - is_identical(const Map_type* t) const; + is_identical(const Map_type* t, bool errors_are_identical) const; // Import a map type. static Map_type* @@ -2212,7 +2209,7 @@ class Channel_type : public Type // Whether this type is identical with T. bool - is_identical(const Channel_type* t) const; + is_identical(const Channel_type* t, bool errors_are_identical) const; // Import a channel type. static Channel_type* @@ -2315,7 +2312,7 @@ class Interface_type : public Type // Whether this type is identical with T. REASON is as in // implements_interface. bool - is_identical(const Interface_type* t) const; + is_identical(const Interface_type* t, bool errors_are_identical) const; // Whether we can assign T to this type. is_identical is known to // be false. |