diff options
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/ChangeLog | 19 | ||||
-rw-r--r-- | gcc/go/Make-lang.in | 9 | ||||
-rw-r--r-- | gcc/go/gccgo.texi | 6 | ||||
-rw-r--r-- | gcc/go/go-c.h | 3 | ||||
-rw-r--r-- | gcc/go/go-lang.c | 8 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 277 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 23 | ||||
-rw-r--r-- | gcc/go/gofrontend/go.cc | 5 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo-tree.cc | 30 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 110 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 43 | ||||
-rw-r--r-- | gcc/go/gofrontend/import.cc | 29 | ||||
-rw-r--r-- | gcc/go/gofrontend/import.h | 4 | ||||
-rw-r--r-- | gcc/go/gofrontend/lex.cc | 55 | ||||
-rw-r--r-- | gcc/go/gofrontend/lex.h | 4 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 171 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.h | 7 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 55 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 341 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 107 | ||||
-rw-r--r-- | gcc/go/lang.opt | 4 |
21 files changed, 987 insertions, 323 deletions
diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index d236325e52a..d78924ba60c 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,22 @@ +2012-10-30 Ian Lance Taylor <iant@google.com> + + * lang.opt (-fgo-relative-import-path): New option. + * go-lang.c (go_relative_import_path): New static variable. + (go_langhook_init): Pass go_relative_import_path to + go_create_gogo. + (go_langhook_handle_option): Handle -fgo-relative-import-path. + * go-c.h (go_create_gogo): Update declaration. + * gccgo.texi (Invoking gccgo): Document + -fgo-relative-import-path. + +2012-09-20 Ian Lance Taylor <iant@google.com> + + * Make-lang.in (go/gogo.o): Depend on filenames.h. + +2012-09-20 Release Manager + + * GCC 4.7.2 released. + 2012-06-14 Release Manager * GCC 4.7.1 released. diff --git a/gcc/go/Make-lang.in b/gcc/go/Make-lang.in index 34e5584cc09..b3cb2bdbc19 100644 --- a/gcc/go/Make-lang.in +++ b/gcc/go/Make-lang.in @@ -289,10 +289,11 @@ go/gogo-tree.o: go/gofrontend/gogo-tree.cc $(GO_SYSTEM_H) $(TOPLEV_H) \ convert.h output.h $(DIAGNOSTIC_H) $(GO_TYPES_H) \ $(GO_EXPRESSIONS_H) $(GO_STATEMENTS_H) $(GO_RUNTIME_H) \ go/gofrontend/backend.h $(GO_GOGO_H) -go/gogo.o: go/gofrontend/gogo.cc $(GO_SYSTEM_H) $(GO_C_H) \ - go/gofrontend/go-dump.h $(GO_LEX_H) $(GO_TYPES_H) $(GO_STATEMENTS_H) \ - $(GO_EXPRESSIONS_H) go/gofrontend/dataflow.h $(GO_RUNTIME_H) \ - $(GO_IMPORT_H) $(GO_EXPORT_H) go/gofrontend/backend.h $(GO_GOGO_H) +go/gogo.o: go/gofrontend/gogo.cc $(GO_SYSTEM_H) \ + $(srcdir)/../include/filenames.h $(GO_C_H) go/gofrontend/go-dump.h \ + $(GO_LEX_H) $(GO_TYPES_H) $(GO_STATEMENTS_H) $(GO_EXPRESSIONS_H) \ + go/gofrontend/dataflow.h $(GO_RUNTIME_H) $(GO_IMPORT_H) \ + $(GO_EXPORT_H) go/gofrontend/backend.h $(GO_GOGO_H) go/import.o: go/gofrontend/import.cc $(GO_SYSTEM_H) \ $(srcdir)/../include/filenames.h $(srcdir)/../include/simple-object.h \ $(GO_C_H) $(GO_GOGO_H) $(GO_LEX_H) $(GO_TYPES_H) $(GO_EXPORT_H) \ diff --git a/gcc/go/gccgo.texi b/gcc/go/gccgo.texi index a5e37e76e80..91930c812f6 100644 --- a/gcc/go/gccgo.texi +++ b/gcc/go/gccgo.texi @@ -184,6 +184,12 @@ Using either @option{-fgo-pkgpath} or @option{-fgo-prefix} disables the special treatment of the @code{main} package and permits that package to be imported like any other. +@item -fgo-relative-import-path=@var{dir} +@cindex @option{-fgo-relative-import-path} +A relative import is an import that starts with @file{./} or +@file{../}. If this option is used, @command{gccgo} will use +@var{dir} as a prefix for the relative import when searching for it. + @item -frequire-return-statement @itemx -fno-require-return-statement @cindex @option{-frequire-return-statement} diff --git a/gcc/go/go-c.h b/gcc/go/go-c.h index d46a08796e3..ea59fb6b39a 100644 --- a/gcc/go/go-c.h +++ b/gcc/go/go-c.h @@ -42,7 +42,8 @@ extern int go_enable_optimize (const char*); extern void go_add_search_path (const char*); extern void go_create_gogo (int int_type_size, int pointer_size, - const char* pkgpath, const char *prefix); + const char* pkgpath, const char *prefix, + const char *relative_import_path); extern void go_parse_input_files (const char**, unsigned int, bool only_check_syntax, diff --git a/gcc/go/go-lang.c b/gcc/go/go-lang.c index f02f769252b..61ca1478be6 100644 --- a/gcc/go/go-lang.c +++ b/gcc/go/go-lang.c @@ -85,6 +85,7 @@ struct GTY(()) language_function static const char *go_pkgpath = NULL; static const char *go_prefix = NULL; +static const char *go_relative_import_path = NULL; /* Language hooks. */ @@ -101,7 +102,8 @@ go_langhook_init (void) to, e.g., unsigned_char_type_node) but before calling build_common_builtin_nodes (because it calls, indirectly, go_type_for_size). */ - go_create_gogo (INT_TYPE_SIZE, POINTER_SIZE, go_pkgpath, go_prefix); + go_create_gogo (INT_TYPE_SIZE, POINTER_SIZE, go_pkgpath, go_prefix, + go_relative_import_path); build_common_builtin_nodes (); @@ -240,6 +242,10 @@ go_langhook_handle_option ( go_prefix = arg; break; + case OPT_fgo_relative_import_path_: + go_relative_import_path = arg; + break; + default: /* Just return 1 to indicate that the option is valid. */ break; diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index f57ca411885..e16cd84d0a0 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -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(); @@ -5184,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) @@ -5314,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. @@ -5450,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")); @@ -6518,7 +6535,7 @@ class Builtin_call_expression : public Call_expression bool do_numeric_constant_value(Numeric_constant*) const; - void + bool do_discarding_value(); Type* @@ -6682,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. @@ -6734,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); @@ -7064,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 @@ -7088,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; @@ -7321,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_) @@ -7342,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: @@ -7351,7 +7367,7 @@ Builtin_call_expression::do_discarding_value() case BUILTIN_PRINT: case BUILTIN_PRINTLN: case BUILTIN_RECOVER: - break; + return true; } } @@ -7474,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()) @@ -8497,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 @@ -9186,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; @@ -9219,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; @@ -14070,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_) @@ -14123,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); @@ -14137,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; @@ -14156,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; } diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index eea141fe776..1b74b801748 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -360,10 +360,11 @@ class Expression // This is called if the value of this expression is being // discarded. This issues warnings about computed values being - // unused. - void + // unused. This returns true if all is well, false if it issued an + // error message. + bool discarding_value() - { this->do_discarding_value(); } + { return this->do_discarding_value(); } // Return whether this is an error expression. bool @@ -689,7 +690,7 @@ class Expression { return false; } // Called by the parser if the value is being discarded. - virtual void + virtual bool do_discarding_value(); // Child class holds type. @@ -1205,7 +1206,7 @@ class Binary_expression : public Expression bool do_numeric_constant_value(Numeric_constant*) const; - void + bool do_discarding_value(); Type* @@ -1373,9 +1374,9 @@ class Call_expression : public Expression virtual Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); - void + bool do_discarding_value() - { } + { return true; } virtual Type* do_type(); @@ -2051,9 +2052,9 @@ class Receive_expression : public Expression do_traverse(Traverse* traverse) { return Expression::traverse(&this->channel_, traverse); } - void + bool do_discarding_value() - { } + { return true; } Type* do_type(); @@ -2219,10 +2220,10 @@ class Numeric_constant check_int_type(Integer_type*, bool, Location) const; bool - check_float_type(Float_type*, bool, Location) const; + check_float_type(Float_type*, bool, Location); bool - check_complex_type(Complex_type*, bool, Location) const; + check_complex_type(Complex_type*, bool, Location); // The kinds of constants. enum Classification diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc index 1f2ce8adcde..11692af8095 100644 --- a/gcc/go/gofrontend/go.cc +++ b/gcc/go/gofrontend/go.cc @@ -21,7 +21,7 @@ static Gogo* gogo; GO_EXTERN_C void go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath, - const char *prefix) + const char *prefix, const char *relative_import_path) { go_assert(::gogo == NULL); Linemap* linemap = go_get_linemap(); @@ -32,6 +32,9 @@ go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath, else if (prefix != NULL) ::gogo->set_prefix(prefix); + if (relative_import_path != NULL) + ::gogo->set_relative_import_path(relative_import_path); + // FIXME: This should be in the gcc dependent code. ::gogo->define_builtin_function_trees(); } diff --git a/gcc/go/gofrontend/gogo-tree.cc b/gcc/go/gofrontend/gogo-tree.cc index c746b9b1ee5..ad38e59d6f3 100644 --- a/gcc/go/gofrontend/gogo-tree.cc +++ b/gcc/go/gofrontend/gogo-tree.cc @@ -1002,9 +1002,19 @@ Named_object::get_id(Gogo* gogo) } if (this->is_type()) { - const Named_object* in_function = this->type_value()->in_function(); + unsigned int index; + const Named_object* in_function = this->type_value()->in_function(&index); if (in_function != NULL) - decl_name += '$' + Gogo::unpack_hidden_name(in_function->name()); + { + decl_name += '$' + Gogo::unpack_hidden_name(in_function->name()); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + decl_name += '$'; + decl_name += buf; + } + } } return get_identifier_from_string(decl_name); } @@ -2133,8 +2143,7 @@ Gogo::slice_constructor(tree slice_type_tree, tree values, tree count, tree Gogo::interface_method_table_for_type(const Interface_type* interface, - Named_type* type, - bool is_pointer) + Type* type, bool is_pointer) { const Typed_identifier_list* interface_methods = interface->methods(); go_assert(!interface_methods->empty()); @@ -2163,7 +2172,9 @@ Gogo::interface_method_table_for_type(const Interface_type* interface, // interface. If the interface has hidden methods, and the named // type is defined in a different package, then the interface // conversion table will be defined by that other package. - if (has_hidden_methods && type->named_object()->package() != NULL) + if (has_hidden_methods + && type->named_type() != NULL + && type->named_type()->named_object()->package() != NULL) { tree array_type = build_array_type(const_ptr_type_node, NULL); tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, array_type); @@ -2191,13 +2202,20 @@ Gogo::interface_method_table_for_type(const Interface_type* interface, Linemap::predeclared_location()); elt->value = fold_convert(const_ptr_type_node, tdp); + Named_type* nt = type->named_type(); + Struct_type* st = type->struct_type(); + go_assert(nt != NULL || st != NULL); size_t i = 1; for (Typed_identifier_list::const_iterator p = interface_methods->begin(); p != interface_methods->end(); ++p, ++i) { bool is_ambiguous; - Method* m = type->method_function(p->name(), &is_ambiguous); + Method* m; + if (nt != NULL) + m = nt->method_function(p->name(), &is_ambiguous); + else + m = st->method_function(p->name(), &is_ambiguous); go_assert(m != NULL); Named_object* no = m->named_object(); diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 6e9b8c124aa..c0aa496acc3 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -6,6 +6,8 @@ #include "go-system.h" +#include "filenames.h" + #include "go-c.h" #include "go-dump.h" #include "lex.h" @@ -42,6 +44,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size, pkgpath_set_(false), pkgpath_from_option_(false), prefix_from_option_(false), + relative_import_path_(), verify_types_(), interface_types_(), specific_type_functions_(), @@ -385,6 +388,57 @@ Gogo::import_package(const std::string& filename, bool is_local_name_exported, Location location) { + if (filename.empty()) + { + error_at(location, "import path is empty"); + return; + } + + const char *pf = filename.data(); + const char *pend = pf + filename.length(); + while (pf < pend) + { + unsigned int c; + int adv = Lex::fetch_char(pf, &c); + if (adv == 0) + { + error_at(location, "import path contains invalid UTF-8 sequence"); + return; + } + if (c == '\0') + { + error_at(location, "import path contains NUL"); + return; + } + if (c < 0x20 || c == 0x7f) + { + error_at(location, "import path contains control character"); + return; + } + if (c == '\\') + { + error_at(location, "import path contains backslash; use slash"); + return; + } + if (Lex::is_unicode_space(c)) + { + error_at(location, "import path contains space character"); + return; + } + if (c < 0x7f && strchr("!\"#$%&'()*,:;<=>?[]^`{|}", c) != NULL) + { + error_at(location, "import path contains invalid character '%c'", c); + return; + } + pf += adv; + } + + if (IS_ABSOLUTE_PATH(filename.c_str())) + { + error_at(location, "import path cannot be absolute path"); + return; + } + if (filename == "unsafe") { this->import_unsafe(local_name, is_local_name_exported, location); @@ -424,7 +478,8 @@ Gogo::import_package(const std::string& filename, return; } - Import::Stream* stream = Import::open_package(filename, location); + Import::Stream* stream = Import::open_package(filename, location, + this->relative_import_path_); if (stream == NULL) { error_at(location, "import file %qs not found", filename.c_str()); @@ -1003,7 +1058,15 @@ Gogo::add_type(const std::string& name, Type* type, Location location) Named_object* no = this->current_bindings()->add_type(name, NULL, type, location); if (!this->in_global_scope() && no->is_type()) - no->type_value()->set_in_function(this->functions_.back().function); + { + Named_object* f = this->functions_.back().function; + unsigned int index; + if (f->is_function()) + index = f->func_value()->new_local_type_index(); + else + index = 0; + no->type_value()->set_in_function(f, index); + } } // Add a named type. @@ -1025,7 +1088,12 @@ Gogo::declare_type(const std::string& name, Location location) if (!this->in_global_scope() && no->is_type_declaration()) { Named_object* f = this->functions_.back().function; - no->type_declaration_value()->set_in_function(f); + unsigned int index; + if (f->is_function()) + index = f->func_value()->new_local_type_index(); + else + index = 0; + no->type_declaration_value()->set_in_function(f, index); } return no; } @@ -2806,7 +2874,8 @@ int Build_method_tables::type(Type* type) { Named_type* nt = type->named_type(); - if (nt != NULL) + Struct_type* st = type->struct_type(); + if (nt != NULL || st != NULL) { for (std::vector<Interface_type*>::const_iterator p = this->interfaces_.begin(); @@ -2816,10 +2885,23 @@ Build_method_tables::type(Type* type) // We ask whether a pointer to the named type implements the // interface, because a pointer can implement more methods // than a value. - if ((*p)->implements_interface(Type::make_pointer_type(nt), NULL)) + if (nt != NULL) + { + if ((*p)->implements_interface(Type::make_pointer_type(nt), + NULL)) + { + nt->interface_method_table(this->gogo_, *p, false); + nt->interface_method_table(this->gogo_, *p, true); + } + } + else { - nt->interface_method_table(this->gogo_, *p, false); - nt->interface_method_table(this->gogo_, *p, true); + if ((*p)->implements_interface(Type::make_pointer_type(st), + NULL)) + { + st->interface_method_table(this->gogo_, *p, false); + st->interface_method_table(this->gogo_, *p, true); + } } } } @@ -2989,9 +3071,10 @@ Gogo::convert_named_types_in_bindings(Bindings* bindings) Function::Function(Function_type* type, Function* enclosing, Block* block, Location location) : type_(type), enclosing_(enclosing), results_(NULL), - closure_var_(NULL), block_(block), location_(location), fndecl_(NULL), - defer_stack_(NULL), results_are_named_(false), calls_recover_(false), - is_recover_thunk_(false), has_recover_thunk_(false) + closure_var_(NULL), block_(block), location_(location), labels_(), + local_type_count_(0), fndecl_(NULL), defer_stack_(NULL), + results_are_named_(false), calls_recover_(false), is_recover_thunk_(false), + has_recover_thunk_(false) { } @@ -4157,7 +4240,7 @@ Variable::determine_type() else if (type->is_call_multiple_result_type()) { error_at(this->location_, - "single variable set to multiple value function call"); + "single variable set to multiple-value function call"); type = Type::make_error_type(); } @@ -4599,9 +4682,10 @@ Named_object::set_type_value(Named_type* named_type) go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION); Type_declaration* td = this->u_.type_declaration; td->define_methods(named_type); - Named_object* in_function = td->in_function(); + unsigned int index; + Named_object* in_function = td->in_function(&index); if (in_function != NULL) - named_type->set_in_function(in_function); + named_type->set_in_function(in_function, index); delete td; this->classification_ = NAMED_OBJECT_TYPE; this->u_.type_value = named_type; diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index deb9968e84f..cc707ad2dde 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -206,6 +206,17 @@ class Gogo pkgpath_from_option() const { return this->pkgpath_from_option_; } + // Return the relative import path as set from the command line. + // Returns an empty string if it was not set. + const std::string& + relative_import_path() const + { return this->relative_import_path_; } + + // Set the relative import path from a command line option. + void + set_relative_import_path(const std::string& s) + {this->relative_import_path_ = s; } + // Return the priority to use for the package we are compiling. // This is two more than the largest priority of any package we // import. @@ -574,7 +585,7 @@ class Gogo // Build an interface method table for a type: a list of function // pointers, one for each interface method. This returns a decl. tree - interface_method_table_for_type(const Interface_type*, Named_type*, + interface_method_table_for_type(const Interface_type*, Type*, bool is_pointer); // Return a tree which allocate SIZE bytes to hold values of type @@ -732,6 +743,9 @@ class Gogo bool pkgpath_from_option_; // Whether an explicit prefix was set by -fgo-prefix. bool prefix_from_option_; + // The relative import path, from the -fgo-relative-import-path + // option. + std::string relative_import_path_; // A list of types to verify. std::vector<Type*> verify_types_; // A list of interface types defined while parsing. @@ -963,6 +977,11 @@ class Function void check_labels() const; + // Note that a new local type has been added. Return its index. + unsigned int + new_local_type_index() + { return this->local_type_count_++; } + // Whether this function calls the predeclared recover function. bool calls_recover() const @@ -1084,6 +1103,8 @@ class Function Location location_; // Labels defined or referenced in the function. Labels labels_; + // The number of local types defined in this function. + unsigned int local_type_count_; // The function decl. tree fndecl_; // The defer stack variable. A pointer to this variable is used to @@ -1638,8 +1659,8 @@ class Type_declaration { public: Type_declaration(Location location) - : location_(location), in_function_(NULL), methods_(), - issued_warning_(false) + : location_(location), in_function_(NULL), in_function_index_(0), + methods_(), issued_warning_(false) { } // Return the location. @@ -1650,13 +1671,19 @@ class Type_declaration // Return the function in which this type is declared. This will // return NULL for a type declared in global scope. Named_object* - in_function() - { return this->in_function_; } + in_function(unsigned int* pindex) + { + *pindex = this->in_function_index_; + return this->in_function_; + } // Set the function in which this type is declared. void - set_in_function(Named_object* f) - { this->in_function_ = f; } + set_in_function(Named_object* f, unsigned int index) + { + this->in_function_ = f; + this->in_function_index_ = index; + } // Add a method to this type. This is used when methods are defined // before the type. @@ -1689,6 +1716,8 @@ class Type_declaration // If this type is declared in a function, a pointer back to the // function in which it is defined. Named_object* in_function_; + // The index of this type in IN_FUNCTION_. + unsigned int in_function_index_; // Methods defined before the type is defined. Methods methods_; // True if we have issued a warning about a use of this type diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc index 9febf231897..4913100b5fd 100644 --- a/gcc/go/gofrontend/import.cc +++ b/gcc/go/gofrontend/import.cc @@ -41,6 +41,9 @@ go_add_search_path(const char* path) // When FILENAME is not an absolute path and does not start with ./ or // ../, we use the search path provided by -I and -L options. +// When FILENAME does start with ./ or ../, we use +// RELATIVE_IMPORT_PATH as a prefix. + // When FILENAME does not exist, we try modifying FILENAME to find the // file. We use the first of these which exists: // * We append ".gox". @@ -55,19 +58,35 @@ go_add_search_path(const char* path) // later in the search path. Import::Stream* -Import::open_package(const std::string& filename, Location location) +Import::open_package(const std::string& filename, Location location, + const std::string& relative_import_path) { bool is_local; if (IS_ABSOLUTE_PATH(filename)) is_local = true; - else if (filename[0] == '.' && IS_DIR_SEPARATOR(filename[1])) + else if (filename[0] == '.' + && (filename[1] == '\0' || IS_DIR_SEPARATOR(filename[1]))) is_local = true; else if (filename[0] == '.' && filename[1] == '.' - && IS_DIR_SEPARATOR(filename[2])) + && (filename[2] == '\0' || IS_DIR_SEPARATOR(filename[2]))) is_local = true; else is_local = false; + + std::string fn = filename; + if (is_local && !IS_ABSOLUTE_PATH(filename) && !relative_import_path.empty()) + { + if (fn == ".") + { + // A special case. + fn = relative_import_path; + } + else + fn = relative_import_path + '/' + fn; + is_local = false; + } + if (!is_local) { for (std::vector<std::string>::const_iterator p = search_path.begin(); @@ -77,14 +96,14 @@ Import::open_package(const std::string& filename, Location location) std::string indir = *p; if (!indir.empty() && indir[indir.size() - 1] != '/') indir += '/'; - indir += filename; + indir += fn; Stream* s = Import::try_package_in_directory(indir, location); if (s != NULL) return s; } } - Stream* s = Import::try_package_in_directory(filename, location); + Stream* s = Import::try_package_in_directory(fn, location); if (s != NULL) return s; diff --git a/gcc/go/gofrontend/import.h b/gcc/go/gofrontend/import.h index 67bdcb02d57..c6844cda8a5 100644 --- a/gcc/go/gofrontend/import.h +++ b/gcc/go/gofrontend/import.h @@ -124,8 +124,10 @@ class Import // Find import data. This searches the file system for FILENAME and // returns a pointer to a Stream object to read the data that it // exports. LOCATION is the location of the import statement. + // RELATIVE_IMPORT_PATH is used as a prefix for a relative import. static Stream* - open_package(const std::string& filename, Location location); + open_package(const std::string& filename, Location location, + const std::string& relative_import_path); // Constructor. Import(Stream*, Location); diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc index 5b7ce6869e6..6add84ed1f7 100644 --- a/gcc/go/gofrontend/lex.cc +++ b/gcc/go/gofrontend/lex.cc @@ -722,7 +722,16 @@ Lex::next_token() unsigned int ci; bool issued_error; this->lineoff_ = p - this->linebuf_; - this->advance_one_utf8_char(p, &ci, &issued_error); + const char *pnext = this->advance_one_utf8_char(p, &ci, + &issued_error); + + // Ignore byte order mark at start of file. + if (ci == 0xfeff) + { + p = pnext; + break; + } + if (Lex::is_unicode_letter(ci)) return this->gather_identifier(); @@ -831,6 +840,14 @@ Lex::advance_one_utf8_char(const char* p, unsigned int* value, *issued_error = true; return p + 1; } + + // Warn about byte order mark, except at start of file. + if (*value == 0xfeff && (this->lineno_ != 1 || this->lineoff_ != 0)) + { + error_at(this->location(), "Unicode (UTF-8) BOM in middle of file"); + *issued_error = true; + } + return p + adv; } @@ -1295,6 +1312,12 @@ Lex::append_char(unsigned int v, bool is_character, std::string* str, // Turn it into the "replacement character". v = 0xfffd; } + if (v >= 0xd800 && v < 0xe000) + { + warning_at(location, 0, + "unicode code point 0x%x is invalid surrogate pair", v); + v = 0xfffd; + } if (v <= 0xffff) { buf[0] = 0xe0 + (v >> 12); @@ -1705,6 +1728,27 @@ struct Unicode_range unsigned int stride; }; +// A table of whitespace characters--Unicode code points classified as +// "Space", "C" locale whitespace characters, the "next line" control +// character (0085), the line separator (2028), the paragraph +// separator (2029), and the "zero-width non-break space" (feff). + +static const Unicode_range unicode_space[] = +{ + { 0x0009, 0x000d, 1 }, + { 0x0020, 0x0020, 1 }, + { 0x0085, 0x0085, 1 }, + { 0x00a0, 0x00a0, 1 }, + { 0x1680, 0x1680, 1 }, + { 0x180e, 0x180e, 1 }, + { 0x2000, 0x200a, 1 }, + { 0x2028, 0x2029, 1 }, + { 0x202f, 0x202f, 1 }, + { 0x205f, 0x205f, 1 }, + { 0x3000, 0x3000, 1 }, + { 0xfeff, 0xfeff, 1 }, +}; + // A table of Unicode digits--Unicode code points classified as // "Digit". @@ -2294,6 +2338,15 @@ Lex::is_in_unicode_range(unsigned int c, const Unicode_range* ranges, } } +// Return whether C is a space character. + +bool +Lex::is_unicode_space(unsigned int c) +{ + return Lex::is_in_unicode_range(c, unicode_space, + ARRAY_SIZE(unicode_space)); +} + // Return whether C is a Unicode digit--a Unicode code point // classified as "Digit". diff --git a/gcc/go/gofrontend/lex.h b/gcc/go/gofrontend/lex.h index 8858e73d97a..074bbaea4ed 100644 --- a/gcc/go/gofrontend/lex.h +++ b/gcc/go/gofrontend/lex.h @@ -375,6 +375,10 @@ class Lex static int fetch_char(const char* str, unsigned int *value); + // Return whether C is a Unicode or "C" locale space character. + static bool + is_unicode_space(unsigned int c); + private: ssize_t get_line(); diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index 29323f05c6c..c65325d016e 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -1631,12 +1631,16 @@ Parse::init_vars(const Typed_identifier_list* til, Type* type, // Note that INIT was already parsed with the old name bindings, so // we don't have to worry that it will accidentally refer to the - // newly declared variables. + // newly declared variables. But we do have to worry about a mix of + // newly declared variables and old variables if the old variables + // appear in the initializations. Expression_list::const_iterator pexpr; if (init != NULL) pexpr = init->begin(); bool any_new = false; + Expression_list* vars = new Expression_list(); + Expression_list* vals = new Expression_list(); for (Typed_identifier_list::const_iterator p = til->begin(); p != til->end(); ++p) @@ -1644,7 +1648,7 @@ Parse::init_vars(const Typed_identifier_list* til, Type* type, if (init != NULL) go_assert(pexpr != init->end()); this->init_var(*p, type, init == NULL ? NULL : *pexpr, is_coloneq, - false, &any_new); + false, &any_new, vars, vals); if (init != NULL) ++pexpr; } @@ -1652,6 +1656,7 @@ Parse::init_vars(const Typed_identifier_list* til, Type* type, go_assert(pexpr == init->end()); if (is_coloneq && !any_new) error_at(location, "variables redeclared but no variable is new"); + this->finish_init_vars(vars, vals, location); } // See if we need to initialize a list of variables from a function @@ -1674,13 +1679,15 @@ Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type, Named_object* first_var = NULL; unsigned int index = 0; bool any_new = false; + Expression_list* ivars = new Expression_list(); + Expression_list* ivals = new Expression_list(); for (Typed_identifier_list::const_iterator pv = vars->begin(); pv != vars->end(); ++pv, ++index) { Expression* init = Expression::make_call_result(call, index); Named_object* no = this->init_var(*pv, type, init, is_coloneq, false, - &any_new); + &any_new, ivars, ivals); if (this->gogo_->in_global_scope() && no->is_variable()) { @@ -1700,6 +1707,8 @@ Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type, if (is_coloneq && !any_new) error_at(location, "variables redeclared but no variable is new"); + this->finish_init_vars(ivars, ivals, location); + return true; } @@ -1725,7 +1734,7 @@ Parse::init_vars_from_map(const Typed_identifier_list* vars, Type* type, Typed_identifier_list::const_iterator p = vars->begin(); Expression* init = type == NULL ? index : NULL; Named_object* val_no = this->init_var(*p, type, init, is_coloneq, - type == NULL, &any_new); + type == NULL, &any_new, NULL, NULL); if (type == NULL && any_new && val_no->is_variable()) val_no->var_value()->set_type_from_init_tuple(); Expression* val_var = Expression::make_var_reference(val_no, location); @@ -1735,7 +1744,7 @@ Parse::init_vars_from_map(const Typed_identifier_list* vars, Type* type, if (var_type == NULL) var_type = Type::lookup_bool_type(); Named_object* no = this->init_var(*p, var_type, NULL, is_coloneq, false, - &any_new); + &any_new, NULL, NULL); Expression* present_var = Expression::make_var_reference(no, location); if (is_coloneq && !any_new) @@ -1790,7 +1799,7 @@ Parse::init_vars_from_receive(const Typed_identifier_list* vars, Type* type, Typed_identifier_list::const_iterator p = vars->begin(); Expression* init = type == NULL ? receive : NULL; Named_object* val_no = this->init_var(*p, type, init, is_coloneq, - type == NULL, &any_new); + type == NULL, &any_new, NULL, NULL); if (type == NULL && any_new && val_no->is_variable()) val_no->var_value()->set_type_from_init_tuple(); Expression* val_var = Expression::make_var_reference(val_no, location); @@ -1800,7 +1809,7 @@ Parse::init_vars_from_receive(const Typed_identifier_list* vars, Type* type, if (var_type == NULL) var_type = Type::lookup_bool_type(); Named_object* no = this->init_var(*p, var_type, NULL, is_coloneq, false, - &any_new); + &any_new, NULL, NULL); Expression* received_var = Expression::make_var_reference(no, location); if (is_coloneq && !any_new) @@ -1857,7 +1866,7 @@ Parse::init_vars_from_type_guard(const Typed_identifier_list* vars, if (var_type == NULL) var_type = type_guard->type(); Named_object* val_no = this->init_var(*p, var_type, NULL, is_coloneq, false, - &any_new); + &any_new, NULL, NULL); Expression* val_var = Expression::make_var_reference(val_no, location); ++p; @@ -1865,7 +1874,7 @@ Parse::init_vars_from_type_guard(const Typed_identifier_list* vars, if (var_type == NULL) var_type = Type::lookup_bool_type(); Named_object* no = this->init_var(*p, var_type, NULL, is_coloneq, false, - &any_new); + &any_new, NULL, NULL); Expression* ok_var = Expression::make_var_reference(no, location); Expression* texpr = type_guard->expr(); @@ -1904,7 +1913,8 @@ Parse::init_vars_from_type_guard(const Typed_identifier_list* vars, Named_object* Parse::init_var(const Typed_identifier& tid, Type* type, Expression* init, - bool is_coloneq, bool type_from_init, bool* is_new) + bool is_coloneq, bool type_from_init, bool* is_new, + Expression_list* vars, Expression_list* vals) { Location location = tid.location(); @@ -1946,9 +1956,9 @@ Parse::init_var(const Typed_identifier& tid, Type* type, Expression* init, // like v, ok := x.(int). if (!type_from_init && init != NULL) { - Expression *v = Expression::make_var_reference(no, location); - Statement *s = Statement::make_assignment(v, init, location); - this->gogo_->add_statement(s); + go_assert(vars != NULL && vals != NULL); + vars->push_back(Expression::make_var_reference(no, location)); + vals->push_back(init); } return no; } @@ -1983,6 +1993,36 @@ Parse::create_dummy_global(Type* type, Expression* init, return this->gogo_->add_variable(buf, var); } +// Finish the variable initialization by executing any assignments to +// existing variables when using :=. These must be done as a tuple +// assignment in case of something like n, a, b := 1, b, a. + +void +Parse::finish_init_vars(Expression_list* vars, Expression_list* vals, + Location location) +{ + if (vars->empty()) + { + delete vars; + delete vals; + } + else if (vars->size() == 1) + { + go_assert(!this->gogo_->in_global_scope()); + this->gogo_->add_statement(Statement::make_assignment(vars->front(), + vals->front(), + location)); + delete vars; + delete vals; + } + else + { + go_assert(!this->gogo_->in_global_scope()); + this->gogo_->add_statement(Statement::make_tuple_assignment(vars, vals, + location)); + } +} + // SimpleVarDecl = identifier ":=" Expression . // We've already seen the identifier. @@ -2723,7 +2763,11 @@ Parse::composite_lit(Type* type, int depth, Location location) } else { - error_at(this->location(), "expected %<,%> or %<}%>"); + if (token->is_op(OPERATOR_SEMICOLON)) + error_at(this->location(), + "need trailing comma before newline in composite literal"); + else + error_at(this->location(), "expected %<,%> or %<}%>"); this->gogo_->mark_locals_used(); int depth = 0; @@ -2911,6 +2955,8 @@ Parse::primary_expr(bool may_be_sink, bool may_be_composite_lit, this->advance_token(); Expression* expr = this->expression(PRECEDENCE_NORMAL, false, true, NULL); + if (this->peek_token()->is_op(OPERATOR_COMMA)) + this->advance_token(); if (this->peek_token()->is_op(OPERATOR_ELLIPSIS)) { error_at(this->location(), @@ -3311,6 +3357,61 @@ Parse::unary_expr(bool may_be_sink, bool may_be_composite_lit, bool* is_type_switch) { const Token* token = this->peek_token(); + + // There is a complex parse for <- chan. The choices are + // Convert x to type <- chan int: + // (<- chan int)(x) + // Receive from (x converted to type chan <- chan int): + // (<- chan <- chan int (x)) + // Convert x to type <- chan (<- chan int). + // (<- chan <- chan int)(x) + if (token->is_op(OPERATOR_CHANOP)) + { + Location location = token->location(); + if (this->advance_token()->is_keyword(KEYWORD_CHAN)) + { + Expression* expr = this->primary_expr(false, may_be_composite_lit, + NULL); + if (expr->is_error_expression()) + return expr; + else if (!expr->is_type_expression()) + return Expression::make_receive(expr, location); + else + { + if (expr->type()->is_error_type()) + return expr; + + // We picked up "chan TYPE", but it is not a type + // conversion. + Channel_type* ct = expr->type()->channel_type(); + if (ct == NULL) + { + // This is probably impossible. + error_at(location, "expected channel type"); + return Expression::make_error(location); + } + else if (ct->may_receive()) + { + // <- chan TYPE. + Type* t = Type::make_channel_type(false, true, + ct->element_type()); + return Expression::make_type(t, location); + } + else + { + // <- chan <- TYPE. Because we skipped the leading + // <-, we parsed this as chan <- TYPE. With the + // leading <-, we parse it as <- chan (<- TYPE). + Type *t = this->reassociate_chan_direction(ct, location); + return Expression::make_type(t, location); + } + } + } + + this->unget_token(Token::make_operator_token(OPERATOR_CHANOP, location)); + token = this->peek_token(); + } + if (token->is_op(OPERATOR_PLUS) || token->is_op(OPERATOR_MINUS) || token->is_op(OPERATOR_NOT) @@ -3323,14 +3424,6 @@ Parse::unary_expr(bool may_be_sink, bool may_be_composite_lit, Operator op = token->op(); this->advance_token(); - if (op == OPERATOR_CHANOP - && this->peek_token()->is_keyword(KEYWORD_CHAN)) - { - // This is "<- chan" which must be the start of a type. - this->unget_token(Token::make_operator_token(op, location)); - return Expression::make_type(this->type(), location); - } - Expression* expr = this->unary_expr(false, may_be_composite_lit, NULL); if (expr->is_error_expression()) ; @@ -3350,6 +3443,32 @@ Parse::unary_expr(bool may_be_sink, bool may_be_composite_lit, is_type_switch); } +// This is called for the obscure case of +// (<- chan <- chan int)(x) +// In unary_expr we remove the leading <- and parse the remainder, +// which gives us +// chan <- (chan int) +// When we add the leading <- back in, we really want +// <- chan (<- chan int) +// This means that we need to reassociate. + +Type* +Parse::reassociate_chan_direction(Channel_type *ct, Location location) +{ + Channel_type* ele = ct->element_type()->channel_type(); + if (ele == NULL) + { + error_at(location, "parse error"); + return Type::make_error_type(); + } + Type* sub = ele; + if (ele->may_send()) + sub = Type::make_channel_type(false, true, ele->element_type()); + else + sub = this->reassociate_chan_direction(ele, location); + return Type::make_channel_type(false, true, sub); +} + // Statement = // Declaration | LabeledStmt | SimpleStmt | // GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt | @@ -5036,7 +5155,8 @@ Parse::range_clause_decl(const Typed_identifier_list* til, bool any_new = false; const Typed_identifier* pti = &til->front(); - Named_object* no = this->init_var(*pti, NULL, expr, true, true, &any_new); + Named_object* no = this->init_var(*pti, NULL, expr, true, true, &any_new, + NULL, NULL); if (any_new && no->is_variable()) no->var_value()->set_type_from_range_index(); p_range_clause->index = Expression::make_var_reference(no, location); @@ -5047,7 +5167,7 @@ Parse::range_clause_decl(const Typed_identifier_list* til, { pti = &til->back(); bool is_new = false; - no = this->init_var(*pti, NULL, expr, true, true, &is_new); + no = this->init_var(*pti, NULL, expr, true, true, &is_new, NULL, NULL); if (is_new && no->is_variable()) no->var_value()->set_type_from_range_value(); if (is_new) @@ -5337,7 +5457,8 @@ Parse::import_spec(void*) if (!token->is_string()) { - error_at(this->location(), "missing import package name"); + error_at(this->location(), "import statement not a string"); + this->advance_token(); return; } diff --git a/gcc/go/gofrontend/parse.h b/gcc/go/gofrontend/parse.h index 3139f7e8908..a355b7d2b3b 100644 --- a/gcc/go/gofrontend/parse.h +++ b/gcc/go/gofrontend/parse.h @@ -14,6 +14,7 @@ class Named_object; class Type; class Typed_identifier; class Typed_identifier_list; +class Channel_type; class Function_type; class Block; class Expression; @@ -205,8 +206,11 @@ class Parse Expression*, bool is_coloneq, Location); Named_object* init_var(const Typed_identifier&, Type*, Expression*, - bool is_coloneq, bool type_from_init, bool* is_new); + bool is_coloneq, bool type_from_init, bool* is_new, + Expression_list* vars, Expression_list* vals); Named_object* create_dummy_global(Type*, Expression*, Location); + void finish_init_vars(Expression_list* vars, Expression_list* vals, + Location); void simple_var_decl_or_assignment(const std::string&, Location, bool may_be_composite_lit, Range_clause*, Type_switch*); @@ -229,6 +233,7 @@ class Parse bool expression_may_start_here(); Expression* unary_expr(bool may_be_sink, bool may_be_composite_lit, bool* is_type_switch); + Type* reassociate_chan_direction(Channel_type*, Location); Expression* qualified_expr(Expression*, Location); Expression* id_to_expression(const std::string&, Location); void statement(Label*); diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index fa7f20836ad..58057f84a56 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -2006,6 +2006,8 @@ Thunk_statement::do_determine_types() void Thunk_statement::do_check_types(Gogo*) { + if (!this->call_->discarding_value()) + return; Call_expression* ce = this->call_->call_expression(); if (ce == NULL) { @@ -2471,11 +2473,15 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) Expression_statement* es = static_cast<Expression_statement*>(call_statement); Call_expression* ce = es->expr()->call_expression(); - go_assert(ce != NULL); - if (may_call_recover) - ce->set_is_deferred(); - if (recover_arg != NULL) - ce->set_recover_arg(recover_arg); + if (ce == NULL) + go_assert(saw_errors()); + else + { + if (may_call_recover) + ce->set_is_deferred(); + if (recover_arg != NULL) + ce->set_recover_arg(recover_arg); + } } // That is all the thunk has to do. @@ -3313,16 +3319,10 @@ Case_clauses::Case_clause::lower(Block* b, Temporary_statement* val_temp, p != this->cases_->end(); ++p) { - Expression* this_cond; - if (val_temp == NULL) - this_cond = *p; - else - { - Expression* ref = Expression::make_temporary_reference(val_temp, - loc); - this_cond = Expression::make_binary(OPERATOR_EQEQ, ref, *p, loc); - } - + Expression* ref = Expression::make_temporary_reference(val_temp, + loc); + Expression* this_cond = Expression::make_binary(OPERATOR_EQEQ, ref, + *p, loc); if (cond == NULL) cond = this_cond; else @@ -3846,6 +3846,16 @@ Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing, return new Constant_switch_statement(this->val_, this->clauses_, this->break_label_, loc); + if (this->val_ != NULL + && !this->val_->type()->is_comparable() + && !Type::are_compatible_for_comparison(true, this->val_->type(), + Type::make_nil_type(), NULL)) + { + error_at(this->val_->location(), + "cannot switch on value whose type that may not be compared"); + return Statement::make_error_statement(loc); + } + Block* b = new Block(enclosing, loc); if (this->clauses_->empty()) @@ -3856,15 +3866,12 @@ Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing, return Statement::make_statement(val, true); } - Temporary_statement* val_temp; - if (this->val_ == NULL) - val_temp = NULL; - else - { - // var val_temp VAL_TYPE = VAL - val_temp = Statement::make_temporary(NULL, this->val_, loc); - b->add_statement(val_temp); - } + // var val_temp VAL_TYPE = VAL + Expression* val = this->val_; + if (val == NULL) + val = Expression::make_boolean(true, loc); + Temporary_statement* val_temp = Statement::make_temporary(NULL, val, loc); + b->add_statement(val_temp); this->clauses_->lower(b, val_temp, this->break_label()); diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 228f3faa4a4..795a1b51026 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -54,8 +54,7 @@ get_backend_interface_fields(Gogo* gogo, Interface_type* type, // Class Type. Type::Type(Type_classification classification) - : classification_(classification), btype_is_placeholder_(false), - btype_(NULL), type_descriptor_var_(NULL) + : classification_(classification), btype_(NULL), type_descriptor_var_(NULL) { } @@ -430,7 +429,7 @@ Type::are_identical(const Type* t1, const Type* t2, bool errors_are_identical, case TYPE_CALL_MULTIPLE_RESULT: if (reason != NULL) - *reason = "invalid use of multiple value function call"; + *reason = "invalid use of multiple-value function call"; return false; default: @@ -588,6 +587,9 @@ Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1, p != fields->end(); ++p) { + if (Gogo::is_sink_name(p->field_name())) + continue; + if (!p->type()->is_comparable()) { if (reason != NULL) @@ -633,8 +635,8 @@ Type::are_assignable_check_hidden(const Type* lhs, const Type* rhs, if (rhs->is_call_multiple_result_type()) { if (reason != NULL) - reason->assign(_("multiple value function call in " - "single value context")); + reason->assign(_("multiple-value function call in " + "single-value context")); return false; } } @@ -916,11 +918,7 @@ Btype* Type::get_backend(Gogo* gogo) { if (this->btype_ != NULL) - { - if (this->btype_is_placeholder_ && gogo->named_types_are_converted()) - this->finish_backend(gogo); - return this->btype_; - } + return this->btype_; if (this->forward_declaration_type() != NULL || this->named_type() != NULL) @@ -934,20 +932,36 @@ Type::get_backend(Gogo* gogo) // that. There is no need to use the hash table for named types, as // named types are only identical to themselves. - std::pair<Type*, Btype*> val(this, NULL); + std::pair<Type*, Type_btype_entry> val; + val.first = this; + val.second.btype = NULL; + val.second.is_placeholder = false; std::pair<Type_btypes::iterator, bool> ins = Type::type_btypes.insert(val); - if (!ins.second && ins.first->second != NULL) + if (!ins.second && ins.first->second.btype != NULL) { - if (gogo != NULL && gogo->named_types_are_converted()) - this->btype_ = ins.first->second; - return ins.first->second; + // Note that GOGO can be NULL here, but only when the GCC + // middle-end is asking for a frontend type. That will only + // happen for simple types, which should never require + // placeholders. + if (!ins.first->second.is_placeholder) + this->btype_ = ins.first->second.btype; + else if (gogo->named_types_are_converted()) + { + this->finish_backend(gogo, ins.first->second.btype); + ins.first->second.is_placeholder = false; + } + + return ins.first->second.btype; } Btype* bt = this->get_btype_without_hash(gogo); - if (ins.first->second == NULL) - ins.first->second = bt; + if (ins.first->second.btype == NULL) + { + ins.first->second.btype = bt; + ins.first->second.is_placeholder = false; + } else { // We have already created a backend representation for this @@ -955,10 +969,9 @@ Type::get_backend(Gogo* gogo) // a named type which in turns uses an identical unnamed type. // Use the tree we created earlier and ignore the one we just // built. - bt = ins.first->second; - if (gogo == NULL || !gogo->named_types_are_converted()) - return bt; - this->btype_ = bt; + if (this->btype_ == bt) + this->btype_ = ins.first->second.btype; + bt = ins.first->second.btype; } return bt; @@ -1025,6 +1038,37 @@ Type::get_backend_placeholder(Gogo* gogo) // These are simple types that can just be created directly. return this->get_backend(gogo); + case TYPE_MAP: + case TYPE_CHANNEL: + // All maps and channels have the same backend representation. + return this->get_backend(gogo); + + case TYPE_NAMED: + case TYPE_FORWARD: + // Named types keep track of their own dependencies and manage + // their own placeholders. + return this->get_backend(gogo); + + case TYPE_INTERFACE: + if (this->interface_type()->is_empty()) + return Interface_type::get_backend_empty_interface_type(gogo); + break; + + default: + break; + } + + std::pair<Type*, Type_btype_entry> val; + val.first = this; + val.second.btype = NULL; + val.second.is_placeholder = false; + std::pair<Type_btypes::iterator, bool> ins = + Type::type_btypes.insert(val); + if (!ins.second && ins.first->second.btype != NULL) + return ins.first->second.btype; + + switch (this->classification_) + { case TYPE_FUNCTION: { Location loc = this->function_type()->location(); @@ -1067,37 +1111,36 @@ Type::get_backend_placeholder(Gogo* gogo) } break; - case TYPE_MAP: - case TYPE_CHANNEL: - // All maps and channels have the same backend representation. - return this->get_backend(gogo); - case TYPE_INTERFACE: - if (this->interface_type()->is_empty()) - return Interface_type::get_backend_empty_interface_type(gogo); - else - { - std::vector<Backend::Btyped_identifier> bfields; - get_backend_interface_fields(gogo, this->interface_type(), true, - &bfields); - bt = gogo->backend()->struct_type(bfields); - } + { + go_assert(!this->interface_type()->is_empty()); + std::vector<Backend::Btyped_identifier> bfields; + get_backend_interface_fields(gogo, this->interface_type(), true, + &bfields); + bt = gogo->backend()->struct_type(bfields); + } break; - case TYPE_NAMED: - case TYPE_FORWARD: - // Named types keep track of their own dependencies and manage - // their own placeholders. - return this->get_backend(gogo); - case TYPE_SINK: case TYPE_CALL_MULTIPLE_RESULT: + /* Note that various classifications were handled in the earlier + switch. */ default: go_unreachable(); } - this->btype_ = bt; - this->btype_is_placeholder_ = true; + if (ins.first->second.btype == NULL) + { + ins.first->second.btype = bt; + ins.first->second.is_placeholder = true; + } + else + { + // A placeholder for this type got created along the way. Use + // that one and ignore the one we just built. + bt = ins.first->second.btype; + } + return bt; } @@ -1105,12 +1148,8 @@ Type::get_backend_placeholder(Gogo* gogo) // using a placeholder type. void -Type::finish_backend(Gogo* gogo) +Type::finish_backend(Gogo* gogo, Btype *placeholder) { - go_assert(this->btype_ != NULL); - if (!this->btype_is_placeholder_) - return; - switch (this->classification_) { case TYPE_ERROR: @@ -1126,7 +1165,7 @@ Type::finish_backend(Gogo* gogo) case TYPE_FUNCTION: { Btype* bt = this->do_get_backend(gogo); - if (!gogo->backend()->set_placeholder_function_type(this->btype_, bt)) + if (!gogo->backend()->set_placeholder_function_type(placeholder, bt)) go_assert(saw_errors()); } break; @@ -1134,7 +1173,7 @@ Type::finish_backend(Gogo* gogo) case TYPE_POINTER: { Btype* bt = this->do_get_backend(gogo); - if (!gogo->backend()->set_placeholder_pointer_type(this->btype_, bt)) + if (!gogo->backend()->set_placeholder_pointer_type(placeholder, bt)) go_assert(saw_errors()); } break; @@ -1171,7 +1210,7 @@ Type::finish_backend(Gogo* gogo) go_unreachable(); } - this->btype_is_placeholder_ = false; + this->btype_ = placeholder; } // Return a pointer to the type descriptor for this type. @@ -1295,7 +1334,8 @@ Type::type_descriptor_var_name(Gogo* gogo, Named_type* nt) return "__go_td_" + this->mangled_name(gogo); Named_object* no = nt->named_object(); - const Named_object* in_function = nt->in_function(); + unsigned int index; + const Named_object* in_function = nt->in_function(&index); std::string ret = "__go_tdn_"; if (nt->is_builtin()) go_assert(in_function == NULL); @@ -1310,6 +1350,13 @@ Type::type_descriptor_var_name(Gogo* gogo, Named_type* nt) { ret.append(Gogo::unpack_hidden_name(in_function->name())); ret.append(1, '.'); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + ret.append(buf); + ret.append(1, '.'); + } } } @@ -1746,9 +1793,19 @@ Type::specific_type_functions(Gogo* gogo, Named_type* name, { // This name is already hidden or not as appropriate. base_name = name->name(); - const Named_object* in_function = name->in_function(); + unsigned int index; + const Named_object* in_function = name->in_function(&index); if (in_function != NULL) - base_name += '$' + Gogo::unpack_hidden_name(in_function->name()); + { + base_name += '$' + Gogo::unpack_hidden_name(in_function->name()); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + base_name += '$'; + base_name += buf; + } + } } std::string hash_name = base_name + "$hash"; std::string equal_name = base_name + "$equal"; @@ -1989,10 +2046,19 @@ Type::uncommon_type_constructor(Gogo* gogo, Type* uncommon_type, ? gogo->pkgpath() : package->pkgpath()); n.assign(pkgpath); - if (name->in_function() != NULL) + unsigned int index; + const Named_object* in_function = name->in_function(&index); + if (in_function != NULL) { n.append(1, '.'); - n.append(Gogo::unpack_hidden_name(name->in_function()->name())); + n.append(Gogo::unpack_hidden_name(in_function->name())); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + n.append(1, '.'); + n.append(buf); + } } s = Expression::make_string(n, bloc); vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); @@ -2361,7 +2427,7 @@ class Error_type : public Type protected: bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } Btype* @@ -2399,7 +2465,7 @@ class Void_type : public Type protected: bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } Btype* @@ -2437,7 +2503,7 @@ class Boolean_type : public Type protected: bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return true; } Btype* @@ -2936,8 +3002,8 @@ String_type::do_get_backend(Gogo* gogo) // backend representation, so force it to be finished now. if (!gogo->named_types_are_converted()) { - pb->get_backend_placeholder(gogo); - pb->finish_backend(gogo); + Btype* bt = pb->get_backend_placeholder(gogo); + pb->finish_backend(gogo, bt); } fields[0].name = "__data"; @@ -3060,7 +3126,7 @@ class Sink_type : public Type protected: bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } Btype* @@ -3938,7 +4004,7 @@ class Nil_type : public Type protected: bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } Btype* @@ -3989,7 +4055,7 @@ class Call_multiple_result_type : public Type } bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } Btype* @@ -4266,7 +4332,7 @@ Struct_type::struct_has_hidden_fields(const Named_type* within, // comparisons. bool -Struct_type::do_compare_is_identity(Gogo* gogo) const +Struct_type::do_compare_is_identity(Gogo* gogo) { const Struct_field_list* fields = this->fields_; if (fields == NULL) @@ -4276,6 +4342,9 @@ Struct_type::do_compare_is_identity(Gogo* gogo) const pf != fields->end(); ++pf) { + if (Gogo::is_sink_name(pf->field_name())) + return false; + if (!pf->type()->compare_is_identity(gogo)) return false; @@ -4295,6 +4364,16 @@ Struct_type::do_compare_is_identity(Gogo* gogo) const return false; offset += field_size; } + + unsigned int struct_size; + if (!this->backend_type_size(gogo, &struct_size)) + return false; + if (offset != struct_size) + { + // Trailing padding may not be zero when on the stack. + return false; + } + return true; } @@ -4530,6 +4609,20 @@ Struct_type::method_function(const std::string& name, bool* is_ambiguous) const return Type::method_function(this->all_methods_, name, is_ambiguous); } +// Return a pointer to the interface method table for this type for +// the interface INTERFACE. IS_POINTER is true if this is for a +// pointer to THIS. + +tree +Struct_type::interface_method_table(Gogo* gogo, + const Interface_type* interface, + bool is_pointer) +{ + return Type::interface_method_table(gogo, this, interface, is_pointer, + &this->interface_method_tables_, + &this->pointer_interface_method_tables_); +} + // Convert struct fields to the backend representation. This is not // declared in types.h so that types.h doesn't have to #include // backend.h. @@ -4749,6 +4842,9 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*, pf != fields->end(); ++pf) { + if (Gogo::is_sink_name(pf->field_name())) + continue; + if (first) first = false; else @@ -4840,6 +4936,9 @@ Struct_type::write_equal_function(Gogo* gogo, Named_type* name) pf != fields->end(); ++pf, ++field_index) { + if (Gogo::is_sink_name(pf->field_name())) + continue; + // Compare one field in both P1 and P2. Expression* f1 = Expression::make_temporary_reference(p1, bloc); f1 = Expression::make_unary(OPERATOR_MULT, f1, bloc); @@ -4875,14 +4974,15 @@ Struct_type::write_equal_function(Gogo* gogo, Named_type* name) void Struct_type::do_reflection(Gogo* gogo, std::string* ret) const { - ret->append("struct { "); + ret->append("struct {"); for (Struct_field_list::const_iterator p = this->fields_->begin(); p != this->fields_->end(); ++p) { if (p != this->fields_->begin()) - ret->append("; "); + ret->push_back(';'); + ret->push_back(' '); if (p->is_anonymous()) ret->push_back('?'); else @@ -4915,7 +5015,10 @@ Struct_type::do_reflection(Gogo* gogo, std::string* ret) const } } - ret->append(" }"); + if (!this->fields_->empty()) + ret->push_back(' '); + + ret->push_back('}'); } // Mangled name. @@ -5215,7 +5318,7 @@ Array_type::do_verify() // Whether we can use memcmp to compare this array. bool -Array_type::do_compare_is_identity(Gogo* gogo) const +Array_type::do_compare_is_identity(Gogo* gogo) { if (this->length_ == NULL) return false; @@ -6815,7 +6918,8 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const std::string n = Gogo::message_name(p->name()); size_t len = 100 + n.length(); char* buf = new char[len]; - snprintf(buf, len, _("method %s%s%s requires a pointer"), + snprintf(buf, len, + _("method %s%s%s requires a pointer receiver"), open_quote, n.c_str(), close_quote); reason->assign(buf); delete[] buf; @@ -7151,7 +7255,17 @@ Interface_type::do_mangled_name(Gogo* gogo, std::string* ret) const { if (!p->name().empty()) { - std::string n = Gogo::unpack_hidden_name(p->name()); + std::string n; + if (!Gogo::is_hidden_name(p->name())) + n = p->name(); + else + { + n = "."; + std::string pkgpath = Gogo::hidden_name_pkgpath(p->name()); + n.append(Gogo::pkgpath_for_symbol(pkgpath)); + n.append(1, '.'); + n.append(Gogo::unpack_hidden_name(p->name())); + } char buf[20]; snprintf(buf, sizeof buf, "%u_", static_cast<unsigned int>(n.length())); @@ -7704,32 +7818,9 @@ tree Named_type::interface_method_table(Gogo* gogo, const Interface_type* interface, bool is_pointer) { - go_assert(!interface->is_empty()); - - Interface_method_tables** pimt = (is_pointer - ? &this->interface_method_tables_ - : &this->pointer_interface_method_tables_); - - if (*pimt == NULL) - *pimt = new Interface_method_tables(5); - - std::pair<const Interface_type*, tree> val(interface, NULL_TREE); - std::pair<Interface_method_tables::iterator, bool> ins = (*pimt)->insert(val); - - if (ins.second) - { - // This is a new entry in the hash table. - go_assert(ins.first->second == NULL_TREE); - ins.first->second = gogo->interface_method_table_for_type(interface, - this, - is_pointer); - } - - tree decl = ins.first->second; - if (decl == error_mark_node) - return error_mark_node; - go_assert(decl != NULL_TREE && TREE_CODE(decl) == VAR_DECL); - return build_fold_addr_expr(decl); + return Type::interface_method_table(gogo, this, interface, is_pointer, + &this->interface_method_tables_, + &this->pointer_interface_method_tables_); } // Return whether a named type has any hidden fields. @@ -7922,7 +8013,7 @@ Named_type::do_has_pointer() const // function. bool -Named_type::do_compare_is_identity(Gogo* gogo) const +Named_type::do_compare_is_identity(Gogo* gogo) { // We don't use this->seen_ here because compare_is_identity may // call base() later, and that will mess up if seen_ is set here. @@ -8358,8 +8449,17 @@ Named_type::do_reflection(Gogo* gogo, std::string* ret) const } if (this->in_function_ != NULL) { + ret->push_back('\t'); ret->append(Gogo::unpack_hidden_name(this->in_function_->name())); ret->push_back('$'); + if (this->in_function_index_ > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", this->in_function_index_); + ret->append(buf); + ret->push_back('$'); + } + ret->push_back('\t'); } ret->append(Gogo::unpack_hidden_name(this->named_object_->name())); } @@ -8389,6 +8489,13 @@ Named_type::do_mangled_name(Gogo* gogo, std::string* ret) const { name.append(Gogo::unpack_hidden_name(this->in_function_->name())); name.append(1, '$'); + if (this->in_function_index_ > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", this->in_function_index_); + name.append(buf); + name.append(1, '$'); + } } } name.append(Gogo::unpack_hidden_name(no->name())); @@ -8899,6 +9006,42 @@ Type::method_function(const Methods* methods, const std::string& name, return m; } +// Return a pointer to the interface method table for TYPE for the +// interface INTERFACE. + +tree +Type::interface_method_table(Gogo* gogo, Type* type, + const Interface_type *interface, + bool is_pointer, + Interface_method_tables** method_tables, + Interface_method_tables** pointer_tables) +{ + go_assert(!interface->is_empty()); + + Interface_method_tables** pimt = is_pointer ? method_tables : pointer_tables; + + if (*pimt == NULL) + *pimt = new Interface_method_tables(5); + + std::pair<const Interface_type*, tree> val(interface, NULL_TREE); + std::pair<Interface_method_tables::iterator, bool> ins = (*pimt)->insert(val); + + if (ins.second) + { + // This is a new entry in the hash table. + go_assert(ins.first->second == NULL_TREE); + ins.first->second = gogo->interface_method_table_for_type(interface, + type, + is_pointer); + } + + tree decl = ins.first->second; + if (decl == error_mark_node) + return error_mark_node; + go_assert(decl != NULL_TREE && TREE_CODE(decl) == VAR_DECL); + return build_fold_addr_expr(decl); +} + // Look for field or method NAME for TYPE. Return an Expression for // the field or method bound to EXPR. If there is no such field or // method, give an appropriate error and return an error expression. @@ -8990,7 +9133,7 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, Gogo::message_name(name).c_str(), ambig1.c_str(), ambig2.c_str()); else if (found_pointer_method) - error_at(location, "method requires a pointer"); + error_at(location, "method requires a pointer receiver"); else if (nt == NULL && st == NULL && it == NULL) error_at(location, ("reference to field %qs in object which " diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index a542bf71738..bdda7a4280e 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -576,7 +576,7 @@ class Type // identity function which gets nothing but a pointer to the value // and a size. bool - compare_is_identity(Gogo* gogo) const + compare_is_identity(Gogo* gogo) { return this->do_compare_is_identity(gogo); } // Return a hash code for this type for the method hash table. @@ -869,7 +869,7 @@ class Type // Finish the backend representation of a placeholder. void - finish_backend(Gogo*); + finish_backend(Gogo*, Btype*); // Build a type descriptor entry for this type. Return a pointer to // it. The location is the location which causes us to need the @@ -950,7 +950,7 @@ class Type { return false; } virtual bool - do_compare_is_identity(Gogo*) const = 0; + do_compare_is_identity(Gogo*) = 0; virtual unsigned int do_hash_for_method(Gogo*) const; @@ -983,6 +983,19 @@ class Type method_function(const Methods*, const std::string& name, bool* is_ambiguous); + // A mapping from interfaces to the associated interface method + // tables for this type. This maps to a decl. + typedef Unordered_map_hash(const Interface_type*, tree, Type_hash_identical, + Type_identical) Interface_method_tables; + + // Return a pointer to the interface method table for TYPE for the + // interface INTERFACE. + static tree + interface_method_table(Gogo* gogo, Type* type, + const Interface_type *interface, bool is_pointer, + Interface_method_tables** method_tables, + Interface_method_tables** pointer_tables); + // Return a composite literal for the type descriptor entry for a // type. static Expression* @@ -1178,10 +1191,18 @@ class Type Btype* get_btype_without_hash(Gogo*); + // A backend type that may be a placeholder. + struct Type_btype_entry + { + Btype *btype; + bool is_placeholder; + }; + // A mapping from Type to Btype*, used to ensure that the backend - // representation of identical types is identical. - typedef Unordered_map_hash(const Type*, Btype*, Type_hash_identical, - Type_identical) Type_btypes; + // representation of identical types is identical. This is only + // used for unnamed types. + typedef Unordered_map_hash(const Type*, Type_btype_entry, + Type_hash_identical, Type_identical) Type_btypes; static Type_btypes type_btypes; @@ -1198,9 +1219,6 @@ class Type // The type classification. Type_classification classification_; - // Whether btype_ is a placeholder type used while named types are - // being converted. - bool btype_is_placeholder_; // The backend representation of the type, once it has been // determined. Btype* btype_; @@ -1445,7 +1463,7 @@ class Integer_type : public Type protected: bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return true; } unsigned int @@ -1522,7 +1540,7 @@ class Float_type : public Type protected: bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } unsigned int @@ -1591,7 +1609,7 @@ class Complex_type : public Type protected: bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } unsigned int @@ -1651,7 +1669,7 @@ class String_type : public Type { return true; } bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } Btype* @@ -1765,7 +1783,7 @@ class Function_type : public Type { return true; } bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } unsigned int @@ -1840,7 +1858,7 @@ class Pointer_type : public Type { return true; } bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return true; } unsigned int @@ -1994,7 +2012,8 @@ class Struct_type : public Type public: Struct_type(Struct_field_list* fields, Location location) : Type(TYPE_STRUCT), - fields_(fields), location_(location), all_methods_(NULL) + fields_(fields), location_(location), all_methods_(NULL), + interface_method_tables_(NULL), pointer_interface_method_tables_(NULL) { } // Return the field NAME. This only looks at local fields, not at @@ -2076,6 +2095,14 @@ class Struct_type : public Type Method* method_function(const std::string& name, bool* is_ambiguous) const; + // Return a pointer to the interface method table for this type for + // the interface INTERFACE. If IS_POINTER is true, set the type + // descriptor to a pointer to this type, otherwise set it to this + // type. + tree + interface_method_table(Gogo*, const Interface_type* interface, + bool is_pointer); + // Traverse just the field types of a struct type. int traverse_field_types(Traverse* traverse) @@ -2117,7 +2144,7 @@ class Struct_type : public Type do_has_pointer() const; bool - do_compare_is_identity(Gogo*) const; + do_compare_is_identity(Gogo*); unsigned int do_hash_for_method(Gogo*) const; @@ -2156,6 +2183,13 @@ class Struct_type : public Type Location location_; // If this struct is unnamed, a list of methods. Methods* all_methods_; + // A mapping from interfaces to the associated interface method + // tables for this type. Only used if this struct is unnamed. + Interface_method_tables* interface_method_tables_; + // A mapping from interfaces to the associated interface method + // tables for pointers to this type. Only used if this struct is + // unnamed. + Interface_method_tables* pointer_interface_method_tables_; }; // The type of an array. @@ -2243,7 +2277,7 @@ class Array_type : public Type } bool - do_compare_is_identity(Gogo*) const; + do_compare_is_identity(Gogo*); unsigned int do_hash_for_method(Gogo*) const; @@ -2336,7 +2370,7 @@ class Map_type : public Type { return true; } bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } unsigned int @@ -2422,7 +2456,7 @@ class Channel_type : public Type { return true; } bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return true; } unsigned int @@ -2553,7 +2587,7 @@ class Interface_type : public Type { return true; } bool - do_compare_is_identity(Gogo*) const + do_compare_is_identity(Gogo*) { return false; } unsigned int @@ -2623,8 +2657,8 @@ class Named_type : public Type public: Named_type(Named_object* named_object, Type* type, Location location) : Type(TYPE_NAMED), - named_object_(named_object), in_function_(NULL), type_(type), - local_methods_(NULL), all_methods_(NULL), + named_object_(named_object), in_function_(NULL), in_function_index_(0), + type_(type), local_methods_(NULL), all_methods_(NULL), interface_method_tables_(NULL), pointer_interface_method_tables_(NULL), location_(location), named_btype_(NULL), dependencies_(), is_visible_(true), is_error_(false), is_placeholder_(false), @@ -2651,13 +2685,19 @@ class Named_type : public Type // Return the function in which this type is defined. This will // return NULL for a type defined in global scope. const Named_object* - in_function() const - { return this->in_function_; } + in_function(unsigned int *pindex) const + { + *pindex = this->in_function_index_; + return this->in_function_; + } // Set the function in which this type is defined. void - set_in_function(Named_object* f) - { this->in_function_ = f; } + set_in_function(Named_object* f, unsigned int index) + { + this->in_function_ = f; + this->in_function_index_ = index; + } // Return the name of the type. const std::string& @@ -2830,7 +2870,7 @@ class Named_type : public Type do_has_pointer() const; bool - do_compare_is_identity(Gogo*) const; + do_compare_is_identity(Gogo*); unsigned int do_hash_for_method(Gogo*) const; @@ -2855,16 +2895,13 @@ class Named_type : public Type void create_placeholder(Gogo*); - // A mapping from interfaces to the associated interface method - // tables for this type. This maps to a decl. - typedef Unordered_map_hash(const Interface_type*, tree, Type_hash_identical, - Type_identical) Interface_method_tables; - // A pointer back to the Named_object for this type. Named_object* named_object_; // If this type is defined in a function, a pointer back to the // function in which it is defined. Named_object* in_function_; + // The index of this type in IN_FUNCTION_. + unsigned int in_function_index_; // The actual type. Type* type_; // The list of methods defined for this type. Any named type can @@ -2917,7 +2954,7 @@ class Named_type : public Type // function exits. mutable bool seen_; // Like seen_, but used only by do_compare_is_identity. - mutable bool seen_in_compare_is_identity_; + bool seen_in_compare_is_identity_; // Like seen_, but used only by do_get_backend. bool seen_in_get_backend_; }; @@ -2972,7 +3009,7 @@ class Forward_declaration_type : public Type { return this->real_type()->has_pointer(); } bool - do_compare_is_identity(Gogo* gogo) const + do_compare_is_identity(Gogo* gogo) { return this->real_type()->compare_is_identity(gogo); } unsigned int diff --git a/gcc/go/lang.opt b/gcc/go/lang.opt index eb9ed9a63a0..22197a71e3d 100644 --- a/gcc/go/lang.opt +++ b/gcc/go/lang.opt @@ -61,6 +61,10 @@ fgo-prefix= Go Joined RejectNegative -fgo-prefix=<string> Set package-specific prefix for exported Go names +fgo-relative-import-path= +Go Joined RejectNegative +-fgo-relative-import-path=<path> Treat a relative import as relative to path + frequire-return-statement Go Var(go_require_return_statement) Init(1) Warning Functions which return values must end with return statements |