From 1bc5aee63eb72b341f506ad058502cd0361f0d10 Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Tue, 25 Mar 2014 22:37:19 -0700 Subject: Initial checkin of GCC 4.9.0 from trunk (r208799). Change-Id: I48a3c08bb98542aa215912a75f03c0890e497dba --- gcc-4.9/gcc/go/ChangeLog | 782 ++ gcc-4.9/gcc/go/Make-lang.in | 235 + gcc-4.9/gcc/go/README.gcc | 3 + gcc-4.9/gcc/go/config-lang.in | 40 + gcc-4.9/gcc/go/gccgo.texi | 402 + gcc-4.9/gcc/go/go-backend.c | 192 + gcc-4.9/gcc/go/go-c.h | 66 + gcc-4.9/gcc/go/go-gcc.cc | 2153 ++++ gcc-4.9/gcc/go/go-lang.c | 498 + gcc-4.9/gcc/go/go-linemap.cc | 126 + gcc-4.9/gcc/go/go-location.h | 45 + gcc-4.9/gcc/go/go-system.h | 142 + gcc-4.9/gcc/go/gofrontend/LICENSE | 27 + gcc-4.9/gcc/go/gofrontend/PATENTS | 22 + gcc-4.9/gcc/go/gofrontend/README | 53 + gcc-4.9/gcc/go/gofrontend/ast-dump.cc | 469 + gcc-4.9/gcc/go/gofrontend/ast-dump.h | 122 + gcc-4.9/gcc/go/gofrontend/backend.h | 594 + gcc-4.9/gcc/go/gofrontend/dataflow.cc | 278 + gcc-4.9/gcc/go/gofrontend/dataflow.h | 91 + gcc-4.9/gcc/go/gofrontend/export.cc | 491 + gcc-4.9/gcc/go/gofrontend/export.h | 201 + gcc-4.9/gcc/go/gofrontend/expressions.cc | 15900 ++++++++++++++++++++++++++ gcc-4.9/gcc/go/gofrontend/expressions.h | 2488 ++++ gcc-4.9/gcc/go/gofrontend/go-dump.cc | 53 + gcc-4.9/gcc/go/gofrontend/go-dump.h | 38 + gcc-4.9/gcc/go/gofrontend/go-linemap.h | 131 + gcc-4.9/gcc/go/gofrontend/go-optimize.cc | 53 + gcc-4.9/gcc/go/gofrontend/go-optimize.h | 38 + gcc-4.9/gcc/go/gofrontend/go.cc | 154 + gcc-4.9/gcc/go/gofrontend/gogo-tree.cc | 2319 ++++ gcc-4.9/gcc/go/gofrontend/gogo.cc | 6198 ++++++++++ gcc-4.9/gcc/go/gofrontend/gogo.h | 3030 +++++ gcc-4.9/gcc/go/gofrontend/import-archive.cc | 660 ++ gcc-4.9/gcc/go/gofrontend/import.cc | 960 ++ gcc-4.9/gcc/go/gofrontend/import.h | 364 + gcc-4.9/gcc/go/gofrontend/lex.cc | 2440 ++++ gcc-4.9/gcc/go/gofrontend/lex.h | 502 + gcc-4.9/gcc/go/gofrontend/operator.h | 66 + gcc-4.9/gcc/go/gofrontend/parse.cc | 5746 ++++++++++ gcc-4.9/gcc/go/gofrontend/parse.h | 335 + gcc-4.9/gcc/go/gofrontend/runtime.cc | 409 + gcc-4.9/gcc/go/gofrontend/runtime.def | 374 + gcc-4.9/gcc/go/gofrontend/runtime.h | 51 + gcc-4.9/gcc/go/gofrontend/statements.cc | 6038 ++++++++++ gcc-4.9/gcc/go/gofrontend/statements.h | 1661 +++ gcc-4.9/gcc/go/gofrontend/string-dump.h | 25 + gcc-4.9/gcc/go/gofrontend/types.cc | 10132 ++++++++++++++++ gcc-4.9/gcc/go/gofrontend/types.h | 3189 ++++++ gcc-4.9/gcc/go/gofrontend/unsafe.cc | 96 + gcc-4.9/gcc/go/gospec.c | 410 + gcc-4.9/gcc/go/lang-specs.h | 25 + gcc-4.9/gcc/go/lang.opt | 76 + 53 files changed, 70993 insertions(+) create mode 100644 gcc-4.9/gcc/go/ChangeLog create mode 100644 gcc-4.9/gcc/go/Make-lang.in create mode 100644 gcc-4.9/gcc/go/README.gcc create mode 100644 gcc-4.9/gcc/go/config-lang.in create mode 100644 gcc-4.9/gcc/go/gccgo.texi create mode 100644 gcc-4.9/gcc/go/go-backend.c create mode 100644 gcc-4.9/gcc/go/go-c.h create mode 100644 gcc-4.9/gcc/go/go-gcc.cc create mode 100644 gcc-4.9/gcc/go/go-lang.c create mode 100644 gcc-4.9/gcc/go/go-linemap.cc create mode 100644 gcc-4.9/gcc/go/go-location.h create mode 100644 gcc-4.9/gcc/go/go-system.h create mode 100644 gcc-4.9/gcc/go/gofrontend/LICENSE create mode 100644 gcc-4.9/gcc/go/gofrontend/PATENTS create mode 100644 gcc-4.9/gcc/go/gofrontend/README create mode 100644 gcc-4.9/gcc/go/gofrontend/ast-dump.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/ast-dump.h create mode 100644 gcc-4.9/gcc/go/gofrontend/backend.h create mode 100644 gcc-4.9/gcc/go/gofrontend/dataflow.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/dataflow.h create mode 100644 gcc-4.9/gcc/go/gofrontend/export.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/export.h create mode 100644 gcc-4.9/gcc/go/gofrontend/expressions.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/expressions.h create mode 100644 gcc-4.9/gcc/go/gofrontend/go-dump.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/go-dump.h create mode 100644 gcc-4.9/gcc/go/gofrontend/go-linemap.h create mode 100644 gcc-4.9/gcc/go/gofrontend/go-optimize.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/go-optimize.h create mode 100644 gcc-4.9/gcc/go/gofrontend/go.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/gogo-tree.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/gogo.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/gogo.h create mode 100644 gcc-4.9/gcc/go/gofrontend/import-archive.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/import.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/import.h create mode 100644 gcc-4.9/gcc/go/gofrontend/lex.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/lex.h create mode 100644 gcc-4.9/gcc/go/gofrontend/operator.h create mode 100644 gcc-4.9/gcc/go/gofrontend/parse.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/parse.h create mode 100644 gcc-4.9/gcc/go/gofrontend/runtime.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/runtime.def create mode 100644 gcc-4.9/gcc/go/gofrontend/runtime.h create mode 100644 gcc-4.9/gcc/go/gofrontend/statements.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/statements.h create mode 100644 gcc-4.9/gcc/go/gofrontend/string-dump.h create mode 100644 gcc-4.9/gcc/go/gofrontend/types.cc create mode 100644 gcc-4.9/gcc/go/gofrontend/types.h create mode 100644 gcc-4.9/gcc/go/gofrontend/unsafe.cc create mode 100644 gcc-4.9/gcc/go/gospec.c create mode 100644 gcc-4.9/gcc/go/lang-specs.h create mode 100644 gcc-4.9/gcc/go/lang.opt (limited to 'gcc-4.9/gcc/go') diff --git a/gcc-4.9/gcc/go/ChangeLog b/gcc-4.9/gcc/go/ChangeLog new file mode 100644 index 000000000..689578e20 --- /dev/null +++ b/gcc-4.9/gcc/go/ChangeLog @@ -0,0 +1,782 @@ +2014-03-03 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::immutable_struct): If IS_COMMON, set + DECL_WEAK. + (GCC_backend::immutable_struct_set_init): If IS_COMMON, clear + DECL_WEAK. + +2014-01-24 Chris Manghane + + * go-gcc.cc (Gcc_backend::unary_expression): New function. + +2014-01-16 Chris Manghane + + * go-gcc.cc (Gcc_backend::conditional_expression): Add btype + parameter. + (operator_to_tree_code): New static function. + (Gcc_backend::binary_expression): New function. + +2014-01-14 Chris Manghane + + * go-gcc.cc (Gcc_backend::compound_expression): New function. + (Gcc_backend::conditional_expression): New function. + +2014-01-02 Richard Sandiford + + Update copyright years + +2014-01-02 Tobias Burnus + + * gccgo.texi: Bump @copying's copyright year. + +2013-12-16 Chris Manghane + + * go-gcc.cc (Gcc_backend::struct_field_expression): New function. + +2013-12-11 Ian Lance Taylor + + * go-lang.c (go_langhook_post_options): Disable sibling calls by + default. + +2013-12-10 Ian Lance Taylor + + * Make-lang.in (check_go_parallelize): Test go-test.exp r* tests + separately. + +2013-12-05 Ian Lance Taylor + + Revert this change; no longer required. + 2013-11-06 Ian Lance Taylor + + * go-lang.c (go_langhook_post_options): If + -fisolate-erroneous-paths was turned on by an optimization option, + turn it off. + +2013-11-23 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::function_type): Add result_struct + parameter. + +2013-11-22 Andrew MacLeod + + * go-gcc.cc: Add required include files from gimple.h. + * go-lang.c: Likewise + +2013-11-18 Richard Sandiford + + * gofrontend/expressions.cc: Replace tree_low_cst (..., 0) with + tree_to_shwi throughout. + +2013-11-18 Richard Sandiford + + * gofrontend/expressions.cc: Replace host_integerp (..., 0) with + tree_fits_shwi_p throughout. + +2013-11-14 Andrew MacLeod + + * go-lang.c: Include only gimplify.h and gimple.h as needed. + +2013-11-14 Diego Novillo + + * go-backend.c: Include stor-layout.h. + * go-gcc.cc: Include stringpool.h. + Include stor-layout.h. + Include varasm.h. + * go-lang.c: Include stor-layout.h. + +2013-11-12 Andrew MacLeod + + * go-lang.c: Include gimplify.h. + +2013-11-06 Ian Lance Taylor + + * go-lang.c (go_langhook_post_options): If + -fisolate-erroneous-paths was turned on by an optimization option, + turn it off. + +2013-10-14 Chris Manghane + + * go-gcc.cc (Gcc_backend::address_expression): New function. + +2013-10-11 Chris Manghane + + * go-gcc.cc (Gcc_backend::function_code_expression): New + function. + +2013-10-10 Chris Manghane + + * go-gcc.cc (Gcc_backend::error_function): New function. + (Gcc_backend::function): New function. + (Gcc_backend::make_function): New function. + (function_to_tree): New function. + +2013-10-04 Chris Manghane + + * go-gcc.cc (Gcc_backend::convert_expression): New function. + +2013-10-02 Chris Manghane + + * go-gcc.cc: Include "real.h" and "realmpfr.h". + (Gcc_backend::integer_constant_expression): New function. + (Gcc_backend::float_constant_expression): New function. + (Gcc_backend::complex_constant_expression): New function. + +2013-09-30 Chris Manghane + + * go-gcc.cc (Gcc_backend::error_expression): New function. + (Gcc_backend::var_expression): New function. + (Gcc_backend::indirect_expression): New function. + +2013-09-25 Tom Tromey + + * Make-lang.in (gospec.o): Remove. + (CFLAGS-go/gospec.o): New variable. + (GCCGO_OBJS): Update to use go/gospec.o. + (go_OBJS): Define. + (GO_SYSTEM_H, GO_C_H, GO_LINEMAP_H, GO_LEX_H, GO_PARSE_H) + (GO_GOGO_H, GO_TYPES_H, GO_STATEMENTS_H, GO_EXPRESSIONS_H) + (GO_EXPORT_H, GO_IMPORT_H, GO_RUNTIME_H, GO_AST_DUMP_H) + (go/go-backend.o, go/go-lang.o, go/go-gcc.o, go/go-linemap.o) + (go/ast-dump.o, go/dataflow.o, go/export.o, go/expressions.o) + (go/go.o, go/go-dump.o, go/go-optimize.o, go/gogo-tree.o) + (go/gogo.o, go/import.o, go/import-archive.o, go/lex.o) + (go/parse.o, go/runtime.o, go/statements.o, go/types.o) + (go/unsafe.o): Remove. + (CFLAGS-go/go-gcc.o, CFLAGS-go/go-linemap.o): New variables. + (go/%.o: go/gofrontend/%.cc): Use COMPILE and POSTCOMPILE. + +2013-09-25 Tom Tromey + + * Make-lang.in (gospec.o): Don't use subshell. + +2013-08-28 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::immutable_struct): Set TREE_PUBLIC if + the struct is not hidden. + (Gcc_backend::immutable_struct_set_init): Don't set TREE_PUBLIC. + +2013-08-06 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::immutable_struct_set_init): Use + compute_reloc_for_constant. + +2013-08-02 Ian Lance Taylor + + * go-gcc.cc (immutable_struct_set_init): Always call + resolve_unique_section. + +2013-07-24 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::non_zero_size_type): If a struct has a + fields, recreate those fields with the first one with a non-zero + size. + +2013-07-23 Ian Lance Taylor + + * go-backend.c: Don't #include "rtl.h". + (go_imported_unsafe): Don't call init_varasm_once. + * Make-lang.in (go/go-backend.o): Don't depend on $(RTL_H). + +2013-07-23 Ian Lance Taylor + + * go-lang.c: Don't #include "except.h". + * Make-lang.in (go/go-lang.o): Don't depend on $(EXCEPT_H). + +2013-06-18 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::immutable_struct): Add is_hidden + parameter. + (Gcc_backend::immutable_struct_set_init): Likewise. + +2013-05-16 Jason Merrill + + * Make-lang.in (go1$(exeext)): Use link mutex. + +2013-01-16 Shenghou Ma + + * gospec.c: pass -u pthread_create to linker when static linking. + +2012-12-21 Ian Lance Taylor + + PR bootstrap/54659 + * go-system.h: Don't include . + +2012-12-18 Ian Lance Taylor + + PR go/55201 + * gospec.c: Revert last patch. + +2012-12-18 Andreas Schwab + + PR go/55201 + * gospec.c (LIBATOMIC): Define. + (LIBATOMIC_PROFILE): Define. + (lang_specific_driver): Add LIBATOMIC[_PROFILE] option. + +2012-11-29 Ian Lance Taylor + + * go-gcc.cc: Include "output.h". + (global_variable): Add is_unique_section parameter. + (global_variable_set_init): Adjust unique section if necessary. + * Make-lang.in (go/go-gcc.o): Add dependency on output.h. + +2012-11-17 Diego Novillo + + Adjust for new vec API (http://gcc.gnu.org/wiki/cxx-conversion/cxx-vec) + + * go-lang.c: Use new vec API in vec.h. + +2012-11-16 Ian Lance Taylor + + * Make-lang.in (gccgo$(exeext)): Add + at start of command. + (go1$(exeext)): Likewise. + +2012-10-30 Ian Lance Taylor + + * 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-17 Ian Lance Taylor + + * config-lang.in (target_libs): Add target-libbacktrace. + +2012-09-16 Ian Lance Taylor + + * Make-lang.in (go/gogo.o): Depend on filenames.h. + +2012-08-14 Diego Novillo + + Merge from cxx-conversion branch. Configury. + + * go-c.h: Remove all handlers of ENABLE_BUILD_WITH_CXX. + * go-gcc.cc: Likewise. + * go-system.h: Likewise. + +2012-07-24 Uros Bizjak + + * go-lang.c (lang_decl): Add variable_size GTY option. + +2012-05-09 Ian Lance Taylor + + * lang.opt: Add -fgo-pkgpath. + * go-lang.c (go_pkgpath): New static variable. + (go_prefix): New static variable. + (go_langhook_init): Pass go_pkgpath and go_prefix to + go_create_gogo. + (go_langhook_handle_option): Handle -fgo-pkgpath. Change + -fgo-prefix handling to just set go_prefix. + * go-c.h (go_set_prefix): Don't declare. + (go_create_gogo): Add pkgpath and prefix to declaration. + * go-gcc.cc (Gcc_backend::global_variable): Change unique_prefix + to pkgpath. Don't include the package name in the asm name. + * gccgo.texi (Invoking gccgo): Document -fgo-pkgpath. Update the + docs for -fgo-prefix. + +2012-04-23 Ian Lance Taylor + + * go-lang.c (go_langhook_init): Set MPFR precision to 256. + +2012-04-20 Ian Lance Taylor + + * lang.opt: Add -fgo-check-divide-zero and + -fgo-check-divide-overflow. + * gccgo.texi (Invoking gccgo): Document new options. + +2012-04-18 Steven Bosscher + + * go-gcc.cc (Gcc_backend::switch_statement): Build SWITCH_EXPR + with NULL_TREE type instead of void_type_node. + +2012-03-09 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::assignment_statement): Convert the rhs + to the lhs type if necessary. + +2012-03-08 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::init_statement): Don't initialize a + zero-sized variable. + (go_non_zero_struct): New global variable. + (Gcc_backend::non_zero_size_type): New function. + (Gcc_backend::global_variable): Don't build an assignment for a + zero-sized value. + * go-c.h (go_non_zero_struct): Declare. + * config-lang.in (gtfiles): Add go-c.h. + +2012-02-29 Ian Lance Taylor + + * go-gcc.cc (class Gcc_tree): Add set_tree method. + (set_placeholder_pointer_type): When setting to a pointer to + error, set to error_mark_node. + +2012-02-23 Richard Guenther + + * go-gcc.cc (Gcc_backend::placeholder_pointer_type): Use + build_distinct_type_copy. + +2012-02-17 Ian Lance Taylor + + * Make-lang.in (go/import.o): Add dependency on $(GO_LEX_H). + +2012-02-17 Ian Lance Taylor + + * gospec.c (lang_specific_driver): If linking, and no -o option + was used, add one. + +2012-02-14 Ian Lance Taylor + + PR go/48411 + * Make-lang.in (gccgo-cross$(exeext)): New target. + (go.all.cross): Depend on gccgo-cross$(exeext) instead of + gccgo$(exeext). + (go.install-common): Only install GCCGO_TARGET_INSTALL_NAME if + gccgo-cross$(exeext) does not exist. + +2012-02-07 Ian Lance Taylor + + * gccgo.texi (Function Names): Document //extern instead of + __asm__. + +2012-02-01 Jakub Jelinek + + PR target/52079 + * go-lang.c (go_langhook_type_for_mode): For TImode and 64-bit HWI + return build_nonstandard_integer_type result if possible. + +2012-01-21 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::type_size): Check for error_mark_node. + (Gcc_backend::type_alignment): Likewise. + (Gcc_backend::type_field_alignment): Likewise. + (Gcc_backend::type_field_offset): Likewise. + +2012-01-20 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::placeholder_struct_type): Permit name to + be empty. + (Gcc_backend::set_placeholder_struct_type): Likewise. + +2012-01-17 Ian Lance Taylor + + * gospec.c (lang_specific_driver): If we see -S without -o, add -o + BASE.s rather than -o BASE.o. + +2012-01-11 Ian Lance Taylor + + * go-lang.c (go_langhook_init): Initialize void_list_node before + calling go_create_gogo. + +2012-01-10 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::type_size): New function. + (Gcc_backend::type_alignment): New function. + (Gcc_backend::type_field_alignment): New function. + (Gcc_backend::type_field_offset): New function. + * go-backend.c (go_type_alignment): Remove. + * go-c.h (go_type_alignment): Don't declare. + +2011-12-27 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::set_placeholder_struct_type): Use + build_distinct_type_copy rather than build_variant_type_copy. + (Gcc_backend::set_placeholder_array_type): Likewise. + (Gcc_backend::named_type): Add special handling for builtin + basic types. + +2011-12-22 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::set_placeholder_pointer_type): Arrange + for the type name to have a DECL_ORIGINAL_TYPE as gcc expects. + (Gcc_backend::set_placeholder_struct_type): Likewise. + (Gcc_backend::set_placeholder_array_type): Likewise. + (Gcc_backend::named_type): Set DECL_ORIGINAL_TYPE. + +2011-12-13 Ian Lance Taylor + + * go-backend.c: #include "simple-object.h" and "intl.h". + (GO_EXPORT_SEGMENT_NAME): Define if not defined. + (GO_EXPORT_SECTION_NAME): Likewise. + (go_write_export_data): Use GO_EXPORT_SECTION_NAME. + (go_read_export_data): New function. + * go-c.h (go_read_export_data): Declare. + +2011-11-29 Sanjoy Das + Ian Lance Taylor + + * go-location.h: New file. + * go-linemap.cc: New file. + * go-gcc.cc: Change all uses of source_location to Location. + * Make-lang.in (GO_OBJS): Add go/go-linemap.o. + (GO_LINEMAP_H): New variable. + (GO_LEX_H): Use $(GO_LINEMAP_H). + (GO_GOGO_H, GO_TYPES_H, GO_IMPORT_H): Likewise. + (go/go-linemap.o): New target. + +2011-11-02 Rainer Orth + + * Make-lang.in (gospec.o): Pass SHLIB instead of SHLIB_LINK. + +2011-08-24 Roberto Lublinerman + + * lang.opt: Add fgo-optimize-. + * go-lang.c (go_langhook_handle_option): Handle OPT_fgo_optimize. + * go-c.h (go_enable_optimize): Declare. + * Make-lang.in (GO_OBJS): Add go/go-optimize.o. + (GO_EXPORT_H): Define. + (GO_IMPORT_H): Add $(GO_EXPORT_H). + (GO_AST_DUMP_H): Define. + (go/ast-dump.o, go/statements.o): Use GO_AST_DUMP_H. + (go/export.o, go/gogo.o, go/import.o): Use GO_EXPORT_H. + (go/types.o): Likewise. + (go/expressions.o): Use GO_AST_DUMP_H and GO_EXPORT_H. + (go/go-optimize.o): New target. + +2011-08-24 Joseph Myers + + * Make-lang.in (CFLAGS-go/go-lang.o): New. + (go/go-lang.o): Remove explicit compilation rule. + +2011-08-08 Rainer Orth + + * Make-lang.in (gccgo$(exeext)): Add $(EXTRA_GCC_LIBS). + +2011-08-02 Roberto Lublinerman + + * Make-lang.in (GO_OBJS): Add go/ast-dump.o. + (go/ast-dump.o): New target. + (go/expressions.o): Depend on go/gofrontend/ast-dump.h. + (go/statements.o): Likewise. + +2011-07-06 Richard Guenther + + * go-lang.c (go_langhook_init): + Merge calls to build_common_tree_nodes and build_common_tree_nodes_2. + +2011-06-14 Joseph Myers + + * Make-lang.in (go/go-lang.o, go/go-backend.o): Update + dependencies. + * go-backend.c: Include common/common-target.h. + (go_write_export_data): Use targetm_common.have_named_sections. + * go-lang.c: Include common/common-target.h. + (go_langhook_init_options_struct): Use + targetm_common.supports_split_stack. + +2011-06-13 Ian Lance Taylor + + * Make-lang.in (go/expressions.o): Depend on $(GO_RUNTIME_H). + +2011-06-10 Ian Lance Taylor + + * go-gcc.cc: Include "toplev.h". + (Gcc_backend::immutable_struct): New function. + (Gcc_backend::immutable_struct_set_init): New function. + (Gcc_backend::immutable_struct_reference): New function. + * Make-lang.in (go/go-gcc.o): Depend on toplev.h. + +2011-06-09 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::zero_expression): New function. + +2011-06-07 Richard Guenther + + * go-lang.c (go_langhook_init): Do not set + size_type_node or call set_sizetype. + +2011-05-27 Ian Lance Taylor + + * go-backend.c: Include "output.h". + (go_write_export_data): New function. + * go-c.h (go_write_export_data): Declare. + * Make-lang.in (go/go-backend.o): Depend on output.h. + (go/export.o): Depend on $(GO_C_H). Do not depend on + $(MACHMODE_H), output.h, or $(TARGET_H). + +2011-05-24 Joseph Myers + + * Make-lang.in (GCCGO_OBJS): Remove prefix.o. + (gccgo$(exeext)): Use libcommon-target.a. + +2011-05-20 Joseph Myers + + * Make-lang.in (GCCGO_OBJS): Remove intl.o and version.o. + +2011-05-13 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::function_type): When building a struct + for multiple results, check that all fields types have a size. + (Gcc_backend::placeholder_pointer_type): Permit name to be empty. + +2011-05-12 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::local_variable): Add is_address_taken + parameter. + (Gcc_backend::parameter_variable): Likewise. + +2011-05-07 Eric Botcazou + + * go-lang.c (global_bindings_p): Return bool and simplify. + +2011-05-05 Nathan Froyd + + * go-gcc.cc (Gcc_backend::switch_statement): Call build_case_label. + +2011-05-04 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::struct_type): Call fill_in_struct. + (Gcc_backend::fill_in_struct): New function. + (Gcc_backend::array_type): Implement. + (Gcc_backend::fill_in_array): New function. + (Gcc_backend::placeholder_pointer_type): New function. + (Gcc_backend::set_placeholder_pointer_type): New function. + (Gcc_backend::set_placeholder_function_type): New function. + (Gcc_backend::placeholder_struct_type): New function. + (Gcc_backend::set_placeholder_struct_type): New function. + (Gcc_backend::placeholder_array_type): New function. + (Gcc_backend::set_placeholder_array_type): New function. + (Gcc_backend::named_type): New function. + (Gcc_backend::circular_pointer_type): New function. + (Gcc_backend::is_circular_pointer_type): New function. + +2011-04-26 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::struct_type): Implement. + +2011-04-25 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::error_type): Implement. + (Gcc_backend::string_type): Remove. + (Gcc_backend::function_type): Change signature and implement. + (Gcc_backend::struct_type): Change signature. + (Gcc_backend::slice_type, Gcc_backend::map_type): Remove. + (Gcc_backend::channel_type, Gcc_backend::interface_type): Remove. + (Gcc_backend::pointer_type): Check for error. + * Make-lang.in (go/types.o): Depend on go/gofrontend/backend.h. + +2011-04-25 Evan Shaw + + * go-gcc.c (class Gcc_tree): Make get_tree const. + (Gcc_backend::void_type): Implement. + (Gcc_backend::bool_type): Implement. + (Gcc_backend::integer_type): Implement. + (Gcc_backend::float_type): Implement. + (Gcc_backend::complex_type): New function. + (Gcc_backend::pointer_type): New function. + (Gcc_backend::make_type): New function. + (type_to_tree): New function. + +2011-04-21 Ian Lance Taylor + + * go-system.h (go_assert, go_unreachable): Define. + +2011-04-19 Ian Lance Taylor + + * go-system.h: Include "intl.h". + * Make-lang.in (GO_SYSTEM_H): Add intl.h. + (go/statements.o): Remove dependencies on intl.h $(TREE_H) + $(GIMPLE_H) convert.h tree-iterator.h $(TREE_FLOW_H) $(REAL_H). + +2011-04-19 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::temporary_variable): New function. + +2011-04-19 Ian Lance Taylor + + * go-gcc.cc (class Bblock): Define. + (Gcc_backend::if_statement): Change then_block and else_block to + Bblock*. + (Gcc_backend::block): New function. + (Gcc_backend::block_add_statements): New function. + (Gcc_backend::block_statement): New function. + (tree_to_block, block_to_tree): New functions. + +2011-04-18 Ian Lance Taylor + + * go-gcc.cc: Include "go-c.h". + (class Bvariable): Define. + (Gcc_backend::init_statement): New function. + (Gcc_backend::global_variable): New function. + (Gcc_backend::global_variable_set_init): New function. + (Gcc_backend::local_variable): New function. + (Gcc_backend::parameter_variable): New function. + (tree_to_type, var_to_tree): New functions. + * Make-lang.in (go/go-gcc.o): Depend on $(GO_C_H). + * (go/gogo-tree.o): Depend on go/gofrontend/backend.h. + +2011-04-15 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::compound_statement): New function. + (Gcc_backend::assignment_statement): Use error_statement. + (Gcc_backend::return_statement): Likewise. + (Gcc_backend::if_statement): Likewise. + (Gcc_backend::switch_statement): Likewise. + (Gcc_backend::statement_list): Likewise. + +2011-04-14 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::error_statement): New function. + +2011-04-13 Ian Lance Taylor + + * Make-lang.in (go/gogo-tree.o): depend on $(GO_RUNTIME_H). + +2011-04-13 Ian Lance Taylor + + * Make-lang.in (GO_OBJS): Add go/runtime.o. + (GO_RUNTIME_H): New variable. + (go/runtime.o): New target. + (go/gogo.o): Depend on $(GO_RUNTIME_H). + (go/statements.o): Likewise. + +2011-04-12 Nathan Froyd + + * go-lang.c (union lang_tree_node): Check for TS_COMMON before + calling TREE_CHAIN. + +2011-04-06 Ian Lance Taylor + + * go-gcc.cc (if_statement): Use build3_loc. + (Gcc_backend::switch_statement): New function. + (Gcc_backend::statement_list): New function. + +2011-04-06 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::if_statement): New function. + (tree_to_stat): New function. + (expr_to_tree): Renamed from expression_to_tree. + (stat_to_tree): Renamed from statement_to_tree. + +2011-04-06 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::expression_statement): New function. + +2011-04-04 Ian Lance Taylor + + * go-gcc.c (class Blabel): Define. + (Gcc_backend::make_expression): New function. + (get_identifier_from_string): New function. + (Gcc_backend::label): New function. + (Gcc_backend::label_definition_statement): New function. + (Gcc_backend::goto_statement): New function. + (Gcc_backend::label_address): New function. + (expression_to_tree): New function. + * Make-lang.in (go/expressions.o): Depend on + go/gofrontend/backend.h. + (go/gogo.o): Likewise. + +2011-04-04 Ian Lance Taylor + + * go-gcc.cc: #include "tree-iterator.h", "gimple.h", and "gogo.h". + (class Bfunction): Define. + (Gcc_backend::assignment_statement): Rename from assignment. + Check for errors. + (Gcc_backend::return_statement): New function. + (tree_to_function): New function. + * Make-lang.in (go/go-gcc.o): Depend on tree-iterator.h, + $(GIMPLE_H), and $(GO_GOGO_H). + +2011-04-03 Ian Lance Taylor + + * go-gcc.cc: New file. + * Make-lang.in (GO_OBJS): Add go/go-gcc.o. + (go/go-gcc.o): New target. + (go/go.o): Depend on go/gofrontend/backend.h. + (go/statements.o): Likewise. + +2011-02-14 Ralf Wildenhues + + * gccgo.texi (Top, Import and Export): Fix a typo and a markup nit. + +2011-02-08 Ian Lance Taylor + + * go-lang.c (go_langhook_init_options_struct): Set + frontend_set_flag_errno_math. Don't set x_flag_trapping_math. + +2011-01-31 Rainer Orth + + * gospec.c (lang_specific_driver) [HAVE_LD_STATIC_DYNAMIC] Use + LD_STATIC_OPTION, LD_DYNAMIC_OPTION. + +2011-01-21 Ian Lance Taylor + + * go-lang.c (go_langhook_init): Omit float_type_size when calling + go_create_gogo. + * go-c.h: Update declaration of go_create_gogo. + +2011-01-13 Ian Lance Taylor + + * go-backend.c: Include "rtl.h" and "target.h". + (go_imported_unsafe): New function. + * go-c.h (go_imported_unsafe): Declare. + * Make-lang.in (go/go-backend.o): Depend on $(RTL_H). + (go/gogo-tree.o): Remove dependency on $(RTL_H). + (go/unsafe.o): Depend on $(GO_C_H). + +2010-12-31 Joern Rennecke + + PR go/47113 + * go-backend.c: (go_field_alignment): Add ATTRIBUTE_UNUSED to + variable ‘field’ . + +2010-12-21 Ian Lance Taylor + + * Make-lang.in (check-go): Remove. + (lang_checks_parallelized): Add check-go. + (check_go_parallelize): Set. + +2010-12-13 Ian Lance Taylor + + * gospec.c (lang_specific_driver): Add a -o option if not linking + and there is no -o option already. + +2010-12-07 Ian Lance Taylor + + PR tree-optimization/46805 + PR tree-optimization/46833 + * go-lang.c (go_langhook_type_for_mode): Handle vector modes. + +2010-12-06 Ian Lance Taylor + + PR other/46789 + PR bootstrap/46812 + * go-lang.c (go_char_p): Define type and vectors. + (go_search_dirs): New static variable. + (go_langhook_handle_option): Use version and version/machine + directories for -L. + (go_langhook_post_options): Add non-specific -L paths. + * Make-lang.in (go/go-lang.o): Define DEFAULT_TARGET_VERSION and + DEFAULT_TARGET_MACHINE when compiling. + * gccgo.texi (Invoking gccgo): Only document -L for linking. + (Import and Export): Don't mention -L for finding import files. + +2010-12-03 Ian Lance Taylor + + PR bootstrap/46776 + * go-backend.c: New file. + * go-c.h (go_type_alignment): Declare. + (go_field_alignment, go_trampoline_info): Declare. + * Make-lang.in (GO_OBJS): Add go/go-backend.o. + (go/go-backend.o): New target. + (go/go-lang.o): Make dependencies match source file. + (go/expressions.o): Don't depend on $(TM_H) $(TM_P_H). + (go/gogo-tree.o): Don't depend on $(TM_H). + +2010-12-03 Ian Lance Taylor + + * config-lang.in (build_by_default): Set to no. + +2010-12-02 Ian Lance Taylor + + Go frontend added to gcc repository. + +Copyright (C) 2010-2014 Free Software Foundation, Inc. + +Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. diff --git a/gcc-4.9/gcc/go/Make-lang.in b/gcc-4.9/gcc/go/Make-lang.in new file mode 100644 index 000000000..abcae66a2 --- /dev/null +++ b/gcc-4.9/gcc/go/Make-lang.in @@ -0,0 +1,235 @@ +# Make-lang.in -- Top level -*- makefile -*- fragment for gcc Go frontend. + +# Copyright (C) 2009-2014 Free Software Foundation, Inc. + +# This file is part of GCC. + +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. + +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# This file provides the language dependent support in the main Makefile. + +# Installation name. + +GCCGO_INSTALL_NAME := $(shell echo gccgo|sed '$(program_transform_name)') +GCCGO_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gccgo|sed '$(program_transform_name)') + +# The name for selecting go in LANGUAGES. +go: go1$(exeext) + +.PHONY: go + +CFLAGS-go/gospec.o += $(DRIVER_DEFINES) + +GCCGO_OBJS = $(GCC_OBJS) go/gospec.o +gccgo$(exeext): $(GCCGO_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS) + +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ + $(GCCGO_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \ + $(EXTRA_GCC_LIBS) $(LIBS) + +# The cross-compiler version. This is built mainly as a signal to the +# go.install-common target. If this executable exists, it means that +# go.all.cross was run. +gccgo-cross$(exeext): gccgo$(exeext) + -rm -f gccgo-cross$(exeext) + cp gccgo$(exeext) gccgo-cross$(exeext) + +# Use strict warnings. +go-warn = $(STRICT_WARN) + +GO_OBJS = \ + go/ast-dump.o \ + go/dataflow.o \ + go/export.o \ + go/expressions.o \ + go/go-backend.o \ + go/go-dump.o \ + go/go-gcc.o \ + go/go-lang.o \ + go/go-linemap.o \ + go/go-optimize.o \ + go/go.o \ + go/gogo-tree.o \ + go/gogo.o \ + go/import.o \ + go/import-archive.o \ + go/lex.o \ + go/parse.o \ + go/runtime.o \ + go/statements.o \ + go/types.o \ + go/unsafe.o + +go_OBJS = $(GO_OBJS) go/gospec.o + +go1$(exeext): $(GO_OBJS) attribs.o $(BACKEND) $(LIBDEPS) + +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ + $(GO_OBJS) attribs.o $(BACKEND) $(LIBS) $(BACKENDLIBS) + +# Documentation. + +GO_TEXI_FILES = \ + go/gccgo.texi \ + $(gcc_docdir)/include/fdl.texi \ + $(gcc_docdir)/include/gpl_v3.texi \ + $(gcc_docdir)/include/gcc-common.texi \ + gcc-vers.texi + +doc/gccgo.info: $(GO_TEXI_FILES) + if test "x$(BUILD_INFO)" = xinfo; then \ + rm -f doc/gccgo.info*; \ + $(MAKEINFO) $(MAKEINFOFLAGS) -I $(gcc_docdir) \ + -I $(gcc_docdir)/include -o $@ $<; \ + else true; fi + +doc/gccgo.dvi: $(GO_TEXI_FILES) + $(TEXI2DVI) -I $(abs_docdir) -I $(abs_docdir)/include -o $@ $< + +doc/gccgo.pdf: $(GO_TEXI_FILES) + $(TEXI2PDF) -I $(abs_docdir) -I $(abs_docdir)/include -o $@ $< + +$(build_htmldir)/go/index.html: $(GO_TEXI_FILES) + $(mkinstalldirs) $(@D) + rm -f $(@D)/* + $(TEXI2HTML) -I $(gcc_docdir) -I $(gcc_docdir)/include \ + -I $(srcdir)/go -o $(@D) $< + +.INTERMEDIATE: gccgo.pod + +gccgo.pod: go/gccgo.texi + -$(TEXI2POD) -D gccgo < $< > $@ + +# Build hooks. + +go.all.cross: gccgo-cross$(exeext) +go.start.encap: gccgo$(exeext) +go.rest.encap: +go.info: doc/gccgo.info +go.dvi: doc/gccgo.dvi +go.pdf: doc/gccgo.pdf +go.html: $(build_htmldir)/go/index.html +go.srcinfo: doc/gccgo.info + -cp -p $^ $(srcdir)/doc +go.srcextra: +go.tags: force + cd $(srcdir)/go; \ + etags -o TAGS.sub *.c *.h gofrontend/*.h gofrontend/*.cc; \ + etags --include TAGS.sub --include ../TAGS.sub +go.man: doc/gccgo.1 +go.srcman: doc/gccgo.1 + -cp -p $^ $(srcdir)/doc + +lang_checks += check-go +lang_checks_parallelized += check-go +check_go_parallelize = go-test.exp=*/test/\[0-57-9a-bd-hj-qs-zA-Z\]* \ + go-test.exp=*/test/c* \ + go-test.exp=*/test/i* \ + go-test.exp=*/test/r* \ + go-test.exp=*/test/6* + +# Install hooks. + +go.install-common: installdirs + -rm -f $(DESTDIR)$(bindir)/$(GCCGO_INSTALL_NAME)$(exeext) + $(INSTALL_PROGRAM) gccgo$(exeext) $(DESTDIR)$(bindir)/$(GCCGO_INSTALL_NAME)$(exeext) + -if test -f go1$(exeext); then \ + if test -f gccgo-cross$(exeext); then \ + :; \ + else \ + rm -f $(DESTDIR)$(bindir)/$(GCCGO_TARGET_INSTALL_NAME)$(exeext); \ + ( cd $(DESTDIR)$(bindir) && \ + $(LN) $(GCCGO_INSTALL_NAME)$(exeext) $(GCCGO_TARGET_INSTALL_NAME)$(exeext) ); \ + fi; \ + fi + +go.install-plugin: + +go.install-info: $(DESTDIR)$(infodir)/gccgo.info + +go.install-pdf: doc/gccgo.pdf + @$(NORMAL_INSTALL) + test -z "$(pdfdir)" || $(mkinstalldirs) "$(DESTDIR)$(pdfdir)/gcc" + @for p in doc/gccgo.pdf; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(pdf__strip_dir) \ + echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(pdfdir)/gcc/$$f'"; \ + $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(pdfdir)/gcc/$$f"; \ + done + +go.install-html: $(build_htmldir)/go + @$(NORMAL_INSTALL) + test -z "$(htmldir)" || $(mkinstalldirs) "$(DESTDIR)$(htmldir)" + @for p in $(build_htmldir)/go; do \ + if test -f "$$p" || test -d "$$p"; then d=""; else d="$(srcdir)/"; fi; \ + f=$(html__strip_dir) \ + if test -d "$$d$$p"; then \ + echo " $(mkinstalldirs) '$(DESTDIR)$(htmldir)/$$f'"; \ + $(mkinstalldirs) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \ + echo " $(INSTALL_DATA) '$$d$$p'/* '$(DESTDIR)$(htmldir)/$$f'"; \ + $(INSTALL_DATA) "$$d$$p"/* "$(DESTDIR)$(htmldir)/$$f"; \ + else \ + echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(htmldir)/$$f'"; \ + $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(htmldir)/$$f"; \ + fi; \ + done + +go.install-man: $(DESTDIR)$(man1dir)/$(GCCGO_INSTALL_NAME)$(man1ext) + +$(DESTDIR)$(man1dir)/$(GCCGO_INSTALL_NAME)$(man1ext): doc/gccgo.1 installdirs + -rm -f $@ + -$(INSTALL_DATA) $< $@ + -chmod a-x $@ + +go.uninstall: + rm -rf $(DESTDIR)$(bindir)/$(GCCGO_INSTALL_NAME)$(exeext) + rm -rf $(DESTDIR)$(man1dir)/$(GCCGO_INSTALL_NAME)$(man1ext) + rm -rf $(DESTDIR)$(bindir)/$(GCCGO_TARGET_INSTALL_NAME)$(exeext) + rm -rf $(DESTDIR)$(infodir)/gccgo.info* + +# Clean hooks. + +go.mostlyclean: + -rm -f go/*$(objext) + -rm -f go/*$(coverageexts) +go.clean: +go.distclean: +go.maintainer-clean: + -rm -f $(docobjdir)/gccgo.1 + +# Stage hooks. + +go.stage1: stage1-start + -mv go/*$(objext) stage1/go +go.stage2: stage2-start + -mv go/*$(objext) stage2/go +go.stage3: stage3-start + -mv go/*$(objext) stage3/go +go.stage4: stage4-start + -mv go/*$(objext) stage4/go +go.stageprofile: stageprofile-start + -mv go/*$(objext) stageprofile/go +go.stagefeedback: stagefeedback-start + -mv go/*$(objext) stagefeedback/go + +CFLAGS-go/go-lang.o += -DDEFAULT_TARGET_VERSION=\"$(version)\" \ + -DDEFAULT_TARGET_MACHINE=\"$(target_noncanonical)\" + +GOINCLUDES = -I $(srcdir)/go -I $(srcdir)/go/gofrontend + +CFLAGS-go/go-gcc.o += $(GOINCLUDES) +CFLAGS-go/go-linemap.o += $(GOINCLUDES) + +go/%.o: go/gofrontend/%.cc + $(COMPILE) $(GOINCLUDES) $< + $(POSTCOMPILE) diff --git a/gcc-4.9/gcc/go/README.gcc b/gcc-4.9/gcc/go/README.gcc new file mode 100644 index 000000000..3c764f7a9 --- /dev/null +++ b/gcc-4.9/gcc/go/README.gcc @@ -0,0 +1,3 @@ +The files in the gofrontend subdirectory are mirrored from the +gofrontend project hosted at http://code.google.com/p/gofrontend. +These files are the ones in the go subdirectory of that project. diff --git a/gcc-4.9/gcc/go/config-lang.in b/gcc-4.9/gcc/go/config-lang.in new file mode 100644 index 000000000..c33e218b4 --- /dev/null +++ b/gcc-4.9/gcc/go/config-lang.in @@ -0,0 +1,40 @@ +# config-lang.in -- Top level configure fragment for gcc Go frontend. + +# Copyright (C) 2009-2014 Free Software Foundation, Inc. + +# This file is part of GCC. + +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. + +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# Configure looks for the existence of this file to auto-config each language. +# We define several parameters used by configure: +# +# language - name of language as it would appear in $(LANGUAGES) +# compilers - value to add to $(COMPILERS) + +language="go" + +compilers="go1\$(exeext)" + +target_libs="target-libgo target-libffi target-libbacktrace" + +# The Go frontend is written in C++, so we need to build the C++ +# compiler during stage 1. +lang_requires_boot_languages=c++ + +gtfiles="\$(srcdir)/go/go-lang.c \$(srcdir)/go/go-c.h" + +# Do not build by default. +build_by_default="no" diff --git a/gcc-4.9/gcc/go/gccgo.texi b/gcc-4.9/gcc/go/gccgo.texi new file mode 100644 index 000000000..d7222d501 --- /dev/null +++ b/gcc-4.9/gcc/go/gccgo.texi @@ -0,0 +1,402 @@ +\input texinfo @c -*-texinfo-*- +@setfilename gccgo.info +@settitle The GNU Go Compiler + +@c Merge the standard indexes into a single one. +@syncodeindex fn cp +@syncodeindex vr cp +@syncodeindex ky cp +@syncodeindex pg cp +@syncodeindex tp cp + +@include gcc-common.texi + +@c Copyright years for this manual. +@set copyrights-go 2010-2014 + +@copying +@c man begin COPYRIGHT +Copyright @copyright{} @value{copyrights-go} Free Software Foundation, Inc. + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, the Front-Cover Texts being (a) (see below), and +with the Back-Cover Texts being (b) (see below). +A copy of the license is included in the +@c man end +section entitled ``GNU Free Documentation License''. +@ignore +@c man begin COPYRIGHT +man page gfdl(7). +@c man end +@end ignore + +@c man begin COPYRIGHT + +(a) The FSF's Front-Cover Text is: + + A GNU Manual + +(b) The FSF's Back-Cover Text is: + + You have freedom to copy and modify this GNU Manual, like GNU + software. Copies published by the Free Software Foundation raise + funds for GNU development. +@c man end +@end copying + +@ifinfo +@format +@dircategory Software development +@direntry +* Gccgo: (gccgo). A GCC-based compiler for the Go language +@end direntry +@end format + +@insertcopying +@end ifinfo + +@titlepage +@title The GNU Go Compiler +@versionsubtitle +@author Ian Lance Taylor + +@page +@vskip 0pt plus 1filll +Published by the Free Software Foundation @* +51 Franklin Street, Fifth Floor@* +Boston, MA 02110-1301, USA@* +@sp 1 +@insertcopying +@end titlepage +@contents +@page + +@node Top +@top Introduction + +This manual describes how to use @command{gccgo}, the GNU compiler for +the Go programming language. This manual is specifically about +@command{gccgo}. For more information about the Go programming +language in general, including language specifications and standard +package documentation, see @uref{http://golang.org/}. + +@menu +* Copying:: The GNU General Public License. +* GNU Free Documentation License:: + How you can share and copy this manual. +* Invoking gccgo:: How to run gccgo. +* Import and Export:: Importing and exporting package data. +* C Interoperability:: Calling C from Go and vice-versa. +* Index:: Index. +@end menu + + +@include gpl_v3.texi + +@include fdl.texi + + +@node Invoking gccgo +@chapter Invoking gccgo + +@c man title gccgo A GCC-based compiler for the Go language + +@ignore +@c man begin SYNOPSIS gccgo +gccgo [@option{-c}|@option{-S}] + [@option{-g}] [@option{-pg}] [@option{-O}@var{level}] + [@option{-I}@var{dir}@dots{}] [@option{-L}@var{dir}@dots{}] + [@option{-o} @var{outfile}] @var{infile}@dots{} + +Only the most useful options are listed here; see below for the +remainder. +@c man end +@c man begin SEEALSO +gpl(7), gfdl(7), fsf-funding(7), gcc(1) +and the Info entries for @file{gccgo} and @file{gcc}. +@c man end +@end ignore + +@c man begin DESCRIPTION gccgo + +The @command{gccgo} command is a frontend to @command{gcc} and +supports many of the same options. @xref{Option Summary, , Option +Summary, gcc, Using the GNU Compiler Collection (GCC)}. This manual +only documents the options specific to @command{gccgo}. + +The @command{gccgo} command may be used to compile Go source code into +an object file, link a collection of object files together, or do both +in sequence. + +Go source code is compiled as packages. A package consists of one or +more Go source files. All the files in a single package must be +compiled together, by passing all the files as arguments to +@command{gccgo}. A single invocation of @command{gccgo} may only +compile a single package. + +One Go package may @code{import} a different Go package. The imported +package must have already been compiled; @command{gccgo} will read +the import data directly from the compiled package. When this package +is later linked, the compiled form of the package must be included in +the link command. + +@c man end + +@c man begin OPTIONS gccgo + +@table @gcctabopt +@item -I@var{dir} +@cindex @option{-I} +Specify a directory to use when searching for an import package at +compile time. + +@item -L@var{dir} +@cindex @option{-L} +When linking, specify a library search directory, as with +@command{gcc}. + +@item -fgo-pkgpath=@var{string} +@cindex @option{-fgo-pkgpath} +Set the package path to use. This sets the value returned by the +PkgPath method of reflect.Type objects. It is also used for the names +of globally visible symbols. The argument to this option should +normally be the string that will be used to import this package after +it has been installed; in other words, a pathname within the +directories specified by the @option{-I} option. + +@item -fgo-prefix=@var{string} +@cindex @option{-fgo-prefix} +An alternative to @option{-fgo-pkgpath}. The argument will be +combined with the package name from the source file to produce the +package path. If @option{-fgo-pkgpath} is used, @option{-fgo-prefix} +will be ignored. + +Go permits a single program to include more than one package with the +same name in the @code{package} clause in the source file, though +obviously the two packages must be imported using different pathnames. +In order for this to work with @command{gccgo}, either +@option{-fgo-pkgpath} or @option{-fgo-prefix} must be specified when +compiling a package. + +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} +@cindex @option{-fno-require-return-statement} +By default @command{gccgo} will warn about functions which have one or +more return parameters but lack an explicit @code{return} statement. +This warning may be disabled using +@option{-fno-require-return-statement}. + +@item -fgo-check-divide-zero +@cindex @option{-fgo-check-divide-zero} +@cindex @option{-fno-go-check-divide-zero} +Add explicit checks for division by zero. In Go a division (or +modulos) by zero causes a panic. On Unix systems this is detected in +the runtime by catching the @code{SIGFPE} signal. Some processors, +such as PowerPC, do not generate a SIGFPE on division by zero. Some +runtimes do not generate a signal that can be caught. On those +systems, this option may be used. Or the checks may be removed via +@option{-fno-go-check-divide-zero}. This option is currently on by +default, but in the future may be off by default on systems that do +not require it. + +@item -fgo-check-divide-overflow +@cindex @option{-fgo-check-divide-overflow} +@cindex @option{-fno-go-check-divide-overflow} +Add explicit checks for division overflow. For example, division +overflow occurs when computing @code{INT_MIN / -1}. In Go this should +be wrapped, to produce @code{INT_MIN}. Some processors, such as x86, +generate a trap on division overflow. On those systems, this option +may be used. Or the checks may be removed via +@option{-fno-go-check-divide-overflow}. This option is currently on +by default, but in the future may be off by default on systems that do +not require it. +@end table + +@c man end + +@node Import and Export +@chapter Import and Export + +When @command{gccgo} compiles a package which exports anything, the +export information will be stored directly in the object file. When a +package is imported, @command{gccgo} must be able to find the file. + +@cindex @file{.gox} +When Go code imports the package @file{@var{gopackage}}, @command{gccgo} +will look for the import data using the following filenames, using the +first one that it finds. + +@table @file +@item @var{gopackage}.gox +@item lib@var{gopackage}.so +@item lib@var{gopackage}.a +@item @var{gopackage}.o +@end table + +The compiler will search for these files in the directories named by +any @option{-I} options, in order in which the directories appear on +the command line. The compiler will then search several standard +system directories. Finally the compiler will search the current +directory (to search the current directory earlier, use @samp{-I.}). + +The compiler will extract the export information directly from the +compiled object file. The file @file{@var{gopackage}.gox} will +typically contain nothing but export data. This can be generated from +@file{@var{gopackage}.o} via + +@smallexample +objcopy -j .go_export @var{gopackage}.o @var{gopackage}.gox +@end smallexample + +For example, it may be desirable to extract the export information +from several different packages into their independent +@file{@var{gopackage}.gox} files, and then to combine the different +package object files together into a single shared library or archive. + +At link time you must explicitly tell @command{gccgo} which files to +link together into the executable, as is usual with @command{gcc}. +This is different from the behaviour of other Go compilers. + +@node C Interoperability +@chapter C Interoperability + +When using @command{gccgo} there is limited interoperability with C, +or with C++ code compiled using @code{extern "C"}. + +@menu +* C Type Interoperability:: How C and Go types match up. +* Function Names:: How Go functions are named. +@end menu + +@node C Type Interoperability +@section C Type Interoperability + +Basic types map directly: an @code{int} in Go is an @code{int} in C, +etc. Go @code{byte} is equivalent to C @code{unsigned char}. +Pointers in Go are pointers in C. A Go @code{struct} is the same as C +@code{struct} with the same field names and types. + +@cindex @code{string} in C +The Go @code{string} type is currently defined as a two-element +structure: + +@smallexample +struct __go_string @{ + const unsigned char *__data; + int __length; +@}; +@end smallexample + +You can't pass arrays between C and Go. However, a pointer to an +array in Go is equivalent to a C pointer to the equivalent of the +element type. For example, Go @code{*[10]int} is equivalent to C +@code{int*}, assuming that the C pointer does point to 10 elements. + +@cindex @code{slice} in C +A slice in Go is a structure. The current definition is: + +@smallexample +struct __go_slice @{ + void *__values; + int __count; + int __capacity; +@}; +@end smallexample + +The type of a Go function with no receiver is equivalent to a C +function whose parameter types are equivalent. When a Go function +returns more than one value, the C function returns a struct. For +example, these functions have equivalent types: + +@smallexample +func GoFunction(int) (int, float) +struct @{ int i; float f; @} CFunction(int) +@end smallexample + +A pointer to a Go function is equivalent to a pointer to a C function +when the functions have equivalent types. + +Go @code{interface}, @code{channel}, and @code{map} types have no +corresponding C type (@code{interface} is a two-element struct and +@code{channel} and @code{map} are pointers to structs in C, but the +structs are deliberately undocumented). C @code{enum} types +correspond to some integer type, but precisely which one is difficult +to predict in general; use a cast. C @code{union} types have no +corresponding Go type. C @code{struct} types containing bitfields +have no corresponding Go type. C++ @code{class} types have no +corresponding Go type. + +Memory allocation is completely different between C and Go, as Go uses +garbage collection. The exact guidelines in this area are +undetermined, but it is likely that it will be permitted to pass a +pointer to allocated memory from C to Go. The responsibility of +eventually freeing the pointer will remain with C side, and of course +if the C side frees the pointer while the Go side still has a copy the +program will fail. When passing a pointer from Go to C, the Go +function must retain a visible copy of it in some Go variable. +Otherwise the Go garbage collector may delete the pointer while the C +function is still using it. + +@node Function Names +@section Function Names + +@cindex @code{extern} +@cindex external names +Go code can call C functions directly using a Go extension implemented +in @command{gccgo}: a function declaration may be preceded by a +comment giving the external name. The comment must be at the +beginning of the line and must start with @code{//extern}. This must +be followed by a space and then the external name of the function. +The function declaration must be on the line immediately after the +comment. For example, here is how the C function @code{open} can be +declared in Go: + +@smallexample +//extern open +func c_open(name *byte, mode int, perm int) int +@end smallexample + +The C function naturally expects a nul terminated string, which in Go +is equivalent to a pointer to an array (not a slice!) of @code{byte} +with a terminating zero byte. So a sample call from Go would look +like (after importing the @code{os} package): + +@smallexample +var name = [4]byte@{'f', 'o', 'o', 0@}; +i := c_open(&name[0], os.O_RDONLY, 0); +@end smallexample + +Note that this serves as an example only. To open a file in Go please +use Go's @code{os.Open} function instead. + +The name of Go functions accessed from C is subject to change. At +present the name of a Go function that does not have a receiver is +@code{prefix.package.Functionname}. The prefix is set by the +@option{-fgo-prefix} option used when the package is compiled; if the +option is not used, the default is simply @code{go}. To call the +function from C you must set the name using the @command{gcc} +@code{__asm__} extension. + +@smallexample +extern int go_function(int) __asm__ ("myprefix.mypackage.Function"); +@end smallexample + +@node Index +@unnumbered Index + +@printindex cp + +@bye diff --git a/gcc-4.9/gcc/go/go-backend.c b/gcc-4.9/gcc/go/go-backend.c new file mode 100644 index 000000000..de33601db --- /dev/null +++ b/gcc-4.9/gcc/go/go-backend.c @@ -0,0 +1,192 @@ +/* go-backend.c -- Go frontend interface to gcc backend. + Copyright (C) 2010-2014 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "simple-object.h" +#include "tm.h" +#include "tree.h" +#include "stor-layout.h" +#include "tm_p.h" +#include "intl.h" +#include "output.h" /* for assemble_string */ +#include "target.h" +#include "common/common-target.h" + +#include "go-c.h" + +/* The segment name we pass to simple_object_start_read to find Go + export data. */ + +#ifndef GO_EXPORT_SEGMENT_NAME +#define GO_EXPORT_SEGMENT_NAME "__GNU_GO" +#endif + +/* The section name we use when reading and writing export data. */ + +#ifndef GO_EXPORT_SECTION_NAME +#define GO_EXPORT_SECTION_NAME ".go_export" +#endif + +/* This file holds all the cases where the Go frontend needs + information from gcc's backend. */ + +/* Return the alignment in bytes of a struct field of type T. */ + +unsigned int +go_field_alignment (tree t) +{ + unsigned int v; + + v = TYPE_ALIGN (t); + +#ifdef BIGGEST_FIELD_ALIGNMENT + if (v > BIGGEST_FIELD_ALIGNMENT) + v = BIGGEST_FIELD_ALIGNMENT; +#endif + +#ifdef ADJUST_FIELD_ALIGN + { + tree field ATTRIBUTE_UNUSED; + field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL, t); + v = ADJUST_FIELD_ALIGN (field, v); + } +#endif + + return v / BITS_PER_UNIT; +} + +/* Return the size and alignment of a trampoline. */ + +void +go_trampoline_info (unsigned int *size, unsigned int *alignment) +{ + *size = TRAMPOLINE_SIZE; + *alignment = TRAMPOLINE_ALIGNMENT; +} + +/* This is called by the Go frontend proper if the unsafe package was + imported. When that happens we can not do type-based alias + analysis. */ + +void +go_imported_unsafe (void) +{ + flag_strict_aliasing = false; + + /* Let the backend know that the options have changed. */ + targetm.override_options_after_change (); +} + +/* This is called by the Go frontend proper to add data to the + section containing Go export data. */ + +void +go_write_export_data (const char *bytes, unsigned int size) +{ + static section* sec; + + if (sec == NULL) + { + gcc_assert (targetm_common.have_named_sections); + sec = get_section (GO_EXPORT_SECTION_NAME, SECTION_DEBUG, NULL); + } + + switch_to_section (sec); + assemble_string (bytes, size); +} + +/* The go_read_export_data function is called by the Go frontend + proper to read Go export data from an object file. FD is a file + descriptor open for reading. OFFSET is the offset within the file + where the object file starts; this will be 0 except when reading an + archive. On success this returns NULL and sets *PBUF to a buffer + allocated using malloc, of size *PLEN, holding the export data. If + the data is not found, this returns NULL and sets *PBUF to NULL and + *PLEN to 0. If some error occurs, this returns an error message + and sets *PERR to an errno value or 0 if there is no relevant + errno. */ + +const char * +go_read_export_data (int fd, off_t offset, char **pbuf, size_t *plen, + int *perr) +{ + simple_object_read *sobj; + const char *errmsg; + off_t sec_offset; + off_t sec_length; + int found; + char *buf; + ssize_t c; + + *pbuf = NULL; + *plen = 0; + + sobj = simple_object_start_read (fd, offset, GO_EXPORT_SEGMENT_NAME, + &errmsg, perr); + if (sobj == NULL) + { + /* If we get an error here, just pretend that we didn't find any + export data. This is the right thing to do if the error is + that the file was not recognized as an object file. This + will ignore file I/O errors, but it's not too big a deal + because we will wind up giving some other error later. */ + return NULL; + } + + found = simple_object_find_section (sobj, GO_EXPORT_SECTION_NAME, + &sec_offset, &sec_length, + &errmsg, perr); + simple_object_release_read (sobj); + if (!found) + return errmsg; + + if (lseek (fd, offset + sec_offset, SEEK_SET) < 0) + { + *perr = errno; + return _("lseek failed while reading export data"); + } + + buf = XNEWVEC (char, sec_length); + if (buf == NULL) + { + *perr = errno; + return _("memory allocation failed while reading export data"); + } + + c = read (fd, buf, sec_length); + if (c < 0) + { + *perr = errno; + free (buf); + return _("read failed while reading export data"); + } + + if (c < sec_length) + { + free (buf); + return _("short read while reading export data"); + } + + *pbuf = buf; + *plen = sec_length; + + return NULL; +} diff --git a/gcc-4.9/gcc/go/go-c.h b/gcc-4.9/gcc/go/go-c.h new file mode 100644 index 000000000..cf0fbfb0a --- /dev/null +++ b/gcc-4.9/gcc/go/go-c.h @@ -0,0 +1,66 @@ +/* go-c.h -- Header file for go frontend gcc C interface. + Copyright (C) 2009-2014 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GO_GO_C_H +#define GO_GO_C_H + +#define GO_EXTERN_C + +#include "machmode.h" + +/* Functions defined in the Go frontend proper called by the GCC + interface. */ + +extern int go_enable_dump (const char*); +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 *relative_import_path); + +extern void go_parse_input_files (const char**, unsigned int, + bool only_check_syntax, + bool require_return_statement); +extern void go_write_globals (void); + +extern tree go_type_for_size (unsigned int bits, int unsignedp); +extern tree go_type_for_mode (enum machine_mode, int unsignedp); + +/* Functions defined in the GCC interface called by the Go frontend + proper. */ + +extern void go_preserve_from_gc (tree); + +extern const char *go_localize_identifier (const char*); + +extern unsigned int go_field_alignment (tree); + +extern void go_trampoline_info (unsigned int *size, unsigned int *alignment); + +extern void go_imported_unsafe (void); + +extern void go_write_export_data (const char *, unsigned int); + +extern const char *go_read_export_data (int, off_t, char **, size_t *, int *); + +extern GTY(()) tree go_non_zero_struct; + +#endif /* !defined(GO_GO_C_H) */ diff --git a/gcc-4.9/gcc/go/go-gcc.cc b/gcc-4.9/gcc/go/go-gcc.cc new file mode 100644 index 000000000..6aec2877d --- /dev/null +++ b/gcc-4.9/gcc/go/go-gcc.cc @@ -0,0 +1,2153 @@ +// go-gcc.cc -- Go frontend to gcc IR. +// Copyright (C) 2011-2014 Free Software Foundation, Inc. +// Contributed by Ian Lance Taylor, Google. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "go-system.h" + +// This has to be included outside of extern "C", so we have to +// include it here before tree.h includes it later. +#include + +#include "tree.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "varasm.h" +#include "tree-iterator.h" +#include "basic-block.h" +#include "gimple-expr.h" +#include "toplev.h" +#include "output.h" +#include "real.h" +#include "realmpfr.h" + +#include "go-c.h" + +#include "gogo.h" +#include "backend.h" + +// A class wrapping a tree. + +class Gcc_tree +{ + public: + Gcc_tree(tree t) + : t_(t) + { } + + tree + get_tree() const + { return this->t_; } + + void + set_tree(tree t) + { this->t_ = t; } + + private: + tree t_; +}; + +// In gcc, types, expressions, and statements are all trees. +class Btype : public Gcc_tree +{ + public: + Btype(tree t) + : Gcc_tree(t) + { } +}; + +class Bexpression : public Gcc_tree +{ + public: + Bexpression(tree t) + : Gcc_tree(t) + { } +}; + +class Bstatement : public Gcc_tree +{ + public: + Bstatement(tree t) + : Gcc_tree(t) + { } +}; + +class Bfunction : public Gcc_tree +{ + public: + Bfunction(tree t) + : Gcc_tree(t) + { } +}; + +class Bblock : public Gcc_tree +{ + public: + Bblock(tree t) + : Gcc_tree(t) + { } +}; + +class Bvariable : public Gcc_tree +{ + public: + Bvariable(tree t) + : Gcc_tree(t) + { } +}; + +class Blabel : public Gcc_tree +{ + public: + Blabel(tree t) + : Gcc_tree(t) + { } +}; + +// This file implements the interface between the Go frontend proper +// and the gcc IR. This implements specific instantiations of +// abstract classes defined by the Go frontend proper. The Go +// frontend proper class methods of these classes to generate the +// backend representation. + +class Gcc_backend : public Backend +{ + public: + // Types. + + Btype* + error_type() + { return this->make_type(error_mark_node); } + + Btype* + void_type() + { return this->make_type(void_type_node); } + + Btype* + bool_type() + { return this->make_type(boolean_type_node); } + + Btype* + integer_type(bool, int); + + Btype* + float_type(int); + + Btype* + complex_type(int); + + Btype* + pointer_type(Btype*); + + Btype* + function_type(const Btyped_identifier&, + const std::vector&, + const std::vector&, + Btype*, + const Location); + + Btype* + struct_type(const std::vector&); + + Btype* + array_type(Btype*, Bexpression*); + + Btype* + placeholder_pointer_type(const std::string&, Location, bool); + + bool + set_placeholder_pointer_type(Btype*, Btype*); + + bool + set_placeholder_function_type(Btype*, Btype*); + + Btype* + placeholder_struct_type(const std::string&, Location); + + bool + set_placeholder_struct_type(Btype* placeholder, + const std::vector&); + + Btype* + placeholder_array_type(const std::string&, Location); + + bool + set_placeholder_array_type(Btype*, Btype*, Bexpression*); + + Btype* + named_type(const std::string&, Btype*, Location); + + Btype* + circular_pointer_type(Btype*, bool); + + bool + is_circular_pointer_type(Btype*); + + size_t + type_size(Btype*); + + size_t + type_alignment(Btype*); + + size_t + type_field_alignment(Btype*); + + size_t + type_field_offset(Btype*, size_t index); + + // Expressions. + + Bexpression* + zero_expression(Btype*); + + Bexpression* + error_expression() + { return this->make_expression(error_mark_node); } + + Bexpression* + var_expression(Bvariable* var, Location); + + Bexpression* + indirect_expression(Bexpression* expr, bool known_valid, Location); + + Bexpression* + integer_constant_expression(Btype* btype, mpz_t val); + + Bexpression* + float_constant_expression(Btype* btype, mpfr_t val); + + Bexpression* + complex_constant_expression(Btype* btype, mpfr_t real, mpfr_t imag); + + Bexpression* + convert_expression(Btype* type, Bexpression* expr, Location); + + Bexpression* + function_code_expression(Bfunction*, Location); + + Bexpression* + address_expression(Bexpression*, Location); + + Bexpression* + struct_field_expression(Bexpression*, size_t, Location); + + Bexpression* + compound_expression(Bstatement*, Bexpression*, Location); + + Bexpression* + conditional_expression(Btype*, Bexpression*, Bexpression*, Bexpression*, + Location); + + Bexpression* + unary_expression(Operator, Bexpression*, Location); + + Bexpression* + binary_expression(Operator, Bexpression*, Bexpression*, Location); + + // Statements. + + Bstatement* + error_statement() + { return this->make_statement(error_mark_node); } + + Bstatement* + expression_statement(Bexpression*); + + Bstatement* + init_statement(Bvariable* var, Bexpression* init); + + Bstatement* + assignment_statement(Bexpression* lhs, Bexpression* rhs, Location); + + Bstatement* + return_statement(Bfunction*, const std::vector&, + Location); + + Bstatement* + if_statement(Bexpression* condition, Bblock* then_block, Bblock* else_block, + Location); + + Bstatement* + switch_statement(Bexpression* value, + const std::vector >& cases, + const std::vector& statements, + Location); + + Bstatement* + compound_statement(Bstatement*, Bstatement*); + + Bstatement* + statement_list(const std::vector&); + + // Blocks. + + Bblock* + block(Bfunction*, Bblock*, const std::vector&, + Location, Location); + + void + block_add_statements(Bblock*, const std::vector&); + + Bstatement* + block_statement(Bblock*); + + // Variables. + + Bvariable* + error_variable() + { return new Bvariable(error_mark_node); } + + Bvariable* + global_variable(const std::string& package_name, + const std::string& pkgpath, + const std::string& name, + Btype* btype, + bool is_external, + bool is_hidden, + bool in_unique_section, + Location location); + + void + global_variable_set_init(Bvariable*, Bexpression*); + + Bvariable* + local_variable(Bfunction*, const std::string&, Btype*, bool, + Location); + + Bvariable* + parameter_variable(Bfunction*, const std::string&, Btype*, bool, + Location); + + Bvariable* + temporary_variable(Bfunction*, Bblock*, Btype*, Bexpression*, bool, + Location, Bstatement**); + + Bvariable* + immutable_struct(const std::string&, bool, bool, Btype*, Location); + + void + immutable_struct_set_init(Bvariable*, const std::string&, bool, bool, Btype*, + Location, Bexpression*); + + Bvariable* + immutable_struct_reference(const std::string&, Btype*, Location); + + // Labels. + + Blabel* + label(Bfunction*, const std::string& name, Location); + + Bstatement* + label_definition_statement(Blabel*); + + Bstatement* + goto_statement(Blabel*, Location); + + Bexpression* + label_address(Blabel*, Location); + + // Functions. + + Bfunction* + error_function() + { return this->make_function(error_mark_node); } + + Bfunction* + function(Btype* fntype, const std::string& name, const std::string& asm_name, + bool is_visible, bool is_declaration, bool is_inlinable, + bool disable_split_stack, bool in_unique_section, Location); + + private: + // Make a Bexpression from a tree. + Bexpression* + make_expression(tree t) + { return new Bexpression(t); } + + // Make a Bstatement from a tree. + Bstatement* + make_statement(tree t) + { return new Bstatement(t); } + + // Make a Btype from a tree. + Btype* + make_type(tree t) + { return new Btype(t); } + + Bfunction* + make_function(tree t) + { return new Bfunction(t); } + + Btype* + fill_in_struct(Btype*, const std::vector&); + + Btype* + fill_in_array(Btype*, Btype*, Bexpression*); + + tree + non_zero_size_type(tree); +}; + +// A helper function. + +static inline tree +get_identifier_from_string(const std::string& str) +{ + return get_identifier_with_length(str.data(), str.length()); +} + +// Get an unnamed integer type. + +Btype* +Gcc_backend::integer_type(bool is_unsigned, int bits) +{ + tree type; + if (is_unsigned) + { + if (bits == INT_TYPE_SIZE) + type = unsigned_type_node; + else if (bits == CHAR_TYPE_SIZE) + type = unsigned_char_type_node; + else if (bits == SHORT_TYPE_SIZE) + type = short_unsigned_type_node; + else if (bits == LONG_TYPE_SIZE) + type = long_unsigned_type_node; + else if (bits == LONG_LONG_TYPE_SIZE) + type = long_long_unsigned_type_node; + else + type = make_unsigned_type(bits); + } + else + { + if (bits == INT_TYPE_SIZE) + type = integer_type_node; + else if (bits == CHAR_TYPE_SIZE) + type = signed_char_type_node; + else if (bits == SHORT_TYPE_SIZE) + type = short_integer_type_node; + else if (bits == LONG_TYPE_SIZE) + type = long_integer_type_node; + else if (bits == LONG_LONG_TYPE_SIZE) + type = long_long_integer_type_node; + else + type = make_signed_type(bits); + } + return this->make_type(type); +} + +// Get an unnamed float type. + +Btype* +Gcc_backend::float_type(int bits) +{ + tree type; + if (bits == FLOAT_TYPE_SIZE) + type = float_type_node; + else if (bits == DOUBLE_TYPE_SIZE) + type = double_type_node; + else if (bits == LONG_DOUBLE_TYPE_SIZE) + type = long_double_type_node; + else + { + type = make_node(REAL_TYPE); + TYPE_PRECISION(type) = bits; + layout_type(type); + } + return this->make_type(type); +} + +// Get an unnamed complex type. + +Btype* +Gcc_backend::complex_type(int bits) +{ + tree type; + if (bits == FLOAT_TYPE_SIZE * 2) + type = complex_float_type_node; + else if (bits == DOUBLE_TYPE_SIZE * 2) + type = complex_double_type_node; + else if (bits == LONG_DOUBLE_TYPE_SIZE * 2) + type = complex_long_double_type_node; + else + { + type = make_node(REAL_TYPE); + TYPE_PRECISION(type) = bits / 2; + layout_type(type); + type = build_complex_type(type); + } + return this->make_type(type); +} + +// Get a pointer type. + +Btype* +Gcc_backend::pointer_type(Btype* to_type) +{ + tree to_type_tree = to_type->get_tree(); + if (to_type_tree == error_mark_node) + return this->error_type(); + tree type = build_pointer_type(to_type_tree); + return this->make_type(type); +} + +// Make a function type. + +Btype* +Gcc_backend::function_type(const Btyped_identifier& receiver, + const std::vector& parameters, + const std::vector& results, + Btype* result_struct, + Location) +{ + tree args = NULL_TREE; + tree* pp = &args; + if (receiver.btype != NULL) + { + tree t = receiver.btype->get_tree(); + if (t == error_mark_node) + return this->error_type(); + *pp = tree_cons(NULL_TREE, t, NULL_TREE); + pp = &TREE_CHAIN(*pp); + } + + for (std::vector::const_iterator p = parameters.begin(); + p != parameters.end(); + ++p) + { + tree t = p->btype->get_tree(); + if (t == error_mark_node) + return this->error_type(); + *pp = tree_cons(NULL_TREE, t, NULL_TREE); + pp = &TREE_CHAIN(*pp); + } + + // Varargs is handled entirely at the Go level. When converted to + // GENERIC functions are not varargs. + *pp = void_list_node; + + tree result; + if (results.empty()) + result = void_type_node; + else if (results.size() == 1) + result = results.front().btype->get_tree(); + else + { + gcc_assert(result_struct != NULL); + result = result_struct->get_tree(); + } + if (result == error_mark_node) + return this->error_type(); + + tree fntype = build_function_type(result, args); + if (fntype == error_mark_node) + return this->error_type(); + + return this->make_type(build_pointer_type(fntype)); +} + +// Make a struct type. + +Btype* +Gcc_backend::struct_type(const std::vector& fields) +{ + return this->fill_in_struct(this->make_type(make_node(RECORD_TYPE)), fields); +} + +// Fill in the fields of a struct type. + +Btype* +Gcc_backend::fill_in_struct(Btype* fill, + const std::vector& fields) +{ + tree fill_tree = fill->get_tree(); + tree field_trees = NULL_TREE; + tree* pp = &field_trees; + for (std::vector::const_iterator p = fields.begin(); + p != fields.end(); + ++p) + { + tree name_tree = get_identifier_from_string(p->name); + tree type_tree = p->btype->get_tree(); + if (type_tree == error_mark_node) + return this->error_type(); + tree field = build_decl(p->location.gcc_location(), FIELD_DECL, name_tree, + type_tree); + DECL_CONTEXT(field) = fill_tree; + *pp = field; + pp = &DECL_CHAIN(field); + } + TYPE_FIELDS(fill_tree) = field_trees; + layout_type(fill_tree); + return fill; +} + +// Make an array type. + +Btype* +Gcc_backend::array_type(Btype* element_btype, Bexpression* length) +{ + return this->fill_in_array(this->make_type(make_node(ARRAY_TYPE)), + element_btype, length); +} + +// Fill in an array type. + +Btype* +Gcc_backend::fill_in_array(Btype* fill, Btype* element_type, + Bexpression* length) +{ + tree element_type_tree = element_type->get_tree(); + tree length_tree = length->get_tree(); + if (element_type_tree == error_mark_node || length_tree == error_mark_node) + return this->error_type(); + + gcc_assert(TYPE_SIZE(element_type_tree) != NULL_TREE); + + length_tree = fold_convert(sizetype, length_tree); + + // build_index_type takes the maximum index, which is one less than + // the length. + tree index_type_tree = build_index_type(fold_build2(MINUS_EXPR, sizetype, + length_tree, + size_one_node)); + + tree fill_tree = fill->get_tree(); + TREE_TYPE(fill_tree) = element_type_tree; + TYPE_DOMAIN(fill_tree) = index_type_tree; + TYPE_ADDR_SPACE(fill_tree) = TYPE_ADDR_SPACE(element_type_tree); + layout_type(fill_tree); + + if (TYPE_STRUCTURAL_EQUALITY_P(element_type_tree)) + SET_TYPE_STRUCTURAL_EQUALITY(fill_tree); + else if (TYPE_CANONICAL(element_type_tree) != element_type_tree + || TYPE_CANONICAL(index_type_tree) != index_type_tree) + TYPE_CANONICAL(fill_tree) = + build_array_type(TYPE_CANONICAL(element_type_tree), + TYPE_CANONICAL(index_type_tree)); + + return fill; +} + +// Create a placeholder for a pointer type. + +Btype* +Gcc_backend::placeholder_pointer_type(const std::string& name, + Location location, bool) +{ + tree ret = build_distinct_type_copy(ptr_type_node); + if (!name.empty()) + { + tree decl = build_decl(location.gcc_location(), TYPE_DECL, + get_identifier_from_string(name), + ret); + TYPE_NAME(ret) = decl; + } + return this->make_type(ret); +} + +// Set the real target type for a placeholder pointer type. + +bool +Gcc_backend::set_placeholder_pointer_type(Btype* placeholder, + Btype* to_type) +{ + tree pt = placeholder->get_tree(); + if (pt == error_mark_node) + return false; + gcc_assert(TREE_CODE(pt) == POINTER_TYPE); + tree tt = to_type->get_tree(); + if (tt == error_mark_node) + { + placeholder->set_tree(error_mark_node); + return false; + } + gcc_assert(TREE_CODE(tt) == POINTER_TYPE); + TREE_TYPE(pt) = TREE_TYPE(tt); + if (TYPE_NAME(pt) != NULL_TREE) + { + // Build the data structure gcc wants to see for a typedef. + tree copy = build_variant_type_copy(pt); + TYPE_NAME(copy) = NULL_TREE; + DECL_ORIGINAL_TYPE(TYPE_NAME(pt)) = copy; + } + return true; +} + +// Set the real values for a placeholder function type. + +bool +Gcc_backend::set_placeholder_function_type(Btype* placeholder, Btype* ft) +{ + return this->set_placeholder_pointer_type(placeholder, ft); +} + +// Create a placeholder for a struct type. + +Btype* +Gcc_backend::placeholder_struct_type(const std::string& name, + Location location) +{ + tree ret = make_node(RECORD_TYPE); + if (!name.empty()) + { + tree decl = build_decl(location.gcc_location(), TYPE_DECL, + get_identifier_from_string(name), + ret); + TYPE_NAME(ret) = decl; + } + return this->make_type(ret); +} + +// Fill in the fields of a placeholder struct type. + +bool +Gcc_backend::set_placeholder_struct_type( + Btype* placeholder, + const std::vector& fields) +{ + tree t = placeholder->get_tree(); + gcc_assert(TREE_CODE(t) == RECORD_TYPE && TYPE_FIELDS(t) == NULL_TREE); + Btype* r = this->fill_in_struct(placeholder, fields); + + if (TYPE_NAME(t) != NULL_TREE) + { + // Build the data structure gcc wants to see for a typedef. + tree copy = build_distinct_type_copy(t); + TYPE_NAME(copy) = NULL_TREE; + DECL_ORIGINAL_TYPE(TYPE_NAME(t)) = copy; + } + + return r->get_tree() != error_mark_node; +} + +// Create a placeholder for an array type. + +Btype* +Gcc_backend::placeholder_array_type(const std::string& name, + Location location) +{ + tree ret = make_node(ARRAY_TYPE); + tree decl = build_decl(location.gcc_location(), TYPE_DECL, + get_identifier_from_string(name), + ret); + TYPE_NAME(ret) = decl; + return this->make_type(ret); +} + +// Fill in the fields of a placeholder array type. + +bool +Gcc_backend::set_placeholder_array_type(Btype* placeholder, + Btype* element_btype, + Bexpression* length) +{ + tree t = placeholder->get_tree(); + gcc_assert(TREE_CODE(t) == ARRAY_TYPE && TREE_TYPE(t) == NULL_TREE); + Btype* r = this->fill_in_array(placeholder, element_btype, length); + + // Build the data structure gcc wants to see for a typedef. + tree copy = build_distinct_type_copy(t); + TYPE_NAME(copy) = NULL_TREE; + DECL_ORIGINAL_TYPE(TYPE_NAME(t)) = copy; + + return r->get_tree() != error_mark_node; +} + +// Return a named version of a type. + +Btype* +Gcc_backend::named_type(const std::string& name, Btype* btype, + Location location) +{ + tree type = btype->get_tree(); + if (type == error_mark_node) + return this->error_type(); + + // The middle-end expects a basic type to have a name. In Go every + // basic type will have a name. The first time we see a basic type, + // give it whatever Go name we have at this point. + if (TYPE_NAME(type) == NULL_TREE + && location.gcc_location() == BUILTINS_LOCATION + && (TREE_CODE(type) == INTEGER_TYPE + || TREE_CODE(type) == REAL_TYPE + || TREE_CODE(type) == COMPLEX_TYPE + || TREE_CODE(type) == BOOLEAN_TYPE)) + { + tree decl = build_decl(BUILTINS_LOCATION, TYPE_DECL, + get_identifier_from_string(name), + type); + TYPE_NAME(type) = decl; + return this->make_type(type); + } + + tree copy = build_variant_type_copy(type); + tree decl = build_decl(location.gcc_location(), TYPE_DECL, + get_identifier_from_string(name), + copy); + DECL_ORIGINAL_TYPE(decl) = type; + TYPE_NAME(copy) = decl; + return this->make_type(copy); +} + +// Return a pointer type used as a marker for a circular type. + +Btype* +Gcc_backend::circular_pointer_type(Btype*, bool) +{ + return this->make_type(ptr_type_node); +} + +// Return whether we might be looking at a circular type. + +bool +Gcc_backend::is_circular_pointer_type(Btype* btype) +{ + return btype->get_tree() == ptr_type_node; +} + +// Return the size of a type. + +size_t +Gcc_backend::type_size(Btype* btype) +{ + tree t = btype->get_tree(); + if (t == error_mark_node) + return 1; + t = TYPE_SIZE_UNIT(t); + gcc_assert(TREE_CODE(t) == INTEGER_CST); + gcc_assert(TREE_INT_CST_HIGH(t) == 0); + unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW(t); + size_t ret = static_cast(val_wide); + gcc_assert(ret == val_wide); + return ret; +} + +// Return the alignment of a type. + +size_t +Gcc_backend::type_alignment(Btype* btype) +{ + tree t = btype->get_tree(); + if (t == error_mark_node) + return 1; + return TYPE_ALIGN_UNIT(t); +} + +// Return the alignment of a struct field of type BTYPE. + +size_t +Gcc_backend::type_field_alignment(Btype* btype) +{ + tree t = btype->get_tree(); + if (t == error_mark_node) + return 1; + return go_field_alignment(t); +} + +// Return the offset of a field in a struct. + +size_t +Gcc_backend::type_field_offset(Btype* btype, size_t index) +{ + tree struct_tree = btype->get_tree(); + if (struct_tree == error_mark_node) + return 0; + gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE); + tree field = TYPE_FIELDS(struct_tree); + for (; index > 0; --index) + { + field = DECL_CHAIN(field); + gcc_assert(field != NULL_TREE); + } + HOST_WIDE_INT offset_wide = int_byte_position(field); + gcc_assert(offset_wide >= 0); + size_t ret = static_cast(offset_wide); + gcc_assert(ret == static_cast(offset_wide)); + return ret; +} + +// Return the zero value for a type. + +Bexpression* +Gcc_backend::zero_expression(Btype* btype) +{ + tree t = btype->get_tree(); + tree ret; + if (t == error_mark_node) + ret = error_mark_node; + else + ret = build_zero_cst(t); + return tree_to_expr(ret); +} + +// An expression that references a variable. + +Bexpression* +Gcc_backend::var_expression(Bvariable* var, Location) +{ + tree ret = var->get_tree(); + if (ret == error_mark_node) + return this->error_expression(); + return tree_to_expr(ret); +} + +// An expression that indirectly references an expression. + +Bexpression* +Gcc_backend::indirect_expression(Bexpression* expr, bool known_valid, + Location location) +{ + tree ret = build_fold_indirect_ref_loc(location.gcc_location(), + expr->get_tree()); + if (known_valid) + TREE_THIS_NOTRAP(ret) = 1; + return tree_to_expr(ret); +} + +// Return a typed value as a constant integer. + +Bexpression* +Gcc_backend::integer_constant_expression(Btype* btype, mpz_t val) +{ + tree t = btype->get_tree(); + if (t == error_mark_node) + return this->error_expression(); + + tree ret = double_int_to_tree(t, mpz_get_double_int(t, val, true)); + return tree_to_expr(ret); +} + +// Return a typed value as a constant floating-point number. + +Bexpression* +Gcc_backend::float_constant_expression(Btype* btype, mpfr_t val) +{ + tree t = btype->get_tree(); + tree ret; + if (t == error_mark_node) + return this->error_expression(); + + REAL_VALUE_TYPE r1; + real_from_mpfr(&r1, val, t, GMP_RNDN); + REAL_VALUE_TYPE r2; + real_convert(&r2, TYPE_MODE(t), &r1); + ret = build_real(t, r2); + return tree_to_expr(ret); +} + +// Return a typed real and imaginary value as a constant complex number. + +Bexpression* +Gcc_backend::complex_constant_expression(Btype* btype, mpfr_t real, mpfr_t imag) +{ + tree t = btype->get_tree(); + tree ret; + if (t == error_mark_node) + return this->error_expression(); + + REAL_VALUE_TYPE r1; + real_from_mpfr(&r1, real, TREE_TYPE(t), GMP_RNDN); + REAL_VALUE_TYPE r2; + real_convert(&r2, TYPE_MODE(TREE_TYPE(t)), &r1); + + REAL_VALUE_TYPE r3; + real_from_mpfr(&r3, imag, TREE_TYPE(t), GMP_RNDN); + REAL_VALUE_TYPE r4; + real_convert(&r4, TYPE_MODE(TREE_TYPE(t)), &r3); + + ret = build_complex(t, build_real(TREE_TYPE(t), r2), + build_real(TREE_TYPE(t), r4)); + return tree_to_expr(ret); +} + +// An expression that converts an expression to a different type. + +Bexpression* +Gcc_backend::convert_expression(Btype* type, Bexpression* expr, Location) +{ + tree type_tree = type->get_tree(); + tree expr_tree = expr->get_tree(); + if (type_tree == error_mark_node || expr_tree == error_mark_node) + return this->error_expression(); + + tree ret = fold_convert(type_tree, expr_tree); + return tree_to_expr(ret); +} + +// Get the address of a function. + +Bexpression* +Gcc_backend::function_code_expression(Bfunction* bfunc, Location location) +{ + tree func = bfunc->get_tree(); + if (func == error_mark_node) + return this->error_expression(); + + tree ret = build_fold_addr_expr_loc(location.gcc_location(), func); + return this->make_expression(ret); +} + +// Get the address of an expression. + +Bexpression* +Gcc_backend::address_expression(Bexpression* bexpr, Location location) +{ + tree expr = bexpr->get_tree(); + if (expr == error_mark_node) + return this->error_expression(); + + tree ret = build_fold_addr_expr_loc(location.gcc_location(), expr); + return this->make_expression(ret); +} + +// Return an expression for the field at INDEX in BSTRUCT. + +Bexpression* +Gcc_backend::struct_field_expression(Bexpression* bstruct, size_t index, + Location location) +{ + tree struct_tree = bstruct->get_tree(); + if (struct_tree == error_mark_node + || TREE_TYPE(struct_tree) == error_mark_node) + return this->error_expression(); + gcc_assert(TREE_CODE(TREE_TYPE(struct_tree)) == RECORD_TYPE); + tree field = TYPE_FIELDS(TREE_TYPE(struct_tree)); + if (field == NULL_TREE) + { + // This can happen for a type which refers to itself indirectly + // and then turns out to be erroneous. + return this->error_expression(); + } + for (unsigned int i = index; i > 0; --i) + { + field = DECL_CHAIN(field); + gcc_assert(field != NULL_TREE); + } + if (TREE_TYPE(field) == error_mark_node) + return this->error_expression(); + tree ret = fold_build3_loc(location.gcc_location(), COMPONENT_REF, + TREE_TYPE(field), struct_tree, field, + NULL_TREE); + if (TREE_CONSTANT(struct_tree)) + TREE_CONSTANT(ret) = 1; + return tree_to_expr(ret); +} + +// Return an expression that executes BSTAT before BEXPR. + +Bexpression* +Gcc_backend::compound_expression(Bstatement* bstat, Bexpression* bexpr, + Location location) +{ + tree stat = bstat->get_tree(); + tree expr = bexpr->get_tree(); + if (stat == error_mark_node || expr == error_mark_node) + return this->error_expression(); + tree ret = fold_build2_loc(location.gcc_location(), COMPOUND_EXPR, + TREE_TYPE(expr), stat, expr); + return this->make_expression(ret); +} + +// Return an expression that executes THEN_EXPR if CONDITION is true, or +// ELSE_EXPR otherwise. + +Bexpression* +Gcc_backend::conditional_expression(Btype* btype, Bexpression* condition, + Bexpression* then_expr, + Bexpression* else_expr, Location location) +{ + tree type_tree = btype == NULL ? void_type_node : btype->get_tree(); + tree cond_tree = condition->get_tree(); + tree then_tree = then_expr->get_tree(); + tree else_tree = else_expr == NULL ? NULL_TREE : else_expr->get_tree(); + if (type_tree == error_mark_node + || cond_tree == error_mark_node + || then_tree == error_mark_node + || else_tree == error_mark_node) + return this->error_expression(); + tree ret = build3_loc(location.gcc_location(), COND_EXPR, type_tree, + cond_tree, then_tree, else_tree); + return this->make_expression(ret); +} + +// Return an expression for the unary operation OP EXPR. + +Bexpression* +Gcc_backend::unary_expression(Operator op, Bexpression* expr, Location location) +{ + tree expr_tree = expr->get_tree(); + if (expr_tree == error_mark_node + || TREE_TYPE(expr_tree) == error_mark_node) + return this->error_expression(); + + tree type_tree = TREE_TYPE(expr_tree); + enum tree_code code; + switch (op) + { + case OPERATOR_MINUS: + { + tree computed_type = excess_precision_type(type_tree); + if (computed_type != NULL_TREE) + { + expr_tree = convert(computed_type, expr_tree); + type_tree = computed_type; + } + code = NEGATE_EXPR; + break; + } + case OPERATOR_NOT: + code = TRUTH_NOT_EXPR; + break; + case OPERATOR_XOR: + code = BIT_NOT_EXPR; + break; + default: + gcc_unreachable(); + break; + } + + tree ret = fold_build1_loc(location.gcc_location(), code, type_tree, + expr_tree); + return this->make_expression(ret); +} + +// Convert a gofrontend operator to an equivalent tree_code. + +static enum tree_code +operator_to_tree_code(Operator op, tree type) +{ + enum tree_code code; + switch (op) + { + case OPERATOR_EQEQ: + code = EQ_EXPR; + break; + case OPERATOR_NOTEQ: + code = NE_EXPR; + break; + case OPERATOR_LT: + code = LT_EXPR; + break; + case OPERATOR_LE: + code = LE_EXPR; + break; + case OPERATOR_GT: + code = GT_EXPR; + break; + case OPERATOR_GE: + code = GE_EXPR; + break; + case OPERATOR_OROR: + code = TRUTH_ORIF_EXPR; + break; + case OPERATOR_ANDAND: + code = TRUTH_ANDIF_EXPR; + break; + case OPERATOR_PLUS: + code = PLUS_EXPR; + break; + case OPERATOR_MINUS: + code = MINUS_EXPR; + break; + case OPERATOR_OR: + code = BIT_IOR_EXPR; + break; + case OPERATOR_XOR: + code = BIT_XOR_EXPR; + break; + case OPERATOR_MULT: + code = MULT_EXPR; + break; + case OPERATOR_DIV: + if (TREE_CODE(type) == REAL_TYPE || TREE_CODE(type) == COMPLEX_TYPE) + code = RDIV_EXPR; + else + code = TRUNC_DIV_EXPR; + break; + case OPERATOR_MOD: + code = TRUNC_MOD_EXPR; + break; + case OPERATOR_LSHIFT: + code = LSHIFT_EXPR; + break; + case OPERATOR_RSHIFT: + code = RSHIFT_EXPR; + break; + case OPERATOR_AND: + code = BIT_AND_EXPR; + break; + case OPERATOR_BITCLEAR: + code = BIT_AND_EXPR; + break; + default: + gcc_unreachable(); + } + + return code; +} + +// Return an expression for the binary operation LEFT OP RIGHT. + +Bexpression* +Gcc_backend::binary_expression(Operator op, Bexpression* left, + Bexpression* right, Location location) +{ + tree left_tree = left->get_tree(); + tree right_tree = right->get_tree(); + if (left_tree == error_mark_node + || right_tree == error_mark_node) + return this->error_expression(); + enum tree_code code = operator_to_tree_code(op, TREE_TYPE(left_tree)); + + bool use_left_type = op != OPERATOR_OROR && op != OPERATOR_ANDAND; + tree type_tree = use_left_type ? TREE_TYPE(left_tree) : TREE_TYPE(right_tree); + tree computed_type = excess_precision_type(type_tree); + if (computed_type != NULL_TREE) + { + left_tree = convert(computed_type, left_tree); + right_tree = convert(computed_type, right_tree); + type_tree = computed_type; + } + + // For comparison operators, the resulting type should be boolean. + switch (op) + { + case OPERATOR_EQEQ: + case OPERATOR_NOTEQ: + case OPERATOR_LT: + case OPERATOR_LE: + case OPERATOR_GT: + case OPERATOR_GE: + type_tree = boolean_type_node; + break; + default: + break; + } + + tree ret = fold_build2_loc(location.gcc_location(), code, type_tree, + left_tree, right_tree); + return this->make_expression(ret); +} + +// An expression as a statement. + +Bstatement* +Gcc_backend::expression_statement(Bexpression* expr) +{ + return this->make_statement(expr->get_tree()); +} + +// Variable initialization. + +Bstatement* +Gcc_backend::init_statement(Bvariable* var, Bexpression* init) +{ + tree var_tree = var->get_tree(); + tree init_tree = init->get_tree(); + if (var_tree == error_mark_node || init_tree == error_mark_node) + return this->error_statement(); + gcc_assert(TREE_CODE(var_tree) == VAR_DECL); + + // To avoid problems with GNU ld, we don't make zero-sized + // externally visible variables. That might lead us to doing an + // initialization of a zero-sized expression to a non-zero sized + // variable, or vice-versa. Avoid crashes by omitting the + // initializer. Such initializations don't mean anything anyhow. + if (int_size_in_bytes(TREE_TYPE(var_tree)) != 0 + && init_tree != NULL_TREE + && int_size_in_bytes(TREE_TYPE(init_tree)) != 0) + { + DECL_INITIAL(var_tree) = init_tree; + init_tree = NULL_TREE; + } + + tree ret = build1_loc(DECL_SOURCE_LOCATION(var_tree), DECL_EXPR, + void_type_node, var_tree); + if (init_tree != NULL_TREE) + ret = build2_loc(DECL_SOURCE_LOCATION(var_tree), COMPOUND_EXPR, + void_type_node, init_tree, ret); + + return this->make_statement(ret); +} + +// Assignment. + +Bstatement* +Gcc_backend::assignment_statement(Bexpression* lhs, Bexpression* rhs, + Location location) +{ + tree lhs_tree = lhs->get_tree(); + tree rhs_tree = rhs->get_tree(); + if (lhs_tree == error_mark_node || rhs_tree == error_mark_node) + return this->error_statement(); + + // To avoid problems with GNU ld, we don't make zero-sized + // externally visible variables. That might lead us to doing an + // assignment of a zero-sized expression to a non-zero sized + // expression; avoid crashes here by avoiding assignments of + // zero-sized expressions. Such assignments don't really mean + // anything anyhow. + if (int_size_in_bytes(TREE_TYPE(lhs_tree)) == 0 + || int_size_in_bytes(TREE_TYPE(rhs_tree)) == 0) + return this->compound_statement(this->expression_statement(lhs), + this->expression_statement(rhs)); + + // Sometimes the same unnamed Go type can be created multiple times + // and thus have multiple tree representations. Make sure this does + // not confuse the middle-end. + if (TREE_TYPE(lhs_tree) != TREE_TYPE(rhs_tree)) + { + tree lhs_type_tree = TREE_TYPE(lhs_tree); + gcc_assert(TREE_CODE(lhs_type_tree) == TREE_CODE(TREE_TYPE(rhs_tree))); + if (POINTER_TYPE_P(lhs_type_tree) + || INTEGRAL_TYPE_P(lhs_type_tree) + || SCALAR_FLOAT_TYPE_P(lhs_type_tree) + || COMPLEX_FLOAT_TYPE_P(lhs_type_tree)) + rhs_tree = fold_convert_loc(location.gcc_location(), lhs_type_tree, + rhs_tree); + else if (TREE_CODE(lhs_type_tree) == RECORD_TYPE + || TREE_CODE(lhs_type_tree) == ARRAY_TYPE) + { + gcc_assert(int_size_in_bytes(lhs_type_tree) + == int_size_in_bytes(TREE_TYPE(rhs_tree))); + rhs_tree = fold_build1_loc(location.gcc_location(), + VIEW_CONVERT_EXPR, + lhs_type_tree, rhs_tree); + } + } + + return this->make_statement(fold_build2_loc(location.gcc_location(), + MODIFY_EXPR, + void_type_node, + lhs_tree, rhs_tree)); +} + +// Return. + +Bstatement* +Gcc_backend::return_statement(Bfunction* bfunction, + const std::vector& vals, + Location location) +{ + tree fntree = bfunction->get_tree(); + if (fntree == error_mark_node) + return this->error_statement(); + tree result = DECL_RESULT(fntree); + if (result == error_mark_node) + return this->error_statement(); + tree ret; + if (vals.empty()) + ret = fold_build1_loc(location.gcc_location(), RETURN_EXPR, void_type_node, + NULL_TREE); + else if (vals.size() == 1) + { + tree val = vals.front()->get_tree(); + if (val == error_mark_node) + return this->error_statement(); + tree set = fold_build2_loc(location.gcc_location(), MODIFY_EXPR, + void_type_node, result, + vals.front()->get_tree()); + ret = fold_build1_loc(location.gcc_location(), RETURN_EXPR, + void_type_node, set); + } + else + { + // To return multiple values, copy the values into a temporary + // variable of the right structure type, and then assign the + // temporary variable to the DECL_RESULT in the return + // statement. + tree stmt_list = NULL_TREE; + tree rettype = TREE_TYPE(result); + tree rettmp = create_tmp_var(rettype, "RESULT"); + tree field = TYPE_FIELDS(rettype); + for (std::vector::const_iterator p = vals.begin(); + p != vals.end(); + p++, field = DECL_CHAIN(field)) + { + gcc_assert(field != NULL_TREE); + tree ref = fold_build3_loc(location.gcc_location(), COMPONENT_REF, + TREE_TYPE(field), rettmp, field, + NULL_TREE); + tree val = (*p)->get_tree(); + if (val == error_mark_node) + return this->error_statement(); + tree set = fold_build2_loc(location.gcc_location(), MODIFY_EXPR, + void_type_node, + ref, (*p)->get_tree()); + append_to_statement_list(set, &stmt_list); + } + gcc_assert(field == NULL_TREE); + tree set = fold_build2_loc(location.gcc_location(), MODIFY_EXPR, + void_type_node, + result, rettmp); + tree ret_expr = fold_build1_loc(location.gcc_location(), RETURN_EXPR, + void_type_node, set); + append_to_statement_list(ret_expr, &stmt_list); + ret = stmt_list; + } + return this->make_statement(ret); +} + +// If. + +Bstatement* +Gcc_backend::if_statement(Bexpression* condition, Bblock* then_block, + Bblock* else_block, Location location) +{ + tree cond_tree = condition->get_tree(); + tree then_tree = then_block->get_tree(); + tree else_tree = else_block == NULL ? NULL_TREE : else_block->get_tree(); + if (cond_tree == error_mark_node + || then_tree == error_mark_node + || else_tree == error_mark_node) + return this->error_statement(); + tree ret = build3_loc(location.gcc_location(), COND_EXPR, void_type_node, + cond_tree, then_tree, else_tree); + return this->make_statement(ret); +} + +// Switch. + +Bstatement* +Gcc_backend::switch_statement( + Bexpression* value, + const std::vector >& cases, + const std::vector& statements, + Location switch_location) +{ + gcc_assert(cases.size() == statements.size()); + + tree stmt_list = NULL_TREE; + std::vector >::const_iterator pc = cases.begin(); + for (std::vector::const_iterator ps = statements.begin(); + ps != statements.end(); + ++ps, ++pc) + { + if (pc->empty()) + { + source_location loc = (*ps != NULL + ? EXPR_LOCATION((*ps)->get_tree()) + : UNKNOWN_LOCATION); + tree label = create_artificial_label(loc); + tree c = build_case_label(NULL_TREE, NULL_TREE, label); + append_to_statement_list(c, &stmt_list); + } + else + { + for (std::vector::const_iterator pcv = pc->begin(); + pcv != pc->end(); + ++pcv) + { + tree t = (*pcv)->get_tree(); + if (t == error_mark_node) + return this->error_statement(); + source_location loc = EXPR_LOCATION(t); + tree label = create_artificial_label(loc); + tree c = build_case_label((*pcv)->get_tree(), NULL_TREE, label); + append_to_statement_list(c, &stmt_list); + } + } + + if (*ps != NULL) + { + tree t = (*ps)->get_tree(); + if (t == error_mark_node) + return this->error_statement(); + append_to_statement_list(t, &stmt_list); + } + } + + tree tv = value->get_tree(); + if (tv == error_mark_node) + return this->error_statement(); + tree t = build3_loc(switch_location.gcc_location(), SWITCH_EXPR, + NULL_TREE, tv, stmt_list, NULL_TREE); + return this->make_statement(t); +} + +// Pair of statements. + +Bstatement* +Gcc_backend::compound_statement(Bstatement* s1, Bstatement* s2) +{ + tree stmt_list = NULL_TREE; + tree t = s1->get_tree(); + if (t == error_mark_node) + return this->error_statement(); + append_to_statement_list(t, &stmt_list); + t = s2->get_tree(); + if (t == error_mark_node) + return this->error_statement(); + append_to_statement_list(t, &stmt_list); + return this->make_statement(stmt_list); +} + +// List of statements. + +Bstatement* +Gcc_backend::statement_list(const std::vector& statements) +{ + tree stmt_list = NULL_TREE; + for (std::vector::const_iterator p = statements.begin(); + p != statements.end(); + ++p) + { + tree t = (*p)->get_tree(); + if (t == error_mark_node) + return this->error_statement(); + append_to_statement_list(t, &stmt_list); + } + return this->make_statement(stmt_list); +} + +// Make a block. For some reason gcc uses a dual structure for +// blocks: BLOCK tree nodes and BIND_EXPR tree nodes. Since the +// BIND_EXPR node points to the BLOCK node, we store the BIND_EXPR in +// the Bblock. + +Bblock* +Gcc_backend::block(Bfunction* function, Bblock* enclosing, + const std::vector& vars, + Location start_location, + Location) +{ + tree block_tree = make_node(BLOCK); + if (enclosing == NULL) + { + // FIXME: Permitting FUNCTION to be NULL is a temporary measure + // until we have a proper representation of the init function. + tree fndecl; + if (function == NULL) + fndecl = current_function_decl; + else + fndecl = function->get_tree(); + gcc_assert(fndecl != NULL_TREE); + + // We may have already created a block for local variables when + // we take the address of a parameter. + if (DECL_INITIAL(fndecl) == NULL_TREE) + { + BLOCK_SUPERCONTEXT(block_tree) = fndecl; + DECL_INITIAL(fndecl) = block_tree; + } + else + { + tree superblock_tree = DECL_INITIAL(fndecl); + BLOCK_SUPERCONTEXT(block_tree) = superblock_tree; + tree* pp; + for (pp = &BLOCK_SUBBLOCKS(superblock_tree); + *pp != NULL_TREE; + pp = &BLOCK_CHAIN(*pp)) + ; + *pp = block_tree; + } + } + else + { + tree superbind_tree = enclosing->get_tree(); + tree superblock_tree = BIND_EXPR_BLOCK(superbind_tree); + gcc_assert(TREE_CODE(superblock_tree) == BLOCK); + + BLOCK_SUPERCONTEXT(block_tree) = superblock_tree; + tree* pp; + for (pp = &BLOCK_SUBBLOCKS(superblock_tree); + *pp != NULL_TREE; + pp = &BLOCK_CHAIN(*pp)) + ; + *pp = block_tree; + } + + tree* pp = &BLOCK_VARS(block_tree); + for (std::vector::const_iterator pv = vars.begin(); + pv != vars.end(); + ++pv) + { + *pp = (*pv)->get_tree(); + if (*pp != error_mark_node) + pp = &DECL_CHAIN(*pp); + } + *pp = NULL_TREE; + + TREE_USED(block_tree) = 1; + + tree bind_tree = build3_loc(start_location.gcc_location(), BIND_EXPR, + void_type_node, BLOCK_VARS(block_tree), + NULL_TREE, block_tree); + TREE_SIDE_EFFECTS(bind_tree) = 1; + + return new Bblock(bind_tree); +} + +// Add statements to a block. + +void +Gcc_backend::block_add_statements(Bblock* bblock, + const std::vector& statements) +{ + tree stmt_list = NULL_TREE; + for (std::vector::const_iterator p = statements.begin(); + p != statements.end(); + ++p) + { + tree s = (*p)->get_tree(); + if (s != error_mark_node) + append_to_statement_list(s, &stmt_list); + } + + tree bind_tree = bblock->get_tree(); + gcc_assert(TREE_CODE(bind_tree) == BIND_EXPR); + BIND_EXPR_BODY(bind_tree) = stmt_list; +} + +// Return a block as a statement. + +Bstatement* +Gcc_backend::block_statement(Bblock* bblock) +{ + tree bind_tree = bblock->get_tree(); + gcc_assert(TREE_CODE(bind_tree) == BIND_EXPR); + return this->make_statement(bind_tree); +} + +// This is not static because we declare it with GTY(()) in go-c.h. +tree go_non_zero_struct; + +// Return a type corresponding to TYPE with non-zero size. + +tree +Gcc_backend::non_zero_size_type(tree type) +{ + if (int_size_in_bytes(type) != 0) + return type; + + switch (TREE_CODE(type)) + { + case RECORD_TYPE: + if (TYPE_FIELDS(type) != NULL_TREE) + { + tree ns = make_node(RECORD_TYPE); + tree field_trees = NULL_TREE; + tree *pp = &field_trees; + for (tree field = TYPE_FIELDS(type); + field != NULL_TREE; + field = DECL_CHAIN(field)) + { + tree ft = TREE_TYPE(field); + if (field == TYPE_FIELDS(type)) + ft = non_zero_size_type(ft); + tree f = build_decl(DECL_SOURCE_LOCATION(field), FIELD_DECL, + DECL_NAME(field), ft); + DECL_CONTEXT(f) = ns; + *pp = f; + pp = &DECL_CHAIN(f); + } + TYPE_FIELDS(ns) = field_trees; + layout_type(ns); + return ns; + } + + if (go_non_zero_struct == NULL_TREE) + { + type = make_node(RECORD_TYPE); + tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, + get_identifier("dummy"), + boolean_type_node); + DECL_CONTEXT(field) = type; + TYPE_FIELDS(type) = field; + layout_type(type); + go_non_zero_struct = type; + } + return go_non_zero_struct; + + case ARRAY_TYPE: + { + tree element_type = non_zero_size_type(TREE_TYPE(type)); + return build_array_type_nelts(element_type, 1); + } + + default: + gcc_unreachable(); + } + + gcc_unreachable(); +} + +// Make a global variable. + +Bvariable* +Gcc_backend::global_variable(const std::string& package_name, + const std::string& pkgpath, + const std::string& name, + Btype* btype, + bool is_external, + bool is_hidden, + bool in_unique_section, + Location location) +{ + tree type_tree = btype->get_tree(); + if (type_tree == error_mark_node) + return this->error_variable(); + + // The GNU linker does not like dynamic variables with zero size. + if ((is_external || !is_hidden) && int_size_in_bytes(type_tree) == 0) + type_tree = this->non_zero_size_type(type_tree); + + std::string var_name(package_name); + var_name.push_back('.'); + var_name.append(name); + tree decl = build_decl(location.gcc_location(), VAR_DECL, + get_identifier_from_string(var_name), + type_tree); + if (is_external) + DECL_EXTERNAL(decl) = 1; + else + TREE_STATIC(decl) = 1; + if (!is_hidden) + { + TREE_PUBLIC(decl) = 1; + + std::string asm_name(pkgpath); + asm_name.push_back('.'); + asm_name.append(name); + SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); + } + TREE_USED(decl) = 1; + + if (in_unique_section) + resolve_unique_section (decl, 0, 1); + + go_preserve_from_gc(decl); + + return new Bvariable(decl); +} + +// Set the initial value of a global variable. + +void +Gcc_backend::global_variable_set_init(Bvariable* var, Bexpression* expr) +{ + tree expr_tree = expr->get_tree(); + if (expr_tree == error_mark_node) + return; + gcc_assert(TREE_CONSTANT(expr_tree)); + tree var_decl = var->get_tree(); + if (var_decl == error_mark_node) + return; + DECL_INITIAL(var_decl) = expr_tree; + + // If this variable goes in a unique section, it may need to go into + // a different one now that DECL_INITIAL is set. + if (DECL_HAS_IMPLICIT_SECTION_NAME_P (var_decl)) + { + DECL_SECTION_NAME (var_decl) = NULL_TREE; + resolve_unique_section (var_decl, + compute_reloc_for_constant (expr_tree), + 1); + } +} + +// Make a local variable. + +Bvariable* +Gcc_backend::local_variable(Bfunction* function, const std::string& name, + Btype* btype, bool is_address_taken, + Location location) +{ + tree type_tree = btype->get_tree(); + if (type_tree == error_mark_node) + return this->error_variable(); + tree decl = build_decl(location.gcc_location(), VAR_DECL, + get_identifier_from_string(name), + type_tree); + DECL_CONTEXT(decl) = function->get_tree(); + TREE_USED(decl) = 1; + if (is_address_taken) + TREE_ADDRESSABLE(decl) = 1; + go_preserve_from_gc(decl); + return new Bvariable(decl); +} + +// Make a function parameter variable. + +Bvariable* +Gcc_backend::parameter_variable(Bfunction* function, const std::string& name, + Btype* btype, bool is_address_taken, + Location location) +{ + tree type_tree = btype->get_tree(); + if (type_tree == error_mark_node) + return this->error_variable(); + tree decl = build_decl(location.gcc_location(), PARM_DECL, + get_identifier_from_string(name), + type_tree); + DECL_CONTEXT(decl) = function->get_tree(); + DECL_ARG_TYPE(decl) = type_tree; + TREE_USED(decl) = 1; + if (is_address_taken) + TREE_ADDRESSABLE(decl) = 1; + go_preserve_from_gc(decl); + return new Bvariable(decl); +} + +// Make a temporary variable. + +Bvariable* +Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock, + Btype* btype, Bexpression* binit, + bool is_address_taken, + Location location, + Bstatement** pstatement) +{ + tree type_tree = btype->get_tree(); + tree init_tree = binit == NULL ? NULL_TREE : binit->get_tree(); + if (type_tree == error_mark_node || init_tree == error_mark_node) + { + *pstatement = this->error_statement(); + return this->error_variable(); + } + + tree var; + // We can only use create_tmp_var if the type is not addressable. + if (!TREE_ADDRESSABLE(type_tree)) + var = create_tmp_var(type_tree, "GOTMP"); + else + { + gcc_assert(bblock != NULL); + var = build_decl(location.gcc_location(), VAR_DECL, + create_tmp_var_name("GOTMP"), + type_tree); + DECL_ARTIFICIAL(var) = 1; + DECL_IGNORED_P(var) = 1; + TREE_USED(var) = 1; + // FIXME: Permitting function to be NULL here is a temporary + // measure until we have a proper representation of the init + // function. + if (function != NULL) + DECL_CONTEXT(var) = function->get_tree(); + else + { + gcc_assert(current_function_decl != NULL_TREE); + DECL_CONTEXT(var) = current_function_decl; + } + + // We have to add this variable to the BLOCK and the BIND_EXPR. + tree bind_tree = bblock->get_tree(); + gcc_assert(TREE_CODE(bind_tree) == BIND_EXPR); + tree block_tree = BIND_EXPR_BLOCK(bind_tree); + gcc_assert(TREE_CODE(block_tree) == BLOCK); + DECL_CHAIN(var) = BLOCK_VARS(block_tree); + BLOCK_VARS(block_tree) = var; + BIND_EXPR_VARS(bind_tree) = BLOCK_VARS(block_tree); + } + + if (init_tree != NULL_TREE) + DECL_INITIAL(var) = fold_convert_loc(location.gcc_location(), type_tree, + init_tree); + + if (is_address_taken) + TREE_ADDRESSABLE(var) = 1; + + *pstatement = this->make_statement(build1_loc(location.gcc_location(), + DECL_EXPR, + void_type_node, var)); + return new Bvariable(var); +} + +// Create a named immutable initialized data structure. + +Bvariable* +Gcc_backend::immutable_struct(const std::string& name, bool is_hidden, + bool is_common, Btype* btype, Location location) +{ + tree type_tree = btype->get_tree(); + if (type_tree == error_mark_node) + return this->error_variable(); + gcc_assert(TREE_CODE(type_tree) == RECORD_TYPE); + tree decl = build_decl(location.gcc_location(), VAR_DECL, + get_identifier_from_string(name), + build_qualified_type(type_tree, TYPE_QUAL_CONST)); + TREE_STATIC(decl) = 1; + TREE_READONLY(decl) = 1; + TREE_CONSTANT(decl) = 1; + TREE_USED(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + if (!is_hidden) + TREE_PUBLIC(decl) = 1; + + // When the initializer for one immutable_struct refers to another, + // it needs to know the visibility of the referenced struct so that + // compute_reloc_for_constant will return the right value. On many + // systems calling make_decl_one_only will mark the decl as weak, + // which will change the return value of compute_reloc_for_constant. + // We can't reliably call make_decl_one_only yet, because we don't + // yet know the initializer. This issue doesn't arise in C because + // Go initializers, unlike C initializers, can be indirectly + // recursive. To ensure that compute_reloc_for_constant computes + // the right value if some other initializer refers to this one, we + // mark this symbol as weak here. We undo that below in + // immutable_struct_set_init before calling mark_decl_one_only. + if (is_common) + DECL_WEAK(decl) = 1; + + // We don't call rest_of_decl_compilation until we have the + // initializer. + + go_preserve_from_gc(decl); + return new Bvariable(decl); +} + +// Set the initializer for a variable created by immutable_struct. +// This is where we finish compiling the variable. + +void +Gcc_backend::immutable_struct_set_init(Bvariable* var, const std::string&, + bool, bool is_common, Btype*, Location, + Bexpression* initializer) +{ + tree decl = var->get_tree(); + tree init_tree = initializer->get_tree(); + if (decl == error_mark_node || init_tree == error_mark_node) + return; + + DECL_INITIAL(decl) = init_tree; + + // Now that DECL_INITIAL is set, we can't call make_decl_one_only. + // See the comment where DECL_WEAK is set in immutable_struct. + if (is_common) + { + DECL_WEAK(decl) = 0; + make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl)); + } + + // These variables are often unneeded in the final program, so put + // them in their own section so that linker GC can discard them. + resolve_unique_section(decl, + compute_reloc_for_constant (init_tree), + 1); + + rest_of_decl_compilation(decl, 1, 0); +} + +// Return a reference to an immutable initialized data structure +// defined in another package. + +Bvariable* +Gcc_backend::immutable_struct_reference(const std::string& name, Btype* btype, + Location location) +{ + tree type_tree = btype->get_tree(); + if (type_tree == error_mark_node) + return this->error_variable(); + gcc_assert(TREE_CODE(type_tree) == RECORD_TYPE); + tree decl = build_decl(location.gcc_location(), VAR_DECL, + get_identifier_from_string(name), + build_qualified_type(type_tree, TYPE_QUAL_CONST)); + TREE_READONLY(decl) = 1; + TREE_CONSTANT(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + go_preserve_from_gc(decl); + return new Bvariable(decl); +} + +// Make a label. + +Blabel* +Gcc_backend::label(Bfunction* function, const std::string& name, + Location location) +{ + tree decl; + if (name.empty()) + decl = create_artificial_label(location.gcc_location()); + else + { + tree id = get_identifier_from_string(name); + decl = build_decl(location.gcc_location(), LABEL_DECL, id, + void_type_node); + DECL_CONTEXT(decl) = function->get_tree(); + } + return new Blabel(decl); +} + +// Make a statement which defines a label. + +Bstatement* +Gcc_backend::label_definition_statement(Blabel* label) +{ + tree lab = label->get_tree(); + tree ret = fold_build1_loc(DECL_SOURCE_LOCATION(lab), LABEL_EXPR, + void_type_node, lab); + return this->make_statement(ret); +} + +// Make a goto statement. + +Bstatement* +Gcc_backend::goto_statement(Blabel* label, Location location) +{ + tree lab = label->get_tree(); + tree ret = fold_build1_loc(location.gcc_location(), GOTO_EXPR, void_type_node, + lab); + return this->make_statement(ret); +} + +// Get the address of a label. + +Bexpression* +Gcc_backend::label_address(Blabel* label, Location location) +{ + tree lab = label->get_tree(); + TREE_USED(lab) = 1; + TREE_ADDRESSABLE(lab) = 1; + tree ret = fold_convert_loc(location.gcc_location(), ptr_type_node, + build_fold_addr_expr_loc(location.gcc_location(), + lab)); + return this->make_expression(ret); +} + +// Declare or define a new function. + +Bfunction* +Gcc_backend::function(Btype* fntype, const std::string& name, + const std::string& asm_name, bool is_visible, + bool is_declaration, bool is_inlinable, + bool disable_split_stack, bool in_unique_section, + Location location) +{ + tree functype = fntype->get_tree(); + if (functype != error_mark_node) + { + gcc_assert(FUNCTION_POINTER_TYPE_P(functype)); + functype = TREE_TYPE(functype); + } + tree id = get_identifier_from_string(name); + if (functype == error_mark_node || id == error_mark_node) + return this->error_function(); + + tree decl = build_decl(location.gcc_location(), FUNCTION_DECL, id, functype); + if (!asm_name.empty()) + SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); + if (is_visible) + TREE_PUBLIC(decl) = 1; + if (is_declaration) + DECL_EXTERNAL(decl) = 1; + else + { + tree restype = TREE_TYPE(functype); + tree resdecl = + build_decl(location.gcc_location(), RESULT_DECL, NULL_TREE, restype); + DECL_ARTIFICIAL(resdecl) = 1; + DECL_IGNORED_P(resdecl) = 1; + DECL_CONTEXT(resdecl) = decl; + DECL_RESULT(decl) = resdecl; + } + if (!is_inlinable) + DECL_UNINLINABLE(decl) = 1; + if (disable_split_stack) + { + tree attr = get_identifier("__no_split_stack__"); + DECL_ATTRIBUTES(decl) = tree_cons(attr, NULL_TREE, NULL_TREE); + } + if (in_unique_section) + resolve_unique_section(decl, 0, 1); + + go_preserve_from_gc(decl); + return new Bfunction(decl); +} + +// The single backend. + +static Gcc_backend gcc_backend; + +// Return the backend generator. + +Backend* +go_get_backend() +{ + return &gcc_backend; +} + +// FIXME: Temporary functions while converting to the new backend +// interface. + +Btype* +tree_to_type(tree t) +{ + return new Btype(t); +} + +Bexpression* +tree_to_expr(tree t) +{ + return new Bexpression(t); +} + +Bstatement* +tree_to_stat(tree t) +{ + return new Bstatement(t); +} + +Bfunction* +tree_to_function(tree t) +{ + return new Bfunction(t); +} + +Bblock* +tree_to_block(tree t) +{ + gcc_assert(TREE_CODE(t) == BIND_EXPR); + return new Bblock(t); +} + +tree +type_to_tree(Btype* bt) +{ + return bt->get_tree(); +} + +tree +expr_to_tree(Bexpression* be) +{ + return be->get_tree(); +} + +tree +stat_to_tree(Bstatement* bs) +{ + return bs->get_tree(); +} + +tree +block_to_tree(Bblock* bb) +{ + return bb->get_tree(); +} + +tree +var_to_tree(Bvariable* bv) +{ + return bv->get_tree(); +} + +tree +function_to_tree(Bfunction* bf) +{ + return bf->get_tree(); +} diff --git a/gcc-4.9/gcc/go/go-lang.c b/gcc-4.9/gcc/go/go-lang.c new file mode 100644 index 000000000..c0f2f1f38 --- /dev/null +++ b/gcc-4.9/gcc/go/go-lang.c @@ -0,0 +1,498 @@ +/* go-lang.c -- Go frontend gcc interface. + Copyright (C) 2009-2014 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "ansidecl.h" +#include "coretypes.h" +#include "opts.h" +#include "tree.h" +#include "basic-block.h" +#include "gimple-expr.h" +#include "gimplify.h" +#include "stor-layout.h" +#include "toplev.h" +#include "debug.h" +#include "options.h" +#include "flags.h" +#include "convert.h" +#include "diagnostic.h" +#include "langhooks.h" +#include "langhooks-def.h" +#include "target.h" +#include "common/common-target.h" + +#include + +#include "go-c.h" + +/* Language-dependent contents of a type. */ + +struct GTY(()) lang_type +{ + char dummy; +}; + +/* Language-dependent contents of a decl. */ + +struct GTY((variable_size)) lang_decl +{ + char dummy; +}; + +/* Language-dependent contents of an identifier. This must include a + tree_identifier. */ + +struct GTY(()) lang_identifier +{ + struct tree_identifier common; +}; + +/* The resulting tree type. */ + +union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"), + chain_next ("CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN (&%h.generic)) : NULL"))) +lang_tree_node +{ + union tree_node GTY((tag ("0"), + desc ("tree_node_structure (&%h)"))) generic; + struct lang_identifier GTY((tag ("1"))) identifier; +}; + +/* We don't use language_function. */ + +struct GTY(()) language_function +{ + int dummy; +}; + +/* Option information we need to pass to go_create_gogo. */ + +static const char *go_pkgpath = NULL; +static const char *go_prefix = NULL; +static const char *go_relative_import_path = NULL; + +/* Language hooks. */ + +static bool +go_langhook_init (void) +{ + build_common_tree_nodes (false, false); + + /* I don't know why this has to be done explicitly. */ + void_list_node = build_tree_list (NULL_TREE, void_type_node); + + /* We must create the gogo IR after calling build_common_tree_nodes + (because Gogo::define_builtin_function_trees refers indirectly + 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_relative_import_path); + + build_common_builtin_nodes (); + + /* The default precision for floating point numbers. This is used + for floating point constants with abstract type. This may + eventually be controllable by a command line option. */ + mpfr_set_default_prec (256); + + /* Go uses exceptions. */ + using_eh_for_cleanups (); + + return true; +} + +/* The option mask. */ + +static unsigned int +go_langhook_option_lang_mask (void) +{ + return CL_Go; +} + +/* Initialize the options structure. */ + +static void +go_langhook_init_options_struct (struct gcc_options *opts) +{ + /* Go says that signed overflow is precisely defined. */ + opts->x_flag_wrapv = 1; + + /* We default to using strict aliasing, since Go pointers are safe. + This is turned off for code that imports the "unsafe" package, + because using unsafe.pointer violates C style aliasing + requirements. */ + opts->x_flag_strict_aliasing = 1; + + /* Default to avoiding range issues for complex multiply and + divide. */ + opts->x_flag_complex_method = 2; + + /* The builtin math functions should not set errno. */ + opts->x_flag_errno_math = 0; + opts->frontend_set_flag_errno_math = true; + + /* We turn on stack splitting if we can. */ + if (targetm_common.supports_split_stack (false, opts)) + opts->x_flag_split_stack = 1; + + /* Exceptions are used to handle recovering from panics. */ + opts->x_flag_exceptions = 1; + opts->x_flag_non_call_exceptions = 1; +} + +/* Infrastructure for a vector of char * pointers. */ + +typedef const char *go_char_p; + +/* The list of directories to search after all the Go specific + directories have been searched. */ + +static vec go_search_dirs; + +/* Handle Go specific options. Return 0 if we didn't do anything. */ + +static bool +go_langhook_handle_option ( + size_t scode, + const char *arg, + int value ATTRIBUTE_UNUSED, + int kind ATTRIBUTE_UNUSED, + location_t loc ATTRIBUTE_UNUSED, + const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED) +{ + enum opt_code code = (enum opt_code) scode; + bool ret = true; + + switch (code) + { + case OPT_I: + go_add_search_path (arg); + break; + + case OPT_L: + /* A -L option is assumed to come from the compiler driver. + This is a system directory. We search the following + directories, if they exist, before this one: + dir/go/VERSION + dir/go/VERSION/MACHINE + This is like include/c++. */ + { + static const char dir_separator_str[] = { DIR_SEPARATOR, 0 }; + size_t len; + char *p; + struct stat st; + + len = strlen (arg); + p = XALLOCAVEC (char, + (len + sizeof "go" + sizeof DEFAULT_TARGET_VERSION + + sizeof DEFAULT_TARGET_MACHINE + 3)); + strcpy (p, arg); + if (len > 0 && !IS_DIR_SEPARATOR (p[len - 1])) + strcat (p, dir_separator_str); + strcat (p, "go"); + strcat (p, dir_separator_str); + strcat (p, DEFAULT_TARGET_VERSION); + if (stat (p, &st) == 0 && S_ISDIR (st.st_mode)) + { + go_add_search_path (p); + strcat (p, dir_separator_str); + strcat (p, DEFAULT_TARGET_MACHINE); + if (stat (p, &st) == 0 && S_ISDIR (st.st_mode)) + go_add_search_path (p); + } + + /* Search ARG too, but only after we've searched to Go + specific directories for all -L arguments. */ + go_search_dirs.safe_push (arg); + } + break; + + case OPT_fgo_dump_: + ret = go_enable_dump (arg) ? true : false; + break; + + case OPT_fgo_optimize_: + ret = go_enable_optimize (arg) ? true : false; + break; + + case OPT_fgo_pkgpath_: + go_pkgpath = arg; + break; + + case OPT_fgo_prefix_: + 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; + } + + return ret; +} + +/* Run after parsing options. */ + +static bool +go_langhook_post_options (const char **pfilename ATTRIBUTE_UNUSED) +{ + unsigned int ix; + const char *dir; + + gcc_assert (num_in_fnames > 0); + + FOR_EACH_VEC_ELT (go_search_dirs, ix, dir) + go_add_search_path (dir); + go_search_dirs.release (); + + if (flag_excess_precision_cmdline == EXCESS_PRECISION_DEFAULT) + flag_excess_precision_cmdline = EXCESS_PRECISION_STANDARD; + + /* Tail call optimizations can confuse uses of runtime.Callers. */ + if (!global_options_set.x_flag_optimize_sibling_calls) + global_options.x_flag_optimize_sibling_calls = 0; + + /* Returning false means that the backend should be used. */ + return false; +} + +static void +go_langhook_parse_file (void) +{ + go_parse_input_files (in_fnames, num_in_fnames, flag_syntax_only, + go_require_return_statement); +} + +static tree +go_langhook_type_for_size (unsigned int bits, int unsignedp) +{ + return go_type_for_size (bits, unsignedp); +} + +static tree +go_langhook_type_for_mode (enum machine_mode mode, int unsignedp) +{ + tree type; + /* Go has no vector types. Build them here. FIXME: It does not + make sense for the middle-end to ask the frontend for a type + which the frontend does not support. However, at least for now + it is required. See PR 46805. */ + if (VECTOR_MODE_P (mode)) + { + tree inner; + + inner = go_langhook_type_for_mode (GET_MODE_INNER (mode), unsignedp); + if (inner != NULL_TREE) + return build_vector_type_for_mode (inner, mode); + return NULL_TREE; + } + + type = go_type_for_mode (mode, unsignedp); + if (type) + return type; + +#if HOST_BITS_PER_WIDE_INT >= 64 + /* The middle-end and some backends rely on TImode being supported + for 64-bit HWI. */ + if (mode == TImode) + { + type = build_nonstandard_integer_type (GET_MODE_BITSIZE (TImode), + unsignedp); + if (type && TYPE_MODE (type) == TImode) + return type; + } +#endif + return NULL_TREE; +} + +/* Record a builtin function. We just ignore builtin functions. */ + +static tree +go_langhook_builtin_function (tree decl) +{ + return decl; +} + +/* Return true if we are in the global binding level. */ + +static bool +go_langhook_global_bindings_p (void) +{ + return current_function_decl == NULL_TREE; +} + +/* Push a declaration into the current binding level. We can't + usefully implement this since we don't want to convert from tree + back to one of our internal data structures. I think the only way + this is used is to record a decl which is to be returned by + getdecls, and we could implement it for that purpose if + necessary. */ + +static tree +go_langhook_pushdecl (tree decl ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + +/* This hook is used to get the current list of declarations as trees. + We don't support that; instead we use the write_globals hook. This + can't simply crash because it is called by -gstabs. */ + +static tree +go_langhook_getdecls (void) +{ + return NULL; +} + +/* Write out globals. */ + +static void +go_langhook_write_globals (void) +{ + go_write_globals (); +} + +/* Go specific gimplification. We need to gimplify + CALL_EXPR_STATIC_CHAIN, because the gimplifier doesn't handle + it. */ + +static int +go_langhook_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) +{ + if (TREE_CODE (*expr_p) == CALL_EXPR + && CALL_EXPR_STATIC_CHAIN (*expr_p) != NULL_TREE) + gimplify_expr (&CALL_EXPR_STATIC_CHAIN (*expr_p), pre_p, post_p, + is_gimple_val, fb_rvalue); + return GS_UNHANDLED; +} + +/* Return a decl for the exception personality function. The function + itself is implemented in libgo/runtime/go-unwind.c. */ + +static tree +go_langhook_eh_personality (void) +{ + static tree personality_decl; + if (personality_decl == NULL_TREE) + { + personality_decl = build_personality_function ("gccgo"); + go_preserve_from_gc (personality_decl); + } + return personality_decl; +} + +/* Functions called directly by the generic backend. */ + +tree +convert (tree type, tree expr) +{ + if (type == error_mark_node + || expr == error_mark_node + || TREE_TYPE (expr) == error_mark_node) + return error_mark_node; + + if (type == TREE_TYPE (expr)) + return expr; + + if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr))) + return fold_convert (type, expr); + + switch (TREE_CODE (type)) + { + case VOID_TYPE: + case BOOLEAN_TYPE: + return fold_convert (type, expr); + case INTEGER_TYPE: + return fold (convert_to_integer (type, expr)); + case POINTER_TYPE: + return fold (convert_to_pointer (type, expr)); + case REAL_TYPE: + return fold (convert_to_real (type, expr)); + case COMPLEX_TYPE: + return fold (convert_to_complex (type, expr)); + default: + break; + } + + gcc_unreachable (); +} + +/* FIXME: This is a hack to preserve trees that we create from the + garbage collector. */ + +static GTY(()) tree go_gc_root; + +void +go_preserve_from_gc (tree t) +{ + go_gc_root = tree_cons (NULL_TREE, t, go_gc_root); +} + +/* Convert an identifier for use in an error message. */ + +const char * +go_localize_identifier (const char *ident) +{ + return identifier_to_locale (ident); +} + +#undef LANG_HOOKS_NAME +#undef LANG_HOOKS_INIT +#undef LANG_HOOKS_OPTION_LANG_MASK +#undef LANG_HOOKS_INIT_OPTIONS_STRUCT +#undef LANG_HOOKS_HANDLE_OPTION +#undef LANG_HOOKS_POST_OPTIONS +#undef LANG_HOOKS_PARSE_FILE +#undef LANG_HOOKS_TYPE_FOR_MODE +#undef LANG_HOOKS_TYPE_FOR_SIZE +#undef LANG_HOOKS_BUILTIN_FUNCTION +#undef LANG_HOOKS_GLOBAL_BINDINGS_P +#undef LANG_HOOKS_PUSHDECL +#undef LANG_HOOKS_GETDECLS +#undef LANG_HOOKS_WRITE_GLOBALS +#undef LANG_HOOKS_GIMPLIFY_EXPR +#undef LANG_HOOKS_EH_PERSONALITY + +#define LANG_HOOKS_NAME "GNU Go" +#define LANG_HOOKS_INIT go_langhook_init +#define LANG_HOOKS_OPTION_LANG_MASK go_langhook_option_lang_mask +#define LANG_HOOKS_INIT_OPTIONS_STRUCT go_langhook_init_options_struct +#define LANG_HOOKS_HANDLE_OPTION go_langhook_handle_option +#define LANG_HOOKS_POST_OPTIONS go_langhook_post_options +#define LANG_HOOKS_PARSE_FILE go_langhook_parse_file +#define LANG_HOOKS_TYPE_FOR_MODE go_langhook_type_for_mode +#define LANG_HOOKS_TYPE_FOR_SIZE go_langhook_type_for_size +#define LANG_HOOKS_BUILTIN_FUNCTION go_langhook_builtin_function +#define LANG_HOOKS_GLOBAL_BINDINGS_P go_langhook_global_bindings_p +#define LANG_HOOKS_PUSHDECL go_langhook_pushdecl +#define LANG_HOOKS_GETDECLS go_langhook_getdecls +#define LANG_HOOKS_WRITE_GLOBALS go_langhook_write_globals +#define LANG_HOOKS_GIMPLIFY_EXPR go_langhook_gimplify_expr +#define LANG_HOOKS_EH_PERSONALITY go_langhook_eh_personality + +struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; + +#include "gt-go-go-lang.h" +#include "gtype-go.h" diff --git a/gcc-4.9/gcc/go/go-linemap.cc b/gcc-4.9/gcc/go/go-linemap.cc new file mode 100644 index 000000000..b41559ed4 --- /dev/null +++ b/gcc-4.9/gcc/go/go-linemap.cc @@ -0,0 +1,126 @@ +// go-linemap.cc -- GCC implementation of Linemap. + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-linemap.h" + +// This class implements the Linemap interface defined by the +// frontend. + +class Gcc_linemap : public Linemap +{ + public: + Gcc_linemap() + : Linemap(), + in_file_(false) + { } + + void + start_file(const char* file_name, unsigned int line_begin); + + void + start_line(unsigned int line_number, unsigned int line_size); + + Location + get_location(unsigned int column); + + void + stop(); + + protected: + Location + get_predeclared_location(); + + Location + get_unknown_location(); + + bool + is_predeclared(Location); + + bool + is_unknown(Location); + + private: + // Whether we are currently reading a file. + bool in_file_; +}; + +Linemap* Linemap::instance_ = NULL; + +// Start getting locations from a new file. + +void +Gcc_linemap::start_file(const char *file_name, unsigned line_begin) +{ + if (this->in_file_) + linemap_add(line_table, LC_LEAVE, 0, NULL, 0); + linemap_add(line_table, LC_ENTER, 0, file_name, line_begin); + this->in_file_ = true; +} + +// Stop getting locations. + +void +Gcc_linemap::stop() +{ + linemap_add(line_table, LC_LEAVE, 0, NULL, 0); + this->in_file_ = false; +} + +// Start a new line. + +void +Gcc_linemap::start_line(unsigned lineno, unsigned linesize) +{ + linemap_line_start(line_table, lineno, linesize); +} + +// Get a location. + +Location +Gcc_linemap::get_location(unsigned column) +{ + return Location(linemap_position_for_column(line_table, column)); +} + +// Get the unknown location. + +Location +Gcc_linemap::get_unknown_location() +{ + return Location(UNKNOWN_LOCATION); +} + +// Get the predeclared location. + +Location +Gcc_linemap::get_predeclared_location() +{ + return Location(BUILTINS_LOCATION); +} + +// Return whether a location is the predeclared location. + +bool +Gcc_linemap::is_predeclared(Location loc) +{ + return loc.gcc_location() == BUILTINS_LOCATION; +} + +// Return whether a location is the unknown location. + +bool +Gcc_linemap::is_unknown(Location loc) +{ + return loc.gcc_location() == UNKNOWN_LOCATION; +} + +// Return the Linemap to use for the gcc backend. + +Linemap* +go_get_linemap() +{ + return new Gcc_linemap; +} diff --git a/gcc-4.9/gcc/go/go-location.h b/gcc-4.9/gcc/go/go-location.h new file mode 100644 index 000000000..f2731d968 --- /dev/null +++ b/gcc-4.9/gcc/go/go-location.h @@ -0,0 +1,45 @@ +// go-location.h -- GCC specific Location declaration. -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_LOCATION_H +#define GO_LOCATION_H + +#include "go-system.h" + +// A location in an input source file. + +class Location +{ + public: + Location() + : gcc_loc_(UNKNOWN_LOCATION) + { } + + explicit Location(source_location loc) + : gcc_loc_(loc) + { } + + source_location + gcc_location() const + { return this->gcc_loc_; } + + // Temporary hack till error_at and warning_at can deal with a Location. + operator source_location() const + { return this->gcc_loc_; } + + private: + source_location gcc_loc_; +}; + +// The Go frontend requires the ability to compare Locations. + +inline bool +operator<(Location loca, Location locb) +{ + return loca.gcc_location() < locb.gcc_location(); +} + +#endif // !defined(GO_LOCATION_H) diff --git a/gcc-4.9/gcc/go/go-system.h b/gcc-4.9/gcc/go/go-system.h new file mode 100644 index 000000000..5a3e81b21 --- /dev/null +++ b/gcc-4.9/gcc/go/go-system.h @@ -0,0 +1,142 @@ +// go-system.h -- Go frontend inclusion of gcc header files -*- C++ -*- +// Copyright (C) 2009-2014 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef GO_SYSTEM_H +#define GO_SYSTEM_H + +#include "config.h" + +// These must be included before the #poison declarations in system.h. + +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_UNORDERED_MAP) + +# include +# include + +# define Unordered_map(KEYTYPE, VALTYPE) \ + std::unordered_map + +# define Unordered_map_hash(KEYTYPE, VALTYPE, HASHFN, EQFN) \ + std::unordered_map + +# define Unordered_set(KEYTYPE) \ + std::unordered_set + +# define Unordered_set_hash(KEYTYPE, HASHFN, EQFN) \ + std::unordered_set + +#elif defined(HAVE_TR1_UNORDERED_MAP) + +# include +# include + +# define Unordered_map(KEYTYPE, VALTYPE) \ + std::tr1::unordered_map + +# define Unordered_map_hash(KEYTYPE, VALTYPE, HASHFN, EQFN) \ + std::tr1::unordered_map + +# define Unordered_set(KEYTYPE) \ + std::tr1::unordered_set + +# define Unordered_set_hash(KEYTYPE, HASHFN, EQFN) \ + std::tr1::unordered_set + +#elif defined(HAVE_EXT_HASH_MAP) + +# include +# include + +# define Unordered_map(KEYTYPE, VALTYPE) \ + __gnu_cxx::hash_map + +# define Unordered_map_hash(KEYTYPE, VALTYPE, HASHFN, EQFN) \ + __gnu_cxx::hash_map + +# define Unordered_set(KEYTYPE) \ + __gnu_cxx::hash_set + +# define Unordered_set_hash(KEYTYPE, HASHFN, EQFN) \ + __gnu_cxx::hash_set + +// Provide hash functions for strings and pointers. + +namespace __gnu_cxx +{ + +template<> +struct hash +{ + size_t + operator()(std::string s) const + { return __stl_hash_string(s.c_str()); } +}; + +template +struct hash +{ + size_t + operator()(T* p) const + { return reinterpret_cast(p); } +}; + +} + +#else + +# define Unordered_map(KEYTYPE, VALTYPE) \ + std::map + +# define Unordered_set(KEYTYPE) \ + std::set + +// We could make this work by writing an adapter class which +// implemented operator< in terms of the hash function. +# error "requires hash table type" + +#endif + +// We don't really need iostream, but some versions of gmp.h include +// it when compiled with C++, which means that we need to include it +// before the macro magic of safe-ctype.h, which is included by +// system.h. +#include + +#include "system.h" +#include "ansidecl.h" +#include "coretypes.h" + +#include "diagnostic-core.h" /* For error_at and friends. */ +#include "input.h" /* For source_location. */ +#include "intl.h" /* For _(). */ + +// When using gcc, go_assert is just gcc_assert. +#define go_assert(EXPR) gcc_assert(EXPR) + +// When using gcc, go_unreachable is just gcc_unreachable. +#define go_unreachable() gcc_unreachable() + +#endif // !defined(GO_SYSTEM_H) diff --git a/gcc-4.9/gcc/go/gofrontend/LICENSE b/gcc-4.9/gcc/go/gofrontend/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gcc-4.9/gcc/go/gofrontend/PATENTS b/gcc-4.9/gcc/go/gofrontend/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/gcc-4.9/gcc/go/gofrontend/README b/gcc-4.9/gcc/go/gofrontend/README new file mode 100644 index 000000000..6d4d0b0a8 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/README @@ -0,0 +1,53 @@ +See ../README. + +The frontend is written in C++. + +The frontend lexes and parses the input into an IR specific to this +frontend known as gogo. It then runs a series of passes over the +code. + +Finally it converts gogo to gcc's GENERIC. A goal is to move the gcc +support code into a gcc-interface subdirectory. The gcc code will be +put under the GPL. The rest of the frontend will not include any gcc +header files. + +Issues to be faced in this transition: + +* Representation of source locations. + + Currently the frontend uses gcc's source_location codes, using the + interface in libcpp/line-map.h. + +* Handling of error messages. + + Currently the frontend uses gcc's error_at and warning_at + functions. + + Currently the frontend uses gcc's diagnostic formatter, using + features such as %<%> for appropriate quoting. + + Localization may be an issue. + +This compiler works, but the code is a work in progress. Notably, the +support for garbage collection is ineffective and needs a complete +rethinking. The frontend pays little attention to its memory usage +and rarely frees any memory. The code could use a general cleanup +which we have not had time to do. + +Contributing +============= + +To contribute patches to the files in this directory, please see +http://golang.org/doc/gccgo_contribute.html . + +The master copy of these files is hosted at +http://code.google.com/p/gofrontend . Changes to these files require +signing a Google contributor license agreement. If you are the +copyright holder, you will need to agree to the individual contributor +license agreement at +http://code.google.com/legal/individual-cla-v1.0.html. This agreement +can be completed online. + +If your organization is the copyright holder, the organization will +need to agree to the corporate contributor license agreement at +http://code.google.com/legal/corporate-cla-v1.0.html. + +If the copyright holder for your code has already completed the +agreement in connection with another Google open source project, it +does not need to be completed again. diff --git a/gcc-4.9/gcc/go/gofrontend/ast-dump.cc b/gcc-4.9/gcc/go/gofrontend/ast-dump.cc new file mode 100644 index 000000000..850e31a81 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/ast-dump.cc @@ -0,0 +1,469 @@ +// ast-dump.cc -- AST debug dump. -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include +#include + +#include "gogo.h" +#include "expressions.h" +#include "statements.h" +#include "types.h" +#include "ast-dump.h" +#include "go-c.h" +#include "go-dump.h" + +// The -fgo-dump-ast flag to activate AST dumps. + +Go_dump ast_dump_flag("ast"); + +// This class is used to traverse the tree to look for blocks and +// function headers. + +class Ast_dump_traverse_blocks_and_functions : public Traverse +{ + public: + Ast_dump_traverse_blocks_and_functions(Ast_dump_context* ast_dump_context) + : Traverse(traverse_blocks | traverse_functions), + ast_dump_context_(ast_dump_context) + { } + + protected: + int + block(Block*); + + int + function(Named_object*); + + private: + Ast_dump_context* ast_dump_context_; +}; + +// This class is used to traverse the tree to look for statements. + +class Ast_dump_traverse_statements : public Traverse +{ + public: + Ast_dump_traverse_statements(Ast_dump_context* ast_dump_context) + : Traverse(traverse_statements), + ast_dump_context_(ast_dump_context) + { } + + protected: + int + statement(Block*, size_t* pindex, Statement*); + + private: + Ast_dump_context* ast_dump_context_; +}; + +// For each block we enclose it in brackets. + +int Ast_dump_traverse_blocks_and_functions::block(Block * block) +{ + this->ast_dump_context_->print_indent(); + this->ast_dump_context_->ostream() << "{" << std::endl; + this->ast_dump_context_->indent(); + + // Dump statememts. + Ast_dump_traverse_statements adts(this->ast_dump_context_); + block->traverse(&adts); + + this->ast_dump_context_->unindent(); + this->ast_dump_context_->print_indent(); + this->ast_dump_context_->ostream() << "}" << std::endl; + + return TRAVERSE_SKIP_COMPONENTS; +} + +// Dump each traversed statement. + +int +Ast_dump_traverse_statements::statement(Block* block, size_t* pindex, + Statement* statement) +{ + statement->dump_statement(this->ast_dump_context_); + + if (statement->is_block_statement()) + { + Ast_dump_traverse_blocks_and_functions adtbf(this->ast_dump_context_); + statement->traverse(block, pindex, &adtbf); + } + + return TRAVERSE_SKIP_COMPONENTS; +} + +// Dump the function header. + +int +Ast_dump_traverse_blocks_and_functions::function(Named_object* no) +{ + this->ast_dump_context_->ostream() << no->name(); + + go_assert(no->is_function()); + Function* func = no->func_value(); + + this->ast_dump_context_->ostream() << "("; + this->ast_dump_context_->dump_typed_identifier_list( + func->type()->parameters()); + + this->ast_dump_context_->ostream() << ")"; + + Function::Results* res = func->result_variables(); + if (res != NULL && !res->empty()) + { + this->ast_dump_context_->ostream() << " ("; + + for (Function::Results::const_iterator it = res->begin(); + it != res->end(); + it++) + { + if (it != res->begin()) + this->ast_dump_context_->ostream() << ","; + Named_object* no = (*it); + + this->ast_dump_context_->ostream() << no->name() << " "; + go_assert(no->is_result_variable()); + Result_variable* resvar = no->result_var_value(); + + this->ast_dump_context_->dump_type(resvar->type()); + + } + this->ast_dump_context_->ostream() << ")"; + } + + this->ast_dump_context_->ostream() << " : "; + this->ast_dump_context_->dump_type(func->type()); + this->ast_dump_context_->ostream() << std::endl; + + return TRAVERSE_CONTINUE; +} + +// Class Ast_dump_context. + +Ast_dump_context::Ast_dump_context(std::ostream* out /* = NULL */, + bool dump_subblocks /* = true */) + : indent_(0), dump_subblocks_(dump_subblocks), ostream_(out), gogo_(NULL) +{ +} + +// Dump files will be named %basename%.dump.ast + +const char* kAstDumpFileExtension = ".dump.ast"; + +// Dump the internal representation. + +void +Ast_dump_context::dump(Gogo* gogo, const char* basename) +{ + std::ofstream* out = new std::ofstream(); + std::string dumpname(basename); + dumpname += ".dump.ast"; + out->open(dumpname.c_str()); + + if (out->fail()) + { + error("cannot open %s:%m, -fgo-dump-ast ignored", dumpname.c_str()); + return; + } + + this->gogo_ = gogo; + this->ostream_ = out; + + Ast_dump_traverse_blocks_and_functions adtbf(this); + gogo->traverse(&adtbf); + + out->close(); +} + +// Dump a textual representation of a type to the +// the dump file. + +void +Ast_dump_context::dump_type(const Type* t) +{ + if (t == NULL) + this->ostream() << "(nil type)"; + else + // FIXME: write a type pretty printer instead of + // using mangled names. + if (this->gogo_ != NULL) + this->ostream() << "(" << t->mangled_name(this->gogo_) << ")"; +} + +// Dump a textual representation of a block to the +// the dump file. + +void +Ast_dump_context::dump_block(Block* b) +{ + Ast_dump_traverse_blocks_and_functions adtbf(this); + b->traverse(&adtbf); +} + +// Dump a textual representation of an expression to the +// the dump file. + +void +Ast_dump_context::dump_expression(const Expression* e) +{ + e->dump_expression(this); +} + +// Dump a textual representation of an expression list to the +// the dump file. + +void +Ast_dump_context::dump_expression_list(const Expression_list* el, + bool as_pairs /* = false */) +{ + if (el == NULL) + return; + + for (std::vector::const_iterator it = el->begin(); + it != el->end(); + it++) + { + if ( it != el->begin()) + this->ostream() << ","; + if (*it != NULL) + (*it)->dump_expression(this); + else + this->ostream() << "NULL"; + if (as_pairs) + { + this->ostream() << ":"; + ++it; + (*it)->dump_expression(this); + } + } +} + +// Dump a textual representation of a typed identifier to the +// the dump file. + +void +Ast_dump_context::dump_typed_identifier(const Typed_identifier* ti) +{ + this->ostream() << ti->name() << " "; + this->dump_type(ti->type()); +} + +// Dump a textual representation of a typed identifier list to the +// the dump file. + +void +Ast_dump_context::dump_typed_identifier_list( + const Typed_identifier_list* ti_list) +{ + if (ti_list == NULL) + return; + + for (Typed_identifier_list::const_iterator it = ti_list->begin(); + it != ti_list->end(); + it++) + { + if (it != ti_list->begin()) + this->ostream() << ","; + this->dump_typed_identifier(&(*it)); + } +} + +// Dump a textual representation of a temporary variable to the +// the dump file. + +void +Ast_dump_context::dump_temp_variable_name(const Statement* s) +{ + go_assert(s->classification() == Statement::STATEMENT_TEMPORARY); + // Use the statement address as part of the name for the temporary variable. + this->ostream() << "tmp." << (uintptr_t) s; +} + +// Dump a textual representation of a label to the +// the dump file. + +void +Ast_dump_context::dump_label_name(const Unnamed_label* l) +{ + // Use the unnamed label address as part of the name for the temporary + // variable. + this->ostream() << "label." << (uintptr_t) l; +} + +// Produce a textual representation of an operator symbol. + +static const char* +op_string(Operator op) +{ +// FIXME: This should be in line with symbols that are parsed, +// exported and/or imported. + switch (op) + { + case OPERATOR_PLUS: + return "+"; + case OPERATOR_MINUS: + return "-"; + case OPERATOR_NOT: + return "!"; + case OPERATOR_XOR: + return "^"; + case OPERATOR_OR: + return "|"; + case OPERATOR_AND: + return "&"; + case OPERATOR_MULT: + return "*"; + case OPERATOR_OROR: + return "||"; + case OPERATOR_ANDAND: + return "&&"; + case OPERATOR_EQEQ: + return "=="; + case OPERATOR_NOTEQ: + return "!="; + case OPERATOR_LT: + return "<"; + case OPERATOR_LE: + return "<="; + case OPERATOR_GT: + return ">"; + case OPERATOR_GE: + return ">="; + case OPERATOR_DIV: + return "/"; + case OPERATOR_MOD: + return "%"; + case OPERATOR_LSHIFT: + return "<<"; + case OPERATOR_RSHIFT: + return "//"; + case OPERATOR_BITCLEAR: + return "&^"; + case OPERATOR_CHANOP: + return "<-"; + case OPERATOR_PLUSEQ: + return "+="; + case OPERATOR_MINUSEQ: + return "-="; + case OPERATOR_OREQ: + return "|="; + case OPERATOR_XOREQ: + return "^="; + case OPERATOR_MULTEQ: + return "*="; + case OPERATOR_DIVEQ: + return "/="; + case OPERATOR_MODEQ: + return "%="; + case OPERATOR_LSHIFTEQ: + return "<<="; + case OPERATOR_RSHIFTEQ: + return ">>="; + case OPERATOR_ANDEQ: + return "&="; + case OPERATOR_BITCLEAREQ: + return "&^="; + case OPERATOR_PLUSPLUS: + return "++"; + case OPERATOR_MINUSMINUS: + return "--"; + case OPERATOR_COLON: + return ":"; + case OPERATOR_COLONEQ: + return ":="; + case OPERATOR_SEMICOLON: + return ";"; + case OPERATOR_DOT: + return "."; + case OPERATOR_ELLIPSIS: + return "..."; + case OPERATOR_COMMA: + return ","; + case OPERATOR_LPAREN: + return "("; + case OPERATOR_RPAREN: + return ")"; + case OPERATOR_LCURLY: + return "{"; + case OPERATOR_RCURLY: + return "}"; + case OPERATOR_LSQUARE: + return "["; + case OPERATOR_RSQUARE: + return "]"; + default: + go_unreachable(); + } + return NULL; +} + +// Dump a textual representation of an operator to the +// the dump file. + +void +Ast_dump_context::dump_operator(Operator op) +{ + this->ostream() << op_string(op); +} + +// Size of a single indent. + +const int Ast_dump_context::offset_ = 2; + +// Print indenting spaces to dump file. + +void +Ast_dump_context::print_indent() +{ + for (int i = 0; i < this->indent_ * this->offset_; i++) + this->ostream() << " "; +} + +// Dump a textual representation of the ast to the +// the dump file. + +void Gogo::dump_ast(const char* basename) +{ + if (::ast_dump_flag.is_enabled()) + { + Ast_dump_context adc; + adc.dump(this, basename); + } +} + +// Implementation of String_dump interface. + +void +Ast_dump_context::write_c_string(const char* s) +{ + this->ostream() << s; +} + +void +Ast_dump_context::write_string(const std::string& s) +{ + this->ostream() << s; +} + +// Dump statment to stream. + +void +Ast_dump_context::dump_to_stream(const Statement* stm, std::ostream* out) +{ + Ast_dump_context adc(out, false); + stm->dump_statement(&adc); +} + +// Dump expression to stream. + +void +Ast_dump_context::dump_to_stream(const Expression* expr, std::ostream* out) +{ + Ast_dump_context adc(out, false); + expr->dump_expression(&adc); +} \ No newline at end of file diff --git a/gcc-4.9/gcc/go/gofrontend/ast-dump.h b/gcc-4.9/gcc/go/gofrontend/ast-dump.h new file mode 100644 index 000000000..55c93693f --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/ast-dump.h @@ -0,0 +1,122 @@ +// ast-dump.h -- AST debug dump. -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_AST_DUMP_H +#define GO_AST_DUMP_H + +#include "string-dump.h" + +class Expression; +class Expression_list; +class Named_object; +class Statement; +class Gogo; + +// This class implements fgo-dump-ast. the +// Abstract syntax tree dump of the Go program. + +class Ast_dump_context : public String_dump +{ + public: + Ast_dump_context(std::ostream* out = NULL, bool dump_subblocks = true); + + // Initialize the dump context. + void + dump(Gogo*, const char* basename); + + // Dump spaces to dumpfile as indentation. + void + print_indent(); + + // Increase current indentation for print_indent(). + void + indent() + { ++this->indent_;} + + // Decrease current indentation for print_indent(). + void + unindent() + { --this->indent_;} + + // Whether subblocks should be dumped or not. + bool + dump_subblocks() + { return this->dump_subblocks_; } + + // Get dump output stream. + std::ostream& + ostream() + { return *this->ostream_;} + + // Dump a Block to dump file. + void + dump_block(Block*); + + // Dump a type to dump file. + void + dump_type(const Type*); + + // Dump an expression to dump file. + void + dump_expression(const Expression*); + + // Dump an expression list to dump file. + void + dump_expression_list(const Expression_list*, bool as_pairs = false); + + // Dump a typed identifier to dump file. + void + dump_typed_identifier(const Typed_identifier*); + + // Dump a typed identifier list to dump file. + void + dump_typed_identifier_list(const Typed_identifier_list*); + + // Dump temporary variable name to dump file. + void + dump_temp_variable_name(const Statement*); + + // Dump unamed lable name to dump file. + void + dump_label_name(const Unnamed_label*); + + // Dump operator symbol to dump file. + void + dump_operator(Operator); + + // Implementation of String_dump interface. + void + write_c_string(const char*); + + // Implements the String_dump interface. + void + write_string(const std::string& s); + + // Dump statement to stream. + static void + dump_to_stream(const Statement*, std::ostream*); + + // Dump expression to stream. + static void + dump_to_stream(const Expression* expr, std::ostream* out); + + private: + // Current indent level. + int indent_; + + // Indentation offset. + static const int offset_; + + // Whether subblocks of composite statements should be dumped or not. + bool dump_subblocks_; + + // Stream on output dump file. + std::ostream* ostream_; + + Gogo* gogo_; +}; + +#endif // GO_AST_DUMP_H diff --git a/gcc-4.9/gcc/go/gofrontend/backend.h b/gcc-4.9/gcc/go/gofrontend/backend.h new file mode 100644 index 000000000..cbe5f22b6 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/backend.h @@ -0,0 +1,594 @@ +// backend.h -- Go frontend interface to backend -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_BACKEND_H +#define GO_BACKEND_H + +#include +#include + +#include "operator.h" + +// Pointers to these types are created by the backend, passed to the +// frontend, and passed back to the backend. The types must be +// defined by the backend using these names. + +// The backend representation of a type. +class Btype; + +// The backend represention of an expression. +class Bexpression; + +// The backend representation of a statement. +class Bstatement; + +// The backend representation of a function definition or declaration. +class Bfunction; + +// The backend representation of a block. +class Bblock; + +// The backend representation of a variable. +class Bvariable; + +// The backend representation of a label. +class Blabel; + +// The backend interface. This is a pure abstract class that a +// specific backend will implement. + +class Backend +{ + public: + virtual ~Backend() { } + + // Name/type/location. Used for function parameters, struct fields, + // interface methods. + struct Btyped_identifier + { + std::string name; + Btype* btype; + Location location; + + Btyped_identifier() + : name(), btype(NULL), location(UNKNOWN_LOCATION) + { } + + Btyped_identifier(const std::string& a_name, Btype* a_btype, + Location a_location) + : name(a_name), btype(a_btype), location(a_location) + { } + }; + + // Types. + + // Produce an error type. Actually the backend could probably just + // crash if this is called. + virtual Btype* + error_type() = 0; + + // Get a void type. This is used in (at least) two ways: 1) as the + // return type of a function with no result parameters; 2) + // unsafe.Pointer is represented as *void. + virtual Btype* + void_type() = 0; + + // Get the unnamed boolean type. + virtual Btype* + bool_type() = 0; + + // Get an unnamed integer type with the given signedness and number + // of bits. + virtual Btype* + integer_type(bool is_unsigned, int bits) = 0; + + // Get an unnamed floating point type with the given number of bits + // (32 or 64). + virtual Btype* + float_type(int bits) = 0; + + // Get an unnamed complex type with the given number of bits (64 or 128). + virtual Btype* + complex_type(int bits) = 0; + + // Get a pointer type. + virtual Btype* + pointer_type(Btype* to_type) = 0; + + // Get a function type. The receiver, parameter, and results are + // generated from the types in the Function_type. The Function_type + // is provided so that the names are available. This should return + // not the type of a Go function (which is a pointer to a struct) + // but the type of a C function pointer (which will be used as the + // type of the first field of the struct). If there is more than + // one result, RESULT_STRUCT is a struct type to hold the results, + // and RESULTS may be ignored; if there are zero or one results, + // RESULT_STRUCT is NULL. + virtual Btype* + function_type(const Btyped_identifier& receiver, + const std::vector& parameters, + const std::vector& results, + Btype* result_struct, + Location location) = 0; + + // Get a struct type. + virtual Btype* + struct_type(const std::vector& fields) = 0; + + // Get an array type. + virtual Btype* + array_type(Btype* element_type, Bexpression* length) = 0; + + // Create a placeholder pointer type. This is used for a named + // pointer type, since in Go a pointer type may refer to itself. + // NAME is the name of the type, and the location is where the named + // type is defined. This function is also used for unnamed function + // types with multiple results, in which case the type has no name + // and NAME will be empty. FOR_FUNCTION is true if this is for a C + // pointer to function type. A Go func type is represented as a + // pointer to a struct, and the first field of the struct is a C + // pointer to function. The return value will later be passed as + // the first parameter to set_placeholder_pointer_type or + // set_placeholder_function_type. + virtual Btype* + placeholder_pointer_type(const std::string& name, Location, + bool for_function) = 0; + + // Fill in a placeholder pointer type as a pointer. This takes a + // type returned by placeholder_pointer_type and arranges for it to + // point to the type that TO_TYPE points to (that is, PLACEHOLDER + // becomes the same type as TO_TYPE). Returns true on success, + // false on failure. + virtual bool + set_placeholder_pointer_type(Btype* placeholder, Btype* to_type) = 0; + + // Fill in a placeholder pointer type as a function. This takes a + // type returned by placeholder_pointer_type and arranges for it to + // become a real Go function type (which corresponds to a C/C++ + // pointer to function type). FT will be something returned by the + // function_type method. Returns true on success, false on failure. + virtual bool + set_placeholder_function_type(Btype* placeholder, Btype* ft) = 0; + + // Create a placeholder struct type. This is used for a named + // struct type, as with placeholder_pointer_type. It is also used + // for interface types, in which case NAME will be the empty string. + virtual Btype* + placeholder_struct_type(const std::string& name, Location) = 0; + + // Fill in a placeholder struct type. This takes a type returned by + // placeholder_struct_type and arranges for it to become a real + // struct type. The parameter is as for struct_type. Returns true + // on success, false on failure. + virtual bool + set_placeholder_struct_type(Btype* placeholder, + const std::vector& fields) + = 0; + + // Create a placeholder array type. This is used for a named array + // type, as with placeholder_pointer_type, to handle cases like + // type A []*A. + virtual Btype* + placeholder_array_type(const std::string& name, Location) = 0; + + // Fill in a placeholder array type. This takes a type returned by + // placeholder_array_type and arranges for it to become a real array + // type. The parameters are as for array_type. Returns true on + // success, false on failure. + virtual bool + set_placeholder_array_type(Btype* placeholder, Btype* element_type, + Bexpression* length) = 0; + + // Return a named version of a type. The location is the location + // of the type definition. This will not be called for a type + // created via placeholder_pointer_type, placeholder_struct_type, or + // placeholder_array_type.. (It may be called for a pointer, + // struct, or array type in a case like "type P *byte; type Q P".) + virtual Btype* + named_type(const std::string& name, Btype*, Location) = 0; + + // Create a marker for a circular pointer type. Go pointer and + // function types can refer to themselves in ways that are not + // permitted in C/C++. When a circular type is found, this function + // is called for the circular reference. This permits the backend + // to decide how to handle such a type. PLACEHOLDER is the + // placeholder type which has already been created; if the backend + // is prepared to handle a circular pointer type, it may simply + // return PLACEHOLDER. FOR_FUNCTION is true if this is for a + // function type. + // + // For "type P *P" the sequence of calls will be + // bt1 = placeholder_pointer_type(); + // bt2 = circular_pointer_type(bt1, false); + // set_placeholder_pointer_type(bt1, bt2); + virtual Btype* + circular_pointer_type(Btype* placeholder, bool for_function) = 0; + + // Return whether the argument could be a special type created by + // circular_pointer_type. This is used to introduce explicit type + // conversions where needed. If circular_pointer_type returns its + // PLACEHOLDER parameter, this may safely always return false. + virtual bool + is_circular_pointer_type(Btype*) = 0; + + // Return the size of a type. + virtual size_t + type_size(Btype*) = 0; + + // Return the alignment of a type. + virtual size_t + type_alignment(Btype*) = 0; + + // Return the alignment of a struct field of this type. This is + // normally the same as type_alignment, but not always. + virtual size_t + type_field_alignment(Btype*) = 0; + + // Return the offset of field INDEX in a struct type. INDEX is the + // entry in the FIELDS std::vector parameter of struct_type or + // set_placeholder_struct_type. + virtual size_t + type_field_offset(Btype*, size_t index) = 0; + + // Expressions. + + // Return an expression for a zero value of the given type. This is + // used for cases such as local variable initialization and + // converting nil to other types. + virtual Bexpression* + zero_expression(Btype*) = 0; + + // Create an error expression. This is used for cases which should + // not occur in a correct program, in order to keep the compilation + // going without crashing. + virtual Bexpression* + error_expression() = 0; + + // Create a reference to a variable. + virtual Bexpression* + var_expression(Bvariable* var, Location) = 0; + + // Create an expression that indirects through the pointer expression EXPR + // (i.e., return the expression for *EXPR). KNOWN_VALID is true if the pointer + // is known to point to a valid memory location. + virtual Bexpression* + indirect_expression(Bexpression* expr, bool known_valid, Location) = 0; + + // Return an expression for the multi-precision integer VAL in BTYPE. + virtual Bexpression* + integer_constant_expression(Btype* btype, mpz_t val) = 0; + + // Return an expression for the floating point value VAL in BTYPE. + virtual Bexpression* + float_constant_expression(Btype* btype, mpfr_t val) = 0; + + // Return an expression for the complex value REAL/IMAG in BTYPE. + virtual Bexpression* + complex_constant_expression(Btype* btype, mpfr_t real, mpfr_t imag) = 0; + + // Return an expression that converts EXPR to TYPE. + virtual Bexpression* + convert_expression(Btype* type, Bexpression* expr, Location) = 0; + + // Create an expression for the address of a function. This is used to + // get the address of the code for a function. + virtual Bexpression* + function_code_expression(Bfunction*, Location) = 0; + + // Create an expression that takes the address of an expression. + virtual Bexpression* + address_expression(Bexpression*, Location) = 0; + + // Return an expression for the field at INDEX in BSTRUCT. + virtual Bexpression* + struct_field_expression(Bexpression* bstruct, size_t index, Location) = 0; + + // Create an expression that executes BSTAT before BEXPR. + virtual Bexpression* + compound_expression(Bstatement* bstat, Bexpression* bexpr, Location) = 0; + + // Return an expression that executes THEN_EXPR if CONDITION is true, or + // ELSE_EXPR otherwise and returns the result as type BTYPE. ELSE_EXPR + // may be NULL. BTYPE may be NULL. + virtual Bexpression* + conditional_expression(Btype* btype, Bexpression* condition, + Bexpression* then_expr, Bexpression* else_expr, + Location) = 0; + + // Return an expression for the unary operation OP EXPR. + // Supported values of OP are (from operators.h): + // MINUS, NOT, XOR. + virtual Bexpression* + unary_expression(Operator op, Bexpression* expr, Location) = 0; + + // Return an expression for the binary operation LEFT OP RIGHT. + // Supported values of OP are (from operators.h): + // EQEQ, NOTEQ, LT, LE, GT, GE, PLUS, MINUS, OR, XOR, MULT, DIV, MOD, + // LSHIFT, RSHIFT, AND, NOT. + virtual Bexpression* + binary_expression(Operator op, Bexpression* left, Bexpression* right, + Location) = 0; + + // Statements. + + // Create an error statement. This is used for cases which should + // not occur in a correct program, in order to keep the compilation + // going without crashing. + virtual Bstatement* + error_statement() = 0; + + // Create an expression statement. + virtual Bstatement* + expression_statement(Bexpression*) = 0; + + // Create a variable initialization statement. This initializes a + // local variable at the point in the program flow where it is + // declared. + virtual Bstatement* + init_statement(Bvariable* var, Bexpression* init) = 0; + + // Create an assignment statement. + virtual Bstatement* + assignment_statement(Bexpression* lhs, Bexpression* rhs, + Location) = 0; + + // Create a return statement, passing the representation of the + // function and the list of values to return. + virtual Bstatement* + return_statement(Bfunction*, const std::vector&, + Location) = 0; + + // Create an if statement. ELSE_BLOCK may be NULL. + virtual Bstatement* + if_statement(Bexpression* condition, Bblock* then_block, Bblock* else_block, + Location) = 0; + + // Create a switch statement where the case values are constants. + // CASES and STATEMENTS must have the same number of entries. If + // VALUE matches any of the list in CASES[i], which will all be + // integers, then STATEMENTS[i] is executed. STATEMENTS[i] will + // either end with a goto statement or will fall through into + // STATEMENTS[i + 1]. CASES[i] is empty for the default clause, + // which need not be last. + virtual Bstatement* + switch_statement(Bexpression* value, + const std::vector >& cases, + const std::vector& statements, + Location) = 0; + + // Create a single statement from two statements. + virtual Bstatement* + compound_statement(Bstatement*, Bstatement*) = 0; + + // Create a single statement from a list of statements. + virtual Bstatement* + statement_list(const std::vector&) = 0; + + // Blocks. + + // Create a block. The frontend will call this function when it + // starts converting a block within a function. FUNCTION is the + // current function. ENCLOSING is the enclosing block; it will be + // NULL for the top-level block in a function. VARS is the list of + // local variables defined within this block; each entry will be + // created by the local_variable function. START_LOCATION is the + // location of the start of the block, more or less the location of + // the initial curly brace. END_LOCATION is the location of the end + // of the block, more or less the location of the final curly brace. + // The statements will be added after the block is created. + virtual Bblock* + block(Bfunction* function, Bblock* enclosing, + const std::vector& vars, + Location start_location, Location end_location) = 0; + + // Add the statements to a block. The block is created first. Then + // the statements are created. Then the statements are added to the + // block. This will called exactly once per block. The vector may + // be empty if there are no statements. + virtual void + block_add_statements(Bblock*, const std::vector&) = 0; + + // Return the block as a statement. This is used to include a block + // in a list of statements. + virtual Bstatement* + block_statement(Bblock*) = 0; + + // Variables. + + // Create an error variable. This is used for cases which should + // not occur in a correct program, in order to keep the compilation + // going without crashing. + virtual Bvariable* + error_variable() = 0; + + // Create a global variable. PACKAGE_NAME is the name of the + // package where the variable is defined. PKGPATH is the package + // path for that package, from the -fgo-pkgpath or -fgo-prefix + // option. NAME is the name of the variable. BTYPE is the type of + // the variable. IS_EXTERNAL is true if the variable is defined in + // some other package. IS_HIDDEN is true if the variable is not + // exported (name begins with a lower case letter). + // IN_UNIQUE_SECTION is true if the variable should be put into a + // unique section if possible; this is intended to permit the linker + // to garbage collect the variable if it is not referenced. + // LOCATION is where the variable was defined. + virtual Bvariable* + global_variable(const std::string& package_name, + const std::string& pkgpath, + const std::string& name, + Btype* btype, + bool is_external, + bool is_hidden, + bool in_unique_section, + Location location) = 0; + + // A global variable will 1) be initialized to zero, or 2) be + // initialized to a constant value, or 3) be initialized in the init + // function. In case 2, the frontend will call + // global_variable_set_init to set the initial value. If this is + // not called, the backend should initialize a global variable to 0. + // The init function may then assign a value to it. + virtual void + global_variable_set_init(Bvariable*, Bexpression*) = 0; + + // Create a local variable. The frontend will create the local + // variables first, and then create the block which contains them. + // FUNCTION is the function in which the variable is defined. NAME + // is the name of the variable. TYPE is the type. IS_ADDRESS_TAKEN + // is true if the address of this variable is taken (this implies + // that the address does not escape the function, as otherwise the + // variable would be on the heap). LOCATION is where the variable + // is defined. For each local variable the frontend will call + // init_statement to set the initial value. + virtual Bvariable* + local_variable(Bfunction* function, const std::string& name, Btype* type, + bool is_address_taken, Location location) = 0; + + // Create a function parameter. This is an incoming parameter, not + // a result parameter (result parameters are treated as local + // variables). The arguments are as for local_variable. + virtual Bvariable* + parameter_variable(Bfunction* function, const std::string& name, + Btype* type, bool is_address_taken, + Location location) = 0; + + // Create a temporary variable. A temporary variable has no name, + // just a type. We pass in FUNCTION and BLOCK in case they are + // needed. If INIT is not NULL, the variable should be initialized + // to that value. Otherwise the initial value is irrelevant--the + // backend does not have to explicitly initialize it to zero. + // ADDRESS_IS_TAKEN is true if the programs needs to take the + // address of this temporary variable. LOCATION is the location of + // the statement or expression which requires creating the temporary + // variable, and may not be very useful. This function should + // return a variable which can be referenced later and should set + // *PSTATEMENT to a statement which initializes the variable. + virtual Bvariable* + temporary_variable(Bfunction*, Bblock*, Btype*, Bexpression* init, + bool address_is_taken, Location location, + Bstatement** pstatement) = 0; + + // Create a named immutable initialized data structure. This is + // used for type descriptors, map descriptors, and function + // descriptors. This returns a Bvariable because it corresponds to + // an initialized const variable in C. + // + // NAME is the name to use for the initialized global variable which + // this call will create. + // + // IS_HIDDEN will be true if the descriptor should only be visible + // within the current object. + // + // IS_COMMON is true if NAME may be defined by several packages, and + // the linker should merge all such definitions. If IS_COMMON is + // false, NAME should be defined in only one file. In general + // IS_COMMON will be true for the type descriptor of an unnamed type + // or a builtin type. IS_HIDDEN and IS_COMMON will never both be + // true. + // + // TYPE will be a struct type; the type of the returned expression + // must be a pointer to this struct type. + // + // We must create the named structure before we know its + // initializer, because the initializer may refer to its own + // address. After calling this the frontend will call + // immutable_struct_set_init. + virtual Bvariable* + immutable_struct(const std::string& name, bool is_hidden, bool is_common, + Btype* type, Location) = 0; + + // Set the initial value of a variable created by immutable_struct. + // The NAME, IS_HIDDEN, IS_COMMON, TYPE, and location parameters are + // the same ones passed to immutable_struct. INITIALIZER will be a + // composite literal of type TYPE. It will not contain any function + // calls or anything else that can not be put into a read-only data + // section. It may contain the address of variables created by + // immutable_struct. + virtual void + immutable_struct_set_init(Bvariable*, const std::string& name, + bool is_hidden, bool is_common, Btype* type, + Location, Bexpression* initializer) = 0; + + // Create a reference to a named immutable initialized data + // structure defined in some other package. This will be a + // structure created by a call to immutable_struct with the same + // NAME and TYPE and with IS_COMMON passed as false. This + // corresponds to an extern const global variable in C. + virtual Bvariable* + immutable_struct_reference(const std::string& name, Btype* type, + Location) = 0; + + // Labels. + + // Create a new label. NAME will be empty if this is a label + // created by the frontend for a loop construct. The location is + // where the the label is defined. + virtual Blabel* + label(Bfunction*, const std::string& name, Location) = 0; + + // Create a statement which defines a label. This statement will be + // put into the codestream at the point where the label should be + // defined. + virtual Bstatement* + label_definition_statement(Blabel*) = 0; + + // Create a goto statement to a label. + virtual Bstatement* + goto_statement(Blabel*, Location) = 0; + + // Create an expression for the address of a label. This is used to + // get the return address of a deferred function which may call + // recover. + virtual Bexpression* + label_address(Blabel*, Location) = 0; + + // Functions. + + // Create an error function. This is used for cases which should + // not occur in a correct program, in order to keep the compilation + // going without crashing. + virtual Bfunction* + error_function() = 0; + + // Declare or define a function of FNTYPE. + // NAME is the Go name of the function. ASM_NAME, if not the empty string, is + // the name that should be used in the symbol table; this will be non-empty if + // a magic extern comment is used. + // IS_VISIBLE is true if this function should be visible outside of the + // current compilation unit. IS_DECLARATION is true if this is a function + // declaration rather than a definition; the function definition will be in + // another compilation unit. + // IS_INLINABLE is true if the function can be inlined. + // DISABLE_SPLIT_STACK is true if this function may not split the stack; this + // is used for the implementation of recover. + // IN_UNIQUE_SECTION is true if this function should be put into a unique + // location if possible; this is used for field tracking. + virtual Bfunction* + function(Btype* fntype, const std::string& name, const std::string& asm_name, + bool is_visible, bool is_declaration, bool is_inlinable, + bool disable_split_stack, bool in_unique_section, Location) = 0; +}; + +// The backend interface has to define this function. + +extern Backend* go_get_backend(); + +// FIXME: Temporary helper functions while converting to new backend +// interface. + +extern Btype* tree_to_type(tree); +extern Bexpression* tree_to_expr(tree); +extern Bstatement* tree_to_stat(tree); +extern Bfunction* tree_to_function(tree); +extern Bblock* tree_to_block(tree); +extern tree type_to_tree(Btype*); +extern tree expr_to_tree(Bexpression*); +extern tree stat_to_tree(Bstatement*); +extern tree block_to_tree(Bblock*); +extern tree var_to_tree(Bvariable*); +extern tree function_to_tree(Bfunction*); + +#endif // !defined(GO_BACKEND_H) diff --git a/gcc-4.9/gcc/go/gofrontend/dataflow.cc b/gcc-4.9/gcc/go/gofrontend/dataflow.cc new file mode 100644 index 000000000..572ab3631 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/dataflow.cc @@ -0,0 +1,278 @@ +// dataflow.cc -- Go frontend dataflow. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "gogo.h" +#include "expressions.h" +#include "statements.h" +#include "dataflow.h" + +// This class is used to traverse the tree to look for uses of +// variables. + +class Dataflow_traverse_expressions : public Traverse +{ + public: + Dataflow_traverse_expressions(Dataflow* dataflow, Statement* statement) + : Traverse(traverse_blocks | traverse_expressions), + dataflow_(dataflow), statement_(statement) + { } + + protected: + // Only look at top-level expressions: do not descend into blocks. + // They will be examined via Dataflow_traverse_statements. + int + block(Block*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + expression(Expression**); + + private: + // The dataflow information. + Dataflow* dataflow_; + // The Statement in which we are looking. + Statement* statement_; +}; + +// Given an expression, return the Named_object that it refers to, if +// it is a local variable. + +static Named_object* +get_var(Expression* expr) +{ + Var_expression* ve = expr->var_expression(); + if (ve == NULL) + return NULL; + Named_object* no = ve->named_object(); + go_assert(no->is_variable() || no->is_result_variable()); + if (no->is_variable() && no->var_value()->is_global()) + return NULL; + return no; +} + +// Look for a reference to a variable in an expression. + +int +Dataflow_traverse_expressions::expression(Expression** expr) +{ + Named_object* no = get_var(*expr); + if (no != NULL) + this->dataflow_->add_ref(no, this->statement_); + return TRAVERSE_CONTINUE; +} + +// This class is used to handle an assignment statement. + +class Dataflow_traverse_assignment : public Traverse_assignments +{ + public: + Dataflow_traverse_assignment(Dataflow* dataflow, Statement* statement) + : dataflow_(dataflow), statement_(statement) + { } + + protected: + void + initialize_variable(Named_object*); + + void + assignment(Expression** lhs, Expression** rhs); + + void + value(Expression**, bool, bool); + + private: + // The dataflow information. + Dataflow* dataflow_; + // The Statement in which we are looking. + Statement* statement_; +}; + +// Handle a variable initialization. + +void +Dataflow_traverse_assignment::initialize_variable(Named_object* var) +{ + Expression* init = var->var_value()->init(); + this->dataflow_->add_def(var, init, this->statement_, true); + if (init != NULL) + { + Expression* e = init; + this->value(&e, true, true); + go_assert(e == init); + } +} + +// Handle an assignment in a statement. + +void +Dataflow_traverse_assignment::assignment(Expression** plhs, Expression** prhs) +{ + Named_object* no = get_var(*plhs); + if (no != NULL) + { + Expression* rhs = prhs == NULL ? NULL : *prhs; + this->dataflow_->add_def(no, rhs, this->statement_, false); + } + else + { + // If this is not a variable it may be some computed lvalue, and + // we want to look for references to variables in that lvalue. + this->value(plhs, false, false); + } + if (prhs != NULL) + this->value(prhs, true, false); +} + +// Handle a value in a statement. + +void +Dataflow_traverse_assignment::value(Expression** pexpr, bool, bool) +{ + Named_object* no = get_var(*pexpr); + if (no != NULL) + this->dataflow_->add_ref(no, this->statement_); + else + { + Dataflow_traverse_expressions dte(this->dataflow_, this->statement_); + Expression::traverse(pexpr, &dte); + } +} + +// This class is used to traverse the tree to look for statements. + +class Dataflow_traverse_statements : public Traverse +{ + public: + Dataflow_traverse_statements(Dataflow* dataflow) + : Traverse(traverse_statements), + dataflow_(dataflow) + { } + + protected: + int + statement(Block*, size_t* pindex, Statement*); + + private: + // The dataflow information. + Dataflow* dataflow_; +}; + +// For each Statement, we look for expressions. + +int +Dataflow_traverse_statements::statement(Block* block, size_t* pindex, + Statement *statement) +{ + Dataflow_traverse_assignment dta(this->dataflow_, statement); + if (!statement->traverse_assignments(&dta)) + { + Dataflow_traverse_expressions dte(this->dataflow_, statement); + statement->traverse(block, pindex, &dte); + } + return TRAVERSE_CONTINUE; +} + +// Compare variables. + +bool +Dataflow::Compare_vars::operator()(const Named_object* no1, + const Named_object* no2) const +{ + if (no1->name() < no2->name()) + return true; + if (no1->name() > no2->name()) + return false; + + // We can have two different variables with the same name. + Location loc1 = no1->location(); + Location loc2 = no2->location(); + if (loc1 < loc2) + return false; + if (loc1 > loc2) + return true; + + if (no1 == no2) + return false; + + // We can't have two variables with the same name in the same + // location. + go_unreachable(); +} + +// Class Dataflow. + +Dataflow::Dataflow() + : defs_(), refs_() +{ +} + +// Build the dataflow information. + +void +Dataflow::initialize(Gogo* gogo) +{ + Dataflow_traverse_statements dts(this); + gogo->traverse(&dts); +} + +// Add a definition of a variable. + +void +Dataflow::add_def(Named_object* var, Expression* val, Statement* statement, + bool is_init) +{ + Defs* defnull = NULL; + std::pair ins = + this->defs_.insert(std::make_pair(var, defnull)); + if (ins.second) + ins.first->second = new Defs; + Def def; + def.statement = statement; + def.val = val; + def.is_init = is_init; + ins.first->second->push_back(def); +} + +// Add a reference to a variable. + +void +Dataflow::add_ref(Named_object* var, Statement* statement) +{ + Refs* refnull = NULL; + std::pair ins = + this->refs_.insert(std::make_pair(var, refnull)); + if (ins.second) + ins.first->second = new Refs; + Ref ref; + ref.statement = statement; + ins.first->second->push_back(ref); +} + +// Return the definitions of a variable. + +const Dataflow::Defs* +Dataflow::find_defs(Named_object* var) const +{ + Defmap::const_iterator p = this->defs_.find(var); + if (p == this->defs_.end()) + return NULL; + else + return p->second; +} + +// Return the references of a variable. + +const Dataflow::Refs* +Dataflow::find_refs(Named_object* var) const +{ + Refmap::const_iterator p = this->refs_.find(var); + if (p == this->refs_.end()) + return NULL; + else + return p->second; +} diff --git a/gcc-4.9/gcc/go/gofrontend/dataflow.h b/gcc-4.9/gcc/go/gofrontend/dataflow.h new file mode 100644 index 000000000..a75c8e661 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/dataflow.h @@ -0,0 +1,91 @@ +// dataflow.h -- Go frontend dataflow. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_DATAFLOW_H +#define GO_DATAFLOW_H + +class Expression; +class Named_object; +class Statement; + +// Dataflow information about the Go program. + +class Dataflow +{ + public: + // A variable definition. + struct Def + { + // The statement where the variable is defined. + Statement* statement; + // The value to which the variable is set. This may be NULL. + Expression* val; + // Whether this is an initialization of the variable. + bool is_init; + }; + + // A variable reference. + struct Ref + { + // The statement where the variable is referenced. + Statement* statement; + }; + + // A list of defs. + typedef std::vector Defs; + + // A list of refs. + typedef std::vector Refs; + + Dataflow(); + + // Initialize the dataflow information. + void + initialize(Gogo*); + + // Add a definition of a variable. STATEMENT assigns a value to + // VAR. VAL is the value if it is known, NULL otherwise. + void + add_def(Named_object* var, Expression* val, Statement* statement, + bool is_init); + + // Add a reference to a variable. VAR is the variable, and + // STATEMENT is the statement which refers to it. + void + add_ref(Named_object* var, Statement* statement); + + // Return the definitions of VAR--the places where it is set. + const Defs* + find_defs(Named_object* var) const; + + // Return the references to VAR--the places where it is used. + const Refs* + find_refs(Named_object* var) const; + + private: + // Order variables in the map. + struct Compare_vars + { + bool + operator()(const Named_object*, const Named_object*) const; + }; + + // Map from variables to a list of defs of the variable. We use a + // map rather than a hash table because the order in which we + // process variables may affect the resulting code. + typedef std::map Defmap; + + // Map from variables to a list of refs to the vairable. + typedef std::map Refmap; + + // Variable defs. + Defmap defs_; + // Variable refs; + Refmap refs_; +}; + + +#endif // !defined(GO_DATAFLOW_H) diff --git a/gcc-4.9/gcc/go/gofrontend/export.cc b/gcc-4.9/gcc/go/gofrontend/export.cc new file mode 100644 index 000000000..13c61a589 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/export.cc @@ -0,0 +1,491 @@ +// export.cc -- Export declarations in Go frontend. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "sha1.h" + +#include "go-c.h" + +#include "gogo.h" +#include "types.h" +#include "statements.h" +#include "export.h" + +// This file handles exporting global declarations. + +// Class Export. + +// Version 1 magic number. + +const int Export::v1_magic_len; + +const char Export::v1_magic[Export::v1_magic_len] = + { + 'v', '1', ';', '\n' + }; + +const int Export::v1_checksum_len; + +// Constructor. + +Export::Export(Stream* stream) + : stream_(stream), type_refs_(), type_index_(1), packages_() +{ +} + +// A functor to sort Named_object pointers by name. + +struct Sort_bindings +{ + bool + operator()(const Named_object* n1, const Named_object* n2) const + { return n1->name() < n2->name(); } +}; + +// Return true if we should export NO. + +static bool +should_export(Named_object* no) +{ + // We only export objects which are locally defined. + if (no->package() != NULL) + return false; + + // We don't export packages. + if (no->is_package()) + return false; + + // We don't export hidden names. + if (Gogo::is_hidden_name(no->name())) + return false; + + // We don't export nested functions. + if (no->is_function() && no->func_value()->enclosing() != NULL) + return false; + + // We don't export thunks. + if (no->is_function() && Gogo::is_thunk(no)) + return false; + + // Methods are exported with the type, not here. + if (no->is_function() + && no->func_value()->type()->is_method()) + return false; + if (no->is_function_declaration() + && no->func_declaration_value()->type()->is_method()) + return false; + + // Don't export dummy global variables created for initializers when + // used with sinks. + if (no->is_variable() && no->name()[0] == '_' && no->name()[1] == '.') + return false; + + return true; +} + +// Export those identifiers marked for exporting. + +void +Export::export_globals(const std::string& package_name, + const std::string& pkgpath, + int package_priority, + const std::map& imports, + const std::string& import_init_fn, + const std::set& imported_init_fns, + const Bindings* bindings) +{ + // If there have been any errors so far, don't try to export + // anything. That way the export code doesn't have to worry about + // mismatched types or other confusions. + if (saw_errors()) + return; + + // Export the symbols in sorted order. That will reduce cases where + // irrelevant changes to the source code affect the exported + // interface. + std::vector exports; + exports.reserve(bindings->size_definitions()); + + for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); + p != bindings->end_definitions(); + ++p) + if (should_export(*p)) + exports.push_back(*p); + + for (Bindings::const_declarations_iterator p = + bindings->begin_declarations(); + p != bindings->end_declarations(); + ++p) + { + // We export a function declaration as it may be implemented in + // supporting C code. We do not export type declarations. + if (p->second->is_function_declaration() + && should_export(p->second)) + exports.push_back(p->second); + } + + std::sort(exports.begin(), exports.end(), Sort_bindings()); + + // Although the export data is readable, at least this version is, + // it is conceptually a binary format. Start with a four byte + // verison number. + this->write_bytes(Export::v1_magic, Export::v1_magic_len); + + // The package name. + this->write_c_string("package "); + this->write_string(package_name); + this->write_c_string(";\n"); + + // The package path, used for all global symbols. + this->write_c_string("pkgpath "); + this->write_string(pkgpath); + this->write_c_string(";\n"); + + // The package priority. + char buf[100]; + snprintf(buf, sizeof buf, "priority %d;\n", package_priority); + this->write_c_string(buf); + + this->write_imports(imports); + + this->write_imported_init_fns(package_name, package_priority, import_init_fn, + imported_init_fns); + + // FIXME: It might be clever to add something about the processor + // and ABI being used, although ideally any problems in that area + // would be caught by the linker. + + for (std::vector::const_iterator p = exports.begin(); + p != exports.end(); + ++p) + (*p)->export_named_object(this); + + std::string checksum = this->stream_->checksum(); + std::string s = "checksum "; + for (std::string::const_iterator p = checksum.begin(); + p != checksum.end(); + ++p) + { + unsigned char c = *p; + unsigned int dig = c >> 4; + s += dig < 10 ? '0' + dig : 'A' + dig - 10; + dig = c & 0xf; + s += dig < 10 ? '0' + dig : 'A' + dig - 10; + } + s += ";\n"; + this->stream_->write_checksum(s); +} + +// Sort imported packages. + +static bool +import_compare(const std::pair& a, + const std::pair& b) +{ + return a.first < b.first; +} + +// Write out the imported packages. + +void +Export::write_imports(const std::map& imports) +{ + // Sort the imports for more consistent output. + std::vector > imp; + for (std::map::const_iterator p = imports.begin(); + p != imports.end(); + ++p) + imp.push_back(std::make_pair(p->first, p->second)); + + std::sort(imp.begin(), imp.end(), import_compare); + + for (std::vector >::const_iterator p = + imp.begin(); + p != imp.end(); + ++p) + { + this->write_c_string("import "); + this->write_string(p->second->package_name()); + this->write_c_string(" "); + this->write_string(p->second->pkgpath()); + this->write_c_string(" \""); + this->write_string(p->first); + this->write_c_string("\";\n"); + + this->packages_.insert(p->second); + } +} + +// Write out the initialization functions which need to run for this +// package. + +void +Export::write_imported_init_fns( + const std::string& package_name, + int priority, + const std::string& import_init_fn, + const std::set& imported_init_fns) +{ + if (import_init_fn.empty() && imported_init_fns.empty()) + return; + + this->write_c_string("init"); + + if (!import_init_fn.empty()) + { + this->write_c_string(" "); + this->write_string(package_name); + this->write_c_string(" "); + this->write_string(import_init_fn); + char buf[100]; + snprintf(buf, sizeof buf, " %d", priority); + this->write_c_string(buf); + } + + if (!imported_init_fns.empty()) + { + // Sort the list of functions for more consistent output. + std::vector v; + for (std::set::const_iterator p = imported_init_fns.begin(); + p != imported_init_fns.end(); + ++p) + v.push_back(*p); + std::sort(v.begin(), v.end()); + + for (std::vector::const_iterator p = v.begin(); + p != v.end(); + ++p) + { + this->write_c_string(" "); + this->write_string(p->package_name()); + this->write_c_string(" "); + this->write_string(p->init_name()); + char buf[100]; + snprintf(buf, sizeof buf, " %d", p->priority()); + this->write_c_string(buf); + } + } + + this->write_c_string(";\n"); +} + +// Write a name to the export stream. + +void +Export::write_name(const std::string& name) +{ + if (name.empty()) + this->write_c_string("?"); + else + this->write_string(Gogo::message_name(name)); +} + +// Export a type. We have to ensure that on import we create a single +// Named_type node for each named type. We do this by keeping a hash +// table mapping named types to reference numbers. The first time we +// see a named type we assign it a reference number by making an entry +// in the hash table. If we see it again, we just refer to the +// reference number. + +// Named types are, of course, associated with packages. Note that we +// may see a named type when importing one package, and then later see +// the same named type when importing a different package. The home +// package may or may not be imported during this compilation. The +// reference number scheme has to get this all right. Basic approach +// taken from "On the Linearization of Graphs and Writing Symbol +// Files" by Robert Griesemer. + +void +Export::write_type(const Type* type) +{ + // We don't want to assign a reference number to a forward + // declaration to a type which was defined later. + type = type->forwarded(); + + Type_refs::const_iterator p = this->type_refs_.find(type); + if (p != this->type_refs_.end()) + { + // This type was already in the table. + int index = p->second; + go_assert(index != 0); + char buf[30]; + snprintf(buf, sizeof buf, "", index); + this->write_c_string(buf); + return; + } + + const Named_type* named_type = type->named_type(); + const Forward_declaration_type* forward = type->forward_declaration_type(); + + int index = this->type_index_; + ++this->type_index_; + + char buf[30]; + snprintf(buf, sizeof buf, "write_c_string(buf); + + if (named_type != NULL || forward != NULL) + { + const Named_object* named_object; + if (named_type != NULL) + { + // The builtin types should have been predefined. + go_assert(!Linemap::is_predeclared_location(named_type->location()) + || (named_type->named_object()->package()->package_name() + == "unsafe")); + named_object = named_type->named_object(); + } + else + named_object = forward->named_object(); + + const Package* package = named_object->package(); + + std::string s = "\""; + if (package != NULL && !Gogo::is_hidden_name(named_object->name())) + { + s += package->pkgpath(); + s += '.'; + } + s += named_object->name(); + s += "\" "; + this->write_string(s); + + // It is possible that this type was imported indirectly, and is + // not in a package in the import list. If we have not + // mentioned this package before, write out the package name + // here so that any package importing this one will know it. + if (package != NULL + && this->packages_.find(package) == this->packages_.end()) + { + this->write_c_string("\""); + this->write_string(package->package_name()); + this->packages_.insert(package); + this->write_c_string("\" "); + } + + // We must add a named type to the table now, since the + // definition of the type may refer to the named type via a + // pointer. + this->type_refs_[type] = index; + } + + type->export_type(this); + + this->write_c_string(">"); + + if (named_type == NULL) + this->type_refs_[type] = index; +} + +// Add the builtin types to the export table. + +void +Export::register_builtin_types(Gogo* gogo) +{ + this->register_builtin_type(gogo, "int8", BUILTIN_INT8); + this->register_builtin_type(gogo, "int16", BUILTIN_INT16); + this->register_builtin_type(gogo, "int32", BUILTIN_INT32); + this->register_builtin_type(gogo, "int64", BUILTIN_INT64); + this->register_builtin_type(gogo, "uint8", BUILTIN_UINT8); + this->register_builtin_type(gogo, "uint16", BUILTIN_UINT16); + this->register_builtin_type(gogo, "uint32", BUILTIN_UINT32); + this->register_builtin_type(gogo, "uint64", BUILTIN_UINT64); + this->register_builtin_type(gogo, "float32", BUILTIN_FLOAT32); + this->register_builtin_type(gogo, "float64", BUILTIN_FLOAT64); + this->register_builtin_type(gogo, "complex64", BUILTIN_COMPLEX64); + this->register_builtin_type(gogo, "complex128", BUILTIN_COMPLEX128); + this->register_builtin_type(gogo, "int", BUILTIN_INT); + this->register_builtin_type(gogo, "uint", BUILTIN_UINT); + this->register_builtin_type(gogo, "uintptr", BUILTIN_UINTPTR); + this->register_builtin_type(gogo, "bool", BUILTIN_BOOL); + this->register_builtin_type(gogo, "string", BUILTIN_STRING); + this->register_builtin_type(gogo, "error", BUILTIN_ERROR); + this->register_builtin_type(gogo, "byte", BUILTIN_BYTE); + this->register_builtin_type(gogo, "rune", BUILTIN_RUNE); +} + +// Register one builtin type in the export table. + +void +Export::register_builtin_type(Gogo* gogo, const char* name, Builtin_code code) +{ + Named_object* named_object = gogo->lookup_global(name); + go_assert(named_object != NULL && named_object->is_type()); + std::pair ins = + this->type_refs_.insert(std::make_pair(named_object->type_value(), code)); + go_assert(ins.second); + + // We also insert the underlying type. We can see the underlying + // type at least for string and bool. We skip the type aliases byte + // and rune here. + if (code != BUILTIN_BYTE && code != BUILTIN_RUNE) + { + Type* real_type = named_object->type_value()->real_type(); + ins = this->type_refs_.insert(std::make_pair(real_type, code)); + go_assert(ins.second); + } +} + +// Class Export::Stream. + +Export::Stream::Stream() +{ + this->checksum_ = new sha1_ctx; + memset(this->checksum_, 0, sizeof(sha1_ctx)); + sha1_init_ctx(this->checksum_); +} + +Export::Stream::~Stream() +{ +} + +// Write bytes to the stream. This keeps a checksum of bytes as they +// go by. + +void +Export::Stream::write_and_sum_bytes(const char* bytes, size_t length) +{ + sha1_process_bytes(bytes, length, this->checksum_); + this->do_write(bytes, length); +} + +// Get the checksum. + +std::string +Export::Stream::checksum() +{ + // Use a union to provide the required alignment. + union + { + char checksum[Export::v1_checksum_len]; + long align; + } u; + sha1_finish_ctx(this->checksum_, u.checksum); + return std::string(u.checksum, Export::v1_checksum_len); +} + +// Write the checksum string to the export data. + +void +Export::Stream::write_checksum(const std::string& s) +{ + this->do_write(s.data(), s.length()); +} + +// Class Stream_to_section. + +Stream_to_section::Stream_to_section() +{ +} + +// Write data to a section. + +void +Stream_to_section::do_write(const char* bytes, size_t length) +{ + go_write_export_data (bytes, length); +} diff --git a/gcc-4.9/gcc/go/gofrontend/export.h b/gcc-4.9/gcc/go/gofrontend/export.h new file mode 100644 index 000000000..c6a481051 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/export.h @@ -0,0 +1,201 @@ +// export.h -- Export declarations in Go frontend. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_EXPORT_H +#define GO_EXPORT_H + +#include "string-dump.h" + +struct sha1_ctx; +class Gogo; +class Import_init; +class Bindings; +class Type; +class Package; + +// Codes used for the builtin types. These are all negative to make +// them easily distinct from the codes assigned by Export::write_type. +// Note that these codes may not be changed! Changing them would +// break existing export data. + +enum Builtin_code +{ + BUILTIN_INT8 = -1, + BUILTIN_INT16 = -2, + BUILTIN_INT32 = -3, + BUILTIN_INT64 = -4, + BUILTIN_UINT8 = -5, + BUILTIN_UINT16 = -6, + BUILTIN_UINT32 = -7, + BUILTIN_UINT64 = -8, + BUILTIN_FLOAT32 = -9, + BUILTIN_FLOAT64 = -10, + BUILTIN_INT = -11, + BUILTIN_UINT = -12, + BUILTIN_UINTPTR = -13, + BUILTIN_BOOL = -15, + BUILTIN_STRING = -16, + BUILTIN_COMPLEX64 = -17, + BUILTIN_COMPLEX128 = -18, + BUILTIN_ERROR = -19, + BUILTIN_BYTE = -20, + BUILTIN_RUNE = -21, + + SMALLEST_BUILTIN_CODE = -21 +}; + +// This class manages exporting Go declarations. It handles the main +// loop of exporting. A pointer to this class is also passed to the +// various specific export implementations. + +class Export : public String_dump +{ + public: + // The Stream class is an interface used to output the exported + // information. The caller should instantiate a child of this + // class. + class Stream + { + public: + Stream(); + virtual ~Stream(); + + // Write a string. Implements the String_dump interface. + void + write_string(const std::string& s) + { this->write_and_sum_bytes(s.data(), s.length()); } + + // Write a nul terminated string. Implements the String_dump interface. + void + write_c_string(const char* s) + { this->write_and_sum_bytes(s, strlen(s)); } + + // Write some bytes. + void + write_bytes(const char* bytes, size_t length) + { this->write_and_sum_bytes(bytes, length); } + + // Return the raw bytes of the checksum data. + std::string + checksum(); + + // Write a checksum string to the stream. This will be called at + // the end of the other output. + void + write_checksum(const std::string&); + + protected: + // This function is called with data to export. This data must be + // made available as a contiguous stream for the importer. + virtual void + do_write(const char* bytes, size_t length) = 0; + + private: + void + write_and_sum_bytes(const char*, size_t); + + // The checksum. + sha1_ctx* checksum_; + }; + + Export(Stream*); + + // The magic code for version 1 export data. + static const int v1_magic_len = 4; + static const char v1_magic[v1_magic_len]; + + // The length of the v1 checksum string. + static const int v1_checksum_len = 20; + + // Register the builtin types. + void + register_builtin_types(Gogo*); + + // Export the identifiers in BINDINGS which are marked for export. + // The exporting is done via a series of calls to THIS->STREAM_. If + // is nothing to export, this->stream_->write will not be called. + // PKGPATH is the package path. + // PACKAGE_PRIORITY is the priority to use for this package. + // IMPORT_INIT_FN is the name of the import initialization function + // for this package; it will be empty if none is needed. + // IMPORTED_INIT_FNS is the list of initialization functions for + // imported packages. + void + export_globals(const std::string& package_name, + const std::string& pkgpath, + int package_priority, + const std::map& imports, + const std::string& import_init_fn, + const std::set& imported_init_fns, + const Bindings* bindings); + + // Write a string to the export stream. + void + write_string(const std::string& s) + { this->stream_->write_string(s); } + + // Write a nul terminated string to the export stream. + void + write_c_string(const char* s) + { this->stream_->write_c_string(s); } + + // Write some bytes to the export stream. + void + write_bytes(const char* bytes, size_t length) + { this->stream_->write_bytes(bytes, length); } + + // Write a name to the export stream. If NAME is empty, write "?". + void + write_name(const std::string& name); + + // Write out a type. This handles references back to previous + // definitions. + void + write_type(const Type*); + + private: + Export(const Export&); + Export& operator=(const Export&); + + // Write out the imported packages. + void + write_imports(const std::map& imports); + + // Write out the imported initialization functions. + void + write_imported_init_fns(const std::string& package_name, int priority, + const std::string&, const std::set&); + + // Register one builtin type. + void + register_builtin_type(Gogo*, const char* name, Builtin_code); + + // Mapping from Type objects to a constant index. + typedef Unordered_map(const Type*, int) Type_refs; + + // The stream to which we are writing data. + Stream* stream_; + // Type mappings. + Type_refs type_refs_; + // Index number of next type. + int type_index_; + // Packages we have written out. + Unordered_set(const Package*) packages_; +}; + +// An export streamer which puts the export stream in a named section. + +class Stream_to_section : public Export::Stream +{ + public: + Stream_to_section(); + + protected: + void + do_write(const char*, size_t); +}; + +#endif // !defined(GO_EXPORT_H) diff --git a/gcc-4.9/gcc/go/gofrontend/expressions.cc b/gcc-4.9/gcc/go/gofrontend/expressions.cc new file mode 100644 index 000000000..643a233ba --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/expressions.cc @@ -0,0 +1,15900 @@ +// expressions.cc -- Go frontend expression handling. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include + +#include "toplev.h" +#include "intl.h" +#include "tree.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "gimple-expr.h" +#include "tree-iterator.h" +#include "convert.h" +#include "real.h" +#include "realmpfr.h" + +#include "go-c.h" +#include "gogo.h" +#include "types.h" +#include "export.h" +#include "import.h" +#include "statements.h" +#include "lex.h" +#include "runtime.h" +#include "backend.h" +#include "expressions.h" +#include "ast-dump.h" + +// Class Expression. + +Expression::Expression(Expression_classification classification, + Location location) + : classification_(classification), location_(location) +{ +} + +Expression::~Expression() +{ +} + +// Traverse the expressions. + +int +Expression::traverse(Expression** pexpr, Traverse* traverse) +{ + Expression* expr = *pexpr; + if ((traverse->traverse_mask() & Traverse::traverse_expressions) != 0) + { + int t = traverse->expression(pexpr); + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + else if (t == TRAVERSE_SKIP_COMPONENTS) + return TRAVERSE_CONTINUE; + } + return expr->do_traverse(traverse); +} + +// Traverse subexpressions of this expression. + +int +Expression::traverse_subexpressions(Traverse* traverse) +{ + return this->do_traverse(traverse); +} + +// Default implementation for do_traverse for child classes. + +int +Expression::do_traverse(Traverse*) +{ + return TRAVERSE_CONTINUE; +} + +// This virtual function is called by the parser if the value of this +// expression is being discarded. By default, we give an error. +// Expressions with side effects override. + +bool +Expression::do_discarding_value() +{ + this->unused_value_error(); + return false; +} + +// This virtual function is called to export expressions. This will +// only be used by expressions which may be constant. + +void +Expression::do_export(Export*) const +{ + go_unreachable(); +} + +// Give an error saying that the value of the expression is not used. + +void +Expression::unused_value_error() +{ + this->report_error(_("value computed is not used")); +} + +// Note that this expression is an error. This is called by children +// when they discover an error. + +void +Expression::set_is_error() +{ + this->classification_ = EXPRESSION_ERROR; +} + +// For children to call to report an error conveniently. + +void +Expression::report_error(const char* msg) +{ + error_at(this->location_, "%s", msg); + this->set_is_error(); +} + +// Set types of variables and constants. This is implemented by the +// child class. + +void +Expression::determine_type(const Type_context* context) +{ + this->do_determine_type(context); +} + +// Set types when there is no context. + +void +Expression::determine_type_no_context() +{ + Type_context context; + this->do_determine_type(&context); +} + +// Return a tree handling any conversions which must be done during +// assignment. + +tree +Expression::convert_for_assignment(Translate_context* context, Type* lhs_type, + Type* rhs_type, tree rhs_tree, + Location location) +{ + if (lhs_type->is_error() || rhs_type->is_error()) + return error_mark_node; + + if (rhs_tree == error_mark_node || TREE_TYPE(rhs_tree) == error_mark_node) + return error_mark_node; + + Gogo* gogo = context->gogo(); + + tree lhs_type_tree = type_to_tree(lhs_type->get_backend(gogo)); + if (lhs_type_tree == error_mark_node) + return error_mark_node; + + if (lhs_type->forwarded() != rhs_type->forwarded() + && lhs_type->interface_type() != NULL) + { + if (rhs_type->interface_type() == NULL) + return Expression::convert_type_to_interface(context, lhs_type, + rhs_type, rhs_tree, + location); + else + return Expression::convert_interface_to_interface(context, lhs_type, + rhs_type, rhs_tree, + false, location); + } + else if (lhs_type->forwarded() != rhs_type->forwarded() + && rhs_type->interface_type() != NULL) + return Expression::convert_interface_to_type(context, lhs_type, rhs_type, + rhs_tree, location); + else if (lhs_type->is_slice_type() && rhs_type->is_nil_type()) + { + // Assigning nil to an open array. + go_assert(TREE_CODE(lhs_type_tree) == RECORD_TYPE); + + vec *init; + vec_alloc(init, 3); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + tree field = TYPE_FIELDS(lhs_type_tree); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), + "__values") == 0); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), null_pointer_node); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), + "__count") == 0); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), integer_zero_node); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), + "__capacity") == 0); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), integer_zero_node); + + tree val = build_constructor(lhs_type_tree, init); + TREE_CONSTANT(val) = 1; + + return val; + } + else if (rhs_type->is_nil_type()) + { + // The left hand side should be a pointer type at the tree + // level. + go_assert(POINTER_TYPE_P(lhs_type_tree)); + return fold_convert(lhs_type_tree, null_pointer_node); + } + else if (lhs_type_tree == TREE_TYPE(rhs_tree)) + { + // No conversion is needed. + return rhs_tree; + } + else if (POINTER_TYPE_P(lhs_type_tree) + || INTEGRAL_TYPE_P(lhs_type_tree) + || SCALAR_FLOAT_TYPE_P(lhs_type_tree) + || COMPLEX_FLOAT_TYPE_P(lhs_type_tree)) + return fold_convert_loc(location.gcc_location(), lhs_type_tree, rhs_tree); + else if ((TREE_CODE(lhs_type_tree) == RECORD_TYPE + && TREE_CODE(TREE_TYPE(rhs_tree)) == RECORD_TYPE) + || (TREE_CODE(lhs_type_tree) == ARRAY_TYPE + && TREE_CODE(TREE_TYPE(rhs_tree)) == ARRAY_TYPE)) + { + // Avoid confusion from zero sized variables which may be + // represented as non-zero-sized. + if (int_size_in_bytes(lhs_type_tree) == 0 + || int_size_in_bytes(TREE_TYPE(rhs_tree)) == 0) + return rhs_tree; + + // This conversion must be permitted by Go, or we wouldn't have + // gotten here. + go_assert(int_size_in_bytes(lhs_type_tree) + == int_size_in_bytes(TREE_TYPE(rhs_tree))); + return fold_build1_loc(location.gcc_location(), VIEW_CONVERT_EXPR, + lhs_type_tree, rhs_tree); + } + else + { + go_assert(useless_type_conversion_p(lhs_type_tree, TREE_TYPE(rhs_tree))); + return rhs_tree; + } +} + +// Return a tree for a conversion from a non-interface type to an +// interface type. + +tree +Expression::convert_type_to_interface(Translate_context* context, + Type* lhs_type, Type* rhs_type, + tree rhs_tree, Location location) +{ + Gogo* gogo = context->gogo(); + Interface_type* lhs_interface_type = lhs_type->interface_type(); + bool lhs_is_empty = lhs_interface_type->is_empty(); + + // Since RHS_TYPE is a static type, we can create the interface + // method table at compile time. + + // When setting an interface to nil, we just set both fields to + // NULL. + if (rhs_type->is_nil_type()) + { + Btype* lhs_btype = lhs_type->get_backend(gogo); + return expr_to_tree(gogo->backend()->zero_expression(lhs_btype)); + } + + // This should have been checked already. + go_assert(lhs_interface_type->implements_interface(rhs_type, NULL)); + + tree lhs_type_tree = type_to_tree(lhs_type->get_backend(gogo)); + if (lhs_type_tree == error_mark_node) + return error_mark_node; + + // An interface is a tuple. If LHS_TYPE is an empty interface type, + // then the first field is the type descriptor for RHS_TYPE. + // Otherwise it is the interface method table for RHS_TYPE. + tree first_field_value; + if (lhs_is_empty) + { + Bexpression* rhs_bexpr = + rhs_type->type_descriptor_pointer(gogo, location); + first_field_value = expr_to_tree(rhs_bexpr); + } + else + { + // Build the interface method table for this interface and this + // 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 && 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 = + 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); + } + if (first_field_value == error_mark_node) + return error_mark_node; + + // Start building a constructor for the value we will return. + + vec *init; + vec_alloc(init, 2); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + tree field = TYPE_FIELDS(lhs_type_tree); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), + (lhs_is_empty ? "__type_descriptor" : "__methods")) == 0); + elt->index = field; + elt->value = fold_convert_loc(location.gcc_location(), TREE_TYPE(field), + first_field_value); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0); + elt->index = field; + + if (rhs_type->points_to() != NULL) + { + // We are assigning a pointer to the interface; the interface + // holds the pointer itself. + elt->value = rhs_tree; + return build_constructor(lhs_type_tree, init); + } + + // We are assigning a non-pointer value to the interface; the + // interface gets a copy of the value in the heap. + + tree object_size = TYPE_SIZE_UNIT(TREE_TYPE(rhs_tree)); + + tree space = gogo->allocate_memory(rhs_type, object_size, location); + space = fold_convert_loc(location.gcc_location(), + build_pointer_type(TREE_TYPE(rhs_tree)), space); + space = save_expr(space); + + tree ref = build_fold_indirect_ref_loc(location.gcc_location(), space); + TREE_THIS_NOTRAP(ref) = 1; + tree set = fold_build2_loc(location.gcc_location(), MODIFY_EXPR, + void_type_node, ref, rhs_tree); + + elt->value = fold_convert_loc(location.gcc_location(), TREE_TYPE(field), + space); + + return build2(COMPOUND_EXPR, lhs_type_tree, set, + build_constructor(lhs_type_tree, init)); +} + +// Return a tree for the type descriptor of RHS_TREE, which has +// interface type RHS_TYPE. If RHS_TREE is nil the result will be +// NULL. + +tree +Expression::get_interface_type_descriptor(Translate_context*, + Type* rhs_type, tree rhs_tree, + Location location) +{ + tree rhs_type_tree = TREE_TYPE(rhs_tree); + go_assert(TREE_CODE(rhs_type_tree) == RECORD_TYPE); + tree rhs_field = TYPE_FIELDS(rhs_type_tree); + tree v = build3(COMPONENT_REF, TREE_TYPE(rhs_field), rhs_tree, rhs_field, + NULL_TREE); + if (rhs_type->interface_type()->is_empty()) + { + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(rhs_field)), + "__type_descriptor") == 0); + return v; + } + + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(rhs_field)), "__methods") + == 0); + go_assert(POINTER_TYPE_P(TREE_TYPE(v))); + v = save_expr(v); + tree v1 = build_fold_indirect_ref_loc(location.gcc_location(), v); + go_assert(TREE_CODE(TREE_TYPE(v1)) == RECORD_TYPE); + tree f = TYPE_FIELDS(TREE_TYPE(v1)); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(f)), "__type_descriptor") + == 0); + v1 = build3(COMPONENT_REF, TREE_TYPE(f), v1, f, NULL_TREE); + + tree eq = fold_build2_loc(location.gcc_location(), EQ_EXPR, boolean_type_node, + v, fold_convert_loc(location.gcc_location(), + TREE_TYPE(v), + null_pointer_node)); + tree n = fold_convert_loc(location.gcc_location(), TREE_TYPE(v1), + null_pointer_node); + return fold_build3_loc(location.gcc_location(), COND_EXPR, TREE_TYPE(v1), + eq, n, v1); +} + +// Return a tree for the conversion of an interface type to an +// interface type. + +tree +Expression::convert_interface_to_interface(Translate_context* context, + Type *lhs_type, Type *rhs_type, + tree rhs_tree, bool for_type_guard, + Location location) +{ + Gogo* gogo = context->gogo(); + Interface_type* lhs_interface_type = lhs_type->interface_type(); + bool lhs_is_empty = lhs_interface_type->is_empty(); + + tree lhs_type_tree = type_to_tree(lhs_type->get_backend(gogo)); + if (lhs_type_tree == error_mark_node) + return error_mark_node; + + // In the general case this requires runtime examination of the type + // method table to match it up with the interface methods. + + // FIXME: If all of the methods in the right hand side interface + // also appear in the left hand side interface, then we don't need + // to do a runtime check, although we still need to build a new + // method table. + + // Get the type descriptor for the right hand side. This will be + // NULL for a nil interface. + + if (!DECL_P(rhs_tree)) + rhs_tree = save_expr(rhs_tree); + + tree rhs_type_descriptor = + Expression::get_interface_type_descriptor(context, rhs_type, rhs_tree, + location); + + // The result is going to be a two element constructor. + + vec *init; + vec_alloc (init, 2); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + tree field = TYPE_FIELDS(lhs_type_tree); + elt->index = field; + + if (for_type_guard) + { + // A type assertion fails when converting a nil interface. + Bexpression* lhs_type_expr = lhs_type->type_descriptor_pointer(gogo, + location); + tree lhs_type_descriptor = expr_to_tree(lhs_type_expr); + static tree assert_interface_decl; + tree call = Gogo::call_builtin(&assert_interface_decl, + location, + "__go_assert_interface", + 2, + ptr_type_node, + TREE_TYPE(lhs_type_descriptor), + 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.gcc_location(), TREE_TYPE(field), + call); + } + else if (lhs_is_empty) + { + // A convertion to an empty interface always succeeds, and the + // first field is just the type descriptor of the object. + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), + "__type_descriptor") == 0); + elt->value = fold_convert_loc(location.gcc_location(), + TREE_TYPE(field), rhs_type_descriptor); + } + else + { + // A conversion to a non-empty interface may fail, but unlike a + // type assertion converting nil will always succeed. + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods") + == 0); + Bexpression* lhs_type_expr = lhs_type->type_descriptor_pointer(gogo, + location); + tree lhs_type_descriptor = expr_to_tree(lhs_type_expr); + + static tree convert_interface_decl; + tree call = Gogo::call_builtin(&convert_interface_decl, + location, + "__go_convert_interface", + 2, + ptr_type_node, + TREE_TYPE(lhs_type_descriptor), + 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.gcc_location(), TREE_TYPE(field), + call); + } + + // The second field is simply the object pointer. + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0); + elt->index = field; + + tree rhs_type_tree = TREE_TYPE(rhs_tree); + go_assert(TREE_CODE(rhs_type_tree) == RECORD_TYPE); + tree rhs_field = DECL_CHAIN(TYPE_FIELDS(rhs_type_tree)); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(rhs_field)), "__object") == 0); + elt->value = build3(COMPONENT_REF, TREE_TYPE(rhs_field), rhs_tree, rhs_field, + NULL_TREE); + + return build_constructor(lhs_type_tree, init); +} + +// Return a tree for the conversion of an interface type to a +// non-interface type. + +tree +Expression::convert_interface_to_type(Translate_context* context, + Type *lhs_type, Type* rhs_type, + tree rhs_tree, Location location) +{ + Gogo* gogo = context->gogo(); + tree rhs_type_tree = TREE_TYPE(rhs_tree); + + tree lhs_type_tree = type_to_tree(lhs_type->get_backend(gogo)); + if (lhs_type_tree == error_mark_node) + return error_mark_node; + + // Call a function to check that the type is valid. The function + // will panic with an appropriate runtime type error if the type is + // not valid. + Bexpression* lhs_type_expr = lhs_type->type_descriptor_pointer(gogo, + location); + tree lhs_type_descriptor = expr_to_tree(lhs_type_expr); + + if (!DECL_P(rhs_tree)) + rhs_tree = save_expr(rhs_tree); + + tree rhs_type_descriptor = + Expression::get_interface_type_descriptor(context, rhs_type, rhs_tree, + location); + + Bexpression* rhs_inter_expr = rhs_type->type_descriptor_pointer(gogo, + location); + tree rhs_inter_descriptor = expr_to_tree(rhs_inter_expr); + + static tree check_interface_type_decl; + tree call = Gogo::call_builtin(&check_interface_type_decl, + location, + "__go_check_interface_type", + 3, + void_type_node, + TREE_TYPE(lhs_type_descriptor), + lhs_type_descriptor, + TREE_TYPE(rhs_type_descriptor), + 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; + + // If the call succeeds, pull out the value. + go_assert(TREE_CODE(rhs_type_tree) == RECORD_TYPE); + tree rhs_field = DECL_CHAIN(TYPE_FIELDS(rhs_type_tree)); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(rhs_field)), "__object") == 0); + tree val = build3(COMPONENT_REF, TREE_TYPE(rhs_field), rhs_tree, rhs_field, + NULL_TREE); + + // If the value is a pointer, then it is the value we want. + // Otherwise it points to the value. + if (lhs_type->points_to() == NULL) + { + val = fold_convert_loc(location.gcc_location(), + build_pointer_type(lhs_type_tree), val); + val = build_fold_indirect_ref_loc(location.gcc_location(), val); + } + + return build2(COMPOUND_EXPR, lhs_type_tree, call, + fold_convert_loc(location.gcc_location(), lhs_type_tree, val)); +} + +// Convert an expression to a tree. This is implemented by the child +// class. Not that it is not in general safe to call this multiple +// times for a single expression, but that we don't catch such errors. + +tree +Expression::get_tree(Translate_context* context) +{ + // The child may have marked this expression as having an error. + if (this->classification_ == EXPRESSION_ERROR) + return error_mark_node; + + return this->do_get_tree(context); +} + +// Return a backend expression for VAL. +Bexpression* +Expression::backend_numeric_constant_expression(Translate_context* context, + Numeric_constant* val) +{ + Gogo* gogo = context->gogo(); + Type* type = val->type(); + if (type == NULL) + return gogo->backend()->error_expression(); + + Btype* btype = type->get_backend(gogo); + Bexpression* ret; + if (type->integer_type() != NULL) + { + mpz_t ival; + if (!val->to_int(&ival)) + { + go_assert(saw_errors()); + return gogo->backend()->error_expression(); + } + ret = gogo->backend()->integer_constant_expression(btype, ival); + mpz_clear(ival); + } + else if (type->float_type() != NULL) + { + mpfr_t fval; + if (!val->to_float(&fval)) + { + go_assert(saw_errors()); + return gogo->backend()->error_expression(); + } + ret = gogo->backend()->float_constant_expression(btype, fval); + mpfr_clear(fval); + } + else if (type->complex_type() != NULL) + { + mpfr_t real; + mpfr_t imag; + if (!val->to_complex(&real, &imag)) + { + go_assert(saw_errors()); + return gogo->backend()->error_expression(); + } + ret = gogo->backend()->complex_constant_expression(btype, real, imag); + mpfr_clear(real); + mpfr_clear(imag); + } + else + go_unreachable(); + + return ret; +} + +// Return a tree which evaluates to true if VAL, of arbitrary integer +// type, is negative or is more than the maximum value of BOUND_TYPE. +// If SOFAR is not NULL, it is or'red into the result. The return +// value may be NULL if SOFAR is NULL. + +tree +Expression::check_bounds(tree val, tree bound_type, tree sofar, + Location loc) +{ + tree val_type = TREE_TYPE(val); + tree ret = NULL_TREE; + + if (!TYPE_UNSIGNED(val_type)) + { + ret = fold_build2_loc(loc.gcc_location(), LT_EXPR, boolean_type_node, val, + build_int_cst(val_type, 0)); + if (ret == boolean_false_node) + ret = NULL_TREE; + } + + HOST_WIDE_INT val_type_size = int_size_in_bytes(val_type); + HOST_WIDE_INT bound_type_size = int_size_in_bytes(bound_type); + go_assert(val_type_size != -1 && bound_type_size != -1); + if (val_type_size > bound_type_size + || (val_type_size == bound_type_size + && TYPE_UNSIGNED(val_type) + && !TYPE_UNSIGNED(bound_type))) + { + tree max = TYPE_MAX_VALUE(bound_type); + tree big = fold_build2_loc(loc.gcc_location(), GT_EXPR, boolean_type_node, + val, fold_convert_loc(loc.gcc_location(), + val_type, max)); + if (big == boolean_false_node) + ; + else if (ret == NULL_TREE) + ret = big; + else + ret = fold_build2_loc(loc.gcc_location(), TRUTH_OR_EXPR, + boolean_type_node, ret, big); + } + + if (ret == NULL_TREE) + return sofar; + else if (sofar == NULL_TREE) + return ret; + else + return fold_build2_loc(loc.gcc_location(), TRUTH_OR_EXPR, boolean_type_node, + sofar, ret); +} + +void +Expression::dump_expression(Ast_dump_context* ast_dump_context) const +{ + this->do_dump_expression(ast_dump_context); +} + +// Error expressions. This are used to avoid cascading errors. + +class Error_expression : public Expression +{ + public: + Error_expression(Location location) + : Expression(EXPRESSION_ERROR, location) + { } + + protected: + bool + do_is_constant() const + { return true; } + + bool + do_numeric_constant_value(Numeric_constant* nc) const + { + nc->set_unsigned_long(NULL, 0); + return true; + } + + bool + do_discarding_value() + { return true; } + + Type* + do_type() + { return Type::make_error_type(); } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return this; } + + bool + do_is_addressable() const + { return true; } + + tree + do_get_tree(Translate_context*) + { return error_mark_node; } + + void + do_dump_expression(Ast_dump_context*) const; +}; + +// Dump the ast representation for an error expression to a dump context. + +void +Error_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "_Error_" ; +} + +Expression* +Expression::make_error(Location location) +{ + return new Error_expression(location); +} + +// An expression which is really a type. This is used during parsing. +// It is an error if these survive after lowering. + +class +Type_expression : public Expression +{ + public: + Type_expression(Type* type, Location location) + : Expression(EXPRESSION_TYPE, location), + type_(type) + { } + + protected: + int + do_traverse(Traverse* traverse) + { return Type::traverse(this->type_, traverse); } + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*) + { } + + void + do_check_types(Gogo*) + { this->report_error(_("invalid use of type")); } + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context*) + { go_unreachable(); } + + void do_dump_expression(Ast_dump_context*) const; + + private: + // The type which we are representing as an expression. + Type* type_; +}; + +void +Type_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->dump_type(this->type_); +} + +Expression* +Expression::make_type(Type* type, Location location) +{ + return new Type_expression(type, location); +} + +// Class Parser_expression. + +Type* +Parser_expression::do_type() +{ + // We should never really ask for the type of a Parser_expression. + // However, it can happen, at least when we have an invalid const + // whose initializer refers to the const itself. In that case we + // may ask for the type when lowering the const itself. + go_assert(saw_errors()); + return Type::make_error_type(); +} + +// Class Var_expression. + +// Lower a variable expression. Here we just make sure that the +// initialization expression of the variable has been lowered. This +// ensures that we will be able to determine the type of the variable +// if necessary. + +Expression* +Var_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) +{ + if (this->variable_->is_variable()) + { + Variable* var = this->variable_->var_value(); + // This is either a local variable or a global variable. A + // reference to a variable which is local to an enclosing + // function will be a reference to a field in a closure. + if (var->is_global()) + { + function = NULL; + inserter = NULL; + } + var->lower_init_expression(gogo, function, inserter); + } + return this; +} + +// Return the type of a reference to a variable. + +Type* +Var_expression::do_type() +{ + if (this->variable_->is_variable()) + return this->variable_->var_value()->type(); + else if (this->variable_->is_result_variable()) + return this->variable_->result_var_value()->type(); + else + go_unreachable(); +} + +// Determine the type of a reference to a variable. + +void +Var_expression::do_determine_type(const Type_context*) +{ + if (this->variable_->is_variable()) + this->variable_->var_value()->determine_type(); +} + +// Something takes the address of this variable. This means that we +// may want to move the variable onto the heap. + +void +Var_expression::do_address_taken(bool escapes) +{ + if (!escapes) + { + if (this->variable_->is_variable()) + this->variable_->var_value()->set_non_escaping_address_taken(); + else if (this->variable_->is_result_variable()) + this->variable_->result_var_value()->set_non_escaping_address_taken(); + else + go_unreachable(); + } + else + { + if (this->variable_->is_variable()) + this->variable_->var_value()->set_address_taken(); + else if (this->variable_->is_result_variable()) + this->variable_->result_var_value()->set_address_taken(); + else + go_unreachable(); + } +} + +// Get the tree for a reference to a variable. + +tree +Var_expression::do_get_tree(Translate_context* context) +{ + Bvariable* bvar = this->variable_->get_backend_variable(context->gogo(), + context->function()); + bool is_in_heap; + Location loc = this->location(); + if (this->variable_->is_variable()) + is_in_heap = this->variable_->var_value()->is_in_heap(); + else if (this->variable_->is_result_variable()) + is_in_heap = this->variable_->result_var_value()->is_in_heap(); + else + go_unreachable(); + + Bexpression* ret = context->backend()->var_expression(bvar, loc); + if (is_in_heap) + ret = context->backend()->indirect_expression(ret, true, loc); + return expr_to_tree(ret); +} + +// Ast dump for variable expression. + +void +Var_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << this->variable_->name() ; +} + +// Make a reference to a variable in an expression. + +Expression* +Expression::make_var_reference(Named_object* var, Location location) +{ + if (var->is_sink()) + return Expression::make_sink(location); + + // FIXME: Creating a new object for each reference to a variable is + // wasteful. + return new Var_expression(var, location); +} + +// Class Temporary_reference_expression. + +// The type. + +Type* +Temporary_reference_expression::do_type() +{ + return this->statement_->type(); +} + +// Called if something takes the address of this temporary variable. +// We never have to move temporary variables to the heap, but we do +// need to know that they must live in the stack rather than in a +// register. + +void +Temporary_reference_expression::do_address_taken(bool) +{ + this->statement_->set_is_address_taken(); +} + +// Get a tree referring to the variable. + +tree +Temporary_reference_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + Bvariable* bvar = this->statement_->get_backend_variable(context); + Bexpression* ret = gogo->backend()->var_expression(bvar, this->location()); + + // The backend can't always represent the same set of recursive types + // that the Go frontend can. In some cases this means that a + // temporary variable won't have the right backend type. Correct + // that here by adding a type cast. We need to use base() to push + // the circularity down one level. + Type* stype = this->statement_->type(); + if (!this->is_lvalue_ + && stype->has_pointer() + && stype->deref()->is_void_type()) + { + Btype* btype = this->type()->base()->get_backend(gogo); + ret = gogo->backend()->convert_expression(btype, ret, this->location()); + } + return expr_to_tree(ret); +} + +// Ast dump for temporary reference. + +void +Temporary_reference_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->dump_temp_variable_name(this->statement_); +} + +// Make a reference to a temporary variable. + +Temporary_reference_expression* +Expression::make_temporary_reference(Temporary_statement* statement, + Location location) +{ + return new Temporary_reference_expression(statement, location); +} + +// Class Set_and_use_temporary_expression. + +// Return the type. + +Type* +Set_and_use_temporary_expression::do_type() +{ + return this->statement_->type(); +} + +// Determine the type of the expression. + +void +Set_and_use_temporary_expression::do_determine_type( + const Type_context* context) +{ + this->expr_->determine_type(context); +} + +// Take the address. + +void +Set_and_use_temporary_expression::do_address_taken(bool) +{ + this->statement_->set_is_address_taken(); +} + +// Return the backend representation. + +tree +Set_and_use_temporary_expression::do_get_tree(Translate_context* context) +{ + Bvariable* bvar = this->statement_->get_backend_variable(context); + tree var_tree = var_to_tree(bvar); + tree expr_tree = this->expr_->get_tree(context); + if (var_tree == error_mark_node || expr_tree == error_mark_node) + return error_mark_node; + Location loc = this->location(); + return build2_loc(loc.gcc_location(), COMPOUND_EXPR, TREE_TYPE(var_tree), + build2_loc(loc.gcc_location(), MODIFY_EXPR, void_type_node, + var_tree, expr_tree), + var_tree); +} + +// Dump. + +void +Set_and_use_temporary_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << '('; + ast_dump_context->dump_temp_variable_name(this->statement_); + ast_dump_context->ostream() << " = "; + this->expr_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << ')'; +} + +// Make a set-and-use temporary. + +Set_and_use_temporary_expression* +Expression::make_set_and_use_temporary(Temporary_statement* statement, + Expression* expr, Location location) +{ + return new Set_and_use_temporary_expression(statement, expr, location); +} + +// A sink expression--a use of the blank identifier _. + +class Sink_expression : public Expression +{ + public: + Sink_expression(Location location) + : Expression(EXPRESSION_SINK, location), + type_(NULL), var_(NULL_TREE) + { } + + protected: + bool + do_discarding_value() + { return true; } + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + Expression* + do_copy() + { return new Sink_expression(this->location()); } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type of this sink variable. + Type* type_; + // The temporary variable we generate. + tree var_; +}; + +// Return the type of a sink expression. + +Type* +Sink_expression::do_type() +{ + if (this->type_ == NULL) + return Type::make_sink_type(); + return this->type_; +} + +// Determine the type of a sink expression. + +void +Sink_expression::do_determine_type(const Type_context* context) +{ + if (context->type != NULL) + this->type_ = context->type; +} + +// Return a temporary variable for a sink expression. This will +// presumably be a write-only variable which the middle-end will drop. + +tree +Sink_expression::do_get_tree(Translate_context* context) +{ + if (this->var_ == NULL_TREE) + { + go_assert(this->type_ != NULL && !this->type_->is_sink_type()); + Btype* bt = this->type_->get_backend(context->gogo()); + this->var_ = create_tmp_var(type_to_tree(bt), "blank"); + } + return this->var_; +} + +// Ast dump for sink expression. + +void +Sink_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "_" ; +} + +// Make a sink expression. + +Expression* +Expression::make_sink(Location location) +{ + return new Sink_expression(location); +} + +// Class Func_expression. + +// FIXME: Can a function expression appear in a constant expression? +// The value is unchanging. Initializing a constant to the address of +// a function seems like it could work, though there might be little +// point to it. + +// Traversal. + +int +Func_expression::do_traverse(Traverse* traverse) +{ + return (this->closure_ == NULL + ? TRAVERSE_CONTINUE + : Expression::traverse(&this->closure_, traverse)); +} + +// Return the type of a function expression. + +Type* +Func_expression::do_type() +{ + if (this->function_->is_function()) + return this->function_->func_value()->type(); + else if (this->function_->is_function_declaration()) + return this->function_->func_declaration_value()->type(); + else + go_unreachable(); +} + +// Get the tree for the code of a function expression. + +Bexpression* +Func_expression::get_code_pointer(Gogo* gogo, Named_object* no, Location loc) +{ + Function_type* fntype; + if (no->is_function()) + fntype = no->func_value()->type(); + else if (no->is_function_declaration()) + fntype = no->func_declaration_value()->type(); + else + go_unreachable(); + + // Builtin functions are handled specially by Call_expression. We + // can't take their address. + if (fntype->is_builtin()) + { + error_at(loc, + "invalid use of special builtin function %qs; must be called", + no->message_name().c_str()); + return gogo->backend()->error_expression(); + } + + Bfunction* fndecl; + if (no->is_function()) + fndecl = no->func_value()->get_or_make_decl(gogo, no); + else if (no->is_function_declaration()) + fndecl = no->func_declaration_value()->get_or_make_decl(gogo, no); + else + go_unreachable(); + + return gogo->backend()->function_code_expression(fndecl, loc); +} + +// Get the tree for a function expression. This is used when we take +// the address of a function rather than simply calling it. A func +// value is represented as a pointer to a block of memory. The first +// word of that memory is a pointer to the function code. The +// remaining parts of that memory are the addresses of variables that +// the function closes over. + +tree +Func_expression::do_get_tree(Translate_context* context) +{ + // If there is no closure, just use the function descriptor. + if (this->closure_ == NULL) + { + Gogo* gogo = context->gogo(); + Named_object* no = this->function_; + Expression* descriptor; + if (no->is_function()) + descriptor = no->func_value()->descriptor(gogo, no); + else if (no->is_function_declaration()) + { + if (no->func_declaration_value()->type()->is_builtin()) + { + error_at(this->location(), + ("invalid use of special builtin function %qs; " + "must be called"), + no->message_name().c_str()); + return error_mark_node; + } + descriptor = no->func_declaration_value()->descriptor(gogo, no); + } + else + go_unreachable(); + + tree dtree = descriptor->get_tree(context); + if (dtree == error_mark_node) + return error_mark_node; + return build_fold_addr_expr_loc(this->location().gcc_location(), dtree); + } + + go_assert(this->function_->func_value()->enclosing() != NULL); + + // If there is a closure, then the closure is itself the function + // expression. It is a pointer to a struct whose first field points + // to the function code and whose remaining fields are the addresses + // of the closed-over variables. + return this->closure_->get_tree(context); +} + +// Ast dump for function. + +void +Func_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << this->function_->name(); + if (this->closure_ != NULL) + { + ast_dump_context->ostream() << " {closure = "; + this->closure_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "}"; + } +} + +// Make a reference to a function in an expression. + +Expression* +Expression::make_func_reference(Named_object* function, Expression* closure, + Location location) +{ + return new Func_expression(function, closure, location); +} + +// Class Func_descriptor_expression. + +// Constructor. + +Func_descriptor_expression::Func_descriptor_expression(Named_object* fn) + : Expression(EXPRESSION_FUNC_DESCRIPTOR, fn->location()), + fn_(fn), dvar_(NULL) +{ + go_assert(!fn->is_function() || !fn->func_value()->needs_closure()); +} + +// Traversal. + +int +Func_descriptor_expression::do_traverse(Traverse*) +{ + return TRAVERSE_CONTINUE; +} + +// All function descriptors have the same type. + +Type* Func_descriptor_expression::descriptor_type; + +void +Func_descriptor_expression::make_func_descriptor_type() +{ + if (Func_descriptor_expression::descriptor_type != NULL) + return; + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* struct_type = Type::make_builtin_struct_type(1, "code", uintptr_type); + Func_descriptor_expression::descriptor_type = + Type::make_builtin_named_type("functionDescriptor", struct_type); +} + +Type* +Func_descriptor_expression::do_type() +{ + Func_descriptor_expression::make_func_descriptor_type(); + return Func_descriptor_expression::descriptor_type; +} + +// The tree for a function descriptor. + +tree +Func_descriptor_expression::do_get_tree(Translate_context* context) +{ + if (this->dvar_ != NULL) + return var_to_tree(this->dvar_); + + Gogo* gogo = context->gogo(); + Named_object* no = this->fn_; + Location loc = no->location(); + + std::string var_name; + if (no->package() == NULL) + var_name = gogo->pkgpath_symbol(); + else + var_name = no->package()->pkgpath_symbol(); + var_name.push_back('.'); + var_name.append(Gogo::unpack_hidden_name(no->name())); + var_name.append("$descriptor"); + + Btype* btype = this->type()->get_backend(gogo); + + Bvariable* bvar; + if (no->package() != NULL + || Linemap::is_predeclared_location(no->location())) + bvar = context->backend()->immutable_struct_reference(var_name, btype, + loc); + else + { + Location bloc = Linemap::predeclared_location(); + bool is_hidden = ((no->is_function() + && no->func_value()->enclosing() != NULL) + || Gogo::is_thunk(no)); + bvar = context->backend()->immutable_struct(var_name, is_hidden, false, + btype, bloc); + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_func_code_reference(this->fn_, bloc)); + Expression* init = + Expression::make_struct_composite_literal(this->type(), vals, bloc); + Translate_context bcontext(gogo, NULL, NULL, NULL); + bcontext.set_is_const(); + Bexpression* binit = tree_to_expr(init->get_tree(&bcontext)); + context->backend()->immutable_struct_set_init(bvar, var_name, is_hidden, + false, btype, bloc, binit); + } + + this->dvar_ = bvar; + return var_to_tree(bvar); +} + +// Print a function descriptor expression. + +void +Func_descriptor_expression::do_dump_expression(Ast_dump_context* context) const +{ + context->ostream() << "[descriptor " << this->fn_->name() << "]"; +} + +// Make a function descriptor expression. + +Func_descriptor_expression* +Expression::make_func_descriptor(Named_object* fn) +{ + return new Func_descriptor_expression(fn); +} + +// Make the function descriptor type, so that it can be converted. + +void +Expression::make_func_descriptor_type() +{ + Func_descriptor_expression::make_func_descriptor_type(); +} + +// A reference to just the code of a function. + +class Func_code_reference_expression : public Expression +{ + public: + Func_code_reference_expression(Named_object* function, Location location) + : Expression(EXPRESSION_FUNC_CODE_REFERENCE, location), + function_(function) + { } + + protected: + int + do_traverse(Traverse*) + { return TRAVERSE_CONTINUE; } + + bool + do_is_immutable() const + { return true; } + + Type* + do_type() + { return Type::make_pointer_type(Type::make_void_type()); } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return Expression::make_func_code_reference(this->function_, + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context* context) const + { context->ostream() << "[raw " << this->function_->name() << "]" ; } + + private: + // The function. + Named_object* function_; +}; + +// Get the tree for a reference to function code. + +tree +Func_code_reference_expression::do_get_tree(Translate_context* context) +{ + Bexpression* ret = + Func_expression::get_code_pointer(context->gogo(), this->function_, + this->location()); + return expr_to_tree(ret); +} + +// Make a reference to the code of a function. + +Expression* +Expression::make_func_code_reference(Named_object* function, Location location) +{ + return new Func_code_reference_expression(function, location); +} + +// Class Unknown_expression. + +// Return the name of an unknown expression. + +const std::string& +Unknown_expression::name() const +{ + return this->named_object_->name(); +} + +// Lower a reference to an unknown name. + +Expression* +Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) +{ + Location location = this->location(); + Named_object* no = this->named_object_; + Named_object* real; + if (!no->is_unknown()) + real = no; + else + { + real = no->unknown_value()->real_named_object(); + if (real == NULL) + { + if (this->is_composite_literal_key_) + return this; + if (!this->no_error_message_) + error_at(location, "reference to undefined name %qs", + this->named_object_->message_name().c_str()); + return Expression::make_error(location); + } + } + switch (real->classification()) + { + case Named_object::NAMED_OBJECT_CONST: + return Expression::make_const_reference(real, location); + case Named_object::NAMED_OBJECT_TYPE: + return Expression::make_type(real->type_value(), location); + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + if (this->is_composite_literal_key_) + return this; + if (!this->no_error_message_) + error_at(location, "reference to undefined type %qs", + real->message_name().c_str()); + return Expression::make_error(location); + case Named_object::NAMED_OBJECT_VAR: + real->var_value()->set_is_used(); + return Expression::make_var_reference(real, location); + case Named_object::NAMED_OBJECT_FUNC: + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + return Expression::make_func_reference(real, NULL, location); + case Named_object::NAMED_OBJECT_PACKAGE: + if (this->is_composite_literal_key_) + return this; + if (!this->no_error_message_) + error_at(location, "unexpected reference to package"); + return Expression::make_error(location); + default: + go_unreachable(); + } +} + +// Dump the ast representation for an unknown expression to a dump context. + +void +Unknown_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "_Unknown_(" << this->named_object_->name() + << ")"; +} + +// Make a reference to an unknown name. + +Unknown_expression* +Expression::make_unknown_reference(Named_object* no, Location location) +{ + return new Unknown_expression(no, location); +} + +// A boolean expression. + +class Boolean_expression : public Expression +{ + public: + Boolean_expression(bool val, Location location) + : Expression(EXPRESSION_BOOLEAN, location), + val_(val), type_(NULL) + { } + + static Expression* + do_import(Import*); + + protected: + bool + do_is_constant() const + { return true; } + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context*) + { return this->val_ ? boolean_true_node : boolean_false_node; } + + void + do_export(Export* exp) const + { exp->write_c_string(this->val_ ? "true" : "false"); } + + void + do_dump_expression(Ast_dump_context* ast_dump_context) const + { ast_dump_context->ostream() << (this->val_ ? "true" : "false"); } + + private: + // The constant. + bool val_; + // The type as determined by context. + Type* type_; +}; + +// Get the type. + +Type* +Boolean_expression::do_type() +{ + if (this->type_ == NULL) + this->type_ = Type::make_boolean_type(); + return this->type_; +} + +// Set the type from the context. + +void +Boolean_expression::do_determine_type(const Type_context* context) +{ + if (this->type_ != NULL && !this->type_->is_abstract()) + ; + else if (context->type != NULL && context->type->is_boolean_type()) + this->type_ = context->type; + else if (!context->may_be_abstract) + this->type_ = Type::lookup_bool_type(); +} + +// Import a boolean constant. + +Expression* +Boolean_expression::do_import(Import* imp) +{ + if (imp->peek_char() == 't') + { + imp->require_c_string("true"); + return Expression::make_boolean(true, imp->location()); + } + else + { + imp->require_c_string("false"); + return Expression::make_boolean(false, imp->location()); + } +} + +// Make a boolean expression. + +Expression* +Expression::make_boolean(bool val, Location location) +{ + return new Boolean_expression(val, location); +} + +// Class String_expression. + +// Get the type. + +Type* +String_expression::do_type() +{ + if (this->type_ == NULL) + this->type_ = Type::make_string_type(); + return this->type_; +} + +// Set the type from the context. + +void +String_expression::do_determine_type(const Type_context* context) +{ + if (this->type_ != NULL && !this->type_->is_abstract()) + ; + else if (context->type != NULL && context->type->is_string_type()) + this->type_ = context->type; + else if (!context->may_be_abstract) + this->type_ = Type::lookup_string_type(); +} + +// Build a string constant. + +tree +String_expression::do_get_tree(Translate_context* context) +{ + return context->gogo()->go_string_constant_tree(this->val_); +} + + // Write string literal to string dump. + +void +String_expression::export_string(String_dump* exp, + const String_expression* str) +{ + std::string s; + s.reserve(str->val_.length() * 4 + 2); + s += '"'; + for (std::string::const_iterator p = str->val_.begin(); + p != str->val_.end(); + ++p) + { + if (*p == '\\' || *p == '"') + { + s += '\\'; + s += *p; + } + else if (*p >= 0x20 && *p < 0x7f) + s += *p; + else if (*p == '\n') + s += "\\n"; + else if (*p == '\t') + s += "\\t"; + else + { + s += "\\x"; + unsigned char c = *p; + unsigned int dig = c >> 4; + s += dig < 10 ? '0' + dig : 'A' + dig - 10; + dig = c & 0xf; + s += dig < 10 ? '0' + dig : 'A' + dig - 10; + } + } + s += '"'; + exp->write_string(s); +} + +// Export a string expression. + +void +String_expression::do_export(Export* exp) const +{ + String_expression::export_string(exp, this); +} + +// Import a string expression. + +Expression* +String_expression::do_import(Import* imp) +{ + imp->require_c_string("\""); + std::string val; + while (true) + { + int c = imp->get_char(); + if (c == '"' || c == -1) + break; + if (c != '\\') + val += static_cast(c); + else + { + c = imp->get_char(); + if (c == '\\' || c == '"') + val += static_cast(c); + else if (c == 'n') + val += '\n'; + else if (c == 't') + val += '\t'; + else if (c == 'x') + { + c = imp->get_char(); + unsigned int vh = c >= '0' && c <= '9' ? c - '0' : c - 'A' + 10; + c = imp->get_char(); + unsigned int vl = c >= '0' && c <= '9' ? c - '0' : c - 'A' + 10; + char v = (vh << 4) | vl; + val += v; + } + else + { + error_at(imp->location(), "bad string constant"); + return Expression::make_error(imp->location()); + } + } + } + return Expression::make_string(val, imp->location()); +} + +// Ast dump for string expression. + +void +String_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + String_expression::export_string(ast_dump_context, this); +} + +// Make a string expression. + +Expression* +Expression::make_string(const std::string& val, Location location) +{ + return new String_expression(val, location); +} + +// Make an integer expression. + +class Integer_expression : public Expression +{ + public: + Integer_expression(const mpz_t* val, Type* type, bool is_character_constant, + Location location) + : Expression(EXPRESSION_INTEGER, location), + type_(type), is_character_constant_(is_character_constant) + { mpz_init_set(this->val_, *val); } + + static Expression* + do_import(Import*); + + // Write VAL to string dump. + static void + export_integer(String_dump* exp, const mpz_t val); + + // Write VAL to dump context. + static void + dump_integer(Ast_dump_context* ast_dump_context, const mpz_t val); + + protected: + bool + do_is_constant() const + { return true; } + + bool + do_numeric_constant_value(Numeric_constant* nc) const; + + Type* + do_type(); + + void + do_determine_type(const Type_context* context); + + void + do_check_types(Gogo*); + + tree + do_get_tree(Translate_context*); + + Expression* + do_copy() + { + if (this->is_character_constant_) + return Expression::make_character(&this->val_, this->type_, + this->location()); + else + return Expression::make_integer(&this->val_, this->type_, + this->location()); + } + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The integer value. + mpz_t val_; + // The type so far. + Type* type_; + // Whether this is a character constant. + bool is_character_constant_; +}; + +// Return a numeric constant for this expression. We have to mark +// this as a character when appropriate. + +bool +Integer_expression::do_numeric_constant_value(Numeric_constant* nc) const +{ + if (this->is_character_constant_) + nc->set_rune(this->type_, this->val_); + else + nc->set_int(this->type_, this->val_); + return true; +} + +// Return the current type. If we haven't set the type yet, we return +// an abstract integer type. + +Type* +Integer_expression::do_type() +{ + if (this->type_ == NULL) + { + if (this->is_character_constant_) + this->type_ = Type::make_abstract_character_type(); + else + this->type_ = Type::make_abstract_integer_type(); + } + return this->type_; +} + +// Set the type of the integer value. Here we may switch from an +// abstract type to a real type. + +void +Integer_expression::do_determine_type(const Type_context* context) +{ + if (this->type_ != NULL && !this->type_->is_abstract()) + ; + else if (context->type != NULL && context->type->is_numeric_type()) + this->type_ = context->type; + else if (!context->may_be_abstract) + { + if (this->is_character_constant_) + this->type_ = Type::lookup_integer_type("int32"); + else + this->type_ = Type::lookup_integer_type("int"); + } +} + +// Check the type of an integer constant. + +void +Integer_expression::do_check_types(Gogo*) +{ + Type* type = this->type_; + if (type == NULL) + return; + Numeric_constant nc; + if (this->is_character_constant_) + nc.set_rune(NULL, this->val_); + else + nc.set_int(NULL, this->val_); + if (!nc.set_type(type, true, this->location())) + this->set_is_error(); +} + +// Get a tree for an integer constant. + +tree +Integer_expression::do_get_tree(Translate_context* context) +{ + Type* resolved_type = NULL; + if (this->type_ != NULL && !this->type_->is_abstract()) + resolved_type = this->type_; + else if (this->type_ != NULL && this->type_->float_type() != NULL) + { + // We are converting to an abstract floating point type. + resolved_type = Type::lookup_float_type("float64"); + } + else if (this->type_ != NULL && this->type_->complex_type() != NULL) + { + // We are converting to an abstract complex type. + resolved_type = Type::lookup_complex_type("complex128"); + } + else + { + // If we still have an abstract type here, then this is being + // used in a constant expression which didn't get reduced for + // some reason. Use a type which will fit the value. We use <, + // not <=, because we need an extra bit for the sign bit. + int bits = mpz_sizeinbase(this->val_, 2); + Type* int_type = Type::lookup_integer_type("int"); + if (bits < int_type->integer_type()->bits()) + resolved_type = int_type; + else if (bits < 64) + resolved_type = Type::lookup_integer_type("int64"); + else + { + if (!saw_errors()) + error_at(this->location(), + "unknown type for large integer constant"); + Bexpression* ret = context->gogo()->backend()->error_expression(); + return expr_to_tree(ret); + } + } + Numeric_constant nc; + nc.set_int(resolved_type, this->val_); + Bexpression* ret = + Expression::backend_numeric_constant_expression(context, &nc); + return expr_to_tree(ret); +} + +// Write VAL to export data. + +void +Integer_expression::export_integer(String_dump* exp, const mpz_t val) +{ + char* s = mpz_get_str(NULL, 10, val); + exp->write_c_string(s); + free(s); +} + +// Export an integer in a constant expression. + +void +Integer_expression::do_export(Export* exp) const +{ + Integer_expression::export_integer(exp, this->val_); + if (this->is_character_constant_) + exp->write_c_string("'"); + // A trailing space lets us reliably identify the end of the number. + exp->write_c_string(" "); +} + +// Import an integer, floating point, or complex value. This handles +// all these types because they all start with digits. + +Expression* +Integer_expression::do_import(Import* imp) +{ + std::string num = imp->read_identifier(); + imp->require_c_string(" "); + if (!num.empty() && num[num.length() - 1] == 'i') + { + mpfr_t real; + size_t plus_pos = num.find('+', 1); + size_t minus_pos = num.find('-', 1); + size_t pos; + if (plus_pos == std::string::npos) + pos = minus_pos; + else if (minus_pos == std::string::npos) + pos = plus_pos; + else + { + error_at(imp->location(), "bad number in import data: %qs", + num.c_str()); + return Expression::make_error(imp->location()); + } + if (pos == std::string::npos) + mpfr_set_ui(real, 0, GMP_RNDN); + else + { + std::string real_str = num.substr(0, pos); + if (mpfr_init_set_str(real, real_str.c_str(), 10, GMP_RNDN) != 0) + { + error_at(imp->location(), "bad number in import data: %qs", + real_str.c_str()); + return Expression::make_error(imp->location()); + } + } + + std::string imag_str; + if (pos == std::string::npos) + imag_str = num; + else + imag_str = num.substr(pos); + imag_str = imag_str.substr(0, imag_str.size() - 1); + mpfr_t imag; + if (mpfr_init_set_str(imag, imag_str.c_str(), 10, GMP_RNDN) != 0) + { + error_at(imp->location(), "bad number in import data: %qs", + imag_str.c_str()); + return Expression::make_error(imp->location()); + } + Expression* ret = Expression::make_complex(&real, &imag, NULL, + imp->location()); + mpfr_clear(real); + mpfr_clear(imag); + return ret; + } + else if (num.find('.') == std::string::npos + && num.find('E') == std::string::npos) + { + bool is_character_constant = (!num.empty() + && num[num.length() - 1] == '\''); + if (is_character_constant) + num = num.substr(0, num.length() - 1); + mpz_t val; + if (mpz_init_set_str(val, num.c_str(), 10) != 0) + { + error_at(imp->location(), "bad number in import data: %qs", + num.c_str()); + return Expression::make_error(imp->location()); + } + Expression* ret; + if (is_character_constant) + ret = Expression::make_character(&val, NULL, imp->location()); + else + ret = Expression::make_integer(&val, NULL, imp->location()); + mpz_clear(val); + return ret; + } + else + { + mpfr_t val; + if (mpfr_init_set_str(val, num.c_str(), 10, GMP_RNDN) != 0) + { + error_at(imp->location(), "bad number in import data: %qs", + num.c_str()); + return Expression::make_error(imp->location()); + } + Expression* ret = Expression::make_float(&val, NULL, imp->location()); + mpfr_clear(val); + return ret; + } +} +// Ast dump for integer expression. + +void +Integer_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + if (this->is_character_constant_) + ast_dump_context->ostream() << '\''; + Integer_expression::export_integer(ast_dump_context, this->val_); + if (this->is_character_constant_) + ast_dump_context->ostream() << '\''; +} + +// Build a new integer value. + +Expression* +Expression::make_integer(const mpz_t* val, Type* type, Location location) +{ + return new Integer_expression(val, type, false, location); +} + +// Build a new character constant value. + +Expression* +Expression::make_character(const mpz_t* val, Type* type, Location location) +{ + return new Integer_expression(val, type, true, location); +} + +// Floats. + +class Float_expression : public Expression +{ + public: + Float_expression(const mpfr_t* val, Type* type, Location location) + : Expression(EXPRESSION_FLOAT, location), + type_(type) + { + mpfr_init_set(this->val_, *val, GMP_RNDN); + } + + // Write VAL to export data. + static void + export_float(String_dump* exp, const mpfr_t val); + + // Write VAL to dump file. + static void + dump_float(Ast_dump_context* ast_dump_context, const mpfr_t val); + + protected: + bool + do_is_constant() const + { return true; } + + bool + do_numeric_constant_value(Numeric_constant* nc) const + { + nc->set_float(this->type_, this->val_); + return true; + } + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { return Expression::make_float(&this->val_, this->type_, + this->location()); } + + tree + do_get_tree(Translate_context*); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The floating point value. + mpfr_t val_; + // The type so far. + Type* type_; +}; + +// Return the current type. If we haven't set the type yet, we return +// an abstract float type. + +Type* +Float_expression::do_type() +{ + if (this->type_ == NULL) + this->type_ = Type::make_abstract_float_type(); + return this->type_; +} + +// Set the type of the float value. Here we may switch from an +// abstract type to a real type. + +void +Float_expression::do_determine_type(const Type_context* context) +{ + if (this->type_ != NULL && !this->type_->is_abstract()) + ; + else if (context->type != NULL + && (context->type->integer_type() != NULL + || context->type->float_type() != NULL + || context->type->complex_type() != NULL)) + this->type_ = context->type; + else if (!context->may_be_abstract) + this->type_ = Type::lookup_float_type("float64"); +} + +// Check the type of a float value. + +void +Float_expression::do_check_types(Gogo*) +{ + Type* type = this->type_; + if (type == NULL) + return; + Numeric_constant nc; + nc.set_float(NULL, this->val_); + if (!nc.set_type(this->type_, true, this->location())) + this->set_is_error(); +} + +// Get a tree for a float constant. + +tree +Float_expression::do_get_tree(Translate_context* context) +{ + Type* resolved_type; + if (this->type_ != NULL && !this->type_->is_abstract()) + resolved_type = this->type_; + else if (this->type_ != NULL && this->type_->integer_type() != NULL) + { + // We have an abstract integer type. We just hope for the best. + resolved_type = Type::lookup_integer_type("int"); + } + else if (this->type_ != NULL && this->type_->complex_type() != NULL) + { + // We are converting to an abstract complex type. + resolved_type = Type::lookup_complex_type("complex128"); + } + else + { + // If we still have an abstract type here, then this is being + // used in a constant expression which didn't get reduced. We + // just use float64 and hope for the best. + resolved_type = Type::lookup_float_type("float64"); + } + + Numeric_constant nc; + nc.set_float(resolved_type, this->val_); + Bexpression* ret = + Expression::backend_numeric_constant_expression(context, &nc); + return expr_to_tree(ret); +} + +// Write a floating point number to a string dump. + +void +Float_expression::export_float(String_dump *exp, const mpfr_t val) +{ + mp_exp_t exponent; + char* s = mpfr_get_str(NULL, &exponent, 10, 0, val, GMP_RNDN); + if (*s == '-') + exp->write_c_string("-"); + exp->write_c_string("0."); + exp->write_c_string(*s == '-' ? s + 1 : s); + mpfr_free_str(s); + char buf[30]; + snprintf(buf, sizeof buf, "E%ld", exponent); + exp->write_c_string(buf); +} + +// Export a floating point number in a constant expression. + +void +Float_expression::do_export(Export* exp) const +{ + Float_expression::export_float(exp, this->val_); + // A trailing space lets us reliably identify the end of the number. + exp->write_c_string(" "); +} + +// Dump a floating point number to the dump file. + +void +Float_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + Float_expression::export_float(ast_dump_context, this->val_); +} + +// Make a float expression. + +Expression* +Expression::make_float(const mpfr_t* val, Type* type, Location location) +{ + return new Float_expression(val, type, location); +} + +// Complex numbers. + +class Complex_expression : public Expression +{ + public: + Complex_expression(const mpfr_t* real, const mpfr_t* imag, Type* type, + Location location) + : Expression(EXPRESSION_COMPLEX, location), + type_(type) + { + mpfr_init_set(this->real_, *real, GMP_RNDN); + mpfr_init_set(this->imag_, *imag, GMP_RNDN); + } + + // Write REAL/IMAG to string dump. + static void + export_complex(String_dump* exp, const mpfr_t real, const mpfr_t val); + + // Write REAL/IMAG to dump context. + static void + dump_complex(Ast_dump_context* ast_dump_context, + const mpfr_t real, const mpfr_t val); + + protected: + bool + do_is_constant() const + { return true; } + + bool + do_numeric_constant_value(Numeric_constant* nc) const + { + nc->set_complex(this->type_, this->real_, this->imag_); + return true; + } + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_complex(&this->real_, &this->imag_, this->type_, + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The real part. + mpfr_t real_; + // The imaginary part; + mpfr_t imag_; + // The type if known. + Type* type_; +}; + +// Return the current type. If we haven't set the type yet, we return +// an abstract complex type. + +Type* +Complex_expression::do_type() +{ + if (this->type_ == NULL) + this->type_ = Type::make_abstract_complex_type(); + return this->type_; +} + +// Set the type of the complex value. Here we may switch from an +// abstract type to a real type. + +void +Complex_expression::do_determine_type(const Type_context* context) +{ + if (this->type_ != NULL && !this->type_->is_abstract()) + ; + else if (context->type != NULL + && context->type->complex_type() != NULL) + this->type_ = context->type; + else if (!context->may_be_abstract) + this->type_ = Type::lookup_complex_type("complex128"); +} + +// Check the type of a complex value. + +void +Complex_expression::do_check_types(Gogo*) +{ + Type* type = this->type_; + if (type == NULL) + return; + Numeric_constant nc; + nc.set_complex(NULL, this->real_, this->imag_); + if (!nc.set_type(this->type_, true, this->location())) + this->set_is_error(); +} + +// Get a tree for a complex constant. + +tree +Complex_expression::do_get_tree(Translate_context* context) +{ + Type* resolved_type; + if (this->type_ != NULL && !this->type_->is_abstract()) + resolved_type = this->type_; + else if (this->type_ != NULL && this->type_->integer_type() != NULL) + { + // We are converting to an abstract integer type. + resolved_type = Type::lookup_integer_type("int"); + } + else if (this->type_ != NULL && this->type_->float_type() != NULL) + { + // We are converting to an abstract float type. + resolved_type = Type::lookup_float_type("float64"); + } + else + { + // If we still have an abstract type here, this this is being + // used in a constant expression which didn't get reduced. We + // just use complex128 and hope for the best. + resolved_type = Type::lookup_complex_type("complex128"); + } + + Numeric_constant nc; + nc.set_complex(resolved_type, this->real_, this->imag_); + Bexpression* ret = + Expression::backend_numeric_constant_expression(context, &nc); + return expr_to_tree(ret); +} + +// Write REAL/IMAG to export data. + +void +Complex_expression::export_complex(String_dump* exp, const mpfr_t real, + const mpfr_t imag) +{ + if (!mpfr_zero_p(real)) + { + Float_expression::export_float(exp, real); + if (mpfr_sgn(imag) > 0) + exp->write_c_string("+"); + } + Float_expression::export_float(exp, imag); + exp->write_c_string("i"); +} + +// Export a complex number in a constant expression. + +void +Complex_expression::do_export(Export* exp) const +{ + Complex_expression::export_complex(exp, this->real_, this->imag_); + // A trailing space lets us reliably identify the end of the number. + exp->write_c_string(" "); +} + +// Dump a complex expression to the dump file. + +void +Complex_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + Complex_expression::export_complex(ast_dump_context, + this->real_, + this->imag_); +} + +// Make a complex expression. + +Expression* +Expression::make_complex(const mpfr_t* real, const mpfr_t* imag, Type* type, + Location location) +{ + 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 +{ + public: + Const_expression(Named_object* constant, Location location) + : Expression(EXPRESSION_CONST_REFERENCE, location), + constant_(constant), type_(NULL), seen_(false) + { } + + Named_object* + named_object() + { return this->constant_; } + + // Check that the initializer does not refer to the constant itself. + void + check_for_init_loop(); + + protected: + int + do_traverse(Traverse*); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + bool + do_is_constant() const + { return true; } + + bool + do_numeric_constant_value(Numeric_constant* nc) const; + + bool + do_string_constant_value(std::string* val) const; + + Type* + do_type(); + + // The type of a const is set by the declaration, not the use. + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context* context); + + // When exporting a reference to a const as part of a const + // expression, we export the value. We ignore the fact that it has + // a name. + void + do_export(Export* exp) const + { this->constant_->const_value()->expr()->export_expression(exp); } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The constant. + Named_object* constant_; + // 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_; +}; + +// Traversal. + +int +Const_expression::do_traverse(Traverse* traverse) +{ + if (this->type_ != NULL) + return Type::traverse(this->type_, traverse); + return TRAVERSE_CONTINUE; +} + +// Lower a constant expression. This is where we convert the +// predeclared constant iota into an integer value. + +Expression* +Const_expression::do_lower(Gogo* gogo, Named_object*, + Statement_inserter*, int iota_value) +{ + if (this->constant_->const_value()->expr()->classification() + == EXPRESSION_IOTA) + { + if (iota_value == -1) + { + error_at(this->location(), + "iota is only defined in const declarations"); + iota_value = 0; + } + mpz_t val; + mpz_init_set_ui(val, static_cast(iota_value)); + Expression* ret = Expression::make_integer(&val, NULL, + this->location()); + mpz_clear(val); + return ret; + } + + // Make sure that the constant itself has been lowered. + gogo->lower_constant(this->constant_); + + return this; +} + +// Return a numeric constant value. + +bool +Const_expression::do_numeric_constant_value(Numeric_constant* nc) const +{ + if (this->seen_) + return false; + + Expression* e = this->constant_->const_value()->expr(); + + this->seen_ = true; + + bool r = e->numeric_constant_value(nc); + + this->seen_ = false; + + Type* ctype; + if (this->type_ != NULL) + ctype = this->type_; + else + ctype = this->constant_->const_value()->type(); + if (r && ctype != NULL) + { + if (!nc->set_type(ctype, false, this->location())) + return false; + } + + return r; +} + +bool +Const_expression::do_string_constant_value(std::string* val) const +{ + if (this->seen_) + return false; + + Expression* e = this->constant_->const_value()->expr(); + + this->seen_ = true; + bool ok = e->string_constant_value(val); + this->seen_ = false; + + return ok; +} + +// Return the type of the const reference. + +Type* +Const_expression::do_type() +{ + if (this->type_ != NULL) + return this->type_; + + Named_constant* nc = this->constant_->const_value(); + + if (this->seen_ || nc->lowering()) + { + this->report_error(_("constant refers to itself")); + this->type_ = Type::make_error_type(); + return this->type_; + } + + this->seen_ = true; + + Type* ret = nc->type(); + + if (ret != NULL) + { + this->seen_ = false; + return ret; + } + + // During parsing, a named constant may have a NULL type, but we + // must not return a NULL type here. + ret = nc->expr()->type(); + + this->seen_ = false; + + return ret; +} + +// Set the type of the const reference. + +void +Const_expression::do_determine_type(const Type_context* context) +{ + Type* ctype = this->constant_->const_value()->type(); + Type* cetype = (ctype != NULL + ? ctype + : this->constant_->const_value()->expr()->type()); + if (ctype != NULL && !ctype->is_abstract()) + ; + else if (context->type != NULL + && context->type->is_numeric_type() + && cetype->is_numeric_type()) + this->type_ = context->type; + else if (context->type != NULL + && context->type->is_string_type() + && cetype->is_string_type()) + this->type_ = context->type; + else if (context->type != NULL + && context->type->is_boolean_type() + && cetype->is_boolean_type()) + this->type_ = context->type; + else if (!context->may_be_abstract) + { + if (cetype->is_abstract()) + cetype = cetype->make_non_abstract_type(); + this->type_ = cetype; + } +} + +// Check for a loop in which the initializer of a constant refers to +// the constant itself. + +void +Const_expression::check_for_init_loop() +{ + if (this->type_ != NULL && this->type_->is_error()) + return; + + if (this->seen_) + { + this->report_error(_("constant refers to itself")); + this->type_ = Type::make_error_type(); + return; + } + + Expression* init = this->constant_->const_value()->expr(); + Find_named_object find_named_object(this->constant_); + + this->seen_ = true; + Expression::traverse(&init, &find_named_object); + this->seen_ = false; + + if (find_named_object.found()) + { + if (this->type_ == NULL || !this->type_->is_error()) + { + this->report_error(_("constant refers to itself")); + this->type_ = Type::make_error_type(); + } + return; + } +} + +// Check types of a const reference. + +void +Const_expression::do_check_types(Gogo*) +{ + if (this->type_ != NULL && this->type_->is_error()) + return; + + this->check_for_init_loop(); + + // Check that numeric constant fits in type. + if (this->type_ != NULL && this->type_->is_numeric_type()) + { + Numeric_constant nc; + if (this->constant_->const_value()->expr()->numeric_constant_value(&nc)) + { + if (!nc.set_type(this->type_, true, this->location())) + this->set_is_error(); + } + } +} + +// Return a tree for the const reference. + +tree +Const_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + tree type_tree; + if (this->type_ == NULL) + type_tree = NULL_TREE; + else + { + type_tree = type_to_tree(this->type_->get_backend(gogo)); + if (type_tree == error_mark_node) + return error_mark_node; + } + + // If the type has been set for this expression, but the underlying + // object is an abstract int or float, we try to get the abstract + // value. Otherwise we may lose something in the conversion. + if (this->type_ != NULL + && this->type_->is_numeric_type() + && (this->constant_->const_value()->type() == NULL + || this->constant_->const_value()->type()->is_abstract())) + { + Expression* expr = this->constant_->const_value()->expr(); + Numeric_constant nc; + if (expr->numeric_constant_value(&nc) + && nc.set_type(this->type_, false, this->location())) + { + Expression* e = nc.expression(this->location()); + return e->get_tree(context); + } + } + + tree const_tree = this->constant_->get_tree(gogo, context->function()); + if (this->type_ == NULL + || const_tree == error_mark_node + || TREE_TYPE(const_tree) == error_mark_node) + return const_tree; + + tree ret; + if (TYPE_MAIN_VARIANT(type_tree) == TYPE_MAIN_VARIANT(TREE_TYPE(const_tree))) + ret = fold_convert(type_tree, const_tree); + else if (TREE_CODE(type_tree) == INTEGER_TYPE) + ret = fold(convert_to_integer(type_tree, const_tree)); + else if (TREE_CODE(type_tree) == REAL_TYPE) + ret = fold(convert_to_real(type_tree, const_tree)); + else if (TREE_CODE(type_tree) == COMPLEX_TYPE) + ret = fold(convert_to_complex(type_tree, const_tree)); + else + go_unreachable(); + return ret; +} + +// Dump ast representation for constant expression. + +void +Const_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << this->constant_->name(); +} + +// Make a reference to a constant in an expression. + +Expression* +Expression::make_const_reference(Named_object* constant, + Location location) +{ + 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: + { + Const_expression* ce = static_cast(*pexpr); + if (ce->named_object() == this->no_) + break; + + // We need to check a constant initializer explicitly, as + // loops here will not be caught by the loop checking for + // variable initializers. + ce->check_for_init_loop(); + + 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 +{ + public: + Nil_expression(Location location) + : Expression(EXPRESSION_NIL, location) + { } + + static Expression* + do_import(Import*); + + protected: + bool + do_is_constant() const + { return true; } + + bool + do_is_immutable() const + { return true; } + + Type* + do_type() + { return Type::make_nil_type(); } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context*) + { return null_pointer_node; } + + void + do_export(Export* exp) const + { exp->write_c_string("nil"); } + + void + do_dump_expression(Ast_dump_context* ast_dump_context) const + { ast_dump_context->ostream() << "nil"; } +}; + +// Import a nil expression. + +Expression* +Nil_expression::do_import(Import* imp) +{ + imp->require_c_string("nil"); + return Expression::make_nil(imp->location()); +} + +// Make a nil expression. + +Expression* +Expression::make_nil(Location location) +{ + return new Nil_expression(location); +} + +// The value of the predeclared constant iota. This is little more +// than a marker. This will be lowered to an integer in +// Const_expression::do_lower, which is where we know the value that +// it should have. + +class Iota_expression : public Parser_expression +{ + public: + Iota_expression(Location location) + : Parser_expression(EXPRESSION_IOTA, location) + { } + + protected: + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int) + { go_unreachable(); } + + // There should only ever be one of these. + Expression* + do_copy() + { go_unreachable(); } + + void + do_dump_expression(Ast_dump_context* ast_dump_context) const + { ast_dump_context->ostream() << "iota"; } +}; + +// Make an iota expression. This is only called for one case: the +// value of the predeclared constant iota. + +Expression* +Expression::make_iota() +{ + static Iota_expression iota_expression(Linemap::unknown_location()); + return &iota_expression; +} + +// A type conversion expression. + +class Type_conversion_expression : public Expression +{ + public: + Type_conversion_expression(Type* type, Expression* expr, + Location location) + : Expression(EXPRESSION_CONVERSION, location), + type_(type), expr_(expr), may_convert_function_types_(false) + { } + + // Return the type to which we are converting. + Type* + type() const + { return this->type_; } + + // Return the expression which we are converting. + Expression* + expr() const + { return this->expr_; } + + // Permit converting from one function type to another. This is + // used internally for method expressions. + void + set_may_convert_function_types() + { + this->may_convert_function_types_ = true; + } + + // Import a type conversion expression. + static Expression* + do_import(Import*); + + protected: + int + do_traverse(Traverse* traverse); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + bool + do_is_constant() const; + + bool + do_numeric_constant_value(Numeric_constant*) const; + + bool + do_string_constant_value(std::string*) const; + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*) + { + Type_context subcontext(this->type_, false); + this->expr_->determine_type(&subcontext); + } + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Type_conversion_expression(this->type_, this->expr_->copy(), + this->location()); + } + + tree + do_get_tree(Translate_context* context); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type to convert to. + Type* type_; + // The expression to convert. + Expression* expr_; + // True if this is permitted to convert function types. This is + // used internally for method expressions. + bool may_convert_function_types_; +}; + +// Traversal. + +int +Type_conversion_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT + || Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Convert to a constant at lowering time. + +Expression* +Type_conversion_expression::do_lower(Gogo*, Named_object*, + Statement_inserter*, int) +{ + Type* type = this->type_; + Expression* val = this->expr_; + Location location = this->location(); + + if (type->is_numeric_type()) + { + Numeric_constant nc; + if (val->numeric_constant_value(&nc)) + { + if (!nc.set_type(type, true, location)) + return Expression::make_error(location); + return nc.expression(location); + } + } + + if (type->is_slice_type()) + { + Type* element_type = type->array_type()->element_type()->forwarded(); + bool is_byte = (element_type->integer_type() != NULL + && element_type->integer_type()->is_byte()); + bool is_rune = (element_type->integer_type() != NULL + && element_type->integer_type()->is_rune()); + if (is_byte || is_rune) + { + std::string s; + if (val->string_constant_value(&s)) + { + Expression_list* vals = new Expression_list(); + if (is_byte) + { + for (std::string::const_iterator p = s.begin(); + p != s.end(); + p++) + { + mpz_t val; + mpz_init_set_ui(val, static_cast(*p)); + Expression* v = Expression::make_integer(&val, + element_type, + location); + vals->push_back(v); + mpz_clear(val); + } + } + else + { + const char *p = s.data(); + const char *pend = s.data() + s.length(); + while (p < pend) + { + unsigned int c; + int adv = Lex::fetch_char(p, &c); + if (adv == 0) + { + warning_at(this->location(), 0, + "invalid UTF-8 encoding"); + adv = 1; + } + p += adv; + mpz_t val; + mpz_init_set_ui(val, c); + Expression* v = Expression::make_integer(&val, + element_type, + location); + vals->push_back(v); + mpz_clear(val); + } + } + + return Expression::make_slice_composite_literal(type, vals, + location); + } + } + } + + return this; +} + +// Flatten a type conversion by using a temporary variable for the slice +// in slice to string conversions. + +Expression* +Type_conversion_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + if (this->type()->is_string_type() + && this->expr_->type()->is_slice_type() + && !this->expr_->is_variable()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, this->expr_, this->location()); + inserter->insert(temp); + this->expr_ = Expression::make_temporary_reference(temp, this->location()); + } + return this; +} + +// Return whether a type conversion is a constant. + +bool +Type_conversion_expression::do_is_constant() const +{ + if (!this->expr_->is_constant()) + return false; + + // A conversion to a type that may not be used as a constant is not + // a constant. For example, []byte(nil). + Type* type = this->type_; + if (type->integer_type() == NULL + && type->float_type() == NULL + && type->complex_type() == NULL + && !type->is_boolean_type() + && !type->is_string_type()) + return false; + + return true; +} + +// Return the constant numeric value if there is one. + +bool +Type_conversion_expression::do_numeric_constant_value( + Numeric_constant* nc) const +{ + if (!this->type_->is_numeric_type()) + return false; + if (!this->expr_->numeric_constant_value(nc)) + return false; + return nc->set_type(this->type_, false, this->location()); +} + +// Return the constant string value if there is one. + +bool +Type_conversion_expression::do_string_constant_value(std::string* val) const +{ + if (this->type_->is_string_type() + && this->expr_->type()->integer_type() != NULL) + { + Numeric_constant nc; + if (this->expr_->numeric_constant_value(&nc)) + { + unsigned long ival; + if (nc.to_unsigned_long(&ival) == Numeric_constant::NC_UL_VALID) + { + val->clear(); + Lex::append_char(ival, true, val, this->location()); + return true; + } + } + } + + // FIXME: Could handle conversion from const []int here. + + return false; +} + +// Check that types are convertible. + +void +Type_conversion_expression::do_check_types(Gogo*) +{ + Type* type = this->type_; + Type* expr_type = this->expr_->type(); + std::string reason; + + if (type->is_error() || expr_type->is_error()) + { + this->set_is_error(); + return; + } + + if (this->may_convert_function_types_ + && type->function_type() != NULL + && expr_type->function_type() != NULL) + return; + + if (Type::are_convertible(type, expr_type, &reason)) + return; + + error_at(this->location(), "%s", reason.c_str()); + this->set_is_error(); +} + +// Get a tree for a type conversion. + +tree +Type_conversion_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + tree type_tree = type_to_tree(this->type_->get_backend(gogo)); + tree expr_tree = this->expr_->get_tree(context); + + if (type_tree == error_mark_node + || expr_tree == error_mark_node + || TREE_TYPE(expr_tree) == error_mark_node) + return error_mark_node; + + if (TYPE_MAIN_VARIANT(type_tree) == TYPE_MAIN_VARIANT(TREE_TYPE(expr_tree))) + return fold_convert(type_tree, expr_tree); + + Type* type = this->type_; + Type* expr_type = this->expr_->type(); + tree ret; + if (type->interface_type() != NULL || expr_type->interface_type() != NULL) + ret = Expression::convert_for_assignment(context, type, expr_type, + expr_tree, this->location()); + else if (type->integer_type() != NULL) + { + if (expr_type->integer_type() != NULL + || expr_type->float_type() != NULL + || expr_type->is_unsafe_pointer_type()) + ret = fold(convert_to_integer(type_tree, expr_tree)); + else + go_unreachable(); + } + else if (type->float_type() != NULL) + { + if (expr_type->integer_type() != NULL + || expr_type->float_type() != NULL) + ret = fold(convert_to_real(type_tree, expr_tree)); + else + go_unreachable(); + } + else if (type->complex_type() != NULL) + { + if (expr_type->complex_type() != NULL) + ret = fold(convert_to_complex(type_tree, expr_tree)); + else + go_unreachable(); + } + else if (type->is_string_type() + && expr_type->integer_type() != NULL) + { + Type* int_type = Type::lookup_integer_type("int"); + tree int_type_tree = type_to_tree(int_type->get_backend(gogo)); + + expr_tree = fold_convert(int_type_tree, expr_tree); + if (tree_fits_shwi_p (expr_tree)) + { + HOST_WIDE_INT intval = tree_to_shwi (expr_tree); + std::string s; + Lex::append_char(intval, true, &s, this->location()); + Expression* se = Expression::make_string(s, this->location()); + return se->get_tree(context); + } + + Expression* i2s_expr = + Runtime::make_call(Runtime::INT_TO_STRING, this->location(), 1, + this->expr_); + i2s_expr = Expression::make_cast(type, i2s_expr, this->location()); + ret = i2s_expr->get_tree(context); + } + else if (type->is_string_type() && expr_type->is_slice_type()) + { + Location location = this->location(); + Array_type* a = expr_type->array_type(); + Type* e = a->element_type()->forwarded(); + go_assert(e->integer_type() != NULL); + go_assert(this->expr_->is_variable()); + + Runtime::Function code; + if (e->integer_type()->is_byte()) + code = Runtime::BYTE_ARRAY_TO_STRING; + else + { + go_assert(e->integer_type()->is_rune()); + code = Runtime::INT_ARRAY_TO_STRING; + } + Expression* valptr = a->get_value_pointer(gogo, this->expr_); + Expression* len = a->get_length(gogo, this->expr_); + Expression* a2s_expr = Runtime::make_call(code, location, 2, valptr, len); + ret = a2s_expr->get_tree(context); + } + else if (type->is_slice_type() && expr_type->is_string_type()) + { + Type* e = type->array_type()->element_type()->forwarded(); + go_assert(e->integer_type() != NULL); + + Expression* s2a_expr; + if (e->integer_type()->is_byte()) + s2a_expr = Runtime::make_call(Runtime::STRING_TO_BYTE_ARRAY, + this->location(), 1, this->expr_); + else + { + go_assert(e->integer_type()->is_rune()); + s2a_expr = Runtime::make_call(Runtime::STRING_TO_INT_ARRAY, + this->location(), 1, this->expr_); + } + s2a_expr = Expression::make_unsafe_cast(type, s2a_expr, + this->location()); + ret = s2a_expr->get_tree(context); + } + else if ((type->is_unsafe_pointer_type() + && expr_type->points_to() != NULL) + || (expr_type->is_unsafe_pointer_type() + && type->points_to() != NULL)) + ret = fold_convert(type_tree, expr_tree); + else if (type->is_unsafe_pointer_type() + && expr_type->integer_type() != NULL) + ret = convert_to_pointer(type_tree, expr_tree); + else if (this->may_convert_function_types_ + && type->function_type() != NULL + && expr_type->function_type() != NULL) + ret = fold_convert_loc(this->location().gcc_location(), type_tree, + expr_tree); + else + ret = Expression::convert_for_assignment(context, type, expr_type, + expr_tree, this->location()); + + return ret; +} + +// Output a type conversion in a constant expression. + +void +Type_conversion_expression::do_export(Export* exp) const +{ + exp->write_c_string("convert("); + exp->write_type(this->type_); + exp->write_c_string(", "); + this->expr_->export_expression(exp); + exp->write_c_string(")"); +} + +// Import a type conversion or a struct construction. + +Expression* +Type_conversion_expression::do_import(Import* imp) +{ + imp->require_c_string("convert("); + Type* type = imp->read_type(); + imp->require_c_string(", "); + Expression* val = Expression::import_expression(imp); + imp->require_c_string(")"); + return Expression::make_cast(type, val, imp->location()); +} + +// Dump ast representation for a type conversion expression. + +void +Type_conversion_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << "("; + ast_dump_context->dump_expression(this->expr_); + ast_dump_context->ostream() << ") "; +} + +// Make a type cast expression. + +Expression* +Expression::make_cast(Type* type, Expression* val, Location location) +{ + if (type->is_error_type() || val->is_error_expression()) + return Expression::make_error(location); + return new Type_conversion_expression(type, val, location); +} + +// An unsafe type conversion, used to pass values to builtin functions. + +class Unsafe_type_conversion_expression : public Expression +{ + public: + Unsafe_type_conversion_expression(Type* type, Expression* expr, + Location location) + : Expression(EXPRESSION_UNSAFE_CONVERSION, location), + type_(type), expr_(expr) + { } + + protected: + int + do_traverse(Traverse* traverse); + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*) + { this->expr_->determine_type_no_context(); } + + Expression* + do_copy() + { + return new Unsafe_type_conversion_expression(this->type_, + this->expr_->copy(), + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type to convert to. + Type* type_; + // The expression to convert. + Expression* expr_; +}; + +// Traversal. + +int +Unsafe_type_conversion_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT + || Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Convert to backend representation. + +tree +Unsafe_type_conversion_expression::do_get_tree(Translate_context* context) +{ + // We are only called for a limited number of cases. + + Type* t = this->type_; + Type* et = this->expr_->type(); + + tree type_tree = type_to_tree(this->type_->get_backend(context->gogo())); + tree expr_tree = this->expr_->get_tree(context); + if (type_tree == error_mark_node || expr_tree == error_mark_node) + return error_mark_node; + + Location loc = this->location(); + + bool use_view_convert = false; + if (t->is_slice_type()) + { + go_assert(et->is_slice_type()); + use_view_convert = true; + } + else if (t->map_type() != NULL) + go_assert(et->map_type() != NULL); + else if (t->channel_type() != NULL) + go_assert(et->channel_type() != NULL); + else if (t->points_to() != NULL) + go_assert(et->points_to() != NULL || et->is_nil_type()); + else if (et->is_unsafe_pointer_type()) + go_assert(t->points_to() != NULL); + else if (t->interface_type() != NULL && !t->interface_type()->is_empty()) + { + go_assert(et->interface_type() != NULL + && !et->interface_type()->is_empty()); + use_view_convert = true; + } + else if (t->interface_type() != NULL && t->interface_type()->is_empty()) + { + go_assert(et->interface_type() != NULL + && et->interface_type()->is_empty()); + use_view_convert = true; + } + else if (t->integer_type() != NULL) + { + go_assert(et->is_boolean_type() + || et->integer_type() != NULL + || et->function_type() != NULL + || et->points_to() != NULL + || et->map_type() != NULL + || et->channel_type() != NULL); + return convert_to_integer(type_tree, expr_tree); + } + else + go_unreachable(); + + if (use_view_convert) + return fold_build1_loc(loc.gcc_location(), VIEW_CONVERT_EXPR, type_tree, + expr_tree); + else + return fold_convert_loc(loc.gcc_location(), type_tree, expr_tree); +} + +// Dump ast representation for an unsafe type conversion expression. + +void +Unsafe_type_conversion_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << "("; + ast_dump_context->dump_expression(this->expr_); + ast_dump_context->ostream() << ") "; +} + +// Make an unsafe type conversion expression. + +Expression* +Expression::make_unsafe_cast(Type* type, Expression* expr, + Location location) +{ + return new Unsafe_type_conversion_expression(type, expr, location); +} + +// Unary expressions. + +class Unary_expression : public Expression +{ + public: + Unary_expression(Operator op, Expression* expr, Location location) + : Expression(EXPRESSION_UNARY, location), + op_(op), escapes_(true), create_temp_(false), expr_(expr), + issue_nil_check_(false) + { } + + // Return the operator. + Operator + op() const + { return this->op_; } + + // Return the operand. + Expression* + operand() const + { return this->expr_; } + + // Record that an address expression does not escape. + void + set_does_not_escape() + { + go_assert(this->op_ == OPERATOR_AND); + this->escapes_ = false; + } + + // Record that this is an address expression which should create a + // temporary variable if necessary. This is used for method calls. + void + set_create_temp() + { + go_assert(this->op_ == OPERATOR_AND); + this->create_temp_ = true; + } + + // Apply unary opcode OP to UNC, setting NC. Return true if this + // could be done, false if not. Issue errors for overflow. + static bool + eval_constant(Operator op, const Numeric_constant* unc, + Location, Numeric_constant* nc); + + static Expression* + do_import(Import*); + + protected: + int + do_traverse(Traverse* traverse) + { return Expression::traverse(&this->expr_, traverse); } + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + bool + do_is_constant() const; + + bool + do_is_immutable() const + { return this->expr_->is_immutable(); } + + bool + do_numeric_constant_value(Numeric_constant*) const; + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_unary(this->op_, this->expr_->copy(), + this->location()); + } + + bool + do_must_eval_subexpressions_in_order(int*) const + { return this->op_ == OPERATOR_MULT; } + + bool + do_is_addressable() const + { return this->op_ == OPERATOR_MULT; } + + tree + do_get_tree(Translate_context*); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->issue_nil_check_ = (this->op_ == OPERATOR_MULT); } + + private: + // The unary operator to apply. + Operator op_; + // Normally true. False if this is an address expression which does + // not escape the current function. + bool escapes_; + // True if this is an address expression which should create a + // temporary variable if necessary. + bool create_temp_; + // The operand. + Expression* expr_; + // Whether or not to issue a nil check for this expression if its address + // is being taken. + bool issue_nil_check_; +}; + +// If we are taking the address of a composite literal, and the +// contents are not constant, then we want to make a heap composite +// instead. + +Expression* +Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) +{ + Location loc = this->location(); + Operator op = this->op_; + Expression* expr = this->expr_; + + if (op == OPERATOR_MULT && expr->is_type_expression()) + return Expression::make_type(Type::make_pointer_type(expr->type()), loc); + + // *&x simplifies to x. *(*T)(unsafe.Pointer)(&x) does not require + // moving x to the heap. FIXME: Is it worth doing a real escape + // analysis here? This case is found in math/unsafe.go and is + // therefore worth special casing. + if (op == OPERATOR_MULT) + { + Expression* e = expr; + while (e->classification() == EXPRESSION_CONVERSION) + { + Type_conversion_expression* te + = static_cast(e); + e = te->expr(); + } + + if (e->classification() == EXPRESSION_UNARY) + { + Unary_expression* ue = static_cast(e); + if (ue->op_ == OPERATOR_AND) + { + if (e == expr) + { + // *&x == x. + if (!ue->expr_->is_addressable() && !ue->create_temp_) + { + error_at(ue->location(), + "invalid operand for unary %<&%>"); + this->set_is_error(); + } + return ue->expr_; + } + ue->set_does_not_escape(); + } + } + } + + // Catching an invalid indirection of unsafe.Pointer here avoid + // having to deal with TYPE_VOID in other places. + if (op == OPERATOR_MULT && expr->type()->is_unsafe_pointer_type()) + { + error_at(this->location(), "invalid indirect of %"); + return Expression::make_error(this->location()); + } + + if (op == OPERATOR_PLUS || op == OPERATOR_MINUS || op == OPERATOR_XOR) + { + Numeric_constant nc; + if (expr->numeric_constant_value(&nc)) + { + Numeric_constant result; + if (Unary_expression::eval_constant(op, &nc, loc, &result)) + return result.expression(loc); + } + } + + return this; +} + +// Flatten expression if a nil check must be performed and create temporary +// variables if necessary. + +Expression* +Unary_expression::do_flatten(Gogo* gogo, Named_object*, + Statement_inserter* inserter) +{ + if (this->is_error_expression() || this->expr_->is_error_expression()) + return Expression::make_error(this->location()); + + Location location = this->location(); + if (this->op_ == OPERATOR_MULT + && !this->expr_->is_variable()) + { + go_assert(this->expr_->type()->points_to() != NULL); + Type* ptype = this->expr_->type()->points_to(); + if (!ptype->is_void_type()) + { + Btype* pbtype = ptype->get_backend(gogo); + size_t s = gogo->backend()->type_size(pbtype); + if (s >= 4096 || this->issue_nil_check_) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, this->expr_, location); + inserter->insert(temp); + this->expr_ = + Expression::make_temporary_reference(temp, location); + } + } + } + + if (this->create_temp_ && !this->expr_->is_variable()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, this->expr_, location); + inserter->insert(temp); + this->expr_ = Expression::make_temporary_reference(temp, location); + } + + return this; +} + +// Return whether a unary expression is a constant. + +bool +Unary_expression::do_is_constant() const +{ + if (this->op_ == OPERATOR_MULT) + { + // Indirecting through a pointer is only constant if the object + // to which the expression points is constant, but we currently + // have no way to determine that. + return false; + } + else if (this->op_ == OPERATOR_AND) + { + // Taking the address of a variable is constant if it is a + // global variable, not constant otherwise. In other cases taking the + // address is probably not a constant. + Var_expression* ve = this->expr_->var_expression(); + if (ve != NULL) + { + Named_object* no = ve->named_object(); + return no->is_variable() && no->var_value()->is_global(); + } + return false; + } + else + return this->expr_->is_constant(); +} + +// Apply unary opcode OP to UNC, setting NC. Return true if this +// could be done, false if not. Issue errors for overflow. + +bool +Unary_expression::eval_constant(Operator op, const Numeric_constant* unc, + Location location, Numeric_constant* nc) +{ + switch (op) + { + case OPERATOR_PLUS: + *nc = *unc; + return true; + + case OPERATOR_MINUS: + if (unc->is_int() || unc->is_rune()) + break; + else if (unc->is_float()) + { + mpfr_t uval; + unc->get_float(&uval); + mpfr_t val; + mpfr_init(val); + mpfr_neg(val, uval, GMP_RNDN); + nc->set_float(unc->type(), val); + mpfr_clear(uval); + mpfr_clear(val); + return true; + } + else if (unc->is_complex()) + { + mpfr_t ureal, uimag; + unc->get_complex(&ureal, &uimag); + mpfr_t real, imag; + mpfr_init(real); + mpfr_init(imag); + mpfr_neg(real, ureal, GMP_RNDN); + mpfr_neg(imag, uimag, GMP_RNDN); + nc->set_complex(unc->type(), real, imag); + mpfr_clear(ureal); + mpfr_clear(uimag); + mpfr_clear(real); + mpfr_clear(imag); + return true; + } + else + go_unreachable(); + + case OPERATOR_XOR: + break; + + case OPERATOR_NOT: + case OPERATOR_AND: + case OPERATOR_MULT: + return false; + + default: + go_unreachable(); + } + + if (!unc->is_int() && !unc->is_rune()) + return false; + + mpz_t uval; + if (unc->is_rune()) + unc->get_rune(&uval); + else + unc->get_int(&uval); + mpz_t val; + mpz_init(val); + + switch (op) + { + case OPERATOR_MINUS: + mpz_neg(val, uval); + break; + + case OPERATOR_NOT: + mpz_set_ui(val, mpz_cmp_si(uval, 0) == 0 ? 1 : 0); + break; + + case OPERATOR_XOR: + { + Type* utype = unc->type(); + if (utype->integer_type() == NULL + || utype->integer_type()->is_abstract()) + mpz_com(val, uval); + else + { + // The number of HOST_WIDE_INTs that it takes to represent + // UVAL. + size_t count = ((mpz_sizeinbase(uval, 2) + + HOST_BITS_PER_WIDE_INT + - 1) + / HOST_BITS_PER_WIDE_INT); + + unsigned HOST_WIDE_INT* phwi = new unsigned HOST_WIDE_INT[count]; + memset(phwi, 0, count * sizeof(HOST_WIDE_INT)); + + size_t obits = utype->integer_type()->bits(); + + if (!utype->integer_type()->is_unsigned() && mpz_sgn(uval) < 0) + { + mpz_t adj; + mpz_init_set_ui(adj, 1); + mpz_mul_2exp(adj, adj, obits); + mpz_add(uval, uval, adj); + mpz_clear(adj); + } + + size_t ecount; + mpz_export(phwi, &ecount, -1, sizeof(HOST_WIDE_INT), 0, 0, uval); + go_assert(ecount <= count); + + // Trim down to the number of words required by the type. + size_t ocount = ((obits + HOST_BITS_PER_WIDE_INT - 1) + / HOST_BITS_PER_WIDE_INT); + go_assert(ocount <= count); + + for (size_t i = 0; i < ocount; ++i) + phwi[i] = ~phwi[i]; + + size_t clearbits = ocount * HOST_BITS_PER_WIDE_INT - obits; + if (clearbits != 0) + phwi[ocount - 1] &= (((unsigned HOST_WIDE_INT) (HOST_WIDE_INT) -1) + >> clearbits); + + mpz_import(val, ocount, -1, sizeof(HOST_WIDE_INT), 0, 0, phwi); + + if (!utype->integer_type()->is_unsigned() + && mpz_tstbit(val, obits - 1)) + { + mpz_t adj; + mpz_init_set_ui(adj, 1); + mpz_mul_2exp(adj, adj, obits); + mpz_sub(val, val, adj); + mpz_clear(adj); + } + + delete[] phwi; + } + } + break; + + default: + go_unreachable(); + } + + if (unc->is_rune()) + nc->set_rune(NULL, val); + else + nc->set_int(NULL, val); + + mpz_clear(uval); + mpz_clear(val); + + return nc->set_type(unc->type(), true, location); +} + +// Return the integral constant value of a unary expression, if it has one. + +bool +Unary_expression::do_numeric_constant_value(Numeric_constant* nc) const +{ + Numeric_constant unc; + if (!this->expr_->numeric_constant_value(&unc)) + return false; + return Unary_expression::eval_constant(this->op_, &unc, this->location(), + nc); +} + +// Return the type of a unary expression. + +Type* +Unary_expression::do_type() +{ + switch (this->op_) + { + case OPERATOR_PLUS: + case OPERATOR_MINUS: + case OPERATOR_NOT: + case OPERATOR_XOR: + return this->expr_->type(); + + case OPERATOR_AND: + return Type::make_pointer_type(this->expr_->type()); + + case OPERATOR_MULT: + { + Type* subtype = this->expr_->type(); + Type* points_to = subtype->points_to(); + if (points_to == NULL) + return Type::make_error_type(); + return points_to; + } + + default: + go_unreachable(); + } +} + +// Determine abstract types for a unary expression. + +void +Unary_expression::do_determine_type(const Type_context* context) +{ + switch (this->op_) + { + case OPERATOR_PLUS: + case OPERATOR_MINUS: + case OPERATOR_NOT: + case OPERATOR_XOR: + this->expr_->determine_type(context); + break; + + case OPERATOR_AND: + // Taking the address of something. + { + Type* subtype = (context->type == NULL + ? NULL + : context->type->points_to()); + Type_context subcontext(subtype, false); + this->expr_->determine_type(&subcontext); + } + break; + + case OPERATOR_MULT: + // Indirecting through a pointer. + { + Type* subtype = (context->type == NULL + ? NULL + : Type::make_pointer_type(context->type)); + Type_context subcontext(subtype, false); + this->expr_->determine_type(&subcontext); + } + break; + + default: + go_unreachable(); + } +} + +// Check types for a unary expression. + +void +Unary_expression::do_check_types(Gogo*) +{ + Type* type = this->expr_->type(); + if (type->is_error()) + { + this->set_is_error(); + return; + } + + switch (this->op_) + { + case OPERATOR_PLUS: + case OPERATOR_MINUS: + if (type->integer_type() == NULL + && type->float_type() == NULL + && type->complex_type() == NULL) + this->report_error(_("expected numeric type")); + break; + + case OPERATOR_NOT: + if (!type->is_boolean_type()) + this->report_error(_("expected boolean type")); + break; + + case OPERATOR_XOR: + if (type->integer_type() == NULL + && !type->is_boolean_type()) + this->report_error(_("expected integer or boolean type")); + break; + + case OPERATOR_AND: + if (!this->expr_->is_addressable()) + { + if (!this->create_temp_) + { + error_at(this->location(), "invalid operand for unary %<&%>"); + this->set_is_error(); + } + } + else + { + this->expr_->address_taken(this->escapes_); + this->expr_->issue_nil_check(); + } + break; + + case OPERATOR_MULT: + // Indirecting through a pointer. + if (type->points_to() == NULL) + this->report_error(_("expected pointer")); + break; + + default: + go_unreachable(); + } +} + +// Get a tree for a unary expression. + +tree +Unary_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + Location loc = this->location(); + + // Taking the address of a set-and-use-temporary expression requires + // setting the temporary and then taking the address. + if (this->op_ == OPERATOR_AND) + { + Set_and_use_temporary_expression* sut = + this->expr_->set_and_use_temporary_expression(); + if (sut != NULL) + { + Temporary_statement* temp = sut->temporary(); + Bvariable* bvar = temp->get_backend_variable(context); + Bexpression* bvar_expr = gogo->backend()->var_expression(bvar, loc); + + Expression* val = sut->expression(); + Bexpression* bval = tree_to_expr(val->get_tree(context)); + + Bstatement* bassign = + gogo->backend()->assignment_statement(bvar_expr, bval, loc); + Bexpression* bvar_addr = + gogo->backend()->address_expression(bvar_expr, loc); + Bexpression* ret = + gogo->backend()->compound_expression(bassign, bvar_addr, loc); + return expr_to_tree(ret); + } + } + + Bexpression* ret; + tree expr = this->expr_->get_tree(context); + Bexpression* bexpr = tree_to_expr(expr); + Btype* btype = this->expr_->type()->get_backend(gogo); + switch (this->op_) + { + case OPERATOR_PLUS: + ret = bexpr; + break; + + case OPERATOR_MINUS: + ret = gogo->backend()->unary_expression(this->op_, bexpr, loc); + ret = gogo->backend()->convert_expression(btype, ret, loc); + break; + + case OPERATOR_NOT: + case OPERATOR_XOR: + ret = gogo->backend()->unary_expression(this->op_, bexpr, loc); + break; + + case OPERATOR_AND: + if (!this->create_temp_) + { + // We should not see a non-constant constructor here; cases + // where we would see one should have been moved onto the + // heap at parse time. Taking the address of a nonconstant + // constructor will not do what the programmer expects. + + go_assert(!this->expr_->is_composite_literal() + || this->expr_->is_immutable()); + Unary_expression* ue = static_cast(this->expr_); + go_assert(ue == NULL || ue->op() != OPERATOR_AND); + } + + // Build a decl for a constant constructor. + if ((this->expr_->is_composite_literal() + || this->expr_->string_expression() != NULL) + && this->expr_->is_immutable()) + { + static unsigned int counter; + char buf[100]; + snprintf(buf, sizeof buf, "C%u", counter); + ++counter; + + Bvariable* decl = + gogo->backend()->immutable_struct(buf, true, false, btype, loc); + gogo->backend()->immutable_struct_set_init(decl, buf, true, false, + btype, loc, bexpr); + bexpr = gogo->backend()->var_expression(decl, loc); + } + + go_assert(!this->create_temp_ || this->expr_->is_variable()); + ret = gogo->backend()->address_expression(bexpr, loc); + break; + + case OPERATOR_MULT: + { + go_assert(this->expr_->type()->points_to() != NULL); + + // If we are dereferencing the pointer to a large struct, we + // need to check for nil. We don't bother to check for small + // structs because we expect the system to crash on a nil + // pointer dereference. However, if we know the address of this + // expression is being taken, we must always check for nil. + + Type* ptype = this->expr_->type()->points_to(); + Btype* pbtype = ptype->get_backend(gogo); + if (!ptype->is_void_type()) + { + size_t s = gogo->backend()->type_size(pbtype); + if (s >= 4096 || this->issue_nil_check_) + { + go_assert(this->expr_->is_variable()); + + Expression* nil_expr = Expression::make_nil(loc); + Bexpression* nil = tree_to_expr(nil_expr->get_tree(context)); + Bexpression* compare = + gogo->backend()->binary_expression(OPERATOR_EQEQ, bexpr, + nil, loc); + + Expression* crash_expr = + gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); + Bexpression* crash = + tree_to_expr(crash_expr->get_tree(context)); + bexpr = gogo->backend()->conditional_expression(btype, compare, + crash, bexpr, + loc); + + } + } + + // If the type of EXPR is a recursive pointer type, then we + // need to insert a cast before indirecting. + tree expr = expr_to_tree(bexpr); + tree target_type_tree = TREE_TYPE(TREE_TYPE(expr)); + if (VOID_TYPE_P(target_type_tree)) + { + tree ind = type_to_tree(pbtype); + expr = fold_convert_loc(loc.gcc_location(), + build_pointer_type(ind), expr); + bexpr = tree_to_expr(expr); + } + + ret = gogo->backend()->indirect_expression(bexpr, false, loc); + } + break; + + default: + go_unreachable(); + } + + return expr_to_tree(ret); +} + +// Export a unary expression. + +void +Unary_expression::do_export(Export* exp) const +{ + switch (this->op_) + { + case OPERATOR_PLUS: + exp->write_c_string("+ "); + break; + case OPERATOR_MINUS: + exp->write_c_string("- "); + break; + case OPERATOR_NOT: + exp->write_c_string("! "); + break; + case OPERATOR_XOR: + exp->write_c_string("^ "); + break; + case OPERATOR_AND: + case OPERATOR_MULT: + default: + go_unreachable(); + } + this->expr_->export_expression(exp); +} + +// Import a unary expression. + +Expression* +Unary_expression::do_import(Import* imp) +{ + Operator op; + switch (imp->get_char()) + { + case '+': + op = OPERATOR_PLUS; + break; + case '-': + op = OPERATOR_MINUS; + break; + case '!': + op = OPERATOR_NOT; + break; + case '^': + op = OPERATOR_XOR; + break; + default: + go_unreachable(); + } + imp->require_c_string(" "); + Expression* expr = Expression::import_expression(imp); + return Expression::make_unary(op, expr, imp->location()); +} + +// Dump ast representation of an unary expression. + +void +Unary_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->dump_operator(this->op_); + ast_dump_context->ostream() << "("; + ast_dump_context->dump_expression(this->expr_); + ast_dump_context->ostream() << ") "; +} + +// Make a unary expression. + +Expression* +Expression::make_unary(Operator op, Expression* expr, Location location) +{ + return new Unary_expression(op, expr, location); +} + +// If this is an indirection through a pointer, return the expression +// being pointed through. Otherwise return this. + +Expression* +Expression::deref() +{ + if (this->classification_ == EXPRESSION_UNARY) + { + Unary_expression* ue = static_cast(this); + if (ue->op() == OPERATOR_MULT) + return ue->operand(); + } + return this; +} + +// Class Binary_expression. + +// Traversal. + +int +Binary_expression::do_traverse(Traverse* traverse) +{ + int t = Expression::traverse(&this->left_, traverse); + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return Expression::traverse(&this->right_, traverse); +} + +// Return the type to use for a binary operation on operands of +// LEFT_TYPE and RIGHT_TYPE. These are the types of constants and as +// such may be NULL or abstract. + +bool +Binary_expression::operation_type(Operator op, Type* left_type, + Type* right_type, Type** result_type) +{ + if (left_type != right_type + && !left_type->is_abstract() + && !right_type->is_abstract() + && left_type->base() != right_type->base() + && op != OPERATOR_LSHIFT + && op != OPERATOR_RSHIFT) + { + // May be a type error--let it be diagnosed elsewhere. + return false; + } + + if (op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT) + { + if (left_type->integer_type() != NULL) + *result_type = left_type; + else + *result_type = Type::make_abstract_integer_type(); + } + else if (!left_type->is_abstract() && left_type->named_type() != NULL) + *result_type = left_type; + else if (!right_type->is_abstract() && right_type->named_type() != NULL) + *result_type = right_type; + else if (!left_type->is_abstract()) + *result_type = left_type; + else if (!right_type->is_abstract()) + *result_type = right_type; + else if (left_type->complex_type() != NULL) + *result_type = left_type; + else if (right_type->complex_type() != NULL) + *result_type = right_type; + else if (left_type->float_type() != NULL) + *result_type = left_type; + else if (right_type->float_type() != NULL) + *result_type = right_type; + else if (left_type->integer_type() != NULL + && left_type->integer_type()->is_rune()) + *result_type = left_type; + else if (right_type->integer_type() != NULL + && right_type->integer_type()->is_rune()) + *result_type = right_type; + else + *result_type = left_type; + + return true; +} + +// Convert an integer comparison code and an operator to a boolean +// value. + +bool +Binary_expression::cmp_to_bool(Operator op, int cmp) +{ + switch (op) + { + case OPERATOR_EQEQ: + return cmp == 0; + break; + case OPERATOR_NOTEQ: + return cmp != 0; + break; + case OPERATOR_LT: + return cmp < 0; + break; + case OPERATOR_LE: + return cmp <= 0; + case OPERATOR_GT: + return cmp > 0; + case OPERATOR_GE: + return cmp >= 0; + default: + go_unreachable(); + } +} + +// Compare constants according to OP. + +bool +Binary_expression::compare_constant(Operator op, Numeric_constant* left_nc, + Numeric_constant* right_nc, + Location location, bool* result) +{ + Type* left_type = left_nc->type(); + Type* right_type = right_nc->type(); + + Type* type; + if (!Binary_expression::operation_type(op, left_type, right_type, &type)) + return false; + + // When comparing an untyped operand to a typed operand, we are + // effectively coercing the untyped operand to the other operand's + // type, so make sure that is valid. + if (!left_nc->set_type(type, true, location) + || !right_nc->set_type(type, true, location)) + return false; + + bool ret; + int cmp; + if (type->complex_type() != NULL) + { + if (op != OPERATOR_EQEQ && op != OPERATOR_NOTEQ) + return false; + ret = Binary_expression::compare_complex(left_nc, right_nc, &cmp); + } + else if (type->float_type() != NULL) + ret = Binary_expression::compare_float(left_nc, right_nc, &cmp); + else + ret = Binary_expression::compare_integer(left_nc, right_nc, &cmp); + + if (ret) + *result = Binary_expression::cmp_to_bool(op, cmp); + + return ret; +} + +// Compare integer constants. + +bool +Binary_expression::compare_integer(const Numeric_constant* left_nc, + const Numeric_constant* right_nc, + int* cmp) +{ + mpz_t left_val; + if (!left_nc->to_int(&left_val)) + return false; + mpz_t right_val; + if (!right_nc->to_int(&right_val)) + { + mpz_clear(left_val); + return false; + } + + *cmp = mpz_cmp(left_val, right_val); + + mpz_clear(left_val); + mpz_clear(right_val); + + return true; +} + +// Compare floating point constants. + +bool +Binary_expression::compare_float(const Numeric_constant* left_nc, + const Numeric_constant* right_nc, + int* cmp) +{ + mpfr_t left_val; + if (!left_nc->to_float(&left_val)) + return false; + mpfr_t right_val; + if (!right_nc->to_float(&right_val)) + { + mpfr_clear(left_val); + return false; + } + + // We already coerced both operands to the same type. If that type + // is not an abstract type, we need to round the values accordingly. + Type* type = left_nc->type(); + if (!type->is_abstract() && type->float_type() != NULL) + { + int bits = type->float_type()->bits(); + mpfr_prec_round(left_val, bits, GMP_RNDN); + mpfr_prec_round(right_val, bits, GMP_RNDN); + } + + *cmp = mpfr_cmp(left_val, right_val); + + mpfr_clear(left_val); + mpfr_clear(right_val); + + return true; +} + +// Compare complex constants. Complex numbers may only be compared +// for equality. + +bool +Binary_expression::compare_complex(const Numeric_constant* left_nc, + const Numeric_constant* right_nc, + int* cmp) +{ + mpfr_t left_real, left_imag; + if (!left_nc->to_complex(&left_real, &left_imag)) + return false; + mpfr_t right_real, right_imag; + if (!right_nc->to_complex(&right_real, &right_imag)) + { + mpfr_clear(left_real); + mpfr_clear(left_imag); + return false; + } + + // We already coerced both operands to the same type. If that type + // is not an abstract type, we need to round the values accordingly. + Type* type = left_nc->type(); + if (!type->is_abstract() && type->complex_type() != NULL) + { + int bits = type->complex_type()->bits(); + mpfr_prec_round(left_real, bits / 2, GMP_RNDN); + mpfr_prec_round(left_imag, bits / 2, GMP_RNDN); + mpfr_prec_round(right_real, bits / 2, GMP_RNDN); + mpfr_prec_round(right_imag, bits / 2, GMP_RNDN); + } + + *cmp = (mpfr_cmp(left_real, right_real) != 0 + || mpfr_cmp(left_imag, right_imag) != 0); + + mpfr_clear(left_real); + mpfr_clear(left_imag); + mpfr_clear(right_real); + mpfr_clear(right_imag); + + return true; +} + +// Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC. Return +// true if this could be done, false if not. Issue errors at LOCATION +// as appropriate. + +bool +Binary_expression::eval_constant(Operator op, Numeric_constant* left_nc, + Numeric_constant* right_nc, + Location location, Numeric_constant* nc) +{ + switch (op) + { + case OPERATOR_OROR: + case OPERATOR_ANDAND: + case OPERATOR_EQEQ: + case OPERATOR_NOTEQ: + case OPERATOR_LT: + case OPERATOR_LE: + case OPERATOR_GT: + case OPERATOR_GE: + // These return boolean values, not numeric. + return false; + default: + break; + } + + Type* left_type = left_nc->type(); + Type* right_type = right_nc->type(); + + Type* type; + if (!Binary_expression::operation_type(op, left_type, right_type, &type)) + return false; + + bool is_shift = op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT; + + // When combining an untyped operand with a typed operand, we are + // effectively coercing the untyped operand to the other operand's + // type, so make sure that is valid. + if (!left_nc->set_type(type, true, location)) + return false; + if (!is_shift && !right_nc->set_type(type, true, location)) + return false; + + bool r; + if (type->complex_type() != NULL) + r = Binary_expression::eval_complex(op, left_nc, right_nc, location, nc); + else if (type->float_type() != NULL) + r = Binary_expression::eval_float(op, left_nc, right_nc, location, nc); + else + r = Binary_expression::eval_integer(op, left_nc, right_nc, location, nc); + + if (r) + r = nc->set_type(type, true, location); + + return r; +} + +// Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC, using +// integer operations. Return true if this could be done, false if +// not. + +bool +Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc, + const Numeric_constant* right_nc, + Location location, Numeric_constant* nc) +{ + mpz_t left_val; + if (!left_nc->to_int(&left_val)) + return false; + mpz_t right_val; + if (!right_nc->to_int(&right_val)) + { + mpz_clear(left_val); + return false; + } + + mpz_t val; + mpz_init(val); + + switch (op) + { + case OPERATOR_PLUS: + mpz_add(val, left_val, right_val); + break; + case OPERATOR_MINUS: + mpz_sub(val, left_val, right_val); + break; + case OPERATOR_OR: + mpz_ior(val, left_val, right_val); + break; + case OPERATOR_XOR: + mpz_xor(val, left_val, right_val); + break; + case OPERATOR_MULT: + mpz_mul(val, left_val, right_val); + break; + case OPERATOR_DIV: + if (mpz_sgn(right_val) != 0) + mpz_tdiv_q(val, left_val, right_val); + else + { + error_at(location, "division by zero"); + mpz_set_ui(val, 0); + } + break; + case OPERATOR_MOD: + if (mpz_sgn(right_val) != 0) + mpz_tdiv_r(val, left_val, right_val); + else + { + error_at(location, "division by zero"); + mpz_set_ui(val, 0); + } + break; + case OPERATOR_LSHIFT: + { + unsigned long shift = mpz_get_ui(right_val); + if (mpz_cmp_ui(right_val, shift) == 0 && shift <= 0x100000) + mpz_mul_2exp(val, left_val, shift); + else + { + error_at(location, "shift count overflow"); + mpz_set_ui(val, 0); + } + break; + } + break; + case OPERATOR_RSHIFT: + { + unsigned long shift = mpz_get_ui(right_val); + if (mpz_cmp_ui(right_val, shift) != 0) + { + error_at(location, "shift count overflow"); + mpz_set_ui(val, 0); + } + else + { + if (mpz_cmp_ui(left_val, 0) >= 0) + mpz_tdiv_q_2exp(val, left_val, shift); + else + mpz_fdiv_q_2exp(val, left_val, shift); + } + break; + } + break; + case OPERATOR_AND: + mpz_and(val, left_val, right_val); + break; + case OPERATOR_BITCLEAR: + { + mpz_t tval; + mpz_init(tval); + mpz_com(tval, right_val); + mpz_and(val, left_val, tval); + mpz_clear(tval); + } + break; + default: + go_unreachable(); + } + + mpz_clear(left_val); + mpz_clear(right_val); + + if (left_nc->is_rune() + || (op != OPERATOR_LSHIFT + && op != OPERATOR_RSHIFT + && right_nc->is_rune())) + nc->set_rune(NULL, val); + else + nc->set_int(NULL, val); + + mpz_clear(val); + + return true; +} + +// Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC, using +// floating point operations. Return true if this could be done, +// false if not. + +bool +Binary_expression::eval_float(Operator op, const Numeric_constant* left_nc, + const Numeric_constant* right_nc, + Location location, Numeric_constant* nc) +{ + mpfr_t left_val; + if (!left_nc->to_float(&left_val)) + return false; + mpfr_t right_val; + if (!right_nc->to_float(&right_val)) + { + mpfr_clear(left_val); + return false; + } + + mpfr_t val; + mpfr_init(val); + + bool ret = true; + switch (op) + { + case OPERATOR_PLUS: + mpfr_add(val, left_val, right_val, GMP_RNDN); + break; + case OPERATOR_MINUS: + mpfr_sub(val, left_val, right_val, GMP_RNDN); + break; + case OPERATOR_OR: + case OPERATOR_XOR: + case OPERATOR_AND: + case OPERATOR_BITCLEAR: + case OPERATOR_MOD: + case OPERATOR_LSHIFT: + case OPERATOR_RSHIFT: + mpfr_set_ui(val, 0, GMP_RNDN); + ret = false; + break; + case OPERATOR_MULT: + mpfr_mul(val, left_val, right_val, GMP_RNDN); + break; + case OPERATOR_DIV: + if (!mpfr_zero_p(right_val)) + mpfr_div(val, left_val, right_val, GMP_RNDN); + else + { + error_at(location, "division by zero"); + mpfr_set_ui(val, 0, GMP_RNDN); + } + break; + default: + go_unreachable(); + } + + mpfr_clear(left_val); + mpfr_clear(right_val); + + nc->set_float(NULL, val); + mpfr_clear(val); + + return ret; +} + +// Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC, using +// complex operations. Return true if this could be done, false if +// not. + +bool +Binary_expression::eval_complex(Operator op, const Numeric_constant* left_nc, + const Numeric_constant* right_nc, + Location location, Numeric_constant* nc) +{ + mpfr_t left_real, left_imag; + if (!left_nc->to_complex(&left_real, &left_imag)) + return false; + mpfr_t right_real, right_imag; + if (!right_nc->to_complex(&right_real, &right_imag)) + { + mpfr_clear(left_real); + mpfr_clear(left_imag); + return false; + } + + mpfr_t real, imag; + mpfr_init(real); + mpfr_init(imag); + + bool ret = true; + switch (op) + { + case OPERATOR_PLUS: + mpfr_add(real, left_real, right_real, GMP_RNDN); + mpfr_add(imag, left_imag, right_imag, GMP_RNDN); + break; + case OPERATOR_MINUS: + mpfr_sub(real, left_real, right_real, GMP_RNDN); + mpfr_sub(imag, left_imag, right_imag, GMP_RNDN); + break; + case OPERATOR_OR: + case OPERATOR_XOR: + case OPERATOR_AND: + case OPERATOR_BITCLEAR: + case OPERATOR_MOD: + case OPERATOR_LSHIFT: + case OPERATOR_RSHIFT: + mpfr_set_ui(real, 0, GMP_RNDN); + mpfr_set_ui(imag, 0, GMP_RNDN); + ret = false; + break; + case OPERATOR_MULT: + { + // You might think that multiplying two complex numbers would + // be simple, and you would be right, until you start to think + // about getting the right answer for infinity. If one + // operand here is infinity and the other is anything other + // than zero or NaN, then we are going to wind up subtracting + // two infinity values. That will give us a NaN, but the + // correct answer is infinity. + + mpfr_t lrrr; + mpfr_init(lrrr); + mpfr_mul(lrrr, left_real, right_real, GMP_RNDN); + + mpfr_t lrri; + mpfr_init(lrri); + mpfr_mul(lrri, left_real, right_imag, GMP_RNDN); + + mpfr_t lirr; + mpfr_init(lirr); + mpfr_mul(lirr, left_imag, right_real, GMP_RNDN); + + mpfr_t liri; + mpfr_init(liri); + mpfr_mul(liri, left_imag, right_imag, GMP_RNDN); + + mpfr_sub(real, lrrr, liri, GMP_RNDN); + mpfr_add(imag, lrri, lirr, GMP_RNDN); + + // If we get NaN on both sides, check whether it should really + // be infinity. The rule is that if either side of the + // complex number is infinity, then the whole value is + // infinity, even if the other side is NaN. So the only case + // we have to fix is the one in which both sides are NaN. + if (mpfr_nan_p(real) && mpfr_nan_p(imag) + && (!mpfr_nan_p(left_real) || !mpfr_nan_p(left_imag)) + && (!mpfr_nan_p(right_real) || !mpfr_nan_p(right_imag))) + { + bool is_infinity = false; + + mpfr_t lr; + mpfr_t li; + mpfr_init_set(lr, left_real, GMP_RNDN); + mpfr_init_set(li, left_imag, GMP_RNDN); + + mpfr_t rr; + mpfr_t ri; + mpfr_init_set(rr, right_real, GMP_RNDN); + mpfr_init_set(ri, right_imag, GMP_RNDN); + + // If the left side is infinity, then the result is + // infinity. + if (mpfr_inf_p(lr) || mpfr_inf_p(li)) + { + mpfr_set_ui(lr, mpfr_inf_p(lr) ? 1 : 0, GMP_RNDN); + mpfr_copysign(lr, lr, left_real, GMP_RNDN); + mpfr_set_ui(li, mpfr_inf_p(li) ? 1 : 0, GMP_RNDN); + mpfr_copysign(li, li, left_imag, GMP_RNDN); + if (mpfr_nan_p(rr)) + { + mpfr_set_ui(rr, 0, GMP_RNDN); + mpfr_copysign(rr, rr, right_real, GMP_RNDN); + } + if (mpfr_nan_p(ri)) + { + mpfr_set_ui(ri, 0, GMP_RNDN); + mpfr_copysign(ri, ri, right_imag, GMP_RNDN); + } + is_infinity = true; + } + + // If the right side is infinity, then the result is + // infinity. + if (mpfr_inf_p(rr) || mpfr_inf_p(ri)) + { + mpfr_set_ui(rr, mpfr_inf_p(rr) ? 1 : 0, GMP_RNDN); + mpfr_copysign(rr, rr, right_real, GMP_RNDN); + mpfr_set_ui(ri, mpfr_inf_p(ri) ? 1 : 0, GMP_RNDN); + mpfr_copysign(ri, ri, right_imag, GMP_RNDN); + if (mpfr_nan_p(lr)) + { + mpfr_set_ui(lr, 0, GMP_RNDN); + mpfr_copysign(lr, lr, left_real, GMP_RNDN); + } + if (mpfr_nan_p(li)) + { + mpfr_set_ui(li, 0, GMP_RNDN); + mpfr_copysign(li, li, left_imag, GMP_RNDN); + } + is_infinity = true; + } + + // If we got an overflow in the intermediate computations, + // then the result is infinity. + if (!is_infinity + && (mpfr_inf_p(lrrr) || mpfr_inf_p(lrri) + || mpfr_inf_p(lirr) || mpfr_inf_p(liri))) + { + if (mpfr_nan_p(lr)) + { + mpfr_set_ui(lr, 0, GMP_RNDN); + mpfr_copysign(lr, lr, left_real, GMP_RNDN); + } + if (mpfr_nan_p(li)) + { + mpfr_set_ui(li, 0, GMP_RNDN); + mpfr_copysign(li, li, left_imag, GMP_RNDN); + } + if (mpfr_nan_p(rr)) + { + mpfr_set_ui(rr, 0, GMP_RNDN); + mpfr_copysign(rr, rr, right_real, GMP_RNDN); + } + if (mpfr_nan_p(ri)) + { + mpfr_set_ui(ri, 0, GMP_RNDN); + mpfr_copysign(ri, ri, right_imag, GMP_RNDN); + } + is_infinity = true; + } + + if (is_infinity) + { + mpfr_mul(lrrr, lr, rr, GMP_RNDN); + mpfr_mul(lrri, lr, ri, GMP_RNDN); + mpfr_mul(lirr, li, rr, GMP_RNDN); + mpfr_mul(liri, li, ri, GMP_RNDN); + mpfr_sub(real, lrrr, liri, GMP_RNDN); + mpfr_add(imag, lrri, lirr, GMP_RNDN); + mpfr_set_inf(real, mpfr_sgn(real)); + mpfr_set_inf(imag, mpfr_sgn(imag)); + } + + mpfr_clear(lr); + mpfr_clear(li); + mpfr_clear(rr); + mpfr_clear(ri); + } + + mpfr_clear(lrrr); + mpfr_clear(lrri); + mpfr_clear(lirr); + mpfr_clear(liri); + } + break; + case OPERATOR_DIV: + { + // For complex division we want to avoid having an + // intermediate overflow turn the whole result in a NaN. We + // scale the values to try to avoid this. + + if (mpfr_zero_p(right_real) && mpfr_zero_p(right_imag)) + { + error_at(location, "division by zero"); + mpfr_set_ui(real, 0, GMP_RNDN); + mpfr_set_ui(imag, 0, GMP_RNDN); + break; + } + + mpfr_t rra; + mpfr_t ria; + mpfr_init(rra); + mpfr_init(ria); + mpfr_abs(rra, right_real, GMP_RNDN); + mpfr_abs(ria, right_imag, GMP_RNDN); + mpfr_t t; + mpfr_init(t); + mpfr_max(t, rra, ria, GMP_RNDN); + + mpfr_t rr; + mpfr_t ri; + mpfr_init_set(rr, right_real, GMP_RNDN); + mpfr_init_set(ri, right_imag, GMP_RNDN); + long ilogbw = 0; + if (!mpfr_inf_p(t) && !mpfr_nan_p(t) && !mpfr_zero_p(t)) + { + ilogbw = mpfr_get_exp(t); + mpfr_mul_2si(rr, rr, - ilogbw, GMP_RNDN); + mpfr_mul_2si(ri, ri, - ilogbw, GMP_RNDN); + } + + mpfr_t denom; + mpfr_init(denom); + mpfr_mul(denom, rr, rr, GMP_RNDN); + mpfr_mul(t, ri, ri, GMP_RNDN); + mpfr_add(denom, denom, t, GMP_RNDN); + + mpfr_mul(real, left_real, rr, GMP_RNDN); + mpfr_mul(t, left_imag, ri, GMP_RNDN); + mpfr_add(real, real, t, GMP_RNDN); + mpfr_div(real, real, denom, GMP_RNDN); + mpfr_mul_2si(real, real, - ilogbw, GMP_RNDN); + + mpfr_mul(imag, left_imag, rr, GMP_RNDN); + mpfr_mul(t, left_real, ri, GMP_RNDN); + mpfr_sub(imag, imag, t, GMP_RNDN); + mpfr_div(imag, imag, denom, GMP_RNDN); + mpfr_mul_2si(imag, imag, - ilogbw, GMP_RNDN); + + // If we wind up with NaN on both sides, check whether we + // should really have infinity. The rule is that if either + // side of the complex number is infinity, then the whole + // value is infinity, even if the other side is NaN. So the + // only case we have to fix is the one in which both sides are + // NaN. + if (mpfr_nan_p(real) && mpfr_nan_p(imag) + && (!mpfr_nan_p(left_real) || !mpfr_nan_p(left_imag)) + && (!mpfr_nan_p(right_real) || !mpfr_nan_p(right_imag))) + { + if (mpfr_zero_p(denom)) + { + mpfr_set_inf(real, mpfr_sgn(rr)); + mpfr_mul(real, real, left_real, GMP_RNDN); + mpfr_set_inf(imag, mpfr_sgn(rr)); + mpfr_mul(imag, imag, left_imag, GMP_RNDN); + } + else if ((mpfr_inf_p(left_real) || mpfr_inf_p(left_imag)) + && mpfr_number_p(rr) && mpfr_number_p(ri)) + { + mpfr_set_ui(t, mpfr_inf_p(left_real) ? 1 : 0, GMP_RNDN); + mpfr_copysign(t, t, left_real, GMP_RNDN); + + mpfr_t t2; + mpfr_init_set_ui(t2, mpfr_inf_p(left_imag) ? 1 : 0, GMP_RNDN); + mpfr_copysign(t2, t2, left_imag, GMP_RNDN); + + mpfr_t t3; + mpfr_init(t3); + mpfr_mul(t3, t, rr, GMP_RNDN); + + mpfr_t t4; + mpfr_init(t4); + mpfr_mul(t4, t2, ri, GMP_RNDN); + + mpfr_add(t3, t3, t4, GMP_RNDN); + mpfr_set_inf(real, mpfr_sgn(t3)); + + mpfr_mul(t3, t2, rr, GMP_RNDN); + mpfr_mul(t4, t, ri, GMP_RNDN); + mpfr_sub(t3, t3, t4, GMP_RNDN); + mpfr_set_inf(imag, mpfr_sgn(t3)); + + mpfr_clear(t2); + mpfr_clear(t3); + mpfr_clear(t4); + } + else if ((mpfr_inf_p(right_real) || mpfr_inf_p(right_imag)) + && mpfr_number_p(left_real) && mpfr_number_p(left_imag)) + { + mpfr_set_ui(t, mpfr_inf_p(rr) ? 1 : 0, GMP_RNDN); + mpfr_copysign(t, t, rr, GMP_RNDN); + + mpfr_t t2; + mpfr_init_set_ui(t2, mpfr_inf_p(ri) ? 1 : 0, GMP_RNDN); + mpfr_copysign(t2, t2, ri, GMP_RNDN); + + mpfr_t t3; + mpfr_init(t3); + mpfr_mul(t3, left_real, t, GMP_RNDN); + + mpfr_t t4; + mpfr_init(t4); + mpfr_mul(t4, left_imag, t2, GMP_RNDN); + + mpfr_add(t3, t3, t4, GMP_RNDN); + mpfr_set_ui(real, 0, GMP_RNDN); + mpfr_mul(real, real, t3, GMP_RNDN); + + mpfr_mul(t3, left_imag, t, GMP_RNDN); + mpfr_mul(t4, left_real, t2, GMP_RNDN); + mpfr_sub(t3, t3, t4, GMP_RNDN); + mpfr_set_ui(imag, 0, GMP_RNDN); + mpfr_mul(imag, imag, t3, GMP_RNDN); + + mpfr_clear(t2); + mpfr_clear(t3); + mpfr_clear(t4); + } + } + + mpfr_clear(denom); + mpfr_clear(rr); + mpfr_clear(ri); + mpfr_clear(t); + mpfr_clear(rra); + mpfr_clear(ria); + } + break; + default: + go_unreachable(); + } + + mpfr_clear(left_real); + mpfr_clear(left_imag); + mpfr_clear(right_real); + mpfr_clear(right_imag); + + nc->set_complex(NULL, real, imag); + mpfr_clear(real); + mpfr_clear(imag); + + return ret; +} + +// Lower a binary expression. We have to evaluate constant +// expressions now, in order to implement Go's unlimited precision +// constants. + +Expression* +Binary_expression::do_lower(Gogo* gogo, Named_object*, + Statement_inserter* inserter, int) +{ + Location location = this->location(); + Operator op = this->op_; + Expression* left = this->left_; + Expression* right = this->right_; + + const bool is_comparison = (op == OPERATOR_EQEQ + || op == OPERATOR_NOTEQ + || op == OPERATOR_LT + || op == OPERATOR_LE + || op == OPERATOR_GT + || op == OPERATOR_GE); + + // Numeric constant expressions. + { + Numeric_constant left_nc; + Numeric_constant right_nc; + if (left->numeric_constant_value(&left_nc) + && right->numeric_constant_value(&right_nc)) + { + if (is_comparison) + { + bool result; + if (!Binary_expression::compare_constant(op, &left_nc, + &right_nc, location, + &result)) + return this; + return Expression::make_cast(Type::make_boolean_type(), + Expression::make_boolean(result, + location), + location); + } + else + { + Numeric_constant nc; + if (!Binary_expression::eval_constant(op, &left_nc, &right_nc, + location, &nc)) + return this; + return nc.expression(location); + } + } + } + + // String constant expressions. + if (left->type()->is_string_type() && right->type()->is_string_type()) + { + std::string left_string; + std::string right_string; + if (left->string_constant_value(&left_string) + && right->string_constant_value(&right_string)) + { + if (op == OPERATOR_PLUS) + return Expression::make_string(left_string + right_string, + location); + else if (is_comparison) + { + int cmp = left_string.compare(right_string); + bool r = Binary_expression::cmp_to_bool(op, cmp); + return Expression::make_boolean(r, location); + } + } + } + + // Lower struct, array, and some interface comparisons. + if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ) + { + if (left->type()->struct_type() != NULL) + return this->lower_struct_comparison(gogo, inserter); + else if (left->type()->array_type() != NULL + && !left->type()->is_slice_type()) + return this->lower_array_comparison(gogo, inserter); + else if ((left->type()->interface_type() != NULL + && right->type()->interface_type() == NULL) + || (left->type()->interface_type() == NULL + && right->type()->interface_type() != NULL)) + return this->lower_interface_value_comparison(gogo, inserter); + } + + return this; +} + +// Lower a struct comparison. + +Expression* +Binary_expression::lower_struct_comparison(Gogo* gogo, + Statement_inserter* inserter) +{ + Struct_type* st = this->left_->type()->struct_type(); + Struct_type* st2 = this->right_->type()->struct_type(); + if (st2 == NULL) + return this; + if (st != st2 && !Type::are_identical(st, st2, false, NULL)) + return this; + if (!Type::are_compatible_for_comparison(true, this->left_->type(), + this->right_->type(), NULL)) + return this; + + // See if we can compare using memcmp. As a heuristic, we use + // memcmp rather than field references and comparisons if there are + // more than two fields. + if (st->compare_is_identity(gogo) && st->total_field_count() > 2) + return this->lower_compare_to_memcmp(gogo, inserter); + + Location loc = this->location(); + + Expression* left = this->left_; + Temporary_statement* left_temp = NULL; + if (left->var_expression() == NULL + && left->temporary_reference_expression() == NULL) + { + left_temp = Statement::make_temporary(left->type(), NULL, loc); + inserter->insert(left_temp); + left = Expression::make_set_and_use_temporary(left_temp, left, loc); + } + + Expression* right = this->right_; + Temporary_statement* right_temp = NULL; + if (right->var_expression() == NULL + && right->temporary_reference_expression() == NULL) + { + right_temp = Statement::make_temporary(right->type(), NULL, loc); + inserter->insert(right_temp); + right = Expression::make_set_and_use_temporary(right_temp, right, loc); + } + + Expression* ret = Expression::make_boolean(true, loc); + const Struct_field_list* fields = st->fields(); + unsigned int field_index = 0; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf, ++field_index) + { + if (Gogo::is_sink_name(pf->field_name())) + continue; + + if (field_index > 0) + { + if (left_temp == NULL) + left = left->copy(); + else + left = Expression::make_temporary_reference(left_temp, loc); + if (right_temp == NULL) + right = right->copy(); + else + right = Expression::make_temporary_reference(right_temp, loc); + } + Expression* f1 = Expression::make_field_reference(left, field_index, + loc); + Expression* f2 = Expression::make_field_reference(right, field_index, + loc); + Expression* cond = Expression::make_binary(OPERATOR_EQEQ, f1, f2, loc); + ret = Expression::make_binary(OPERATOR_ANDAND, ret, cond, loc); + } + + if (this->op_ == OPERATOR_NOTEQ) + ret = Expression::make_unary(OPERATOR_NOT, ret, loc); + + return ret; +} + +// Lower an array comparison. + +Expression* +Binary_expression::lower_array_comparison(Gogo* gogo, + Statement_inserter* inserter) +{ + Array_type* at = this->left_->type()->array_type(); + Array_type* at2 = this->right_->type()->array_type(); + if (at2 == NULL) + return this; + if (at != at2 && !Type::are_identical(at, at2, false, NULL)) + return this; + if (!Type::are_compatible_for_comparison(true, this->left_->type(), + this->right_->type(), NULL)) + return this; + + // Call memcmp directly if possible. This may let the middle-end + // optimize the call. + if (at->compare_is_identity(gogo)) + return this->lower_compare_to_memcmp(gogo, inserter); + + // Call the array comparison function. + Named_object* hash_fn; + Named_object* equal_fn; + at->type_functions(gogo, this->left_->type()->named_type(), NULL, NULL, + &hash_fn, &equal_fn); + + Location loc = this->location(); + + Expression* func = Expression::make_func_reference(equal_fn, NULL, loc); + + Expression_list* args = new Expression_list(); + args->push_back(this->operand_address(inserter, this->left_)); + args->push_back(this->operand_address(inserter, this->right_)); + args->push_back(Expression::make_type_info(at, TYPE_INFO_SIZE)); + + Expression* ret = Expression::make_call(func, args, false, loc); + + if (this->op_ == OPERATOR_NOTEQ) + ret = Expression::make_unary(OPERATOR_NOT, ret, loc); + + return ret; +} + +// Lower an interface to value comparison. + +Expression* +Binary_expression::lower_interface_value_comparison(Gogo*, + Statement_inserter* inserter) +{ + Type* left_type = this->left_->type(); + Type* right_type = this->right_->type(); + Interface_type* ift; + if (left_type->interface_type() != NULL) + { + ift = left_type->interface_type(); + if (!ift->implements_interface(right_type, NULL)) + return this; + } + else + { + ift = right_type->interface_type(); + if (!ift->implements_interface(left_type, NULL)) + return this; + } + if (!Type::are_compatible_for_comparison(true, left_type, right_type, NULL)) + return this; + + Location loc = this->location(); + + if (left_type->interface_type() == NULL + && left_type->points_to() == NULL + && !this->left_->is_addressable()) + { + Temporary_statement* temp = + Statement::make_temporary(left_type, NULL, loc); + inserter->insert(temp); + this->left_ = + Expression::make_set_and_use_temporary(temp, this->left_, loc); + } + + if (right_type->interface_type() == NULL + && right_type->points_to() == NULL + && !this->right_->is_addressable()) + { + Temporary_statement* temp = + Statement::make_temporary(right_type, NULL, loc); + inserter->insert(temp); + this->right_ = + Expression::make_set_and_use_temporary(temp, this->right_, loc); + } + + return this; +} + +// Lower a struct or array comparison to a call to memcmp. + +Expression* +Binary_expression::lower_compare_to_memcmp(Gogo*, Statement_inserter* inserter) +{ + Location loc = this->location(); + + Expression* a1 = this->operand_address(inserter, this->left_); + Expression* a2 = this->operand_address(inserter, this->right_); + Expression* len = Expression::make_type_info(this->left_->type(), + TYPE_INFO_SIZE); + + Expression* call = Runtime::make_call(Runtime::MEMCMP, loc, 3, a1, a2, len); + + mpz_t zval; + mpz_init_set_ui(zval, 0); + Expression* zero = Expression::make_integer(&zval, NULL, loc); + mpz_clear(zval); + + return Expression::make_binary(this->op_, call, zero, loc); +} + +Expression* +Binary_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + Location loc = this->location(); + Temporary_statement* temp; + if (this->left_->type()->is_string_type() + && this->op_ == OPERATOR_PLUS) + { + if (!this->left_->is_variable()) + { + temp = Statement::make_temporary(NULL, this->left_, loc); + inserter->insert(temp); + this->left_ = Expression::make_temporary_reference(temp, loc); + } + if (!this->right_->is_variable()) + { + temp = + Statement::make_temporary(this->left_->type(), this->right_, loc); + this->right_ = Expression::make_temporary_reference(temp, loc); + inserter->insert(temp); + } + } + + Type* left_type = this->left_->type(); + bool is_shift_op = (this->op_ == OPERATOR_LSHIFT + || this->op_ == OPERATOR_RSHIFT); + bool is_idiv_op = ((this->op_ == OPERATOR_DIV && + left_type->integer_type() != NULL) + || this->op_ == OPERATOR_MOD); + + // FIXME: go_check_divide_zero and go_check_divide_overflow are globals + // defined in gcc/go/lang.opt. These should be defined in go_create_gogo + // and accessed from the Gogo* passed to do_flatten. + if (is_shift_op + || (is_idiv_op && (go_check_divide_zero || go_check_divide_overflow))) + { + if (!this->left_->is_variable()) + { + temp = Statement::make_temporary(NULL, this->left_, loc); + inserter->insert(temp); + this->left_ = Expression::make_temporary_reference(temp, loc); + } + if (!this->right_->is_variable()) + { + temp = + Statement::make_temporary(NULL, this->right_, loc); + this->right_ = Expression::make_temporary_reference(temp, loc); + inserter->insert(temp); + } + } + return this; +} + + +// Return the address of EXPR, cast to unsafe.Pointer. + +Expression* +Binary_expression::operand_address(Statement_inserter* inserter, + Expression* expr) +{ + Location loc = this->location(); + + if (!expr->is_addressable()) + { + Temporary_statement* temp = Statement::make_temporary(expr->type(), NULL, + loc); + inserter->insert(temp); + expr = Expression::make_set_and_use_temporary(temp, expr, loc); + } + expr = Expression::make_unary(OPERATOR_AND, expr, loc); + static_cast(expr)->set_does_not_escape(); + Type* void_type = Type::make_void_type(); + Type* unsafe_pointer_type = Type::make_pointer_type(void_type); + return Expression::make_cast(unsafe_pointer_type, expr, loc); +} + +// Return the numeric constant value, if it has one. + +bool +Binary_expression::do_numeric_constant_value(Numeric_constant* nc) const +{ + Numeric_constant left_nc; + if (!this->left_->numeric_constant_value(&left_nc)) + return false; + Numeric_constant right_nc; + if (!this->right_->numeric_constant_value(&right_nc)) + return false; + return Binary_expression::eval_constant(this->op_, &left_nc, &right_nc, + this->location(), nc); +} + +// Note that the value is being discarded. + +bool +Binary_expression::do_discarding_value() +{ + if (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_ANDAND) + return this->right_->discarding_value(); + else + { + this->unused_value_error(); + return false; + } +} + +// Get type. + +Type* +Binary_expression::do_type() +{ + if (this->classification() == EXPRESSION_ERROR) + return Type::make_error_type(); + + switch (this->op_) + { + case OPERATOR_EQEQ: + case OPERATOR_NOTEQ: + case OPERATOR_LT: + case OPERATOR_LE: + case OPERATOR_GT: + case OPERATOR_GE: + if (this->type_ == NULL) + this->type_ = Type::make_boolean_type(); + return this->type_; + + case OPERATOR_PLUS: + case OPERATOR_MINUS: + case OPERATOR_OR: + case OPERATOR_XOR: + case OPERATOR_MULT: + case OPERATOR_DIV: + case OPERATOR_MOD: + case OPERATOR_AND: + case OPERATOR_BITCLEAR: + case OPERATOR_OROR: + case OPERATOR_ANDAND: + { + Type* type; + if (!Binary_expression::operation_type(this->op_, + this->left_->type(), + this->right_->type(), + &type)) + return Type::make_error_type(); + return type; + } + + case OPERATOR_LSHIFT: + case OPERATOR_RSHIFT: + return this->left_->type(); + + default: + go_unreachable(); + } +} + +// Set type for a binary expression. + +void +Binary_expression::do_determine_type(const Type_context* context) +{ + Type* tleft = this->left_->type(); + Type* tright = this->right_->type(); + + // Both sides should have the same type, except for the shift + // operations. For a comparison, we should ignore the incoming + // type. + + bool is_shift_op = (this->op_ == OPERATOR_LSHIFT + || this->op_ == OPERATOR_RSHIFT); + + bool is_comparison = (this->op_ == OPERATOR_EQEQ + || this->op_ == OPERATOR_NOTEQ + || this->op_ == OPERATOR_LT + || this->op_ == OPERATOR_LE + || this->op_ == OPERATOR_GT + || this->op_ == OPERATOR_GE); + + Type_context subcontext(*context); + + if (is_comparison) + { + // In a comparison, the context does not determine the types of + // the operands. + subcontext.type = NULL; + } + + if (this->op_ == OPERATOR_ANDAND || this->op_ == OPERATOR_OROR) + { + // For a logical operation, the context does not determine the + // types of the operands. The operands must be some boolean + // type but if the context has a boolean type they do not + // inherit it. See http://golang.org/issue/3924. + subcontext.type = NULL; + } + + // Set the context for the left hand operand. + if (is_shift_op) + { + // The right hand operand of a shift plays no role in + // determining the type of the left hand operand. + } + else if (!tleft->is_abstract()) + subcontext.type = tleft; + else if (!tright->is_abstract()) + subcontext.type = tright; + else if (subcontext.type == NULL) + { + if ((tleft->integer_type() != NULL && tright->integer_type() != NULL) + || (tleft->float_type() != NULL && tright->float_type() != NULL) + || (tleft->complex_type() != NULL && tright->complex_type() != NULL)) + { + // Both sides have an abstract integer, abstract float, or + // abstract complex type. Just let CONTEXT determine + // whether they may remain abstract or not. + } + else if (tleft->complex_type() != NULL) + subcontext.type = tleft; + else if (tright->complex_type() != NULL) + subcontext.type = tright; + else if (tleft->float_type() != NULL) + subcontext.type = tleft; + else if (tright->float_type() != NULL) + subcontext.type = tright; + else + subcontext.type = tleft; + + if (subcontext.type != NULL && !context->may_be_abstract) + subcontext.type = subcontext.type->make_non_abstract_type(); + } + + this->left_->determine_type(&subcontext); + + if (is_shift_op) + { + // We may have inherited an unusable type for the shift operand. + // Give a useful error if that happened. + if (tleft->is_abstract() + && subcontext.type != NULL + && !subcontext.may_be_abstract + && subcontext.type->interface_type() == NULL + && subcontext.type->integer_type() == NULL) + this->report_error(("invalid context-determined non-integer type " + "for left operand of shift")); + + // The context for the right hand operand is the same as for the + // left hand operand, except for a shift operator. + subcontext.type = Type::lookup_integer_type("uint"); + subcontext.may_be_abstract = false; + } + + this->right_->determine_type(&subcontext); + + if (is_comparison) + { + if (this->type_ != NULL && !this->type_->is_abstract()) + ; + else if (context->type != NULL && context->type->is_boolean_type()) + this->type_ = context->type; + else if (!context->may_be_abstract) + this->type_ = Type::lookup_bool_type(); + } +} + +// Report an error if the binary operator OP does not support TYPE. +// OTYPE is the type of the other operand. Return whether the +// operation is OK. This should not be used for shift. + +bool +Binary_expression::check_operator_type(Operator op, Type* type, Type* otype, + Location location) +{ + switch (op) + { + case OPERATOR_OROR: + case OPERATOR_ANDAND: + if (!type->is_boolean_type()) + { + error_at(location, "expected boolean type"); + return false; + } + break; + + case OPERATOR_EQEQ: + case OPERATOR_NOTEQ: + { + std::string reason; + if (!Type::are_compatible_for_comparison(true, type, otype, &reason)) + { + error_at(location, "%s", reason.c_str()); + return false; + } + } + break; + + case OPERATOR_LT: + case OPERATOR_LE: + case OPERATOR_GT: + case OPERATOR_GE: + { + std::string reason; + if (!Type::are_compatible_for_comparison(false, type, otype, &reason)) + { + error_at(location, "%s", reason.c_str()); + return false; + } + } + break; + + case OPERATOR_PLUS: + case OPERATOR_PLUSEQ: + if (type->integer_type() == NULL + && type->float_type() == NULL + && type->complex_type() == NULL + && !type->is_string_type()) + { + error_at(location, + "expected integer, floating, complex, or string type"); + return false; + } + break; + + case OPERATOR_MINUS: + case OPERATOR_MINUSEQ: + case OPERATOR_MULT: + case OPERATOR_MULTEQ: + case OPERATOR_DIV: + case OPERATOR_DIVEQ: + if (type->integer_type() == NULL + && type->float_type() == NULL + && type->complex_type() == NULL) + { + error_at(location, "expected integer, floating, or complex type"); + return false; + } + break; + + case OPERATOR_MOD: + case OPERATOR_MODEQ: + case OPERATOR_OR: + case OPERATOR_OREQ: + case OPERATOR_AND: + case OPERATOR_ANDEQ: + case OPERATOR_XOR: + case OPERATOR_XOREQ: + case OPERATOR_BITCLEAR: + case OPERATOR_BITCLEAREQ: + if (type->integer_type() == NULL) + { + error_at(location, "expected integer type"); + return false; + } + break; + + default: + go_unreachable(); + } + + return true; +} + +// Check types. + +void +Binary_expression::do_check_types(Gogo*) +{ + if (this->classification() == EXPRESSION_ERROR) + return; + + Type* left_type = this->left_->type(); + Type* right_type = this->right_->type(); + if (left_type->is_error() || right_type->is_error()) + { + this->set_is_error(); + return; + } + + if (this->op_ == OPERATOR_EQEQ + || this->op_ == OPERATOR_NOTEQ + || this->op_ == OPERATOR_LT + || this->op_ == OPERATOR_LE + || this->op_ == OPERATOR_GT + || this->op_ == OPERATOR_GE) + { + if (left_type->is_nil_type() && right_type->is_nil_type()) + { + this->report_error(_("invalid comparison of nil with nil")); + return; + } + if (!Type::are_assignable(left_type, right_type, NULL) + && !Type::are_assignable(right_type, left_type, NULL)) + { + this->report_error(_("incompatible types in binary expression")); + return; + } + if (!Binary_expression::check_operator_type(this->op_, left_type, + right_type, + this->location()) + || !Binary_expression::check_operator_type(this->op_, right_type, + left_type, + this->location())) + { + this->set_is_error(); + return; + } + } + else if (this->op_ != OPERATOR_LSHIFT && this->op_ != OPERATOR_RSHIFT) + { + if (!Type::are_compatible_for_binop(left_type, right_type)) + { + this->report_error(_("incompatible types in binary expression")); + return; + } + if (!Binary_expression::check_operator_type(this->op_, left_type, + right_type, + this->location())) + { + this->set_is_error(); + return; + } + if (this->op_ == OPERATOR_DIV || this->op_ == OPERATOR_MOD) + { + // Division by a zero integer constant is an error. + Numeric_constant rconst; + unsigned long rval; + if (left_type->integer_type() != NULL + && this->right_->numeric_constant_value(&rconst) + && rconst.to_unsigned_long(&rval) == Numeric_constant::NC_UL_VALID + && rval == 0) + { + this->report_error(_("integer division by zero")); + return; + } + } + } + else + { + if (left_type->integer_type() == NULL) + this->report_error(_("shift of non-integer operand")); + + if (!right_type->is_abstract() + && (right_type->integer_type() == NULL + || !right_type->integer_type()->is_unsigned())) + this->report_error(_("shift count not unsigned integer")); + else + { + Numeric_constant nc; + if (this->right_->numeric_constant_value(&nc)) + { + mpz_t val; + if (!nc.to_int(&val)) + this->report_error(_("shift count not unsigned integer")); + else + { + if (mpz_sgn(val) < 0) + { + this->report_error(_("negative shift count")); + mpz_set_ui(val, 0); + Location rloc = this->right_->location(); + this->right_ = Expression::make_integer(&val, right_type, + rloc); + } + mpz_clear(val); + } + } + } + } +} + +// Get a tree for a binary expression. + +tree +Binary_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + Location loc = this->location(); + Type* left_type = this->left_->type(); + Type* right_type = this->right_->type(); + + bool use_left_type = true; + bool is_shift_op = false; + bool is_idiv_op = false; + switch (this->op_) + { + case OPERATOR_EQEQ: + case OPERATOR_NOTEQ: + case OPERATOR_LT: + case OPERATOR_LE: + case OPERATOR_GT: + case OPERATOR_GE: + { + Bexpression* ret = + Expression::comparison(context, this->type_, this->op_, + this->left_, this->right_, loc); + return expr_to_tree(ret); + } + + case OPERATOR_OROR: + case OPERATOR_ANDAND: + use_left_type = false; + break; + case OPERATOR_PLUS: + case OPERATOR_MINUS: + case OPERATOR_OR: + case OPERATOR_XOR: + case OPERATOR_MULT: + break; + case OPERATOR_DIV: + if (left_type->float_type() != NULL || left_type->complex_type() != NULL) + break; + case OPERATOR_MOD: + is_idiv_op = true; + break; + case OPERATOR_LSHIFT: + case OPERATOR_RSHIFT: + is_shift_op = true; + break; + case OPERATOR_BITCLEAR: + this->right_ = Expression::make_unary(OPERATOR_XOR, this->right_, loc); + case OPERATOR_AND: + break; + default: + go_unreachable(); + } + + if (left_type->is_string_type()) + { + go_assert(this->op_ == OPERATOR_PLUS); + Expression* string_plus = + Runtime::make_call(Runtime::STRING_PLUS, loc, 2, + this->left_, this->right_); + return string_plus->get_tree(context); + } + + // For complex division Go might want slightly different results than the + // backend implementation provides, so we have our own runtime routine. + if (this->op_ == OPERATOR_DIV && this->left_->type()->complex_type() != NULL) + { + Runtime::Function complex_code; + switch (this->left_->type()->complex_type()->bits()) + { + case 64: + complex_code = Runtime::COMPLEX64_DIV; + break; + case 128: + complex_code = Runtime::COMPLEX128_DIV; + break; + default: + go_unreachable(); + } + Expression* complex_div = + Runtime::make_call(complex_code, loc, 2, this->left_, this->right_); + return complex_div->get_tree(context); + } + + Bexpression* left = tree_to_expr(this->left_->get_tree(context)); + Bexpression* right = tree_to_expr(this->right_->get_tree(context)); + + Type* type = use_left_type ? left_type : right_type; + Btype* btype = type->get_backend(gogo); + + Bexpression* ret = + gogo->backend()->binary_expression(this->op_, left, right, loc); + ret = gogo->backend()->convert_expression(btype, ret, loc); + + // Initialize overflow constants. + Bexpression* overflow; + mpz_t zero; + mpz_init_set_ui(zero, 0UL); + mpz_t one; + mpz_init_set_ui(one, 1UL); + mpz_t neg_one; + mpz_init_set_si(neg_one, -1); + + Btype* left_btype = left_type->get_backend(gogo); + Btype* right_btype = right_type->get_backend(gogo); + + // In Go, a shift larger than the size of the type is well-defined. + // This is not true in C, so we need to insert a conditional. + if (is_shift_op) + { + go_assert(left_type->integer_type() != NULL); + + mpz_t bitsval; + int bits = left_type->integer_type()->bits(); + mpz_init_set_ui(bitsval, bits); + Bexpression* bits_expr = + gogo->backend()->integer_constant_expression(right_btype, bitsval); + Bexpression* compare = + gogo->backend()->binary_expression(OPERATOR_LT, + right, bits_expr, loc); + + Bexpression* zero_expr = + gogo->backend()->integer_constant_expression(left_btype, zero); + overflow = zero_expr; + if (this->op_ == OPERATOR_RSHIFT + && !left_type->integer_type()->is_unsigned()) + { + Bexpression* neg_expr = + gogo->backend()->binary_expression(OPERATOR_LT, left, + zero_expr, loc); + Bexpression* neg_one_expr = + gogo->backend()->integer_constant_expression(left_btype, neg_one); + overflow = gogo->backend()->conditional_expression(btype, neg_expr, + neg_one_expr, + zero_expr, loc); + } + ret = gogo->backend()->conditional_expression(btype, compare, ret, + overflow, loc); + mpz_clear(bitsval); + } + + // Add checks for division by zero and division overflow as needed. + if (is_idiv_op) + { + if (go_check_divide_zero) + { + // right == 0 + Bexpression* zero_expr = + gogo->backend()->integer_constant_expression(right_btype, zero); + Bexpression* check = + gogo->backend()->binary_expression(OPERATOR_EQEQ, + right, zero_expr, loc); + + // __go_runtime_error(RUNTIME_ERROR_DIVISION_BY_ZERO) + int errcode = RUNTIME_ERROR_DIVISION_BY_ZERO; + Expression* crash = gogo->runtime_error(errcode, loc); + Bexpression* crash_expr = tree_to_expr(crash->get_tree(context)); + + // right == 0 ? (__go_runtime_error(...), 0) : ret + ret = gogo->backend()->conditional_expression(btype, check, + crash_expr, ret, loc); + } + + if (go_check_divide_overflow) + { + // right == -1 + // FIXME: It would be nice to say that this test is expected + // to return false. + + Bexpression* neg_one_expr = + gogo->backend()->integer_constant_expression(right_btype, neg_one); + Bexpression* check = + gogo->backend()->binary_expression(OPERATOR_EQEQ, + right, neg_one_expr, loc); + + Bexpression* zero_expr = + gogo->backend()->integer_constant_expression(btype, zero); + Bexpression* one_expr = + gogo->backend()->integer_constant_expression(btype, one); + + if (type->integer_type()->is_unsigned()) + { + // An unsigned -1 is the largest possible number, so + // dividing is always 1 or 0. + + Bexpression* cmp = + gogo->backend()->binary_expression(OPERATOR_EQEQ, + left, right, loc); + if (this->op_ == OPERATOR_DIV) + overflow = + gogo->backend()->conditional_expression(btype, cmp, + one_expr, zero_expr, + loc); + else + overflow = + gogo->backend()->conditional_expression(btype, cmp, + zero_expr, left, + loc); + } + else + { + // Computing left / -1 is the same as computing - left, + // which does not overflow since Go sets -fwrapv. + if (this->op_ == OPERATOR_DIV) + { + Expression* negate_expr = + Expression::make_unary(OPERATOR_MINUS, this->left_, loc); + overflow = tree_to_expr(negate_expr->get_tree(context)); + } + else + overflow = zero_expr; + } + overflow = gogo->backend()->convert_expression(btype, overflow, loc); + + // right == -1 ? - left : ret + ret = gogo->backend()->conditional_expression(btype, check, overflow, + ret, loc); + } + } + + mpz_clear(zero); + mpz_clear(one); + mpz_clear(neg_one); + return expr_to_tree(ret); +} + +// Export a binary expression. + +void +Binary_expression::do_export(Export* exp) const +{ + exp->write_c_string("("); + this->left_->export_expression(exp); + switch (this->op_) + { + case OPERATOR_OROR: + exp->write_c_string(" || "); + break; + case OPERATOR_ANDAND: + exp->write_c_string(" && "); + break; + case OPERATOR_EQEQ: + exp->write_c_string(" == "); + break; + case OPERATOR_NOTEQ: + exp->write_c_string(" != "); + break; + case OPERATOR_LT: + exp->write_c_string(" < "); + break; + case OPERATOR_LE: + exp->write_c_string(" <= "); + break; + case OPERATOR_GT: + exp->write_c_string(" > "); + break; + case OPERATOR_GE: + exp->write_c_string(" >= "); + break; + case OPERATOR_PLUS: + exp->write_c_string(" + "); + break; + case OPERATOR_MINUS: + exp->write_c_string(" - "); + break; + case OPERATOR_OR: + exp->write_c_string(" | "); + break; + case OPERATOR_XOR: + exp->write_c_string(" ^ "); + break; + case OPERATOR_MULT: + exp->write_c_string(" * "); + break; + case OPERATOR_DIV: + exp->write_c_string(" / "); + break; + case OPERATOR_MOD: + exp->write_c_string(" % "); + break; + case OPERATOR_LSHIFT: + exp->write_c_string(" << "); + break; + case OPERATOR_RSHIFT: + exp->write_c_string(" >> "); + break; + case OPERATOR_AND: + exp->write_c_string(" & "); + break; + case OPERATOR_BITCLEAR: + exp->write_c_string(" &^ "); + break; + default: + go_unreachable(); + } + this->right_->export_expression(exp); + exp->write_c_string(")"); +} + +// Import a binary expression. + +Expression* +Binary_expression::do_import(Import* imp) +{ + imp->require_c_string("("); + + Expression* left = Expression::import_expression(imp); + + Operator op; + if (imp->match_c_string(" || ")) + { + op = OPERATOR_OROR; + imp->advance(4); + } + else if (imp->match_c_string(" && ")) + { + op = OPERATOR_ANDAND; + imp->advance(4); + } + else if (imp->match_c_string(" == ")) + { + op = OPERATOR_EQEQ; + imp->advance(4); + } + else if (imp->match_c_string(" != ")) + { + op = OPERATOR_NOTEQ; + imp->advance(4); + } + else if (imp->match_c_string(" < ")) + { + op = OPERATOR_LT; + imp->advance(3); + } + else if (imp->match_c_string(" <= ")) + { + op = OPERATOR_LE; + imp->advance(4); + } + else if (imp->match_c_string(" > ")) + { + op = OPERATOR_GT; + imp->advance(3); + } + else if (imp->match_c_string(" >= ")) + { + op = OPERATOR_GE; + imp->advance(4); + } + else if (imp->match_c_string(" + ")) + { + op = OPERATOR_PLUS; + imp->advance(3); + } + else if (imp->match_c_string(" - ")) + { + op = OPERATOR_MINUS; + imp->advance(3); + } + else if (imp->match_c_string(" | ")) + { + op = OPERATOR_OR; + imp->advance(3); + } + else if (imp->match_c_string(" ^ ")) + { + op = OPERATOR_XOR; + imp->advance(3); + } + else if (imp->match_c_string(" * ")) + { + op = OPERATOR_MULT; + imp->advance(3); + } + else if (imp->match_c_string(" / ")) + { + op = OPERATOR_DIV; + imp->advance(3); + } + else if (imp->match_c_string(" % ")) + { + op = OPERATOR_MOD; + imp->advance(3); + } + else if (imp->match_c_string(" << ")) + { + op = OPERATOR_LSHIFT; + imp->advance(4); + } + else if (imp->match_c_string(" >> ")) + { + op = OPERATOR_RSHIFT; + imp->advance(4); + } + else if (imp->match_c_string(" & ")) + { + op = OPERATOR_AND; + imp->advance(3); + } + else if (imp->match_c_string(" &^ ")) + { + op = OPERATOR_BITCLEAR; + imp->advance(4); + } + else + { + error_at(imp->location(), "unrecognized binary operator"); + return Expression::make_error(imp->location()); + } + + Expression* right = Expression::import_expression(imp); + + imp->require_c_string(")"); + + return Expression::make_binary(op, left, right, imp->location()); +} + +// Dump ast representation of a binary expression. + +void +Binary_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "("; + ast_dump_context->dump_expression(this->left_); + ast_dump_context->ostream() << " "; + ast_dump_context->dump_operator(this->op_); + ast_dump_context->ostream() << " "; + ast_dump_context->dump_expression(this->right_); + ast_dump_context->ostream() << ") "; +} + +// Make a binary expression. + +Expression* +Expression::make_binary(Operator op, Expression* left, Expression* right, + Location location) +{ + return new Binary_expression(op, left, right, location); +} + +// Implement a comparison. + +Bexpression* +Expression::comparison(Translate_context* context, Type* result_type, + Operator op, Expression* left, Expression* right, + Location location) +{ + Type* left_type = left->type(); + Type* right_type = right->type(); + + mpz_t zval; + mpz_init_set_ui(zval, 0UL); + Expression* zexpr = Expression::make_integer(&zval, NULL, location); + mpz_clear(zval); + + if (left_type->is_string_type() && right_type->is_string_type()) + { + left = Runtime::make_call(Runtime::STRCMP, location, 2, + left, right); + right = zexpr; + } + else if ((left_type->interface_type() != NULL + && right_type->interface_type() == NULL + && !right_type->is_nil_type()) + || (left_type->interface_type() == NULL + && !left_type->is_nil_type() + && right_type->interface_type() != NULL)) + { + // Comparing an interface value to a non-interface value. + if (left_type->interface_type() == NULL) + { + std::swap(left_type, right_type); + std::swap(left, right); + } + + // The right operand is not an interface. We need to take its + // address if it is not a pointer. + Expression* pointer_arg = NULL; + if (right_type->points_to() != NULL) + pointer_arg = right; + else + { + go_assert(right->is_addressable()); + pointer_arg = Expression::make_unary(OPERATOR_AND, right, + location); + } + + Expression* descriptor = + Expression::make_type_descriptor(right_type, location); + left = + Runtime::make_call((left_type->interface_type()->is_empty() + ? Runtime::EMPTY_INTERFACE_VALUE_COMPARE + : Runtime::INTERFACE_VALUE_COMPARE), + location, 3, left, descriptor, + pointer_arg); + right = zexpr; + } + else if (left_type->interface_type() != NULL + && right_type->interface_type() != NULL) + { + Runtime::Function compare_function; + if (left_type->interface_type()->is_empty() + && right_type->interface_type()->is_empty()) + compare_function = Runtime::EMPTY_INTERFACE_COMPARE; + else if (!left_type->interface_type()->is_empty() + && !right_type->interface_type()->is_empty()) + compare_function = Runtime::INTERFACE_COMPARE; + else + { + if (left_type->interface_type()->is_empty()) + { + go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ); + std::swap(left_type, right_type); + std::swap(left, right); + } + go_assert(!left_type->interface_type()->is_empty()); + go_assert(right_type->interface_type()->is_empty()); + compare_function = Runtime::INTERFACE_EMPTY_COMPARE; + } + + left = Runtime::make_call(compare_function, location, 2, left, right); + right = zexpr; + } + + if (left_type->is_nil_type() + && (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ)) + { + std::swap(left_type, right_type); + std::swap(left, right); + } + + if (right_type->is_nil_type()) + { + right = Expression::make_nil(location); + if (left_type->array_type() != NULL + && left_type->array_type()->length() == NULL) + { + Array_type* at = left_type->array_type(); + left = at->get_value_pointer(context->gogo(), left); + } + else if (left_type->interface_type() != NULL) + { + // An interface is nil if the first field is nil. + left = Expression::make_field_reference(left, 0, location); + } + } + + Bexpression* left_bexpr = tree_to_expr(left->get_tree(context)); + Bexpression* right_bexpr = tree_to_expr(right->get_tree(context)); + + Gogo* gogo = context->gogo(); + Bexpression* ret = gogo->backend()->binary_expression(op, left_bexpr, + right_bexpr, location); + if (result_type != NULL) + ret = gogo->backend()->convert_expression(result_type->get_backend(gogo), + ret, location); + return ret; +} + +// Class Bound_method_expression. + +// Traversal. + +int +Bound_method_expression::do_traverse(Traverse* traverse) +{ + return Expression::traverse(&this->expr_, traverse); +} + +// Lower the expression. If this is a method value rather than being +// called, and the method is accessed via a pointer, we may need to +// add nil checks. Introduce a temporary variable so that those nil +// checks do not cause multiple evaluation. + +Expression* +Bound_method_expression::do_lower(Gogo*, Named_object*, + Statement_inserter* inserter, int) +{ + // For simplicity we use a temporary for every call to an embedded + // method, even though some of them might be pure value methods and + // not require a temporary. + if (this->expr_->var_expression() == NULL + && this->expr_->temporary_reference_expression() == NULL + && this->expr_->set_and_use_temporary_expression() == NULL + && (this->method_->field_indexes() != NULL + || (this->method_->is_value_method() + && this->expr_->type()->points_to() != NULL))) + { + Temporary_statement* temp = + Statement::make_temporary(this->expr_->type(), NULL, this->location()); + inserter->insert(temp); + this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_, + this->location()); + } + return this; +} + +// Return the type of a bound method expression. The type of this +// object is simply the type of the method with no receiver. + +Type* +Bound_method_expression::do_type() +{ + Named_object* fn = this->method_->named_object(); + Function_type* fntype; + if (fn->is_function()) + fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + fntype = fn->func_declaration_value()->type(); + else + return Type::make_error_type(); + return fntype->copy_without_receiver(); +} + +// Determine the types of a method expression. + +void +Bound_method_expression::do_determine_type(const Type_context*) +{ + Named_object* fn = this->method_->named_object(); + Function_type* fntype; + if (fn->is_function()) + fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + fntype = fn->func_declaration_value()->type(); + else + fntype = NULL; + if (fntype == NULL || !fntype->is_method()) + this->expr_->determine_type_no_context(); + else + { + Type_context subcontext(fntype->receiver()->type(), false); + this->expr_->determine_type(&subcontext); + } +} + +// Check the types of a method expression. + +void +Bound_method_expression::do_check_types(Gogo*) +{ + Named_object* fn = this->method_->named_object(); + if (!fn->is_function() && !fn->is_function_declaration()) + { + this->report_error(_("object is not a method")); + return; + } + + Function_type* fntype; + if (fn->is_function()) + fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + fntype = fn->func_declaration_value()->type(); + else + go_unreachable(); + Type* rtype = fntype->receiver()->type()->deref(); + Type* etype = (this->expr_type_ != NULL + ? this->expr_type_ + : this->expr_->type()); + etype = etype->deref(); + if (!Type::are_identical(rtype, etype, true, NULL)) + this->report_error(_("method type does not match object type")); +} + +// If a bound method expression is not simply called, then it is +// represented as a closure. The closure will hold a single variable, +// the receiver to pass to the method. The function will be a simple +// thunk that pulls that value from the closure and calls the method +// with the remaining arguments. +// +// Because method values are not common, we don't build all thunks for +// every methods, but instead only build them as we need them. In +// particular, we even build them on demand for methods defined in +// other packages. + +Bound_method_expression::Method_value_thunks + Bound_method_expression::method_value_thunks; + +// Find or create the thunk for METHOD. + +Named_object* +Bound_method_expression::create_thunk(Gogo* gogo, const Method* method, + Named_object* fn) +{ + std::pair val(fn, NULL); + std::pair ins = + Bound_method_expression::method_value_thunks.insert(val); + if (!ins.second) + { + // We have seen this method before. + go_assert(ins.first->second != NULL); + return ins.first->second; + } + + Location loc = fn->location(); + + Function_type* orig_fntype; + if (fn->is_function()) + orig_fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + orig_fntype = fn->func_declaration_value()->type(); + else + orig_fntype = NULL; + + if (orig_fntype == NULL || !orig_fntype->is_method()) + { + ins.first->second = Named_object::make_erroneous_name(Gogo::thunk_name()); + return ins.first->second; + } + + Struct_field_list* sfl = new Struct_field_list(); + // The type here is wrong--it should be the C function type. But it + // doesn't really matter. + Type* vt = Type::make_pointer_type(Type::make_void_type()); + sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val.1", + orig_fntype->receiver()->type(), + loc))); + Type* closure_type = Type::make_struct_type(sfl, loc); + closure_type = Type::make_pointer_type(closure_type); + + Function_type* new_fntype = orig_fntype->copy_with_names(); + + Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype, + false, loc); + + Variable* cvar = new Variable(closure_type, NULL, false, false, false, loc); + cvar->set_is_used(); + Named_object* cp = Named_object::make_variable("$closure", NULL, cvar); + new_no->func_value()->set_closure_var(cp); + + gogo->start_block(loc); + + // Field 0 of the closure is the function code pointer, field 1 is + // the value on which to invoke the method. + Expression* arg = Expression::make_var_reference(cp, loc); + arg = Expression::make_unary(OPERATOR_MULT, arg, loc); + arg = Expression::make_field_reference(arg, 1, loc); + + Expression* bme = Expression::make_bound_method(arg, method, fn, loc); + + const Typed_identifier_list* orig_params = orig_fntype->parameters(); + Expression_list* args; + if (orig_params == NULL || orig_params->empty()) + args = NULL; + else + { + const Typed_identifier_list* new_params = new_fntype->parameters(); + args = new Expression_list(); + for (Typed_identifier_list::const_iterator p = new_params->begin(); + p != new_params->end(); + ++p) + { + Named_object* p_no = gogo->lookup(p->name(), NULL); + go_assert(p_no != NULL + && p_no->is_variable() + && p_no->var_value()->is_parameter()); + args->push_back(Expression::make_var_reference(p_no, loc)); + } + } + + Call_expression* call = Expression::make_call(bme, args, + orig_fntype->is_varargs(), + loc); + call->set_varargs_are_lowered(); + + Statement* s = Statement::make_return_from_call(call, loc); + gogo->add_statement(s); + Block* b = gogo->finish_block(loc); + gogo->add_block(b, loc); + gogo->lower_block(new_no, b); + gogo->flatten_block(new_no, b); + gogo->finish_function(loc); + + ins.first->second = new_no; + return new_no; +} + +// Return an expression to check *REF for nil while dereferencing +// according to FIELD_INDEXES. Update *REF to build up the field +// reference. This is a static function so that we don't have to +// worry about declaring Field_indexes in expressions.h. + +static Expression* +bme_check_nil(const Method::Field_indexes* field_indexes, Location loc, + Expression** ref) +{ + if (field_indexes == NULL) + return Expression::make_boolean(false, loc); + Expression* cond = bme_check_nil(field_indexes->next, loc, ref); + Struct_type* stype = (*ref)->type()->deref()->struct_type(); + go_assert(stype != NULL + && field_indexes->field_index < stype->field_count()); + if ((*ref)->type()->struct_type() == NULL) + { + go_assert((*ref)->type()->points_to() != NULL); + Expression* n = Expression::make_binary(OPERATOR_EQEQ, *ref, + Expression::make_nil(loc), + loc); + cond = Expression::make_binary(OPERATOR_OROR, cond, n, loc); + *ref = Expression::make_unary(OPERATOR_MULT, *ref, loc); + go_assert((*ref)->type()->struct_type() == stype); + } + *ref = Expression::make_field_reference(*ref, field_indexes->field_index, + loc); + return cond; +} + +// Get the tree for a method value. + +tree +Bound_method_expression::do_get_tree(Translate_context* context) +{ + Named_object* thunk = Bound_method_expression::create_thunk(context->gogo(), + this->method_, + this->function_); + if (thunk->is_erroneous()) + { + go_assert(saw_errors()); + return error_mark_node; + } + + // FIXME: We should lower this earlier, but we can't lower it in the + // lowering pass because at that point we don't know whether we need + // to create the thunk or not. If the expression is called, we + // don't need the thunk. + + Location loc = this->location(); + + // If the method expects a value, and we have a pointer, we need to + // dereference the pointer. + + Named_object* fn = this->method_->named_object(); + Function_type* fntype; + if (fn->is_function()) + fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + fntype = fn->func_declaration_value()->type(); + else + go_unreachable(); + + Expression* val = this->expr_; + if (fntype->receiver()->type()->points_to() == NULL + && val->type()->points_to() != NULL) + val = Expression::make_unary(OPERATOR_MULT, val, loc); + + // Note that we are ignoring this->expr_type_ here. The thunk will + // expect a closure whose second field has type this->expr_type_ (if + // that is not NULL). We are going to pass it a closure whose + // second field has type this->expr_->type(). Since + // this->expr_type_ is only not-NULL for pointer types, we can get + // away with this. + + Struct_field_list* fields = new Struct_field_list(); + fields->push_back(Struct_field(Typed_identifier("fn.0", + thunk->func_value()->type(), + loc))); + fields->push_back(Struct_field(Typed_identifier("val.1", val->type(), loc))); + Struct_type* st = Type::make_struct_type(fields, loc); + + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_func_code_reference(thunk, loc)); + vals->push_back(val); + + Expression* ret = Expression::make_struct_composite_literal(st, vals, loc); + ret = Expression::make_heap_composite(ret, loc); + + tree ret_tree = ret->get_tree(context); + + Expression* nil_check = NULL; + + // See whether the expression or any embedded pointers are nil. + + Expression* expr = this->expr_; + if (this->method_->field_indexes() != NULL) + { + // Note that we are evaluating this->expr_ twice, but that is OK + // because in the lowering pass we forced it into a temporary + // variable. + Expression* ref = expr; + nil_check = bme_check_nil(this->method_->field_indexes(), loc, &ref); + expr = ref; + } + + if (this->method_->is_value_method() && expr->type()->points_to() != NULL) + { + Expression* n = Expression::make_binary(OPERATOR_EQEQ, expr, + Expression::make_nil(loc), + loc); + if (nil_check == NULL) + nil_check = n; + else + nil_check = Expression::make_binary(OPERATOR_OROR, nil_check, n, loc); + } + + if (nil_check != NULL) + { + tree nil_check_tree = nil_check->get_tree(context); + Expression* crash_expr = + context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); + tree crash = crash_expr->get_tree(context); + if (ret_tree == error_mark_node + || nil_check_tree == error_mark_node + || crash == error_mark_node) + return error_mark_node; + + ret_tree = fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR, + TREE_TYPE(ret_tree), + build3_loc(loc.gcc_location(), COND_EXPR, + void_type_node, nil_check_tree, + crash, NULL_TREE), + ret_tree); + } + + return ret_tree; +} + +// Dump ast representation of a bound method expression. + +void +Bound_method_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + if (this->expr_type_ != NULL) + ast_dump_context->ostream() << "("; + ast_dump_context->dump_expression(this->expr_); + if (this->expr_type_ != NULL) + { + ast_dump_context->ostream() << ":"; + ast_dump_context->dump_type(this->expr_type_); + ast_dump_context->ostream() << ")"; + } + + ast_dump_context->ostream() << "." << this->function_->name(); +} + +// Make a method expression. + +Bound_method_expression* +Expression::make_bound_method(Expression* expr, const Method* method, + Named_object* function, Location location) +{ + return new Bound_method_expression(expr, method, function, location); +} + +// Class Builtin_call_expression. This is used for a call to a +// builtin function. + +class Builtin_call_expression : public Call_expression +{ + public: + Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args, + bool is_varargs, Location location); + + protected: + // This overrides Call_expression::do_lower. + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + bool + do_is_constant() const; + + bool + do_numeric_constant_value(Numeric_constant*) const; + + bool + do_discarding_value(); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Builtin_call_expression(this->gogo_, this->fn()->copy(), + this->args()->copy(), + this->is_varargs(), + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_export(Export*) const; + + virtual bool + do_is_recover_call() const; + + virtual void + do_set_recover_arg(Expression*); + + private: + // The builtin functions. + enum Builtin_function_code + { + BUILTIN_INVALID, + + // Predeclared builtin functions. + BUILTIN_APPEND, + BUILTIN_CAP, + BUILTIN_CLOSE, + BUILTIN_COMPLEX, + BUILTIN_COPY, + BUILTIN_DELETE, + BUILTIN_IMAG, + BUILTIN_LEN, + BUILTIN_MAKE, + BUILTIN_NEW, + BUILTIN_PANIC, + BUILTIN_PRINT, + BUILTIN_PRINTLN, + BUILTIN_REAL, + BUILTIN_RECOVER, + + // Builtin functions from the unsafe package. + BUILTIN_ALIGNOF, + BUILTIN_OFFSETOF, + BUILTIN_SIZEOF + }; + + Expression* + one_arg() const; + + bool + check_one_arg(); + + static Type* + real_imag_type(Type*); + + static Type* + complex_type(Type*); + + Expression* + lower_make(); + + bool + check_int_value(Expression*, bool is_length); + + // A pointer back to the general IR structure. This avoids a global + // variable, or passing it around everywhere. + Gogo* gogo_; + // The builtin function being called. + Builtin_function_code code_; + // Used to stop endless loops when the length of an array uses len + // or cap of the array itself. + mutable bool seen_; +}; + +Builtin_call_expression::Builtin_call_expression(Gogo* gogo, + Expression* fn, + Expression_list* args, + bool is_varargs, + Location location) + : Call_expression(fn, args, is_varargs, location), + gogo_(gogo), code_(BUILTIN_INVALID), seen_(false) +{ + Func_expression* fnexp = this->fn()->func_expression(); + go_assert(fnexp != NULL); + const std::string& name(fnexp->named_object()->name()); + if (name == "append") + this->code_ = BUILTIN_APPEND; + else if (name == "cap") + this->code_ = BUILTIN_CAP; + else if (name == "close") + this->code_ = BUILTIN_CLOSE; + else if (name == "complex") + this->code_ = BUILTIN_COMPLEX; + else if (name == "copy") + this->code_ = BUILTIN_COPY; + else if (name == "delete") + this->code_ = BUILTIN_DELETE; + else if (name == "imag") + this->code_ = BUILTIN_IMAG; + else if (name == "len") + this->code_ = BUILTIN_LEN; + else if (name == "make") + this->code_ = BUILTIN_MAKE; + else if (name == "new") + this->code_ = BUILTIN_NEW; + else if (name == "panic") + this->code_ = BUILTIN_PANIC; + else if (name == "print") + this->code_ = BUILTIN_PRINT; + else if (name == "println") + this->code_ = BUILTIN_PRINTLN; + else if (name == "real") + this->code_ = BUILTIN_REAL; + else if (name == "recover") + this->code_ = BUILTIN_RECOVER; + else if (name == "Alignof") + this->code_ = BUILTIN_ALIGNOF; + else if (name == "Offsetof") + this->code_ = BUILTIN_OFFSETOF; + else if (name == "Sizeof") + this->code_ = BUILTIN_SIZEOF; + else + go_unreachable(); +} + +// Return whether this is a call to recover. This is a virtual +// function called from the parent class. + +bool +Builtin_call_expression::do_is_recover_call() const +{ + if (this->classification() == EXPRESSION_ERROR) + return false; + return this->code_ == BUILTIN_RECOVER; +} + +// Set the argument for a call to recover. + +void +Builtin_call_expression::do_set_recover_arg(Expression* arg) +{ + const Expression_list* args = this->args(); + go_assert(args == NULL || args->empty()); + Expression_list* new_args = new Expression_list(); + new_args->push_back(arg); + this->set_args(new_args); +} + +// Lower a builtin call expression. This turns new and make into +// specific expressions. We also convert to a constant if we can. + +Expression* +Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) +{ + if (this->classification() == EXPRESSION_ERROR) + return this; + + Location loc = this->location(); + + if (this->is_varargs() && this->code_ != BUILTIN_APPEND) + { + this->report_error(_("invalid use of %<...%> with builtin function")); + return Expression::make_error(loc); + } + + if (this->code_ == BUILTIN_OFFSETOF) + { + Expression* arg = this->one_arg(); + + if (arg->bound_method_expression() != NULL + || arg->interface_field_reference_expression() != NULL) + { + this->report_error(_("invalid use of method value as argument " + "of Offsetof")); + return this; + } + + Field_reference_expression* farg = arg->field_reference_expression(); + while (farg != NULL) + { + if (!farg->implicit()) + break; + // When the selector refers to an embedded field, + // it must not be reached through pointer indirections. + if (farg->expr()->deref() != farg->expr()) + { + this->report_error(_("argument of Offsetof implies " + "indirection of an embedded field")); + return this; + } + // Go up until we reach the original base. + farg = farg->expr()->field_reference_expression(); + } + } + + if (this->is_constant()) + { + Numeric_constant nc; + if (this->numeric_constant_value(&nc)) + return nc.expression(loc); + } + + switch (this->code_) + { + default: + break; + + case BUILTIN_NEW: + { + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 1) + this->report_error(_("not enough arguments")); + else if (args->size() > 1) + this->report_error(_("too many arguments")); + else + { + Expression* arg = args->front(); + if (!arg->is_type_expression()) + { + error_at(arg->location(), "expected type"); + this->set_is_error(); + } + else + return Expression::make_allocation(arg->type(), loc); + } + } + break; + + case BUILTIN_MAKE: + return this->lower_make(); + + case BUILTIN_RECOVER: + if (function != NULL) + function->func_value()->set_calls_recover(); + else + { + // Calling recover outside of a function always returns the + // nil empty interface. + Type* eface = Type::make_empty_interface_type(loc); + return Expression::make_cast(eface, Expression::make_nil(loc), loc); + } + break; + + case BUILTIN_APPEND: + { + // Lower the varargs. + const Expression_list* args = this->args(); + if (args == NULL || args->empty()) + return this; + Type* slice_type = args->front()->type(); + if (!slice_type->is_slice_type()) + { + if (slice_type->is_nil_type()) + error_at(args->front()->location(), "use of untyped nil"); + else + error_at(args->front()->location(), + "argument 1 must be a slice"); + this->set_is_error(); + return this; + } + Type* element_type = slice_type->array_type()->element_type(); + this->lower_varargs(gogo, function, inserter, + Type::make_array_type(element_type, NULL), + 2); + } + break; + + case BUILTIN_DELETE: + { + // Lower to a runtime function call. + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 2) + this->report_error(_("not enough arguments")); + else if (args->size() > 2) + this->report_error(_("too many arguments")); + else if (args->front()->type()->map_type() == NULL) + this->report_error(_("argument 1 must be a map")); + else + { + // Since this function returns no value it must appear in + // a statement by itself, so we don't have to worry about + // order of evaluation of values around it. Evaluate the + // map first to get order of evaluation right. + Map_type* mt = args->front()->type()->map_type(); + Temporary_statement* map_temp = + Statement::make_temporary(mt, args->front(), loc); + inserter->insert(map_temp); + + Temporary_statement* key_temp = + Statement::make_temporary(mt->key_type(), args->back(), loc); + inserter->insert(key_temp); + + Expression* e1 = Expression::make_temporary_reference(map_temp, + loc); + Expression* e2 = Expression::make_temporary_reference(key_temp, + loc); + e2 = Expression::make_unary(OPERATOR_AND, e2, loc); + return Runtime::make_call(Runtime::MAPDELETE, this->location(), + 2, e1, e2); + } + } + break; + } + + return this; +} + +// Flatten a builtin call expression. This turns the arguments of copy and +// append into temporary expressions. + +Expression* +Builtin_call_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + if (this->code_ == BUILTIN_APPEND + || this->code_ == BUILTIN_COPY) + { + Location loc = this->location(); + Type* at = this->args()->front()->type(); + for (Expression_list::iterator pa = this->args()->begin(); + pa != this->args()->end(); + ++pa) + { + if ((*pa)->is_nil_expression()) + *pa = Expression::make_slice_composite_literal(at, NULL, loc); + if (!(*pa)->is_variable()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + } + return this; +} + +// Lower a make expression. + +Expression* +Builtin_call_expression::lower_make() +{ + Location loc = this->location(); + + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 1) + { + this->report_error(_("not enough arguments")); + return Expression::make_error(this->location()); + } + + Expression_list::const_iterator parg = args->begin(); + + Expression* first_arg = *parg; + if (!first_arg->is_type_expression()) + { + error_at(first_arg->location(), "expected type"); + this->set_is_error(); + return Expression::make_error(this->location()); + } + Type* type = first_arg->type(); + + bool is_slice = false; + bool is_map = false; + bool is_chan = false; + if (type->is_slice_type()) + is_slice = true; + else if (type->map_type() != NULL) + is_map = true; + else if (type->channel_type() != NULL) + is_chan = true; + else + { + this->report_error(_("invalid type for make function")); + return Expression::make_error(this->location()); + } + + bool have_big_args = false; + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + int uintptr_bits = uintptr_type->integer_type()->bits(); + + Type_context int_context(Type::lookup_integer_type("int"), false); + + ++parg; + Expression* len_arg; + if (parg == args->end()) + { + if (is_slice) + { + this->report_error(_("length required when allocating a slice")); + return Expression::make_error(this->location()); + } + + mpz_t zval; + mpz_init_set_ui(zval, 0); + len_arg = Expression::make_integer(&zval, NULL, loc); + mpz_clear(zval); + } + else + { + len_arg = *parg; + len_arg->determine_type(&int_context); + if (!this->check_int_value(len_arg, true)) + return Expression::make_error(this->location()); + if (len_arg->type()->integer_type() != NULL + && len_arg->type()->integer_type()->bits() > uintptr_bits) + have_big_args = true; + ++parg; + } + + Expression* cap_arg = NULL; + if (is_slice && parg != args->end()) + { + cap_arg = *parg; + cap_arg->determine_type(&int_context); + if (!this->check_int_value(cap_arg, false)) + return Expression::make_error(this->location()); + + Numeric_constant nclen; + Numeric_constant nccap; + unsigned long vlen; + unsigned long vcap; + if (len_arg->numeric_constant_value(&nclen) + && cap_arg->numeric_constant_value(&nccap) + && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID + && nccap.to_unsigned_long(&vcap) == Numeric_constant::NC_UL_VALID + && vlen > vcap) + { + this->report_error(_("len larger than cap")); + return Expression::make_error(this->location()); + } + + if (cap_arg->type()->integer_type() != NULL + && cap_arg->type()->integer_type()->bits() > uintptr_bits) + have_big_args = true; + ++parg; + } + + if (parg != args->end()) + { + this->report_error(_("too many arguments to make")); + return Expression::make_error(this->location()); + } + + Location type_loc = first_arg->location(); + Expression* type_arg; + if (is_slice || is_chan) + type_arg = Expression::make_type_descriptor(type, type_loc); + else if (is_map) + type_arg = Expression::make_map_descriptor(type->map_type(), type_loc); + else + go_unreachable(); + + Expression* call; + if (is_slice) + { + if (cap_arg == NULL) + call = Runtime::make_call((have_big_args + ? Runtime::MAKESLICE1BIG + : Runtime::MAKESLICE1), + loc, 2, type_arg, len_arg); + else + call = Runtime::make_call((have_big_args + ? Runtime::MAKESLICE2BIG + : Runtime::MAKESLICE2), + loc, 3, type_arg, len_arg, cap_arg); + } + else if (is_map) + call = Runtime::make_call((have_big_args + ? Runtime::MAKEMAPBIG + : Runtime::MAKEMAP), + loc, 2, type_arg, len_arg); + else if (is_chan) + call = Runtime::make_call((have_big_args + ? Runtime::MAKECHANBIG + : Runtime::MAKECHAN), + loc, 2, type_arg, len_arg); + else + go_unreachable(); + + return Expression::make_unsafe_cast(type, call, loc); +} + +// Return whether an expression has an integer value. Report an error +// if not. This is used when handling calls to the predeclared make +// function. + +bool +Builtin_call_expression::check_int_value(Expression* e, bool is_length) +{ + Numeric_constant nc; + if (e->numeric_constant_value(&nc)) + { + unsigned long v; + switch (nc.to_unsigned_long(&v)) + { + case Numeric_constant::NC_UL_VALID: + break; + case Numeric_constant::NC_UL_NOTINT: + error_at(e->location(), "non-integer %s argument to make", + is_length ? "len" : "cap"); + return false; + case Numeric_constant::NC_UL_NEGATIVE: + error_at(e->location(), "negative %s argument to make", + is_length ? "len" : "cap"); + return false; + case Numeric_constant::NC_UL_BIG: + // We don't want to give a compile-time error for a 64-bit + // value on a 32-bit target. + break; + } + + mpz_t val; + if (!nc.to_int(&val)) + go_unreachable(); + int bits = mpz_sizeinbase(val, 2); + mpz_clear(val); + Type* int_type = Type::lookup_integer_type("int"); + if (bits >= int_type->integer_type()->bits()) + { + error_at(e->location(), "%s argument too large for make", + is_length ? "len" : "cap"); + return false; + } + + return true; + } + + if (e->type()->integer_type() != NULL) + return true; + + error_at(e->location(), "non-integer %s argument to make", + is_length ? "len" : "cap"); + return false; +} + +// Return the type of the real or imag functions, given the type of +// the argument. We need to map complex to float, complex64 to +// float32, and complex128 to float64, so it has to be done by name. +// This returns NULL if it can't figure out the type. + +Type* +Builtin_call_expression::real_imag_type(Type* arg_type) +{ + if (arg_type == NULL || arg_type->is_abstract()) + return NULL; + Named_type* nt = arg_type->named_type(); + if (nt == NULL) + return NULL; + while (nt->real_type()->named_type() != NULL) + nt = nt->real_type()->named_type(); + if (nt->name() == "complex64") + return Type::lookup_float_type("float32"); + else if (nt->name() == "complex128") + return Type::lookup_float_type("float64"); + else + return NULL; +} + +// Return the type of the complex function, given the type of one of the +// argments. Like real_imag_type, we have to map by name. + +Type* +Builtin_call_expression::complex_type(Type* arg_type) +{ + if (arg_type == NULL || arg_type->is_abstract()) + return NULL; + Named_type* nt = arg_type->named_type(); + if (nt == NULL) + return NULL; + while (nt->real_type()->named_type() != NULL) + nt = nt->real_type()->named_type(); + if (nt->name() == "float32") + return Type::lookup_complex_type("complex64"); + else if (nt->name() == "float64") + return Type::lookup_complex_type("complex128"); + else + return NULL; +} + +// Return a single argument, or NULL if there isn't one. + +Expression* +Builtin_call_expression::one_arg() const +{ + const Expression_list* args = this->args(); + if (args == NULL || args->size() != 1) + return NULL; + return args->front(); +} + +// 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 +{ + if (this->is_error_expression()) + return true; + switch (this->code_) + { + case BUILTIN_LEN: + case BUILTIN_CAP: + { + if (this->seen_) + return false; + + Expression* arg = this->one_arg(); + if (arg == NULL) + return false; + Type* arg_type = arg->type(); + + if (arg_type->points_to() != NULL + && arg_type->points_to()->array_type() != NULL + && !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; + + if (this->code_ == BUILTIN_LEN && arg_type->is_string_type()) + { + this->seen_ = true; + bool ret = arg->is_constant(); + this->seen_ = false; + return ret; + } + } + break; + + case BUILTIN_SIZEOF: + case BUILTIN_ALIGNOF: + return this->one_arg() != NULL; + + case BUILTIN_OFFSETOF: + { + Expression* arg = this->one_arg(); + if (arg == NULL) + return false; + return arg->field_reference_expression() != NULL; + } + + case BUILTIN_COMPLEX: + { + const Expression_list* args = this->args(); + if (args != NULL && args->size() == 2) + return args->front()->is_constant() && args->back()->is_constant(); + } + break; + + case BUILTIN_REAL: + case BUILTIN_IMAG: + { + Expression* arg = this->one_arg(); + return arg != NULL && arg->is_constant(); + } + + default: + break; + } + + return false; +} + +// Return a numeric constant if possible. + +bool +Builtin_call_expression::do_numeric_constant_value(Numeric_constant* nc) const +{ + if (this->code_ == BUILTIN_LEN + || this->code_ == BUILTIN_CAP) + { + Expression* arg = this->one_arg(); + if (arg == NULL) + return false; + Type* arg_type = arg->type(); + + if (this->code_ == BUILTIN_LEN && arg_type->is_string_type()) + { + std::string sval; + if (arg->string_constant_value(&sval)) + { + nc->set_unsigned_long(Type::lookup_integer_type("int"), + sval.length()); + return true; + } + } + + if (arg_type->points_to() != NULL + && arg_type->points_to()->array_type() != NULL + && !arg_type->points_to()->is_slice_type()) + arg_type = arg_type->points_to(); + + if (arg_type->array_type() != NULL + && arg_type->array_type()->length() != NULL) + { + if (this->seen_) + return false; + Expression* e = arg_type->array_type()->length(); + this->seen_ = true; + bool r = e->numeric_constant_value(nc); + this->seen_ = false; + if (r) + { + if (!nc->set_type(Type::lookup_integer_type("int"), false, + this->location())) + r = false; + } + return r; + } + } + else if (this->code_ == BUILTIN_SIZEOF + || this->code_ == BUILTIN_ALIGNOF) + { + Expression* arg = this->one_arg(); + if (arg == NULL) + return false; + Type* arg_type = arg->type(); + if (arg_type->is_error()) + return false; + if (arg_type->is_abstract()) + return false; + + unsigned int ret; + if (this->code_ == BUILTIN_SIZEOF) + { + if (!arg_type->backend_type_size(this->gogo_, &ret)) + return false; + } + else if (this->code_ == BUILTIN_ALIGNOF) + { + if (arg->field_reference_expression() == NULL) + { + if (!arg_type->backend_type_align(this->gogo_, &ret)) + return false; + } + else + { + // Calling unsafe.Alignof(s.f) returns the alignment of + // the type of f when it is used as a field in a struct. + if (!arg_type->backend_type_field_align(this->gogo_, &ret)) + return false; + } + } + else + go_unreachable(); + + nc->set_unsigned_long(Type::lookup_integer_type("uintptr"), + static_cast(ret)); + return true; + } + else if (this->code_ == BUILTIN_OFFSETOF) + { + Expression* arg = this->one_arg(); + if (arg == NULL) + return false; + Field_reference_expression* farg = arg->field_reference_expression(); + if (farg == NULL) + return false; + unsigned int total_offset = 0; + while (true) + { + Expression* struct_expr = farg->expr(); + Type* st = struct_expr->type(); + if (st->struct_type() == NULL) + return false; + if (st->named_type() != NULL) + st->named_type()->convert(this->gogo_); + unsigned int offset; + if (!st->struct_type()->backend_field_offset(this->gogo_, + farg->field_index(), + &offset)) + return false; + total_offset += offset; + if (farg->implicit() && struct_expr->field_reference_expression() != NULL) + { + // Go up until we reach the original base. + farg = struct_expr->field_reference_expression(); + continue; + } + break; + } + nc->set_unsigned_long(Type::lookup_integer_type("uintptr"), + static_cast(total_offset)); + return true; + } + else if (this->code_ == BUILTIN_REAL || this->code_ == BUILTIN_IMAG) + { + Expression* arg = this->one_arg(); + if (arg == NULL) + return false; + + Numeric_constant argnc; + if (!arg->numeric_constant_value(&argnc)) + return false; + + mpfr_t real; + mpfr_t imag; + if (!argnc.to_complex(&real, &imag)) + return false; + + Type* type = Builtin_call_expression::real_imag_type(argnc.type()); + if (this->code_ == BUILTIN_REAL) + nc->set_float(type, real); + else + nc->set_float(type, imag); + return true; + } + else if (this->code_ == BUILTIN_COMPLEX) + { + const Expression_list* args = this->args(); + if (args == NULL || args->size() != 2) + return false; + + Numeric_constant rnc; + if (!args->front()->numeric_constant_value(&rnc)) + return false; + Numeric_constant inc; + if (!args->back()->numeric_constant_value(&inc)) + return false; + + if (rnc.type() != NULL + && !rnc.type()->is_abstract() + && inc.type() != NULL + && !inc.type()->is_abstract() + && !Type::are_identical(rnc.type(), inc.type(), false, NULL)) + return false; + + mpfr_t r; + if (!rnc.to_float(&r)) + return false; + mpfr_t i; + if (!inc.to_float(&i)) + { + mpfr_clear(r); + return false; + } + + Type* arg_type = rnc.type(); + if (arg_type == NULL || arg_type->is_abstract()) + arg_type = inc.type(); + + Type* type = Builtin_call_expression::complex_type(arg_type); + nc->set_complex(type, r, i); + + mpfr_clear(r); + mpfr_clear(i); + + return true; + } + + return false; +} + +// Give an error if we are discarding the value of an expression which +// should not normally be discarded. We don't give an error for +// discarding the value of an ordinary function call, but we do for +// builtin functions, purely for consistency with the gc compiler. + +bool +Builtin_call_expression::do_discarding_value() +{ + switch (this->code_) + { + case BUILTIN_INVALID: + default: + go_unreachable(); + + case BUILTIN_APPEND: + case BUILTIN_CAP: + case BUILTIN_COMPLEX: + case BUILTIN_IMAG: + case BUILTIN_LEN: + case BUILTIN_MAKE: + case BUILTIN_NEW: + case BUILTIN_REAL: + case BUILTIN_ALIGNOF: + case BUILTIN_OFFSETOF: + case BUILTIN_SIZEOF: + this->unused_value_error(); + return false; + + case BUILTIN_CLOSE: + case BUILTIN_COPY: + case BUILTIN_DELETE: + case BUILTIN_PANIC: + case BUILTIN_PRINT: + case BUILTIN_PRINTLN: + case BUILTIN_RECOVER: + return true; + } +} + +// Return the type. + +Type* +Builtin_call_expression::do_type() +{ + switch (this->code_) + { + case BUILTIN_INVALID: + default: + go_unreachable(); + + case BUILTIN_NEW: + case BUILTIN_MAKE: + { + const Expression_list* args = this->args(); + if (args == NULL || args->empty()) + return Type::make_error_type(); + return Type::make_pointer_type(args->front()->type()); + } + + case BUILTIN_CAP: + case BUILTIN_COPY: + case BUILTIN_LEN: + return Type::lookup_integer_type("int"); + + case BUILTIN_ALIGNOF: + case BUILTIN_OFFSETOF: + case BUILTIN_SIZEOF: + return Type::lookup_integer_type("uintptr"); + + case BUILTIN_CLOSE: + case BUILTIN_DELETE: + case BUILTIN_PANIC: + case BUILTIN_PRINT: + case BUILTIN_PRINTLN: + return Type::make_void_type(); + + case BUILTIN_RECOVER: + return Type::make_empty_interface_type(Linemap::predeclared_location()); + + case BUILTIN_APPEND: + { + const Expression_list* args = this->args(); + if (args == NULL || args->empty()) + return Type::make_error_type(); + Type *ret = args->front()->type(); + if (!ret->is_slice_type()) + return Type::make_error_type(); + return ret; + } + + case BUILTIN_REAL: + case BUILTIN_IMAG: + { + Expression* arg = this->one_arg(); + if (arg == NULL) + return Type::make_error_type(); + Type* t = arg->type(); + if (t->is_abstract()) + t = t->make_non_abstract_type(); + t = Builtin_call_expression::real_imag_type(t); + if (t == NULL) + t = Type::make_error_type(); + return t; + } + + case BUILTIN_COMPLEX: + { + const Expression_list* args = this->args(); + if (args == NULL || args->size() != 2) + return Type::make_error_type(); + Type* t = args->front()->type(); + if (t->is_abstract()) + { + t = args->back()->type(); + if (t->is_abstract()) + t = t->make_non_abstract_type(); + } + t = Builtin_call_expression::complex_type(t); + if (t == NULL) + t = Type::make_error_type(); + return t; + } + } +} + +// Determine the type. + +void +Builtin_call_expression::do_determine_type(const Type_context* context) +{ + if (!this->determining_types()) + return; + + this->fn()->determine_type_no_context(); + + const Expression_list* args = this->args(); + + bool is_print; + Type* arg_type = NULL; + switch (this->code_) + { + case BUILTIN_PRINT: + case BUILTIN_PRINTLN: + // Do not force a large integer constant to "int". + is_print = true; + break; + + case BUILTIN_REAL: + case BUILTIN_IMAG: + arg_type = Builtin_call_expression::complex_type(context->type); + if (arg_type == NULL) + arg_type = Type::lookup_complex_type("complex128"); + is_print = false; + break; + + case BUILTIN_COMPLEX: + { + // For the complex function the type of one operand can + // determine the type of the other, as in a binary expression. + arg_type = Builtin_call_expression::real_imag_type(context->type); + if (arg_type == NULL) + arg_type = Type::lookup_float_type("float64"); + if (args != NULL && args->size() == 2) + { + Type* t1 = args->front()->type(); + Type* t2 = args->back()->type(); + if (!t1->is_abstract()) + arg_type = t1; + else if (!t2->is_abstract()) + arg_type = t2; + } + is_print = false; + } + break; + + default: + is_print = false; + break; + } + + if (args != NULL) + { + for (Expression_list::const_iterator pa = args->begin(); + pa != args->end(); + ++pa) + { + Type_context subcontext; + subcontext.type = arg_type; + + if (is_print) + { + // We want to print large constants, we so can't just + // use the appropriate nonabstract type. Use uint64 for + // an integer if we know it is nonnegative, otherwise + // use int64 for a integer, otherwise use float64 for a + // float or complex128 for a complex. + Type* want_type = NULL; + Type* atype = (*pa)->type(); + if (atype->is_abstract()) + { + if (atype->integer_type() != NULL) + { + Numeric_constant nc; + if (this->numeric_constant_value(&nc)) + { + mpz_t val; + if (nc.to_int(&val)) + { + if (mpz_sgn(val) >= 0) + want_type = Type::lookup_integer_type("uint64"); + mpz_clear(val); + } + } + if (want_type == NULL) + want_type = Type::lookup_integer_type("int64"); + } + else if (atype->float_type() != NULL) + want_type = Type::lookup_float_type("float64"); + else if (atype->complex_type() != NULL) + want_type = Type::lookup_complex_type("complex128"); + else if (atype->is_abstract_string_type()) + want_type = Type::lookup_string_type(); + else if (atype->is_abstract_boolean_type()) + want_type = Type::lookup_bool_type(); + else + go_unreachable(); + subcontext.type = want_type; + } + } + + (*pa)->determine_type(&subcontext); + } + } +} + +// If there is exactly one argument, return true. Otherwise give an +// error message and return false. + +bool +Builtin_call_expression::check_one_arg() +{ + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 1) + { + this->report_error(_("not enough arguments")); + return false; + } + else if (args->size() > 1) + { + this->report_error(_("too many arguments")); + return false; + } + if (args->front()->is_error_expression() + || args->front()->type()->is_error()) + { + this->set_is_error(); + return false; + } + return true; +} + +// Check argument types for a builtin function. + +void +Builtin_call_expression::do_check_types(Gogo*) +{ + if (this->is_error_expression()) + return; + switch (this->code_) + { + case BUILTIN_INVALID: + case BUILTIN_NEW: + case BUILTIN_MAKE: + case BUILTIN_DELETE: + return; + + case BUILTIN_LEN: + case BUILTIN_CAP: + { + // The single argument may be either a string or an array or a + // map or a channel, or a pointer to a closed array. + if (this->check_one_arg()) + { + Type* arg_type = this->one_arg()->type(); + if (arg_type->points_to() != NULL + && arg_type->points_to()->array_type() != NULL + && !arg_type->points_to()->is_slice_type()) + arg_type = arg_type->points_to(); + if (this->code_ == BUILTIN_CAP) + { + if (!arg_type->is_error() + && arg_type->array_type() == NULL + && arg_type->channel_type() == NULL) + this->report_error(_("argument must be array or slice " + "or channel")); + } + else + { + if (!arg_type->is_error() + && !arg_type->is_string_type() + && arg_type->array_type() == NULL + && arg_type->map_type() == NULL + && arg_type->channel_type() == NULL) + this->report_error(_("argument must be string or " + "array or slice or map or channel")); + } + } + } + break; + + case BUILTIN_PRINT: + case BUILTIN_PRINTLN: + { + const Expression_list* args = this->args(); + if (args == NULL) + { + if (this->code_ == BUILTIN_PRINT) + warning_at(this->location(), 0, + "no arguments for builtin function %<%s%>", + (this->code_ == BUILTIN_PRINT + ? "print" + : "println")); + } + else + { + for (Expression_list::const_iterator p = args->begin(); + p != args->end(); + ++p) + { + Type* type = (*p)->type(); + if (type->is_error() + || type->is_string_type() + || type->integer_type() != NULL + || type->float_type() != NULL + || type->complex_type() != NULL + || type->is_boolean_type() + || type->points_to() != NULL + || type->interface_type() != NULL + || type->channel_type() != NULL + || type->map_type() != NULL + || type->function_type() != NULL + || type->is_slice_type()) + ; + else if ((*p)->is_type_expression()) + { + // If this is a type expression it's going to give + // an error anyhow, so we don't need one here. + } + else + this->report_error(_("unsupported argument type to " + "builtin function")); + } + } + } + break; + + case BUILTIN_CLOSE: + if (this->check_one_arg()) + { + if (this->one_arg()->type()->channel_type() == NULL) + this->report_error(_("argument must be channel")); + else if (!this->one_arg()->type()->channel_type()->may_send()) + this->report_error(_("cannot close receive-only channel")); + } + break; + + case BUILTIN_PANIC: + case BUILTIN_SIZEOF: + case BUILTIN_ALIGNOF: + this->check_one_arg(); + break; + + case BUILTIN_RECOVER: + if (this->args() != NULL && !this->args()->empty()) + this->report_error(_("too many arguments")); + break; + + case BUILTIN_OFFSETOF: + if (this->check_one_arg()) + { + Expression* arg = this->one_arg(); + if (arg->field_reference_expression() == NULL) + this->report_error(_("argument must be a field reference")); + } + break; + + case BUILTIN_COPY: + { + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 2) + { + this->report_error(_("not enough arguments")); + break; + } + else if (args->size() > 2) + { + this->report_error(_("too many arguments")); + break; + } + Type* arg1_type = args->front()->type(); + Type* arg2_type = args->back()->type(); + if (arg1_type->is_error() || arg2_type->is_error()) + break; + + Type* e1; + if (arg1_type->is_slice_type()) + e1 = arg1_type->array_type()->element_type(); + else + { + this->report_error(_("left argument must be a slice")); + break; + } + + if (arg2_type->is_slice_type()) + { + Type* e2 = arg2_type->array_type()->element_type(); + if (!Type::are_identical(e1, e2, true, NULL)) + this->report_error(_("element types must be the same")); + } + else if (arg2_type->is_string_type()) + { + if (e1->integer_type() == NULL || !e1->integer_type()->is_byte()) + this->report_error(_("first argument must be []byte")); + } + else + this->report_error(_("second argument must be slice or string")); + } + break; + + case BUILTIN_APPEND: + { + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 2) + { + this->report_error(_("not enough arguments")); + break; + } + if (args->size() > 2) + { + this->report_error(_("too many arguments")); + break; + } + if (args->front()->type()->is_error() + || args->back()->type()->is_error()) + break; + + Array_type* at = args->front()->type()->array_type(); + Type* e = at->element_type(); + + // The language permits appending a string to a []byte, as a + // special case. + if (args->back()->type()->is_string_type()) + { + if (e->integer_type() != NULL && e->integer_type()->is_byte()) + break; + } + + // The language says that the second argument must be + // assignable to a slice of the element type of the first + // argument. We already know the first argument is a slice + // type. + Type* arg2_type = Type::make_array_type(e, NULL); + std::string reason; + if (!Type::are_assignable(arg2_type, args->back()->type(), &reason)) + { + if (reason.empty()) + this->report_error(_("argument 2 has invalid type")); + else + { + error_at(this->location(), "argument 2 has invalid type (%s)", + reason.c_str()); + this->set_is_error(); + } + } + break; + } + + case BUILTIN_REAL: + case BUILTIN_IMAG: + if (this->check_one_arg()) + { + if (this->one_arg()->type()->complex_type() == NULL) + this->report_error(_("argument must have complex type")); + } + break; + + case BUILTIN_COMPLEX: + { + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 2) + this->report_error(_("not enough arguments")); + else if (args->size() > 2) + this->report_error(_("too many arguments")); + else if (args->front()->is_error_expression() + || args->front()->type()->is_error() + || args->back()->is_error_expression() + || args->back()->type()->is_error()) + this->set_is_error(); + else if (!Type::are_identical(args->front()->type(), + args->back()->type(), true, NULL)) + this->report_error(_("complex arguments must have identical types")); + else if (args->front()->type()->float_type() == NULL) + this->report_error(_("complex arguments must have " + "floating-point type")); + } + break; + + default: + go_unreachable(); + } +} + +// Return the tree for a builtin function. + +tree +Builtin_call_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + Location location = this->location(); + switch (this->code_) + { + case BUILTIN_INVALID: + case BUILTIN_NEW: + case BUILTIN_MAKE: + go_unreachable(); + + case BUILTIN_LEN: + case BUILTIN_CAP: + { + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 1); + Expression* arg = *args->begin(); + Type* arg_type = arg->type(); + + if (this->seen_) + { + go_assert(saw_errors()); + return error_mark_node; + } + this->seen_ = true; + + tree arg_tree = arg->get_tree(context); + + this->seen_ = false; + + if (arg_tree == error_mark_node) + return error_mark_node; + + if (arg_type->points_to() != NULL) + { + arg_type = arg_type->points_to(); + go_assert(arg_type->array_type() != NULL + && !arg_type->is_slice_type()); + go_assert(POINTER_TYPE_P(TREE_TYPE(arg_tree))); + arg_tree = build_fold_indirect_ref(arg_tree); + } + + Type* int_type = Type::lookup_integer_type("int"); + tree int_type_tree = type_to_tree(int_type->get_backend(gogo)); + + tree val_tree; + if (this->code_ == BUILTIN_LEN) + { + if (arg_type->is_string_type()) + val_tree = String_type::length_tree(gogo, arg_tree); + else if (arg_type->array_type() != NULL) + { + if (this->seen_) + { + go_assert(saw_errors()); + return error_mark_node; + } + this->seen_ = true; + Expression* len = arg_type->array_type()->get_length(gogo, arg); + val_tree = len->get_tree(context); + this->seen_ = false; + } + else if (arg_type->map_type() != NULL) + { + tree arg_type_tree = type_to_tree(arg_type->get_backend(gogo)); + static tree map_len_fndecl; + val_tree = Gogo::call_builtin(&map_len_fndecl, + location, + "__go_map_len", + 1, + int_type_tree, + arg_type_tree, + arg_tree); + } + else if (arg_type->channel_type() != NULL) + { + tree arg_type_tree = type_to_tree(arg_type->get_backend(gogo)); + static tree chan_len_fndecl; + val_tree = Gogo::call_builtin(&chan_len_fndecl, + location, + "__go_chan_len", + 1, + int_type_tree, + arg_type_tree, + arg_tree); + } + else + go_unreachable(); + } + else + { + if (arg_type->array_type() != NULL) + { + if (this->seen_) + { + go_assert(saw_errors()); + return error_mark_node; + } + this->seen_ = true; + Expression* cap = + arg_type->array_type()->get_capacity(gogo, arg); + val_tree = cap->get_tree(context); + this->seen_ = false; + } + else if (arg_type->channel_type() != NULL) + { + tree arg_type_tree = type_to_tree(arg_type->get_backend(gogo)); + static tree chan_cap_fndecl; + val_tree = Gogo::call_builtin(&chan_cap_fndecl, + location, + "__go_chan_cap", + 1, + int_type_tree, + arg_type_tree, + arg_tree); + } + else + go_unreachable(); + } + + return fold_convert_loc(location.gcc_location(), int_type_tree, + val_tree); + } + + case BUILTIN_PRINT: + case BUILTIN_PRINTLN: + { + const bool is_ln = this->code_ == BUILTIN_PRINTLN; + tree stmt_list = NULL_TREE; + + const Expression_list* call_args = this->args(); + if (call_args != NULL) + { + for (Expression_list::const_iterator p = call_args->begin(); + p != call_args->end(); + ++p) + { + if (is_ln && p != call_args->begin()) + { + static tree print_space_fndecl; + tree call = Gogo::call_builtin(&print_space_fndecl, + location, + "__go_print_space", + 0, + void_type_node); + if (call == error_mark_node) + return error_mark_node; + append_to_statement_list(call, &stmt_list); + } + + Type* type = (*p)->type(); + + tree arg = (*p)->get_tree(context); + if (arg == error_mark_node) + return error_mark_node; + + tree* pfndecl; + const char* fnname; + if (type->is_string_type()) + { + static tree print_string_fndecl; + pfndecl = &print_string_fndecl; + fnname = "__go_print_string"; + } + else if (type->integer_type() != NULL + && type->integer_type()->is_unsigned()) + { + static tree print_uint64_fndecl; + pfndecl = &print_uint64_fndecl; + fnname = "__go_print_uint64"; + Type* itype = Type::lookup_integer_type("uint64"); + Btype* bitype = itype->get_backend(gogo); + arg = fold_convert_loc(location.gcc_location(), + type_to_tree(bitype), arg); + } + else if (type->integer_type() != NULL) + { + static tree print_int64_fndecl; + pfndecl = &print_int64_fndecl; + fnname = "__go_print_int64"; + Type* itype = Type::lookup_integer_type("int64"); + Btype* bitype = itype->get_backend(gogo); + arg = fold_convert_loc(location.gcc_location(), + type_to_tree(bitype), arg); + } + else if (type->float_type() != NULL) + { + static tree print_double_fndecl; + pfndecl = &print_double_fndecl; + fnname = "__go_print_double"; + arg = fold_convert_loc(location.gcc_location(), + double_type_node, arg); + } + else if (type->complex_type() != NULL) + { + static tree print_complex_fndecl; + pfndecl = &print_complex_fndecl; + fnname = "__go_print_complex"; + arg = fold_convert_loc(location.gcc_location(), + complex_double_type_node, arg); + } + else if (type->is_boolean_type()) + { + static tree print_bool_fndecl; + pfndecl = &print_bool_fndecl; + fnname = "__go_print_bool"; + } + else if (type->points_to() != NULL + || type->channel_type() != NULL + || type->map_type() != NULL + || type->function_type() != NULL) + { + static tree print_pointer_fndecl; + pfndecl = &print_pointer_fndecl; + fnname = "__go_print_pointer"; + arg = fold_convert_loc(location.gcc_location(), + ptr_type_node, arg); + } + else if (type->interface_type() != NULL) + { + if (type->interface_type()->is_empty()) + { + static tree print_empty_interface_fndecl; + pfndecl = &print_empty_interface_fndecl; + fnname = "__go_print_empty_interface"; + } + else + { + static tree print_interface_fndecl; + pfndecl = &print_interface_fndecl; + fnname = "__go_print_interface"; + } + } + else if (type->is_slice_type()) + { + static tree print_slice_fndecl; + pfndecl = &print_slice_fndecl; + fnname = "__go_print_slice"; + } + else + { + go_assert(saw_errors()); + return error_mark_node; + } + + tree call = Gogo::call_builtin(pfndecl, + location, + fnname, + 1, + void_type_node, + TREE_TYPE(arg), + arg); + if (call == error_mark_node) + return error_mark_node; + append_to_statement_list(call, &stmt_list); + } + } + + if (is_ln) + { + static tree print_nl_fndecl; + tree call = Gogo::call_builtin(&print_nl_fndecl, + location, + "__go_print_nl", + 0, + void_type_node); + if (call == error_mark_node) + return error_mark_node; + append_to_statement_list(call, &stmt_list); + } + + return stmt_list; + } + + case BUILTIN_PANIC: + { + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 1); + Expression* arg = args->front(); + tree arg_tree = arg->get_tree(context); + if (arg_tree == error_mark_node) + return error_mark_node; + Type *empty = + Type::make_empty_interface_type(Linemap::predeclared_location()); + arg_tree = Expression::convert_for_assignment(context, empty, + arg->type(), + arg_tree, location); + static tree panic_fndecl; + tree call = Gogo::call_builtin(&panic_fndecl, + location, + "__go_panic", + 1, + 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. + TREE_THIS_VOLATILE(panic_fndecl) = 1; + return call; + } + + case BUILTIN_RECOVER: + { + // The argument is set when building recover thunks. It's a + // boolean value which is true if we can recover a value now. + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 1); + Expression* arg = args->front(); + tree arg_tree = arg->get_tree(context); + if (arg_tree == error_mark_node) + return error_mark_node; + + Type *empty = + Type::make_empty_interface_type(Linemap::predeclared_location()); + tree empty_tree = type_to_tree(empty->get_backend(context->gogo())); + + Type* nil_type = Type::make_nil_type(); + Expression* nil = Expression::make_nil(location); + tree nil_tree = nil->get_tree(context); + tree empty_nil_tree = Expression::convert_for_assignment(context, + empty, + nil_type, + nil_tree, + location); + + // We need to handle a deferred call to recover specially, + // because it changes whether it can recover a panic or not. + // See test7 in test/recover1.go. + tree call; + if (this->is_deferred()) + { + static tree deferred_recover_fndecl; + call = Gogo::call_builtin(&deferred_recover_fndecl, + location, + "__go_deferred_recover", + 0, + empty_tree); + } + else + { + static tree recover_fndecl; + call = Gogo::call_builtin(&recover_fndecl, + location, + "__go_recover", + 0, + empty_tree); + } + if (call == error_mark_node) + return error_mark_node; + return fold_build3_loc(location.gcc_location(), COND_EXPR, empty_tree, + arg_tree, call, empty_nil_tree); + } + + case BUILTIN_CLOSE: + { + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 1); + Expression* arg = args->front(); + tree arg_tree = arg->get_tree(context); + if (arg_tree == error_mark_node) + return error_mark_node; + static tree close_fndecl; + return Gogo::call_builtin(&close_fndecl, + location, + "__go_builtin_close", + 1, + void_type_node, + TREE_TYPE(arg_tree), + arg_tree); + } + + case BUILTIN_SIZEOF: + case BUILTIN_OFFSETOF: + case BUILTIN_ALIGNOF: + { + Numeric_constant nc; + unsigned long val; + if (!this->numeric_constant_value(&nc) + || nc.to_unsigned_long(&val) != Numeric_constant::NC_UL_VALID) + { + go_assert(saw_errors()); + return error_mark_node; + } + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + tree type = type_to_tree(uintptr_type->get_backend(gogo)); + return build_int_cst(type, val); + } + + case BUILTIN_COPY: + { + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 2); + Expression* arg1 = args->front(); + Expression* arg2 = args->back(); + + 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; + + Type* arg1_type = arg1->type(); + Array_type* at = arg1_type->array_type(); + go_assert(arg1->is_variable()); + Expression* arg1_valptr = at->get_value_pointer(gogo, arg1); + Expression* arg1_len_expr = at->get_length(gogo, arg1); + tree arg1_val = arg1_valptr->get_tree(context); + tree arg1_len = arg1_len_expr->get_tree(context); + if (arg1_val == error_mark_node || arg1_len == error_mark_node) + return error_mark_node; + + Type* arg2_type = arg2->type(); + tree arg2_val; + tree arg2_len; + if (arg2_type->is_slice_type()) + { + at = arg2_type->array_type(); + go_assert(arg2->is_variable()); + Expression* arg2_valptr = at->get_value_pointer(gogo, arg2); + Expression* arg2_len_expr = at->get_length(gogo, arg2); + arg2_val = arg2_valptr->get_tree(context); + arg2_len = arg2_len_expr->get_tree(context); + } + else + { + arg2_tree = save_expr(arg2_tree); + 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); + tree len = fold_build3_loc(location.gcc_location(), COND_EXPR, + TREE_TYPE(arg1_len), + fold_build2_loc(location.gcc_location(), + LT_EXPR, boolean_type_node, + arg1_len, arg2_len), + arg1_len, arg2_len); + len = save_expr(len); + + Type* element_type = at->element_type(); + Btype* element_btype = element_type->get_backend(gogo); + tree element_type_tree = type_to_tree(element_btype); + 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.gcc_location(), + TREE_TYPE(element_size), len); + bytecount = fold_build2_loc(location.gcc_location(), MULT_EXPR, + TREE_TYPE(element_size), + bytecount, element_size); + bytecount = fold_convert_loc(location.gcc_location(), size_type_node, + bytecount); + + arg1_val = fold_convert_loc(location.gcc_location(), ptr_type_node, + arg1_val); + arg2_val = fold_convert_loc(location.gcc_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.gcc_location(), COMPOUND_EXPR, + TREE_TYPE(len), call, len); + } + + case BUILTIN_APPEND: + { + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 2); + Expression* arg1 = args->front(); + Expression* arg2 = args->back(); + + 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; + + Array_type* at = arg1->type()->array_type(); + Type* element_type = at->element_type()->forwarded(); + + tree arg2_val; + tree arg2_len; + tree element_size; + if (arg2->type()->is_string_type() + && element_type->integer_type() != NULL + && element_type->integer_type()->is_byte()) + { + arg2_tree = save_expr(arg2_tree); + arg2_val = String_type::bytes_tree(gogo, arg2_tree); + arg2_len = String_type::length_tree(gogo, arg2_tree); + element_size = size_int(1); + } + else + { + go_assert(arg2->is_variable()); + arg2_val = + at->get_value_pointer(gogo, arg2)->get_tree(context); + arg2_len = at->get_length(gogo, arg2)->get_tree(context); + Btype* element_btype = element_type->get_backend(gogo); + tree element_type_tree = type_to_tree(element_btype); + if (element_type_tree == error_mark_node) + return error_mark_node; + element_size = TYPE_SIZE_UNIT(element_type_tree); + } + + arg2_val = fold_convert_loc(location.gcc_location(), ptr_type_node, + arg2_val); + arg2_len = fold_convert_loc(location.gcc_location(), size_type_node, + arg2_len); + element_size = fold_convert_loc(location.gcc_location(), size_type_node, + element_size); + + if (arg2_val == error_mark_node + || arg2_len == error_mark_node + || element_size == error_mark_node) + return error_mark_node; + + // We rebuild the decl each time since the slice types may + // change. + tree append_fndecl = NULL_TREE; + return Gogo::call_builtin(&append_fndecl, + location, + "__go_append", + 4, + TREE_TYPE(arg1_tree), + TREE_TYPE(arg1_tree), + arg1_tree, + ptr_type_node, + arg2_val, + size_type_node, + arg2_len, + size_type_node, + element_size); + } + + case BUILTIN_REAL: + case BUILTIN_IMAG: + { + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 1); + Expression* arg = args->front(); + tree arg_tree = arg->get_tree(context); + if (arg_tree == error_mark_node) + return error_mark_node; + go_assert(COMPLEX_FLOAT_TYPE_P(TREE_TYPE(arg_tree))); + if (this->code_ == BUILTIN_REAL) + return fold_build1_loc(location.gcc_location(), REALPART_EXPR, + TREE_TYPE(TREE_TYPE(arg_tree)), + arg_tree); + else + return fold_build1_loc(location.gcc_location(), IMAGPART_EXPR, + TREE_TYPE(TREE_TYPE(arg_tree)), + arg_tree); + } + + case BUILTIN_COMPLEX: + { + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 2); + tree r = args->front()->get_tree(context); + tree i = args->back()->get_tree(context); + if (r == error_mark_node || i == error_mark_node) + return error_mark_node; + go_assert(TYPE_MAIN_VARIANT(TREE_TYPE(r)) + == TYPE_MAIN_VARIANT(TREE_TYPE(i))); + go_assert(SCALAR_FLOAT_TYPE_P(TREE_TYPE(r))); + return fold_build2_loc(location.gcc_location(), COMPLEX_EXPR, + build_complex_type(TREE_TYPE(r)), + r, i); + } + + default: + go_unreachable(); + } +} + +// We have to support exporting a builtin call expression, because +// code can set a constant to the result of a builtin expression. + +void +Builtin_call_expression::do_export(Export* exp) const +{ + Numeric_constant nc; + if (!this->numeric_constant_value(&nc)) + { + error_at(this->location(), "value is not constant"); + return; + } + + if (nc.is_int()) + { + mpz_t val; + nc.get_int(&val); + Integer_expression::export_integer(exp, val); + mpz_clear(val); + } + else if (nc.is_float()) + { + mpfr_t fval; + nc.get_float(&fval); + Float_expression::export_float(exp, fval); + mpfr_clear(fval); + } + else if (nc.is_complex()) + { + mpfr_t real; + mpfr_t imag; + Complex_expression::export_complex(exp, real, imag); + mpfr_clear(real); + mpfr_clear(imag); + } + else + go_unreachable(); + + // A trailing space lets us reliably identify the end of the number. + exp->write_c_string(" "); +} + +// Class Call_expression. + +// A Go function can be viewed in a couple of different ways. The +// code of a Go function becomes a backend function with parameters +// whose types are simply the backend representation of the Go types. +// If there are multiple results, they are returned as a backend +// struct. + +// However, when Go code refers to a function other than simply +// calling it, the backend type of that function is actually a struct. +// The first field of the struct points to the Go function code +// (sometimes a wrapper as described below). The remaining fields +// hold addresses of closed-over variables. This struct is called a +// closure. + +// There are a few cases to consider. + +// A direct function call of a known function in package scope. In +// this case there are no closed-over variables, and we know the name +// of the function code. We can simply produce a backend call to the +// function directly, and not worry about the closure. + +// A direct function call of a known function literal. In this case +// we know the function code and we know the closure. We generate the +// function code such that it expects an additional final argument of +// the closure type. We pass the closure as the last argument, after +// the other arguments. + +// An indirect function call. In this case we have a closure. We +// load the pointer to the function code from the first field of the +// closure. We pass the address of the closure as the last argument. + +// A call to a method of an interface. Type methods are always at +// package scope, so we call the function directly, and don't worry +// about the closure. + +// This means that for a function at package scope we have two cases. +// One is the direct call, which has no closure. The other is the +// indirect call, which does have a closure. We can't simply ignore +// the closure, even though it is the last argument, because that will +// fail on targets where the function pops its arguments. So when +// generating a closure for a package-scope function we set the +// function code pointer in the closure to point to a wrapper +// function. This wrapper function accepts a final argument that +// points to the closure, ignores it, and calls the real function as a +// direct function call. This wrapper will normally be efficient, and +// can often simply be a tail call to the real function. + +// We don't use GCC's static chain pointer because 1) we don't need +// it; 2) GCC only permits using a static chain to call a known +// function, so we can't use it for an indirect call anyhow. Since we +// can't use it for an indirect call, we may as well not worry about +// using it for a direct call either. + +// We pass the closure last rather than first because it means that +// the function wrapper we put into a closure for a package-scope +// function can normally just be a tail call to the real function. + +// For method expressions we generate a wrapper that loads the +// receiver from the closure and then calls the method. This +// unfortunately forces reshuffling the arguments, since there is a +// new first argument, but we can't avoid reshuffling either for +// method expressions or for indirect calls of package-scope +// functions, and since the latter are more common we reshuffle for +// method expressions. + +// Note that the Go code retains the Go types. The extra final +// argument only appears when we convert to the backend +// representation. + +// Traversal. + +int +Call_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->fn_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->args_ != NULL) + { + if (this->args_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Lower a call statement. + +Expression* +Call_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) +{ + Location loc = this->location(); + + // A type cast can look like a function call. + if (this->fn_->is_type_expression() + && this->args_ != NULL + && this->args_->size() == 1) + 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. + Function_type* fntype = this->get_function_type(); + if (fntype == NULL) + { + if (!this->fn_->type()->is_error()) + this->report_error(_("expected function")); + return Expression::make_error(loc); + } + + // Handle an argument which is a call to a function which returns + // multiple results. + if (this->args_ != NULL + && this->args_->size() == 1 + && this->args_->front()->call_expression() != NULL) + { + size_t rc = this->args_->front()->call_expression()->result_count(); + if (rc > 1 + && ((fntype->parameters() != NULL + && (fntype->parameters()->size() == rc + || (fntype->is_varargs() + && fntype->parameters()->size() - 1 <= rc))) + || fntype->is_builtin())) + { + Call_expression* call = this->args_->front()->call_expression(); + Expression_list* args = new Expression_list; + for (size_t i = 0; i < rc; ++i) + args->push_back(Expression::make_call_result(call, i)); + // We can't return a new call expression here, because this + // one may be referenced by Call_result expressions. We + // also can't delete the old arguments, because we may still + // traverse them somewhere up the call stack. FIXME. + this->args_ = args; + } + } + + // Recognize a call to a builtin function. + if (fntype->is_builtin()) + return new Builtin_call_expression(gogo, this->fn_, this->args_, + this->is_varargs_, loc); + + // If this call returns multiple results, create a temporary + // variable for each result. + size_t rc = this->result_count(); + if (rc > 1 && this->results_ == NULL) + { + std::vector* temps = + new std::vector; + temps->reserve(rc); + const Typed_identifier_list* results = fntype->results(); + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p) + { + Temporary_statement* temp = Statement::make_temporary(p->type(), + NULL, loc); + inserter->insert(temp); + temps->push_back(temp); + } + this->results_ = temps; + } + + // Handle a call to a varargs function by packaging up the extra + // parameters. + if (fntype->is_varargs()) + { + const Typed_identifier_list* parameters = fntype->parameters(); + go_assert(parameters != NULL && !parameters->empty()); + Type* varargs_type = parameters->back().type(); + this->lower_varargs(gogo, function, inserter, varargs_type, + parameters->size()); + } + + // If this is call to a method, call the method directly passing the + // object as the first parameter. + Bound_method_expression* bme = this->fn_->bound_method_expression(); + if (bme != NULL) + { + Named_object* methodfn = bme->function(); + Expression* first_arg = bme->first_argument(); + + // We always pass a pointer when calling a method. + if (first_arg->type()->points_to() == NULL + && !first_arg->type()->is_error()) + { + first_arg = Expression::make_unary(OPERATOR_AND, first_arg, loc); + // We may need to create a temporary variable so that we can + // take the address. We can't do that here because it will + // mess up the order of evaluation. + Unary_expression* ue = static_cast(first_arg); + ue->set_create_temp(); + } + + // If we are calling a method which was inherited from an + // embedded struct, and the method did not get a stub, then the + // first type may be wrong. + Type* fatype = bme->first_argument_type(); + if (fatype != NULL) + { + if (fatype->points_to() == NULL) + fatype = Type::make_pointer_type(fatype); + first_arg = Expression::make_unsafe_cast(fatype, first_arg, loc); + } + + Expression_list* new_args = new Expression_list(); + new_args->push_back(first_arg); + if (this->args_ != NULL) + { + for (Expression_list::const_iterator p = this->args_->begin(); + p != this->args_->end(); + ++p) + new_args->push_back(*p); + } + + // We have to change in place because this structure may be + // referenced by Call_result_expressions. We can't delete the + // old arguments, because we may be traversing them up in some + // caller. FIXME. + this->args_ = new_args; + this->fn_ = Expression::make_func_reference(methodfn, NULL, + bme->location()); + } + + return this; +} + +// Lower a call to a varargs function. FUNCTION is the function in +// which the call occurs--it's not the function we are calling. +// VARARGS_TYPE is the type of the varargs parameter, a slice type. +// PARAM_COUNT is the number of parameters of the function we are +// calling; the last of these parameters will be the varargs +// parameter. + +void +Call_expression::lower_varargs(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, + Type* varargs_type, size_t param_count) +{ + if (this->varargs_are_lowered_) + return; + + Location loc = this->location(); + + go_assert(param_count > 0); + go_assert(varargs_type->is_slice_type()); + + size_t arg_count = this->args_ == NULL ? 0 : this->args_->size(); + if (arg_count < param_count - 1) + { + // Not enough arguments; will be caught in check_types. + return; + } + + Expression_list* old_args = this->args_; + Expression_list* new_args = new Expression_list(); + bool push_empty_arg = false; + if (old_args == NULL || old_args->empty()) + { + go_assert(param_count == 1); + push_empty_arg = true; + } + else + { + Expression_list::const_iterator pa; + int i = 1; + for (pa = old_args->begin(); pa != old_args->end(); ++pa, ++i) + { + if (static_cast(i) == param_count) + break; + new_args->push_back(*pa); + } + + // We have reached the varargs parameter. + + bool issued_error = false; + if (pa == old_args->end()) + push_empty_arg = true; + else if (pa + 1 == old_args->end() && this->is_varargs_) + new_args->push_back(*pa); + else if (this->is_varargs_) + { + if ((*pa)->type()->is_slice_type()) + this->report_error(_("too many arguments")); + else + { + error_at(this->location(), + _("invalid use of %<...%> with non-slice")); + this->set_is_error(); + } + return; + } + else + { + Type* element_type = varargs_type->array_type()->element_type(); + Expression_list* vals = new Expression_list; + for (; pa != old_args->end(); ++pa, ++i) + { + // Check types here so that we get a better message. + Type* patype = (*pa)->type(); + Location paloc = (*pa)->location(); + if (!this->check_argument_type(i, element_type, patype, + paloc, issued_error)) + continue; + vals->push_back(*pa); + } + Expression* val = + Expression::make_slice_composite_literal(varargs_type, vals, loc); + gogo->lower_expression(function, inserter, &val); + new_args->push_back(val); + } + } + + if (push_empty_arg) + new_args->push_back(Expression::make_nil(loc)); + + // We can't return a new call expression here, because this one may + // be referenced by Call_result expressions. FIXME. We can't + // delete OLD_ARGS because we may have both a Call_expression and a + // Builtin_call_expression which refer to them. FIXME. + this->args_ = new_args; + this->varargs_are_lowered_ = true; +} + +// Get the function type. This can return NULL in error cases. + +Function_type* +Call_expression::get_function_type() const +{ + return this->fn_->type()->function_type(); +} + +// Return the number of values which this call will return. + +size_t +Call_expression::result_count() const +{ + const Function_type* fntype = this->get_function_type(); + if (fntype == NULL) + return 0; + if (fntype->results() == NULL) + return 0; + return fntype->results()->size(); +} + +// Return the temporary which holds a result. + +Temporary_statement* +Call_expression::result(size_t i) const +{ + if (this->results_ == NULL || this->results_->size() <= i) + { + go_assert(saw_errors()); + return NULL; + } + return (*this->results_)[i]; +} + +// Return whether this is a call to the predeclared function recover. + +bool +Call_expression::is_recover_call() const +{ + return this->do_is_recover_call(); +} + +// Set the argument to the recover function. + +void +Call_expression::set_recover_arg(Expression* arg) +{ + this->do_set_recover_arg(arg); +} + +// Virtual functions also implemented by Builtin_call_expression. + +bool +Call_expression::do_is_recover_call() const +{ + return false; +} + +void +Call_expression::do_set_recover_arg(Expression*) +{ + go_unreachable(); +} + +// We have found an error with this call expression; return true if +// we should report it. + +bool +Call_expression::issue_error() +{ + if (this->issued_error_) + return false; + else + { + this->issued_error_ = true; + return true; + } +} + +// Get the type. + +Type* +Call_expression::do_type() +{ + if (this->type_ != NULL) + return this->type_; + + Type* ret; + Function_type* fntype = this->get_function_type(); + if (fntype == NULL) + return Type::make_error_type(); + + const Typed_identifier_list* results = fntype->results(); + if (results == NULL) + ret = Type::make_void_type(); + else if (results->size() == 1) + ret = results->begin()->type(); + else + ret = Type::make_call_multiple_result_type(this); + + this->type_ = ret; + + return this->type_; +} + +// Determine types for a call expression. We can use the function +// parameter types to set the types of the arguments. + +void +Call_expression::do_determine_type(const Type_context*) +{ + if (!this->determining_types()) + return; + + this->fn_->determine_type_no_context(); + Function_type* fntype = this->get_function_type(); + const Typed_identifier_list* parameters = NULL; + if (fntype != NULL) + parameters = fntype->parameters(); + if (this->args_ != NULL) + { + Typed_identifier_list::const_iterator pt; + if (parameters != NULL) + pt = parameters->begin(); + bool first = true; + for (Expression_list::const_iterator pa = this->args_->begin(); + pa != this->args_->end(); + ++pa) + { + if (first) + { + first = false; + // If this is a method, the first argument is the + // receiver. + if (fntype != NULL && fntype->is_method()) + { + Type* rtype = fntype->receiver()->type(); + // The receiver is always passed as a pointer. + if (rtype->points_to() == NULL) + rtype = Type::make_pointer_type(rtype); + Type_context subcontext(rtype, false); + (*pa)->determine_type(&subcontext); + continue; + } + } + + if (parameters != NULL && pt != parameters->end()) + { + Type_context subcontext(pt->type(), false); + (*pa)->determine_type(&subcontext); + ++pt; + } + else + (*pa)->determine_type_no_context(); + } + } +} + +// Called when determining types for a Call_expression. Return true +// if we should go ahead, false if they have already been determined. + +bool +Call_expression::determining_types() +{ + if (this->types_are_determined_) + return false; + else + { + this->types_are_determined_ = true; + return true; + } +} + +// Check types for parameter I. + +bool +Call_expression::check_argument_type(int i, const Type* parameter_type, + const Type* argument_type, + Location argument_location, + bool issued_error) +{ + std::string reason; + bool ok; + if (this->are_hidden_fields_ok_) + ok = Type::are_assignable_hidden_ok(parameter_type, argument_type, + &reason); + else + ok = Type::are_assignable(parameter_type, argument_type, &reason); + if (!ok) + { + if (!issued_error) + { + if (reason.empty()) + error_at(argument_location, "argument %d has incompatible type", i); + else + error_at(argument_location, + "argument %d has incompatible type (%s)", + i, reason.c_str()); + } + this->set_is_error(); + return false; + } + return true; +} + +// Check types. + +void +Call_expression::do_check_types(Gogo*) +{ + if (this->classification() == EXPRESSION_ERROR) + return; + + Function_type* fntype = this->get_function_type(); + if (fntype == NULL) + { + if (!this->fn_->type()->is_error()) + this->report_error(_("expected function")); + return; + } + + bool is_method = fntype->is_method(); + if (is_method) + { + go_assert(this->args_ != NULL && !this->args_->empty()); + Type* rtype = fntype->receiver()->type(); + Expression* first_arg = this->args_->front(); + // The language permits copying hidden fields for a method + // receiver. We dereference the values since receivers are + // always passed as pointers. + std::string reason; + if (!Type::are_assignable_hidden_ok(rtype->deref(), + first_arg->type()->deref(), + &reason)) + { + if (reason.empty()) + this->report_error(_("incompatible type for receiver")); + else + { + error_at(this->location(), + "incompatible type for receiver (%s)", + reason.c_str()); + this->set_is_error(); + } + } + } + + // Note that varargs was handled by the lower_varargs() method, so + // we don't have to worry about it here unless something is wrong. + if (this->is_varargs_ && !this->varargs_are_lowered_) + { + if (!fntype->is_varargs()) + { + error_at(this->location(), + _("invalid use of %<...%> calling non-variadic function")); + this->set_is_error(); + return; + } + } + + const Typed_identifier_list* parameters = fntype->parameters(); + if (this->args_ == NULL) + { + if (parameters != NULL && !parameters->empty()) + this->report_error(_("not enough arguments")); + } + else if (parameters == NULL) + { + if (!is_method || this->args_->size() > 1) + this->report_error(_("too many arguments")); + } + else + { + int i = 0; + Expression_list::const_iterator pa = this->args_->begin(); + if (is_method) + ++pa; + for (Typed_identifier_list::const_iterator pt = parameters->begin(); + pt != parameters->end(); + ++pt, ++pa, ++i) + { + if (pa == this->args_->end()) + { + this->report_error(_("not enough arguments")); + return; + } + this->check_argument_type(i + 1, pt->type(), (*pa)->type(), + (*pa)->location(), false); + } + if (pa != this->args_->end()) + this->report_error(_("too many arguments")); + } +} + +// Return whether we have to use a temporary variable to ensure that +// we evaluate this call expression in order. If the call returns no +// results then it will inevitably be executed last. + +bool +Call_expression::do_must_eval_in_order() const +{ + return this->result_count() > 0; +} + +// Get the function and the first argument to use when calling an +// interface method. + +Expression* +Call_expression::interface_method_function( + Interface_field_reference_expression* interface_method, + Expression** first_arg_ptr) +{ + *first_arg_ptr = interface_method->get_underlying_object(); + return interface_method->get_function(); +} + +// Build the call expression. + +tree +Call_expression::do_get_tree(Translate_context* context) +{ + if (this->tree_ != NULL_TREE) + return this->tree_; + + Function_type* fntype = this->get_function_type(); + if (fntype == NULL) + return error_mark_node; + + if (this->fn_->is_error_expression()) + return error_mark_node; + + Gogo* gogo = context->gogo(); + Location location = this->location(); + + Func_expression* func = this->fn_->func_expression(); + Interface_field_reference_expression* interface_method = + this->fn_->interface_field_reference_expression(); + const bool has_closure = func != NULL && func->closure() != NULL; + const bool is_interface_method = interface_method != NULL; + + bool has_closure_arg; + if (has_closure) + has_closure_arg = true; + else if (func != NULL) + has_closure_arg = false; + else if (is_interface_method) + has_closure_arg = false; + else + has_closure_arg = true; + + int nargs; + tree* args; + if (this->args_ == NULL || this->args_->empty()) + { + nargs = is_interface_method ? 1 : 0; + args = nargs == 0 ? NULL : new tree[nargs]; + } + else if (fntype->parameters() == NULL || fntype->parameters()->empty()) + { + // Passing a receiver parameter. + go_assert(!is_interface_method + && fntype->is_method() + && this->args_->size() == 1); + nargs = 1; + args = new tree[nargs]; + args[0] = this->args_->front()->get_tree(context); + } + else + { + const Typed_identifier_list* params = fntype->parameters(); + + nargs = this->args_->size(); + int i = is_interface_method ? 1 : 0; + nargs += i; + args = new tree[nargs]; + + Typed_identifier_list::const_iterator pp = params->begin(); + Expression_list::const_iterator pe = this->args_->begin(); + if (!is_interface_method && fntype->is_method()) + { + args[i] = (*pe)->get_tree(context); + ++pe; + ++i; + } + for (; pe != this->args_->end(); ++pe, ++pp, ++i) + { + go_assert(pp != params->end()); + tree arg_val = (*pe)->get_tree(context); + args[i] = Expression::convert_for_assignment(context, + pp->type(), + (*pe)->type(), + arg_val, + location); + if (args[i] == error_mark_node) + return error_mark_node; + } + go_assert(pp == params->end()); + go_assert(i == nargs); + } + + tree fntype_tree = type_to_tree(fntype->get_backend(gogo)); + tree fnfield_type = type_to_tree(fntype->get_backend_fntype(gogo)); + if (fntype_tree == error_mark_node || fnfield_type == error_mark_node) + return error_mark_node; + go_assert(FUNCTION_POINTER_TYPE_P(fnfield_type)); + tree rettype = TREE_TYPE(TREE_TYPE(fnfield_type)); + if (rettype == error_mark_node) + return error_mark_node; + + tree fn; + tree closure_tree; + if (func != NULL) + { + Named_object* no = func->named_object(); + fn = expr_to_tree(Func_expression::get_code_pointer(gogo, no, location)); + if (!has_closure) + closure_tree = NULL_TREE; + else + { + closure_tree = func->closure()->get_tree(context); + if (closure_tree == error_mark_node) + return error_mark_node; + } + } + else if (!is_interface_method) + { + closure_tree = this->fn_->get_tree(context); + if (closure_tree == error_mark_node) + return error_mark_node; + tree fnc = fold_convert_loc(location.gcc_location(), fntype_tree, + closure_tree); + go_assert(POINTER_TYPE_P(TREE_TYPE(fnc)) + && (TREE_CODE(TREE_TYPE(TREE_TYPE(fnc))) + == RECORD_TYPE)); + tree field = TYPE_FIELDS(TREE_TYPE(TREE_TYPE(fnc))); + fn = fold_build3_loc(location.gcc_location(), COMPONENT_REF, + TREE_TYPE(field), + build_fold_indirect_ref_loc(location.gcc_location(), + fnc), + field, NULL_TREE); + } + else + { + Expression* first_arg; + Expression* fn_expr = + this->interface_method_function(interface_method, &first_arg); + args[0] = first_arg->get_tree(context); + fn = fn_expr->get_tree(context); + + if (fn == error_mark_node) + return error_mark_node; + closure_tree = NULL_TREE; + } + + if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node) + return error_mark_node; + + tree fndecl = fn; + if (TREE_CODE(fndecl) == ADDR_EXPR) + fndecl = TREE_OPERAND(fndecl, 0); + + // Add a type cast in case the type of the function is a recursive + // type which refers to itself. We don't do this for an interface + // method because 1) an interface method never refers to itself, so + // we always have a function type here; 2) we pass an extra first + // argument to an interface method, so fnfield_type is not correct. + if ((!DECL_P(fndecl) || !DECL_IS_BUILTIN(fndecl)) && !is_interface_method) + fn = fold_convert_loc(location.gcc_location(), fnfield_type, fn); + + // This is to support builtin math functions when using 80387 math. + tree excess_type = NULL_TREE; + if (optimize + && TREE_CODE(fndecl) == FUNCTION_DECL + && DECL_IS_BUILTIN(fndecl) + && DECL_BUILT_IN_CLASS(fndecl) == BUILT_IN_NORMAL + && nargs > 0 + && ((SCALAR_FLOAT_TYPE_P(rettype) + && SCALAR_FLOAT_TYPE_P(TREE_TYPE(args[0]))) + || (COMPLEX_FLOAT_TYPE_P(rettype) + && COMPLEX_FLOAT_TYPE_P(TREE_TYPE(args[0]))))) + { + excess_type = excess_precision_type(TREE_TYPE(args[0])); + if (excess_type != NULL_TREE) + { + tree excess_fndecl = mathfn_built_in(excess_type, + DECL_FUNCTION_CODE(fndecl)); + if (excess_fndecl == NULL_TREE) + excess_type = NULL_TREE; + else + { + fn = build_fold_addr_expr_loc(location.gcc_location(), + excess_fndecl); + for (int i = 0; i < nargs; ++i) + { + if (SCALAR_FLOAT_TYPE_P(TREE_TYPE(args[i])) + || COMPLEX_FLOAT_TYPE_P(TREE_TYPE(args[i]))) + args[i] = ::convert(excess_type, args[i]); + } + } + } + } + + if (func == NULL) + fn = save_expr(fn); + + if (!has_closure_arg) + go_assert(closure_tree == NULL_TREE); + else + { + // Pass the closure argument by calling the function function + // __go_set_closure. In the order_evaluations pass we have + // ensured that if any parameters contain call expressions, they + // will have been moved out to temporary variables. + + go_assert(closure_tree != NULL_TREE); + closure_tree = fold_convert_loc(location.gcc_location(), ptr_type_node, + closure_tree); + static tree set_closure_fndecl; + tree set_closure = Gogo::call_builtin(&set_closure_fndecl, + location, + "__go_set_closure", + 1, + void_type_node, + ptr_type_node, + closure_tree); + if (set_closure == error_mark_node) + return error_mark_node; + fn = build2_loc(location.gcc_location(), COMPOUND_EXPR, + TREE_TYPE(fn), set_closure, fn); + } + + tree ret = build_call_array(excess_type != NULL_TREE ? excess_type : rettype, + fn, nargs, args); + delete[] args; + + SET_EXPR_LOCATION(ret, location.gcc_location()); + + // If this is a recursive function type which returns itself, as in + // type F func() F + // we have used ptr_type_node for the return type. Add a cast here + // to the correct type. + if (TREE_TYPE(ret) == ptr_type_node) + { + tree t = type_to_tree(this->type()->base()->get_backend(gogo)); + ret = fold_convert_loc(location.gcc_location(), t, ret); + } + + if (excess_type != NULL_TREE) + { + // Calling convert here can undo our excess precision change. + // That may or may not be a bug in convert_to_real. + ret = build1(NOP_EXPR, rettype, ret); + } + + if (this->results_ != NULL) + ret = this->set_results(context, ret); + + this->tree_ = ret; + + return ret; +} + +// Set the result variables if this call returns multiple results. + +tree +Call_expression::set_results(Translate_context* context, tree call_tree) +{ + tree stmt_list = NULL_TREE; + + call_tree = save_expr(call_tree); + + if (TREE_CODE(TREE_TYPE(call_tree)) != RECORD_TYPE) + { + go_assert(saw_errors()); + return call_tree; + } + + Location loc = this->location(); + tree field = TYPE_FIELDS(TREE_TYPE(call_tree)); + size_t rc = this->result_count(); + for (size_t i = 0; i < rc; ++i, field = DECL_CHAIN(field)) + { + go_assert(field != NULL_TREE); + + Temporary_statement* temp = this->result(i); + if (temp == NULL) + { + go_assert(saw_errors()); + return error_mark_node; + } + Temporary_reference_expression* ref = + Expression::make_temporary_reference(temp, loc); + ref->set_is_lvalue(); + tree temp_tree = ref->get_tree(context); + if (temp_tree == error_mark_node) + return error_mark_node; + + tree val_tree = build3_loc(loc.gcc_location(), COMPONENT_REF, + TREE_TYPE(field), call_tree, field, NULL_TREE); + tree set_tree = build2_loc(loc.gcc_location(), MODIFY_EXPR, + void_type_node, temp_tree, val_tree); + + append_to_statement_list(set_tree, &stmt_list); + } + go_assert(field == NULL_TREE); + + return save_expr(stmt_list); +} + +// Dump ast representation for a call expressin. + +void +Call_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + this->fn_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "("; + if (args_ != NULL) + ast_dump_context->dump_expression_list(this->args_); + + ast_dump_context->ostream() << ") "; +} + +// Make a call expression. + +Call_expression* +Expression::make_call(Expression* fn, Expression_list* args, bool is_varargs, + Location location) +{ + return new Call_expression(fn, args, is_varargs, location); +} + +// A single result from a call which returns multiple results. + +class Call_result_expression : public Expression +{ + public: + Call_result_expression(Call_expression* call, unsigned int index) + : Expression(EXPRESSION_CALL_RESULT, call->location()), + call_(call), index_(index) + { } + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Call_result_expression(this->call_->call_expression(), + this->index_); + } + + bool + do_must_eval_in_order() const + { return true; } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The underlying call expression. + Expression* call_; + // Which result we want. + unsigned int index_; +}; + +// Traverse a call result. + +int +Call_result_expression::do_traverse(Traverse* traverse) +{ + if (traverse->remember_expression(this->call_)) + { + // We have already traversed the call expression. + return TRAVERSE_CONTINUE; + } + return Expression::traverse(&this->call_, traverse); +} + +// Get the type. + +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(); + if (ce == NULL) + { + this->set_is_error(); + return Type::make_error_type(); + } + Function_type* fntype = ce->get_function_type(); + if (fntype == NULL) + { + if (ce->issue_error()) + { + if (!ce->fn()->type()->is_error()) + this->report_error(_("expected function")); + } + this->set_is_error(); + return Type::make_error_type(); + } + const Typed_identifier_list* results = fntype->results(); + if (results == NULL || results->size() < 2) + { + if (ce->issue_error()) + this->report_error(_("number of results does not match " + "number of values")); + return Type::make_error_type(); + } + Typed_identifier_list::const_iterator pr = results->begin(); + for (unsigned int i = 0; i < this->index_; ++i) + { + if (pr == results->end()) + break; + ++pr; + } + if (pr == results->end()) + { + if (ce->issue_error()) + this->report_error(_("number of results does not match " + "number of values")); + return Type::make_error_type(); + } + return pr->type(); +} + +// Check the type. Just make sure that we trigger the warning in +// do_type. + +void +Call_result_expression::do_check_types(Gogo*) +{ + this->type(); +} + +// Determine the type. We have nothing to do here, but the 0 result +// needs to pass down to the caller. + +void +Call_result_expression::do_determine_type(const Type_context*) +{ + this->call_->determine_type_no_context(); +} + +// Return the tree. We just refer to the temporary set by the call +// expression. We don't do this at lowering time because it makes it +// hard to evaluate the call at the right time. + +tree +Call_result_expression::do_get_tree(Translate_context* context) +{ + Call_expression* ce = this->call_->call_expression(); + if (ce == NULL) + { + go_assert(this->call_->is_error_expression()); + return error_mark_node; + } + Temporary_statement* ts = ce->result(this->index_); + if (ts == NULL) + { + go_assert(saw_errors()); + return error_mark_node; + } + Expression* ref = Expression::make_temporary_reference(ts, this->location()); + return ref->get_tree(context); +} + +// Dump ast representation for a call result expression. + +void +Call_result_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + // FIXME: Wouldn't it be better if the call is assigned to a temporary + // (struct) and the fields are referenced instead. + ast_dump_context->ostream() << this->index_ << "@("; + ast_dump_context->dump_expression(this->call_); + ast_dump_context->ostream() << ")"; +} + +// Make a reference to a single result of a call which returns +// multiple results. + +Expression* +Expression::make_call_result(Call_expression* call, unsigned int index) +{ + return new Call_result_expression(call, index); +} + +// Class Index_expression. + +// Traversal. + +int +Index_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->left_, traverse) == TRAVERSE_EXIT + || Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT + || (this->end_ != NULL + && Expression::traverse(&this->end_, traverse) == TRAVERSE_EXIT) + || (this->cap_ != NULL + && Expression::traverse(&this->cap_, traverse) == TRAVERSE_EXIT)) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Lower an index expression. This converts the generic index +// expression into an array index, a string index, or a map index. + +Expression* +Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) +{ + Location location = this->location(); + Expression* left = this->left_; + Expression* start = this->start_; + Expression* end = this->end_; + Expression* cap = this->cap_; + + Type* type = left->type(); + if (type->is_error()) + return Expression::make_error(location); + else if (left->is_type_expression()) + { + error_at(location, "attempt to index type expression"); + return Expression::make_error(location); + } + else if (type->array_type() != NULL) + return Expression::make_array_index(left, start, end, cap, location); + else if (type->points_to() != NULL + && type->points_to()->array_type() != NULL + && !type->points_to()->is_slice_type()) + { + Expression* deref = Expression::make_unary(OPERATOR_MULT, left, + location); + + // For an ordinary index into the array, the pointer will be + // dereferenced. For a slice it will not--the resulting slice + // will simply reuse the pointer, which is incorrect if that + // pointer is nil. + if (end != NULL || cap != NULL) + deref->issue_nil_check(); + + return Expression::make_array_index(deref, start, end, cap, location); + } + else if (type->is_string_type()) + { + if (cap != NULL) + { + error_at(location, "invalid 3-index slice of string"); + return Expression::make_error(location); + } + return Expression::make_string_index(left, start, end, location); + } + else if (type->map_type() != NULL) + { + if (end != NULL || cap != NULL) + { + error_at(location, "invalid slice of map"); + return Expression::make_error(location); + } + Map_index_expression* ret = Expression::make_map_index(left, start, + location); + if (this->is_lvalue_) + ret->set_is_lvalue(); + return ret; + } + else + { + error_at(location, + "attempt to index object which is not array, string, or map"); + return Expression::make_error(location); + } +} + +// Write an indexed expression +// (expr[expr:expr:expr], expr[expr:expr] or expr[expr]) to a dump context. + +void +Index_expression::dump_index_expression(Ast_dump_context* ast_dump_context, + const Expression* expr, + const Expression* start, + const Expression* end, + const Expression* cap) +{ + expr->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "["; + start->dump_expression(ast_dump_context); + if (end != NULL) + { + ast_dump_context->ostream() << ":"; + end->dump_expression(ast_dump_context); + } + if (cap != NULL) + { + ast_dump_context->ostream() << ":"; + cap->dump_expression(ast_dump_context); + } + ast_dump_context->ostream() << "]"; +} + +// Dump ast representation for an index expression. + +void +Index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + Index_expression::dump_index_expression(ast_dump_context, this->left_, + this->start_, this->end_, this->cap_); +} + +// Make an index expression. + +Expression* +Expression::make_index(Expression* left, Expression* start, Expression* end, + Expression* cap, Location location) +{ + return new Index_expression(left, start, end, cap, location); +} + +// An array index. This is used for both indexing and slicing. + +class Array_index_expression : public Expression +{ + public: + Array_index_expression(Expression* array, Expression* start, + Expression* end, Expression* cap, Location location) + : Expression(EXPRESSION_ARRAY_INDEX, location), + array_(array), start_(start), end_(end), cap_(cap), type_(NULL) + { } + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Expression* + do_copy() + { + return Expression::make_array_index(this->array_->copy(), + this->start_->copy(), + (this->end_ == NULL + ? NULL + : this->end_->copy()), + (this->cap_ == NULL + ? NULL + : this->cap_->copy()), + this->location()); + } + + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + + bool + do_is_addressable() const; + + void + do_address_taken(bool escapes) + { this->array_->address_taken(escapes); } + + void + do_issue_nil_check() + { this->array_->issue_nil_check(); } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The array we are getting a value from. + Expression* array_; + // The start or only index. + Expression* start_; + // The end index of a slice. This may be NULL for a simple array + // index, or it may be a nil expression for the length of the array. + Expression* end_; + // The capacity argument of a slice. This may be NULL for an array index or + // slice. + Expression* cap_; + // The type of the expression. + Type* type_; +}; + +// Array index traversal. + +int +Array_index_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->array_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->end_ != NULL) + { + if (Expression::traverse(&this->end_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->cap_ != NULL) + { + if (Expression::traverse(&this->cap_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Return the type of an array index. + +Type* +Array_index_expression::do_type() +{ + if (this->type_ == NULL) + { + Array_type* type = this->array_->type()->array_type(); + if (type == NULL) + this->type_ = Type::make_error_type(); + else if (this->end_ == NULL) + this->type_ = type->element_type(); + else if (type->is_slice_type()) + { + // A slice of a slice has the same type as the original + // slice. + this->type_ = this->array_->type()->deref(); + } + else + { + // A slice of an array is a slice. + this->type_ = Type::make_array_type(type->element_type(), NULL); + } + } + return this->type_; +} + +// Set the type of an array index. + +void +Array_index_expression::do_determine_type(const Type_context*) +{ + this->array_->determine_type_no_context(); + this->start_->determine_type_no_context(); + if (this->end_ != NULL) + this->end_->determine_type_no_context(); + if (this->cap_ != NULL) + this->cap_->determine_type_no_context(); +} + +// Check types of an array index. + +void +Array_index_expression::do_check_types(Gogo*) +{ + Numeric_constant nc; + unsigned long v; + if (this->start_->type()->integer_type() == NULL + && !this->start_->type()->is_error() + && (!this->start_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("index must be integer")); + if (this->end_ != NULL + && this->end_->type()->integer_type() == NULL + && !this->end_->type()->is_error() + && !this->end_->is_nil_expression() + && !this->end_->is_error_expression() + && (!this->end_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("slice end must be integer")); + if (this->cap_ != NULL + && this->cap_->type()->integer_type() == NULL + && !this->cap_->type()->is_error() + && !this->cap_->is_nil_expression() + && !this->cap_->is_error_expression() + && (!this->cap_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("slice capacity must be integer")); + + Array_type* array_type = this->array_->type()->array_type(); + if (array_type == NULL) + { + go_assert(this->array_->type()->is_error()); + return; + } + + unsigned int int_bits = + Type::lookup_integer_type("int")->integer_type()->bits(); + + Numeric_constant lvalnc; + mpz_t lval; + bool lval_valid = (array_type->length() != NULL + && array_type->length()->numeric_constant_value(&lvalnc) + && lvalnc.to_int(&lval)); + Numeric_constant inc; + mpz_t ival; + bool ival_valid = false; + if (this->start_->numeric_constant_value(&inc) && inc.to_int(&ival)) + { + ival_valid = true; + if (mpz_sgn(ival) < 0 + || mpz_sizeinbase(ival, 2) >= int_bits + || (lval_valid + && (this->end_ == NULL + ? mpz_cmp(ival, lval) >= 0 + : mpz_cmp(ival, lval) > 0))) + { + error_at(this->start_->location(), "array index out of bounds"); + this->set_is_error(); + } + } + if (this->end_ != NULL && !this->end_->is_nil_expression()) + { + Numeric_constant enc; + mpz_t eval; + bool eval_valid = false; + if (this->end_->numeric_constant_value(&enc) && enc.to_int(&eval)) + { + eval_valid = true; + if (mpz_sgn(eval) < 0 + || mpz_sizeinbase(eval, 2) >= int_bits + || (lval_valid && mpz_cmp(eval, lval) > 0)) + { + error_at(this->end_->location(), "array index out of bounds"); + this->set_is_error(); + } + else if (ival_valid && mpz_cmp(ival, eval) > 0) + this->report_error(_("inverted slice range")); + } + + Numeric_constant cnc; + mpz_t cval; + if (this->cap_ != NULL + && this->cap_->numeric_constant_value(&cnc) && cnc.to_int(&cval)) + { + if (mpz_sgn(cval) < 0 + || mpz_sizeinbase(cval, 2) >= int_bits + || (lval_valid && mpz_cmp(cval, lval) > 0)) + { + error_at(this->cap_->location(), "array index out of bounds"); + this->set_is_error(); + } + else if (ival_valid && mpz_cmp(ival, cval) > 0) + { + error_at(this->cap_->location(), + "invalid slice index: capacity less than start"); + this->set_is_error(); + } + else if (eval_valid && mpz_cmp(eval, cval) > 0) + { + error_at(this->cap_->location(), + "invalid slice index: capacity less than length"); + this->set_is_error(); + } + mpz_clear(cval); + } + + if (eval_valid) + mpz_clear(eval); + } + if (ival_valid) + mpz_clear(ival); + if (lval_valid) + mpz_clear(lval); + + // A slice of an array requires an addressable array. A slice of a + // slice is always possible. + if (this->end_ != NULL && !array_type->is_slice_type()) + { + if (!this->array_->is_addressable()) + this->report_error(_("slice of unaddressable value")); + else + this->array_->address_taken(true); + } +} + +// Flatten array indexing by using a temporary variable for slices. + +Expression* +Array_index_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + Location loc = this->location(); + if (this->array_->type()->is_slice_type() && !this->array_->is_variable()) + { + Temporary_statement* temp = Statement::make_temporary(NULL, this->array_, loc); + inserter->insert(temp); + this->array_ = Expression::make_temporary_reference(temp, loc); + } + return this; +} + +// Return whether this expression is addressable. + +bool +Array_index_expression::do_is_addressable() const +{ + // A slice expression is not addressable. + if (this->end_ != NULL) + return false; + + // An index into a slice is addressable. + if (this->array_->type()->is_slice_type()) + return true; + + // An index into an array is addressable if the array is + // addressable. + return this->array_->is_addressable(); +} + +// Get a tree for an array index. + +tree +Array_index_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + Location loc = this->location(); + + Array_type* array_type = this->array_->type()->array_type(); + if (array_type == NULL) + { + go_assert(this->array_->type()->is_error()); + return error_mark_node; + } + go_assert(!array_type->is_slice_type() || this->array_->is_variable()); + + tree type_tree = type_to_tree(array_type->get_backend(gogo)); + if (type_tree == error_mark_node) + return error_mark_node; + + tree length_tree = NULL_TREE; + if (this->end_ == NULL || this->end_->is_nil_expression()) + { + Expression* len = array_type->get_length(gogo, this->array_); + length_tree = len->get_tree(context); + if (length_tree == error_mark_node) + return error_mark_node; + length_tree = save_expr(length_tree); + } + + tree capacity_tree = NULL_TREE; + if (this->end_ != NULL) + { + Expression* cap = array_type->get_capacity(gogo, this->array_); + capacity_tree = cap->get_tree(context); + if (capacity_tree == error_mark_node) + return error_mark_node; + capacity_tree = save_expr(capacity_tree); + } + + tree cap_arg = capacity_tree; + if (this->cap_ != NULL) + { + cap_arg = this->cap_->get_tree(context); + if (cap_arg == error_mark_node) + return error_mark_node; + } + + tree length_type = (length_tree != NULL_TREE + ? TREE_TYPE(length_tree) + : TREE_TYPE(cap_arg)); + + tree bad_index = boolean_false_node; + + tree start_tree = this->start_->get_tree(context); + if (start_tree == error_mark_node) + return error_mark_node; + if (!DECL_P(start_tree)) + start_tree = save_expr(start_tree); + if (!INTEGRAL_TYPE_P(TREE_TYPE(start_tree))) + start_tree = convert_to_integer(length_type, start_tree); + + bad_index = Expression::check_bounds(start_tree, length_type, bad_index, + loc); + + start_tree = fold_convert_loc(loc.gcc_location(), length_type, start_tree); + bad_index = fold_build2_loc(loc.gcc_location(), TRUTH_OR_EXPR, + boolean_type_node, bad_index, + fold_build2_loc(loc.gcc_location(), + (this->end_ == NULL + ? GE_EXPR + : GT_EXPR), + boolean_type_node, start_tree, + (this->end_ == NULL + ? length_tree + : capacity_tree))); + + int code = (array_type->length() != NULL + ? (this->end_ == NULL + ? RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS + : RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS) + : (this->end_ == NULL + ? RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS + : RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS)); + tree crash = gogo->runtime_error(code, loc)->get_tree(context); + + if (this->end_ == NULL) + { + // Simple array indexing. This has to return an l-value, so + // wrap the index check into START_TREE. + start_tree = build2(COMPOUND_EXPR, TREE_TYPE(start_tree), + build3(COND_EXPR, void_type_node, + bad_index, crash, NULL_TREE), + start_tree); + start_tree = fold_convert_loc(loc.gcc_location(), sizetype, start_tree); + + if (array_type->length() != NULL) + { + // Fixed array. + tree array_tree = this->array_->get_tree(context); + if (array_tree == error_mark_node) + return error_mark_node; + return build4(ARRAY_REF, TREE_TYPE(type_tree), array_tree, + start_tree, NULL_TREE, NULL_TREE); + } + else + { + // Open array. + Expression* valptr = + array_type->get_value_pointer(gogo, this->array_); + tree values = valptr->get_tree(context); + Type* element_type = array_type->element_type(); + Btype* belement_type = element_type->get_backend(gogo); + tree element_type_tree = type_to_tree(belement_type); + if (element_type_tree == error_mark_node) + return error_mark_node; + tree element_size = TYPE_SIZE_UNIT(element_type_tree); + tree offset = fold_build2_loc(loc.gcc_location(), MULT_EXPR, sizetype, + start_tree, element_size); + tree ptr = fold_build2_loc(loc.gcc_location(), POINTER_PLUS_EXPR, + TREE_TYPE(values), values, offset); + return build_fold_indirect_ref(ptr); + } + } + + // Array slice. + + if (this->cap_ != NULL) + { + if (!DECL_P(cap_arg)) + cap_arg = save_expr(cap_arg); + if (!INTEGRAL_TYPE_P(TREE_TYPE(cap_arg))) + cap_arg = convert_to_integer(length_type, cap_arg); + + bad_index = Expression::check_bounds(cap_arg, length_type, bad_index, + loc); + cap_arg = fold_convert_loc(loc.gcc_location(), length_type, cap_arg); + + tree bad_cap = fold_build2_loc(loc.gcc_location(), TRUTH_OR_EXPR, + boolean_type_node, + fold_build2_loc(loc.gcc_location(), + LT_EXPR, boolean_type_node, + cap_arg, start_tree), + fold_build2_loc(loc.gcc_location(), + GT_EXPR, boolean_type_node, + cap_arg, capacity_tree)); + bad_index = fold_build2_loc(loc.gcc_location(), TRUTH_OR_EXPR, + boolean_type_node, bad_index, bad_cap); + } + + tree end_tree; + if (this->end_->is_nil_expression()) + end_tree = length_tree; + else + { + end_tree = this->end_->get_tree(context); + if (end_tree == error_mark_node) + return error_mark_node; + if (!DECL_P(end_tree)) + end_tree = save_expr(end_tree); + if (!INTEGRAL_TYPE_P(TREE_TYPE(end_tree))) + end_tree = convert_to_integer(length_type, end_tree); + + bad_index = Expression::check_bounds(end_tree, length_type, bad_index, + loc); + + end_tree = fold_convert_loc(loc.gcc_location(), length_type, end_tree); + + tree bad_end = fold_build2_loc(loc.gcc_location(), TRUTH_OR_EXPR, + boolean_type_node, + fold_build2_loc(loc.gcc_location(), + LT_EXPR, boolean_type_node, + end_tree, start_tree), + fold_build2_loc(loc.gcc_location(), + GT_EXPR, boolean_type_node, + end_tree, cap_arg)); + bad_index = fold_build2_loc(loc.gcc_location(), TRUTH_OR_EXPR, + boolean_type_node, bad_index, bad_end); + } + + + Type* element_type = array_type->element_type(); + tree element_type_tree = type_to_tree(element_type->get_backend(gogo)); + if (element_type_tree == error_mark_node) + return error_mark_node; + tree element_size = TYPE_SIZE_UNIT(element_type_tree); + + tree offset = fold_build2_loc(loc.gcc_location(), MULT_EXPR, sizetype, + fold_convert_loc(loc.gcc_location(), sizetype, + start_tree), + element_size); + + Expression* valptr = array_type->get_value_pointer(gogo, this->array_); + tree value_pointer = valptr->get_tree(context); + if (value_pointer == error_mark_node) + return error_mark_node; + + value_pointer = fold_build2_loc(loc.gcc_location(), POINTER_PLUS_EXPR, + TREE_TYPE(value_pointer), + value_pointer, offset); + + tree result_length_tree = fold_build2_loc(loc.gcc_location(), MINUS_EXPR, + length_type, end_tree, start_tree); + + tree result_capacity_tree = fold_build2_loc(loc.gcc_location(), MINUS_EXPR, + length_type, cap_arg, start_tree); + + tree struct_tree = type_to_tree(this->type()->get_backend(gogo)); + go_assert(TREE_CODE(struct_tree) == RECORD_TYPE); + + vec *init; + vec_alloc (init, 3); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + tree field = TYPE_FIELDS(struct_tree); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0); + elt->index = field; + elt->value = value_pointer; + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0); + elt->index = field; + elt->value = fold_convert_loc(loc.gcc_location(), TREE_TYPE(field), + result_length_tree); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0); + elt->index = field; + elt->value = fold_convert_loc(loc.gcc_location(), TREE_TYPE(field), + result_capacity_tree); + + tree constructor = build_constructor(struct_tree, init); + + if (TREE_CONSTANT(value_pointer) + && TREE_CONSTANT(result_length_tree) + && TREE_CONSTANT(result_capacity_tree)) + TREE_CONSTANT(constructor) = 1; + + return fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR, + TREE_TYPE(constructor), + build3(COND_EXPR, void_type_node, + bad_index, crash, NULL_TREE), + constructor); +} + +// Dump ast representation for an array index expression. + +void +Array_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + Index_expression::dump_index_expression(ast_dump_context, this->array_, + this->start_, this->end_, this->cap_); +} + +// Make an array index expression. END and CAP may be NULL. + +Expression* +Expression::make_array_index(Expression* array, Expression* start, + Expression* end, Expression* cap, + Location location) +{ + return new Array_index_expression(array, start, end, cap, location); +} + +// A string index. This is used for both indexing and slicing. + +class String_index_expression : public Expression +{ + public: + String_index_expression(Expression* string, Expression* start, + Expression* end, Location location) + : Expression(EXPRESSION_STRING_INDEX, location), + string_(string), start_(start), end_(end) + { } + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_string_index(this->string_->copy(), + this->start_->copy(), + (this->end_ == NULL + ? NULL + : this->end_->copy()), + this->location()); + } + + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The string we are getting a value from. + Expression* string_; + // The start or only index. + Expression* start_; + // The end index of a slice. This may be NULL for a single index, + // or it may be a nil expression for the length of the string. + Expression* end_; +}; + +// String index traversal. + +int +String_index_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->string_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->end_ != NULL) + { + if (Expression::traverse(&this->end_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Return the type of a string index. + +Type* +String_index_expression::do_type() +{ + if (this->end_ == NULL) + return Type::lookup_integer_type("uint8"); + else + return this->string_->type(); +} + +// Determine the type of a string index. + +void +String_index_expression::do_determine_type(const Type_context*) +{ + this->string_->determine_type_no_context(); + this->start_->determine_type_no_context(); + if (this->end_ != NULL) + this->end_->determine_type_no_context(); +} + +// Check types of a string index. + +void +String_index_expression::do_check_types(Gogo*) +{ + Numeric_constant nc; + unsigned long v; + if (this->start_->type()->integer_type() == NULL + && !this->start_->type()->is_error() + && (!this->start_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("index must be integer")); + if (this->end_ != NULL + && this->end_->type()->integer_type() == NULL + && !this->end_->type()->is_error() + && !this->end_->is_nil_expression() + && !this->end_->is_error_expression() + && (!this->end_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("slice end must be integer")); + + std::string sval; + bool sval_valid = this->string_->string_constant_value(&sval); + + Numeric_constant inc; + mpz_t ival; + bool ival_valid = false; + if (this->start_->numeric_constant_value(&inc) && inc.to_int(&ival)) + { + ival_valid = true; + if (mpz_sgn(ival) < 0 + || (sval_valid && mpz_cmp_ui(ival, sval.length()) >= 0)) + { + error_at(this->start_->location(), "string index out of bounds"); + this->set_is_error(); + } + } + if (this->end_ != NULL && !this->end_->is_nil_expression()) + { + Numeric_constant enc; + mpz_t eval; + if (this->end_->numeric_constant_value(&enc) && enc.to_int(&eval)) + { + if (mpz_sgn(eval) < 0 + || (sval_valid && mpz_cmp_ui(eval, sval.length()) > 0)) + { + error_at(this->end_->location(), "string index out of bounds"); + this->set_is_error(); + } + else if (ival_valid && mpz_cmp(ival, eval) > 0) + this->report_error(_("inverted slice range")); + mpz_clear(eval); + } + } + if (ival_valid) + mpz_clear(ival); +} + +// Get a tree for a string index. + +tree +String_index_expression::do_get_tree(Translate_context* context) +{ + Location loc = this->location(); + + tree string_tree = this->string_->get_tree(context); + if (string_tree == error_mark_node) + return error_mark_node; + + if (this->string_->type()->points_to() != NULL) + string_tree = build_fold_indirect_ref(string_tree); + if (!DECL_P(string_tree)) + string_tree = save_expr(string_tree); + tree string_type = TREE_TYPE(string_tree); + + tree length_tree = String_type::length_tree(context->gogo(), string_tree); + length_tree = save_expr(length_tree); + + Type* int_type = Type::lookup_integer_type("int"); + tree length_type = type_to_tree(int_type->get_backend(context->gogo())); + + tree bad_index = boolean_false_node; + + tree start_tree = this->start_->get_tree(context); + if (start_tree == error_mark_node) + return error_mark_node; + if (!DECL_P(start_tree)) + start_tree = save_expr(start_tree); + if (!INTEGRAL_TYPE_P(TREE_TYPE(start_tree))) + start_tree = convert_to_integer(length_type, start_tree); + + bad_index = Expression::check_bounds(start_tree, length_type, bad_index, + loc); + + start_tree = fold_convert_loc(loc.gcc_location(), length_type, start_tree); + + int code = (this->end_ == NULL + ? RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS + : RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS); + tree crash = context->gogo()->runtime_error(code, loc)->get_tree(context); + + if (this->end_ == NULL) + { + bad_index = fold_build2_loc(loc.gcc_location(), TRUTH_OR_EXPR, + boolean_type_node, bad_index, + fold_build2_loc(loc.gcc_location(), GE_EXPR, + boolean_type_node, + start_tree, length_tree)); + + tree bytes_tree = String_type::bytes_tree(context->gogo(), string_tree); + tree ptr = fold_build2_loc(loc.gcc_location(), POINTER_PLUS_EXPR, + TREE_TYPE(bytes_tree), + bytes_tree, + fold_convert_loc(loc.gcc_location(), sizetype, + start_tree)); + tree index = build_fold_indirect_ref_loc(loc.gcc_location(), ptr); + + return build2(COMPOUND_EXPR, TREE_TYPE(index), + build3(COND_EXPR, void_type_node, + bad_index, crash, NULL_TREE), + index); + } + else + { + tree end_tree; + if (this->end_->is_nil_expression()) + end_tree = build_int_cst(length_type, -1); + else + { + end_tree = this->end_->get_tree(context); + if (end_tree == error_mark_node) + return error_mark_node; + if (!DECL_P(end_tree)) + end_tree = save_expr(end_tree); + if (!INTEGRAL_TYPE_P(TREE_TYPE(end_tree))) + end_tree = convert_to_integer(length_type, end_tree); + + bad_index = Expression::check_bounds(end_tree, length_type, + bad_index, loc); + + end_tree = fold_convert_loc(loc.gcc_location(), length_type, + end_tree); + } + + static tree strslice_fndecl; + tree ret = Gogo::call_builtin(&strslice_fndecl, + loc, + "__go_string_slice", + 3, + string_type, + string_type, + string_tree, + length_type, + 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; + + if (bad_index == boolean_false_node) + return ret; + else + return build2(COMPOUND_EXPR, TREE_TYPE(ret), + build3(COND_EXPR, void_type_node, + bad_index, crash, NULL_TREE), + ret); + } +} + +// Dump ast representation for a string index expression. + +void +String_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + Index_expression::dump_index_expression(ast_dump_context, this->string_, + this->start_, this->end_, NULL); +} + +// Make a string index expression. END may be NULL. + +Expression* +Expression::make_string_index(Expression* string, Expression* start, + Expression* end, Location location) +{ + return new String_index_expression(string, start, end, location); +} + +// Class Map_index. + +// Get the type of the map. + +Map_type* +Map_index_expression::get_map_type() const +{ + Map_type* mt = this->map_->type()->deref()->map_type(); + if (mt == NULL) + go_assert(saw_errors()); + return mt; +} + +// Map index traversal. + +int +Map_index_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->map_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return Expression::traverse(&this->index_, traverse); +} + +// Return the type of a map index. + +Type* +Map_index_expression::do_type() +{ + Map_type* mt = this->get_map_type(); + if (mt == NULL) + return Type::make_error_type(); + Type* type = mt->val_type(); + // If this map index is in a tuple assignment, we actually return a + // pointer to the value type. Tuple_map_assignment_statement is + // responsible for handling this correctly. We need to get the type + // right in case this gets assigned to a temporary variable. + if (this->is_in_tuple_assignment_) + type = Type::make_pointer_type(type); + return type; +} + +// Fix the type of a map index. + +void +Map_index_expression::do_determine_type(const Type_context*) +{ + this->map_->determine_type_no_context(); + Map_type* mt = this->get_map_type(); + Type* key_type = mt == NULL ? NULL : mt->key_type(); + Type_context subcontext(key_type, false); + this->index_->determine_type(&subcontext); +} + +// Check types of a map index. + +void +Map_index_expression::do_check_types(Gogo*) +{ + std::string reason; + Map_type* mt = this->get_map_type(); + if (mt == NULL) + return; + if (!Type::are_assignable(mt->key_type(), this->index_->type(), &reason)) + { + if (reason.empty()) + this->report_error(_("incompatible type for map index")); + else + { + error_at(this->location(), "incompatible type for map index (%s)", + reason.c_str()); + this->set_is_error(); + } + } +} + +// Get a tree for a map index. + +tree +Map_index_expression::do_get_tree(Translate_context* context) +{ + Map_type* type = this->get_map_type(); + if (type == NULL) + return error_mark_node; + + tree valptr = this->get_value_pointer(context, this->is_lvalue_); + if (valptr == error_mark_node) + return error_mark_node; + valptr = save_expr(valptr); + + tree val_type_tree = TREE_TYPE(TREE_TYPE(valptr)); + + if (this->is_lvalue_) + return build_fold_indirect_ref(valptr); + else if (this->is_in_tuple_assignment_) + { + // Tuple_map_assignment_statement is responsible for using this + // appropriately. + return valptr; + } + else + { + Gogo* gogo = context->gogo(); + Btype* val_btype = type->val_type()->get_backend(gogo); + Bexpression* val_zero = gogo->backend()->zero_expression(val_btype); + return fold_build3(COND_EXPR, val_type_tree, + fold_build2(EQ_EXPR, boolean_type_node, valptr, + fold_convert(TREE_TYPE(valptr), + null_pointer_node)), + expr_to_tree(val_zero), + build_fold_indirect_ref(valptr)); + } +} + +// Get a tree for the map index. This returns a tree which evaluates +// to a pointer to a value. The pointer will be NULL if the key is +// not in the map. + +tree +Map_index_expression::get_value_pointer(Translate_context* context, + bool insert) +{ + Map_type* type = this->get_map_type(); + if (type == NULL) + return error_mark_node; + + tree map_tree = this->map_->get_tree(context); + tree index_tree = this->index_->get_tree(context); + index_tree = Expression::convert_for_assignment(context, type->key_type(), + this->index_->type(), + index_tree, + this->location()); + if (map_tree == error_mark_node || index_tree == error_mark_node) + return error_mark_node; + + if (this->map_->type()->points_to() != NULL) + map_tree = build_fold_indirect_ref(map_tree); + + // We need to pass in a pointer to the key, so stuff it into a + // variable. + tree tmp; + tree make_tmp; + if (current_function_decl != NULL) + { + tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree)); + DECL_IGNORED_P(tmp) = 0; + DECL_INITIAL(tmp) = index_tree; + make_tmp = build1(DECL_EXPR, void_type_node, tmp); + TREE_ADDRESSABLE(tmp) = 1; + } + else + { + tmp = build_decl(this->location().gcc_location(), VAR_DECL, + create_tmp_var_name("M"), + TREE_TYPE(index_tree)); + DECL_EXTERNAL(tmp) = 0; + TREE_PUBLIC(tmp) = 0; + TREE_STATIC(tmp) = 1; + DECL_ARTIFICIAL(tmp) = 1; + if (!TREE_CONSTANT(index_tree)) + make_tmp = fold_build2_loc(this->location().gcc_location(), + INIT_EXPR, void_type_node, + tmp, index_tree); + else + { + TREE_READONLY(tmp) = 1; + TREE_CONSTANT(tmp) = 1; + DECL_INITIAL(tmp) = index_tree; + make_tmp = NULL_TREE; + } + rest_of_decl_compilation(tmp, 1, 0); + } + tree tmpref = + fold_convert_loc(this->location().gcc_location(), const_ptr_type_node, + build_fold_addr_expr_loc(this->location().gcc_location(), + tmp)); + + static tree map_index_fndecl; + tree call = Gogo::call_builtin(&map_index_fndecl, + this->location(), + "__go_map_index", + 3, + const_ptr_type_node, + TREE_TYPE(map_tree), + map_tree, + const_ptr_type_node, + tmpref, + boolean_type_node, + (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; + + Type* val_type = type->val_type(); + tree val_type_tree = type_to_tree(val_type->get_backend(context->gogo())); + if (val_type_tree == error_mark_node) + return error_mark_node; + tree ptr_val_type_tree = build_pointer_type(val_type_tree); + + tree ret = fold_convert_loc(this->location().gcc_location(), + ptr_val_type_tree, call); + if (make_tmp != NULL_TREE) + ret = build2(COMPOUND_EXPR, ptr_val_type_tree, make_tmp, ret); + return ret; +} + +// Dump ast representation for a map index expression + +void +Map_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + Index_expression::dump_index_expression(ast_dump_context, this->map_, + this->index_, NULL, NULL); +} + +// Make a map index expression. + +Map_index_expression* +Expression::make_map_index(Expression* map, Expression* index, + Location location) +{ + return new Map_index_expression(map, index, location); +} + +// Class Field_reference_expression. + +// Lower a field reference expression. There is nothing to lower, but +// this is where we generate the tracking information for fields with +// the magic go:"track" tag. + +Expression* +Field_reference_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) +{ + Struct_type* struct_type = this->expr_->type()->struct_type(); + if (struct_type == NULL) + { + // Error will be reported elsewhere. + return this; + } + const Struct_field* field = struct_type->field(this->field_index_); + if (field == NULL) + return this; + if (!field->has_tag()) + return this; + if (field->tag().find("go:\"track\"") == std::string::npos) + return this; + + // We have found a reference to a tracked field. Build a call to + // the runtime function __go_fieldtrack with a string that describes + // the field. FIXME: We should only call this once per referenced + // field per function, not once for each reference to the field. + + if (this->called_fieldtrack_) + return this; + this->called_fieldtrack_ = true; + + Location loc = this->location(); + + std::string s = "fieldtrack \""; + Named_type* nt = this->expr_->type()->named_type(); + if (nt == NULL || nt->named_object()->package() == NULL) + s.append(gogo->pkgpath()); + else + s.append(nt->named_object()->package()->pkgpath()); + s.push_back('.'); + if (nt != NULL) + s.append(Gogo::unpack_hidden_name(nt->name())); + s.push_back('.'); + s.append(field->field_name()); + s.push_back('"'); + + // We can't use a string here, because internally a string holds a + // pointer to the actual bytes; when the linker garbage collects the + // string, it won't garbage collect the bytes. So we use a + // [...]byte. + + mpz_t val; + mpz_init_set_ui(val, s.length()); + Expression* length_expr = Expression::make_integer(&val, NULL, loc); + mpz_clear(val); + + Type* byte_type = gogo->lookup_global("byte")->type_value(); + Type* array_type = Type::make_array_type(byte_type, length_expr); + + Expression_list* bytes = new Expression_list(); + for (std::string::const_iterator p = s.begin(); p != s.end(); p++) + { + mpz_init_set_ui(val, *p); + Expression* byte = Expression::make_integer(&val, NULL, loc); + mpz_clear(val); + bytes->push_back(byte); + } + + Expression* e = Expression::make_composite_literal(array_type, 0, false, + bytes, false, loc); + + Variable* var = new Variable(array_type, e, true, false, false, loc); + + static int count; + char buf[50]; + snprintf(buf, sizeof buf, "fieldtrack.%d", count); + ++count; + + Named_object* no = gogo->add_variable(buf, var); + e = Expression::make_var_reference(no, loc); + e = Expression::make_unary(OPERATOR_AND, e, loc); + + Expression* call = Runtime::make_call(Runtime::FIELDTRACK, loc, 1, e); + inserter->insert(Statement::make_statement(call, false)); + + // Put this function, and the global variable we just created, into + // unique sections. This will permit the linker to garbage collect + // them if they are not referenced. The effect is that the only + // strings, indicating field references, that will wind up in the + // executable will be those for functions that are actually needed. + if (function != NULL) + function->func_value()->set_in_unique_section(); + var->set_in_unique_section(); + + return this; +} + +// Return the type of a field reference. + +Type* +Field_reference_expression::do_type() +{ + Type* type = this->expr_->type(); + if (type->is_error()) + return type; + Struct_type* struct_type = type->struct_type(); + go_assert(struct_type != NULL); + return struct_type->field(this->field_index_)->type(); +} + +// Check the types for a field reference. + +void +Field_reference_expression::do_check_types(Gogo*) +{ + Type* type = this->expr_->type(); + if (type->is_error()) + return; + Struct_type* struct_type = type->struct_type(); + go_assert(struct_type != NULL); + go_assert(struct_type->field(this->field_index_) != NULL); +} + +// Get a tree for a field reference. + +tree +Field_reference_expression::do_get_tree(Translate_context* context) +{ + Bexpression* bstruct = tree_to_expr(this->expr_->get_tree(context)); + Bexpression* ret = + context->gogo()->backend()->struct_field_expression(bstruct, + this->field_index_, + this->location()); + return expr_to_tree(ret); +} + +// Dump ast representation for a field reference expression. + +void +Field_reference_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + this->expr_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "." << this->field_index_; +} + +// Make a reference to a qualified identifier in an expression. + +Field_reference_expression* +Expression::make_field_reference(Expression* expr, unsigned int field_index, + Location location) +{ + return new Field_reference_expression(expr, field_index, location); +} + +// Class Interface_field_reference_expression. + +// Return an expression for the pointer to the function to call. + +Expression* +Interface_field_reference_expression::get_function() +{ + Expression* ref = this->expr_; + Location loc = this->location(); + if (ref->type()->points_to() != NULL) + ref = Expression::make_unary(OPERATOR_MULT, ref, loc); + + Expression* mtable = + Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc); + Struct_type* mtable_type = mtable->type()->points_to()->struct_type(); + + std::string name = Gogo::unpack_hidden_name(this->name_); + unsigned int index; + const Struct_field* field = mtable_type->find_local_field(name, &index); + go_assert(field != NULL); + mtable = Expression::make_unary(OPERATOR_MULT, mtable, loc); + return Expression::make_field_reference(mtable, index, loc); +} + +// Return an expression for the first argument to pass to the interface +// function. + +Expression* +Interface_field_reference_expression::get_underlying_object() +{ + Expression* expr = this->expr_; + if (expr->type()->points_to() != NULL) + expr = Expression::make_unary(OPERATOR_MULT, expr, this->location()); + return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT, + this->location()); +} + +// Traversal. + +int +Interface_field_reference_expression::do_traverse(Traverse* traverse) +{ + return Expression::traverse(&this->expr_, traverse); +} + +// Lower the expression. If this expression is not called, we need to +// evaluate the expression twice when converting to the backend +// interface. So introduce a temporary variable if necessary. + +Expression* +Interface_field_reference_expression::do_lower(Gogo*, Named_object*, + Statement_inserter* inserter, + int) +{ + if (!this->expr_->is_variable()) + { + Temporary_statement* temp = + Statement::make_temporary(this->expr_->type(), NULL, this->location()); + inserter->insert(temp); + this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_, + this->location()); + } + return this; +} + +// Return the type of an interface field reference. + +Type* +Interface_field_reference_expression::do_type() +{ + Type* expr_type = this->expr_->type(); + + Type* points_to = expr_type->points_to(); + if (points_to != NULL) + expr_type = points_to; + + Interface_type* interface_type = expr_type->interface_type(); + if (interface_type == NULL) + return Type::make_error_type(); + + const Typed_identifier* method = interface_type->find_method(this->name_); + if (method == NULL) + return Type::make_error_type(); + + return method->type(); +} + +// Determine types. + +void +Interface_field_reference_expression::do_determine_type(const Type_context*) +{ + this->expr_->determine_type_no_context(); +} + +// Check the types for an interface field reference. + +void +Interface_field_reference_expression::do_check_types(Gogo*) +{ + Type* type = this->expr_->type(); + + Type* points_to = type->points_to(); + if (points_to != NULL) + type = points_to; + + Interface_type* interface_type = type->interface_type(); + if (interface_type == NULL) + { + if (!type->is_error_type()) + this->report_error(_("expected interface or pointer to interface")); + } + else + { + const Typed_identifier* method = + interface_type->find_method(this->name_); + if (method == NULL) + { + error_at(this->location(), "method %qs not in interface", + Gogo::message_name(this->name_).c_str()); + this->set_is_error(); + } + } +} + +// If an interface field reference is not simply called, then it is +// represented as a closure. The closure will hold a single variable, +// the value of the interface on which the method should be called. +// The function will be a simple thunk that pulls the value from the +// closure and calls the method with the remaining arguments. + +// Because method values are not common, we don't build all thunks for +// all possible interface methods, but instead only build them as we +// need them. In particular, we even build them on demand for +// interface methods defined in other packages. + +Interface_field_reference_expression::Interface_method_thunks + Interface_field_reference_expression::interface_method_thunks; + +// Find or create the thunk to call method NAME on TYPE. + +Named_object* +Interface_field_reference_expression::create_thunk(Gogo* gogo, + Interface_type* type, + const std::string& name) +{ + std::pair val(type, NULL); + std::pair ins = + Interface_field_reference_expression::interface_method_thunks.insert(val); + if (ins.second) + { + // This is the first time we have seen this interface. + ins.first->second = new Method_thunks(); + } + + for (Method_thunks::const_iterator p = ins.first->second->begin(); + p != ins.first->second->end(); + p++) + if (p->first == name) + return p->second; + + Location loc = type->location(); + + const Typed_identifier* method_id = type->find_method(name); + if (method_id == NULL) + return Named_object::make_erroneous_name(Gogo::thunk_name()); + + Function_type* orig_fntype = method_id->type()->function_type(); + if (orig_fntype == NULL) + return Named_object::make_erroneous_name(Gogo::thunk_name()); + + Struct_field_list* sfl = new Struct_field_list(); + // The type here is wrong--it should be the C function type. But it + // doesn't really matter. + Type* vt = Type::make_pointer_type(Type::make_void_type()); + sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc))); + Type* closure_type = Type::make_struct_type(sfl, loc); + closure_type = Type::make_pointer_type(closure_type); + + Function_type* new_fntype = orig_fntype->copy_with_names(); + + Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype, + false, loc); + + Variable* cvar = new Variable(closure_type, NULL, false, false, false, loc); + cvar->set_is_used(); + Named_object* cp = Named_object::make_variable("$closure", NULL, cvar); + new_no->func_value()->set_closure_var(cp); + + gogo->start_block(loc); + + // Field 0 of the closure is the function code pointer, field 1 is + // the value on which to invoke the method. + Expression* arg = Expression::make_var_reference(cp, loc); + arg = Expression::make_unary(OPERATOR_MULT, arg, loc); + arg = Expression::make_field_reference(arg, 1, loc); + + Expression *ifre = Expression::make_interface_field_reference(arg, name, + loc); + + const Typed_identifier_list* orig_params = orig_fntype->parameters(); + Expression_list* args; + if (orig_params == NULL || orig_params->empty()) + args = NULL; + else + { + const Typed_identifier_list* new_params = new_fntype->parameters(); + args = new Expression_list(); + for (Typed_identifier_list::const_iterator p = new_params->begin(); + p != new_params->end(); + ++p) + { + Named_object* p_no = gogo->lookup(p->name(), NULL); + go_assert(p_no != NULL + && p_no->is_variable() + && p_no->var_value()->is_parameter()); + args->push_back(Expression::make_var_reference(p_no, loc)); + } + } + + Call_expression* call = Expression::make_call(ifre, args, + orig_fntype->is_varargs(), + loc); + call->set_varargs_are_lowered(); + + Statement* s = Statement::make_return_from_call(call, loc); + gogo->add_statement(s); + Block* b = gogo->finish_block(loc); + gogo->add_block(b, loc); + gogo->lower_block(new_no, b); + gogo->flatten_block(new_no, b); + gogo->finish_function(loc); + + ins.first->second->push_back(std::make_pair(name, new_no)); + return new_no; +} + +// Get a tree for a method value. + +tree +Interface_field_reference_expression::do_get_tree(Translate_context* context) +{ + Interface_type* type = this->expr_->type()->interface_type(); + if (type == NULL) + { + go_assert(saw_errors()); + return error_mark_node; + } + + Named_object* thunk = + Interface_field_reference_expression::create_thunk(context->gogo(), + type, this->name_); + if (thunk->is_erroneous()) + { + go_assert(saw_errors()); + return error_mark_node; + } + + // FIXME: We should lower this earlier, but we can't it lower it in + // the lowering pass because at that point we don't know whether we + // need to create the thunk or not. If the expression is called, we + // don't need the thunk. + + Location loc = this->location(); + + Struct_field_list* fields = new Struct_field_list(); + fields->push_back(Struct_field(Typed_identifier("fn.0", + thunk->func_value()->type(), + loc))); + fields->push_back(Struct_field(Typed_identifier("val.1", + this->expr_->type(), + loc))); + Struct_type* st = Type::make_struct_type(fields, loc); + + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_func_code_reference(thunk, loc)); + vals->push_back(this->expr_); + + Expression* expr = Expression::make_struct_composite_literal(st, vals, loc); + expr = Expression::make_heap_composite(expr, loc); + + Bexpression* bclosure = tree_to_expr(expr->get_tree(context)); + Expression* nil_check = + Expression::make_binary(OPERATOR_EQEQ, this->expr_, + Expression::make_nil(loc), loc); + Bexpression* bnil_check = tree_to_expr(nil_check->get_tree(context)); + + Gogo* gogo = context->gogo(); + Expression* crash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); + Bexpression* bcrash = tree_to_expr(crash->get_tree(context)); + + Bexpression* bcond = + gogo->backend()->conditional_expression(NULL, bnil_check, bcrash, NULL, loc); + Bstatement* cond_statement = gogo->backend()->expression_statement(bcond); + Bexpression* ret = + gogo->backend()->compound_expression(cond_statement, bclosure, loc); + return expr_to_tree(ret); +} + +// Dump ast representation for an interface field reference. + +void +Interface_field_reference_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + this->expr_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "." << this->name_; +} + +// Make a reference to a field in an interface. + +Expression* +Expression::make_interface_field_reference(Expression* expr, + const std::string& field, + Location location) +{ + return new Interface_field_reference_expression(expr, field, location); +} + +// A general selector. This is a Parser_expression for LEFT.NAME. It +// is lowered after we know the type of the left hand side. + +class Selector_expression : public Parser_expression +{ + public: + Selector_expression(Expression* left, const std::string& name, + Location location) + : Parser_expression(EXPRESSION_SELECTOR, location), + left_(left), name_(name) + { } + + protected: + int + do_traverse(Traverse* traverse) + { return Expression::traverse(&this->left_, traverse); } + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_copy() + { + return new Selector_expression(this->left_->copy(), this->name_, + this->location()); + } + + void + do_dump_expression(Ast_dump_context* ast_dump_context) const; + + private: + Expression* + lower_method_expression(Gogo*); + + // The expression on the left hand side. + Expression* left_; + // The name on the right hand side. + std::string name_; +}; + +// Lower a selector expression once we know the real type of the left +// hand side. + +Expression* +Selector_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*, + int) +{ + Expression* left = this->left_; + if (left->is_type_expression()) + return this->lower_method_expression(gogo); + return Type::bind_field_or_method(gogo, left->type(), left, this->name_, + this->location()); +} + +// Lower a method expression T.M or (*T).M. We turn this into a +// function literal. + +Expression* +Selector_expression::lower_method_expression(Gogo* gogo) +{ + Location location = this->location(); + Type* type = this->left_->type(); + const std::string& name(this->name_); + + bool is_pointer; + if (type->points_to() == NULL) + is_pointer = false; + else + { + is_pointer = true; + type = type->points_to(); + } + Named_type* nt = type->named_type(); + if (nt == NULL) + { + error_at(location, + ("method expression requires named type or " + "pointer to named type")); + return Expression::make_error(location); + } + + bool is_ambiguous; + Method* method = nt->method_function(name, &is_ambiguous); + const Typed_identifier* imethod = NULL; + if (method == NULL && !is_pointer) + { + Interface_type* it = nt->interface_type(); + if (it != NULL) + imethod = it->find_method(name); + } + + if (method == NULL && imethod == NULL) + { + if (!is_ambiguous) + error_at(location, "type %<%s%s%> has no method %<%s%>", + is_pointer ? "*" : "", + nt->message_name().c_str(), + Gogo::message_name(name).c_str()); + else + error_at(location, "method %<%s%s%> is ambiguous in type %<%s%>", + Gogo::message_name(name).c_str(), + is_pointer ? "*" : "", + nt->message_name().c_str()); + return Expression::make_error(location); + } + + if (method != NULL && !is_pointer && !method->is_value_method()) + { + error_at(location, "method requires pointer (use %<(*%s).%s)%>", + nt->message_name().c_str(), + Gogo::message_name(name).c_str()); + return Expression::make_error(location); + } + + // Build a new function type in which the receiver becomes the first + // argument. + Function_type* method_type; + if (method != NULL) + { + method_type = method->type(); + go_assert(method_type->is_method()); + } + else + { + method_type = imethod->type()->function_type(); + go_assert(method_type != NULL && !method_type->is_method()); + } + + const char* const receiver_name = "$this"; + Typed_identifier_list* parameters = new Typed_identifier_list(); + parameters->push_back(Typed_identifier(receiver_name, this->left_->type(), + location)); + + const Typed_identifier_list* method_parameters = method_type->parameters(); + if (method_parameters != NULL) + { + int i = 0; + for (Typed_identifier_list::const_iterator p = method_parameters->begin(); + p != method_parameters->end(); + ++p, ++i) + { + if (!p->name().empty()) + parameters->push_back(*p); + else + { + char buf[20]; + snprintf(buf, sizeof buf, "$param%d", i); + parameters->push_back(Typed_identifier(buf, p->type(), + p->location())); + } + } + } + + const Typed_identifier_list* method_results = method_type->results(); + Typed_identifier_list* results; + if (method_results == NULL) + results = NULL; + else + { + results = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator p = method_results->begin(); + p != method_results->end(); + ++p) + results->push_back(*p); + } + + Function_type* fntype = Type::make_function_type(NULL, parameters, results, + location); + if (method_type->is_varargs()) + fntype->set_is_varargs(); + + // We generate methods which always takes a pointer to the receiver + // as their first argument. If this is for a pointer type, we can + // simply reuse the existing function. We use an internal hack to + // get the right type. + // FIXME: This optimization is disabled because it doesn't yet work + // with function descriptors when the method expression is not + // directly called. + if (method != NULL && is_pointer && false) + { + Named_object* mno = (method->needs_stub_method() + ? method->stub_object() + : method->named_object()); + Expression* f = Expression::make_func_reference(mno, NULL, location); + f = Expression::make_cast(fntype, f, location); + Type_conversion_expression* tce = + static_cast(f); + tce->set_may_convert_function_types(); + return f; + } + + Named_object* no = gogo->start_function(Gogo::thunk_name(), fntype, false, + location); + + Named_object* vno = gogo->lookup(receiver_name, NULL); + go_assert(vno != NULL); + Expression* ve = Expression::make_var_reference(vno, location); + Expression* bm; + if (method != NULL) + bm = Type::bind_field_or_method(gogo, nt, ve, name, location); + else + bm = Expression::make_interface_field_reference(ve, name, location); + + // Even though we found the method above, if it has an error type we + // may see an error here. + if (bm->is_error_expression()) + { + gogo->finish_function(location); + return bm; + } + + Expression_list* args; + if (parameters->size() <= 1) + args = NULL; + else + { + args = new Expression_list(); + Typed_identifier_list::const_iterator p = parameters->begin(); + ++p; + for (; p != parameters->end(); ++p) + { + vno = gogo->lookup(p->name(), NULL); + go_assert(vno != NULL); + args->push_back(Expression::make_var_reference(vno, location)); + } + } + + gogo->start_block(location); + + Call_expression* call = Expression::make_call(bm, args, + method_type->is_varargs(), + location); + + Statement* s = Statement::make_return_from_call(call, location); + gogo->add_statement(s); + + Block* b = gogo->finish_block(location); + + gogo->add_block(b, location); + + // Lower the call in case there are multiple results. + gogo->lower_block(no, b); + gogo->flatten_block(no, b); + + gogo->finish_function(location); + + return Expression::make_func_reference(no, NULL, location); +} + +// Dump the ast for a selector expression. + +void +Selector_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + ast_dump_context->dump_expression(this->left_); + ast_dump_context->ostream() << "."; + ast_dump_context->ostream() << this->name_; +} + +// Make a selector expression. + +Expression* +Expression::make_selector(Expression* left, const std::string& name, + Location location) +{ + return new Selector_expression(left, name, location); +} + +// Implement the builtin function new. + +class Allocation_expression : public Expression +{ + public: + Allocation_expression(Type* type, Location location) + : Expression(EXPRESSION_ALLOCATION, location), + type_(type) + { } + + protected: + int + do_traverse(Traverse* traverse) + { return Type::traverse(this->type_, traverse); } + + Type* + do_type() + { return Type::make_pointer_type(this->type_); } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return new Allocation_expression(this->type_, this->location()); } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type we are allocating. + Type* type_; +}; + +// Return a tree for an allocation expression. + +tree +Allocation_expression::do_get_tree(Translate_context* context) +{ + tree type_tree = type_to_tree(this->type_->get_backend(context->gogo())); + if (type_tree == error_mark_node) + return error_mark_node; + tree size_tree = TYPE_SIZE_UNIT(type_tree); + tree space = context->gogo()->allocate_memory(this->type_, size_tree, + this->location()); + if (space == error_mark_node) + return error_mark_node; + return fold_convert(build_pointer_type(type_tree), space); +} + +// Dump ast representation for an allocation expression. + +void +Allocation_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + ast_dump_context->ostream() << "new("; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << ")"; +} + +// Make an allocation expression. + +Expression* +Expression::make_allocation(Type* type, Location location) +{ + return new Allocation_expression(type, location); +} + +// Construct a struct. + +class Struct_construction_expression : public Expression +{ + public: + Struct_construction_expression(Type* type, Expression_list* vals, + Location location) + : Expression(EXPRESSION_STRUCT_CONSTRUCTION, location), + type_(type), vals_(vals), traverse_order_(NULL) + { } + + // Set the traversal order, used to ensure that we implement the + // order of evaluation rules. Takes ownership of the argument. + void + set_traverse_order(std::vector* traverse_order) + { this->traverse_order_ = traverse_order; } + + // Return whether this is a constant initializer. + bool + is_constant_struct() const; + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_is_immutable() const; + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + Struct_construction_expression* ret = + new Struct_construction_expression(this->type_, this->vals_->copy(), + this->location()); + if (this->traverse_order_ != NULL) + ret->set_traverse_order(this->traverse_order_); + return ret; + } + + tree + do_get_tree(Translate_context*); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type of the struct to construct. + Type* type_; + // The list of values, in order of the fields in the struct. A NULL + // entry means that the field should be zero-initialized. + Expression_list* vals_; + // If not NULL, the order in which to traverse vals_. This is used + // so that we implement the order of evaluation rules correctly. + std::vector* traverse_order_; +}; + +// Traversal. + +int +Struct_construction_expression::do_traverse(Traverse* traverse) +{ + if (this->vals_ != NULL) + { + if (this->traverse_order_ == NULL) + { + if (this->vals_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + else + { + for (std::vector::const_iterator p = + this->traverse_order_->begin(); + p != this->traverse_order_->end(); + ++p) + { + if (Expression::traverse(&this->vals_->at(*p), traverse) + == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + } + if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Return whether this is a constant initializer. + +bool +Struct_construction_expression::is_constant_struct() const +{ + if (this->vals_ == NULL) + return true; + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + if (*pv != NULL + && !(*pv)->is_constant() + && (!(*pv)->is_composite_literal() + || (*pv)->is_nonconstant_composite_literal())) + return false; + } + + const Struct_field_list* fields = this->type_->struct_type()->fields(); + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + // There are no constant constructors for interfaces. + if (pf->type()->interface_type() != NULL) + return false; + } + + return true; +} + +// Return whether this struct is immutable. + +bool +Struct_construction_expression::do_is_immutable() const +{ + if (this->vals_ == NULL) + return true; + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + if (*pv != NULL && !(*pv)->is_immutable()) + return false; + } + return true; +} + +// Final type determination. + +void +Struct_construction_expression::do_determine_type(const Type_context*) +{ + if (this->vals_ == NULL) + return; + const Struct_field_list* fields = this->type_->struct_type()->fields(); + Expression_list::const_iterator pv = this->vals_->begin(); + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf, ++pv) + { + if (pv == this->vals_->end()) + return; + if (*pv != NULL) + { + Type_context subcontext(pf->type(), false); + (*pv)->determine_type(&subcontext); + } + } + // Extra values are an error we will report elsewhere; we still want + // to determine the type to avoid knockon errors. + for (; pv != this->vals_->end(); ++pv) + (*pv)->determine_type_no_context(); +} + +// Check types. + +void +Struct_construction_expression::do_check_types(Gogo*) +{ + if (this->vals_ == NULL) + return; + + Struct_type* st = this->type_->struct_type(); + if (this->vals_->size() > st->field_count()) + { + this->report_error(_("too many expressions for struct")); + return; + } + + const Struct_field_list* fields = st->fields(); + Expression_list::const_iterator pv = this->vals_->begin(); + int i = 0; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf, ++pv, ++i) + { + if (pv == this->vals_->end()) + { + this->report_error(_("too few expressions for struct")); + break; + } + + if (*pv == NULL) + continue; + + std::string reason; + if (!Type::are_assignable(pf->type(), (*pv)->type(), &reason)) + { + if (reason.empty()) + error_at((*pv)->location(), + "incompatible type for field %d in struct construction", + i + 1); + else + error_at((*pv)->location(), + ("incompatible type for field %d in " + "struct construction (%s)"), + i + 1, reason.c_str()); + this->set_is_error(); + } + } + go_assert(pv == this->vals_->end()); +} + +// Return a tree for constructing a struct. + +tree +Struct_construction_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + + if (this->vals_ == NULL) + { + Btype* btype = this->type_->get_backend(gogo); + return expr_to_tree(gogo->backend()->zero_expression(btype)); + } + + tree type_tree = type_to_tree(this->type_->get_backend(gogo)); + if (type_tree == error_mark_node) + return error_mark_node; + go_assert(TREE_CODE(type_tree) == RECORD_TYPE); + + bool is_constant = true; + const Struct_field_list* fields = this->type_->struct_type()->fields(); + vec *elts; + vec_alloc (elts, fields->size()); + Struct_field_list::const_iterator pf = fields->begin(); + Expression_list::const_iterator pv = this->vals_->begin(); + for (tree field = TYPE_FIELDS(type_tree); + field != NULL_TREE; + field = DECL_CHAIN(field), ++pf) + { + go_assert(pf != fields->end()); + + Btype* fbtype = pf->type()->get_backend(gogo); + + tree val; + if (pv == this->vals_->end()) + val = expr_to_tree(gogo->backend()->zero_expression(fbtype)); + else if (*pv == NULL) + { + val = expr_to_tree(gogo->backend()->zero_expression(fbtype)); + ++pv; + } + else + { + val = Expression::convert_for_assignment(context, pf->type(), + (*pv)->type(), + (*pv)->get_tree(context), + this->location()); + ++pv; + } + + if (val == error_mark_node || TREE_TYPE(val) == error_mark_node) + return error_mark_node; + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = elts->quick_push(empty); + elt->index = field; + elt->value = val; + if (!TREE_CONSTANT(val)) + is_constant = false; + } + go_assert(pf == fields->end()); + + tree ret = build_constructor(type_tree, elts); + if (is_constant) + TREE_CONSTANT(ret) = 1; + return ret; +} + +// Export a struct construction. + +void +Struct_construction_expression::do_export(Export* exp) const +{ + exp->write_c_string("convert("); + exp->write_type(this->type_); + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + exp->write_c_string(", "); + if (*pv != NULL) + (*pv)->export_expression(exp); + } + exp->write_c_string(")"); +} + +// Dump ast representation of a struct construction expression. + +void +Struct_construction_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << "{"; + ast_dump_context->dump_expression_list(this->vals_); + ast_dump_context->ostream() << "}"; +} + +// Make a struct composite literal. This used by the thunk code. + +Expression* +Expression::make_struct_composite_literal(Type* type, Expression_list* vals, + Location location) +{ + go_assert(type->struct_type() != NULL); + return new Struct_construction_expression(type, vals, location); +} + +// Construct an array. This class is not used directly; instead we +// use the child classes, Fixed_array_construction_expression and +// Open_array_construction_expression. + +class Array_construction_expression : public Expression +{ + protected: + Array_construction_expression(Expression_classification classification, + Type* type, + const std::vector* indexes, + Expression_list* vals, Location location) + : Expression(classification, location), + type_(type), indexes_(indexes), vals_(vals) + { go_assert(indexes == NULL || indexes->size() == vals->size()); } + + public: + // Return whether this is a constant initializer. + bool + is_constant_array() const; + + // Return the number of elements. + size_t + element_count() const + { return this->vals_ == NULL ? 0 : this->vals_->size(); } + +protected: + int + do_traverse(Traverse* traverse); + + bool + do_is_immutable() const; + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + void + do_export(Export*) const; + + // The indexes. + const std::vector* + indexes() + { return this->indexes_; } + + // The list of values. + Expression_list* + vals() + { return this->vals_; } + + // Get a constructor tree for the array values. + tree + get_constructor_tree(Translate_context* context, tree type_tree); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type of the array to construct. + Type* type_; + // The list of indexes into the array, one for each value. This may + // be NULL, in which case the indexes start at zero and increment. + const std::vector* indexes_; + // The list of values. This may be NULL if there are no values. + Expression_list* vals_; +}; + +// Traversal. + +int +Array_construction_expression::do_traverse(Traverse* traverse) +{ + if (this->vals_ != NULL + && this->vals_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Return whether this is a constant initializer. + +bool +Array_construction_expression::is_constant_array() const +{ + if (this->vals_ == NULL) + return true; + + // There are no constant constructors for interfaces. + if (this->type_->array_type()->element_type()->interface_type() != NULL) + return false; + + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + if (*pv != NULL + && !(*pv)->is_constant() + && (!(*pv)->is_composite_literal() + || (*pv)->is_nonconstant_composite_literal())) + return false; + } + return true; +} + +// Return whether this is an immutable array initializer. + +bool +Array_construction_expression::do_is_immutable() const +{ + if (this->vals_ == NULL) + return true; + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + if (*pv != NULL && !(*pv)->is_immutable()) + return false; + } + return true; +} + +// Final type determination. + +void +Array_construction_expression::do_determine_type(const Type_context*) +{ + if (this->vals_ == NULL) + return; + Type_context subcontext(this->type_->array_type()->element_type(), false); + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + if (*pv != NULL) + (*pv)->determine_type(&subcontext); + } +} + +// Check types. + +void +Array_construction_expression::do_check_types(Gogo*) +{ + if (this->vals_ == NULL) + return; + + Array_type* at = this->type_->array_type(); + int i = 0; + Type* element_type = at->element_type(); + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv, ++i) + { + if (*pv != NULL + && !Type::are_assignable(element_type, (*pv)->type(), NULL)) + { + error_at((*pv)->location(), + "incompatible type for element %d in composite literal", + i + 1); + this->set_is_error(); + } + } +} + +// Get a constructor tree for the array values. + +tree +Array_construction_expression::get_constructor_tree(Translate_context* context, + tree type_tree) +{ + vec *values; + vec_alloc (values, (this->vals_ == NULL ? 0 : this->vals_->size())); + Type* element_type = this->type_->array_type()->element_type(); + bool is_constant = true; + if (this->vals_ != NULL) + { + size_t i = 0; + std::vector::const_iterator pi; + if (this->indexes_ != NULL) + pi = this->indexes_->begin(); + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv, ++i) + { + if (this->indexes_ != NULL) + go_assert(pi != this->indexes_->end()); + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = values->quick_push(empty); + + if (this->indexes_ == NULL) + elt->index = size_int(i); + else + elt->index = size_int(*pi); + + if (*pv == NULL) + { + Gogo* gogo = context->gogo(); + Btype* ebtype = element_type->get_backend(gogo); + Bexpression *zv = gogo->backend()->zero_expression(ebtype); + elt->value = expr_to_tree(zv); + } + else + { + tree value_tree = (*pv)->get_tree(context); + elt->value = Expression::convert_for_assignment(context, + element_type, + (*pv)->type(), + value_tree, + this->location()); + } + if (elt->value == error_mark_node) + return error_mark_node; + if (!TREE_CONSTANT(elt->value)) + is_constant = false; + if (this->indexes_ != NULL) + ++pi; + } + if (this->indexes_ != NULL) + go_assert(pi == this->indexes_->end()); + } + + tree ret = build_constructor(type_tree, values); + if (is_constant) + TREE_CONSTANT(ret) = 1; + return ret; +} + +// Export an array construction. + +void +Array_construction_expression::do_export(Export* exp) const +{ + exp->write_c_string("convert("); + exp->write_type(this->type_); + if (this->vals_ != NULL) + { + std::vector::const_iterator pi; + if (this->indexes_ != NULL) + pi = this->indexes_->begin(); + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + exp->write_c_string(", "); + + if (this->indexes_ != NULL) + { + char buf[100]; + snprintf(buf, sizeof buf, "%lu", *pi); + exp->write_c_string(buf); + exp->write_c_string(":"); + } + + if (*pv != NULL) + (*pv)->export_expression(exp); + + if (this->indexes_ != NULL) + ++pi; + } + } + exp->write_c_string(")"); +} + +// Dump ast representation of an array construction expressin. + +void +Array_construction_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + Expression* length = this->type_->array_type()->length(); + + ast_dump_context->ostream() << "[" ; + if (length != NULL) + { + ast_dump_context->dump_expression(length); + } + ast_dump_context->ostream() << "]" ; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << "{" ; + if (this->indexes_ == NULL) + ast_dump_context->dump_expression_list(this->vals_); + else + { + Expression_list::const_iterator pv = this->vals_->begin(); + for (std::vector::const_iterator pi = + this->indexes_->begin(); + pi != this->indexes_->end(); + ++pi, ++pv) + { + if (pi != this->indexes_->begin()) + ast_dump_context->ostream() << ", "; + ast_dump_context->ostream() << *pi << ':'; + ast_dump_context->dump_expression(*pv); + } + } + ast_dump_context->ostream() << "}" ; + +} + +// Construct a fixed array. + +class Fixed_array_construction_expression : + public Array_construction_expression +{ + public: + Fixed_array_construction_expression(Type* type, + const std::vector* indexes, + Expression_list* vals, Location location) + : Array_construction_expression(EXPRESSION_FIXED_ARRAY_CONSTRUCTION, + type, indexes, vals, location) + { go_assert(type->array_type() != NULL && !type->is_slice_type()); } + + protected: + Expression* + do_copy() + { + return new Fixed_array_construction_expression(this->type(), + this->indexes(), + (this->vals() == NULL + ? NULL + : this->vals()->copy()), + this->location()); + } + + tree + do_get_tree(Translate_context*); +}; + +// Return a tree for constructing a fixed array. + +tree +Fixed_array_construction_expression::do_get_tree(Translate_context* context) +{ + Type* type = this->type(); + Btype* btype = type->get_backend(context->gogo()); + return this->get_constructor_tree(context, type_to_tree(btype)); +} + +// Construct an open array. + +class Open_array_construction_expression : public Array_construction_expression +{ + public: + Open_array_construction_expression(Type* type, + const std::vector* indexes, + Expression_list* vals, Location location) + : Array_construction_expression(EXPRESSION_OPEN_ARRAY_CONSTRUCTION, + type, indexes, vals, location) + { go_assert(type->is_slice_type()); } + + protected: + // Note that taking the address of an open array literal is invalid. + + Expression* + do_copy() + { + return new Open_array_construction_expression(this->type(), + this->indexes(), + (this->vals() == NULL + ? NULL + : this->vals()->copy()), + this->location()); + } + + tree + do_get_tree(Translate_context*); +}; + +// Return a tree for constructing an open array. + +tree +Open_array_construction_expression::do_get_tree(Translate_context* context) +{ + Array_type* array_type = this->type()->array_type(); + if (array_type == NULL) + { + go_assert(this->type()->is_error()); + return error_mark_node; + } + + Type* element_type = array_type->element_type(); + Btype* belement_type = element_type->get_backend(context->gogo()); + tree element_type_tree = type_to_tree(belement_type); + if (element_type_tree == error_mark_node) + return error_mark_node; + + tree values; + tree length_tree; + if (this->vals() == NULL || this->vals()->empty()) + { + // We need to create a unique value. + tree max = size_int(0); + tree constructor_type = build_array_type(element_type_tree, + build_index_type(max)); + if (constructor_type == error_mark_node) + return error_mark_node; + vec *vec; + vec_alloc(vec, 1); + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = vec->quick_push(empty); + elt->index = size_int(0); + Gogo* gogo = context->gogo(); + Btype* btype = element_type->get_backend(gogo); + elt->value = expr_to_tree(gogo->backend()->zero_expression(btype)); + values = build_constructor(constructor_type, vec); + if (TREE_CONSTANT(elt->value)) + TREE_CONSTANT(values) = 1; + length_tree = size_int(0); + } + else + { + unsigned long max_index; + if (this->indexes() == NULL) + max_index = this->vals()->size() - 1; + else + max_index = this->indexes()->back(); + tree max_tree = size_int(max_index); + tree constructor_type = build_array_type(element_type_tree, + build_index_type(max_tree)); + if (constructor_type == error_mark_node) + return error_mark_node; + values = this->get_constructor_tree(context, constructor_type); + length_tree = size_int(max_index + 1); + } + + if (values == error_mark_node) + return error_mark_node; + + bool is_constant_initializer = TREE_CONSTANT(values); + + // We have to copy the initial values into heap memory if we are in + // a function or if the values are not constants. We also have to + // copy them if they may contain pointers in a non-constant context, + // as otherwise the garbage collector won't see them. + bool copy_to_heap = (context->function() != NULL + || !is_constant_initializer + || (element_type->has_pointer() + && !context->is_const())); + + if (is_constant_initializer) + { + tree tmp = build_decl(this->location().gcc_location(), VAR_DECL, + create_tmp_var_name("C"), TREE_TYPE(values)); + DECL_EXTERNAL(tmp) = 0; + TREE_PUBLIC(tmp) = 0; + TREE_STATIC(tmp) = 1; + DECL_ARTIFICIAL(tmp) = 1; + if (copy_to_heap) + { + // If we are not copying the value to the heap, we will only + // initialize the value once, so we can use this directly + // rather than copying it. In that case we can't make it + // read-only, because the program is permitted to change it. + TREE_READONLY(tmp) = 1; + TREE_CONSTANT(tmp) = 1; + } + DECL_INITIAL(tmp) = values; + rest_of_decl_compilation(tmp, 1, 0); + values = tmp; + } + + tree space; + tree set; + if (!copy_to_heap) + { + // the initializer will only run once. + space = build_fold_addr_expr(values); + set = NULL_TREE; + } + else + { + tree memsize = TYPE_SIZE_UNIT(TREE_TYPE(values)); + space = context->gogo()->allocate_memory(element_type, memsize, + this->location()); + space = save_expr(space); + + tree s = fold_convert(build_pointer_type(TREE_TYPE(values)), space); + tree ref = build_fold_indirect_ref_loc(this->location().gcc_location(), + s); + TREE_THIS_NOTRAP(ref) = 1; + set = build2(MODIFY_EXPR, void_type_node, ref, values); + } + + // Build a constructor for the open array. + + tree type_tree = type_to_tree(this->type()->get_backend(context->gogo())); + if (type_tree == error_mark_node) + return error_mark_node; + go_assert(TREE_CODE(type_tree) == RECORD_TYPE); + + vec *init; + vec_alloc(init, 3); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + tree field = TYPE_FIELDS(type_tree); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), space); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), length_tree); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),"__capacity") == 0); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), length_tree); + + tree constructor = build_constructor(type_tree, init); + if (constructor == error_mark_node) + return error_mark_node; + if (!copy_to_heap) + TREE_CONSTANT(constructor) = 1; + + if (set == NULL_TREE) + return constructor; + else + return build2(COMPOUND_EXPR, type_tree, set, constructor); +} + +// Make a slice composite literal. This is used by the type +// descriptor code. + +Expression* +Expression::make_slice_composite_literal(Type* type, Expression_list* vals, + Location location) +{ + go_assert(type->is_slice_type()); + return new Open_array_construction_expression(type, NULL, vals, location); +} + +// Construct a map. + +class Map_construction_expression : public Expression +{ + public: + Map_construction_expression(Type* type, Expression_list* vals, + Location location) + : Expression(EXPRESSION_MAP_CONSTRUCTION, location), + type_(type), vals_(vals) + { go_assert(vals == NULL || vals->size() % 2 == 0); } + + protected: + int + do_traverse(Traverse* traverse); + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Map_construction_expression(this->type_, this->vals_->copy(), + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type of the map to construct. + Type* type_; + // The list of values. + Expression_list* vals_; +}; + +// Traversal. + +int +Map_construction_expression::do_traverse(Traverse* traverse) +{ + if (this->vals_ != NULL + && this->vals_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Final type determination. + +void +Map_construction_expression::do_determine_type(const Type_context*) +{ + if (this->vals_ == NULL) + return; + + Map_type* mt = this->type_->map_type(); + Type_context key_context(mt->key_type(), false); + Type_context val_context(mt->val_type(), false); + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + (*pv)->determine_type(&key_context); + ++pv; + (*pv)->determine_type(&val_context); + } +} + +// Check types. + +void +Map_construction_expression::do_check_types(Gogo*) +{ + if (this->vals_ == NULL) + return; + + Map_type* mt = this->type_->map_type(); + int i = 0; + Type* key_type = mt->key_type(); + Type* val_type = mt->val_type(); + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv, ++i) + { + if (!Type::are_assignable(key_type, (*pv)->type(), NULL)) + { + error_at((*pv)->location(), + "incompatible type for element %d key in map construction", + i + 1); + this->set_is_error(); + } + ++pv; + if (!Type::are_assignable(val_type, (*pv)->type(), NULL)) + { + error_at((*pv)->location(), + ("incompatible type for element %d value " + "in map construction"), + i + 1); + this->set_is_error(); + } + } +} + +// Return a tree for constructing a map. + +tree +Map_construction_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + Location loc = this->location(); + + Map_type* mt = this->type_->map_type(); + + // Build a struct to hold the key and value. + tree struct_type = make_node(RECORD_TYPE); + + Type* key_type = mt->key_type(); + tree id = get_identifier("__key"); + tree key_type_tree = type_to_tree(key_type->get_backend(gogo)); + if (key_type_tree == error_mark_node) + return error_mark_node; + tree key_field = build_decl(loc.gcc_location(), FIELD_DECL, id, + key_type_tree); + DECL_CONTEXT(key_field) = struct_type; + TYPE_FIELDS(struct_type) = key_field; + + Type* val_type = mt->val_type(); + id = get_identifier("__val"); + tree val_type_tree = type_to_tree(val_type->get_backend(gogo)); + if (val_type_tree == error_mark_node) + return error_mark_node; + tree val_field = build_decl(loc.gcc_location(), FIELD_DECL, id, + val_type_tree); + DECL_CONTEXT(val_field) = struct_type; + DECL_CHAIN(key_field) = val_field; + + layout_type(struct_type); + + bool is_constant = true; + size_t i = 0; + tree valaddr; + tree make_tmp; + + if (this->vals_ == NULL || this->vals_->empty()) + { + valaddr = null_pointer_node; + make_tmp = NULL_TREE; + } + else + { + vec *values; + vec_alloc(values, this->vals_->size() / 2); + + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv, ++i) + { + bool one_is_constant = true; + + vec *one; + vec_alloc(one, 2); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = one->quick_push(empty); + elt->index = key_field; + tree val_tree = (*pv)->get_tree(context); + elt->value = Expression::convert_for_assignment(context, key_type, + (*pv)->type(), + val_tree, loc); + if (elt->value == error_mark_node) + return error_mark_node; + if (!TREE_CONSTANT(elt->value)) + one_is_constant = false; + + ++pv; + + elt = one->quick_push(empty); + elt->index = val_field; + val_tree = (*pv)->get_tree(context); + elt->value = Expression::convert_for_assignment(context, val_type, + (*pv)->type(), + val_tree, loc); + if (elt->value == error_mark_node) + return error_mark_node; + if (!TREE_CONSTANT(elt->value)) + one_is_constant = false; + + elt = values->quick_push(empty); + elt->index = size_int(i); + elt->value = build_constructor(struct_type, one); + if (one_is_constant) + TREE_CONSTANT(elt->value) = 1; + else + is_constant = false; + } + + tree index_type = build_index_type(size_int(i - 1)); + tree array_type = build_array_type(struct_type, index_type); + tree init = build_constructor(array_type, values); + if (is_constant) + TREE_CONSTANT(init) = 1; + tree tmp; + if (current_function_decl != NULL) + { + tmp = create_tmp_var(array_type, get_name(array_type)); + DECL_INITIAL(tmp) = init; + make_tmp = fold_build1_loc(loc.gcc_location(), DECL_EXPR, + void_type_node, tmp); + TREE_ADDRESSABLE(tmp) = 1; + } + else + { + tmp = build_decl(loc.gcc_location(), VAR_DECL, + create_tmp_var_name("M"), array_type); + DECL_EXTERNAL(tmp) = 0; + TREE_PUBLIC(tmp) = 0; + TREE_STATIC(tmp) = 1; + DECL_ARTIFICIAL(tmp) = 1; + if (!TREE_CONSTANT(init)) + make_tmp = fold_build2_loc(loc.gcc_location(), INIT_EXPR, + void_type_node, tmp, init); + else + { + TREE_READONLY(tmp) = 1; + TREE_CONSTANT(tmp) = 1; + DECL_INITIAL(tmp) = init; + make_tmp = NULL_TREE; + } + rest_of_decl_compilation(tmp, 1, 0); + } + + valaddr = build_fold_addr_expr(tmp); + } + + Bexpression* bdescriptor = mt->map_descriptor_pointer(gogo, loc); + tree descriptor = expr_to_tree(bdescriptor); + + tree type_tree = type_to_tree(this->type_->get_backend(gogo)); + if (type_tree == error_mark_node) + return error_mark_node; + + static tree construct_map_fndecl; + tree call = Gogo::call_builtin(&construct_map_fndecl, + loc, + "__go_construct_map", + 6, + type_tree, + TREE_TYPE(descriptor), + descriptor, + sizetype, + size_int(i), + sizetype, + TYPE_SIZE_UNIT(struct_type), + sizetype, + byte_position(val_field), + sizetype, + 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) + ret = call; + else + ret = fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR, type_tree, + make_tmp, call); + return ret; +} + +// Export an array construction. + +void +Map_construction_expression::do_export(Export* exp) const +{ + exp->write_c_string("convert("); + exp->write_type(this->type_); + for (Expression_list::const_iterator pv = this->vals_->begin(); + pv != this->vals_->end(); + ++pv) + { + exp->write_c_string(", "); + (*pv)->export_expression(exp); + } + exp->write_c_string(")"); +} + +// Dump ast representation for a map construction expression. + +void +Map_construction_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "{" ; + ast_dump_context->dump_expression_list(this->vals_, true); + ast_dump_context->ostream() << "}"; +} + +// A general composite literal. This is lowered to a type specific +// version. + +class Composite_literal_expression : public Parser_expression +{ + public: + Composite_literal_expression(Type* type, int depth, bool has_keys, + Expression_list* vals, bool all_are_names, + Location location) + : Parser_expression(EXPRESSION_COMPOSITE_LITERAL, location), + type_(type), depth_(depth), vals_(vals), has_keys_(has_keys), + all_are_names_(all_are_names) + { } + + protected: + int + do_traverse(Traverse* traverse); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_copy() + { + return new Composite_literal_expression(this->type_, this->depth_, + this->has_keys_, + (this->vals_ == NULL + ? NULL + : this->vals_->copy()), + this->all_are_names_, + this->location()); + } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + Expression* + lower_struct(Gogo*, Type*); + + Expression* + lower_array(Type*); + + Expression* + make_array(Type*, const std::vector*, Expression_list*); + + Expression* + lower_map(Gogo*, Named_object*, Statement_inserter*, Type*); + + // The type of the composite literal. + Type* type_; + // The depth within a list of composite literals within a composite + // literal, when the type is omitted. + int depth_; + // The values to put in the composite literal. + Expression_list* vals_; + // If this is true, then VALS_ is a list of pairs: a key and a + // value. In an array initializer, a missing key will be NULL. + bool has_keys_; + // If this is true, then HAS_KEYS_ is true, and every key is a + // simple identifier. + bool all_are_names_; +}; + +// Traversal. + +int +Composite_literal_expression::do_traverse(Traverse* traverse) +{ + if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + + // If this is a struct composite literal with keys, then the keys + // are field names, not expressions. We don't want to traverse them + // in that case. If we do, we can give an erroneous error "variable + // initializer refers to itself." See bug482.go in the testsuite. + if (this->has_keys_ && this->vals_ != NULL) + { + // The type may not be resolvable at this point. + Type* type = this->type_; + + for (int depth = this->depth_; depth > 0; --depth) + { + if (type->array_type() != NULL) + type = type->array_type()->element_type(); + else if (type->map_type() != NULL) + type = type->map_type()->val_type(); + else + { + // This error will be reported during lowering. + return TRAVERSE_CONTINUE; + } + } + + while (true) + { + if (type->classification() == Type::TYPE_NAMED) + type = type->named_type()->real_type(); + else if (type->classification() == Type::TYPE_FORWARD) + { + Type* t = type->forwarded(); + if (t == type) + break; + type = t; + } + else + break; + } + + if (type->classification() == Type::TYPE_STRUCT) + { + Expression_list::iterator p = this->vals_->begin(); + while (p != this->vals_->end()) + { + // Skip key. + ++p; + go_assert(p != this->vals_->end()); + if (Expression::traverse(&*p, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + ++p; + } + return TRAVERSE_CONTINUE; + } + } + + if (this->vals_ != NULL) + return this->vals_->traverse(traverse); + + return TRAVERSE_CONTINUE; +} + +// Lower a generic composite literal into a specific version based on +// the type. + +Expression* +Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) +{ + Type* type = this->type_; + + for (int depth = this->depth_; depth > 0; --depth) + { + if (type->array_type() != NULL) + type = type->array_type()->element_type(); + else if (type->map_type() != NULL) + type = type->map_type()->val_type(); + else + { + if (!type->is_error()) + error_at(this->location(), + ("may only omit types within composite literals " + "of slice, array, or map type")); + return Expression::make_error(this->location()); + } + } + + Type *pt = type->points_to(); + bool is_pointer = false; + if (pt != NULL) + { + is_pointer = true; + type = pt; + } + + Expression* ret; + if (type->is_error()) + return Expression::make_error(this->location()); + else if (type->struct_type() != NULL) + ret = this->lower_struct(gogo, type); + else if (type->array_type() != NULL) + ret = this->lower_array(type); + else if (type->map_type() != NULL) + ret = this->lower_map(gogo, function, inserter, type); + else + { + error_at(this->location(), + ("expected struct, slice, array, or map type " + "for composite literal")); + return Expression::make_error(this->location()); + } + + if (is_pointer) + ret = Expression::make_heap_composite(ret, this->location()); + + return ret; +} + +// Lower a struct composite literal. + +Expression* +Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) +{ + Location location = this->location(); + Struct_type* st = type->struct_type(); + if (this->vals_ == NULL || !this->has_keys_) + { + if (this->vals_ != NULL + && !this->vals_->empty() + && type->named_type() != NULL + && type->named_type()->named_object()->package() != NULL) + { + for (Struct_field_list::const_iterator pf = st->fields()->begin(); + pf != st->fields()->end(); + ++pf) + { + if (Gogo::is_hidden_name(pf->field_name())) + error_at(this->location(), + "assignment of unexported field %qs in %qs literal", + Gogo::message_name(pf->field_name()).c_str(), + type->named_type()->message_name().c_str()); + } + } + + return new Struct_construction_expression(type, this->vals_, location); + } + + size_t field_count = st->field_count(); + std::vector vals(field_count); + std::vector* traverse_order = new(std::vector); + Expression_list::const_iterator p = this->vals_->begin(); + Expression* external_expr = NULL; + const Named_object* external_no = NULL; + while (p != this->vals_->end()) + { + Expression* name_expr = *p; + + ++p; + go_assert(p != this->vals_->end()); + Expression* val = *p; + + ++p; + + if (name_expr == NULL) + { + error_at(val->location(), "mixture of field and value initializers"); + return Expression::make_error(location); + } + + bool bad_key = false; + std::string name; + const Named_object* no = NULL; + switch (name_expr->classification()) + { + case EXPRESSION_UNKNOWN_REFERENCE: + name = name_expr->unknown_expression()->name(); + break; + + case EXPRESSION_CONST_REFERENCE: + no = static_cast(name_expr)->named_object(); + break; + + case EXPRESSION_TYPE: + { + Type* t = name_expr->type(); + Named_type* nt = t->named_type(); + if (nt == NULL) + bad_key = true; + else + no = nt->named_object(); + } + break; + + case EXPRESSION_VAR_REFERENCE: + no = name_expr->var_expression()->named_object(); + break; + + case EXPRESSION_FUNC_REFERENCE: + no = name_expr->func_expression()->named_object(); + break; + + case EXPRESSION_UNARY: + // If there is a local variable around with the same name as + // the field, and this occurs in the closure, then the + // parser may turn the field reference into an indirection + // through the closure. FIXME: This is a mess. + { + bad_key = true; + Unary_expression* ue = static_cast(name_expr); + if (ue->op() == OPERATOR_MULT) + { + Field_reference_expression* fre = + ue->operand()->field_reference_expression(); + if (fre != NULL) + { + Struct_type* st = + fre->expr()->type()->deref()->struct_type(); + if (st != NULL) + { + const Struct_field* sf = st->field(fre->field_index()); + name = sf->field_name(); + + // See below. FIXME. + if (!Gogo::is_hidden_name(name) + && name[0] >= 'a' + && name[0] <= 'z') + { + if (gogo->lookup_global(name.c_str()) != NULL) + name = gogo->pack_hidden_name(name, false); + } + + char buf[20]; + snprintf(buf, sizeof buf, "%u", fre->field_index()); + size_t buflen = strlen(buf); + if (name.compare(name.length() - buflen, buflen, buf) + == 0) + { + name = name.substr(0, name.length() - buflen); + bad_key = false; + } + } + } + } + } + break; + + default: + bad_key = true; + break; + } + if (bad_key) + { + error_at(name_expr->location(), "expected struct field name"); + return Expression::make_error(location); + } + + if (no != NULL) + { + if (no->package() != NULL && external_expr == NULL) + { + external_expr = name_expr; + external_no = no; + } + + name = no->name(); + + // A predefined name won't be packed. If it starts with a + // lower case letter we need to check for that case, because + // the field name will be packed. FIXME. + if (!Gogo::is_hidden_name(name) + && name[0] >= 'a' + && name[0] <= 'z') + { + Named_object* gno = gogo->lookup_global(name.c_str()); + if (gno == no) + name = gogo->pack_hidden_name(name, false); + } + } + + unsigned int index; + const Struct_field* sf = st->find_local_field(name, &index); + if (sf == NULL) + { + error_at(name_expr->location(), "unknown field %qs in %qs", + Gogo::message_name(name).c_str(), + (type->named_type() != NULL + ? type->named_type()->message_name().c_str() + : "unnamed struct")); + return Expression::make_error(location); + } + if (vals[index] != NULL) + { + error_at(name_expr->location(), + "duplicate value for field %qs in %qs", + Gogo::message_name(name).c_str(), + (type->named_type() != NULL + ? type->named_type()->message_name().c_str() + : "unnamed struct")); + return Expression::make_error(location); + } + + if (type->named_type() != NULL + && type->named_type()->named_object()->package() != NULL + && Gogo::is_hidden_name(sf->field_name())) + error_at(name_expr->location(), + "assignment of unexported field %qs in %qs literal", + Gogo::message_name(sf->field_name()).c_str(), + type->named_type()->message_name().c_str()); + + vals[index] = val; + traverse_order->push_back(index); + } + + if (!this->all_are_names_) + { + // This is a weird case like bug462 in the testsuite. + if (external_expr == NULL) + error_at(this->location(), "unknown field in %qs literal", + (type->named_type() != NULL + ? type->named_type()->message_name().c_str() + : "unnamed struct")); + else + error_at(external_expr->location(), "unknown field %qs in %qs", + external_no->message_name().c_str(), + (type->named_type() != NULL + ? type->named_type()->message_name().c_str() + : "unnamed struct")); + return Expression::make_error(location); + } + + Expression_list* list = new Expression_list; + list->reserve(field_count); + for (size_t i = 0; i < field_count; ++i) + list->push_back(vals[i]); + + Struct_construction_expression* ret = + new Struct_construction_expression(type, list, location); + ret->set_traverse_order(traverse_order); + return ret; +} + +// Used to sort an index/value array. + +class Index_value_compare +{ + public: + bool + operator()(const std::pair& a, + const std::pair& b) + { return a.first < b.first; } +}; + +// Lower an array composite literal. + +Expression* +Composite_literal_expression::lower_array(Type* type) +{ + Location location = this->location(); + if (this->vals_ == NULL || !this->has_keys_) + return this->make_array(type, NULL, this->vals_); + + std::vector* indexes = new std::vector; + indexes->reserve(this->vals_->size()); + bool indexes_out_of_order = false; + Expression_list* vals = new Expression_list(); + vals->reserve(this->vals_->size()); + unsigned long index = 0; + Expression_list::const_iterator p = this->vals_->begin(); + while (p != this->vals_->end()) + { + Expression* index_expr = *p; + + ++p; + go_assert(p != this->vals_->end()); + Expression* val = *p; + + ++p; + + if (index_expr == NULL) + { + if (!indexes->empty()) + indexes->push_back(index); + } + else + { + if (indexes->empty() && !vals->empty()) + { + for (size_t i = 0; i < vals->size(); ++i) + indexes->push_back(i); + } + + Numeric_constant nc; + if (!index_expr->numeric_constant_value(&nc)) + { + error_at(index_expr->location(), + "index expression is not integer constant"); + return Expression::make_error(location); + } + + switch (nc.to_unsigned_long(&index)) + { + case Numeric_constant::NC_UL_VALID: + break; + case Numeric_constant::NC_UL_NOTINT: + error_at(index_expr->location(), + "index expression is not integer constant"); + return Expression::make_error(location); + case Numeric_constant::NC_UL_NEGATIVE: + error_at(index_expr->location(), "index expression is negative"); + return Expression::make_error(location); + case Numeric_constant::NC_UL_BIG: + error_at(index_expr->location(), "index value overflow"); + return Expression::make_error(location); + default: + go_unreachable(); + } + + Named_type* ntype = Type::lookup_integer_type("int"); + Integer_type* inttype = ntype->integer_type(); + if (sizeof(index) <= static_cast(inttype->bits() * 8) + && index >> (inttype->bits() - 1) != 0) + { + error_at(index_expr->location(), "index value overflow"); + return Expression::make_error(location); + } + + if (std::find(indexes->begin(), indexes->end(), index) + != indexes->end()) + { + error_at(index_expr->location(), "duplicate value for index %lu", + index); + return Expression::make_error(location); + } + + if (!indexes->empty() && index < indexes->back()) + indexes_out_of_order = true; + + indexes->push_back(index); + } + + vals->push_back(val); + + ++index; + } + + if (indexes->empty()) + { + delete indexes; + indexes = NULL; + } + + if (indexes_out_of_order) + { + typedef std::vector > V; + + V v; + v.reserve(indexes->size()); + std::vector::const_iterator pi = indexes->begin(); + for (Expression_list::const_iterator pe = vals->begin(); + pe != vals->end(); + ++pe, ++pi) + v.push_back(std::make_pair(*pi, *pe)); + + std::sort(v.begin(), v.end(), Index_value_compare()); + + delete indexes; + delete vals; + indexes = new std::vector(); + indexes->reserve(v.size()); + vals = new Expression_list(); + vals->reserve(v.size()); + + for (V::const_iterator p = v.begin(); p != v.end(); ++p) + { + indexes->push_back(p->first); + vals->push_back(p->second); + } + } + + return this->make_array(type, indexes, vals); +} + +// Actually build the array composite literal. This handles +// [...]{...}. + +Expression* +Composite_literal_expression::make_array( + Type* type, + const std::vector* indexes, + Expression_list* vals) +{ + Location location = this->location(); + Array_type* at = type->array_type(); + + if (at->length() != NULL && at->length()->is_nil_expression()) + { + size_t size; + if (vals == NULL) + size = 0; + else if (indexes != NULL) + size = indexes->back() + 1; + else + { + size = vals->size(); + Integer_type* it = Type::lookup_integer_type("int")->integer_type(); + if (sizeof(size) <= static_cast(it->bits() * 8) + && size >> (it->bits() - 1) != 0) + { + error_at(location, "too many elements in composite literal"); + return Expression::make_error(location); + } + } + + mpz_t vlen; + mpz_init_set_ui(vlen, size); + Expression* elen = Expression::make_integer(&vlen, NULL, location); + mpz_clear(vlen); + at = Type::make_array_type(at->element_type(), elen); + type = at; + } + else if (at->length() != NULL + && !at->length()->is_error_expression() + && this->vals_ != NULL) + { + Numeric_constant nc; + unsigned long val; + if (at->length()->numeric_constant_value(&nc) + && nc.to_unsigned_long(&val) == Numeric_constant::NC_UL_VALID) + { + if (indexes == NULL) + { + if (this->vals_->size() > val) + { + error_at(location, "too many elements in composite literal"); + return Expression::make_error(location); + } + } + else + { + unsigned long max = indexes->back(); + if (max >= val) + { + error_at(location, + ("some element keys in composite literal " + "are out of range")); + return Expression::make_error(location); + } + } + } + } + + if (at->length() != NULL) + return new Fixed_array_construction_expression(type, indexes, vals, + location); + else + return new Open_array_construction_expression(type, indexes, vals, + location); +} + +// Lower a map composite literal. + +Expression* +Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, + Type* type) +{ + Location location = this->location(); + if (this->vals_ != NULL) + { + if (!this->has_keys_) + { + error_at(location, "map composite literal must have keys"); + return Expression::make_error(location); + } + + for (Expression_list::iterator p = this->vals_->begin(); + p != this->vals_->end(); + p += 2) + { + if (*p == NULL) + { + ++p; + error_at((*p)->location(), + "map composite literal must have keys for every value"); + return Expression::make_error(location); + } + // Make sure we have lowered the key; it may not have been + // lowered in order to handle keys for struct composite + // literals. Lower it now to get the right error message. + if ((*p)->unknown_expression() != NULL) + { + (*p)->unknown_expression()->clear_is_composite_literal_key(); + gogo->lower_expression(function, inserter, &*p); + go_assert((*p)->is_error_expression()); + return Expression::make_error(location); + } + } + } + + return new Map_construction_expression(type, this->vals_, location); +} + +// Dump ast representation for a composite literal expression. + +void +Composite_literal_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "composite("; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << ", {"; + ast_dump_context->dump_expression_list(this->vals_, this->has_keys_); + ast_dump_context->ostream() << "})"; +} + +// Make a composite literal expression. + +Expression* +Expression::make_composite_literal(Type* type, int depth, bool has_keys, + Expression_list* vals, bool all_are_names, + Location location) +{ + return new Composite_literal_expression(type, depth, has_keys, vals, + all_are_names, location); +} + +// Return whether this expression is a composite literal. + +bool +Expression::is_composite_literal() const +{ + switch (this->classification_) + { + case EXPRESSION_COMPOSITE_LITERAL: + case EXPRESSION_STRUCT_CONSTRUCTION: + case EXPRESSION_FIXED_ARRAY_CONSTRUCTION: + case EXPRESSION_OPEN_ARRAY_CONSTRUCTION: + case EXPRESSION_MAP_CONSTRUCTION: + return true; + default: + return false; + } +} + +// Return whether this expression is a composite literal which is not +// constant. + +bool +Expression::is_nonconstant_composite_literal() const +{ + switch (this->classification_) + { + case EXPRESSION_STRUCT_CONSTRUCTION: + { + const Struct_construction_expression *psce = + static_cast(this); + return !psce->is_constant_struct(); + } + case EXPRESSION_FIXED_ARRAY_CONSTRUCTION: + { + const Fixed_array_construction_expression *pace = + static_cast(this); + return !pace->is_constant_array(); + } + case EXPRESSION_OPEN_ARRAY_CONSTRUCTION: + { + const Open_array_construction_expression *pace = + static_cast(this); + return !pace->is_constant_array(); + } + case EXPRESSION_MAP_CONSTRUCTION: + return true; + default: + return false; + } +} + +// Return true if this is a variable or temporary_variable. + +bool +Expression::is_variable() const +{ + switch (this->classification_) + { + case EXPRESSION_VAR_REFERENCE: + case EXPRESSION_TEMPORARY_REFERENCE: + case EXPRESSION_SET_AND_USE_TEMPORARY: + return true; + default: + return false; + } +} + +// Return true if this is a reference to a local variable. + +bool +Expression::is_local_variable() const +{ + const Var_expression* ve = this->var_expression(); + if (ve == NULL) + return false; + const Named_object* no = ve->named_object(); + return (no->is_result_variable() + || (no->is_variable() && !no->var_value()->is_global())); +} + +// Class Type_guard_expression. + +// Traversal. + +int +Type_guard_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT + || Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Check types of a type guard expression. The expression must have +// an interface type, but the actual type conversion is checked at run +// time. + +void +Type_guard_expression::do_check_types(Gogo*) +{ + Type* expr_type = this->expr_->type(); + if (expr_type->interface_type() == NULL) + { + if (!expr_type->is_error() && !this->type_->is_error()) + this->report_error(_("type assertion only valid for interface types")); + this->set_is_error(); + } + else if (this->type_->interface_type() == NULL) + { + std::string reason; + if (!expr_type->interface_type()->implements_interface(this->type_, + &reason)) + { + if (!this->type_->is_error()) + { + if (reason.empty()) + this->report_error(_("impossible type assertion: " + "type does not implement interface")); + else + error_at(this->location(), + ("impossible type assertion: " + "type does not implement interface (%s)"), + reason.c_str()); + } + this->set_is_error(); + } + } +} + +// Return a tree for a type guard expression. + +tree +Type_guard_expression::do_get_tree(Translate_context* context) +{ + tree expr_tree = this->expr_->get_tree(context); + if (expr_tree == error_mark_node) + return error_mark_node; + if (this->type_->interface_type() != NULL) + return Expression::convert_interface_to_interface(context, this->type_, + this->expr_->type(), + expr_tree, true, + this->location()); + else + return Expression::convert_for_assignment(context, this->type_, + this->expr_->type(), expr_tree, + this->location()); +} + +// Dump ast representation for a type guard expression. + +void +Type_guard_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + this->expr_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "."; + ast_dump_context->dump_type(this->type_); +} + +// Make a type guard expression. + +Expression* +Expression::make_type_guard(Expression* expr, Type* type, + Location location) +{ + return new Type_guard_expression(expr, type, location); +} + +// Class Heap_composite_expression. + +// When you take the address of a composite literal, it is allocated +// on the heap. This class implements that. + +class Heap_composite_expression : public Expression +{ + public: + Heap_composite_expression(Expression* expr, Location location) + : Expression(EXPRESSION_HEAP_COMPOSITE, location), + expr_(expr) + { } + + protected: + int + do_traverse(Traverse* traverse) + { return Expression::traverse(&this->expr_, traverse); } + + Type* + do_type() + { return Type::make_pointer_type(this->expr_->type()); } + + void + do_determine_type(const Type_context*) + { this->expr_->determine_type_no_context(); } + + Expression* + do_copy() + { + return Expression::make_heap_composite(this->expr_->copy(), + this->location()); + } + + tree + do_get_tree(Translate_context*); + + // We only export global objects, and the parser does not generate + // this in global scope. + void + do_export(Export*) const + { go_unreachable(); } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The composite literal which is being put on the heap. + Expression* expr_; +}; + +// Return a tree which allocates a composite literal on the heap. + +tree +Heap_composite_expression::do_get_tree(Translate_context* context) +{ + tree expr_tree = this->expr_->get_tree(context); + if (expr_tree == error_mark_node || TREE_TYPE(expr_tree) == error_mark_node) + return error_mark_node; + tree expr_size = TYPE_SIZE_UNIT(TREE_TYPE(expr_tree)); + go_assert(TREE_CODE(expr_size) == INTEGER_CST); + tree space = context->gogo()->allocate_memory(this->expr_->type(), + expr_size, this->location()); + space = fold_convert(build_pointer_type(TREE_TYPE(expr_tree)), space); + space = save_expr(space); + tree ref = build_fold_indirect_ref_loc(this->location().gcc_location(), + space); + TREE_THIS_NOTRAP(ref) = 1; + tree ret = build2(COMPOUND_EXPR, TREE_TYPE(space), + build2(MODIFY_EXPR, void_type_node, ref, expr_tree), + space); + SET_EXPR_LOCATION(ret, this->location().gcc_location()); + return ret; +} + +// Dump ast representation for a heap composite expression. + +void +Heap_composite_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "&("; + ast_dump_context->dump_expression(this->expr_); + ast_dump_context->ostream() << ")"; +} + +// Allocate a composite literal on the heap. + +Expression* +Expression::make_heap_composite(Expression* expr, Location location) +{ + return new Heap_composite_expression(expr, location); +} + +// Class Receive_expression. + +// Return the type of a receive expression. + +Type* +Receive_expression::do_type() +{ + Channel_type* channel_type = this->channel_->type()->channel_type(); + if (channel_type == NULL) + return Type::make_error_type(); + return channel_type->element_type(); +} + +// Check types for a receive expression. + +void +Receive_expression::do_check_types(Gogo*) +{ + Type* type = this->channel_->type(); + if (type->is_error()) + { + this->set_is_error(); + return; + } + if (type->channel_type() == NULL) + { + this->report_error(_("expected channel")); + return; + } + if (!type->channel_type()->may_receive()) + { + this->report_error(_("invalid receive on send-only channel")); + return; + } +} + +// Get a tree for a receive expression. + +tree +Receive_expression::do_get_tree(Translate_context* context) +{ + Location loc = this->location(); + + Channel_type* channel_type = this->channel_->type()->channel_type(); + if (channel_type == NULL) + { + go_assert(this->channel_->type()->is_error()); + return error_mark_node; + } + + Expression* td = Expression::make_type_descriptor(channel_type, loc); + tree td_tree = td->get_tree(context); + + Type* element_type = channel_type->element_type(); + Btype* element_type_btype = element_type->get_backend(context->gogo()); + tree element_type_tree = type_to_tree(element_type_btype); + + tree channel = this->channel_->get_tree(context); + if (element_type_tree == error_mark_node || channel == error_mark_node) + return error_mark_node; + + return Gogo::receive_from_channel(element_type_tree, td_tree, channel, loc); +} + +// Dump ast representation for a receive expression. + +void +Receive_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << " <- " ; + ast_dump_context->dump_expression(channel_); +} + +// Make a receive expression. + +Receive_expression* +Expression::make_receive(Expression* channel, Location location) +{ + return new Receive_expression(channel, location); +} + +// An expression which evaluates to a pointer to the type descriptor +// of a type. + +class Type_descriptor_expression : public Expression +{ + public: + Type_descriptor_expression(Type* type, Location location) + : Expression(EXPRESSION_TYPE_DESCRIPTOR, location), + type_(type) + { } + + protected: + Type* + do_type() + { return Type::make_type_descriptor_ptr_type(); } + + bool + do_is_immutable() const + { return true; } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context* context) + { + Bexpression* ret = this->type_->type_descriptor_pointer(context->gogo(), + this->location()); + return expr_to_tree(ret); + } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type for which this is the descriptor. + Type* type_; +}; + +// Dump ast representation for a type descriptor expression. + +void +Type_descriptor_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->dump_type(this->type_); +} + +// Make a type descriptor expression. + +Expression* +Expression::make_type_descriptor(Type* type, Location location) +{ + return new Type_descriptor_expression(type, location); +} + +// An expression which evaluates to some characteristic of a type. +// This is only used to initialize fields of a type descriptor. Using +// a new expression class is slightly inefficient but gives us a good +// separation between the frontend and the middle-end with regard to +// how types are laid out. + +class Type_info_expression : public Expression +{ + public: + Type_info_expression(Type* type, Type_info type_info) + : Expression(EXPRESSION_TYPE_INFO, Linemap::predeclared_location()), + type_(type), type_info_(type_info) + { } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type for which we are getting information. + Type* type_; + // What information we want. + Type_info type_info_; +}; + +// The type is chosen to match what the type descriptor struct +// expects. + +Type* +Type_info_expression::do_type() +{ + switch (this->type_info_) + { + case TYPE_INFO_SIZE: + return Type::lookup_integer_type("uintptr"); + case TYPE_INFO_ALIGNMENT: + case TYPE_INFO_FIELD_ALIGNMENT: + return Type::lookup_integer_type("uint8"); + default: + go_unreachable(); + } +} + +// Return type information in GENERIC. + +tree +Type_info_expression::do_get_tree(Translate_context* context) +{ + Btype* btype = this->type_->get_backend(context->gogo()); + Gogo* gogo = context->gogo(); + size_t val; + switch (this->type_info_) + { + case TYPE_INFO_SIZE: + val = gogo->backend()->type_size(btype); + break; + case TYPE_INFO_ALIGNMENT: + val = gogo->backend()->type_alignment(btype); + break; + case TYPE_INFO_FIELD_ALIGNMENT: + val = gogo->backend()->type_field_alignment(btype); + break; + default: + go_unreachable(); + } + tree val_type_tree = type_to_tree(this->type()->get_backend(gogo)); + go_assert(val_type_tree != error_mark_node); + return build_int_cstu(val_type_tree, val); +} + +// Dump ast representation for a type info expression. + +void +Type_info_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "typeinfo("; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << ","; + ast_dump_context->ostream() << + (this->type_info_ == TYPE_INFO_ALIGNMENT ? "alignment" + : this->type_info_ == TYPE_INFO_FIELD_ALIGNMENT ? "field alignment" + : this->type_info_ == TYPE_INFO_SIZE ? "size " + : "unknown"); + ast_dump_context->ostream() << ")"; +} + +// Make a type info expression. + +Expression* +Expression::make_type_info(Type* type, Type_info type_info) +{ + return new Type_info_expression(type, type_info); +} + +// An expression that evaluates to some characteristic of a slice. +// This is used when indexing, bound-checking, or nil checking a slice. + +class Slice_info_expression : public Expression +{ + public: + Slice_info_expression(Expression* slice, Slice_info slice_info, + Location location) + : Expression(EXPRESSION_SLICE_INFO, location), + slice_(slice), slice_info_(slice_info) + { } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Slice_info_expression(this->slice_->copy(), this->slice_info_, + this->location()); + } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->slice_->issue_nil_check(); } + + private: + // The slice for which we are getting information. + Expression* slice_; + // What information we want. + Slice_info slice_info_; +}; + +// Return the type of the slice info. + +Type* +Slice_info_expression::do_type() +{ + switch (this->slice_info_) + { + case SLICE_INFO_VALUE_POINTER: + return Type::make_pointer_type( + this->slice_->type()->array_type()->element_type()); + case SLICE_INFO_LENGTH: + case SLICE_INFO_CAPACITY: + return Type::lookup_integer_type("int"); + default: + go_unreachable(); + } +} + +// Return slice information in GENERIC. + +tree +Slice_info_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + + Bexpression* bslice = tree_to_expr(this->slice_->get_tree(context)); + Bexpression* ret; + switch (this->slice_info_) + { + case SLICE_INFO_VALUE_POINTER: + case SLICE_INFO_LENGTH: + case SLICE_INFO_CAPACITY: + ret = gogo->backend()->struct_field_expression(bslice, this->slice_info_, + this->location()); + break; + default: + go_unreachable(); + } + return expr_to_tree(ret); +} + +// Dump ast representation for a type info expression. + +void +Slice_info_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "sliceinfo("; + this->slice_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << ","; + ast_dump_context->ostream() << + (this->slice_info_ == SLICE_INFO_VALUE_POINTER ? "values" + : this->slice_info_ == SLICE_INFO_LENGTH ? "length" + : this->slice_info_ == SLICE_INFO_CAPACITY ? "capacity " + : "unknown"); + ast_dump_context->ostream() << ")"; +} + +// Make a slice info expression. + +Expression* +Expression::make_slice_info(Expression* slice, Slice_info slice_info, + Location location) +{ + return new Slice_info_expression(slice, slice_info, location); +} + + +// An expression that evaluates to some characteristic of a non-empty interface. +// This is used to access the method table or underlying object of an interface. + +class Interface_info_expression : public Expression +{ + public: + Interface_info_expression(Expression* iface, Interface_info iface_info, + Location location) + : Expression(EXPRESSION_INTERFACE_INFO, location), + iface_(iface), iface_info_(iface_info) + { } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Interface_info_expression(this->iface_->copy(), + this->iface_info_, this->location()); + } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->iface_->issue_nil_check(); } + + private: + // The interface for which we are getting information. + Expression* iface_; + // What information we want. + Interface_info iface_info_; +}; + +// Return the type of the interface info. + +Type* +Interface_info_expression::do_type() +{ + switch (this->iface_info_) + { + case INTERFACE_INFO_METHODS: + { + Location loc = this->location(); + Struct_field_list* sfl = new Struct_field_list(); + Type* pdt = Type::make_type_descriptor_ptr_type(); + sfl->push_back( + Struct_field(Typed_identifier("__type_descriptor", pdt, loc))); + + Interface_type* itype = this->iface_->type()->interface_type(); + for (Typed_identifier_list::const_iterator p = itype->methods()->begin(); + p != itype->methods()->end(); + ++p) + { + Function_type* ft = p->type()->function_type(); + go_assert(ft->receiver() == NULL); + + const Typed_identifier_list* params = ft->parameters(); + Typed_identifier_list* mparams = new Typed_identifier_list(); + if (params != NULL) + mparams->reserve(params->size() + 1); + Type* vt = Type::make_pointer_type(Type::make_void_type()); + mparams->push_back(Typed_identifier("", vt, ft->location())); + if (params != NULL) + { + for (Typed_identifier_list::const_iterator pp = params->begin(); + pp != params->end(); + ++pp) + mparams->push_back(*pp); + } + + Typed_identifier_list* mresults = (ft->results() == NULL + ? NULL + : ft->results()->copy()); + Backend_function_type* mft = + Type::make_backend_function_type(NULL, mparams, mresults, + ft->location()); + + std::string fname = Gogo::unpack_hidden_name(p->name()); + sfl->push_back(Struct_field(Typed_identifier(fname, mft, loc))); + } + + return Type::make_pointer_type(Type::make_struct_type(sfl, loc)); + } + case INTERFACE_INFO_OBJECT: + return Type::make_pointer_type(Type::make_void_type()); + default: + go_unreachable(); + } +} + +// Return interface information in GENERIC. + +tree +Interface_info_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + + Bexpression* biface = tree_to_expr(this->iface_->get_tree(context)); + Bexpression* ret; + switch (this->iface_info_) + { + case INTERFACE_INFO_METHODS: + case INTERFACE_INFO_OBJECT: + ret = gogo->backend()->struct_field_expression(biface, this->iface_info_, + this->location()); + break; + default: + go_unreachable(); + } + return expr_to_tree(ret); +} + +// Dump ast representation for an interface info expression. + +void +Interface_info_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "interfaceinfo("; + this->iface_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << ","; + ast_dump_context->ostream() << + (this->iface_info_ == INTERFACE_INFO_METHODS ? "methods" + : this->iface_info_ == INTERFACE_INFO_OBJECT ? "object" + : "unknown"); + ast_dump_context->ostream() << ")"; +} + +// Make an interface info expression. + +Expression* +Expression::make_interface_info(Expression* iface, Interface_info iface_info, + Location location) +{ + return new Interface_info_expression(iface, iface_info, location); +} + +// An expression which evaluates to the offset of a field within a +// struct. This, like Type_info_expression, q.v., is only used to +// initialize fields of a type descriptor. + +class Struct_field_offset_expression : public Expression +{ + public: + Struct_field_offset_expression(Struct_type* type, const Struct_field* field) + : Expression(EXPRESSION_STRUCT_FIELD_OFFSET, + Linemap::predeclared_location()), + type_(type), field_(field) + { } + + protected: + Type* + do_type() + { return Type::lookup_integer_type("uintptr"); } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type of the struct. + Struct_type* type_; + // The field. + const Struct_field* field_; +}; + +// Return a struct field offset in GENERIC. + +tree +Struct_field_offset_expression::do_get_tree(Translate_context* context) +{ + tree type_tree = type_to_tree(this->type_->get_backend(context->gogo())); + if (type_tree == error_mark_node) + return error_mark_node; + + tree val_type_tree = type_to_tree(this->type()->get_backend(context->gogo())); + go_assert(val_type_tree != error_mark_node); + + const Struct_field_list* fields = this->type_->fields(); + tree struct_field_tree = TYPE_FIELDS(type_tree); + Struct_field_list::const_iterator p; + for (p = fields->begin(); + p != fields->end(); + ++p, struct_field_tree = DECL_CHAIN(struct_field_tree)) + { + go_assert(struct_field_tree != NULL_TREE); + if (&*p == this->field_) + break; + } + go_assert(&*p == this->field_); + + return fold_convert_loc(BUILTINS_LOCATION, val_type_tree, + byte_position(struct_field_tree)); +} + +// Dump ast representation for a struct field offset expression. + +void +Struct_field_offset_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "unsafe.Offsetof("; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << '.'; + ast_dump_context->ostream() << + Gogo::message_name(this->field_->field_name()); + ast_dump_context->ostream() << ")"; +} + +// Make an expression for a struct field offset. + +Expression* +Expression::make_struct_field_offset(Struct_type* type, + const Struct_field* field) +{ + return new Struct_field_offset_expression(type, field); +} + +// An expression which evaluates to a pointer to the map descriptor of +// a map type. + +class Map_descriptor_expression : public Expression +{ + public: + Map_descriptor_expression(Map_type* type, Location location) + : Expression(EXPRESSION_MAP_DESCRIPTOR, location), + type_(type) + { } + + protected: + Type* + do_type() + { return Type::make_pointer_type(Map_type::make_map_descriptor_type()); } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context* context) + { + Bexpression* ret = this->type_->map_descriptor_pointer(context->gogo(), + this->location()); + return expr_to_tree(ret); + } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type for which this is the descriptor. + Map_type* type_; +}; + +// Dump ast representation for a map descriptor expression. + +void +Map_descriptor_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "map_descriptor("; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << ")"; +} + +// Make a map descriptor expression. + +Expression* +Expression::make_map_descriptor(Map_type* type, Location location) +{ + return new Map_descriptor_expression(type, location); +} + +// An expression which evaluates to the address of an unnamed label. + +class Label_addr_expression : public Expression +{ + public: + Label_addr_expression(Label* label, Location location) + : Expression(EXPRESSION_LABEL_ADDR, location), + label_(label) + { } + + protected: + Type* + do_type() + { return Type::make_pointer_type(Type::make_void_type()); } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return new Label_addr_expression(this->label_, this->location()); } + + tree + do_get_tree(Translate_context* context) + { + return expr_to_tree(this->label_->get_addr(context, this->location())); + } + + void + do_dump_expression(Ast_dump_context* ast_dump_context) const + { ast_dump_context->ostream() << this->label_->name(); } + + private: + // The label whose address we are taking. + Label* label_; +}; + +// Make an expression for the address of an unnamed label. + +Expression* +Expression::make_label_addr(Label* label, Location location) +{ + return new Label_addr_expression(label, location); +} + +// Conditional expressions. + +class Conditional_expression : public Expression +{ + public: + Conditional_expression(Expression* cond, Expression* then_expr, + Expression* else_expr, Location location) + : Expression(EXPRESSION_CONDITIONAL, location), + cond_(cond), then_(then_expr), else_(else_expr) + {} + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Conditional_expression(this->cond_->copy(), this->then_->copy(), + this->else_->copy(), this->location()); + } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The condition to be checked. + Expression* cond_; + // The expression to execute if the condition is true. + Expression* then_; + // The expression to execute if the condition is false. + Expression* else_; +}; + +// Return the type of the conditional expression. + +Type* +Conditional_expression::do_type() +{ + Type* result_type = Type::make_void_type(); + if (this->then_->type() == this->else_->type()) + result_type = this->then_->type(); + else if (this->then_->is_nil_expression() + || this->else_->is_nil_expression()) + result_type = (!this->then_->is_nil_expression() + ? this->then_->type() + : this->else_->type()); + return result_type; +} + +// Get the backend representation of a conditional expression. + +tree +Conditional_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + Btype* result_btype = this->type()->get_backend(gogo); + Bexpression* cond = tree_to_expr(this->cond_->get_tree(context)); + Bexpression* then = tree_to_expr(this->then_->get_tree(context)); + Bexpression* belse = tree_to_expr(this->else_->get_tree(context)); + Bexpression* ret = + gogo->backend()->conditional_expression(result_btype, cond, then, belse, + this->location()); + return expr_to_tree(ret); +} + +// Dump ast representation of a conditional expression. + +void +Conditional_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "("; + ast_dump_context->dump_expression(this->cond_); + ast_dump_context->ostream() << " ? "; + ast_dump_context->dump_expression(this->then_); + ast_dump_context->ostream() << " : "; + ast_dump_context->dump_expression(this->else_); + ast_dump_context->ostream() << ") "; +} + +// Make a conditional expression. + +Expression* +Expression::make_conditional(Expression* cond, Expression* then, + Expression* else_expr, Location location) +{ + return new Conditional_expression(cond, then, else_expr, location); +} + +// Import an expression. This comes at the end in order to see the +// various class definitions. + +Expression* +Expression::import_expression(Import* imp) +{ + int c = imp->peek_char(); + if (imp->match_c_string("- ") + || imp->match_c_string("! ") + || imp->match_c_string("^ ")) + return Unary_expression::do_import(imp); + else if (c == '(') + return Binary_expression::do_import(imp); + else if (imp->match_c_string("true") + || imp->match_c_string("false")) + return Boolean_expression::do_import(imp); + else if (c == '"') + return String_expression::do_import(imp); + else if (c == '-' || (c >= '0' && c <= '9')) + { + // This handles integers, floats and complex constants. + return Integer_expression::do_import(imp); + } + else if (imp->match_c_string("nil")) + return Nil_expression::do_import(imp); + else if (imp->match_c_string("convert")) + return Type_conversion_expression::do_import(imp); + else + { + error_at(imp->location(), "import error: expected expression"); + return Expression::make_error(imp->location()); + } +} + +// Class Expression_list. + +// Traverse the list. + +int +Expression_list::traverse(Traverse* traverse) +{ + for (Expression_list::iterator p = this->begin(); + p != this->end(); + ++p) + { + if (*p != NULL) + { + if (Expression::traverse(&*p, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + return TRAVERSE_CONTINUE; +} + +// Copy the list. + +Expression_list* +Expression_list::copy() +{ + Expression_list* ret = new Expression_list(); + for (Expression_list::iterator p = this->begin(); + p != this->end(); + ++p) + { + if (*p == NULL) + ret->push_back(NULL); + else + ret->push_back((*p)->copy()); + } + return ret; +} + +// Return whether an expression list has an error expression. + +bool +Expression_list::contains_error() const +{ + for (Expression_list::const_iterator p = this->begin(); + p != this->end(); + ++p) + if (*p != NULL && (*p)->is_error_expression()) + return true; + return false; +} + +// Class Numeric_constant. + +// Destructor. + +Numeric_constant::~Numeric_constant() +{ + this->clear(); +} + +// Copy constructor. + +Numeric_constant::Numeric_constant(const Numeric_constant& a) + : classification_(a.classification_), type_(a.type_) +{ + switch (a.classification_) + { + case NC_INVALID: + break; + case NC_INT: + case NC_RUNE: + mpz_init_set(this->u_.int_val, a.u_.int_val); + break; + case NC_FLOAT: + mpfr_init_set(this->u_.float_val, a.u_.float_val, GMP_RNDN); + break; + case NC_COMPLEX: + mpfr_init_set(this->u_.complex_val.real, a.u_.complex_val.real, + GMP_RNDN); + mpfr_init_set(this->u_.complex_val.imag, a.u_.complex_val.imag, + GMP_RNDN); + break; + default: + go_unreachable(); + } +} + +// Assignment operator. + +Numeric_constant& +Numeric_constant::operator=(const Numeric_constant& a) +{ + this->clear(); + this->classification_ = a.classification_; + this->type_ = a.type_; + switch (a.classification_) + { + case NC_INVALID: + break; + case NC_INT: + case NC_RUNE: + mpz_init_set(this->u_.int_val, a.u_.int_val); + break; + case NC_FLOAT: + mpfr_init_set(this->u_.float_val, a.u_.float_val, GMP_RNDN); + break; + case NC_COMPLEX: + mpfr_init_set(this->u_.complex_val.real, a.u_.complex_val.real, + GMP_RNDN); + mpfr_init_set(this->u_.complex_val.imag, a.u_.complex_val.imag, + GMP_RNDN); + break; + default: + go_unreachable(); + } + return *this; +} + +// Clear the contents. + +void +Numeric_constant::clear() +{ + switch (this->classification_) + { + case NC_INVALID: + break; + case NC_INT: + case NC_RUNE: + mpz_clear(this->u_.int_val); + break; + case NC_FLOAT: + mpfr_clear(this->u_.float_val); + break; + case NC_COMPLEX: + mpfr_clear(this->u_.complex_val.real); + mpfr_clear(this->u_.complex_val.imag); + break; + default: + go_unreachable(); + } + this->classification_ = NC_INVALID; +} + +// Set to an unsigned long value. + +void +Numeric_constant::set_unsigned_long(Type* type, unsigned long val) +{ + this->clear(); + this->classification_ = NC_INT; + this->type_ = type; + mpz_init_set_ui(this->u_.int_val, val); +} + +// Set to an integer value. + +void +Numeric_constant::set_int(Type* type, const mpz_t val) +{ + this->clear(); + this->classification_ = NC_INT; + this->type_ = type; + mpz_init_set(this->u_.int_val, val); +} + +// Set to a rune value. + +void +Numeric_constant::set_rune(Type* type, const mpz_t val) +{ + this->clear(); + this->classification_ = NC_RUNE; + this->type_ = type; + mpz_init_set(this->u_.int_val, val); +} + +// Set to a floating point value. + +void +Numeric_constant::set_float(Type* type, const mpfr_t val) +{ + this->clear(); + this->classification_ = NC_FLOAT; + this->type_ = type; + // Numeric constants do not have negative zero values, so remove + // them here. They also don't have infinity or NaN values, but we + // should never see them here. + if (mpfr_zero_p(val)) + mpfr_init_set_ui(this->u_.float_val, 0, GMP_RNDN); + else + mpfr_init_set(this->u_.float_val, val, GMP_RNDN); +} + +// Set to a complex value. + +void +Numeric_constant::set_complex(Type* type, const mpfr_t real, const mpfr_t imag) +{ + this->clear(); + this->classification_ = NC_COMPLEX; + this->type_ = type; + mpfr_init_set(this->u_.complex_val.real, real, GMP_RNDN); + mpfr_init_set(this->u_.complex_val.imag, imag, GMP_RNDN); +} + +// Get an int value. + +void +Numeric_constant::get_int(mpz_t* val) const +{ + go_assert(this->is_int()); + mpz_init_set(*val, this->u_.int_val); +} + +// Get a rune value. + +void +Numeric_constant::get_rune(mpz_t* val) const +{ + go_assert(this->is_rune()); + mpz_init_set(*val, this->u_.int_val); +} + +// Get a floating point value. + +void +Numeric_constant::get_float(mpfr_t* val) const +{ + go_assert(this->is_float()); + mpfr_init_set(*val, this->u_.float_val, GMP_RNDN); +} + +// Get a complex value. + +void +Numeric_constant::get_complex(mpfr_t* real, mpfr_t* imag) const +{ + go_assert(this->is_complex()); + mpfr_init_set(*real, this->u_.complex_val.real, GMP_RNDN); + mpfr_init_set(*imag, this->u_.complex_val.imag, GMP_RNDN); +} + +// Express value as unsigned long if possible. + +Numeric_constant::To_unsigned_long +Numeric_constant::to_unsigned_long(unsigned long* val) const +{ + switch (this->classification_) + { + case NC_INT: + case NC_RUNE: + return this->mpz_to_unsigned_long(this->u_.int_val, val); + case NC_FLOAT: + return this->mpfr_to_unsigned_long(this->u_.float_val, val); + case NC_COMPLEX: + if (!mpfr_zero_p(this->u_.complex_val.imag)) + return NC_UL_NOTINT; + return this->mpfr_to_unsigned_long(this->u_.complex_val.real, val); + default: + go_unreachable(); + } +} + +// Express integer value as unsigned long if possible. + +Numeric_constant::To_unsigned_long +Numeric_constant::mpz_to_unsigned_long(const mpz_t ival, + unsigned long *val) const +{ + if (mpz_sgn(ival) < 0) + return NC_UL_NEGATIVE; + unsigned long ui = mpz_get_ui(ival); + if (mpz_cmp_ui(ival, ui) != 0) + return NC_UL_BIG; + *val = ui; + return NC_UL_VALID; +} + +// Express floating point value as unsigned long if possible. + +Numeric_constant::To_unsigned_long +Numeric_constant::mpfr_to_unsigned_long(const mpfr_t fval, + unsigned long *val) const +{ + if (!mpfr_integer_p(fval)) + return NC_UL_NOTINT; + mpz_t ival; + mpz_init(ival); + mpfr_get_z(ival, fval, GMP_RNDN); + To_unsigned_long ret = this->mpz_to_unsigned_long(ival, val); + mpz_clear(ival); + return ret; +} + +// Convert value to integer if possible. + +bool +Numeric_constant::to_int(mpz_t* val) const +{ + switch (this->classification_) + { + case NC_INT: + case NC_RUNE: + mpz_init_set(*val, this->u_.int_val); + return true; + case NC_FLOAT: + if (!mpfr_integer_p(this->u_.float_val)) + return false; + mpz_init(*val); + mpfr_get_z(*val, this->u_.float_val, GMP_RNDN); + return true; + case NC_COMPLEX: + if (!mpfr_zero_p(this->u_.complex_val.imag) + || !mpfr_integer_p(this->u_.complex_val.real)) + return false; + mpz_init(*val); + mpfr_get_z(*val, this->u_.complex_val.real, GMP_RNDN); + return true; + default: + go_unreachable(); + } +} + +// Convert value to floating point if possible. + +bool +Numeric_constant::to_float(mpfr_t* val) const +{ + switch (this->classification_) + { + case NC_INT: + case NC_RUNE: + mpfr_init_set_z(*val, this->u_.int_val, GMP_RNDN); + return true; + case NC_FLOAT: + mpfr_init_set(*val, this->u_.float_val, GMP_RNDN); + return true; + case NC_COMPLEX: + if (!mpfr_zero_p(this->u_.complex_val.imag)) + return false; + mpfr_init_set(*val, this->u_.complex_val.real, GMP_RNDN); + return true; + default: + go_unreachable(); + } +} + +// Convert value to complex. + +bool +Numeric_constant::to_complex(mpfr_t* vr, mpfr_t* vi) const +{ + switch (this->classification_) + { + case NC_INT: + case NC_RUNE: + mpfr_init_set_z(*vr, this->u_.int_val, GMP_RNDN); + mpfr_init_set_ui(*vi, 0, GMP_RNDN); + return true; + case NC_FLOAT: + mpfr_init_set(*vr, this->u_.float_val, GMP_RNDN); + mpfr_init_set_ui(*vi, 0, GMP_RNDN); + return true; + case NC_COMPLEX: + mpfr_init_set(*vr, this->u_.complex_val.real, GMP_RNDN); + mpfr_init_set(*vi, this->u_.complex_val.imag, GMP_RNDN); + return true; + default: + go_unreachable(); + } +} + +// Get the type. + +Type* +Numeric_constant::type() const +{ + if (this->type_ != NULL) + return this->type_; + switch (this->classification_) + { + case NC_INT: + return Type::make_abstract_integer_type(); + case NC_RUNE: + return Type::make_abstract_character_type(); + case NC_FLOAT: + return Type::make_abstract_float_type(); + case NC_COMPLEX: + return Type::make_abstract_complex_type(); + default: + go_unreachable(); + } +} + +// If the constant can be expressed in TYPE, then set the type of the +// constant to TYPE and return true. Otherwise return false, and, if +// ISSUE_ERROR is true, report an appropriate error message. + +bool +Numeric_constant::set_type(Type* type, bool issue_error, Location loc) +{ + bool ret; + if (type == NULL) + ret = true; + else if (type->integer_type() != NULL) + ret = this->check_int_type(type->integer_type(), issue_error, loc); + else if (type->float_type() != NULL) + ret = this->check_float_type(type->float_type(), issue_error, loc); + else if (type->complex_type() != NULL) + ret = this->check_complex_type(type->complex_type(), issue_error, loc); + else + go_unreachable(); + if (ret) + this->type_ = type; + return ret; +} + +// Check whether the constant can be expressed in an integer type. + +bool +Numeric_constant::check_int_type(Integer_type* type, bool issue_error, + Location location) const +{ + mpz_t val; + switch (this->classification_) + { + case NC_INT: + case NC_RUNE: + mpz_init_set(val, this->u_.int_val); + break; + + case NC_FLOAT: + if (!mpfr_integer_p(this->u_.float_val)) + { + if (issue_error) + error_at(location, "floating point constant truncated to integer"); + return false; + } + mpz_init(val); + mpfr_get_z(val, this->u_.float_val, GMP_RNDN); + break; + + case NC_COMPLEX: + if (!mpfr_integer_p(this->u_.complex_val.real) + || !mpfr_zero_p(this->u_.complex_val.imag)) + { + if (issue_error) + error_at(location, "complex constant truncated to integer"); + return false; + } + mpz_init(val); + mpfr_get_z(val, this->u_.complex_val.real, GMP_RNDN); + break; + + default: + go_unreachable(); + } + + bool ret; + if (type->is_abstract()) + ret = true; + else + { + int bits = mpz_sizeinbase(val, 2); + if (type->is_unsigned()) + { + // For an unsigned type we can only accept a nonnegative + // number, and we must be able to represents at least BITS. + ret = mpz_sgn(val) >= 0 && bits <= type->bits(); + } + else + { + // For a signed type we need an extra bit to indicate the + // sign. We have to handle the most negative integer + // specially. + ret = (bits + 1 <= type->bits() + || (bits <= type->bits() + && mpz_sgn(val) < 0 + && (mpz_scan1(val, 0) + == static_cast(type->bits() - 1)) + && mpz_scan0(val, type->bits()) == ULONG_MAX)); + } + } + + if (!ret && issue_error) + error_at(location, "integer constant overflow"); + + return ret; +} + +// Check whether the constant can be expressed in a floating point +// type. + +bool +Numeric_constant::check_float_type(Float_type* type, bool issue_error, + Location location) +{ + mpfr_t val; + switch (this->classification_) + { + case NC_INT: + case NC_RUNE: + mpfr_init_set_z(val, this->u_.int_val, GMP_RNDN); + break; + + case NC_FLOAT: + mpfr_init_set(val, this->u_.float_val, GMP_RNDN); + break; + + case NC_COMPLEX: + if (!mpfr_zero_p(this->u_.complex_val.imag)) + { + if (issue_error) + error_at(location, "complex constant truncated to float"); + return false; + } + mpfr_init_set(val, this->u_.complex_val.real, GMP_RNDN); + break; + + default: + go_unreachable(); + } + + bool ret; + if (type->is_abstract()) + ret = true; + else if (mpfr_nan_p(val) || mpfr_inf_p(val) || mpfr_zero_p(val)) + { + // A NaN or Infinity always fits in the range of the type. + ret = true; + } + else + { + mp_exp_t exp = mpfr_get_exp(val); + mp_exp_t max_exp; + switch (type->bits()) + { + case 32: + max_exp = 128; + break; + case 64: + max_exp = 1024; + break; + default: + go_unreachable(); + } + + 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); + + if (!ret && issue_error) + error_at(location, "floating point constant overflow"); + + return ret; +} + +// Check whether the constant can be expressed in a complex type. + +bool +Numeric_constant::check_complex_type(Complex_type* type, bool issue_error, + Location location) +{ + if (type->is_abstract()) + return true; + + mp_exp_t max_exp; + switch (type->bits()) + { + case 64: + max_exp = 128; + break; + case 128: + max_exp = 1024; + break; + default: + go_unreachable(); + } + + 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: + 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 = 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; + } + + 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) + { + // 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; +} + +// Return an Expression for this value. + +Expression* +Numeric_constant::expression(Location loc) const +{ + switch (this->classification_) + { + case NC_INT: + return Expression::make_integer(&this->u_.int_val, this->type_, loc); + case NC_RUNE: + return Expression::make_character(&this->u_.int_val, this->type_, loc); + case NC_FLOAT: + return Expression::make_float(&this->u_.float_val, this->type_, loc); + case NC_COMPLEX: + return Expression::make_complex(&this->u_.complex_val.real, + &this->u_.complex_val.imag, + this->type_, loc); + default: + go_unreachable(); + } +} diff --git a/gcc-4.9/gcc/go/gofrontend/expressions.h b/gcc-4.9/gcc/go/gofrontend/expressions.h new file mode 100644 index 000000000..99a0d0720 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/expressions.h @@ -0,0 +1,2488 @@ +// expressions.h -- Go frontend expression handling. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_EXPRESSIONS_H +#define GO_EXPRESSIONS_H + +#include + +#include "operator.h" + +class Gogo; +class Translate_context; +class Traverse; +class Statement_inserter; +class Type; +class Method; +struct Type_context; +class Integer_type; +class Float_type; +class Complex_type; +class Function_type; +class Map_type; +class Struct_type; +class Struct_field; +class Expression_list; +class Var_expression; +class Temporary_reference_expression; +class Set_and_use_temporary_expression; +class String_expression; +class Binary_expression; +class Call_expression; +class Func_expression; +class Func_descriptor_expression; +class Unknown_expression; +class Index_expression; +class Map_index_expression; +class Bound_method_expression; +class Field_reference_expression; +class Interface_field_reference_expression; +class Type_guard_expression; +class Receive_expression; +class Numeric_constant; +class Named_object; +class Export; +class Import; +class Temporary_statement; +class Label; +class Ast_dump_context; +class String_dump; + +// The base class for all expressions. + +class Expression +{ + public: + // The types of expressions. + enum Expression_classification + { + EXPRESSION_ERROR, + EXPRESSION_TYPE, + EXPRESSION_UNARY, + EXPRESSION_BINARY, + EXPRESSION_CONST_REFERENCE, + EXPRESSION_VAR_REFERENCE, + EXPRESSION_TEMPORARY_REFERENCE, + EXPRESSION_SET_AND_USE_TEMPORARY, + EXPRESSION_SINK, + EXPRESSION_FUNC_REFERENCE, + EXPRESSION_FUNC_DESCRIPTOR, + EXPRESSION_FUNC_CODE_REFERENCE, + EXPRESSION_UNKNOWN_REFERENCE, + EXPRESSION_BOOLEAN, + EXPRESSION_STRING, + EXPRESSION_INTEGER, + EXPRESSION_FLOAT, + EXPRESSION_COMPLEX, + EXPRESSION_NIL, + EXPRESSION_IOTA, + EXPRESSION_CALL, + EXPRESSION_CALL_RESULT, + EXPRESSION_BOUND_METHOD, + EXPRESSION_INDEX, + EXPRESSION_ARRAY_INDEX, + EXPRESSION_STRING_INDEX, + EXPRESSION_MAP_INDEX, + EXPRESSION_SELECTOR, + EXPRESSION_FIELD_REFERENCE, + EXPRESSION_INTERFACE_FIELD_REFERENCE, + EXPRESSION_ALLOCATION, + EXPRESSION_TYPE_GUARD, + EXPRESSION_CONVERSION, + EXPRESSION_UNSAFE_CONVERSION, + EXPRESSION_STRUCT_CONSTRUCTION, + EXPRESSION_FIXED_ARRAY_CONSTRUCTION, + EXPRESSION_OPEN_ARRAY_CONSTRUCTION, + EXPRESSION_MAP_CONSTRUCTION, + EXPRESSION_COMPOSITE_LITERAL, + EXPRESSION_HEAP_COMPOSITE, + EXPRESSION_RECEIVE, + EXPRESSION_TYPE_DESCRIPTOR, + EXPRESSION_TYPE_INFO, + EXPRESSION_SLICE_INFO, + EXPRESSION_INTERFACE_INFO, + EXPRESSION_STRUCT_FIELD_OFFSET, + EXPRESSION_MAP_DESCRIPTOR, + EXPRESSION_LABEL_ADDR, + EXPRESSION_CONDITIONAL + }; + + Expression(Expression_classification, Location); + + virtual ~Expression(); + + // Make an error expression. This is used when a parse error occurs + // to prevent cascading errors. + static Expression* + make_error(Location); + + // Make an expression which is really a type. This is used during + // parsing. + static Expression* + make_type(Type*, Location); + + // Make a unary expression. + static Expression* + make_unary(Operator, Expression*, Location); + + // Make a binary expression. + static Expression* + make_binary(Operator, Expression*, Expression*, Location); + + // Make a reference to a constant in an expression. + static Expression* + make_const_reference(Named_object*, Location); + + // Make a reference to a variable in an expression. + static Expression* + make_var_reference(Named_object*, Location); + + // Make a reference to a temporary variable. Temporary variables + // are always created by a single statement, which is what we use to + // refer to them. + static Temporary_reference_expression* + make_temporary_reference(Temporary_statement*, Location); + + // Make an expressions which sets a temporary variable and then + // evaluates to a reference to that temporary variable. This is + // used to set a temporary variable while retaining the order of + // evaluation. + static Set_and_use_temporary_expression* + make_set_and_use_temporary(Temporary_statement*, Expression*, Location); + + // Make a sink expression--a reference to the blank identifier _. + static Expression* + make_sink(Location); + + // Make a reference to a function in an expression. This returns a + // pointer to the struct holding the address of the function + // followed by any closed-over variables. + static Expression* + make_func_reference(Named_object*, Expression* closure, Location); + + // Make a function descriptor, an immutable struct with a single + // field that points to the function code. This may only be used + // with functions that do not have closures. FN is the function for + // which we are making the descriptor. + static Func_descriptor_expression* + make_func_descriptor(Named_object* fn); + + // Make a reference to the code of a function. This is used to set + // descriptor and closure fields. + static Expression* + make_func_code_reference(Named_object*, Location); + + // Make a reference to an unknown name. In a correct program this + // will always be lowered to a real const/var/func reference. + static Unknown_expression* + make_unknown_reference(Named_object*, Location); + + // Make a constant bool expression. + static Expression* + make_boolean(bool val, Location); + + // Make a constant string expression. + static Expression* + make_string(const std::string&, Location); + + // Make a character constant expression. TYPE should be NULL for an + // abstract type. + static Expression* + make_character(const mpz_t*, Type*, Location); + + // Make a constant integer expression. TYPE should be NULL for an + // abstract type. + static Expression* + make_integer(const mpz_t*, Type*, Location); + + // Make a constant float expression. TYPE should be NULL for an + // abstract type. + static Expression* + make_float(const mpfr_t*, Type*, Location); + + // Make a constant complex expression. TYPE should be NULL for an + // abstract type. + static Expression* + make_complex(const mpfr_t* real, const mpfr_t* imag, Type*, Location); + + // Make a nil expression. + static Expression* + make_nil(Location); + + // Make an iota expression. This is used for the predeclared + // constant iota. + static Expression* + make_iota(); + + // Make a call expression. + static Call_expression* + make_call(Expression* func, Expression_list* args, bool is_varargs, + Location); + + // Make a reference to a specific result of a call expression which + // returns a tuple. + static Expression* + make_call_result(Call_expression*, unsigned int index); + + // Make an expression which is a method bound to its first + // parameter. METHOD is the method being called, FUNCTION is the + // function to call. + static Bound_method_expression* + make_bound_method(Expression* object, const Method* method, + Named_object* function, Location); + + // Make an index or slice expression. This is a parser expression + // which represents LEFT[START:END:CAP]. END may be NULL, meaning an + // index rather than a slice. CAP may be NULL, meaning we use the default + // capacity of LEFT. At parse time we may not know the type of LEFT. + // After parsing this is lowered to an array index, a string index, + // or a map index. + static Expression* + make_index(Expression* left, Expression* start, Expression* end, + Expression* cap, Location); + + // Make an array index expression. END may be NULL, in which case + // this is an lvalue. CAP may be NULL, in which case it defaults + // to cap(ARRAY). + static Expression* + make_array_index(Expression* array, Expression* start, Expression* end, + Expression* cap, Location); + + // Make a string index expression. END may be NULL. This is never + // an lvalue. + static Expression* + make_string_index(Expression* string, Expression* start, Expression* end, + Location); + + // Make a map index expression. This is an lvalue. + static Map_index_expression* + make_map_index(Expression* map, Expression* val, Location); + + // Make a selector. This is a parser expression which represents + // LEFT.NAME. At parse time we may not know the type of the left + // hand side. + static Expression* + make_selector(Expression* left, const std::string& name, Location); + + // Make a reference to a field in a struct. + static Field_reference_expression* + make_field_reference(Expression*, unsigned int field_index, Location); + + // Make a reference to a field of an interface, with an associated + // object. + static Expression* + make_interface_field_reference(Expression*, const std::string&, + Location); + + // Make an allocation expression. + static Expression* + make_allocation(Type*, Location); + + // Make a type guard expression. + static Expression* + make_type_guard(Expression*, Type*, Location); + + // Make a type cast expression. + static Expression* + make_cast(Type*, Expression*, Location); + + // Make an unsafe type cast expression. This is only used when + // passing parameter to builtin functions that are part of the Go + // runtime. + static Expression* + make_unsafe_cast(Type*, Expression*, Location); + + // Make a composite literal. The DEPTH parameter is how far down we + // are in a list of composite literals with omitted types. HAS_KEYS + // is true if the expression list has keys alternating with values. + // ALL_ARE_NAMES is true if all the keys could be struct field + // names. + static Expression* + make_composite_literal(Type*, int depth, bool has_keys, Expression_list*, + bool all_are_names, Location); + + // Make a struct composite literal. + static Expression* + make_struct_composite_literal(Type*, Expression_list*, Location); + + // Make a slice composite literal. + static Expression* + make_slice_composite_literal(Type*, Expression_list*, Location); + + // Take a composite literal and allocate it on the heap. + static Expression* + make_heap_composite(Expression*, Location); + + // Make a receive expression. VAL is NULL for a unary receive. + static Receive_expression* + make_receive(Expression* channel, Location); + + // Make an expression which evaluates to the address of the type + // descriptor for TYPE. + static Expression* + make_type_descriptor(Type* type, Location); + + // Make an expression which evaluates to some characteristic of a + // type. These are only used for type descriptors, so there is no + // location parameter. + enum Type_info + { + // The size of a value of the type. + TYPE_INFO_SIZE, + // The required alignment of a value of the type. + TYPE_INFO_ALIGNMENT, + // The required alignment of a value of the type when used as a + // field in a struct. + TYPE_INFO_FIELD_ALIGNMENT + }; + + static Expression* + make_type_info(Type* type, Type_info); + + // Make an expression that evaluates to some characteristic of a + // slice. For simplicity, the enum values must match the field indexes + // in the underlying struct. + enum Slice_info + { + // The underlying data of the slice. + SLICE_INFO_VALUE_POINTER, + // The length of the slice. + SLICE_INFO_LENGTH, + // The capacity of the slice. + SLICE_INFO_CAPACITY + }; + + static Expression* + make_slice_info(Expression* slice, Slice_info, Location); + + + // Make an expression that evaluates to some characteristic of a + // interface. For simplicity, the enum values must match the field indexes + // of a non-empty interface in the underlying struct. + enum Interface_info + { + // The methods of an interface. + INTERFACE_INFO_METHODS, + // The first argument to pass to an interface method. + INTERFACE_INFO_OBJECT + }; + + static Expression* + make_interface_info(Expression* iface, Interface_info, Location); + + // Make an expression which evaluates to the offset of a field in a + // struct. This is only used for type descriptors, so there is no + // location parameter. + static Expression* + make_struct_field_offset(Struct_type*, const Struct_field*); + + // Make an expression which evaluates to the address of the map + // descriptor for TYPE. + static Expression* + make_map_descriptor(Map_type* type, Location); + + // Make an expression which evaluates to the address of an unnamed + // label. + static Expression* + make_label_addr(Label*, Location); + + // Make a conditional expression. + static Expression* + make_conditional(Expression*, Expression*, Expression*, Location); + + // Return the expression classification. + Expression_classification + classification() const + { return this->classification_; } + + // Return the location of the expression. + Location + location() const + { return this->location_; } + + // Return whether this is a constant expression. + bool + is_constant() const + { return this->do_is_constant(); } + + // Return whether this is an immutable expression. + bool + is_immutable() const + { return this->do_is_immutable(); } + + // If this is not a numeric constant, return false. If it is one, + // return true, and set VAL to hold the value. + bool + numeric_constant_value(Numeric_constant* val) const + { return this->do_numeric_constant_value(val); } + + // If this is not a constant expression with string type, return + // false. If it is one, return true, and set VAL to the value. + bool + string_constant_value(std::string* val) const + { return this->do_string_constant_value(val); } + + // This is called if the value of this expression is being + // discarded. This issues warnings about computed values being + // unused. This returns true if all is well, false if it issued an + // error message. + bool + discarding_value() + { return this->do_discarding_value(); } + + // Return whether this is an error expression. + bool + is_error_expression() const + { return this->classification_ == EXPRESSION_ERROR; } + + // Return whether this expression really represents a type. + bool + is_type_expression() const + { return this->classification_ == EXPRESSION_TYPE; } + + // If this is a variable reference, return the Var_expression + // structure. Otherwise, return NULL. This is a controlled dynamic + // cast. + Var_expression* + var_expression() + { return this->convert(); } + + const Var_expression* + var_expression() const + { return this->convert(); } + + // If this is a reference to a temporary variable, return the + // Temporary_reference_expression. Otherwise, return NULL. + Temporary_reference_expression* + temporary_reference_expression() + { + return this->convert(); + } + + // If this is a set-and-use-temporary, return the + // Set_and_use_temporary_expression. Otherwise, return NULL. + Set_and_use_temporary_expression* + set_and_use_temporary_expression() + { + return this->convert(); + } + + // Return whether this is a sink expression. + bool + is_sink_expression() const + { return this->classification_ == EXPRESSION_SINK; } + + // If this is a string expression, return the String_expression + // structure. Otherwise, return NULL. + String_expression* + string_expression() + { return this->convert(); } + + // Return whether this is the expression nil. + bool + is_nil_expression() const + { return this->classification_ == EXPRESSION_NIL; } + + // If this is an indirection through a pointer, return the + // expression being pointed through. Otherwise return this. + Expression* + deref(); + + // If this is a binary expression, return the Binary_expression + // structure. Otherwise return NULL. + Binary_expression* + binary_expression() + { return this->convert(); } + + // If this is a call expression, return the Call_expression + // structure. Otherwise, return NULL. This is a controlled dynamic + // cast. + Call_expression* + call_expression() + { return this->convert(); } + + // If this is an expression which refers to a function, return the + // Func_expression structure. Otherwise, return NULL. + Func_expression* + func_expression() + { return this->convert(); } + + const Func_expression* + func_expression() const + { return this->convert(); } + + // If this is an expression which refers to an unknown name, return + // the Unknown_expression structure. Otherwise, return NULL. + Unknown_expression* + unknown_expression() + { return this->convert(); } + + const Unknown_expression* + unknown_expression() const + { + return this->convert(); + } + + // If this is an index expression, return the Index_expression + // structure. Otherwise, return NULL. + Index_expression* + index_expression() + { return this->convert(); } + + // If this is an expression which refers to indexing in a map, + // return the Map_index_expression structure. Otherwise, return + // NULL. + Map_index_expression* + map_index_expression() + { return this->convert(); } + + // If this is a bound method expression, return the + // Bound_method_expression structure. Otherwise, return NULL. + Bound_method_expression* + bound_method_expression() + { return this->convert(); } + + // If this is a reference to a field in a struct, return the + // Field_reference_expression structure. Otherwise, return NULL. + Field_reference_expression* + field_reference_expression() + { + return this->convert(); + } + + // If this is a reference to a field in an interface, return the + // Interface_field_reference_expression structure. Otherwise, + // return NULL. + Interface_field_reference_expression* + interface_field_reference_expression() + { + return this->convert(); + } + + // If this is a type guard expression, return the + // Type_guard_expression structure. Otherwise, return NULL. + Type_guard_expression* + type_guard_expression() + { return this->convert(); } + + // If this is a receive expression, return the Receive_expression + // structure. Otherwise, return NULL. + Receive_expression* + receive_expression() + { return this->convert(); } + + // Return true if this is a composite literal. + bool + is_composite_literal() const; + + // Return true if this is a composite literal which is not constant. + bool + is_nonconstant_composite_literal() const; + + // Return true if this is a variable or temporary variable. + bool + is_variable() const; + + // Return true if this is a reference to a local variable. + bool + is_local_variable() const; + + // Make the builtin function descriptor type, so that it can be + // converted. + static void + make_func_descriptor_type(); + + // Traverse an expression. + static int + traverse(Expression**, Traverse*); + + // Traverse subexpressions of this expression. + int + traverse_subexpressions(Traverse*); + + // Lower an expression. This is called immediately after parsing. + // FUNCTION is the function we are in; it will be NULL for an + // expression initializing a global variable. INSERTER may be used + // to insert statements before the statement or initializer + // containing this expression; it is normally used to create + // temporary variables. IOTA_VALUE is the value that we should give + // to any iota expressions. This function must resolve expressions + // which could not be fully parsed into their final form. It + // returns the same Expression or a new one. + Expression* + lower(Gogo* gogo, Named_object* function, Statement_inserter* inserter, + int iota_value) + { return this->do_lower(gogo, function, inserter, iota_value); } + + // Flatten an expression. This is called after order_evaluation. + // FUNCTION is the function we are in; it will be NULL for an + // expression initializing a global variable. INSERTER may be used + // to insert statements before the statement or initializer + // containing this expression; it is normally used to create + // temporary variables. This function must resolve expressions + // which could not be fully parsed into their final form. It + // returns the same Expression or a new one. + Expression* + flatten(Gogo* gogo, Named_object* function, Statement_inserter* inserter) + { return this->do_flatten(gogo, function, inserter); } + + // Determine the real type of an expression with abstract integer, + // floating point, or complex type. TYPE_CONTEXT describes the + // expected type. + void + determine_type(const Type_context*); + + // Check types in an expression. + void + check_types(Gogo* gogo) + { this->do_check_types(gogo); } + + // Determine the type when there is no context. + void + determine_type_no_context(); + + // Return the current type of the expression. This may be changed + // by determine_type. + Type* + type() + { return this->do_type(); } + + // Return a copy of an expression. + Expression* + copy() + { return this->do_copy(); } + + // Return whether the expression is addressable--something which may + // be used as the operand of the unary & operator. + bool + is_addressable() const + { return this->do_is_addressable(); } + + // Note that we are taking the address of this expression. ESCAPES + // is true if this address escapes the current function. + void + address_taken(bool escapes) + { this->do_address_taken(escapes); } + + // Note that a nil check must be issued for this expression. + void + issue_nil_check() + { this->do_issue_nil_check(); } + + // Return whether this expression must be evaluated in order + // according to the order of evaluation rules. This is basically + // true of all expressions with side-effects. + bool + must_eval_in_order() const + { return this->do_must_eval_in_order(); } + + // Return whether subexpressions of this expression must be + // evaluated in order. This is true of index expressions and + // pointer indirections. This sets *SKIP to the number of + // subexpressions to skip during traversing, as index expressions + // only requiring moving the index, not the array. + bool + must_eval_subexpressions_in_order(int* skip) const + { + *skip = 0; + return this->do_must_eval_subexpressions_in_order(skip); + } + + // Return the tree for this expression. + tree + get_tree(Translate_context*); + + // Return a tree handling any conversions which must be done during + // assignment. + static tree + convert_for_assignment(Translate_context*, Type* lhs_type, Type* rhs_type, + tree rhs_tree, Location location); + + // Return a tree converting a value of one interface type to another + // interface type. If FOR_TYPE_GUARD is true this is for a type + // assertion. + static tree + convert_interface_to_interface(Translate_context*, Type* lhs_type, + Type* rhs_type, tree rhs_tree, + bool for_type_guard, Location); + + // Return a backend expression implementing the comparison LEFT OP RIGHT. + // TYPE is the type of both sides. + static Bexpression* + comparison(Translate_context*, Type* result_type, Operator op, + Expression* left, Expression* right, Location); + + // Return the backend expression for the numeric constant VAL. + static Bexpression* + backend_numeric_constant_expression(Translate_context*, + Numeric_constant* val); + + // Export the expression. This is only used for constants. It will + // be used for things like values of named constants and sizes of + // arrays. + void + export_expression(Export* exp) const + { this->do_export(exp); } + + // Import an expression. + static Expression* + import_expression(Import*); + + // Return a tree which checks that VAL, of arbitrary integer type, + // is non-negative and is not more than the maximum value of + // BOUND_TYPE. If SOFAR is not NULL, it is or'red into the result. + // The return value may be NULL if SOFAR is NULL. + static tree + check_bounds(tree val, tree bound_type, tree sofar, Location); + + // Dump an expression to a dump constext. + void + dump_expression(Ast_dump_context*) const; + + protected: + // May be implemented by child class: traverse the expressions. + virtual int + do_traverse(Traverse*); + + // Return a lowered expression. + virtual Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int) + { return this; } + + // Return a flattened expression. + virtual Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*) + { return this; } + + + // Return whether this is a constant expression. + virtual bool + do_is_constant() const + { return false; } + + // Return whether this is an immutable expression. + virtual bool + do_is_immutable() const + { return false; } + + // Return whether this is a constant expression of numeric type, and + // set the Numeric_constant to the value. + virtual bool + do_numeric_constant_value(Numeric_constant*) const + { return false; } + + // Return whether this is a constant expression of string type, and + // set VAL to the value. + virtual bool + do_string_constant_value(std::string*) const + { return false; } + + // Called by the parser if the value is being discarded. + virtual bool + do_discarding_value(); + + // Child class holds type. + virtual Type* + do_type() = 0; + + // Child class implements determining type information. + virtual void + do_determine_type(const Type_context*) = 0; + + // Child class implements type checking if needed. + virtual void + do_check_types(Gogo*) + { } + + // Child class implements copying. + virtual Expression* + do_copy() = 0; + + // Child class implements whether the expression is addressable. + virtual bool + do_is_addressable() const + { return false; } + + // Child class implements taking the address of an expression. + virtual void + do_address_taken(bool) + { } + + // Child class implements issuing a nil check if the address is taken. + virtual void + do_issue_nil_check() + { } + + // Child class implements whether this expression must be evaluated + // in order. + virtual bool + do_must_eval_in_order() const + { return false; } + + // Child class implements whether this expressions requires that + // subexpressions be evaluated in order. The child implementation + // may set *SKIP if it should be non-zero. + virtual bool + do_must_eval_subexpressions_in_order(int* /* skip */) const + { return false; } + + // Child class implements conversion to tree. + virtual tree + do_get_tree(Translate_context*) = 0; + + // Child class implements export. + virtual void + do_export(Export*) const; + + // For children to call to give an error for an unused value. + void + unused_value_error(); + + // For children to call when they detect that they are in error. + void + set_is_error(); + + // For children to call to report an error conveniently. + void + report_error(const char*); + + // Child class implements dumping to a dump context. + virtual void + do_dump_expression(Ast_dump_context*) const = 0; + + private: + // Convert to the desired statement classification, or return NULL. + // This is a controlled dynamic cast. + template + Expression_class* + convert() + { + return (this->classification_ == expr_classification + ? static_cast(this) + : NULL); + } + + template + const Expression_class* + convert() const + { + return (this->classification_ == expr_classification + ? static_cast(this) + : NULL); + } + + static tree + convert_type_to_interface(Translate_context*, Type*, Type*, tree, + Location); + + static tree + get_interface_type_descriptor(Translate_context*, Type*, tree, + Location); + + static tree + convert_interface_to_type(Translate_context*, Type*, Type*, tree, + Location); + + // The expression classification. + Expression_classification classification_; + // The location in the input file. + Location location_; +}; + +// A list of Expressions. + +class Expression_list +{ + public: + Expression_list() + : entries_() + { } + + // Return whether the list is empty. + bool + empty() const + { return this->entries_.empty(); } + + // Return the number of entries in the list. + size_t + size() const + { return this->entries_.size(); } + + // Add an entry to the end of the list. + void + push_back(Expression* expr) + { this->entries_.push_back(expr); } + + void + append(Expression_list* add) + { this->entries_.insert(this->entries_.end(), add->begin(), add->end()); } + + // Reserve space in the list. + void + reserve(size_t size) + { this->entries_.reserve(size); } + + // Traverse the expressions in the list. + int + traverse(Traverse*); + + // Copy the list. + Expression_list* + copy(); + + // Return true if the list contains an error expression. + bool + contains_error() const; + + // Retrieve an element by index. + Expression*& + at(size_t i) + { return this->entries_.at(i); } + + // Return the first and last elements. + Expression*& + front() + { return this->entries_.front(); } + + Expression* + front() const + { return this->entries_.front(); } + + Expression*& + back() + { return this->entries_.back(); } + + Expression* + back() const + { return this->entries_.back(); } + + // Iterators. + + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + + iterator + begin() + { return this->entries_.begin(); } + + const_iterator + begin() const + { return this->entries_.begin(); } + + iterator + end() + { return this->entries_.end(); } + + const_iterator + end() const + { return this->entries_.end(); } + + // Erase an entry. + void + erase(iterator p) + { this->entries_.erase(p); } + + private: + std::vector entries_; +}; + +// An abstract base class for an expression which is only used by the +// parser, and is lowered in the lowering pass. + +class Parser_expression : public Expression +{ + public: + Parser_expression(Expression_classification classification, + Location location) + : Expression(classification, location) + { } + + protected: + virtual Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int) = 0; + + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { go_unreachable(); } + + void + do_check_types(Gogo*) + { go_unreachable(); } + + tree + do_get_tree(Translate_context*) + { go_unreachable(); } +}; + +// An expression which is simply a variable. + +class Var_expression : public Expression +{ + public: + Var_expression(Named_object* variable, Location location) + : Expression(EXPRESSION_VAR_REFERENCE, location), + variable_(variable) + { } + + // Return the variable. + Named_object* + named_object() const + { return this->variable_; } + + protected: + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + Expression* + do_copy() + { return this; } + + bool + do_is_addressable() const + { return true; } + + void + do_address_taken(bool); + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The variable we are referencing. + Named_object* variable_; +}; + +// A reference to a temporary variable. + +class Temporary_reference_expression : public Expression +{ + public: + Temporary_reference_expression(Temporary_statement* statement, + Location location) + : Expression(EXPRESSION_TEMPORARY_REFERENCE, location), + statement_(statement), is_lvalue_(false) + { } + + // The temporary that this expression refers to. + Temporary_statement* + statement() const + { return this->statement_; } + + // Indicate that this reference appears on the left hand side of an + // assignment statement. + void + set_is_lvalue() + { this->is_lvalue_ = true; } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return make_temporary_reference(this->statement_, this->location()); } + + bool + do_is_addressable() const + { return true; } + + void + do_address_taken(bool); + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The statement where the temporary variable is defined. + Temporary_statement* statement_; + // Whether this reference appears on the left hand side of an + // assignment statement. + bool is_lvalue_; +}; + +// Set and use a temporary variable. + +class Set_and_use_temporary_expression : public Expression +{ + public: + Set_and_use_temporary_expression(Temporary_statement* statement, + Expression* expr, Location location) + : Expression(EXPRESSION_SET_AND_USE_TEMPORARY, location), + statement_(statement), expr_(expr) + { } + + // Return the temporary. + Temporary_statement* + temporary() const + { return this->statement_; } + + // Return the expression. + Expression* + expression() const + { return this->expr_; } + + protected: + int + do_traverse(Traverse* traverse) + { return Expression::traverse(&this->expr_, traverse); } + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + Expression* + do_copy() + { + return make_set_and_use_temporary(this->statement_, this->expr_, + this->location()); + } + + bool + do_is_addressable() const + { return true; } + + void + do_address_taken(bool); + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The statement where the temporary variable is defined. + Temporary_statement* statement_; + // The expression to assign to the temporary. + Expression* expr_; +}; + +// A string expression. + +class String_expression : public Expression +{ + public: + String_expression(const std::string& val, Location location) + : Expression(EXPRESSION_STRING, location), + val_(val), type_(NULL) + { } + + const std::string& + val() const + { return this->val_; } + + static Expression* + do_import(Import*); + + protected: + bool + do_is_constant() const + { return true; } + + bool + do_is_immutable() const + { return true; } + + bool + do_string_constant_value(std::string* val) const + { + *val = this->val_; + return true; + } + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + Expression* + do_copy() + { return this; } + + tree + do_get_tree(Translate_context*); + + // Write string literal to a string dump. + static void + export_string(String_dump* exp, const String_expression* str); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The string value. This is immutable. + const std::string val_; + // The type as determined by context. + Type* type_; +}; + +// A binary expression. + +class Binary_expression : public Expression +{ + public: + Binary_expression(Operator op, Expression* left, Expression* right, + Location location) + : Expression(EXPRESSION_BINARY, location), + op_(op), left_(left), right_(right), type_(NULL) + { } + + // Return the operator. + Operator + op() + { return this->op_; } + + // Return the left hand expression. + Expression* + left() + { return this->left_; } + + // Return the right hand expression. + Expression* + right() + { return this->right_; } + + // Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC. + // Return true if this could be done, false if not. Issue errors at + // LOCATION as appropriate. + static bool + eval_constant(Operator op, Numeric_constant* left_nc, + Numeric_constant* right_nc, Location location, + Numeric_constant* nc); + + // Compare constants LEFT_NC and RIGHT_NC according to OP, setting + // *RESULT. Return true if this could be done, false if not. Issue + // errors at LOCATION as appropriate. + static bool + compare_constant(Operator op, Numeric_constant* left_nc, + Numeric_constant* right_nc, Location location, + bool* result); + + static Expression* + do_import(Import*); + + // Report an error if OP can not be applied to TYPE. Return whether + // it can. OTYPE is the type of the other operand. + static bool + check_operator_type(Operator op, Type* type, Type* otype, Location); + + protected: + int + do_traverse(Traverse* traverse); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + bool + do_is_constant() const + { return this->left_->is_constant() && this->right_->is_constant(); } + + bool + do_numeric_constant_value(Numeric_constant*) const; + + bool + do_discarding_value(); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_binary(this->op_, this->left_->copy(), + this->right_->copy(), this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + static bool + operation_type(Operator op, Type* left_type, Type* right_type, + Type** result_type); + + static bool + cmp_to_bool(Operator op, int cmp); + + static bool + eval_integer(Operator op, const Numeric_constant*, const Numeric_constant*, + Location, Numeric_constant*); + + static bool + eval_float(Operator op, const Numeric_constant*, const Numeric_constant*, + Location, Numeric_constant*); + + static bool + eval_complex(Operator op, const Numeric_constant*, const Numeric_constant*, + Location, Numeric_constant*); + + static bool + compare_integer(const Numeric_constant*, const Numeric_constant*, int*); + + static bool + compare_float(const Numeric_constant*, const Numeric_constant *, int*); + + static bool + compare_complex(const Numeric_constant*, const Numeric_constant*, int*); + + Expression* + lower_struct_comparison(Gogo*, Statement_inserter*); + + Expression* + lower_array_comparison(Gogo*, Statement_inserter*); + + Expression* + lower_interface_value_comparison(Gogo*, Statement_inserter*); + + Expression* + lower_compare_to_memcmp(Gogo*, Statement_inserter*); + + Expression* + operand_address(Statement_inserter*, Expression*); + + // The binary operator to apply. + Operator op_; + // The left hand side operand. + Expression* left_; + // The right hand side operand. + Expression* right_; + // The type of a comparison operation. + Type* type_; +}; + +// A call expression. The go statement needs to dig inside this. + +class Call_expression : public Expression +{ + public: + Call_expression(Expression* fn, Expression_list* args, bool is_varargs, + Location location) + : Expression(EXPRESSION_CALL, location), + fn_(fn), args_(args), type_(NULL), results_(NULL), tree_(NULL), + is_varargs_(is_varargs), are_hidden_fields_ok_(false), + varargs_are_lowered_(false), types_are_determined_(false), + is_deferred_(false), issued_error_(false) + { } + + // The function to call. + Expression* + fn() const + { return this->fn_; } + + // The arguments. + Expression_list* + args() + { return this->args_; } + + const Expression_list* + args() const + { return this->args_; } + + // Get the function type. + Function_type* + get_function_type() const; + + // Return the number of values this call will return. + size_t + result_count() const; + + // Return the temporary variable which holds result I. This is only + // valid after the expression has been lowered, and is only valid + // for calls which return multiple results. + Temporary_statement* + result(size_t i) const; + + // Return whether this is a call to the predeclared function + // recover. + bool + is_recover_call() const; + + // Set the argument for a call to recover. + void + set_recover_arg(Expression*); + + // Whether the last argument is a varargs argument (f(a...)). + bool + is_varargs() const + { return this->is_varargs_; } + + // Note that varargs have already been lowered. + void + set_varargs_are_lowered() + { this->varargs_are_lowered_ = true; } + + // Note that it is OK for this call to set hidden fields when + // passing arguments. + void + set_hidden_fields_are_ok() + { this->are_hidden_fields_ok_ = true; } + + // Whether this call is being deferred. + bool + is_deferred() const + { return this->is_deferred_; } + + // Note that the call is being deferred. + void + set_is_deferred() + { this->is_deferred_ = true; } + + // We have found an error with this call expression; return true if + // we should report it. + bool + issue_error(); + + protected: + int + do_traverse(Traverse*); + + virtual Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + bool + do_discarding_value() + { return true; } + + virtual Type* + do_type(); + + virtual void + do_determine_type(const Type_context*); + + virtual void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_call(this->fn_->copy(), + (this->args_ == NULL + ? NULL + : this->args_->copy()), + this->is_varargs_, this->location()); + } + + bool + do_must_eval_in_order() const; + + virtual tree + do_get_tree(Translate_context*); + + virtual bool + do_is_recover_call() const; + + virtual void + do_set_recover_arg(Expression*); + + // Let a builtin expression change the argument list. + void + set_args(Expression_list* args) + { this->args_ = args; } + + // Let a builtin expression lower varargs. + void + lower_varargs(Gogo*, Named_object* function, Statement_inserter* inserter, + Type* varargs_type, size_t param_count); + + // Let a builtin expression check whether types have been + // determined. + bool + determining_types(); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + bool + check_argument_type(int, const Type*, const Type*, Location, bool); + + Expression* + interface_method_function(Interface_field_reference_expression*, + Expression**); + + tree + set_results(Translate_context*, tree); + + // The function to call. + Expression* fn_; + // The arguments to pass. This may be NULL if there are no + // arguments. + Expression_list* args_; + // The type of the expression, to avoid recomputing it. + Type* type_; + // The list of temporaries which will hold the results if the + // function returns a tuple. + std::vector* results_; + // The tree for the call, used for a call which returns a tuple. + tree tree_; + // True if the last argument is a varargs argument (f(a...)). + bool is_varargs_; + // True if this statement may pass hidden fields in the arguments. + // This is used for generated method stubs. + bool are_hidden_fields_ok_; + // True if varargs have already been lowered. + bool varargs_are_lowered_; + // True if types have been determined. + bool types_are_determined_; + // True if the call is an argument to a defer statement. + bool is_deferred_; + // True if we reported an error about a mismatch between call + // results and uses. This is to avoid producing multiple errors + // when there are multiple Call_result_expressions. + bool issued_error_; +}; + +// An expression which represents a pointer to a function. + +class Func_expression : public Expression +{ + public: + Func_expression(Named_object* function, Expression* closure, + Location location) + : Expression(EXPRESSION_FUNC_REFERENCE, location), + function_(function), closure_(closure) + { } + + // Return the object associated with the function. + Named_object* + named_object() const + { return this->function_; } + + // Return the closure for this function. This will return NULL if + // the function has no closure, which is the normal case. + Expression* + closure() + { return this->closure_; } + + // Return a backend expression for the code of a function. + static Bexpression* + get_code_pointer(Gogo*, Named_object* function, Location loc); + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { + if (this->closure_ != NULL) + this->closure_->determine_type_no_context(); + } + + Expression* + do_copy() + { + return Expression::make_func_reference(this->function_, + (this->closure_ == NULL + ? NULL + : this->closure_->copy()), + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The function itself. + Named_object* function_; + // A closure. This is normally NULL. For a nested function, it may + // be a struct holding pointers to all the variables referenced by + // this function and defined in enclosing functions. + Expression* closure_; +}; + +// A function descriptor. A function descriptor is a struct with a +// single field pointing to the function code. This is used for +// functions without closures. + +class Func_descriptor_expression : public Expression +{ + public: + Func_descriptor_expression(Named_object* fn); + + // Make the function descriptor type, so that it can be converted. + static void + make_func_descriptor_type(); + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return Expression::make_func_descriptor(this->fn_); } + + bool + do_is_addressable() const + { return true; } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context* context) const; + + private: + // The type of all function descriptors. + static Type* descriptor_type; + + // The function for which this is the descriptor. + Named_object* fn_; + // The descriptor variable. + Bvariable* dvar_; +}; + +// A reference to an unknown name. + +class Unknown_expression : public Parser_expression +{ + public: + Unknown_expression(Named_object* named_object, Location location) + : Parser_expression(EXPRESSION_UNKNOWN_REFERENCE, location), + named_object_(named_object), no_error_message_(false), + is_composite_literal_key_(false) + { } + + // The associated named object. + Named_object* + named_object() const + { return this->named_object_; } + + // The name of the identifier which was unknown. + const std::string& + name() const; + + // Call this to indicate that we should not give an error if this + // name is never defined. This is used to avoid knock-on errors + // during an erroneous parse. + void + set_no_error_message() + { this->no_error_message_ = true; } + + // Note that this expression is being used as the key in a composite + // literal, so it may be OK if it is not resolved. + void + set_is_composite_literal_key() + { this->is_composite_literal_key_ = true; } + + // Note that this expression should no longer be treated as a + // composite literal key. + void + clear_is_composite_literal_key() + { this->is_composite_literal_key_ = false; } + + protected: + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_copy() + { return new Unknown_expression(this->named_object_, this->location()); } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The unknown name. + Named_object* named_object_; + // True if we should not give errors if this is undefined. This is + // used if there was a parse failure. + bool no_error_message_; + // True if this is the key in a composite literal. + bool is_composite_literal_key_; +}; + +// An index expression. This is lowered to an array index, a string +// index, or a map index. + +class Index_expression : public Parser_expression +{ + public: + Index_expression(Expression* left, Expression* start, Expression* end, + Expression* cap, Location location) + : Parser_expression(EXPRESSION_INDEX, location), + left_(left), start_(start), end_(end), cap_(cap), is_lvalue_(false) + { } + + // Record that this expression is an lvalue. + void + set_is_lvalue() + { this->is_lvalue_ = true; } + + // Dump an index expression, i.e. an expression of the form + // expr[expr], expr[expr:expr], or expr[expr:expr:expr] to a dump context. + static void + dump_index_expression(Ast_dump_context*, const Expression* expr, + const Expression* start, const Expression* end, + const Expression* cap); + + protected: + int + do_traverse(Traverse*); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_copy() + { + return new Index_expression(this->left_->copy(), this->start_->copy(), + (this->end_ == NULL + ? NULL + : this->end_->copy()), + (this->cap_ == NULL + ? NULL + : this->cap_->copy()), + this->location()); + } + + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->left_->issue_nil_check(); } + private: + // The expression being indexed. + Expression* left_; + // The first index. + Expression* start_; + // The second index. This is NULL for an index, non-NULL for a + // slice. + Expression* end_; + // The capacity argument. This is NULL for indices and slices that use the + // default capacity, non-NULL for indices and slices that specify the + // capacity. + Expression* cap_; + // Whether this is being used as an l-value. We set this during the + // parse because map index expressions need to know. + bool is_lvalue_; +}; + +// An index into a map. + +class Map_index_expression : public Expression +{ + public: + Map_index_expression(Expression* map, Expression* index, + Location location) + : Expression(EXPRESSION_MAP_INDEX, location), + map_(map), index_(index), is_lvalue_(false), + is_in_tuple_assignment_(false) + { } + + // Return the map. + Expression* + map() + { return this->map_; } + + const Expression* + map() const + { return this->map_; } + + // Return the index. + Expression* + index() + { return this->index_; } + + const Expression* + index() const + { return this->index_; } + + // Get the type of the map being indexed. + Map_type* + get_map_type() const; + + // Record that this map expression is an lvalue. The difference is + // that an lvalue always inserts the key. + void + set_is_lvalue() + { this->is_lvalue_ = true; } + + // Return whether this map expression occurs in an assignment to a + // pair of values. + bool + is_in_tuple_assignment() const + { return this->is_in_tuple_assignment_; } + + // Record that this map expression occurs in an assignment to a pair + // of values. + void + set_is_in_tuple_assignment() + { this->is_in_tuple_assignment_ = true; } + + // Return a tree for the map index. This returns a tree which + // evaluates to a pointer to a value in the map. If INSERT is true, + // the key will be inserted if not present, and the value pointer + // will be zero initialized. If INSERT is false, and the key is not + // present in the map, the pointer will be NULL. + tree + get_value_pointer(Translate_context*, bool insert); + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_map_index(this->map_->copy(), + this->index_->copy(), + this->location()); + } + + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + + // A map index expression is an lvalue but it is not addressable. + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The map we are looking into. + Expression* map_; + // The index. + Expression* index_; + // Whether this is an lvalue. + bool is_lvalue_; + // Whether this is in a tuple assignment to a pair of values. + bool is_in_tuple_assignment_; +}; + +// An expression which represents a method bound to its first +// argument. + +class Bound_method_expression : public Expression +{ + public: + Bound_method_expression(Expression* expr, const Method *method, + Named_object* function, Location location) + : Expression(EXPRESSION_BOUND_METHOD, location), + expr_(expr), expr_type_(NULL), method_(method), function_(function) + { } + + // Return the object which is the first argument. + Expression* + first_argument() + { return this->expr_; } + + // Return the implicit type of the first argument. This will be + // non-NULL when using a method from an anonymous field without + // using an explicit stub. + Type* + first_argument_type() const + { return this->expr_type_; } + + // Return the method. + const Method* + method() const + { return this->method_; } + + // Return the function to call. + Named_object* + function() const + { return this->function_; } + + // Set the implicit type of the expression. + void + set_first_argument_type(Type* type) + { this->expr_type_ = type; } + + // Create a thunk to call FUNCTION, for METHOD, when it is used as + // part of a method value. + static Named_object* + create_thunk(Gogo*, const Method* method, Named_object* function); + + protected: + int + do_traverse(Traverse*); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Bound_method_expression(this->expr_->copy(), this->method_, + this->function_, this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // A mapping from method functions to the thunks we have created for + // them. + typedef Unordered_map(Named_object*, Named_object*) Method_value_thunks; + static Method_value_thunks method_value_thunks; + + // The object used to find the method. This is passed to the method + // as the first argument. + Expression* expr_; + // The implicit type of the object to pass to the method. This is + // NULL in the normal case, non-NULL when using a method from an + // anonymous field which does not require a stub. + Type* expr_type_; + // The method. + const Method* method_; + // The function to call. This is not the same as + // method_->named_object() when the method has a stub. This will be + // the real function rather than the stub. + Named_object* function_; +}; + +// A reference to a field in a struct. + +class Field_reference_expression : public Expression +{ + public: + Field_reference_expression(Expression* expr, unsigned int field_index, + Location location) + : Expression(EXPRESSION_FIELD_REFERENCE, location), + expr_(expr), field_index_(field_index), implicit_(false), called_fieldtrack_(false) + { } + + // Return the struct expression. + Expression* + expr() const + { return this->expr_; } + + // Return the field index. + unsigned int + field_index() const + { return this->field_index_; } + + // Return whether this node was implied by an anonymous field. + bool + implicit() const + { return this->implicit_; } + + void + set_implicit(bool implicit) + { this->implicit_ = implicit; } + + // Set the struct expression. This is used when parsing. + void + set_struct_expression(Expression* expr) + { + go_assert(this->expr_ == NULL); + this->expr_ = expr; + } + + protected: + int + do_traverse(Traverse* traverse) + { return Expression::traverse(&this->expr_, traverse); } + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { this->expr_->determine_type_no_context(); } + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_field_reference(this->expr_->copy(), + this->field_index_, + this->location()); + } + + bool + do_is_addressable() const + { return this->expr_->is_addressable(); } + + void + do_address_taken(bool escapes) + { this->expr_->address_taken(escapes); } + + void + do_issue_nil_check() + { this->expr_->issue_nil_check(); } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The expression we are looking into. This should have a type of + // struct. + Expression* expr_; + // The zero-based index of the field we are retrieving. + unsigned int field_index_; + // Whether this node was emitted implicitly for an embedded field, + // that is, expr_ is not the expr_ of the original user node. + bool implicit_; + // Whether we have already emitted a fieldtrack call. + bool called_fieldtrack_; +}; + +// A reference to a field of an interface. + +class Interface_field_reference_expression : public Expression +{ + public: + Interface_field_reference_expression(Expression* expr, + const std::string& name, + Location location) + : Expression(EXPRESSION_INTERFACE_FIELD_REFERENCE, location), + expr_(expr), name_(name) + { } + + // Return the expression for the interface object. + Expression* + expr() + { return this->expr_; } + + // Return the name of the method to call. + const std::string& + name() const + { return this->name_; } + + // Create a thunk to call the method NAME in TYPE when it is used as + // part of a method value. + static Named_object* + create_thunk(Gogo*, Interface_type* type, const std::string& name); + + // Return an expression for the pointer to the function to call. + Expression* + get_function(); + + // Return an expression for the first argument to pass to the interface + // function. This is the real object associated with the interface object. + Expression* + get_underlying_object(); + + protected: + int + do_traverse(Traverse* traverse); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_interface_field_reference(this->expr_->copy(), + this->name_, + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // A mapping from interface types to a list of thunks we have + // created for methods. + typedef std::vector > Method_thunks; + typedef Unordered_map(Interface_type*, Method_thunks*) + Interface_method_thunks; + static Interface_method_thunks interface_method_thunks; + + // The expression for the interface object. This should have a type + // of interface or pointer to interface. + Expression* expr_; + // The field we are retrieving--the name of the method. + std::string name_; +}; + +// A type guard expression. + +class Type_guard_expression : public Expression +{ + public: + Type_guard_expression(Expression* expr, Type* type, Location location) + : Expression(EXPRESSION_TYPE_GUARD, location), + expr_(expr), type_(type) + { } + + // Return the expression to convert. + Expression* + expr() + { return this->expr_; } + + // Return the type to which to convert. + Type* + type() + { return this->type_; } + + protected: + int + do_traverse(Traverse* traverse); + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*) + { this->expr_->determine_type_no_context(); } + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Type_guard_expression(this->expr_->copy(), this->type_, + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The expression to convert. + Expression* expr_; + // The type to which to convert. + Type* type_; +}; + +// A receive expression. + +class Receive_expression : public Expression +{ + public: + Receive_expression(Expression* channel, Location location) + : Expression(EXPRESSION_RECEIVE, location), + channel_(channel) + { } + + // Return the channel. + Expression* + channel() + { return this->channel_; } + + protected: + int + do_traverse(Traverse* traverse) + { return Expression::traverse(&this->channel_, traverse); } + + bool + do_discarding_value() + { return true; } + + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { this->channel_->determine_type_no_context(); } + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_receive(this->channel_->copy(), this->location()); + } + + bool + do_must_eval_in_order() const + { return true; } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The channel from which we are receiving. + Expression* channel_; +}; + +// A numeric constant. This is used both for untyped constants and +// for constants that have a type. + +class Numeric_constant +{ + public: + Numeric_constant() + : classification_(NC_INVALID), type_(NULL) + { } + + ~Numeric_constant(); + + Numeric_constant(const Numeric_constant&); + + Numeric_constant& operator=(const Numeric_constant&); + + // Set to an unsigned long value. + void + set_unsigned_long(Type*, unsigned long); + + // Set to an integer value. + void + set_int(Type*, const mpz_t); + + // Set to a rune value. + void + set_rune(Type*, const mpz_t); + + // Set to a floating point value. + void + set_float(Type*, const mpfr_t); + + // Set to a complex value. + void + set_complex(Type*, const mpfr_t, const mpfr_t); + + // Classifiers. + bool + is_int() const + { return this->classification_ == Numeric_constant::NC_INT; } + + bool + is_rune() const + { return this->classification_ == Numeric_constant::NC_RUNE; } + + bool + is_float() const + { return this->classification_ == Numeric_constant::NC_FLOAT; } + + bool + is_complex() const + { return this->classification_ == Numeric_constant::NC_COMPLEX; } + + // Value retrievers. These will initialize the values as well as + // set them. GET_INT is only valid if IS_INT returns true, and + // likewise respectively. + void + get_int(mpz_t*) const; + + void + get_rune(mpz_t*) const; + + void + get_float(mpfr_t*) const; + + void + get_complex(mpfr_t*, mpfr_t*) const; + + // Codes returned by to_unsigned_long. + enum To_unsigned_long + { + // Value is integer and fits in unsigned long. + NC_UL_VALID, + // Value is not integer. + NC_UL_NOTINT, + // Value is integer but is negative. + NC_UL_NEGATIVE, + // Value is non-negative integer but does not fit in unsigned + // long. + NC_UL_BIG + }; + + // If the value can be expressed as an integer that fits in an + // unsigned long, set *VAL and return NC_UL_VALID. Otherwise return + // one of the other To_unsigned_long codes. + To_unsigned_long + to_unsigned_long(unsigned long* val) const; + + // If the value can be expressed as an int, return true and + // initialize and set VAL. This will return false for a value with + // an explicit float or complex type, even if the value is integral. + bool + to_int(mpz_t* val) const; + + // If the value can be expressed as a float, return true and + // initialize and set VAL. + bool + to_float(mpfr_t* val) const; + + // If the value can be expressed as a complex, return true and + // initialize and set VR and VI. + bool + to_complex(mpfr_t* vr, mpfr_t* vi) const; + + // Get the type. + Type* + type() const; + + // If the constant can be expressed in TYPE, then set the type of + // the constant to TYPE and return true. Otherwise return false, + // and, if ISSUE_ERROR is true, issue an error message. LOCATION is + // the location to use for the error. + bool + set_type(Type* type, bool issue_error, Location location); + + // Return an Expression for this value. + Expression* + expression(Location) const; + + private: + void + clear(); + + To_unsigned_long + mpz_to_unsigned_long(const mpz_t ival, unsigned long *val) const; + + To_unsigned_long + mpfr_to_unsigned_long(const mpfr_t fval, unsigned long *val) const; + + bool + check_int_type(Integer_type*, bool, Location) const; + + bool + check_float_type(Float_type*, bool, Location); + + bool + check_complex_type(Complex_type*, bool, Location); + + // The kinds of constants. + enum Classification + { + NC_INVALID, + NC_RUNE, + NC_INT, + NC_FLOAT, + NC_COMPLEX + }; + + // The kind of constant. + Classification classification_; + // The value. + union + { + // If NC_INT or NC_RUNE. + mpz_t int_val; + // If NC_FLOAT. + mpfr_t float_val; + // If NC_COMPLEX. + struct + { + mpfr_t real; + mpfr_t imag; + } complex_val; + } u_; + // The type if there is one. This will be NULL for an untyped + // constant. + Type* type_; +}; + +#endif // !defined(GO_EXPRESSIONS_H) diff --git a/gcc-4.9/gcc/go/gofrontend/go-dump.cc b/gcc-4.9/gcc/go/gofrontend/go-dump.cc new file mode 100644 index 000000000..dd5a4c3f2 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/go-dump.cc @@ -0,0 +1,53 @@ +// go-dump.cc -- Go frontend debug dumps. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "go-c.h" +#include "go-dump.h" + +namespace { + +// The list of dumps. + +Go_dump* dumps; + +} // End empty namespace. + +// Create a new dump. + +Go_dump::Go_dump(const char* name) + : next_(dumps), name_(name), is_enabled_(false) +{ + dumps = this; +} + +// Enable a dump by name. + +bool +Go_dump::enable_by_name(const char* name) +{ + bool is_all = strcmp(name, "all") == 0; + bool found = false; + for (Go_dump* p = dumps; p != NULL; p = p->next_) + { + if (is_all || strcmp(name, p->name_) == 0) + { + p->is_enabled_ = true; + found = true; + } + } + return found; +} + +// Enable a dump. Return 1 if this is a real name, 0 if not. + +GO_EXTERN_C +int +go_enable_dump(const char* name) +{ + return Go_dump::enable_by_name(name) ? 1 : 0; +} diff --git a/gcc-4.9/gcc/go/gofrontend/go-dump.h b/gcc-4.9/gcc/go/gofrontend/go-dump.h new file mode 100644 index 000000000..13639bc12 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/go-dump.h @@ -0,0 +1,38 @@ +// go-dump.h -- Go frontend debug dumps. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_DUMP_H +#define GO_DUMP_H + +// This class manages different arguments to -fgo-dump-XXX. If you +// want to create a new dump, create a variable of this type with the +// name to use for XXX. You can then use is_enabled to see whether +// the -fgo-dump-XXX option was used on the command line. + +class Go_dump +{ + public: + Go_dump(const char* name); + + // Whether this dump was enabled. + bool + is_enabled() const + { return this->is_enabled_; } + + // Enable a dump by name. Return true if the dump was found. + static bool + enable_by_name(const char*); + + private: + // The next dump. These are not in any order. + Go_dump* next_; + // The name of this dump. + const char* name_; + // Whether this dump was enabled. + bool is_enabled_; +}; + +#endif // !defined(GO_DUMP_H) diff --git a/gcc-4.9/gcc/go/gofrontend/go-linemap.h b/gcc-4.9/gcc/go/gofrontend/go-linemap.h new file mode 100644 index 000000000..ffbcbe778 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/go-linemap.h @@ -0,0 +1,131 @@ +// go-linemap.h -- interface to location tracking -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_LINEMAP_H +#define GO_LINEMAP_H + +#include "go-system.h" + +// The backend must define a type named Location which holds +// information about a location in a source file. The only thing the +// frontend does with instances of Location is pass them back to the +// backend interface. The Location type must be assignable, and it +// must be comparable: i.e., it must support operator= and operator<. +// The type is normally passed by value rather than by reference, and +// it should support that efficiently. The type should be defined in +// "go-location.h". + +#include "go-location.h" + +// The Linemap class is a pure abstract interface, plus some static +// convenience functions. The backend must implement the interface. + +class Linemap +{ + public: + Linemap() + { + // Only one instance of Linemap is allowed to exist. + go_assert(Linemap::instance_ == NULL); + Linemap::instance_ = this; + } + + virtual + ~Linemap() { Linemap::instance_ = NULL; } + + // Subsequent Location values will come from the file named + // FILE_NAME, starting at LINE_BEGIN. Normally LINE_BEGIN will be + // 0, but it will be non-zero if the Go source has a //line comment. + virtual void + start_file(const char* file_name, unsigned int line_begin) = 0; + + // Subsequent Location values will come from the line LINE_NUMBER, + // in the current file. LINE_SIZE is the size of the line in bytes. + // This will normally be called for every line in a source file. + virtual void + start_line(unsigned int line_number, unsigned int line_size) = 0; + + // Get a Location representing column position COLUMN on the current + // line in the current file. + virtual Location + get_location(unsigned int column) = 0; + + // Stop generating Location values. This will be called after all + // input files have been read, in case any cleanup is required. + virtual void + stop() = 0; + + protected: + // Return a special Location used for predeclared identifiers. This + // Location should be different from that for any actual source + // file. This location will be used for various different types, + // functions, and objects created by the frontend. + virtual Location + get_predeclared_location() = 0; + + // Return a special Location which indicates that no actual location + // is known. This is used for undefined objects and for errors. + virtual Location + get_unknown_location() = 0; + + // Return whether the argument is the Location returned by + // get_predeclared_location. + virtual bool + is_predeclared(Location) = 0; + + // Return whether the argument is the Location returned by + // get_unknown_location. + virtual bool + is_unknown(Location) = 0; + + // The single existing instance of Linemap. + static Linemap *instance_; + + public: + // Following are convenience static functions, which allow us to + // access some virtual functions without explicitly passing around + // an instance of Linemap. + + // Return the special Location used for predeclared identifiers. + static Location + predeclared_location() + { + go_assert(Linemap::instance_ != NULL); + return Linemap::instance_->get_predeclared_location(); + } + + // Return the special Location used when no location is known. + static Location + unknown_location() + { + go_assert(Linemap::instance_ != NULL); + return Linemap::instance_->get_unknown_location(); + } + + // Return whether the argument is the special location used for + // predeclared identifiers. + static bool + is_predeclared_location(Location loc) + { + go_assert(Linemap::instance_ != NULL); + return Linemap::instance_->is_predeclared(loc); + } + + // Return whether the argument is the special location used when no + // location is known. + static bool + is_unknown_location(Location loc) + { + go_assert(Linemap::instance_ != NULL); + return Linemap::instance_->is_unknown(loc); + } +}; + +// The backend interface must define this function. It should return +// a fully implemented instance of Linemap. +extern Linemap* go_get_linemap(); + +#endif // !defined(GO_LINEMAP_H) diff --git a/gcc-4.9/gcc/go/gofrontend/go-optimize.cc b/gcc-4.9/gcc/go/gofrontend/go-optimize.cc new file mode 100644 index 000000000..6da934f4d --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/go-optimize.cc @@ -0,0 +1,53 @@ +// go-optimize.cc -- Go frontend optimizer flags. + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "go-c.h" +#include "go-optimize.h" + +namespace { + +// The list of optimizations. + +Go_optimize* optimizations; + +} // End empty namespace. + +// Create a new optimization. + +Go_optimize::Go_optimize(const char* name) + : next_(optimizations), name_(name), is_enabled_(false) +{ + optimizations = this; +} + +// Enable an optimization by name. + +bool +Go_optimize::enable_by_name(const char* name) +{ + bool is_all = strcmp(name, "all") == 0; + bool found = false; + for (Go_optimize* p = optimizations; p != NULL; p = p->next_) + { + if (is_all || strcmp(name, p->name_) == 0) + { + p->is_enabled_ = true; + found = true; + } + } + return found; +} + +// Enable an optimization. Return 1 if this is a real name, 0 if not. + +GO_EXTERN_C +int +go_enable_optimize(const char* name) +{ + return Go_optimize::enable_by_name(name) ? 1 : 0; +} diff --git a/gcc-4.9/gcc/go/gofrontend/go-optimize.h b/gcc-4.9/gcc/go/gofrontend/go-optimize.h new file mode 100644 index 000000000..8638498e1 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/go-optimize.h @@ -0,0 +1,38 @@ +// go-optimize.h -- Go frontend optimizer flags. -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_OPTIMIZE_H +#define GO_OPTIMIZE_H + +// This class manages different arguments to -fgo-optimize-XXX. If you +// want to create a new optimization, create a variable of this type with the +// name to use for XXX. You can then use is_enabled to see whether +// the -fgo-optimize-XXX option was used on the command line. + +class Go_optimize +{ + public: + Go_optimize(const char* name); + + // Whether this optimizaiton was enabled. + bool + is_enabled() const + { return this->is_enabled_; } + + // Enable an optimization by name. Return true if found. + static bool + enable_by_name(const char*); + + private: + // The next optimize flag. These are not in any order. + Go_optimize* next_; + // The name of this optimization pass. + const char* name_; + // Whether this dump was enabled. + bool is_enabled_; +}; + +#endif // !defined(GO_OPTIMIZE_H) diff --git a/gcc-4.9/gcc/go/gofrontend/go.cc b/gcc-4.9/gcc/go/gofrontend/go.cc new file mode 100644 index 000000000..ac772a095 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/go.cc @@ -0,0 +1,154 @@ +// go.cc -- Go frontend main file for gcc. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "go-c.h" + +#include "lex.h" +#include "parse.h" +#include "backend.h" +#include "gogo.h" + +// The data structures we build to represent the file. +static Gogo* gogo; + +// Create the main IR data structure. + +GO_EXTERN_C +void +go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath, + const char *prefix, const char *relative_import_path) +{ + go_assert(::gogo == NULL); + Linemap* linemap = go_get_linemap(); + ::gogo = new Gogo(go_get_backend(), linemap, int_type_size, pointer_size); + + if (pkgpath != NULL) + ::gogo->set_pkgpath(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(); +} + +// Parse the input files. + +GO_EXTERN_C +void +go_parse_input_files(const char** filenames, unsigned int filename_count, + bool only_check_syntax, bool) +{ + go_assert(filename_count > 0); + + for (unsigned int i = 0; i < filename_count; ++i) + { + if (i > 0) + ::gogo->clear_file_scope(); + + const char* filename = filenames[i]; + FILE* file; + if (strcmp(filename, "-") == 0) + file = stdin; + else + { + file = fopen(filename, "r"); + if (file == NULL) + fatal_error("cannot open %s: %m", filename); + } + + Lex lexer(filename, file, ::gogo->linemap()); + + Parse parse(&lexer, ::gogo); + parse.program(); + + if (strcmp(filename, "-") != 0) + fclose(file); + } + + ::gogo->linemap()->stop(); + + ::gogo->clear_file_scope(); + + // If the global predeclared names are referenced but not defined, + // define them now. + ::gogo->define_global_names(); + + // Finalize method lists and build stub methods for named types. + ::gogo->finalize_methods(); + + // Check that functions have a terminating statement. + ::gogo->check_return_statements(); + + // Now that we have seen all the names, lower the parse tree into a + // form which is easier to use. + ::gogo->lower_parse_tree(); + + // Create function descriptors as needed. + ::gogo->create_function_descriptors(); + + // Write out queued up functions for hash and comparison of types. + ::gogo->write_specific_type_functions(); + + // Now that we have seen all the names, verify that types are + // correct. + ::gogo->verify_types(); + + // Work out types of unspecified constants and variables. + ::gogo->determine_types(); + + // Check types and issue errors as appropriate. + ::gogo->check_types(); + + if (only_check_syntax) + return; + + // Export global identifiers as appropriate. + ::gogo->do_exports(); + + // Turn short-cut operators (&&, ||) into explicit if statements. + ::gogo->remove_shortcuts(); + + // Use temporary variables to force order of evaluation. + ::gogo->order_evaluations(); + + // Convert named types to backend representation. + ::gogo->convert_named_types(); + + // Flatten the parse tree. + ::gogo->flatten(); + + // Build thunks for functions which call recover. + ::gogo->build_recover_thunks(); + + // Convert complicated go and defer statements into simpler ones. + ::gogo->simplify_thunk_statements(); + + // Dump ast, use filename[0] as the base name + ::gogo->dump_ast(filenames[0]); +} + +// Write out globals. + +GO_EXTERN_C +void +go_write_globals() +{ + return ::gogo->write_globals(); +} + +// Return the global IR structure. This is used by some of the +// langhooks to pass to other code. + +Gogo* +go_get_gogo() +{ + return ::gogo; +} diff --git a/gcc-4.9/gcc/go/gofrontend/gogo-tree.cc b/gcc-4.9/gcc/go/gofrontend/gogo-tree.cc new file mode 100644 index 000000000..1950090b9 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/gogo-tree.cc @@ -0,0 +1,2319 @@ +// gogo-tree.cc -- convert Go frontend Gogo IR to gcc trees. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "toplev.h" +#include "tree.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "varasm.h" +#include "gimple-expr.h" +#include "gimplify.h" +#include "tree-iterator.h" +#include "cgraph.h" +#include "langhooks.h" +#include "convert.h" +#include "output.h" +#include "diagnostic.h" +#include "go-c.h" + +#include "types.h" +#include "expressions.h" +#include "statements.h" +#include "runtime.h" +#include "backend.h" +#include "gogo.h" + +// Whether we have seen any errors. + +bool +saw_errors() +{ + return errorcount != 0 || sorrycount != 0; +} + +// A helper function. + +static inline tree +get_identifier_from_string(const std::string& str) +{ + return get_identifier_with_length(str.data(), str.length()); +} + +// Builtin functions. + +static std::map builtin_functions; + +// Define a builtin function. BCODE is the builtin function code +// defined by builtins.def. NAME is the name of the builtin function. +// LIBNAME is the name of the corresponding library function, and is +// NULL if there isn't one. FNTYPE is the type of the function. +// CONST_P is true if the function has the const attribute. + +static void +define_builtin(built_in_function bcode, const char* name, const char* libname, + tree fntype, bool const_p) +{ + tree decl = add_builtin_function(name, fntype, bcode, BUILT_IN_NORMAL, + libname, NULL_TREE); + if (const_p) + TREE_READONLY(decl) = 1; + set_builtin_decl(bcode, decl, true); + builtin_functions[name] = decl; + if (libname != NULL) + { + decl = add_builtin_function(libname, fntype, bcode, BUILT_IN_NORMAL, + NULL, NULL_TREE); + if (const_p) + TREE_READONLY(decl) = 1; + builtin_functions[libname] = decl; + } +} + +// Create trees for implicit builtin functions. + +void +Gogo::define_builtin_function_trees() +{ + /* We need to define the fetch_and_add functions, since we use them + for ++ and --. */ + tree t = go_type_for_size(BITS_PER_UNIT, 1); + tree p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE)); + define_builtin(BUILT_IN_SYNC_ADD_AND_FETCH_1, "__sync_fetch_and_add_1", NULL, + build_function_type_list(t, p, t, NULL_TREE), false); + + t = go_type_for_size(BITS_PER_UNIT * 2, 1); + p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE)); + define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_2, "__sync_fetch_and_add_2", NULL, + build_function_type_list(t, p, t, NULL_TREE), false); + + t = go_type_for_size(BITS_PER_UNIT * 4, 1); + p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE)); + define_builtin(BUILT_IN_SYNC_ADD_AND_FETCH_4, "__sync_fetch_and_add_4", NULL, + build_function_type_list(t, p, t, NULL_TREE), false); + + t = go_type_for_size(BITS_PER_UNIT * 8, 1); + p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE)); + define_builtin(BUILT_IN_SYNC_ADD_AND_FETCH_8, "__sync_fetch_and_add_8", NULL, + build_function_type_list(t, p, t, NULL_TREE), false); + + // We use __builtin_expect for magic import functions. + define_builtin(BUILT_IN_EXPECT, "__builtin_expect", NULL, + build_function_type_list(long_integer_type_node, + long_integer_type_node, + long_integer_type_node, + NULL_TREE), + true); + + // We use __builtin_memcmp for struct comparisons. + define_builtin(BUILT_IN_MEMCMP, "__builtin_memcmp", "memcmp", + build_function_type_list(integer_type_node, + const_ptr_type_node, + const_ptr_type_node, + size_type_node, + NULL_TREE), + false); + + // We provide some functions for the math library. + tree math_function_type = build_function_type_list(double_type_node, + double_type_node, + NULL_TREE); + tree math_function_type_long = + build_function_type_list(long_double_type_node, long_double_type_node, + long_double_type_node, NULL_TREE); + tree math_function_type_two = build_function_type_list(double_type_node, + double_type_node, + double_type_node, + NULL_TREE); + tree math_function_type_long_two = + build_function_type_list(long_double_type_node, long_double_type_node, + long_double_type_node, NULL_TREE); + define_builtin(BUILT_IN_ACOS, "__builtin_acos", "acos", + math_function_type, true); + define_builtin(BUILT_IN_ACOSL, "__builtin_acosl", "acosl", + math_function_type_long, true); + define_builtin(BUILT_IN_ASIN, "__builtin_asin", "asin", + math_function_type, true); + define_builtin(BUILT_IN_ASINL, "__builtin_asinl", "asinl", + math_function_type_long, true); + define_builtin(BUILT_IN_ATAN, "__builtin_atan", "atan", + math_function_type, true); + define_builtin(BUILT_IN_ATANL, "__builtin_atanl", "atanl", + math_function_type_long, true); + define_builtin(BUILT_IN_ATAN2, "__builtin_atan2", "atan2", + math_function_type_two, true); + define_builtin(BUILT_IN_ATAN2L, "__builtin_atan2l", "atan2l", + math_function_type_long_two, true); + define_builtin(BUILT_IN_CEIL, "__builtin_ceil", "ceil", + math_function_type, true); + define_builtin(BUILT_IN_CEILL, "__builtin_ceill", "ceill", + math_function_type_long, true); + define_builtin(BUILT_IN_COS, "__builtin_cos", "cos", + math_function_type, true); + define_builtin(BUILT_IN_COSL, "__builtin_cosl", "cosl", + math_function_type_long, true); + define_builtin(BUILT_IN_EXP, "__builtin_exp", "exp", + math_function_type, true); + define_builtin(BUILT_IN_EXPL, "__builtin_expl", "expl", + math_function_type_long, true); + define_builtin(BUILT_IN_EXPM1, "__builtin_expm1", "expm1", + math_function_type, true); + define_builtin(BUILT_IN_EXPM1L, "__builtin_expm1l", "expm1l", + math_function_type_long, true); + define_builtin(BUILT_IN_FABS, "__builtin_fabs", "fabs", + math_function_type, true); + define_builtin(BUILT_IN_FABSL, "__builtin_fabsl", "fabsl", + math_function_type_long, true); + define_builtin(BUILT_IN_FLOOR, "__builtin_floor", "floor", + math_function_type, true); + define_builtin(BUILT_IN_FLOORL, "__builtin_floorl", "floorl", + math_function_type_long, true); + define_builtin(BUILT_IN_FMOD, "__builtin_fmod", "fmod", + math_function_type_two, true); + define_builtin(BUILT_IN_FMODL, "__builtin_fmodl", "fmodl", + math_function_type_long_two, true); + define_builtin(BUILT_IN_LDEXP, "__builtin_ldexp", "ldexp", + build_function_type_list(double_type_node, + double_type_node, + integer_type_node, + NULL_TREE), + true); + define_builtin(BUILT_IN_LDEXPL, "__builtin_ldexpl", "ldexpl", + build_function_type_list(long_double_type_node, + long_double_type_node, + integer_type_node, + NULL_TREE), + true); + define_builtin(BUILT_IN_LOG, "__builtin_log", "log", + math_function_type, true); + define_builtin(BUILT_IN_LOGL, "__builtin_logl", "logl", + math_function_type_long, true); + define_builtin(BUILT_IN_LOG1P, "__builtin_log1p", "log1p", + math_function_type, true); + define_builtin(BUILT_IN_LOG1PL, "__builtin_log1pl", "log1pl", + math_function_type_long, true); + define_builtin(BUILT_IN_LOG10, "__builtin_log10", "log10", + math_function_type, true); + define_builtin(BUILT_IN_LOG10L, "__builtin_log10l", "log10l", + math_function_type_long, true); + define_builtin(BUILT_IN_LOG2, "__builtin_log2", "log2", + math_function_type, true); + define_builtin(BUILT_IN_LOG2L, "__builtin_log2l", "log2l", + math_function_type_long, true); + define_builtin(BUILT_IN_SIN, "__builtin_sin", "sin", + math_function_type, true); + define_builtin(BUILT_IN_SINL, "__builtin_sinl", "sinl", + math_function_type_long, true); + define_builtin(BUILT_IN_SQRT, "__builtin_sqrt", "sqrt", + math_function_type, true); + define_builtin(BUILT_IN_SQRTL, "__builtin_sqrtl", "sqrtl", + math_function_type_long, true); + define_builtin(BUILT_IN_TAN, "__builtin_tan", "tan", + math_function_type, true); + define_builtin(BUILT_IN_TANL, "__builtin_tanl", "tanl", + math_function_type_long, true); + define_builtin(BUILT_IN_TRUNC, "__builtin_trunc", "trunc", + math_function_type, true); + define_builtin(BUILT_IN_TRUNCL, "__builtin_truncl", "truncl", + math_function_type_long, true); + + // We use __builtin_return_address in the thunk we build for + // functions which call recover. + define_builtin(BUILT_IN_RETURN_ADDRESS, "__builtin_return_address", NULL, + build_function_type_list(ptr_type_node, + unsigned_type_node, + NULL_TREE), + false); + + // The compiler uses __builtin_trap for some exception handling + // cases. + define_builtin(BUILT_IN_TRAP, "__builtin_trap", NULL, + build_function_type(void_type_node, void_list_node), + false); +} + +// Get the name to use for the import control function. If there is a +// global function or variable, then we know that that name must be +// unique in the link, and we use it as the basis for our name. + +const std::string& +Gogo::get_init_fn_name() +{ + if (this->init_fn_name_.empty()) + { + go_assert(this->package_ != NULL); + if (this->is_main_package()) + { + // Use a name which the runtime knows. + this->init_fn_name_ = "__go_init_main"; + } + else + { + std::string s = this->pkgpath_symbol(); + s.append("..import"); + this->init_fn_name_ = s; + } + } + + return this->init_fn_name_; +} + +// Add statements to INIT_STMT_LIST which run the initialization +// functions for imported packages. This is only used for the "main" +// package. + +void +Gogo::init_imports(tree* init_stmt_list) +{ + go_assert(this->is_main_package()); + + if (this->imported_init_fns_.empty()) + return; + + tree fntype = build_function_type(void_type_node, void_list_node); + + // We must call them in increasing priority order. + std::vector v; + for (std::set::const_iterator p = + this->imported_init_fns_.begin(); + p != this->imported_init_fns_.end(); + ++p) + v.push_back(*p); + std::sort(v.begin(), v.end()); + + for (std::vector::const_iterator p = v.begin(); + p != v.end(); + ++p) + { + std::string user_name = p->package_name() + ".init"; + tree decl = build_decl(UNKNOWN_LOCATION, FUNCTION_DECL, + get_identifier_from_string(user_name), + fntype); + const std::string& init_name(p->init_name()); + SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(init_name)); + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + append_to_statement_list(build_call_expr(decl, 0), init_stmt_list); + } +} + +// Register global variables with the garbage collector. We need to +// register all variables which can hold a pointer value. They become +// roots during the mark phase. We build a struct that is easy to +// hook into a list of roots. + +// struct __go_gc_root_list +// { +// struct __go_gc_root_list* __next; +// struct __go_gc_root +// { +// void* __decl; +// size_t __size; +// } __roots[]; +// }; + +// The last entry in the roots array has a NULL decl field. + +void +Gogo::register_gc_vars(const std::vector& var_gc, + tree* init_stmt_list) +{ + if (var_gc.empty()) + return; + + size_t count = var_gc.size(); + + tree root_type = Gogo::builtin_struct(NULL, "__go_gc_root", NULL_TREE, 2, + "__next", + ptr_type_node, + "__size", + sizetype); + + tree index_type = build_index_type(size_int(count)); + tree array_type = build_array_type(root_type, index_type); + + tree root_list_type = make_node(RECORD_TYPE); + root_list_type = Gogo::builtin_struct(NULL, "__go_gc_root_list", + root_list_type, 2, + "__next", + build_pointer_type(root_list_type), + "__roots", + array_type); + + // Build an initialier for the __roots array. + + vec *roots_init; + vec_alloc(roots_init, count + 1); + + size_t i = 0; + for (std::vector::const_iterator p = var_gc.begin(); + p != var_gc.end(); + ++p, ++i) + { + vec *init; + vec_alloc(init, 2); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + tree field = TYPE_FIELDS(root_type); + elt->index = field; + Bvariable* bvar = (*p)->get_backend_variable(this, NULL); + tree decl = var_to_tree(bvar); + go_assert(TREE_CODE(decl) == VAR_DECL); + elt->value = build_fold_addr_expr(decl); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + elt->index = field; + elt->value = DECL_SIZE_UNIT(decl); + + elt = roots_init->quick_push(empty); + elt->index = size_int(i); + elt->value = build_constructor(root_type, init); + } + + // The list ends with a NULL entry. + + vec *init; + vec_alloc(init, 2); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + tree field = TYPE_FIELDS(root_type); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), null_pointer_node); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + elt->index = field; + elt->value = size_zero_node; + + elt = roots_init->quick_push(empty); + elt->index = size_int(i); + elt->value = build_constructor(root_type, init); + + // Build a constructor for the struct. + + vec *root_list_init; + vec_alloc(root_list_init, 2); + + elt = root_list_init->quick_push(empty); + field = TYPE_FIELDS(root_list_type); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), null_pointer_node); + + elt = root_list_init->quick_push(empty); + field = DECL_CHAIN(field); + elt->index = field; + elt->value = build_constructor(array_type, roots_init); + + // Build a decl to register. + + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, + create_tmp_var_name("gc"), root_list_type); + DECL_EXTERNAL(decl) = 0; + TREE_PUBLIC(decl) = 0; + TREE_STATIC(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + DECL_INITIAL(decl) = build_constructor(root_list_type, root_list_init); + rest_of_decl_compilation(decl, 1, 0); + + static tree register_gc_fndecl; + tree call = Gogo::call_builtin(®ister_gc_fndecl, + Linemap::predeclared_location(), + "__go_register_gc_roots", + 1, + void_type_node, + build_pointer_type(root_list_type), + build_fold_addr_expr(decl)); + if (call != error_mark_node) + append_to_statement_list(call, init_stmt_list); +} + +// Build the decl for the initialization function. + +tree +Gogo::initialization_function_decl() +{ + // The tedious details of building your own function. There doesn't + // seem to be a helper function for this. + std::string name = this->package_name() + ".init"; + tree fndecl = build_decl(this->package_->location().gcc_location(), + FUNCTION_DECL, get_identifier_from_string(name), + build_function_type(void_type_node, + void_list_node)); + const std::string& asm_name(this->get_init_fn_name()); + SET_DECL_ASSEMBLER_NAME(fndecl, get_identifier_from_string(asm_name)); + + tree resdecl = build_decl(this->package_->location().gcc_location(), + RESULT_DECL, NULL_TREE, void_type_node); + DECL_ARTIFICIAL(resdecl) = 1; + DECL_CONTEXT(resdecl) = fndecl; + DECL_RESULT(fndecl) = resdecl; + + TREE_STATIC(fndecl) = 1; + TREE_USED(fndecl) = 1; + DECL_ARTIFICIAL(fndecl) = 1; + TREE_PUBLIC(fndecl) = 1; + + DECL_INITIAL(fndecl) = make_node(BLOCK); + TREE_USED(DECL_INITIAL(fndecl)) = 1; + + return fndecl; +} + +// Create the magic initialization function. INIT_STMT_LIST is the +// code that it needs to run. + +void +Gogo::write_initialization_function(tree fndecl, tree init_stmt_list) +{ + // Make sure that we thought we needed an initialization function, + // as otherwise we will not have reported it in the export data. + go_assert(this->is_main_package() || this->need_init_fn_); + + if (fndecl == NULL_TREE) + fndecl = this->initialization_function_decl(); + + DECL_SAVED_TREE(fndecl) = init_stmt_list; + + if (DECL_STRUCT_FUNCTION(fndecl) == NULL) + push_struct_function(fndecl); + else + push_cfun(DECL_STRUCT_FUNCTION(fndecl)); + cfun->function_start_locus = this->package_->location().gcc_location(); + cfun->function_end_locus = cfun->function_start_locus; + + gimplify_function_tree(fndecl); + + cgraph_add_new_function(fndecl, false); + + pop_cfun(); +} + +// Search for references to VAR in any statements or called functions. + +class Find_var : public Traverse +{ + public: + // A hash table we use to avoid looping. The index is the name of a + // named object. We only look through objects defined in this + // package. + typedef Unordered_set(const void*) Seen_objects; + + Find_var(Named_object* var, Seen_objects* seen_objects) + : Traverse(traverse_expressions), + var_(var), seen_objects_(seen_objects), found_(false) + { } + + // Whether the variable was found. + bool + found() const + { return this->found_; } + + int + expression(Expression**); + + private: + // The variable we are looking for. + Named_object* var_; + // Names of objects we have already seen. + Seen_objects* seen_objects_; + // True if the variable was found. + bool found_; +}; + +// See if EXPR refers to VAR, looking through function calls and +// variable initializations. + +int +Find_var::expression(Expression** pexpr) +{ + Expression* e = *pexpr; + + Var_expression* ve = e->var_expression(); + if (ve != NULL) + { + Named_object* v = ve->named_object(); + if (v == this->var_) + { + this->found_ = true; + return TRAVERSE_EXIT; + } + + if (v->is_variable() && v->package() == NULL) + { + Expression* init = v->var_value()->init(); + if (init != NULL) + { + std::pair ins = + this->seen_objects_->insert(v); + if (ins.second) + { + // This is the first time we have seen this name. + if (Expression::traverse(&init, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + } + } + + // We traverse the code of any function we see. Note that this + // means that we will traverse the code of a function whose address + // is taken even if it is not called. + Func_expression* fe = e->func_expression(); + if (fe != NULL) + { + const Named_object* f = fe->named_object(); + if (f->is_function() && f->package() == NULL) + { + std::pair ins = + this->seen_objects_->insert(f); + if (ins.second) + { + // This is the first time we have seen this name. + if (f->func_value()->block()->traverse(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + } + + Temporary_reference_expression* tre = e->temporary_reference_expression(); + if (tre != NULL) + { + Temporary_statement* ts = tre->statement(); + Expression* init = ts->init(); + if (init != NULL) + { + std::pair ins = + this->seen_objects_->insert(ts); + if (ins.second) + { + // This is the first time we have seen this temporary + // statement. + if (Expression::traverse(&init, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + } + + return TRAVERSE_CONTINUE; +} + +// Return true if EXPR, PREINIT, or DEP refers to VAR. + +static bool +expression_requires(Expression* expr, Block* preinit, Named_object* dep, + Named_object* var) +{ + Find_var::Seen_objects seen_objects; + Find_var find_var(var, &seen_objects); + if (expr != NULL) + Expression::traverse(&expr, &find_var); + if (preinit != NULL) + preinit->traverse(&find_var); + if (dep != NULL) + { + Expression* init = dep->var_value()->init(); + if (init != NULL) + Expression::traverse(&init, &find_var); + if (dep->var_value()->has_pre_init()) + dep->var_value()->preinit()->traverse(&find_var); + } + + return find_var.found(); +} + +// Sort variable initializations. If the initialization expression +// for variable A refers directly or indirectly to the initialization +// expression for variable B, then we must initialize B before A. + +class Var_init +{ + public: + Var_init() + : var_(NULL), init_(NULL_TREE) + { } + + Var_init(Named_object* var, tree init) + : var_(var), init_(init) + { } + + // Return the variable. + Named_object* + var() const + { return this->var_; } + + // Return the initialization expression. + tree + init() const + { return this->init_; } + + private: + // The variable being initialized. + Named_object* var_; + // The initialization expression to run. + tree init_; +}; + +typedef std::list Var_inits; + +// Sort the variable initializations. The rule we follow is that we +// emit them in the order they appear in the array, except that if the +// initialization expression for a variable V1 depends upon another +// variable V2 then we initialize V1 after V2. + +static void +sort_var_inits(Gogo* gogo, Var_inits* var_inits) +{ + typedef std::pair No_no; + typedef std::map Cache; + Cache cache; + + Var_inits ready; + while (!var_inits->empty()) + { + Var_inits::iterator p1 = var_inits->begin(); + Named_object* var = p1->var(); + Expression* init = var->var_value()->init(); + Block* preinit = var->var_value()->preinit(); + Named_object* dep = gogo->var_depends_on(var->var_value()); + + // Start walking through the list to see which variables VAR + // needs to wait for. + Var_inits::iterator p2 = p1; + ++p2; + + for (; p2 != var_inits->end(); ++p2) + { + Named_object* p2var = p2->var(); + No_no key(var, p2var); + std::pair ins = + cache.insert(std::make_pair(key, false)); + if (ins.second) + ins.first->second = expression_requires(init, preinit, dep, p2var); + if (ins.first->second) + { + // Check for cycles. + key = std::make_pair(p2var, var); + ins = cache.insert(std::make_pair(key, false)); + if (ins.second) + ins.first->second = + expression_requires(p2var->var_value()->init(), + p2var->var_value()->preinit(), + gogo->var_depends_on(p2var->var_value()), + var); + if (ins.first->second) + { + error_at(var->location(), + ("initialization expressions for %qs and " + "%qs depend upon each other"), + var->message_name().c_str(), + p2var->message_name().c_str()); + inform(p2->var()->location(), "%qs defined here", + p2var->message_name().c_str()); + p2 = var_inits->end(); + } + else + { + // We can't emit P1 until P2 is emitted. Move P1. + Var_inits::iterator p3 = p2; + ++p3; + var_inits->splice(p3, *var_inits, p1); + } + break; + } + } + + if (p2 == var_inits->end()) + { + // VAR does not depends upon any other initialization expressions. + + // Check for a loop of VAR on itself. We only do this if + // INIT is not NULL and there is no dependency; when INIT is + // NULL, it means that PREINIT sets VAR, which we will + // interpret as a loop. + if (init != NULL && dep == NULL + && expression_requires(init, preinit, NULL, var)) + error_at(var->location(), + "initialization expression for %qs depends upon itself", + var->message_name().c_str()); + ready.splice(ready.end(), *var_inits, p1); + } + } + + // Now READY is the list in the desired initialization order. + var_inits->swap(ready); +} + +// Write out the global definitions. + +void +Gogo::write_globals() +{ + this->build_interface_method_tables(); + + Bindings* bindings = this->current_bindings(); + + for (Bindings::const_declarations_iterator p = bindings->begin_declarations(); + p != bindings->end_declarations(); + ++p) + { + // If any function declarations needed a descriptor, make sure + // we build it. + Named_object* no = p->second; + if (no->is_function_declaration()) + no->func_declaration_value()->build_backend_descriptor(this); + } + + size_t count_definitions = bindings->size_definitions(); + size_t count = count_definitions; + + tree* vec = new tree[count]; + + tree init_fndecl = NULL_TREE; + tree init_stmt_list = NULL_TREE; + + if (this->is_main_package()) + this->init_imports(&init_stmt_list); + + // A list of variable initializations. + Var_inits var_inits; + + // A list of variables which need to be registered with the garbage + // collector. + std::vector var_gc; + var_gc.reserve(count); + + tree var_init_stmt_list = NULL_TREE; + size_t i = 0; + for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); + p != bindings->end_definitions(); + ++p, ++i) + { + Named_object* no = *p; + + go_assert(i < count); + + go_assert(!no->is_type_declaration() && !no->is_function_declaration()); + // There is nothing to do for a package. + if (no->is_package()) + { + --i; + --count; + continue; + } + + // There is nothing to do for an object which was imported from + // a different package into the global scope. + if (no->package() != NULL) + { + --i; + --count; + continue; + } + + // Skip blank named functions and constants. + if ((no->is_function() && no->func_value()->is_sink()) + || (no->is_const() && no->const_value()->is_sink())) + { + --i; + --count; + continue; + } + + // There is nothing useful we can output for constants which + // have ideal or non-integral type. + if (no->is_const()) + { + Type* type = no->const_value()->type(); + if (type == NULL) + type = no->const_value()->expr()->type(); + if (type->is_abstract() || type->integer_type() == NULL) + { + --i; + --count; + continue; + } + } + + if (!no->is_variable()) + { + vec[i] = no->get_tree(this, NULL); + if (vec[i] == error_mark_node) + { + go_assert(saw_errors()); + --i; + --count; + continue; + } + } + else + { + Bvariable* var = no->get_backend_variable(this, NULL); + vec[i] = var_to_tree(var); + if (vec[i] == error_mark_node) + { + go_assert(saw_errors()); + --i; + --count; + continue; + } + + // Check for a sink variable, which may be used to run an + // initializer purely for its side effects. + bool is_sink = no->name()[0] == '_' && no->name()[1] == '.'; + + tree var_init_tree = NULL_TREE; + if (!no->var_value()->has_pre_init()) + { + tree init = no->var_value()->get_init_tree(this, NULL); + if (init == error_mark_node) + go_assert(saw_errors()); + else if (init == NULL_TREE) + ; + else if (TREE_CONSTANT(init)) + { + if (expression_requires(no->var_value()->init(), NULL, + this->var_depends_on(no->var_value()), + no)) + error_at(no->location(), + "initialization expression for %qs depends " + "upon itself", + no->message_name().c_str()); + this->backend()->global_variable_set_init(var, + tree_to_expr(init)); + } + else if (is_sink + || int_size_in_bytes(TREE_TYPE(init)) == 0 + || int_size_in_bytes(TREE_TYPE(vec[i])) == 0) + var_init_tree = init; + else + var_init_tree = fold_build2_loc(no->location().gcc_location(), + MODIFY_EXPR, void_type_node, + vec[i], init); + } + else + { + // We are going to create temporary variables which + // means that we need an fndecl. + if (init_fndecl == NULL_TREE) + init_fndecl = this->initialization_function_decl(); + if (DECL_STRUCT_FUNCTION(init_fndecl) == NULL) + push_struct_function(init_fndecl); + else + push_cfun(DECL_STRUCT_FUNCTION(init_fndecl)); + tree var_decl = is_sink ? NULL_TREE : vec[i]; + var_init_tree = no->var_value()->get_init_block(this, NULL, + var_decl); + pop_cfun(); + } + + if (var_init_tree != NULL_TREE && var_init_tree != error_mark_node) + { + if (no->var_value()->init() == NULL + && !no->var_value()->has_pre_init()) + append_to_statement_list(var_init_tree, &var_init_stmt_list); + else + var_inits.push_back(Var_init(no, var_init_tree)); + } + else if (this->var_depends_on(no->var_value()) != NULL) + { + // This variable is initialized from something that is + // not in its init or preinit. This variable needs to + // participate in dependency analysis sorting, in case + // some other variable depends on this one. + var_inits.push_back(Var_init(no, integer_zero_node)); + } + + if (!is_sink && no->var_value()->type()->has_pointer()) + var_gc.push_back(no); + } + } + + // Register global variables with the garbage collector. + this->register_gc_vars(var_gc, &init_stmt_list); + + // Simple variable initializations, after all variables are + // registered. + append_to_statement_list(var_init_stmt_list, &init_stmt_list); + + // Complex variable initializations, first sorting them into a + // workable order. + if (!var_inits.empty()) + { + sort_var_inits(this, &var_inits); + for (Var_inits::const_iterator p = var_inits.begin(); + p != var_inits.end(); + ++p) + append_to_statement_list(p->init(), &init_stmt_list); + } + + // After all the variables are initialized, call the "init" + // functions if there are any. + for (std::vector::const_iterator p = + this->init_functions_.begin(); + p != this->init_functions_.end(); + ++p) + { + tree decl = (*p)->get_tree(this, NULL); + tree call = build_call_expr(decl, 0); + append_to_statement_list(call, &init_stmt_list); + } + + // Set up a magic function to do all the initialization actions. + // This will be called if this package is imported. + if (init_stmt_list != NULL_TREE + || this->need_init_fn_ + || this->is_main_package()) + this->write_initialization_function(init_fndecl, init_stmt_list); + + // We should not have seen any new bindings created during the + // conversion. + go_assert(count_definitions == this->current_bindings()->size_definitions()); + + // Pass everything back to the middle-end. + + wrapup_global_declarations(vec, count); + + finalize_compilation_unit(); + + check_global_declarations(vec, count); + emit_debug_global_declarations(vec, count); + + delete[] vec; +} + +// Get a tree for a named object. + +tree +Named_object::get_tree(Gogo* gogo, Named_object* function) +{ + if (this->tree_ != NULL_TREE) + return this->tree_; + + if (Gogo::is_erroneous_name(this->name_)) + { + this->tree_ = error_mark_node; + return error_mark_node; + } + + tree decl; + switch (this->classification_) + { + case NAMED_OBJECT_CONST: + { + Named_constant* named_constant = this->u_.const_value; + Translate_context subcontext(gogo, function, NULL, NULL); + tree expr_tree = named_constant->expr()->get_tree(&subcontext); + if (expr_tree == error_mark_node) + decl = error_mark_node; + else + { + Type* type = named_constant->type(); + if (type != NULL && !type->is_abstract()) + { + if (type->is_error()) + expr_tree = error_mark_node; + else + { + Btype* btype = type->get_backend(gogo); + expr_tree = fold_convert(type_to_tree(btype), expr_tree); + } + } + if (expr_tree == error_mark_node) + decl = error_mark_node; + else if (INTEGRAL_TYPE_P(TREE_TYPE(expr_tree))) + { + tree name = get_identifier_from_string(this->get_id(gogo)); + decl = build_decl(named_constant->location().gcc_location(), + CONST_DECL, name, TREE_TYPE(expr_tree)); + DECL_INITIAL(decl) = expr_tree; + TREE_CONSTANT(decl) = 1; + TREE_READONLY(decl) = 1; + } + else + { + // A CONST_DECL is only for an enum constant, so we + // shouldn't use for non-integral types. Instead we + // just return the constant itself, rather than a + // decl. + decl = expr_tree; + } + } + } + break; + + case NAMED_OBJECT_TYPE: + { + Named_type* named_type = this->u_.type_value; + tree type_tree = type_to_tree(named_type->get_backend(gogo)); + if (type_tree == error_mark_node) + decl = error_mark_node; + else + { + decl = TYPE_NAME(type_tree); + go_assert(decl != NULL_TREE); + + // We need to produce a type descriptor for every named + // type, and for a pointer to every named type, since + // other files or packages might refer to them. We need + // to do this even for hidden types, because they might + // still be returned by some function. Simply calling the + // type_descriptor method is enough to create the type + // descriptor, even though we don't do anything with it. + if (this->package_ == NULL) + { + named_type-> + type_descriptor_pointer(gogo, + Linemap::predeclared_location()); + Type* pn = Type::make_pointer_type(named_type); + pn->type_descriptor_pointer(gogo, + Linemap::predeclared_location()); + } + } + } + break; + + case NAMED_OBJECT_TYPE_DECLARATION: + error("reference to undefined type %qs", + this->message_name().c_str()); + return error_mark_node; + + case NAMED_OBJECT_VAR: + case NAMED_OBJECT_RESULT_VAR: + case NAMED_OBJECT_SINK: + go_unreachable(); + + case NAMED_OBJECT_FUNC: + { + Function* func = this->u_.func_value; + decl = function_to_tree(func->get_or_make_decl(gogo, this)); + if (decl != error_mark_node) + { + if (func->block() != NULL) + { + if (DECL_STRUCT_FUNCTION(decl) == NULL) + push_struct_function(decl); + else + push_cfun(DECL_STRUCT_FUNCTION(decl)); + + cfun->function_start_locus = func->location().gcc_location(); + cfun->function_end_locus = + func->block()->end_location().gcc_location(); + + func->build_tree(gogo, this); + + gimplify_function_tree(decl); + + cgraph_finalize_function(decl, true); + + pop_cfun(); + } + } + } + break; + + case NAMED_OBJECT_ERRONEOUS: + decl = error_mark_node; + break; + + default: + go_unreachable(); + } + + if (TREE_TYPE(decl) == error_mark_node) + decl = error_mark_node; + + tree ret = decl; + + this->tree_ = ret; + + if (ret != error_mark_node) + go_preserve_from_gc(ret); + + return ret; +} + +// Get the initial value of a variable as a tree. This does not +// consider whether the variable is in the heap--it returns the +// initial value as though it were always stored in the stack. + +tree +Variable::get_init_tree(Gogo* gogo, Named_object* function) +{ + go_assert(this->preinit_ == NULL); + if (this->init_ == NULL) + { + go_assert(!this->is_parameter_); + if (this->is_global_ || this->is_in_heap()) + return NULL; + Btype* btype = this->type_->get_backend(gogo); + return expr_to_tree(gogo->backend()->zero_expression(btype)); + } + else + { + Translate_context context(gogo, function, NULL, NULL); + tree rhs_tree = this->init_->get_tree(&context); + return Expression::convert_for_assignment(&context, this->type(), + this->init_->type(), + rhs_tree, this->location()); + } +} + +// Get the initial value of a variable when a block is required. +// VAR_DECL is the decl to set; it may be NULL for a sink variable. + +tree +Variable::get_init_block(Gogo* gogo, Named_object* function, tree var_decl) +{ + go_assert(this->preinit_ != NULL); + + // We want to add the variable assignment to the end of the preinit + // block. The preinit block may have a TRY_FINALLY_EXPR and a + // TRY_CATCH_EXPR; if it does, we want to add to the end of the + // regular statements. + + Translate_context context(gogo, function, NULL, NULL); + Bblock* bblock = this->preinit_->get_backend(&context); + tree block_tree = block_to_tree(bblock); + if (block_tree == error_mark_node) + return error_mark_node; + go_assert(TREE_CODE(block_tree) == BIND_EXPR); + tree statements = BIND_EXPR_BODY(block_tree); + while (statements != NULL_TREE + && (TREE_CODE(statements) == TRY_FINALLY_EXPR + || TREE_CODE(statements) == TRY_CATCH_EXPR)) + statements = TREE_OPERAND(statements, 0); + + // It's possible to have pre-init statements without an initializer + // if the pre-init statements set the variable. + if (this->init_ != NULL) + { + tree rhs_tree = this->init_->get_tree(&context); + if (rhs_tree == error_mark_node) + return error_mark_node; + if (var_decl == NULL_TREE) + append_to_statement_list(rhs_tree, &statements); + else + { + tree val = Expression::convert_for_assignment(&context, this->type(), + this->init_->type(), + rhs_tree, + this->location()); + if (val == error_mark_node) + return error_mark_node; + tree set = fold_build2_loc(this->location().gcc_location(), + MODIFY_EXPR, void_type_node, var_decl, + val); + append_to_statement_list(set, &statements); + } + } + + return block_tree; +} + +// Get the backend representation. + +Bfunction* +Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no) +{ + if (this->fndecl_ == NULL) + { + // Let Go code use an asm declaration to pick up a builtin + // function. + if (!this->asm_name_.empty()) + { + std::map::const_iterator p = + builtin_functions.find(this->asm_name_); + if (p != builtin_functions.end()) + { + this->fndecl_ = tree_to_function(p->second); + return this->fndecl_; + } + } + + std::string asm_name; + if (this->asm_name_.empty()) + { + asm_name = (no->package() == NULL + ? gogo->pkgpath_symbol() + : no->package()->pkgpath_symbol()); + asm_name.append(1, '.'); + asm_name.append(Gogo::unpack_hidden_name(no->name())); + if (this->fntype_->is_method()) + { + asm_name.append(1, '.'); + Type* rtype = this->fntype_->receiver()->type(); + asm_name.append(rtype->mangled_name(gogo)); + } + } + + Btype* functype = this->fntype_->get_backend_fntype(gogo); + this->fndecl_ = + gogo->backend()->function(functype, no->get_id(gogo), asm_name, + true, true, true, false, false, + this->location()); + } + + return this->fndecl_; +} + +// Return the function's decl after it has been built. + +tree +Function::get_decl() const +{ + go_assert(this->fndecl_ != NULL); + return function_to_tree(this->fndecl_); +} + +// We always pass the receiver to a method as a pointer. If the +// receiver is actually declared as a non-pointer type, then we copy +// the value into a local variable, so that it has the right type. In +// this function we create the real PARM_DECL to use, and set +// DEC_INITIAL of the var_decl to be the value passed in. + +tree +Function::make_receiver_parm_decl(Gogo* gogo, Named_object* no, tree var_decl) +{ + if (var_decl == error_mark_node) + return error_mark_node; + go_assert(TREE_CODE(var_decl) == VAR_DECL); + tree val_type = TREE_TYPE(var_decl); + bool is_in_heap = no->var_value()->is_in_heap(); + if (is_in_heap) + { + go_assert(POINTER_TYPE_P(val_type)); + val_type = TREE_TYPE(val_type); + } + + source_location loc = DECL_SOURCE_LOCATION(var_decl); + std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl)); + name += ".pointer"; + tree id = get_identifier_from_string(name); + tree parm_decl = build_decl(loc, PARM_DECL, id, build_pointer_type(val_type)); + DECL_CONTEXT(parm_decl) = current_function_decl; + DECL_ARG_TYPE(parm_decl) = TREE_TYPE(parm_decl); + + go_assert(DECL_INITIAL(var_decl) == NULL_TREE); + tree init = build_fold_indirect_ref_loc(loc, parm_decl); + + if (is_in_heap) + { + tree size = TYPE_SIZE_UNIT(val_type); + tree space = gogo->allocate_memory(no->var_value()->type(), size, + no->location()); + space = save_expr(space); + space = fold_convert(build_pointer_type(val_type), space); + tree spaceref = build_fold_indirect_ref_loc(no->location().gcc_location(), + space); + TREE_THIS_NOTRAP(spaceref) = 1; + tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node, + spaceref, init); + init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space), set, space); + } + + DECL_INITIAL(var_decl) = init; + + return parm_decl; +} + +// If we take the address of a parameter, then we need to copy it into +// the heap. We will access it as a local variable via an +// indirection. + +tree +Function::copy_parm_to_heap(Gogo* gogo, Named_object* no, tree var_decl) +{ + if (var_decl == error_mark_node) + return error_mark_node; + go_assert(TREE_CODE(var_decl) == VAR_DECL); + Location loc(DECL_SOURCE_LOCATION(var_decl)); + + std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl)); + name += ".param"; + tree id = get_identifier_from_string(name); + + tree type = TREE_TYPE(var_decl); + go_assert(POINTER_TYPE_P(type)); + type = TREE_TYPE(type); + + tree parm_decl = build_decl(loc.gcc_location(), PARM_DECL, id, type); + DECL_CONTEXT(parm_decl) = current_function_decl; + DECL_ARG_TYPE(parm_decl) = type; + + tree size = TYPE_SIZE_UNIT(type); + tree space = gogo->allocate_memory(no->var_value()->type(), size, loc); + space = save_expr(space); + space = fold_convert(TREE_TYPE(var_decl), space); + tree spaceref = build_fold_indirect_ref_loc(loc.gcc_location(), space); + TREE_THIS_NOTRAP(spaceref) = 1; + tree init = build2(COMPOUND_EXPR, TREE_TYPE(space), + build2(MODIFY_EXPR, void_type_node, spaceref, parm_decl), + space); + DECL_INITIAL(var_decl) = init; + + return parm_decl; +} + +// Get a tree for function code. + +void +Function::build_tree(Gogo* gogo, Named_object* named_function) +{ + tree fndecl = this->get_decl(); + go_assert(fndecl != NULL_TREE); + + tree params = NULL_TREE; + tree* pp = ¶ms; + + tree declare_vars = NULL_TREE; + for (Bindings::const_definitions_iterator p = + this->block_->bindings()->begin_definitions(); + p != this->block_->bindings()->end_definitions(); + ++p) + { + if ((*p)->is_variable() && (*p)->var_value()->is_parameter()) + { + Bvariable* bvar = (*p)->get_backend_variable(gogo, named_function); + *pp = var_to_tree(bvar); + + // We always pass the receiver to a method as a pointer. If + // the receiver is declared as a non-pointer type, then we + // copy the value into a local variable. + if ((*p)->var_value()->is_receiver() + && (*p)->var_value()->type()->points_to() == NULL) + { + tree parm_decl = this->make_receiver_parm_decl(gogo, *p, *pp); + tree var = *pp; + if (var != error_mark_node) + { + go_assert(TREE_CODE(var) == VAR_DECL); + DECL_CHAIN(var) = declare_vars; + declare_vars = var; + } + *pp = parm_decl; + } + else if ((*p)->var_value()->is_in_heap()) + { + // If we take the address of a parameter, then we need + // to copy it into the heap. + tree parm_decl = this->copy_parm_to_heap(gogo, *p, *pp); + tree var = *pp; + if (var != error_mark_node) + { + go_assert(TREE_CODE(var) == VAR_DECL); + DECL_CHAIN(var) = declare_vars; + declare_vars = var; + } + *pp = parm_decl; + } + + if (*pp != error_mark_node) + { + go_assert(TREE_CODE(*pp) == PARM_DECL); + pp = &DECL_CHAIN(*pp); + } + } + else if ((*p)->is_result_variable()) + { + Bvariable* bvar = (*p)->get_backend_variable(gogo, named_function); + tree var_decl = var_to_tree(bvar); + + Type* type = (*p)->result_var_value()->type(); + tree init; + if (!(*p)->result_var_value()->is_in_heap()) + { + Btype* btype = type->get_backend(gogo); + init = expr_to_tree(gogo->backend()->zero_expression(btype)); + } + else + { + Location loc = (*p)->location(); + tree type_tree = type_to_tree(type->get_backend(gogo)); + tree space = gogo->allocate_memory(type, + TYPE_SIZE_UNIT(type_tree), + loc); + tree ptr_type_tree = build_pointer_type(type_tree); + init = fold_convert_loc(loc.gcc_location(), ptr_type_tree, space); + } + + if (var_decl != error_mark_node) + { + go_assert(TREE_CODE(var_decl) == VAR_DECL); + DECL_INITIAL(var_decl) = init; + DECL_CHAIN(var_decl) = declare_vars; + declare_vars = var_decl; + } + } + } + + *pp = NULL_TREE; + + DECL_ARGUMENTS(fndecl) = params; + + // If we need a closure variable, fetch it by calling a runtime + // function. The caller will have called __go_set_closure before + // the function call. + if (this->closure_var_ != NULL) + { + Bvariable* bvar = + this->closure_var_->get_backend_variable(gogo, named_function); + tree var_decl = var_to_tree(bvar); + if (var_decl != error_mark_node) + { + go_assert(TREE_CODE(var_decl) == VAR_DECL); + static tree get_closure_fndecl; + tree get_closure = Gogo::call_builtin(&get_closure_fndecl, + this->location_, + "__go_get_closure", + 0, + ptr_type_node); + + // Mark the __go_get_closure function as pure, since it + // depends only on the global variable g. + DECL_PURE_P(get_closure_fndecl) = 1; + + get_closure = fold_convert_loc(this->location_.gcc_location(), + TREE_TYPE(var_decl), get_closure); + DECL_INITIAL(var_decl) = get_closure; + DECL_CHAIN(var_decl) = declare_vars; + declare_vars = var_decl; + } + } + + if (this->block_ != NULL) + { + go_assert(DECL_INITIAL(fndecl) == NULL_TREE); + + // Declare variables if necessary. + tree bind = NULL_TREE; + tree defer_init = NULL_TREE; + if (declare_vars != NULL_TREE || this->defer_stack_ != NULL) + { + tree block = make_node(BLOCK); + BLOCK_SUPERCONTEXT(block) = fndecl; + DECL_INITIAL(fndecl) = block; + BLOCK_VARS(block) = declare_vars; + TREE_USED(block) = 1; + + bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block), + NULL_TREE, block); + TREE_SIDE_EFFECTS(bind) = 1; + + if (this->defer_stack_ != NULL) + { + Translate_context dcontext(gogo, named_function, this->block_, + tree_to_block(bind)); + Bstatement* bdi = this->defer_stack_->get_backend(&dcontext); + defer_init = stat_to_tree(bdi); + } + } + + // Build the trees for all the statements in the function. + Translate_context context(gogo, named_function, NULL, NULL); + Bblock* bblock = this->block_->get_backend(&context); + tree code = block_to_tree(bblock); + + tree init = NULL_TREE; + tree except = NULL_TREE; + tree fini = NULL_TREE; + + // Initialize variables if necessary. + for (tree v = declare_vars; v != NULL_TREE; v = DECL_CHAIN(v)) + { + tree dv = build1(DECL_EXPR, void_type_node, v); + SET_EXPR_LOCATION(dv, DECL_SOURCE_LOCATION(v)); + append_to_statement_list(dv, &init); + } + + // If we have a defer stack, initialize it at the start of a + // function. + if (defer_init != NULL_TREE && defer_init != error_mark_node) + { + SET_EXPR_LOCATION(defer_init, + this->block_->start_location().gcc_location()); + append_to_statement_list(defer_init, &init); + + // Clean up the defer stack when we leave the function. + this->build_defer_wrapper(gogo, named_function, &except, &fini); + } + + if (code != NULL_TREE && code != error_mark_node) + { + if (init != NULL_TREE) + code = build2(COMPOUND_EXPR, void_type_node, init, code); + if (except != NULL_TREE) + code = build2(TRY_CATCH_EXPR, void_type_node, code, + build2(CATCH_EXPR, void_type_node, NULL, except)); + if (fini != NULL_TREE) + code = build2(TRY_FINALLY_EXPR, void_type_node, code, fini); + } + + // Stick the code into the block we built for the receiver, if + // we built on. + if (bind != NULL_TREE && code != NULL_TREE && code != error_mark_node) + { + BIND_EXPR_BODY(bind) = code; + code = bind; + } + + DECL_SAVED_TREE(fndecl) = code; + } + + // If we created a descriptor for the function, make sure we emit it. + if (this->descriptor_ != NULL) + { + Translate_context context(gogo, NULL, NULL, NULL); + this->descriptor_->get_tree(&context); + } +} + +// Build the wrappers around function code needed if the function has +// any defer statements. This sets *EXCEPT to an exception handler +// and *FINI to a finally handler. + +void +Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, + tree *except, tree *fini) +{ + Location end_loc = this->block_->end_location(); + + // Add an exception handler. This is used if a panic occurs. Its + // purpose is to stop the stack unwinding if a deferred function + // calls recover. There are more details in + // libgo/runtime/go-unwind.c. + + tree stmt_list = NULL_TREE; + + Expression* call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1, + this->defer_stack(end_loc)); + Translate_context context(gogo, named_function, NULL, NULL); + tree call_tree = call->get_tree(&context); + if (call_tree != error_mark_node) + append_to_statement_list(call_tree, &stmt_list); + + tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list); + tree set; + if (retval == NULL_TREE) + set = NULL_TREE; + else + set = fold_build2_loc(end_loc.gcc_location(), MODIFY_EXPR, void_type_node, + DECL_RESULT(this->get_decl()), retval); + tree ret_stmt = fold_build1_loc(end_loc.gcc_location(), RETURN_EXPR, + void_type_node, set); + append_to_statement_list(ret_stmt, &stmt_list); + + go_assert(*except == NULL_TREE); + *except = stmt_list; + + // Add some finally code to run the defer functions. This is used + // both in the normal case, when no panic occurs, and also if a + // panic occurs to run any further defer functions. Of course, it + // is possible for a defer function to call panic which should be + // caught by another defer function. To handle that we use a loop. + // finish: + // try { __go_undefer(); } catch { __go_check_defer(); goto finish; } + // if (return values are named) return named_vals; + + stmt_list = NULL; + + tree label = create_artificial_label(end_loc.gcc_location()); + tree define_label = fold_build1_loc(end_loc.gcc_location(), LABEL_EXPR, + void_type_node, label); + append_to_statement_list(define_label, &stmt_list); + + call = Runtime::make_call(Runtime::UNDEFER, end_loc, 1, + this->defer_stack(end_loc)); + tree undefer = call->get_tree(&context); + + call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1, + this->defer_stack(end_loc)); + tree defer = call->get_tree(&context); + + if (undefer == error_mark_node || defer == error_mark_node) + return; + + tree jump = fold_build1_loc(end_loc.gcc_location(), GOTO_EXPR, void_type_node, + label); + tree catch_body = build2(COMPOUND_EXPR, void_type_node, defer, jump); + catch_body = build2(CATCH_EXPR, void_type_node, NULL, catch_body); + tree try_catch = build2(TRY_CATCH_EXPR, void_type_node, undefer, catch_body); + + append_to_statement_list(try_catch, &stmt_list); + + if (this->type_->results() != NULL + && !this->type_->results()->empty() + && !this->type_->results()->front().name().empty()) + { + // If the result variables are named, and we are returning from + // this function rather than panicing through it, we need to + // return them again, because they might have been changed by a + // defer function. The runtime routines set the defer_stack + // variable to true if we are returning from this function. + retval = this->return_value(gogo, named_function, end_loc, + &stmt_list); + set = fold_build2_loc(end_loc.gcc_location(), MODIFY_EXPR, void_type_node, + DECL_RESULT(this->get_decl()), retval); + ret_stmt = fold_build1_loc(end_loc.gcc_location(), RETURN_EXPR, + void_type_node, set); + + Expression* ref = + Expression::make_temporary_reference(this->defer_stack_, end_loc); + tree tref = ref->get_tree(&context); + tree s = build3_loc(end_loc.gcc_location(), COND_EXPR, void_type_node, + tref, ret_stmt, NULL_TREE); + + append_to_statement_list(s, &stmt_list); + + } + + go_assert(*fini == NULL_TREE); + *fini = stmt_list; +} + +// Return the value to assign to DECL_RESULT(this->get_decl()). This may +// also add statements to STMT_LIST, which need to be executed before +// the assignment. This is used for a return statement with no +// explicit values. + +tree +Function::return_value(Gogo* gogo, Named_object* named_function, + Location location, tree* stmt_list) const +{ + const Typed_identifier_list* results = this->type_->results(); + if (results == NULL || results->empty()) + return NULL_TREE; + + go_assert(this->results_ != NULL); + if (this->results_->size() != results->size()) + { + go_assert(saw_errors()); + return error_mark_node; + } + + tree retval; + if (results->size() == 1) + { + Bvariable* bvar = + this->results_->front()->get_backend_variable(gogo, + named_function); + tree ret = var_to_tree(bvar); + if (this->results_->front()->result_var_value()->is_in_heap()) + ret = build_fold_indirect_ref_loc(location.gcc_location(), ret); + return ret; + } + else + { + tree rettype = TREE_TYPE(DECL_RESULT(this->get_decl())); + retval = create_tmp_var(rettype, "RESULT"); + tree field = TYPE_FIELDS(rettype); + int index = 0; + for (Typed_identifier_list::const_iterator pr = results->begin(); + pr != results->end(); + ++pr, ++index, field = DECL_CHAIN(field)) + { + go_assert(field != NULL); + Named_object* no = (*this->results_)[index]; + Bvariable* bvar = no->get_backend_variable(gogo, named_function); + tree val = var_to_tree(bvar); + if (no->result_var_value()->is_in_heap()) + val = build_fold_indirect_ref_loc(location.gcc_location(), val); + tree set = fold_build2_loc(location.gcc_location(), MODIFY_EXPR, + void_type_node, + build3(COMPONENT_REF, TREE_TYPE(field), + retval, field, NULL_TREE), + val); + append_to_statement_list(set, stmt_list); + } + return retval; + } +} + +// Build the descriptor for a function declaration. This won't +// necessarily happen if the package has just a declaration for the +// function and no other reference to it, but we may still need the +// descriptor for references from other packages. +void +Function_declaration::build_backend_descriptor(Gogo* gogo) +{ + if (this->descriptor_ != NULL) + { + Translate_context context(gogo, NULL, NULL, NULL); + this->descriptor_->get_tree(&context); + } +} + +// Return the integer type to use for a size. + +GO_EXTERN_C +tree +go_type_for_size(unsigned int bits, int unsignedp) +{ + const char* name; + switch (bits) + { + case 8: + name = unsignedp ? "uint8" : "int8"; + break; + case 16: + name = unsignedp ? "uint16" : "int16"; + break; + case 32: + name = unsignedp ? "uint32" : "int32"; + break; + case 64: + name = unsignedp ? "uint64" : "int64"; + break; + default: + if (bits == POINTER_SIZE && unsignedp) + name = "uintptr"; + else + return NULL_TREE; + } + Type* type = Type::lookup_integer_type(name); + return type_to_tree(type->get_backend(go_get_gogo())); +} + +// Return the type to use for a mode. + +GO_EXTERN_C +tree +go_type_for_mode(enum machine_mode mode, int unsignedp) +{ + // FIXME: This static_cast should be in machmode.h. + enum mode_class mc = static_cast(GET_MODE_CLASS(mode)); + if (mc == MODE_INT) + return go_type_for_size(GET_MODE_BITSIZE(mode), unsignedp); + else if (mc == MODE_FLOAT) + { + Type* type; + switch (GET_MODE_BITSIZE (mode)) + { + case 32: + type = Type::lookup_float_type("float32"); + break; + case 64: + type = Type::lookup_float_type("float64"); + break; + default: + // We have to check for long double in order to support + // i386 excess precision. + if (mode == TYPE_MODE(long_double_type_node)) + return long_double_type_node; + return NULL_TREE; + } + return type_to_tree(type->get_backend(go_get_gogo())); + } + else if (mc == MODE_COMPLEX_FLOAT) + { + Type *type; + switch (GET_MODE_BITSIZE (mode)) + { + case 64: + type = Type::lookup_complex_type("complex64"); + break; + case 128: + type = Type::lookup_complex_type("complex128"); + break; + default: + // We have to check for long double in order to support + // i386 excess precision. + if (mode == TYPE_MODE(complex_long_double_type_node)) + return complex_long_double_type_node; + return NULL_TREE; + } + return type_to_tree(type->get_backend(go_get_gogo())); + } + else + return NULL_TREE; +} + +// Return a tree which allocates SIZE bytes which will holds value of +// type TYPE. + +tree +Gogo::allocate_memory(Type* type, tree size, Location location) +{ + // If the package imports unsafe, then it may play games with + // pointers that look like integers. + if (this->imported_unsafe_ || type->has_pointer()) + { + static tree new_fndecl; + return Gogo::call_builtin(&new_fndecl, + location, + "__go_new", + 1, + ptr_type_node, + sizetype, + size); + } + else + { + static tree new_nopointers_fndecl; + return Gogo::call_builtin(&new_nopointers_fndecl, + location, + "__go_new_nopointers", + 1, + ptr_type_node, + sizetype, + size); + } +} + +// Build a builtin struct with a list of fields. The name is +// STRUCT_NAME. STRUCT_TYPE is NULL_TREE or an empty RECORD_TYPE +// node; this exists so that the struct can have fields which point to +// itself. If PTYPE is not NULL, store the result in *PTYPE. There +// are NFIELDS fields. Each field is a name (a const char*) followed +// by a type (a tree). + +tree +Gogo::builtin_struct(tree* ptype, const char* struct_name, tree struct_type, + int nfields, ...) +{ + if (ptype != NULL && *ptype != NULL_TREE) + return *ptype; + + va_list ap; + va_start(ap, nfields); + + tree fields = NULL_TREE; + for (int i = 0; i < nfields; ++i) + { + const char* field_name = va_arg(ap, const char*); + tree type = va_arg(ap, tree); + if (type == error_mark_node) + { + if (ptype != NULL) + *ptype = error_mark_node; + return error_mark_node; + } + tree field = build_decl(BUILTINS_LOCATION, FIELD_DECL, + get_identifier(field_name), type); + DECL_CHAIN(field) = fields; + fields = field; + } + + va_end(ap); + + if (struct_type == NULL_TREE) + struct_type = make_node(RECORD_TYPE); + finish_builtin_struct(struct_type, struct_name, fields, NULL_TREE); + + if (ptype != NULL) + { + go_preserve_from_gc(struct_type); + *ptype = struct_type; + } + + return struct_type; +} + +// Return a type to use for pointer to const char for a string. + +tree +Gogo::const_char_pointer_type_tree() +{ + static tree type; + if (type == NULL_TREE) + { + tree const_char_type = build_qualified_type(unsigned_char_type_node, + TYPE_QUAL_CONST); + type = build_pointer_type(const_char_type); + go_preserve_from_gc(type); + } + return type; +} + +// Return a tree for a string constant. + +tree +Gogo::string_constant_tree(const std::string& val) +{ + tree index_type = build_index_type(size_int(val.length())); + tree const_char_type = build_qualified_type(unsigned_char_type_node, + TYPE_QUAL_CONST); + tree string_type = build_array_type(const_char_type, index_type); + string_type = build_variant_type_copy(string_type); + TYPE_STRING_FLAG(string_type) = 1; + tree string_val = build_string(val.length(), val.data()); + TREE_TYPE(string_val) = string_type; + return string_val; +} + +// Return a tree for a Go string constant. + +tree +Gogo::go_string_constant_tree(const std::string& val) +{ + tree string_type = type_to_tree(Type::make_string_type()->get_backend(this)); + + vec *init; + vec_alloc(init, 2); + + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + tree field = TYPE_FIELDS(string_type); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__data") == 0); + elt->index = field; + tree str = Gogo::string_constant_tree(val); + elt->value = fold_convert(TREE_TYPE(field), + build_fold_addr_expr(str)); + + elt = init->quick_push(empty); + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__length") == 0); + elt->index = field; + elt->value = build_int_cst_type(TREE_TYPE(field), val.length()); + + tree constructor = build_constructor(string_type, init); + TREE_READONLY(constructor) = 1; + TREE_CONSTANT(constructor) = 1; + + return constructor; +} + +// Return a tree for a pointer to a Go string constant. This is only +// used for type descriptors, so we return a pointer to a constant +// decl. + +tree +Gogo::ptr_go_string_constant_tree(const std::string& val) +{ + tree pval = this->go_string_constant_tree(val); + + tree decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, + create_tmp_var_name("SP"), TREE_TYPE(pval)); + DECL_EXTERNAL(decl) = 0; + TREE_PUBLIC(decl) = 0; + TREE_USED(decl) = 1; + TREE_READONLY(decl) = 1; + TREE_CONSTANT(decl) = 1; + TREE_STATIC(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + DECL_INITIAL(decl) = pval; + rest_of_decl_compilation(decl, 1, 0); + + return build_fold_addr_expr(decl); +} + +// Build a constructor for a slice. SLICE_TYPE_TREE is the type of +// the slice. VALUES is the value pointer and COUNT is the number of +// entries. If CAPACITY is not NULL, it is the capacity; otherwise +// the capacity and the count are the same. + +tree +Gogo::slice_constructor(tree slice_type_tree, tree values, tree count, + tree capacity) +{ + go_assert(TREE_CODE(slice_type_tree) == RECORD_TYPE); + + vec *init; + vec_alloc(init, 3); + + tree field = TYPE_FIELDS(slice_type_tree); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0); + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = init->quick_push(empty); + elt->index = field; + go_assert(TYPE_MAIN_VARIANT(TREE_TYPE(field)) + == TYPE_MAIN_VARIANT(TREE_TYPE(values))); + elt->value = values; + + count = fold_convert(sizetype, count); + if (capacity == NULL_TREE) + { + count = save_expr(count); + capacity = count; + } + + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0); + elt = init->quick_push(empty); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), count); + + field = DECL_CHAIN(field); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0); + elt = init->quick_push(empty); + elt->index = field; + elt->value = fold_convert(TREE_TYPE(field), capacity); + + return build_constructor(slice_type_tree, init); +} + +// Build an interface method table for a type: a list of function +// pointers, one for each interface method. This is used for +// interfaces. + +tree +Gogo::interface_method_table_for_type(const Interface_type* interface, + Type* type, bool is_pointer) +{ + const Typed_identifier_list* interface_methods = interface->methods(); + go_assert(!interface_methods->empty()); + + std::string mangled_name = ((is_pointer ? "__go_pimt__" : "__go_imt_") + + interface->mangled_name(this) + + "__" + + type->mangled_name(this)); + + tree id = get_identifier_from_string(mangled_name); + + // See whether this interface has any hidden methods. + bool has_hidden_methods = false; + for (Typed_identifier_list::const_iterator p = interface_methods->begin(); + p != interface_methods->end(); + ++p) + { + if (Gogo::is_hidden_name(p->name())) + { + has_hidden_methods = true; + break; + } + } + + // We already know that the named type is convertible to the + // 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_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); + TREE_READONLY(decl) = 1; + TREE_CONSTANT(decl) = 1; + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + go_preserve_from_gc(decl); + return decl; + } + + size_t count = interface_methods->size(); + vec *pointers; + vec_alloc(pointers, count + 1); + + // The first element is the type descriptor. + constructor_elt empty = {NULL, NULL}; + constructor_elt* elt = pointers->quick_push(empty); + elt->index = size_zero_node; + Type* td_type; + if (!is_pointer) + td_type = type; + else + td_type = Type::make_pointer_type(type); + + Location loc = Linemap::predeclared_location(); + Bexpression* tdp_bexpr = td_type->type_descriptor_pointer(this, loc); + tree tdp = expr_to_tree(tdp_bexpr); + 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; + 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(); + Bfunction* bf; + if (no->is_function()) + bf = no->func_value()->get_or_make_decl(this, no); + else if (no->is_function_declaration()) + bf = no->func_declaration_value()->get_or_make_decl(this, no); + else + go_unreachable(); + tree fndecl = build_fold_addr_expr(function_to_tree(bf)); + + elt = pointers->quick_push(empty); + elt->index = size_int(i); + elt->value = fold_convert(const_ptr_type_node, fndecl); + } + go_assert(i == count + 1); + + tree array_type = build_array_type(const_ptr_type_node, + build_index_type(size_int(count))); + tree constructor = build_constructor(array_type, pointers); + + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, array_type); + TREE_STATIC(decl) = 1; + TREE_USED(decl) = 1; + TREE_READONLY(decl) = 1; + TREE_CONSTANT(decl) = 1; + DECL_INITIAL(decl) = constructor; + + // If the interface type has hidden methods, and the table is for a + // named type, then this is the only definition of the table. + // Otherwise it is a comdat table which may be defined in multiple + // packages. + if (has_hidden_methods && type->named_type() != NULL) + TREE_PUBLIC(decl) = 1; + else + { + make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl)); + resolve_unique_section(decl, 1, 0); + } + + rest_of_decl_compilation(decl, 1, 0); + + go_preserve_from_gc(decl); + + return decl; +} + +// Mark a function as a builtin library function. + +void +Gogo::mark_fndecl_as_builtin_library(tree fndecl) +{ + DECL_EXTERNAL(fndecl) = 1; + TREE_PUBLIC(fndecl) = 1; + DECL_ARTIFICIAL(fndecl) = 1; + TREE_NOTHROW(fndecl) = 1; + DECL_VISIBILITY(fndecl) = VISIBILITY_DEFAULT; + DECL_VISIBILITY_SPECIFIED(fndecl) = 1; +} + +// Build a call to a builtin function. + +tree +Gogo::call_builtin(tree* pdecl, Location location, const char* name, + int nargs, tree rettype, ...) +{ + if (rettype == error_mark_node) + return error_mark_node; + + tree* types = new tree[nargs]; + tree* args = new tree[nargs]; + + va_list ap; + va_start(ap, rettype); + for (int i = 0; i < nargs; ++i) + { + types[i] = va_arg(ap, tree); + args[i] = va_arg(ap, tree); + if (types[i] == error_mark_node || args[i] == error_mark_node) + { + delete[] types; + delete[] args; + return error_mark_node; + } + } + va_end(ap); + + if (*pdecl == NULL_TREE) + { + tree fnid = get_identifier(name); + + tree argtypes = NULL_TREE; + tree* pp = &argtypes; + for (int i = 0; i < nargs; ++i) + { + *pp = tree_cons(NULL_TREE, types[i], NULL_TREE); + pp = &TREE_CHAIN(*pp); + } + *pp = void_list_node; + + tree fntype = build_function_type(rettype, argtypes); + + *pdecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL, fnid, fntype); + Gogo::mark_fndecl_as_builtin_library(*pdecl); + go_preserve_from_gc(*pdecl); + } + + tree fnptr = build_fold_addr_expr(*pdecl); + if (CAN_HAVE_LOCATION_P(fnptr)) + SET_EXPR_LOCATION(fnptr, location.gcc_location()); + + tree ret = build_call_array(rettype, fnptr, nargs, args); + SET_EXPR_LOCATION(ret, location.gcc_location()); + + delete[] types; + delete[] args; + + return ret; +} + +// Return a tree for receiving a value of type TYPE_TREE on CHANNEL. +// TYPE_DESCRIPTOR_TREE is the channel's type descriptor. This does a +// blocking receive and returns the value read from the channel. + +tree +Gogo::receive_from_channel(tree type_tree, tree type_descriptor_tree, + tree channel, Location location) +{ + if (type_tree == error_mark_node || channel == error_mark_node) + return error_mark_node; + + if (int_size_in_bytes(type_tree) <= 8 + && !AGGREGATE_TYPE_P(type_tree) + && !FLOAT_TYPE_P(type_tree)) + { + static tree receive_small_fndecl; + tree call = Gogo::call_builtin(&receive_small_fndecl, + location, + "__go_receive_small", + 2, + uint64_type_node, + TREE_TYPE(type_descriptor_tree), + type_descriptor_tree, + ptr_type_node, + channel); + 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; + int bitsize = GET_MODE_BITSIZE(TYPE_MODE(type_tree)); + tree int_type_tree = go_type_for_size(bitsize, 1); + return fold_convert_loc(location.gcc_location(), type_tree, + fold_convert_loc(location.gcc_location(), + int_type_tree, call)); + } + else + { + tree tmp = create_tmp_var(type_tree, get_name(type_tree)); + DECL_IGNORED_P(tmp) = 0; + TREE_ADDRESSABLE(tmp) = 1; + tree make_tmp = build1(DECL_EXPR, void_type_node, tmp); + SET_EXPR_LOCATION(make_tmp, location.gcc_location()); + tree tmpaddr = build_fold_addr_expr(tmp); + tmpaddr = fold_convert(ptr_type_node, tmpaddr); + static tree receive_big_fndecl; + tree call = Gogo::call_builtin(&receive_big_fndecl, + location, + "__go_receive_big", + 3, + void_type_node, + TREE_TYPE(type_descriptor_tree), + type_descriptor_tree, + ptr_type_node, + channel, + ptr_type_node, + tmpaddr); + 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; + return build2(COMPOUND_EXPR, type_tree, make_tmp, + build2(COMPOUND_EXPR, type_tree, call, tmp)); + } +} diff --git a/gcc-4.9/gcc/go/gofrontend/gogo.cc b/gcc-4.9/gcc/go/gofrontend/gogo.cc new file mode 100644 index 000000000..9739f289f --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/gogo.cc @@ -0,0 +1,6198 @@ +// gogo.cc -- Go frontend parsed representation. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "filenames.h" + +#include "go-c.h" +#include "go-dump.h" +#include "lex.h" +#include "types.h" +#include "statements.h" +#include "expressions.h" +#include "dataflow.h" +#include "runtime.h" +#include "import.h" +#include "export.h" +#include "backend.h" +#include "gogo.h" + +// Class Gogo. + +Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) + : backend_(backend), + linemap_(linemap), + package_(NULL), + functions_(), + globals_(new Bindings(NULL)), + file_block_names_(), + imports_(), + imported_unsafe_(false), + packages_(), + init_functions_(), + var_deps_(), + need_init_fn_(false), + init_fn_name_(), + imported_init_fns_(), + pkgpath_(), + pkgpath_symbol_(), + prefix_(), + pkgpath_set_(false), + pkgpath_from_option_(false), + prefix_from_option_(false), + relative_import_path_(), + verify_types_(), + interface_types_(), + specific_type_functions_(), + specific_type_functions_are_written_(false), + named_types_are_converted_(false) +{ + const Location loc = Linemap::predeclared_location(); + + Named_type* uint8_type = Type::make_integer_type("uint8", true, 8, + RUNTIME_TYPE_KIND_UINT8); + this->add_named_type(uint8_type); + this->add_named_type(Type::make_integer_type("uint16", true, 16, + RUNTIME_TYPE_KIND_UINT16)); + this->add_named_type(Type::make_integer_type("uint32", true, 32, + RUNTIME_TYPE_KIND_UINT32)); + this->add_named_type(Type::make_integer_type("uint64", true, 64, + RUNTIME_TYPE_KIND_UINT64)); + + this->add_named_type(Type::make_integer_type("int8", false, 8, + RUNTIME_TYPE_KIND_INT8)); + this->add_named_type(Type::make_integer_type("int16", false, 16, + RUNTIME_TYPE_KIND_INT16)); + Named_type* int32_type = Type::make_integer_type("int32", false, 32, + RUNTIME_TYPE_KIND_INT32); + this->add_named_type(int32_type); + this->add_named_type(Type::make_integer_type("int64", false, 64, + RUNTIME_TYPE_KIND_INT64)); + + this->add_named_type(Type::make_float_type("float32", 32, + RUNTIME_TYPE_KIND_FLOAT32)); + this->add_named_type(Type::make_float_type("float64", 64, + RUNTIME_TYPE_KIND_FLOAT64)); + + this->add_named_type(Type::make_complex_type("complex64", 64, + RUNTIME_TYPE_KIND_COMPLEX64)); + this->add_named_type(Type::make_complex_type("complex128", 128, + RUNTIME_TYPE_KIND_COMPLEX128)); + + int int_type_size = pointer_size; + if (int_type_size < 32) + int_type_size = 32; + this->add_named_type(Type::make_integer_type("uint", true, + int_type_size, + RUNTIME_TYPE_KIND_UINT)); + Named_type* int_type = Type::make_integer_type("int", false, int_type_size, + RUNTIME_TYPE_KIND_INT); + this->add_named_type(int_type); + + this->add_named_type(Type::make_integer_type("uintptr", true, + pointer_size, + RUNTIME_TYPE_KIND_UINTPTR)); + + // "byte" is an alias for "uint8". + uint8_type->integer_type()->set_is_byte(); + Named_object* byte_type = Named_object::make_type("byte", NULL, uint8_type, + loc); + this->add_named_type(byte_type->type_value()); + + // "rune" is an alias for "int32". + int32_type->integer_type()->set_is_rune(); + Named_object* rune_type = Named_object::make_type("rune", NULL, int32_type, + loc); + this->add_named_type(rune_type->type_value()); + + this->add_named_type(Type::make_named_bool_type()); + + this->add_named_type(Type::make_named_string_type()); + + // "error" is interface { Error() string }. + { + Typed_identifier_list *methods = new Typed_identifier_list; + Typed_identifier_list *results = new Typed_identifier_list; + results->push_back(Typed_identifier("", Type::lookup_string_type(), loc)); + Type *method_type = Type::make_function_type(NULL, NULL, results, loc); + methods->push_back(Typed_identifier("Error", method_type, loc)); + Interface_type *error_iface = Type::make_interface_type(methods, loc); + error_iface->finalize_methods(); + Named_type *error_type = Named_object::make_type("error", NULL, error_iface, loc)->type_value(); + this->add_named_type(error_type); + } + + this->globals_->add_constant(Typed_identifier("true", + Type::make_boolean_type(), + loc), + NULL, + Expression::make_boolean(true, loc), + 0); + this->globals_->add_constant(Typed_identifier("false", + Type::make_boolean_type(), + loc), + NULL, + Expression::make_boolean(false, loc), + 0); + + this->globals_->add_constant(Typed_identifier("nil", Type::make_nil_type(), + loc), + NULL, + Expression::make_nil(loc), + 0); + + Type* abstract_int_type = Type::make_abstract_integer_type(); + this->globals_->add_constant(Typed_identifier("iota", abstract_int_type, + loc), + NULL, + Expression::make_iota(), + 0); + + Function_type* new_type = Type::make_function_type(NULL, NULL, NULL, loc); + new_type->set_is_varargs(); + new_type->set_is_builtin(); + this->globals_->add_function_declaration("new", NULL, new_type, loc); + + Function_type* make_type = Type::make_function_type(NULL, NULL, NULL, loc); + make_type->set_is_varargs(); + make_type->set_is_builtin(); + this->globals_->add_function_declaration("make", NULL, make_type, loc); + + Typed_identifier_list* len_result = new Typed_identifier_list(); + len_result->push_back(Typed_identifier("", int_type, loc)); + Function_type* len_type = Type::make_function_type(NULL, NULL, len_result, + loc); + len_type->set_is_builtin(); + this->globals_->add_function_declaration("len", NULL, len_type, loc); + + Typed_identifier_list* cap_result = new Typed_identifier_list(); + cap_result->push_back(Typed_identifier("", int_type, loc)); + Function_type* cap_type = Type::make_function_type(NULL, NULL, len_result, + loc); + cap_type->set_is_builtin(); + this->globals_->add_function_declaration("cap", NULL, cap_type, loc); + + Function_type* print_type = Type::make_function_type(NULL, NULL, NULL, loc); + print_type->set_is_varargs(); + print_type->set_is_builtin(); + this->globals_->add_function_declaration("print", NULL, print_type, loc); + + print_type = Type::make_function_type(NULL, NULL, NULL, loc); + print_type->set_is_varargs(); + print_type->set_is_builtin(); + this->globals_->add_function_declaration("println", NULL, print_type, loc); + + Type *empty = Type::make_empty_interface_type(loc); + Typed_identifier_list* panic_parms = new Typed_identifier_list(); + panic_parms->push_back(Typed_identifier("e", empty, loc)); + Function_type *panic_type = Type::make_function_type(NULL, panic_parms, + NULL, loc); + panic_type->set_is_builtin(); + this->globals_->add_function_declaration("panic", NULL, panic_type, loc); + + Typed_identifier_list* recover_result = new Typed_identifier_list(); + recover_result->push_back(Typed_identifier("", empty, loc)); + Function_type* recover_type = Type::make_function_type(NULL, NULL, + recover_result, + loc); + recover_type->set_is_builtin(); + this->globals_->add_function_declaration("recover", NULL, recover_type, loc); + + Function_type* close_type = Type::make_function_type(NULL, NULL, NULL, loc); + close_type->set_is_varargs(); + close_type->set_is_builtin(); + this->globals_->add_function_declaration("close", NULL, close_type, loc); + + Typed_identifier_list* copy_result = new Typed_identifier_list(); + copy_result->push_back(Typed_identifier("", int_type, loc)); + Function_type* copy_type = Type::make_function_type(NULL, NULL, + copy_result, loc); + copy_type->set_is_varargs(); + copy_type->set_is_builtin(); + this->globals_->add_function_declaration("copy", NULL, copy_type, loc); + + Function_type* append_type = Type::make_function_type(NULL, NULL, NULL, loc); + append_type->set_is_varargs(); + append_type->set_is_builtin(); + this->globals_->add_function_declaration("append", NULL, append_type, loc); + + Function_type* complex_type = Type::make_function_type(NULL, NULL, NULL, loc); + complex_type->set_is_varargs(); + complex_type->set_is_builtin(); + this->globals_->add_function_declaration("complex", NULL, complex_type, loc); + + Function_type* real_type = Type::make_function_type(NULL, NULL, NULL, loc); + real_type->set_is_varargs(); + real_type->set_is_builtin(); + this->globals_->add_function_declaration("real", NULL, real_type, loc); + + Function_type* imag_type = Type::make_function_type(NULL, NULL, NULL, loc); + imag_type->set_is_varargs(); + imag_type->set_is_builtin(); + this->globals_->add_function_declaration("imag", NULL, imag_type, loc); + + Function_type* delete_type = Type::make_function_type(NULL, NULL, NULL, loc); + delete_type->set_is_varargs(); + delete_type->set_is_builtin(); + this->globals_->add_function_declaration("delete", NULL, delete_type, loc); +} + +// Convert a pkgpath into a string suitable for a symbol. Note that +// this transformation is convenient but imperfect. A -fgo-pkgpath +// option of a/b_c will conflict with a -fgo-pkgpath option of a_b/c, +// possibly leading to link time errors. + +std::string +Gogo::pkgpath_for_symbol(const std::string& pkgpath) +{ + std::string s = pkgpath; + for (size_t i = 0; i < s.length(); ++i) + { + char c = s[i]; + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '_' + || c == '.' + || c == '$') + ; + else + s[i] = '_'; + } + return s; +} + +// Get the package path to use for type reflection data. This should +// ideally be unique across the entire link. + +const std::string& +Gogo::pkgpath() const +{ + go_assert(this->pkgpath_set_); + return this->pkgpath_; +} + +// Set the package path from the -fgo-pkgpath command line option. + +void +Gogo::set_pkgpath(const std::string& arg) +{ + go_assert(!this->pkgpath_set_); + this->pkgpath_ = arg; + this->pkgpath_set_ = true; + this->pkgpath_from_option_ = true; +} + +// Get the package path to use for symbol names. + +const std::string& +Gogo::pkgpath_symbol() const +{ + go_assert(this->pkgpath_set_); + return this->pkgpath_symbol_; +} + +// Set the unique prefix to use to determine the package path, from +// the -fgo-prefix command line option. + +void +Gogo::set_prefix(const std::string& arg) +{ + go_assert(!this->prefix_from_option_); + this->prefix_ = arg; + this->prefix_from_option_ = true; +} + +// Munge name for use in an error message. + +std::string +Gogo::message_name(const std::string& name) +{ + return go_localize_identifier(Gogo::unpack_hidden_name(name).c_str()); +} + +// Get the package name. + +const std::string& +Gogo::package_name() const +{ + go_assert(this->package_ != NULL); + return this->package_->package_name(); +} + +// Set the package name. + +void +Gogo::set_package_name(const std::string& package_name, + Location location) +{ + if (this->package_ != NULL) + { + if (this->package_->package_name() != package_name) + error_at(location, "expected package %<%s%>", + Gogo::message_name(this->package_->package_name()).c_str()); + return; + } + + // Now that we know the name of the package we are compiling, set + // the package path to use for reflect.Type.PkgPath and global + // symbol names. + if (!this->pkgpath_set_) + { + if (!this->prefix_from_option_ && package_name == "main") + this->pkgpath_ = package_name; + else + { + if (!this->prefix_from_option_) + this->prefix_ = "go"; + this->pkgpath_ = this->prefix_ + '.' + package_name; + } + this->pkgpath_set_ = true; + } + + this->pkgpath_symbol_ = Gogo::pkgpath_for_symbol(this->pkgpath_); + + this->package_ = this->register_package(this->pkgpath_, location); + this->package_->set_package_name(package_name, location); + + if (this->is_main_package()) + { + // Declare "main" as a function which takes no parameters and + // returns no value. + Location uloc = Linemap::unknown_location(); + this->declare_function(Gogo::pack_hidden_name("main", false), + Type::make_function_type (NULL, NULL, NULL, uloc), + uloc); + } +} + +// Return whether this is the "main" package. This is not true if +// -fgo-pkgpath or -fgo-prefix was used. + +bool +Gogo::is_main_package() const +{ + return (this->package_name() == "main" + && !this->pkgpath_from_option_ + && !this->prefix_from_option_); +} + +// Import a package. + +void +Gogo::import_package(const std::string& filename, + const std::string& local_name, + 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 (local_name == "init") + error_at(location, "cannot import package as init"); + + if (filename == "unsafe") + { + this->import_unsafe(local_name, is_local_name_exported, location); + return; + } + + Imports::const_iterator p = this->imports_.find(filename); + if (p != this->imports_.end()) + { + Package* package = p->second; + package->set_location(location); + package->set_is_imported(); + std::string ln = local_name; + bool is_ln_exported = is_local_name_exported; + if (ln.empty()) + { + ln = package->package_name(); + go_assert(!ln.empty()); + is_ln_exported = Lex::is_exported_name(ln); + } + if (ln == ".") + { + Bindings* bindings = package->bindings(); + for (Bindings::const_declarations_iterator p = + bindings->begin_declarations(); + p != bindings->end_declarations(); + ++p) + this->add_named_object(p->second); + } + else if (ln == "_") + package->set_uses_sink_alias(); + else + { + ln = this->pack_hidden_name(ln, is_ln_exported); + this->package_->bindings()->add_package(ln, package); + } + return; + } + + 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()); + return; + } + + Import imp(stream, location); + imp.register_builtin_types(this); + Package* package = imp.import(this, local_name, is_local_name_exported); + if (package != NULL) + { + if (package->pkgpath() == this->pkgpath()) + error_at(location, + ("imported package uses same package path as package " + "being compiled (see -fgo-pkgpath option)")); + + this->imports_.insert(std::make_pair(filename, package)); + package->set_is_imported(); + } + + delete stream; +} + +// Add an import control function for an imported package to the list. + +void +Gogo::add_import_init_fn(const std::string& package_name, + const std::string& init_name, int prio) +{ + for (std::set::const_iterator p = + this->imported_init_fns_.begin(); + p != this->imported_init_fns_.end(); + ++p) + { + if (p->init_name() == init_name) + { + // If a test of package P1, built as part of package P1, + // imports package P2, and P2 imports P1 (perhaps + // indirectly), then we will see the same import name with + // different import priorities. That is OK, so don't give + // an error about it. + if (p->package_name() != package_name) + { + error("duplicate package initialization name %qs", + Gogo::message_name(init_name).c_str()); + inform(UNKNOWN_LOCATION, "used by package %qs at priority %d", + Gogo::message_name(p->package_name()).c_str(), + p->priority()); + inform(UNKNOWN_LOCATION, " and by package %qs at priority %d", + Gogo::message_name(package_name).c_str(), prio); + } + return; + } + } + + this->imported_init_fns_.insert(Import_init(package_name, init_name, + prio)); +} + +// Return whether we are at the global binding level. + +bool +Gogo::in_global_scope() const +{ + return this->functions_.empty(); +} + +// Return the current binding contour. + +Bindings* +Gogo::current_bindings() +{ + if (!this->functions_.empty()) + return this->functions_.back().blocks.back()->bindings(); + else if (this->package_ != NULL) + return this->package_->bindings(); + else + return this->globals_; +} + +const Bindings* +Gogo::current_bindings() const +{ + if (!this->functions_.empty()) + return this->functions_.back().blocks.back()->bindings(); + else if (this->package_ != NULL) + return this->package_->bindings(); + else + return this->globals_; +} + +// Return the current block. + +Block* +Gogo::current_block() +{ + if (this->functions_.empty()) + return NULL; + else + return this->functions_.back().blocks.back(); +} + +// Look up a name in the current binding contour. If PFUNCTION is not +// NULL, set it to the function in which the name is defined, or NULL +// if the name is defined in global scope. + +Named_object* +Gogo::lookup(const std::string& name, Named_object** pfunction) const +{ + if (pfunction != NULL) + *pfunction = NULL; + + if (Gogo::is_sink_name(name)) + return Named_object::make_sink(); + + for (Open_functions::const_reverse_iterator p = this->functions_.rbegin(); + p != this->functions_.rend(); + ++p) + { + Named_object* ret = p->blocks.back()->bindings()->lookup(name); + if (ret != NULL) + { + if (pfunction != NULL) + *pfunction = p->function; + return ret; + } + } + + if (this->package_ != NULL) + { + Named_object* ret = this->package_->bindings()->lookup(name); + if (ret != NULL) + { + if (ret->package() != NULL) + ret->package()->set_used(); + return ret; + } + } + + // We do not look in the global namespace. If we did, the global + // namespace would effectively hide names which were defined in + // package scope which we have not yet seen. Instead, + // define_global_names is called after parsing is over to connect + // undefined names at package scope with names defined at global + // scope. + + return NULL; +} + +// Look up a name in the current block, without searching enclosing +// blocks. + +Named_object* +Gogo::lookup_in_block(const std::string& name) const +{ + go_assert(!this->functions_.empty()); + go_assert(!this->functions_.back().blocks.empty()); + return this->functions_.back().blocks.back()->bindings()->lookup_local(name); +} + +// Look up a name in the global namespace. + +Named_object* +Gogo::lookup_global(const char* name) const +{ + return this->globals_->lookup(name); +} + +// Add an imported package. + +Package* +Gogo::add_imported_package(const std::string& real_name, + const std::string& alias_arg, + bool is_alias_exported, + const std::string& pkgpath, + Location location, + bool* padd_to_globals) +{ + Package* ret = this->register_package(pkgpath, location); + ret->set_package_name(real_name, location); + + *padd_to_globals = false; + + if (alias_arg == ".") + *padd_to_globals = true; + else if (alias_arg == "_") + ret->set_uses_sink_alias(); + else + { + std::string alias = alias_arg; + if (alias.empty()) + { + alias = real_name; + is_alias_exported = Lex::is_exported_name(alias); + } + alias = this->pack_hidden_name(alias, is_alias_exported); + Named_object* no = this->package_->bindings()->add_package(alias, ret); + if (!no->is_package()) + return NULL; + } + + return ret; +} + +// Register a package. This package may or may not be imported. This +// returns the Package structure for the package, creating if it +// necessary. LOCATION is the location of the import statement that +// led us to see this package. + +Package* +Gogo::register_package(const std::string& pkgpath, Location location) +{ + Package* package = NULL; + std::pair ins = + this->packages_.insert(std::make_pair(pkgpath, package)); + if (!ins.second) + { + // We have seen this package name before. + package = ins.first->second; + go_assert(package != NULL && package->pkgpath() == pkgpath); + if (Linemap::is_unknown_location(package->location())) + package->set_location(location); + } + else + { + // First time we have seen this package name. + package = new Package(pkgpath, location); + go_assert(ins.first->second == NULL); + ins.first->second = package; + } + + return package; +} + +// Start compiling a function. + +Named_object* +Gogo::start_function(const std::string& name, Function_type* type, + bool add_method_to_type, Location location) +{ + bool at_top_level = this->functions_.empty(); + + Block* block = new Block(NULL, location); + + Function* enclosing = (at_top_level + ? NULL + : this->functions_.back().function->func_value()); + + Function* function = new Function(type, enclosing, block, location); + + if (type->is_method()) + { + const Typed_identifier* receiver = type->receiver(); + Variable* this_param = new Variable(receiver->type(), NULL, false, + true, true, location); + std::string rname = receiver->name(); + if (rname.empty() || Gogo::is_sink_name(rname)) + { + // We need to give receivers a name since they wind up in + // DECL_ARGUMENTS. FIXME. + static unsigned int count; + char buf[50]; + snprintf(buf, sizeof buf, "r.%u", count); + ++count; + rname = buf; + } + block->bindings()->add_variable(rname, NULL, this_param); + } + + const Typed_identifier_list* parameters = type->parameters(); + bool is_varargs = type->is_varargs(); + if (parameters != NULL) + { + for (Typed_identifier_list::const_iterator p = parameters->begin(); + p != parameters->end(); + ++p) + { + Variable* param = new Variable(p->type(), NULL, false, true, false, + location); + if (is_varargs && p + 1 == parameters->end()) + param->set_is_varargs_parameter(); + + std::string pname = p->name(); + if (pname.empty() || Gogo::is_sink_name(pname)) + { + // We need to give parameters a name since they wind up + // in DECL_ARGUMENTS. FIXME. + static unsigned int count; + char buf[50]; + snprintf(buf, sizeof buf, "p.%u", count); + ++count; + pname = buf; + } + block->bindings()->add_variable(pname, NULL, param); + } + } + + function->create_result_variables(this); + + const std::string* pname; + std::string nested_name; + bool is_init = false; + if (Gogo::unpack_hidden_name(name) == "init" && !type->is_method()) + { + if ((type->parameters() != NULL && !type->parameters()->empty()) + || (type->results() != NULL && !type->results()->empty())) + error_at(location, + "func init must have no arguments and no return values"); + // There can be multiple "init" functions, so give them each a + // different name. + static int init_count; + char buf[30]; + snprintf(buf, sizeof buf, ".$init%d", init_count); + ++init_count; + nested_name = buf; + pname = &nested_name; + is_init = true; + } + else if (!name.empty()) + pname = &name; + else + { + // Invent a name for a nested function. + static int nested_count; + char buf[30]; + snprintf(buf, sizeof buf, ".$nested%d", nested_count); + ++nested_count; + nested_name = buf; + pname = &nested_name; + } + + Named_object* ret; + if (Gogo::is_sink_name(*pname)) + { + static int sink_count; + char buf[30]; + snprintf(buf, sizeof buf, ".$sink%d", sink_count); + ++sink_count; + ret = this->package_->bindings()->add_function(buf, NULL, function); + ret->func_value()->set_is_sink(); + } + else if (!type->is_method()) + { + ret = this->package_->bindings()->add_function(*pname, NULL, function); + if (!ret->is_function() || ret->func_value() != function) + { + // Redefinition error. Invent a name to avoid knockon + // errors. + static int redefinition_count; + char buf[30]; + snprintf(buf, sizeof buf, ".$redefined%d", redefinition_count); + ++redefinition_count; + ret = this->package_->bindings()->add_function(buf, NULL, function); + } + } + else + { + if (!add_method_to_type) + ret = Named_object::make_function(name, NULL, function); + else + { + go_assert(at_top_level); + Type* rtype = type->receiver()->type(); + + // We want to look through the pointer created by the + // parser, without getting an error if the type is not yet + // defined. + if (rtype->classification() == Type::TYPE_POINTER) + rtype = rtype->points_to(); + + if (rtype->is_error_type()) + ret = Named_object::make_function(name, NULL, function); + else if (rtype->named_type() != NULL) + { + ret = rtype->named_type()->add_method(name, function); + if (!ret->is_function()) + { + // Redefinition error. + ret = Named_object::make_function(name, NULL, function); + } + } + else if (rtype->forward_declaration_type() != NULL) + { + Named_object* type_no = + rtype->forward_declaration_type()->named_object(); + if (type_no->is_unknown()) + { + // If we are seeing methods it really must be a + // type. Declare it as such. An alternative would + // be to support lists of methods for unknown + // expressions. Either way the error messages if + // this is not a type are going to get confusing. + Named_object* declared = + this->declare_package_type(type_no->name(), + type_no->location()); + go_assert(declared + == type_no->unknown_value()->real_named_object()); + } + ret = rtype->forward_declaration_type()->add_method(name, + function); + } + else + go_unreachable(); + } + this->package_->bindings()->add_method(ret); + } + + this->functions_.resize(this->functions_.size() + 1); + Open_function& of(this->functions_.back()); + of.function = ret; + of.blocks.push_back(block); + + if (is_init) + { + this->init_functions_.push_back(ret); + this->need_init_fn_ = true; + } + + return ret; +} + +// Finish compiling a function. + +void +Gogo::finish_function(Location location) +{ + this->finish_block(location); + go_assert(this->functions_.back().blocks.empty()); + this->functions_.pop_back(); +} + +// Return the current function. + +Named_object* +Gogo::current_function() const +{ + go_assert(!this->functions_.empty()); + return this->functions_.back().function; +} + +// Start a new block. + +void +Gogo::start_block(Location location) +{ + go_assert(!this->functions_.empty()); + Block* block = new Block(this->current_block(), location); + this->functions_.back().blocks.push_back(block); +} + +// Finish a block. + +Block* +Gogo::finish_block(Location location) +{ + go_assert(!this->functions_.empty()); + go_assert(!this->functions_.back().blocks.empty()); + Block* block = this->functions_.back().blocks.back(); + this->functions_.back().blocks.pop_back(); + block->set_end_location(location); + return block; +} + +// Add an erroneous name. + +Named_object* +Gogo::add_erroneous_name(const std::string& name) +{ + return this->package_->bindings()->add_erroneous_name(name); +} + +// Add an unknown name. + +Named_object* +Gogo::add_unknown_name(const std::string& name, Location location) +{ + return this->package_->bindings()->add_unknown_name(name, location); +} + +// Declare a function. + +Named_object* +Gogo::declare_function(const std::string& name, Function_type* type, + Location location) +{ + if (!type->is_method()) + return this->current_bindings()->add_function_declaration(name, NULL, type, + location); + else + { + // We don't bother to add this to the list of global + // declarations. + Type* rtype = type->receiver()->type(); + + // We want to look through the pointer created by the + // parser, without getting an error if the type is not yet + // defined. + if (rtype->classification() == Type::TYPE_POINTER) + rtype = rtype->points_to(); + + if (rtype->is_error_type()) + return NULL; + else if (rtype->named_type() != NULL) + return rtype->named_type()->add_method_declaration(name, NULL, type, + location); + else if (rtype->forward_declaration_type() != NULL) + { + Forward_declaration_type* ftype = rtype->forward_declaration_type(); + return ftype->add_method_declaration(name, NULL, type, location); + } + else + go_unreachable(); + } +} + +// Add a label definition. + +Label* +Gogo::add_label_definition(const std::string& label_name, + Location location) +{ + go_assert(!this->functions_.empty()); + Function* func = this->functions_.back().function->func_value(); + Label* label = func->add_label_definition(this, label_name, location); + this->add_statement(Statement::make_label_statement(label, location)); + return label; +} + +// Add a label reference. + +Label* +Gogo::add_label_reference(const std::string& label_name, + Location location, bool issue_goto_errors) +{ + go_assert(!this->functions_.empty()); + Function* func = this->functions_.back().function->func_value(); + return func->add_label_reference(this, label_name, location, + issue_goto_errors); +} + +// Return the current binding state. + +Bindings_snapshot* +Gogo::bindings_snapshot(Location location) +{ + return new Bindings_snapshot(this->current_block(), location); +} + +// Add a statement. + +void +Gogo::add_statement(Statement* statement) +{ + go_assert(!this->functions_.empty() + && !this->functions_.back().blocks.empty()); + this->functions_.back().blocks.back()->add_statement(statement); +} + +// Add a block. + +void +Gogo::add_block(Block* block, Location location) +{ + go_assert(!this->functions_.empty() + && !this->functions_.back().blocks.empty()); + Statement* statement = Statement::make_block_statement(block, location); + this->functions_.back().blocks.back()->add_statement(statement); +} + +// Add a constant. + +Named_object* +Gogo::add_constant(const Typed_identifier& tid, Expression* expr, + int iota_value) +{ + return this->current_bindings()->add_constant(tid, NULL, expr, iota_value); +} + +// Add a type. + +void +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()) + { + 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. + +void +Gogo::add_named_type(Named_type* type) +{ + go_assert(this->in_global_scope()); + this->current_bindings()->add_named_type(type); +} + +// Declare a type. + +Named_object* +Gogo::declare_type(const std::string& name, Location location) +{ + Bindings* bindings = this->current_bindings(); + Named_object* no = bindings->add_type_declaration(name, NULL, location); + if (!this->in_global_scope() && no->is_type_declaration()) + { + 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_declaration_value()->set_in_function(f, index); + } + return no; +} + +// Declare a type at the package level. + +Named_object* +Gogo::declare_package_type(const std::string& name, Location location) +{ + return this->package_->bindings()->add_type_declaration(name, NULL, location); +} + +// Declare a function at the package level. + +Named_object* +Gogo::declare_package_function(const std::string& name, Function_type* type, + Location location) +{ + return this->package_->bindings()->add_function_declaration(name, NULL, type, + location); +} + +// Define a type which was already declared. + +void +Gogo::define_type(Named_object* no, Named_type* type) +{ + this->current_bindings()->define_type(no, type); +} + +// Add a variable. + +Named_object* +Gogo::add_variable(const std::string& name, Variable* variable) +{ + Named_object* no = this->current_bindings()->add_variable(name, NULL, + variable); + + // In a function the middle-end wants to see a DECL_EXPR node. + if (no != NULL + && no->is_variable() + && !no->var_value()->is_parameter() + && !this->functions_.empty()) + this->add_statement(Statement::make_variable_declaration(no)); + + return no; +} + +// Add a sink--a reference to the blank identifier _. + +Named_object* +Gogo::add_sink() +{ + return Named_object::make_sink(); +} + +// Add a named object. + +void +Gogo::add_named_object(Named_object* no) +{ + this->current_bindings()->add_named_object(no); +} + +// Mark all local variables used. This is used when some types of +// parse error occur. + +void +Gogo::mark_locals_used() +{ + for (Open_functions::iterator pf = this->functions_.begin(); + pf != this->functions_.end(); + ++pf) + { + for (std::vector::iterator pb = pf->blocks.begin(); + pb != pf->blocks.end(); + ++pb) + (*pb)->bindings()->mark_locals_used(); + } +} + +// Record that we've seen an interface type. + +void +Gogo::record_interface_type(Interface_type* itype) +{ + this->interface_types_.push_back(itype); +} + +// Return an erroneous name that indicates that an error has already +// been reported. + +std::string +Gogo::erroneous_name() +{ + static int erroneous_count; + char name[50]; + snprintf(name, sizeof name, "$erroneous%d", erroneous_count); + ++erroneous_count; + return name; +} + +// Return whether a name is an erroneous name. + +bool +Gogo::is_erroneous_name(const std::string& name) +{ + return name.compare(0, 10, "$erroneous") == 0; +} + +// Return a name for a thunk object. + +std::string +Gogo::thunk_name() +{ + static int thunk_count; + char thunk_name[50]; + snprintf(thunk_name, sizeof thunk_name, "$thunk%d", thunk_count); + ++thunk_count; + return thunk_name; +} + +// Return whether a function is a thunk. + +bool +Gogo::is_thunk(const Named_object* no) +{ + return no->name().compare(0, 6, "$thunk") == 0; +} + +// Define the global names. We do this only after parsing all the +// input files, because the program might define the global names +// itself. + +void +Gogo::define_global_names() +{ + for (Bindings::const_declarations_iterator p = + this->globals_->begin_declarations(); + p != this->globals_->end_declarations(); + ++p) + { + Named_object* global_no = p->second; + std::string name(Gogo::pack_hidden_name(global_no->name(), false)); + Named_object* no = this->package_->bindings()->lookup(name); + if (no == NULL) + continue; + no = no->resolve(); + if (no->is_type_declaration()) + { + if (global_no->is_type()) + { + if (no->type_declaration_value()->has_methods()) + error_at(no->location(), + "may not define methods for global type"); + no->set_type_value(global_no->type_value()); + } + else + { + error_at(no->location(), "expected type"); + Type* errtype = Type::make_error_type(); + Named_object* err = + Named_object::make_type("erroneous_type", NULL, errtype, + Linemap::predeclared_location()); + no->set_type_value(err->type_value()); + } + } + else if (no->is_unknown()) + no->unknown_value()->set_real_named_object(global_no); + } + + // Give an error if any name is defined in both the package block + // and the file block. For example, this can happen if one file + // imports "fmt" and another file defines a global variable fmt. + for (Bindings::const_declarations_iterator p = + this->package_->bindings()->begin_declarations(); + p != this->package_->bindings()->end_declarations(); + ++p) + { + if (p->second->is_unknown() + && p->second->unknown_value()->real_named_object() == NULL) + { + // No point in warning about an undefined name, as we will + // get other errors later anyhow. + continue; + } + File_block_names::const_iterator pf = + this->file_block_names_.find(p->second->name()); + if (pf != this->file_block_names_.end()) + { + std::string n = p->second->message_name(); + error_at(p->second->location(), + "%qs defined as both imported name and global name", + n.c_str()); + inform(pf->second, "%qs imported here", n.c_str()); + } + + // No package scope identifier may be named "init". + if (!p->second->is_function() + && Gogo::unpack_hidden_name(p->second->name()) == "init") + { + error_at(p->second->location(), + "cannot declare init - must be func"); + } + } +} + +// Clear out names in file scope. + +void +Gogo::clear_file_scope() +{ + this->package_->bindings()->clear_file_scope(this); + + // Warn about packages which were imported but not used. + bool quiet = saw_errors(); + for (Packages::iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + { + Package* package = p->second; + if (package != this->package_ + && package->is_imported() + && !package->used() + && !package->uses_sink_alias() + && !quiet) + error_at(package->location(), "imported and not used: %s", + Gogo::message_name(package->package_name()).c_str()); + package->clear_is_imported(); + package->clear_uses_sink_alias(); + package->clear_used(); + } +} + +// Queue up a type specific function for later writing. These are +// written out in write_specific_type_functions, called after the +// parse tree is lowered. + +void +Gogo::queue_specific_type_function(Type* type, Named_type* name, + const std::string& hash_name, + Function_type* hash_fntype, + const std::string& equal_name, + Function_type* equal_fntype) +{ + go_assert(!this->specific_type_functions_are_written_); + go_assert(!this->in_global_scope()); + Specific_type_function* tsf = new Specific_type_function(type, name, + hash_name, + hash_fntype, + equal_name, + equal_fntype); + this->specific_type_functions_.push_back(tsf); +} + +// Look for types which need specific hash or equality functions. + +class Specific_type_functions : public Traverse +{ + public: + Specific_type_functions(Gogo* gogo) + : Traverse(traverse_types), + gogo_(gogo) + { } + + int + type(Type*); + + private: + Gogo* gogo_; +}; + +int +Specific_type_functions::type(Type* t) +{ + Named_object* hash_fn; + Named_object* equal_fn; + switch (t->classification()) + { + case Type::TYPE_NAMED: + { + Named_type* nt = t->named_type(); + if (!t->compare_is_identity(this->gogo_) && t->is_comparable()) + t->type_functions(this->gogo_, nt, NULL, NULL, &hash_fn, &equal_fn); + + // If this is a struct type, we don't want to make functions + // for the unnamed struct. + Type* rt = nt->real_type(); + if (rt->struct_type() == NULL) + { + if (Type::traverse(rt, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + else + { + // If this type is defined in another package, then we don't + // need to worry about the unexported fields. + bool is_defined_elsewhere = nt->named_object()->package() != NULL; + const Struct_field_list* fields = rt->struct_type()->fields(); + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + if (is_defined_elsewhere + && Gogo::is_hidden_name(p->field_name())) + continue; + if (Type::traverse(p->type(), this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + + return TRAVERSE_SKIP_COMPONENTS; + } + + case Type::TYPE_STRUCT: + case Type::TYPE_ARRAY: + if (!t->compare_is_identity(this->gogo_) && t->is_comparable()) + t->type_functions(this->gogo_, NULL, NULL, NULL, &hash_fn, &equal_fn); + break; + + default: + break; + } + + return TRAVERSE_CONTINUE; +} + +// Write out type specific functions. + +void +Gogo::write_specific_type_functions() +{ + Specific_type_functions stf(this); + this->traverse(&stf); + + while (!this->specific_type_functions_.empty()) + { + Specific_type_function* tsf = this->specific_type_functions_.back(); + this->specific_type_functions_.pop_back(); + tsf->type->write_specific_type_functions(this, tsf->name, + tsf->hash_name, + tsf->hash_fntype, + tsf->equal_name, + tsf->equal_fntype); + delete tsf; + } + this->specific_type_functions_are_written_ = true; +} + +// Traverse the tree. + +void +Gogo::traverse(Traverse* traverse) +{ + // Traverse the current package first for consistency. The other + // packages will only contain imported types, constants, and + // declarations. + if (this->package_->bindings()->traverse(traverse, true) == TRAVERSE_EXIT) + return; + for (Packages::const_iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + { + if (p->second != this->package_) + { + if (p->second->bindings()->traverse(traverse, true) == TRAVERSE_EXIT) + break; + } + } +} + +// Add a type to verify. This is used for types of sink variables, in +// order to give appropriate error messages. + +void +Gogo::add_type_to_verify(Type* type) +{ + this->verify_types_.push_back(type); +} + +// Traversal class used to verify types. + +class Verify_types : public Traverse +{ + public: + Verify_types() + : Traverse(traverse_types) + { } + + int + type(Type*); +}; + +// Verify that a type is correct. + +int +Verify_types::type(Type* t) +{ + if (!t->verify()) + return TRAVERSE_SKIP_COMPONENTS; + return TRAVERSE_CONTINUE; +} + +// Verify that all types are correct. + +void +Gogo::verify_types() +{ + Verify_types traverse; + this->traverse(&traverse); + + for (std::vector::iterator p = this->verify_types_.begin(); + p != this->verify_types_.end(); + ++p) + (*p)->verify(); + this->verify_types_.clear(); +} + +// Traversal class used to lower parse tree. + +class Lower_parse_tree : public Traverse +{ + public: + Lower_parse_tree(Gogo* gogo, Named_object* function) + : Traverse(traverse_variables + | traverse_constants + | traverse_functions + | traverse_statements + | traverse_expressions), + gogo_(gogo), function_(function), iota_value_(-1), inserter_() + { } + + void + set_inserter(const Statement_inserter* inserter) + { this->inserter_ = *inserter; } + + int + variable(Named_object*); + + int + constant(Named_object*, bool); + + int + function(Named_object*); + + int + statement(Block*, size_t* pindex, Statement*); + + int + expression(Expression**); + + private: + // General IR. + Gogo* gogo_; + // The function we are traversing. + Named_object* function_; + // Value to use for the predeclared constant iota. + int iota_value_; + // Current statement inserter for use by expressions. + Statement_inserter inserter_; +}; + +// Lower variables. + +int +Lower_parse_tree::variable(Named_object* no) +{ + if (!no->is_variable()) + return TRAVERSE_CONTINUE; + + if (no->is_variable() && no->var_value()->is_global()) + { + // Global variables can have loops in their initialization + // expressions. This is handled in lower_init_expression. + no->var_value()->lower_init_expression(this->gogo_, this->function_, + &this->inserter_); + return TRAVERSE_CONTINUE; + } + + // This is a local variable. We are going to return + // TRAVERSE_SKIP_COMPONENTS here because we want to traverse the + // initialization expression when we reach the variable declaration + // statement. However, that means that we need to traverse the type + // ourselves. + if (no->var_value()->has_type()) + { + Type* type = no->var_value()->type(); + if (type != NULL) + { + if (Type::traverse(type, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + go_assert(!no->var_value()->has_pre_init()); + + return TRAVERSE_SKIP_COMPONENTS; +} + +// Lower constants. We handle constants specially so that we can set +// the right value for the predeclared constant iota. This works in +// conjunction with the way we lower Const_expression objects. + +int +Lower_parse_tree::constant(Named_object* no, bool) +{ + Named_constant* nc = no->const_value(); + + // Don't get into trouble if the constant's initializer expression + // refers to the constant itself. + if (nc->lowering()) + return TRAVERSE_CONTINUE; + nc->set_lowering(); + + go_assert(this->iota_value_ == -1); + this->iota_value_ = nc->iota_value(); + nc->traverse_expression(this); + this->iota_value_ = -1; + + nc->clear_lowering(); + + // We will traverse the expression a second time, but that will be + // fast. + + return TRAVERSE_CONTINUE; +} + +// Lower the body of a function, and set the closure type. Record the +// function while lowering it, so that we can pass it down when +// lowering an expression. + +int +Lower_parse_tree::function(Named_object* no) +{ + no->func_value()->set_closure_type(); + + go_assert(this->function_ == NULL); + this->function_ = no; + int t = no->func_value()->traverse(this); + this->function_ = NULL; + + if (t == TRAVERSE_EXIT) + return t; + return TRAVERSE_SKIP_COMPONENTS; +} + +// Lower statement parse trees. + +int +Lower_parse_tree::statement(Block* block, size_t* pindex, Statement* sorig) +{ + // Because we explicitly traverse the statement's contents + // ourselves, we want to skip block statements here. There is + // nothing to lower in a block statement. + if (sorig->is_block_statement()) + return TRAVERSE_CONTINUE; + + Statement_inserter hold_inserter(this->inserter_); + this->inserter_ = Statement_inserter(block, pindex); + + // Lower the expressions first. + int t = sorig->traverse_contents(this); + if (t == TRAVERSE_EXIT) + { + this->inserter_ = hold_inserter; + return t; + } + + // Keep lowering until nothing changes. + Statement* s = sorig; + while (true) + { + Statement* snew = s->lower(this->gogo_, this->function_, block, + &this->inserter_); + if (snew == s) + break; + s = snew; + t = s->traverse_contents(this); + if (t == TRAVERSE_EXIT) + { + this->inserter_ = hold_inserter; + return t; + } + } + + if (s != sorig) + block->replace_statement(*pindex, s); + + this->inserter_ = hold_inserter; + return TRAVERSE_SKIP_COMPONENTS; +} + +// Lower expression parse trees. + +int +Lower_parse_tree::expression(Expression** pexpr) +{ + // We have to lower all subexpressions first, so that we can get + // their type if necessary. This is awkward, because we don't have + // a postorder traversal pass. + if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + // Keep lowering until nothing changes. + while (true) + { + Expression* e = *pexpr; + Expression* enew = e->lower(this->gogo_, this->function_, + &this->inserter_, this->iota_value_); + if (enew == e) + break; + if (enew->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + *pexpr = enew; + } + return TRAVERSE_SKIP_COMPONENTS; +} + +// Lower the parse tree. This is called after the parse is complete, +// when all names should be resolved. + +void +Gogo::lower_parse_tree() +{ + Lower_parse_tree lower_parse_tree(this, NULL); + this->traverse(&lower_parse_tree); +} + +// Lower a block. + +void +Gogo::lower_block(Named_object* function, Block* block) +{ + Lower_parse_tree lower_parse_tree(this, function); + block->traverse(&lower_parse_tree); +} + +// Lower an expression. INSERTER may be NULL, in which case the +// expression had better not need to create any temporaries. + +void +Gogo::lower_expression(Named_object* function, Statement_inserter* inserter, + Expression** pexpr) +{ + Lower_parse_tree lower_parse_tree(this, function); + if (inserter != NULL) + lower_parse_tree.set_inserter(inserter); + lower_parse_tree.expression(pexpr); +} + +// Lower a constant. This is called when lowering a reference to a +// constant. We have to make sure that the constant has already been +// lowered. + +void +Gogo::lower_constant(Named_object* no) +{ + go_assert(no->is_const()); + Lower_parse_tree lower(this, NULL); + lower.constant(no, false); +} + +// Traverse the tree to create function descriptors as needed. + +class Create_function_descriptors : public Traverse +{ + public: + Create_function_descriptors(Gogo* gogo) + : Traverse(traverse_functions | traverse_expressions), + gogo_(gogo) + { } + + int + function(Named_object*); + + int + expression(Expression**); + + private: + Gogo* gogo_; +}; + +// Create a descriptor for every top-level exported function. + +int +Create_function_descriptors::function(Named_object* no) +{ + if (no->is_function() + && no->func_value()->enclosing() == NULL + && !no->func_value()->is_method() + && !Gogo::is_hidden_name(no->name()) + && !Gogo::is_thunk(no)) + no->func_value()->descriptor(this->gogo_, no); + + return TRAVERSE_CONTINUE; +} + +// If we see a function referenced in any way other than calling it, +// create a descriptor for it. + +int +Create_function_descriptors::expression(Expression** pexpr) +{ + Expression* expr = *pexpr; + + Func_expression* fe = expr->func_expression(); + if (fe != NULL) + { + // We would not get here for a call to this function, so this is + // a reference to a function other than calling it. We need a + // descriptor. + if (fe->closure() != NULL) + return TRAVERSE_CONTINUE; + Named_object* no = fe->named_object(); + if (no->is_function() && !no->func_value()->is_method()) + no->func_value()->descriptor(this->gogo_, no); + else if (no->is_function_declaration() + && !no->func_declaration_value()->type()->is_method() + && !Linemap::is_predeclared_location(no->location())) + no->func_declaration_value()->descriptor(this->gogo_, no); + return TRAVERSE_CONTINUE; + } + + Bound_method_expression* bme = expr->bound_method_expression(); + if (bme != NULL) + { + // We would not get here for a call to this method, so this is a + // method value. We need to create a thunk. + Bound_method_expression::create_thunk(this->gogo_, bme->method(), + bme->function()); + return TRAVERSE_CONTINUE; + } + + Interface_field_reference_expression* ifre = + expr->interface_field_reference_expression(); + if (ifre != NULL) + { + // We would not get here for a call to this interface method, so + // this is a method value. We need to create a thunk. + Interface_type* type = ifre->expr()->type()->interface_type(); + if (type != NULL) + Interface_field_reference_expression::create_thunk(this->gogo_, type, + ifre->name()); + return TRAVERSE_CONTINUE; + } + + Call_expression* ce = expr->call_expression(); + if (ce != NULL) + { + Expression* fn = ce->fn(); + if (fn->func_expression() != NULL + || fn->bound_method_expression() != NULL + || fn->interface_field_reference_expression() != NULL) + { + // Traverse the arguments but not the function. + Expression_list* args = ce->args(); + if (args != NULL) + { + if (args->traverse(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_SKIP_COMPONENTS; + } + } + + return TRAVERSE_CONTINUE; +} + +// Create function descriptors as needed. We need a function +// descriptor for all exported functions and for all functions that +// are referenced without being called. + +void +Gogo::create_function_descriptors() +{ + // Create a function descriptor for any exported function that is + // declared in this package. This is so that we have a descriptor + // for functions written in assembly. Gather the descriptors first + // so that we don't add declarations while looping over them. + std::vector fndecls; + Bindings* b = this->package_->bindings(); + for (Bindings::const_declarations_iterator p = b->begin_declarations(); + p != b->end_declarations(); + ++p) + { + Named_object* no = p->second; + if (no->is_function_declaration() + && !no->func_declaration_value()->type()->is_method() + && !Linemap::is_predeclared_location(no->location()) + && !Gogo::is_hidden_name(no->name())) + fndecls.push_back(no); + } + for (std::vector::const_iterator p = fndecls.begin(); + p != fndecls.end(); + ++p) + (*p)->func_declaration_value()->descriptor(this, *p); + fndecls.clear(); + + Create_function_descriptors cfd(this); + this->traverse(&cfd); +} + +// Look for interface types to finalize methods of inherited +// interfaces. + +class Finalize_methods : public Traverse +{ + public: + Finalize_methods(Gogo* gogo) + : Traverse(traverse_types), + gogo_(gogo) + { } + + int + type(Type*); + + private: + Gogo* gogo_; +}; + +// Finalize the methods of an interface type. + +int +Finalize_methods::type(Type* t) +{ + // Check the classification so that we don't finalize the methods + // twice for a named interface type. + switch (t->classification()) + { + case Type::TYPE_INTERFACE: + t->interface_type()->finalize_methods(); + break; + + case Type::TYPE_NAMED: + { + // We have to finalize the methods of the real type first. + // But if the real type is a struct type, then we only want to + // finalize the methods of the field types, not of the struct + // type itself. We don't want to add methods to the struct, + // since it has a name. + Named_type* nt = t->named_type(); + Type* rt = nt->real_type(); + if (rt->classification() != Type::TYPE_STRUCT) + { + if (Type::traverse(rt, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + else + { + if (rt->struct_type()->traverse_field_types(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + nt->finalize_methods(this->gogo_); + + // If this type is defined in a different package, then finalize the + // types of all the methods, since we won't see them otherwise. + if (nt->named_object()->package() != NULL && nt->has_any_methods()) + { + const Methods* methods = nt->methods(); + for (Methods::const_iterator p = methods->begin(); + p != methods->end(); + ++p) + { + if (Type::traverse(p->second->type(), this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + + // Finalize the types of all methods that are declared but not + // defined, since we won't see the declarations otherwise. + if (nt->named_object()->package() == NULL + && nt->local_methods() != NULL) + { + const Bindings* methods = nt->local_methods(); + for (Bindings::const_declarations_iterator p = + methods->begin_declarations(); + p != methods->end_declarations(); + p++) + { + if (p->second->is_function_declaration()) + { + Type* mt = p->second->func_declaration_value()->type(); + if (Type::traverse(mt, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + } + + return TRAVERSE_SKIP_COMPONENTS; + } + + case Type::TYPE_STRUCT: + // Traverse the field types first in case there is an embedded + // field with methods that the struct should inherit. + if (t->struct_type()->traverse_field_types(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + t->struct_type()->finalize_methods(this->gogo_); + return TRAVERSE_SKIP_COMPONENTS; + + default: + break; + } + + return TRAVERSE_CONTINUE; +} + +// Finalize method lists and build stub methods for types. + +void +Gogo::finalize_methods() +{ + Finalize_methods finalize(this); + this->traverse(&finalize); +} + +// Set types for unspecified variables and constants. + +void +Gogo::determine_types() +{ + Bindings* bindings = this->current_bindings(); + for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); + p != bindings->end_definitions(); + ++p) + { + if ((*p)->is_function()) + (*p)->func_value()->determine_types(); + else if ((*p)->is_variable()) + (*p)->var_value()->determine_type(); + else if ((*p)->is_const()) + (*p)->const_value()->determine_type(); + + // See if a variable requires us to build an initialization + // function. We know that we will see all global variables + // here. + if (!this->need_init_fn_ && (*p)->is_variable()) + { + Variable* variable = (*p)->var_value(); + + // If this is a global variable which requires runtime + // initialization, we need an initialization function. + if (!variable->is_global()) + ; + else if (variable->init() == NULL) + ; + else if (variable->type()->interface_type() != NULL) + this->need_init_fn_ = true; + else if (variable->init()->is_constant()) + ; + else if (!variable->init()->is_composite_literal()) + this->need_init_fn_ = true; + else if (variable->init()->is_nonconstant_composite_literal()) + this->need_init_fn_ = true; + + // If this is a global variable which holds a pointer value, + // then we need an initialization function to register it as a + // GC root. + if (variable->is_global() && variable->type()->has_pointer()) + this->need_init_fn_ = true; + } + } + + // Determine the types of constants in packages. + for (Packages::const_iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + p->second->determine_types(); +} + +// Traversal class used for type checking. + +class Check_types_traverse : public Traverse +{ + public: + Check_types_traverse(Gogo* gogo) + : Traverse(traverse_variables + | traverse_constants + | traverse_functions + | traverse_statements + | traverse_expressions), + gogo_(gogo) + { } + + int + variable(Named_object*); + + int + constant(Named_object*, bool); + + int + function(Named_object*); + + int + statement(Block*, size_t* pindex, Statement*); + + int + expression(Expression**); + + private: + // General IR. + Gogo* gogo_; +}; + +// Check that a variable initializer has the right type. + +int +Check_types_traverse::variable(Named_object* named_object) +{ + if (named_object->is_variable()) + { + Variable* var = named_object->var_value(); + + // Give error if variable type is not defined. + var->type()->base(); + + Expression* init = var->init(); + std::string reason; + if (init != NULL + && !Type::are_assignable(var->type(), init->type(), &reason)) + { + if (reason.empty()) + error_at(var->location(), "incompatible type in initialization"); + else + error_at(var->location(), + "incompatible type in initialization (%s)", + reason.c_str()); + var->clear_init(); + } + else if (!var->is_used() + && !var->is_global() + && !var->is_parameter() + && !var->is_receiver() + && !var->type()->is_error() + && (init == NULL || !init->is_error_expression()) + && !Lex::is_invalid_identifier(named_object->name())) + error_at(var->location(), "%qs declared and not used", + named_object->message_name().c_str()); + } + return TRAVERSE_CONTINUE; +} + +// Check that a constant initializer has the right type. + +int +Check_types_traverse::constant(Named_object* named_object, bool) +{ + Named_constant* constant = named_object->const_value(); + Type* ctype = constant->type(); + if (ctype->integer_type() == NULL + && ctype->float_type() == NULL + && ctype->complex_type() == NULL + && !ctype->is_boolean_type() + && !ctype->is_string_type()) + { + if (ctype->is_nil_type()) + error_at(constant->location(), "const initializer cannot be nil"); + else if (!ctype->is_error()) + error_at(constant->location(), "invalid constant type"); + constant->set_error(); + } + else if (!constant->expr()->is_constant()) + { + error_at(constant->expr()->location(), "expression is not constant"); + constant->set_error(); + } + else if (!Type::are_assignable(constant->type(), constant->expr()->type(), + NULL)) + { + error_at(constant->location(), + "initialization expression has wrong type"); + constant->set_error(); + } + return TRAVERSE_CONTINUE; +} + +// There are no types to check in a function, but this is where we +// issue warnings about labels which are defined but not referenced. + +int +Check_types_traverse::function(Named_object* no) +{ + no->func_value()->check_labels(); + return TRAVERSE_CONTINUE; +} + +// Check that types are valid in a statement. + +int +Check_types_traverse::statement(Block*, size_t*, Statement* s) +{ + s->check_types(this->gogo_); + return TRAVERSE_CONTINUE; +} + +// Check that types are valid in an expression. + +int +Check_types_traverse::expression(Expression** expr) +{ + (*expr)->check_types(this->gogo_); + return TRAVERSE_CONTINUE; +} + +// Check that types are valid. + +void +Gogo::check_types() +{ + Check_types_traverse traverse(this); + this->traverse(&traverse); +} + +// Check the types in a single block. + +void +Gogo::check_types_in_block(Block* block) +{ + Check_types_traverse traverse(this); + block->traverse(&traverse); +} + +// A traversal class used to find a single shortcut operator within an +// expression. + +class Find_shortcut : public Traverse +{ + public: + Find_shortcut() + : Traverse(traverse_blocks + | traverse_statements + | traverse_expressions), + found_(NULL) + { } + + // A pointer to the expression which was found, or NULL if none was + // found. + Expression** + found() const + { return this->found_; } + + protected: + int + block(Block*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + statement(Block*, size_t*, Statement*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + expression(Expression**); + + private: + Expression** found_; +}; + +// Find a shortcut expression. + +int +Find_shortcut::expression(Expression** pexpr) +{ + Expression* expr = *pexpr; + Binary_expression* be = expr->binary_expression(); + if (be == NULL) + return TRAVERSE_CONTINUE; + Operator op = be->op(); + if (op != OPERATOR_OROR && op != OPERATOR_ANDAND) + return TRAVERSE_CONTINUE; + go_assert(this->found_ == NULL); + this->found_ = pexpr; + return TRAVERSE_EXIT; +} + +// A traversal class used to turn shortcut operators into explicit if +// statements. + +class Shortcuts : public Traverse +{ + public: + Shortcuts(Gogo* gogo) + : Traverse(traverse_variables + | traverse_statements), + gogo_(gogo) + { } + + protected: + int + variable(Named_object*); + + int + statement(Block*, size_t*, Statement*); + + private: + // Convert a shortcut operator. + Statement* + convert_shortcut(Block* enclosing, Expression** pshortcut); + + // The IR. + Gogo* gogo_; +}; + +// Remove shortcut operators in a single statement. + +int +Shortcuts::statement(Block* block, size_t* pindex, Statement* s) +{ + // FIXME: This approach doesn't work for switch statements, because + // we add the new statements before the whole switch when we need to + // instead add them just before the switch expression. The right + // fix is probably to lower switch statements with nonconstant cases + // to a series of conditionals. + if (s->switch_statement() != NULL) + return TRAVERSE_CONTINUE; + + while (true) + { + Find_shortcut find_shortcut; + + // If S is a variable declaration, then ordinary traversal won't + // do anything. We want to explicitly traverse the + // initialization expression if there is one. + Variable_declaration_statement* vds = s->variable_declaration_statement(); + Expression* init = NULL; + if (vds == NULL) + s->traverse_contents(&find_shortcut); + else + { + init = vds->var()->var_value()->init(); + if (init == NULL) + return TRAVERSE_CONTINUE; + init->traverse(&init, &find_shortcut); + } + Expression** pshortcut = find_shortcut.found(); + if (pshortcut == NULL) + return TRAVERSE_CONTINUE; + + Statement* snew = this->convert_shortcut(block, pshortcut); + block->insert_statement_before(*pindex, snew); + ++*pindex; + + if (pshortcut == &init) + vds->var()->var_value()->set_init(init); + } +} + +// Remove shortcut operators in the initializer of a global variable. + +int +Shortcuts::variable(Named_object* no) +{ + if (no->is_result_variable()) + return TRAVERSE_CONTINUE; + Variable* var = no->var_value(); + Expression* init = var->init(); + if (!var->is_global() || init == NULL) + return TRAVERSE_CONTINUE; + + while (true) + { + Find_shortcut find_shortcut; + init->traverse(&init, &find_shortcut); + Expression** pshortcut = find_shortcut.found(); + if (pshortcut == NULL) + return TRAVERSE_CONTINUE; + + Statement* snew = this->convert_shortcut(NULL, pshortcut); + var->add_preinit_statement(this->gogo_, snew); + if (pshortcut == &init) + var->set_init(init); + } +} + +// Given an expression which uses a shortcut operator, return a +// statement which implements it, and update *PSHORTCUT accordingly. + +Statement* +Shortcuts::convert_shortcut(Block* enclosing, Expression** pshortcut) +{ + Binary_expression* shortcut = (*pshortcut)->binary_expression(); + Expression* left = shortcut->left(); + Expression* right = shortcut->right(); + Location loc = shortcut->location(); + + Block* retblock = new Block(enclosing, loc); + retblock->set_end_location(loc); + + Temporary_statement* ts = Statement::make_temporary(shortcut->type(), + left, loc); + retblock->add_statement(ts); + + Block* block = new Block(retblock, loc); + block->set_end_location(loc); + Expression* tmpref = Expression::make_temporary_reference(ts, loc); + Statement* assign = Statement::make_assignment(tmpref, right, loc); + block->add_statement(assign); + + Expression* cond = Expression::make_temporary_reference(ts, loc); + if (shortcut->binary_expression()->op() == OPERATOR_OROR) + cond = Expression::make_unary(OPERATOR_NOT, cond, loc); + + Statement* if_statement = Statement::make_if_statement(cond, block, NULL, + loc); + retblock->add_statement(if_statement); + + *pshortcut = Expression::make_temporary_reference(ts, loc); + + delete shortcut; + + // Now convert any shortcut operators in LEFT and RIGHT. + Shortcuts shortcuts(this->gogo_); + retblock->traverse(&shortcuts); + + return Statement::make_block_statement(retblock, loc); +} + +// Turn shortcut operators into explicit if statements. Doing this +// considerably simplifies the order of evaluation rules. + +void +Gogo::remove_shortcuts() +{ + Shortcuts shortcuts(this); + this->traverse(&shortcuts); +} + +// A traversal class which finds all the expressions which must be +// evaluated in order within a statement or larger expression. This +// is used to implement the rules about order of evaluation. + +class Find_eval_ordering : public Traverse +{ + private: + typedef std::vector Expression_pointers; + + public: + Find_eval_ordering() + : Traverse(traverse_blocks + | traverse_statements + | traverse_expressions), + exprs_() + { } + + size_t + size() const + { return this->exprs_.size(); } + + typedef Expression_pointers::const_iterator const_iterator; + + const_iterator + begin() const + { return this->exprs_.begin(); } + + const_iterator + end() const + { return this->exprs_.end(); } + + protected: + int + block(Block*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + statement(Block*, size_t*, Statement*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + expression(Expression**); + + private: + // A list of pointers to expressions with side-effects. + Expression_pointers exprs_; +}; + +// If an expression must be evaluated in order, put it on the list. + +int +Find_eval_ordering::expression(Expression** expression_pointer) +{ + // We have to look at subexpressions before this one. + if ((*expression_pointer)->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if ((*expression_pointer)->must_eval_in_order()) + this->exprs_.push_back(expression_pointer); + return TRAVERSE_SKIP_COMPONENTS; +} + +// A traversal class for ordering evaluations. + +class Order_eval : public Traverse +{ + public: + Order_eval(Gogo* gogo) + : Traverse(traverse_variables + | traverse_statements), + gogo_(gogo) + { } + + int + variable(Named_object*); + + int + statement(Block*, size_t*, Statement*); + + private: + // The IR. + Gogo* gogo_; +}; + +// Implement the order of evaluation rules for a statement. + +int +Order_eval::statement(Block* block, size_t* pindex, Statement* s) +{ + // FIXME: This approach doesn't work for switch statements, because + // we add the new statements before the whole switch when we need to + // instead add them just before the switch expression. The right + // fix is probably to lower switch statements with nonconstant cases + // to a series of conditionals. + if (s->switch_statement() != NULL) + return TRAVERSE_CONTINUE; + + Find_eval_ordering find_eval_ordering; + + // If S is a variable declaration, then ordinary traversal won't do + // anything. We want to explicitly traverse the initialization + // expression if there is one. + Variable_declaration_statement* vds = s->variable_declaration_statement(); + Expression* init = NULL; + Expression* orig_init = NULL; + if (vds == NULL) + s->traverse_contents(&find_eval_ordering); + else + { + init = vds->var()->var_value()->init(); + if (init == NULL) + return TRAVERSE_CONTINUE; + orig_init = init; + + // It might seem that this could be + // init->traverse_subexpressions. Unfortunately that can fail + // in a case like + // var err os.Error + // newvar, err := call(arg()) + // Here newvar will have an init of call result 0 of + // call(arg()). If we only traverse subexpressions, we will + // only find arg(), and we won't bother to move anything out. + // Then we get to the assignment to err, we will traverse the + // whole statement, and this time we will find both call() and + // arg(), and so we will move them out. This will cause them to + // be put into temporary variables before the assignment to err + // but after the declaration of newvar. To avoid that problem, + // we traverse the entire expression here. + Expression::traverse(&init, &find_eval_ordering); + } + + size_t c = find_eval_ordering.size(); + if (c == 0) + return TRAVERSE_CONTINUE; + + // If there is only one expression with a side-effect, we can + // usually leave it in place. + if (c == 1) + { + switch (s->classification()) + { + case Statement::STATEMENT_ASSIGNMENT: + // For an assignment statement, we need to evaluate an + // expression on the right hand side before we evaluate any + // index expression on the left hand side, so for that case + // we always move the expression. Otherwise we mishandle + // m[0] = len(m) where m is a map. + break; + + case Statement::STATEMENT_EXPRESSION: + { + // If this is a call statement that doesn't return any + // values, it will not have been counted as a value to + // move. We need to move any subexpressions in case they + // are themselves call statements that require passing a + // closure. + Expression* expr = s->expression_statement()->expr(); + if (expr->call_expression() != NULL + && expr->call_expression()->result_count() == 0) + break; + return TRAVERSE_CONTINUE; + } + + default: + // We can leave the expression in place. + return TRAVERSE_CONTINUE; + } + } + + bool is_thunk = s->thunk_statement() != NULL; + for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin(); + p != find_eval_ordering.end(); + ++p) + { + Expression** pexpr = *p; + + // The last expression in a thunk will be the call passed to go + // or defer, which we must not evaluate early. + if (is_thunk && p + 1 == find_eval_ordering.end()) + break; + + Location loc = (*pexpr)->location(); + Statement* s; + if ((*pexpr)->call_expression() == NULL + || (*pexpr)->call_expression()->result_count() < 2) + { + Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, + loc); + s = ts; + *pexpr = Expression::make_temporary_reference(ts, loc); + } + else + { + // A call expression which returns multiple results needs to + // be handled specially. We can't create a temporary + // because there is no type to give it. Any actual uses of + // the values will be done via Call_result_expressions. + s = Statement::make_statement(*pexpr, true); + } + + block->insert_statement_before(*pindex, s); + ++*pindex; + } + + if (init != orig_init) + vds->var()->var_value()->set_init(init); + + return TRAVERSE_CONTINUE; +} + +// Implement the order of evaluation rules for the initializer of a +// global variable. + +int +Order_eval::variable(Named_object* no) +{ + if (no->is_result_variable()) + return TRAVERSE_CONTINUE; + Variable* var = no->var_value(); + Expression* init = var->init(); + if (!var->is_global() || init == NULL) + return TRAVERSE_CONTINUE; + + Find_eval_ordering find_eval_ordering; + Expression::traverse(&init, &find_eval_ordering); + + if (find_eval_ordering.size() <= 1) + { + // If there is only one expression with a side-effect, we can + // leave it in place. + return TRAVERSE_SKIP_COMPONENTS; + } + + Expression* orig_init = init; + + for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin(); + p != find_eval_ordering.end(); + ++p) + { + Expression** pexpr = *p; + Location loc = (*pexpr)->location(); + Statement* s; + if ((*pexpr)->call_expression() == NULL + || (*pexpr)->call_expression()->result_count() < 2) + { + Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, + loc); + s = ts; + *pexpr = Expression::make_temporary_reference(ts, loc); + } + else + { + // A call expression which returns multiple results needs to + // be handled specially. + s = Statement::make_statement(*pexpr, true); + } + var->add_preinit_statement(this->gogo_, s); + } + + if (init != orig_init) + var->set_init(init); + + return TRAVERSE_SKIP_COMPONENTS; +} + +// Use temporary variables to implement the order of evaluation rules. + +void +Gogo::order_evaluations() +{ + Order_eval order_eval(this); + this->traverse(&order_eval); +} + +// Traversal to flatten parse tree after order of evaluation rules are applied. + +class Flatten : public Traverse +{ + public: + Flatten(Gogo* gogo, Named_object* function) + : Traverse(traverse_variables + | traverse_functions + | traverse_statements + | traverse_expressions), + gogo_(gogo), function_(function), inserter_() + { } + + void + set_inserter(const Statement_inserter* inserter) + { this->inserter_ = *inserter; } + + int + variable(Named_object*); + + int + function(Named_object*); + + int + statement(Block*, size_t* pindex, Statement*); + + int + expression(Expression**); + + private: + // General IR. + Gogo* gogo_; + // The function we are traversing. + Named_object* function_; + // Current statement inserter for use by expressions. + Statement_inserter inserter_; +}; + +// Flatten variables. + +int +Flatten::variable(Named_object* no) +{ + if (!no->is_variable()) + return TRAVERSE_CONTINUE; + + if (no->is_variable() && no->var_value()->is_global()) + { + // Global variables can have loops in their initialization + // expressions. This is handled in flatten_init_expression. + no->var_value()->flatten_init_expression(this->gogo_, this->function_, + &this->inserter_); + return TRAVERSE_CONTINUE; + } + + go_assert(!no->var_value()->has_pre_init()); + + return TRAVERSE_SKIP_COMPONENTS; +} + +// Flatten the body of a function. Record the function while flattening it, +// so that we can pass it down when flattening an expression. + +int +Flatten::function(Named_object* no) +{ + go_assert(this->function_ == NULL); + this->function_ = no; + int t = no->func_value()->traverse(this); + this->function_ = NULL; + + if (t == TRAVERSE_EXIT) + return t; + return TRAVERSE_SKIP_COMPONENTS; +} + +// Flatten statement parse trees. + +int +Flatten::statement(Block* block, size_t* pindex, Statement* sorig) +{ + // Because we explicitly traverse the statement's contents + // ourselves, we want to skip block statements here. There is + // nothing to flatten in a block statement. + if (sorig->is_block_statement()) + return TRAVERSE_CONTINUE; + + Statement_inserter hold_inserter(this->inserter_); + this->inserter_ = Statement_inserter(block, pindex); + + // Flatten the expressions first. + int t = sorig->traverse_contents(this); + if (t == TRAVERSE_EXIT) + { + this->inserter_ = hold_inserter; + return t; + } + + // Keep flattening until nothing changes. + Statement* s = sorig; + while (true) + { + Statement* snew = s->flatten(this->gogo_, this->function_, block, + &this->inserter_); + if (snew == s) + break; + s = snew; + t = s->traverse_contents(this); + if (t == TRAVERSE_EXIT) + { + this->inserter_ = hold_inserter; + return t; + } + } + + if (s != sorig) + block->replace_statement(*pindex, s); + + this->inserter_ = hold_inserter; + return TRAVERSE_SKIP_COMPONENTS; +} + +// Flatten expression parse trees. + +int +Flatten::expression(Expression** pexpr) +{ + // Keep flattening until nothing changes. + while (true) + { + Expression* e = *pexpr; + if (e->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + + Expression* enew = e->flatten(this->gogo_, this->function_, + &this->inserter_); + if (enew == e) + break; + *pexpr = enew; + } + return TRAVERSE_SKIP_COMPONENTS; +} + +// Flatten a block. + +void +Gogo::flatten_block(Named_object* function, Block* block) +{ + Flatten flatten(this, function); + block->traverse(&flatten); +} + +// Flatten an expression. INSERTER may be NULL, in which case the +// expression had better not need to create any temporaries. + +void +Gogo::flatten_expression(Named_object* function, Statement_inserter* inserter, + Expression** pexpr) +{ + Flatten flatten(this, function); + if (inserter != NULL) + flatten.set_inserter(inserter); + flatten.expression(pexpr); +} + +void +Gogo::flatten() +{ + Flatten flatten(this, NULL); + this->traverse(&flatten); +} + +// Traversal to convert calls to the predeclared recover function to +// pass in an argument indicating whether it can recover from a panic +// or not. + +class Convert_recover : public Traverse +{ + public: + Convert_recover(Named_object* arg) + : Traverse(traverse_expressions), + arg_(arg) + { } + + protected: + int + expression(Expression**); + + private: + // The argument to pass to the function. + Named_object* arg_; +}; + +// Convert calls to recover. + +int +Convert_recover::expression(Expression** pp) +{ + Call_expression* ce = (*pp)->call_expression(); + if (ce != NULL && ce->is_recover_call()) + ce->set_recover_arg(Expression::make_var_reference(this->arg_, + ce->location())); + return TRAVERSE_CONTINUE; +} + +// Traversal for build_recover_thunks. + +class Build_recover_thunks : public Traverse +{ + public: + Build_recover_thunks(Gogo* gogo) + : Traverse(traverse_functions), + gogo_(gogo) + { } + + int + function(Named_object*); + + private: + Expression* + can_recover_arg(Location); + + // General IR. + Gogo* gogo_; +}; + +// If this function calls recover, turn it into a thunk. + +int +Build_recover_thunks::function(Named_object* orig_no) +{ + Function* orig_func = orig_no->func_value(); + if (!orig_func->calls_recover() + || orig_func->is_recover_thunk() + || orig_func->has_recover_thunk()) + return TRAVERSE_CONTINUE; + + Gogo* gogo = this->gogo_; + Location location = orig_func->location(); + + static int count; + char buf[50]; + + Function_type* orig_fntype = orig_func->type(); + Typed_identifier_list* new_params = new Typed_identifier_list(); + std::string receiver_name; + if (orig_fntype->is_method()) + { + const Typed_identifier* receiver = orig_fntype->receiver(); + snprintf(buf, sizeof buf, "rt.%u", count); + ++count; + receiver_name = buf; + new_params->push_back(Typed_identifier(receiver_name, receiver->type(), + receiver->location())); + } + const Typed_identifier_list* orig_params = orig_fntype->parameters(); + if (orig_params != NULL && !orig_params->empty()) + { + for (Typed_identifier_list::const_iterator p = orig_params->begin(); + p != orig_params->end(); + ++p) + { + snprintf(buf, sizeof buf, "pt.%u", count); + ++count; + new_params->push_back(Typed_identifier(buf, p->type(), + p->location())); + } + } + snprintf(buf, sizeof buf, "pr.%u", count); + ++count; + std::string can_recover_name = buf; + new_params->push_back(Typed_identifier(can_recover_name, + Type::lookup_bool_type(), + orig_fntype->location())); + + const Typed_identifier_list* orig_results = orig_fntype->results(); + Typed_identifier_list* new_results; + if (orig_results == NULL || orig_results->empty()) + new_results = NULL; + else + { + new_results = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator p = orig_results->begin(); + p != orig_results->end(); + ++p) + new_results->push_back(Typed_identifier("", p->type(), p->location())); + } + + Function_type *new_fntype = Type::make_function_type(NULL, new_params, + new_results, + orig_fntype->location()); + if (orig_fntype->is_varargs()) + new_fntype->set_is_varargs(); + + std::string name = orig_no->name(); + if (orig_fntype->is_method()) + name += "$" + orig_fntype->receiver()->type()->mangled_name(gogo); + name += "$recover"; + Named_object *new_no = gogo->start_function(name, new_fntype, false, + location); + Function *new_func = new_no->func_value(); + if (orig_func->enclosing() != NULL) + new_func->set_enclosing(orig_func->enclosing()); + + // We build the code for the original function attached to the new + // function, and then swap the original and new function bodies. + // This means that existing references to the original function will + // then refer to the new function. That makes this code a little + // confusing, in that the reference to NEW_NO really refers to the + // other function, not the one we are building. + + Expression* closure = NULL; + if (orig_func->needs_closure()) + { + // For the new function we are creating, declare a new parameter + // variable NEW_CLOSURE_NO and set it to be the closure variable + // of the function. This will be set to the closure value + // passed in by the caller. Then pass a reference to this + // variable as the closure value when calling the original + // function. In other words, simply pass the closure value + // through the thunk we are creating. + Named_object* orig_closure_no = orig_func->closure_var(); + Variable* orig_closure_var = orig_closure_no->var_value(); + Variable* new_var = new Variable(orig_closure_var->type(), NULL, false, + false, false, location); + snprintf(buf, sizeof buf, "closure.%u", count); + ++count; + Named_object* new_closure_no = Named_object::make_variable(buf, NULL, + new_var); + new_func->set_closure_var(new_closure_no); + closure = Expression::make_var_reference(new_closure_no, location); + } + + Expression* fn = Expression::make_func_reference(new_no, closure, location); + + Expression_list* args = new Expression_list(); + if (new_params != NULL) + { + // Note that we skip the last parameter, which is the boolean + // indicating whether recover can succed. + for (Typed_identifier_list::const_iterator p = new_params->begin(); + p + 1 != new_params->end(); + ++p) + { + Named_object* p_no = gogo->lookup(p->name(), NULL); + go_assert(p_no != NULL + && p_no->is_variable() + && p_no->var_value()->is_parameter()); + args->push_back(Expression::make_var_reference(p_no, location)); + } + } + args->push_back(this->can_recover_arg(location)); + + gogo->start_block(location); + + Call_expression* call = Expression::make_call(fn, args, false, location); + + // Any varargs call has already been lowered. + call->set_varargs_are_lowered(); + + Statement* s = Statement::make_return_from_call(call, location); + s->determine_types(); + gogo->add_statement(s); + + Block* b = gogo->finish_block(location); + + gogo->add_block(b, location); + + // Lower the call in case it returns multiple results. + gogo->lower_block(new_no, b); + + gogo->finish_function(location); + + // Swap the function bodies and types. + new_func->swap_for_recover(orig_func); + orig_func->set_is_recover_thunk(); + new_func->set_calls_recover(); + new_func->set_has_recover_thunk(); + + Bindings* orig_bindings = orig_func->block()->bindings(); + Bindings* new_bindings = new_func->block()->bindings(); + if (orig_fntype->is_method()) + { + // We changed the receiver to be a regular parameter. We have + // to update the binding accordingly in both functions. + Named_object* orig_rec_no = orig_bindings->lookup_local(receiver_name); + go_assert(orig_rec_no != NULL + && orig_rec_no->is_variable() + && !orig_rec_no->var_value()->is_receiver()); + orig_rec_no->var_value()->set_is_receiver(); + + std::string new_receiver_name(orig_fntype->receiver()->name()); + if (new_receiver_name.empty()) + { + // Find the receiver. It was named "r.NNN" in + // Gogo::start_function. + for (Bindings::const_definitions_iterator p = + new_bindings->begin_definitions(); + p != new_bindings->end_definitions(); + ++p) + { + const std::string& pname((*p)->name()); + if (pname[0] == 'r' && pname[1] == '.') + { + new_receiver_name = pname; + break; + } + } + go_assert(!new_receiver_name.empty()); + } + Named_object* new_rec_no = new_bindings->lookup_local(new_receiver_name); + if (new_rec_no == NULL) + go_assert(saw_errors()); + else + { + go_assert(new_rec_no->is_variable() + && new_rec_no->var_value()->is_receiver()); + new_rec_no->var_value()->set_is_not_receiver(); + } + } + + // Because we flipped blocks but not types, the can_recover + // parameter appears in the (now) old bindings as a parameter. + // Change it to a local variable, whereupon it will be discarded. + Named_object* can_recover_no = orig_bindings->lookup_local(can_recover_name); + go_assert(can_recover_no != NULL + && can_recover_no->is_variable() + && can_recover_no->var_value()->is_parameter()); + orig_bindings->remove_binding(can_recover_no); + + // Add the can_recover argument to the (now) new bindings, and + // attach it to any recover statements. + Variable* can_recover_var = new Variable(Type::lookup_bool_type(), NULL, + false, true, false, location); + can_recover_no = new_bindings->add_variable(can_recover_name, NULL, + can_recover_var); + Convert_recover convert_recover(can_recover_no); + new_func->traverse(&convert_recover); + + // Update the function pointers in any named results. + new_func->update_result_variables(); + orig_func->update_result_variables(); + + return TRAVERSE_CONTINUE; +} + +// Return the expression to pass for the .can_recover parameter to the +// new function. This indicates whether a call to recover may return +// non-nil. The expression is +// __go_can_recover(__builtin_return_address()). + +Expression* +Build_recover_thunks::can_recover_arg(Location location) +{ + static Named_object* builtin_return_address; + if (builtin_return_address == NULL) + { + const Location bloc = Linemap::predeclared_location(); + + Typed_identifier_list* param_types = new Typed_identifier_list(); + Type* uint_type = Type::lookup_integer_type("uint"); + param_types->push_back(Typed_identifier("l", uint_type, bloc)); + + Typed_identifier_list* return_types = new Typed_identifier_list(); + Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); + return_types->push_back(Typed_identifier("", voidptr_type, bloc)); + + Function_type* fntype = Type::make_function_type(NULL, param_types, + return_types, bloc); + builtin_return_address = + Named_object::make_function_declaration("__builtin_return_address", + NULL, fntype, bloc); + const char* n = "__builtin_return_address"; + builtin_return_address->func_declaration_value()->set_asm_name(n); + } + + static Named_object* can_recover; + if (can_recover == NULL) + { + const Location bloc = Linemap::predeclared_location(); + Typed_identifier_list* param_types = new Typed_identifier_list(); + Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); + param_types->push_back(Typed_identifier("a", voidptr_type, bloc)); + Type* boolean_type = Type::lookup_bool_type(); + Typed_identifier_list* results = new Typed_identifier_list(); + results->push_back(Typed_identifier("", boolean_type, bloc)); + Function_type* fntype = Type::make_function_type(NULL, param_types, + results, bloc); + can_recover = Named_object::make_function_declaration("__go_can_recover", + NULL, fntype, + bloc); + can_recover->func_declaration_value()->set_asm_name("__go_can_recover"); + } + + Expression* fn = Expression::make_func_reference(builtin_return_address, + NULL, location); + + mpz_t zval; + mpz_init_set_ui(zval, 0UL); + Expression* zexpr = Expression::make_integer(&zval, NULL, location); + mpz_clear(zval); + Expression_list *args = new Expression_list(); + args->push_back(zexpr); + + Expression* call = Expression::make_call(fn, args, false, location); + + args = new Expression_list(); + args->push_back(call); + + fn = Expression::make_func_reference(can_recover, NULL, location); + return Expression::make_call(fn, args, false, location); +} + +// Build thunks for functions which call recover. We build a new +// function with an extra parameter, which is whether a call to +// recover can succeed. We then move the body of this function to +// that one. We then turn this function into a thunk which calls the +// new one, passing the value of +// __go_can_recover(__builtin_return_address()). The function will be +// marked as not splitting the stack. This will cooperate with the +// implementation of defer to make recover do the right thing. + +void +Gogo::build_recover_thunks() +{ + Build_recover_thunks build_recover_thunks(this); + this->traverse(&build_recover_thunks); +} + +// Build a call to the runtime error function. + +Expression* +Gogo::runtime_error(int code, Location location) +{ + Type* int32_type = Type::lookup_integer_type("int32"); + mpz_t val; + mpz_init_set_ui(val, code); + Expression* code_expr = Expression::make_integer(&val, int32_type, location); + mpz_clear(val); + return Runtime::make_call(Runtime::RUNTIME_ERROR, location, 1, code_expr); +} + +// Look for named types to see whether we need to create an interface +// method table. + +class Build_method_tables : public Traverse +{ + public: + Build_method_tables(Gogo* gogo, + const std::vector& interfaces) + : Traverse(traverse_types), + gogo_(gogo), interfaces_(interfaces) + { } + + int + type(Type*); + + private: + // The IR. + Gogo* gogo_; + // A list of locally defined interfaces which have hidden methods. + const std::vector& interfaces_; +}; + +// Build all required interface method tables for types. We need to +// ensure that we have an interface method table for every interface +// which has a hidden method, for every named type which implements +// that interface. Normally we can just build interface method tables +// as we need them. However, in some cases we can require an +// interface method table for an interface defined in a different +// package for a type defined in that package. If that interface and +// type both use a hidden method, that is OK. However, we will not be +// able to build that interface method table when we need it, because +// the type's hidden method will be static. So we have to build it +// here, and just refer it from other packages as needed. + +void +Gogo::build_interface_method_tables() +{ + if (saw_errors()) + return; + + std::vector hidden_interfaces; + hidden_interfaces.reserve(this->interface_types_.size()); + for (std::vector::const_iterator pi = + this->interface_types_.begin(); + pi != this->interface_types_.end(); + ++pi) + { + const Typed_identifier_list* methods = (*pi)->methods(); + if (methods == NULL) + continue; + for (Typed_identifier_list::const_iterator pm = methods->begin(); + pm != methods->end(); + ++pm) + { + if (Gogo::is_hidden_name(pm->name())) + { + hidden_interfaces.push_back(*pi); + break; + } + } + } + + if (!hidden_interfaces.empty()) + { + // Now traverse the tree looking for all named types. + Build_method_tables bmt(this, hidden_interfaces); + this->traverse(&bmt); + } + + // We no longer need the list of interfaces. + + this->interface_types_.clear(); +} + +// This is called for each type. For a named type, for each of the +// interfaces with hidden methods that it implements, create the +// method table. + +int +Build_method_tables::type(Type* type) +{ + Named_type* nt = type->named_type(); + Struct_type* st = type->struct_type(); + if (nt != NULL || st != NULL) + { + for (std::vector::const_iterator p = + this->interfaces_.begin(); + p != this->interfaces_.end(); + ++p) + { + // We ask whether a pointer to the named type implements the + // interface, because a pointer can implement more methods + // than a value. + 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 + { + 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); + } + } + } + } + return TRAVERSE_CONTINUE; +} + +// Traversal class used to check for return statements. + +class Check_return_statements_traverse : public Traverse +{ + public: + Check_return_statements_traverse() + : Traverse(traverse_functions) + { } + + int + function(Named_object*); +}; + +// Check that a function has a return statement if it needs one. + +int +Check_return_statements_traverse::function(Named_object* no) +{ + Function* func = no->func_value(); + const Function_type* fntype = func->type(); + const Typed_identifier_list* results = fntype->results(); + + // We only need a return statement if there is a return value. + if (results == NULL || results->empty()) + return TRAVERSE_CONTINUE; + + if (func->block()->may_fall_through()) + error_at(func->block()->end_location(), + "missing return at end of function"); + + return TRAVERSE_CONTINUE; +} + +// Check return statements. + +void +Gogo::check_return_statements() +{ + Check_return_statements_traverse traverse; + this->traverse(&traverse); +} + +// Work out the package priority. It is one more than the maximum +// priority of an imported package. + +int +Gogo::package_priority() const +{ + int priority = 0; + for (Packages::const_iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + if (p->second->priority() > priority) + priority = p->second->priority(); + return priority + 1; +} + +// Export identifiers as requested. + +void +Gogo::do_exports() +{ + // For now we always stream to a section. Later we may want to + // support streaming to a separate file. + Stream_to_section stream; + + Export exp(&stream); + exp.register_builtin_types(this); + exp.export_globals(this->package_name(), + this->pkgpath(), + this->package_priority(), + this->imports_, + (this->need_init_fn_ && !this->is_main_package() + ? this->get_init_fn_name() + : ""), + this->imported_init_fns_, + this->package_->bindings()); +} + +// Find the blocks in order to convert named types defined in blocks. + +class Convert_named_types : public Traverse +{ + public: + Convert_named_types(Gogo* gogo) + : Traverse(traverse_blocks), + gogo_(gogo) + { } + + protected: + int + block(Block* block); + + private: + Gogo* gogo_; +}; + +int +Convert_named_types::block(Block* block) +{ + this->gogo_->convert_named_types_in_bindings(block->bindings()); + return TRAVERSE_CONTINUE; +} + +// Convert all named types to the backend representation. Since named +// types can refer to other types, this needs to be done in the right +// sequence, which is handled by Named_type::convert. Here we arrange +// to call that for each named type. + +void +Gogo::convert_named_types() +{ + this->convert_named_types_in_bindings(this->globals_); + for (Packages::iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + { + Package* package = p->second; + this->convert_named_types_in_bindings(package->bindings()); + } + + Convert_named_types cnt(this); + this->traverse(&cnt); + + // Make all the builtin named types used for type descriptors, and + // then convert them. They will only be written out if they are + // needed. + Type::make_type_descriptor_type(); + Type::make_type_descriptor_ptr_type(); + Function_type::make_function_type_descriptor_type(); + Pointer_type::make_pointer_type_descriptor_type(); + Struct_type::make_struct_type_descriptor_type(); + Array_type::make_array_type_descriptor_type(); + Array_type::make_slice_type_descriptor_type(); + Map_type::make_map_type_descriptor_type(); + Map_type::make_map_descriptor_type(); + Channel_type::make_chan_type_descriptor_type(); + Interface_type::make_interface_type_descriptor_type(); + Expression::make_func_descriptor_type(); + Type::convert_builtin_named_types(this); + + Runtime::convert_types(this); + + this->named_types_are_converted_ = true; +} + +// Convert all names types in a set of bindings. + +void +Gogo::convert_named_types_in_bindings(Bindings* bindings) +{ + for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); + p != bindings->end_definitions(); + ++p) + { + if ((*p)->is_type()) + (*p)->type_value()->convert(this); + } +} + +// Class Function. + +Function::Function(Function_type* type, Function* enclosing, Block* block, + Location location) + : type_(type), enclosing_(enclosing), results_(NULL), + closure_var_(NULL), block_(block), location_(location), labels_(), + local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL), + is_sink_(false), results_are_named_(false), nointerface_(false), + is_unnamed_type_stub_method_(false), calls_recover_(false), + is_recover_thunk_(false), has_recover_thunk_(false), + in_unique_section_(false) +{ +} + +// Create the named result variables. + +void +Function::create_result_variables(Gogo* gogo) +{ + const Typed_identifier_list* results = this->type_->results(); + if (results == NULL || results->empty()) + return; + + if (!results->front().name().empty()) + this->results_are_named_ = true; + + this->results_ = new Results(); + this->results_->reserve(results->size()); + + Block* block = this->block_; + int index = 0; + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p, ++index) + { + std::string name = p->name(); + if (name.empty() || Gogo::is_sink_name(name)) + { + static int result_counter; + char buf[100]; + snprintf(buf, sizeof buf, "$ret%d", result_counter); + ++result_counter; + name = gogo->pack_hidden_name(buf, false); + } + Result_variable* result = new Result_variable(p->type(), this, index, + p->location()); + Named_object* no = block->bindings()->add_result_variable(name, result); + if (no->is_result_variable()) + this->results_->push_back(no); + else + { + static int dummy_result_count; + char buf[100]; + snprintf(buf, sizeof buf, "$dret%d", dummy_result_count); + ++dummy_result_count; + name = gogo->pack_hidden_name(buf, false); + no = block->bindings()->add_result_variable(name, result); + go_assert(no->is_result_variable()); + this->results_->push_back(no); + } + } +} + +// Update the named result variables when cloning a function which +// calls recover. + +void +Function::update_result_variables() +{ + if (this->results_ == NULL) + return; + + for (Results::iterator p = this->results_->begin(); + p != this->results_->end(); + ++p) + (*p)->result_var_value()->set_function(this); +} + +// Return the closure variable, creating it if necessary. + +Named_object* +Function::closure_var() +{ + if (this->closure_var_ == NULL) + { + go_assert(this->descriptor_ == NULL); + // We don't know the type of the variable yet. We add fields as + // we find them. + Location loc = this->type_->location(); + Struct_field_list* sfl = new Struct_field_list; + Type* struct_type = Type::make_struct_type(sfl, loc); + Variable* var = new Variable(Type::make_pointer_type(struct_type), + NULL, false, false, false, loc); + var->set_is_used(); + this->closure_var_ = Named_object::make_variable("$closure", NULL, var); + // Note that the new variable is not in any binding contour. + } + return this->closure_var_; +} + +// Set the type of the closure variable. + +void +Function::set_closure_type() +{ + if (this->closure_var_ == NULL) + return; + Named_object* closure = this->closure_var_; + Struct_type* st = closure->var_value()->type()->deref()->struct_type(); + + // The first field of a closure is always a pointer to the function + // code. + Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); + st->push_field(Struct_field(Typed_identifier(".$f", voidptr_type, + this->location_))); + + unsigned int index = 1; + for (Closure_fields::const_iterator p = this->closure_fields_.begin(); + p != this->closure_fields_.end(); + ++p, ++index) + { + Named_object* no = p->first; + char buf[20]; + snprintf(buf, sizeof buf, "%u", index); + std::string n = no->name() + buf; + Type* var_type; + if (no->is_variable()) + var_type = no->var_value()->type(); + else + var_type = no->result_var_value()->type(); + Type* field_type = Type::make_pointer_type(var_type); + st->push_field(Struct_field(Typed_identifier(n, field_type, p->second))); + } +} + +// Return whether this function is a method. + +bool +Function::is_method() const +{ + return this->type_->is_method(); +} + +// Add a label definition. + +Label* +Function::add_label_definition(Gogo* gogo, const std::string& label_name, + Location location) +{ + Label* lnull = NULL; + std::pair ins = + this->labels_.insert(std::make_pair(label_name, lnull)); + Label* label; + if (ins.second) + { + // This is a new label. + label = new Label(label_name); + ins.first->second = label; + } + else + { + // The label was already in the hash table. + label = ins.first->second; + if (label->is_defined()) + { + error_at(location, "label %qs already defined", + Gogo::message_name(label_name).c_str()); + inform(label->location(), "previous definition of %qs was here", + Gogo::message_name(label_name).c_str()); + return new Label(label_name); + } + } + + label->define(location, gogo->bindings_snapshot(location)); + + // Issue any errors appropriate for any previous goto's to this + // label. + const std::vector& refs(label->refs()); + for (std::vector::const_iterator p = refs.begin(); + p != refs.end(); + ++p) + (*p)->check_goto_to(gogo->current_block()); + label->clear_refs(); + + return label; +} + +// Add a reference to a label. + +Label* +Function::add_label_reference(Gogo* gogo, const std::string& label_name, + Location location, bool issue_goto_errors) +{ + Label* lnull = NULL; + std::pair ins = + this->labels_.insert(std::make_pair(label_name, lnull)); + Label* label; + if (!ins.second) + { + // The label was already in the hash table. + label = ins.first->second; + } + else + { + go_assert(ins.first->second == NULL); + label = new Label(label_name); + ins.first->second = label; + } + + label->set_is_used(); + + if (issue_goto_errors) + { + Bindings_snapshot* snapshot = label->snapshot(); + if (snapshot != NULL) + snapshot->check_goto_from(gogo->current_block(), location); + else + label->add_snapshot_ref(gogo->bindings_snapshot(location)); + } + + return label; +} + +// Warn about labels that are defined but not used. + +void +Function::check_labels() const +{ + for (Labels::const_iterator p = this->labels_.begin(); + p != this->labels_.end(); + p++) + { + Label* label = p->second; + if (!label->is_used()) + error_at(label->location(), "label %qs defined and not used", + Gogo::message_name(label->name()).c_str()); + } +} + +// Swap one function with another. This is used when building the +// thunk we use to call a function which calls recover. It may not +// work for any other case. + +void +Function::swap_for_recover(Function *x) +{ + go_assert(this->enclosing_ == x->enclosing_); + std::swap(this->results_, x->results_); + std::swap(this->closure_var_, x->closure_var_); + std::swap(this->block_, x->block_); + go_assert(this->location_ == x->location_); + go_assert(this->fndecl_ == NULL && x->fndecl_ == NULL); + go_assert(this->defer_stack_ == NULL && x->defer_stack_ == NULL); +} + +// Traverse the tree. + +int +Function::traverse(Traverse* traverse) +{ + unsigned int traverse_mask = traverse->traverse_mask(); + + if ((traverse_mask + & (Traverse::traverse_types | Traverse::traverse_expressions)) + != 0) + { + if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + // FIXME: We should check traverse_functions here if nested + // functions are stored in block bindings. + if (this->block_ != NULL + && (traverse_mask + & (Traverse::traverse_variables + | Traverse::traverse_constants + | Traverse::traverse_blocks + | Traverse::traverse_statements + | Traverse::traverse_expressions + | Traverse::traverse_types)) != 0) + { + if (this->block_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + return TRAVERSE_CONTINUE; +} + +// Work out types for unspecified variables and constants. + +void +Function::determine_types() +{ + if (this->block_ != NULL) + this->block_->determine_types(); +} + +// Return the function descriptor, the value you get when you refer to +// the function in Go code without calling it. + +Expression* +Function::descriptor(Gogo*, Named_object* no) +{ + go_assert(!this->is_method()); + go_assert(this->closure_var_ == NULL); + if (this->descriptor_ == NULL) + this->descriptor_ = Expression::make_func_descriptor(no); + return this->descriptor_; +} + +// Get a pointer to the variable representing the defer stack for this +// function, making it if necessary. The value of the variable is set +// by the runtime routines to true if the function is returning, +// rather than panicing through. A pointer to this variable is used +// as a marker for the functions on the defer stack associated with +// this function. A function-specific variable permits inlining a +// function which uses defer. + +Expression* +Function::defer_stack(Location location) +{ + if (this->defer_stack_ == NULL) + { + Type* t = Type::lookup_bool_type(); + Expression* n = Expression::make_boolean(false, location); + this->defer_stack_ = Statement::make_temporary(t, n, location); + this->defer_stack_->set_is_address_taken(); + } + Expression* ref = Expression::make_temporary_reference(this->defer_stack_, + location); + return Expression::make_unary(OPERATOR_AND, ref, location); +} + +// Export the function. + +void +Function::export_func(Export* exp, const std::string& name) const +{ + Function::export_func_with_type(exp, name, this->type_); +} + +// Export a function with a type. + +void +Function::export_func_with_type(Export* exp, const std::string& name, + const Function_type* fntype) +{ + exp->write_c_string("func "); + + if (fntype->is_method()) + { + exp->write_c_string("("); + const Typed_identifier* receiver = fntype->receiver(); + exp->write_name(receiver->name()); + exp->write_c_string(" "); + exp->write_type(receiver->type()); + exp->write_c_string(") "); + } + + exp->write_string(name); + + exp->write_c_string(" ("); + const Typed_identifier_list* parameters = fntype->parameters(); + if (parameters != NULL) + { + bool is_varargs = fntype->is_varargs(); + bool first = true; + for (Typed_identifier_list::const_iterator p = parameters->begin(); + p != parameters->end(); + ++p) + { + if (first) + first = false; + else + exp->write_c_string(", "); + exp->write_name(p->name()); + exp->write_c_string(" "); + if (!is_varargs || p + 1 != parameters->end()) + exp->write_type(p->type()); + else + { + exp->write_c_string("..."); + exp->write_type(p->type()->array_type()->element_type()); + } + } + } + exp->write_c_string(")"); + + const Typed_identifier_list* results = fntype->results(); + if (results != NULL) + { + if (results->size() == 1 && results->begin()->name().empty()) + { + exp->write_c_string(" "); + exp->write_type(results->begin()->type()); + } + else + { + exp->write_c_string(" ("); + bool first = true; + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p) + { + if (first) + first = false; + else + exp->write_c_string(", "); + exp->write_name(p->name()); + exp->write_c_string(" "); + exp->write_type(p->type()); + } + exp->write_c_string(")"); + } + } + exp->write_c_string(";\n"); +} + +// Import a function. + +void +Function::import_func(Import* imp, std::string* pname, + Typed_identifier** preceiver, + Typed_identifier_list** pparameters, + Typed_identifier_list** presults, + bool* is_varargs) +{ + imp->require_c_string("func "); + + *preceiver = NULL; + if (imp->peek_char() == '(') + { + imp->require_c_string("("); + std::string name = imp->read_name(); + imp->require_c_string(" "); + Type* rtype = imp->read_type(); + *preceiver = new Typed_identifier(name, rtype, imp->location()); + imp->require_c_string(") "); + } + + *pname = imp->read_identifier(); + + Typed_identifier_list* parameters; + *is_varargs = false; + imp->require_c_string(" ("); + if (imp->peek_char() == ')') + parameters = NULL; + else + { + parameters = new Typed_identifier_list(); + while (true) + { + std::string name = imp->read_name(); + imp->require_c_string(" "); + + if (imp->match_c_string("...")) + { + imp->advance(3); + *is_varargs = true; + } + + Type* ptype = imp->read_type(); + if (*is_varargs) + ptype = Type::make_array_type(ptype, NULL); + parameters->push_back(Typed_identifier(name, ptype, + imp->location())); + if (imp->peek_char() != ',') + break; + go_assert(!*is_varargs); + imp->require_c_string(", "); + } + } + imp->require_c_string(")"); + *pparameters = parameters; + + Typed_identifier_list* results; + if (imp->peek_char() != ' ') + results = NULL; + else + { + results = new Typed_identifier_list(); + imp->require_c_string(" "); + if (imp->peek_char() != '(') + { + Type* rtype = imp->read_type(); + results->push_back(Typed_identifier("", rtype, imp->location())); + } + else + { + imp->require_c_string("("); + while (true) + { + std::string name = imp->read_name(); + imp->require_c_string(" "); + Type* rtype = imp->read_type(); + results->push_back(Typed_identifier(name, rtype, + imp->location())); + if (imp->peek_char() != ',') + break; + imp->require_c_string(", "); + } + imp->require_c_string(")"); + } + } + imp->require_c_string(";\n"); + *presults = results; +} + +// Get the backend representation. + +Bfunction* +Function::get_or_make_decl(Gogo* gogo, Named_object* no) +{ + if (this->fndecl_ == NULL) + { + std::string asm_name; + bool is_visible = false; + if (no->package() != NULL) + ; + else if (this->enclosing_ != NULL || Gogo::is_thunk(no)) + ; + else if (Gogo::unpack_hidden_name(no->name()) == "init" + && !this->type_->is_method()) + ; + else if (Gogo::unpack_hidden_name(no->name()) == "main" + && gogo->is_main_package()) + is_visible = true; + // Methods have to be public even if they are hidden because + // they can be pulled into type descriptors when using + // anonymous fields. + else if (!Gogo::is_hidden_name(no->name()) + || this->type_->is_method()) + { + if (!this->is_unnamed_type_stub_method_) + is_visible = true; + std::string pkgpath = gogo->pkgpath_symbol(); + if (this->type_->is_method() + && Gogo::is_hidden_name(no->name()) + && Gogo::hidden_name_pkgpath(no->name()) != gogo->pkgpath()) + { + // This is a method we created for an unexported + // method of an imported embedded type. We need to + // use the pkgpath of the imported package to avoid + // a possible name collision. See bug478 for a test + // case. + pkgpath = Gogo::hidden_name_pkgpath(no->name()); + pkgpath = Gogo::pkgpath_for_symbol(pkgpath); + } + + asm_name = pkgpath; + asm_name.append(1, '.'); + asm_name.append(Gogo::unpack_hidden_name(no->name())); + if (this->type_->is_method()) + { + asm_name.append(1, '.'); + Type* rtype = this->type_->receiver()->type(); + asm_name.append(rtype->mangled_name(gogo)); + } + } + + // If a function calls the predeclared recover function, we + // can't inline it, because recover behaves differently in a + // function passed directly to defer. If this is a recover + // thunk that we built to test whether a function can be + // recovered, we can't inline it, because that will mess up + // our return address comparison. + bool is_inlinable = !(this->calls_recover_ || this->is_recover_thunk_); + + // If this is a thunk created to call a function which calls + // the predeclared recover function, we need to disable + // stack splitting for the thunk. + bool disable_split_stack = this->is_recover_thunk_; + + // This should go into a unique section if that has been + // requested elsewhere, or if this is a nointerface function. + // We want to put a nointerface function into a unique section + // because there is a good chance that the linker garbage + // collection can discard it. + bool in_unique_section = this->in_unique_section_ || this->nointerface_; + + Btype* functype = this->type_->get_backend_fntype(gogo); + this->fndecl_ = + gogo->backend()->function(functype, no->get_id(gogo), asm_name, + is_visible, false, is_inlinable, + disable_split_stack, in_unique_section, + this->location()); + } + return this->fndecl_; +} + +// Class Block. + +Block::Block(Block* enclosing, Location location) + : enclosing_(enclosing), statements_(), + bindings_(new Bindings(enclosing == NULL + ? NULL + : enclosing->bindings())), + start_location_(location), + end_location_(UNKNOWN_LOCATION) +{ +} + +// Add a statement to a block. + +void +Block::add_statement(Statement* statement) +{ + this->statements_.push_back(statement); +} + +// Add a statement to the front of a block. This is slow but is only +// used for reference counts of parameters. + +void +Block::add_statement_at_front(Statement* statement) +{ + this->statements_.insert(this->statements_.begin(), statement); +} + +// Replace a statement in a block. + +void +Block::replace_statement(size_t index, Statement* s) +{ + go_assert(index < this->statements_.size()); + this->statements_[index] = s; +} + +// Add a statement before another statement. + +void +Block::insert_statement_before(size_t index, Statement* s) +{ + go_assert(index < this->statements_.size()); + this->statements_.insert(this->statements_.begin() + index, s); +} + +// Add a statement after another statement. + +void +Block::insert_statement_after(size_t index, Statement* s) +{ + go_assert(index < this->statements_.size()); + this->statements_.insert(this->statements_.begin() + index + 1, s); +} + +// Traverse the tree. + +int +Block::traverse(Traverse* traverse) +{ + unsigned int traverse_mask = traverse->traverse_mask(); + + if ((traverse_mask & Traverse::traverse_blocks) != 0) + { + int t = traverse->block(this); + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + else if (t == TRAVERSE_SKIP_COMPONENTS) + return TRAVERSE_CONTINUE; + } + + if ((traverse_mask + & (Traverse::traverse_variables + | Traverse::traverse_constants + | Traverse::traverse_expressions + | Traverse::traverse_types)) != 0) + { + const unsigned int e_or_t = (Traverse::traverse_expressions + | Traverse::traverse_types); + const unsigned int e_or_t_or_s = (e_or_t + | Traverse::traverse_statements); + for (Bindings::const_definitions_iterator pb = + this->bindings_->begin_definitions(); + pb != this->bindings_->end_definitions(); + ++pb) + { + int t = TRAVERSE_CONTINUE; + switch ((*pb)->classification()) + { + case Named_object::NAMED_OBJECT_CONST: + if ((traverse_mask & Traverse::traverse_constants) != 0) + t = traverse->constant(*pb, false); + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t) != 0) + { + Type* tc = (*pb)->const_value()->type(); + if (tc != NULL + && Type::traverse(tc, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + t = (*pb)->const_value()->traverse_expression(traverse); + } + break; + + case Named_object::NAMED_OBJECT_VAR: + case Named_object::NAMED_OBJECT_RESULT_VAR: + if ((traverse_mask & Traverse::traverse_variables) != 0) + t = traverse->variable(*pb); + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t) != 0) + { + if ((*pb)->is_result_variable() + || (*pb)->var_value()->has_type()) + { + Type* tv = ((*pb)->is_variable() + ? (*pb)->var_value()->type() + : (*pb)->result_var_value()->type()); + if (tv != NULL + && Type::traverse(tv, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t_or_s) != 0 + && (*pb)->is_variable()) + t = (*pb)->var_value()->traverse_expression(traverse, + traverse_mask); + break; + + case Named_object::NAMED_OBJECT_FUNC: + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + go_unreachable(); + + case Named_object::NAMED_OBJECT_TYPE: + if ((traverse_mask & e_or_t) != 0) + t = Type::traverse((*pb)->type_value(), traverse); + break; + + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + case Named_object::NAMED_OBJECT_UNKNOWN: + case Named_object::NAMED_OBJECT_ERRONEOUS: + break; + + case Named_object::NAMED_OBJECT_PACKAGE: + case Named_object::NAMED_OBJECT_SINK: + go_unreachable(); + + default: + go_unreachable(); + } + + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + + // No point in checking traverse_mask here--if we got here we always + // want to walk the statements. The traversal can insert new + // statements before or after the current statement. Inserting + // statements before the current statement requires updating I via + // the pointer; those statements will not be traversed. Any new + // statements inserted after the current statement will be traversed + // in their turn. + for (size_t i = 0; i < this->statements_.size(); ++i) + { + if (this->statements_[i]->traverse(this, &i, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + return TRAVERSE_CONTINUE; +} + +// Work out types for unspecified variables and constants. + +void +Block::determine_types() +{ + for (Bindings::const_definitions_iterator pb = + this->bindings_->begin_definitions(); + pb != this->bindings_->end_definitions(); + ++pb) + { + if ((*pb)->is_variable()) + (*pb)->var_value()->determine_type(); + else if ((*pb)->is_const()) + (*pb)->const_value()->determine_type(); + } + + for (std::vector::const_iterator ps = this->statements_.begin(); + ps != this->statements_.end(); + ++ps) + (*ps)->determine_types(); +} + +// Return true if the statements in this block may fall through. + +bool +Block::may_fall_through() const +{ + if (this->statements_.empty()) + return true; + return this->statements_.back()->may_fall_through(); +} + +// Convert a block to the backend representation. + +Bblock* +Block::get_backend(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + Named_object* function = context->function(); + std::vector vars; + vars.reserve(this->bindings_->size_definitions()); + for (Bindings::const_definitions_iterator pv = + this->bindings_->begin_definitions(); + pv != this->bindings_->end_definitions(); + ++pv) + { + if ((*pv)->is_variable() && !(*pv)->var_value()->is_parameter()) + vars.push_back((*pv)->get_backend_variable(gogo, function)); + } + + // FIXME: Permitting FUNCTION to be NULL here is a temporary measure + // until we have a proper representation of the init function. + Bfunction* bfunction; + if (function == NULL) + bfunction = NULL; + else + bfunction = tree_to_function(function->func_value()->get_decl()); + Bblock* ret = context->backend()->block(bfunction, context->bblock(), + vars, this->start_location_, + this->end_location_); + + Translate_context subcontext(gogo, function, this, ret); + std::vector bstatements; + bstatements.reserve(this->statements_.size()); + for (std::vector::const_iterator p = this->statements_.begin(); + p != this->statements_.end(); + ++p) + bstatements.push_back((*p)->get_backend(&subcontext)); + + context->backend()->block_add_statements(ret, bstatements); + + return ret; +} + +// Class Bindings_snapshot. + +Bindings_snapshot::Bindings_snapshot(const Block* b, Location location) + : block_(b), counts_(), location_(location) +{ + while (b != NULL) + { + this->counts_.push_back(b->bindings()->size_definitions()); + b = b->enclosing(); + } +} + +// Report errors appropriate for a goto from B to this. + +void +Bindings_snapshot::check_goto_from(const Block* b, Location loc) +{ + size_t dummy; + if (!this->check_goto_block(loc, b, this->block_, &dummy)) + return; + this->check_goto_defs(loc, this->block_, + this->block_->bindings()->size_definitions(), + this->counts_[0]); +} + +// Report errors appropriate for a goto from this to B. + +void +Bindings_snapshot::check_goto_to(const Block* b) +{ + size_t index; + if (!this->check_goto_block(this->location_, this->block_, b, &index)) + return; + this->check_goto_defs(this->location_, b, this->counts_[index], + b->bindings()->size_definitions()); +} + +// Report errors appropriate for a goto at LOC from BFROM to BTO. +// Return true if all is well, false if we reported an error. If this +// returns true, it sets *PINDEX to the number of blocks BTO is above +// BFROM. + +bool +Bindings_snapshot::check_goto_block(Location loc, const Block* bfrom, + const Block* bto, size_t* pindex) +{ + // It is an error if BTO is not either BFROM or above BFROM. + size_t index = 0; + for (const Block* pb = bfrom; pb != bto; pb = pb->enclosing(), ++index) + { + if (pb == NULL) + { + error_at(loc, "goto jumps into block"); + inform(bto->start_location(), "goto target block starts here"); + return false; + } + } + *pindex = index; + return true; +} + +// Report errors appropriate for a goto at LOC ending at BLOCK, where +// CFROM is the number of names defined at the point of the goto and +// CTO is the number of names defined at the point of the label. + +void +Bindings_snapshot::check_goto_defs(Location loc, const Block* block, + size_t cfrom, size_t cto) +{ + if (cfrom < cto) + { + Bindings::const_definitions_iterator p = + block->bindings()->begin_definitions(); + for (size_t i = 0; i < cfrom; ++i) + { + go_assert(p != block->bindings()->end_definitions()); + ++p; + } + go_assert(p != block->bindings()->end_definitions()); + + std::string n = (*p)->message_name(); + error_at(loc, "goto jumps over declaration of %qs", n.c_str()); + inform((*p)->location(), "%qs defined here", n.c_str()); + } +} + +// Class Function_declaration. + +// Return the function descriptor. + +Expression* +Function_declaration::descriptor(Gogo*, Named_object* no) +{ + go_assert(!this->fntype_->is_method()); + if (this->descriptor_ == NULL) + this->descriptor_ = Expression::make_func_descriptor(no); + return this->descriptor_; +} + +// Class Variable. + +Variable::Variable(Type* type, Expression* init, bool is_global, + bool is_parameter, bool is_receiver, + Location location) + : type_(type), init_(init), preinit_(NULL), location_(location), + backend_(NULL), is_global_(is_global), is_parameter_(is_parameter), + is_receiver_(is_receiver), is_varargs_parameter_(false), is_used_(false), + is_address_taken_(false), is_non_escaping_address_taken_(false), + seen_(false), init_is_lowered_(false), init_is_flattened_(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), determined_type_(false), + in_unique_section_(false) +{ + go_assert(type != NULL || init != NULL); + go_assert(!is_parameter || init == NULL); +} + +// Traverse the initializer expression. + +int +Variable::traverse_expression(Traverse* traverse, unsigned int traverse_mask) +{ + if (this->preinit_ != NULL) + { + if (this->preinit_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->init_ != NULL + && ((traverse_mask + & (Traverse::traverse_expressions | Traverse::traverse_types)) + != 0)) + { + if (Expression::traverse(&this->init_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Lower the initialization expression after parsing is complete. + +void +Variable::lower_init_expression(Gogo* gogo, Named_object* function, + Statement_inserter* inserter) +{ + Named_object* dep = gogo->var_depends_on(this); + if (dep != NULL && dep->is_variable()) + dep->var_value()->lower_init_expression(gogo, function, inserter); + + 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; + + Statement_inserter global_inserter; + if (this->is_global_) + { + global_inserter = Statement_inserter(gogo, this); + inserter = &global_inserter; + } + + gogo->lower_expression(function, inserter, &this->init_); + + this->seen_ = false; + + this->init_is_lowered_ = true; + } +} + +// Flatten the initialization expression after ordering evaluations. + +void +Variable::flatten_init_expression(Gogo* gogo, Named_object* function, + Statement_inserter* inserter) +{ + Named_object* dep = gogo->var_depends_on(this); + if (dep != NULL && dep->is_variable()) + dep->var_value()->flatten_init_expression(gogo, function, inserter); + + if (this->init_ != NULL && !this->init_is_flattened_) + { + if (this->seen_) + { + // We will give an error elsewhere, this is just to prevent + // an infinite loop. + return; + } + this->seen_ = true; + + Statement_inserter global_inserter; + if (this->is_global_) + { + global_inserter = Statement_inserter(gogo, this); + inserter = &global_inserter; + } + + gogo->flatten_expression(function, inserter, &this->init_); + + this->seen_ = false; + this->init_is_flattened_ = true; + } +} + +// Get the preinit block. + +Block* +Variable::preinit_block(Gogo* gogo) +{ + go_assert(this->is_global_); + if (this->preinit_ == NULL) + this->preinit_ = new Block(NULL, this->location()); + + // If a global variable has a preinitialization statement, then we + // need to have an initialization function. + gogo->set_need_init_fn(); + + return this->preinit_; +} + +// Add a statement to be run before the initialization expression. + +void +Variable::add_preinit_statement(Gogo* gogo, Statement* s) +{ + Block* b = this->preinit_block(gogo); + b->add_statement(s); + b->set_end_location(s->location()); +} + +// Whether this variable has a type. + +bool +Variable::has_type() const +{ + if (this->type_ == NULL) + return false; + + // A variable created in a type switch case nil does not actually + // have a type yet. It will be changed to use the initializer's + // type in determine_type. + if (this->is_type_switch_var_ + && this->type_->is_nil_constant_as_type()) + return false; + + return true; +} + +// In an assignment which sets a variable to a tuple of EXPR, return +// the type of the first element of the tuple. + +Type* +Variable::type_from_tuple(Expression* expr, bool report_error) const +{ + if (expr->map_index_expression() != NULL) + { + Map_type* mt = expr->map_index_expression()->get_map_type(); + if (mt == NULL) + return Type::make_error_type(); + return mt->val_type(); + } + else if (expr->receive_expression() != NULL) + { + Expression* channel = expr->receive_expression()->channel(); + Type* channel_type = channel->type(); + if (channel_type->channel_type() == NULL) + return Type::make_error_type(); + return channel_type->channel_type()->element_type(); + } + else + { + if (report_error) + error_at(this->location(), "invalid tuple definition"); + return Type::make_error_type(); + } +} + +// Given EXPR used in a range clause, return either the index type or +// the value type of the range, depending upon GET_INDEX_TYPE. + +Type* +Variable::type_from_range(Expression* expr, bool get_index_type, + bool report_error) const +{ + Type* t = expr->type(); + if (t->array_type() != NULL + || (t->points_to() != NULL + && t->points_to()->array_type() != NULL + && !t->points_to()->is_slice_type())) + { + if (get_index_type) + return Type::lookup_integer_type("int"); + else + return t->deref()->array_type()->element_type(); + } + else if (t->is_string_type()) + { + if (get_index_type) + return Type::lookup_integer_type("int"); + else + return Type::lookup_integer_type("int32"); + } + else if (t->map_type() != NULL) + { + if (get_index_type) + return t->map_type()->key_type(); + else + return t->map_type()->val_type(); + } + else if (t->channel_type() != NULL) + { + if (get_index_type) + return t->channel_type()->element_type(); + else + { + if (report_error) + error_at(this->location(), + "invalid definition of value variable for channel range"); + return Type::make_error_type(); + } + } + else + { + if (report_error) + error_at(this->location(), "invalid type for range clause"); + return Type::make_error_type(); + } +} + +// EXPR should be a channel. Return the channel's element type. + +Type* +Variable::type_from_chan_element(Expression* expr, bool report_error) const +{ + Type* t = expr->type(); + if (t->channel_type() != NULL) + return t->channel_type()->element_type(); + else + { + if (report_error) + error_at(this->location(), "expected channel"); + return Type::make_error_type(); + } +} + +// Return the type of the Variable. This may be called before +// Variable::determine_type is called, which means that we may need to +// get the type from the initializer. FIXME: If we combine lowering +// with type determination, then this should be unnecessary. + +Type* +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. + Type* type = this->type_; + Expression* init = this->init_; + if (this->is_type_switch_var_ + && this->type_->is_nil_constant_as_type()) + { + Type_guard_expression* tge = this->init_->type_guard_expression(); + go_assert(tge != NULL); + init = tge->expr(); + 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) + ; + else if (this->type_from_init_tuple_) + type = this->type_from_tuple(init, false); + else if (this->type_from_range_index_ || this->type_from_range_value_) + type = this->type_from_range(init, this->type_from_range_index_, false); + else if (this->type_from_chan_element_) + type = this->type_from_chan_element(init, false); + else + { + go_assert(init != NULL); + type = init->type(); + go_assert(type != NULL); + + // Variables should not have abstract types. + if (type->is_abstract()) + type = type->make_non_abstract_type(); + + if (type->is_void_type()) + type = Type::make_error_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 +{ + go_assert(this->type_ != NULL); + return this->type_; +} + +// Set the type if necessary. + +void +Variable::determine_type() +{ + if (this->determined_type_) + return; + this->determined_type_ = true; + + if (this->preinit_ != NULL) + this->preinit_->determine_types(); + + // A variable in a type switch with a nil case will have the wrong + // type here. It will have an initializer which is a type guard. + // We want to initialize it to the value without the type guard, and + // use the type of that value as well. + if (this->is_type_switch_var_ && this->type_->is_nil_constant_as_type()) + { + Type_guard_expression* tge = this->init_->type_guard_expression(); + go_assert(tge != NULL); + this->type_ = NULL; + this->init_ = tge->expr(); + } + + if (this->init_ == NULL) + go_assert(this->type_ != NULL && !this->type_->is_abstract()); + else if (this->type_from_init_tuple_) + { + Expression *init = this->init_; + init->determine_type_no_context(); + this->type_ = this->type_from_tuple(init, true); + this->init_ = NULL; + } + else if (this->type_from_range_index_ || this->type_from_range_value_) + { + Expression* init = this->init_; + init->determine_type_no_context(); + this->type_ = this->type_from_range(init, this->type_from_range_index_, + true); + this->init_ = NULL; + } + else if (this->type_from_chan_element_) + { + Expression* init = this->init_; + init->determine_type_no_context(); + this->type_ = this->type_from_chan_element(init, true); + this->init_ = NULL; + } + else + { + Type_context context(this->type_, false); + this->init_->determine_type(&context); + if (this->type_ == NULL) + { + Type* type = this->init_->type(); + go_assert(type != NULL); + if (type->is_abstract()) + type = type->make_non_abstract_type(); + + if (type->is_void_type()) + { + error_at(this->location_, "variable has no type"); + type = Type::make_error_type(); + } + else if (type->is_nil_type()) + { + error_at(this->location_, "variable defined to nil type"); + type = Type::make_error_type(); + } + else if (type->is_call_multiple_result_type()) + { + error_at(this->location_, + "single variable set to multiple-value function call"); + type = Type::make_error_type(); + } + + this->type_ = type; + } + } +} + +// Export the variable + +void +Variable::export_var(Export* exp, const std::string& name) const +{ + go_assert(this->is_global_); + exp->write_c_string("var "); + exp->write_string(name); + exp->write_c_string(" "); + exp->write_type(this->type()); + exp->write_c_string(";\n"); +} + +// Import a variable. + +void +Variable::import_var(Import* imp, std::string* pname, Type** ptype) +{ + imp->require_c_string("var "); + *pname = imp->read_identifier(); + imp->require_c_string(" "); + *ptype = imp->read_type(); + imp->require_c_string(";\n"); +} + +// Convert a variable to the backend representation. + +Bvariable* +Variable::get_backend_variable(Gogo* gogo, Named_object* function, + const Package* package, const std::string& name) +{ + if (this->backend_ == NULL) + { + Backend* backend = gogo->backend(); + Type* type = this->type_; + if (type->is_error_type() + || (type->is_undefined() + && (!this->is_global_ || package == NULL))) + this->backend_ = backend->error_variable(); + else + { + bool is_parameter = this->is_parameter_; + if (this->is_receiver_ && type->points_to() == NULL) + is_parameter = false; + if (this->is_in_heap()) + { + is_parameter = false; + type = Type::make_pointer_type(type); + } + + std::string n = Gogo::unpack_hidden_name(name); + Btype* btype = type->get_backend(gogo); + + Bvariable* bvar; + if (this->is_global_) + bvar = backend->global_variable((package == NULL + ? gogo->package_name() + : package->package_name()), + (package == NULL + ? gogo->pkgpath_symbol() + : package->pkgpath_symbol()), + n, + btype, + package != NULL, + Gogo::is_hidden_name(name), + this->in_unique_section_, + this->location_); + else if (function == NULL) + { + go_assert(saw_errors()); + bvar = backend->error_variable(); + } + else + { + tree fndecl = function->func_value()->get_decl(); + Bfunction* bfunction = tree_to_function(fndecl); + bool is_address_taken = (this->is_non_escaping_address_taken_ + && !this->is_in_heap()); + if (is_parameter) + bvar = backend->parameter_variable(bfunction, n, btype, + is_address_taken, + this->location_); + else + bvar = backend->local_variable(bfunction, n, btype, + is_address_taken, + this->location_); + } + this->backend_ = bvar; + } + } + return this->backend_; +} + +// Class Result_variable. + +// Convert a result variable to the backend representation. + +Bvariable* +Result_variable::get_backend_variable(Gogo* gogo, Named_object* function, + const std::string& name) +{ + if (this->backend_ == NULL) + { + Backend* backend = gogo->backend(); + Type* type = this->type_; + if (type->is_error()) + this->backend_ = backend->error_variable(); + else + { + if (this->is_in_heap()) + type = Type::make_pointer_type(type); + Btype* btype = type->get_backend(gogo); + tree fndecl = function->func_value()->get_decl(); + Bfunction* bfunction = tree_to_function(fndecl); + std::string n = Gogo::unpack_hidden_name(name); + bool is_address_taken = (this->is_non_escaping_address_taken_ + && !this->is_in_heap()); + this->backend_ = backend->local_variable(bfunction, n, btype, + is_address_taken, + this->location_); + } + } + return this->backend_; +} + +// Class Named_constant. + +// Traverse the initializer expression. + +int +Named_constant::traverse_expression(Traverse* traverse) +{ + return Expression::traverse(&this->expr_, traverse); +} + +// Determine the type of the constant. + +void +Named_constant::determine_type() +{ + if (this->type_ != NULL) + { + Type_context context(this->type_, false); + this->expr_->determine_type(&context); + } + else + { + // A constant may have an abstract type. + Type_context context(NULL, true); + this->expr_->determine_type(&context); + this->type_ = this->expr_->type(); + go_assert(this->type_ != NULL); + } +} + +// Indicate that we found and reported an error for this constant. + +void +Named_constant::set_error() +{ + this->type_ = Type::make_error_type(); + this->expr_ = Expression::make_error(this->location_); +} + +// Export a constant. + +void +Named_constant::export_const(Export* exp, const std::string& name) const +{ + exp->write_c_string("const "); + exp->write_string(name); + exp->write_c_string(" "); + if (!this->type_->is_abstract()) + { + exp->write_type(this->type_); + exp->write_c_string(" "); + } + exp->write_c_string("= "); + this->expr()->export_expression(exp); + exp->write_c_string(";\n"); +} + +// Import a constant. + +void +Named_constant::import_const(Import* imp, std::string* pname, Type** ptype, + Expression** pexpr) +{ + imp->require_c_string("const "); + *pname = imp->read_identifier(); + imp->require_c_string(" "); + if (imp->peek_char() == '=') + *ptype = NULL; + else + { + *ptype = imp->read_type(); + imp->require_c_string(" "); + } + imp->require_c_string("= "); + *pexpr = Expression::import_expression(imp); + imp->require_c_string(";\n"); +} + +// Add a method. + +Named_object* +Type_declaration::add_method(const std::string& name, Function* function) +{ + Named_object* ret = Named_object::make_function(name, NULL, function); + this->methods_.push_back(ret); + return ret; +} + +// Add a method declaration. + +Named_object* +Type_declaration::add_method_declaration(const std::string& name, + Package* package, + Function_type* type, + Location location) +{ + Named_object* ret = Named_object::make_function_declaration(name, package, + type, location); + this->methods_.push_back(ret); + return ret; +} + +// Return whether any methods ere defined. + +bool +Type_declaration::has_methods() const +{ + return !this->methods_.empty(); +} + +// Define methods for the real type. + +void +Type_declaration::define_methods(Named_type* nt) +{ + for (std::vector::const_iterator p = this->methods_.begin(); + p != this->methods_.end(); + ++p) + nt->add_existing_method(*p); +} + +// We are using the type. Return true if we should issue a warning. + +bool +Type_declaration::using_type() +{ + bool ret = !this->issued_warning_; + this->issued_warning_ = true; + return ret; +} + +// Class Unknown_name. + +// Set the real named object. + +void +Unknown_name::set_real_named_object(Named_object* no) +{ + go_assert(this->real_named_object_ == NULL); + go_assert(!no->is_unknown()); + this->real_named_object_ = no; +} + +// Class Named_object. + +Named_object::Named_object(const std::string& name, + const Package* package, + Classification classification) + : name_(name), package_(package), classification_(classification), + tree_(NULL) +{ + if (Gogo::is_sink_name(name)) + go_assert(classification == NAMED_OBJECT_SINK); +} + +// Make an unknown name. This is used by the parser. The name must +// be resolved later. Unknown names are only added in the current +// package. + +Named_object* +Named_object::make_unknown_name(const std::string& name, + Location location) +{ + Named_object* named_object = new Named_object(name, NULL, + NAMED_OBJECT_UNKNOWN); + Unknown_name* value = new Unknown_name(location); + named_object->u_.unknown_value = value; + return named_object; +} + +// Make a constant. + +Named_object* +Named_object::make_constant(const Typed_identifier& tid, + const Package* package, Expression* expr, + int iota_value) +{ + Named_object* named_object = new Named_object(tid.name(), package, + NAMED_OBJECT_CONST); + Named_constant* named_constant = new Named_constant(tid.type(), expr, + iota_value, + tid.location()); + named_object->u_.const_value = named_constant; + return named_object; +} + +// Make a named type. + +Named_object* +Named_object::make_type(const std::string& name, const Package* package, + Type* type, Location location) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_TYPE); + Named_type* named_type = Type::make_named_type(named_object, type, location); + named_object->u_.type_value = named_type; + return named_object; +} + +// Make a type declaration. + +Named_object* +Named_object::make_type_declaration(const std::string& name, + const Package* package, + Location location) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_TYPE_DECLARATION); + Type_declaration* type_declaration = new Type_declaration(location); + named_object->u_.type_declaration = type_declaration; + return named_object; +} + +// Make a variable. + +Named_object* +Named_object::make_variable(const std::string& name, const Package* package, + Variable* variable) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_VAR); + named_object->u_.var_value = variable; + return named_object; +} + +// Make a result variable. + +Named_object* +Named_object::make_result_variable(const std::string& name, + Result_variable* result) +{ + Named_object* named_object = new Named_object(name, NULL, + NAMED_OBJECT_RESULT_VAR); + named_object->u_.result_var_value = result; + return named_object; +} + +// Make a sink. This is used for the special blank identifier _. + +Named_object* +Named_object::make_sink() +{ + return new Named_object("_", NULL, NAMED_OBJECT_SINK); +} + +// Make a named function. + +Named_object* +Named_object::make_function(const std::string& name, const Package* package, + Function* function) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_FUNC); + named_object->u_.func_value = function; + return named_object; +} + +// Make a function declaration. + +Named_object* +Named_object::make_function_declaration(const std::string& name, + const Package* package, + Function_type* fntype, + Location location) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_FUNC_DECLARATION); + Function_declaration *func_decl = new Function_declaration(fntype, location); + named_object->u_.func_declaration_value = func_decl; + return named_object; +} + +// Make a package. + +Named_object* +Named_object::make_package(const std::string& alias, Package* package) +{ + Named_object* named_object = new Named_object(alias, NULL, + NAMED_OBJECT_PACKAGE); + named_object->u_.package_value = package; + return named_object; +} + +// Return the name to use in an error message. + +std::string +Named_object::message_name() const +{ + if (this->package_ == NULL) + return Gogo::message_name(this->name_); + std::string ret; + if (this->package_->has_package_name()) + ret = this->package_->package_name(); + else + ret = this->package_->pkgpath(); + ret = Gogo::message_name(ret); + ret += '.'; + ret += Gogo::message_name(this->name_); + return ret; +} + +// Set the type when a declaration is defined. + +void +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); + unsigned int index; + Named_object* in_function = td->in_function(&index); + if (in_function != NULL) + named_type->set_in_function(in_function, index); + delete td; + this->classification_ = NAMED_OBJECT_TYPE; + this->u_.type_value = named_type; +} + +// Define a function which was previously declared. + +void +Named_object::set_function_value(Function* function) +{ + go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION); + if (this->func_declaration_value()->has_descriptor()) + { + Expression* descriptor = + this->func_declaration_value()->descriptor(NULL, NULL); + function->set_descriptor(descriptor); + } + this->classification_ = NAMED_OBJECT_FUNC; + // FIXME: We should free the old value. + this->u_.func_value = function; +} + +// Declare an unknown object as a type declaration. + +void +Named_object::declare_as_type() +{ + go_assert(this->classification_ == NAMED_OBJECT_UNKNOWN); + Unknown_name* unk = this->u_.unknown_value; + this->classification_ = NAMED_OBJECT_TYPE_DECLARATION; + this->u_.type_declaration = new Type_declaration(unk->location()); + delete unk; +} + +// Return the location of a named object. + +Location +Named_object::location() const +{ + switch (this->classification_) + { + default: + case NAMED_OBJECT_UNINITIALIZED: + go_unreachable(); + + case NAMED_OBJECT_ERRONEOUS: + return Linemap::unknown_location(); + + case NAMED_OBJECT_UNKNOWN: + return this->unknown_value()->location(); + + case NAMED_OBJECT_CONST: + return this->const_value()->location(); + + case NAMED_OBJECT_TYPE: + return this->type_value()->location(); + + case NAMED_OBJECT_TYPE_DECLARATION: + return this->type_declaration_value()->location(); + + case NAMED_OBJECT_VAR: + return this->var_value()->location(); + + case NAMED_OBJECT_RESULT_VAR: + return this->result_var_value()->location(); + + case NAMED_OBJECT_SINK: + go_unreachable(); + + case NAMED_OBJECT_FUNC: + return this->func_value()->location(); + + case NAMED_OBJECT_FUNC_DECLARATION: + return this->func_declaration_value()->location(); + + case NAMED_OBJECT_PACKAGE: + return this->package_value()->location(); + } +} + +// Export a named object. + +void +Named_object::export_named_object(Export* exp) const +{ + switch (this->classification_) + { + default: + case NAMED_OBJECT_UNINITIALIZED: + case NAMED_OBJECT_UNKNOWN: + go_unreachable(); + + case NAMED_OBJECT_ERRONEOUS: + break; + + case NAMED_OBJECT_CONST: + this->const_value()->export_const(exp, this->name_); + break; + + case NAMED_OBJECT_TYPE: + this->type_value()->export_named_type(exp, this->name_); + break; + + case NAMED_OBJECT_TYPE_DECLARATION: + error_at(this->type_declaration_value()->location(), + "attempt to export %<%s%> which was declared but not defined", + this->message_name().c_str()); + break; + + case NAMED_OBJECT_FUNC_DECLARATION: + this->func_declaration_value()->export_func(exp, this->name_); + break; + + case NAMED_OBJECT_VAR: + this->var_value()->export_var(exp, this->name_); + break; + + case NAMED_OBJECT_RESULT_VAR: + case NAMED_OBJECT_SINK: + go_unreachable(); + + case NAMED_OBJECT_FUNC: + this->func_value()->export_func(exp, this->name_); + break; + } +} + +// Convert a variable to the backend representation. + +Bvariable* +Named_object::get_backend_variable(Gogo* gogo, Named_object* function) +{ + if (this->classification_ == NAMED_OBJECT_VAR) + return this->var_value()->get_backend_variable(gogo, function, + this->package_, this->name_); + else if (this->classification_ == NAMED_OBJECT_RESULT_VAR) + return this->result_var_value()->get_backend_variable(gogo, function, + this->name_); + else + go_unreachable(); +} + + +// Return the external identifier for this object. + +std::string +Named_object::get_id(Gogo* gogo) +{ + go_assert(!this->is_variable() && !this->is_result_variable()); + std::string decl_name; + if (this->is_function_declaration() + && !this->func_declaration_value()->asm_name().empty()) + decl_name = this->func_declaration_value()->asm_name(); + else if (this->is_type() + && Linemap::is_predeclared_location(this->type_value()->location())) + { + // We don't need the package name for builtin types. + decl_name = Gogo::unpack_hidden_name(this->name_); + } + else + { + std::string package_name; + if (this->package_ == NULL) + package_name = gogo->package_name(); + else + package_name = this->package_->package_name(); + + // Note that this will be misleading if this is an unexported + // method generated for an embedded imported type. In that case + // the unexported method should have the package name of the + // package from which it is imported, but we are going to give + // it our package name. Fixing this would require knowing the + // package name, but we only know the package path. It might be + // better to use package paths here anyhow. This doesn't affect + // the assembler code, because we always set that name in + // Function::get_or_make_decl anyhow. FIXME. + + decl_name = package_name + '.' + Gogo::unpack_hidden_name(this->name_); + + Function_type* fntype; + if (this->is_function()) + fntype = this->func_value()->type(); + else if (this->is_function_declaration()) + fntype = this->func_declaration_value()->type(); + else + fntype = NULL; + if (fntype != NULL && fntype->is_method()) + { + decl_name.push_back('.'); + decl_name.append(fntype->receiver()->type()->mangled_name(gogo)); + } + } + if (this->is_type()) + { + 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()); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + decl_name += '$'; + decl_name += buf; + } + } + } + return decl_name; +} + +// Class Bindings. + +Bindings::Bindings(Bindings* enclosing) + : enclosing_(enclosing), named_objects_(), bindings_() +{ +} + +// Clear imports. + +void +Bindings::clear_file_scope(Gogo* gogo) +{ + Contour::iterator p = this->bindings_.begin(); + while (p != this->bindings_.end()) + { + bool keep; + if (p->second->package() != NULL) + keep = false; + else if (p->second->is_package()) + keep = false; + else if (p->second->is_function() + && !p->second->func_value()->type()->is_method() + && Gogo::unpack_hidden_name(p->second->name()) == "init") + keep = false; + else + keep = true; + + if (keep) + ++p; + else + { + gogo->add_file_block_name(p->second->name(), p->second->location()); + p = this->bindings_.erase(p); + } + } +} + +// Look up a symbol. + +Named_object* +Bindings::lookup(const std::string& name) const +{ + Contour::const_iterator p = this->bindings_.find(name); + if (p != this->bindings_.end()) + return p->second->resolve(); + else if (this->enclosing_ != NULL) + return this->enclosing_->lookup(name); + else + return NULL; +} + +// Look up a symbol locally. + +Named_object* +Bindings::lookup_local(const std::string& name) const +{ + Contour::const_iterator p = this->bindings_.find(name); + if (p == this->bindings_.end()) + return NULL; + return p->second; +} + +// Remove an object from a set of bindings. This is used for a +// special case in thunks for functions which call recover. + +void +Bindings::remove_binding(Named_object* no) +{ + Contour::iterator pb = this->bindings_.find(no->name()); + go_assert(pb != this->bindings_.end()); + this->bindings_.erase(pb); + for (std::vector::iterator pn = this->named_objects_.begin(); + pn != this->named_objects_.end(); + ++pn) + { + if (*pn == no) + { + this->named_objects_.erase(pn); + return; + } + } + go_unreachable(); +} + +// Add a method to the list of objects. This is not added to the +// lookup table. This is so that we have a single list of objects +// declared at the top level, which we walk through when it's time to +// convert to trees. + +void +Bindings::add_method(Named_object* method) +{ + this->named_objects_.push_back(method); +} + +// Add a generic Named_object to a Contour. + +Named_object* +Bindings::add_named_object_to_contour(Contour* contour, + Named_object* named_object) +{ + go_assert(named_object == named_object->resolve()); + const std::string& name(named_object->name()); + go_assert(!Gogo::is_sink_name(name)); + + std::pair ins = + contour->insert(std::make_pair(name, named_object)); + if (!ins.second) + { + // The name was already there. + if (named_object->package() != NULL + && ins.first->second->package() == named_object->package() + && (ins.first->second->classification() + == named_object->classification())) + { + // This is a second import of the same object. + return ins.first->second; + } + ins.first->second = this->new_definition(ins.first->second, + named_object); + return ins.first->second; + } + else + { + // Don't push declarations on the list. We push them on when + // and if we find the definitions. That way we genericize the + // functions in order. + if (!named_object->is_type_declaration() + && !named_object->is_function_declaration() + && !named_object->is_unknown()) + this->named_objects_.push_back(named_object); + return named_object; + } +} + +// We had an existing named object OLD_OBJECT, and we've seen a new +// one NEW_OBJECT with the same name. FIXME: This does not free the +// new object when we don't need it. + +Named_object* +Bindings::new_definition(Named_object* old_object, Named_object* new_object) +{ + if (new_object->is_erroneous() && !old_object->is_erroneous()) + return new_object; + + std::string reason; + switch (old_object->classification()) + { + default: + case Named_object::NAMED_OBJECT_UNINITIALIZED: + go_unreachable(); + + case Named_object::NAMED_OBJECT_ERRONEOUS: + return old_object; + + case Named_object::NAMED_OBJECT_UNKNOWN: + { + Named_object* real = old_object->unknown_value()->real_named_object(); + if (real != NULL) + return this->new_definition(real, new_object); + go_assert(!new_object->is_unknown()); + old_object->unknown_value()->set_real_named_object(new_object); + if (!new_object->is_type_declaration() + && !new_object->is_function_declaration()) + this->named_objects_.push_back(new_object); + return new_object; + } + + case Named_object::NAMED_OBJECT_CONST: + break; + + case Named_object::NAMED_OBJECT_TYPE: + if (new_object->is_type_declaration()) + return old_object; + break; + + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + if (new_object->is_type_declaration()) + return old_object; + if (new_object->is_type()) + { + old_object->set_type_value(new_object->type_value()); + new_object->type_value()->set_named_object(old_object); + this->named_objects_.push_back(old_object); + return old_object; + } + break; + + case Named_object::NAMED_OBJECT_VAR: + case Named_object::NAMED_OBJECT_RESULT_VAR: + // We have already given an error in the parser for cases where + // one parameter or result variable redeclares another one. + if ((new_object->is_variable() + && new_object->var_value()->is_parameter()) + || new_object->is_result_variable()) + return old_object; + break; + + case Named_object::NAMED_OBJECT_SINK: + go_unreachable(); + + case Named_object::NAMED_OBJECT_FUNC: + if (new_object->is_function_declaration()) + { + if (!new_object->func_declaration_value()->asm_name().empty()) + sorry("__asm__ for function definitions"); + Function_type* old_type = old_object->func_value()->type(); + Function_type* new_type = + new_object->func_declaration_value()->type(); + if (old_type->is_valid_redeclaration(new_type, &reason)) + return old_object; + } + break; + + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + { + Function_type* old_type = old_object->func_declaration_value()->type(); + if (new_object->is_function_declaration()) + { + Function_type* new_type = + new_object->func_declaration_value()->type(); + if (old_type->is_valid_redeclaration(new_type, &reason)) + return old_object; + } + if (new_object->is_function()) + { + Function_type* new_type = new_object->func_value()->type(); + if (old_type->is_valid_redeclaration(new_type, &reason)) + { + if (!old_object->func_declaration_value()->asm_name().empty()) + sorry("__asm__ for function definitions"); + old_object->set_function_value(new_object->func_value()); + this->named_objects_.push_back(old_object); + return old_object; + } + } + } + break; + + case Named_object::NAMED_OBJECT_PACKAGE: + break; + } + + std::string n = old_object->message_name(); + if (reason.empty()) + error_at(new_object->location(), "redefinition of %qs", n.c_str()); + else + error_at(new_object->location(), "redefinition of %qs: %s", n.c_str(), + reason.c_str()); + + inform(old_object->location(), "previous definition of %qs was here", + n.c_str()); + + return old_object; +} + +// Add a named type. + +Named_object* +Bindings::add_named_type(Named_type* named_type) +{ + return this->add_named_object(named_type->named_object()); +} + +// Add a function. + +Named_object* +Bindings::add_function(const std::string& name, const Package* package, + Function* function) +{ + return this->add_named_object(Named_object::make_function(name, package, + function)); +} + +// Add a function declaration. + +Named_object* +Bindings::add_function_declaration(const std::string& name, + const Package* package, + Function_type* type, + Location location) +{ + Named_object* no = Named_object::make_function_declaration(name, package, + type, location); + return this->add_named_object(no); +} + +// Define a type which was previously declared. + +void +Bindings::define_type(Named_object* no, Named_type* type) +{ + no->set_type_value(type); + this->named_objects_.push_back(no); +} + +// Mark all local variables as used. This is used for some types of +// parse error. + +void +Bindings::mark_locals_used() +{ + for (std::vector::iterator p = this->named_objects_.begin(); + p != this->named_objects_.end(); + ++p) + if ((*p)->is_variable()) + (*p)->var_value()->set_is_used(); +} + +// Traverse bindings. + +int +Bindings::traverse(Traverse* traverse, bool is_global) +{ + unsigned int traverse_mask = traverse->traverse_mask(); + + // We don't use an iterator because we permit the traversal to add + // new global objects. + const unsigned int e_or_t = (Traverse::traverse_expressions + | Traverse::traverse_types); + const unsigned int e_or_t_or_s = (e_or_t + | Traverse::traverse_statements); + for (size_t i = 0; i < this->named_objects_.size(); ++i) + { + Named_object* p = this->named_objects_[i]; + int t = TRAVERSE_CONTINUE; + switch (p->classification()) + { + case Named_object::NAMED_OBJECT_CONST: + if ((traverse_mask & Traverse::traverse_constants) != 0) + t = traverse->constant(p, is_global); + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t) != 0) + { + Type* tc = p->const_value()->type(); + if (tc != NULL + && Type::traverse(tc, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + t = p->const_value()->traverse_expression(traverse); + } + break; + + case Named_object::NAMED_OBJECT_VAR: + case Named_object::NAMED_OBJECT_RESULT_VAR: + if ((traverse_mask & Traverse::traverse_variables) != 0) + t = traverse->variable(p); + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t) != 0) + { + if (p->is_result_variable() + || p->var_value()->has_type()) + { + Type* tv = (p->is_variable() + ? p->var_value()->type() + : p->result_var_value()->type()); + if (tv != NULL + && Type::traverse(tv, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t_or_s) != 0 + && p->is_variable()) + t = p->var_value()->traverse_expression(traverse, traverse_mask); + break; + + case Named_object::NAMED_OBJECT_FUNC: + if ((traverse_mask & Traverse::traverse_functions) != 0) + t = traverse->function(p); + + if (t == TRAVERSE_CONTINUE + && (traverse_mask + & (Traverse::traverse_variables + | Traverse::traverse_constants + | Traverse::traverse_functions + | Traverse::traverse_blocks + | Traverse::traverse_statements + | Traverse::traverse_expressions + | Traverse::traverse_types)) != 0) + t = p->func_value()->traverse(traverse); + break; + + case Named_object::NAMED_OBJECT_PACKAGE: + // These are traversed in Gogo::traverse. + go_assert(is_global); + break; + + case Named_object::NAMED_OBJECT_TYPE: + if ((traverse_mask & e_or_t) != 0) + t = Type::traverse(p->type_value(), traverse); + break; + + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + case Named_object::NAMED_OBJECT_UNKNOWN: + case Named_object::NAMED_OBJECT_ERRONEOUS: + break; + + case Named_object::NAMED_OBJECT_SINK: + default: + go_unreachable(); + } + + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + // If we need to traverse types, check the function declarations, + // which have types. Also check any methods of a type declaration. + if ((traverse_mask & e_or_t) != 0) + { + for (Bindings::const_declarations_iterator p = + this->begin_declarations(); + p != this->end_declarations(); + ++p) + { + if (p->second->is_function_declaration()) + { + if (Type::traverse(p->second->func_declaration_value()->type(), + traverse) + == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + else if (p->second->is_type_declaration()) + { + const std::vector* methods = + p->second->type_declaration_value()->methods(); + for (std::vector::const_iterator pm = + methods->begin(); + pm != methods->end(); + pm++) + { + Named_object* no = *pm; + Type *t; + if (no->is_function()) + t = no->func_value()->type(); + else if (no->is_function_declaration()) + t = no->func_declaration_value()->type(); + else + continue; + if (Type::traverse(t, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + } + } + + return TRAVERSE_CONTINUE; +} + +// Class Label. + +// Clear any references to this label. + +void +Label::clear_refs() +{ + for (std::vector::iterator p = this->refs_.begin(); + p != this->refs_.end(); + ++p) + delete *p; + this->refs_.clear(); +} + +// Get the backend representation for a label. + +Blabel* +Label::get_backend_label(Translate_context* context) +{ + if (this->blabel_ == NULL) + { + Function* function = context->function()->func_value(); + tree fndecl = function->get_decl(); + Bfunction* bfunction = tree_to_function(fndecl); + this->blabel_ = context->backend()->label(bfunction, this->name_, + this->location_); + } + return this->blabel_; +} + +// Return an expression for the address of this label. + +Bexpression* +Label::get_addr(Translate_context* context, Location location) +{ + Blabel* label = this->get_backend_label(context); + return context->backend()->label_address(label, location); +} + +// Class Unnamed_label. + +// Get the backend representation for an unnamed label. + +Blabel* +Unnamed_label::get_blabel(Translate_context* context) +{ + if (this->blabel_ == NULL) + { + Function* function = context->function()->func_value(); + tree fndecl = function->get_decl(); + Bfunction* bfunction = tree_to_function(fndecl); + this->blabel_ = context->backend()->label(bfunction, "", + this->location_); + } + return this->blabel_; +} + +// Return a statement which defines this unnamed label. + +Bstatement* +Unnamed_label::get_definition(Translate_context* context) +{ + Blabel* blabel = this->get_blabel(context); + return context->backend()->label_definition_statement(blabel); +} + +// Return a goto statement to this unnamed label. + +Bstatement* +Unnamed_label::get_goto(Translate_context* context, Location location) +{ + Blabel* blabel = this->get_blabel(context); + return context->backend()->goto_statement(blabel, location); +} + +// Class Package. + +Package::Package(const std::string& pkgpath, Location location) + : pkgpath_(pkgpath), pkgpath_symbol_(Gogo::pkgpath_for_symbol(pkgpath)), + package_name_(), bindings_(new Bindings(NULL)), priority_(0), + location_(location), used_(false), is_imported_(false), + uses_sink_alias_(false) +{ + go_assert(!pkgpath.empty()); + +} + +// Set the package name. + +void +Package::set_package_name(const std::string& package_name, Location location) +{ + go_assert(!package_name.empty()); + if (this->package_name_.empty()) + this->package_name_ = package_name; + else if (this->package_name_ != package_name) + error_at(location, + "saw two different packages with the same package path %s: %s, %s", + this->pkgpath_.c_str(), this->package_name_.c_str(), + package_name.c_str()); +} + +// Set the priority. We may see multiple priorities for an imported +// package; we want to use the largest one. + +void +Package::set_priority(int priority) +{ + if (priority > this->priority_) + this->priority_ = priority; +} + +// Determine types of constants. Everything else in a package +// (variables, function declarations) should already have a fixed +// type. Constants may have abstract types. + +void +Package::determine_types() +{ + Bindings* bindings = this->bindings_; + for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); + p != bindings->end_definitions(); + ++p) + { + if ((*p)->is_const()) + (*p)->const_value()->determine_type(); + } +} + +// Class Traverse. + +// Destructor. + +Traverse::~Traverse() +{ + if (this->types_seen_ != NULL) + delete this->types_seen_; + if (this->expressions_seen_ != NULL) + delete this->expressions_seen_; +} + +// Record that we are looking at a type, and return true if we have +// already seen it. + +bool +Traverse::remember_type(const Type* type) +{ + if (type->is_error_type()) + return true; + go_assert((this->traverse_mask() & traverse_types) != 0 + || (this->traverse_mask() & traverse_expressions) != 0); + // We mostly only have to remember named types. But it turns out + // that an interface type can refer to itself without using a name + // by relying on interface inheritance, as in + // type I interface { F() interface{I} } + if (type->classification() != Type::TYPE_NAMED + && type->classification() != Type::TYPE_INTERFACE) + return false; + if (this->types_seen_ == NULL) + this->types_seen_ = new Types_seen(); + std::pair ins = this->types_seen_->insert(type); + return !ins.second; +} + +// Record that we are looking at an expression, and return true if we +// have already seen it. + +bool +Traverse::remember_expression(const Expression* expression) +{ + go_assert((this->traverse_mask() & traverse_types) != 0 + || (this->traverse_mask() & traverse_expressions) != 0); + if (this->expressions_seen_ == NULL) + this->expressions_seen_ = new Expressions_seen(); + std::pair ins = + this->expressions_seen_->insert(expression); + return !ins.second; +} + +// The default versions of these functions should never be called: the +// traversal mask indicates which functions may be called. + +int +Traverse::variable(Named_object*) +{ + go_unreachable(); +} + +int +Traverse::constant(Named_object*, bool) +{ + go_unreachable(); +} + +int +Traverse::function(Named_object*) +{ + go_unreachable(); +} + +int +Traverse::block(Block*) +{ + go_unreachable(); +} + +int +Traverse::statement(Block*, size_t*, Statement*) +{ + go_unreachable(); +} + +int +Traverse::expression(Expression**) +{ + go_unreachable(); +} + +int +Traverse::type(Type*) +{ + go_unreachable(); +} + +// Class Statement_inserter. + +void +Statement_inserter::insert(Statement* s) +{ + if (this->block_ != NULL) + { + go_assert(this->pindex_ != NULL); + this->block_->insert_statement_before(*this->pindex_, s); + ++*this->pindex_; + } + else if (this->var_ != NULL) + this->var_->add_preinit_statement(this->gogo_, s); + else + go_assert(saw_errors()); +} diff --git a/gcc-4.9/gcc/go/gofrontend/gogo.h b/gcc-4.9/gcc/go/gofrontend/gogo.h new file mode 100644 index 000000000..3f2808781 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/gogo.h @@ -0,0 +1,3030 @@ +// gogo.h -- Go frontend parsed representation. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_GOGO_H +#define GO_GOGO_H + +#include "go-linemap.h" + +class Traverse; +class Statement_inserter; +class Type; +class Type_hash_identical; +class Type_equal; +class Type_identical; +class Typed_identifier; +class Typed_identifier_list; +class Function_type; +class Expression; +class Statement; +class Temporary_statement; +class Block; +class Function; +class Bindings; +class Bindings_snapshot; +class Package; +class Variable; +class Pointer_type; +class Struct_type; +class Struct_field; +class Struct_field_list; +class Array_type; +class Map_type; +class Channel_type; +class Interface_type; +class Named_type; +class Forward_declaration_type; +class Named_object; +class Label; +class Translate_context; +class Backend; +class Export; +class Import; +class Bexpression; +class Bstatement; +class Bblock; +class Bvariable; +class Blabel; +class Bfunction; + +// This file declares the basic classes used to hold the internal +// representation of Go which is built by the parser. + +// An initialization function for an imported package. This is a +// magic function which initializes variables and runs the "init" +// function. + +class Import_init +{ + public: + Import_init(const std::string& package_name, const std::string& init_name, + int priority) + : package_name_(package_name), init_name_(init_name), priority_(priority) + { } + + // The name of the package being imported. + const std::string& + package_name() const + { return this->package_name_; } + + // The name of the package's init function. + const std::string& + init_name() const + { return this->init_name_; } + + // The priority of the initialization function. Functions with a + // lower priority number must be run first. + int + priority() const + { return this->priority_; } + + private: + // The name of the package being imported. + std::string package_name_; + // The name of the package's init function. + std::string init_name_; + // The priority. + int priority_; +}; + +// For sorting purposes. + +inline bool +operator<(const Import_init& i1, const Import_init& i2) +{ + if (i1.priority() < i2.priority()) + return true; + if (i1.priority() > i2.priority()) + return false; + if (i1.package_name() != i2.package_name()) + return i1.package_name() < i2.package_name(); + return i1.init_name() < i2.init_name(); +} + +// The holder for the internal representation of the entire +// compilation unit. + +class Gogo +{ + public: + // Create the IR, passing in the sizes of the types "int" and + // "uintptr" in bits. + Gogo(Backend* backend, Linemap *linemap, int int_type_size, int pointer_size); + + // Get the backend generator. + Backend* + backend() + { return this->backend_; } + + // Get the Location generator. + Linemap* + linemap() + { return this->linemap_; } + + // Get the package name. + const std::string& + package_name() const; + + // Set the package name. + void + set_package_name(const std::string&, Location); + + // Return whether this is the "main" package. + bool + is_main_package() const; + + // If necessary, adjust the name to use for a hidden symbol. We add + // the package name, so that hidden symbols in different packages do + // not collide. + std::string + pack_hidden_name(const std::string& name, bool is_exported) const + { + return (is_exported + ? name + : '.' + this->pkgpath() + '.' + name); + } + + // Unpack a name which may have been hidden. Returns the + // user-visible name of the object. + static std::string + unpack_hidden_name(const std::string& name) + { return name[0] != '.' ? name : name.substr(name.rfind('.') + 1); } + + // Return whether a possibly packed name is hidden. + static bool + is_hidden_name(const std::string& name) + { return name[0] == '.'; } + + // Return the package path of a hidden name. + static std::string + hidden_name_pkgpath(const std::string& name) + { + go_assert(Gogo::is_hidden_name(name)); + return name.substr(1, name.rfind('.') - 1); + } + + // Given a name which may or may not have been hidden, return the + // name to use in an error message. + static std::string + message_name(const std::string& name); + + // Return whether a name is the blank identifier _. + static bool + is_sink_name(const std::string& name) + { + return (name[0] == '.' + && name[name.length() - 1] == '_' + && name[name.length() - 2] == '.'); + } + + // Convert a pkgpath into a string suitable for a symbol + static std::string + pkgpath_for_symbol(const std::string& pkgpath); + + // Return the package path to use for reflect.Type.PkgPath. + const std::string& + pkgpath() const; + + // Return the package path to use for a symbol name. + const std::string& + pkgpath_symbol() const; + + // Set the package path from a command line option. + void + set_pkgpath(const std::string&); + + // Set the prefix from a command line option. + void + set_prefix(const std::string&); + + // Return whether pkgpath was set from a command line option. + bool + 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. + int + package_priority() const; + + // Import a package. FILENAME is the file name argument, LOCAL_NAME + // is the local name to give to the package. If LOCAL_NAME is empty + // the declarations are added to the global scope. + void + import_package(const std::string& filename, const std::string& local_name, + bool is_local_name_exported, Location); + + // Whether we are the global binding level. + bool + in_global_scope() const; + + // Look up a name in the current binding contours. + Named_object* + lookup(const std::string&, Named_object** pfunction) const; + + // Look up a name in the current block. + Named_object* + lookup_in_block(const std::string&) const; + + // Look up a name in the global namespace--the universal scope. + Named_object* + lookup_global(const char*) const; + + // Add a new imported package. REAL_NAME is the real name of the + // package. ALIAS is the alias of the package; this may be the same + // as REAL_NAME. This sets *PADD_TO_GLOBALS if symbols added to + // this package should be added to the global namespace; this is + // true if the alias is ".". LOCATION is the location of the import + // statement. This returns the new package, or NULL on error. + Package* + add_imported_package(const std::string& real_name, const std::string& alias, + bool is_alias_exported, + const std::string& pkgpath, + Location location, + bool* padd_to_globals); + + // Register a package. This package may or may not be imported. + // This returns the Package structure for the package, creating if + // it necessary. + Package* + register_package(const std::string& pkgpath, Location); + + // Start compiling a function. ADD_METHOD_TO_TYPE is true if a + // method function should be added to the type of its receiver. + Named_object* + start_function(const std::string& name, Function_type* type, + bool add_method_to_type, Location); + + // Finish compiling a function. + void + finish_function(Location); + + // Return the current function. + Named_object* + current_function() const; + + // Return the current block. + Block* + current_block(); + + // Start a new block. This is not initially associated with a + // function. + void + start_block(Location); + + // Finish the current block and return it. + Block* + finish_block(Location); + + // Declare an erroneous name. This is used to avoid knock-on errors + // after a parsing error. + Named_object* + add_erroneous_name(const std::string& name); + + // Declare an unknown name. This is used while parsing. The name + // must be resolved by the end of the parse. Unknown names are + // always added at the package level. + Named_object* + add_unknown_name(const std::string& name, Location); + + // Declare a function. + Named_object* + declare_function(const std::string&, Function_type*, Location); + + // Declare a function at the package level. This is used for + // functions generated for a type. + Named_object* + declare_package_function(const std::string&, Function_type*, Location); + + // Add a label. + Label* + add_label_definition(const std::string&, Location); + + // Add a label reference. ISSUE_GOTO_ERRORS is true if we should + // report errors for a goto from the current location to the label + // location. + Label* + add_label_reference(const std::string&, Location, + bool issue_goto_errors); + + // Return a snapshot of the current binding state. + Bindings_snapshot* + bindings_snapshot(Location); + + // Add a statement to the current block. + void + add_statement(Statement*); + + // Add a block to the current block. + void + add_block(Block*, Location); + + // Add a constant. + Named_object* + add_constant(const Typed_identifier&, Expression*, int iota_value); + + // Add a type. + void + add_type(const std::string&, Type*, Location); + + // Add a named type. This is used for builtin types, and to add an + // imported type to the global scope. + void + add_named_type(Named_type*); + + // Declare a type. + Named_object* + declare_type(const std::string&, Location); + + // Declare a type at the package level. This is used when the + // parser sees an unknown name where a type name is required. + Named_object* + declare_package_type(const std::string&, Location); + + // Define a type which was already declared. + void + define_type(Named_object*, Named_type*); + + // Add a variable. + Named_object* + add_variable(const std::string&, Variable*); + + // Add a sink--a reference to the blank identifier _. + Named_object* + add_sink(); + + // Add a type which needs to be verified. This is used for sink + // types, just to give appropriate error messages. + void + add_type_to_verify(Type* type); + + // Add a named object to the current namespace. This is used for + // import . "package". + void + add_named_object(Named_object*); + + // Add an identifier to the list of names seen in the file block. + void + add_file_block_name(const std::string& name, Location location) + { this->file_block_names_[name] = location; } + + // Mark all local variables in current bindings as used. This is + // used when there is a parse error to avoid useless errors. + void + mark_locals_used(); + + // Return a name to use for an error case. This should only be used + // after reporting an error, and is used to avoid useless knockon + // errors. + static std::string + erroneous_name(); + + // Return whether the name indicates an error. + static bool + is_erroneous_name(const std::string&); + + // Return a name to use for a thunk function. A thunk function is + // one we create during the compilation, for a go statement or a + // defer statement or a method expression. + static std::string + thunk_name(); + + // Return whether an object is a thunk. + static bool + is_thunk(const Named_object*); + + // Note that we've seen an interface type. This is used to build + // all required interface method tables. + void + record_interface_type(Interface_type*); + + // Note that we need an initialization function. + void + set_need_init_fn() + { this->need_init_fn_ = true; } + + // Clear out all names in file scope. This is called when we start + // parsing a new file. + void + clear_file_scope(); + + // Record that VAR1 must be initialized after VAR2. This is used + // when VAR2 does not appear in VAR1's INIT or PREINIT. + void + record_var_depends_on(Variable* var1, Named_object* var2) + { + go_assert(this->var_deps_.find(var1) == this->var_deps_.end()); + this->var_deps_[var1] = var2; + } + + // Return the variable that VAR depends on, or NULL if none. + Named_object* + var_depends_on(Variable* var) const + { + Var_deps::const_iterator p = this->var_deps_.find(var); + return p != this->var_deps_.end() ? p->second : NULL; + } + + // Queue up a type-specific function to be written out. This is + // used when a type-specific function is needed when not at the top + // level. + void + queue_specific_type_function(Type* type, Named_type* name, + const std::string& hash_name, + Function_type* hash_fntype, + const std::string& equal_name, + Function_type* equal_fntype); + + // Write out queued specific type functions. + void + write_specific_type_functions(); + + // Whether we are done writing out specific type functions. + bool + specific_type_functions_are_written() const + { return this->specific_type_functions_are_written_; } + + // Traverse the tree. See the Traverse class. + void + traverse(Traverse*); + + // Define the predeclared global names. + void + define_global_names(); + + // Verify and complete all types. + void + verify_types(); + + // Lower the parse tree. + void + lower_parse_tree(); + + // Lower all the statements in a block. + void + lower_block(Named_object* function, Block*); + + // Lower an expression. + void + lower_expression(Named_object* function, Statement_inserter*, Expression**); + + // Lower a constant. + void + lower_constant(Named_object*); + + // Flatten all the statements in a block. + void + flatten_block(Named_object* function, Block*); + + // Flatten an expression. + void + flatten_expression(Named_object* function, Statement_inserter*, Expression**); + + // Create all necessary function descriptors. + void + create_function_descriptors(); + + // Finalize the method lists and build stub methods for named types. + void + finalize_methods(); + + // Work out the types to use for unspecified variables and + // constants. + void + determine_types(); + + // Type check the program. + void + check_types(); + + // Check the types in a single block. This is used for complicated + // go statements. + void + check_types_in_block(Block*); + + // Check for return statements. + void + check_return_statements(); + + // Do all exports. + void + do_exports(); + + // Add an import control function for an imported package to the + // list. + void + add_import_init_fn(const std::string& package_name, + const std::string& init_name, int prio); + + // Turn short-cut operators (&&, ||) into explicit if statements. + void + remove_shortcuts(); + + // Use temporary variables to force order of evaluation. + void + order_evaluations(); + + // Flatten parse tree. + void + flatten(); + + // Build thunks for functions which call recover. + void + build_recover_thunks(); + + // Simplify statements which might use thunks: go and defer + // statements. + void + simplify_thunk_statements(); + + // Dump AST if -fgo-dump-ast is set + void + dump_ast(const char* basename); + + // Convert named types to the backend representation. + void + convert_named_types(); + + // Convert named types in a list of bindings. + void + convert_named_types_in_bindings(Bindings*); + + // True if named types have been converted to the backend + // representation. + bool + named_types_are_converted() const + { return this->named_types_are_converted_; } + + // Write out the global values. + void + write_globals(); + + // Create trees for implicit builtin functions. + void + define_builtin_function_trees(); + + // Build a call to a builtin function. PDECL should point to a NULL + // initialized static pointer which will hold the fndecl. NAME is + // the name of the function. NARGS is the number of arguments. + // RETTYPE is the return type. It is followed by NARGS pairs of + // type and argument (both trees). + static tree + call_builtin(tree* pdecl, Location, const char* name, int nargs, + tree rettype, ...); + + // Build a call to the runtime error function. + Expression* + runtime_error(int code, Location); + + // Build a builtin struct with a list of fields. + static tree + builtin_struct(tree* ptype, const char* struct_name, tree struct_type, + int nfields, ...); + + // Mark a function declaration as a builtin library function. + static void + mark_fndecl_as_builtin_library(tree fndecl); + + // Build a constructor for a slice. SLICE_TYPE_TREE is the type of + // the slice. VALUES points to the values. COUNT is the size, + // CAPACITY is the capacity. If CAPACITY is NULL, it is set to + // COUNT. + static tree + slice_constructor(tree slice_type_tree, tree values, tree count, + tree capacity); + + // Build required interface method tables. + void + build_interface_method_tables(); + + // 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*, Type*, + bool is_pointer); + + // Return a tree which allocate SIZE bytes to hold values of type + // TYPE. + tree + allocate_memory(Type *type, tree size, Location); + + // Return a type to use for pointer to const char. + static tree + const_char_pointer_type_tree(); + + // Build a string constant with the right type. + static tree + string_constant_tree(const std::string&); + + // Build a Go string constant. This returns a pointer to the + // constant. + tree + go_string_constant_tree(const std::string&); + + // Receive a value from a channel. + static tree + receive_from_channel(tree type_tree, tree type_descriptor_tree, tree channel, + Location); + + private: + // During parsing, we keep a stack of functions. Each function on + // the stack is one that we are currently parsing. For each + // function, we keep track of the current stack of blocks. + struct Open_function + { + // The function. + Named_object* function; + // The stack of active blocks in the function. + std::vector blocks; + }; + + // The stack of functions. + typedef std::vector Open_functions; + + // Set up the built-in unsafe package. + void + import_unsafe(const std::string&, bool is_exported, Location); + + // Return the current binding contour. + Bindings* + current_bindings(); + + const Bindings* + current_bindings() const; + + // Get the name of the magic initialization function. + const std::string& + get_init_fn_name(); + + // Get the decl for the magic initialization function. + tree + initialization_function_decl(); + + // Write the magic initialization function. + void + write_initialization_function(tree fndecl, tree init_stmt_list); + + // Initialize imported packages. + void + init_imports(tree*); + + // Register variables with the garbage collector. + void + register_gc_vars(const std::vector&, tree*); + + // Build a pointer to a Go string constant. This returns a pointer + // to the pointer. + tree + ptr_go_string_constant_tree(const std::string&); + + // Type used to map import names to packages. + typedef std::map Imports; + + // Type used to map package names to packages. + typedef std::map Packages; + + // Type used to map variables to the function calls that set them. + // This is used for initialization dependency analysis. + typedef std::map Var_deps; + + // Type used to map identifiers in the file block to the location + // where they were defined. + typedef Unordered_map(std::string, Location) File_block_names; + + // Type used to queue writing a type specific function. + struct Specific_type_function + { + Type* type; + Named_type* name; + std::string hash_name; + Function_type* hash_fntype; + std::string equal_name; + Function_type* equal_fntype; + + Specific_type_function(Type* atype, Named_type* aname, + const std::string& ahash_name, + Function_type* ahash_fntype, + const std::string& aequal_name, + Function_type* aequal_fntype) + : type(atype), name(aname), hash_name(ahash_name), + hash_fntype(ahash_fntype), equal_name(aequal_name), + equal_fntype(aequal_fntype) + { } + }; + + // The backend generator. + Backend* backend_; + // The object used to keep track of file names and line numbers. + Linemap* linemap_; + // The package we are compiling. + Package* package_; + // The list of currently open functions during parsing. + Open_functions functions_; + // The global binding contour. This includes the builtin functions + // and the package we are compiling. + Bindings* globals_; + // The list of names we have seen in the file block. + File_block_names file_block_names_; + // Mapping from import file names to packages. + Imports imports_; + // Whether the magic unsafe package was imported. + bool imported_unsafe_; + // Mapping from package names we have seen to packages. This does + // not include the package we are compiling. + Packages packages_; + // The functions named "init", if there are any. + std::vector init_functions_; + // A mapping from variables to the function calls that initialize + // them, if it is not stored in the variable's init or preinit. + // This is used for dependency analysis. + Var_deps var_deps_; + // Whether we need a magic initialization function. + bool need_init_fn_; + // The name of the magic initialization function. + std::string init_fn_name_; + // A list of import control variables for packages that we import. + std::set imported_init_fns_; + // The package path used for reflection data. + std::string pkgpath_; + // The package path to use for a symbol name. + std::string pkgpath_symbol_; + // The prefix to use for symbols, from the -fgo-prefix option. + std::string prefix_; + // Whether pkgpath_ has been set. + bool pkgpath_set_; + // Whether an explicit package path was set by -fgo-pkgpath. + 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 verify_types_; + // A list of interface types defined while parsing. + std::vector interface_types_; + // Type specific functions to write out. + std::vector specific_type_functions_; + // Whether we are done writing out specific type functions. + bool specific_type_functions_are_written_; + // Whether named types have been converted. + bool named_types_are_converted_; +}; + +// A block of statements. + +class Block +{ + public: + Block(Block* enclosing, Location); + + // Return the enclosing block. + const Block* + enclosing() const + { return this->enclosing_; } + + // Return the bindings of the block. + Bindings* + bindings() + { return this->bindings_; } + + const Bindings* + bindings() const + { return this->bindings_; } + + // Look at the block's statements. + const std::vector* + statements() const + { return &this->statements_; } + + // Return the start location. This is normally the location of the + // left curly brace which starts the block. + Location + start_location() const + { return this->start_location_; } + + // Return the end location. This is normally the location of the + // right curly brace which ends the block. + Location + end_location() const + { return this->end_location_; } + + // Add a statement to the block. + void + add_statement(Statement*); + + // Add a statement to the front of the block. + void + add_statement_at_front(Statement*); + + // Replace a statement in a block. + void + replace_statement(size_t index, Statement*); + + // Add a Statement before statement number INDEX. + void + insert_statement_before(size_t index, Statement*); + + // Add a Statement after statement number INDEX. + void + insert_statement_after(size_t index, Statement*); + + // Set the end location of the block. + void + set_end_location(Location location) + { this->end_location_ = location; } + + // Traverse the tree. + int + traverse(Traverse*); + + // Set final types for unspecified variables and constants. + void + determine_types(); + + // Return true if execution of this block may fall through to the + // next block. + bool + may_fall_through() const; + + // Convert the block to the backend representation. + Bblock* + get_backend(Translate_context*); + + // Iterate over statements. + + typedef std::vector::iterator iterator; + + iterator + begin() + { return this->statements_.begin(); } + + iterator + end() + { return this->statements_.end(); } + + private: + // Enclosing block. + Block* enclosing_; + // Statements in the block. + std::vector statements_; + // Binding contour. + Bindings* bindings_; + // Location of start of block. + Location start_location_; + // Location of end of block. + Location end_location_; +}; + +// A function. + +class Function +{ + public: + Function(Function_type* type, Function*, Block*, Location); + + // Return the function's type. + Function_type* + type() const + { return this->type_; } + + // Return the enclosing function if there is one. + Function* + enclosing() + { return this->enclosing_; } + + // Set the enclosing function. This is used when building thunks + // for functions which call recover. + void + set_enclosing(Function* enclosing) + { + go_assert(this->enclosing_ == NULL); + this->enclosing_ = enclosing; + } + + // The result variables. + typedef std::vector Results; + + // Create the result variables in the outer block. + void + create_result_variables(Gogo*); + + // Update the named result variables when cloning a function which + // calls recover. + void + update_result_variables(); + + // Return the result variables. + Results* + result_variables() + { return this->results_; } + + bool + is_sink() const + { return this->is_sink_; } + + void + set_is_sink() + { this->is_sink_ = true; } + + // Whether the result variables have names. + bool + results_are_named() const + { return this->results_are_named_; } + + // Whether this method should not be included in the type + // descriptor. + bool + nointerface() const + { + go_assert(this->is_method()); + return this->nointerface_; + } + + // Record that this method should not be included in the type + // descriptor. + void + set_nointerface() + { + go_assert(this->is_method()); + this->nointerface_ = true; + } + + // Record that this function is a stub method created for an unnamed + // type. + void + set_is_unnamed_type_stub_method() + { + go_assert(this->is_method()); + this->is_unnamed_type_stub_method_ = true; + } + + // Add a new field to the closure variable. + void + add_closure_field(Named_object* var, Location loc) + { this->closure_fields_.push_back(std::make_pair(var, loc)); } + + // Whether this function needs a closure. + bool + needs_closure() const + { return !this->closure_fields_.empty(); } + + // Return the closure variable, creating it if necessary. This is + // passed to the function as a static chain parameter. + Named_object* + closure_var(); + + // Set the closure variable. This is used when building thunks for + // functions which call recover. + void + set_closure_var(Named_object* v) + { + go_assert(this->closure_var_ == NULL); + this->closure_var_ = v; + } + + // Return the variable for a reference to field INDEX in the closure + // variable. + Named_object* + enclosing_var(unsigned int index) + { + go_assert(index < this->closure_fields_.size()); + return closure_fields_[index].first; + } + + // Set the type of the closure variable if there is one. + void + set_closure_type(); + + // Get the block of statements associated with the function. + Block* + block() const + { return this->block_; } + + // Get the location of the start of the function. + Location + location() const + { return this->location_; } + + // Return whether this function is actually a method. + bool + is_method() const; + + // Add a label definition to the function. + Label* + add_label_definition(Gogo*, const std::string& label_name, Location); + + // Add a label reference to a function. ISSUE_GOTO_ERRORS is true + // if we should report errors for a goto from the current location + // to the label location. + Label* + add_label_reference(Gogo*, const std::string& label_name, + Location, bool issue_goto_errors); + + // Warn about labels that are defined but not used. + 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 + { return this->calls_recover_; } + + // Record that this function calls the predeclared recover function. + // This is set during the lowering pass. + void + set_calls_recover() + { this->calls_recover_ = true; } + + // Whether this is a recover thunk function. + bool + is_recover_thunk() const + { return this->is_recover_thunk_; } + + // Record that this is a thunk built for a function which calls + // recover. + void + set_is_recover_thunk() + { this->is_recover_thunk_ = true; } + + // Whether this function already has a recover thunk. + bool + has_recover_thunk() const + { return this->has_recover_thunk_; } + + // Record that this function already has a recover thunk. + void + set_has_recover_thunk() + { this->has_recover_thunk_ = true; } + + // Mark the function as going into a unique section. + void + set_in_unique_section() + { this->in_unique_section_ = true; } + + // Swap with another function. Used only for the thunk which calls + // recover. + void + swap_for_recover(Function *); + + // Traverse the tree. + int + traverse(Traverse*); + + // Determine types in the function. + void + determine_types(); + + // Return an expression for the function descriptor, given the named + // object for this function. This may only be called for functions + // without a closure. This will be an immutable struct with one + // field that points to the function's code. + Expression* + descriptor(Gogo*, Named_object*); + + // Set the descriptor for this function. This is used when a + // function declaration is followed by a function definition. + void + set_descriptor(Expression* descriptor) + { + go_assert(this->descriptor_ == NULL); + this->descriptor_ = descriptor; + } + + // Return the backend representation. + Bfunction* + get_or_make_decl(Gogo*, Named_object*); + + // Return the function's decl after it has been built. + tree + get_decl() const; + + // Set the function decl to hold a tree of the function code. + void + build_tree(Gogo*, Named_object*); + + // Get the value to return when not explicitly specified. May also + // add statements to execute first to STMT_LIST. + tree + return_value(Gogo*, Named_object*, Location, tree* stmt_list) const; + + // Get a tree for the variable holding the defer stack. + Expression* + defer_stack(Location); + + // Export the function. + void + export_func(Export*, const std::string& name) const; + + // Export a function with a type. + static void + export_func_with_type(Export*, const std::string& name, + const Function_type*); + + // Import a function. + static void + import_func(Import*, std::string* pname, Typed_identifier** receiver, + Typed_identifier_list** pparameters, + Typed_identifier_list** presults, bool* is_varargs); + + private: + // Type for mapping from label names to Label objects. + typedef Unordered_map(std::string, Label*) Labels; + + tree + make_receiver_parm_decl(Gogo*, Named_object*, tree); + + tree + copy_parm_to_heap(Gogo*, Named_object*, tree); + + void + build_defer_wrapper(Gogo*, Named_object*, tree*, tree*); + + typedef std::vector > Closure_fields; + + // The function's type. + Function_type* type_; + // The enclosing function. This is NULL when there isn't one, which + // is the normal case. + Function* enclosing_; + // The result variables, if any. + Results* results_; + // If there is a closure, this is the list of variables which appear + // in the closure. This is created by the parser, and then resolved + // to a real type when we lower parse trees. + Closure_fields closure_fields_; + // The closure variable, passed as a parameter using the static + // chain parameter. Normally NULL. + Named_object* closure_var_; + // The outer block of statements in the function. + Block* block_; + // The source location of the start of the 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 descriptor, if any. + Expression* descriptor_; + // The function decl. + Bfunction* fndecl_; + // The defer stack variable. A pointer to this variable is used to + // distinguish the defer stack for one function from another. This + // is NULL unless we actually need a defer stack. + Temporary_statement* defer_stack_; + // True if this function is sink-named. No code is generated. + bool is_sink_ : 1; + // True if the result variables are named. + bool results_are_named_ : 1; + // True if this method should not be included in the type descriptor. + bool nointerface_ : 1; + // True if this function is a stub method created for an unnamed + // type. + bool is_unnamed_type_stub_method_ : 1; + // True if this function calls the predeclared recover function. + bool calls_recover_ : 1; + // True if this a thunk built for a function which calls recover. + bool is_recover_thunk_ : 1; + // True if this function already has a recover thunk. + bool has_recover_thunk_ : 1; + // True if this function should be put in a unique section. This is + // turned on for field tracking. + bool in_unique_section_ : 1; +}; + +// A snapshot of the current binding state. + +class Bindings_snapshot +{ + public: + Bindings_snapshot(const Block*, Location); + + // Report any errors appropriate for a goto from the current binding + // state of B to this one. + void + check_goto_from(const Block* b, Location); + + // Report any errors appropriate for a goto from this binding state + // to the current state of B. + void + check_goto_to(const Block* b); + + private: + bool + check_goto_block(Location, const Block*, const Block*, size_t*); + + void + check_goto_defs(Location, const Block*, size_t, size_t); + + // The current block. + const Block* block_; + // The number of names currently defined in each open block. + // Element 0 is this->block_, element 1 is + // this->block_->enclosing(), etc. + std::vector counts_; + // The location where this snapshot was taken. + Location location_; +}; + +// A function declaration. + +class Function_declaration +{ + public: + Function_declaration(Function_type* fntype, Location location) + : fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL), + fndecl_(NULL) + { } + + Function_type* + type() const + { return this->fntype_; } + + Location + location() const + { return this->location_; } + + const std::string& + asm_name() const + { return this->asm_name_; } + + // Set the assembler name. + void + set_asm_name(const std::string& asm_name) + { this->asm_name_ = asm_name; } + + // Return an expression for the function descriptor, given the named + // object for this function. This may only be called for functions + // without a closure. This will be an immutable struct with one + // field that points to the function's code. + Expression* + descriptor(Gogo*, Named_object*); + + // Return true if we have created a descriptor for this declaration. + bool + has_descriptor() const + { return this->descriptor_ != NULL; } + + // Return a backend representation. + Bfunction* + get_or_make_decl(Gogo*, Named_object*); + + // If there is a descriptor, build it into the backend + // representation. + void + build_backend_descriptor(Gogo*); + + // Export a function declaration. + void + export_func(Export* exp, const std::string& name) const + { Function::export_func_with_type(exp, name, this->fntype_); } + + private: + // The type of the function. + Function_type* fntype_; + // The location of the declaration. + Location location_; + // The assembler name: this is the name to use in references to the + // function. This is normally empty. + std::string asm_name_; + // The function descriptor, if any. + Expression* descriptor_; + // The function decl if needed. + Bfunction* fndecl_; +}; + +// A variable. + +class Variable +{ + public: + Variable(Type*, Expression*, bool is_global, bool is_parameter, + bool is_receiver, Location); + + // Get the type of the variable. + Type* + type(); + + Type* + type() const; + + // Return whether the type is defined yet. + bool + has_type() const; + + // Get the initial value. + Expression* + init() const + { return this->init_; } + + // Return whether there are any preinit statements. + bool + has_pre_init() const + { return this->preinit_ != NULL; } + + // Return the preinit statements if any. + Block* + preinit() const + { return this->preinit_; } + + // Return whether this is a global variable. + bool + is_global() const + { return this->is_global_; } + + // Return whether this is a function parameter. + bool + is_parameter() const + { return this->is_parameter_; } + + // Return whether this is the receiver parameter of a method. + bool + is_receiver() const + { return this->is_receiver_; } + + // Change this parameter to be a receiver. This is used when + // creating the thunks created for functions which call recover. + void + set_is_receiver() + { + go_assert(this->is_parameter_); + this->is_receiver_ = true; + } + + // Change this parameter to not be a receiver. This is used when + // creating the thunks created for functions which call recover. + void + set_is_not_receiver() + { + go_assert(this->is_parameter_); + this->is_receiver_ = false; + } + + // Return whether this is the varargs parameter of a function. + bool + is_varargs_parameter() const + { return this->is_varargs_parameter_; } + + // Whether this variable's address is taken. + bool + is_address_taken() const + { return this->is_address_taken_; } + + // Whether this variable should live in the heap. + bool + is_in_heap() const + { return this->is_address_taken_ && !this->is_global_; } + + // Note that something takes the address of this variable. + void + set_address_taken() + { this->is_address_taken_ = true; } + + // Return whether the address is taken but does not escape. + bool + is_non_escaping_address_taken() const + { return this->is_non_escaping_address_taken_; } + + // Note that something takes the address of this variable such that + // the address does not escape the function. + void + set_non_escaping_address_taken() + { this->is_non_escaping_address_taken_ = true; } + + // Get the source location of the variable's declaration. + Location + location() const + { return this->location_; } + + // Record that this is the varargs parameter of a function. + void + set_is_varargs_parameter() + { + go_assert(this->is_parameter_); + this->is_varargs_parameter_ = true; + } + + // Return whether the variable has been used. + bool + is_used() const + { return this->is_used_; } + + // Mark that the variable has been used. + void + set_is_used() + { this->is_used_ = true; } + + // Clear the initial value; used for error handling. + void + clear_init() + { this->init_ = NULL; } + + // Set the initial value; used for converting shortcuts. + void + set_init(Expression* init) + { this->init_ = init; } + + // Get the preinit block, a block of statements to be run before the + // initialization expression. + Block* + preinit_block(Gogo*); + + // Add a statement to be run before the initialization expression. + // This is only used for global variables. + void + add_preinit_statement(Gogo*, Statement*); + + // Lower the initialization expression after parsing is complete. + void + lower_init_expression(Gogo*, Named_object*, Statement_inserter*); + + // Flatten the initialization expression after ordering evaluations. + void + flatten_init_expression(Gogo*, Named_object*, Statement_inserter*); + + // A special case: the init value is used only to determine the + // type. This is used if the variable is defined using := with the + // comma-ok form of a map index or a receive expression. The init + // value is actually the map index expression or receive expression. + // We use this because we may not know the right type at parse time. + void + set_type_from_init_tuple() + { this->type_from_init_tuple_ = true; } + + // Another special case: the init value is used only to determine + // the type. This is used if the variable is defined using := with + // a range clause. The init value is the range expression. The + // type of the variable is the index type of the range expression + // (i.e., the first value returned by a range). + void + set_type_from_range_index() + { this->type_from_range_index_ = true; } + + // Another special case: like set_type_from_range_index, but the + // type is the value type of the range expression (i.e., the second + // value returned by a range). + void + set_type_from_range_value() + { this->type_from_range_value_ = true; } + + // Another special case: the init value is used only to determine + // the type. This is used if the variable is defined using := with + // a case in a select statement. The init value is the channel. + // The type of the variable is the channel's element type. + void + set_type_from_chan_element() + { this->type_from_chan_element_ = true; } + + // After we lower the select statement, we once again set the type + // from the initialization expression. + void + clear_type_from_chan_element() + { + go_assert(this->type_from_chan_element_); + this->type_from_chan_element_ = false; + } + + // Note that this variable was created for a type switch clause. + void + set_is_type_switch_var() + { this->is_type_switch_var_ = true; } + + // Mark the variable as going into a unique section. + void + set_in_unique_section() + { + go_assert(this->is_global_); + this->in_unique_section_ = true; + } + + // Traverse the initializer expression. + int + traverse_expression(Traverse*, unsigned int traverse_mask); + + // Determine the type of the variable if necessary. + void + determine_type(); + + // Get the backend representation of the variable. + Bvariable* + get_backend_variable(Gogo*, Named_object*, const Package*, + const std::string&); + + // Get the initial value of the variable as a tree. This may only + // be called if has_pre_init() returns false. + tree + get_init_tree(Gogo*, Named_object* function); + + // Return a series of statements which sets the value of the + // variable in DECL. This should only be called is has_pre_init() + // returns true. DECL may be NULL for a sink variable. + tree + get_init_block(Gogo*, Named_object* function, tree decl); + + // Export the variable. + void + export_var(Export*, const std::string& name) const; + + // Import a variable. + static void + import_var(Import*, std::string* pname, Type** ptype); + + private: + // The type of a tuple. + Type* + type_from_tuple(Expression*, bool) const; + + // The type of a range. + Type* + type_from_range(Expression*, bool, bool) const; + + // The element type of a channel. + Type* + type_from_chan_element(Expression*, bool) const; + + // The variable's type. This may be NULL if the type is set from + // the expression. + Type* type_; + // The initial value. This may be NULL if the variable should be + // initialized to the default value for the type. + Expression* init_; + // Statements to run before the init statement. + Block* preinit_; + // Location of variable definition. + Location location_; + // Backend representation. + Bvariable* backend_; + // Whether this is a global variable. + bool is_global_ : 1; + // Whether this is a function parameter. + bool is_parameter_ : 1; + // Whether this is the receiver parameter of a method. + bool is_receiver_ : 1; + // Whether this is the varargs parameter of a function. + bool is_varargs_parameter_ : 1; + // Whether this variable is ever referenced. + bool is_used_ : 1; + // Whether something takes the address of this variable. For a + // local variable this implies that the variable has to be on the + // heap. + bool is_address_taken_ : 1; + // Whether something takes the address of this variable such that + // the address does not escape the function. + bool is_non_escaping_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 we have flattened the initialization expression. + bool init_is_flattened_ : 1; + // True if init is a tuple used to set the type. + bool type_from_init_tuple_ : 1; + // True if init is a range clause and the type is the index type. + bool type_from_range_index_ : 1; + // True if init is a range clause and the type is the value type. + bool type_from_range_value_ : 1; + // True if init is a channel and the type is the channel's element type. + bool type_from_chan_element_ : 1; + // True if this is a variable created for a type switch case. + bool is_type_switch_var_ : 1; + // True if we have determined types. + bool determined_type_ : 1; + // True if this variable should be put in a unique section. This is + // used for field tracking. + bool in_unique_section_ : 1; +}; + +// A variable which is really the name for a function return value, or +// part of one. + +class Result_variable +{ + public: + Result_variable(Type* type, Function* function, int index, + Location location) + : type_(type), function_(function), index_(index), location_(location), + backend_(NULL), is_address_taken_(false), + is_non_escaping_address_taken_(false) + { } + + // Get the type of the result variable. + Type* + type() const + { return this->type_; } + + // Get the function that this is associated with. + Function* + function() const + { return this->function_; } + + // Index in the list of function results. + int + index() const + { return this->index_; } + + // The location of the variable definition. + Location + location() const + { return this->location_; } + + // Whether this variable's address is taken. + bool + is_address_taken() const + { return this->is_address_taken_; } + + // Note that something takes the address of this variable. + void + set_address_taken() + { this->is_address_taken_ = true; } + + // Return whether the address is taken but does not escape. + bool + is_non_escaping_address_taken() const + { return this->is_non_escaping_address_taken_; } + + // Note that something takes the address of this variable such that + // the address does not escape the function. + void + set_non_escaping_address_taken() + { this->is_non_escaping_address_taken_ = true; } + + // Whether this variable should live in the heap. + bool + is_in_heap() const + { return this->is_address_taken_; } + + // Set the function. This is used when cloning functions which call + // recover. + void + set_function(Function* function) + { this->function_ = function; } + + // Get the backend representation of the variable. + Bvariable* + get_backend_variable(Gogo*, Named_object*, const std::string&); + + private: + // Type of result variable. + Type* type_; + // Function with which this is associated. + Function* function_; + // Index in list of results. + int index_; + // Where the result variable is defined. + Location location_; + // Backend representation. + Bvariable* backend_; + // Whether something takes the address of this variable. + bool is_address_taken_; + // Whether something takes the address of this variable such that + // the address does not escape the function. + bool is_non_escaping_address_taken_; +}; + +// The value we keep for a named constant. This lets us hold a type +// and an expression. + +class Named_constant +{ + public: + Named_constant(Type* type, Expression* expr, int iota_value, + Location location) + : type_(type), expr_(expr), iota_value_(iota_value), location_(location), + lowering_(false), is_sink_(false) + { } + + Type* + type() const + { return this->type_; } + + Expression* + expr() const + { return this->expr_; } + + int + iota_value() const + { return this->iota_value_; } + + Location + location() const + { return this->location_; } + + // Whether we are lowering. + bool + lowering() const + { return this->lowering_; } + + // Set that we are lowering. + void + set_lowering() + { this->lowering_ = true; } + + // We are no longer lowering. + void + clear_lowering() + { this->lowering_ = false; } + + bool + is_sink() const + { return this->is_sink_; } + + void + set_is_sink() + { this->is_sink_ = true; } + + // Traverse the expression. + int + traverse_expression(Traverse*); + + // Determine the type of the constant if necessary. + void + determine_type(); + + // Indicate that we found and reported an error for this constant. + void + set_error(); + + // Export the constant. + void + export_const(Export*, const std::string& name) const; + + // Import a constant. + static void + import_const(Import*, std::string*, Type**, Expression**); + + private: + // The type of the constant. + Type* type_; + // The expression for the constant. + Expression* expr_; + // If the predeclared constant iota is used in EXPR_, this is the + // value it will have. We do this because at parse time we don't + // know whether the name "iota" will refer to the predeclared + // constant or to something else. We put in the right value in when + // we lower. + int iota_value_; + // The location of the definition. + Location location_; + // Whether we are currently lowering this constant. + bool lowering_; + // Whether this constant is blank named and needs only type checking. + bool is_sink_; +}; + +// A type declaration. + +class Type_declaration +{ + public: + Type_declaration(Location location) + : location_(location), in_function_(NULL), in_function_index_(0), + methods_(), issued_warning_(false) + { } + + // Return the location. + Location + location() const + { return this->location_; } + + // Return the function in which this type is declared. This will + // return NULL for a type declared in global scope. + Named_object* + 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, 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. + Named_object* + add_method(const std::string& name, Function* function); + + // Add a method declaration to this type. + Named_object* + add_method_declaration(const std::string& name, Package*, + Function_type* type, Location location); + + // Return whether any methods were defined. + bool + has_methods() const; + + // Return the methods. + const std::vector* + methods() const + { return &this->methods_; } + + // Define methods when the real type is known. + void + define_methods(Named_type*); + + // This is called if we are trying to use this type. It returns + // true if we should issue a warning. + bool + using_type(); + + private: + // The location of the type declaration. + Location location_; + // 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. + std::vector methods_; + // True if we have issued a warning about a use of this type + // declaration when it is undefined. + bool issued_warning_; +}; + +// An unknown object. These are created by the parser for forward +// references to names which have not been seen before. In a correct +// program, these will always point to a real definition by the end of +// the parse. Because they point to another Named_object, these may +// only be referenced by Unknown_expression objects. + +class Unknown_name +{ + public: + Unknown_name(Location location) + : location_(location), real_named_object_(NULL) + { } + + // Return the location where this name was first seen. + Location + location() const + { return this->location_; } + + // Return the real named object that this points to, or NULL if it + // was never resolved. + Named_object* + real_named_object() const + { return this->real_named_object_; } + + // Set the real named object that this points to. + void + set_real_named_object(Named_object* no); + + private: + // The location where this name was first seen. + Location location_; + // The real named object when it is known. + Named_object* + real_named_object_; +}; + +// A named object named. This is the result of a declaration. We +// don't use a superclass because they all have to be handled +// differently. + +class Named_object +{ + public: + enum Classification + { + // An uninitialized Named_object. We should never see this. + NAMED_OBJECT_UNINITIALIZED, + // An erroneous name. This indicates a parse error, to avoid + // later errors about undefined references. + NAMED_OBJECT_ERRONEOUS, + // An unknown name. This is used for forward references. In a + // correct program, these will all be resolved by the end of the + // parse. + NAMED_OBJECT_UNKNOWN, + // A const. + NAMED_OBJECT_CONST, + // A type. + NAMED_OBJECT_TYPE, + // A forward type declaration. + NAMED_OBJECT_TYPE_DECLARATION, + // A var. + NAMED_OBJECT_VAR, + // A result variable in a function. + NAMED_OBJECT_RESULT_VAR, + // The blank identifier--the special variable named _. + NAMED_OBJECT_SINK, + // A func. + NAMED_OBJECT_FUNC, + // A forward func declaration. + NAMED_OBJECT_FUNC_DECLARATION, + // A package. + NAMED_OBJECT_PACKAGE + }; + + // Return the classification. + Classification + classification() const + { return this->classification_; } + + // Classifiers. + + bool + is_erroneous() const + { return this->classification_ == NAMED_OBJECT_ERRONEOUS; } + + bool + is_unknown() const + { return this->classification_ == NAMED_OBJECT_UNKNOWN; } + + bool + is_const() const + { return this->classification_ == NAMED_OBJECT_CONST; } + + bool + is_type() const + { return this->classification_ == NAMED_OBJECT_TYPE; } + + bool + is_type_declaration() const + { return this->classification_ == NAMED_OBJECT_TYPE_DECLARATION; } + + bool + is_variable() const + { return this->classification_ == NAMED_OBJECT_VAR; } + + bool + is_result_variable() const + { return this->classification_ == NAMED_OBJECT_RESULT_VAR; } + + bool + is_sink() const + { return this->classification_ == NAMED_OBJECT_SINK; } + + bool + is_function() const + { return this->classification_ == NAMED_OBJECT_FUNC; } + + bool + is_function_declaration() const + { return this->classification_ == NAMED_OBJECT_FUNC_DECLARATION; } + + bool + is_package() const + { return this->classification_ == NAMED_OBJECT_PACKAGE; } + + // Creators. + + static Named_object* + make_erroneous_name(const std::string& name) + { return new Named_object(name, NULL, NAMED_OBJECT_ERRONEOUS); } + + static Named_object* + make_unknown_name(const std::string& name, Location); + + static Named_object* + make_constant(const Typed_identifier&, const Package*, Expression*, + int iota_value); + + static Named_object* + make_type(const std::string&, const Package*, Type*, Location); + + static Named_object* + make_type_declaration(const std::string&, const Package*, Location); + + static Named_object* + make_variable(const std::string&, const Package*, Variable*); + + static Named_object* + make_result_variable(const std::string&, Result_variable*); + + static Named_object* + make_sink(); + + static Named_object* + make_function(const std::string&, const Package*, Function*); + + static Named_object* + make_function_declaration(const std::string&, const Package*, Function_type*, + Location); + + static Named_object* + make_package(const std::string& alias, Package* package); + + // Getters. + + Unknown_name* + unknown_value() + { + go_assert(this->classification_ == NAMED_OBJECT_UNKNOWN); + return this->u_.unknown_value; + } + + const Unknown_name* + unknown_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_UNKNOWN); + return this->u_.unknown_value; + } + + Named_constant* + const_value() + { + go_assert(this->classification_ == NAMED_OBJECT_CONST); + return this->u_.const_value; + } + + const Named_constant* + const_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_CONST); + return this->u_.const_value; + } + + Named_type* + type_value() + { + go_assert(this->classification_ == NAMED_OBJECT_TYPE); + return this->u_.type_value; + } + + const Named_type* + type_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_TYPE); + return this->u_.type_value; + } + + Type_declaration* + type_declaration_value() + { + go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION); + return this->u_.type_declaration; + } + + const Type_declaration* + type_declaration_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION); + return this->u_.type_declaration; + } + + Variable* + var_value() + { + go_assert(this->classification_ == NAMED_OBJECT_VAR); + return this->u_.var_value; + } + + const Variable* + var_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_VAR); + return this->u_.var_value; + } + + Result_variable* + result_var_value() + { + go_assert(this->classification_ == NAMED_OBJECT_RESULT_VAR); + return this->u_.result_var_value; + } + + const Result_variable* + result_var_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_RESULT_VAR); + return this->u_.result_var_value; + } + + Function* + func_value() + { + go_assert(this->classification_ == NAMED_OBJECT_FUNC); + return this->u_.func_value; + } + + const Function* + func_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_FUNC); + return this->u_.func_value; + } + + Function_declaration* + func_declaration_value() + { + go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION); + return this->u_.func_declaration_value; + } + + const Function_declaration* + func_declaration_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION); + return this->u_.func_declaration_value; + } + + Package* + package_value() + { + go_assert(this->classification_ == NAMED_OBJECT_PACKAGE); + return this->u_.package_value; + } + + const Package* + package_value() const + { + go_assert(this->classification_ == NAMED_OBJECT_PACKAGE); + return this->u_.package_value; + } + + const std::string& + name() const + { return this->name_; } + + // Return the name to use in an error message. The difference is + // that if this Named_object is defined in a different package, this + // will return PACKAGE.NAME. + std::string + message_name() const; + + const Package* + package() const + { return this->package_; } + + // Resolve an unknown value if possible. This returns the same + // Named_object or a new one. + Named_object* + resolve() + { + Named_object* ret = this; + if (this->is_unknown()) + { + Named_object* r = this->unknown_value()->real_named_object(); + if (r != NULL) + ret = r; + } + return ret; + } + + const Named_object* + resolve() const + { + const Named_object* ret = this; + if (this->is_unknown()) + { + const Named_object* r = this->unknown_value()->real_named_object(); + if (r != NULL) + ret = r; + } + return ret; + } + + // The location where this object was defined or referenced. + Location + location() const; + + // Convert a variable to the backend representation. + Bvariable* + get_backend_variable(Gogo*, Named_object* function); + + // Return the external identifier for this object. + std::string + get_id(Gogo*); + + // Return a tree representing this object. + tree + get_tree(Gogo*, Named_object* function); + + // Define a type declaration. + void + set_type_value(Named_type*); + + // Define a function declaration. + void + set_function_value(Function*); + + // Declare an unknown name as a type declaration. + void + declare_as_type(); + + // Export this object. + void + export_named_object(Export*) const; + + private: + Named_object(const std::string&, const Package*, Classification); + + // The name of the object. + std::string name_; + // The package that this object is in. This is NULL if it is in the + // file we are compiling. + const Package* package_; + // The type of object this is. + Classification classification_; + // The real data. + union + { + Unknown_name* unknown_value; + Named_constant* const_value; + Named_type* type_value; + Type_declaration* type_declaration; + Variable* var_value; + Result_variable* result_var_value; + Function* func_value; + Function_declaration* func_declaration_value; + Package* package_value; + } u_; + // The DECL tree for this object if we have already converted it. + tree tree_; +}; + +// A binding contour. This binds names to objects. + +class Bindings +{ + public: + // Type for mapping from names to objects. + typedef Unordered_map(std::string, Named_object*) Contour; + + Bindings(Bindings* enclosing); + + // Add an erroneous name. + Named_object* + add_erroneous_name(const std::string& name) + { return this->add_named_object(Named_object::make_erroneous_name(name)); } + + // Add an unknown name. + Named_object* + add_unknown_name(const std::string& name, Location location) + { + return this->add_named_object(Named_object::make_unknown_name(name, + location)); + } + + // Add a constant. + Named_object* + add_constant(const Typed_identifier& tid, const Package* package, + Expression* expr, int iota_value) + { + return this->add_named_object(Named_object::make_constant(tid, package, + expr, + iota_value)); + } + + // Add a type. + Named_object* + add_type(const std::string& name, const Package* package, Type* type, + Location location) + { + return this->add_named_object(Named_object::make_type(name, package, type, + location)); + } + + // Add a named type. This is used for builtin types, and to add an + // imported type to the global scope. + Named_object* + add_named_type(Named_type* named_type); + + // Add a type declaration. + Named_object* + add_type_declaration(const std::string& name, const Package* package, + Location location) + { + Named_object* no = Named_object::make_type_declaration(name, package, + location); + return this->add_named_object(no); + } + + // Add a variable. + Named_object* + add_variable(const std::string& name, const Package* package, + Variable* variable) + { + return this->add_named_object(Named_object::make_variable(name, package, + variable)); + } + + // Add a result variable. + Named_object* + add_result_variable(const std::string& name, Result_variable* result) + { + return this->add_named_object(Named_object::make_result_variable(name, + result)); + } + + // Add a function. + Named_object* + add_function(const std::string& name, const Package*, Function* function); + + // Add a function declaration. + Named_object* + add_function_declaration(const std::string& name, const Package* package, + Function_type* type, Location location); + + // Add a package. The location is the location of the import + // statement. + Named_object* + add_package(const std::string& alias, Package* package) + { + Named_object* no = Named_object::make_package(alias, package); + return this->add_named_object(no); + } + + // Define a type which was already declared. + void + define_type(Named_object*, Named_type*); + + // Add a method to the list of objects. This is not added to the + // lookup table. + void + add_method(Named_object*); + + // Add a named object to this binding. + Named_object* + add_named_object(Named_object* no) + { return this->add_named_object_to_contour(&this->bindings_, no); } + + // Clear all names in file scope from the bindings. + void + clear_file_scope(Gogo*); + + // Look up a name in this binding contour and in any enclosing + // binding contours. This returns NULL if the name is not found. + Named_object* + lookup(const std::string&) const; + + // Look up a name in this binding contour without looking in any + // enclosing binding contours. Returns NULL if the name is not found. + Named_object* + lookup_local(const std::string&) const; + + // Remove a name. + void + remove_binding(Named_object*); + + // Mark all variables as used. This is used for some types of parse + // error. + void + mark_locals_used(); + + // Traverse the tree. See the Traverse class. + int + traverse(Traverse*, bool is_global); + + // Iterate over definitions. This does not include things which + // were only declared. + + typedef std::vector::const_iterator + const_definitions_iterator; + + const_definitions_iterator + begin_definitions() const + { return this->named_objects_.begin(); } + + const_definitions_iterator + end_definitions() const + { return this->named_objects_.end(); } + + // Return the number of definitions. + size_t + size_definitions() const + { return this->named_objects_.size(); } + + // Return whether there are no definitions. + bool + empty_definitions() const + { return this->named_objects_.empty(); } + + // Iterate over declarations. This is everything that has been + // declared, which includes everything which has been defined. + + typedef Contour::const_iterator const_declarations_iterator; + + const_declarations_iterator + begin_declarations() const + { return this->bindings_.begin(); } + + const_declarations_iterator + end_declarations() const + { return this->bindings_.end(); } + + // Return the number of declarations. + size_t + size_declarations() const + { return this->bindings_.size(); } + + // Return whether there are no declarations. + bool + empty_declarations() const + { return this->bindings_.empty(); } + + // Return the first declaration. + Named_object* + first_declaration() + { return this->bindings_.empty() ? NULL : this->bindings_.begin()->second; } + + private: + Named_object* + add_named_object_to_contour(Contour*, Named_object*); + + Named_object* + new_definition(Named_object*, Named_object*); + + // Enclosing bindings. + Bindings* enclosing_; + // The list of objects. + std::vector named_objects_; + // The mapping from names to objects. + Contour bindings_; +}; + +// A label. + +class Label +{ + public: + Label(const std::string& name) + : name_(name), location_(Linemap::unknown_location()), snapshot_(NULL), + refs_(), is_used_(false), blabel_(NULL) + { } + + // Return the label's name. + const std::string& + name() const + { return this->name_; } + + // Return whether the label has been defined. + bool + is_defined() const + { return !Linemap::is_unknown_location(this->location_); } + + // Return whether the label has been used. + bool + is_used() const + { return this->is_used_; } + + // Record that the label is used. + void + set_is_used() + { this->is_used_ = true; } + + // Return the location of the definition. + Location + location() const + { return this->location_; } + + // Return the bindings snapshot. + Bindings_snapshot* + snapshot() const + { return this->snapshot_; } + + // Add a snapshot of a goto which refers to this label. + void + add_snapshot_ref(Bindings_snapshot* snapshot) + { + go_assert(Linemap::is_unknown_location(this->location_)); + this->refs_.push_back(snapshot); + } + + // Return the list of snapshots of goto statements which refer to + // this label. + const std::vector& + refs() const + { return this->refs_; } + + // Clear the references. + void + clear_refs(); + + // Define the label at LOCATION with the given bindings snapshot. + void + define(Location location, Bindings_snapshot* snapshot) + { + go_assert(Linemap::is_unknown_location(this->location_) + && this->snapshot_ == NULL); + this->location_ = location; + this->snapshot_ = snapshot; + } + + // Return the backend representation for this label. + Blabel* + get_backend_label(Translate_context*); + + // Return an expression for the address of this label. This is used + // to get the return address of a deferred function to see whether + // the function may call recover. + Bexpression* + get_addr(Translate_context*, Location location); + + private: + // The name of the label. + std::string name_; + // The location of the definition. This is 0 if the label has not + // yet been defined. + Location location_; + // A snapshot of the set of bindings defined at this label, used to + // issue errors about invalid goto statements. + Bindings_snapshot* snapshot_; + // A list of snapshots of goto statements which refer to this label. + std::vector refs_; + // Whether the label has been used. + bool is_used_; + // The backend representation. + Blabel* blabel_; +}; + +// An unnamed label. These are used when lowering loops. + +class Unnamed_label +{ + public: + Unnamed_label(Location location) + : location_(location), blabel_(NULL) + { } + + // Get the location where the label is defined. + Location + location() const + { return this->location_; } + + // Set the location where the label is defined. + void + set_location(Location location) + { this->location_ = location; } + + // Return a statement which defines this label. + Bstatement* + get_definition(Translate_context*); + + // Return a goto to this label from LOCATION. + Bstatement* + get_goto(Translate_context*, Location location); + + private: + // Return the backend representation. + Blabel* + get_blabel(Translate_context*); + + // The location where the label is defined. + Location location_; + // The backend representation of this label. + Blabel* blabel_; +}; + +// An imported package. + +class Package +{ + public: + Package(const std::string& pkgpath, Location location); + + // Get the package path used for all symbols exported from this + // package. + const std::string& + pkgpath() const + { return this->pkgpath_; } + + // Return the package path to use for a symbol name. + const std::string& + pkgpath_symbol() const + { return this->pkgpath_symbol_; } + + // Return the location of the import statement. + Location + location() const + { return this->location_; } + + // Return whether we know the name of this package yet. + bool + has_package_name() const + { return !this->package_name_.empty(); } + + // The name that this package uses in its package clause. This may + // be different from the name in the associated Named_object if the + // import statement used an alias. + const std::string& + package_name() const + { + go_assert(!this->package_name_.empty()); + return this->package_name_; + } + + // The priority of this package. The init function of packages with + // lower priority must be run before the init function of packages + // with higher priority. + int + priority() const + { return this->priority_; } + + // Set the priority. + void + set_priority(int priority); + + // Return the bindings. + Bindings* + bindings() + { return this->bindings_; } + + // Whether some symbol from the package was used. + bool + used() const + { return this->used_; } + + // Note that some symbol from this package was used. + void + set_used() const + { this->used_ = true; } + + // Clear the used field for the next file. + void + clear_used() + { this->used_ = false; } + + // Whether this package was imported in the current file. + bool + is_imported() const + { return this->is_imported_; } + + // Note that this package was imported in the current file. + void + set_is_imported() + { this->is_imported_ = true; } + + // Clear the imported field for the next file. + void + clear_is_imported() + { this->is_imported_ = false; } + + // Whether this package was imported with a name of "_". + bool + uses_sink_alias() const + { return this->uses_sink_alias_; } + + // Note that this package was imported with a name of "_". + void + set_uses_sink_alias() + { this->uses_sink_alias_ = true; } + + // Clear the sink alias field for the next file. + void + clear_uses_sink_alias() + { this->uses_sink_alias_ = false; } + + // Look up a name in the package. Returns NULL if the name is not + // found. + Named_object* + lookup(const std::string& name) const + { return this->bindings_->lookup(name); } + + // Set the name of the package. + void + set_package_name(const std::string& name, Location); + + // Set the location of the package. This is used to record the most + // recent import location. + void + set_location(Location location) + { this->location_ = location; } + + // Add a constant to the package. + Named_object* + add_constant(const Typed_identifier& tid, Expression* expr) + { return this->bindings_->add_constant(tid, this, expr, 0); } + + // Add a type to the package. + Named_object* + add_type(const std::string& name, Type* type, Location location) + { return this->bindings_->add_type(name, this, type, location); } + + // Add a type declaration to the package. + Named_object* + add_type_declaration(const std::string& name, Location location) + { return this->bindings_->add_type_declaration(name, this, location); } + + // Add a variable to the package. + Named_object* + add_variable(const std::string& name, Variable* variable) + { return this->bindings_->add_variable(name, this, variable); } + + // Add a function declaration to the package. + Named_object* + add_function_declaration(const std::string& name, Function_type* type, + Location loc) + { return this->bindings_->add_function_declaration(name, this, type, loc); } + + // Determine types of constants. + void + determine_types(); + + private: + // The package path for type reflection data. + std::string pkgpath_; + // The package path for symbol names. + std::string pkgpath_symbol_; + // The name that this package uses in the package clause. This may + // be the empty string if it is not yet known. + std::string package_name_; + // The names in this package. + Bindings* bindings_; + // The priority of this package. A package has a priority higher + // than the priority of all of the packages that it imports. This + // is used to run init functions in the right order. + int priority_; + // The location of the import statement. + Location location_; + // True if some name from this package was used. This is mutable + // because we can use a package even if we have a const pointer to + // it. + mutable bool used_; + // True if this package was imported in the current file. + bool is_imported_; + // True if this package was imported with a name of "_". + bool uses_sink_alias_; +}; + +// Return codes for the traversal functions. This is not an enum +// because we want to be able to declare traversal functions in other +// header files without including this one. + +// Continue traversal as usual. +const int TRAVERSE_CONTINUE = -1; + +// Exit traversal. +const int TRAVERSE_EXIT = 0; + +// Continue traversal, but skip components of the current object. +// E.g., if this is returned by Traverse::statement, we do not +// traverse the expressions in the statement even if +// traverse_expressions is set in the traverse_mask. +const int TRAVERSE_SKIP_COMPONENTS = 1; + +// This class is used when traversing the parse tree. The caller uses +// a subclass which overrides functions as desired. + +class Traverse +{ + public: + // These bitmasks say what to traverse. + static const unsigned int traverse_variables = 0x1; + static const unsigned int traverse_constants = 0x2; + static const unsigned int traverse_functions = 0x4; + static const unsigned int traverse_blocks = 0x8; + static const unsigned int traverse_statements = 0x10; + static const unsigned int traverse_expressions = 0x20; + static const unsigned int traverse_types = 0x40; + + Traverse(unsigned int traverse_mask) + : traverse_mask_(traverse_mask), types_seen_(NULL), expressions_seen_(NULL) + { } + + virtual ~Traverse(); + + // The bitmask of what to traverse. + unsigned int + traverse_mask() const + { return this->traverse_mask_; } + + // Record that we are going to traverse a type. This returns true + // if the type has already been seen in this traversal. This is + // required because types, unlike expressions, can form a circular + // graph. + bool + remember_type(const Type*); + + // Record that we are going to see an expression. This returns true + // if the expression has already been seen in this traversal. This + // is only needed for cases where multiple expressions can point to + // a single one. + bool + remember_expression(const Expression*); + + // These functions return one of the TRAVERSE codes defined above. + + // If traverse_variables is set in the mask, this is called for + // every variable in the tree. + virtual int + variable(Named_object*); + + // If traverse_constants is set in the mask, this is called for + // every named constant in the tree. The bool parameter is true for + // a global constant. + virtual int + constant(Named_object*, bool); + + // If traverse_functions is set in the mask, this is called for + // every function in the tree. + virtual int + function(Named_object*); + + // If traverse_blocks is set in the mask, this is called for every + // block in the tree. + virtual int + block(Block*); + + // If traverse_statements is set in the mask, this is called for + // every statement in the tree. + virtual int + statement(Block*, size_t* index, Statement*); + + // If traverse_expressions is set in the mask, this is called for + // every expression in the tree. + virtual int + expression(Expression**); + + // If traverse_types is set in the mask, this is called for every + // type in the tree. + virtual int + type(Type*); + + private: + // A hash table for types we have seen during this traversal. Note + // that this uses the default hash functions for pointers rather + // than Type_hash_identical and Type_identical. This is because for + // traversal we care about seeing a specific type structure. If + // there are two separate instances of identical types, we want to + // traverse both. + typedef Unordered_set(const Type*) Types_seen; + + typedef Unordered_set(const Expression*) Expressions_seen; + + // Bitmask of what sort of objects to traverse. + unsigned int traverse_mask_; + // Types which have been seen in this traversal. + Types_seen* types_seen_; + // Expressions which have been seen in this traversal. + Expressions_seen* expressions_seen_; +}; + +// A class which makes it easier to insert new statements before the +// current statement during a traversal. + +class Statement_inserter +{ + public: + // Empty constructor. + Statement_inserter() + : block_(NULL), pindex_(NULL), gogo_(NULL), var_(NULL) + { } + + // Constructor for a statement in a block. + Statement_inserter(Block* block, size_t *pindex) + : block_(block), pindex_(pindex), gogo_(NULL), var_(NULL) + { } + + // Constructor for a global variable. + Statement_inserter(Gogo* gogo, Variable* var) + : block_(NULL), pindex_(NULL), gogo_(gogo), var_(var) + { go_assert(var->is_global()); } + + // We use the default copy constructor and assignment operator. + + // Insert S before the statement we are traversing, or before the + // initialization expression of a global variable. + void + insert(Statement* s); + + private: + // The block that the statement is in. + Block* block_; + // The index of the statement that we are traversing. + size_t* pindex_; + // The IR, needed when looking at an initializer expression for a + // global variable. + Gogo* gogo_; + // The global variable, when looking at an initializer expression. + Variable* var_; +}; + +// When translating the gogo IR into the backend data structure, this +// is the context we pass down the blocks and statements. + +class Translate_context +{ + public: + Translate_context(Gogo* gogo, Named_object* function, Block* block, + Bblock* bblock) + : gogo_(gogo), backend_(gogo->backend()), function_(function), + block_(block), bblock_(bblock), is_const_(false) + { } + + // Accessors. + + Gogo* + gogo() + { return this->gogo_; } + + Backend* + backend() + { return this->backend_; } + + Named_object* + function() + { return this->function_; } + + Block* + block() + { return this->block_; } + + Bblock* + bblock() + { return this->bblock_; } + + bool + is_const() + { return this->is_const_; } + + // Make a constant context. + void + set_is_const() + { this->is_const_ = true; } + + private: + // The IR for the entire compilation unit. + Gogo* gogo_; + // The generator for the backend data structures. + Backend* backend_; + // The function we are currently translating. NULL if not in a + // function, e.g., the initializer of a global variable. + Named_object* function_; + // The block we are currently translating. NULL if not in a + // function. + Block *block_; + // The backend representation of the current block. NULL if block_ + // is NULL. + Bblock* bblock_; + // Whether this is being evaluated in a constant context. This is + // used for type descriptor initializers. + bool is_const_; +}; + +// Runtime error codes. These must match the values in +// libgo/runtime/go-runtime-error.c. + +// Slice index out of bounds: negative or larger than the length of +// the slice. +static const int RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS = 0; + +// Array index out of bounds. +static const int RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS = 1; + +// String index out of bounds. +static const int RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS = 2; + +// Slice slice out of bounds: negative or larger than the length of +// the slice or high bound less than low bound. +static const int RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS = 3; + +// Array slice out of bounds. +static const int RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS = 4; + +// String slice out of bounds. +static const int RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS = 5; + +// Dereference of nil pointer. This is used when there is a +// dereference of a pointer to a very large struct or array, to ensure +// that a gigantic array is not used a proxy to access random memory +// locations. +static const int RUNTIME_ERROR_NIL_DEREFERENCE = 6; + +// Slice length or capacity out of bounds in make: negative or +// overflow or length greater than capacity. +static const int RUNTIME_ERROR_MAKE_SLICE_OUT_OF_BOUNDS = 7; + +// Map capacity out of bounds in make: negative or overflow. +static const int RUNTIME_ERROR_MAKE_MAP_OUT_OF_BOUNDS = 8; + +// Channel capacity out of bounds in make: negative or overflow. +static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9; + +// Division by zero. +static const int RUNTIME_ERROR_DIVISION_BY_ZERO = 10; + +// This is used by some of the langhooks. +extern Gogo* go_get_gogo(); + +// Whether we have seen any errors. FIXME: Replace with a backend +// interface. +extern bool saw_errors(); + +#endif // !defined(GO_GOGO_H) diff --git a/gcc-4.9/gcc/go/gofrontend/import-archive.cc b/gcc-4.9/gcc/go/gofrontend/import-archive.cc new file mode 100644 index 000000000..9a1d5b3d7 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/import-archive.cc @@ -0,0 +1,660 @@ +// import-archive.cc -- Go frontend read import data from an archive file. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "import.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +// Archive magic numbers. + +static const char armag[] = +{ + '!', '<', 'a', 'r', 'c', 'h', '>', '\n' +}; + +static const char armagt[] = +{ + '!', '<', 't', 'h', 'i', 'n', '>', '\n' +}; + +static const char arfmag[2] = { '`', '\n' }; + +// The header of an entry in an archive. This is all readable text, +// padded with spaces where necesary. + +struct Archive_header +{ + // The entry name. + char ar_name[16]; + // The file modification time. + char ar_date[12]; + // The user's UID in decimal. + char ar_uid[6]; + // The user's GID in decimal. + char ar_gid[6]; + // The file mode in octal. + char ar_mode[8]; + // The file size in decimal. + char ar_size[10]; + // The final magic code. + char ar_fmag[2]; +}; + +// The functions in this file extract Go export data from an archive. + +const int Import::archive_magic_len; + +// Return true if BYTES, which are from the start of the file, are an +// archive magic number. + +bool +Import::is_archive_magic(const char* bytes) +{ + return (memcmp(bytes, armag, Import::archive_magic_len) == 0 + || memcmp(bytes, armagt, Import::archive_magic_len) == 0); +} + +// An object used to read an archive file. + +class Archive_file +{ + public: + Archive_file(const std::string& filename, int fd, Location location) + : filename_(filename), fd_(fd), filesize_(-1), extended_names_(), + is_thin_archive_(false), location_(location), nested_archives_() + { } + + // Initialize. + bool + initialize(); + + // Return the file name. + const std::string& + filename() const + { return this->filename_; } + + // Get the file size. + off_t + filesize() const + { return this->filesize_; } + + // Return whether this is a thin archive. + bool + is_thin_archive() const + { return this->is_thin_archive_; } + + // Return the location of the import statement. + Location + location() const + { return this->location_; } + + // Read bytes. + bool + read(off_t offset, off_t size, char*); + + // Read the archive header at OFF, setting *PNAME, *SIZE, and + // *NESTED_OFF. + bool + read_header(off_t off, std::string* pname, off_t* size, off_t* nested_off); + + // Interpret the header of HDR, the header of the archive member at + // file offset OFF. Return whether it succeeded. Set *SIZE to the + // size of the member. Set *PNAME to the name of the member. Set + // *NESTED_OFF to the offset in a nested archive. + bool + interpret_header(const Archive_header* hdr, off_t off, + std::string* pname, off_t* size, off_t* nested_off) const; + + // Get the file and offset for an archive member. + bool + get_file_and_offset(off_t off, const std::string& hdrname, + off_t nested_off, int* memfd, off_t* memoff, + std::string* memname); + + private: + // For keeping track of open nested archives in a thin archive file. + typedef std::map Nested_archive_table; + + // The name of the file. + std::string filename_; + // The file descriptor. + int fd_; + // The file size; + off_t filesize_; + // The extended name table. + std::string extended_names_; + // Whether this is a thin archive. + bool is_thin_archive_; + // The location of the import statements. + Location location_; + // Table of nested archives. + Nested_archive_table nested_archives_; +}; + +bool +Archive_file::initialize() +{ + struct stat st; + if (fstat(this->fd_, &st) < 0) + { + error_at(this->location_, "%s: %m", this->filename_.c_str()); + return false; + } + this->filesize_ = st.st_size; + + char buf[sizeof(armagt)]; + if (::lseek(this->fd_, 0, SEEK_SET) < 0 + || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt)) + { + error_at(this->location_, "%s: %m", this->filename_.c_str()); + return false; + } + this->is_thin_archive_ = memcmp(buf, armagt, sizeof(armagt)) == 0; + + if (this->filesize_ == sizeof(armag)) + { + // Empty archive. + return true; + } + + // Look for the extended name table. + std::string filename; + off_t size; + if (!this->read_header(sizeof(armagt), &filename, &size, NULL)) + return false; + if (filename.empty()) + { + // We found the symbol table. + off_t off = sizeof(armagt) + sizeof(Archive_header) + size; + if ((off & 1) != 0) + ++off; + if (!this->read_header(off, &filename, &size, NULL)) + filename.clear(); + } + if (filename == "/") + { + char* rdbuf = new char[size]; + if (::read(this->fd_, rdbuf, size) != size) + { + error_at(this->location_, "%s: could not read extended names", + filename.c_str()); + delete[] rdbuf; + return false; + } + this->extended_names_.assign(rdbuf, size); + delete[] rdbuf; + } + + return true; +} + +// Read bytes from the file. + +bool +Archive_file::read(off_t offset, off_t size, char* buf) +{ + if (::lseek(this->fd_, offset, SEEK_SET) < 0 + || ::read(this->fd_, buf, size) != size) + { + error_at(this->location_, "%s: %m", this->filename_.c_str()); + return false; + } + return true; +} + +// Read the header at OFF. Set *PNAME to the name, *SIZE to the size, +// and *NESTED_OFF to the nested offset. + +bool +Archive_file::read_header(off_t off, std::string* pname, off_t* size, + off_t* nested_off) +{ + Archive_header hdr; + if (::lseek(this->fd_, off, SEEK_SET) < 0) + { + error_at(this->location_, "%s: %m", this->filename_.c_str()); + return false; + } + ssize_t got = ::read(this->fd_, &hdr, sizeof hdr); + if (got != sizeof hdr) + { + if (got < 0) + error_at(this->location_, "%s: %m", this->filename_.c_str()); + else if (got > 0) + error_at(this->location_, "%s: short archive header at %ld", + this->filename_.c_str(), static_cast(off)); + else + error_at(this->location_, "%s: unexpected EOF at %ld", + this->filename_.c_str(), static_cast(off)); + } + off_t local_nested_off; + if (!this->interpret_header(&hdr, off, pname, size, &local_nested_off)) + return false; + if (nested_off != NULL) + *nested_off = local_nested_off; + return true; +} + +// Interpret the header of HDR, the header of the archive member at +// file offset OFF. + +bool +Archive_file::interpret_header(const Archive_header* hdr, off_t off, + std::string* pname, off_t* size, + off_t* nested_off) const +{ + if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0) + { + error_at(this->location_, "%s: malformed archive header at %lu", + this->filename_.c_str(), static_cast(off)); + return false; + } + + const int size_string_size = sizeof hdr->ar_size; + char size_string[size_string_size + 1]; + memcpy(size_string, hdr->ar_size, size_string_size); + char* ps = size_string + size_string_size; + while (ps[-1] == ' ') + --ps; + *ps = '\0'; + + errno = 0; + char* end; + *size = strtol(size_string, &end, 10); + if (*end != '\0' + || *size < 0 + || (*size == LONG_MAX && errno == ERANGE)) + { + error_at(this->location_, "%s: malformed archive header size at %lu", + this->filename_.c_str(), static_cast(off)); + return false; + } + + *nested_off = 0; + if (hdr->ar_name[0] != '/') + { + const char* name_end = strchr(hdr->ar_name, '/'); + if (name_end == NULL + || name_end - hdr->ar_name >= static_cast(sizeof hdr->ar_name)) + { + error_at(this->location_, "%s: malformed archive header name at %lu", + this->filename_.c_str(), static_cast(off)); + return false; + } + pname->assign(hdr->ar_name, name_end - hdr->ar_name); + } + else if (hdr->ar_name[1] == ' ') + { + // This is the symbol table. + pname->clear(); + } + else if (hdr->ar_name[1] == '/') + { + // This is the extended name table. + pname->assign(1, '/'); + } + else + { + errno = 0; + long x = strtol(hdr->ar_name + 1, &end, 10); + long y = 0; + if (*end == ':') + y = strtol(end + 1, &end, 10); + if (*end != ' ' + || x < 0 + || (x == LONG_MAX && errno == ERANGE) + || static_cast(x) >= this->extended_names_.size()) + { + error_at(this->location_, "%s: bad extended name index at %lu", + this->filename_.c_str(), static_cast(off)); + return false; + } + + const char* name = this->extended_names_.data() + x; + const char* name_end = strchr(name, '\n'); + if (static_cast(name_end - name) > this->extended_names_.size() + || name_end[-1] != '/') + { + error_at(this->location_, "%s: bad extended name entry at header %lu", + this->filename_.c_str(), static_cast(off)); + return false; + } + pname->assign(name, name_end - 1 - name); + *nested_off = y; + } + + return true; +} + +// Get the file and offset for an archive member. + +bool +Archive_file::get_file_and_offset(off_t off, const std::string& hdrname, + off_t nested_off, int* memfd, off_t* memoff, + std::string* memname) +{ + if (!this->is_thin_archive_) + { + *memfd = this->fd_; + *memoff = off + sizeof(Archive_header); + *memname = this->filename_ + '(' + hdrname + ')'; + return true; + } + + std::string filename = hdrname; + if (!IS_ABSOLUTE_PATH(filename.c_str())) + { + const char* archive_path = this->filename_.c_str(); + const char* basename = lbasename(archive_path); + if (basename > archive_path) + filename.replace(0, 0, + this->filename_.substr(0, basename - archive_path)); + } + + if (nested_off > 0) + { + // This is a member of a nested archive. + Archive_file* nfile; + Nested_archive_table::const_iterator p = + this->nested_archives_.find(filename); + if (p != this->nested_archives_.end()) + nfile = p->second; + else + { + int nfd = open(filename.c_str(), O_RDONLY | O_BINARY); + if (nfd < 0) + { + error_at(this->location_, "%s: can't open nested archive %s", + this->filename_.c_str(), filename.c_str()); + return false; + } + nfile = new Archive_file(filename, nfd, this->location_); + if (!nfile->initialize()) + { + delete nfile; + return false; + } + this->nested_archives_[filename] = nfile; + } + + std::string nname; + off_t nsize; + off_t nnested_off; + if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off)) + return false; + return nfile->get_file_and_offset(nested_off, nname, nnested_off, + memfd, memoff, memname); + } + + // An external member of a thin archive. + *memfd = open(filename.c_str(), O_RDONLY | O_BINARY); + if (*memfd < 0) + { + error_at(this->location_, "%s: %m", filename.c_str()); + return false; + } + *memoff = 0; + *memname = filename; + return true; +} + +// An archive member iterator. This is more-or-less copied from gold. + +class Archive_iterator +{ + public: + // The header of an archive member. This is what this iterator + // points to. + struct Header + { + // The name of the member. + std::string name; + // The file offset of the member. + off_t off; + // The file offset of a nested archive member. + off_t nested_off; + // The size of the member. + off_t size; + }; + + Archive_iterator(Archive_file* afile, off_t off) + : afile_(afile), off_(off) + { this->read_next_header(); } + + const Header& + operator*() const + { return this->header_; } + + const Header* + operator->() const + { return &this->header_; } + + Archive_iterator& + operator++() + { + if (this->off_ == this->afile_->filesize()) + return *this; + this->off_ += sizeof(Archive_header); + if (!this->afile_->is_thin_archive()) + this->off_ += this->header_.size; + if ((this->off_ & 1) != 0) + ++this->off_; + this->read_next_header(); + return *this; + } + + Archive_iterator + operator++(int) + { + Archive_iterator ret = *this; + ++*this; + return ret; + } + + bool + operator==(const Archive_iterator p) const + { return this->off_ == p->off; } + + bool + operator!=(const Archive_iterator p) const + { return this->off_ != p->off; } + + private: + void + read_next_header(); + + // The underlying archive file. + Archive_file* afile_; + // The current offset in the file. + off_t off_; + // The current archive header. + Header header_; +}; + +// Read the next archive header. + +void +Archive_iterator::read_next_header() +{ + off_t filesize = this->afile_->filesize(); + while (true) + { + if (filesize - this->off_ < static_cast(sizeof(Archive_header))) + { + if (filesize != this->off_) + { + error_at(this->afile_->location(), + "%s: short archive header at %lu", + this->afile_->filename().c_str(), + static_cast(this->off_)); + this->off_ = filesize; + } + this->header_.off = filesize; + return; + } + + char buf[sizeof(Archive_header)]; + if (!this->afile_->read(this->off_, sizeof(Archive_header), buf)) + { + this->header_.off = filesize; + return; + } + + const Archive_header* hdr = reinterpret_cast(buf); + if (!this->afile_->interpret_header(hdr, this->off_, &this->header_.name, + &this->header_.size, + &this->header_.nested_off)) + { + this->header_.off = filesize; + return; + } + this->header_.off = this->off_; + + // Skip special members. + if (!this->header_.name.empty() && this->header_.name != "/") + return; + + this->off_ += sizeof(Archive_header) + this->header_.size; + if ((this->off_ & 1) != 0) + ++this->off_; + } +} + +// Initial iterator. + +Archive_iterator +archive_begin(Archive_file* afile) +{ + return Archive_iterator(afile, sizeof(armag)); +} + +// Final iterator. + +Archive_iterator +archive_end(Archive_file* afile) +{ + return Archive_iterator(afile, afile->filesize()); +} + +// A type of Import_stream which concatenates other Import_streams +// together. + +class Stream_concatenate : public Import::Stream +{ + public: + Stream_concatenate() + : inputs_() + { } + + // Add a new stream. + void + add(Import::Stream* is) + { this->inputs_.push_back(is); } + + protected: + bool + do_peek(size_t, const char**); + + void + do_advance(size_t); + + private: + std::list inputs_; +}; + +// Peek ahead. + +bool +Stream_concatenate::do_peek(size_t length, const char** bytes) +{ + while (true) + { + if (this->inputs_.empty()) + return false; + if (this->inputs_.front()->peek(length, bytes)) + return true; + delete this->inputs_.front(); + this->inputs_.pop_front(); + } +} + +// Advance. + +void +Stream_concatenate::do_advance(size_t skip) +{ + while (true) + { + if (this->inputs_.empty()) + return; + if (!this->inputs_.front()->at_eof()) + { + // We just assume that this will do the right thing. It + // should be OK since we should never want to skip past + // multiple streams. + this->inputs_.front()->advance(skip); + return; + } + delete this->inputs_.front(); + this->inputs_.pop_front(); + } +} + +// Import data from an archive. We walk through the archive and +// import data from each member. + +Import::Stream* +Import::find_archive_export_data(const std::string& filename, int fd, + Location location) +{ + Archive_file afile(filename, fd, location); + if (!afile.initialize()) + return NULL; + + Stream_concatenate* ret = new Stream_concatenate; + + bool any_data = false; + bool any_members = false; + Archive_iterator pend = archive_end(&afile); + for (Archive_iterator p = archive_begin(&afile); p != pend; p++) + { + any_members = true; + int member_fd; + off_t member_off; + std::string member_name; + if (!afile.get_file_and_offset(p->off, p->name, p->nested_off, + &member_fd, &member_off, &member_name)) + return NULL; + + Import::Stream* is = Import::find_object_export_data(member_name, + member_fd, + member_off, + location); + if (is != NULL) + { + ret->add(is); + any_data = true; + } + } + + if (!any_members) + { + // It's normal to have an empty archive file when using gobuild. + return new Stream_from_string(""); + } + + if (!any_data) + { + delete ret; + return NULL; + } + + return ret; +} diff --git a/gcc-4.9/gcc/go/gofrontend/import.cc b/gcc-4.9/gcc/go/gofrontend/import.cc new file mode 100644 index 000000000..4913100b5 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/import.cc @@ -0,0 +1,960 @@ +// import.cc -- Go frontend import declarations. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "filenames.h" +#include "simple-object.h" + +#include "go-c.h" +#include "gogo.h" +#include "lex.h" +#include "types.h" +#include "export.h" +#include "import.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +// The list of paths we search for import files. + +static std::vector search_path; + +// Add a directory to the search path. This is called from the option +// handling language hook. + +GO_EXTERN_C +void +go_add_search_path(const char* path) +{ + search_path.push_back(std::string(path)); +} + +// 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. If the file is not found, it returns NULL. + +// 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". +// * We turn the base of FILENAME into libFILENAME.so. +// * We turn the base of FILENAME into libFILENAME.a. +// * We append ".o". + +// When using a search path, we apply each of these transformations at +// each entry on the search path before moving on to the next entry. +// If the file exists, but does not contain any Go export data, we +// stop; we do not keep looking for another file with the same name +// later in the search path. + +Import::Stream* +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] == '.' + && (filename[1] == '\0' || IS_DIR_SEPARATOR(filename[1]))) + is_local = true; + else if (filename[0] == '.' + && filename[1] == '.' + && (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::const_iterator p = search_path.begin(); + p != search_path.end(); + ++p) + { + std::string indir = *p; + if (!indir.empty() && indir[indir.size() - 1] != '/') + indir += '/'; + indir += fn; + Stream* s = Import::try_package_in_directory(indir, location); + if (s != NULL) + return s; + } + } + + Stream* s = Import::try_package_in_directory(fn, location); + if (s != NULL) + return s; + + return NULL; +} + +// Try to find the export data for FILENAME. + +Import::Stream* +Import::try_package_in_directory(const std::string& filename, + Location location) +{ + std::string found_filename = filename; + int fd = open(found_filename.c_str(), O_RDONLY | O_BINARY); + + if (fd >= 0) + { + struct stat s; + if (fstat(fd, &s) >= 0 && S_ISDIR(s.st_mode)) + { + close(fd); + fd = -1; + errno = EISDIR; + } + } + + if (fd < 0) + { + if (errno != ENOENT && errno != EISDIR) + warning_at(location, 0, "%s: %m", filename.c_str()); + + fd = Import::try_suffixes(&found_filename); + if (fd < 0) + return NULL; + } + + // The export data may not be in this file. + Stream* s = Import::find_export_data(found_filename, fd, location); + if (s != NULL) + return s; + + close(fd); + + error_at(location, "%s exists but does not contain any Go export data", + found_filename.c_str()); + + return NULL; +} + +// Given import "*PFILENAME", where *PFILENAME does not exist, try +// various suffixes. If we find one, set *PFILENAME to the one we +// found. Return the open file descriptor. + +int +Import::try_suffixes(std::string* pfilename) +{ + std::string filename = *pfilename + ".gox"; + int fd = open(filename.c_str(), O_RDONLY | O_BINARY); + if (fd >= 0) + { + *pfilename = filename; + return fd; + } + + const char* basename = lbasename(pfilename->c_str()); + size_t basename_pos = basename - pfilename->c_str(); + filename = pfilename->substr(0, basename_pos) + "lib" + basename + ".so"; + fd = open(filename.c_str(), O_RDONLY | O_BINARY); + if (fd >= 0) + { + *pfilename = filename; + return fd; + } + + filename = pfilename->substr(0, basename_pos) + "lib" + basename + ".a"; + fd = open(filename.c_str(), O_RDONLY | O_BINARY); + if (fd >= 0) + { + *pfilename = filename; + return fd; + } + + filename = *pfilename + ".o"; + fd = open(filename.c_str(), O_RDONLY | O_BINARY); + if (fd >= 0) + { + *pfilename = filename; + return fd; + } + + return -1; +} + +// Look for export data in the file descriptor FD. + +Import::Stream* +Import::find_export_data(const std::string& filename, int fd, + Location location) +{ + // See if we can read this as an object file. + Import::Stream* stream = Import::find_object_export_data(filename, fd, 0, + location); + if (stream != NULL) + return stream; + + const int len = MAX(Export::v1_magic_len, Import::archive_magic_len); + + if (lseek(fd, 0, SEEK_SET) < 0) + { + error_at(location, "lseek %s failed: %m", filename.c_str()); + return NULL; + } + + char buf[len]; + ssize_t c = read(fd, buf, len); + if (c < len) + return NULL; + + // Check for a file containing nothing but Go export data. + if (memcmp(buf, Export::v1_magic, Export::v1_magic_len) == 0) + return new Stream_from_file(fd); + + // See if we can read this as an archive. + if (Import::is_archive_magic(buf)) + return Import::find_archive_export_data(filename, fd, location); + + return NULL; +} + +// Look for export data in a simple_object. + +Import::Stream* +Import::find_object_export_data(const std::string& filename, + int fd, + off_t offset, + Location location) +{ + char *buf; + size_t len; + int err; + const char *errmsg = go_read_export_data(fd, offset, &buf, &len, &err); + if (errmsg != NULL) + { + if (err == 0) + error_at(location, "%s: %s", filename.c_str(), errmsg); + else + error_at(location, "%s: %s: %s", filename.c_str(), errmsg, + xstrerror(err)); + return NULL; + } + + if (buf == NULL) + return NULL; + + return new Stream_from_buffer(buf, len); +} + +// Class Import. + +// Construct an Import object. We make the builtin_types_ vector +// large enough to hold all the builtin types. + +Import::Import(Stream* stream, Location location) + : gogo_(NULL), stream_(stream), location_(location), package_(NULL), + add_to_globals_(false), + builtin_types_((- SMALLEST_BUILTIN_CODE) + 1), + types_() +{ +} + +// Import the data in the associated stream. + +Package* +Import::import(Gogo* gogo, const std::string& local_name, + bool is_local_name_exported) +{ + // Hold on to the Gogo structure. Otherwise we need to pass it + // through all the import functions, because we need it when reading + // a type. + this->gogo_ = gogo; + + // A stream of export data can include data from more than one input + // file. Here we loop over each input file. + Stream* stream = this->stream_; + while (!stream->at_eof() && !stream->saw_error()) + { + // The vector of types is package specific. + this->types_.clear(); + + stream->require_bytes(this->location_, Export::v1_magic, + Export::v1_magic_len); + + this->require_c_string("package "); + std::string package_name = this->read_identifier(); + this->require_c_string(";\n"); + + std::string pkgpath; + if (this->match_c_string("prefix ")) + { + this->advance(7); + std::string unique_prefix = this->read_identifier(); + this->require_c_string(";\n"); + pkgpath = unique_prefix + '.' + package_name; + } + else + { + this->require_c_string("pkgpath "); + pkgpath = this->read_identifier(); + this->require_c_string(";\n"); + } + + this->package_ = gogo->add_imported_package(package_name, local_name, + is_local_name_exported, + pkgpath, + this->location_, + &this->add_to_globals_); + if (this->package_ == NULL) + { + stream->set_saw_error(); + return NULL; + } + + this->require_c_string("priority "); + std::string priority_string = this->read_identifier(); + int prio; + if (!this->string_to_int(priority_string, false, &prio)) + return NULL; + this->package_->set_priority(prio); + this->require_c_string(";\n"); + + while (stream->match_c_string("import")) + this->read_one_import(); + + if (stream->match_c_string("init")) + this->read_import_init_fns(gogo); + + // Loop over all the input data for this package. + while (!stream->saw_error()) + { + if (stream->match_c_string("const ")) + this->import_const(); + else if (stream->match_c_string("type ")) + this->import_type(); + else if (stream->match_c_string("var ")) + this->import_var(); + else if (stream->match_c_string("func ")) + this->import_func(this->package_); + else if (stream->match_c_string("checksum ")) + break; + else + { + error_at(this->location_, + ("error in import data at %d: " + "expected %, %, %, " + "%, or %"), + stream->pos()); + stream->set_saw_error(); + return NULL; + } + } + + // We currently ignore the checksum. In the future we could + // store the checksum somewhere in the generated object and then + // verify that the checksum matches at link time or at dynamic + // load time. + this->require_c_string("checksum "); + stream->advance(Export::v1_checksum_len * 2); + this->require_c_string(";\n"); + } + + return this->package_; +} + +// Read an import line. We don't actually care about these. + +void +Import::read_one_import() +{ + this->require_c_string("import "); + std::string package_name = this->read_identifier(); + this->require_c_string(" "); + std::string pkgpath = this->read_identifier(); + this->require_c_string(" \""); + Stream* stream = this->stream_; + while (stream->peek_char() != '"') + stream->advance(1); + this->require_c_string("\";\n"); + + Package* p = this->gogo_->register_package(pkgpath, + Linemap::unknown_location()); + p->set_package_name(package_name, this->location()); +} + +// Read the list of import control functions. + +void +Import::read_import_init_fns(Gogo* gogo) +{ + this->require_c_string("init"); + while (!this->match_c_string(";")) + { + this->require_c_string(" "); + std::string package_name = this->read_identifier(); + this->require_c_string(" "); + std::string init_name = this->read_identifier(); + this->require_c_string(" "); + std::string prio_string = this->read_identifier(); + int prio; + if (!this->string_to_int(prio_string, false, &prio)) + return; + gogo->add_import_init_fn(package_name, init_name, prio); + } + this->require_c_string(";\n"); +} + +// Import a constant. + +void +Import::import_const() +{ + std::string name; + Type* type; + Expression* expr; + Named_constant::import_const(this, &name, &type, &expr); + Typed_identifier tid(name, type, this->location_); + Named_object* no = this->package_->add_constant(tid, expr); + if (this->add_to_globals_) + this->gogo_->add_named_object(no); +} + +// Import a type. + +void +Import::import_type() +{ + Named_type* type; + Named_type::import_named_type(this, &type); + + // The named type has been added to the package by the type import + // process. Here we need to make it visible to the parser, and it + // to the global bindings if necessary. + type->set_is_visible(); + + if (this->add_to_globals_) + this->gogo_->add_named_type(type); +} + +// Import a variable. + +void +Import::import_var() +{ + std::string name; + Type* type; + Variable::import_var(this, &name, &type); + Variable* var = new Variable(type, NULL, true, false, false, + this->location_); + Named_object* no; + no = this->package_->add_variable(name, var); + if (this->add_to_globals_) + this->gogo_->add_named_object(no); +} + +// Import a function into PACKAGE. PACKAGE is normally +// THIS->PACKAGE_, but it will be different for a method associated +// with a type defined in a different package. + +Named_object* +Import::import_func(Package* package) +{ + std::string name; + Typed_identifier* receiver; + Typed_identifier_list* parameters; + Typed_identifier_list* results; + bool is_varargs; + Function::import_func(this, &name, &receiver, ¶meters, &results, + &is_varargs); + Function_type *fntype = Type::make_function_type(receiver, parameters, + results, this->location_); + if (is_varargs) + fntype->set_is_varargs(); + + Location loc = this->location_; + Named_object* no; + if (fntype->is_method()) + { + Type* rtype = receiver->type(); + + // We may still be reading the definition of RTYPE, so we have + // to be careful to avoid calling base or convert. If RTYPE is + // a named type or a forward declaration, then we know that it + // is not a pointer, because we are reading a method on RTYPE + // and named pointers can't have methods. + + if (rtype->classification() == Type::TYPE_POINTER) + rtype = rtype->points_to(); + + if (rtype->is_error_type()) + return NULL; + else if (rtype->named_type() != NULL) + no = rtype->named_type()->add_method_declaration(name, package, fntype, + loc); + else if (rtype->forward_declaration_type() != NULL) + no = rtype->forward_declaration_type()->add_method_declaration(name, + package, + fntype, + loc); + else + go_unreachable(); + } + else + { + no = package->add_function_declaration(name, fntype, loc); + if (this->add_to_globals_) + this->gogo_->add_named_object(no); + } + return no; +} + +// Read a type in the import stream. This records the type by the +// type index. If the type is named, it registers the name, but marks +// it as invisible. + +Type* +Import::read_type() +{ + Stream* stream = this->stream_; + this->require_c_string("get_char(); + if (c != '-' && (c < '0' || c > '9')) + break; + number += c; + } + + int index; + if (!this->string_to_int(number, true, &index)) + return Type::make_error_type(); + + if (c == '>') + { + // This type was already defined. + if (index < 0 + ? (static_cast(- index) >= this->builtin_types_.size() + || this->builtin_types_[- index] == NULL) + : (static_cast(index) >= this->types_.size() + || this->types_[index] == NULL)) + { + error_at(this->location_, + "error in import data at %d: bad type index %d", + stream->pos(), index); + stream->set_saw_error(); + return Type::make_error_type(); + } + + return index < 0 ? this->builtin_types_[- index] : this->types_[index]; + } + + if (c != ' ') + { + if (!stream->saw_error()) + error_at(this->location_, + "error in import data at %d: expect %< %> or %<>%>'", + stream->pos()); + stream->set_saw_error(); + stream->advance(1); + return Type::make_error_type(); + } + + if (index <= 0 + || (static_cast(index) < this->types_.size() + && this->types_[index] != NULL)) + { + error_at(this->location_, + "error in import data at %d: type index already defined", + stream->pos()); + stream->set_saw_error(); + return Type::make_error_type(); + } + + if (static_cast(index) >= this->types_.size()) + { + int newsize = std::max(static_cast(index) + 1, + this->types_.size() * 2); + this->types_.resize(newsize, NULL); + } + + if (stream->peek_char() != '"') + { + Type* type = Type::import_type(this); + this->require_c_string(">"); + this->types_[index] = type; + return type; + } + + // This type has a name. + + stream->advance(1); + std::string type_name; + while ((c = stream->get_char()) != '"') + type_name += c; + + // If this type is in the package we are currently importing, the + // name will be .PKGPATH.NAME or simply NAME with no dots. + // Otherwise, a non-hidden symbol will be PKGPATH.NAME and a hidden + // symbol will be .PKGPATH.NAME. + std::string pkgpath; + if (type_name.find('.') != std::string::npos) + { + size_t start = 0; + if (type_name[0] == '.') + start = 1; + size_t dot = type_name.rfind('.'); + pkgpath = type_name.substr(start, dot - start); + if (type_name[0] != '.') + type_name.erase(0, dot + 1); + } + + this->require_c_string(" "); + + // The package name may follow. This is the name of the package in + // the package clause of that package. The type name will include + // the pkgpath, which may be different. + std::string package_name; + if (stream->peek_char() == '"') + { + stream->advance(1); + while ((c = stream->get_char()) != '"') + package_name += c; + this->require_c_string(" "); + } + + // Declare the type in the appropriate package. If we haven't seen + // it before, mark it as invisible. We declare it before we read + // the actual definition of the type, since the definition may refer + // to the type itself. + Package* package; + if (pkgpath.empty() || pkgpath == this->gogo_->pkgpath()) + package = this->package_; + else + { + package = this->gogo_->register_package(pkgpath, + Linemap::unknown_location()); + if (!package_name.empty()) + package->set_package_name(package_name, this->location()); + } + + Named_object* no = package->bindings()->lookup(type_name); + if (no == NULL) + no = package->add_type_declaration(type_name, this->location_); + else if (!no->is_type_declaration() && !no->is_type()) + { + error_at(this->location_, "imported %<%s.%s%> both type and non-type", + pkgpath.c_str(), Gogo::message_name(type_name).c_str()); + stream->set_saw_error(); + return Type::make_error_type(); + } + else + go_assert(no->package() == package); + + if (this->types_[index] == NULL) + { + if (no->is_type_declaration()) + { + // FIXME: It's silly to make a forward declaration every time. + this->types_[index] = Type::make_forward_declaration(no); + } + else + { + go_assert(no->is_type()); + this->types_[index] = no->type_value(); + } + } + + // If there is no type definition, then this is just a forward + // declaration of a type defined in some other file. + Type* type; + if (this->match_c_string(">")) + type = this->types_[index]; + else + { + type = this->read_type(); + + if (no->is_type_declaration()) + { + // We can define the type now. + + no = package->add_type(type_name, type, this->location_); + Named_type* ntype = no->type_value(); + + // This type has not yet been imported. + ntype->clear_is_visible(); + + if (!type->is_undefined() && type->interface_type() != NULL) + this->gogo_->record_interface_type(type->interface_type()); + + type = ntype; + } + else if (no->is_type()) + { + // We have seen this type before. FIXME: it would be a good + // idea to check that the two imported types are identical, + // but we have not finalized the methods yet, which means + // that we can not reliably compare interface types. + type = no->type_value(); + + // Don't change the visibility of the existing type. + } + + this->types_[index] = type; + + // Read the type methods. + if (this->match_c_string("\n")) + { + this->advance(1); + while (this->match_c_string(" func")) + { + this->advance(1); + this->import_func(package); + } + } + } + + this->require_c_string(">"); + + return type; +} + +// Register the builtin types. + +void +Import::register_builtin_types(Gogo* gogo) +{ + this->register_builtin_type(gogo, "int8", BUILTIN_INT8); + this->register_builtin_type(gogo, "int16", BUILTIN_INT16); + this->register_builtin_type(gogo, "int32", BUILTIN_INT32); + this->register_builtin_type(gogo, "int64", BUILTIN_INT64); + this->register_builtin_type(gogo, "uint8", BUILTIN_UINT8); + this->register_builtin_type(gogo, "uint16", BUILTIN_UINT16); + this->register_builtin_type(gogo, "uint32", BUILTIN_UINT32); + this->register_builtin_type(gogo, "uint64", BUILTIN_UINT64); + this->register_builtin_type(gogo, "float32", BUILTIN_FLOAT32); + this->register_builtin_type(gogo, "float64", BUILTIN_FLOAT64); + this->register_builtin_type(gogo, "complex64", BUILTIN_COMPLEX64); + this->register_builtin_type(gogo, "complex128", BUILTIN_COMPLEX128); + this->register_builtin_type(gogo, "int", BUILTIN_INT); + this->register_builtin_type(gogo, "uint", BUILTIN_UINT); + this->register_builtin_type(gogo, "uintptr", BUILTIN_UINTPTR); + this->register_builtin_type(gogo, "bool", BUILTIN_BOOL); + this->register_builtin_type(gogo, "string", BUILTIN_STRING); + this->register_builtin_type(gogo, "error", BUILTIN_ERROR); + this->register_builtin_type(gogo, "byte", BUILTIN_BYTE); + this->register_builtin_type(gogo, "rune", BUILTIN_RUNE); +} + +// Register a single builtin type. + +void +Import::register_builtin_type(Gogo* gogo, const char* name, Builtin_code code) +{ + Named_object* named_object = gogo->lookup_global(name); + go_assert(named_object != NULL && named_object->is_type()); + int index = - static_cast(code); + go_assert(index > 0 + && static_cast(index) < this->builtin_types_.size()); + this->builtin_types_[index] = named_object->type_value(); +} + +// Read an identifier from the stream. + +std::string +Import::read_identifier() +{ + std::string ret; + Stream* stream = this->stream_; + int c; + while (true) + { + c = stream->peek_char(); + if (c == -1 || c == ' ' || c == ';') + break; + ret += c; + stream->advance(1); + } + return ret; +} + +// Read a name from the stream. + +std::string +Import::read_name() +{ + std::string ret = this->read_identifier(); + if (ret == "?") + ret.clear(); + else if (!Lex::is_exported_name(ret)) + ret = '.' + this->package_->pkgpath() + '.' + ret; + return ret; +} + +// Turn a string into a integer with appropriate error handling. + +bool +Import::string_to_int(const std::string &s, bool is_neg_ok, int* ret) +{ + char* end; + long prio = strtol(s.c_str(), &end, 10); + if (*end != '\0' || prio > 0x7fffffff || (prio < 0 && !is_neg_ok)) + { + error_at(this->location_, "invalid integer in import data at %d", + this->stream_->pos()); + this->stream_->set_saw_error(); + return false; + } + *ret = prio; + return true; +} + +// Class Import::Stream. + +Import::Stream::Stream() + : pos_(0), saw_error_(false) +{ +} + +Import::Stream::~Stream() +{ +} + +// Return the next character to come from the stream. + +int +Import::Stream::peek_char() +{ + const char* read; + if (!this->do_peek(1, &read)) + return -1; + // Make sure we return an unsigned char, so that we don't get + // confused by \xff. + unsigned char ret = *read; + return ret; +} + +// Return true if the next LENGTH characters from the stream match +// BYTES + +bool +Import::Stream::match_bytes(const char* bytes, size_t length) +{ + const char* read; + if (!this->do_peek(length, &read)) + return false; + return memcmp(bytes, read, length) == 0; +} + +// Require that the next LENGTH bytes from the stream match BYTES. + +void +Import::Stream::require_bytes(Location location, const char* bytes, + size_t length) +{ + const char* read; + if (!this->do_peek(length, &read) + || memcmp(bytes, read, length) != 0) + { + if (!this->saw_error_) + error_at(location, "import error at %d: expected %<%.*s%>", + this->pos(), static_cast(length), bytes); + this->saw_error_ = true; + return; + } + this->advance(length); +} + +// Class Stream_from_file. + +Stream_from_file::Stream_from_file(int fd) + : fd_(fd), data_() +{ + if (lseek(fd, 0, SEEK_SET) != 0) + { + error("lseek failed: %m"); + this->set_saw_error(); + } +} + +Stream_from_file::~Stream_from_file() +{ + close(this->fd_); +} + +// Read next bytes. + +bool +Stream_from_file::do_peek(size_t length, const char** bytes) +{ + if (this->data_.length() <= length) + { + *bytes = this->data_.data(); + return true; + } + // Don't bother to handle the general case, since we don't need it. + go_assert(length < 64); + char buf[64]; + ssize_t got = read(this->fd_, buf, length); + + if (got < 0) + { + if (!this->saw_error()) + error("read failed: %m"); + this->set_saw_error(); + return false; + } + + if (lseek(this->fd_, - got, SEEK_CUR) != 0) + { + if (!this->saw_error()) + error("lseek failed: %m"); + this->set_saw_error(); + return false; + } + + if (static_cast(got) < length) + return false; + + this->data_.assign(buf, got); + + *bytes = this->data_.data(); + return true; +} + +// Advance. + +void +Stream_from_file::do_advance(size_t skip) +{ + if (lseek(this->fd_, skip, SEEK_CUR) != 0) + { + if (!this->saw_error()) + error("lseek failed: %m"); + this->set_saw_error(); + } + if (!this->data_.empty()) + { + if (this->data_.length() < skip) + this->data_.erase(0, skip); + else + this->data_.clear(); + } +} diff --git a/gcc-4.9/gcc/go/gofrontend/import.h b/gcc-4.9/gcc/go/gofrontend/import.h new file mode 100644 index 000000000..9917937e4 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/import.h @@ -0,0 +1,364 @@ +// import.h -- Go frontend import declarations. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_IMPORT_H +#define GO_IMPORT_H + +#include "export.h" +#include "go-linemap.h" + +class Gogo; +class Package; +class Type; +class Named_object; +class Named_type; +class Expression; + +// This class manages importing Go declarations. + +class Import +{ + public: + // The Stream class is an interface used to read the data. The + // caller should instantiate a child of this class. + class Stream + { + public: + Stream(); + virtual ~Stream(); + + // Return whether we have seen an error. + bool + saw_error() const + { return this->saw_error_; } + + // Record that we've seen an error. + void + set_saw_error() + { this->saw_error_ = true; } + + // Return the next character (a value from 0 to 0xff) without + // advancing. Returns -1 at end of stream. + int + peek_char(); + + // Look for LENGTH characters, setting *BYTES to point to them. + // Returns false if the bytes are not available. Does not + // advance. + bool + peek(size_t length, const char** bytes) + { return this->do_peek(length, bytes); } + + // Return the next character (a value from 0 to 0xff) and advance + // the read position by 1. Returns -1 at end of stream. + int + get_char() + { + int c = this->peek_char(); + this->advance(1); + return c; + } + + // Return true if at the end of the stream. + bool + at_eof() + { return this->peek_char() == -1; } + + // Return true if the next bytes match STR. + bool + match_c_string(const char* str) + { return this->match_bytes(str, strlen(str)); } + + // Return true if the next LENGTH bytes match BYTES. + bool + match_bytes(const char* bytes, size_t length); + + // Give an error if the next bytes do not match STR. Advance the + // read position by the length of STR. + void + require_c_string(Location location, const char* str) + { this->require_bytes(location, str, strlen(str)); } + + // Given an error if the next LENGTH bytes do not match BYTES. + // Advance the read position by LENGTH. + void + require_bytes(Location, const char* bytes, size_t length); + + // Advance the read position by SKIP bytes. + void + advance(size_t skip) + { + this->do_advance(skip); + this->pos_ += skip; + } + + // Return the current read position. This returns int because it + // is more convenient in error reporting. FIXME. + int + pos() + { return static_cast(this->pos_); } + + protected: + // This function should set *BYTES to point to a buffer holding + // the LENGTH bytes at the current read position. It should + // return false if the bytes are not available. This should not + // change the current read position. + virtual bool + do_peek(size_t length, const char** bytes) = 0; + + // This function should advance the current read position LENGTH + // bytes. + virtual void + do_advance(size_t skip) = 0; + + private: + // The current read position. + size_t pos_; + // True if we've seen an error reading from this stream. + bool saw_error_; + }; + + // 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, + const std::string& relative_import_path); + + // Constructor. + Import(Stream*, Location); + + // Register the builtin types. + void + register_builtin_types(Gogo*); + + // Import everything defined in the stream. LOCAL_NAME is the local + // name to be used for bindings; if it is the string "." then + // bindings should be inserted in the global scope. If LOCAL_NAME + // is the empty string then the name of the package itself is the + // local name. This returns the imported package, or NULL on error. + Package* + import(Gogo*, const std::string& local_name, bool is_local_name_exported); + + // The location of the import statement. + Location + location() const + { return this->location_; } + + // Return the package we are importing. + Package* + package() const + { return this->package_; } + + // Return the next character. + int + peek_char() + { return this->stream_->peek_char(); } + + // Return the next character and advance. + int + get_char() + { return this->stream_->get_char(); } + + // Return true at the end of the stream. + bool + at_eof() + { return this->stream_->at_eof(); } + + // Return whether the next bytes match STR. + bool + match_c_string(const char* str) + { return this->stream_->match_c_string(str); } + + // Require that the next bytes match STR. + void + require_c_string(const char* str) + { this->stream_->require_c_string(this->location_, str); } + + // Advance the stream SKIP bytes. + void + advance(size_t skip) + { this->stream_->advance(skip); } + + // Read an identifier. + std::string + read_identifier(); + + // Read a name. This is like read_identifier, except that a "?" is + // returned as an empty string. This matches Export::write_name. + std::string + read_name(); + + // Read a type. + Type* + read_type(); + + private: + static Stream* + try_package_in_directory(const std::string&, Location); + + static int + try_suffixes(std::string*); + + static Stream* + find_export_data(const std::string& filename, int fd, Location); + + static Stream* + find_object_export_data(const std::string& filename, int fd, + off_t offset, Location); + + static const int archive_magic_len = 8; + + static bool + is_archive_magic(const char*); + + static Stream* + find_archive_export_data(const std::string& filename, int fd, + Location); + + // Read an import line. + void + read_one_import(); + + // Read the import control functions. + void + read_import_init_fns(Gogo*); + + // Import a constant. + void + import_const(); + + // Import a type. + void + import_type(); + + // Import a variable. + void + import_var(); + + // Import a function. + Named_object* + import_func(Package*); + + // Register a single builtin type. + void + register_builtin_type(Gogo*, const char* name, Builtin_code); + + // Get an integer from a string. + bool + string_to_int(const std::string&, bool is_neg_ok, int* ret); + + // The general IR. + Gogo* gogo_; + // The stream from which to read import data. + Stream* stream_; + // The location of the import statement we are processing. + Location location_; + // The package we are importing. + Package* package_; + // Whether to add new objects to the global scope, rather than to a + // package scope. + bool add_to_globals_; + // Mapping from negated builtin type codes to Type structures. + std::vector builtin_types_; + // Mapping from exported type codes to Type structures. + std::vector types_; +}; + +// Read import data from a string. + +class Stream_from_string : public Import::Stream +{ + public: + Stream_from_string(const std::string& str) + : str_(str), pos_(0) + { } + + protected: + bool + do_peek(size_t length, const char** bytes) + { + if (this->pos_ + length > this->str_.length()) + return false; + *bytes = this->str_.data() + this->pos_; + return true; + } + + void + do_advance(size_t len) + { this->pos_ += len; } + + private: + // The string of data we are reading. + std::string str_; + // The current position within the string. + size_t pos_; +}; + +// Read import data from a buffer allocated using malloc. + +class Stream_from_buffer : public Import::Stream +{ + public: + Stream_from_buffer(char* buf, size_t length) + : buf_(buf), length_(length), pos_(0) + { } + + ~Stream_from_buffer() + { free(this->buf_); } + + protected: + bool + do_peek(size_t length, const char** bytes) + { + if (this->pos_ + length > this->length_) + return false; + *bytes = this->buf_ + this->pos_; + return true; + } + + void + do_advance(size_t len) + { this->pos_ += len; } + + private: + // The data we are reading. + char* buf_; + // The length of the buffer. + size_t length_; + // The current position within the buffer. + size_t pos_; +}; + +// Read import data from an open file descriptor. + +class Stream_from_file : public Import::Stream +{ + public: + Stream_from_file(int fd); + + ~Stream_from_file(); + + protected: + bool + do_peek(size_t, const char**); + + void + do_advance(size_t); + + private: + // No copying. + Stream_from_file(const Stream_from_file&); + Stream_from_file& operator=(const Stream_from_file&); + + // The file descriptor. + int fd_; + // Data read from the file. + std::string data_; +}; + +#endif // !defined(GO_IMPORT_H) diff --git a/gcc-4.9/gcc/go/gofrontend/lex.cc b/gcc-4.9/gcc/go/gofrontend/lex.cc new file mode 100644 index 000000000..161696347 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/lex.cc @@ -0,0 +1,2440 @@ +// lex.cc -- Go frontend lexer. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "lex.h" + +// Manage mapping from keywords to the Keyword codes. + +class Keywords +{ + public: + // The structure which maps keywords to codes. + struct Mapping + { + // Keyword string. + const char* keystring; + // Keyword code. + Keyword keycode; + }; + + // Return the parsecode corresponding to KEYSTRING, or + // KEYWORD_INVALID if it is not a keyword. + Keyword + keyword_to_code(const char* keyword, size_t len) const; + + // Return the string for a keyword. + const char* + keyword_to_string(Keyword) const; + + private: + static const Mapping mapping_[]; + static const int count_; +}; + +// Mapping from keyword string to keyword code. This array must be +// kept in sorted order, and the order must match the Keyword enum. +// Strings are looked up using bsearch. + +const Keywords::Mapping +Keywords::mapping_[] = +{ + { NULL, KEYWORD_INVALID }, + { "__asm__", KEYWORD_ASM }, + { "break", KEYWORD_BREAK }, + { "case", KEYWORD_CASE }, + { "chan", KEYWORD_CHAN }, + { "const", KEYWORD_CONST }, + { "continue", KEYWORD_CONTINUE }, + { "default", KEYWORD_DEFAULT }, + { "defer", KEYWORD_DEFER }, + { "else", KEYWORD_ELSE }, + { "fallthrough", KEYWORD_FALLTHROUGH }, + { "for", KEYWORD_FOR }, + { "func", KEYWORD_FUNC }, + { "go", KEYWORD_GO }, + { "goto", KEYWORD_GOTO }, + { "if", KEYWORD_IF }, + { "import", KEYWORD_IMPORT }, + { "interface", KEYWORD_INTERFACE }, + { "map", KEYWORD_MAP }, + { "package", KEYWORD_PACKAGE }, + { "range", KEYWORD_RANGE }, + { "return", KEYWORD_RETURN }, + { "select", KEYWORD_SELECT }, + { "struct", KEYWORD_STRUCT }, + { "switch", KEYWORD_SWITCH }, + { "type", KEYWORD_TYPE }, + { "var", KEYWORD_VAR } +}; + +// Number of entries in the map. + +const int Keywords::count_ = + sizeof(Keywords::mapping_) / sizeof(Keywords::mapping_[0]); + +// Comparison function passed to bsearch. + +extern "C" +{ + +struct Keywords_search_key +{ + const char* str; + size_t len; +}; + +static int +keyword_compare(const void* keyv, const void* mapv) +{ + const Keywords_search_key* key = + static_cast(keyv); + const Keywords::Mapping* map = + static_cast(mapv); + if (map->keystring == NULL) + return 1; + int i = strncmp(key->str, map->keystring, key->len); + if (i != 0) + return i; + if (map->keystring[key->len] != '\0') + return -1; + return 0; +} + +} // End extern "C". + +// Convert a string to a keyword code. Return KEYWORD_INVALID if the +// string is not a keyword. + +Keyword +Keywords::keyword_to_code(const char* keyword, size_t len) const +{ + Keywords_search_key key; + key.str = keyword; + key.len = len; + void* mapv = bsearch(&key, + this->mapping_, + this->count_, + sizeof(this->mapping_[0]), + keyword_compare); + if (mapv == NULL) + return KEYWORD_INVALID; + Mapping* map = static_cast(mapv); + return map->keycode; +} + +// Convert a keyword code to a string. + +const char* +Keywords::keyword_to_string(Keyword code) const +{ + go_assert(code > KEYWORD_INVALID && code < this->count_); + const Mapping* map = &this->mapping_[code]; + go_assert(map->keycode == code); + return map->keystring; +} + +// There is one instance of the Keywords class. + +static Keywords keywords; + +// Class Token. + +// Make a general token. + +Token::Token(Classification classification, Location location) + : classification_(classification), location_(location) +{ +} + +// Destroy a token. + +Token::~Token() +{ + this->clear(); +} + +// Clear a token--release memory. + +void +Token::clear() +{ + if (this->classification_ == TOKEN_INTEGER + || this->classification_ == TOKEN_CHARACTER) + mpz_clear(this->u_.integer_value); + else if (this->classification_ == TOKEN_FLOAT + || this->classification_ == TOKEN_IMAGINARY) + mpfr_clear(this->u_.float_value); +} + +// Construct a token. + +Token::Token(const Token& tok) + : classification_(tok.classification_), location_(tok.location_) +{ + switch (this->classification_) + { + case TOKEN_INVALID: + case TOKEN_EOF: + break; + case TOKEN_KEYWORD: + this->u_.keyword = tok.u_.keyword; + break; + case TOKEN_IDENTIFIER: + case TOKEN_STRING: + this->u_.string_value = tok.u_.string_value; + break; + case TOKEN_OPERATOR: + this->u_.op = tok.u_.op; + break; + case TOKEN_CHARACTER: + case TOKEN_INTEGER: + mpz_init_set(this->u_.integer_value, tok.u_.integer_value); + break; + case TOKEN_FLOAT: + case TOKEN_IMAGINARY: + mpfr_init_set(this->u_.float_value, tok.u_.float_value, GMP_RNDN); + break; + default: + go_unreachable(); + } +} + +// Assign to a token. + +Token& +Token::operator=(const Token& tok) +{ + this->clear(); + this->classification_ = tok.classification_; + this->location_ = tok.location_; + switch (tok.classification_) + { + case TOKEN_INVALID: + case TOKEN_EOF: + break; + case TOKEN_KEYWORD: + this->u_.keyword = tok.u_.keyword; + break; + case TOKEN_IDENTIFIER: + this->u_.identifier_value.name = tok.u_.identifier_value.name; + this->u_.identifier_value.is_exported = + tok.u_.identifier_value.is_exported; + break; + case TOKEN_STRING: + this->u_.string_value = tok.u_.string_value; + break; + case TOKEN_OPERATOR: + this->u_.op = tok.u_.op; + break; + case TOKEN_CHARACTER: + case TOKEN_INTEGER: + mpz_init_set(this->u_.integer_value, tok.u_.integer_value); + break; + case TOKEN_FLOAT: + case TOKEN_IMAGINARY: + mpfr_init_set(this->u_.float_value, tok.u_.float_value, GMP_RNDN); + break; + default: + go_unreachable(); + } + return *this; +} + +// Print the token for debugging. + +void +Token::print(FILE* file) const +{ + switch (this->classification_) + { + case TOKEN_INVALID: + fprintf(file, "invalid"); + break; + case TOKEN_EOF: + fprintf(file, "EOF"); + break; + case TOKEN_KEYWORD: + fprintf(file, "keyword %s", keywords.keyword_to_string(this->u_.keyword)); + break; + case TOKEN_IDENTIFIER: + fprintf(file, "identifier \"%s\"", this->u_.string_value->c_str()); + break; + case TOKEN_STRING: + fprintf(file, "quoted string \"%s\"", this->u_.string_value->c_str()); + break; + case TOKEN_CHARACTER: + fprintf(file, "character "); + mpz_out_str(file, 10, this->u_.integer_value); + break; + case TOKEN_INTEGER: + fprintf(file, "integer "); + mpz_out_str(file, 10, this->u_.integer_value); + break; + case TOKEN_FLOAT: + fprintf(file, "float "); + mpfr_out_str(file, 10, 0, this->u_.float_value, GMP_RNDN); + break; + case TOKEN_IMAGINARY: + fprintf(file, "imaginary "); + mpfr_out_str(file, 10, 0, this->u_.float_value, GMP_RNDN); + break; + case TOKEN_OPERATOR: + fprintf(file, "operator "); + switch (this->u_.op) + { + case OPERATOR_INVALID: + fprintf(file, "invalid"); + break; + case OPERATOR_OROR: + fprintf(file, "||"); + break; + case OPERATOR_ANDAND: + fprintf(file, "&&"); + break; + case OPERATOR_EQEQ: + fprintf(file, "=="); + break; + case OPERATOR_NOTEQ: + fprintf(file, "!="); + break; + case OPERATOR_LT: + fprintf(file, "<"); + break; + case OPERATOR_LE: + fprintf(file, "<="); + break; + case OPERATOR_GT: + fprintf(file, ">"); + break; + case OPERATOR_GE: + fprintf(file, ">="); + break; + case OPERATOR_PLUS: + fprintf(file, "+"); + break; + case OPERATOR_MINUS: + fprintf(file, "-"); + break; + case OPERATOR_OR: + fprintf(file, "|"); + break; + case OPERATOR_XOR: + fprintf(file, "^"); + break; + case OPERATOR_MULT: + fprintf(file, "*"); + break; + case OPERATOR_DIV: + fprintf(file, "/"); + break; + case OPERATOR_MOD: + fprintf(file, "%%"); + break; + case OPERATOR_LSHIFT: + fprintf(file, "<<"); + break; + case OPERATOR_RSHIFT: + fprintf(file, ">>"); + break; + case OPERATOR_AND: + fprintf(file, "&"); + break; + case OPERATOR_BITCLEAR: + fprintf(file, "&^"); + break; + case OPERATOR_NOT: + fprintf(file, "!"); + break; + case OPERATOR_CHANOP: + fprintf(file, "<-"); + break; + case OPERATOR_EQ: + fprintf(file, "="); + break; + case OPERATOR_PLUSEQ: + fprintf(file, "+="); + break; + case OPERATOR_MINUSEQ: + fprintf(file, "-="); + break; + case OPERATOR_OREQ: + fprintf(file, "|="); + break; + case OPERATOR_XOREQ: + fprintf(file, "^="); + break; + case OPERATOR_MULTEQ: + fprintf(file, "*="); + break; + case OPERATOR_DIVEQ: + fprintf(file, "/="); + break; + case OPERATOR_MODEQ: + fprintf(file, "%%="); + break; + case OPERATOR_LSHIFTEQ: + fprintf(file, "<<="); + break; + case OPERATOR_RSHIFTEQ: + fprintf(file, ">>="); + break; + case OPERATOR_ANDEQ: + fprintf(file, "&="); + break; + case OPERATOR_BITCLEAREQ: + fprintf(file, "&^="); + break; + case OPERATOR_PLUSPLUS: + fprintf(file, "++"); + break; + case OPERATOR_MINUSMINUS: + fprintf(file, "--"); + break; + case OPERATOR_COLON: + fprintf(file, ":"); + break; + case OPERATOR_COLONEQ: + fprintf(file, ":="); + break; + case OPERATOR_SEMICOLON: + fprintf(file, ";"); + break; + case OPERATOR_DOT: + fprintf(file, "."); + break; + case OPERATOR_COMMA: + fprintf(file, ","); + break; + case OPERATOR_LPAREN: + fprintf(file, "("); + break; + case OPERATOR_RPAREN: + fprintf(file, ")"); + break; + case OPERATOR_LCURLY: + fprintf(file, "{"); + break; + case OPERATOR_RCURLY: + fprintf(file, "}"); + break; + case OPERATOR_LSQUARE: + fprintf(file, "["); + break; + case OPERATOR_RSQUARE: + fprintf(file, "]"); + break; + default: + go_unreachable(); + } + break; + default: + go_unreachable(); + } +} + +// Class Lex. + +Lex::Lex(const char* input_file_name, FILE* input_file, Linemap* linemap) + : input_file_name_(input_file_name), input_file_(input_file), + linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0), + lineoff_(0), lineno_(0), add_semi_at_eol_(false), saw_nointerface_(false), + extern_() +{ + this->linebuf_ = new char[this->linebufsize_]; + this->linemap_->start_file(input_file_name, 0); +} + +Lex::~Lex() +{ + delete[] this->linebuf_; +} + +// Read a new line from the file. + +ssize_t +Lex::get_line() +{ + char* buf = this->linebuf_; + size_t size = this->linebufsize_; + + FILE* file = this->input_file_; + size_t cur = 0; + while (true) + { + int c = getc(file); + if (c == EOF) + { + if (cur == 0) + return -1; + break; + } + if (cur + 1 >= size) + { + size_t ns = 2 * size + 1; + if (ns < size || static_cast(ns) < 0) + error_at(this->location(), "out of memory"); + char* nb = new char[ns]; + memcpy(nb, buf, cur); + delete[] buf; + buf = nb; + size = ns; + } + buf[cur] = c; + ++cur; + + if (c == '\n') + break; + } + + buf[cur] = '\0'; + + this->linebuf_ = buf; + this->linebufsize_ = size; + + return cur; +} + +// See if we need to read a new line. Return true if there is a new +// line, false if we are at EOF. + +bool +Lex::require_line() +{ + if (this->lineoff_ < this->linesize_) + return true; + + ssize_t got = this->get_line(); + if (got < 0) + return false; + ++this->lineno_; + this->linesize_= got; + this->lineoff_ = 0; + + this->linemap_->start_line(this->lineno_, this->linesize_); + + return true; +} + +// Get the current location. + +Location +Lex::location() const +{ + return this->linemap_->get_location(this->lineoff_ + 1); +} + +// Get a location slightly before the current one. This is used for +// slightly more efficient handling of operator tokens. + +Location +Lex::earlier_location(int chars) const +{ + return this->linemap_->get_location(this->lineoff_ + 1 - chars); +} + +// Get the next token. + +Token +Lex::next_token() +{ + bool saw_cpp_comment = false; + while (true) + { + if (!this->require_line()) + { + bool add_semi_at_eol = this->add_semi_at_eol_; + this->add_semi_at_eol_ = false; + if (add_semi_at_eol) + return this->make_operator(OPERATOR_SEMICOLON, 1); + return this->make_eof_token(); + } + + if (!saw_cpp_comment) + this->extern_.clear(); + saw_cpp_comment = false; + + const char* p = this->linebuf_ + this->lineoff_; + const char* pend = this->linebuf_ + this->linesize_; + + while (p < pend) + { + unsigned char cc = *p; + switch (cc) + { + case ' ': case '\t': case '\r': + ++p; + // Skip whitespace quickly. + while (*p == ' ' || *p == '\t' || *p == '\r') + ++p; + break; + + case '\n': + { + ++p; + bool add_semi_at_eol = this->add_semi_at_eol_; + this->add_semi_at_eol_ = false; + if (add_semi_at_eol) + { + this->lineoff_ = p - this->linebuf_; + return this->make_operator(OPERATOR_SEMICOLON, 1); + } + } + break; + + case '/': + if (p[1] == '/') + { + this->lineoff_ = p + 2 - this->linebuf_; + this->skip_cpp_comment(); + p = pend; + if (p[-1] == '\n' && this->add_semi_at_eol_) + --p; + saw_cpp_comment = true; + } + else if (p[1] == '*') + { + this->lineoff_ = p - this->linebuf_; + Location location = this->location(); + if (!this->skip_c_comment()) + return Token::make_invalid_token(location); + p = this->linebuf_ + this->lineoff_; + pend = this->linebuf_ + this->linesize_; + } + else if (p[1] == '=') + { + this->add_semi_at_eol_ = false; + this->lineoff_ = p + 2 - this->linebuf_; + return this->make_operator(OPERATOR_DIVEQ, 2); + } + else + { + this->add_semi_at_eol_ = false; + this->lineoff_ = p + 1 - this->linebuf_; + return this->make_operator(OPERATOR_DIV, 1); + } + break; + + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': + case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case '_': + this->lineoff_ = p - this->linebuf_; + return this->gather_identifier(); + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + this->add_semi_at_eol_ = true; + this->lineoff_ = p - this->linebuf_; + return this->gather_number(); + + case '\'': + this->add_semi_at_eol_ = true; + this->lineoff_ = p - this->linebuf_; + return this->gather_character(); + + case '"': + this->add_semi_at_eol_ = true; + this->lineoff_ = p - this->linebuf_; + return this->gather_string(); + + case '`': + this->add_semi_at_eol_ = true; + this->lineoff_ = p - this->linebuf_; + return this->gather_raw_string(); + + case '<': + case '>': + case '&': + if (p + 2 < pend) + { + this->add_semi_at_eol_ = false; + Operator op = this->three_character_operator(cc, p[1], p[2]); + if (op != OPERATOR_INVALID) + { + this->lineoff_ = p + 3 - this->linebuf_; + return this->make_operator(op, 3); + } + } + // Fall through. + case '|': + case '=': + case '!': + case '+': + case '-': + case '^': + case '*': + // '/' handled above. + case '%': + case ':': + case ';': + case ',': + case '(': case ')': + case '{': case '}': + case '[': case ']': + { + this->add_semi_at_eol_ = false; + Operator op = this->two_character_operator(cc, p[1]); + int chars; + if (op != OPERATOR_INVALID) + { + ++p; + chars = 2; + } + else + { + op = this->one_character_operator(cc); + chars = 1; + } + this->lineoff_ = p + 1 - this->linebuf_; + return this->make_operator(op, chars); + } + + case '.': + if (p[1] >= '0' && p[1] <= '9') + { + this->add_semi_at_eol_ = true; + this->lineoff_ = p - this->linebuf_; + return this->gather_number(); + } + if (p[1] == '.' && p[2] == '.') + { + this->add_semi_at_eol_ = false; + this->lineoff_ = p + 3 - this->linebuf_; + return this->make_operator(OPERATOR_ELLIPSIS, 3); + } + this->add_semi_at_eol_ = false; + this->lineoff_ = p + 1 - this->linebuf_; + return this->make_operator(OPERATOR_DOT, 1); + + default: + { + unsigned int ci; + bool issued_error; + this->lineoff_ = p - this->linebuf_; + 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(); + + if (!issued_error) + error_at(this->location(), + "invalid character 0x%x in input file", + ci); + + p = pend; + + break; + } + } + } + + this->lineoff_ = p - this->linebuf_; + } +} + +// Fetch one UTF-8 character from a string. Set *VALUE to the value. +// Return the number of bytes read from the string. Returns 0 if the +// string does not point to a valid UTF-8 character. + +int +Lex::fetch_char(const char* p, unsigned int* value) +{ + unsigned char c = *p; + if (c <= 0x7f) + { + *value = c; + return 1; + } + else if ((c & 0xe0) == 0xc0 + && (p[1] & 0xc0) == 0x80) + { + *value = (((c & 0x1f) << 6) + + (p[1] & 0x3f)); + if (*value <= 0x7f) + { + *value = 0xfffd; + return 0; + } + return 2; + } + else if ((c & 0xf0) == 0xe0 + && (p[1] & 0xc0) == 0x80 + && (p[2] & 0xc0) == 0x80) + { + *value = (((c & 0xf) << 12) + + ((p[1] & 0x3f) << 6) + + (p[2] & 0x3f)); + if (*value <= 0x7ff) + { + *value = 0xfffd; + return 0; + } + return 3; + } + else if ((c & 0xf8) == 0xf0 + && (p[1] & 0xc0) == 0x80 + && (p[2] & 0xc0) == 0x80 + && (p[3] & 0xc0) == 0x80) + { + *value = (((c & 0x7) << 18) + + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + + (p[3] & 0x3f)); + if (*value <= 0xffff) + { + *value = 0xfffd; + return 0; + } + return 4; + } + else + { + /* Invalid encoding. Return the Unicode replacement + character. */ + *value = 0xfffd; + return 0; + } +} + +// Advance one UTF-8 character. Return the pointer beyond the +// character. Set *VALUE to the value. Set *ISSUED_ERROR if an error +// was issued. + +const char* +Lex::advance_one_utf8_char(const char* p, unsigned int* value, + bool* issued_error) +{ + *issued_error = false; + + if (*p == '\0') + { + error_at(this->location(), "invalid NUL byte"); + *issued_error = true; + *value = 0; + return p + 1; + } + + int adv = Lex::fetch_char(p, value); + if (adv == 0) + { + error_at(this->location(), "invalid UTF-8 encoding"); + *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; +} + +// Pick up an identifier. + +Token +Lex::gather_identifier() +{ + const char* pstart = this->linebuf_ + this->lineoff_; + const char* p = pstart; + const char* pend = this->linebuf_ + this->linesize_; + bool is_first = true; + bool is_exported = false; + bool has_non_ascii_char = false; + std::string buf; + while (p < pend) + { + unsigned char cc = *p; + if (cc <= 0x7f) + { + if ((cc < 'A' || cc > 'Z') + && (cc < 'a' || cc > 'z') + && cc != '_' + && (cc < '0' || cc > '9')) + { + // Check for an invalid character here, as we get better + // error behaviour if we swallow them as part of the + // identifier we are building. + if ((cc >= ' ' && cc < 0x7f) + || cc == '\t' + || cc == '\r' + || cc == '\n') + break; + + this->lineoff_ = p - this->linebuf_; + error_at(this->location(), + "invalid character 0x%x in identifier", + cc); + if (!has_non_ascii_char) + { + buf.assign(pstart, p - pstart); + has_non_ascii_char = true; + } + if (!Lex::is_invalid_identifier(buf)) + buf.append("$INVALID$"); + } + ++p; + if (is_first) + { + is_exported = cc >= 'A' && cc <= 'Z'; + is_first = false; + } + if (has_non_ascii_char) + buf.push_back(cc); + } + else + { + unsigned int ci; + bool issued_error; + this->lineoff_ = p - this->linebuf_; + const char* pnext = this->advance_one_utf8_char(p, &ci, + &issued_error); + bool is_invalid = false; + if (!Lex::is_unicode_letter(ci) && !Lex::is_unicode_digit(ci)) + { + // There is no valid place for a non-ASCII character + // other than an identifier, so we get better error + // handling behaviour if we swallow this character after + // giving an error. + if (!issued_error) + error_at(this->location(), + "invalid character 0x%x in identifier", + ci); + is_invalid = true; + } + if (is_first) + { + is_exported = Lex::is_unicode_uppercase(ci); + is_first = false; + } + if (!has_non_ascii_char) + { + buf.assign(pstart, p - pstart); + has_non_ascii_char = true; + } + if (is_invalid && !Lex::is_invalid_identifier(buf)) + buf.append("$INVALID$"); + buf.append(p, pnext - p); + p = pnext; + } + } + Location location = this->location(); + this->add_semi_at_eol_ = true; + this->lineoff_ = p - this->linebuf_; + if (has_non_ascii_char) + return Token::make_identifier_token(buf, is_exported, location); + else + { + Keyword code = keywords.keyword_to_code(pstart, p - pstart); + if (code == KEYWORD_INVALID) + return Token::make_identifier_token(std::string(pstart, p - pstart), + is_exported, location); + else + { + switch (code) + { + case KEYWORD_BREAK: + case KEYWORD_CONTINUE: + case KEYWORD_FALLTHROUGH: + case KEYWORD_RETURN: + break; + default: + this->add_semi_at_eol_ = false; + break; + } + return Token::make_keyword_token(code, location); + } + } +} + +// Return whether C is a hex digit. + +bool +Lex::is_hex_digit(char c) +{ + return ((c >= '0' && c <= '9') + || (c >= 'A' && c <= 'F') + || (c >= 'a' && c <= 'f')); +} + +// Return whether an exponent could start at P. + +bool +Lex::could_be_exponent(const char* p, const char* pend) +{ + if (*p != 'e' && *p != 'E') + return false; + ++p; + if (p >= pend) + return false; + if (*p == '+' || *p == '-') + { + ++p; + if (p >= pend) + return false; + } + return *p >= '0' && *p <= '9'; +} + +// Pick up a number. + +Token +Lex::gather_number() +{ + const char* pstart = this->linebuf_ + this->lineoff_; + const char* p = pstart; + const char* pend = this->linebuf_ + this->linesize_; + + Location location = this->location(); + + bool neg = false; + if (*p == '+') + ++p; + else if (*p == '-') + { + ++p; + neg = true; + } + + const char* pnum = p; + if (*p == '0') + { + int base; + if ((p[1] == 'x' || p[1] == 'X') + && Lex::is_hex_digit(p[2])) + { + base = 16; + p += 2; + pnum = p; + while (p < pend) + { + if (!Lex::is_hex_digit(*p)) + break; + ++p; + } + } + else + { + base = 8; + pnum = p; + while (p < pend) + { + if (*p < '0' || *p > '7') + break; + ++p; + } + } + + // A partial token that looks like an octal literal might actually be the + // beginning of a floating-point or imaginary literal. + if (base == 16 || (*p != '.' && *p != 'i' && !Lex::could_be_exponent(p, pend))) + { + std::string s(pnum, p - pnum); + mpz_t val; + int r = mpz_init_set_str(val, s.c_str(), base); + go_assert(r == 0); + + if (neg) + mpz_neg(val, val); + + this->lineoff_ = p - this->linebuf_; + Token ret = Token::make_integer_token(val, location); + mpz_clear(val); + return ret; + } + } + + while (p < pend) + { + if (*p < '0' || *p > '9') + break; + ++p; + } + + if (*p != '.' && *p != 'i' && !Lex::could_be_exponent(p, pend)) + { + std::string s(pnum, p - pnum); + mpz_t val; + int r = mpz_init_set_str(val, s.c_str(), 10); + go_assert(r == 0); + + if (neg) + mpz_neg(val, val); + + this->lineoff_ = p - this->linebuf_; + Token ret = Token::make_integer_token(val, location); + mpz_clear(val); + return ret; + } + + if (*p != 'i') + { + bool dot = *p == '.'; + + ++p; + + if (!dot) + { + if (*p == '+' || *p == '-') + ++p; + } + + while (p < pend) + { + if (*p < '0' || *p > '9') + break; + ++p; + } + + if (dot && Lex::could_be_exponent(p, pend)) + { + ++p; + if (*p == '+' || *p == '-') + ++p; + while (p < pend) + { + if (*p < '0' || *p > '9') + break; + ++p; + } + } + } + + std::string s(pnum, p - pnum); + mpfr_t val; + int r = mpfr_init_set_str(val, s.c_str(), 10, GMP_RNDN); + go_assert(r == 0); + + if (neg) + mpfr_neg(val, val, GMP_RNDN); + + bool is_imaginary = *p == 'i'; + if (is_imaginary) + ++p; + + this->lineoff_ = p - this->linebuf_; + if (is_imaginary) + { + Token ret = Token::make_imaginary_token(val, location); + mpfr_clear(val); + return ret; + } + else + { + Token ret = Token::make_float_token(val, location); + mpfr_clear(val); + return ret; + } +} + +// Advance one character, possibly escaped. Return the pointer beyond +// the character. Set *VALUE to the character. Set *IS_CHARACTER if +// this is a character (e.g., 'a' or '\u1234') rather than a byte +// value (e.g., '\001'). + +const char* +Lex::advance_one_char(const char* p, bool is_single_quote, unsigned int* value, + bool* is_character) +{ + *value = 0; + *is_character = true; + if (*p != '\\') + { + bool issued_error; + const char* ret = this->advance_one_utf8_char(p, value, &issued_error); + if (is_single_quote + && (*value == '\'' || *value == '\n') + && !issued_error) + error_at(this->location(), "invalid character literal"); + return ret; + } + else + { + ++p; + switch (*p) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + *is_character = false; + if (p[1] >= '0' && p[1] <= '7' + && p[2] >= '0' && p[2] <= '7') + { + *value = ((Lex::octal_value(p[0]) << 6) + + (Lex::octal_value(p[1]) << 3) + + Lex::octal_value(p[2])); + if (*value > 255) + { + error_at(this->location(), "invalid octal constant"); + *value = 255; + } + return p + 3; + } + error_at(this->location(), "invalid octal character"); + return (p[1] >= '0' && p[1] <= '7' + ? p + 2 + : p + 1); + + case 'x': + case 'X': + *is_character = false; + if (Lex::is_hex_digit(p[1]) && Lex::is_hex_digit(p[2])) + { + *value = (hex_value(p[1]) << 4) + hex_value(p[2]); + return p + 3; + } + error_at(this->location(), "invalid hex character"); + return (Lex::is_hex_digit(p[1]) + ? p + 2 + : p + 1); + + case 'a': + *value = '\a'; + return p + 1; + case 'b': + *value = '\b'; + return p + 1; + case 'f': + *value = '\f'; + return p + 1; + case 'n': + *value = '\n'; + return p + 1; + case 'r': + *value = '\r'; + return p + 1; + case 't': + *value = '\t'; + return p + 1; + case 'v': + *value = '\v'; + return p + 1; + case '\\': + *value = '\\'; + return p + 1; + case '\'': + if (!is_single_quote) + error_at(this->location(), "invalid quoted character"); + *value = '\''; + return p + 1; + case '"': + if (is_single_quote) + error_at(this->location(), "invalid quoted character"); + *value = '"'; + return p + 1; + + case 'u': + if (Lex::is_hex_digit(p[1]) && Lex::is_hex_digit(p[2]) + && Lex::is_hex_digit(p[3]) && Lex::is_hex_digit(p[4])) + { + *value = ((hex_value(p[1]) << 12) + + (hex_value(p[2]) << 8) + + (hex_value(p[3]) << 4) + + hex_value(p[4])); + if (*value >= 0xd800 && *value < 0xe000) + { + error_at(this->location(), + "invalid unicode code point 0x%x", + *value); + // Use the replacement character. + *value = 0xfffd; + } + return p + 5; + } + error_at(this->location(), "invalid little unicode code point"); + return p + 1; + + case 'U': + if (Lex::is_hex_digit(p[1]) && Lex::is_hex_digit(p[2]) + && Lex::is_hex_digit(p[3]) && Lex::is_hex_digit(p[4]) + && Lex::is_hex_digit(p[5]) && Lex::is_hex_digit(p[6]) + && Lex::is_hex_digit(p[7]) && Lex::is_hex_digit(p[8])) + { + *value = ((hex_value(p[1]) << 28) + + (hex_value(p[2]) << 24) + + (hex_value(p[3]) << 20) + + (hex_value(p[4]) << 16) + + (hex_value(p[5]) << 12) + + (hex_value(p[6]) << 8) + + (hex_value(p[7]) << 4) + + hex_value(p[8])); + if (*value > 0x10ffff + || (*value >= 0xd800 && *value < 0xe000)) + { + error_at(this->location(), "invalid unicode code point 0x%x", + *value); + // Use the replacement character. + *value = 0xfffd; + } + return p + 9; + } + error_at(this->location(), "invalid big unicode code point"); + return p + 1; + + default: + error_at(this->location(), "invalid character after %<\\%>"); + *value = *p; + return p + 1; + } + } +} + +// Append V to STR. IS_CHARACTER is true for a character which should +// be stored in UTF-8, false for a general byte value which should be +// stored directly. + +void +Lex::append_char(unsigned int v, bool is_character, std::string* str, + Location location) +{ + char buf[4]; + size_t len; + if (v <= 0x7f || !is_character) + { + buf[0] = v; + len = 1; + } + else if (v <= 0x7ff) + { + buf[0] = 0xc0 + (v >> 6); + buf[1] = 0x80 + (v & 0x3f); + len = 2; + } + else + { + if (v > 0x10ffff) + { + warning_at(location, 0, + "unicode code point 0x%x out of range in string", v); + // 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); + buf[1] = 0x80 + ((v >> 6) & 0x3f); + buf[2] = 0x80 + (v & 0x3f); + len = 3; + } + else + { + buf[0] = 0xf0 + (v >> 18); + buf[1] = 0x80 + ((v >> 12) & 0x3f); + buf[2] = 0x80 + ((v >> 6) & 0x3f); + buf[3] = 0x80 + (v & 0x3f); + len = 4; + } + } + str->append(buf, len); +} + +// Pick up a character literal. + +Token +Lex::gather_character() +{ + ++this->lineoff_; + const char* pstart = this->linebuf_ + this->lineoff_; + const char* p = pstart; + + unsigned int value; + bool is_character; + p = this->advance_one_char(p, true, &value, &is_character); + + if (*p != '\'') + { + error_at(this->location(), "unterminated character constant"); + this->lineoff_ = p - this->linebuf_; + return this->make_invalid_token(); + } + + mpz_t val; + mpz_init_set_ui(val, value); + + Location location = this->location(); + this->lineoff_ = p + 1 - this->linebuf_; + Token ret = Token::make_character_token(val, location); + mpz_clear(val); + return ret; +} + +// Pick up a quoted string. + +Token +Lex::gather_string() +{ + const char* pstart = this->linebuf_ + this->lineoff_ + 1; + const char* p = pstart; + const char* pend = this->linebuf_ + this->linesize_; + + std::string value; + while (*p != '"') + { + Location loc = this->location(); + unsigned int c; + bool is_character; + this->lineoff_ = p - this->linebuf_; + p = this->advance_one_char(p, false, &c, &is_character); + if (p >= pend) + { + error_at(this->location(), "unterminated string"); + --p; + break; + } + Lex::append_char(c, is_character, &value, loc); + } + + Location location = this->location(); + this->lineoff_ = p + 1 - this->linebuf_; + return Token::make_string_token(value, location); +} + +// Pick up a raw string. + +Token +Lex::gather_raw_string() +{ + const char* p = this->linebuf_ + this->lineoff_ + 1; + const char* pend = this->linebuf_ + this->linesize_; + Location location = this->location(); + + std::string value; + while (true) + { + while (p < pend) + { + if (*p == '`') + { + this->lineoff_ = p + 1 - this->linebuf_; + return Token::make_string_token(value, location); + } + Location loc = this->location(); + unsigned int c; + bool issued_error; + this->lineoff_ = p - this->linebuf_; + p = this->advance_one_utf8_char(p, &c, &issued_error); + Lex::append_char(c, true, &value, loc); + } + this->lineoff_ = p - this->linebuf_; + if (!this->require_line()) + { + error_at(location, "unterminated raw string"); + return Token::make_string_token(value, location); + } + p = this->linebuf_ + this->lineoff_; + pend = this->linebuf_ + this->linesize_; + } +} + +// If C1 C2 C3 are a three character operator, return the code. + +Operator +Lex::three_character_operator(char c1, char c2, char c3) +{ + if (c3 == '=') + { + if (c1 == '<' && c2 == '<') + return OPERATOR_LSHIFTEQ; + else if (c1 == '>' && c2 == '>') + return OPERATOR_RSHIFTEQ; + else if (c1 == '&' && c2 == '^') + return OPERATOR_BITCLEAREQ; + } + return OPERATOR_INVALID; +} + +// If C1 C2 are a two character operator, return the code. + +Operator +Lex::two_character_operator(char c1, char c2) +{ + switch (c1) + { + case '|': + if (c2 == '|') + return OPERATOR_OROR; + else if (c2 == '=') + return OPERATOR_OREQ; + break; + case '&': + if (c2 == '&') + return OPERATOR_ANDAND; + else if (c2 == '^') + return OPERATOR_BITCLEAR; + else if (c2 == '=') + return OPERATOR_ANDEQ; + break; + case '^': + if (c2 == '=') + return OPERATOR_XOREQ; + break; + case '=': + if (c2 == '=') + return OPERATOR_EQEQ; + break; + case '!': + if (c2 == '=') + return OPERATOR_NOTEQ; + break; + case '<': + if (c2 == '=') + return OPERATOR_LE; + else if (c2 == '<') + return OPERATOR_LSHIFT; + else if (c2 == '-') + return OPERATOR_CHANOP; + break; + case '>': + if (c2 == '=') + return OPERATOR_GE; + else if (c2 == '>') + return OPERATOR_RSHIFT; + break; + case '*': + if (c2 == '=') + return OPERATOR_MULTEQ; + break; + case '/': + if (c2 == '=') + return OPERATOR_DIVEQ; + break; + case '%': + if (c2 == '=') + return OPERATOR_MODEQ; + break; + case '+': + if (c2 == '+') + { + this->add_semi_at_eol_ = true; + return OPERATOR_PLUSPLUS; + } + else if (c2 == '=') + return OPERATOR_PLUSEQ; + break; + case '-': + if (c2 == '-') + { + this->add_semi_at_eol_ = true; + return OPERATOR_MINUSMINUS; + } + else if (c2 == '=') + return OPERATOR_MINUSEQ; + break; + case ':': + if (c2 == '=') + return OPERATOR_COLONEQ; + break; + default: + break; + } + return OPERATOR_INVALID; +} + +// If character C is an operator, return the code. + +Operator +Lex::one_character_operator(char c) +{ + switch (c) + { + case '<': + return OPERATOR_LT; + case '>': + return OPERATOR_GT; + case '+': + return OPERATOR_PLUS; + case '-': + return OPERATOR_MINUS; + case '|': + return OPERATOR_OR; + case '^': + return OPERATOR_XOR; + case '*': + return OPERATOR_MULT; + case '/': + return OPERATOR_DIV; + case '%': + return OPERATOR_MOD; + case '&': + return OPERATOR_AND; + case '!': + return OPERATOR_NOT; + case '=': + return OPERATOR_EQ; + case ':': + return OPERATOR_COLON; + case ';': + return OPERATOR_SEMICOLON; + case '.': + return OPERATOR_DOT; + case ',': + return OPERATOR_COMMA; + case '(': + return OPERATOR_LPAREN; + case ')': + this->add_semi_at_eol_ = true; + return OPERATOR_RPAREN; + case '{': + return OPERATOR_LCURLY; + case '}': + this->add_semi_at_eol_ = true; + return OPERATOR_RCURLY; + case '[': + return OPERATOR_LSQUARE; + case ']': + this->add_semi_at_eol_ = true; + return OPERATOR_RSQUARE; + default: + return OPERATOR_INVALID; + } +} + +// Skip a C-style comment. + +bool +Lex::skip_c_comment() +{ + while (true) + { + if (!this->require_line()) + { + error_at(this->location(), "unterminated comment"); + return false; + } + + const char* p = this->linebuf_ + this->lineoff_; + const char* pend = this->linebuf_ + this->linesize_; + + while (p < pend) + { + if (p[0] == '*' && p + 1 < pend && p[1] == '/') + { + this->lineoff_ = p + 2 - this->linebuf_; + return true; + } + + this->lineoff_ = p - this->linebuf_; + unsigned int c; + bool issued_error; + p = this->advance_one_utf8_char(p, &c, &issued_error); + } + + this->lineoff_ = p - this->linebuf_; + } +} + +// Skip a C++-style comment. + +void +Lex::skip_cpp_comment() +{ + // Ensure that if EXTERN_ is set, it means that we just saw a + // //extern comment. + this->extern_.clear(); + + const char* p = this->linebuf_ + this->lineoff_; + const char* pend = this->linebuf_ + this->linesize_; + + // By convention, a C++ comment at the start of the line of the form + // //line FILE:LINENO + // is interpreted as setting the file name and line number of the + // next source line. + + if (this->lineoff_ == 2 + && pend - p > 5 + && memcmp(p, "line ", 5) == 0) + { + p += 5; + while (p < pend && *p == ' ') + ++p; + const char* pcolon = static_cast(memchr(p, ':', pend - p)); + if (pcolon != NULL + && pcolon[1] >= '0' + && pcolon[1] <= '9') + { + char* plend; + long lineno = strtol(pcolon + 1, &plend, 10); + if (plend > pcolon + 1 + && (plend == pend + || *plend < '0' + || *plend > '9') + && lineno > 0 + && lineno < 0x7fffffff) + { + unsigned int filelen = pcolon - p; + char* file = new char[filelen + 1]; + memcpy(file, p, filelen); + file[filelen] = '\0'; + + this->linemap_->start_file(file, lineno); + this->lineno_ = lineno - 1; + + p = plend; + } + } + } + + // As a special gccgo extension, a C++ comment at the start of the + // line of the form + // //extern NAME + // which immediately precedes a function declaration means that the + // external name of the function declaration is NAME. This is + // normally used to permit Go code to call a C function. + if (this->lineoff_ == 2 + && pend - p > 7 + && memcmp(p, "extern ", 7) == 0) + { + p += 7; + while (p < pend && (*p == ' ' || *p == '\t')) + ++p; + const char* plend = pend; + while (plend > p + && (plend[-1] == ' ' || plend[-1] == '\t' || plend[-1] == '\n')) + --plend; + if (plend > p) + this->extern_ = std::string(p, plend - p); + } + + // For field tracking analysis: a //go:nointerface comment means + // that the next interface method should not be stored in the type + // descriptor. This permits it to be discarded if it is not needed. + if (this->lineoff_ == 2 && memcmp(p, "go:nointerface", 14) == 0) + this->saw_nointerface_ = true; + + while (p < pend) + { + this->lineoff_ = p - this->linebuf_; + unsigned int c; + bool issued_error; + p = this->advance_one_utf8_char(p, &c, &issued_error); + if (issued_error) + this->extern_.clear(); + } +} + +// The Unicode tables use this struct. + +struct Unicode_range +{ + // The low end of the range. + unsigned int low; + // The high end of the range. + unsigned int high; + // The stride. This entries represents low, low + stride, low + 2 * + // stride, etc., up to high. + 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". + +static const Unicode_range unicode_digits[] = +{ + { 0x0030, 0x0039, 1}, + { 0x0660, 0x0669, 1}, + { 0x06f0, 0x06f9, 1}, + { 0x07c0, 0x07c9, 1}, + { 0x0966, 0x096f, 1}, + { 0x09e6, 0x09ef, 1}, + { 0x0a66, 0x0a6f, 1}, + { 0x0ae6, 0x0aef, 1}, + { 0x0b66, 0x0b6f, 1}, + { 0x0be6, 0x0bef, 1}, + { 0x0c66, 0x0c6f, 1}, + { 0x0ce6, 0x0cef, 1}, + { 0x0d66, 0x0d6f, 1}, + { 0x0e50, 0x0e59, 1}, + { 0x0ed0, 0x0ed9, 1}, + { 0x0f20, 0x0f29, 1}, + { 0x1040, 0x1049, 1}, + { 0x17e0, 0x17e9, 1}, + { 0x1810, 0x1819, 1}, + { 0x1946, 0x194f, 1}, + { 0x19d0, 0x19d9, 1}, + { 0x1b50, 0x1b59, 1}, + { 0xff10, 0xff19, 1}, + { 0x104a0, 0x104a9, 1}, + { 0x1d7ce, 0x1d7ff, 1}, +}; + +// A table of Unicode letters--Unicode code points classified as +// "Letter". + +static const Unicode_range unicode_letters[] = +{ + { 0x0041, 0x005a, 1}, + { 0x0061, 0x007a, 1}, + { 0x00aa, 0x00b5, 11}, + { 0x00ba, 0x00ba, 1}, + { 0x00c0, 0x00d6, 1}, + { 0x00d8, 0x00f6, 1}, + { 0x00f8, 0x02c1, 1}, + { 0x02c6, 0x02d1, 1}, + { 0x02e0, 0x02e4, 1}, + { 0x02ec, 0x02ee, 2}, + { 0x0370, 0x0374, 1}, + { 0x0376, 0x0377, 1}, + { 0x037a, 0x037d, 1}, + { 0x0386, 0x0386, 1}, + { 0x0388, 0x038a, 1}, + { 0x038c, 0x038c, 1}, + { 0x038e, 0x03a1, 1}, + { 0x03a3, 0x03f5, 1}, + { 0x03f7, 0x0481, 1}, + { 0x048a, 0x0523, 1}, + { 0x0531, 0x0556, 1}, + { 0x0559, 0x0559, 1}, + { 0x0561, 0x0587, 1}, + { 0x05d0, 0x05ea, 1}, + { 0x05f0, 0x05f2, 1}, + { 0x0621, 0x064a, 1}, + { 0x066e, 0x066f, 1}, + { 0x0671, 0x06d3, 1}, + { 0x06d5, 0x06d5, 1}, + { 0x06e5, 0x06e6, 1}, + { 0x06ee, 0x06ef, 1}, + { 0x06fa, 0x06fc, 1}, + { 0x06ff, 0x0710, 17}, + { 0x0712, 0x072f, 1}, + { 0x074d, 0x07a5, 1}, + { 0x07b1, 0x07b1, 1}, + { 0x07ca, 0x07ea, 1}, + { 0x07f4, 0x07f5, 1}, + { 0x07fa, 0x07fa, 1}, + { 0x0904, 0x0939, 1}, + { 0x093d, 0x0950, 19}, + { 0x0958, 0x0961, 1}, + { 0x0971, 0x0972, 1}, + { 0x097b, 0x097f, 1}, + { 0x0985, 0x098c, 1}, + { 0x098f, 0x0990, 1}, + { 0x0993, 0x09a8, 1}, + { 0x09aa, 0x09b0, 1}, + { 0x09b2, 0x09b2, 1}, + { 0x09b6, 0x09b9, 1}, + { 0x09bd, 0x09ce, 17}, + { 0x09dc, 0x09dd, 1}, + { 0x09df, 0x09e1, 1}, + { 0x09f0, 0x09f1, 1}, + { 0x0a05, 0x0a0a, 1}, + { 0x0a0f, 0x0a10, 1}, + { 0x0a13, 0x0a28, 1}, + { 0x0a2a, 0x0a30, 1}, + { 0x0a32, 0x0a33, 1}, + { 0x0a35, 0x0a36, 1}, + { 0x0a38, 0x0a39, 1}, + { 0x0a59, 0x0a5c, 1}, + { 0x0a5e, 0x0a5e, 1}, + { 0x0a72, 0x0a74, 1}, + { 0x0a85, 0x0a8d, 1}, + { 0x0a8f, 0x0a91, 1}, + { 0x0a93, 0x0aa8, 1}, + { 0x0aaa, 0x0ab0, 1}, + { 0x0ab2, 0x0ab3, 1}, + { 0x0ab5, 0x0ab9, 1}, + { 0x0abd, 0x0ad0, 19}, + { 0x0ae0, 0x0ae1, 1}, + { 0x0b05, 0x0b0c, 1}, + { 0x0b0f, 0x0b10, 1}, + { 0x0b13, 0x0b28, 1}, + { 0x0b2a, 0x0b30, 1}, + { 0x0b32, 0x0b33, 1}, + { 0x0b35, 0x0b39, 1}, + { 0x0b3d, 0x0b3d, 1}, + { 0x0b5c, 0x0b5d, 1}, + { 0x0b5f, 0x0b61, 1}, + { 0x0b71, 0x0b83, 18}, + { 0x0b85, 0x0b8a, 1}, + { 0x0b8e, 0x0b90, 1}, + { 0x0b92, 0x0b95, 1}, + { 0x0b99, 0x0b9a, 1}, + { 0x0b9c, 0x0b9c, 1}, + { 0x0b9e, 0x0b9f, 1}, + { 0x0ba3, 0x0ba4, 1}, + { 0x0ba8, 0x0baa, 1}, + { 0x0bae, 0x0bb9, 1}, + { 0x0bd0, 0x0bd0, 1}, + { 0x0c05, 0x0c0c, 1}, + { 0x0c0e, 0x0c10, 1}, + { 0x0c12, 0x0c28, 1}, + { 0x0c2a, 0x0c33, 1}, + { 0x0c35, 0x0c39, 1}, + { 0x0c3d, 0x0c3d, 1}, + { 0x0c58, 0x0c59, 1}, + { 0x0c60, 0x0c61, 1}, + { 0x0c85, 0x0c8c, 1}, + { 0x0c8e, 0x0c90, 1}, + { 0x0c92, 0x0ca8, 1}, + { 0x0caa, 0x0cb3, 1}, + { 0x0cb5, 0x0cb9, 1}, + { 0x0cbd, 0x0cde, 33}, + { 0x0ce0, 0x0ce1, 1}, + { 0x0d05, 0x0d0c, 1}, + { 0x0d0e, 0x0d10, 1}, + { 0x0d12, 0x0d28, 1}, + { 0x0d2a, 0x0d39, 1}, + { 0x0d3d, 0x0d3d, 1}, + { 0x0d60, 0x0d61, 1}, + { 0x0d7a, 0x0d7f, 1}, + { 0x0d85, 0x0d96, 1}, + { 0x0d9a, 0x0db1, 1}, + { 0x0db3, 0x0dbb, 1}, + { 0x0dbd, 0x0dbd, 1}, + { 0x0dc0, 0x0dc6, 1}, + { 0x0e01, 0x0e30, 1}, + { 0x0e32, 0x0e33, 1}, + { 0x0e40, 0x0e46, 1}, + { 0x0e81, 0x0e82, 1}, + { 0x0e84, 0x0e84, 1}, + { 0x0e87, 0x0e88, 1}, + { 0x0e8a, 0x0e8d, 3}, + { 0x0e94, 0x0e97, 1}, + { 0x0e99, 0x0e9f, 1}, + { 0x0ea1, 0x0ea3, 1}, + { 0x0ea5, 0x0ea7, 2}, + { 0x0eaa, 0x0eab, 1}, + { 0x0ead, 0x0eb0, 1}, + { 0x0eb2, 0x0eb3, 1}, + { 0x0ebd, 0x0ebd, 1}, + { 0x0ec0, 0x0ec4, 1}, + { 0x0ec6, 0x0ec6, 1}, + { 0x0edc, 0x0edd, 1}, + { 0x0f00, 0x0f00, 1}, + { 0x0f40, 0x0f47, 1}, + { 0x0f49, 0x0f6c, 1}, + { 0x0f88, 0x0f8b, 1}, + { 0x1000, 0x102a, 1}, + { 0x103f, 0x103f, 1}, + { 0x1050, 0x1055, 1}, + { 0x105a, 0x105d, 1}, + { 0x1061, 0x1061, 1}, + { 0x1065, 0x1066, 1}, + { 0x106e, 0x1070, 1}, + { 0x1075, 0x1081, 1}, + { 0x108e, 0x108e, 1}, + { 0x10a0, 0x10c5, 1}, + { 0x10d0, 0x10fa, 1}, + { 0x10fc, 0x10fc, 1}, + { 0x1100, 0x1159, 1}, + { 0x115f, 0x11a2, 1}, + { 0x11a8, 0x11f9, 1}, + { 0x1200, 0x1248, 1}, + { 0x124a, 0x124d, 1}, + { 0x1250, 0x1256, 1}, + { 0x1258, 0x1258, 1}, + { 0x125a, 0x125d, 1}, + { 0x1260, 0x1288, 1}, + { 0x128a, 0x128d, 1}, + { 0x1290, 0x12b0, 1}, + { 0x12b2, 0x12b5, 1}, + { 0x12b8, 0x12be, 1}, + { 0x12c0, 0x12c0, 1}, + { 0x12c2, 0x12c5, 1}, + { 0x12c8, 0x12d6, 1}, + { 0x12d8, 0x1310, 1}, + { 0x1312, 0x1315, 1}, + { 0x1318, 0x135a, 1}, + { 0x1380, 0x138f, 1}, + { 0x13a0, 0x13f4, 1}, + { 0x1401, 0x166c, 1}, + { 0x166f, 0x1676, 1}, + { 0x1681, 0x169a, 1}, + { 0x16a0, 0x16ea, 1}, + { 0x1700, 0x170c, 1}, + { 0x170e, 0x1711, 1}, + { 0x1720, 0x1731, 1}, + { 0x1740, 0x1751, 1}, + { 0x1760, 0x176c, 1}, + { 0x176e, 0x1770, 1}, + { 0x1780, 0x17b3, 1}, + { 0x17d7, 0x17dc, 5}, + { 0x1820, 0x1877, 1}, + { 0x1880, 0x18a8, 1}, + { 0x18aa, 0x18aa, 1}, + { 0x1900, 0x191c, 1}, + { 0x1950, 0x196d, 1}, + { 0x1970, 0x1974, 1}, + { 0x1980, 0x19a9, 1}, + { 0x19c1, 0x19c7, 1}, + { 0x1a00, 0x1a16, 1}, + { 0x1b05, 0x1b33, 1}, + { 0x1b45, 0x1b4b, 1}, + { 0x1b83, 0x1ba0, 1}, + { 0x1bae, 0x1baf, 1}, + { 0x1c00, 0x1c23, 1}, + { 0x1c4d, 0x1c4f, 1}, + { 0x1c5a, 0x1c7d, 1}, + { 0x1d00, 0x1dbf, 1}, + { 0x1e00, 0x1f15, 1}, + { 0x1f18, 0x1f1d, 1}, + { 0x1f20, 0x1f45, 1}, + { 0x1f48, 0x1f4d, 1}, + { 0x1f50, 0x1f57, 1}, + { 0x1f59, 0x1f5d, 2}, + { 0x1f5f, 0x1f7d, 1}, + { 0x1f80, 0x1fb4, 1}, + { 0x1fb6, 0x1fbc, 1}, + { 0x1fbe, 0x1fbe, 1}, + { 0x1fc2, 0x1fc4, 1}, + { 0x1fc6, 0x1fcc, 1}, + { 0x1fd0, 0x1fd3, 1}, + { 0x1fd6, 0x1fdb, 1}, + { 0x1fe0, 0x1fec, 1}, + { 0x1ff2, 0x1ff4, 1}, + { 0x1ff6, 0x1ffc, 1}, + { 0x2071, 0x207f, 14}, + { 0x2090, 0x2094, 1}, + { 0x2102, 0x2107, 5}, + { 0x210a, 0x2113, 1}, + { 0x2115, 0x2115, 1}, + { 0x2119, 0x211d, 1}, + { 0x2124, 0x2128, 2}, + { 0x212a, 0x212d, 1}, + { 0x212f, 0x2139, 1}, + { 0x213c, 0x213f, 1}, + { 0x2145, 0x2149, 1}, + { 0x214e, 0x214e, 1}, + { 0x2183, 0x2184, 1}, + { 0x2c00, 0x2c2e, 1}, + { 0x2c30, 0x2c5e, 1}, + { 0x2c60, 0x2c6f, 1}, + { 0x2c71, 0x2c7d, 1}, + { 0x2c80, 0x2ce4, 1}, + { 0x2d00, 0x2d25, 1}, + { 0x2d30, 0x2d65, 1}, + { 0x2d6f, 0x2d6f, 1}, + { 0x2d80, 0x2d96, 1}, + { 0x2da0, 0x2da6, 1}, + { 0x2da8, 0x2dae, 1}, + { 0x2db0, 0x2db6, 1}, + { 0x2db8, 0x2dbe, 1}, + { 0x2dc0, 0x2dc6, 1}, + { 0x2dc8, 0x2dce, 1}, + { 0x2dd0, 0x2dd6, 1}, + { 0x2dd8, 0x2dde, 1}, + { 0x2e2f, 0x2e2f, 1}, + { 0x3005, 0x3006, 1}, + { 0x3031, 0x3035, 1}, + { 0x303b, 0x303c, 1}, + { 0x3041, 0x3096, 1}, + { 0x309d, 0x309f, 1}, + { 0x30a1, 0x30fa, 1}, + { 0x30fc, 0x30ff, 1}, + { 0x3105, 0x312d, 1}, + { 0x3131, 0x318e, 1}, + { 0x31a0, 0x31b7, 1}, + { 0x31f0, 0x31ff, 1}, + { 0x3400, 0x4db5, 1}, + { 0x4e00, 0x9fc3, 1}, + { 0xa000, 0xa48c, 1}, + { 0xa500, 0xa60c, 1}, + { 0xa610, 0xa61f, 1}, + { 0xa62a, 0xa62b, 1}, + { 0xa640, 0xa65f, 1}, + { 0xa662, 0xa66e, 1}, + { 0xa67f, 0xa697, 1}, + { 0xa717, 0xa71f, 1}, + { 0xa722, 0xa788, 1}, + { 0xa78b, 0xa78c, 1}, + { 0xa7fb, 0xa801, 1}, + { 0xa803, 0xa805, 1}, + { 0xa807, 0xa80a, 1}, + { 0xa80c, 0xa822, 1}, + { 0xa840, 0xa873, 1}, + { 0xa882, 0xa8b3, 1}, + { 0xa90a, 0xa925, 1}, + { 0xa930, 0xa946, 1}, + { 0xaa00, 0xaa28, 1}, + { 0xaa40, 0xaa42, 1}, + { 0xaa44, 0xaa4b, 1}, + { 0xac00, 0xd7a3, 1}, + { 0xf900, 0xfa2d, 1}, + { 0xfa30, 0xfa6a, 1}, + { 0xfa70, 0xfad9, 1}, + { 0xfb00, 0xfb06, 1}, + { 0xfb13, 0xfb17, 1}, + { 0xfb1d, 0xfb1d, 1}, + { 0xfb1f, 0xfb28, 1}, + { 0xfb2a, 0xfb36, 1}, + { 0xfb38, 0xfb3c, 1}, + { 0xfb3e, 0xfb3e, 1}, + { 0xfb40, 0xfb41, 1}, + { 0xfb43, 0xfb44, 1}, + { 0xfb46, 0xfbb1, 1}, + { 0xfbd3, 0xfd3d, 1}, + { 0xfd50, 0xfd8f, 1}, + { 0xfd92, 0xfdc7, 1}, + { 0xfdf0, 0xfdfb, 1}, + { 0xfe70, 0xfe74, 1}, + { 0xfe76, 0xfefc, 1}, + { 0xff21, 0xff3a, 1}, + { 0xff41, 0xff5a, 1}, + { 0xff66, 0xffbe, 1}, + { 0xffc2, 0xffc7, 1}, + { 0xffca, 0xffcf, 1}, + { 0xffd2, 0xffd7, 1}, + { 0xffda, 0xffdc, 1}, + { 0x10000, 0x1000b, 1}, + { 0x1000d, 0x10026, 1}, + { 0x10028, 0x1003a, 1}, + { 0x1003c, 0x1003d, 1}, + { 0x1003f, 0x1004d, 1}, + { 0x10050, 0x1005d, 1}, + { 0x10080, 0x100fa, 1}, + { 0x10280, 0x1029c, 1}, + { 0x102a0, 0x102d0, 1}, + { 0x10300, 0x1031e, 1}, + { 0x10330, 0x10340, 1}, + { 0x10342, 0x10349, 1}, + { 0x10380, 0x1039d, 1}, + { 0x103a0, 0x103c3, 1}, + { 0x103c8, 0x103cf, 1}, + { 0x10400, 0x1049d, 1}, + { 0x10800, 0x10805, 1}, + { 0x10808, 0x10808, 1}, + { 0x1080a, 0x10835, 1}, + { 0x10837, 0x10838, 1}, + { 0x1083c, 0x1083f, 3}, + { 0x10900, 0x10915, 1}, + { 0x10920, 0x10939, 1}, + { 0x10a00, 0x10a00, 1}, + { 0x10a10, 0x10a13, 1}, + { 0x10a15, 0x10a17, 1}, + { 0x10a19, 0x10a33, 1}, + { 0x12000, 0x1236e, 1}, + { 0x1d400, 0x1d454, 1}, + { 0x1d456, 0x1d49c, 1}, + { 0x1d49e, 0x1d49f, 1}, + { 0x1d4a2, 0x1d4a2, 1}, + { 0x1d4a5, 0x1d4a6, 1}, + { 0x1d4a9, 0x1d4ac, 1}, + { 0x1d4ae, 0x1d4b9, 1}, + { 0x1d4bb, 0x1d4bb, 1}, + { 0x1d4bd, 0x1d4c3, 1}, + { 0x1d4c5, 0x1d505, 1}, + { 0x1d507, 0x1d50a, 1}, + { 0x1d50d, 0x1d514, 1}, + { 0x1d516, 0x1d51c, 1}, + { 0x1d51e, 0x1d539, 1}, + { 0x1d53b, 0x1d53e, 1}, + { 0x1d540, 0x1d544, 1}, + { 0x1d546, 0x1d546, 1}, + { 0x1d54a, 0x1d550, 1}, + { 0x1d552, 0x1d6a5, 1}, + { 0x1d6a8, 0x1d6c0, 1}, + { 0x1d6c2, 0x1d6da, 1}, + { 0x1d6dc, 0x1d6fa, 1}, + { 0x1d6fc, 0x1d714, 1}, + { 0x1d716, 0x1d734, 1}, + { 0x1d736, 0x1d74e, 1}, + { 0x1d750, 0x1d76e, 1}, + { 0x1d770, 0x1d788, 1}, + { 0x1d78a, 0x1d7a8, 1}, + { 0x1d7aa, 0x1d7c2, 1}, + { 0x1d7c4, 0x1d7cb, 1}, + { 0x20000, 0x2a6d6, 1}, + { 0x2f800, 0x2fa1d, 1}, +}; + +// A table of Unicode uppercase letters--Unicode code points +// classified as "Letter, uppercase". + +static const Unicode_range unicode_uppercase_letters[] = +{ + { 0x0041, 0x005a, 1}, + { 0x00c0, 0x00d6, 1}, + { 0x00d8, 0x00de, 1}, + { 0x0100, 0x0136, 2}, + { 0x0139, 0x0147, 2}, + { 0x014a, 0x0176, 2}, + { 0x0178, 0x0179, 1}, + { 0x017b, 0x017d, 2}, + { 0x0181, 0x0182, 1}, + { 0x0184, 0x0184, 1}, + { 0x0186, 0x0187, 1}, + { 0x0189, 0x018b, 1}, + { 0x018e, 0x0191, 1}, + { 0x0193, 0x0194, 1}, + { 0x0196, 0x0198, 1}, + { 0x019c, 0x019d, 1}, + { 0x019f, 0x01a0, 1}, + { 0x01a2, 0x01a4, 2}, + { 0x01a6, 0x01a7, 1}, + { 0x01a9, 0x01ac, 3}, + { 0x01ae, 0x01af, 1}, + { 0x01b1, 0x01b3, 1}, + { 0x01b5, 0x01b5, 1}, + { 0x01b7, 0x01b8, 1}, + { 0x01bc, 0x01c4, 8}, + { 0x01c7, 0x01cd, 3}, + { 0x01cf, 0x01db, 2}, + { 0x01de, 0x01ee, 2}, + { 0x01f1, 0x01f4, 3}, + { 0x01f6, 0x01f8, 1}, + { 0x01fa, 0x0232, 2}, + { 0x023a, 0x023b, 1}, + { 0x023d, 0x023e, 1}, + { 0x0241, 0x0241, 1}, + { 0x0243, 0x0246, 1}, + { 0x0248, 0x024e, 2}, + { 0x0370, 0x0372, 2}, + { 0x0376, 0x0386, 16}, + { 0x0388, 0x038a, 1}, + { 0x038c, 0x038c, 1}, + { 0x038e, 0x038f, 1}, + { 0x0391, 0x03a1, 1}, + { 0x03a3, 0x03ab, 1}, + { 0x03cf, 0x03cf, 1}, + { 0x03d2, 0x03d4, 1}, + { 0x03d8, 0x03ee, 2}, + { 0x03f4, 0x03f7, 3}, + { 0x03f9, 0x03fa, 1}, + { 0x03fd, 0x042f, 1}, + { 0x0460, 0x0480, 2}, + { 0x048a, 0x04be, 2}, + { 0x04c0, 0x04c1, 1}, + { 0x04c3, 0x04cd, 2}, + { 0x04d0, 0x0522, 2}, + { 0x0531, 0x0556, 1}, + { 0x10a0, 0x10c5, 1}, + { 0x1e00, 0x1e94, 2}, + { 0x1e9e, 0x1efe, 2}, + { 0x1f08, 0x1f0f, 1}, + { 0x1f18, 0x1f1d, 1}, + { 0x1f28, 0x1f2f, 1}, + { 0x1f38, 0x1f3f, 1}, + { 0x1f48, 0x1f4d, 1}, + { 0x1f59, 0x1f5f, 2}, + { 0x1f68, 0x1f6f, 1}, + { 0x1fb8, 0x1fbb, 1}, + { 0x1fc8, 0x1fcb, 1}, + { 0x1fd8, 0x1fdb, 1}, + { 0x1fe8, 0x1fec, 1}, + { 0x1ff8, 0x1ffb, 1}, + { 0x2102, 0x2107, 5}, + { 0x210b, 0x210d, 1}, + { 0x2110, 0x2112, 1}, + { 0x2115, 0x2115, 1}, + { 0x2119, 0x211d, 1}, + { 0x2124, 0x2128, 2}, + { 0x212a, 0x212d, 1}, + { 0x2130, 0x2133, 1}, + { 0x213e, 0x213f, 1}, + { 0x2145, 0x2183, 62}, + { 0x2c00, 0x2c2e, 1}, + { 0x2c60, 0x2c60, 1}, + { 0x2c62, 0x2c64, 1}, + { 0x2c67, 0x2c6b, 2}, + { 0x2c6d, 0x2c6f, 1}, + { 0x2c72, 0x2c75, 3}, + { 0x2c80, 0x2ce2, 2}, + { 0xa640, 0xa65e, 2}, + { 0xa662, 0xa66c, 2}, + { 0xa680, 0xa696, 2}, + { 0xa722, 0xa72e, 2}, + { 0xa732, 0xa76e, 2}, + { 0xa779, 0xa77b, 2}, + { 0xa77d, 0xa77e, 1}, + { 0xa780, 0xa786, 2}, + { 0xa78b, 0xa78b, 1}, + { 0xff21, 0xff3a, 1}, + { 0x10400, 0x10427, 1}, + { 0x1d400, 0x1d419, 1}, + { 0x1d434, 0x1d44d, 1}, + { 0x1d468, 0x1d481, 1}, + { 0x1d49c, 0x1d49c, 1}, + { 0x1d49e, 0x1d49f, 1}, + { 0x1d4a2, 0x1d4a2, 1}, + { 0x1d4a5, 0x1d4a6, 1}, + { 0x1d4a9, 0x1d4ac, 1}, + { 0x1d4ae, 0x1d4b5, 1}, + { 0x1d4d0, 0x1d4e9, 1}, + { 0x1d504, 0x1d505, 1}, + { 0x1d507, 0x1d50a, 1}, + { 0x1d50d, 0x1d514, 1}, + { 0x1d516, 0x1d51c, 1}, + { 0x1d538, 0x1d539, 1}, + { 0x1d53b, 0x1d53e, 1}, + { 0x1d540, 0x1d544, 1}, + { 0x1d546, 0x1d546, 1}, + { 0x1d54a, 0x1d550, 1}, + { 0x1d56c, 0x1d585, 1}, + { 0x1d5a0, 0x1d5b9, 1}, + { 0x1d5d4, 0x1d5ed, 1}, + { 0x1d608, 0x1d621, 1}, + { 0x1d63c, 0x1d655, 1}, + { 0x1d670, 0x1d689, 1}, + { 0x1d6a8, 0x1d6c0, 1}, + { 0x1d6e2, 0x1d6fa, 1}, + { 0x1d71c, 0x1d734, 1}, + { 0x1d756, 0x1d76e, 1}, + { 0x1d790, 0x1d7a8, 1}, + { 0x1d7ca, 0x1d7ca, 1}, +}; + +// Return true if C is in RANGES. + +bool +Lex::is_in_unicode_range(unsigned int c, const Unicode_range* ranges, + size_t range_size) +{ + if (c < 0x100) + { + // The common case is a small value, and we know that it will be + // in the first few entries of the table. Do a linear scan + // rather than a binary search. + for (size_t i = 0; i < range_size; ++i) + { + const Unicode_range* p = &ranges[i]; + if (c <= p->high) + { + if (c < p->low) + return false; + return (c - p->low) % p->stride == 0; + } + } + return false; + } + else + { + size_t lo = 0; + size_t hi = range_size; + while (lo < hi) + { + size_t mid = lo + (hi - lo) / 2; + const Unicode_range* p = &ranges[mid]; + if (c < p->low) + hi = mid; + else if (c > p->high) + lo = mid + 1; + else + return (c - p->low) % p->stride == 0; + } + return false; + } +} + +// 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". + +bool +Lex::is_unicode_digit(unsigned int c) +{ + return Lex::is_in_unicode_range(c, unicode_digits, + ARRAY_SIZE(unicode_digits)); +} + +// Return whether C is a Unicode letter--a Unicode code point +// classified as "Letter". + +bool +Lex::is_unicode_letter(unsigned int c) +{ + return Lex::is_in_unicode_range(c, unicode_letters, + ARRAY_SIZE(unicode_letters)); +} + +// Return whether C is a Unicode uppercase letter. a Unicode code +// point classified as "Letter, uppercase". + +bool +Lex::is_unicode_uppercase(unsigned int c) +{ + return Lex::is_in_unicode_range(c, unicode_uppercase_letters, + ARRAY_SIZE(unicode_uppercase_letters)); +} + +// Return whether the identifier NAME should be exported. NAME is a +// mangled name which includes only ASCII characters. + +bool +Lex::is_exported_name(const std::string& name) +{ + unsigned char c = name[0]; + if (c != '$') + return c >= 'A' && c <= 'Z'; + else + { + const char* p = name.data(); + size_t len = name.length(); + if (len < 2 || p[1] != 'U') + return false; + unsigned int ci = 0; + for (size_t i = 2; i < len && p[i] != '$'; ++i) + { + c = p[i]; + if (!hex_p(c)) + return false; + ci <<= 4; + ci |= hex_value(c); + } + return Lex::is_unicode_uppercase(ci); + } +} + +// Return whether the identifier NAME contains an invalid character. +// This is based on how we handle invalid characters in +// gather_identifier. + +bool +Lex::is_invalid_identifier(const std::string& name) +{ + return name.find("$INVALID$") != std::string::npos; +} diff --git a/gcc-4.9/gcc/go/gofrontend/lex.h b/gcc-4.9/gcc/go/gofrontend/lex.h new file mode 100644 index 000000000..383a91787 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/lex.h @@ -0,0 +1,502 @@ +// lex.h -- Go frontend lexer. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_LEX_H +#define GO_LEX_H + +#include + +#include "operator.h" +#include "go-linemap.h" + +struct Unicode_range; + +// The keywords. These must be in sorted order, other than +// KEYWORD_INVALID. They must match the Keywords::mapping_ array in +// lex.cc. + +enum Keyword +{ + KEYWORD_INVALID, // Not a keyword. + KEYWORD_ASM, + KEYWORD_BREAK, + KEYWORD_CASE, + KEYWORD_CHAN, + KEYWORD_CONST, + KEYWORD_CONTINUE, + KEYWORD_DEFAULT, + KEYWORD_DEFER, + KEYWORD_ELSE, + KEYWORD_FALLTHROUGH, + KEYWORD_FOR, + KEYWORD_FUNC, + KEYWORD_GO, + KEYWORD_GOTO, + KEYWORD_IF, + KEYWORD_IMPORT, + KEYWORD_INTERFACE, + KEYWORD_MAP, + KEYWORD_PACKAGE, + KEYWORD_RANGE, + KEYWORD_RETURN, + KEYWORD_SELECT, + KEYWORD_STRUCT, + KEYWORD_SWITCH, + KEYWORD_TYPE, + KEYWORD_VAR +}; + +// A token returned from the lexer. + +class Token +{ + public: + // Token classification. + enum Classification + { + // Token is invalid. + TOKEN_INVALID, + // Token indicates end of input. + TOKEN_EOF, + // Token is a keyword. + TOKEN_KEYWORD, + // Token is an identifier. + TOKEN_IDENTIFIER, + // Token is a string of characters. + TOKEN_STRING, + // Token is an operator. + TOKEN_OPERATOR, + // Token is a character constant. + TOKEN_CHARACTER, + // Token is an integer. + TOKEN_INTEGER, + // Token is a floating point number. + TOKEN_FLOAT, + // Token is an imaginary number. + TOKEN_IMAGINARY + }; + + ~Token(); + Token(const Token&); + Token& operator=(const Token&); + + // Get token classification. + Classification + classification() const + { return this->classification_; } + + // Make a token for an invalid value. + static Token + make_invalid_token(Location location) + { return Token(TOKEN_INVALID, location); } + + // Make a token representing end of file. + static Token + make_eof_token(Location location) + { return Token(TOKEN_EOF, location); } + + // Make a keyword token. + static Token + make_keyword_token(Keyword keyword, Location location) + { + Token tok(TOKEN_KEYWORD, location); + tok.u_.keyword = keyword; + return tok; + } + + // Make an identifier token. + static Token + make_identifier_token(const std::string& value, bool is_exported, + Location location) + { + Token tok(TOKEN_IDENTIFIER, location); + tok.u_.identifier_value.name = new std::string(value); + tok.u_.identifier_value.is_exported = is_exported; + return tok; + } + + // Make a quoted string token. + static Token + make_string_token(const std::string& value, Location location) + { + Token tok(TOKEN_STRING, location); + tok.u_.string_value = new std::string(value); + return tok; + } + + // Make an operator token. + static Token + make_operator_token(Operator op, Location location) + { + Token tok(TOKEN_OPERATOR, location); + tok.u_.op = op; + return tok; + } + + // Make a character constant token. + static Token + make_character_token(mpz_t val, Location location) + { + Token tok(TOKEN_CHARACTER, location); + mpz_init(tok.u_.integer_value); + mpz_swap(tok.u_.integer_value, val); + return tok; + } + + // Make an integer token. + static Token + make_integer_token(mpz_t val, Location location) + { + Token tok(TOKEN_INTEGER, location); + mpz_init(tok.u_.integer_value); + mpz_swap(tok.u_.integer_value, val); + return tok; + } + + // Make a float token. + static Token + make_float_token(mpfr_t val, Location location) + { + Token tok(TOKEN_FLOAT, location); + mpfr_init(tok.u_.float_value); + mpfr_swap(tok.u_.float_value, val); + return tok; + } + + // Make a token for an imaginary number. + static Token + make_imaginary_token(mpfr_t val, Location location) + { + Token tok(TOKEN_IMAGINARY, location); + mpfr_init(tok.u_.float_value); + mpfr_swap(tok.u_.float_value, val); + return tok; + } + + // Get the location of the token. + Location + location() const + { return this->location_; } + + // Return whether this is an invalid token. + bool + is_invalid() const + { return this->classification_ == TOKEN_INVALID; } + + // Return whether this is the EOF token. + bool + is_eof() const + { return this->classification_ == TOKEN_EOF; } + + // Return the keyword value for a keyword token. + Keyword + keyword() const + { + go_assert(this->classification_ == TOKEN_KEYWORD); + return this->u_.keyword; + } + + // Return whether this is an identifier. + bool + is_identifier() const + { return this->classification_ == TOKEN_IDENTIFIER; } + + // Return the identifier. + const std::string& + identifier() const + { + go_assert(this->classification_ == TOKEN_IDENTIFIER); + return *this->u_.identifier_value.name; + } + + // Return whether the identifier is exported. + bool + is_identifier_exported() const + { + go_assert(this->classification_ == TOKEN_IDENTIFIER); + return this->u_.identifier_value.is_exported; + } + + // Return whether this is a string. + bool + is_string() const + { + return this->classification_ == TOKEN_STRING; + } + + // Return the value of a string. The returned value is a string of + // UTF-8 characters. + std::string + string_value() const + { + go_assert(this->classification_ == TOKEN_STRING); + return *this->u_.string_value; + } + + // Return the value of a character constant. + const mpz_t* + character_value() const + { + go_assert(this->classification_ == TOKEN_CHARACTER); + return &this->u_.integer_value; + } + + // Return the value of an integer. + const mpz_t* + integer_value() const + { + go_assert(this->classification_ == TOKEN_INTEGER); + return &this->u_.integer_value; + } + + // Return the value of a float. + const mpfr_t* + float_value() const + { + go_assert(this->classification_ == TOKEN_FLOAT); + return &this->u_.float_value; + } + + // Return the value of an imaginary number. + const mpfr_t* + imaginary_value() const + { + go_assert(this->classification_ == TOKEN_IMAGINARY); + return &this->u_.float_value; + } + + // Return the operator value for an operator token. + Operator + op() const + { + go_assert(this->classification_ == TOKEN_OPERATOR); + return this->u_.op; + } + + // Return whether this token is KEYWORD. + bool + is_keyword(Keyword keyword) const + { + return (this->classification_ == TOKEN_KEYWORD + && this->u_.keyword == keyword); + } + + // Return whether this token is OP. + bool + is_op(Operator op) const + { return this->classification_ == TOKEN_OPERATOR && this->u_.op == op; } + + // Print the token for debugging. + void + print(FILE*) const; + + private: + // Private constructor used by make_..._token functions above. + Token(Classification, Location); + + // Clear the token. + void + clear(); + + // The token classification. + Classification classification_; + union + { + // The keyword value for TOKEN_KEYWORD. + Keyword keyword; + // The token value for TOKEN_IDENTIFIER. + struct + { + // The name of the identifier. This has been mangled to only + // include ASCII characters. + std::string* name; + // Whether this name should be exported. This is true if the + // first letter in the name is upper case. + bool is_exported; + } identifier_value; + // The string value for TOKEN_STRING. + std::string* string_value; + // The token value for TOKEN_CHARACTER or TOKEN_INTEGER. + mpz_t integer_value; + // The token value for TOKEN_FLOAT or TOKEN_IMAGINARY. + mpfr_t float_value; + // The token value for TOKEN_OPERATOR or the keyword value + Operator op; + } u_; + // The source location. + Location location_; +}; + +// The lexer itself. + +class Lex +{ + public: + Lex(const char* input_file_name, FILE* input_file, Linemap *linemap); + + ~Lex(); + + // Return the next token. + Token + next_token(); + + // Return the contents of any current //extern comment. + const std::string& + extern_name() const + { return this->extern_; } + + // Return whether we have seen a //go:nointerface comment, clearing + // the flag. + bool + get_and_clear_nointerface() + { + bool ret = this->saw_nointerface_; + this->saw_nointerface_ = false; + return ret; + } + + // Return whether the identifier NAME should be exported. NAME is a + // mangled name which includes only ASCII characters. + static bool + is_exported_name(const std::string& name); + + // Return whether the identifier NAME is invalid. When we see an + // invalid character we still build an identifier, but we use a + // magic string to indicate that the identifier is invalid. We then + // use this to avoid knockon errors. + static bool + is_invalid_identifier(const std::string& name); + + // A helper function. Append V to STR. IS_CHARACTER is true if V + // is a Unicode character which should be converted into UTF-8, + // false if it is a byte value to be appended directly. The + // location is used to warn about an out of range character. + static void + append_char(unsigned int v, bool is_charater, std::string* str, + Location); + + // A helper function. Fetch a UTF-8 character from STR and store it + // in *VALUE. Return the number of bytes read from STR. Return 0 + // if STR does not point to a valid UTF-8 character. + 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(); + + bool + require_line(); + + // The current location. + Location + location() const; + + // A position CHARS column positions before the current location. + Location + earlier_location(int chars) const; + + static bool + is_hex_digit(char); + + static unsigned char + octal_value(char c) + { return c - '0'; } + + Token + make_invalid_token() + { return Token::make_invalid_token(this->location()); } + + Token + make_eof_token() + { return Token::make_eof_token(this->location()); } + + Token + make_operator(Operator op, int chars) + { return Token::make_operator_token(op, this->earlier_location(chars)); } + + Token + gather_identifier(); + + static bool + could_be_exponent(const char*, const char*); + + Token + gather_number(); + + Token + gather_character(); + + Token + gather_string(); + + Token + gather_raw_string(); + + const char* + advance_one_utf8_char(const char*, unsigned int*, bool*); + + const char* + advance_one_char(const char*, bool, unsigned int*, bool*); + + static bool + is_unicode_digit(unsigned int c); + + static bool + is_unicode_letter(unsigned int c); + + static bool + is_unicode_uppercase(unsigned int c); + + static bool + is_in_unicode_range(unsigned int C, const Unicode_range* ranges, + size_t range_size); + + Operator + three_character_operator(char, char, char); + + Operator + two_character_operator(char, char); + + Operator + one_character_operator(char); + + bool + skip_c_comment(); + + void + skip_cpp_comment(); + + // The input file name. + const char* input_file_name_; + // The input file. + FILE* input_file_; + // The object used to keep track of file names and line numbers. + Linemap* linemap_; + // The line buffer. This holds the current line. + char* linebuf_; + // The size of the line buffer. + size_t linebufsize_; + // The nmber of characters in the current line. + size_t linesize_; + // The current offset in linebuf_. + size_t lineoff_; + // The current line number. + size_t lineno_; + // Whether to add a semicolon if we see a newline now. + bool add_semi_at_eol_; + // Whether we just saw a magic go:nointerface comment. + bool saw_nointerface_; + // The external name to use for a function declaration, from a magic + // //extern comment. + std::string extern_; +}; + +#endif // !defined(GO_LEX_H) diff --git a/gcc-4.9/gcc/go/gofrontend/operator.h b/gcc-4.9/gcc/go/gofrontend/operator.h new file mode 100644 index 000000000..f3e0fd074 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/operator.h @@ -0,0 +1,66 @@ +// operator.h -- Go frontend operators. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_OPERATOR_H +#define GO_OPERATOR_H + +// The operators. + +enum Operator +{ + OPERATOR_INVALID, + OPERATOR_OROR, // || + OPERATOR_ANDAND, // && + OPERATOR_EQEQ, // == + OPERATOR_NOTEQ, // != + OPERATOR_LT, // < + OPERATOR_LE, // <= + OPERATOR_GT, // > + OPERATOR_GE, // >= + OPERATOR_PLUS, // + + OPERATOR_MINUS, // - + OPERATOR_OR, // | + OPERATOR_XOR, // ^ + OPERATOR_MULT, // * + OPERATOR_DIV, // / + OPERATOR_MOD, // % + OPERATOR_LSHIFT, // << + OPERATOR_RSHIFT, // >> + OPERATOR_AND, // & + OPERATOR_NOT, // ! + OPERATOR_BITCLEAR, // &^ + OPERATOR_CHANOP, // <- + + OPERATOR_EQ, // = + OPERATOR_PLUSEQ, // += + OPERATOR_MINUSEQ, // -= + OPERATOR_OREQ, // |= + OPERATOR_XOREQ, // ^= + OPERATOR_MULTEQ, // *= + OPERATOR_DIVEQ, // /= + OPERATOR_MODEQ, // %= + OPERATOR_LSHIFTEQ, // <<= + OPERATOR_RSHIFTEQ, // >>= + OPERATOR_ANDEQ, // &= + OPERATOR_BITCLEAREQ, // &^= + OPERATOR_PLUSPLUS, // ++ + OPERATOR_MINUSMINUS, // -- + + OPERATOR_COLON, // : + OPERATOR_COLONEQ, // := + OPERATOR_SEMICOLON, // ; + OPERATOR_DOT, // . + OPERATOR_ELLIPSIS, // ... + OPERATOR_COMMA, // , + OPERATOR_LPAREN, // ( + OPERATOR_RPAREN, // ) + OPERATOR_LCURLY, // { + OPERATOR_RCURLY, // } + OPERATOR_LSQUARE, // [ + OPERATOR_RSQUARE // ] +}; + +#endif // !defined(GO_OPERATOR_H) diff --git a/gcc-4.9/gcc/go/gofrontend/parse.cc b/gcc-4.9/gcc/go/gofrontend/parse.cc new file mode 100644 index 000000000..7614e6fc7 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/parse.cc @@ -0,0 +1,5746 @@ +// parse.cc -- Go frontend parser. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "lex.h" +#include "gogo.h" +#include "types.h" +#include "statements.h" +#include "expressions.h" +#include "parse.h" + +// Struct Parse::Enclosing_var_comparison. + +// Return true if v1 should be considered to be less than v2. + +bool +Parse::Enclosing_var_comparison::operator()(const Enclosing_var& v1, + const Enclosing_var& v2) +{ + if (v1.var() == v2.var()) + return false; + + const std::string& n1(v1.var()->name()); + const std::string& n2(v2.var()->name()); + int i = n1.compare(n2); + if (i < 0) + return true; + else if (i > 0) + return false; + + // If we get here it means that a single nested function refers to + // two different variables defined in enclosing functions, and both + // variables have the same name. I think this is impossible. + go_unreachable(); +} + +// Class Parse. + +Parse::Parse(Lex* lex, Gogo* gogo) + : lex_(lex), + token_(Token::make_invalid_token(Linemap::unknown_location())), + unget_token_(Token::make_invalid_token(Linemap::unknown_location())), + unget_token_valid_(false), + is_erroneous_function_(false), + gogo_(gogo), + break_stack_(NULL), + continue_stack_(NULL), + iota_(0), + enclosing_vars_(), + type_switch_vars_() +{ +} + +// Return the current token. + +const Token* +Parse::peek_token() +{ + if (this->unget_token_valid_) + return &this->unget_token_; + if (this->token_.is_invalid()) + this->token_ = this->lex_->next_token(); + return &this->token_; +} + +// Advance to the next token and return it. + +const Token* +Parse::advance_token() +{ + if (this->unget_token_valid_) + { + this->unget_token_valid_ = false; + if (!this->token_.is_invalid()) + return &this->token_; + } + this->token_ = this->lex_->next_token(); + return &this->token_; +} + +// Push a token back on the input stream. + +void +Parse::unget_token(const Token& token) +{ + go_assert(!this->unget_token_valid_); + this->unget_token_ = token; + this->unget_token_valid_ = true; +} + +// The location of the current token. + +Location +Parse::location() +{ + return this->peek_token()->location(); +} + +// IdentifierList = identifier { "," identifier } . + +void +Parse::identifier_list(Typed_identifier_list* til) +{ + const Token* token = this->peek_token(); + while (true) + { + if (!token->is_identifier()) + { + error_at(this->location(), "expected identifier"); + return; + } + std::string name = + this->gogo_->pack_hidden_name(token->identifier(), + token->is_identifier_exported()); + til->push_back(Typed_identifier(name, NULL, token->location())); + token = this->advance_token(); + if (!token->is_op(OPERATOR_COMMA)) + return; + token = this->advance_token(); + } +} + +// ExpressionList = Expression { "," Expression } . + +// If MAY_BE_COMPOSITE_LIT is true, an expression may be a composite +// literal. + +// If MAY_BE_SINK is true, the expressions in the list may be "_". + +Expression_list* +Parse::expression_list(Expression* first, bool may_be_sink, + bool may_be_composite_lit) +{ + Expression_list* ret = new Expression_list(); + if (first != NULL) + ret->push_back(first); + while (true) + { + ret->push_back(this->expression(PRECEDENCE_NORMAL, may_be_sink, + may_be_composite_lit, NULL, NULL)); + + const Token* token = this->peek_token(); + if (!token->is_op(OPERATOR_COMMA)) + return ret; + + // Most expression lists permit a trailing comma. + Location location = token->location(); + this->advance_token(); + if (!this->expression_may_start_here()) + { + this->unget_token(Token::make_operator_token(OPERATOR_COMMA, + location)); + return ret; + } + } +} + +// QualifiedIdent = [ PackageName "." ] identifier . +// PackageName = identifier . + +// This sets *PNAME to the identifier and sets *PPACKAGE to the +// package or NULL if there isn't one. This returns true on success, +// false on failure in which case it will have emitted an error +// message. + +bool +Parse::qualified_ident(std::string* pname, Named_object** ppackage) +{ + const Token* token = this->peek_token(); + if (!token->is_identifier()) + { + error_at(this->location(), "expected identifier"); + return false; + } + + std::string name = token->identifier(); + bool is_exported = token->is_identifier_exported(); + name = this->gogo_->pack_hidden_name(name, is_exported); + + token = this->advance_token(); + if (!token->is_op(OPERATOR_DOT)) + { + *pname = name; + *ppackage = NULL; + return true; + } + + Named_object* package = this->gogo_->lookup(name, NULL); + if (package == NULL || !package->is_package()) + { + error_at(this->location(), "expected package"); + // We expect . IDENTIFIER; skip both. + if (this->advance_token()->is_identifier()) + this->advance_token(); + return false; + } + + package->package_value()->set_used(); + + token = this->advance_token(); + if (!token->is_identifier()) + { + error_at(this->location(), "expected identifier"); + return false; + } + + name = token->identifier(); + + if (name == "_") + { + error_at(this->location(), "invalid use of %<_%>"); + name = Gogo::erroneous_name(); + } + + if (package->name() == this->gogo_->package_name()) + name = this->gogo_->pack_hidden_name(name, + token->is_identifier_exported()); + + *pname = name; + *ppackage = package; + + this->advance_token(); + + return true; +} + +// Type = TypeName | TypeLit | "(" Type ")" . +// TypeLit = +// ArrayType | StructType | PointerType | FunctionType | InterfaceType | +// SliceType | MapType | ChannelType . + +Type* +Parse::type() +{ + const Token* token = this->peek_token(); + if (token->is_identifier()) + return this->type_name(true); + else if (token->is_op(OPERATOR_LSQUARE)) + return this->array_type(false); + else if (token->is_keyword(KEYWORD_CHAN) + || token->is_op(OPERATOR_CHANOP)) + return this->channel_type(); + else if (token->is_keyword(KEYWORD_INTERFACE)) + return this->interface_type(); + else if (token->is_keyword(KEYWORD_FUNC)) + { + Location location = token->location(); + this->advance_token(); + Type* type = this->signature(NULL, location); + if (type == NULL) + return Type::make_error_type(); + return type; + } + else if (token->is_keyword(KEYWORD_MAP)) + return this->map_type(); + else if (token->is_keyword(KEYWORD_STRUCT)) + return this->struct_type(); + else if (token->is_op(OPERATOR_MULT)) + return this->pointer_type(); + else if (token->is_op(OPERATOR_LPAREN)) + { + this->advance_token(); + Type* ret = this->type(); + if (this->peek_token()->is_op(OPERATOR_RPAREN)) + this->advance_token(); + else + { + if (!ret->is_error_type()) + error_at(this->location(), "expected %<)%>"); + } + return ret; + } + else + { + error_at(token->location(), "expected type"); + return Type::make_error_type(); + } +} + +bool +Parse::type_may_start_here() +{ + const Token* token = this->peek_token(); + return (token->is_identifier() + || token->is_op(OPERATOR_LSQUARE) + || token->is_op(OPERATOR_CHANOP) + || token->is_keyword(KEYWORD_CHAN) + || token->is_keyword(KEYWORD_INTERFACE) + || token->is_keyword(KEYWORD_FUNC) + || token->is_keyword(KEYWORD_MAP) + || token->is_keyword(KEYWORD_STRUCT) + || token->is_op(OPERATOR_MULT) + || token->is_op(OPERATOR_LPAREN)); +} + +// TypeName = QualifiedIdent . + +// If MAY_BE_NIL is true, then an identifier with the value of the +// predefined constant nil is accepted, returning the nil type. + +Type* +Parse::type_name(bool issue_error) +{ + Location location = this->location(); + + std::string name; + Named_object* package; + if (!this->qualified_ident(&name, &package)) + return Type::make_error_type(); + + Named_object* named_object; + if (package == NULL) + named_object = this->gogo_->lookup(name, NULL); + else + { + named_object = package->package_value()->lookup(name); + if (named_object == NULL + && issue_error + && package->name() != this->gogo_->package_name()) + { + // Check whether the name is there but hidden. + std::string s = ('.' + package->package_value()->pkgpath() + + '.' + name); + named_object = package->package_value()->lookup(s); + if (named_object != NULL) + { + Package* p = package->package_value(); + const std::string& packname(p->package_name()); + error_at(location, "invalid reference to hidden type %<%s.%s%>", + Gogo::message_name(packname).c_str(), + Gogo::message_name(name).c_str()); + issue_error = false; + } + } + } + + bool ok = true; + if (named_object == NULL) + { + if (package == NULL) + named_object = this->gogo_->add_unknown_name(name, location); + else + { + const std::string& packname(package->package_value()->package_name()); + error_at(location, "reference to undefined identifier %<%s.%s%>", + Gogo::message_name(packname).c_str(), + Gogo::message_name(name).c_str()); + issue_error = false; + ok = false; + } + } + else if (named_object->is_type()) + { + if (!named_object->type_value()->is_visible()) + ok = false; + } + else if (named_object->is_unknown() || named_object->is_type_declaration()) + ; + else + ok = false; + + if (!ok) + { + if (issue_error) + error_at(location, "expected type"); + return Type::make_error_type(); + } + + if (named_object->is_type()) + return named_object->type_value(); + else if (named_object->is_unknown() || named_object->is_type_declaration()) + return Type::make_forward_declaration(named_object); + else + go_unreachable(); +} + +// ArrayType = "[" [ ArrayLength ] "]" ElementType . +// ArrayLength = Expression . +// ElementType = CompleteType . + +Type* +Parse::array_type(bool may_use_ellipsis) +{ + go_assert(this->peek_token()->is_op(OPERATOR_LSQUARE)); + const Token* token = this->advance_token(); + + Expression* length = NULL; + if (token->is_op(OPERATOR_RSQUARE)) + this->advance_token(); + else + { + if (!token->is_op(OPERATOR_ELLIPSIS)) + length = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + else if (may_use_ellipsis) + { + // An ellipsis is used in composite literals to represent a + // fixed array of the size of the number of elements. We + // use a length of nil to represent this, and change the + // length when parsing the composite literal. + length = Expression::make_nil(this->location()); + this->advance_token(); + } + else + { + error_at(this->location(), + "use of %<[...]%> outside of array literal"); + length = Expression::make_error(this->location()); + this->advance_token(); + } + if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) + { + error_at(this->location(), "expected %<]%>"); + return Type::make_error_type(); + } + this->advance_token(); + } + + Type* element_type = this->type(); + + return Type::make_array_type(element_type, length); +} + +// MapType = "map" "[" KeyType "]" ValueType . +// KeyType = CompleteType . +// ValueType = CompleteType . + +Type* +Parse::map_type() +{ + Location location = this->location(); + go_assert(this->peek_token()->is_keyword(KEYWORD_MAP)); + if (!this->advance_token()->is_op(OPERATOR_LSQUARE)) + { + error_at(this->location(), "expected %<[%>"); + return Type::make_error_type(); + } + this->advance_token(); + + Type* key_type = this->type(); + + if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) + { + error_at(this->location(), "expected %<]%>"); + return Type::make_error_type(); + } + this->advance_token(); + + Type* value_type = this->type(); + + if (key_type->is_error_type() || value_type->is_error_type()) + return Type::make_error_type(); + + return Type::make_map_type(key_type, value_type, location); +} + +// StructType = "struct" "{" { FieldDecl ";" } "}" . + +Type* +Parse::struct_type() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_STRUCT)); + Location location = this->location(); + if (!this->advance_token()->is_op(OPERATOR_LCURLY)) + { + Location token_loc = this->location(); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON) + && this->advance_token()->is_op(OPERATOR_LCURLY)) + error_at(token_loc, "unexpected semicolon or newline before %<{%>"); + else + { + error_at(this->location(), "expected %<{%>"); + return Type::make_error_type(); + } + } + this->advance_token(); + + Struct_field_list* sfl = new Struct_field_list; + while (!this->peek_token()->is_op(OPERATOR_RCURLY)) + { + this->field_decl(sfl); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + else if (!this->peek_token()->is_op(OPERATOR_RCURLY)) + { + error_at(this->location(), "expected %<;%> or %<}%> or newline"); + if (!this->skip_past_error(OPERATOR_RCURLY)) + return Type::make_error_type(); + } + } + this->advance_token(); + + for (Struct_field_list::const_iterator pi = sfl->begin(); + pi != sfl->end(); + ++pi) + { + if (pi->type()->is_error_type()) + return pi->type(); + for (Struct_field_list::const_iterator pj = pi + 1; + pj != sfl->end(); + ++pj) + { + if (pi->field_name() == pj->field_name() + && !Gogo::is_sink_name(pi->field_name())) + error_at(pi->location(), "duplicate field name %<%s%>", + Gogo::message_name(pi->field_name()).c_str()); + } + } + + return Type::make_struct_type(sfl, location); +} + +// FieldDecl = (IdentifierList CompleteType | TypeName) [ Tag ] . +// Tag = string_lit . + +void +Parse::field_decl(Struct_field_list* sfl) +{ + const Token* token = this->peek_token(); + Location location = token->location(); + bool is_anonymous; + bool is_anonymous_pointer; + if (token->is_op(OPERATOR_MULT)) + { + is_anonymous = true; + is_anonymous_pointer = true; + } + else if (token->is_identifier()) + { + std::string id = token->identifier(); + bool is_id_exported = token->is_identifier_exported(); + Location id_location = token->location(); + token = this->advance_token(); + is_anonymous = (token->is_op(OPERATOR_SEMICOLON) + || token->is_op(OPERATOR_RCURLY) + || token->is_op(OPERATOR_DOT) + || token->is_string()); + is_anonymous_pointer = false; + this->unget_token(Token::make_identifier_token(id, is_id_exported, + id_location)); + } + else + { + error_at(this->location(), "expected field name"); + this->gogo_->mark_locals_used(); + while (!token->is_op(OPERATOR_SEMICOLON) + && !token->is_op(OPERATOR_RCURLY) + && !token->is_eof()) + token = this->advance_token(); + return; + } + + if (is_anonymous) + { + if (is_anonymous_pointer) + { + this->advance_token(); + if (!this->peek_token()->is_identifier()) + { + error_at(this->location(), "expected field name"); + this->gogo_->mark_locals_used(); + while (!token->is_op(OPERATOR_SEMICOLON) + && !token->is_op(OPERATOR_RCURLY) + && !token->is_eof()) + token = this->advance_token(); + return; + } + } + Type* type = this->type_name(true); + + std::string tag; + if (this->peek_token()->is_string()) + { + tag = this->peek_token()->string_value(); + this->advance_token(); + } + + if (!type->is_error_type()) + { + if (is_anonymous_pointer) + type = Type::make_pointer_type(type); + sfl->push_back(Struct_field(Typed_identifier("", type, location))); + if (!tag.empty()) + sfl->back().set_tag(tag); + } + } + else + { + Typed_identifier_list til; + while (true) + { + token = this->peek_token(); + if (!token->is_identifier()) + { + error_at(this->location(), "expected identifier"); + return; + } + std::string name = + this->gogo_->pack_hidden_name(token->identifier(), + token->is_identifier_exported()); + til.push_back(Typed_identifier(name, NULL, token->location())); + if (!this->advance_token()->is_op(OPERATOR_COMMA)) + break; + this->advance_token(); + } + + Type* type = this->type(); + + std::string tag; + if (this->peek_token()->is_string()) + { + tag = this->peek_token()->string_value(); + this->advance_token(); + } + + for (Typed_identifier_list::iterator p = til.begin(); + p != til.end(); + ++p) + { + p->set_type(type); + sfl->push_back(Struct_field(*p)); + if (!tag.empty()) + sfl->back().set_tag(tag); + } + } +} + +// PointerType = "*" Type . + +Type* +Parse::pointer_type() +{ + go_assert(this->peek_token()->is_op(OPERATOR_MULT)); + this->advance_token(); + Type* type = this->type(); + if (type->is_error_type()) + return type; + return Type::make_pointer_type(type); +} + +// ChannelType = Channel | SendChannel | RecvChannel . +// Channel = "chan" ElementType . +// SendChannel = "chan" "<-" ElementType . +// RecvChannel = "<-" "chan" ElementType . + +Type* +Parse::channel_type() +{ + const Token* token = this->peek_token(); + bool send = true; + bool receive = true; + if (token->is_op(OPERATOR_CHANOP)) + { + if (!this->advance_token()->is_keyword(KEYWORD_CHAN)) + { + error_at(this->location(), "expected %"); + return Type::make_error_type(); + } + send = false; + this->advance_token(); + } + else + { + go_assert(token->is_keyword(KEYWORD_CHAN)); + if (this->advance_token()->is_op(OPERATOR_CHANOP)) + { + receive = false; + this->advance_token(); + } + } + + // Better error messages for the common error of omitting the + // channel element type. + if (!this->type_may_start_here()) + { + token = this->peek_token(); + if (token->is_op(OPERATOR_RCURLY)) + error_at(this->location(), "unexpected %<}%> in channel type"); + else if (token->is_op(OPERATOR_RPAREN)) + error_at(this->location(), "unexpected %<)%> in channel type"); + else if (token->is_op(OPERATOR_COMMA)) + error_at(this->location(), "unexpected comma in channel type"); + else + error_at(this->location(), "expected channel element type"); + return Type::make_error_type(); + } + + Type* element_type = this->type(); + return Type::make_channel_type(send, receive, element_type); +} + +// Give an error for a duplicate parameter or receiver name. + +void +Parse::check_signature_names(const Typed_identifier_list* params, + Parse::Names* names) +{ + for (Typed_identifier_list::const_iterator p = params->begin(); + p != params->end(); + ++p) + { + if (p->name().empty() || Gogo::is_sink_name(p->name())) + continue; + std::pair val = + std::make_pair(p->name(), &*p); + std::pair ins = names->insert(val); + if (!ins.second) + { + error_at(p->location(), "redefinition of %qs", + Gogo::message_name(p->name()).c_str()); + inform(ins.first->second->location(), + "previous definition of %qs was here", + Gogo::message_name(p->name()).c_str()); + } + } +} + +// Signature = Parameters [ Result ] . + +// RECEIVER is the receiver if there is one, or NULL. LOCATION is the +// location of the start of the type. + +// This returns NULL on a parse error. + +Function_type* +Parse::signature(Typed_identifier* receiver, Location location) +{ + bool is_varargs = false; + Typed_identifier_list* params; + bool params_ok = this->parameters(¶ms, &is_varargs); + + Typed_identifier_list* results = NULL; + if (this->peek_token()->is_op(OPERATOR_LPAREN) + || this->type_may_start_here()) + { + if (!this->result(&results)) + return NULL; + } + + if (!params_ok) + return NULL; + + Parse::Names names; + if (receiver != NULL) + names[receiver->name()] = receiver; + if (params != NULL) + this->check_signature_names(params, &names); + if (results != NULL) + this->check_signature_names(results, &names); + + Function_type* ret = Type::make_function_type(receiver, params, results, + location); + if (is_varargs) + ret->set_is_varargs(); + return ret; +} + +// Parameters = "(" [ ParameterList [ "," ] ] ")" . + +// This returns false on a parse error. + +bool +Parse::parameters(Typed_identifier_list** pparams, bool* is_varargs) +{ + *pparams = NULL; + + if (!this->peek_token()->is_op(OPERATOR_LPAREN)) + { + error_at(this->location(), "expected %<(%>"); + return false; + } + + Typed_identifier_list* params = NULL; + bool saw_error = false; + + const Token* token = this->advance_token(); + if (!token->is_op(OPERATOR_RPAREN)) + { + params = this->parameter_list(is_varargs); + if (params == NULL) + saw_error = true; + token = this->peek_token(); + } + + // The optional trailing comma is picked up in parameter_list. + + if (!token->is_op(OPERATOR_RPAREN)) + error_at(this->location(), "expected %<)%>"); + else + this->advance_token(); + + if (saw_error) + return false; + + *pparams = params; + return true; +} + +// ParameterList = ParameterDecl { "," ParameterDecl } . + +// This sets *IS_VARARGS if the list ends with an ellipsis. +// IS_VARARGS will be NULL if varargs are not permitted. + +// We pick up an optional trailing comma. + +// This returns NULL if some error is seen. + +Typed_identifier_list* +Parse::parameter_list(bool* is_varargs) +{ + Location location = this->location(); + Typed_identifier_list* ret = new Typed_identifier_list(); + + bool saw_error = false; + + // If we see an identifier and then a comma, then we don't know + // whether we are looking at a list of identifiers followed by a + // type, or a list of types given by name. We have to do an + // arbitrary lookahead to figure it out. + + bool parameters_have_names; + const Token* token = this->peek_token(); + if (!token->is_identifier()) + { + // This must be a type which starts with something like '*'. + parameters_have_names = false; + } + else + { + std::string name = token->identifier(); + bool is_exported = token->is_identifier_exported(); + Location location = token->location(); + token = this->advance_token(); + if (!token->is_op(OPERATOR_COMMA)) + { + if (token->is_op(OPERATOR_DOT)) + { + // This is a qualified identifier, which must turn out + // to be a type. + parameters_have_names = false; + } + else if (token->is_op(OPERATOR_RPAREN)) + { + // A single identifier followed by a parenthesis must be + // a type name. + parameters_have_names = false; + } + else + { + // An identifier followed by something other than a + // comma or a dot or a right parenthesis must be a + // parameter name followed by a type. + parameters_have_names = true; + } + + this->unget_token(Token::make_identifier_token(name, is_exported, + location)); + } + else + { + // An identifier followed by a comma may be the first in a + // list of parameter names followed by a type, or it may be + // the first in a list of types without parameter names. To + // find out we gather as many identifiers separated by + // commas as we can. + std::string id_name = this->gogo_->pack_hidden_name(name, + is_exported); + ret->push_back(Typed_identifier(id_name, NULL, location)); + bool just_saw_comma = true; + while (this->advance_token()->is_identifier()) + { + name = this->peek_token()->identifier(); + is_exported = this->peek_token()->is_identifier_exported(); + location = this->peek_token()->location(); + id_name = this->gogo_->pack_hidden_name(name, is_exported); + ret->push_back(Typed_identifier(id_name, NULL, location)); + if (!this->advance_token()->is_op(OPERATOR_COMMA)) + { + just_saw_comma = false; + break; + } + } + + if (just_saw_comma) + { + // We saw ID1 "," ID2 "," followed by something which + // was not an identifier. We must be seeing the start + // of a type, and ID1 and ID2 must be types, and the + // parameters don't have names. + parameters_have_names = false; + } + else if (this->peek_token()->is_op(OPERATOR_RPAREN)) + { + // We saw ID1 "," ID2 ")". ID1 and ID2 must be types, + // and the parameters don't have names. + parameters_have_names = false; + } + else if (this->peek_token()->is_op(OPERATOR_DOT)) + { + // We saw ID1 "," ID2 ".". ID2 must be a package name, + // ID1 must be a type, and the parameters don't have + // names. + parameters_have_names = false; + this->unget_token(Token::make_identifier_token(name, is_exported, + location)); + ret->pop_back(); + just_saw_comma = true; + } + else + { + // We saw ID1 "," ID2 followed by something other than + // ",", ".", or ")". We must be looking at the start of + // a type, and ID1 and ID2 must be parameter names. + parameters_have_names = true; + } + + if (parameters_have_names) + { + go_assert(!just_saw_comma); + // We have just seen ID1, ID2 xxx. + Type* type; + if (!this->peek_token()->is_op(OPERATOR_ELLIPSIS)) + type = this->type(); + else + { + error_at(this->location(), "%<...%> only permits one name"); + saw_error = true; + this->advance_token(); + type = this->type(); + } + for (size_t i = 0; i < ret->size(); ++i) + ret->set_type(i, type); + if (!this->peek_token()->is_op(OPERATOR_COMMA)) + return saw_error ? NULL : ret; + if (this->advance_token()->is_op(OPERATOR_RPAREN)) + return saw_error ? NULL : ret; + } + else + { + Typed_identifier_list* tret = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator p = ret->begin(); + p != ret->end(); + ++p) + { + Named_object* no = this->gogo_->lookup(p->name(), NULL); + Type* type; + if (no == NULL) + no = this->gogo_->add_unknown_name(p->name(), + p->location()); + + if (no->is_type()) + type = no->type_value(); + else if (no->is_unknown() || no->is_type_declaration()) + type = Type::make_forward_declaration(no); + else + { + error_at(p->location(), "expected %<%s%> to be a type", + Gogo::message_name(p->name()).c_str()); + saw_error = true; + type = Type::make_error_type(); + } + tret->push_back(Typed_identifier("", type, p->location())); + } + delete ret; + ret = tret; + if (!just_saw_comma + || this->peek_token()->is_op(OPERATOR_RPAREN)) + return saw_error ? NULL : ret; + } + } + } + + bool mix_error = false; + this->parameter_decl(parameters_have_names, ret, is_varargs, &mix_error); + while (this->peek_token()->is_op(OPERATOR_COMMA)) + { + if (this->advance_token()->is_op(OPERATOR_RPAREN)) + break; + if (is_varargs != NULL && *is_varargs) + { + error_at(this->location(), "%<...%> must be last parameter"); + saw_error = true; + } + this->parameter_decl(parameters_have_names, ret, is_varargs, &mix_error); + } + if (mix_error) + { + error_at(location, "invalid named/anonymous mix"); + saw_error = true; + } + if (saw_error) + { + delete ret; + return NULL; + } + return ret; +} + +// ParameterDecl = [ IdentifierList ] [ "..." ] Type . + +void +Parse::parameter_decl(bool parameters_have_names, + Typed_identifier_list* til, + bool* is_varargs, + bool* mix_error) +{ + if (!parameters_have_names) + { + Type* type; + Location location = this->location(); + if (!this->peek_token()->is_identifier()) + { + if (!this->peek_token()->is_op(OPERATOR_ELLIPSIS)) + type = this->type(); + else + { + if (is_varargs == NULL) + error_at(this->location(), "invalid use of %<...%>"); + else + *is_varargs = true; + this->advance_token(); + if (is_varargs == NULL + && this->peek_token()->is_op(OPERATOR_RPAREN)) + type = Type::make_error_type(); + else + { + Type* element_type = this->type(); + type = Type::make_array_type(element_type, NULL); + } + } + } + else + { + type = this->type_name(false); + if (type->is_error_type() + || (!this->peek_token()->is_op(OPERATOR_COMMA) + && !this->peek_token()->is_op(OPERATOR_RPAREN))) + { + *mix_error = true; + while (!this->peek_token()->is_op(OPERATOR_COMMA) + && !this->peek_token()->is_op(OPERATOR_RPAREN)) + this->advance_token(); + } + } + if (!type->is_error_type()) + til->push_back(Typed_identifier("", type, location)); + } + else + { + size_t orig_count = til->size(); + if (this->peek_token()->is_identifier()) + this->identifier_list(til); + else + *mix_error = true; + size_t new_count = til->size(); + + Type* type; + if (!this->peek_token()->is_op(OPERATOR_ELLIPSIS)) + type = this->type(); + else + { + if (is_varargs == NULL) + error_at(this->location(), "invalid use of %<...%>"); + else if (new_count > orig_count + 1) + error_at(this->location(), "%<...%> only permits one name"); + else + *is_varargs = true; + this->advance_token(); + Type* element_type = this->type(); + type = Type::make_array_type(element_type, NULL); + } + for (size_t i = orig_count; i < new_count; ++i) + til->set_type(i, type); + } +} + +// Result = Parameters | Type . + +// This returns false on a parse error. + +bool +Parse::result(Typed_identifier_list** presults) +{ + if (this->peek_token()->is_op(OPERATOR_LPAREN)) + return this->parameters(presults, NULL); + else + { + Location location = this->location(); + Type* type = this->type(); + if (type->is_error_type()) + { + *presults = NULL; + return false; + } + Typed_identifier_list* til = new Typed_identifier_list(); + til->push_back(Typed_identifier("", type, location)); + *presults = til; + return true; + } +} + +// Block = "{" [ StatementList ] "}" . + +// Returns the location of the closing brace. + +Location +Parse::block() +{ + if (!this->peek_token()->is_op(OPERATOR_LCURLY)) + { + Location loc = this->location(); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON) + && this->advance_token()->is_op(OPERATOR_LCURLY)) + error_at(loc, "unexpected semicolon or newline before %<{%>"); + else + { + error_at(this->location(), "expected %<{%>"); + return Linemap::unknown_location(); + } + } + + const Token* token = this->advance_token(); + + if (!token->is_op(OPERATOR_RCURLY)) + { + this->statement_list(); + token = this->peek_token(); + if (!token->is_op(OPERATOR_RCURLY)) + { + if (!token->is_eof() || !saw_errors()) + error_at(this->location(), "expected %<}%>"); + + this->gogo_->mark_locals_used(); + + // Skip ahead to the end of the block, in hopes of avoiding + // lots of meaningless errors. + Location ret = token->location(); + int nest = 0; + while (!token->is_eof()) + { + if (token->is_op(OPERATOR_LCURLY)) + ++nest; + else if (token->is_op(OPERATOR_RCURLY)) + { + --nest; + if (nest < 0) + { + this->advance_token(); + break; + } + } + token = this->advance_token(); + ret = token->location(); + } + return ret; + } + } + + Location ret = token->location(); + this->advance_token(); + return ret; +} + +// InterfaceType = "interface" "{" [ MethodSpecList ] "}" . +// MethodSpecList = MethodSpec { ";" MethodSpec } [ ";" ] . + +Type* +Parse::interface_type() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_INTERFACE)); + Location location = this->location(); + + if (!this->advance_token()->is_op(OPERATOR_LCURLY)) + { + Location token_loc = this->location(); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON) + && this->advance_token()->is_op(OPERATOR_LCURLY)) + error_at(token_loc, "unexpected semicolon or newline before %<{%>"); + else + { + error_at(this->location(), "expected %<{%>"); + return Type::make_error_type(); + } + } + this->advance_token(); + + Typed_identifier_list* methods = new Typed_identifier_list(); + if (!this->peek_token()->is_op(OPERATOR_RCURLY)) + { + this->method_spec(methods); + while (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + { + if (this->advance_token()->is_op(OPERATOR_RCURLY)) + break; + this->method_spec(methods); + } + if (!this->peek_token()->is_op(OPERATOR_RCURLY)) + { + error_at(this->location(), "expected %<}%>"); + while (!this->advance_token()->is_op(OPERATOR_RCURLY)) + { + if (this->peek_token()->is_eof()) + return Type::make_error_type(); + } + } + } + this->advance_token(); + + if (methods->empty()) + { + delete methods; + methods = NULL; + } + + Interface_type* ret = Type::make_interface_type(methods, location); + this->gogo_->record_interface_type(ret); + return ret; +} + +// MethodSpec = MethodName Signature | InterfaceTypeName . +// MethodName = identifier . +// InterfaceTypeName = TypeName . + +void +Parse::method_spec(Typed_identifier_list* methods) +{ + const Token* token = this->peek_token(); + if (!token->is_identifier()) + { + error_at(this->location(), "expected identifier"); + return; + } + + std::string name = token->identifier(); + bool is_exported = token->is_identifier_exported(); + Location location = token->location(); + + if (this->advance_token()->is_op(OPERATOR_LPAREN)) + { + // This is a MethodName. + name = this->gogo_->pack_hidden_name(name, is_exported); + Type* type = this->signature(NULL, location); + if (type == NULL) + return; + methods->push_back(Typed_identifier(name, type, location)); + } + else + { + this->unget_token(Token::make_identifier_token(name, is_exported, + location)); + Type* type = this->type_name(false); + if (type->is_error_type() + || (!this->peek_token()->is_op(OPERATOR_SEMICOLON) + && !this->peek_token()->is_op(OPERATOR_RCURLY))) + { + if (this->peek_token()->is_op(OPERATOR_COMMA)) + error_at(this->location(), + "name list not allowed in interface type"); + else + error_at(location, "expected signature or type name"); + this->gogo_->mark_locals_used(); + token = this->peek_token(); + while (!token->is_eof() + && !token->is_op(OPERATOR_SEMICOLON) + && !token->is_op(OPERATOR_RCURLY)) + token = this->advance_token(); + return; + } + // This must be an interface type, but we can't check that now. + // We check it and pull out the methods in + // Interface_type::do_verify. + methods->push_back(Typed_identifier("", type, location)); + } +} + +// Declaration = ConstDecl | TypeDecl | VarDecl | FunctionDecl | MethodDecl . + +void +Parse::declaration() +{ + const Token* token = this->peek_token(); + + bool saw_nointerface = this->lex_->get_and_clear_nointerface(); + if (saw_nointerface && !token->is_keyword(KEYWORD_FUNC)) + warning_at(token->location(), 0, + "ignoring magic //go:nointerface comment before non-method"); + + if (token->is_keyword(KEYWORD_CONST)) + this->const_decl(); + else if (token->is_keyword(KEYWORD_TYPE)) + this->type_decl(); + else if (token->is_keyword(KEYWORD_VAR)) + this->var_decl(); + else if (token->is_keyword(KEYWORD_FUNC)) + this->function_decl(saw_nointerface); + else + { + error_at(this->location(), "expected declaration"); + this->advance_token(); + } +} + +bool +Parse::declaration_may_start_here() +{ + const Token* token = this->peek_token(); + return (token->is_keyword(KEYWORD_CONST) + || token->is_keyword(KEYWORD_TYPE) + || token->is_keyword(KEYWORD_VAR) + || token->is_keyword(KEYWORD_FUNC)); +} + +// Decl

= P | "(" [ List

] ")" . + +void +Parse::decl(void (Parse::*pfn)(void*), void* varg) +{ + if (this->peek_token()->is_eof()) + { + if (!saw_errors()) + error_at(this->location(), "unexpected end of file"); + return; + } + + if (!this->peek_token()->is_op(OPERATOR_LPAREN)) + (this->*pfn)(varg); + else + { + if (!this->advance_token()->is_op(OPERATOR_RPAREN)) + { + this->list(pfn, varg, true); + if (!this->peek_token()->is_op(OPERATOR_RPAREN)) + { + error_at(this->location(), "missing %<)%>"); + while (!this->advance_token()->is_op(OPERATOR_RPAREN)) + { + if (this->peek_token()->is_eof()) + return; + } + } + } + this->advance_token(); + } +} + +// List

= P { ";" P } [ ";" ] . + +// In order to pick up the trailing semicolon we need to know what +// might follow. This is either a '}' or a ')'. + +void +Parse::list(void (Parse::*pfn)(void*), void* varg, bool follow_is_paren) +{ + (this->*pfn)(varg); + Operator follow = follow_is_paren ? OPERATOR_RPAREN : OPERATOR_RCURLY; + while (this->peek_token()->is_op(OPERATOR_SEMICOLON) + || this->peek_token()->is_op(OPERATOR_COMMA)) + { + if (this->peek_token()->is_op(OPERATOR_COMMA)) + error_at(this->location(), "unexpected comma"); + if (this->advance_token()->is_op(follow)) + break; + (this->*pfn)(varg); + } +} + +// ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) . + +void +Parse::const_decl() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_CONST)); + this->advance_token(); + this->reset_iota(); + + Type* last_type = NULL; + Expression_list* last_expr_list = NULL; + + if (!this->peek_token()->is_op(OPERATOR_LPAREN)) + this->const_spec(&last_type, &last_expr_list); + else + { + this->advance_token(); + while (!this->peek_token()->is_op(OPERATOR_RPAREN)) + { + this->const_spec(&last_type, &last_expr_list); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + else if (!this->peek_token()->is_op(OPERATOR_RPAREN)) + { + error_at(this->location(), "expected %<;%> or %<)%> or newline"); + if (!this->skip_past_error(OPERATOR_RPAREN)) + return; + } + } + this->advance_token(); + } + + if (last_expr_list != NULL) + delete last_expr_list; +} + +// ConstSpec = IdentifierList [ [ CompleteType ] "=" ExpressionList ] . + +void +Parse::const_spec(Type** last_type, Expression_list** last_expr_list) +{ + Typed_identifier_list til; + this->identifier_list(&til); + + Type* type = NULL; + if (this->type_may_start_here()) + { + type = this->type(); + *last_type = NULL; + *last_expr_list = NULL; + } + + Expression_list *expr_list; + if (!this->peek_token()->is_op(OPERATOR_EQ)) + { + if (*last_expr_list == NULL) + { + error_at(this->location(), "expected %<=%>"); + return; + } + type = *last_type; + expr_list = new Expression_list; + for (Expression_list::const_iterator p = (*last_expr_list)->begin(); + p != (*last_expr_list)->end(); + ++p) + expr_list->push_back((*p)->copy()); + } + else + { + this->advance_token(); + expr_list = this->expression_list(NULL, false, true); + *last_type = type; + if (*last_expr_list != NULL) + delete *last_expr_list; + *last_expr_list = expr_list; + } + + Expression_list::const_iterator pe = expr_list->begin(); + for (Typed_identifier_list::iterator pi = til.begin(); + pi != til.end(); + ++pi, ++pe) + { + if (pe == expr_list->end()) + { + error_at(this->location(), "not enough initializers"); + return; + } + if (type != NULL) + pi->set_type(type); + + if (!Gogo::is_sink_name(pi->name())) + this->gogo_->add_constant(*pi, *pe, this->iota_value()); + else + { + static int count; + char buf[30]; + snprintf(buf, sizeof buf, ".$sinkconst%d", count); + ++count; + Typed_identifier ti(std::string(buf), type, pi->location()); + Named_object* no = this->gogo_->add_constant(ti, *pe, this->iota_value()); + no->const_value()->set_is_sink(); + } + } + if (pe != expr_list->end()) + error_at(this->location(), "too many initializers"); + + this->increment_iota(); + + return; +} + +// TypeDecl = "type" Decl . + +void +Parse::type_decl() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_TYPE)); + this->advance_token(); + this->decl(&Parse::type_spec, NULL); +} + +// TypeSpec = identifier Type . + +void +Parse::type_spec(void*) +{ + const Token* token = this->peek_token(); + if (!token->is_identifier()) + { + error_at(this->location(), "expected identifier"); + return; + } + std::string name = token->identifier(); + bool is_exported = token->is_identifier_exported(); + Location location = token->location(); + token = this->advance_token(); + + // The scope of the type name starts at the point where the + // identifier appears in the source code. We implement this by + // declaring the type before we read the type definition. + Named_object* named_type = NULL; + if (name != "_") + { + name = this->gogo_->pack_hidden_name(name, is_exported); + named_type = this->gogo_->declare_type(name, location); + } + + Type* type; + if (!this->peek_token()->is_op(OPERATOR_SEMICOLON)) + type = this->type(); + else + { + error_at(this->location(), + "unexpected semicolon or newline in type declaration"); + type = Type::make_error_type(); + this->advance_token(); + } + + if (type->is_error_type()) + { + this->gogo_->mark_locals_used(); + while (!this->peek_token()->is_op(OPERATOR_SEMICOLON) + && !this->peek_token()->is_eof()) + this->advance_token(); + } + + if (name != "_") + { + if (named_type->is_type_declaration()) + { + Type* ftype = type->forwarded(); + if (ftype->forward_declaration_type() != NULL + && (ftype->forward_declaration_type()->named_object() + == named_type)) + { + error_at(location, "invalid recursive type"); + type = Type::make_error_type(); + } + + this->gogo_->define_type(named_type, + Type::make_named_type(named_type, type, + location)); + go_assert(named_type->package() == NULL); + } + else + { + // This will probably give a redefinition error. + this->gogo_->add_type(name, type, location); + } + } +} + +// VarDecl = "var" Decl . + +void +Parse::var_decl() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_VAR)); + this->advance_token(); + this->decl(&Parse::var_spec, NULL); +} + +// VarSpec = IdentifierList +// ( CompleteType [ "=" ExpressionList ] | "=" ExpressionList ) . + +void +Parse::var_spec(void*) +{ + // Get the variable names. + Typed_identifier_list til; + this->identifier_list(&til); + + Location location = this->location(); + + Type* type = NULL; + Expression_list* init = NULL; + if (!this->peek_token()->is_op(OPERATOR_EQ)) + { + type = this->type(); + if (type->is_error_type()) + { + this->gogo_->mark_locals_used(); + while (!this->peek_token()->is_op(OPERATOR_EQ) + && !this->peek_token()->is_op(OPERATOR_SEMICOLON) + && !this->peek_token()->is_eof()) + this->advance_token(); + } + if (this->peek_token()->is_op(OPERATOR_EQ)) + { + this->advance_token(); + init = this->expression_list(NULL, false, true); + } + } + else + { + this->advance_token(); + init = this->expression_list(NULL, false, true); + } + + this->init_vars(&til, type, init, false, location); + + if (init != NULL) + delete init; +} + +// Create variables. TIL is a list of variable names. If TYPE is not +// NULL, it is the type of all the variables. If INIT is not NULL, it +// is an initializer list for the variables. + +void +Parse::init_vars(const Typed_identifier_list* til, Type* type, + Expression_list* init, bool is_coloneq, + Location location) +{ + // Check for an initialization which can yield multiple values. + if (init != NULL && init->size() == 1 && til->size() > 1) + { + if (this->init_vars_from_call(til, type, *init->begin(), is_coloneq, + location)) + return; + if (this->init_vars_from_map(til, type, *init->begin(), is_coloneq, + location)) + return; + if (this->init_vars_from_receive(til, type, *init->begin(), is_coloneq, + location)) + return; + if (this->init_vars_from_type_guard(til, type, *init->begin(), + is_coloneq, location)) + return; + } + + if (init != NULL && init->size() != til->size()) + { + if (init->empty() || !init->front()->is_error_expression()) + error_at(location, "wrong number of initializations"); + init = NULL; + if (type == NULL) + type = Type::make_error_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. 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) + { + if (init != NULL) + go_assert(pexpr != init->end()); + this->init_var(*p, type, init == NULL ? NULL : *pexpr, is_coloneq, + false, &any_new, vars, vals); + if (init != NULL) + ++pexpr; + } + if (init != NULL) + 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 +// call. This returns true if we have set up the variables and the +// initialization. + +bool +Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type, + Expression* expr, bool is_coloneq, + Location location) +{ + Call_expression* call = expr->call_expression(); + if (call == NULL) + return false; + + // This is a function call. We can't check here whether it returns + // the right number of values, but it might. Declare the variables, + // and then assign the results of the call to them. + + 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, ivars, ivals); + + if (this->gogo_->in_global_scope() && no->is_variable()) + { + if (first_var == NULL) + first_var = no; + else + { + // The subsequent vars have an implicit dependency on + // the first one, so that everything gets initialized in + // the right order and so that we detect cycles + // correctly. + this->gogo_->record_var_depends_on(no->var_value(), first_var); + } + } + } + + if (is_coloneq && !any_new) + error_at(location, "variables redeclared but no variable is new"); + + this->finish_init_vars(ivars, ivals, location); + + return true; +} + +// See if we need to initialize a pair of values from a map index +// expression. This returns true if we have set up the variables and +// the initialization. + +bool +Parse::init_vars_from_map(const Typed_identifier_list* vars, Type* type, + Expression* expr, bool is_coloneq, + Location location) +{ + Index_expression* index = expr->index_expression(); + if (index == NULL) + return false; + if (vars->size() != 2) + return false; + + // This is an index which is being assigned to two variables. It + // must be a map index. Declare the variables, and then assign the + // results of the map index. + bool any_new = false; + 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, 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); + + ++p; + Type* var_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, NULL, NULL); + Expression* present_var = Expression::make_var_reference(no, location); + + if (is_coloneq && !any_new) + error_at(location, "variables redeclared but no variable is new"); + + Statement* s = Statement::make_tuple_map_assignment(val_var, present_var, + index, location); + + if (!this->gogo_->in_global_scope()) + this->gogo_->add_statement(s); + else if (!val_no->is_sink()) + { + if (val_no->is_variable()) + val_no->var_value()->add_preinit_statement(this->gogo_, s); + } + else if (!no->is_sink()) + { + if (no->is_variable()) + no->var_value()->add_preinit_statement(this->gogo_, s); + } + else + { + // Execute the map index expression just so that we can fail if + // the map is nil. + Named_object* dummy = this->create_dummy_global(Type::lookup_bool_type(), + NULL, location); + dummy->var_value()->add_preinit_statement(this->gogo_, s); + } + + return true; +} + +// See if we need to initialize a pair of values from a receive +// expression. This returns true if we have set up the variables and +// the initialization. + +bool +Parse::init_vars_from_receive(const Typed_identifier_list* vars, Type* type, + Expression* expr, bool is_coloneq, + Location location) +{ + Receive_expression* receive = expr->receive_expression(); + if (receive == NULL) + return false; + if (vars->size() != 2) + return false; + + // This is a receive expression which is being assigned to two + // variables. Declare the variables, and then assign the results of + // the receive. + bool any_new = false; + 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, 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); + + ++p; + Type* var_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, NULL, NULL); + Expression* received_var = Expression::make_var_reference(no, location); + + if (is_coloneq && !any_new) + error_at(location, "variables redeclared but no variable is new"); + + Statement* s = Statement::make_tuple_receive_assignment(val_var, + received_var, + receive->channel(), + location); + + if (!this->gogo_->in_global_scope()) + this->gogo_->add_statement(s); + else if (!val_no->is_sink()) + { + if (val_no->is_variable()) + val_no->var_value()->add_preinit_statement(this->gogo_, s); + } + else if (!no->is_sink()) + { + if (no->is_variable()) + no->var_value()->add_preinit_statement(this->gogo_, s); + } + else + { + Named_object* dummy = this->create_dummy_global(Type::lookup_bool_type(), + NULL, location); + dummy->var_value()->add_preinit_statement(this->gogo_, s); + } + + return true; +} + +// See if we need to initialize a pair of values from a type guard +// expression. This returns true if we have set up the variables and +// the initialization. + +bool +Parse::init_vars_from_type_guard(const Typed_identifier_list* vars, + Type* type, Expression* expr, + bool is_coloneq, Location location) +{ + Type_guard_expression* type_guard = expr->type_guard_expression(); + if (type_guard == NULL) + return false; + if (vars->size() != 2) + return false; + + // This is a type guard expression which is being assigned to two + // variables. Declare the variables, and then assign the results of + // the type guard. + bool any_new = false; + Typed_identifier_list::const_iterator p = vars->begin(); + Type* var_type = type; + 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, NULL, NULL); + Expression* val_var = Expression::make_var_reference(val_no, location); + + ++p; + var_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, NULL, NULL); + Expression* ok_var = Expression::make_var_reference(no, location); + + Expression* texpr = type_guard->expr(); + Type* t = type_guard->type(); + Statement* s = Statement::make_tuple_type_guard_assignment(val_var, ok_var, + texpr, t, + location); + + if (is_coloneq && !any_new) + error_at(location, "variables redeclared but no variable is new"); + + if (!this->gogo_->in_global_scope()) + this->gogo_->add_statement(s); + else if (!val_no->is_sink()) + { + if (val_no->is_variable()) + val_no->var_value()->add_preinit_statement(this->gogo_, s); + } + else if (!no->is_sink()) + { + if (no->is_variable()) + no->var_value()->add_preinit_statement(this->gogo_, s); + } + else + { + Named_object* dummy = this->create_dummy_global(type, NULL, location); + dummy->var_value()->add_preinit_statement(this->gogo_, s); + } + + return true; +} + +// Create a single variable. If IS_COLONEQ is true, we permit +// redeclarations in the same block, and we set *IS_NEW when we find a +// new variable which is not a redeclaration. + +Named_object* +Parse::init_var(const Typed_identifier& tid, Type* type, Expression* init, + bool is_coloneq, bool type_from_init, bool* is_new, + Expression_list* vars, Expression_list* vals) +{ + Location location = tid.location(); + + if (Gogo::is_sink_name(tid.name())) + { + if (!type_from_init && init != NULL) + { + if (this->gogo_->in_global_scope()) + return this->create_dummy_global(type, init, location); + else + { + // Create a dummy variable so that we will check whether the + // initializer can be assigned to the type. + Variable* var = new Variable(type, init, false, false, false, + location); + var->set_is_used(); + static int count; + char buf[30]; + snprintf(buf, sizeof buf, "sink$%d", count); + ++count; + return this->gogo_->add_variable(buf, var); + } + } + if (type != NULL) + this->gogo_->add_type_to_verify(type); + return this->gogo_->add_sink(); + } + + if (is_coloneq) + { + Named_object* no = this->gogo_->lookup_in_block(tid.name()); + if (no != NULL + && (no->is_variable() || no->is_result_variable())) + { + // INIT may be NULL even when IS_COLONEQ is true for cases + // like v, ok := x.(int). + if (!type_from_init && init != NULL) + { + go_assert(vars != NULL && vals != NULL); + vars->push_back(Expression::make_var_reference(no, location)); + vals->push_back(init); + } + return no; + } + } + *is_new = true; + Variable* var = new Variable(type, init, this->gogo_->in_global_scope(), + false, false, location); + Named_object* no = this->gogo_->add_variable(tid.name(), var); + if (!no->is_variable()) + { + // The name is already defined, so we just gave an error. + return this->gogo_->add_sink(); + } + return no; +} + +// Create a dummy global variable to force an initializer to be run in +// the right place. This is used when a sink variable is initialized +// at global scope. + +Named_object* +Parse::create_dummy_global(Type* type, Expression* init, + Location location) +{ + if (type == NULL && init == NULL) + type = Type::lookup_bool_type(); + Variable* var = new Variable(type, init, true, false, false, location); + static int count; + char buf[30]; + snprintf(buf, sizeof buf, "_.%d", count); + ++count; + 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. + +// FIXME: We also have to implement +// IdentifierList ":=" ExpressionList +// In order to support both "a, b := 1, 0" and "a, b = 1, 0" we accept +// tuple assignments here as well. + +// If MAY_BE_COMPOSITE_LIT is true, the expression on the right hand +// side may be a composite literal. + +// If P_RANGE_CLAUSE is not NULL, then this will recognize a +// RangeClause. + +// If P_TYPE_SWITCH is not NULL, this will recognize a type switch +// guard (var := expr.("type") using the literal keyword "type"). + +void +Parse::simple_var_decl_or_assignment(const std::string& name, + Location location, + bool may_be_composite_lit, + Range_clause* p_range_clause, + Type_switch* p_type_switch) +{ + Typed_identifier_list til; + til.push_back(Typed_identifier(name, NULL, location)); + + // We've seen one identifier. If we see a comma now, this could be + // "a, *p = 1, 2". + if (this->peek_token()->is_op(OPERATOR_COMMA)) + { + go_assert(p_type_switch == NULL); + while (true) + { + const Token* token = this->advance_token(); + if (!token->is_identifier()) + break; + + std::string id = token->identifier(); + bool is_id_exported = token->is_identifier_exported(); + Location id_location = token->location(); + + token = this->advance_token(); + if (!token->is_op(OPERATOR_COMMA)) + { + if (token->is_op(OPERATOR_COLONEQ)) + { + id = this->gogo_->pack_hidden_name(id, is_id_exported); + til.push_back(Typed_identifier(id, NULL, location)); + } + else + this->unget_token(Token::make_identifier_token(id, + is_id_exported, + id_location)); + break; + } + + id = this->gogo_->pack_hidden_name(id, is_id_exported); + til.push_back(Typed_identifier(id, NULL, location)); + } + + // We have a comma separated list of identifiers in TIL. If the + // next token is COLONEQ, then this is a simple var decl, and we + // have the complete list of identifiers. If the next token is + // not COLONEQ, then the only valid parse is a tuple assignment. + // The list of identifiers we have so far is really a list of + // expressions. There are more expressions following. + + if (!this->peek_token()->is_op(OPERATOR_COLONEQ)) + { + Expression_list* exprs = new Expression_list; + for (Typed_identifier_list::const_iterator p = til.begin(); + p != til.end(); + ++p) + exprs->push_back(this->id_to_expression(p->name(), + p->location())); + + Expression_list* more_exprs = + this->expression_list(NULL, true, may_be_composite_lit); + for (Expression_list::const_iterator p = more_exprs->begin(); + p != more_exprs->end(); + ++p) + exprs->push_back(*p); + delete more_exprs; + + this->tuple_assignment(exprs, may_be_composite_lit, p_range_clause); + return; + } + } + + go_assert(this->peek_token()->is_op(OPERATOR_COLONEQ)); + const Token* token = this->advance_token(); + + if (p_range_clause != NULL && token->is_keyword(KEYWORD_RANGE)) + { + this->range_clause_decl(&til, p_range_clause); + return; + } + + Expression_list* init; + if (p_type_switch == NULL) + init = this->expression_list(NULL, false, may_be_composite_lit); + else + { + bool is_type_switch = false; + Expression* expr = this->expression(PRECEDENCE_NORMAL, false, + may_be_composite_lit, + &is_type_switch, NULL); + if (is_type_switch) + { + p_type_switch->found = true; + p_type_switch->name = name; + p_type_switch->location = location; + p_type_switch->expr = expr; + return; + } + + if (!this->peek_token()->is_op(OPERATOR_COMMA)) + { + init = new Expression_list(); + init->push_back(expr); + } + else + { + this->advance_token(); + init = this->expression_list(expr, false, may_be_composite_lit); + } + } + + this->init_vars(&til, NULL, init, true, location); +} + +// FunctionDecl = "func" identifier Signature [ Block ] . +// MethodDecl = "func" Receiver identifier Signature [ Block ] . + +// Deprecated gcc extension: +// FunctionDecl = "func" identifier Signature +// __asm__ "(" string_lit ")" . +// This extension means a function whose real name is the identifier +// inside the asm. This extension will be removed at some future +// date. It has been replaced with //extern comments. + +// SAW_NOINTERFACE is true if we saw a magic //go:nointerface comment, +// which means that we omit the method from the type descriptor. + +void +Parse::function_decl(bool saw_nointerface) +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC)); + Location location = this->location(); + std::string extern_name = this->lex_->extern_name(); + const Token* token = this->advance_token(); + + Typed_identifier* rec = NULL; + if (token->is_op(OPERATOR_LPAREN)) + { + rec = this->receiver(); + token = this->peek_token(); + } + else if (saw_nointerface) + { + warning_at(location, 0, + "ignoring magic //go:nointerface comment before non-method"); + saw_nointerface = false; + } + + if (!token->is_identifier()) + { + error_at(this->location(), "expected function name"); + return; + } + + std::string name = + this->gogo_->pack_hidden_name(token->identifier(), + token->is_identifier_exported()); + + this->advance_token(); + + Function_type* fntype = this->signature(rec, this->location()); + + Named_object* named_object = NULL; + + if (this->peek_token()->is_keyword(KEYWORD_ASM)) + { + if (!this->advance_token()->is_op(OPERATOR_LPAREN)) + { + error_at(this->location(), "expected %<(%>"); + return; + } + token = this->advance_token(); + if (!token->is_string()) + { + error_at(this->location(), "expected string"); + return; + } + std::string asm_name = token->string_value(); + if (!this->advance_token()->is_op(OPERATOR_RPAREN)) + { + error_at(this->location(), "expected %<)%>"); + return; + } + this->advance_token(); + if (!Gogo::is_sink_name(name)) + { + named_object = this->gogo_->declare_function(name, fntype, location); + if (named_object->is_function_declaration()) + named_object->func_declaration_value()->set_asm_name(asm_name); + } + } + + // Check for the easy error of a newline before the opening brace. + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + { + Location semi_loc = this->location(); + if (this->advance_token()->is_op(OPERATOR_LCURLY)) + error_at(this->location(), + "unexpected semicolon or newline before %<{%>"); + else + this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, + semi_loc)); + } + + if (!this->peek_token()->is_op(OPERATOR_LCURLY)) + { + if (named_object == NULL && !Gogo::is_sink_name(name)) + { + if (fntype == NULL) + this->gogo_->add_erroneous_name(name); + else + { + named_object = this->gogo_->declare_function(name, fntype, + location); + if (!extern_name.empty() + && named_object->is_function_declaration()) + { + Function_declaration* fd = + named_object->func_declaration_value(); + fd->set_asm_name(extern_name); + } + } + } + + if (saw_nointerface) + warning_at(location, 0, + ("ignoring magic //go:nointerface comment " + "before declaration")); + } + else + { + bool hold_is_erroneous_function = this->is_erroneous_function_; + if (fntype == NULL) + { + fntype = Type::make_function_type(NULL, NULL, NULL, location); + this->is_erroneous_function_ = true; + if (!Gogo::is_sink_name(name)) + this->gogo_->add_erroneous_name(name); + name = this->gogo_->pack_hidden_name("_", false); + } + named_object = this->gogo_->start_function(name, fntype, true, location); + Location end_loc = this->block(); + this->gogo_->finish_function(end_loc); + if (saw_nointerface + && !this->is_erroneous_function_ + && named_object->is_function()) + named_object->func_value()->set_nointerface(); + this->is_erroneous_function_ = hold_is_erroneous_function; + } +} + +// Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" . +// BaseTypeName = identifier . + +Typed_identifier* +Parse::receiver() +{ + go_assert(this->peek_token()->is_op(OPERATOR_LPAREN)); + + std::string name; + const Token* token = this->advance_token(); + Location location = token->location(); + if (!token->is_op(OPERATOR_MULT)) + { + if (!token->is_identifier()) + { + error_at(this->location(), "method has no receiver"); + this->gogo_->mark_locals_used(); + while (!token->is_eof() && !token->is_op(OPERATOR_RPAREN)) + token = this->advance_token(); + if (!token->is_eof()) + this->advance_token(); + return NULL; + } + name = token->identifier(); + bool is_exported = token->is_identifier_exported(); + token = this->advance_token(); + if (!token->is_op(OPERATOR_DOT) && !token->is_op(OPERATOR_RPAREN)) + { + // An identifier followed by something other than a dot or a + // right parenthesis must be a receiver name followed by a + // type. + name = this->gogo_->pack_hidden_name(name, is_exported); + } + else + { + // This must be a type name. + this->unget_token(Token::make_identifier_token(name, is_exported, + location)); + token = this->peek_token(); + name.clear(); + } + } + + // Here the receiver name is in NAME (it is empty if the receiver is + // unnamed) and TOKEN is the first token in the type. + + bool is_pointer = false; + if (token->is_op(OPERATOR_MULT)) + { + is_pointer = true; + token = this->advance_token(); + } + + if (!token->is_identifier()) + { + error_at(this->location(), "expected receiver name or type"); + this->gogo_->mark_locals_used(); + int c = token->is_op(OPERATOR_LPAREN) ? 1 : 0; + while (!token->is_eof()) + { + token = this->advance_token(); + if (token->is_op(OPERATOR_LPAREN)) + ++c; + else if (token->is_op(OPERATOR_RPAREN)) + { + if (c == 0) + break; + --c; + } + } + if (!token->is_eof()) + this->advance_token(); + return NULL; + } + + Type* type = this->type_name(true); + + if (is_pointer && !type->is_error_type()) + type = Type::make_pointer_type(type); + + if (this->peek_token()->is_op(OPERATOR_RPAREN)) + this->advance_token(); + else + { + if (this->peek_token()->is_op(OPERATOR_COMMA)) + error_at(this->location(), "method has multiple receivers"); + else + error_at(this->location(), "expected %<)%>"); + this->gogo_->mark_locals_used(); + while (!token->is_eof() && !token->is_op(OPERATOR_RPAREN)) + token = this->advance_token(); + if (!token->is_eof()) + this->advance_token(); + return NULL; + } + + return new Typed_identifier(name, type, location); +} + +// Operand = Literal | QualifiedIdent | MethodExpr | "(" Expression ")" . +// Literal = BasicLit | CompositeLit | FunctionLit . +// BasicLit = int_lit | float_lit | imaginary_lit | char_lit | string_lit . + +// If MAY_BE_SINK is true, this operand may be "_". + +// If IS_PARENTHESIZED is not NULL, *IS_PARENTHESIZED is set to true +// if the entire expression is in parentheses. + +Expression* +Parse::operand(bool may_be_sink, bool* is_parenthesized) +{ + const Token* token = this->peek_token(); + Expression* ret; + switch (token->classification()) + { + case Token::TOKEN_IDENTIFIER: + { + Location location = token->location(); + std::string id = token->identifier(); + bool is_exported = token->is_identifier_exported(); + std::string packed = this->gogo_->pack_hidden_name(id, is_exported); + + Named_object* in_function; + Named_object* named_object = this->gogo_->lookup(packed, &in_function); + + Package* package = NULL; + if (named_object != NULL && named_object->is_package()) + { + if (!this->advance_token()->is_op(OPERATOR_DOT) + || !this->advance_token()->is_identifier()) + { + error_at(location, "unexpected reference to package"); + return Expression::make_error(location); + } + package = named_object->package_value(); + package->set_used(); + id = this->peek_token()->identifier(); + is_exported = this->peek_token()->is_identifier_exported(); + packed = this->gogo_->pack_hidden_name(id, is_exported); + named_object = package->lookup(packed); + location = this->location(); + go_assert(in_function == NULL); + } + + this->advance_token(); + + if (named_object != NULL + && named_object->is_type() + && !named_object->type_value()->is_visible()) + { + go_assert(package != NULL); + error_at(location, "invalid reference to hidden type %<%s.%s%>", + Gogo::message_name(package->package_name()).c_str(), + Gogo::message_name(id).c_str()); + return Expression::make_error(location); + } + + + if (named_object == NULL) + { + if (package != NULL) + { + std::string n1 = Gogo::message_name(package->package_name()); + std::string n2 = Gogo::message_name(id); + if (!is_exported) + error_at(location, + ("invalid reference to unexported identifier " + "%<%s.%s%>"), + n1.c_str(), n2.c_str()); + else + error_at(location, + "reference to undefined identifier %<%s.%s%>", + n1.c_str(), n2.c_str()); + return Expression::make_error(location); + } + + named_object = this->gogo_->add_unknown_name(packed, location); + } + + if (in_function != NULL + && in_function != this->gogo_->current_function() + && (named_object->is_variable() + || named_object->is_result_variable())) + return this->enclosing_var_reference(in_function, named_object, + location); + + switch (named_object->classification()) + { + case Named_object::NAMED_OBJECT_CONST: + return Expression::make_const_reference(named_object, location); + case Named_object::NAMED_OBJECT_TYPE: + return Expression::make_type(named_object->type_value(), location); + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + { + Type* t = Type::make_forward_declaration(named_object); + return Expression::make_type(t, location); + } + case Named_object::NAMED_OBJECT_VAR: + case Named_object::NAMED_OBJECT_RESULT_VAR: + this->mark_var_used(named_object); + return Expression::make_var_reference(named_object, location); + case Named_object::NAMED_OBJECT_SINK: + if (may_be_sink) + return Expression::make_sink(location); + else + { + error_at(location, "cannot use _ as value"); + return Expression::make_error(location); + } + case Named_object::NAMED_OBJECT_FUNC: + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + return Expression::make_func_reference(named_object, NULL, + location); + case Named_object::NAMED_OBJECT_UNKNOWN: + { + Unknown_expression* ue = + Expression::make_unknown_reference(named_object, location); + if (this->is_erroneous_function_) + ue->set_no_error_message(); + return ue; + } + case Named_object::NAMED_OBJECT_ERRONEOUS: + return Expression::make_error(location); + default: + go_unreachable(); + } + } + go_unreachable(); + + case Token::TOKEN_STRING: + ret = Expression::make_string(token->string_value(), token->location()); + this->advance_token(); + return ret; + + case Token::TOKEN_CHARACTER: + ret = Expression::make_character(token->character_value(), NULL, + token->location()); + this->advance_token(); + return ret; + + case Token::TOKEN_INTEGER: + ret = Expression::make_integer(token->integer_value(), NULL, + token->location()); + this->advance_token(); + return ret; + + case Token::TOKEN_FLOAT: + ret = Expression::make_float(token->float_value(), NULL, + token->location()); + this->advance_token(); + return ret; + + case Token::TOKEN_IMAGINARY: + { + mpfr_t zero; + mpfr_init_set_ui(zero, 0, GMP_RNDN); + ret = Expression::make_complex(&zero, token->imaginary_value(), + NULL, token->location()); + mpfr_clear(zero); + this->advance_token(); + return ret; + } + + case Token::TOKEN_KEYWORD: + switch (token->keyword()) + { + case KEYWORD_FUNC: + return this->function_lit(); + case KEYWORD_CHAN: + case KEYWORD_INTERFACE: + case KEYWORD_MAP: + case KEYWORD_STRUCT: + { + Location location = token->location(); + return Expression::make_type(this->type(), location); + } + default: + break; + } + break; + + case Token::TOKEN_OPERATOR: + if (token->is_op(OPERATOR_LPAREN)) + { + this->advance_token(); + ret = this->expression(PRECEDENCE_NORMAL, may_be_sink, true, NULL, + NULL); + if (!this->peek_token()->is_op(OPERATOR_RPAREN)) + error_at(this->location(), "missing %<)%>"); + else + this->advance_token(); + if (is_parenthesized != NULL) + *is_parenthesized = true; + return ret; + } + else if (token->is_op(OPERATOR_LSQUARE)) + { + // Here we call array_type directly, as this is the only + // case where an ellipsis is permitted for an array type. + Location location = token->location(); + return Expression::make_type(this->array_type(true), location); + } + break; + + default: + break; + } + + error_at(this->location(), "expected operand"); + return Expression::make_error(this->location()); +} + +// Handle a reference to a variable in an enclosing function. We add +// it to a list of such variables. We return a reference to a field +// in a struct which will be passed on the static chain when calling +// the current function. + +Expression* +Parse::enclosing_var_reference(Named_object* in_function, Named_object* var, + Location location) +{ + go_assert(var->is_variable() || var->is_result_variable()); + + this->mark_var_used(var); + + Named_object* this_function = this->gogo_->current_function(); + Named_object* closure = this_function->func_value()->closure_var(); + + // The last argument to the Enclosing_var constructor is the index + // of this variable in the closure. We add 1 to the current number + // of enclosed variables, because the first field in the closure + // points to the function code. + Enclosing_var ev(var, in_function, this->enclosing_vars_.size() + 1); + std::pair ins = + this->enclosing_vars_.insert(ev); + if (ins.second) + { + // This is a variable we have not seen before. Add a new field + // to the closure type. + this_function->func_value()->add_closure_field(var, location); + } + + Expression* closure_ref = Expression::make_var_reference(closure, + location); + closure_ref = Expression::make_unary(OPERATOR_MULT, closure_ref, location); + + // The closure structure holds pointers to the variables, so we need + // to introduce an indirection. + Expression* e = Expression::make_field_reference(closure_ref, + ins.first->index(), + location); + e = Expression::make_unary(OPERATOR_MULT, e, location); + return e; +} + +// CompositeLit = LiteralType LiteralValue . +// LiteralType = StructType | ArrayType | "[" "..." "]" ElementType | +// SliceType | MapType | TypeName . +// LiteralValue = "{" [ ElementList [ "," ] ] "}" . +// ElementList = Element { "," Element } . +// Element = [ Key ":" ] Value . +// Key = FieldName | ElementIndex . +// FieldName = identifier . +// ElementIndex = Expression . +// Value = Expression | LiteralValue . + +// We have already seen the type if there is one, and we are now +// looking at the LiteralValue. The case "[" "..." "]" ElementType +// will be seen here as an array type whose length is "nil". The +// DEPTH parameter is non-zero if this is an embedded composite +// literal and the type was omitted. It gives the number of steps up +// to the type which was provided. E.g., in [][]int{{1}} it will be +// 1. In [][][]int{{{1}}} it will be 2. + +Expression* +Parse::composite_lit(Type* type, int depth, Location location) +{ + go_assert(this->peek_token()->is_op(OPERATOR_LCURLY)); + this->advance_token(); + + if (this->peek_token()->is_op(OPERATOR_RCURLY)) + { + this->advance_token(); + return Expression::make_composite_literal(type, depth, false, NULL, + false, location); + } + + bool has_keys = false; + bool all_are_names = true; + Expression_list* vals = new Expression_list; + while (true) + { + Expression* val; + bool is_type_omitted = false; + bool is_name = false; + + const Token* token = this->peek_token(); + + if (token->is_identifier()) + { + std::string identifier = token->identifier(); + bool is_exported = token->is_identifier_exported(); + Location location = token->location(); + + if (this->advance_token()->is_op(OPERATOR_COLON)) + { + // This may be a field name. We don't know for sure--it + // could also be an expression for an array index. We + // don't want to parse it as an expression because may + // trigger various errors, e.g., if this identifier + // happens to be the name of a package. + Gogo* gogo = this->gogo_; + val = this->id_to_expression(gogo->pack_hidden_name(identifier, + is_exported), + location); + is_name = true; + } + else + { + this->unget_token(Token::make_identifier_token(identifier, + is_exported, + location)); + val = this->expression(PRECEDENCE_NORMAL, false, true, NULL, + NULL); + } + } + else if (!token->is_op(OPERATOR_LCURLY)) + val = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + else + { + // This must be a composite literal inside another composite + // literal, with the type omitted for the inner one. + val = this->composite_lit(type, depth + 1, token->location()); + is_type_omitted = true; + } + + token = this->peek_token(); + if (!token->is_op(OPERATOR_COLON)) + { + if (has_keys) + vals->push_back(NULL); + is_name = false; + } + else + { + if (is_type_omitted && !val->is_error_expression()) + { + error_at(this->location(), "unexpected %<:%>"); + val = Expression::make_error(this->location()); + } + + this->advance_token(); + + if (!has_keys && !vals->empty()) + { + Expression_list* newvals = new Expression_list; + for (Expression_list::const_iterator p = vals->begin(); + p != vals->end(); + ++p) + { + newvals->push_back(NULL); + newvals->push_back(*p); + } + delete vals; + vals = newvals; + } + has_keys = true; + + if (val->unknown_expression() != NULL) + val->unknown_expression()->set_is_composite_literal_key(); + + vals->push_back(val); + + if (!token->is_op(OPERATOR_LCURLY)) + val = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + else + { + // This must be a composite literal inside another + // composite literal, with the type omitted for the + // inner one. + val = this->composite_lit(type, depth + 1, token->location()); + } + + token = this->peek_token(); + } + + vals->push_back(val); + + if (!is_name) + all_are_names = false; + + if (token->is_op(OPERATOR_COMMA)) + { + if (this->advance_token()->is_op(OPERATOR_RCURLY)) + { + this->advance_token(); + break; + } + } + else if (token->is_op(OPERATOR_RCURLY)) + { + this->advance_token(); + break; + } + else + { + 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; + while (!token->is_eof() + && (depth > 0 || !token->is_op(OPERATOR_RCURLY))) + { + if (token->is_op(OPERATOR_LCURLY)) + ++depth; + else if (token->is_op(OPERATOR_RCURLY)) + --depth; + token = this->advance_token(); + } + if (token->is_op(OPERATOR_RCURLY)) + this->advance_token(); + + return Expression::make_error(location); + } + } + + return Expression::make_composite_literal(type, depth, has_keys, vals, + all_are_names, location); +} + +// FunctionLit = "func" Signature Block . + +Expression* +Parse::function_lit() +{ + Location location = this->location(); + go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC)); + this->advance_token(); + + Enclosing_vars hold_enclosing_vars; + hold_enclosing_vars.swap(this->enclosing_vars_); + + Function_type* type = this->signature(NULL, location); + bool fntype_is_error = false; + if (type == NULL) + { + type = Type::make_function_type(NULL, NULL, NULL, location); + fntype_is_error = true; + } + + // For a function literal, the next token must be a '{'. If we + // don't see that, then we may have a type expression. + if (!this->peek_token()->is_op(OPERATOR_LCURLY)) + return Expression::make_type(type, location); + + bool hold_is_erroneous_function = this->is_erroneous_function_; + if (fntype_is_error) + this->is_erroneous_function_ = true; + + Bc_stack* hold_break_stack = this->break_stack_; + Bc_stack* hold_continue_stack = this->continue_stack_; + this->break_stack_ = NULL; + this->continue_stack_ = NULL; + + Named_object* no = this->gogo_->start_function("", type, true, location); + + Location end_loc = this->block(); + + this->gogo_->finish_function(end_loc); + + if (this->break_stack_ != NULL) + delete this->break_stack_; + if (this->continue_stack_ != NULL) + delete this->continue_stack_; + this->break_stack_ = hold_break_stack; + this->continue_stack_ = hold_continue_stack; + + this->is_erroneous_function_ = hold_is_erroneous_function; + + hold_enclosing_vars.swap(this->enclosing_vars_); + + Expression* closure = this->create_closure(no, &hold_enclosing_vars, + location); + + return Expression::make_func_reference(no, closure, location); +} + +// Create a closure for the nested function FUNCTION. This is based +// on ENCLOSING_VARS, which is a list of all variables defined in +// enclosing functions and referenced from FUNCTION. A closure is the +// address of a struct which point to the real function code and +// contains the addresses of all the referenced variables. This +// returns NULL if no closure is required. + +Expression* +Parse::create_closure(Named_object* function, Enclosing_vars* enclosing_vars, + Location location) +{ + if (enclosing_vars->empty()) + return NULL; + + // Get the variables in order by their field index. + + size_t enclosing_var_count = enclosing_vars->size(); + std::vector ev(enclosing_var_count); + for (Enclosing_vars::const_iterator p = enclosing_vars->begin(); + p != enclosing_vars->end(); + ++p) + { + // Subtract 1 because index 0 is the function code. + ev[p->index() - 1] = *p; + } + + // Build an initializer for a composite literal of the closure's + // type. + + Named_object* enclosing_function = this->gogo_->current_function(); + Expression_list* initializer = new Expression_list; + + initializer->push_back(Expression::make_func_code_reference(function, + location)); + + for (size_t i = 0; i < enclosing_var_count; ++i) + { + // Add 1 to i because the first field in the closure is a + // pointer to the function code. + go_assert(ev[i].index() == i + 1); + Named_object* var = ev[i].var(); + Expression* ref; + if (ev[i].in_function() == enclosing_function) + ref = Expression::make_var_reference(var, location); + else + ref = this->enclosing_var_reference(ev[i].in_function(), var, + location); + Expression* refaddr = Expression::make_unary(OPERATOR_AND, ref, + location); + initializer->push_back(refaddr); + } + + Named_object* closure_var = function->func_value()->closure_var(); + Struct_type* st = closure_var->var_value()->type()->deref()->struct_type(); + Expression* cv = Expression::make_struct_composite_literal(st, initializer, + location); + return Expression::make_heap_composite(cv, location); +} + +// PrimaryExpr = Operand { Selector | Index | Slice | TypeGuard | Call } . + +// If MAY_BE_SINK is true, this expression may be "_". + +// If MAY_BE_COMPOSITE_LIT is true, this expression may be a composite +// literal. + +// If IS_TYPE_SWITCH is not NULL, this will recognize a type switch +// guard (var := expr.("type") using the literal keyword "type"). + +// If IS_PARENTHESIZED is not NULL, *IS_PARENTHESIZED is set to true +// if the entire expression is in parentheses. + +Expression* +Parse::primary_expr(bool may_be_sink, bool may_be_composite_lit, + bool* is_type_switch, bool* is_parenthesized) +{ + Location start_loc = this->location(); + bool operand_is_parenthesized = false; + bool whole_is_parenthesized = false; + + Expression* ret = this->operand(may_be_sink, &operand_is_parenthesized); + + whole_is_parenthesized = operand_is_parenthesized; + + // An unknown name followed by a curly brace must be a composite + // literal, and the unknown name must be a type. + if (may_be_composite_lit + && !operand_is_parenthesized + && ret->unknown_expression() != NULL + && this->peek_token()->is_op(OPERATOR_LCURLY)) + { + Named_object* no = ret->unknown_expression()->named_object(); + Type* type = Type::make_forward_declaration(no); + ret = Expression::make_type(type, ret->location()); + } + + // We handle composite literals and type casts here, as it is the + // easiest way to handle types which are in parentheses, as in + // "((uint))(1)". + if (ret->is_type_expression()) + { + if (this->peek_token()->is_op(OPERATOR_LCURLY)) + { + whole_is_parenthesized = false; + if (!may_be_composite_lit) + { + Type* t = ret->type(); + if (t->named_type() != NULL + || t->forward_declaration_type() != NULL) + error_at(start_loc, + _("parentheses required around this composite literal " + "to avoid parsing ambiguity")); + } + else if (operand_is_parenthesized) + error_at(start_loc, + "cannot parenthesize type in composite literal"); + ret = this->composite_lit(ret->type(), 0, ret->location()); + } + else if (this->peek_token()->is_op(OPERATOR_LPAREN)) + { + whole_is_parenthesized = false; + Location loc = this->location(); + this->advance_token(); + Expression* expr = this->expression(PRECEDENCE_NORMAL, false, true, + NULL, NULL); + if (this->peek_token()->is_op(OPERATOR_COMMA)) + this->advance_token(); + if (this->peek_token()->is_op(OPERATOR_ELLIPSIS)) + { + error_at(this->location(), + "invalid use of %<...%> in type conversion"); + this->advance_token(); + } + if (!this->peek_token()->is_op(OPERATOR_RPAREN)) + error_at(this->location(), "expected %<)%>"); + else + this->advance_token(); + if (expr->is_error_expression()) + ret = expr; + else + { + Type* t = ret->type(); + if (t->classification() == Type::TYPE_ARRAY + && t->array_type()->length() != NULL + && t->array_type()->length()->is_nil_expression()) + { + error_at(ret->location(), + "use of %<[...]%> outside of array literal"); + ret = Expression::make_error(loc); + } + else + ret = Expression::make_cast(t, expr, loc); + } + } + } + + while (true) + { + const Token* token = this->peek_token(); + if (token->is_op(OPERATOR_LPAREN)) + { + whole_is_parenthesized = false; + ret = this->call(this->verify_not_sink(ret)); + } + else if (token->is_op(OPERATOR_DOT)) + { + whole_is_parenthesized = false; + ret = this->selector(this->verify_not_sink(ret), is_type_switch); + if (is_type_switch != NULL && *is_type_switch) + break; + } + else if (token->is_op(OPERATOR_LSQUARE)) + { + whole_is_parenthesized = false; + ret = this->index(this->verify_not_sink(ret)); + } + else + break; + } + + if (whole_is_parenthesized && is_parenthesized != NULL) + *is_parenthesized = true; + + return ret; +} + +// Selector = "." identifier . +// TypeGuard = "." "(" QualifiedIdent ")" . + +// Note that Operand can expand to QualifiedIdent, which contains a +// ".". That is handled directly in operand when it sees a package +// name. + +// If IS_TYPE_SWITCH is not NULL, this will recognize a type switch +// guard (var := expr.("type") using the literal keyword "type"). + +Expression* +Parse::selector(Expression* left, bool* is_type_switch) +{ + go_assert(this->peek_token()->is_op(OPERATOR_DOT)); + Location location = this->location(); + + const Token* token = this->advance_token(); + if (token->is_identifier()) + { + // This could be a field in a struct, or a method in an + // interface, or a method associated with a type. We can't know + // which until we have seen all the types. + std::string name = + this->gogo_->pack_hidden_name(token->identifier(), + token->is_identifier_exported()); + if (token->identifier() == "_") + { + error_at(this->location(), "invalid use of %<_%>"); + name = Gogo::erroneous_name(); + } + this->advance_token(); + return Expression::make_selector(left, name, location); + } + else if (token->is_op(OPERATOR_LPAREN)) + { + this->advance_token(); + Type* type = NULL; + if (!this->peek_token()->is_keyword(KEYWORD_TYPE)) + type = this->type(); + else + { + if (is_type_switch != NULL) + *is_type_switch = true; + else + { + error_at(this->location(), + "use of %<.(type)%> outside type switch"); + type = Type::make_error_type(); + } + this->advance_token(); + } + if (!this->peek_token()->is_op(OPERATOR_RPAREN)) + error_at(this->location(), "missing %<)%>"); + else + this->advance_token(); + if (is_type_switch != NULL && *is_type_switch) + return left; + return Expression::make_type_guard(left, type, location); + } + else + { + error_at(this->location(), "expected identifier or %<(%>"); + return left; + } +} + +// Index = "[" Expression "]" . +// Slice = "[" Expression ":" [ Expression ] [ ":" Expression ] "]" . + +Expression* +Parse::index(Expression* expr) +{ + Location location = this->location(); + go_assert(this->peek_token()->is_op(OPERATOR_LSQUARE)); + this->advance_token(); + + Expression* start; + if (!this->peek_token()->is_op(OPERATOR_COLON)) + start = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + else + { + mpz_t zero; + mpz_init_set_ui(zero, 0); + start = Expression::make_integer(&zero, NULL, location); + mpz_clear(zero); + } + + Expression* end = NULL; + if (this->peek_token()->is_op(OPERATOR_COLON)) + { + // We use nil to indicate a missing high expression. + if (this->advance_token()->is_op(OPERATOR_RSQUARE)) + end = Expression::make_nil(this->location()); + else if (this->peek_token()->is_op(OPERATOR_COLON)) + { + error_at(this->location(), "middle index required in 3-index slice"); + end = Expression::make_error(this->location()); + } + else + end = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + } + + Expression* cap = NULL; + if (this->peek_token()->is_op(OPERATOR_COLON)) + { + if (this->advance_token()->is_op(OPERATOR_RSQUARE)) + { + error_at(this->location(), "final index required in 3-index slice"); + cap = Expression::make_error(this->location()); + } + else + cap = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + } + if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) + error_at(this->location(), "missing %<]%>"); + else + this->advance_token(); + return Expression::make_index(expr, start, end, cap, location); +} + +// Call = "(" [ ArgumentList [ "," ] ] ")" . +// ArgumentList = ExpressionList [ "..." ] . + +Expression* +Parse::call(Expression* func) +{ + go_assert(this->peek_token()->is_op(OPERATOR_LPAREN)); + Expression_list* args = NULL; + bool is_varargs = false; + const Token* token = this->advance_token(); + if (!token->is_op(OPERATOR_RPAREN)) + { + args = this->expression_list(NULL, false, true); + token = this->peek_token(); + if (token->is_op(OPERATOR_ELLIPSIS)) + { + is_varargs = true; + token = this->advance_token(); + } + } + if (token->is_op(OPERATOR_COMMA)) + token = this->advance_token(); + if (!token->is_op(OPERATOR_RPAREN)) + error_at(this->location(), "missing %<)%>"); + else + this->advance_token(); + if (func->is_error_expression()) + return func; + return Expression::make_call(func, args, is_varargs, func->location()); +} + +// Return an expression for a single unqualified identifier. + +Expression* +Parse::id_to_expression(const std::string& name, Location location) +{ + Named_object* in_function; + Named_object* named_object = this->gogo_->lookup(name, &in_function); + if (named_object == NULL) + named_object = this->gogo_->add_unknown_name(name, location); + + if (in_function != NULL + && in_function != this->gogo_->current_function() + && (named_object->is_variable() || named_object->is_result_variable())) + return this->enclosing_var_reference(in_function, named_object, + location); + + switch (named_object->classification()) + { + case Named_object::NAMED_OBJECT_CONST: + return Expression::make_const_reference(named_object, location); + case Named_object::NAMED_OBJECT_VAR: + case Named_object::NAMED_OBJECT_RESULT_VAR: + this->mark_var_used(named_object); + return Expression::make_var_reference(named_object, location); + case Named_object::NAMED_OBJECT_SINK: + return Expression::make_sink(location); + case Named_object::NAMED_OBJECT_FUNC: + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + return Expression::make_func_reference(named_object, NULL, location); + case Named_object::NAMED_OBJECT_UNKNOWN: + { + Unknown_expression* ue = + Expression::make_unknown_reference(named_object, location); + if (this->is_erroneous_function_) + ue->set_no_error_message(); + return ue; + } + case Named_object::NAMED_OBJECT_PACKAGE: + case Named_object::NAMED_OBJECT_TYPE: + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + { + // These cases can arise for a field name in a composite + // literal. + Unknown_expression* ue = + Expression::make_unknown_reference(named_object, location); + if (this->is_erroneous_function_) + ue->set_no_error_message(); + return ue; + } + case Named_object::NAMED_OBJECT_ERRONEOUS: + return Expression::make_error(location); + default: + error_at(this->location(), "unexpected type of identifier"); + return Expression::make_error(location); + } +} + +// Expression = UnaryExpr { binary_op Expression } . + +// PRECEDENCE is the precedence of the current operator. + +// If MAY_BE_SINK is true, this expression may be "_". + +// If MAY_BE_COMPOSITE_LIT is true, this expression may be a composite +// literal. + +// If IS_TYPE_SWITCH is not NULL, this will recognize a type switch +// guard (var := expr.("type") using the literal keyword "type"). + +// If IS_PARENTHESIZED is not NULL, *IS_PARENTHESIZED is set to true +// if the entire expression is in parentheses. + +Expression* +Parse::expression(Precedence precedence, bool may_be_sink, + bool may_be_composite_lit, bool* is_type_switch, + bool *is_parenthesized) +{ + Expression* left = this->unary_expr(may_be_sink, may_be_composite_lit, + is_type_switch, is_parenthesized); + + while (true) + { + if (is_type_switch != NULL && *is_type_switch) + return left; + + const Token* token = this->peek_token(); + if (token->classification() != Token::TOKEN_OPERATOR) + { + // Not a binary_op. + return left; + } + + Precedence right_precedence; + switch (token->op()) + { + case OPERATOR_OROR: + right_precedence = PRECEDENCE_OROR; + break; + case OPERATOR_ANDAND: + right_precedence = PRECEDENCE_ANDAND; + break; + case OPERATOR_EQEQ: + case OPERATOR_NOTEQ: + case OPERATOR_LT: + case OPERATOR_LE: + case OPERATOR_GT: + case OPERATOR_GE: + right_precedence = PRECEDENCE_RELOP; + break; + case OPERATOR_PLUS: + case OPERATOR_MINUS: + case OPERATOR_OR: + case OPERATOR_XOR: + right_precedence = PRECEDENCE_ADDOP; + break; + case OPERATOR_MULT: + case OPERATOR_DIV: + case OPERATOR_MOD: + case OPERATOR_LSHIFT: + case OPERATOR_RSHIFT: + case OPERATOR_AND: + case OPERATOR_BITCLEAR: + right_precedence = PRECEDENCE_MULOP; + break; + default: + right_precedence = PRECEDENCE_INVALID; + break; + } + + if (right_precedence == PRECEDENCE_INVALID) + { + // Not a binary_op. + return left; + } + + if (is_parenthesized != NULL) + *is_parenthesized = false; + + Operator op = token->op(); + Location binop_location = token->location(); + + if (precedence >= right_precedence) + { + // We've already seen A * B, and we see + C. We want to + // return so that A * B becomes a group. + return left; + } + + this->advance_token(); + + left = this->verify_not_sink(left); + Expression* right = this->expression(right_precedence, false, + may_be_composite_lit, + NULL, NULL); + left = Expression::make_binary(op, left, right, binop_location); + } +} + +bool +Parse::expression_may_start_here() +{ + const Token* token = this->peek_token(); + switch (token->classification()) + { + case Token::TOKEN_INVALID: + case Token::TOKEN_EOF: + return false; + case Token::TOKEN_KEYWORD: + switch (token->keyword()) + { + case KEYWORD_CHAN: + case KEYWORD_FUNC: + case KEYWORD_MAP: + case KEYWORD_STRUCT: + case KEYWORD_INTERFACE: + return true; + default: + return false; + } + case Token::TOKEN_IDENTIFIER: + return true; + case Token::TOKEN_STRING: + return true; + case Token::TOKEN_OPERATOR: + switch (token->op()) + { + case OPERATOR_PLUS: + case OPERATOR_MINUS: + case OPERATOR_NOT: + case OPERATOR_XOR: + case OPERATOR_MULT: + case OPERATOR_CHANOP: + case OPERATOR_AND: + case OPERATOR_LPAREN: + case OPERATOR_LSQUARE: + return true; + default: + return false; + } + case Token::TOKEN_CHARACTER: + case Token::TOKEN_INTEGER: + case Token::TOKEN_FLOAT: + case Token::TOKEN_IMAGINARY: + return true; + default: + go_unreachable(); + } +} + +// UnaryExpr = unary_op UnaryExpr | PrimaryExpr . + +// If MAY_BE_SINK is true, this expression may be "_". + +// If MAY_BE_COMPOSITE_LIT is true, this expression may be a composite +// literal. + +// If IS_TYPE_SWITCH is not NULL, this will recognize a type switch +// guard (var := expr.("type") using the literal keyword "type"). + +// If IS_PARENTHESIZED is not NULL, *IS_PARENTHESIZED is set to true +// if the entire expression is in parentheses. + +Expression* +Parse::unary_expr(bool may_be_sink, bool may_be_composite_lit, + bool* is_type_switch, bool* is_parenthesized) +{ + 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, 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) + || token->is_op(OPERATOR_XOR) + || token->is_op(OPERATOR_CHANOP) + || token->is_op(OPERATOR_MULT) + || token->is_op(OPERATOR_AND)) + { + Location location = token->location(); + Operator op = token->op(); + this->advance_token(); + + Expression* expr = this->unary_expr(false, may_be_composite_lit, NULL, + NULL); + if (expr->is_error_expression()) + ; + else if (op == OPERATOR_MULT && expr->is_type_expression()) + expr = Expression::make_type(Type::make_pointer_type(expr->type()), + location); + else if (op == OPERATOR_AND && expr->is_composite_literal()) + expr = Expression::make_heap_composite(expr, location); + else if (op != OPERATOR_CHANOP) + expr = Expression::make_unary(op, expr, location); + else + expr = Expression::make_receive(expr, location); + return expr; + } + else + return this->primary_expr(may_be_sink, may_be_composite_lit, + is_type_switch, is_parenthesized); +} + +// 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 | +// FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | +// DeferStmt . + +// LABEL is the label of this statement if it has one. + +void +Parse::statement(Label* label) +{ + const Token* token = this->peek_token(); + switch (token->classification()) + { + case Token::TOKEN_KEYWORD: + { + switch (token->keyword()) + { + case KEYWORD_CONST: + case KEYWORD_TYPE: + case KEYWORD_VAR: + this->declaration(); + break; + case KEYWORD_FUNC: + case KEYWORD_MAP: + case KEYWORD_STRUCT: + case KEYWORD_INTERFACE: + this->simple_stat(true, NULL, NULL, NULL); + break; + case KEYWORD_GO: + case KEYWORD_DEFER: + this->go_or_defer_stat(); + break; + case KEYWORD_RETURN: + this->return_stat(); + break; + case KEYWORD_BREAK: + this->break_stat(); + break; + case KEYWORD_CONTINUE: + this->continue_stat(); + break; + case KEYWORD_GOTO: + this->goto_stat(); + break; + case KEYWORD_IF: + this->if_stat(); + break; + case KEYWORD_SWITCH: + this->switch_stat(label); + break; + case KEYWORD_SELECT: + this->select_stat(label); + break; + case KEYWORD_FOR: + this->for_stat(label); + break; + default: + error_at(this->location(), "expected statement"); + this->advance_token(); + break; + } + } + break; + + case Token::TOKEN_IDENTIFIER: + { + std::string identifier = token->identifier(); + bool is_exported = token->is_identifier_exported(); + Location location = token->location(); + if (this->advance_token()->is_op(OPERATOR_COLON)) + { + this->advance_token(); + this->labeled_stmt(identifier, location); + } + else + { + this->unget_token(Token::make_identifier_token(identifier, + is_exported, + location)); + this->simple_stat(true, NULL, NULL, NULL); + } + } + break; + + case Token::TOKEN_OPERATOR: + if (token->is_op(OPERATOR_LCURLY)) + { + Location location = token->location(); + this->gogo_->start_block(location); + Location end_loc = this->block(); + this->gogo_->add_block(this->gogo_->finish_block(end_loc), + location); + } + else if (!token->is_op(OPERATOR_SEMICOLON)) + this->simple_stat(true, NULL, NULL, NULL); + break; + + case Token::TOKEN_STRING: + case Token::TOKEN_CHARACTER: + case Token::TOKEN_INTEGER: + case Token::TOKEN_FLOAT: + case Token::TOKEN_IMAGINARY: + this->simple_stat(true, NULL, NULL, NULL); + break; + + default: + error_at(this->location(), "expected statement"); + this->advance_token(); + break; + } +} + +bool +Parse::statement_may_start_here() +{ + const Token* token = this->peek_token(); + switch (token->classification()) + { + case Token::TOKEN_KEYWORD: + { + switch (token->keyword()) + { + case KEYWORD_CONST: + case KEYWORD_TYPE: + case KEYWORD_VAR: + case KEYWORD_FUNC: + case KEYWORD_MAP: + case KEYWORD_STRUCT: + case KEYWORD_INTERFACE: + case KEYWORD_GO: + case KEYWORD_DEFER: + case KEYWORD_RETURN: + case KEYWORD_BREAK: + case KEYWORD_CONTINUE: + case KEYWORD_GOTO: + case KEYWORD_IF: + case KEYWORD_SWITCH: + case KEYWORD_SELECT: + case KEYWORD_FOR: + return true; + + default: + return false; + } + } + break; + + case Token::TOKEN_IDENTIFIER: + return true; + + case Token::TOKEN_OPERATOR: + if (token->is_op(OPERATOR_LCURLY) + || token->is_op(OPERATOR_SEMICOLON)) + return true; + else + return this->expression_may_start_here(); + + case Token::TOKEN_STRING: + case Token::TOKEN_CHARACTER: + case Token::TOKEN_INTEGER: + case Token::TOKEN_FLOAT: + case Token::TOKEN_IMAGINARY: + return true; + + default: + return false; + } +} + +// LabeledStmt = Label ":" Statement . +// Label = identifier . + +void +Parse::labeled_stmt(const std::string& label_name, Location location) +{ + Label* label = this->gogo_->add_label_definition(label_name, location); + + if (this->peek_token()->is_op(OPERATOR_RCURLY)) + { + // This is a label at the end of a block. A program is + // permitted to omit a semicolon here. + return; + } + + if (!this->statement_may_start_here()) + { + // Mark the label as used to avoid a useless error about an + // unused label. + label->set_is_used(); + + error_at(location, "missing statement after label"); + this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, + location)); + return; + } + + this->statement(label); +} + +// SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | +// Assignment | ShortVarDecl . + +// EmptyStmt was handled in Parse::statement. + +// In order to make this work for if and switch statements, if +// RETURN_EXP is not NULL, and we see an ExpressionStat, we return the +// expression rather than adding an expression statement to the +// current block. If we see something other than an ExpressionStat, +// we add the statement, set *RETURN_EXP to true if we saw a send +// statement, and return NULL. The handling of send statements is for +// better error messages. + +// If P_RANGE_CLAUSE is not NULL, then this will recognize a +// RangeClause. + +// If P_TYPE_SWITCH is not NULL, this will recognize a type switch +// guard (var := expr.("type") using the literal keyword "type"). + +Expression* +Parse::simple_stat(bool may_be_composite_lit, bool* return_exp, + Range_clause* p_range_clause, Type_switch* p_type_switch) +{ + const Token* token = this->peek_token(); + + // An identifier follow by := is a SimpleVarDecl. + if (token->is_identifier()) + { + std::string identifier = token->identifier(); + bool is_exported = token->is_identifier_exported(); + Location location = token->location(); + + token = this->advance_token(); + if (token->is_op(OPERATOR_COLONEQ) + || token->is_op(OPERATOR_COMMA)) + { + identifier = this->gogo_->pack_hidden_name(identifier, is_exported); + this->simple_var_decl_or_assignment(identifier, location, + may_be_composite_lit, + p_range_clause, + (token->is_op(OPERATOR_COLONEQ) + ? p_type_switch + : NULL)); + return NULL; + } + + this->unget_token(Token::make_identifier_token(identifier, is_exported, + location)); + } + + Expression* exp = this->expression(PRECEDENCE_NORMAL, true, + may_be_composite_lit, + (p_type_switch == NULL + ? NULL + : &p_type_switch->found), + NULL); + if (p_type_switch != NULL && p_type_switch->found) + { + p_type_switch->name.clear(); + p_type_switch->location = exp->location(); + p_type_switch->expr = this->verify_not_sink(exp); + return NULL; + } + token = this->peek_token(); + if (token->is_op(OPERATOR_CHANOP)) + { + this->send_stmt(this->verify_not_sink(exp)); + if (return_exp != NULL) + *return_exp = true; + } + else if (token->is_op(OPERATOR_PLUSPLUS) + || token->is_op(OPERATOR_MINUSMINUS)) + this->inc_dec_stat(this->verify_not_sink(exp)); + else if (token->is_op(OPERATOR_COMMA) + || token->is_op(OPERATOR_EQ)) + this->assignment(exp, may_be_composite_lit, p_range_clause); + else if (token->is_op(OPERATOR_PLUSEQ) + || token->is_op(OPERATOR_MINUSEQ) + || token->is_op(OPERATOR_OREQ) + || token->is_op(OPERATOR_XOREQ) + || token->is_op(OPERATOR_MULTEQ) + || token->is_op(OPERATOR_DIVEQ) + || token->is_op(OPERATOR_MODEQ) + || token->is_op(OPERATOR_LSHIFTEQ) + || token->is_op(OPERATOR_RSHIFTEQ) + || token->is_op(OPERATOR_ANDEQ) + || token->is_op(OPERATOR_BITCLEAREQ)) + this->assignment(this->verify_not_sink(exp), may_be_composite_lit, + p_range_clause); + else if (return_exp != NULL) + return this->verify_not_sink(exp); + else + { + exp = this->verify_not_sink(exp); + + if (token->is_op(OPERATOR_COLONEQ)) + { + if (!exp->is_error_expression()) + error_at(token->location(), "non-name on left side of %<:=%>"); + this->gogo_->mark_locals_used(); + while (!token->is_op(OPERATOR_SEMICOLON) + && !token->is_eof()) + token = this->advance_token(); + return NULL; + } + + this->expression_stat(exp); + } + + return NULL; +} + +bool +Parse::simple_stat_may_start_here() +{ + return this->expression_may_start_here(); +} + +// Parse { Statement ";" } which is used in a few places. The list of +// statements may end with a right curly brace, in which case the +// semicolon may be omitted. + +void +Parse::statement_list() +{ + while (this->statement_may_start_here()) + { + this->statement(NULL); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + else if (this->peek_token()->is_op(OPERATOR_RCURLY)) + break; + else + { + if (!this->peek_token()->is_eof() || !saw_errors()) + error_at(this->location(), "expected %<;%> or %<}%> or newline"); + if (!this->skip_past_error(OPERATOR_RCURLY)) + return; + } + } +} + +bool +Parse::statement_list_may_start_here() +{ + return this->statement_may_start_here(); +} + +// ExpressionStat = Expression . + +void +Parse::expression_stat(Expression* exp) +{ + this->gogo_->add_statement(Statement::make_statement(exp, false)); +} + +// SendStmt = Channel "<-" Expression . +// Channel = Expression . + +void +Parse::send_stmt(Expression* channel) +{ + go_assert(this->peek_token()->is_op(OPERATOR_CHANOP)); + Location loc = this->location(); + this->advance_token(); + Expression* val = this->expression(PRECEDENCE_NORMAL, false, true, NULL, + NULL); + Statement* s = Statement::make_send_statement(channel, val, loc); + this->gogo_->add_statement(s); +} + +// IncDecStat = Expression ( "++" | "--" ) . + +void +Parse::inc_dec_stat(Expression* exp) +{ + const Token* token = this->peek_token(); + + // Lvalue maps require special handling. + if (exp->index_expression() != NULL) + exp->index_expression()->set_is_lvalue(); + + if (token->is_op(OPERATOR_PLUSPLUS)) + this->gogo_->add_statement(Statement::make_inc_statement(exp)); + else if (token->is_op(OPERATOR_MINUSMINUS)) + this->gogo_->add_statement(Statement::make_dec_statement(exp)); + else + go_unreachable(); + this->advance_token(); +} + +// Assignment = ExpressionList assign_op ExpressionList . + +// EXP is an expression that we have already parsed. + +// If MAY_BE_COMPOSITE_LIT is true, an expression on the right hand +// side may be a composite literal. + +// If RANGE_CLAUSE is not NULL, then this will recognize a +// RangeClause. + +void +Parse::assignment(Expression* expr, bool may_be_composite_lit, + Range_clause* p_range_clause) +{ + Expression_list* vars; + if (!this->peek_token()->is_op(OPERATOR_COMMA)) + { + vars = new Expression_list(); + vars->push_back(expr); + } + else + { + this->advance_token(); + vars = this->expression_list(expr, true, may_be_composite_lit); + } + + this->tuple_assignment(vars, may_be_composite_lit, p_range_clause); +} + +// An assignment statement. LHS is the list of expressions which +// appear on the left hand side. + +// If MAY_BE_COMPOSITE_LIT is true, an expression on the right hand +// side may be a composite literal. + +// If RANGE_CLAUSE is not NULL, then this will recognize a +// RangeClause. + +void +Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, + Range_clause* p_range_clause) +{ + const Token* token = this->peek_token(); + if (!token->is_op(OPERATOR_EQ) + && !token->is_op(OPERATOR_PLUSEQ) + && !token->is_op(OPERATOR_MINUSEQ) + && !token->is_op(OPERATOR_OREQ) + && !token->is_op(OPERATOR_XOREQ) + && !token->is_op(OPERATOR_MULTEQ) + && !token->is_op(OPERATOR_DIVEQ) + && !token->is_op(OPERATOR_MODEQ) + && !token->is_op(OPERATOR_LSHIFTEQ) + && !token->is_op(OPERATOR_RSHIFTEQ) + && !token->is_op(OPERATOR_ANDEQ) + && !token->is_op(OPERATOR_BITCLEAREQ)) + { + error_at(this->location(), "expected assignment operator"); + return; + } + Operator op = token->op(); + Location location = token->location(); + + token = this->advance_token(); + + if (p_range_clause != NULL && token->is_keyword(KEYWORD_RANGE)) + { + if (op != OPERATOR_EQ) + error_at(this->location(), "range clause requires %<=%>"); + this->range_clause_expr(lhs, p_range_clause); + return; + } + + Expression_list* vals = this->expression_list(NULL, false, + may_be_composite_lit); + + // We've parsed everything; check for errors. + if (lhs == NULL || vals == NULL) + return; + for (Expression_list::const_iterator pe = lhs->begin(); + pe != lhs->end(); + ++pe) + { + if ((*pe)->is_error_expression()) + return; + if (op != OPERATOR_EQ && (*pe)->is_sink_expression()) + error_at((*pe)->location(), "cannot use _ as value"); + } + for (Expression_list::const_iterator pe = vals->begin(); + pe != vals->end(); + ++pe) + { + if ((*pe)->is_error_expression()) + return; + } + + // Map expressions act differently when they are lvalues. + for (Expression_list::iterator plv = lhs->begin(); + plv != lhs->end(); + ++plv) + if ((*plv)->index_expression() != NULL) + (*plv)->index_expression()->set_is_lvalue(); + + Call_expression* call; + Index_expression* map_index; + Receive_expression* receive; + Type_guard_expression* type_guard; + if (lhs->size() == vals->size()) + { + Statement* s; + if (lhs->size() > 1) + { + if (op != OPERATOR_EQ) + error_at(location, "multiple values only permitted with %<=%>"); + s = Statement::make_tuple_assignment(lhs, vals, location); + } + else + { + if (op == OPERATOR_EQ) + s = Statement::make_assignment(lhs->front(), vals->front(), + location); + else + s = Statement::make_assignment_operation(op, lhs->front(), + vals->front(), location); + delete lhs; + delete vals; + } + this->gogo_->add_statement(s); + } + else if (vals->size() == 1 + && (call = (*vals->begin())->call_expression()) != NULL) + { + if (op != OPERATOR_EQ) + error_at(location, "multiple results only permitted with %<=%>"); + delete vals; + vals = new Expression_list; + for (unsigned int i = 0; i < lhs->size(); ++i) + vals->push_back(Expression::make_call_result(call, i)); + Statement* s = Statement::make_tuple_assignment(lhs, vals, location); + this->gogo_->add_statement(s); + } + else if (lhs->size() == 2 + && vals->size() == 1 + && (map_index = (*vals->begin())->index_expression()) != NULL) + { + if (op != OPERATOR_EQ) + error_at(location, "two values from map requires %<=%>"); + Expression* val = lhs->front(); + Expression* present = lhs->back(); + Statement* s = Statement::make_tuple_map_assignment(val, present, + map_index, location); + this->gogo_->add_statement(s); + } + else if (lhs->size() == 1 + && vals->size() == 2 + && (map_index = lhs->front()->index_expression()) != NULL) + { + if (op != OPERATOR_EQ) + error_at(location, "assigning tuple to map index requires %<=%>"); + Expression* val = vals->front(); + Expression* should_set = vals->back(); + Statement* s = Statement::make_map_assignment(map_index, val, should_set, + location); + this->gogo_->add_statement(s); + } + else if (lhs->size() == 2 + && vals->size() == 1 + && (receive = (*vals->begin())->receive_expression()) != NULL) + { + if (op != OPERATOR_EQ) + error_at(location, "two values from receive requires %<=%>"); + Expression* val = lhs->front(); + Expression* success = lhs->back(); + Expression* channel = receive->channel(); + Statement* s = Statement::make_tuple_receive_assignment(val, success, + channel, + location); + this->gogo_->add_statement(s); + } + else if (lhs->size() == 2 + && vals->size() == 1 + && (type_guard = (*vals->begin())->type_guard_expression()) != NULL) + { + if (op != OPERATOR_EQ) + error_at(location, "two values from type guard requires %<=%>"); + Expression* val = lhs->front(); + Expression* ok = lhs->back(); + Expression* expr = type_guard->expr(); + Type* type = type_guard->type(); + Statement* s = Statement::make_tuple_type_guard_assignment(val, ok, + expr, type, + location); + this->gogo_->add_statement(s); + } + else + { + error_at(location, "number of variables does not match number of values"); + } +} + +// GoStat = "go" Expression . +// DeferStat = "defer" Expression . + +void +Parse::go_or_defer_stat() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_GO) + || this->peek_token()->is_keyword(KEYWORD_DEFER)); + bool is_go = this->peek_token()->is_keyword(KEYWORD_GO); + Location stat_location = this->location(); + + this->advance_token(); + Location expr_location = this->location(); + + bool is_parenthesized = false; + Expression* expr = this->expression(PRECEDENCE_NORMAL, false, true, NULL, + &is_parenthesized); + Call_expression* call_expr = expr->call_expression(); + if (is_parenthesized || call_expr == NULL) + { + error_at(expr_location, "argument to go/defer must be function call"); + return; + } + + // Make it easier to simplify go/defer statements by putting every + // statement in its own block. + this->gogo_->start_block(stat_location); + Statement* stat; + if (is_go) + stat = Statement::make_go_statement(call_expr, stat_location); + else + stat = Statement::make_defer_statement(call_expr, stat_location); + this->gogo_->add_statement(stat); + this->gogo_->add_block(this->gogo_->finish_block(stat_location), + stat_location); +} + +// ReturnStat = "return" [ ExpressionList ] . + +void +Parse::return_stat() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_RETURN)); + Location location = this->location(); + this->advance_token(); + Expression_list* vals = NULL; + if (this->expression_may_start_here()) + vals = this->expression_list(NULL, false, true); + this->gogo_->add_statement(Statement::make_return_statement(vals, location)); + + if (vals == NULL + && this->gogo_->current_function()->func_value()->results_are_named()) + { + Named_object* function = this->gogo_->current_function(); + Function::Results* results = function->func_value()->result_variables(); + for (Function::Results::const_iterator p = results->begin(); + p != results->end(); + ++p) + { + Named_object* no = this->gogo_->lookup((*p)->name(), NULL); + if (no == NULL) + go_assert(saw_errors()); + else if (!no->is_result_variable()) + error_at(location, "%qs is shadowed during return", + (*p)->message_name().c_str()); + } + } +} + +// IfStmt = "if" [ SimpleStmt ";" ] Expression Block +// [ "else" ( IfStmt | Block ) ] . + +void +Parse::if_stat() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_IF)); + Location location = this->location(); + this->advance_token(); + + this->gogo_->start_block(location); + + bool saw_simple_stat = false; + Expression* cond = NULL; + bool saw_send_stmt = false; + if (this->simple_stat_may_start_here()) + { + cond = this->simple_stat(false, &saw_send_stmt, NULL, NULL); + saw_simple_stat = true; + } + if (cond != NULL && this->peek_token()->is_op(OPERATOR_SEMICOLON)) + { + // The SimpleStat is an expression statement. + this->expression_stat(cond); + cond = NULL; + } + if (cond == NULL) + { + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + else if (saw_simple_stat) + { + if (saw_send_stmt) + error_at(this->location(), + ("send statement used as value; " + "use select for non-blocking send")); + else + error_at(this->location(), + "expected %<;%> after statement in if expression"); + if (!this->expression_may_start_here()) + cond = Expression::make_error(this->location()); + } + if (cond == NULL && this->peek_token()->is_op(OPERATOR_LCURLY)) + { + error_at(this->location(), + "missing condition in if statement"); + cond = Expression::make_error(this->location()); + } + if (cond == NULL) + cond = this->expression(PRECEDENCE_NORMAL, false, false, NULL, NULL); + } + + // Check for the easy error of a newline before starting the block. + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + { + Location semi_loc = this->location(); + if (this->advance_token()->is_op(OPERATOR_LCURLY)) + error_at(semi_loc, "missing %<{%> after if clause"); + // Otherwise we will get an error when we call this->block + // below. + } + + this->gogo_->start_block(this->location()); + Location end_loc = this->block(); + Block* then_block = this->gogo_->finish_block(end_loc); + + // Check for the easy error of a newline before "else". + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + { + Location semi_loc = this->location(); + if (this->advance_token()->is_keyword(KEYWORD_ELSE)) + error_at(this->location(), + "unexpected semicolon or newline before %"); + else + this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, + semi_loc)); + } + + Block* else_block = NULL; + if (this->peek_token()->is_keyword(KEYWORD_ELSE)) + { + this->gogo_->start_block(this->location()); + const Token* token = this->advance_token(); + if (token->is_keyword(KEYWORD_IF)) + this->if_stat(); + else if (token->is_op(OPERATOR_LCURLY)) + this->block(); + else + { + error_at(this->location(), "expected % or %<{%>"); + this->statement(NULL); + } + else_block = this->gogo_->finish_block(this->location()); + } + + this->gogo_->add_statement(Statement::make_if_statement(cond, then_block, + else_block, + location)); + + this->gogo_->add_block(this->gogo_->finish_block(this->location()), + location); +} + +// SwitchStmt = ExprSwitchStmt | TypeSwitchStmt . +// ExprSwitchStmt = "switch" [ [ SimpleStat ] ";" ] [ Expression ] +// "{" { ExprCaseClause } "}" . +// TypeSwitchStmt = "switch" [ [ SimpleStat ] ";" ] TypeSwitchGuard +// "{" { TypeCaseClause } "}" . +// TypeSwitchGuard = [ identifier ":=" ] Expression "." "(" "type" ")" . + +void +Parse::switch_stat(Label* label) +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_SWITCH)); + Location location = this->location(); + this->advance_token(); + + this->gogo_->start_block(location); + + bool saw_simple_stat = false; + Expression* switch_val = NULL; + bool saw_send_stmt; + Type_switch type_switch; + bool have_type_switch_block = false; + if (this->simple_stat_may_start_here()) + { + switch_val = this->simple_stat(false, &saw_send_stmt, NULL, + &type_switch); + saw_simple_stat = true; + } + if (switch_val != NULL && this->peek_token()->is_op(OPERATOR_SEMICOLON)) + { + // The SimpleStat is an expression statement. + this->expression_stat(switch_val); + switch_val = NULL; + } + if (switch_val == NULL && !type_switch.found) + { + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + else if (saw_simple_stat) + { + if (saw_send_stmt) + error_at(this->location(), + ("send statement used as value; " + "use select for non-blocking send")); + else + error_at(this->location(), + "expected %<;%> after statement in switch expression"); + } + if (!this->peek_token()->is_op(OPERATOR_LCURLY)) + { + if (this->peek_token()->is_identifier()) + { + const Token* token = this->peek_token(); + std::string identifier = token->identifier(); + bool is_exported = token->is_identifier_exported(); + Location id_loc = token->location(); + + token = this->advance_token(); + bool is_coloneq = token->is_op(OPERATOR_COLONEQ); + this->unget_token(Token::make_identifier_token(identifier, + is_exported, + id_loc)); + if (is_coloneq) + { + // This must be a TypeSwitchGuard. It is in a + // different block from any initial SimpleStat. + if (saw_simple_stat) + { + this->gogo_->start_block(id_loc); + have_type_switch_block = true; + } + + switch_val = this->simple_stat(false, &saw_send_stmt, NULL, + &type_switch); + if (!type_switch.found) + { + if (switch_val == NULL + || !switch_val->is_error_expression()) + { + error_at(id_loc, "expected type switch assignment"); + switch_val = Expression::make_error(id_loc); + } + } + } + } + if (switch_val == NULL && !type_switch.found) + { + switch_val = this->expression(PRECEDENCE_NORMAL, false, false, + &type_switch.found, NULL); + if (type_switch.found) + { + type_switch.name.clear(); + type_switch.expr = switch_val; + type_switch.location = switch_val->location(); + } + } + } + } + + if (!this->peek_token()->is_op(OPERATOR_LCURLY)) + { + Location token_loc = this->location(); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON) + && this->advance_token()->is_op(OPERATOR_LCURLY)) + error_at(token_loc, "missing %<{%> after switch clause"); + else if (this->peek_token()->is_op(OPERATOR_COLONEQ)) + { + error_at(token_loc, "invalid variable name"); + this->advance_token(); + this->expression(PRECEDENCE_NORMAL, false, false, + &type_switch.found, NULL); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + if (!this->peek_token()->is_op(OPERATOR_LCURLY)) + { + if (have_type_switch_block) + this->gogo_->add_block(this->gogo_->finish_block(location), + location); + this->gogo_->add_block(this->gogo_->finish_block(location), + location); + return; + } + if (type_switch.found) + type_switch.expr = Expression::make_error(location); + } + else + { + error_at(this->location(), "expected %<{%>"); + if (have_type_switch_block) + this->gogo_->add_block(this->gogo_->finish_block(this->location()), + location); + this->gogo_->add_block(this->gogo_->finish_block(this->location()), + location); + return; + } + } + this->advance_token(); + + Statement* statement; + if (type_switch.found) + statement = this->type_switch_body(label, type_switch, location); + else + statement = this->expr_switch_body(label, switch_val, location); + + if (statement != NULL) + this->gogo_->add_statement(statement); + + if (have_type_switch_block) + this->gogo_->add_block(this->gogo_->finish_block(this->location()), + location); + + this->gogo_->add_block(this->gogo_->finish_block(this->location()), + location); +} + +// The body of an expression switch. +// "{" { ExprCaseClause } "}" + +Statement* +Parse::expr_switch_body(Label* label, Expression* switch_val, + Location location) +{ + Switch_statement* statement = Statement::make_switch_statement(switch_val, + location); + + this->push_break_statement(statement, label); + + Case_clauses* case_clauses = new Case_clauses(); + bool saw_default = false; + while (!this->peek_token()->is_op(OPERATOR_RCURLY)) + { + if (this->peek_token()->is_eof()) + { + if (!saw_errors()) + error_at(this->location(), "missing %<}%>"); + return NULL; + } + this->expr_case_clause(case_clauses, &saw_default); + } + this->advance_token(); + + statement->add_clauses(case_clauses); + + this->pop_break_statement(); + + return statement; +} + +// ExprCaseClause = ExprSwitchCase ":" [ StatementList ] . +// FallthroughStat = "fallthrough" . + +void +Parse::expr_case_clause(Case_clauses* clauses, bool* saw_default) +{ + Location location = this->location(); + + bool is_default = false; + Expression_list* vals = this->expr_switch_case(&is_default); + + if (!this->peek_token()->is_op(OPERATOR_COLON)) + { + if (!saw_errors()) + error_at(this->location(), "expected %<:%>"); + return; + } + else + this->advance_token(); + + Block* statements = NULL; + if (this->statement_list_may_start_here()) + { + this->gogo_->start_block(this->location()); + this->statement_list(); + statements = this->gogo_->finish_block(this->location()); + } + + bool is_fallthrough = false; + if (this->peek_token()->is_keyword(KEYWORD_FALLTHROUGH)) + { + Location fallthrough_loc = this->location(); + is_fallthrough = true; + if (this->advance_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + if (this->peek_token()->is_op(OPERATOR_RCURLY)) + error_at(fallthrough_loc, _("cannot fallthrough final case in switch")); + } + + if (is_default) + { + if (*saw_default) + { + error_at(location, "multiple defaults in switch"); + return; + } + *saw_default = true; + } + + if (is_default || vals != NULL) + clauses->add(vals, is_default, statements, is_fallthrough, location); +} + +// ExprSwitchCase = "case" ExpressionList | "default" . + +Expression_list* +Parse::expr_switch_case(bool* is_default) +{ + const Token* token = this->peek_token(); + if (token->is_keyword(KEYWORD_CASE)) + { + this->advance_token(); + return this->expression_list(NULL, false, true); + } + else if (token->is_keyword(KEYWORD_DEFAULT)) + { + this->advance_token(); + *is_default = true; + return NULL; + } + else + { + if (!saw_errors()) + error_at(this->location(), "expected % or %"); + if (!token->is_op(OPERATOR_RCURLY)) + this->advance_token(); + return NULL; + } +} + +// The body of a type switch. +// "{" { TypeCaseClause } "}" . + +Statement* +Parse::type_switch_body(Label* label, const Type_switch& type_switch, + Location location) +{ + Named_object* switch_no = NULL; + if (!type_switch.name.empty()) + { + if (Gogo::is_sink_name(type_switch.name)) + error_at(type_switch.location, + "no new variables on left side of %<:=%>"); + else + { + Variable* switch_var = new Variable(NULL, type_switch.expr, false, + false, false, + type_switch.location); + switch_no = this->gogo_->add_variable(type_switch.name, switch_var); + } + } + + Type_switch_statement* statement = + Statement::make_type_switch_statement(switch_no, + (switch_no == NULL + ? type_switch.expr + : NULL), + location); + + this->push_break_statement(statement, label); + + Type_case_clauses* case_clauses = new Type_case_clauses(); + bool saw_default = false; + while (!this->peek_token()->is_op(OPERATOR_RCURLY)) + { + if (this->peek_token()->is_eof()) + { + error_at(this->location(), "missing %<}%>"); + return NULL; + } + this->type_case_clause(switch_no, case_clauses, &saw_default); + } + this->advance_token(); + + statement->add_clauses(case_clauses); + + this->pop_break_statement(); + + return statement; +} + +// TypeCaseClause = TypeSwitchCase ":" [ StatementList ] . + +void +Parse::type_case_clause(Named_object* switch_no, Type_case_clauses* clauses, + bool* saw_default) +{ + Location location = this->location(); + + std::vector types; + bool is_default = false; + this->type_switch_case(&types, &is_default); + + if (!this->peek_token()->is_op(OPERATOR_COLON)) + error_at(this->location(), "expected %<:%>"); + else + this->advance_token(); + + Block* statements = NULL; + if (this->statement_list_may_start_here()) + { + this->gogo_->start_block(this->location()); + if (switch_no != NULL && types.size() == 1) + { + Type* type = types.front(); + Expression* init = Expression::make_var_reference(switch_no, + location); + init = Expression::make_type_guard(init, type, location); + Variable* v = new Variable(type, init, false, false, false, + location); + v->set_is_type_switch_var(); + Named_object* no = this->gogo_->add_variable(switch_no->name(), v); + + // We don't want to issue an error if the compiler + // introduced special variable is not used. Instead we want + // to issue an error if the variable defined by the switch + // is not used. That is handled via type_switch_vars_ and + // Parse::mark_var_used. + v->set_is_used(); + this->type_switch_vars_[no] = switch_no; + } + this->statement_list(); + statements = this->gogo_->finish_block(this->location()); + } + + if (this->peek_token()->is_keyword(KEYWORD_FALLTHROUGH)) + { + error_at(this->location(), + "fallthrough is not permitted in a type switch"); + if (this->advance_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + } + + if (is_default) + { + go_assert(types.empty()); + if (*saw_default) + { + error_at(location, "multiple defaults in type switch"); + return; + } + *saw_default = true; + clauses->add(NULL, false, true, statements, location); + } + else if (!types.empty()) + { + for (std::vector::const_iterator p = types.begin(); + p + 1 != types.end(); + ++p) + 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" + +// We accept a comma separated list of types. + +void +Parse::type_switch_case(std::vector* types, bool* is_default) +{ + const Token* token = this->peek_token(); + if (token->is_keyword(KEYWORD_CASE)) + { + this->advance_token(); + while (true) + { + Type* t = this->type(); + + if (!t->is_error_type()) + types->push_back(t); + else + { + this->gogo_->mark_locals_used(); + token = this->peek_token(); + while (!token->is_op(OPERATOR_COLON) + && !token->is_op(OPERATOR_COMMA) + && !token->is_op(OPERATOR_RCURLY) + && !token->is_eof()) + token = this->advance_token(); + } + + if (!this->peek_token()->is_op(OPERATOR_COMMA)) + break; + this->advance_token(); + } + } + else if (token->is_keyword(KEYWORD_DEFAULT)) + { + this->advance_token(); + *is_default = true; + } + else + { + error_at(this->location(), "expected % or %"); + if (!token->is_op(OPERATOR_RCURLY)) + this->advance_token(); + } +} + +// SelectStat = "select" "{" { CommClause } "}" . + +void +Parse::select_stat(Label* label) +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_SELECT)); + Location location = this->location(); + const Token* token = this->advance_token(); + + if (!token->is_op(OPERATOR_LCURLY)) + { + Location token_loc = token->location(); + if (token->is_op(OPERATOR_SEMICOLON) + && this->advance_token()->is_op(OPERATOR_LCURLY)) + error_at(token_loc, "unexpected semicolon or newline before %<{%>"); + else + { + error_at(this->location(), "expected %<{%>"); + return; + } + } + this->advance_token(); + + Select_statement* statement = Statement::make_select_statement(location); + + this->push_break_statement(statement, label); + + Select_clauses* select_clauses = new Select_clauses(); + bool saw_default = false; + while (!this->peek_token()->is_op(OPERATOR_RCURLY)) + { + if (this->peek_token()->is_eof()) + { + error_at(this->location(), "expected %<}%>"); + return; + } + this->comm_clause(select_clauses, &saw_default); + } + + this->advance_token(); + + statement->add_clauses(select_clauses); + + this->pop_break_statement(); + + this->gogo_->add_statement(statement); +} + +// CommClause = CommCase ":" { Statement ";" } . + +void +Parse::comm_clause(Select_clauses* clauses, bool* saw_default) +{ + Location location = this->location(); + bool is_send = false; + Expression* channel = NULL; + Expression* val = NULL; + Expression* closed = NULL; + std::string varname; + std::string closedname; + bool is_default = false; + bool got_case = this->comm_case(&is_send, &channel, &val, &closed, + &varname, &closedname, &is_default); + + if (!is_send + && varname.empty() + && closedname.empty() + && val != NULL + && val->index_expression() != NULL) + val->index_expression()->set_is_lvalue(); + + if (this->peek_token()->is_op(OPERATOR_COLON)) + this->advance_token(); + else + error_at(this->location(), "expected colon"); + + this->gogo_->start_block(this->location()); + + Named_object* var = NULL; + if (!varname.empty()) + { + // FIXME: LOCATION is slightly wrong here. + Variable* v = new Variable(NULL, channel, false, false, false, + location); + v->set_type_from_chan_element(); + var = this->gogo_->add_variable(varname, v); + } + + Named_object* closedvar = NULL; + if (!closedname.empty()) + { + // FIXME: LOCATION is slightly wrong here. + Variable* v = new Variable(Type::lookup_bool_type(), NULL, + false, false, false, location); + closedvar = this->gogo_->add_variable(closedname, v); + } + + this->statement_list(); + + Block* statements = this->gogo_->finish_block(this->location()); + + if (is_default) + { + if (*saw_default) + { + error_at(location, "multiple defaults in select"); + return; + } + *saw_default = true; + } + + if (got_case) + clauses->add(is_send, channel, val, closed, var, closedvar, is_default, + statements, location); + else if (statements != NULL) + { + // Add the statements to make sure that any names they define + // are traversed. + this->gogo_->add_block(statements, location); + } +} + +// CommCase = "case" ( SendStmt | RecvStmt ) | "default" . + +bool +Parse::comm_case(bool* is_send, Expression** channel, Expression** val, + Expression** closed, std::string* varname, + std::string* closedname, bool* is_default) +{ + const Token* token = this->peek_token(); + if (token->is_keyword(KEYWORD_DEFAULT)) + { + this->advance_token(); + *is_default = true; + } + else if (token->is_keyword(KEYWORD_CASE)) + { + this->advance_token(); + if (!this->send_or_recv_stmt(is_send, channel, val, closed, varname, + closedname)) + return false; + } + else + { + error_at(this->location(), "expected % or %"); + if (!token->is_op(OPERATOR_RCURLY)) + this->advance_token(); + return false; + } + + return true; +} + +// RecvStmt = [ Expression [ "," Expression ] ( "=" | ":=" ) ] RecvExpr . +// RecvExpr = Expression . + +bool +Parse::send_or_recv_stmt(bool* is_send, Expression** channel, Expression** val, + Expression** closed, std::string* varname, + std::string* closedname) +{ + const Token* token = this->peek_token(); + bool saw_comma = false; + bool closed_is_id = false; + if (token->is_identifier()) + { + Gogo* gogo = this->gogo_; + std::string recv_var = token->identifier(); + bool is_rv_exported = token->is_identifier_exported(); + Location recv_var_loc = token->location(); + token = this->advance_token(); + if (token->is_op(OPERATOR_COLONEQ)) + { + // case rv := <-c: + this->advance_token(); + Expression* e = this->expression(PRECEDENCE_NORMAL, false, false, + NULL, NULL); + Receive_expression* re = e->receive_expression(); + if (re == NULL) + { + if (!e->is_error_expression()) + error_at(this->location(), "expected receive expression"); + return false; + } + if (recv_var == "_") + { + error_at(recv_var_loc, + "no new variables on left side of %<:=%>"); + recv_var = Gogo::erroneous_name(); + } + *is_send = false; + *varname = gogo->pack_hidden_name(recv_var, is_rv_exported); + *channel = re->channel(); + return true; + } + else if (token->is_op(OPERATOR_COMMA)) + { + token = this->advance_token(); + if (token->is_identifier()) + { + std::string recv_closed = token->identifier(); + bool is_rc_exported = token->is_identifier_exported(); + Location recv_closed_loc = token->location(); + closed_is_id = true; + + token = this->advance_token(); + if (token->is_op(OPERATOR_COLONEQ)) + { + // case rv, rc := <-c: + this->advance_token(); + Expression* e = this->expression(PRECEDENCE_NORMAL, false, + false, NULL, NULL); + Receive_expression* re = e->receive_expression(); + if (re == NULL) + { + if (!e->is_error_expression()) + error_at(this->location(), + "expected receive expression"); + return false; + } + if (recv_var == "_" && recv_closed == "_") + { + error_at(recv_var_loc, + "no new variables on left side of %<:=%>"); + recv_var = Gogo::erroneous_name(); + } + *is_send = false; + if (recv_var != "_") + *varname = gogo->pack_hidden_name(recv_var, + is_rv_exported); + if (recv_closed != "_") + *closedname = gogo->pack_hidden_name(recv_closed, + is_rc_exported); + *channel = re->channel(); + return true; + } + + this->unget_token(Token::make_identifier_token(recv_closed, + is_rc_exported, + recv_closed_loc)); + } + + *val = this->id_to_expression(gogo->pack_hidden_name(recv_var, + is_rv_exported), + recv_var_loc); + saw_comma = true; + } + else + this->unget_token(Token::make_identifier_token(recv_var, + is_rv_exported, + recv_var_loc)); + } + + // If SAW_COMMA is false, then we are looking at the start of the + // send or receive expression. If SAW_COMMA is true, then *VAL is + // set and we just read a comma. + + Expression* e; + if (saw_comma || !this->peek_token()->is_op(OPERATOR_CHANOP)) + e = this->expression(PRECEDENCE_NORMAL, true, true, NULL, NULL); + else + { + // case <-c: + *is_send = false; + this->advance_token(); + *channel = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + + // The next token should be ':'. If it is '<-', then we have + // case <-c <- v: + // which is to say, send on a channel received from a channel. + if (!this->peek_token()->is_op(OPERATOR_CHANOP)) + return true; + + e = Expression::make_receive(*channel, (*channel)->location()); + } + + if (this->peek_token()->is_op(OPERATOR_EQ)) + { + if (!this->advance_token()->is_op(OPERATOR_CHANOP)) + { + error_at(this->location(), "missing %<<-%>"); + return false; + } + *is_send = false; + this->advance_token(); + *channel = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + if (saw_comma) + { + // case v, e = <-c: + // *VAL is already set. + if (!e->is_sink_expression()) + *closed = e; + } + else + { + // case v = <-c: + if (!e->is_sink_expression()) + *val = e; + } + return true; + } + + if (saw_comma) + { + if (closed_is_id) + error_at(this->location(), "expected %<=%> or %<:=%>"); + else + error_at(this->location(), "expected %<=%>"); + return false; + } + + if (this->peek_token()->is_op(OPERATOR_CHANOP)) + { + // case c <- v: + *is_send = true; + *channel = this->verify_not_sink(e); + this->advance_token(); + *val = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + return true; + } + + error_at(this->location(), "expected %<<-%> or %<=%>"); + return false; +} + +// ForStat = "for" [ Condition | ForClause | RangeClause ] Block . +// Condition = Expression . + +void +Parse::for_stat(Label* label) +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_FOR)); + Location location = this->location(); + const Token* token = this->advance_token(); + + // Open a block to hold any variables defined in the init statement + // of the for statement. + this->gogo_->start_block(location); + + Block* init = NULL; + Expression* cond = NULL; + Block* post = NULL; + Range_clause range_clause; + + if (!token->is_op(OPERATOR_LCURLY)) + { + if (token->is_keyword(KEYWORD_VAR)) + { + error_at(this->location(), + "var declaration not allowed in for initializer"); + this->var_decl(); + } + + if (token->is_op(OPERATOR_SEMICOLON)) + this->for_clause(&cond, &post); + else + { + // We might be looking at a Condition, an InitStat, or a + // RangeClause. + bool saw_send_stmt; + cond = this->simple_stat(false, &saw_send_stmt, &range_clause, NULL); + if (!this->peek_token()->is_op(OPERATOR_SEMICOLON)) + { + if (cond == NULL && !range_clause.found) + { + if (saw_send_stmt) + error_at(this->location(), + ("send statement used as value; " + "use select for non-blocking send")); + else + error_at(this->location(), "parse error in for statement"); + } + } + else + { + if (range_clause.found) + error_at(this->location(), "parse error after range clause"); + + if (cond != NULL) + { + // COND is actually an expression statement for + // InitStat at the start of a ForClause. + this->expression_stat(cond); + cond = NULL; + } + + this->for_clause(&cond, &post); + } + } + } + + // Check for the easy error of a newline before starting the block. + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + { + Location semi_loc = this->location(); + if (this->advance_token()->is_op(OPERATOR_LCURLY)) + error_at(semi_loc, "missing %<{%> after for clause"); + // Otherwise we will get an error when we call this->block + // below. + } + + // Build the For_statement and note that it is the current target + // for break and continue statements. + + For_statement* sfor; + For_range_statement* srange; + Statement* s; + if (!range_clause.found) + { + sfor = Statement::make_for_statement(init, cond, post, location); + s = sfor; + srange = NULL; + } + else + { + srange = Statement::make_for_range_statement(range_clause.index, + range_clause.value, + range_clause.range, + location); + s = srange; + sfor = NULL; + } + + this->push_break_statement(s, label); + this->push_continue_statement(s, label); + + // Gather the block of statements in the loop and add them to the + // For_statement. + + this->gogo_->start_block(this->location()); + Location end_loc = this->block(); + Block* statements = this->gogo_->finish_block(end_loc); + + if (sfor != NULL) + sfor->add_statements(statements); + else + srange->add_statements(statements); + + // This is no longer the break/continue target. + this->pop_break_statement(); + this->pop_continue_statement(); + + // Add the For_statement to the list of statements, and close out + // the block we started to hold any variables defined in the for + // statement. + + this->gogo_->add_statement(s); + + this->gogo_->add_block(this->gogo_->finish_block(this->location()), + location); +} + +// ForClause = [ InitStat ] ";" [ Condition ] ";" [ PostStat ] . +// InitStat = SimpleStat . +// PostStat = SimpleStat . + +// We have already read InitStat at this point. + +void +Parse::for_clause(Expression** cond, Block** post) +{ + go_assert(this->peek_token()->is_op(OPERATOR_SEMICOLON)); + this->advance_token(); + if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) + *cond = NULL; + else if (this->peek_token()->is_op(OPERATOR_LCURLY)) + { + error_at(this->location(), "missing %<{%> after for clause"); + *cond = NULL; + *post = NULL; + return; + } + else + *cond = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); + if (!this->peek_token()->is_op(OPERATOR_SEMICOLON)) + error_at(this->location(), "expected semicolon"); + else + this->advance_token(); + + if (this->peek_token()->is_op(OPERATOR_LCURLY)) + *post = NULL; + else + { + this->gogo_->start_block(this->location()); + this->simple_stat(false, NULL, NULL, NULL); + *post = this->gogo_->finish_block(this->location()); + } +} + +// RangeClause = IdentifierList ( "=" | ":=" ) "range" Expression . + +// This is the := version. It is called with a list of identifiers. + +void +Parse::range_clause_decl(const Typed_identifier_list* til, + Range_clause* p_range_clause) +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_RANGE)); + Location location = this->location(); + + p_range_clause->found = true; + + go_assert(til->size() >= 1); + if (til->size() > 2) + error_at(this->location(), "too many variables for range clause"); + + this->advance_token(); + Expression* expr = this->expression(PRECEDENCE_NORMAL, false, false, NULL, + NULL); + p_range_clause->range = expr; + + bool any_new = false; + + const Typed_identifier* pti = &til->front(); + 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); + + if (til->size() == 1) + p_range_clause->value = NULL; + else + { + pti = &til->back(); + bool is_new = false; + 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) + any_new = true; + if (!Gogo::is_sink_name(pti->name())) + p_range_clause->value = Expression::make_var_reference(no, location); + } + + if (!any_new) + error_at(location, "variables redeclared but no variable is new"); +} + +// The = version of RangeClause. This is called with a list of +// expressions. + +void +Parse::range_clause_expr(const Expression_list* vals, + Range_clause* p_range_clause) +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_RANGE)); + + p_range_clause->found = true; + + go_assert(vals->size() >= 1); + if (vals->size() > 2) + error_at(this->location(), "too many variables for range clause"); + + this->advance_token(); + p_range_clause->range = this->expression(PRECEDENCE_NORMAL, false, false, + NULL, NULL); + + p_range_clause->index = vals->front(); + if (vals->size() == 1) + p_range_clause->value = NULL; + else + p_range_clause->value = vals->back(); +} + +// Push a statement on the break stack. + +void +Parse::push_break_statement(Statement* enclosing, Label* label) +{ + if (this->break_stack_ == NULL) + this->break_stack_ = new Bc_stack(); + this->break_stack_->push_back(std::make_pair(enclosing, label)); +} + +// Push a statement on the continue stack. + +void +Parse::push_continue_statement(Statement* enclosing, Label* label) +{ + if (this->continue_stack_ == NULL) + this->continue_stack_ = new Bc_stack(); + this->continue_stack_->push_back(std::make_pair(enclosing, label)); +} + +// Pop the break stack. + +void +Parse::pop_break_statement() +{ + this->break_stack_->pop_back(); +} + +// Pop the continue stack. + +void +Parse::pop_continue_statement() +{ + this->continue_stack_->pop_back(); +} + +// Find a break or continue statement given a label name. + +Statement* +Parse::find_bc_statement(const Bc_stack* bc_stack, const std::string& label) +{ + if (bc_stack == NULL) + return NULL; + for (Bc_stack::const_reverse_iterator p = bc_stack->rbegin(); + p != bc_stack->rend(); + ++p) + { + if (p->second != NULL && p->second->name() == label) + { + p->second->set_is_used(); + return p->first; + } + } + return NULL; +} + +// BreakStat = "break" [ identifier ] . + +void +Parse::break_stat() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_BREAK)); + Location location = this->location(); + + const Token* token = this->advance_token(); + Statement* enclosing; + if (!token->is_identifier()) + { + if (this->break_stack_ == NULL || this->break_stack_->empty()) + { + error_at(this->location(), + "break statement not within for or switch or select"); + return; + } + enclosing = this->break_stack_->back().first; + } + else + { + enclosing = this->find_bc_statement(this->break_stack_, + token->identifier()); + if (enclosing == NULL) + { + // If there is a label with this name, mark it as used to + // avoid a useless error about an unused label. + this->gogo_->add_label_reference(token->identifier(), + Linemap::unknown_location(), false); + + error_at(token->location(), "invalid break label %qs", + Gogo::message_name(token->identifier()).c_str()); + this->advance_token(); + return; + } + this->advance_token(); + } + + Unnamed_label* label; + if (enclosing->classification() == Statement::STATEMENT_FOR) + label = enclosing->for_statement()->break_label(); + else if (enclosing->classification() == Statement::STATEMENT_FOR_RANGE) + label = enclosing->for_range_statement()->break_label(); + else if (enclosing->classification() == Statement::STATEMENT_SWITCH) + label = enclosing->switch_statement()->break_label(); + else if (enclosing->classification() == Statement::STATEMENT_TYPE_SWITCH) + label = enclosing->type_switch_statement()->break_label(); + else if (enclosing->classification() == Statement::STATEMENT_SELECT) + label = enclosing->select_statement()->break_label(); + else + go_unreachable(); + + this->gogo_->add_statement(Statement::make_break_statement(label, + location)); +} + +// ContinueStat = "continue" [ identifier ] . + +void +Parse::continue_stat() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_CONTINUE)); + Location location = this->location(); + + const Token* token = this->advance_token(); + Statement* enclosing; + if (!token->is_identifier()) + { + if (this->continue_stack_ == NULL || this->continue_stack_->empty()) + { + error_at(this->location(), "continue statement not within for"); + return; + } + enclosing = this->continue_stack_->back().first; + } + else + { + enclosing = this->find_bc_statement(this->continue_stack_, + token->identifier()); + if (enclosing == NULL) + { + // If there is a label with this name, mark it as used to + // avoid a useless error about an unused label. + this->gogo_->add_label_reference(token->identifier(), + Linemap::unknown_location(), false); + + error_at(token->location(), "invalid continue label %qs", + Gogo::message_name(token->identifier()).c_str()); + this->advance_token(); + return; + } + this->advance_token(); + } + + Unnamed_label* label; + if (enclosing->classification() == Statement::STATEMENT_FOR) + label = enclosing->for_statement()->continue_label(); + else if (enclosing->classification() == Statement::STATEMENT_FOR_RANGE) + label = enclosing->for_range_statement()->continue_label(); + else + go_unreachable(); + + this->gogo_->add_statement(Statement::make_continue_statement(label, + location)); +} + +// GotoStat = "goto" identifier . + +void +Parse::goto_stat() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_GOTO)); + Location location = this->location(); + const Token* token = this->advance_token(); + if (!token->is_identifier()) + error_at(this->location(), "expected label for goto"); + else + { + Label* label = this->gogo_->add_label_reference(token->identifier(), + location, true); + Statement* s = Statement::make_goto_statement(label, location); + this->gogo_->add_statement(s); + this->advance_token(); + } +} + +// PackageClause = "package" PackageName . + +void +Parse::package_clause() +{ + const Token* token = this->peek_token(); + Location location = token->location(); + std::string name; + if (!token->is_keyword(KEYWORD_PACKAGE)) + { + error_at(this->location(), "program must start with package clause"); + name = "ERROR"; + } + else + { + token = this->advance_token(); + if (token->is_identifier()) + { + name = token->identifier(); + if (name == "_") + { + error_at(this->location(), "invalid package name _"); + name = Gogo::erroneous_name(); + } + this->advance_token(); + } + else + { + error_at(this->location(), "package name must be an identifier"); + name = "ERROR"; + } + } + this->gogo_->set_package_name(name, location); +} + +// ImportDecl = "import" Decl . + +void +Parse::import_decl() +{ + go_assert(this->peek_token()->is_keyword(KEYWORD_IMPORT)); + this->advance_token(); + this->decl(&Parse::import_spec, NULL); +} + +// ImportSpec = [ "." | PackageName ] PackageFileName . + +void +Parse::import_spec(void*) +{ + const Token* token = this->peek_token(); + Location location = token->location(); + + std::string local_name; + bool is_local_name_exported = false; + if (token->is_op(OPERATOR_DOT)) + { + local_name = "."; + token = this->advance_token(); + } + else if (token->is_identifier()) + { + local_name = token->identifier(); + is_local_name_exported = token->is_identifier_exported(); + token = this->advance_token(); + } + + if (!token->is_string()) + { + error_at(this->location(), "import statement not a string"); + this->advance_token(); + return; + } + + this->gogo_->import_package(token->string_value(), local_name, + is_local_name_exported, location); + + this->advance_token(); +} + +// SourceFile = PackageClause ";" { ImportDecl ";" } +// { TopLevelDecl ";" } . + +void +Parse::program() +{ + this->package_clause(); + + const Token* token = this->peek_token(); + if (token->is_op(OPERATOR_SEMICOLON)) + token = this->advance_token(); + else + error_at(this->location(), + "expected %<;%> or newline after package clause"); + + while (token->is_keyword(KEYWORD_IMPORT)) + { + this->import_decl(); + token = this->peek_token(); + if (token->is_op(OPERATOR_SEMICOLON)) + token = this->advance_token(); + else + error_at(this->location(), + "expected %<;%> or newline after import declaration"); + } + + while (!token->is_eof()) + { + if (this->declaration_may_start_here()) + this->declaration(); + else + { + error_at(this->location(), "expected declaration"); + this->gogo_->mark_locals_used(); + do + this->advance_token(); + while (!this->peek_token()->is_eof() + && !this->peek_token()->is_op(OPERATOR_SEMICOLON) + && !this->peek_token()->is_op(OPERATOR_RCURLY)); + if (!this->peek_token()->is_eof() + && !this->peek_token()->is_op(OPERATOR_SEMICOLON)) + this->advance_token(); + } + token = this->peek_token(); + if (token->is_op(OPERATOR_SEMICOLON)) + token = this->advance_token(); + else if (!token->is_eof() || !saw_errors()) + { + if (token->is_op(OPERATOR_CHANOP)) + error_at(this->location(), + ("send statement used as value; " + "use select for non-blocking send")); + else + error_at(this->location(), + "expected %<;%> or newline after top level declaration"); + this->skip_past_error(OPERATOR_INVALID); + } + } +} + +// Reset the current iota value. + +void +Parse::reset_iota() +{ + this->iota_ = 0; +} + +// Return the current iota value. + +int +Parse::iota_value() +{ + return this->iota_; +} + +// Increment the current iota value. + +void +Parse::increment_iota() +{ + ++this->iota_; +} + +// Skip forward to a semicolon or OP. OP will normally be +// OPERATOR_RPAREN or OPERATOR_RCURLY. If we find a semicolon, move +// past it and return. If we find OP, it will be the next token to +// read. Return true if we are OK, false if we found EOF. + +bool +Parse::skip_past_error(Operator op) +{ + this->gogo_->mark_locals_used(); + const Token* token = this->peek_token(); + while (!token->is_op(op)) + { + if (token->is_eof()) + return false; + if (token->is_op(OPERATOR_SEMICOLON)) + { + this->advance_token(); + return true; + } + token = this->advance_token(); + } + return true; +} + +// Check that an expression is not a sink. + +Expression* +Parse::verify_not_sink(Expression* expr) +{ + if (expr->is_sink_expression()) + { + error_at(expr->location(), "cannot use _ as value"); + expr = Expression::make_error(expr->location()); + } + return expr; +} + +// Mark a variable as used. + +void +Parse::mark_var_used(Named_object* no) +{ + if (no->is_variable()) + { + no->var_value()->set_is_used(); + + // When a type switch uses := to define a variable, then for + // each case with a single type we introduce a new variable with + // the appropriate type. When we do, if the newly introduced + // variable is used, then the type switch variable is used. + Type_switch_vars::iterator p = this->type_switch_vars_.find(no); + if (p != this->type_switch_vars_.end()) + p->second->var_value()->set_is_used(); + } +} diff --git a/gcc-4.9/gcc/go/gofrontend/parse.h b/gcc-4.9/gcc/go/gofrontend/parse.h new file mode 100644 index 000000000..99e0eeebc --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/parse.h @@ -0,0 +1,335 @@ +// parse.h -- Go frontend parser. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_PARSE_H +#define GO_PARSE_H + +class Set_iota_traverse; +class Lex; +class Gogo; +class Named_object; +class Type; +class Typed_identifier; +class Typed_identifier_list; +class Channel_type; +class Function_type; +class Block; +class Expression; +class Expression_list; +class Struct_field_list; +class Case_clauses; +class Type_case_clauses; +class Select_clauses; +class Statement; +class Label; + +// Parse the program. + +class Parse +{ + public: + Parse(Lex*, Gogo*); + + // Parse a program. + void + program(); + + private: + // Precedence values. + enum Precedence + { + PRECEDENCE_INVALID = -1, + PRECEDENCE_NORMAL = 0, + PRECEDENCE_OROR, + PRECEDENCE_ANDAND, + PRECEDENCE_RELOP, + PRECEDENCE_ADDOP, + PRECEDENCE_MULOP + }; + + // We use this when parsing the range clause of a for statement. + struct Range_clause + { + // Set to true if we found a range clause. + bool found; + // The index expression. + Expression* index; + // The value expression. + Expression* value; + // The range expression. + Expression* range; + + Range_clause() + : found(false), index(NULL), value(NULL), range(NULL) + { } + }; + + // We use this when parsing the statement at the start of a switch, + // in order to recognize type switches. + struct Type_switch + { + // Set to true if we find a type switch. + bool found; + // The variable name. + std::string name; + // The location of the variable. + Location location; + // The expression. + Expression* expr; + + Type_switch() + : found(false), name(), location(UNKNOWN_LOCATION), expr(NULL) + { } + }; + + // A variable defined in an enclosing function referenced by the + // current function. + class Enclosing_var + { + public: + Enclosing_var(Named_object* var, Named_object* in_function, + unsigned int index) + : var_(var), in_function_(in_function), index_(index) + { } + + // We put these in a vector, so we need a default constructor. + Enclosing_var() + : var_(NULL), in_function_(NULL), index_(-1U) + { } + + Named_object* + var() const + { return this->var_; } + + Named_object* + in_function() const + { return this->in_function_; } + + unsigned int + index() const + { return this->index_; } + + private: + // The variable which is being referred to. + Named_object* var_; + // The function where the variable is defined. + Named_object* in_function_; + // The index of the field in this function's closure struct for + // this variable. + unsigned int index_; + }; + + // We store Enclosing_var entries in a set, so we need a comparator. + struct Enclosing_var_comparison + { + bool + operator()(const Enclosing_var&, const Enclosing_var&); + }; + + // A set of Enclosing_var entries. + typedef std::set Enclosing_vars; + + // Used to detect duplicate parameter/result names. + typedef std::map Names; + + // Peek at the current token from the lexer. + const Token* + peek_token(); + + // Consume the current token, return the next one. + const Token* + advance_token(); + + // Push a token back on the input stream. + void + unget_token(const Token&); + + // The location of the current token. + Location + location(); + + // For break and continue we keep a stack of statements with + // associated labels (if any). The top of the stack is used for a + // break or continue statement with no label. + typedef std::vector > Bc_stack; + + // Map from type switch variables to the variables they mask, so + // that a use of the type switch variable can become a use of the + // real variable. + typedef Unordered_map(Named_object*, Named_object*) Type_switch_vars; + + // Parser nonterminals. + void identifier_list(Typed_identifier_list*); + Expression_list* expression_list(Expression*, bool may_be_sink, + bool may_be_composite_lit); + bool qualified_ident(std::string*, Named_object**); + Type* type(); + bool type_may_start_here(); + Type* type_name(bool issue_error); + Type* array_type(bool may_use_ellipsis); + Type* map_type(); + Type* struct_type(); + void field_decl(Struct_field_list*); + Type* pointer_type(); + Type* channel_type(); + void check_signature_names(const Typed_identifier_list*, Names*); + Function_type* signature(Typed_identifier*, Location); + bool parameters(Typed_identifier_list**, bool* is_varargs); + Typed_identifier_list* parameter_list(bool* is_varargs); + void parameter_decl(bool, Typed_identifier_list*, bool*, bool*); + bool result(Typed_identifier_list**); + Location block(); + Type* interface_type(); + void method_spec(Typed_identifier_list*); + void declaration(); + bool declaration_may_start_here(); + void decl(void (Parse::*)(void*), void*); + void list(void (Parse::*)(void*), void*, bool); + void const_decl(); + void const_spec(Type**, Expression_list**); + void type_decl(); + void type_spec(void*); + void var_decl(); + void var_spec(void*); + void init_vars(const Typed_identifier_list*, Type*, Expression_list*, + bool is_coloneq, Location); + bool init_vars_from_call(const Typed_identifier_list*, Type*, Expression*, + bool is_coloneq, Location); + bool init_vars_from_map(const Typed_identifier_list*, Type*, Expression*, + bool is_coloneq, Location); + bool init_vars_from_receive(const Typed_identifier_list*, Type*, + Expression*, bool is_coloneq, Location); + bool init_vars_from_type_guard(const Typed_identifier_list*, Type*, + Expression*, bool is_coloneq, + Location); + Named_object* init_var(const Typed_identifier&, Type*, Expression*, + 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*); + void function_decl(bool saw_nointerface); + Typed_identifier* receiver(); + Expression* operand(bool may_be_sink, bool *is_parenthesized); + Expression* enclosing_var_reference(Named_object*, Named_object*, + Location); + Expression* composite_lit(Type*, int depth, Location); + Expression* function_lit(); + Expression* create_closure(Named_object* function, Enclosing_vars*, + Location); + Expression* primary_expr(bool may_be_sink, bool may_be_composite_lit, + bool* is_type_switch, bool* is_parenthesized); + Expression* selector(Expression*, bool* is_type_switch); + Expression* index(Expression*); + Expression* call(Expression*); + Expression* expression(Precedence, bool may_be_sink, + bool may_be_composite_lit, bool* is_type_switch, + bool *is_parenthesized); + bool expression_may_start_here(); + Expression* unary_expr(bool may_be_sink, bool may_be_composite_lit, + bool* is_type_switch, bool* is_parenthesized); + Type* reassociate_chan_direction(Channel_type*, Location); + Expression* qualified_expr(Expression*, Location); + Expression* id_to_expression(const std::string&, Location); + void statement(Label*); + bool statement_may_start_here(); + void labeled_stmt(const std::string&, Location); + Expression* simple_stat(bool, bool*, Range_clause*, Type_switch*); + bool simple_stat_may_start_here(); + void statement_list(); + bool statement_list_may_start_here(); + void expression_stat(Expression*); + void send_stmt(Expression*); + void inc_dec_stat(Expression*); + void assignment(Expression*, bool may_be_composite_lit, Range_clause*); + void tuple_assignment(Expression_list*, bool may_be_composite_lit, + Range_clause*); + void send(); + void go_or_defer_stat(); + void return_stat(); + void if_stat(); + void switch_stat(Label*); + Statement* expr_switch_body(Label*, Expression*, Location); + void expr_case_clause(Case_clauses*, bool* saw_default); + Expression_list* expr_switch_case(bool*); + Statement* type_switch_body(Label*, const Type_switch&, Location); + void type_case_clause(Named_object*, Type_case_clauses*, bool* saw_default); + void type_switch_case(std::vector*, bool*); + void select_stat(Label*); + void comm_clause(Select_clauses*, bool* saw_default); + bool comm_case(bool*, Expression**, Expression**, Expression**, + std::string*, std::string*, bool*); + bool send_or_recv_stmt(bool*, Expression**, Expression**, Expression**, + std::string*, std::string*); + void for_stat(Label*); + void for_clause(Expression**, Block**); + void range_clause_decl(const Typed_identifier_list*, Range_clause*); + void range_clause_expr(const Expression_list*, Range_clause*); + void push_break_statement(Statement*, Label*); + void push_continue_statement(Statement*, Label*); + void pop_break_statement(); + void pop_continue_statement(); + Statement* find_bc_statement(const Bc_stack*, const std::string&); + void break_stat(); + void continue_stat(); + void goto_stat(); + void package_clause(); + void import_decl(); + void import_spec(void*); + + void reset_iota(); + int iota_value(); + void increment_iota(); + + // Skip past an error looking for a semicolon or OP. Return true if + // all is well, false if we found EOF. + bool + skip_past_error(Operator op); + + // Verify that an expression is not a sink, and return either the + // expression or an error. + Expression* + verify_not_sink(Expression*); + + // Return the statement associated with a label in a Bc_stack, or + // NULL. + Statement* + find_bc_statement(const Bc_stack*, const std::string&) const; + + // Mark a variable as used. + void + mark_var_used(Named_object*); + + // The lexer output we are parsing. + Lex* lex_; + // The current token. + Token token_; + // A token pushed back on the input stream. + Token unget_token_; + // Whether unget_token_ is valid. + bool unget_token_valid_; + // Whether the function we are parsing had errors in the signature. + bool is_erroneous_function_; + // The code we are generating. + Gogo* gogo_; + // A stack of statements for which break may be used. + Bc_stack* break_stack_; + // A stack of statements for which continue may be used. + Bc_stack* continue_stack_; + // The current iota value. + int iota_; + // References from the local function to variables defined in + // enclosing functions. + Enclosing_vars enclosing_vars_; + // Map from type switch variables to real variables. + Type_switch_vars type_switch_vars_; +}; + + +#endif // !defined(GO_PARSE_H) diff --git a/gcc-4.9/gcc/go/gofrontend/runtime.cc b/gcc-4.9/gcc/go/gofrontend/runtime.cc new file mode 100644 index 000000000..3b0f18807 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/runtime.cc @@ -0,0 +1,409 @@ +// runtime.cc -- runtime functions called by generated code + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "gogo.h" +#include "types.h" +#include "expressions.h" +#include "runtime.h" + +// The frontend generates calls to various runtime functions. They +// are implemented in libgo/runtime. This is how the runtime +// functions are represented in the frontend. Note that there is +// currently nothing which ensures that the compiler's understanding +// of the runtime function matches the actual implementation in +// libgo/runtime. + +// Parameter and result types used by runtime functions. + +enum Runtime_function_type +{ + // General indicator that value is not used. + RFT_VOID, + // Go type bool, C type _Bool. + RFT_BOOL, + // Go type *bool, C type _Bool*. + RFT_BOOLPTR, + // Go type int, C type intgo. + RFT_INT, + // Go type int32, C type int32_t. + RFT_INT32, + // Go type int64, C type int64_t. + RFT_INT64, + // Go type uint64, C type uint64_t. + RFT_UINT64, + // Go type uintptr, C type uintptr_t. + RFT_UINTPTR, + // Go type rune, C type int32_t. + RFT_RUNE, + // Go type float64, C type double. + RFT_FLOAT64, + // Go type complex64, C type __complex float. + RFT_COMPLEX64, + // Go type complex128, C type __complex double. + RFT_COMPLEX128, + // Go type string, C type struct __go_string. + RFT_STRING, + // Go type unsafe.Pointer, C type "void *". + RFT_POINTER, + // Go type []any, C type struct __go_open_array. + RFT_SLICE, + // Go type map[any]any, C type struct __go_map *. + RFT_MAP, + // Pointer to map iteration type. + RFT_MAPITER, + // Go type chan any, C type struct __go_channel *. + RFT_CHAN, + // Go type non-empty interface, C type struct __go_interface. + RFT_IFACE, + // Go type interface{}, C type struct __go_empty_interface. + RFT_EFACE, + // Go type func(unsafe.Pointer), C type void (*) (void *). + RFT_FUNC_PTR, + // Pointer to Go type descriptor. + RFT_TYPE, + // Pointer to map descriptor. + RFT_MAPDESCRIPTOR, + + NUMBER_OF_RUNTIME_FUNCTION_TYPES +}; + +// The Type structures for the runtime function types. + +static Type* runtime_function_types[NUMBER_OF_RUNTIME_FUNCTION_TYPES]; + +// Get the Type for a Runtime_function_type code. + +static Type* +runtime_function_type(Runtime_function_type bft) +{ + go_assert(bft < NUMBER_OF_RUNTIME_FUNCTION_TYPES); + if (runtime_function_types[bft] == NULL) + { + const Location bloc = Linemap::predeclared_location(); + Type* t; + switch (bft) + { + default: + case RFT_VOID: + go_unreachable(); + + case RFT_BOOL: + t = Type::lookup_bool_type(); + break; + + case RFT_BOOLPTR: + t = Type::make_pointer_type(Type::lookup_bool_type()); + break; + + case RFT_INT: + t = Type::lookup_integer_type("int"); + break; + + case RFT_INT32: + t = Type::lookup_integer_type("int32"); + break; + + case RFT_INT64: + t = Type::lookup_integer_type("int64"); + break; + + case RFT_UINT64: + t = Type::lookup_integer_type("uint64"); + break; + + case RFT_RUNE: + t = Type::lookup_integer_type("int32"); + break; + + case RFT_UINTPTR: + t = Type::lookup_integer_type("uintptr"); + break; + + case RFT_FLOAT64: + t = Type::lookup_float_type("float64"); + break; + + case RFT_COMPLEX64: + t = Type::lookup_complex_type("complex64"); + break; + + case RFT_COMPLEX128: + t = Type::lookup_complex_type("complex128"); + break; + + case RFT_STRING: + t = Type::lookup_string_type(); + break; + + case RFT_POINTER: + t = Type::make_pointer_type(Type::make_void_type()); + break; + + case RFT_SLICE: + t = Type::make_array_type(Type::make_void_type(), NULL); + break; + + case RFT_MAP: + t = Type::make_map_type(Type::make_void_type(), + Type::make_void_type(), + bloc); + break; + + case RFT_MAPITER: + t = Type::make_pointer_type(Runtime::map_iteration_type()); + break; + + case RFT_CHAN: + t = Type::make_channel_type(true, true, Type::make_void_type()); + break; + + case RFT_IFACE: + { + Typed_identifier_list* methods = new Typed_identifier_list(); + Type* mtype = Type::make_function_type(NULL, NULL, NULL, bloc); + methods->push_back(Typed_identifier("x", mtype, bloc)); + Interface_type* it = Type::make_interface_type(methods, bloc); + it->finalize_methods(); + t = it; + } + break; + + case RFT_EFACE: + t = Type::make_empty_interface_type(bloc); + break; + + case RFT_FUNC_PTR: + { + Typed_identifier_list* param_types = new Typed_identifier_list(); + Type* ptrtype = runtime_function_type(RFT_POINTER); + param_types->push_back(Typed_identifier("", ptrtype, bloc)); + t = Type::make_function_type(NULL, param_types, NULL, bloc); + } + break; + + case RFT_TYPE: + t = Type::make_type_descriptor_ptr_type(); + break; + + case RFT_MAPDESCRIPTOR: + t = Type::make_pointer_type(Map_type::make_map_descriptor_type()); + break; + } + + runtime_function_types[bft] = t; + } + + return runtime_function_types[bft]; +} + +// Convert an expression to the type to pass to a runtime function. + +static Expression* +convert_to_runtime_function_type(Runtime_function_type bft, Expression* e, + Location loc) +{ + switch (bft) + { + default: + case RFT_VOID: + go_unreachable(); + + case RFT_BOOL: + case RFT_BOOLPTR: + case RFT_INT: + case RFT_INT32: + case RFT_INT64: + case RFT_UINT64: + case RFT_UINTPTR: + case RFT_RUNE: + case RFT_FLOAT64: + case RFT_COMPLEX64: + case RFT_COMPLEX128: + case RFT_STRING: + case RFT_POINTER: + case RFT_MAPITER: + case RFT_FUNC_PTR: + { + Type* t = runtime_function_type(bft); + if (!Type::are_identical(t, e->type(), true, NULL)) + e = Expression::make_cast(t, e, loc); + return e; + } + + case RFT_SLICE: + case RFT_MAP: + case RFT_CHAN: + case RFT_IFACE: + case RFT_EFACE: + return Expression::make_unsafe_cast(runtime_function_type(bft), e, loc); + + case RFT_TYPE: + go_assert(e->type() == Type::make_type_descriptor_ptr_type()); + return e; + + case RFT_MAPDESCRIPTOR: + go_assert(e->type()->points_to() + == Map_type::make_map_descriptor_type()); + return e; + } +} + +// Convert all the types used for runtime functions to the backend +// representation. + +void +Runtime::convert_types(Gogo* gogo) +{ + for (int i = 0; i < static_cast(NUMBER_OF_RUNTIME_FUNCTION_TYPES); ++i) + { + Type* t = runtime_function_types[i]; + if (t != NULL && t->named_type() != NULL) + { + bool r = t->verify(); + go_assert(r); + t->named_type()->convert(gogo); + } + } +} + +// The type used to define a runtime function. + +struct Runtime_function +{ + // Function name. + const char* name; + // Parameter types. Never more than 6, as it happens. RFT_VOID if + // not used. + Runtime_function_type parameter_types[6]; + // Result types. Never more than 2, as it happens. RFT_VOID if not + // used. + Runtime_function_type result_types[2]; +}; + +static const Runtime_function runtime_functions[] = +{ + +#define DEF_GO_RUNTIME(CODE, NAME, PARAMS, RESULTS) { NAME, PARAMS, RESULTS } , + +#include "runtime.def" + +#undef DEF_GO_RUNTIME + +}; + +static Named_object* +runtime_function_declarations[Runtime::NUMBER_OF_FUNCTIONS]; + +// Get the declaration of a runtime function. + +Named_object* +Runtime::runtime_declaration(Function code) +{ + go_assert(code < Runtime::NUMBER_OF_FUNCTIONS); + if (runtime_function_declarations[code] == NULL) + { + const Runtime_function* pb = &runtime_functions[code]; + + Location bloc = Linemap::predeclared_location(); + + Typed_identifier_list* param_types = NULL; + if (pb->parameter_types[0] != RFT_VOID) + { + param_types = new Typed_identifier_list(); + for (unsigned int i = 0; + i < (sizeof(pb->parameter_types) + / sizeof (pb->parameter_types[0])); + i++) + { + if (pb->parameter_types[i] == RFT_VOID) + break; + Type* t = runtime_function_type(pb->parameter_types[i]); + param_types->push_back(Typed_identifier("", t, bloc)); + } + } + + Typed_identifier_list* result_types = NULL; + if (pb->result_types[0] != RFT_VOID) + { + result_types = new Typed_identifier_list(); + for (unsigned int i = 0; + i < sizeof(pb->result_types) / sizeof(pb->result_types[0]); + i++) + { + if (pb->result_types[i] == RFT_VOID) + break; + Type* t = runtime_function_type(pb->result_types[i]); + result_types->push_back(Typed_identifier("", t, bloc)); + } + } + + Function_type* fntype = Type::make_function_type(NULL, param_types, + result_types, bloc); + const char* n = pb->name; + const char* n1 = strchr(n, '.'); + if (n1 != NULL) + n = n1 + 1; + Named_object* no = Named_object::make_function_declaration(n, NULL, + fntype, bloc); + no->func_declaration_value()->set_asm_name(pb->name); + + runtime_function_declarations[code] = no; + } + + return runtime_function_declarations[code]; +} + +// Make a call to a runtime function. + +Call_expression* +Runtime::make_call(Runtime::Function code, Location loc, + int param_count, ...) +{ + go_assert(code < Runtime::NUMBER_OF_FUNCTIONS); + + const Runtime_function* pb = &runtime_functions[code]; + + go_assert(static_cast(param_count) + <= sizeof(pb->parameter_types) / sizeof(pb->parameter_types[0])); + + Named_object* no = runtime_declaration(code); + Expression* func = Expression::make_func_reference(no, NULL, loc); + + Expression_list* args = new Expression_list(); + args->reserve(param_count); + + va_list ap; + va_start(ap, param_count); + for (int i = 0; i < param_count; ++i) + { + Expression* e = va_arg(ap, Expression*); + Runtime_function_type rft = pb->parameter_types[i]; + args->push_back(convert_to_runtime_function_type(rft, e, loc)); + } + va_end(ap); + + return Expression::make_call(func, args, false, loc); +} + +// The type we use for a map iteration. This is really a struct which +// is four pointers long. This must match the runtime struct +// __go_hash_iter. + +Type* +Runtime::map_iteration_type() +{ + const unsigned long map_iteration_size = 4; + + mpz_t ival; + mpz_init_set_ui(ival, map_iteration_size); + Expression* iexpr = Expression::make_integer(&ival, NULL, + Linemap::predeclared_location()); + mpz_clear(ival); + + return Type::make_array_type(runtime_function_type(RFT_POINTER), iexpr); +} diff --git a/gcc-4.9/gcc/go/gofrontend/runtime.def b/gcc-4.9/gcc/go/gofrontend/runtime.def new file mode 100644 index 000000000..a303a5041 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/runtime.def @@ -0,0 +1,374 @@ +// runtime.def -- runtime functions called by generated code. -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Definitions for the Go runtime functions. + +// Parameter type helper macros. +#define ABFT6(T1, T2, T3, T4, T5, T6) \ + { RFT_ ## T1, RFT_ ## T2, RFT_ ## T3, RFT_ ## T4, RFT_ ## T5, RFT_ ## T6 } +#define P0() ABFT6(VOID, VOID, VOID, VOID, VOID, VOID) +#define P1(T) ABFT6(T, VOID, VOID, VOID, VOID, VOID) +#define P2(T1, T2) ABFT6(T1, T2, VOID, VOID, VOID, VOID) +#define P3(T1, T2, T3) ABFT6(T1, T2, T3, VOID, VOID, VOID) +#define P4(T1, T2, T3, T4) ABFT6(T1, T2, T3, T4, VOID, VOID) +#define P5(T1, T2, T3, T4, T5) ABFT6(T1, T2, T3, T4, T5, VOID) +#define P6(T1,T2,T3,T4,T5,T6) ABFT6(T1, T2, T3, T4, T5, T6) + +// Result type helper macros. +#define ABFT2(T1, T2) { RFT_ ## T1, RFT_ ## T2 } +#define R0() ABFT2(VOID, VOID) +#define R1(T) ABFT2(T, VOID) +#define R2(T1, T2) ABFT2(T1, T2) + +// Define all the Go runtime functions. The first parameter is the +// enum code used to refer to the function. The second parameter is +// the name. The third is the parameter types and the fourth is the +// result types. + +// The standard C memcmp function, used for struct comparisons. +DEF_GO_RUNTIME(MEMCMP, "__go_memcmp", P3(POINTER, POINTER, UINTPTR), R1(INT)) + +// Range over a string, returning the next index. +DEF_GO_RUNTIME(STRINGITER, "runtime.stringiter", P2(STRING, INT), R1(INT)) + +// Range over a string, returning the next index and character. +DEF_GO_RUNTIME(STRINGITER2, "runtime.stringiter2", P2(STRING, INT), + R2(INT, RUNE)) + +// Concatenate two strings. +DEF_GO_RUNTIME(STRING_PLUS, "__go_string_plus", P2(STRING, STRING), R1(STRING)) + +// Compare two strings. +DEF_GO_RUNTIME(STRCMP, "__go_strcmp", P2(STRING, STRING), R1(INT)) + +// Take a slice of a string. +DEF_GO_RUNTIME(STRING_SLICE, "__go_string_slice", P3(STRING, INT, INT), + R1(STRING)) + +// Convert an integer to a string. +DEF_GO_RUNTIME(INT_TO_STRING, "__go_int_to_string", P1(INT), R1(STRING)) + +// Convert a byte array to a string. +DEF_GO_RUNTIME(BYTE_ARRAY_TO_STRING, "__go_byte_array_to_string", + P2(POINTER, INT), R1(STRING)) + +// Convert an int array to a string. +DEF_GO_RUNTIME(INT_ARRAY_TO_STRING, "__go_int_array_to_string", + P2(POINTER, INT), R1(STRING)) + +// Convert a string to a byte slice. +DEF_GO_RUNTIME(STRING_TO_BYTE_ARRAY, "__go_string_to_byte_array", + P1(STRING), R1(SLICE)) + +// Convert a string to an int slice. +DEF_GO_RUNTIME(STRING_TO_INT_ARRAY, "__go_string_to_int_array", + P1(STRING), R1(SLICE)) + + +// Complex division. +DEF_GO_RUNTIME(COMPLEX64_DIV, "__go_complex64_div", + P2(COMPLEX64, COMPLEX64), R1(COMPLEX64)) +DEF_GO_RUNTIME(COMPLEX128_DIV, "__go_complex128_div", + P2(COMPLEX128, COMPLEX128), R1(COMPLEX128)) + +// Make a slice. +DEF_GO_RUNTIME(MAKESLICE1, "__go_make_slice1", P2(TYPE, UINTPTR), R1(SLICE)) +DEF_GO_RUNTIME(MAKESLICE2, "__go_make_slice2", P3(TYPE, UINTPTR, UINTPTR), + R1(SLICE)) +DEF_GO_RUNTIME(MAKESLICE1BIG, "__go_make_slice1_big", P2(TYPE, UINT64), + R1(SLICE)) +DEF_GO_RUNTIME(MAKESLICE2BIG, "__go_make_slice2_big", P3(TYPE, UINT64, UINT64), + R1(SLICE)) + + +// Make a map. +DEF_GO_RUNTIME(MAKEMAP, "__go_new_map", P2(MAPDESCRIPTOR, UINTPTR), R1(MAP)) +DEF_GO_RUNTIME(MAKEMAPBIG, "__go_new_map_big", P2(MAPDESCRIPTOR, UINT64), + R1(MAP)) + +// Build a map from a composite literal. +DEF_GO_RUNTIME(CONSTRUCT_MAP, "__go_construct_map", + P6(POINTER, UINTPTR, UINTPTR, UINTPTR, UINTPTR, POINTER), + R1(MAP)) + +// Get the length of a map (the number of entries). +DEF_GO_RUNTIME(MAP_LEN, "__go_map_len", P1(MAP), R1(INT)) + +// Look up a key in a map. +DEF_GO_RUNTIME(MAP_INDEX, "__go_map_index", P3(MAP, POINTER, BOOL), + R1(POINTER)) + +// Look up a key in a map returning whether it is present. +DEF_GO_RUNTIME(MAPACCESS2, "runtime.mapaccess2", + P4(TYPE, MAP, POINTER, POINTER), R1(BOOL)) + +// Tuple assignment to a map element. +DEF_GO_RUNTIME(MAPASSIGN2, "runtime.mapassign2", + P4(MAP, POINTER, POINTER, BOOL), R0()) + +// Delete a key from a map. +DEF_GO_RUNTIME(MAPDELETE, "runtime.mapdelete", P2(MAP, POINTER), R0()) + +// Begin a range over a map. +DEF_GO_RUNTIME(MAPITERINIT, "runtime.mapiterinit", P2(MAP, MAPITER), R0()) + +// Range over a map, returning the next key. +DEF_GO_RUNTIME(MAPITER1, "runtime.mapiter1", P2(MAPITER, POINTER), R0()) + +// Range over a map, returning the next key and value. +DEF_GO_RUNTIME(MAPITER2, "runtime.mapiter2", P3(MAPITER, POINTER, POINTER), + R0()) + +// Range over a map, moving to the next map entry. +DEF_GO_RUNTIME(MAPITERNEXT, "runtime.mapiternext", P1(MAPITER), R0()) + + +// Make a channel. +DEF_GO_RUNTIME(MAKECHAN, "__go_new_channel", P2(TYPE, UINTPTR), R1(CHAN)) +DEF_GO_RUNTIME(MAKECHANBIG, "__go_new_channel_big", P2(TYPE, UINT64), R1(CHAN)) + +// Get the length of a channel (the number of unread values). +DEF_GO_RUNTIME(CHAN_LEN, "__go_chan_len", P1(CHAN), R1(INT)) + +// Get the capacity of a channel (the size of the buffer). +DEF_GO_RUNTIME(CHAN_CAP, "__go_chan_cap", P1(CHAN), R1(INT)) + +// Send a small value on a channel. +DEF_GO_RUNTIME(SEND_SMALL, "__go_send_small", P3(TYPE, CHAN, UINT64), R0()) + +// Send a big value on a channel. +DEF_GO_RUNTIME(SEND_BIG, "__go_send_big", P3(TYPE, CHAN, POINTER), R0()) + +// Receive a small value from a channel. +DEF_GO_RUNTIME(RECEIVE_SMALL, "__go_receive_small", P2(TYPE, CHAN), R1(UINT64)) + +// Receive a big value from a channel. +DEF_GO_RUNTIME(RECEIVE_BIG, "__go_receive_big", P3(TYPE, CHAN, POINTER), R0()) + +// Receive a value from a channel returning whether it is closed. +DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P3(TYPE, CHAN, POINTER), + R1(BOOL)) + + +// Start building a select statement. +DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P1(INT32), R1(POINTER)) + +// Add a default clause to a select statement. +DEF_GO_RUNTIME(SELECTDEFAULT, "runtime.selectdefault", + P2(POINTER, INT32), R0()) + +// Add a send clause to a select statement. +DEF_GO_RUNTIME(SELECTSEND, "runtime.selectsend", + P4(POINTER, CHAN, POINTER, INT32), R0()) + +// Add a receive clause to a select statement, for a clause which does +// not check whether the channel is closed. +DEF_GO_RUNTIME(SELECTRECV, "runtime.selectrecv", + P4(POINTER, CHAN, POINTER, INT32), R0()) + +// Add a receive clause to a select statement, for a clause which does +// check whether the channel is closed. +DEF_GO_RUNTIME(SELECTRECV2, "runtime.selectrecv2", + P5(POINTER, CHAN, POINTER, BOOLPTR, INT32), R0()) + +// Run a select, returning the index of the selected clause. +DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT32)) + + +// Panic. +DEF_GO_RUNTIME(PANIC, "__go_panic", P1(EFACE), R0()) + +// Recover. +DEF_GO_RUNTIME(RECOVER, "__go_recover", P0(), R1(EFACE)) + +// Recover when called directly from defer. +DEF_GO_RUNTIME(DEFERRED_RECOVER, "__go_deferred_recover", P0(), R1(EFACE)) + +// Decide whether this function can call recover. +DEF_GO_RUNTIME(CAN_RECOVER, "__go_can_recover", P1(POINTER), R1(BOOL)) + +// Get the return address of the function. +DEF_GO_RUNTIME(RETURN_ADDRESS, "__go_return_address", P1(INT), R1(POINTER)) + +// Set the return address for defer in a defer thunk. +DEF_GO_RUNTIME(SET_DEFER_RETADDR, "__go_set_defer_retaddr", P1(POINTER), + R1(BOOL)) + +// Check for a deferred function in an exception handler. +DEF_GO_RUNTIME(CHECK_DEFER, "__go_check_defer", P1(BOOLPTR), R0()) + +// Run deferred functions. +DEF_GO_RUNTIME(UNDEFER, "__go_undefer", P1(BOOLPTR), R0()) + +// Panic with a runtime error. +DEF_GO_RUNTIME(RUNTIME_ERROR, "__go_runtime_error", P1(INT32), R0()) + + +// Close. +DEF_GO_RUNTIME(CLOSE, "__go_close", P1(CHAN), R0()) + + +// Copy. +DEF_GO_RUNTIME(COPY, "__go_copy", P3(POINTER, POINTER, UINTPTR), R0()) + +// Append. +DEF_GO_RUNTIME(APPEND, "__go_append", P4(SLICE, POINTER, UINTPTR, UINTPTR), + R1(SLICE)) + + +// Register roots (global variables) for the garbage collector. +DEF_GO_RUNTIME(REGISTER_GC_ROOTS, "__go_register_gc_roots", P1(POINTER), R0()) + + +// Allocate memory. +DEF_GO_RUNTIME(NEW, "__go_new", P1(UINTPTR), R1(POINTER)) + +// Allocate memory which can not contain pointers. +DEF_GO_RUNTIME(NEW_NOPOINTERS, "__go_new_nopointers", P1(UINTPTR), R1(POINTER)) + + +// Start a new goroutine. +DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0()) + + +// Defer a function. +DEF_GO_RUNTIME(DEFER, "__go_defer", P3(BOOLPTR, FUNC_PTR, POINTER), R0()) + + +// Convert an empty interface to an empty interface, returning ok. +DEF_GO_RUNTIME(IFACEE2E2, "runtime.ifaceE2E2", P1(EFACE), R2(EFACE, BOOL)) + +// Convert a non-empty interface to an empty interface, returning ok. +DEF_GO_RUNTIME(IFACEI2E2, "runtime.ifaceI2E2", P1(IFACE), R2(EFACE, BOOL)) + +// Convert an empty interface to a non-empty interface, returning ok. +DEF_GO_RUNTIME(IFACEE2I2, "runtime.ifaceE2I2", P2(TYPE, EFACE), + R2(IFACE, BOOL)) + +// Convert a non-empty interface to a non-empty interface, returning ok. +DEF_GO_RUNTIME(IFACEI2I2, "runtime.ifaceI2I2", P2(TYPE, IFACE), + R2(IFACE, BOOL)) + +// Convert an empty interface to a pointer type, returning ok. +DEF_GO_RUNTIME(IFACEE2T2P, "runtime.ifaceE2T2P", P2(TYPE, EFACE), + R2(POINTER, BOOL)) + +// Convert a non-empty interface to a pointer type, return ok. +DEF_GO_RUNTIME(IFACEI2T2P, "runtime.ifaceI2T2P", P2(TYPE, IFACE), + R2(POINTER, BOOL)) + +// Convert an empty interface to a non-pointer type, returning ok. +DEF_GO_RUNTIME(IFACEE2T2, "runtime.ifaceE2T2", P3(TYPE, EFACE, POINTER), + R1(BOOL)) + +// Convert a non-empty interface to a non-pointer type, returning ok. +DEF_GO_RUNTIME(IFACEI2T2, "runtime.ifaceI2T2", P3(TYPE, IFACE, POINTER), + R1(BOOL)) + +// A type assertion from one interface type to another. This is +// used for a type assertion. +DEF_GO_RUNTIME(ASSERT_INTERFACE, "__go_assert_interface", P2(TYPE, TYPE), R0()) + +// Convert one interface type to another. This is used for an +// assignment. +DEF_GO_RUNTIME(CONVERT_INTERFACE, "__go_convert_interface", P2(TYPE, TYPE), + R1(POINTER)) + +// Check whether an interface type may be converted to a +// non-interface type. +DEF_GO_RUNTIME(CHECK_INTERFACE_TYPE, "__go_check_interface_type", + P3(TYPE, TYPE, TYPE), R0()) + +// Return whether we can convert an interface type to a type. +DEF_GO_RUNTIME(IFACEI2TP, "runtime.ifaceI2Tp", P2(TYPE, TYPE), R1(BOOL)) + +// Get the type descriptor of an empty interface. +DEF_GO_RUNTIME(EFACETYPE, "runtime.efacetype", P1(EFACE), R1(TYPE)) + +// Get the type descriptor of a non-empty interface. +DEF_GO_RUNTIME(IFACETYPE, "runtime.ifacetype", P1(IFACE), R1(TYPE)) + + +// Compare two type descriptors for equality. +DEF_GO_RUNTIME(IFACETYPEEQ, "runtime.ifacetypeeq", P2(TYPE, TYPE), R1(BOOL)) + +// Compare two empty interface values. +DEF_GO_RUNTIME(EMPTY_INTERFACE_COMPARE, "__go_empty_interface_compare", + P2(EFACE, EFACE), R1(INT)) + +// Compare an empty interface value to a non-interface value. +DEF_GO_RUNTIME(EMPTY_INTERFACE_VALUE_COMPARE, + "__go_empty_interface_value_compare", + P3(EFACE, TYPE, POINTER), R1(INT)) + +// Compare two non-empty interface values. +DEF_GO_RUNTIME(INTERFACE_COMPARE, "__go_interface_compare", + P2(IFACE, IFACE), R1(INT)) + +// Compare a non-empty interface value to a non-interface value. +DEF_GO_RUNTIME(INTERFACE_VALUE_COMPARE, "__go_interface_value_compare", + P3(IFACE, TYPE, POINTER), R1(INT)) + +// Compare a non-empty interface value to an interface value. +DEF_GO_RUNTIME(INTERFACE_EMPTY_COMPARE, "__go_interface_empty_compare", + P2(IFACE, EFACE), R1(INT)) + + +// Print a string (for print/println). +DEF_GO_RUNTIME(PRINT_STRING, "__go_print_string", P1(STRING), R0()) + +// Print a uint64 (for print/println). +DEF_GO_RUNTIME(PRINT_UINT64, "__go_print_uint64", P1(UINT64), R0()) + +// Print a int64 (for print/println). +DEF_GO_RUNTIME(PRINT_INT64, "__go_print_int64", P1(INT64), R0()) + +// Print a float64 (for print/println). +DEF_GO_RUNTIME(PRINT_DOUBLE, "__go_print_double", P1(FLOAT64), R0()) + +// Print a complex128 (for print/println). +DEF_GO_RUNTIME(PRINT_COMPLEX, "__go_print_complex", P1(COMPLEX128), R0()) + +// Print a bool (for print/println). +DEF_GO_RUNTIME(PRINT_BOOL, "__go_print_bool", P1(BOOL), R0()) + +// Print a pointer/map/channel/function (for print/println). +DEF_GO_RUNTIME(PRINT_POINTER, "__go_print_pointer", P1(POINTER), R0()) + +// Print an empty interface (for print/println). +DEF_GO_RUNTIME(PRINT_EMPTY_INTERFACE, "__go_print_empty_interface", + P1(EFACE), R0()) + +// Print a non-empty interface (for print/println). +DEF_GO_RUNTIME(PRINT_INTERFACE, "__go_print_interface", P1(IFACE), R0()) + +// Print a slice (for print/println). +DEF_GO_RUNTIME(PRINT_SLICE, "__go_print_slice", P1(SLICE), R0()) + +// Print a space (for println). +DEF_GO_RUNTIME(PRINT_SPACE, "__go_print_space", P0(), R0()) + +// Print a newline (for println). +DEF_GO_RUNTIME(PRINT_NL, "__go_print_nl", P0(), R0()) + + +// Used for field tracking for data analysis. +DEF_GO_RUNTIME(FIELDTRACK, "__go_fieldtrack", P1(POINTER), R0()) + + +// Remove helper macros. +#undef ABFT6 +#undef ABFT2 +#undef P0 +#undef P1 +#undef P2 +#undef P3 +#undef P4 +#undef P5 +#undef P6 +#undef R0 +#undef R1 +#undef R2 diff --git a/gcc-4.9/gcc/go/gofrontend/runtime.h b/gcc-4.9/gcc/go/gofrontend/runtime.h new file mode 100644 index 000000000..be5dcbe25 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/runtime.h @@ -0,0 +1,51 @@ +// runtime.h -- runtime functions called by generated code -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_RUNTIME_H +#define GO_RUNTIME_H + +class Gogo; +class Type; +class Named_object; +class Call_expression; + +class Runtime +{ + public: + + // The runtime functions which may be called by generated code. + enum Function + { + +#define DEF_GO_RUNTIME(CODE, NAME, PARAMS, RESULTS) CODE , + +#include "runtime.def" + +#undef DEF_GO_RUNTIME + + // Number of runtime functions. + NUMBER_OF_FUNCTIONS + }; + + // Make a call to a runtime function. + static Call_expression* + make_call(Function, Location, int, ...); + + // Convert all the types used by runtime functions to the backend + // representation. + static void + convert_types(Gogo*); + + // Return the type used for iterations over maps. + static Type* + map_iteration_type(); + + private: + static Named_object* + runtime_declaration(Function); +}; + +#endif // !defined(GO_BUILTINS_H) diff --git a/gcc-4.9/gcc/go/gofrontend/statements.cc b/gcc-4.9/gcc/go/gofrontend/statements.cc new file mode 100644 index 000000000..d195ab984 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/statements.cc @@ -0,0 +1,6038 @@ +// statements.cc -- Go frontend statements. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "go-c.h" +#include "types.h" +#include "expressions.h" +#include "gogo.h" +#include "runtime.h" +#include "backend.h" +#include "statements.h" +#include "ast-dump.h" + +// Class Statement. + +Statement::Statement(Statement_classification classification, + Location location) + : classification_(classification), location_(location) +{ +} + +Statement::~Statement() +{ +} + +// Traverse the tree. The work of walking the components is handled +// by the subclasses. + +int +Statement::traverse(Block* block, size_t* pindex, Traverse* traverse) +{ + if (this->classification_ == STATEMENT_ERROR) + return TRAVERSE_CONTINUE; + + unsigned int traverse_mask = traverse->traverse_mask(); + + if ((traverse_mask & Traverse::traverse_statements) != 0) + { + int t = traverse->statement(block, pindex, this); + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + else if (t == TRAVERSE_SKIP_COMPONENTS) + return TRAVERSE_CONTINUE; + } + + // No point in checking traverse_mask here--a statement may contain + // other blocks or statements, and if we got here we always want to + // walk them. + return this->do_traverse(traverse); +} + +// Traverse the contents of a statement. + +int +Statement::traverse_contents(Traverse* traverse) +{ + return this->do_traverse(traverse); +} + +// Traverse assignments. + +bool +Statement::traverse_assignments(Traverse_assignments* tassign) +{ + if (this->classification_ == STATEMENT_ERROR) + return false; + return this->do_traverse_assignments(tassign); +} + +// Traverse an expression in a statement. This is a helper function +// for child classes. + +int +Statement::traverse_expression(Traverse* traverse, Expression** expr) +{ + if ((traverse->traverse_mask() + & (Traverse::traverse_types | Traverse::traverse_expressions)) == 0) + return TRAVERSE_CONTINUE; + return Expression::traverse(expr, traverse); +} + +// Traverse an expression list in a statement. This is a helper +// function for child classes. + +int +Statement::traverse_expression_list(Traverse* traverse, + Expression_list* expr_list) +{ + if (expr_list == NULL) + return TRAVERSE_CONTINUE; + if ((traverse->traverse_mask() + & (Traverse::traverse_types | Traverse::traverse_expressions)) == 0) + return TRAVERSE_CONTINUE; + return expr_list->traverse(traverse); +} + +// Traverse a type in a statement. This is a helper function for +// child classes. + +int +Statement::traverse_type(Traverse* traverse, Type* type) +{ + if ((traverse->traverse_mask() + & (Traverse::traverse_types | Traverse::traverse_expressions)) == 0) + return TRAVERSE_CONTINUE; + return Type::traverse(type, traverse); +} + +// Set type information for unnamed constants. This is really done by +// the child class. + +void +Statement::determine_types() +{ + this->do_determine_types(); +} + +// If this is a thunk statement, return it. + +Thunk_statement* +Statement::thunk_statement() +{ + Thunk_statement* ret = this->convert(); + if (ret == NULL) + ret = this->convert(); + return ret; +} + +// Convert a Statement to the backend representation. This is really +// done by the child class. + +Bstatement* +Statement::get_backend(Translate_context* context) +{ + if (this->classification_ == STATEMENT_ERROR) + return context->backend()->error_statement(); + return this->do_get_backend(context); +} + +// Dump AST representation for a statement to a dump context. + +void +Statement::dump_statement(Ast_dump_context* ast_dump_context) const +{ + this->do_dump_statement(ast_dump_context); +} + +// Note that this statement is erroneous. This is called by children +// when they discover an error. + +void +Statement::set_is_error() +{ + this->classification_ = STATEMENT_ERROR; +} + +// For children to call to report an error conveniently. + +void +Statement::report_error(const char* msg) +{ + error_at(this->location_, "%s", msg); + this->set_is_error(); +} + +// An error statement, used to avoid crashing after we report an +// error. + +class Error_statement : public Statement +{ + public: + Error_statement(Location location) + : Statement(STATEMENT_ERROR, location) + { } + + protected: + int + do_traverse(Traverse*) + { return TRAVERSE_CONTINUE; } + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; +}; + +// Dump the AST representation for an error statement. + +void +Error_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "Error statement" << std::endl; +} + +// Make an error statement. + +Statement* +Statement::make_error_statement(Location location) +{ + return new Error_statement(location); +} + +// Class Variable_declaration_statement. + +Variable_declaration_statement::Variable_declaration_statement( + Named_object* var) + : Statement(STATEMENT_VARIABLE_DECLARATION, var->var_value()->location()), + var_(var) +{ +} + +// We don't actually traverse the variable here; it was traversed +// while traversing the Block. + +int +Variable_declaration_statement::do_traverse(Traverse*) +{ + return TRAVERSE_CONTINUE; +} + +// Traverse the assignments in a variable declaration. Note that this +// traversal is different from the usual traversal. + +bool +Variable_declaration_statement::do_traverse_assignments( + Traverse_assignments* tassign) +{ + tassign->initialize_variable(this->var_); + return true; +} + +// Lower the variable's initialization expression. + +Statement* +Variable_declaration_statement::do_lower(Gogo* gogo, Named_object* function, + Block*, Statement_inserter* inserter) +{ + this->var_->var_value()->lower_init_expression(gogo, function, inserter); + return this; +} + +// Flatten the variable's initialization expression. + +Statement* +Variable_declaration_statement::do_flatten(Gogo* gogo, Named_object* function, + Block*, Statement_inserter* inserter) +{ + this->var_->var_value()->flatten_init_expression(gogo, function, inserter); + return this; +} + +// Convert a variable declaration to the backend representation. + +Bstatement* +Variable_declaration_statement::do_get_backend(Translate_context* context) +{ + Variable* var = this->var_->var_value(); + Bvariable* bvar = this->var_->get_backend_variable(context->gogo(), + context->function()); + tree init = var->get_init_tree(context->gogo(), context->function()); + Bexpression* binit = init == NULL ? NULL : tree_to_expr(init); + + if (!var->is_in_heap()) + { + go_assert(binit != NULL); + return context->backend()->init_statement(bvar, binit); + } + + // Something takes the address of this variable, so the value is + // stored in the heap. Initialize it to newly allocated memory + // space, and assign the initial value to the new space. + Location loc = this->location(); + Named_object* newfn = context->gogo()->lookup_global("new"); + go_assert(newfn != NULL && newfn->is_function_declaration()); + Expression* func = Expression::make_func_reference(newfn, NULL, loc); + Expression_list* params = new Expression_list(); + params->push_back(Expression::make_type(var->type(), loc)); + Expression* call = Expression::make_call(func, params, false, loc); + context->gogo()->lower_expression(context->function(), NULL, &call); + Temporary_statement* temp = Statement::make_temporary(NULL, call, loc); + Bstatement* btemp = temp->get_backend(context); + + Bstatement* set = NULL; + if (binit != NULL) + { + Expression* e = Expression::make_temporary_reference(temp, loc); + e = Expression::make_unary(OPERATOR_MULT, e, loc); + Bexpression* be = tree_to_expr(e->get_tree(context)); + set = context->backend()->assignment_statement(be, binit, loc); + } + + Expression* ref = Expression::make_temporary_reference(temp, loc); + Bexpression* bref = tree_to_expr(ref->get_tree(context)); + Bstatement* sinit = context->backend()->init_statement(bvar, bref); + + std::vector stats; + stats.reserve(3); + stats.push_back(btemp); + if (set != NULL) + stats.push_back(set); + stats.push_back(sinit); + return context->backend()->statement_list(stats); +} + +// Dump the AST representation for a variable declaration. + +void +Variable_declaration_statement::do_dump_statement( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + + go_assert(var_->is_variable()); + ast_dump_context->ostream() << "var " << this->var_->name() << " "; + Variable* var = this->var_->var_value(); + if (var->has_type()) + { + ast_dump_context->dump_type(var->type()); + ast_dump_context->ostream() << " "; + } + if (var->init() != NULL) + { + ast_dump_context->ostream() << "= "; + ast_dump_context->dump_expression(var->init()); + } + ast_dump_context->ostream() << std::endl; +} + +// Make a variable declaration. + +Statement* +Statement::make_variable_declaration(Named_object* var) +{ + return new Variable_declaration_statement(var); +} + +// Class Temporary_statement. + +// Return the type of the temporary variable. + +Type* +Temporary_statement::type() const +{ + return this->type_ != NULL ? this->type_ : this->init_->type(); +} + +// Traversal. + +int +Temporary_statement::do_traverse(Traverse* traverse) +{ + if (this->type_ != NULL + && this->traverse_type(traverse, this->type_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->init_ == NULL) + return TRAVERSE_CONTINUE; + else + return this->traverse_expression(traverse, &this->init_); +} + +// Traverse assignments. + +bool +Temporary_statement::do_traverse_assignments(Traverse_assignments* tassign) +{ + if (this->init_ == NULL) + return false; + tassign->value(&this->init_, true, true); + return true; +} + +// Determine types. + +void +Temporary_statement::do_determine_types() +{ + if (this->type_ != NULL && this->type_->is_abstract()) + this->type_ = this->type_->make_non_abstract_type(); + + if (this->init_ != NULL) + { + if (this->type_ == NULL) + this->init_->determine_type_no_context(); + else + { + Type_context context(this->type_, false); + this->init_->determine_type(&context); + } + } + + if (this->type_ == NULL) + { + this->type_ = this->init_->type(); + go_assert(!this->type_->is_abstract()); + } +} + +// Check types. + +void +Temporary_statement::do_check_types(Gogo*) +{ + if (this->type_ != NULL && this->init_ != NULL) + { + std::string reason; + bool ok; + if (this->are_hidden_fields_ok_) + ok = Type::are_assignable_hidden_ok(this->type_, this->init_->type(), + &reason); + else + ok = Type::are_assignable(this->type_, this->init_->type(), &reason); + if (!ok) + { + if (reason.empty()) + error_at(this->location(), "incompatible types in assignment"); + else + error_at(this->location(), "incompatible types in assignment (%s)", + reason.c_str()); + this->set_is_error(); + } + } +} + +// Convert to backend representation. + +Bstatement* +Temporary_statement::do_get_backend(Translate_context* context) +{ + go_assert(this->bvariable_ == NULL); + + // FIXME: Permitting FUNCTION to be NULL here is a temporary measure + // until we have a better representation of the init function. + Named_object* function = context->function(); + Bfunction* bfunction; + if (function == NULL) + bfunction = NULL; + else + bfunction = tree_to_function(function->func_value()->get_decl()); + + Btype* btype = this->type()->get_backend(context->gogo()); + + Bexpression* binit; + if (this->init_ == NULL) + binit = NULL; + else if (this->type_ == NULL) + binit = tree_to_expr(this->init_->get_tree(context)); + else + { + Expression* init = Expression::make_cast(this->type_, this->init_, + this->location()); + context->gogo()->lower_expression(context->function(), NULL, &init); + binit = tree_to_expr(init->get_tree(context)); + } + + Bstatement* statement; + this->bvariable_ = + context->backend()->temporary_variable(bfunction, context->bblock(), + btype, binit, + this->is_address_taken_, + this->location(), &statement); + return statement; +} + +// Return the backend variable. + +Bvariable* +Temporary_statement::get_backend_variable(Translate_context* context) const +{ + if (this->bvariable_ == NULL) + { + go_assert(saw_errors()); + return context->backend()->error_variable(); + } + return this->bvariable_; +} + +// Dump the AST represemtation for a temporary statement + +void +Temporary_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_temp_variable_name(this); + if (this->type_ != NULL) + { + ast_dump_context->ostream() << " "; + ast_dump_context->dump_type(this->type_); + } + if (this->init_ != NULL) + { + ast_dump_context->ostream() << " = "; + ast_dump_context->dump_expression(this->init_); + } + ast_dump_context->ostream() << std::endl; +} + +// Make and initialize a temporary variable in BLOCK. + +Temporary_statement* +Statement::make_temporary(Type* type, Expression* init, + Location location) +{ + return new Temporary_statement(type, init, location); +} + +// An assignment statement. + +class Assignment_statement : public Statement +{ + public: + Assignment_statement(Expression* lhs, Expression* rhs, + Location location) + : Statement(STATEMENT_ASSIGNMENT, location), + lhs_(lhs), rhs_(rhs), are_hidden_fields_ok_(false) + { } + + // Note that it is OK for this assignment statement to set hidden + // fields. + void + set_hidden_fields_are_ok() + { this->are_hidden_fields_ok_ = true; } + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_traverse_assignments(Traverse_assignments*); + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // Left hand side--the lvalue. + Expression* lhs_; + // Right hand side--the rvalue. + Expression* rhs_; + // True if this statement may set hidden fields in the assignment + // statement. This is used for generated method stubs. + bool are_hidden_fields_ok_; +}; + +// Traversal. + +int +Assignment_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->lhs_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->traverse_expression(traverse, &this->rhs_); +} + +bool +Assignment_statement::do_traverse_assignments(Traverse_assignments* tassign) +{ + tassign->assignment(&this->lhs_, &this->rhs_); + return true; +} + +// Set types for the assignment. + +void +Assignment_statement::do_determine_types() +{ + this->lhs_->determine_type_no_context(); + Type* rhs_context_type = this->lhs_->type(); + if (rhs_context_type->is_sink_type()) + rhs_context_type = NULL; + Type_context context(rhs_context_type, false); + this->rhs_->determine_type(&context); +} + +// Check types for an assignment. + +void +Assignment_statement::do_check_types(Gogo*) +{ + // The left hand side must be either addressable, a map index + // expression, or the blank identifier. + if (!this->lhs_->is_addressable() + && this->lhs_->map_index_expression() == NULL + && !this->lhs_->is_sink_expression()) + { + if (!this->lhs_->type()->is_error()) + this->report_error(_("invalid left hand side of assignment")); + return; + } + + Type* lhs_type = this->lhs_->type(); + Type* rhs_type = this->rhs_->type(); + + // Invalid assignment of nil to the blank identifier. + if (lhs_type->is_sink_type() + && rhs_type->is_nil_type()) + { + this->report_error(_("use of untyped nil")); + return; + } + + std::string reason; + bool ok; + if (this->are_hidden_fields_ok_) + ok = Type::are_assignable_hidden_ok(lhs_type, rhs_type, &reason); + else + ok = Type::are_assignable(lhs_type, rhs_type, &reason); + if (!ok) + { + if (reason.empty()) + error_at(this->location(), "incompatible types in assignment"); + else + error_at(this->location(), "incompatible types in assignment (%s)", + reason.c_str()); + this->set_is_error(); + } + + if (lhs_type->is_error() || rhs_type->is_error()) + this->set_is_error(); +} + +// Convert an assignment statement to the backend representation. + +Bstatement* +Assignment_statement::do_get_backend(Translate_context* context) +{ + tree rhs_tree = this->rhs_->get_tree(context); + if (this->lhs_->is_sink_expression()) + return context->backend()->expression_statement(tree_to_expr(rhs_tree)); + tree lhs_tree = this->lhs_->get_tree(context); + rhs_tree = Expression::convert_for_assignment(context, this->lhs_->type(), + this->rhs_->type(), rhs_tree, + this->location()); + return context->backend()->assignment_statement(tree_to_expr(lhs_tree), + tree_to_expr(rhs_tree), + this->location()); +} + +// Dump the AST representation for an assignment statement. + +void +Assignment_statement::do_dump_statement(Ast_dump_context* ast_dump_context) + const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(this->lhs_); + ast_dump_context->ostream() << " = " ; + ast_dump_context->dump_expression(this->rhs_); + ast_dump_context->ostream() << std::endl; +} + +// Make an assignment statement. + +Statement* +Statement::make_assignment(Expression* lhs, Expression* rhs, + Location location) +{ + return new Assignment_statement(lhs, rhs, location); +} + +// The Move_subexpressions class is used to move all top-level +// subexpressions of an expression. This is used for things like +// index expressions in which we must evaluate the index value before +// it can be changed by a multiple assignment. + +class Move_subexpressions : public Traverse +{ + public: + Move_subexpressions(int skip, Block* block) + : Traverse(traverse_expressions), + skip_(skip), block_(block) + { } + + protected: + int + expression(Expression**); + + private: + // The number of subexpressions to skip moving. This is used to + // avoid moving the array itself, as we only need to move the index. + int skip_; + // The block where new temporary variables should be added. + Block* block_; +}; + +int +Move_subexpressions::expression(Expression** pexpr) +{ + if (this->skip_ > 0) + --this->skip_; + else if ((*pexpr)->temporary_reference_expression() == NULL) + { + Location loc = (*pexpr)->location(); + Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc); + this->block_->add_statement(temp); + *pexpr = Expression::make_temporary_reference(temp, loc); + } + // We only need to move top-level subexpressions. + return TRAVERSE_SKIP_COMPONENTS; +} + +// The Move_ordered_evals class is used to find any subexpressions of +// an expression that have an evaluation order dependency. It creates +// temporary variables to hold them. + +class Move_ordered_evals : public Traverse +{ + public: + Move_ordered_evals(Block* block) + : Traverse(traverse_expressions), + block_(block) + { } + + protected: + int + expression(Expression**); + + private: + // The block where new temporary variables should be added. + Block* block_; +}; + +int +Move_ordered_evals::expression(Expression** pexpr) +{ + // We have to look at subexpressions first. + if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + + int i; + if ((*pexpr)->must_eval_subexpressions_in_order(&i)) + { + Move_subexpressions ms(i, this->block_); + if ((*pexpr)->traverse_subexpressions(&ms) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + if ((*pexpr)->must_eval_in_order()) + { + Location loc = (*pexpr)->location(); + Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc); + this->block_->add_statement(temp); + *pexpr = Expression::make_temporary_reference(temp, loc); + } + return TRAVERSE_SKIP_COMPONENTS; +} + +// An assignment operation statement. + +class Assignment_operation_statement : public Statement +{ + public: + Assignment_operation_statement(Operator op, Expression* lhs, Expression* rhs, + Location location) + : Statement(STATEMENT_ASSIGNMENT_OPERATION, location), + op_(op), lhs_(lhs), rhs_(rhs) + { } + + protected: + int + do_traverse(Traverse*); + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The operator (OPERATOR_PLUSEQ, etc.). + Operator op_; + // Left hand side. + Expression* lhs_; + // Right hand side. + Expression* rhs_; +}; + +// Traversal. + +int +Assignment_operation_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->lhs_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->traverse_expression(traverse, &this->rhs_); +} + +// Lower an assignment operation statement to a regular assignment +// statement. + +Statement* +Assignment_operation_statement::do_lower(Gogo*, Named_object*, + Block* enclosing, Statement_inserter*) +{ + Location loc = this->location(); + + // We have to evaluate the left hand side expression only once. We + // do this by moving out any expression with side effects. + Block* b = new Block(enclosing, loc); + Move_ordered_evals moe(b); + this->lhs_->traverse_subexpressions(&moe); + + Expression* lval = this->lhs_->copy(); + + Operator op; + switch (this->op_) + { + case OPERATOR_PLUSEQ: + op = OPERATOR_PLUS; + break; + case OPERATOR_MINUSEQ: + op = OPERATOR_MINUS; + break; + case OPERATOR_OREQ: + op = OPERATOR_OR; + break; + case OPERATOR_XOREQ: + op = OPERATOR_XOR; + break; + case OPERATOR_MULTEQ: + op = OPERATOR_MULT; + break; + case OPERATOR_DIVEQ: + op = OPERATOR_DIV; + break; + case OPERATOR_MODEQ: + op = OPERATOR_MOD; + break; + case OPERATOR_LSHIFTEQ: + op = OPERATOR_LSHIFT; + break; + case OPERATOR_RSHIFTEQ: + op = OPERATOR_RSHIFT; + break; + case OPERATOR_ANDEQ: + op = OPERATOR_AND; + break; + case OPERATOR_BITCLEAREQ: + op = OPERATOR_BITCLEAR; + break; + default: + go_unreachable(); + } + + Expression* binop = Expression::make_binary(op, lval, this->rhs_, loc); + Statement* s = Statement::make_assignment(this->lhs_, binop, loc); + if (b->statements()->empty()) + { + delete b; + return s; + } + else + { + b->add_statement(s); + return Statement::make_block_statement(b, loc); + } +} + +// Dump the AST representation for an assignment operation statement + +void +Assignment_operation_statement::do_dump_statement( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(this->lhs_); + ast_dump_context->dump_operator(this->op_); + ast_dump_context->dump_expression(this->rhs_); + ast_dump_context->ostream() << std::endl; +} + +// Make an assignment operation statement. + +Statement* +Statement::make_assignment_operation(Operator op, Expression* lhs, + Expression* rhs, Location location) +{ + return new Assignment_operation_statement(op, lhs, rhs, location); +} + +// A tuple assignment statement. This differs from an assignment +// statement in that the right-hand-side expressions are evaluated in +// parallel. + +class Tuple_assignment_statement : public Statement +{ + public: + Tuple_assignment_statement(Expression_list* lhs, Expression_list* rhs, + Location location) + : Statement(STATEMENT_TUPLE_ASSIGNMENT, location), + lhs_(lhs), rhs_(rhs), are_hidden_fields_ok_(false) + { } + + // Note that it is OK for this assignment statement to set hidden + // fields. + void + set_hidden_fields_are_ok() + { this->are_hidden_fields_ok_ = true; } + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // Left hand side--a list of lvalues. + Expression_list* lhs_; + // Right hand side--a list of rvalues. + Expression_list* rhs_; + // True if this statement may set hidden fields in the assignment + // statement. This is used for generated method stubs. + bool are_hidden_fields_ok_; +}; + +// Traversal. + +int +Tuple_assignment_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression_list(traverse, this->lhs_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->traverse_expression_list(traverse, this->rhs_); +} + +// Lower a tuple assignment. We use temporary variables to split it +// up into a set of single assignments. + +Statement* +Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) +{ + Location loc = this->location(); + + Block* b = new Block(enclosing, loc); + + // First move out any subexpressions on the left hand side. The + // right hand side will be evaluated in the required order anyhow. + Move_ordered_evals moe(b); + for (Expression_list::iterator plhs = this->lhs_->begin(); + plhs != this->lhs_->end(); + ++plhs) + Expression::traverse(&*plhs, &moe); + + std::vector temps; + temps.reserve(this->lhs_->size()); + + Expression_list::const_iterator prhs = this->rhs_->begin(); + for (Expression_list::const_iterator plhs = this->lhs_->begin(); + plhs != this->lhs_->end(); + ++plhs, ++prhs) + { + go_assert(prhs != this->rhs_->end()); + + if ((*plhs)->is_error_expression() + || (*plhs)->type()->is_error() + || (*prhs)->is_error_expression() + || (*prhs)->type()->is_error()) + continue; + + if ((*plhs)->is_sink_expression()) + { + if ((*prhs)->type()->is_nil_type()) + this->report_error(_("use of untyped nil")); + else + b->add_statement(Statement::make_statement(*prhs, true)); + continue; + } + + Temporary_statement* temp = Statement::make_temporary((*plhs)->type(), + *prhs, loc); + if (this->are_hidden_fields_ok_) + temp->set_hidden_fields_are_ok(); + b->add_statement(temp); + temps.push_back(temp); + + } + go_assert(prhs == this->rhs_->end()); + + prhs = this->rhs_->begin(); + std::vector::const_iterator ptemp = temps.begin(); + for (Expression_list::const_iterator plhs = this->lhs_->begin(); + plhs != this->lhs_->end(); + ++plhs, ++prhs) + { + if ((*plhs)->is_error_expression() + || (*plhs)->type()->is_error() + || (*prhs)->is_error_expression() + || (*prhs)->type()->is_error()) + continue; + + if ((*plhs)->is_sink_expression()) + continue; + + Expression* ref = Expression::make_temporary_reference(*ptemp, loc); + Statement* s = Statement::make_assignment(*plhs, ref, loc); + if (this->are_hidden_fields_ok_) + { + Assignment_statement* as = static_cast(s); + as->set_hidden_fields_are_ok(); + } + b->add_statement(s); + ++ptemp; + } + go_assert(ptemp == temps.end() || saw_errors()); + + return Statement::make_block_statement(b, loc); +} + +// Dump the AST representation for a tuple assignment statement. + +void +Tuple_assignment_statement::do_dump_statement( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression_list(this->lhs_); + ast_dump_context->ostream() << " = "; + ast_dump_context->dump_expression_list(this->rhs_); + ast_dump_context->ostream() << std::endl; +} + +// Make a tuple assignment statement. + +Statement* +Statement::make_tuple_assignment(Expression_list* lhs, Expression_list* rhs, + Location location) +{ + return new Tuple_assignment_statement(lhs, rhs, location); +} + +// A tuple assignment from a map index expression. +// v, ok = m[k] + +class Tuple_map_assignment_statement : public Statement +{ +public: + Tuple_map_assignment_statement(Expression* val, Expression* present, + Expression* map_index, + Location location) + : Statement(STATEMENT_TUPLE_MAP_ASSIGNMENT, location), + val_(val), present_(present), map_index_(map_index) + { } + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // Lvalue which receives the value from the map. + Expression* val_; + // Lvalue which receives whether the key value was present. + Expression* present_; + // The map index expression. + Expression* map_index_; +}; + +// Traversal. + +int +Tuple_map_assignment_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT + || this->traverse_expression(traverse, &this->present_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->traverse_expression(traverse, &this->map_index_); +} + +// Lower a tuple map assignment. + +Statement* +Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*, + Block* enclosing, Statement_inserter*) +{ + Location loc = this->location(); + + Map_index_expression* map_index = this->map_index_->map_index_expression(); + if (map_index == NULL) + { + this->report_error(_("expected map index on right hand side")); + return Statement::make_error_statement(loc); + } + Map_type* map_type = map_index->get_map_type(); + if (map_type == NULL) + return Statement::make_error_statement(loc); + + Block* b = new Block(enclosing, loc); + + // Move out any subexpressions to make sure that functions are + // called in the required order. + Move_ordered_evals moe(b); + this->val_->traverse_subexpressions(&moe); + this->present_->traverse_subexpressions(&moe); + + // Copy the key value into a temporary so that we can take its + // address without pushing the value onto the heap. + + // var key_temp KEY_TYPE = MAP_INDEX + Temporary_statement* key_temp = + Statement::make_temporary(map_type->key_type(), map_index->index(), loc); + b->add_statement(key_temp); + + // var val_temp VAL_TYPE + Temporary_statement* val_temp = + Statement::make_temporary(map_type->val_type(), NULL, loc); + b->add_statement(val_temp); + + // var present_temp bool + Temporary_statement* present_temp = + Statement::make_temporary(Type::lookup_bool_type(), NULL, loc); + b->add_statement(present_temp); + + // present_temp = mapaccess2(DESCRIPTOR, MAP, &key_temp, &val_temp) + Expression* a1 = Expression::make_type_descriptor(map_type, loc); + Expression* a2 = map_index->map(); + Temporary_reference_expression* ref = + Expression::make_temporary_reference(key_temp, loc); + Expression* a3 = Expression::make_unary(OPERATOR_AND, ref, loc); + ref = Expression::make_temporary_reference(val_temp, loc); + Expression* a4 = Expression::make_unary(OPERATOR_AND, ref, loc); + Expression* call = Runtime::make_call(Runtime::MAPACCESS2, loc, 4, + a1, a2, a3, a4); + + ref = Expression::make_temporary_reference(present_temp, loc); + ref->set_is_lvalue(); + Statement* s = Statement::make_assignment(ref, call, loc); + b->add_statement(s); + + // val = val_temp + ref = Expression::make_temporary_reference(val_temp, loc); + s = Statement::make_assignment(this->val_, ref, loc); + b->add_statement(s); + + // present = present_temp + ref = Expression::make_temporary_reference(present_temp, loc); + s = Statement::make_assignment(this->present_, ref, loc); + b->add_statement(s); + + return Statement::make_block_statement(b, loc); +} + +// Dump the AST representation for a tuple map assignment statement. + +void +Tuple_map_assignment_statement::do_dump_statement( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(this->val_); + ast_dump_context->ostream() << ", "; + ast_dump_context->dump_expression(this->present_); + ast_dump_context->ostream() << " = "; + ast_dump_context->dump_expression(this->map_index_); + ast_dump_context->ostream() << std::endl; +} + +// Make a map assignment statement which returns a pair of values. + +Statement* +Statement::make_tuple_map_assignment(Expression* val, Expression* present, + Expression* map_index, + Location location) +{ + return new Tuple_map_assignment_statement(val, present, map_index, location); +} + +// Assign a pair of entries to a map. +// m[k] = v, p + +class Map_assignment_statement : public Statement +{ + public: + Map_assignment_statement(Expression* map_index, + Expression* val, Expression* should_set, + Location location) + : Statement(STATEMENT_MAP_ASSIGNMENT, location), + map_index_(map_index), val_(val), should_set_(should_set) + { } + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // A reference to the map index which should be set or deleted. + Expression* map_index_; + // The value to add to the map. + Expression* val_; + // Whether or not to add the value. + Expression* should_set_; +}; + +// Traverse a map assignment. + +int +Map_assignment_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->map_index_) == TRAVERSE_EXIT + || this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->traverse_expression(traverse, &this->should_set_); +} + +// Lower a map assignment to a function call. + +Statement* +Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) +{ + Location loc = this->location(); + + Map_index_expression* map_index = this->map_index_->map_index_expression(); + if (map_index == NULL) + { + this->report_error(_("expected map index on left hand side")); + return Statement::make_error_statement(loc); + } + Map_type* map_type = map_index->get_map_type(); + if (map_type == NULL) + return Statement::make_error_statement(loc); + + Block* b = new Block(enclosing, loc); + + // Evaluate the map first to get order of evaluation right. + // map_temp := m // we are evaluating m[k] = v, p + Temporary_statement* map_temp = Statement::make_temporary(map_type, + map_index->map(), + loc); + b->add_statement(map_temp); + + // var key_temp MAP_KEY_TYPE = k + Temporary_statement* key_temp = + Statement::make_temporary(map_type->key_type(), map_index->index(), loc); + b->add_statement(key_temp); + + // var val_temp MAP_VAL_TYPE = v + Temporary_statement* val_temp = + Statement::make_temporary(map_type->val_type(), this->val_, loc); + b->add_statement(val_temp); + + // var insert_temp bool = p + Temporary_statement* insert_temp = + Statement::make_temporary(Type::lookup_bool_type(), this->should_set_, + loc); + b->add_statement(insert_temp); + + // mapassign2(map_temp, &key_temp, &val_temp, p) + Expression* p1 = Expression::make_temporary_reference(map_temp, loc); + Expression* ref = Expression::make_temporary_reference(key_temp, loc); + Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc); + ref = Expression::make_temporary_reference(val_temp, loc); + Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc); + Expression* p4 = Expression::make_temporary_reference(insert_temp, loc); + Expression* call = Runtime::make_call(Runtime::MAPASSIGN2, loc, 4, + p1, p2, p3, p4); + Statement* s = Statement::make_statement(call, true); + b->add_statement(s); + + return Statement::make_block_statement(b, loc); +} + +// Dump the AST representation for a map assignment statement. + +void +Map_assignment_statement::do_dump_statement( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(this->map_index_); + ast_dump_context->ostream() << " = "; + ast_dump_context->dump_expression(this->val_); + ast_dump_context->ostream() << ", "; + ast_dump_context->dump_expression(this->should_set_); + ast_dump_context->ostream() << std::endl; +} + +// Make a statement which assigns a pair of entries to a map. + +Statement* +Statement::make_map_assignment(Expression* map_index, + Expression* val, Expression* should_set, + Location location) +{ + return new Map_assignment_statement(map_index, val, should_set, location); +} + +// A tuple assignment from a receive statement. + +class Tuple_receive_assignment_statement : public Statement +{ + public: + Tuple_receive_assignment_statement(Expression* val, Expression* closed, + Expression* channel, Location location) + : Statement(STATEMENT_TUPLE_RECEIVE_ASSIGNMENT, location), + val_(val), closed_(closed), channel_(channel) + { } + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // Lvalue which receives the value from the channel. + Expression* val_; + // Lvalue which receives whether the channel is closed. + Expression* closed_; + // The channel on which we receive the value. + Expression* channel_; +}; + +// Traversal. + +int +Tuple_receive_assignment_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT + || this->traverse_expression(traverse, &this->closed_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->traverse_expression(traverse, &this->channel_); +} + +// Lower to a function call. + +Statement* +Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*, + Block* enclosing, + Statement_inserter*) +{ + Location loc = this->location(); + + Channel_type* channel_type = this->channel_->type()->channel_type(); + if (channel_type == NULL) + { + this->report_error(_("expected channel")); + return Statement::make_error_statement(loc); + } + if (!channel_type->may_receive()) + { + this->report_error(_("invalid receive on send-only channel")); + return Statement::make_error_statement(loc); + } + + Block* b = new Block(enclosing, loc); + + // Make sure that any subexpressions on the left hand side are + // evaluated in the right order. + Move_ordered_evals moe(b); + this->val_->traverse_subexpressions(&moe); + this->closed_->traverse_subexpressions(&moe); + + // var val_temp ELEMENT_TYPE + Temporary_statement* val_temp = + Statement::make_temporary(channel_type->element_type(), NULL, loc); + b->add_statement(val_temp); + + // var closed_temp bool + Temporary_statement* closed_temp = + Statement::make_temporary(Type::lookup_bool_type(), NULL, loc); + b->add_statement(closed_temp); + + // closed_temp = chanrecv2(type, channel, &val_temp) + Expression* td = Expression::make_type_descriptor(this->channel_->type(), + loc); + Temporary_reference_expression* ref = + Expression::make_temporary_reference(val_temp, loc); + Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc); + Expression* call = Runtime::make_call(Runtime::CHANRECV2, + loc, 3, td, this->channel_, p2); + ref = Expression::make_temporary_reference(closed_temp, loc); + ref->set_is_lvalue(); + Statement* s = Statement::make_assignment(ref, call, loc); + b->add_statement(s); + + // val = val_temp + ref = Expression::make_temporary_reference(val_temp, loc); + s = Statement::make_assignment(this->val_, ref, loc); + b->add_statement(s); + + // closed = closed_temp + ref = Expression::make_temporary_reference(closed_temp, loc); + s = Statement::make_assignment(this->closed_, ref, loc); + b->add_statement(s); + + return Statement::make_block_statement(b, loc); +} + +// Dump the AST representation for a tuple receive statement. + +void +Tuple_receive_assignment_statement::do_dump_statement( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(this->val_); + ast_dump_context->ostream() << ", "; + ast_dump_context->dump_expression(this->closed_); + ast_dump_context->ostream() << " <- "; + ast_dump_context->dump_expression(this->channel_); + ast_dump_context->ostream() << std::endl; +} + +// Make a nonblocking receive statement. + +Statement* +Statement::make_tuple_receive_assignment(Expression* val, Expression* closed, + Expression* channel, + Location location) +{ + return new Tuple_receive_assignment_statement(val, closed, channel, + location); +} + +// An assignment to a pair of values from a type guard. This is a +// conditional type guard. v, ok = i.(type). + +class Tuple_type_guard_assignment_statement : public Statement +{ + public: + Tuple_type_guard_assignment_statement(Expression* val, Expression* ok, + Expression* expr, Type* type, + Location location) + : Statement(STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT, location), + val_(val), ok_(ok), expr_(expr), type_(type) + { } + + protected: + int + do_traverse(Traverse*); + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Call_expression* + lower_to_type(Runtime::Function); + + void + lower_to_object_type(Block*, Runtime::Function); + + // The variable which recieves the converted value. + Expression* val_; + // The variable which receives the indication of success. + Expression* ok_; + // The expression being converted. + Expression* expr_; + // The type to which the expression is being converted. + Type* type_; +}; + +// Traverse a type guard tuple assignment. + +int +Tuple_type_guard_assignment_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT + || this->traverse_expression(traverse, &this->ok_) == TRAVERSE_EXIT + || this->traverse_type(traverse, this->type_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->traverse_expression(traverse, &this->expr_); +} + +// Lower to a function call. + +Statement* +Tuple_type_guard_assignment_statement::do_lower(Gogo*, Named_object*, + Block* enclosing, + Statement_inserter*) +{ + Location loc = this->location(); + + Type* expr_type = this->expr_->type(); + if (expr_type->interface_type() == NULL) + { + if (!expr_type->is_error() && !this->type_->is_error()) + this->report_error(_("type assertion only valid for interface types")); + return Statement::make_error_statement(loc); + } + + Block* b = new Block(enclosing, loc); + + // Make sure that any subexpressions on the left hand side are + // evaluated in the right order. + Move_ordered_evals moe(b); + this->val_->traverse_subexpressions(&moe); + this->ok_->traverse_subexpressions(&moe); + + bool expr_is_empty = expr_type->interface_type()->is_empty(); + Call_expression* call; + if (this->type_->interface_type() != NULL) + { + if (this->type_->interface_type()->is_empty()) + call = Runtime::make_call((expr_is_empty + ? Runtime::IFACEE2E2 + : Runtime::IFACEI2E2), + loc, 1, this->expr_); + else + call = this->lower_to_type(expr_is_empty + ? Runtime::IFACEE2I2 + : Runtime::IFACEI2I2); + } + else if (this->type_->points_to() != NULL) + call = this->lower_to_type(expr_is_empty + ? Runtime::IFACEE2T2P + : Runtime::IFACEI2T2P); + else + { + this->lower_to_object_type(b, + (expr_is_empty + ? Runtime::IFACEE2T2 + : Runtime::IFACEI2T2)); + call = NULL; + } + + if (call != NULL) + { + Expression* res = Expression::make_call_result(call, 0); + res = Expression::make_unsafe_cast(this->type_, res, loc); + Statement* s = Statement::make_assignment(this->val_, res, loc); + b->add_statement(s); + + res = Expression::make_call_result(call, 1); + s = Statement::make_assignment(this->ok_, res, loc); + b->add_statement(s); + } + + return Statement::make_block_statement(b, loc); +} + +// Lower a conversion to a non-empty interface type or a pointer type. + +Call_expression* +Tuple_type_guard_assignment_statement::lower_to_type(Runtime::Function code) +{ + Location loc = this->location(); + return Runtime::make_call(code, loc, 2, + Expression::make_type_descriptor(this->type_, loc), + this->expr_); +} + +// Lower a conversion to a non-interface non-pointer type. + +void +Tuple_type_guard_assignment_statement::lower_to_object_type( + Block* b, + Runtime::Function code) +{ + Location loc = this->location(); + + // var val_temp TYPE + Temporary_statement* val_temp = Statement::make_temporary(this->type_, + NULL, loc); + b->add_statement(val_temp); + + // ok = CODE(type_descriptor, expr, &val_temp) + Expression* p1 = Expression::make_type_descriptor(this->type_, loc); + Expression* ref = Expression::make_temporary_reference(val_temp, loc); + Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc); + Expression* call = Runtime::make_call(code, loc, 3, p1, this->expr_, p3); + Statement* s = Statement::make_assignment(this->ok_, call, loc); + b->add_statement(s); + + // val = val_temp + ref = Expression::make_temporary_reference(val_temp, loc); + s = Statement::make_assignment(this->val_, ref, loc); + b->add_statement(s); +} + +// Dump the AST representation for a tuple type guard statement. + +void +Tuple_type_guard_assignment_statement::do_dump_statement( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(this->val_); + ast_dump_context->ostream() << ", "; + ast_dump_context->dump_expression(this->ok_); + ast_dump_context->ostream() << " = "; + ast_dump_context->dump_expression(this->expr_); + ast_dump_context->ostream() << " . "; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << std::endl; +} + +// Make an assignment from a type guard to a pair of variables. + +Statement* +Statement::make_tuple_type_guard_assignment(Expression* val, Expression* ok, + Expression* expr, Type* type, + Location location) +{ + return new Tuple_type_guard_assignment_statement(val, ok, expr, type, + location); +} + +// Class Expression_statement. + +// Constructor. + +Expression_statement::Expression_statement(Expression* expr, bool is_ignored) + : Statement(STATEMENT_EXPRESSION, expr->location()), + expr_(expr), is_ignored_(is_ignored) +{ +} + +// Determine types. + +void +Expression_statement::do_determine_types() +{ + this->expr_->determine_type_no_context(); +} + +// Check the types of an expression statement. The only check we do +// is to possibly give an error about discarding the value of the +// expression. + +void +Expression_statement::do_check_types(Gogo*) +{ + if (!this->is_ignored_) + this->expr_->discarding_value(); +} + +// An expression statement is only a terminating statement if it is +// a call to panic. + +bool +Expression_statement::do_may_fall_through() const +{ + const Call_expression* call = this->expr_->call_expression(); + if (call != NULL) + { + const Expression* fn = call->fn(); + // panic is still an unknown named object. + const Unknown_expression* ue = fn->unknown_expression(); + if (ue != NULL) + { + Named_object* no = ue->named_object(); + + if (no->is_unknown()) + no = no->unknown_value()->real_named_object(); + if (no != NULL) + { + Function_type* fntype; + if (no->is_function()) + fntype = no->func_value()->type(); + else if (no->is_function_declaration()) + fntype = no->func_declaration_value()->type(); + else + fntype = NULL; + + // The builtin function panic does not return. + if (fntype != NULL && fntype->is_builtin() && no->name() == "panic") + return false; + } + } + } + return true; +} + +// Convert to backend representation. + +Bstatement* +Expression_statement::do_get_backend(Translate_context* context) +{ + tree expr_tree = this->expr_->get_tree(context); + return context->backend()->expression_statement(tree_to_expr(expr_tree)); +} + +// Dump the AST representation for an expression statement + +void +Expression_statement::do_dump_statement(Ast_dump_context* ast_dump_context) + const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(expr_); + ast_dump_context->ostream() << std::endl; +} + +// Make an expression statement from an Expression. + +Statement* +Statement::make_statement(Expression* expr, bool is_ignored) +{ + return new Expression_statement(expr, is_ignored); +} + +// A block statement--a list of statements which may include variable +// definitions. + +class Block_statement : public Statement +{ + public: + Block_statement(Block* block, Location location) + : Statement(STATEMENT_BLOCK, location), + block_(block) + { } + + protected: + int + do_traverse(Traverse* traverse) + { return this->block_->traverse(traverse); } + + void + do_determine_types() + { this->block_->determine_types(); } + + bool + do_may_fall_through() const + { return this->block_->may_fall_through(); } + + Bstatement* + do_get_backend(Translate_context* context); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Block* block_; +}; + +// Convert a block to the backend representation of a statement. + +Bstatement* +Block_statement::do_get_backend(Translate_context* context) +{ + Bblock* bblock = this->block_->get_backend(context); + return context->backend()->block_statement(bblock); +} + +// Dump the AST for a block statement + +void +Block_statement::do_dump_statement(Ast_dump_context*) const +{ + // block statement braces are dumped when traversing. +} + +// Make a block statement. + +Statement* +Statement::make_block_statement(Block* block, Location location) +{ + return new Block_statement(block, location); +} + +// An increment or decrement statement. + +class Inc_dec_statement : public Statement +{ + public: + Inc_dec_statement(bool is_inc, Expression* expr) + : Statement(STATEMENT_INCDEC, expr->location()), + expr_(expr), is_inc_(is_inc) + { } + + protected: + int + do_traverse(Traverse* traverse) + { return this->traverse_expression(traverse, &this->expr_); } + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The l-value to increment or decrement. + Expression* expr_; + // Whether to increment or decrement. + bool is_inc_; +}; + +// Lower to += or -=. + +Statement* +Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*, Statement_inserter*) +{ + Location loc = this->location(); + + mpz_t oval; + mpz_init_set_ui(oval, 1UL); + Expression* oexpr = Expression::make_integer(&oval, NULL, loc); + mpz_clear(oval); + + Operator op = this->is_inc_ ? OPERATOR_PLUSEQ : OPERATOR_MINUSEQ; + return Statement::make_assignment_operation(op, this->expr_, oexpr, loc); +} + +// Dump the AST representation for a inc/dec statement. + +void +Inc_dec_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(expr_); + ast_dump_context->ostream() << (is_inc_? "++": "--") << std::endl; +} + +// Make an increment statement. + +Statement* +Statement::make_inc_statement(Expression* expr) +{ + return new Inc_dec_statement(true, expr); +} + +// Make a decrement statement. + +Statement* +Statement::make_dec_statement(Expression* expr) +{ + return new Inc_dec_statement(false, expr); +} + +// Class Thunk_statement. This is the base class for go and defer +// statements. + +// Constructor. + +Thunk_statement::Thunk_statement(Statement_classification classification, + Call_expression* call, + Location location) + : Statement(classification, location), + call_(call), struct_type_(NULL) +{ +} + +// Return whether this is a simple statement which does not require a +// thunk. + +bool +Thunk_statement::is_simple(Function_type* fntype) const +{ + // We need a thunk to call a method, or to pass a variable number of + // arguments. + if (fntype->is_method() || fntype->is_varargs()) + return false; + + // A defer statement requires a thunk to set up for whether the + // function can call recover. + if (this->classification() == STATEMENT_DEFER) + return false; + + // We can only permit a single parameter of pointer type. + const Typed_identifier_list* parameters = fntype->parameters(); + if (parameters != NULL + && (parameters->size() > 1 + || (parameters->size() == 1 + && parameters->begin()->type()->points_to() == NULL))) + return false; + + // If the function returns multiple values, or returns a type other + // than integer, floating point, or pointer, then it may get a + // hidden first parameter, in which case we need the more + // complicated approach. This is true even though we are going to + // ignore the return value. + const Typed_identifier_list* results = fntype->results(); + if (results != NULL + && (results->size() > 1 + || (results->size() == 1 + && !results->begin()->type()->is_basic_type() + && results->begin()->type()->points_to() == NULL))) + return false; + + // If this calls something that is not a simple function, then we + // need a thunk. + Expression* fn = this->call_->call_expression()->fn(); + if (fn->func_expression() == NULL) + return false; + + // If the function uses a closure, then we need a thunk. FIXME: We + // could accept a zero argument function with a closure. + if (fn->func_expression()->closure() != NULL) + return false; + + return true; +} + +// Traverse a thunk statement. + +int +Thunk_statement::do_traverse(Traverse* traverse) +{ + return this->traverse_expression(traverse, &this->call_); +} + +// We implement traverse_assignment for a thunk statement because it +// effectively copies the function call. + +bool +Thunk_statement::do_traverse_assignments(Traverse_assignments* tassign) +{ + Expression* fn = this->call_->call_expression()->fn(); + Expression* fn2 = fn; + tassign->value(&fn2, true, false); + return true; +} + +// Determine types in a thunk statement. + +void +Thunk_statement::do_determine_types() +{ + this->call_->determine_type_no_context(); + + // Now that we know the types of the call, build the struct used to + // pass parameters. + Call_expression* ce = this->call_->call_expression(); + if (ce == NULL) + return; + Function_type* fntype = ce->get_function_type(); + if (fntype != NULL && !this->is_simple(fntype)) + this->struct_type_ = this->build_struct(fntype); +} + +// Check types in a thunk statement. + +void +Thunk_statement::do_check_types(Gogo*) +{ + if (!this->call_->discarding_value()) + return; + Call_expression* ce = this->call_->call_expression(); + if (ce == NULL) + { + if (!this->call_->is_error_expression()) + this->report_error("expected call expression"); + return; + } +} + +// The Traverse class used to find and simplify thunk statements. + +class Simplify_thunk_traverse : public Traverse +{ + public: + Simplify_thunk_traverse(Gogo* gogo) + : Traverse(traverse_functions | traverse_blocks), + gogo_(gogo), function_(NULL) + { } + + int + function(Named_object*); + + int + block(Block*); + + private: + // General IR. + Gogo* gogo_; + // The function we are traversing. + Named_object* function_; +}; + +// Keep track of the current function while looking for thunks. + +int +Simplify_thunk_traverse::function(Named_object* no) +{ + go_assert(this->function_ == NULL); + this->function_ = no; + int t = no->func_value()->traverse(this); + this->function_ = NULL; + if (t == TRAVERSE_EXIT) + return t; + return TRAVERSE_SKIP_COMPONENTS; +} + +// Look for thunks in a block. + +int +Simplify_thunk_traverse::block(Block* b) +{ + // The parser ensures that thunk statements always appear at the end + // of a block. + if (b->statements()->size() < 1) + return TRAVERSE_CONTINUE; + Thunk_statement* stat = b->statements()->back()->thunk_statement(); + if (stat == NULL) + return TRAVERSE_CONTINUE; + if (stat->simplify_statement(this->gogo_, this->function_, b)) + return TRAVERSE_SKIP_COMPONENTS; + return TRAVERSE_CONTINUE; +} + +// Simplify all thunk statements. + +void +Gogo::simplify_thunk_statements() +{ + Simplify_thunk_traverse thunk_traverse(this); + this->traverse(&thunk_traverse); +} + +// Return true if the thunk function is a constant, which means that +// it does not need to be passed to the thunk routine. + +bool +Thunk_statement::is_constant_function() const +{ + Call_expression* ce = this->call_->call_expression(); + Function_type* fntype = ce->get_function_type(); + if (fntype == NULL) + { + go_assert(saw_errors()); + return false; + } + if (fntype->is_builtin()) + return true; + Expression* fn = ce->fn(); + if (fn->func_expression() != NULL) + return fn->func_expression()->closure() == NULL; + if (fn->interface_field_reference_expression() != NULL) + return true; + return false; +} + +// Simplify complex thunk statements into simple ones. A complicated +// thunk statement is one which takes anything other than zero +// parameters or a single pointer parameter. We rewrite it into code +// which allocates a struct, stores the parameter values into the +// struct, and does a simple go or defer statement which passes the +// struct to a thunk. The thunk does the real call. + +bool +Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function, + Block* block) +{ + if (this->classification() == STATEMENT_ERROR) + return false; + if (this->call_->is_error_expression()) + return false; + + if (this->classification() == STATEMENT_DEFER) + { + // Make sure that the defer stack exists for the function. We + // will use when converting this statement to the backend + // representation, but we want it to exist when we start + // converting the function. + function->func_value()->defer_stack(this->location()); + } + + Call_expression* ce = this->call_->call_expression(); + Function_type* fntype = ce->get_function_type(); + if (fntype == NULL) + { + go_assert(saw_errors()); + this->set_is_error(); + return false; + } + if (this->is_simple(fntype)) + return false; + + Expression* fn = ce->fn(); + Interface_field_reference_expression* interface_method = + fn->interface_field_reference_expression(); + + Location location = this->location(); + + std::string thunk_name = Gogo::thunk_name(); + + // Build the thunk. + this->build_thunk(gogo, thunk_name); + + // Generate code to call the thunk. + + // Get the values to store into the struct which is the single + // argument to the thunk. + + Expression_list* vals = new Expression_list(); + if (!this->is_constant_function()) + vals->push_back(fn); + + if (interface_method != NULL) + vals->push_back(interface_method->expr()); + + if (ce->args() != NULL) + { + for (Expression_list::const_iterator p = ce->args()->begin(); + p != ce->args()->end(); + ++p) + vals->push_back(*p); + } + + // Build the struct. + Expression* constructor = + Expression::make_struct_composite_literal(this->struct_type_, vals, + location); + + // Allocate the initialized struct on the heap. + constructor = Expression::make_heap_composite(constructor, location); + + // Look up the thunk. + Named_object* named_thunk = gogo->lookup(thunk_name, NULL); + go_assert(named_thunk != NULL && named_thunk->is_function()); + + // Build the call. + Expression* func = Expression::make_func_reference(named_thunk, NULL, + location); + Expression_list* params = new Expression_list(); + params->push_back(constructor); + Call_expression* call = Expression::make_call(func, params, false, location); + + // Build the simple go or defer statement. + Statement* s; + if (this->classification() == STATEMENT_GO) + s = Statement::make_go_statement(call, location); + else if (this->classification() == STATEMENT_DEFER) + s = Statement::make_defer_statement(call, location); + else + go_unreachable(); + + // The current block should end with the go statement. + go_assert(block->statements()->size() >= 1); + go_assert(block->statements()->back() == this); + block->replace_statement(block->statements()->size() - 1, s); + + // We already ran the determine_types pass, so we need to run it now + // for the new statement. + s->determine_types(); + + // Sanity check. + gogo->check_types_in_block(block); + + // Return true to tell the block not to keep looking at statements. + return true; +} + +// Set the name to use for thunk parameter N. + +void +Thunk_statement::thunk_field_param(int n, char* buf, size_t buflen) +{ + snprintf(buf, buflen, "a%d", n); +} + +// Build a new struct type to hold the parameters for a complicated +// thunk statement. FNTYPE is the type of the function call. + +Struct_type* +Thunk_statement::build_struct(Function_type* fntype) +{ + Location location = this->location(); + + Struct_field_list* fields = new Struct_field_list(); + + Call_expression* ce = this->call_->call_expression(); + Expression* fn = ce->fn(); + + if (!this->is_constant_function()) + { + // The function to call. + fields->push_back(Struct_field(Typed_identifier("fn", fntype, + location))); + } + + // If this thunk statement calls a method on an interface, we pass + // the interface object to the thunk. + Interface_field_reference_expression* interface_method = + fn->interface_field_reference_expression(); + if (interface_method != NULL) + { + Typed_identifier tid("object", interface_method->expr()->type(), + location); + fields->push_back(Struct_field(tid)); + } + + // The predeclared recover function has no argument. However, we + // add an argument when building recover thunks. Handle that here. + if (ce->is_recover_call()) + { + fields->push_back(Struct_field(Typed_identifier("can_recover", + Type::lookup_bool_type(), + location))); + } + + const Expression_list* args = ce->args(); + if (args != NULL) + { + int i = 0; + for (Expression_list::const_iterator p = args->begin(); + p != args->end(); + ++p, ++i) + { + char buf[50]; + this->thunk_field_param(i, buf, sizeof buf); + fields->push_back(Struct_field(Typed_identifier(buf, (*p)->type(), + location))); + } + } + + return Type::make_struct_type(fields, location); +} + +// Build the thunk we are going to call. This is a brand new, albeit +// artificial, function. + +void +Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) +{ + Location location = this->location(); + + Call_expression* ce = this->call_->call_expression(); + + bool may_call_recover = false; + if (this->classification() == STATEMENT_DEFER) + { + Func_expression* fn = ce->fn()->func_expression(); + if (fn == NULL) + may_call_recover = true; + else + { + const Named_object* no = fn->named_object(); + if (!no->is_function()) + may_call_recover = true; + else + may_call_recover = no->func_value()->calls_recover(); + } + } + + // Build the type of the thunk. The thunk takes a single parameter, + // which is a pointer to the special structure we build. + const char* const parameter_name = "__go_thunk_parameter"; + Typed_identifier_list* thunk_parameters = new Typed_identifier_list(); + Type* pointer_to_struct_type = Type::make_pointer_type(this->struct_type_); + thunk_parameters->push_back(Typed_identifier(parameter_name, + pointer_to_struct_type, + location)); + + Typed_identifier_list* thunk_results = NULL; + if (may_call_recover) + { + // When deferring a function which may call recover, add a + // return value, to disable tail call optimizations which will + // break the way we check whether recover is permitted. + thunk_results = new Typed_identifier_list(); + thunk_results->push_back(Typed_identifier("", Type::lookup_bool_type(), + location)); + } + + Function_type* thunk_type = Type::make_function_type(NULL, thunk_parameters, + thunk_results, + location); + + // Start building the thunk. + Named_object* function = gogo->start_function(thunk_name, thunk_type, true, + location); + + gogo->start_block(location); + + // For a defer statement, start with a call to + // __go_set_defer_retaddr. */ + Label* retaddr_label = NULL; + if (may_call_recover) + { + retaddr_label = gogo->add_label_reference("retaddr", location, false); + Expression* arg = Expression::make_label_addr(retaddr_label, location); + Expression* call = Runtime::make_call(Runtime::SET_DEFER_RETADDR, + location, 1, arg); + + // This is a hack to prevent the middle-end from deleting the + // label. + gogo->start_block(location); + gogo->add_statement(Statement::make_goto_statement(retaddr_label, + location)); + Block* then_block = gogo->finish_block(location); + then_block->determine_types(); + + Statement* s = Statement::make_if_statement(call, then_block, NULL, + location); + s->determine_types(); + gogo->add_statement(s); + } + + // Get a reference to the parameter. + Named_object* named_parameter = gogo->lookup(parameter_name, NULL); + go_assert(named_parameter != NULL && named_parameter->is_variable()); + + // Build the call. Note that the field names are the same as the + // ones used in build_struct. + Expression* thunk_parameter = Expression::make_var_reference(named_parameter, + location); + thunk_parameter = Expression::make_unary(OPERATOR_MULT, thunk_parameter, + location); + + Interface_field_reference_expression* interface_method = + ce->fn()->interface_field_reference_expression(); + + Expression* func_to_call; + unsigned int next_index; + if (this->is_constant_function()) + { + func_to_call = ce->fn(); + next_index = 0; + } + else + { + func_to_call = Expression::make_field_reference(thunk_parameter, + 0, location); + next_index = 1; + } + + if (interface_method != NULL) + { + // The main program passes the interface object. + go_assert(next_index == 0); + Expression* r = Expression::make_field_reference(thunk_parameter, 0, + location); + const std::string& name(interface_method->name()); + func_to_call = Expression::make_interface_field_reference(r, name, + location); + next_index = 1; + } + + Expression_list* call_params = new Expression_list(); + const Struct_field_list* fields = this->struct_type_->fields(); + Struct_field_list::const_iterator p = fields->begin(); + for (unsigned int i = 0; i < next_index; ++i) + ++p; + bool is_recover_call = ce->is_recover_call(); + Expression* recover_arg = NULL; + for (; p != fields->end(); ++p, ++next_index) + { + Expression* thunk_param = Expression::make_var_reference(named_parameter, + location); + thunk_param = Expression::make_unary(OPERATOR_MULT, thunk_param, + location); + Expression* param = Expression::make_field_reference(thunk_param, + next_index, + location); + if (!is_recover_call) + call_params->push_back(param); + else + { + go_assert(call_params->empty()); + recover_arg = param; + } + } + + if (call_params->empty()) + { + delete call_params; + call_params = NULL; + } + + Call_expression* call = Expression::make_call(func_to_call, call_params, + false, location); + + // This call expression was already lowered before entering the + // thunk statement. Don't try to lower varargs again, as that will + // cause confusion for, e.g., method calls which already have a + // receiver parameter. + call->set_varargs_are_lowered(); + + Statement* call_statement = Statement::make_statement(call, true); + + gogo->add_statement(call_statement); + + // If this is a defer statement, the label comes immediately after + // the call. + if (may_call_recover) + { + gogo->add_label_definition("retaddr", location); + + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_boolean(false, location)); + gogo->add_statement(Statement::make_return_statement(vals, location)); + } + + Block* b = gogo->finish_block(location); + + gogo->add_block(b, location); + + gogo->lower_block(function, b); + gogo->flatten_block(function, b); + + // We already ran the determine_types pass, so we need to run it + // just for the call statement now. The other types are known. + call_statement->determine_types(); + + if (may_call_recover || recover_arg != NULL) + { + // Dig up the call expression, which may have been changed + // during lowering. + go_assert(call_statement->classification() == STATEMENT_EXPRESSION); + Expression_statement* es = + static_cast(call_statement); + Call_expression* ce = es->expr()->call_expression(); + 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. + gogo->finish_function(location); +} + +// Get the function and argument expressions. + +bool +Thunk_statement::get_fn_and_arg(Expression** pfn, Expression** parg) +{ + if (this->call_->is_error_expression()) + return false; + + Call_expression* ce = this->call_->call_expression(); + + Expression* fn = ce->fn(); + Func_expression* fe = fn->func_expression(); + go_assert(fe != NULL); + *pfn = Expression::make_func_code_reference(fe->named_object(), + fe->location()); + + const Expression_list* args = ce->args(); + if (args == NULL || args->empty()) + *parg = Expression::make_nil(this->location()); + else + { + go_assert(args->size() == 1); + *parg = args->front(); + } + + return true; +} + +// Class Go_statement. + +Bstatement* +Go_statement::do_get_backend(Translate_context* context) +{ + Expression* fn; + Expression* arg; + if (!this->get_fn_and_arg(&fn, &arg)) + return context->backend()->error_statement(); + + Expression* call = Runtime::make_call(Runtime::GO, this->location(), 2, + fn, arg); + tree call_tree = call->get_tree(context); + Bexpression* call_bexpr = tree_to_expr(call_tree); + return context->backend()->expression_statement(call_bexpr); +} + +// Dump the AST representation for go statement. + +void +Go_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "go "; + ast_dump_context->dump_expression(this->call()); + ast_dump_context->ostream() << std::endl; +} + +// Make a go statement. + +Statement* +Statement::make_go_statement(Call_expression* call, Location location) +{ + return new Go_statement(call, location); +} + +// Class Defer_statement. + +Bstatement* +Defer_statement::do_get_backend(Translate_context* context) +{ + Expression* fn; + Expression* arg; + if (!this->get_fn_and_arg(&fn, &arg)) + return context->backend()->error_statement(); + + Location loc = this->location(); + Expression* ds = context->function()->func_value()->defer_stack(loc); + + Expression* call = Runtime::make_call(Runtime::DEFER, loc, 3, + ds, fn, arg); + tree call_tree = call->get_tree(context); + Bexpression* call_bexpr = tree_to_expr(call_tree); + return context->backend()->expression_statement(call_bexpr); +} + +// Dump the AST representation for defer statement. + +void +Defer_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "defer "; + ast_dump_context->dump_expression(this->call()); + ast_dump_context->ostream() << std::endl; +} + +// Make a defer statement. + +Statement* +Statement::make_defer_statement(Call_expression* call, + Location location) +{ + return new Defer_statement(call, location); +} + +// Class Return_statement. + +// Traverse assignments. We treat each return value as a top level +// RHS in an expression. + +bool +Return_statement::do_traverse_assignments(Traverse_assignments* tassign) +{ + Expression_list* vals = this->vals_; + if (vals != NULL) + { + for (Expression_list::iterator p = vals->begin(); + p != vals->end(); + ++p) + tassign->value(&*p, true, true); + } + return true; +} + +// Lower a return statement. If we are returning a function call +// which returns multiple values which match the current function, +// split up the call's results. If the return statement lists +// explicit values, implement this statement by assigning the values +// to the result variables and change this statement to a naked +// return. This lets panic/recover work correctly. + +Statement* +Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing, + Statement_inserter*) +{ + if (this->is_lowered_) + return this; + + Expression_list* vals = this->vals_; + this->vals_ = NULL; + this->is_lowered_ = true; + + Location loc = this->location(); + + size_t vals_count = vals == NULL ? 0 : vals->size(); + Function::Results* results = function->func_value()->result_variables(); + size_t results_count = results == NULL ? 0 : results->size(); + + if (vals_count == 0) + { + if (results_count > 0 && !function->func_value()->results_are_named()) + { + this->report_error(_("not enough arguments to return")); + return this; + } + return this; + } + + if (results_count == 0) + { + this->report_error(_("return with value in function " + "with no return type")); + return this; + } + + // If the current function has multiple return values, and we are + // returning a single call expression, split up the call expression. + if (results_count > 1 + && vals->size() == 1 + && vals->front()->call_expression() != NULL) + { + Call_expression* call = vals->front()->call_expression(); + delete vals; + vals = new Expression_list; + for (size_t i = 0; i < results_count; ++i) + vals->push_back(Expression::make_call_result(call, i)); + vals_count = results_count; + } + + if (vals_count < results_count) + { + this->report_error(_("not enough arguments to return")); + return this; + } + + if (vals_count > results_count) + { + this->report_error(_("too many values in return statement")); + return this; + } + + Block* b = new Block(enclosing, loc); + + Expression_list* lhs = new Expression_list(); + Expression_list* rhs = new Expression_list(); + + Expression_list::const_iterator pe = vals->begin(); + int i = 1; + for (Function::Results::const_iterator pr = results->begin(); + pr != results->end(); + ++pr, ++pe, ++i) + { + Named_object* rv = *pr; + Expression* e = *pe; + + // Check types now so that we give a good error message. The + // result type is known. We determine the expression type + // early. + + Type *rvtype = rv->result_var_value()->type(); + Type_context type_context(rvtype, false); + e->determine_type(&type_context); + + std::string reason; + bool ok; + if (this->are_hidden_fields_ok_) + ok = Type::are_assignable_hidden_ok(rvtype, e->type(), &reason); + else + ok = Type::are_assignable(rvtype, e->type(), &reason); + if (ok) + { + Expression* ve = Expression::make_var_reference(rv, e->location()); + lhs->push_back(ve); + rhs->push_back(e); + } + else + { + if (reason.empty()) + error_at(e->location(), "incompatible type for return value %d", i); + else + error_at(e->location(), + "incompatible type for return value %d (%s)", + i, reason.c_str()); + } + } + go_assert(lhs->size() == rhs->size()); + + if (lhs->empty()) + ; + else if (lhs->size() == 1) + { + Statement* s = Statement::make_assignment(lhs->front(), rhs->front(), + loc); + if (this->are_hidden_fields_ok_) + { + Assignment_statement* as = static_cast(s); + as->set_hidden_fields_are_ok(); + } + b->add_statement(s); + delete lhs; + delete rhs; + } + else + { + Statement* s = Statement::make_tuple_assignment(lhs, rhs, loc); + if (this->are_hidden_fields_ok_) + { + Tuple_assignment_statement* tas = + static_cast(s); + tas->set_hidden_fields_are_ok(); + } + b->add_statement(s); + } + + b->add_statement(this); + + delete vals; + + return Statement::make_block_statement(b, loc); +} + +// Convert a return statement to the backend representation. + +Bstatement* +Return_statement::do_get_backend(Translate_context* context) +{ + Location loc = this->location(); + + Function* function = context->function()->func_value(); + tree fndecl = function->get_decl(); + + Function::Results* results = function->result_variables(); + std::vector retvals; + if (results != NULL && !results->empty()) + { + retvals.reserve(results->size()); + for (Function::Results::const_iterator p = results->begin(); + p != results->end(); + p++) + { + Expression* vr = Expression::make_var_reference(*p, loc); + retvals.push_back(tree_to_expr(vr->get_tree(context))); + } + } + + return context->backend()->return_statement(tree_to_function(fndecl), + retvals, loc); +} + +// Dump the AST representation for a return statement. + +void +Return_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "return " ; + ast_dump_context->dump_expression_list(this->vals_); + ast_dump_context->ostream() << std::endl; +} + +// Make a return statement. + +Return_statement* +Statement::make_return_statement(Expression_list* vals, + Location location) +{ + return new Return_statement(vals, location); +} + +// Make a statement that returns the result of a call expression. + +Statement* +Statement::make_return_from_call(Call_expression* call, Location location) +{ + size_t rc = call->result_count(); + if (rc == 0) + return Statement::make_statement(call, true); + else + { + Expression_list* vals = new Expression_list(); + if (rc == 1) + vals->push_back(call); + else + { + for (size_t i = 0; i < rc; ++i) + vals->push_back(Expression::make_call_result(call, i)); + } + return Statement::make_return_statement(vals, location); + } +} + +// A break or continue statement. + +class Bc_statement : public Statement +{ + public: + Bc_statement(bool is_break, Unnamed_label* label, Location location) + : Statement(STATEMENT_BREAK_OR_CONTINUE, location), + label_(label), is_break_(is_break) + { } + + bool + is_break() const + { return this->is_break_; } + + protected: + int + do_traverse(Traverse*) + { return TRAVERSE_CONTINUE; } + + bool + do_may_fall_through() const + { return false; } + + Bstatement* + do_get_backend(Translate_context* context) + { return this->label_->get_goto(context, this->location()); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The label that this branches to. + Unnamed_label* label_; + // True if this is "break", false if it is "continue". + bool is_break_; +}; + +// Dump the AST representation for a break/continue statement + +void +Bc_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << (this->is_break_ ? "break" : "continue"); + if (this->label_ != NULL) + { + ast_dump_context->ostream() << " "; + ast_dump_context->dump_label_name(this->label_); + } + ast_dump_context->ostream() << std::endl; +} + +// Make a break statement. + +Statement* +Statement::make_break_statement(Unnamed_label* label, Location location) +{ + return new Bc_statement(true, label, location); +} + +// Make a continue statement. + +Statement* +Statement::make_continue_statement(Unnamed_label* label, + Location location) +{ + return new Bc_statement(false, label, location); +} + +// A goto statement. + +class Goto_statement : public Statement +{ + public: + Goto_statement(Label* label, Location location) + : Statement(STATEMENT_GOTO, location), + label_(label) + { } + + protected: + int + do_traverse(Traverse*) + { return TRAVERSE_CONTINUE; } + + void + do_check_types(Gogo*); + + bool + do_may_fall_through() const + { return false; } + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Label* label_; +}; + +// Check types for a label. There aren't any types per se, but we use +// this to give an error if the label was never defined. + +void +Goto_statement::do_check_types(Gogo*) +{ + if (!this->label_->is_defined()) + { + error_at(this->location(), "reference to undefined label %qs", + Gogo::message_name(this->label_->name()).c_str()); + this->set_is_error(); + } +} + +// Convert the goto statement to the backend representation. + +Bstatement* +Goto_statement::do_get_backend(Translate_context* context) +{ + Blabel* blabel = this->label_->get_backend_label(context); + return context->backend()->goto_statement(blabel, this->location()); +} + +// Dump the AST representation for a goto statement. + +void +Goto_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "goto " << this->label_->name() << std::endl; +} + +// Make a goto statement. + +Statement* +Statement::make_goto_statement(Label* label, Location location) +{ + return new Goto_statement(label, location); +} + +// A goto statement to an unnamed label. + +class Goto_unnamed_statement : public Statement +{ + public: + Goto_unnamed_statement(Unnamed_label* label, Location location) + : Statement(STATEMENT_GOTO_UNNAMED, location), + label_(label) + { } + + protected: + int + do_traverse(Traverse*) + { return TRAVERSE_CONTINUE; } + + bool + do_may_fall_through() const + { return false; } + + Bstatement* + do_get_backend(Translate_context* context) + { return this->label_->get_goto(context, this->location()); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Unnamed_label* label_; +}; + +// Dump the AST representation for an unnamed goto statement + +void +Goto_unnamed_statement::do_dump_statement( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "goto "; + ast_dump_context->dump_label_name(this->label_); + ast_dump_context->ostream() << std::endl; +} + +// Make a goto statement to an unnamed label. + +Statement* +Statement::make_goto_unnamed_statement(Unnamed_label* label, + Location location) +{ + return new Goto_unnamed_statement(label, location); +} + +// Class Label_statement. + +// Traversal. + +int +Label_statement::do_traverse(Traverse*) +{ + return TRAVERSE_CONTINUE; +} + +// Return the backend representation of the statement defining this +// label. + +Bstatement* +Label_statement::do_get_backend(Translate_context* context) +{ + Blabel* blabel = this->label_->get_backend_label(context); + return context->backend()->label_definition_statement(blabel); +} + +// Dump the AST for a label definition statement. + +void +Label_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << this->label_->name() << ":" << std::endl; +} + +// Make a label statement. + +Statement* +Statement::make_label_statement(Label* label, Location location) +{ + return new Label_statement(label, location); +} + +// An unnamed label statement. + +class Unnamed_label_statement : public Statement +{ + public: + Unnamed_label_statement(Unnamed_label* label) + : Statement(STATEMENT_UNNAMED_LABEL, label->location()), + label_(label) + { } + + protected: + int + do_traverse(Traverse*) + { return TRAVERSE_CONTINUE; } + + Bstatement* + do_get_backend(Translate_context* context) + { return this->label_->get_definition(context); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The label. + Unnamed_label* label_; +}; + +// Dump the AST representation for an unnamed label definition statement. + +void +Unnamed_label_statement::do_dump_statement(Ast_dump_context* ast_dump_context) + const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_label_name(this->label_); + ast_dump_context->ostream() << ":" << std::endl; +} + +// Make an unnamed label statement. + +Statement* +Statement::make_unnamed_label_statement(Unnamed_label* label) +{ + return new Unnamed_label_statement(label); +} + +// An if statement. + +class If_statement : public Statement +{ + public: + If_statement(Expression* cond, Block* then_block, Block* else_block, + Location location) + : Statement(STATEMENT_IF, location), + cond_(cond), then_block_(then_block), else_block_(else_block) + { } + + protected: + int + do_traverse(Traverse*); + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + bool + do_may_fall_through() const; + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Expression* cond_; + Block* then_block_; + Block* else_block_; +}; + +// Traversal. + +int +If_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->cond_) == TRAVERSE_EXIT + || this->then_block_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->else_block_ != NULL) + { + if (this->else_block_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +void +If_statement::do_determine_types() +{ + Type_context context(Type::lookup_bool_type(), false); + this->cond_->determine_type(&context); + this->then_block_->determine_types(); + if (this->else_block_ != NULL) + this->else_block_->determine_types(); +} + +// Check types. + +void +If_statement::do_check_types(Gogo*) +{ + Type* type = this->cond_->type(); + if (type->is_error()) + this->set_is_error(); + else if (!type->is_boolean_type()) + this->report_error(_("expected boolean expression")); +} + +// Whether the overall statement may fall through. + +bool +If_statement::do_may_fall_through() const +{ + return (this->else_block_ == NULL + || this->then_block_->may_fall_through() + || this->else_block_->may_fall_through()); +} + +// Get the backend representation. + +Bstatement* +If_statement::do_get_backend(Translate_context* context) +{ + go_assert(this->cond_->type()->is_boolean_type() + || this->cond_->type()->is_error()); + tree cond_tree = this->cond_->get_tree(context); + Bexpression* cond_expr = tree_to_expr(cond_tree); + Bblock* then_block = this->then_block_->get_backend(context); + Bblock* else_block = (this->else_block_ == NULL + ? NULL + : this->else_block_->get_backend(context)); + return context->backend()->if_statement(cond_expr, then_block, + else_block, this->location()); +} + +// Dump the AST representation for an if statement + +void +If_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "if "; + ast_dump_context->dump_expression(this->cond_); + ast_dump_context->ostream() << std::endl; + if (ast_dump_context->dump_subblocks()) + { + ast_dump_context->dump_block(this->then_block_); + if (this->else_block_ != NULL) + { + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "else" << std::endl; + ast_dump_context->dump_block(this->else_block_); + } + } +} + +// Make an if statement. + +Statement* +Statement::make_if_statement(Expression* cond, Block* then_block, + Block* else_block, Location location) +{ + return new If_statement(cond, then_block, else_block, location); +} + +// Class Case_clauses::Hash_integer_value. + +class Case_clauses::Hash_integer_value +{ + public: + size_t + operator()(Expression*) const; +}; + +size_t +Case_clauses::Hash_integer_value::operator()(Expression* pe) const +{ + Numeric_constant nc; + mpz_t ival; + if (!pe->numeric_constant_value(&nc) || !nc.to_int(&ival)) + go_unreachable(); + size_t ret = mpz_get_ui(ival); + mpz_clear(ival); + return ret; +} + +// Class Case_clauses::Eq_integer_value. + +class Case_clauses::Eq_integer_value +{ + public: + bool + operator()(Expression*, Expression*) const; +}; + +bool +Case_clauses::Eq_integer_value::operator()(Expression* a, Expression* b) const +{ + Numeric_constant anc; + mpz_t aval; + Numeric_constant bnc; + mpz_t bval; + if (!a->numeric_constant_value(&anc) + || !anc.to_int(&aval) + || !b->numeric_constant_value(&bnc) + || !bnc.to_int(&bval)) + go_unreachable(); + bool ret = mpz_cmp(aval, bval) == 0; + mpz_clear(aval); + mpz_clear(bval); + return ret; +} + +// Class Case_clauses::Case_clause. + +// Traversal. + +int +Case_clauses::Case_clause::traverse(Traverse* traverse) +{ + if (this->cases_ != NULL + && (traverse->traverse_mask() + & (Traverse::traverse_types | Traverse::traverse_expressions)) != 0) + { + if (this->cases_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->statements_ != NULL) + { + if (this->statements_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Check whether all the case expressions are integer constants. + +bool +Case_clauses::Case_clause::is_constant() const +{ + if (this->cases_ != NULL) + { + for (Expression_list::const_iterator p = this->cases_->begin(); + p != this->cases_->end(); + ++p) + if (!(*p)->is_constant() || (*p)->type()->integer_type() == NULL) + return false; + } + return true; +} + +// Lower a case clause for a nonconstant switch. VAL_TEMP is the +// value we are switching on; it may be NULL. If START_LABEL is not +// NULL, it goes at the start of the statements, after the condition +// test. We branch to FINISH_LABEL at the end of the statements. + +void +Case_clauses::Case_clause::lower(Block* b, Temporary_statement* val_temp, + Unnamed_label* start_label, + Unnamed_label* finish_label) const +{ + Location loc = this->location_; + Unnamed_label* next_case_label; + if (this->cases_ == NULL || this->cases_->empty()) + { + go_assert(this->is_default_); + next_case_label = NULL; + } + else + { + Expression* cond = NULL; + + for (Expression_list::const_iterator p = this->cases_->begin(); + p != this->cases_->end(); + ++p) + { + 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 + cond = Expression::make_binary(OPERATOR_OROR, cond, this_cond, loc); + } + + Block* then_block = new Block(b, loc); + next_case_label = new Unnamed_label(Linemap::unknown_location()); + Statement* s = Statement::make_goto_unnamed_statement(next_case_label, + loc); + then_block->add_statement(s); + + // if !COND { goto NEXT_CASE_LABEL } + cond = Expression::make_unary(OPERATOR_NOT, cond, loc); + s = Statement::make_if_statement(cond, then_block, NULL, loc); + b->add_statement(s); + } + + if (start_label != NULL) + b->add_statement(Statement::make_unnamed_label_statement(start_label)); + + if (this->statements_ != NULL) + b->add_statement(Statement::make_block_statement(this->statements_, loc)); + + Statement* s = Statement::make_goto_unnamed_statement(finish_label, loc); + b->add_statement(s); + + if (next_case_label != NULL) + b->add_statement(Statement::make_unnamed_label_statement(next_case_label)); +} + +// Determine types. + +void +Case_clauses::Case_clause::determine_types(Type* type) +{ + if (this->cases_ != NULL) + { + Type_context case_context(type, false); + for (Expression_list::iterator p = this->cases_->begin(); + p != this->cases_->end(); + ++p) + (*p)->determine_type(&case_context); + } + if (this->statements_ != NULL) + this->statements_->determine_types(); +} + +// Check types. Returns false if there was an error. + +bool +Case_clauses::Case_clause::check_types(Type* type) +{ + if (this->cases_ != NULL) + { + for (Expression_list::iterator p = this->cases_->begin(); + p != this->cases_->end(); + ++p) + { + if (!Type::are_assignable(type, (*p)->type(), NULL) + && !Type::are_assignable((*p)->type(), type, NULL)) + { + error_at((*p)->location(), + "type mismatch between switch value and case clause"); + return false; + } + } + } + return true; +} + +// Return true if this clause may fall through to the following +// statements. Note that this is not the same as whether the case +// uses the "fallthrough" keyword. + +bool +Case_clauses::Case_clause::may_fall_through() const +{ + if (this->statements_ == NULL) + return true; + return this->statements_->may_fall_through(); +} + +// Convert the case values and statements to the backend +// representation. BREAK_LABEL is the label which break statements +// should branch to. CASE_CONSTANTS is used to detect duplicate +// constants. *CASES should be passed as an empty vector; the values +// for this case will be added to it. If this is the default case, +// *CASES will remain empty. This returns the statement to execute if +// one of these cases is selected. + +Bstatement* +Case_clauses::Case_clause::get_backend(Translate_context* context, + Unnamed_label* break_label, + Case_constants* case_constants, + std::vector* cases) const +{ + if (this->cases_ != NULL) + { + go_assert(!this->is_default_); + for (Expression_list::const_iterator p = this->cases_->begin(); + p != this->cases_->end(); + ++p) + { + Expression* e = *p; + if (e->classification() != Expression::EXPRESSION_INTEGER) + { + Numeric_constant nc; + mpz_t ival; + if (!(*p)->numeric_constant_value(&nc) || !nc.to_int(&ival)) + { + // Something went wrong. This can happen with a + // negative constant and an unsigned switch value. + go_assert(saw_errors()); + continue; + } + go_assert(nc.type() != NULL); + e = Expression::make_integer(&ival, nc.type(), e->location()); + mpz_clear(ival); + } + + std::pair ins = + case_constants->insert(e); + if (!ins.second) + { + // Value was already present. + error_at(this->location_, "duplicate case in switch"); + e = Expression::make_error(this->location_); + } + + tree case_tree = e->get_tree(context); + Bexpression* case_expr = tree_to_expr(case_tree); + cases->push_back(case_expr); + } + } + + Bstatement* statements; + if (this->statements_ == NULL) + statements = NULL; + else + { + Bblock* bblock = this->statements_->get_backend(context); + statements = context->backend()->block_statement(bblock); + } + + Bstatement* break_stat; + if (this->is_fallthrough_) + break_stat = NULL; + else + break_stat = break_label->get_goto(context, this->location_); + + if (statements == NULL) + return break_stat; + else if (break_stat == NULL) + return statements; + else + return context->backend()->compound_statement(statements, break_stat); +} + +// Dump the AST representation for a case clause + +void +Case_clauses::Case_clause::dump_clause(Ast_dump_context* ast_dump_context) + const +{ + ast_dump_context->print_indent(); + if (this->is_default_) + { + ast_dump_context->ostream() << "default:"; + } + else + { + ast_dump_context->ostream() << "case "; + ast_dump_context->dump_expression_list(this->cases_); + ast_dump_context->ostream() << ":" ; + } + ast_dump_context->dump_block(this->statements_); + if (this->is_fallthrough_) + { + ast_dump_context->print_indent(); + ast_dump_context->ostream() << " (fallthrough)" << std::endl; + } +} + +// Class Case_clauses. + +// Traversal. + +int +Case_clauses::traverse(Traverse* traverse) +{ + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + if (p->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Check whether all the case expressions are constant. + +bool +Case_clauses::is_constant() const +{ + for (Clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + if (!p->is_constant()) + return false; + return true; +} + +// Lower case clauses for a nonconstant switch. + +void +Case_clauses::lower(Block* b, Temporary_statement* val_temp, + Unnamed_label* break_label) const +{ + // The default case. + const Case_clause* default_case = NULL; + + // The label for the fallthrough of the previous case. + Unnamed_label* last_fallthrough_label = NULL; + + // The label for the start of the default case. This is used if the + // case before the default case falls through. + Unnamed_label* default_start_label = NULL; + + // The label for the end of the default case. This normally winds + // up as BREAK_LABEL, but it will be different if the default case + // falls through. + Unnamed_label* default_finish_label = NULL; + + for (Clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + // The label to use for the start of the statements for this + // case. This is NULL unless the previous case falls through. + Unnamed_label* start_label = last_fallthrough_label; + + // The label to jump to after the end of the statements for this + // case. + Unnamed_label* finish_label = break_label; + + last_fallthrough_label = NULL; + if (p->is_fallthrough() && p + 1 != this->clauses_.end()) + { + finish_label = new Unnamed_label(p->location()); + last_fallthrough_label = finish_label; + } + + if (!p->is_default()) + p->lower(b, val_temp, start_label, finish_label); + else + { + // We have to move the default case to the end, so that we + // only use it if all the other tests fail. + default_case = &*p; + default_start_label = start_label; + default_finish_label = finish_label; + } + } + + if (default_case != NULL) + default_case->lower(b, val_temp, default_start_label, + default_finish_label); +} + +// Determine types. + +void +Case_clauses::determine_types(Type* type) +{ + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + p->determine_types(type); +} + +// Check types. Returns false if there was an error. + +bool +Case_clauses::check_types(Type* type) +{ + bool ret = true; + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + if (!p->check_types(type)) + ret = false; + } + return ret; +} + +// Return true if these clauses may fall through to the statements +// following the switch statement. + +bool +Case_clauses::may_fall_through() const +{ + bool found_default = false; + for (Clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + if (p->may_fall_through() && !p->is_fallthrough()) + return true; + if (p->is_default()) + found_default = true; + } + return !found_default; +} + +// Convert the cases to the backend representation. This sets +// *ALL_CASES and *ALL_STATEMENTS. + +void +Case_clauses::get_backend(Translate_context* context, + Unnamed_label* break_label, + std::vector >* all_cases, + std::vector* all_statements) const +{ + Case_constants case_constants; + + size_t c = this->clauses_.size(); + all_cases->resize(c); + all_statements->resize(c); + + size_t i = 0; + for (Clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p, ++i) + { + std::vector cases; + Bstatement* stat = p->get_backend(context, break_label, &case_constants, + &cases); + (*all_cases)[i].swap(cases); + (*all_statements)[i] = stat; + } +} + +// Dump the AST representation for case clauses (from a switch statement) + +void +Case_clauses::dump_clauses(Ast_dump_context* ast_dump_context) const +{ + for (Clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + p->dump_clause(ast_dump_context); +} + +// A constant switch statement. A Switch_statement is lowered to this +// when all the cases are constants. + +class Constant_switch_statement : public Statement +{ + public: + Constant_switch_statement(Expression* val, Case_clauses* clauses, + Unnamed_label* break_label, + Location location) + : Statement(STATEMENT_CONSTANT_SWITCH, location), + val_(val), clauses_(clauses), break_label_(break_label) + { } + + protected: + int + do_traverse(Traverse*); + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The value to switch on. + Expression* val_; + // The case clauses. + Case_clauses* clauses_; + // The break label, if needed. + Unnamed_label* break_label_; +}; + +// Traversal. + +int +Constant_switch_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->clauses_->traverse(traverse); +} + +// Determine types. + +void +Constant_switch_statement::do_determine_types() +{ + this->val_->determine_type_no_context(); + this->clauses_->determine_types(this->val_->type()); +} + +// Check types. + +void +Constant_switch_statement::do_check_types(Gogo*) +{ + if (!this->clauses_->check_types(this->val_->type())) + this->set_is_error(); +} + +// Convert to GENERIC. + +Bstatement* +Constant_switch_statement::do_get_backend(Translate_context* context) +{ + tree switch_val_tree = this->val_->get_tree(context); + Bexpression* switch_val_expr = tree_to_expr(switch_val_tree); + + Unnamed_label* break_label = this->break_label_; + if (break_label == NULL) + break_label = new Unnamed_label(this->location()); + + std::vector > all_cases; + std::vector all_statements; + this->clauses_->get_backend(context, break_label, &all_cases, + &all_statements); + + Bstatement* switch_statement; + switch_statement = context->backend()->switch_statement(switch_val_expr, + all_cases, + all_statements, + this->location()); + Bstatement* ldef = break_label->get_definition(context); + return context->backend()->compound_statement(switch_statement, ldef); +} + +// Dump the AST representation for a constant switch statement. + +void +Constant_switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context) + const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "switch "; + ast_dump_context->dump_expression(this->val_); + + if (ast_dump_context->dump_subblocks()) + { + ast_dump_context->ostream() << " {" << std::endl; + this->clauses_->dump_clauses(ast_dump_context); + ast_dump_context->ostream() << "}"; + } + + ast_dump_context->ostream() << std::endl; +} + +// Class Switch_statement. + +// Traversal. + +int +Switch_statement::do_traverse(Traverse* traverse) +{ + if (this->val_ != NULL) + { + if (this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return this->clauses_->traverse(traverse); +} + +// Lower a Switch_statement to a Constant_switch_statement or a series +// of if statements. + +Statement* +Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) +{ + Location loc = this->location(); + + if (this->val_ != NULL + && (this->val_->is_error_expression() + || this->val_->type()->is_error())) + return Statement::make_error_statement(loc); + + if (this->val_ != NULL + && this->val_->type()->integer_type() != NULL + && !this->clauses_->empty() + && this->clauses_->is_constant()) + 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()) + { + Expression* val = this->val_; + if (val == NULL) + val = Expression::make_boolean(true, loc); + return Statement::make_statement(val, true); + } + + // 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()); + + Statement* s = Statement::make_unnamed_label_statement(this->break_label_); + b->add_statement(s); + + return Statement::make_block_statement(b, loc); +} + +// Return the break label for this switch statement, creating it if +// necessary. + +Unnamed_label* +Switch_statement::break_label() +{ + if (this->break_label_ == NULL) + this->break_label_ = new Unnamed_label(this->location()); + return this->break_label_; +} + +// Dump the AST representation for a switch statement. + +void +Switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "switch "; + if (this->val_ != NULL) + { + ast_dump_context->dump_expression(this->val_); + } + if (ast_dump_context->dump_subblocks()) + { + ast_dump_context->ostream() << " {" << std::endl; + this->clauses_->dump_clauses(ast_dump_context); + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "}"; + } + ast_dump_context->ostream() << std::endl; +} + +// Return whether this switch may fall through. + +bool +Switch_statement::do_may_fall_through() const +{ + if (this->clauses_ == NULL) + return true; + + // If we have a break label, then some case needed it. That implies + // that the switch statement as a whole can fall through. + if (this->break_label_ != NULL) + return true; + + return this->clauses_->may_fall_through(); +} + +// Make a switch statement. + +Switch_statement* +Statement::make_switch_statement(Expression* val, Location location) +{ + return new Switch_statement(val, location); +} + +// Class Type_case_clauses::Type_case_clause. + +// Traversal. + +int +Type_case_clauses::Type_case_clause::traverse(Traverse* traverse) +{ + if (!this->is_default_ + && ((traverse->traverse_mask() + & (Traverse::traverse_types | Traverse::traverse_expressions)) != 0) + && Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->statements_ != NULL) + return this->statements_->traverse(traverse); + return TRAVERSE_CONTINUE; +} + +// Lower one clause in a type switch. Add statements to the block B. +// The type descriptor we are switching on is in DESCRIPTOR_TEMP. +// BREAK_LABEL is the label at the end of the type switch. +// *STMTS_LABEL, if not NULL, is a label to put at the start of the +// statements. + +void +Type_case_clauses::Type_case_clause::lower(Type* switch_val_type, + Block* b, + Temporary_statement* descriptor_temp, + Unnamed_label* break_label, + Unnamed_label** stmts_label) const +{ + Location loc = this->location_; + + Unnamed_label* next_case_label = NULL; + if (!this->is_default_) + { + Type* type = this->type_; + + std::string reason; + if (switch_val_type->interface_type() != NULL + && !type->is_nil_constant_as_type() + && type->interface_type() == NULL + && !switch_val_type->interface_type()->implements_interface(type, + &reason)) + { + if (reason.empty()) + error_at(this->location_, "impossible type switch case"); + else + error_at(this->location_, "impossible type switch case (%s)", + reason.c_str()); + } + + Expression* ref = Expression::make_temporary_reference(descriptor_temp, + loc); + + Expression* cond; + // The language permits case nil, which is of course a constant + // rather than a type. It will appear here as an invalid + // forwarding type. + if (type->is_nil_constant_as_type()) + cond = Expression::make_binary(OPERATOR_EQEQ, ref, + Expression::make_nil(loc), + loc); + else + cond = Runtime::make_call((type->interface_type() == NULL + ? Runtime::IFACETYPEEQ + : Runtime::IFACEI2TP), + loc, 2, + Expression::make_type_descriptor(type, loc), + ref); + + Unnamed_label* dest; + if (!this->is_fallthrough_) + { + // if !COND { goto NEXT_CASE_LABEL } + next_case_label = new Unnamed_label(Linemap::unknown_location()); + dest = next_case_label; + cond = Expression::make_unary(OPERATOR_NOT, cond, loc); + } + else + { + // if COND { goto STMTS_LABEL } + go_assert(stmts_label != NULL); + if (*stmts_label == NULL) + *stmts_label = new Unnamed_label(Linemap::unknown_location()); + dest = *stmts_label; + } + Block* then_block = new Block(b, loc); + Statement* s = Statement::make_goto_unnamed_statement(dest, loc); + then_block->add_statement(s); + s = Statement::make_if_statement(cond, then_block, NULL, loc); + b->add_statement(s); + } + + if (this->statements_ != NULL + || (!this->is_fallthrough_ + && stmts_label != NULL + && *stmts_label != NULL)) + { + go_assert(!this->is_fallthrough_); + if (stmts_label != NULL && *stmts_label != NULL) + { + go_assert(!this->is_default_); + if (this->statements_ != NULL) + (*stmts_label)->set_location(this->statements_->start_location()); + Statement* s = Statement::make_unnamed_label_statement(*stmts_label); + b->add_statement(s); + *stmts_label = NULL; + } + if (this->statements_ != NULL) + b->add_statement(Statement::make_block_statement(this->statements_, + loc)); + } + + if (this->is_fallthrough_) + go_assert(next_case_label == NULL); + else + { + Location gloc = (this->statements_ == NULL + ? loc + : this->statements_->end_location()); + b->add_statement(Statement::make_goto_unnamed_statement(break_label, + gloc)); + if (next_case_label != NULL) + { + Statement* s = + Statement::make_unnamed_label_statement(next_case_label); + b->add_statement(s); + } + } +} + +// Return true if this type clause may fall through to the statements +// following the switch. + +bool +Type_case_clauses::Type_case_clause::may_fall_through() const +{ + if (this->is_fallthrough_) + { + // This case means that we automatically fall through to the + // next case (it's used for T1 in case T1, T2:). It does not + // mean that we fall through to the end of the type switch as a + // whole. There is sure to be a next case and that next case + // will determine whether we fall through to the statements + // after the type switch. + return false; + } + if (this->statements_ == NULL) + return true; + return this->statements_->may_fall_through(); +} + +// Dump the AST representation for a type case clause + +void +Type_case_clauses::Type_case_clause::dump_clause( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + if (this->is_default_) + { + ast_dump_context->ostream() << "default:"; + } + else + { + ast_dump_context->ostream() << "case "; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << ":" ; + } + ast_dump_context->dump_block(this->statements_); + if (this->is_fallthrough_) + { + ast_dump_context->print_indent(); + ast_dump_context->ostream() << " (fallthrough)" << std::endl; + } +} + +// Class Type_case_clauses. + +// Traversal. + +int +Type_case_clauses::traverse(Traverse* traverse) +{ + for (Type_clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + if (p->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Check for duplicate types. + +void +Type_case_clauses::check_duplicates() const +{ + typedef Unordered_set_hash(const Type*, Type_hash_identical, + Type_identical) Types_seen; + Types_seen types_seen; + for (Type_clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + Type* t = p->type(); + if (t == NULL) + continue; + if (t->is_nil_constant_as_type()) + t = Type::make_nil_type(); + std::pair ins = types_seen.insert(t); + if (!ins.second) + error_at(p->location(), "duplicate type in switch"); + } +} + +// Lower the clauses in a type switch. Add statements to the block B. +// The type descriptor we are switching on is in DESCRIPTOR_TEMP. +// BREAK_LABEL is the label at the end of the type switch. + +void +Type_case_clauses::lower(Type* switch_val_type, Block* b, + Temporary_statement* descriptor_temp, + Unnamed_label* break_label) const +{ + const Type_case_clause* default_case = NULL; + + Unnamed_label* stmts_label = NULL; + for (Type_clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + if (!p->is_default()) + p->lower(switch_val_type, b, descriptor_temp, break_label, + &stmts_label); + else + { + // We are generating a series of tests, which means that we + // need to move the default case to the end. + default_case = &*p; + } + } + go_assert(stmts_label == NULL); + + if (default_case != NULL) + default_case->lower(switch_val_type, b, descriptor_temp, break_label, + NULL); +} + +// Return true if these clauses may fall through to the statements +// following the switch statement. + +bool +Type_case_clauses::may_fall_through() const +{ + bool found_default = false; + for (Type_clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + if (p->may_fall_through()) + return true; + if (p->is_default()) + found_default = true; + } + return !found_default; +} + +// Dump the AST representation for case clauses (from a switch statement) + +void +Type_case_clauses::dump_clauses(Ast_dump_context* ast_dump_context) const +{ + for (Type_clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + p->dump_clause(ast_dump_context); +} + +// Class Type_switch_statement. + +// Traversal. + +int +Type_switch_statement::do_traverse(Traverse* traverse) +{ + if (this->var_ == NULL) + { + if (this->traverse_expression(traverse, &this->expr_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->clauses_ != NULL) + return this->clauses_->traverse(traverse); + return TRAVERSE_CONTINUE; +} + +// Lower a type switch statement to a series of if statements. The gc +// compiler is able to generate a table in some cases. However, that +// does not work for us because we may have type descriptors in +// different shared libraries, so we can't compare them with simple +// equality testing. + +Statement* +Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) +{ + const Location loc = this->location(); + + if (this->clauses_ != NULL) + this->clauses_->check_duplicates(); + + Block* b = new Block(enclosing, loc); + + Type* val_type = (this->var_ != NULL + ? this->var_->var_value()->type() + : this->expr_->type()); + + if (val_type->interface_type() == NULL) + { + if (!val_type->is_error()) + this->report_error(_("cannot type switch on non-interface value")); + return Statement::make_error_statement(loc); + } + + // var descriptor_temp DESCRIPTOR_TYPE + Type* descriptor_type = Type::make_type_descriptor_ptr_type(); + Temporary_statement* descriptor_temp = + Statement::make_temporary(descriptor_type, NULL, loc); + b->add_statement(descriptor_temp); + + // descriptor_temp = ifacetype(val_temp) FIXME: This should be + // inlined. + bool is_empty = val_type->interface_type()->is_empty(); + Expression* ref; + if (this->var_ == NULL) + ref = this->expr_; + else + ref = Expression::make_var_reference(this->var_, loc); + Expression* call = Runtime::make_call((is_empty + ? Runtime::EFACETYPE + : Runtime::IFACETYPE), + loc, 1, ref); + Temporary_reference_expression* lhs = + Expression::make_temporary_reference(descriptor_temp, loc); + lhs->set_is_lvalue(); + Statement* s = Statement::make_assignment(lhs, call, loc); + b->add_statement(s); + + if (this->clauses_ != NULL) + this->clauses_->lower(val_type, b, descriptor_temp, this->break_label()); + + s = Statement::make_unnamed_label_statement(this->break_label_); + b->add_statement(s); + + return Statement::make_block_statement(b, loc); +} + +// Return whether this switch may fall through. + +bool +Type_switch_statement::do_may_fall_through() const +{ + if (this->clauses_ == NULL) + return true; + + // If we have a break label, then some case needed it. That implies + // that the switch statement as a whole can fall through. + if (this->break_label_ != NULL) + return true; + + return this->clauses_->may_fall_through(); +} + +// Return the break label for this type switch statement, creating it +// if necessary. + +Unnamed_label* +Type_switch_statement::break_label() +{ + if (this->break_label_ == NULL) + this->break_label_ = new Unnamed_label(this->location()); + return this->break_label_; +} + +// Dump the AST representation for a type switch statement + +void +Type_switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context) + const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "switch " << this->var_->name() << " = "; + ast_dump_context->dump_expression(this->expr_); + ast_dump_context->ostream() << " .(type)"; + if (ast_dump_context->dump_subblocks()) + { + ast_dump_context->ostream() << " {" << std::endl; + this->clauses_->dump_clauses(ast_dump_context); + ast_dump_context->ostream() << "}"; + } + ast_dump_context->ostream() << std::endl; +} + +// Make a type switch statement. + +Type_switch_statement* +Statement::make_type_switch_statement(Named_object* var, Expression* expr, + Location location) +{ + return new Type_switch_statement(var, expr, location); +} + +// Class Send_statement. + +// Traversal. + +int +Send_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->channel_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->traverse_expression(traverse, &this->val_); +} + +// Determine types. + +void +Send_statement::do_determine_types() +{ + this->channel_->determine_type_no_context(); + Type* type = this->channel_->type(); + Type_context context; + if (type->channel_type() != NULL) + context.type = type->channel_type()->element_type(); + this->val_->determine_type(&context); +} + +// Check types. + +void +Send_statement::do_check_types(Gogo*) +{ + Type* type = this->channel_->type(); + if (type->is_error()) + { + this->set_is_error(); + return; + } + Channel_type* channel_type = type->channel_type(); + if (channel_type == NULL) + { + error_at(this->location(), "left operand of %<<-%> must be channel"); + this->set_is_error(); + return; + } + Type* element_type = channel_type->element_type(); + if (!Type::are_assignable(element_type, this->val_->type(), NULL)) + { + this->report_error(_("incompatible types in send")); + return; + } + if (!channel_type->may_send()) + { + this->report_error(_("invalid send on receive-only channel")); + return; + } +} + +// Convert a send statement to the backend representation. + +Bstatement* +Send_statement::do_get_backend(Translate_context* context) +{ + Location loc = this->location(); + + Channel_type* channel_type = this->channel_->type()->channel_type(); + Type* element_type = channel_type->element_type(); + Expression* val = Expression::make_cast(element_type, this->val_, loc); + + bool is_small; + bool can_take_address; + switch (element_type->base()->classification()) + { + case Type::TYPE_BOOLEAN: + case Type::TYPE_INTEGER: + case Type::TYPE_FUNCTION: + case Type::TYPE_POINTER: + case Type::TYPE_MAP: + case Type::TYPE_CHANNEL: + is_small = true; + can_take_address = false; + break; + + case Type::TYPE_FLOAT: + case Type::TYPE_COMPLEX: + case Type::TYPE_STRING: + case Type::TYPE_INTERFACE: + is_small = false; + can_take_address = false; + break; + + case Type::TYPE_STRUCT: + is_small = false; + can_take_address = true; + break; + + case Type::TYPE_ARRAY: + is_small = false; + can_take_address = !element_type->is_slice_type(); + break; + + default: + case Type::TYPE_ERROR: + case Type::TYPE_VOID: + case Type::TYPE_SINK: + case Type::TYPE_NIL: + case Type::TYPE_NAMED: + case Type::TYPE_FORWARD: + go_assert(saw_errors()); + return context->backend()->error_statement(); + } + + // Only try to take the address of a variable. We have already + // moved variables to the heap, so this should not cause that to + // happen unnecessarily. + if (can_take_address + && val->var_expression() == NULL + && val->temporary_reference_expression() == NULL) + can_take_address = false; + + Expression* td = Expression::make_type_descriptor(this->channel_->type(), + loc); + + Runtime::Function code; + Bstatement* btemp = NULL; + if (is_small) + { + // Type is small enough to handle as uint64. + code = Runtime::SEND_SMALL; + val = Expression::make_unsafe_cast(Type::lookup_integer_type("uint64"), + val, loc); + } + else if (can_take_address) + { + // Must pass address of value. The function doesn't change the + // value, so just take its address directly. + code = Runtime::SEND_BIG; + val = Expression::make_unary(OPERATOR_AND, val, loc); + } + else + { + // Must pass address of value, but the value is small enough + // that it might be in registers. Copy value into temporary + // variable to take address. + code = Runtime::SEND_BIG; + Temporary_statement* temp = Statement::make_temporary(element_type, + val, loc); + Expression* ref = Expression::make_temporary_reference(temp, loc); + val = Expression::make_unary(OPERATOR_AND, ref, loc); + btemp = temp->get_backend(context); + } + + Expression* call = Runtime::make_call(code, loc, 3, td, this->channel_, val); + + context->gogo()->lower_expression(context->function(), NULL, &call); + Bexpression* bcall = tree_to_expr(call->get_tree(context)); + Bstatement* s = context->backend()->expression_statement(bcall); + + if (btemp == NULL) + return s; + else + return context->backend()->compound_statement(btemp, s); +} + +// Dump the AST representation for a send statement + +void +Send_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->dump_expression(this->channel_); + ast_dump_context->ostream() << " <- "; + ast_dump_context->dump_expression(this->val_); + ast_dump_context->ostream() << std::endl; +} + +// Make a send statement. + +Send_statement* +Statement::make_send_statement(Expression* channel, Expression* val, + Location location) +{ + return new Send_statement(channel, val, location); +} + +// Class Select_clauses::Select_clause. + +// Traversal. + +int +Select_clauses::Select_clause::traverse(Traverse* traverse) +{ + if (!this->is_lowered_ + && (traverse->traverse_mask() + & (Traverse::traverse_types | Traverse::traverse_expressions)) != 0) + { + if (this->channel_ != NULL) + { + if (Expression::traverse(&this->channel_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->val_ != NULL) + { + if (Expression::traverse(&this->val_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->closed_ != NULL) + { + if (Expression::traverse(&this->closed_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + if (this->statements_ != NULL) + { + if (this->statements_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Lowering. We call a function to register this clause, and arrange +// to set any variables in any receive clause. + +void +Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function, + Block* b, Temporary_statement* sel) +{ + Location loc = this->location_; + + Expression* selref = Expression::make_temporary_reference(sel, loc); + + mpz_t ival; + mpz_init_set_ui(ival, this->index_); + Expression* index_expr = Expression::make_integer(&ival, NULL, loc); + mpz_clear(ival); + + if (this->is_default_) + { + go_assert(this->channel_ == NULL && this->val_ == NULL); + this->lower_default(b, selref, index_expr); + this->is_lowered_ = true; + return; + } + + // Evaluate the channel before the select statement. + Temporary_statement* channel_temp = Statement::make_temporary(NULL, + this->channel_, + loc); + b->add_statement(channel_temp); + Expression* chanref = Expression::make_temporary_reference(channel_temp, + loc); + + if (this->is_send_) + this->lower_send(b, selref, chanref, index_expr); + else + this->lower_recv(gogo, function, b, selref, chanref, index_expr); + + // Now all references should be handled through the statements, not + // through here. + this->is_lowered_ = true; + this->val_ = NULL; + this->var_ = NULL; +} + +// Lower a default clause in a select statement. + +void +Select_clauses::Select_clause::lower_default(Block* b, Expression* selref, + Expression* index_expr) +{ + Location loc = this->location_; + Expression* call = Runtime::make_call(Runtime::SELECTDEFAULT, loc, 2, selref, + index_expr); + b->add_statement(Statement::make_statement(call, true)); +} + +// Lower a send clause in a select statement. + +void +Select_clauses::Select_clause::lower_send(Block* b, Expression* selref, + Expression* chanref, + Expression* index_expr) +{ + Location loc = this->location_; + + Channel_type* ct = this->channel_->type()->channel_type(); + if (ct == NULL) + return; + + Type* valtype = ct->element_type(); + + // Note that copying the value to a temporary here means that we + // evaluate the send values in the required order. + Temporary_statement* val = Statement::make_temporary(valtype, this->val_, + loc); + b->add_statement(val); + + Expression* valref = Expression::make_temporary_reference(val, loc); + Expression* valaddr = Expression::make_unary(OPERATOR_AND, valref, loc); + + Expression* call = Runtime::make_call(Runtime::SELECTSEND, loc, 4, selref, + chanref, valaddr, index_expr); + b->add_statement(Statement::make_statement(call, true)); +} + +// Lower a receive clause in a select statement. + +void +Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function, + Block* b, Expression* selref, + Expression* chanref, + Expression* index_expr) +{ + Location loc = this->location_; + + Channel_type* ct = this->channel_->type()->channel_type(); + if (ct == NULL) + return; + + Type* valtype = ct->element_type(); + Temporary_statement* val = Statement::make_temporary(valtype, NULL, loc); + b->add_statement(val); + + Expression* valref = Expression::make_temporary_reference(val, loc); + Expression* valaddr = Expression::make_unary(OPERATOR_AND, valref, loc); + + Temporary_statement* closed_temp = NULL; + + Expression* call; + if (this->closed_ == NULL && this->closedvar_ == NULL) + call = Runtime::make_call(Runtime::SELECTRECV, loc, 4, selref, chanref, + valaddr, index_expr); + else + { + closed_temp = Statement::make_temporary(Type::lookup_bool_type(), NULL, + loc); + b->add_statement(closed_temp); + Expression* cref = Expression::make_temporary_reference(closed_temp, + loc); + Expression* caddr = Expression::make_unary(OPERATOR_AND, cref, loc); + call = Runtime::make_call(Runtime::SELECTRECV2, loc, 5, selref, chanref, + valaddr, caddr, index_expr); + } + + b->add_statement(Statement::make_statement(call, true)); + + // If the block of statements is executed, arrange for the received + // value to move from VAL to the place where the statements expect + // it. + + Block* init = NULL; + + if (this->var_ != NULL) + { + go_assert(this->val_ == NULL); + valref = Expression::make_temporary_reference(val, loc); + this->var_->var_value()->set_init(valref); + this->var_->var_value()->clear_type_from_chan_element(); + } + else if (this->val_ != NULL && !this->val_->is_sink_expression()) + { + init = new Block(b, loc); + valref = Expression::make_temporary_reference(val, loc); + init->add_statement(Statement::make_assignment(this->val_, valref, loc)); + } + + if (this->closedvar_ != NULL) + { + go_assert(this->closed_ == NULL); + Expression* cref = Expression::make_temporary_reference(closed_temp, + loc); + this->closedvar_->var_value()->set_init(cref); + } + else if (this->closed_ != NULL && !this->closed_->is_sink_expression()) + { + if (init == NULL) + init = new Block(b, loc); + Expression* cref = Expression::make_temporary_reference(closed_temp, + loc); + init->add_statement(Statement::make_assignment(this->closed_, cref, + loc)); + } + + if (init != NULL) + { + gogo->lower_block(function, init); + + if (this->statements_ != NULL) + init->add_statement(Statement::make_block_statement(this->statements_, + loc)); + this->statements_ = init; + } +} + +// Determine types. + +void +Select_clauses::Select_clause::determine_types() +{ + go_assert(this->is_lowered_); + if (this->statements_ != NULL) + this->statements_->determine_types(); +} + +// Check types. + +void +Select_clauses::Select_clause::check_types() +{ + if (this->is_default_) + return; + + Channel_type* ct = this->channel_->type()->channel_type(); + if (ct == NULL) + { + error_at(this->channel_->location(), "expected channel"); + return; + } + + if (this->is_send_ && !ct->may_send()) + error_at(this->location(), "invalid send on receive-only channel"); + else if (!this->is_send_ && !ct->may_receive()) + error_at(this->location(), "invalid receive on send-only channel"); +} + +// Whether this clause may fall through to the statement which follows +// the overall select statement. + +bool +Select_clauses::Select_clause::may_fall_through() const +{ + if (this->statements_ == NULL) + return true; + return this->statements_->may_fall_through(); +} + +// Return the backend representation for the statements to execute. + +Bstatement* +Select_clauses::Select_clause::get_statements_backend( + Translate_context* context) +{ + if (this->statements_ == NULL) + return NULL; + Bblock* bblock = this->statements_->get_backend(context); + return context->backend()->block_statement(bblock); +} + +// Dump the AST representation for a select case clause + +void +Select_clauses::Select_clause::dump_clause( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + if (this->is_default_) + { + ast_dump_context->ostream() << "default:"; + } + else + { + ast_dump_context->ostream() << "case " ; + if (this->is_send_) + { + ast_dump_context->dump_expression(this->channel_); + ast_dump_context->ostream() << " <- " ; + if (this->val_ != NULL) + ast_dump_context->dump_expression(this->val_); + } + else + { + if (this->val_ != NULL) + ast_dump_context->dump_expression(this->val_); + if (this->closed_ != NULL) + { + // FIXME: can val_ == NULL and closed_ ! = NULL? + ast_dump_context->ostream() << " , " ; + ast_dump_context->dump_expression(this->closed_); + } + if (this->closedvar_ != NULL || this->var_ != NULL) + ast_dump_context->ostream() << " := " ; + + ast_dump_context->ostream() << " <- " ; + ast_dump_context->dump_expression(this->channel_); + } + ast_dump_context->ostream() << ":" ; + } + ast_dump_context->dump_block(this->statements_); +} + +// Class Select_clauses. + +// Traversal. + +int +Select_clauses::traverse(Traverse* traverse) +{ + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + { + if (p->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Lowering. Here we pull out the channel and the send values, to +// enforce the order of evaluation. We also add explicit send and +// receive statements to the clauses. + +void +Select_clauses::lower(Gogo* gogo, Named_object* function, Block* b, + Temporary_statement* sel) +{ + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + p->lower(gogo, function, b, sel); +} + +// Determine types. + +void +Select_clauses::determine_types() +{ + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + p->determine_types(); +} + +// Check types. + +void +Select_clauses::check_types() +{ + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + p->check_types(); +} + +// Return whether these select clauses fall through to the statement +// following the overall select statement. + +bool +Select_clauses::may_fall_through() const +{ + for (Clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + if (p->may_fall_through()) + return true; + return false; +} + +// Convert to the backend representation. We have already accumulated +// all the select information. Now we call selectgo, which will +// return the index of the clause to execute. + +Bstatement* +Select_clauses::get_backend(Translate_context* context, + Temporary_statement* sel, + Unnamed_label *break_label, + Location location) +{ + size_t count = this->clauses_.size(); + std::vector > cases(count); + std::vector clauses(count); + + Type* int32_type = Type::lookup_integer_type("int32"); + + int i = 0; + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p, ++i) + { + int index = p->index(); + mpz_t ival; + mpz_init_set_ui(ival, index); + Expression* index_expr = Expression::make_integer(&ival, int32_type, + location); + mpz_clear(ival); + cases[i].push_back(tree_to_expr(index_expr->get_tree(context))); + + Bstatement* s = p->get_statements_backend(context); + Location gloc = (p->statements() == NULL + ? p->location() + : p->statements()->end_location()); + Bstatement* g = break_label->get_goto(context, gloc); + + if (s == NULL) + clauses[i] = g; + else + clauses[i] = context->backend()->compound_statement(s, g); + } + + Expression* selref = Expression::make_temporary_reference(sel, location); + Expression* call = Runtime::make_call(Runtime::SELECTGO, location, 1, + selref); + context->gogo()->lower_expression(context->function(), NULL, &call); + Bexpression* bcall = tree_to_expr(call->get_tree(context)); + + if (count == 0) + return context->backend()->expression_statement(bcall); + + std::vector statements; + statements.reserve(2); + + Bstatement* switch_stmt = context->backend()->switch_statement(bcall, + cases, + clauses, + location); + statements.push_back(switch_stmt); + + Bstatement* ldef = break_label->get_definition(context); + statements.push_back(ldef); + + return context->backend()->statement_list(statements); +} +// Dump the AST representation for select clauses. + +void +Select_clauses::dump_clauses(Ast_dump_context* ast_dump_context) const +{ + for (Clauses::const_iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + p->dump_clause(ast_dump_context); +} + +// Class Select_statement. + +// Return the break label for this switch statement, creating it if +// necessary. + +Unnamed_label* +Select_statement::break_label() +{ + if (this->break_label_ == NULL) + this->break_label_ = new Unnamed_label(this->location()); + return this->break_label_; +} + +// Lower a select statement. This will still return a select +// statement, but it will be modified to implement the order of +// evaluation rules, and to include the send and receive statements as +// explicit statements in the clauses. + +Statement* +Select_statement::do_lower(Gogo* gogo, Named_object* function, + Block* enclosing, Statement_inserter*) +{ + if (this->is_lowered_) + return this; + + Location loc = this->location(); + + Block* b = new Block(enclosing, loc); + + go_assert(this->sel_ == NULL); + + mpz_t ival; + mpz_init_set_ui(ival, this->clauses_->size()); + Expression* size_expr = Expression::make_integer(&ival, NULL, loc); + mpz_clear(ival); + + Expression* call = Runtime::make_call(Runtime::NEWSELECT, loc, 1, size_expr); + + this->sel_ = Statement::make_temporary(NULL, call, loc); + b->add_statement(this->sel_); + + this->clauses_->lower(gogo, function, b, this->sel_); + this->is_lowered_ = true; + b->add_statement(this); + + return Statement::make_block_statement(b, loc); +} + +// Whether the select statement itself may fall through to the following +// statement. + +bool +Select_statement::do_may_fall_through() const +{ + // A select statement is terminating if no break statement + // refers to it and all of its clauses are terminating. + if (this->break_label_ != NULL) + return true; + return this->clauses_->may_fall_through(); +} + +// Return the backend representation for a select statement. + +Bstatement* +Select_statement::do_get_backend(Translate_context* context) +{ + return this->clauses_->get_backend(context, this->sel_, this->break_label(), + this->location()); +} + +// Dump the AST representation for a select statement. + +void +Select_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "select"; + if (ast_dump_context->dump_subblocks()) + { + ast_dump_context->ostream() << " {" << std::endl; + this->clauses_->dump_clauses(ast_dump_context); + ast_dump_context->ostream() << "}"; + } + ast_dump_context->ostream() << std::endl; +} + +// Make a select statement. + +Select_statement* +Statement::make_select_statement(Location location) +{ + return new Select_statement(location); +} + +// Class For_statement. + +// Traversal. + +int +For_statement::do_traverse(Traverse* traverse) +{ + if (this->init_ != NULL) + { + if (this->init_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->cond_ != NULL) + { + if (this->traverse_expression(traverse, &this->cond_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->post_ != NULL) + { + if (this->post_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return this->statements_->traverse(traverse); +} + +// Lower a For_statement into if statements and gotos. Getting rid of +// complex statements make it easier to handle garbage collection. + +Statement* +For_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) +{ + Statement* s; + Location loc = this->location(); + + Block* b = new Block(enclosing, this->location()); + if (this->init_ != NULL) + { + s = Statement::make_block_statement(this->init_, + this->init_->start_location()); + b->add_statement(s); + } + + Unnamed_label* entry = NULL; + if (this->cond_ != NULL) + { + entry = new Unnamed_label(this->location()); + b->add_statement(Statement::make_goto_unnamed_statement(entry, loc)); + } + + Unnamed_label* top = new Unnamed_label(this->location()); + b->add_statement(Statement::make_unnamed_label_statement(top)); + + s = Statement::make_block_statement(this->statements_, + this->statements_->start_location()); + b->add_statement(s); + + Location end_loc = this->statements_->end_location(); + + Unnamed_label* cont = this->continue_label_; + if (cont != NULL) + b->add_statement(Statement::make_unnamed_label_statement(cont)); + + if (this->post_ != NULL) + { + s = Statement::make_block_statement(this->post_, + this->post_->start_location()); + b->add_statement(s); + end_loc = this->post_->end_location(); + } + + if (this->cond_ == NULL) + b->add_statement(Statement::make_goto_unnamed_statement(top, end_loc)); + else + { + b->add_statement(Statement::make_unnamed_label_statement(entry)); + + Location cond_loc = this->cond_->location(); + Block* then_block = new Block(b, cond_loc); + s = Statement::make_goto_unnamed_statement(top, cond_loc); + then_block->add_statement(s); + + s = Statement::make_if_statement(this->cond_, then_block, NULL, cond_loc); + b->add_statement(s); + } + + Unnamed_label* brk = this->break_label_; + if (brk != NULL) + b->add_statement(Statement::make_unnamed_label_statement(brk)); + + b->set_end_location(end_loc); + + return Statement::make_block_statement(b, loc); +} + +// Return the break label, creating it if necessary. + +Unnamed_label* +For_statement::break_label() +{ + if (this->break_label_ == NULL) + this->break_label_ = new Unnamed_label(this->location()); + return this->break_label_; +} + +// Return the continue LABEL_EXPR. + +Unnamed_label* +For_statement::continue_label() +{ + if (this->continue_label_ == NULL) + this->continue_label_ = new Unnamed_label(this->location()); + return this->continue_label_; +} + +// Set the break and continue labels a for statement. This is used +// when lowering a for range statement. + +void +For_statement::set_break_continue_labels(Unnamed_label* break_label, + Unnamed_label* continue_label) +{ + go_assert(this->break_label_ == NULL && this->continue_label_ == NULL); + this->break_label_ = break_label; + this->continue_label_ = continue_label; +} + +// Whether the overall statement may fall through. + +bool +For_statement::do_may_fall_through() const +{ + // A for loop is terminating if it has no condition and + // no break statement. + if(this->cond_ != NULL) + return true; + if(this->break_label_ != NULL) + return true; + return false; +} + +// Dump the AST representation for a for statement. + +void +For_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + if (this->init_ != NULL && ast_dump_context->dump_subblocks()) + { + ast_dump_context->print_indent(); + ast_dump_context->indent(); + ast_dump_context->ostream() << "// INIT " << std::endl; + ast_dump_context->dump_block(this->init_); + ast_dump_context->unindent(); + } + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "for "; + if (this->cond_ != NULL) + ast_dump_context->dump_expression(this->cond_); + + if (ast_dump_context->dump_subblocks()) + { + ast_dump_context->ostream() << " {" << std::endl; + ast_dump_context->dump_block(this->statements_); + if (this->init_ != NULL) + { + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "// POST " << std::endl; + ast_dump_context->dump_block(this->post_); + } + ast_dump_context->unindent(); + + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "}"; + } + + ast_dump_context->ostream() << std::endl; +} + +// Make a for statement. + +For_statement* +Statement::make_for_statement(Block* init, Expression* cond, Block* post, + Location location) +{ + return new For_statement(init, cond, post, location); +} + +// Class For_range_statement. + +// Traversal. + +int +For_range_statement::do_traverse(Traverse* traverse) +{ + if (this->traverse_expression(traverse, &this->index_var_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->value_var_ != NULL) + { + if (this->traverse_expression(traverse, &this->value_var_) + == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->traverse_expression(traverse, &this->range_) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return this->statements_->traverse(traverse); +} + +// Lower a for range statement. For simplicity we lower this into a +// for statement, which will then be lowered in turn to goto +// statements. + +Statement* +For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, + Statement_inserter*) +{ + Type* range_type = this->range_->type(); + if (range_type->points_to() != NULL + && range_type->points_to()->array_type() != NULL + && !range_type->points_to()->is_slice_type()) + range_type = range_type->points_to(); + + Type* index_type; + Type* value_type = NULL; + if (range_type->array_type() != NULL) + { + index_type = Type::lookup_integer_type("int"); + value_type = range_type->array_type()->element_type(); + } + else if (range_type->is_string_type()) + { + index_type = Type::lookup_integer_type("int"); + value_type = Type::lookup_integer_type("int32"); + } + else if (range_type->map_type() != NULL) + { + index_type = range_type->map_type()->key_type(); + value_type = range_type->map_type()->val_type(); + } + else if (range_type->channel_type() != NULL) + { + index_type = range_type->channel_type()->element_type(); + if (this->value_var_ != NULL) + { + if (!this->value_var_->type()->is_error()) + this->report_error(_("too many variables for range clause " + "with channel")); + return Statement::make_error_statement(this->location()); + } + } + else + { + this->report_error(_("range clause must have " + "array, slice, string, map, or channel type")); + return Statement::make_error_statement(this->location()); + } + + Location loc = this->location(); + Block* temp_block = new Block(enclosing, loc); + + Named_object* range_object = NULL; + Temporary_statement* range_temp = NULL; + Var_expression* ve = this->range_->var_expression(); + if (ve != NULL) + range_object = ve->named_object(); + else + { + range_temp = Statement::make_temporary(NULL, this->range_, loc); + temp_block->add_statement(range_temp); + this->range_ = NULL; + } + + Temporary_statement* index_temp = Statement::make_temporary(index_type, + NULL, loc); + temp_block->add_statement(index_temp); + + Temporary_statement* value_temp = NULL; + if (this->value_var_ != NULL) + { + value_temp = Statement::make_temporary(value_type, NULL, loc); + temp_block->add_statement(value_temp); + } + + Block* body = new Block(temp_block, loc); + + Block* init; + Expression* cond; + Block* iter_init; + Block* post; + + // Arrange to do a loop appropriate for the type. We will produce + // for INIT ; COND ; POST { + // ITER_INIT + // INDEX = INDEX_TEMP + // VALUE = VALUE_TEMP // If there is a value + // original statements + // } + + if (range_type->is_slice_type()) + this->lower_range_slice(gogo, temp_block, body, range_object, range_temp, + index_temp, value_temp, &init, &cond, &iter_init, + &post); + else if (range_type->array_type() != NULL) + this->lower_range_array(gogo, temp_block, body, range_object, range_temp, + index_temp, value_temp, &init, &cond, &iter_init, + &post); + else if (range_type->is_string_type()) + this->lower_range_string(gogo, temp_block, body, range_object, range_temp, + index_temp, value_temp, &init, &cond, &iter_init, + &post); + else if (range_type->map_type() != NULL) + this->lower_range_map(gogo, temp_block, body, range_object, range_temp, + index_temp, value_temp, &init, &cond, &iter_init, + &post); + else if (range_type->channel_type() != NULL) + this->lower_range_channel(gogo, temp_block, body, range_object, range_temp, + index_temp, value_temp, &init, &cond, &iter_init, + &post); + else + go_unreachable(); + + if (iter_init != NULL) + body->add_statement(Statement::make_block_statement(iter_init, loc)); + + Statement* assign; + Expression* index_ref = Expression::make_temporary_reference(index_temp, loc); + if (this->value_var_ == NULL) + { + assign = Statement::make_assignment(this->index_var_, index_ref, loc); + } + else + { + Expression_list* lhs = new Expression_list(); + lhs->push_back(this->index_var_); + lhs->push_back(this->value_var_); + + Expression_list* rhs = new Expression_list(); + rhs->push_back(index_ref); + rhs->push_back(Expression::make_temporary_reference(value_temp, loc)); + + assign = Statement::make_tuple_assignment(lhs, rhs, loc); + } + body->add_statement(assign); + + body->add_statement(Statement::make_block_statement(this->statements_, loc)); + + body->set_end_location(this->statements_->end_location()); + + For_statement* loop = Statement::make_for_statement(init, cond, post, + this->location()); + loop->add_statements(body); + loop->set_break_continue_labels(this->break_label_, this->continue_label_); + + temp_block->add_statement(loop); + + return Statement::make_block_statement(temp_block, loc); +} + +// Return a reference to the range, which may be in RANGE_OBJECT or in +// RANGE_TEMP. + +Expression* +For_range_statement::make_range_ref(Named_object* range_object, + Temporary_statement* range_temp, + Location loc) +{ + if (range_object != NULL) + return Expression::make_var_reference(range_object, loc); + else + return Expression::make_temporary_reference(range_temp, loc); +} + +// Return a call to the predeclared function FUNCNAME passing a +// reference to the temporary variable ARG. + +Expression* +For_range_statement::call_builtin(Gogo* gogo, const char* funcname, + Expression* arg, + Location loc) +{ + Named_object* no = gogo->lookup_global(funcname); + go_assert(no != NULL && no->is_function_declaration()); + Expression* func = Expression::make_func_reference(no, NULL, loc); + Expression_list* params = new Expression_list(); + params->push_back(arg); + return Expression::make_call(func, params, false, loc); +} + +// Lower a for range over an array. + +void +For_range_statement::lower_range_array(Gogo* gogo, + Block* enclosing, + Block* body_block, + Named_object* range_object, + Temporary_statement* range_temp, + Temporary_statement* index_temp, + Temporary_statement* value_temp, + Block** pinit, + Expression** pcond, + Block** piter_init, + Block** ppost) +{ + Location loc = this->location(); + + // The loop we generate: + // len_temp := len(range) + // for index_temp = 0; index_temp < len_temp; index_temp++ { + // value_temp = range[index_temp] + // index = index_temp + // value = value_temp + // original body + // } + + // Set *PINIT to + // var len_temp int + // len_temp = len(range) + // index_temp = 0 + + Block* init = new Block(enclosing, loc); + + Expression* ref = this->make_range_ref(range_object, range_temp, loc); + Expression* len_call = this->call_builtin(gogo, "len", ref, loc); + Temporary_statement* len_temp = Statement::make_temporary(index_temp->type(), + len_call, loc); + init->add_statement(len_temp); + + mpz_t zval; + mpz_init_set_ui(zval, 0UL); + Expression* zexpr = Expression::make_integer(&zval, NULL, loc); + mpz_clear(zval); + + Temporary_reference_expression* tref = + Expression::make_temporary_reference(index_temp, loc); + tref->set_is_lvalue(); + Statement* s = Statement::make_assignment(tref, zexpr, loc); + init->add_statement(s); + + *pinit = init; + + // Set *PCOND to + // index_temp < len_temp + + ref = Expression::make_temporary_reference(index_temp, loc); + Expression* ref2 = Expression::make_temporary_reference(len_temp, loc); + Expression* lt = Expression::make_binary(OPERATOR_LT, ref, ref2, loc); + + *pcond = lt; + + // Set *PITER_INIT to + // value_temp = range[index_temp] + + Block* iter_init = NULL; + if (value_temp != NULL) + { + iter_init = new Block(body_block, loc); + + ref = this->make_range_ref(range_object, range_temp, loc); + Expression* ref2 = Expression::make_temporary_reference(index_temp, loc); + Expression* index = Expression::make_index(ref, ref2, NULL, NULL, loc); + + tref = Expression::make_temporary_reference(value_temp, loc); + tref->set_is_lvalue(); + s = Statement::make_assignment(tref, index, loc); + + iter_init->add_statement(s); + } + *piter_init = iter_init; + + // Set *PPOST to + // index_temp++ + + Block* post = new Block(enclosing, loc); + tref = Expression::make_temporary_reference(index_temp, loc); + tref->set_is_lvalue(); + s = Statement::make_inc_statement(tref); + post->add_statement(s); + *ppost = post; +} + +// Lower a for range over a slice. + +void +For_range_statement::lower_range_slice(Gogo* gogo, + Block* enclosing, + Block* body_block, + Named_object* range_object, + Temporary_statement* range_temp, + Temporary_statement* index_temp, + Temporary_statement* value_temp, + Block** pinit, + Expression** pcond, + Block** piter_init, + Block** ppost) +{ + Location loc = this->location(); + + // The loop we generate: + // for_temp := range + // len_temp := len(for_temp) + // for index_temp = 0; index_temp < len_temp; index_temp++ { + // value_temp = for_temp[index_temp] + // index = index_temp + // value = value_temp + // original body + // } + // + // Using for_temp means that we don't need to check bounds when + // fetching range_temp[index_temp]. + + // Set *PINIT to + // range_temp := range + // var len_temp int + // len_temp = len(range_temp) + // index_temp = 0 + + Block* init = new Block(enclosing, loc); + + Expression* ref = this->make_range_ref(range_object, range_temp, loc); + Temporary_statement* for_temp = Statement::make_temporary(NULL, ref, loc); + init->add_statement(for_temp); + + ref = Expression::make_temporary_reference(for_temp, loc); + Expression* len_call = this->call_builtin(gogo, "len", ref, loc); + Temporary_statement* len_temp = Statement::make_temporary(index_temp->type(), + len_call, loc); + init->add_statement(len_temp); + + mpz_t zval; + mpz_init_set_ui(zval, 0UL); + Expression* zexpr = Expression::make_integer(&zval, NULL, loc); + mpz_clear(zval); + + Temporary_reference_expression* tref = + Expression::make_temporary_reference(index_temp, loc); + tref->set_is_lvalue(); + Statement* s = Statement::make_assignment(tref, zexpr, loc); + init->add_statement(s); + + *pinit = init; + + // Set *PCOND to + // index_temp < len_temp + + ref = Expression::make_temporary_reference(index_temp, loc); + Expression* ref2 = Expression::make_temporary_reference(len_temp, loc); + Expression* lt = Expression::make_binary(OPERATOR_LT, ref, ref2, loc); + + *pcond = lt; + + // Set *PITER_INIT to + // value_temp = range[index_temp] + + Block* iter_init = NULL; + if (value_temp != NULL) + { + iter_init = new Block(body_block, loc); + + ref = Expression::make_temporary_reference(for_temp, loc); + Expression* ref2 = Expression::make_temporary_reference(index_temp, loc); + Expression* index = Expression::make_index(ref, ref2, NULL, NULL, loc); + + tref = Expression::make_temporary_reference(value_temp, loc); + tref->set_is_lvalue(); + s = Statement::make_assignment(tref, index, loc); + + iter_init->add_statement(s); + } + *piter_init = iter_init; + + // Set *PPOST to + // index_temp++ + + Block* post = new Block(enclosing, loc); + tref = Expression::make_temporary_reference(index_temp, loc); + tref->set_is_lvalue(); + s = Statement::make_inc_statement(tref); + post->add_statement(s); + *ppost = post; +} + +// Lower a for range over a string. + +void +For_range_statement::lower_range_string(Gogo*, + Block* enclosing, + Block* body_block, + Named_object* range_object, + Temporary_statement* range_temp, + Temporary_statement* index_temp, + Temporary_statement* value_temp, + Block** pinit, + Expression** pcond, + Block** piter_init, + Block** ppost) +{ + Location loc = this->location(); + + // The loop we generate: + // var next_index_temp int + // for index_temp = 0; ; index_temp = next_index_temp { + // next_index_temp, value_temp = stringiter2(range, index_temp) + // if next_index_temp == 0 { + // break + // } + // index = index_temp + // value = value_temp + // original body + // } + + // Set *PINIT to + // var next_index_temp int + // index_temp = 0 + + Block* init = new Block(enclosing, loc); + + Temporary_statement* next_index_temp = + Statement::make_temporary(index_temp->type(), NULL, loc); + init->add_statement(next_index_temp); + + mpz_t zval; + mpz_init_set_ui(zval, 0UL); + Expression* zexpr = Expression::make_integer(&zval, NULL, loc); + + Temporary_reference_expression* ref = + Expression::make_temporary_reference(index_temp, loc); + ref->set_is_lvalue(); + Statement* s = Statement::make_assignment(ref, zexpr, loc); + + init->add_statement(s); + *pinit = init; + + // The loop has no condition. + + *pcond = NULL; + + // Set *PITER_INIT to + // next_index_temp = runtime.stringiter(range, index_temp) + // or + // next_index_temp, value_temp = runtime.stringiter2(range, index_temp) + // followed by + // if next_index_temp == 0 { + // break + // } + + Block* iter_init = new Block(body_block, loc); + + Expression* p1 = this->make_range_ref(range_object, range_temp, loc); + Expression* p2 = Expression::make_temporary_reference(index_temp, loc); + Call_expression* call = Runtime::make_call((value_temp == NULL + ? Runtime::STRINGITER + : Runtime::STRINGITER2), + loc, 2, p1, p2); + + if (value_temp == NULL) + { + ref = Expression::make_temporary_reference(next_index_temp, loc); + ref->set_is_lvalue(); + s = Statement::make_assignment(ref, call, loc); + } + else + { + Expression_list* lhs = new Expression_list(); + + ref = Expression::make_temporary_reference(next_index_temp, loc); + ref->set_is_lvalue(); + lhs->push_back(ref); + + ref = Expression::make_temporary_reference(value_temp, loc); + ref->set_is_lvalue(); + lhs->push_back(ref); + + Expression_list* rhs = new Expression_list(); + rhs->push_back(Expression::make_call_result(call, 0)); + rhs->push_back(Expression::make_call_result(call, 1)); + + s = Statement::make_tuple_assignment(lhs, rhs, loc); + } + iter_init->add_statement(s); + + ref = Expression::make_temporary_reference(next_index_temp, loc); + zexpr = Expression::make_integer(&zval, NULL, loc); + mpz_clear(zval); + Expression* equals = Expression::make_binary(OPERATOR_EQEQ, ref, zexpr, loc); + + Block* then_block = new Block(iter_init, loc); + s = Statement::make_break_statement(this->break_label(), loc); + then_block->add_statement(s); + + s = Statement::make_if_statement(equals, then_block, NULL, loc); + iter_init->add_statement(s); + + *piter_init = iter_init; + + // Set *PPOST to + // index_temp = next_index_temp + + Block* post = new Block(enclosing, loc); + + Temporary_reference_expression* lhs = + Expression::make_temporary_reference(index_temp, loc); + lhs->set_is_lvalue(); + Expression* rhs = Expression::make_temporary_reference(next_index_temp, loc); + s = Statement::make_assignment(lhs, rhs, loc); + + post->add_statement(s); + *ppost = post; +} + +// Lower a for range over a map. + +void +For_range_statement::lower_range_map(Gogo*, + Block* enclosing, + Block* body_block, + Named_object* range_object, + Temporary_statement* range_temp, + Temporary_statement* index_temp, + Temporary_statement* value_temp, + Block** pinit, + Expression** pcond, + Block** piter_init, + Block** ppost) +{ + Location loc = this->location(); + + // The runtime uses a struct to handle ranges over a map. The + // struct is four pointers long. The first pointer is NULL when we + // have completed the iteration. + + // The loop we generate: + // var hiter map_iteration_struct + // for mapiterinit(range, &hiter); hiter[0] != nil; mapiternext(&hiter) { + // mapiter2(hiter, &index_temp, &value_temp) + // index = index_temp + // value = value_temp + // original body + // } + + // Set *PINIT to + // var hiter map_iteration_struct + // runtime.mapiterinit(range, &hiter) + + Block* init = new Block(enclosing, loc); + + Type* map_iteration_type = Runtime::map_iteration_type(); + Temporary_statement* hiter = Statement::make_temporary(map_iteration_type, + NULL, loc); + init->add_statement(hiter); + + Expression* p1 = this->make_range_ref(range_object, range_temp, loc); + Expression* ref = Expression::make_temporary_reference(hiter, loc); + Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc); + Expression* call = Runtime::make_call(Runtime::MAPITERINIT, loc, 2, p1, p2); + init->add_statement(Statement::make_statement(call, true)); + + *pinit = init; + + // Set *PCOND to + // hiter[0] != nil + + ref = Expression::make_temporary_reference(hiter, loc); + + mpz_t zval; + mpz_init_set_ui(zval, 0UL); + Expression* zexpr = Expression::make_integer(&zval, NULL, loc); + mpz_clear(zval); + + Expression* index = Expression::make_index(ref, zexpr, NULL, NULL, loc); + + Expression* ne = Expression::make_binary(OPERATOR_NOTEQ, index, + Expression::make_nil(loc), + loc); + + *pcond = ne; + + // Set *PITER_INIT to + // mapiter1(hiter, &index_temp) + // or + // mapiter2(hiter, &index_temp, &value_temp) + + Block* iter_init = new Block(body_block, loc); + + ref = Expression::make_temporary_reference(hiter, loc); + p1 = Expression::make_unary(OPERATOR_AND, ref, loc); + ref = Expression::make_temporary_reference(index_temp, loc); + p2 = Expression::make_unary(OPERATOR_AND, ref, loc); + if (value_temp == NULL) + call = Runtime::make_call(Runtime::MAPITER1, loc, 2, p1, p2); + else + { + ref = Expression::make_temporary_reference(value_temp, loc); + Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc); + call = Runtime::make_call(Runtime::MAPITER2, loc, 3, p1, p2, p3); + } + iter_init->add_statement(Statement::make_statement(call, true)); + + *piter_init = iter_init; + + // Set *PPOST to + // mapiternext(&hiter) + + Block* post = new Block(enclosing, loc); + + ref = Expression::make_temporary_reference(hiter, loc); + p1 = Expression::make_unary(OPERATOR_AND, ref, loc); + call = Runtime::make_call(Runtime::MAPITERNEXT, loc, 1, p1); + post->add_statement(Statement::make_statement(call, true)); + + *ppost = post; +} + +// Lower a for range over a channel. + +void +For_range_statement::lower_range_channel(Gogo*, + Block*, + Block* body_block, + Named_object* range_object, + Temporary_statement* range_temp, + Temporary_statement* index_temp, + Temporary_statement* value_temp, + Block** pinit, + Expression** pcond, + Block** piter_init, + Block** ppost) +{ + go_assert(value_temp == NULL); + + Location loc = this->location(); + + // The loop we generate: + // for { + // index_temp, ok_temp = <-range + // if !ok_temp { + // break + // } + // index = index_temp + // original body + // } + + // We have no initialization code, no condition, and no post code. + + *pinit = NULL; + *pcond = NULL; + *ppost = NULL; + + // Set *PITER_INIT to + // index_temp, ok_temp = <-range + // if !ok_temp { + // break + // } + + Block* iter_init = new Block(body_block, loc); + + Temporary_statement* ok_temp = + Statement::make_temporary(Type::lookup_bool_type(), NULL, loc); + iter_init->add_statement(ok_temp); + + Expression* cref = this->make_range_ref(range_object, range_temp, loc); + Temporary_reference_expression* iref = + Expression::make_temporary_reference(index_temp, loc); + iref->set_is_lvalue(); + Temporary_reference_expression* oref = + Expression::make_temporary_reference(ok_temp, loc); + oref->set_is_lvalue(); + Statement* s = Statement::make_tuple_receive_assignment(iref, oref, cref, + loc); + iter_init->add_statement(s); + + Block* then_block = new Block(iter_init, loc); + s = Statement::make_break_statement(this->break_label(), loc); + then_block->add_statement(s); + + oref = Expression::make_temporary_reference(ok_temp, loc); + Expression* cond = Expression::make_unary(OPERATOR_NOT, oref, loc); + s = Statement::make_if_statement(cond, then_block, NULL, loc); + iter_init->add_statement(s); + + *piter_init = iter_init; +} + +// Return the break LABEL_EXPR. + +Unnamed_label* +For_range_statement::break_label() +{ + if (this->break_label_ == NULL) + this->break_label_ = new Unnamed_label(this->location()); + return this->break_label_; +} + +// Return the continue LABEL_EXPR. + +Unnamed_label* +For_range_statement::continue_label() +{ + if (this->continue_label_ == NULL) + this->continue_label_ = new Unnamed_label(this->location()); + return this->continue_label_; +} + +// Dump the AST representation for a for range statement. + +void +For_range_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const +{ + + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "for "; + ast_dump_context->dump_expression(this->index_var_); + if (this->value_var_ != NULL) + { + ast_dump_context->ostream() << ", "; + ast_dump_context->dump_expression(this->value_var_); + } + + ast_dump_context->ostream() << " = range "; + ast_dump_context->dump_expression(this->range_); + if (ast_dump_context->dump_subblocks()) + { + ast_dump_context->ostream() << " {" << std::endl; + + ast_dump_context->indent(); + + ast_dump_context->dump_block(this->statements_); + + ast_dump_context->unindent(); + ast_dump_context->print_indent(); + ast_dump_context->ostream() << "}"; + } + ast_dump_context->ostream() << std::endl; +} + +// Make a for statement with a range clause. + +For_range_statement* +Statement::make_for_range_statement(Expression* index_var, + Expression* value_var, + Expression* range, + Location location) +{ + return new For_range_statement(index_var, value_var, range, location); +} diff --git a/gcc-4.9/gcc/go/gofrontend/statements.h b/gcc-4.9/gcc/go/gofrontend/statements.h new file mode 100644 index 000000000..7d9bcfde8 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/statements.h @@ -0,0 +1,1661 @@ +// statements.h -- Go frontend statements. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_STATEMENTS_H +#define GO_STATEMENTS_H + +#include "operator.h" + +class Gogo; +class Traverse; +class Statement_inserter; +class Block; +class Function; +class Unnamed_label; +class Temporary_statement; +class Variable_declaration_statement; +class Expression_statement; +class Return_statement; +class Thunk_statement; +class Label_statement; +class For_statement; +class For_range_statement; +class Switch_statement; +class Type_switch_statement; +class Send_statement; +class Select_statement; +class Variable; +class Named_object; +class Label; +class Translate_context; +class Expression; +class Expression_list; +class Struct_type; +class Call_expression; +class Map_index_expression; +class Receive_expression; +class Case_clauses; +class Type_case_clauses; +class Select_clauses; +class Typed_identifier_list; +class Bexpression; +class Bstatement; +class Bvariable; +class Ast_dump_context; + +// This class is used to traverse assignments made by a statement +// which makes assignments. + +class Traverse_assignments +{ + public: + Traverse_assignments() + { } + + virtual ~Traverse_assignments() + { } + + // This is called for a variable initialization. + virtual void + initialize_variable(Named_object*) = 0; + + // This is called for each assignment made by the statement. PLHS + // points to the left hand side, and PRHS points to the right hand + // side. PRHS may be NULL if there is no associated expression, as + // in the bool set by a non-blocking receive. + virtual void + assignment(Expression** plhs, Expression** prhs) = 0; + + // This is called for each expression which is not passed to the + // assignment function. This is used for some of the statements + // which assign two values, for which there is no expression which + // describes the value. For ++ and -- the value is passed to both + // the assignment method and the rhs method. IS_STORED is true if + // this value is being stored directly. It is false if the value is + // computed but not stored. IS_LOCAL is true if the value is being + // stored in a local variable or this is being called by a return + // statement. + virtual void + value(Expression**, bool is_stored, bool is_local) = 0; +}; + +// A single statement. + +class Statement +{ + public: + // The types of statements. + enum Statement_classification + { + STATEMENT_ERROR, + STATEMENT_VARIABLE_DECLARATION, + STATEMENT_TEMPORARY, + STATEMENT_ASSIGNMENT, + STATEMENT_EXPRESSION, + STATEMENT_BLOCK, + STATEMENT_GO, + STATEMENT_DEFER, + STATEMENT_RETURN, + STATEMENT_BREAK_OR_CONTINUE, + STATEMENT_GOTO, + STATEMENT_GOTO_UNNAMED, + STATEMENT_LABEL, + STATEMENT_UNNAMED_LABEL, + STATEMENT_IF, + STATEMENT_CONSTANT_SWITCH, + STATEMENT_SEND, + STATEMENT_SELECT, + + // These statements types are created by the parser, but they + // disappear during the lowering pass. + STATEMENT_ASSIGNMENT_OPERATION, + STATEMENT_TUPLE_ASSIGNMENT, + STATEMENT_TUPLE_MAP_ASSIGNMENT, + STATEMENT_MAP_ASSIGNMENT, + STATEMENT_TUPLE_RECEIVE_ASSIGNMENT, + STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT, + STATEMENT_INCDEC, + STATEMENT_FOR, + STATEMENT_FOR_RANGE, + STATEMENT_SWITCH, + STATEMENT_TYPE_SWITCH + }; + + Statement(Statement_classification, Location); + + virtual ~Statement(); + + // Make a variable declaration. + static Statement* + make_variable_declaration(Named_object*); + + // Make a statement which creates a temporary variable and + // initializes it to an expression. The block is used if the + // temporary variable has to be explicitly destroyed; the variable + // must still be added to the block. References to the temporary + // variable may be constructed using make_temporary_reference. + // Either the type or the initialization expression may be NULL, but + // not both. + static Temporary_statement* + make_temporary(Type*, Expression*, Location); + + // Make an assignment statement. + static Statement* + make_assignment(Expression*, Expression*, Location); + + // Make an assignment operation (+=, etc.). + static Statement* + make_assignment_operation(Operator, Expression*, Expression*, + Location); + + // Make a tuple assignment statement. + static Statement* + make_tuple_assignment(Expression_list*, Expression_list*, Location); + + // Make an assignment from a map index to a pair of variables. + static Statement* + make_tuple_map_assignment(Expression* val, Expression* present, + Expression*, Location); + + // Make a statement which assigns a pair of values to a map. + static Statement* + make_map_assignment(Expression*, Expression* val, + Expression* should_set, Location); + + // Make an assignment from a nonblocking receive to a pair of + // variables. + static Statement* + make_tuple_receive_assignment(Expression* val, Expression* closed, + Expression* channel, Location); + + // Make an assignment from a type guard to a pair of variables. + static Statement* + make_tuple_type_guard_assignment(Expression* val, Expression* ok, + Expression* expr, Type* type, + Location); + + // Make an expression statement from an Expression. IS_IGNORED is + // true if the value is being explicitly ignored, as in an + // assignment to _. + static Statement* + make_statement(Expression*, bool is_ignored); + + // Make a block statement from a Block. This is an embedded list of + // statements which may also include variable definitions. + static Statement* + make_block_statement(Block*, Location); + + // Make an increment statement. + static Statement* + make_inc_statement(Expression*); + + // Make a decrement statement. + static Statement* + make_dec_statement(Expression*); + + // Make a go statement. + static Statement* + make_go_statement(Call_expression* call, Location); + + // Make a defer statement. + static Statement* + make_defer_statement(Call_expression* call, Location); + + // Make a return statement. + static Return_statement* + make_return_statement(Expression_list*, Location); + + // Make a statement that returns the result of a call expression. + // If the call does not return any results, this just returns the + // call expression as a statement, assuming that the function will + // end immediately afterward. + static Statement* + make_return_from_call(Call_expression*, Location); + + // Make a break statement. + static Statement* + make_break_statement(Unnamed_label* label, Location); + + // Make a continue statement. + static Statement* + make_continue_statement(Unnamed_label* label, Location); + + // Make a goto statement. + static Statement* + make_goto_statement(Label* label, Location); + + // Make a goto statement to an unnamed label. + static Statement* + make_goto_unnamed_statement(Unnamed_label* label, Location); + + // Make a label statement--where the label is defined. + static Statement* + make_label_statement(Label* label, Location); + + // Make an unnamed label statement--where the label is defined. + static Statement* + make_unnamed_label_statement(Unnamed_label* label); + + // Make an if statement. + static Statement* + make_if_statement(Expression* cond, Block* then_block, Block* else_block, + Location); + + // Make a switch statement. + static Switch_statement* + make_switch_statement(Expression* switch_val, Location); + + // Make a type switch statement. + static Type_switch_statement* + make_type_switch_statement(Named_object* var, Expression*, Location); + + // Make a send statement. + static Send_statement* + make_send_statement(Expression* channel, Expression* val, Location); + + // Make a select statement. + static Select_statement* + make_select_statement(Location); + + // Make a for statement. + static For_statement* + make_for_statement(Block* init, Expression* cond, Block* post, + Location location); + + // Make a for statement with a range clause. + static For_range_statement* + make_for_range_statement(Expression* index_var, Expression* value_var, + Expression* range, Location); + + // Return the statement classification. + Statement_classification + classification() const + { return this->classification_; } + + // Get the statement location. + Location + location() const + { return this->location_; } + + // Traverse the tree. + int + traverse(Block*, size_t* index, Traverse*); + + // Traverse the contents of this statement--the expressions and + // statements which it contains. + int + traverse_contents(Traverse*); + + // If this statement assigns some values, it calls a function for + // each value to which this statement assigns a value, and returns + // true. If this statement does not assign any values, it returns + // false. + bool + traverse_assignments(Traverse_assignments* tassign); + + // Lower a statement. This is called immediately after parsing to + // simplify statements for further processing. It returns the same + // Statement or a new one. FUNCTION is the function containing this + // statement. BLOCK is the block containing this statement. + // INSERTER can be used to insert new statements before this one. + Statement* + lower(Gogo* gogo, Named_object* function, Block* block, + Statement_inserter* inserter) + { return this->do_lower(gogo, function, block, inserter); } + + // Flatten a statement. This is called immediately after the order of + // evaluation rules are applied to statements. It returns the same + // Statement or a new one. FUNCTION is the function containing this + // statement. BLOCK is the block containing this statement. + // INSERTER can be used to insert new statements before this one. + Statement* + flatten(Gogo* gogo, Named_object* function, Block* block, + Statement_inserter* inserter) + { return this->do_flatten(gogo, function, block, inserter); } + + // Set type information for unnamed constants. + void + determine_types(); + + // Check types in a statement. This simply checks that any + // expressions used by the statement have the right type. + void + check_types(Gogo* gogo) + { this->do_check_types(gogo); } + + // Return whether this is a block statement. + bool + is_block_statement() const + { return this->classification_ == STATEMENT_BLOCK; } + + // If this is a variable declaration statement, return it. + // Otherwise return NULL. + Variable_declaration_statement* + variable_declaration_statement() + { + return this->convert(); + } + + // If this is an expression statement, return it. Otherwise return + // NULL. + Expression_statement* + expression_statement() + { + return this->convert(); + } + + // If this is a return statement, return it. Otherwise return NULL. + Return_statement* + return_statement() + { return this->convert(); } + + // If this is a thunk statement (a go or defer statement), return + // it. Otherwise return NULL. + Thunk_statement* + thunk_statement(); + + // If this is a label statement, return it. Otherwise return NULL. + Label_statement* + label_statement() + { return this->convert(); } + + // If this is a for statement, return it. Otherwise return NULL. + For_statement* + for_statement() + { return this->convert(); } + + // If this is a for statement over a range clause, return it. + // Otherwise return NULL. + For_range_statement* + for_range_statement() + { return this->convert(); } + + // If this is a switch statement, return it. Otherwise return NULL. + Switch_statement* + switch_statement() + { return this->convert(); } + + // If this is a type switch statement, return it. Otherwise return + // NULL. + Type_switch_statement* + type_switch_statement() + { return this->convert(); } + + // If this is a select statement, return it. Otherwise return NULL. + Select_statement* + select_statement() + { return this->convert(); } + + // Return true if this statement may fall through--if after + // executing this statement we may go on to execute the following + // statement, if any. + bool + may_fall_through() const + { return this->do_may_fall_through(); } + + // Convert the statement to the backend representation. + Bstatement* + get_backend(Translate_context*); + + // Dump AST representation of a statement to a dump context. + void + dump_statement(Ast_dump_context*) const; + + protected: + // Implemented by child class: traverse the tree. + virtual int + do_traverse(Traverse*) = 0; + + // Implemented by child class: traverse assignments. Any statement + // which includes an assignment should implement this. + virtual bool + do_traverse_assignments(Traverse_assignments*) + { return false; } + + // Implemented by the child class: lower this statement to a simpler + // one. + virtual Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*) + { return this; } + + // Implemented by the child class: lower this statement to a simpler + // one. + virtual Statement* + do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*) + { return this; } + + // Implemented by child class: set type information for unnamed + // constants. Any statement which includes an expression needs to + // implement this. + virtual void + do_determine_types() + { } + + // Implemented by child class: check types of expressions used in a + // statement. + virtual void + do_check_types(Gogo*) + { } + + // Implemented by child class: return true if this statement may + // fall through. + virtual bool + do_may_fall_through() const + { return true; } + + // Implemented by child class: convert to backend representation. + virtual Bstatement* + do_get_backend(Translate_context*) = 0; + + // Implemented by child class: dump ast representation. + virtual void + do_dump_statement(Ast_dump_context*) const = 0; + + // Traverse an expression in a statement. + int + traverse_expression(Traverse*, Expression**); + + // Traverse an expression list in a statement. The Expression_list + // may be NULL. + int + traverse_expression_list(Traverse*, Expression_list*); + + // Traverse a type in a statement. + int + traverse_type(Traverse*, Type*); + + // For children to call when they detect that they are in error. + void + set_is_error(); + + // For children to call to report an error conveniently. + void + report_error(const char*); + + // For children to return an error statement from lower(). + static Statement* + make_error_statement(Location); + + private: + // Convert to the desired statement classification, or return NULL. + // This is a controlled dynamic cast. + template + Statement_class* + convert() + { + return (this->classification_ == sc + ? static_cast(this) + : NULL); + } + + template + const Statement_class* + convert() const + { + return (this->classification_ == sc + ? static_cast(this) + : NULL); + } + + // The statement classification. + Statement_classification classification_; + // The location in the input file of the start of this statement. + Location location_; +}; + +// A statement which creates and initializes a temporary variable. + +class Temporary_statement : public Statement +{ + public: + Temporary_statement(Type* type, Expression* init, Location location) + : Statement(STATEMENT_TEMPORARY, location), + type_(type), init_(init), bvariable_(NULL), are_hidden_fields_ok_(false), + is_address_taken_(false) + { } + + // Return the type of the temporary variable. + Type* + type() const; + + // Return the initializer if there is one. + Expression* + init() const + { return this->init_; } + + // Note that it is OK for this statement to set hidden fields. + void + set_hidden_fields_are_ok() + { this->are_hidden_fields_ok_ = true; } + + // Record that something takes the address of this temporary + // variable. + void + set_is_address_taken() + { this->is_address_taken_ = true; } + + // Return the temporary variable. This should not be called until + // after the statement itself has been converted. + Bvariable* + get_backend_variable(Translate_context*) const; + + protected: + int + do_traverse(Traverse*); + + bool + do_traverse_assignments(Traverse_assignments*); + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The type of the temporary variable. + Type* type_; + // The initial value of the temporary variable. This may be NULL. + Expression* init_; + // The backend representation of the temporary variable. + Bvariable* bvariable_; + // True if this statement may set hidden fields when assigning the + // value to the temporary. This is used for generated method stubs. + bool are_hidden_fields_ok_; + // True if something takes the address of this temporary variable. + bool is_address_taken_; +}; + +// A variable declaration. This marks the point in the code where a +// variable is declared. The Variable is also attached to a Block. + +class Variable_declaration_statement : public Statement +{ + public: + Variable_declaration_statement(Named_object* var); + + // The variable being declared. + Named_object* + var() + { return this->var_; } + + protected: + int + do_traverse(Traverse*); + + bool + do_traverse_assignments(Traverse_assignments*); + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Statement* + do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Named_object* var_; +}; + +// A return statement. + +class Return_statement : public Statement +{ + public: + Return_statement(Expression_list* vals, Location location) + : Statement(STATEMENT_RETURN, location), + vals_(vals), are_hidden_fields_ok_(false), is_lowered_(false) + { } + + // The list of values being returned. This may be NULL. + const Expression_list* + vals() const + { return this->vals_; } + + // Note that it is OK for this return statement to set hidden + // fields. + void + set_hidden_fields_are_ok() + { this->are_hidden_fields_ok_ = true; } + + protected: + int + do_traverse(Traverse* traverse) + { return this->traverse_expression_list(traverse, this->vals_); } + + bool + do_traverse_assignments(Traverse_assignments*); + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + bool + do_may_fall_through() const + { return false; } + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // Return values. This may be NULL. + Expression_list* vals_; + // True if this statement may pass hidden fields in the return + // value. This is used for generated method stubs. + bool are_hidden_fields_ok_; + // True if this statement has been lowered. + bool is_lowered_; +}; + +// An expression statement. + +class Expression_statement : public Statement +{ + public: + Expression_statement(Expression* expr, bool is_ignored); + + Expression* + expr() + { return this->expr_; } + + protected: + int + do_traverse(Traverse* traverse) + { return this->traverse_expression(traverse, &this->expr_); } + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + bool + do_may_fall_through() const; + + Bstatement* + do_get_backend(Translate_context* context); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Expression* expr_; + // Whether the value of this expression is being explicitly ignored. + bool is_ignored_; +}; + +// A send statement. + +class Send_statement : public Statement +{ + public: + Send_statement(Expression* channel, Expression* val, + Location location) + : Statement(STATEMENT_SEND, location), + channel_(channel), val_(val) + { } + + protected: + int + do_traverse(Traverse* traverse); + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The channel on which to send the value. + Expression* channel_; + // The value to send. + Expression* val_; +}; + +// Select_clauses holds the clauses of a select statement. This is +// built by the parser. + +class Select_clauses +{ + public: + Select_clauses() + : clauses_() + { } + + // Add a new clause. IS_SEND is true if this is a send clause, + // false for a receive clause. For a send clause CHANNEL is the + // channel and VAL is the value to send. For a receive clause + // CHANNEL is the channel, VAL is either NULL or a Var_expression + // for the variable to set, and CLOSED is either NULL or a + // Var_expression to set to whether the channel is closed. If VAL + // is NULL, VAR may be a variable to be initialized with the + // received value, and CLOSEDVAR ma be a variable to be initialized + // with whether the channel is closed. IS_DEFAULT is true if this + // is the default clause. STATEMENTS is the list of statements to + // execute. + void + add(bool is_send, Expression* channel, Expression* val, Expression* closed, + Named_object* var, Named_object* closedvar, bool is_default, + Block* statements, Location location) + { + int index = static_cast(this->clauses_.size()); + this->clauses_.push_back(Select_clause(index, is_send, channel, val, + closed, var, closedvar, is_default, + statements, location)); + } + + size_t + size() const + { return this->clauses_.size(); } + + // Traverse the select clauses. + int + traverse(Traverse*); + + // Lower statements. + void + lower(Gogo*, Named_object*, Block*, Temporary_statement*); + + // Determine types. + void + determine_types(); + + // Check types. + void + check_types(); + + // Whether the select clauses may fall through to the statement + // which follows the overall select statement. + bool + may_fall_through() const; + + // Convert to the backend representation. + Bstatement* + get_backend(Translate_context*, Temporary_statement* sel, + Unnamed_label* break_label, Location); + + // Dump AST representation. + void + dump_clauses(Ast_dump_context*) const; + + private: + // A single clause. + class Select_clause + { + public: + Select_clause() + : channel_(NULL), val_(NULL), closed_(NULL), var_(NULL), + closedvar_(NULL), statements_(NULL), is_send_(false), + is_default_(false) + { } + + Select_clause(int index, bool is_send, Expression* channel, + Expression* val, Expression* closed, Named_object* var, + Named_object* closedvar, bool is_default, Block* statements, + Location location) + : index_(index), channel_(channel), val_(val), closed_(closed), + var_(var), closedvar_(closedvar), statements_(statements), + location_(location), is_send_(is_send), is_default_(is_default), + is_lowered_(false) + { go_assert(is_default ? channel == NULL : channel != NULL); } + + // Return the index of this clause. + int + index() const + { return this->index_; } + + // Traverse the select clause. + int + traverse(Traverse*); + + // Lower statements. + void + lower(Gogo*, Named_object*, Block*, Temporary_statement*); + + // Determine types. + void + determine_types(); + + // Check types. + void + check_types(); + + // Return true if this is the default clause. + bool + is_default() const + { return this->is_default_; } + + // Return the channel. This will return NULL for the default + // clause. + Expression* + channel() const + { return this->channel_; } + + // Return true for a send, false for a receive. + bool + is_send() const + { + go_assert(!this->is_default_); + return this->is_send_; + } + + // Return the statements. + const Block* + statements() const + { return this->statements_; } + + // Return the location. + Location + location() const + { return this->location_; } + + // Whether this clause may fall through to the statement which + // follows the overall select statement. + bool + may_fall_through() const; + + // Convert the statements to the backend representation. + Bstatement* + get_statements_backend(Translate_context*); + + // Dump AST representation. + void + dump_clause(Ast_dump_context*) const; + + private: + void + lower_default(Block*, Expression*, Expression*); + + void + lower_send(Block*, Expression*, Expression*, Expression*); + + void + lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*, + Expression*); + + // The index of this case in the generated switch statement. + int index_; + // The channel. + Expression* channel_; + // The value to send or the lvalue to receive into. + Expression* val_; + // The lvalue to set to whether the channel is closed on a + // receive. + Expression* closed_; + // The variable to initialize, for "case a := <-ch". + Named_object* var_; + // The variable to initialize to whether the channel is closed, + // for "case a, c := <-ch". + Named_object* closedvar_; + // The statements to execute. + Block* statements_; + // The location of this clause. + Location location_; + // Whether this is a send or a receive. + bool is_send_; + // Whether this is the default. + bool is_default_; + // Whether this has been lowered. + bool is_lowered_; + }; + + typedef std::vector Clauses; + + Clauses clauses_; +}; + +// A select statement. + +class Select_statement : public Statement +{ + public: + Select_statement(Location location) + : Statement(STATEMENT_SELECT, location), + clauses_(NULL), sel_(NULL), break_label_(NULL), is_lowered_(false) + { } + + // Add the clauses. + void + add_clauses(Select_clauses* clauses) + { + go_assert(this->clauses_ == NULL); + this->clauses_ = clauses; + } + + // Return the break label for this select statement. + Unnamed_label* + break_label(); + + protected: + int + do_traverse(Traverse* traverse) + { return this->clauses_->traverse(traverse); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + void + do_determine_types() + { this->clauses_->determine_types(); } + + void + do_check_types(Gogo*) + { this->clauses_->check_types(); } + + bool + do_may_fall_through() const; + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The select clauses. + Select_clauses* clauses_; + // A temporary which holds the select structure we build up at runtime. + Temporary_statement* sel_; + // The break label. + Unnamed_label* break_label_; + // Whether this statement has been lowered. + bool is_lowered_; +}; + +// A statement which requires a thunk: go or defer. + +class Thunk_statement : public Statement +{ + public: + Thunk_statement(Statement_classification, Call_expression*, + Location); + + // Return the call expression. + Expression* + call() const + { return this->call_; } + + // Simplify a go or defer statement so that it only uses a single + // parameter. + bool + simplify_statement(Gogo*, Named_object*, Block*); + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_traverse_assignments(Traverse_assignments*); + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + // Return the function and argument for the call. + bool + get_fn_and_arg(Expression** pfn, Expression** parg); + + private: + // Return whether this is a simple go statement. + bool + is_simple(Function_type*) const; + + // Return whether the thunk function is a constant. + bool + is_constant_function() const; + + // Build the struct to use for a complex case. + Struct_type* + build_struct(Function_type* fntype); + + // Build the thunk. + void + build_thunk(Gogo*, const std::string&); + + // Set the name to use for thunk field N. + void + thunk_field_param(int n, char* buf, size_t buflen); + + // The function call to be executed in a separate thread (go) or + // later (defer). + Expression* call_; + // The type used for a struct to pass to a thunk, if this is not a + // simple call. + Struct_type* struct_type_; +}; + +// A go statement. + +class Go_statement : public Thunk_statement +{ + public: + Go_statement(Call_expression* call, Location location) + : Thunk_statement(STATEMENT_GO, call, location) + { } + + protected: + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; +}; + +// A defer statement. + +class Defer_statement : public Thunk_statement +{ + public: + Defer_statement(Call_expression* call, Location location) + : Thunk_statement(STATEMENT_DEFER, call, location) + { } + + protected: + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; +}; + +// A label statement. + +class Label_statement : public Statement +{ + public: + Label_statement(Label* label, Location location) + : Statement(STATEMENT_LABEL, location), + label_(label) + { } + + // Return the label itself. + const Label* + label() const + { return this->label_; } + + protected: + int + do_traverse(Traverse*); + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The label. + Label* label_; +}; + +// A for statement. + +class For_statement : public Statement +{ + public: + For_statement(Block* init, Expression* cond, Block* post, + Location location) + : Statement(STATEMENT_FOR, location), + init_(init), cond_(cond), post_(post), statements_(NULL), + break_label_(NULL), continue_label_(NULL) + { } + + // Add the statements. + void + add_statements(Block* statements) + { + go_assert(this->statements_ == NULL); + this->statements_ = statements; + } + + // Return the break label for this for statement. + Unnamed_label* + break_label(); + + // Return the continue label for this for statement. + Unnamed_label* + continue_label(); + + // Set the break and continue labels for this statement. + void + set_break_continue_labels(Unnamed_label* break_label, + Unnamed_label* continue_label); + + protected: + int + do_traverse(Traverse*); + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + bool + do_may_fall_through() const; + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The initialization statements. This may be NULL. + Block* init_; + // The condition. This may be NULL. + Expression* cond_; + // The statements to run after each iteration. This may be NULL. + Block* post_; + // The statements in the loop itself. + Block* statements_; + // The break label, if needed. + Unnamed_label* break_label_; + // The continue label, if needed. + Unnamed_label* continue_label_; +}; + +// A for statement over a range clause. + +class For_range_statement : public Statement +{ + public: + For_range_statement(Expression* index_var, Expression* value_var, + Expression* range, Location location) + : Statement(STATEMENT_FOR_RANGE, location), + index_var_(index_var), value_var_(value_var), range_(range), + statements_(NULL), break_label_(NULL), continue_label_(NULL) + { } + + // Add the statements. + void + add_statements(Block* statements) + { + go_assert(this->statements_ == NULL); + this->statements_ = statements; + } + + // Return the break label for this for statement. + Unnamed_label* + break_label(); + + // Return the continue label for this for statement. + Unnamed_label* + continue_label(); + + protected: + int + do_traverse(Traverse*); + + bool + do_traverse_assignments(Traverse_assignments*) + { go_unreachable(); } + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Expression* + make_range_ref(Named_object*, Temporary_statement*, Location); + + Expression* + call_builtin(Gogo*, const char* funcname, Expression* arg, Location); + + void + lower_range_array(Gogo*, Block*, Block*, Named_object*, Temporary_statement*, + Temporary_statement*, Temporary_statement*, + Block**, Expression**, Block**, Block**); + + void + lower_range_slice(Gogo*, Block*, Block*, Named_object*, Temporary_statement*, + Temporary_statement*, Temporary_statement*, + Block**, Expression**, Block**, Block**); + + void + lower_range_string(Gogo*, Block*, Block*, Named_object*, Temporary_statement*, + Temporary_statement*, Temporary_statement*, + Block**, Expression**, Block**, Block**); + + void + lower_range_map(Gogo*, Block*, Block*, Named_object*, Temporary_statement*, + Temporary_statement*, Temporary_statement*, + Block**, Expression**, Block**, Block**); + + void + lower_range_channel(Gogo*, Block*, Block*, Named_object*, + Temporary_statement*, Temporary_statement*, + Temporary_statement*, Block**, Expression**, Block**, + Block**); + + // The variable which is set to the index value. + Expression* index_var_; + // The variable which is set to the element value. This may be + // NULL. + Expression* value_var_; + // The expression we are ranging over. + Expression* range_; + // The statements in the block. + Block* statements_; + // The break label, if needed. + Unnamed_label* break_label_; + // The continue label, if needed. + Unnamed_label* continue_label_; +}; + +// Class Case_clauses holds the clauses of a switch statement. This +// is built by the parser. + +class Case_clauses +{ + public: + Case_clauses() + : clauses_() + { } + + // Add a new clause. CASES is a list of case expressions; it may be + // NULL. IS_DEFAULT is true if this is the default case. + // STATEMENTS is a block of statements. IS_FALLTHROUGH is true if + // after the statements the case clause should fall through to the + // next clause. + void + add(Expression_list* cases, bool is_default, Block* statements, + bool is_fallthrough, Location location) + { + this->clauses_.push_back(Case_clause(cases, is_default, statements, + is_fallthrough, location)); + } + + // Return whether there are no clauses. + bool + empty() const + { return this->clauses_.empty(); } + + // Traverse the case clauses. + int + traverse(Traverse*); + + // Lower for a nonconstant switch. + void + lower(Block*, Temporary_statement*, Unnamed_label*) const; + + // Determine types of expressions. The Type parameter is the type + // of the switch value. + void + determine_types(Type*); + + // Check types. The Type parameter is the type of the switch value. + bool + check_types(Type*); + + // Return true if all the clauses are constant values. + bool + is_constant() const; + + // Return true if these clauses may fall through to the statements + // following the switch statement. + bool + may_fall_through() const; + + // Return the body of a SWITCH_EXPR when all the clauses are + // constants. + void + get_backend(Translate_context*, Unnamed_label* break_label, + std::vector >* all_cases, + std::vector* all_statements) const; + + // Dump the AST representation to a dump context. + void + dump_clauses(Ast_dump_context*) const; + + private: + // For a constant switch we need to keep a record of constants we + // have already seen. + class Hash_integer_value; + class Eq_integer_value; + typedef Unordered_set_hash(Expression*, Hash_integer_value, + Eq_integer_value) Case_constants; + + // One case clause. + class Case_clause + { + public: + Case_clause() + : cases_(NULL), statements_(NULL), is_default_(false), + is_fallthrough_(false), location_(UNKNOWN_LOCATION) + { } + + Case_clause(Expression_list* cases, bool is_default, Block* statements, + bool is_fallthrough, Location location) + : cases_(cases), statements_(statements), is_default_(is_default), + is_fallthrough_(is_fallthrough), location_(location) + { } + + // Whether this clause falls through to the next clause. + bool + is_fallthrough() const + { return this->is_fallthrough_; } + + // Whether this is the default. + bool + is_default() const + { return this->is_default_; } + + // The location of this clause. + Location + location() const + { return this->location_; } + + // Traversal. + int + traverse(Traverse*); + + // Lower for a nonconstant switch. + void + lower(Block*, Temporary_statement*, Unnamed_label*, Unnamed_label*) const; + + // Determine types. + void + determine_types(Type*); + + // Check types. + bool + check_types(Type*); + + // Return true if all the case expressions are constant. + bool + is_constant() const; + + // Return true if this clause may fall through to execute the + // statements following the switch statement. This is not the + // same as whether this clause falls through to the next clause. + bool + may_fall_through() const; + + // Convert the case values and statements to the backend + // representation. + Bstatement* + get_backend(Translate_context*, Unnamed_label* break_label, + Case_constants*, std::vector* cases) const; + + // Dump the AST representation to a dump context. + void + dump_clause(Ast_dump_context*) const; + + private: + // The list of case expressions. + Expression_list* cases_; + // The statements to execute. + Block* statements_; + // Whether this is the default case. + bool is_default_; + // Whether this falls through after the statements. + bool is_fallthrough_; + // The location of this case clause. + Location location_; + }; + + friend class Case_clause; + + // The type of the list of clauses. + typedef std::vector Clauses; + + // All the case clauses. + Clauses clauses_; +}; + +// A switch statement. + +class Switch_statement : public Statement +{ + public: + Switch_statement(Expression* val, Location location) + : Statement(STATEMENT_SWITCH, location), + val_(val), clauses_(NULL), break_label_(NULL) + { } + + // Add the clauses. + void + add_clauses(Case_clauses* clauses) + { + go_assert(this->clauses_ == NULL); + this->clauses_ = clauses; + } + + // Return the break label for this switch statement. + Unnamed_label* + break_label(); + + protected: + int + do_traverse(Traverse*); + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + bool + do_may_fall_through() const; + + private: + // The value to switch on. This may be NULL. + Expression* val_; + // The case clauses. + Case_clauses* clauses_; + // The break label, if needed. + Unnamed_label* break_label_; +}; + +// Class Type_case_clauses holds the clauses of a type switch +// statement. This is built by the parser. + +class Type_case_clauses +{ + public: + Type_case_clauses() + : clauses_() + { } + + // Add a new clause. TYPE is the type for this clause; it may be + // NULL. IS_FALLTHROUGH is true if this falls through to the next + // clause; in this case STATEMENTS will be NULL. IS_DEFAULT is true + // if this is the default case. STATEMENTS is a block of + // statements; it may be NULL. + void + add(Type* type, bool is_fallthrough, bool is_default, Block* statements, + Location location) + { + this->clauses_.push_back(Type_case_clause(type, is_fallthrough, is_default, + statements, location)); + } + + // Return whether there are no clauses. + bool + empty() const + { return this->clauses_.empty(); } + + // Traverse the type case clauses. + int + traverse(Traverse*); + + // Check for duplicates. + void + check_duplicates() const; + + // Lower to if and goto statements. + void + lower(Type*, Block*, Temporary_statement* descriptor_temp, + Unnamed_label* break_label) const; + + // Return true if these clauses may fall through to the statements + // following the switch statement. + bool + may_fall_through() const; + + // Dump the AST representation to a dump context. + void + dump_clauses(Ast_dump_context*) const; + + private: + // One type case clause. + class Type_case_clause + { + public: + Type_case_clause() + : type_(NULL), statements_(NULL), is_default_(false), + location_(UNKNOWN_LOCATION) + { } + + Type_case_clause(Type* type, bool is_fallthrough, bool is_default, + Block* statements, Location location) + : type_(type), statements_(statements), is_fallthrough_(is_fallthrough), + is_default_(is_default), location_(location) + { } + + // The type. + Type* + type() const + { return this->type_; } + + // Whether this is the default. + bool + is_default() const + { return this->is_default_; } + + // The location of this type clause. + Location + location() const + { return this->location_; } + + // Traversal. + int + traverse(Traverse*); + + // Lower to if and goto statements. + void + lower(Type*, Block*, Temporary_statement* descriptor_temp, + Unnamed_label* break_label, Unnamed_label** stmts_label) const; + + // Return true if this clause may fall through to execute the + // statements following the switch statement. This is not the + // same as whether this clause falls through to the next clause. + bool + may_fall_through() const; + + // Dump the AST representation to a dump context. + void + dump_clause(Ast_dump_context*) const; + + private: + // The type for this type clause. + Type* type_; + // The statements to execute. + Block* statements_; + // Whether this falls through--this is true for "case T1, T2". + bool is_fallthrough_; + // Whether this is the default case. + bool is_default_; + // The location of this type case clause. + Location location_; + }; + + friend class Type_case_clause; + + // The type of the list of type clauses. + typedef std::vector Type_clauses; + + // All the type case clauses. + Type_clauses clauses_; +}; + +// A type switch statement. + +class Type_switch_statement : public Statement +{ + public: + Type_switch_statement(Named_object* var, Expression* expr, + Location location) + : Statement(STATEMENT_TYPE_SWITCH, location), + var_(var), expr_(expr), clauses_(NULL), break_label_(NULL) + { go_assert(var == NULL || expr == NULL); } + + // Add the clauses. + void + add_clauses(Type_case_clauses* clauses) + { + go_assert(this->clauses_ == NULL); + this->clauses_ = clauses; + } + + // Return the break label for this type switch statement. + Unnamed_label* + break_label(); + + protected: + int + do_traverse(Traverse*); + + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_dump_statement(Ast_dump_context*) const; + + bool + do_may_fall_through() const; + + private: + // The variable holding the value we are switching on. + Named_object* var_; + // The expression we are switching on if there is no variable. + Expression* expr_; + // The type case clauses. + Type_case_clauses* clauses_; + // The break label, if needed. + Unnamed_label* break_label_; +}; + +#endif // !defined(GO_STATEMENTS_H) diff --git a/gcc-4.9/gcc/go/gofrontend/string-dump.h b/gcc-4.9/gcc/go/gofrontend/string-dump.h new file mode 100644 index 000000000..fe4807d16 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/string-dump.h @@ -0,0 +1,25 @@ +// string-dump.h -- Abstract base class for dumping strings. -*- C++ -*- + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_STRING_DUMP_H +#define GO_STRING_DUMP_H + +// This abstract class provides an interface strings for whatever purpose. +// Used for example for exporting and dumping objects. + +class String_dump +{ + public: + // Write a string. Implements the String_dump interface. + virtual void + write_string(const std::string& s) = 0; + + // Implementors should override this member, to dump a formatted c string. + virtual void + write_c_string(const char*) = 0; +}; + +#endif // GO_STRING_DUMP_H diff --git a/gcc-4.9/gcc/go/gofrontend/types.cc b/gcc-4.9/gcc/go/gofrontend/types.cc new file mode 100644 index 000000000..2148a1a43 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/types.cc @@ -0,0 +1,10132 @@ +// types.cc -- Go frontend types. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "toplev.h" +#include "intl.h" +#include "tree.h" +#include "real.h" +#include "convert.h" + +#include "go-c.h" +#include "gogo.h" +#include "operator.h" +#include "expressions.h" +#include "statements.h" +#include "export.h" +#include "import.h" +#include "backend.h" +#include "types.h" + +// Forward declarations so that we don't have to make types.h #include +// backend.h. + +static void +get_backend_struct_fields(Gogo* gogo, const Struct_field_list* fields, + bool use_placeholder, + std::vector* bfields); + +static void +get_backend_slice_fields(Gogo* gogo, Array_type* type, bool use_placeholder, + std::vector* bfields); + +static void +get_backend_interface_fields(Gogo* gogo, Interface_type* type, + bool use_placeholder, + std::vector* bfields); + +// Class Type. + +Type::Type(Type_classification classification) + : classification_(classification), btype_(NULL), type_descriptor_var_(NULL) +{ +} + +Type::~Type() +{ +} + +// Get the base type for a type--skip names and forward declarations. + +Type* +Type::base() +{ + switch (this->classification_) + { + case TYPE_NAMED: + return this->named_type()->named_base(); + case TYPE_FORWARD: + return this->forward_declaration_type()->real_type()->base(); + default: + return this; + } +} + +const Type* +Type::base() const +{ + switch (this->classification_) + { + case TYPE_NAMED: + return this->named_type()->named_base(); + case TYPE_FORWARD: + return this->forward_declaration_type()->real_type()->base(); + default: + return this; + } +} + +// Skip defined forward declarations. + +Type* +Type::forwarded() +{ + Type* t = this; + Forward_declaration_type* ftype = t->forward_declaration_type(); + while (ftype != NULL && ftype->is_defined()) + { + t = ftype->real_type(); + ftype = t->forward_declaration_type(); + } + return t; +} + +const Type* +Type::forwarded() const +{ + const Type* t = this; + const Forward_declaration_type* ftype = t->forward_declaration_type(); + while (ftype != NULL && ftype->is_defined()) + { + t = ftype->real_type(); + ftype = t->forward_declaration_type(); + } + return t; +} + +// If this is a named type, return it. Otherwise, return NULL. + +Named_type* +Type::named_type() +{ + return this->forwarded()->convert_no_base(); +} + +const Named_type* +Type::named_type() const +{ + return this->forwarded()->convert_no_base(); +} + +// Return true if this type is not defined. + +bool +Type::is_undefined() const +{ + return this->forwarded()->forward_declaration_type() != NULL; +} + +// Return true if this is a basic type: a type which is not composed +// of other types, and is not void. + +bool +Type::is_basic_type() const +{ + switch (this->classification_) + { + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_BOOLEAN: + case TYPE_STRING: + case TYPE_NIL: + return true; + + case TYPE_ERROR: + case TYPE_VOID: + case TYPE_FUNCTION: + case TYPE_POINTER: + case TYPE_STRUCT: + case TYPE_ARRAY: + case TYPE_MAP: + case TYPE_CHANNEL: + case TYPE_INTERFACE: + return false; + + case TYPE_NAMED: + case TYPE_FORWARD: + return this->base()->is_basic_type(); + + default: + go_unreachable(); + } +} + +// Return true if this is an abstract type. + +bool +Type::is_abstract() const +{ + switch (this->classification()) + { + case TYPE_INTEGER: + return this->integer_type()->is_abstract(); + case TYPE_FLOAT: + return this->float_type()->is_abstract(); + case TYPE_COMPLEX: + return this->complex_type()->is_abstract(); + case TYPE_STRING: + return this->is_abstract_string_type(); + case TYPE_BOOLEAN: + return this->is_abstract_boolean_type(); + default: + return false; + } +} + +// Return a non-abstract version of an abstract type. + +Type* +Type::make_non_abstract_type() +{ + go_assert(this->is_abstract()); + switch (this->classification()) + { + case TYPE_INTEGER: + if (this->integer_type()->is_rune()) + return Type::lookup_integer_type("int32"); + else + return Type::lookup_integer_type("int"); + case TYPE_FLOAT: + return Type::lookup_float_type("float64"); + case TYPE_COMPLEX: + return Type::lookup_complex_type("complex128"); + case TYPE_STRING: + return Type::lookup_string_type(); + case TYPE_BOOLEAN: + return Type::lookup_bool_type(); + default: + go_unreachable(); + } +} + +// Return true if this is an error type. Don't give an error if we +// try to dereference an undefined forwarding type, as this is called +// in the parser when the type may legitimately be undefined. + +bool +Type::is_error_type() const +{ + const Type* t = this->forwarded(); + // Note that we return false for an undefined forward type. + switch (t->classification_) + { + case TYPE_ERROR: + return true; + case TYPE_NAMED: + return t->named_type()->is_named_error_type(); + default: + return false; + } +} + +// If this is a pointer type, return the type to which it points. +// Otherwise, return NULL. + +Type* +Type::points_to() const +{ + const Pointer_type* ptype = this->convert(); + return ptype == NULL ? NULL : ptype->points_to(); +} + +// Return whether this is an open array type. + +bool +Type::is_slice_type() const +{ + return this->array_type() != NULL && this->array_type()->length() == NULL; +} + +// Return whether this is the predeclared constant nil being used as a +// type. + +bool +Type::is_nil_constant_as_type() const +{ + const Type* t = this->forwarded(); + if (t->forward_declaration_type() != NULL) + { + const Named_object* no = t->forward_declaration_type()->named_object(); + if (no->is_unknown()) + no = no->unknown_value()->real_named_object(); + if (no != NULL + && no->is_const() + && no->const_value()->expr()->is_nil_expression()) + return true; + } + return false; +} + +// Traverse a type. + +int +Type::traverse(Type* type, Traverse* traverse) +{ + go_assert((traverse->traverse_mask() & Traverse::traverse_types) != 0 + || (traverse->traverse_mask() + & Traverse::traverse_expressions) != 0); + if (traverse->remember_type(type)) + { + // We have already traversed this type. + return TRAVERSE_CONTINUE; + } + if ((traverse->traverse_mask() & Traverse::traverse_types) != 0) + { + int t = traverse->type(type); + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + else if (t == TRAVERSE_SKIP_COMPONENTS) + return TRAVERSE_CONTINUE; + } + // An array type has an expression which we need to traverse if + // traverse_expressions is set. + if (type->do_traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Default implementation for do_traverse for child class. + +int +Type::do_traverse(Traverse*) +{ + return TRAVERSE_CONTINUE; +} + +// 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, bool errors_are_identical, + std::string* reason) +{ + if (t1 == NULL || t2 == NULL) + { + // Something is wrong. + return errors_are_identical ? true : t1 == t2; + } + + // Skip defined forward declarations. + t1 = t1->forwarded(); + t2 = t2->forwarded(); + + // Ignore aliases for purposes of type identity. + if (t1->named_type() != NULL && t1->named_type()->is_alias()) + t1 = t1->named_type()->real_type(); + if (t2->named_type() != NULL && t2->named_type()->is_alias()) + t2 = t2->named_type()->real_type(); + + if (t1 == t2) + return true; + + // An undefined forward declaration is an error. + if (t1->forward_declaration_type() != NULL + || t2->forward_declaration_type() != NULL) + return errors_are_identical; + + // Avoid cascading errors with error types. + if (t1->is_error_type() || t2->is_error_type()) + { + 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. + if (t1->is_sink_type() || t2->is_sink_type()) + { + if (reason != NULL) + *reason = "invalid use of _"; + return false; + } + + // A named type is only identical to itself. + if (t1->named_type() != NULL || t2->named_type() != NULL) + return false; + + // Check type shapes. + if (t1->classification() != t2->classification()) + return false; + + switch (t1->classification()) + { + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_STRING: + case TYPE_NIL: + // These types are always identical. + return true; + + case TYPE_INTEGER: + return t1->integer_type()->is_identical(t2->integer_type()); + + case TYPE_FLOAT: + return t1->float_type()->is_identical(t2->float_type()); + + case TYPE_COMPLEX: + return t1->complex_type()->is_identical(t2->complex_type()); + + 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(), + errors_are_identical, reason); + + case TYPE_STRUCT: + return t1->struct_type()->is_identical(t2->struct_type(), + errors_are_identical); + + case TYPE_ARRAY: + return t1->array_type()->is_identical(t2->array_type(), + errors_are_identical); + + case TYPE_MAP: + return t1->map_type()->is_identical(t2->map_type(), + errors_are_identical); + + case TYPE_CHANNEL: + return t1->channel_type()->is_identical(t2->channel_type(), + errors_are_identical); + + case TYPE_INTERFACE: + return t1->interface_type()->is_identical(t2->interface_type(), + errors_are_identical); + + case TYPE_CALL_MULTIPLE_RESULT: + if (reason != NULL) + *reason = "invalid use of multiple-value function call"; + return false; + + default: + go_unreachable(); + } +} + +// 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, true, NULL)) + return true; + + // A constant of abstract bool type may be mixed with any bool type. + if ((rhs->is_abstract_boolean_type() && lhs->is_boolean_type()) + || (lhs->is_abstract_boolean_type() && rhs->is_boolean_type())) + return true; + + // A constant of abstract string type may be mixed with any string + // type. + if ((rhs->is_abstract_string_type() && lhs->is_string_type()) + || (lhs->is_abstract_string_type() && rhs->is_string_type())) + return true; + + lhs = lhs->base(); + rhs = rhs->base(); + + // A constant of abstract integer, float, or complex type may be + // mixed with an integer, float, or complex type. + if ((rhs->is_abstract() + && (rhs->integer_type() != NULL + || rhs->float_type() != NULL + || rhs->complex_type() != NULL) + && (lhs->integer_type() != NULL + || lhs->float_type() != NULL + || lhs->complex_type() != NULL)) + || (lhs->is_abstract() + && (lhs->integer_type() != NULL + || lhs->float_type() != NULL + || lhs->complex_type() != NULL) + && (rhs->integer_type() != NULL + || rhs->float_type() != NULL + || rhs->complex_type() != NULL))) + return true; + + // The nil type may be compared to a pointer, an interface type, a + // slice type, a channel type, a map type, or a function type. + if (lhs->is_nil_type() + && (rhs->points_to() != NULL + || rhs->interface_type() != NULL + || rhs->is_slice_type() + || rhs->map_type() != NULL + || rhs->channel_type() != NULL + || rhs->function_type() != NULL)) + return true; + if (rhs->is_nil_type() + && (lhs->points_to() != NULL + || lhs->interface_type() != NULL + || lhs->is_slice_type() + || lhs->map_type() != NULL + || lhs->channel_type() != NULL + || lhs->function_type() != NULL)) + return true; + + return false; +} + +// Return true if a value with type T1 may be compared with a value of +// type T2. IS_EQUALITY_OP is true for == or !=, false for <, etc. + +bool +Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1, + const Type *t2, std::string *reason) +{ + if (t1 != t2 + && !Type::are_assignable(t1, t2, NULL) + && !Type::are_assignable(t2, t1, NULL)) + { + if (reason != NULL) + *reason = "incompatible types in binary expression"; + return false; + } + + if (!is_equality_op) + { + if (t1->integer_type() == NULL + && t1->float_type() == NULL + && !t1->is_string_type()) + { + if (reason != NULL) + *reason = _("invalid comparison of non-ordered type"); + return false; + } + } + else if (t1->is_slice_type() + || t1->map_type() != NULL + || t1->function_type() != NULL + || t2->is_slice_type() + || t2->map_type() != NULL + || t2->function_type() != NULL) + { + if (!t1->is_nil_type() && !t2->is_nil_type()) + { + if (reason != NULL) + { + if (t1->is_slice_type() || t2->is_slice_type()) + *reason = _("slice can only be compared to nil"); + else if (t1->map_type() != NULL || t2->map_type() != NULL) + *reason = _("map can only be compared to nil"); + else + *reason = _("func can only be compared to nil"); + + // Match 6g error messages. + if (t1->interface_type() != NULL || t2->interface_type() != NULL) + { + char buf[200]; + snprintf(buf, sizeof buf, _("invalid operation (%s)"), + reason->c_str()); + *reason = buf; + } + } + return false; + } + } + else + { + if (!t1->is_boolean_type() + && t1->integer_type() == NULL + && t1->float_type() == NULL + && t1->complex_type() == NULL + && !t1->is_string_type() + && t1->points_to() == NULL + && t1->channel_type() == NULL + && t1->interface_type() == NULL + && t1->struct_type() == NULL + && t1->array_type() == NULL + && !t1->is_nil_type()) + { + if (reason != NULL) + *reason = _("invalid comparison of non-comparable type"); + return false; + } + + if (t1->named_type() != NULL) + return t1->named_type()->named_type_is_comparable(reason); + else if (t2->named_type() != NULL) + return t2->named_type()->named_type_is_comparable(reason); + else if (t1->struct_type() != NULL) + { + const Struct_field_list* fields = t1->struct_type()->fields(); + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + if (!p->type()->is_comparable()) + { + if (reason != NULL) + *reason = _("invalid comparison of non-comparable struct"); + return false; + } + } + } + else if (t1->array_type() != NULL) + { + if (t1->array_type()->length()->is_nil_expression() + || !t1->array_type()->element_type()->is_comparable()) + { + if (reason != NULL) + *reason = _("invalid comparison of non-comparable array"); + return false; + } + } + } + + return true; +} + +// Return true if a value with type RHS may be assigned to a variable +// with type LHS. If CHECK_HIDDEN_FIELDS is true, check whether any +// hidden fields are modified. If REASON is not NULL, set *REASON to +// the reason the types are not assignable. + +bool +Type::are_assignable_check_hidden(const Type* lhs, const Type* rhs, + bool check_hidden_fields, + std::string* reason) +{ + // Do some checks first. Make sure the types are defined. + if (rhs != NULL && !rhs->is_undefined()) + { + if (rhs->is_void_type()) + { + if (reason != NULL) + *reason = "non-value used as value"; + return false; + } + if (rhs->is_call_multiple_result_type()) + { + if (reason != NULL) + reason->assign(_("multiple-value function call in " + "single-value context")); + return false; + } + } + + if (lhs != NULL && !lhs->is_undefined()) + { + // Any value may be assigned to the blank identifier. + if (lhs->is_sink_type()) + return true; + + // All fields of a struct must be exported, or the assignment + // must be in the same package. + if (check_hidden_fields && rhs != NULL && !rhs->is_undefined()) + { + if (lhs->has_hidden_fields(NULL, reason) + || rhs->has_hidden_fields(NULL, reason)) + return false; + } + } + + // Identical types are assignable. + 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(), true, reason)) + return true; + + // The types are assignable if LHS is an interface type and RHS + // implements the required methods. + const Interface_type* lhs_interface_type = lhs->interface_type(); + if (lhs_interface_type != NULL) + { + if (lhs_interface_type->implements_interface(rhs, reason)) + return true; + const Interface_type* rhs_interface_type = rhs->interface_type(); + if (rhs_interface_type != NULL + && lhs_interface_type->is_compatible_for_assign(rhs_interface_type, + reason)) + return true; + } + + // The type are assignable if RHS is a bidirectional channel type, + // LHS is a channel type, they have identical element types, and + // either LHS or RHS is not a named type. + if (lhs->channel_type() != NULL + && rhs->channel_type() != NULL + && rhs->channel_type()->may_send() + && rhs->channel_type()->may_receive() + && (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; + + // The nil type may be assigned to a pointer, function, slice, map, + // channel, or interface type. + if (rhs->is_nil_type() + && (lhs->points_to() != NULL + || lhs->function_type() != NULL + || lhs->is_slice_type() + || lhs->map_type() != NULL + || lhs->channel_type() != NULL + || lhs->interface_type() != NULL)) + return true; + + // An untyped numeric constant may be assigned to a numeric type if + // it is representable in that type. + if ((rhs->is_abstract() + && (rhs->integer_type() != NULL + || rhs->float_type() != NULL + || rhs->complex_type() != NULL)) + && (lhs->integer_type() != NULL + || lhs->float_type() != NULL + || lhs->complex_type() != NULL)) + return true; + + // Give some better error messages. + if (reason != NULL && reason->empty()) + { + if (rhs->interface_type() != NULL) + reason->assign(_("need explicit conversion")); + else if (lhs->named_type() != NULL && rhs->named_type() != NULL) + { + size_t len = (lhs->named_type()->name().length() + + rhs->named_type()->name().length() + + 100); + char* buf = new char[len]; + snprintf(buf, len, _("cannot use type %s as type %s"), + rhs->named_type()->message_name().c_str(), + lhs->named_type()->message_name().c_str()); + reason->assign(buf); + delete[] buf; + } + } + + return false; +} + +// Return true if a value with type RHS may be assigned to a variable +// with type LHS. If REASON is not NULL, set *REASON to the reason +// the types are not assignable. + +bool +Type::are_assignable(const Type* lhs, const Type* rhs, std::string* reason) +{ + return Type::are_assignable_check_hidden(lhs, rhs, false, reason); +} + +// Like are_assignable but don't check for hidden fields. + +bool +Type::are_assignable_hidden_ok(const Type* lhs, const Type* rhs, + std::string* reason) +{ + return Type::are_assignable_check_hidden(lhs, rhs, false, reason); +} + +// Return true if a value with type RHS may be converted to type LHS. +// If REASON is not NULL, set *REASON to the reason the types are not +// convertible. + +bool +Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) +{ + // The types are convertible if they are assignable. + if (Type::are_assignable(lhs, rhs, reason)) + return true; + + // 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(), true, reason)) + return true; + + // The types are convertible if they are both unnamed pointer types + // and their pointer base types have identical underlying types. + if (lhs->named_type() == NULL + && rhs->named_type() == NULL + && lhs->points_to() != NULL + && rhs->points_to() != NULL + && (lhs->points_to()->named_type() != NULL + || rhs->points_to()->named_type() != NULL) + && Type::are_identical(lhs->points_to()->base(), + rhs->points_to()->base(), + true, + reason)) + return true; + + // Integer and floating point types are convertible to each other. + if ((lhs->integer_type() != NULL || lhs->float_type() != NULL) + && (rhs->integer_type() != NULL || rhs->float_type() != NULL)) + return true; + + // Complex types are convertible to each other. + if (lhs->complex_type() != NULL && rhs->complex_type() != NULL) + return true; + + // An integer, or []byte, or []rune, may be converted to a string. + if (lhs->is_string_type()) + { + if (rhs->integer_type() != NULL) + return true; + if (rhs->is_slice_type()) + { + const Type* e = rhs->array_type()->element_type()->forwarded(); + if (e->integer_type() != NULL + && (e->integer_type()->is_byte() + || e->integer_type()->is_rune())) + return true; + } + } + + // A string may be converted to []byte or []rune. + if (rhs->is_string_type() && lhs->is_slice_type()) + { + const Type* e = lhs->array_type()->element_type()->forwarded(); + if (e->integer_type() != NULL + && (e->integer_type()->is_byte() || e->integer_type()->is_rune())) + return true; + } + + // An unsafe.Pointer type may be converted to any pointer type or to + // uintptr, and vice-versa. + if (lhs->is_unsafe_pointer_type() + && (rhs->points_to() != NULL + || (rhs->integer_type() != NULL + && rhs->forwarded() == Type::lookup_integer_type("uintptr")))) + return true; + if (rhs->is_unsafe_pointer_type() + && (lhs->points_to() != NULL + || (lhs->integer_type() != NULL + && lhs->forwarded() == Type::lookup_integer_type("uintptr")))) + return true; + + // Give a better error message. + if (reason != NULL) + { + if (reason->empty()) + *reason = "invalid type conversion"; + else + { + std::string s = "invalid type conversion ("; + s += *reason; + s += ')'; + *reason = s; + } + } + + return false; +} + +// Return whether this type has any hidden fields. This is only a +// possibility for a few types. + +bool +Type::has_hidden_fields(const Named_type* within, std::string* reason) const +{ + switch (this->forwarded()->classification_) + { + case TYPE_NAMED: + return this->named_type()->named_type_has_hidden_fields(reason); + case TYPE_STRUCT: + return this->struct_type()->struct_has_hidden_fields(within, reason); + case TYPE_ARRAY: + return this->array_type()->array_has_hidden_fields(within, reason); + default: + return false; + } +} + +// Return a hash code for the type to be used for method lookup. + +unsigned int +Type::hash_for_method(Gogo* gogo) const +{ + unsigned int ret = 0; + if (this->classification_ != TYPE_FORWARD) + ret += this->classification_; + return ret + this->do_hash_for_method(gogo); +} + +// Default implementation of do_hash_for_method. This is appropriate +// for types with no subfields. + +unsigned int +Type::do_hash_for_method(Gogo*) const +{ + return 0; +} + +// Return a hash code for a string, given a starting hash. + +unsigned int +Type::hash_string(const std::string& s, unsigned int h) +{ + const char* p = s.data(); + size_t len = s.length(); + for (; len > 0; --len) + { + h ^= *p++; + h*= 16777619; + } + return h; +} + +// A hash table mapping unnamed types to the backend representation of +// those types. + +Type::Type_btypes Type::type_btypes; + +// Return a tree representing this type. + +Btype* +Type::get_backend(Gogo* gogo) +{ + if (this->btype_ != NULL) + return this->btype_; + + if (this->forward_declaration_type() != NULL + || this->named_type() != NULL) + return this->get_btype_without_hash(gogo); + + if (this->is_error_type()) + return gogo->backend()->error_type(); + + // To avoid confusing the backend, translate all identical Go types + // to the same backend representation. We use a hash table to do + // that. There is no need to use the hash table for named types, as + // named types are only identical to themselves. + + std::pair val; + val.first = this; + val.second.btype = NULL; + val.second.is_placeholder = false; + std::pair ins = + Type::type_btypes.insert(val); + if (!ins.second && ins.first->second.btype != NULL) + { + // 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.btype == NULL) + { + ins.first->second.btype = bt; + ins.first->second.is_placeholder = false; + } + else + { + // We have already created a backend representation for this + // type. This can happen when an unnamed type is defined using + // a named type which in turns uses an identical unnamed type. + // Use the tree we created earlier and ignore the one we just + // built. + if (this->btype_ == bt) + this->btype_ = ins.first->second.btype; + bt = ins.first->second.btype; + } + + return bt; +} + +// Return the backend representation for a type without looking in the +// hash table for identical types. This is used for named types, +// since a named type is never identical to any other type. + +Btype* +Type::get_btype_without_hash(Gogo* gogo) +{ + if (this->btype_ == NULL) + { + Btype* bt = this->do_get_backend(gogo); + + // For a recursive function or pointer type, we will temporarily + // return a circular pointer type during the recursion. We + // don't want to record that for a forwarding type, as it may + // confuse us later. + if (this->forward_declaration_type() != NULL + && gogo->backend()->is_circular_pointer_type(bt)) + return bt; + + if (gogo == NULL || !gogo->named_types_are_converted()) + return bt; + + this->btype_ = bt; + } + return this->btype_; +} + +// Get the backend representation of a type without forcing the +// creation of the backend representation of all supporting types. +// This will return a backend type that has the correct size but may +// be incomplete. E.g., a pointer will just be a placeholder pointer, +// and will not contain the final representation of the type to which +// it points. This is used while converting all named types to the +// backend representation, to avoid problems with indirect references +// to types which are not yet complete. When this is called, the +// sizes of all direct references (e.g., a struct field) should be +// known, but the sizes of indirect references (e.g., the type to +// which a pointer points) may not. + +Btype* +Type::get_backend_placeholder(Gogo* gogo) +{ + if (gogo->named_types_are_converted()) + return this->get_backend(gogo); + if (this->btype_ != NULL) + return this->btype_; + + Btype* bt; + switch (this->classification_) + { + case TYPE_ERROR: + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_STRING: + case TYPE_NIL: + // 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 val; + val.first = this; + val.second.btype = NULL; + val.second.is_placeholder = false; + std::pair 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: + { + // A Go function type is a pointer to a struct type. + Location loc = this->function_type()->location(); + bt = gogo->backend()->placeholder_pointer_type("", loc, false); + } + break; + + case TYPE_POINTER: + { + Location loc = Linemap::unknown_location(); + bt = gogo->backend()->placeholder_pointer_type("", loc, false); + } + break; + + case TYPE_STRUCT: + // We don't have to make the struct itself be a placeholder. We + // are promised that we know the sizes of the struct fields. + // But we may have to use a placeholder for any particular + // struct field. + { + std::vector bfields; + get_backend_struct_fields(gogo, this->struct_type()->fields(), + true, &bfields); + bt = gogo->backend()->struct_type(bfields); + } + break; + + case TYPE_ARRAY: + if (this->is_slice_type()) + { + std::vector bfields; + get_backend_slice_fields(gogo, this->array_type(), true, &bfields); + bt = gogo->backend()->struct_type(bfields); + } + else + { + Btype* element = this->array_type()->get_backend_element(gogo, true); + Bexpression* len = this->array_type()->get_backend_length(gogo); + bt = gogo->backend()->array_type(element, len); + } + break; + + case TYPE_INTERFACE: + { + go_assert(!this->interface_type()->is_empty()); + std::vector bfields; + get_backend_interface_fields(gogo, this->interface_type(), true, + &bfields); + bt = gogo->backend()->struct_type(bfields); + } + break; + + case TYPE_SINK: + case TYPE_CALL_MULTIPLE_RESULT: + /* Note that various classifications were handled in the earlier + switch. */ + default: + go_unreachable(); + } + + 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; +} + +// Complete the backend representation. This is called for a type +// using a placeholder type. + +void +Type::finish_backend(Gogo* gogo, Btype *placeholder) +{ + switch (this->classification_) + { + case TYPE_ERROR: + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_STRING: + case TYPE_NIL: + go_unreachable(); + + case TYPE_FUNCTION: + { + Btype* bt = this->do_get_backend(gogo); + if (!gogo->backend()->set_placeholder_pointer_type(placeholder, bt)) + go_assert(saw_errors()); + } + break; + + case TYPE_POINTER: + { + Btype* bt = this->do_get_backend(gogo); + if (!gogo->backend()->set_placeholder_pointer_type(placeholder, bt)) + go_assert(saw_errors()); + } + break; + + case TYPE_STRUCT: + // The struct type itself is done, but we have to make sure that + // all the field types are converted. + this->struct_type()->finish_backend_fields(gogo); + break; + + case TYPE_ARRAY: + // The array type itself is done, but make sure the element type + // is converted. + this->array_type()->finish_backend_element(gogo); + break; + + case TYPE_MAP: + case TYPE_CHANNEL: + go_unreachable(); + + case TYPE_INTERFACE: + // The interface type itself is done, but make sure the method + // types are converted. + this->interface_type()->finish_backend_methods(gogo); + break; + + case TYPE_NAMED: + case TYPE_FORWARD: + go_unreachable(); + + case TYPE_SINK: + case TYPE_CALL_MULTIPLE_RESULT: + default: + go_unreachable(); + } + + this->btype_ = placeholder; +} + +// Return a pointer to the type descriptor for this type. + +Bexpression* +Type::type_descriptor_pointer(Gogo* gogo, Location location) +{ + Type* t = this->forwarded(); + if (t->named_type() != NULL && t->named_type()->is_alias()) + t = t->named_type()->real_type(); + if (t->type_descriptor_var_ == NULL) + { + t->make_type_descriptor_var(gogo); + go_assert(t->type_descriptor_var_ != NULL); + } + Bexpression* var_expr = + gogo->backend()->var_expression(t->type_descriptor_var_, location); + return gogo->backend()->address_expression(var_expr, location); +} + +// A mapping from unnamed types to type descriptor variables. + +Type::Type_descriptor_vars Type::type_descriptor_vars; + +// Build the type descriptor for this type. + +void +Type::make_type_descriptor_var(Gogo* gogo) +{ + go_assert(this->type_descriptor_var_ == NULL); + + Named_type* nt = this->named_type(); + + // We can have multiple instances of unnamed types, but we only want + // to emit the type descriptor once. We use a hash table. This is + // not necessary for named types, as they are unique, and we store + // the type descriptor in the type itself. + Bvariable** phash = NULL; + if (nt == NULL) + { + Bvariable* bvnull = NULL; + std::pair ins = + Type::type_descriptor_vars.insert(std::make_pair(this, bvnull)); + if (!ins.second) + { + // We've already build a type descriptor for this type. + this->type_descriptor_var_ = ins.first->second; + return; + } + phash = &ins.first->second; + } + + std::string var_name = this->type_descriptor_var_name(gogo, nt); + + // Build the contents of the type descriptor. + Expression* initializer = this->do_type_descriptor(gogo, NULL); + + Btype* initializer_btype = initializer->type()->get_backend(gogo); + + Location loc = nt == NULL ? Linemap::predeclared_location() : nt->location(); + + const Package* dummy; + if (this->type_descriptor_defined_elsewhere(nt, &dummy)) + { + this->type_descriptor_var_ = + gogo->backend()->immutable_struct_reference(var_name, + initializer_btype, + loc); + if (phash != NULL) + *phash = this->type_descriptor_var_; + return; + } + + // See if this type descriptor can appear in multiple packages. + bool is_common = false; + if (nt != NULL) + { + // We create the descriptor for a builtin type whenever we need + // it. + is_common = nt->is_builtin(); + } + else + { + // This is an unnamed type. The descriptor could be defined in + // any package where it is needed, and the linker will pick one + // descriptor to keep. + is_common = true; + } + + // We are going to build the type descriptor in this package. We + // must create the variable before we convert the initializer to the + // backend representation, because the initializer may refer to the + // type descriptor of this type. By setting type_descriptor_var_ we + // ensure that type_descriptor_pointer will work if called while + // converting INITIALIZER. + + this->type_descriptor_var_ = + gogo->backend()->immutable_struct(var_name, false, is_common, + initializer_btype, loc); + if (phash != NULL) + *phash = this->type_descriptor_var_; + + Translate_context context(gogo, NULL, NULL, NULL); + context.set_is_const(); + Bexpression* binitializer = tree_to_expr(initializer->get_tree(&context)); + + gogo->backend()->immutable_struct_set_init(this->type_descriptor_var_, + var_name, false, is_common, + initializer_btype, loc, + binitializer); +} + +// Return the name of the type descriptor variable. If NT is not +// NULL, use it to get the name. Otherwise this is an unnamed type. + +std::string +Type::type_descriptor_var_name(Gogo* gogo, Named_type* nt) +{ + if (nt == NULL) + return "__go_td_" + this->mangled_name(gogo); + + Named_object* no = nt->named_object(); + 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); + else + { + const std::string& pkgpath(no->package() == NULL + ? gogo->pkgpath_symbol() + : no->package()->pkgpath_symbol()); + ret.append(pkgpath); + ret.append(1, '.'); + if (in_function != NULL) + { + 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, '.'); + } + } + } + + // FIXME: This adds in pkgpath twice for hidden symbols, which is + // pointless. + const std::string& name(no->name()); + if (!Gogo::is_hidden_name(name)) + ret.append(name); + else + { + ret.append(1, '.'); + ret.append(Gogo::pkgpath_for_symbol(Gogo::hidden_name_pkgpath(name))); + ret.append(1, '.'); + ret.append(Gogo::unpack_hidden_name(name)); + } + + return ret; +} + +// Return true if this type descriptor is defined in a different +// package. If this returns true it sets *PACKAGE to the package. + +bool +Type::type_descriptor_defined_elsewhere(Named_type* nt, + const Package** package) +{ + if (nt != NULL) + { + if (nt->named_object()->package() != NULL) + { + // This is a named type defined in a different package. The + // type descriptor should be defined in that package. + *package = nt->named_object()->package(); + return true; + } + } + else + { + if (this->points_to() != NULL + && this->points_to()->named_type() != NULL + && this->points_to()->named_type()->named_object()->package() != NULL) + { + // This is an unnamed pointer to a named type defined in a + // different package. The descriptor should be defined in + // that package. + *package = this->points_to()->named_type()->named_object()->package(); + return true; + } + } + return false; +} + +// Return a composite literal for a type descriptor. + +Expression* +Type::type_descriptor(Gogo* gogo, Type* type) +{ + return type->do_type_descriptor(gogo, NULL); +} + +// Return a composite literal for a type descriptor with a name. + +Expression* +Type::named_type_descriptor(Gogo* gogo, Type* type, Named_type* name) +{ + go_assert(name != NULL && type->named_type() != name); + return type->do_type_descriptor(gogo, name); +} + +// Make a builtin struct type from a list of fields. The fields are +// pairs of a name and a type. + +Struct_type* +Type::make_builtin_struct_type(int nfields, ...) +{ + va_list ap; + va_start(ap, nfields); + + Location bloc = Linemap::predeclared_location(); + Struct_field_list* sfl = new Struct_field_list(); + for (int i = 0; i < nfields; i++) + { + const char* field_name = va_arg(ap, const char *); + Type* type = va_arg(ap, Type*); + sfl->push_back(Struct_field(Typed_identifier(field_name, type, bloc))); + } + + va_end(ap); + + return Type::make_struct_type(sfl, bloc); +} + +// A list of builtin named types. + +std::vector Type::named_builtin_types; + +// Make a builtin named type. + +Named_type* +Type::make_builtin_named_type(const char* name, Type* type) +{ + Location bloc = Linemap::predeclared_location(); + Named_object* no = Named_object::make_type(name, NULL, type, bloc); + Named_type* ret = no->type_value(); + Type::named_builtin_types.push_back(ret); + return ret; +} + +// Convert the named builtin types. + +void +Type::convert_builtin_named_types(Gogo* gogo) +{ + for (std::vector::const_iterator p = + Type::named_builtin_types.begin(); + p != Type::named_builtin_types.end(); + ++p) + { + bool r = (*p)->verify(); + go_assert(r); + (*p)->convert(gogo); + } +} + +// Return the type of a type descriptor. We should really tie this to +// runtime.Type rather than copying it. This must match commonType in +// libgo/go/runtime/type.go. + +Type* +Type::make_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Location bloc = Linemap::predeclared_location(); + + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint32_type = Type::lookup_integer_type("uint32"); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* string_type = Type::lookup_string_type(); + Type* pointer_string_type = Type::make_pointer_type(string_type); + + // This is an unnamed version of unsafe.Pointer. Perhaps we + // should use the named version instead, although that would + // require us to create the unsafe package if it has not been + // imported. It probably doesn't matter. + Type* void_type = Type::make_void_type(); + Type* unsafe_pointer_type = Type::make_pointer_type(void_type); + + // Forward declaration for the type descriptor type. + Named_object* named_type_descriptor_type = + Named_object::make_type_declaration("commonType", NULL, bloc); + Type* ft = Type::make_forward_declaration(named_type_descriptor_type); + Type* pointer_type_descriptor_type = Type::make_pointer_type(ft); + + // The type of a method on a concrete type. + Struct_type* method_type = + Type::make_builtin_struct_type(5, + "name", pointer_string_type, + "pkgPath", pointer_string_type, + "mtyp", pointer_type_descriptor_type, + "typ", pointer_type_descriptor_type, + "tfn", unsafe_pointer_type); + Named_type* named_method_type = + Type::make_builtin_named_type("method", method_type); + + // Information for types with a name or methods. + Type* slice_named_method_type = + Type::make_array_type(named_method_type, NULL); + Struct_type* uncommon_type = + Type::make_builtin_struct_type(3, + "name", pointer_string_type, + "pkgPath", pointer_string_type, + "methods", slice_named_method_type); + Named_type* named_uncommon_type = + Type::make_builtin_named_type("uncommonType", uncommon_type); + + Type* pointer_uncommon_type = + Type::make_pointer_type(named_uncommon_type); + + // The type descriptor type. + + Struct_type* type_descriptor_type = + Type::make_builtin_struct_type(10, + "Kind", uint8_type, + "align", uint8_type, + "fieldAlign", uint8_type, + "size", uintptr_type, + "hash", uint32_type, + "hashfn", uintptr_type, + "equalfn", uintptr_type, + "string", pointer_string_type, + "", pointer_uncommon_type, + "ptrToThis", + pointer_type_descriptor_type); + + Named_type* named = Type::make_builtin_named_type("commonType", + type_descriptor_type); + + named_type_descriptor_type->set_type_value(named); + + ret = named; + } + + return ret; +} + +// Make the type of a pointer to a type descriptor as represented in +// Go. + +Type* +Type::make_type_descriptor_ptr_type() +{ + static Type* ret; + if (ret == NULL) + ret = Type::make_pointer_type(Type::make_type_descriptor_type()); + return ret; +} + +// Set *HASH_FN and *EQUAL_FN to the runtime functions which compute a +// hash code for this type and which compare whether two values of +// this type are equal. If NAME is not NULL it is the name of this +// type. HASH_FNTYPE and EQUAL_FNTYPE are the types of these +// functions, for convenience; they may be NULL. + +void +Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, + Function_type* equal_fntype, Named_object** hash_fn, + Named_object** equal_fn) +{ + if (hash_fntype == NULL || equal_fntype == NULL) + { + Location bloc = Linemap::predeclared_location(); + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* void_type = Type::make_void_type(); + Type* unsafe_pointer_type = Type::make_pointer_type(void_type); + + if (hash_fntype == NULL) + { + Typed_identifier_list* params = new Typed_identifier_list(); + params->push_back(Typed_identifier("key", unsafe_pointer_type, + bloc)); + params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); + + Typed_identifier_list* results = new Typed_identifier_list(); + results->push_back(Typed_identifier("", uintptr_type, bloc)); + + hash_fntype = Type::make_function_type(NULL, params, results, bloc); + } + if (equal_fntype == NULL) + { + Typed_identifier_list* params = new Typed_identifier_list(); + params->push_back(Typed_identifier("key1", unsafe_pointer_type, + bloc)); + params->push_back(Typed_identifier("key2", unsafe_pointer_type, + bloc)); + params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); + + Typed_identifier_list* results = new Typed_identifier_list(); + results->push_back(Typed_identifier("", Type::lookup_bool_type(), + bloc)); + + equal_fntype = Type::make_function_type(NULL, params, results, bloc); + } + } + + const char* hash_fnname; + const char* equal_fnname; + if (this->compare_is_identity(gogo)) + { + hash_fnname = "__go_type_hash_identity"; + equal_fnname = "__go_type_equal_identity"; + } + else if (!this->is_comparable()) + { + hash_fnname = "__go_type_hash_error"; + equal_fnname = "__go_type_equal_error"; + } + else + { + switch (this->base()->classification()) + { + case Type::TYPE_ERROR: + case Type::TYPE_VOID: + case Type::TYPE_NIL: + case Type::TYPE_FUNCTION: + case Type::TYPE_MAP: + // For these types is_comparable should have returned false. + go_unreachable(); + + case Type::TYPE_BOOLEAN: + case Type::TYPE_INTEGER: + case Type::TYPE_POINTER: + case Type::TYPE_CHANNEL: + // For these types compare_is_identity should have returned true. + go_unreachable(); + + case Type::TYPE_FLOAT: + hash_fnname = "__go_type_hash_float"; + equal_fnname = "__go_type_equal_float"; + break; + + case Type::TYPE_COMPLEX: + hash_fnname = "__go_type_hash_complex"; + equal_fnname = "__go_type_equal_complex"; + break; + + case Type::TYPE_STRING: + hash_fnname = "__go_type_hash_string"; + equal_fnname = "__go_type_equal_string"; + break; + + case Type::TYPE_STRUCT: + { + // This is a struct which can not be compared using a + // simple identity function. We need to build a function + // for comparison. + this->specific_type_functions(gogo, name, hash_fntype, + equal_fntype, hash_fn, equal_fn); + return; + } + + case Type::TYPE_ARRAY: + if (this->is_slice_type()) + { + // Type::is_compatible_for_comparison should have + // returned false. + go_unreachable(); + } + else + { + // This is an array which can not be compared using a + // simple identity function. We need to build a + // function for comparison. + this->specific_type_functions(gogo, name, hash_fntype, + equal_fntype, hash_fn, equal_fn); + return; + } + break; + + case Type::TYPE_INTERFACE: + if (this->interface_type()->is_empty()) + { + hash_fnname = "__go_type_hash_empty_interface"; + equal_fnname = "__go_type_equal_empty_interface"; + } + else + { + hash_fnname = "__go_type_hash_interface"; + equal_fnname = "__go_type_equal_interface"; + } + break; + + case Type::TYPE_NAMED: + case Type::TYPE_FORWARD: + go_unreachable(); + + default: + go_unreachable(); + } + } + + + Location bloc = Linemap::predeclared_location(); + *hash_fn = Named_object::make_function_declaration(hash_fnname, NULL, + hash_fntype, bloc); + (*hash_fn)->func_declaration_value()->set_asm_name(hash_fnname); + *equal_fn = Named_object::make_function_declaration(equal_fnname, NULL, + equal_fntype, bloc); + (*equal_fn)->func_declaration_value()->set_asm_name(equal_fnname); +} + +// A hash table mapping types to the specific hash functions. + +Type::Type_functions Type::type_functions_table; + +// Handle a type function which is specific to a type: a struct or +// array which can not use an identity comparison. + +void +Type::specific_type_functions(Gogo* gogo, Named_type* name, + Function_type* hash_fntype, + Function_type* equal_fntype, + Named_object** hash_fn, + Named_object** equal_fn) +{ + Hash_equal_fn fnull(NULL, NULL); + std::pair val(name != NULL ? name : this, fnull); + std::pair ins = + Type::type_functions_table.insert(val); + if (!ins.second) + { + // We already have functions for this type + *hash_fn = ins.first->second.first; + *equal_fn = ins.first->second.second; + return; + } + + std::string base_name; + if (name == NULL) + { + // Mangled names can have '.' if they happen to refer to named + // types in some way. That's fine if this is simply a named + // type, but otherwise it will confuse the code that builds + // function identifiers. Remove '.' when necessary. + base_name = this->mangled_name(gogo); + size_t i; + while ((i = base_name.find('.')) != std::string::npos) + base_name[i] = '$'; + base_name = gogo->pack_hidden_name(base_name, false); + } + else + { + // This name is already hidden or not as appropriate. + base_name = name->name(); + 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()); + 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"; + + Location bloc = Linemap::predeclared_location(); + + const Package* package = NULL; + bool is_defined_elsewhere = + this->type_descriptor_defined_elsewhere(name, &package); + if (is_defined_elsewhere) + { + *hash_fn = Named_object::make_function_declaration(hash_name, package, + hash_fntype, bloc); + *equal_fn = Named_object::make_function_declaration(equal_name, package, + equal_fntype, bloc); + } + else + { + *hash_fn = gogo->declare_package_function(hash_name, hash_fntype, bloc); + *equal_fn = gogo->declare_package_function(equal_name, equal_fntype, + bloc); + } + + ins.first->second.first = *hash_fn; + ins.first->second.second = *equal_fn; + + if (!is_defined_elsewhere) + { + if (gogo->in_global_scope()) + this->write_specific_type_functions(gogo, name, hash_name, hash_fntype, + equal_name, equal_fntype); + else + gogo->queue_specific_type_function(this, name, hash_name, hash_fntype, + equal_name, equal_fntype); + } +} + +// Write the hash and equality functions for a type which needs to be +// written specially. + +void +Type::write_specific_type_functions(Gogo* gogo, Named_type* name, + const std::string& hash_name, + Function_type* hash_fntype, + const std::string& equal_name, + Function_type* equal_fntype) +{ + Location bloc = Linemap::predeclared_location(); + + if (gogo->specific_type_functions_are_written()) + { + go_assert(saw_errors()); + return; + } + + Named_object* hash_fn = gogo->start_function(hash_name, hash_fntype, false, + bloc); + gogo->start_block(bloc); + + if (name != NULL && name->real_type()->named_type() != NULL) + this->write_named_hash(gogo, name, hash_fntype, equal_fntype); + else if (this->struct_type() != NULL) + this->struct_type()->write_hash_function(gogo, name, hash_fntype, + equal_fntype); + else if (this->array_type() != NULL) + this->array_type()->write_hash_function(gogo, name, hash_fntype, + equal_fntype); + else + go_unreachable(); + + Block* b = gogo->finish_block(bloc); + gogo->add_block(b, bloc); + gogo->lower_block(hash_fn, b); + gogo->finish_function(bloc); + + Named_object *equal_fn = gogo->start_function(equal_name, equal_fntype, + false, bloc); + gogo->start_block(bloc); + + if (name != NULL && name->real_type()->named_type() != NULL) + this->write_named_equal(gogo, name); + else if (this->struct_type() != NULL) + this->struct_type()->write_equal_function(gogo, name); + else if (this->array_type() != NULL) + this->array_type()->write_equal_function(gogo, name); + else + go_unreachable(); + + b = gogo->finish_block(bloc); + gogo->add_block(b, bloc); + gogo->lower_block(equal_fn, b); + gogo->finish_function(bloc); +} + +// Write a hash function that simply calls the hash function for a +// named type. This is used when one named type is defined as +// another. This ensures that this case works when the other named +// type is defined in another package and relies on calling hash +// functions defined only in that package. + +void +Type::write_named_hash(Gogo* gogo, Named_type* name, + Function_type* hash_fntype, Function_type* equal_fntype) +{ + Location bloc = Linemap::predeclared_location(); + + Named_type* base_type = name->real_type()->named_type(); + go_assert(base_type != NULL); + + // The pointer to the type we are going to hash. This is an + // unsafe.Pointer. + Named_object* key_arg = gogo->lookup("key", NULL); + go_assert(key_arg != NULL); + + // The size of the type we are going to hash. + Named_object* keysz_arg = gogo->lookup("key_size", NULL); + go_assert(keysz_arg != NULL); + + Named_object* hash_fn; + Named_object* equal_fn; + name->real_type()->type_functions(gogo, base_type, hash_fntype, equal_fntype, + &hash_fn, &equal_fn); + + // Call the hash function for the base type. + Expression* key_ref = Expression::make_var_reference(key_arg, bloc); + Expression* keysz_ref = Expression::make_var_reference(keysz_arg, bloc); + Expression_list* args = new Expression_list(); + args->push_back(key_ref); + args->push_back(keysz_ref); + Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); + Expression* call = Expression::make_call(func, args, false, bloc); + + // Return the hash of the base type. + Expression_list* vals = new Expression_list(); + vals->push_back(call); + Statement* s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); +} + +// Write an equality function that simply calls the equality function +// for a named type. This is used when one named type is defined as +// another. This ensures that this case works when the other named +// type is defined in another package and relies on calling equality +// functions defined only in that package. + +void +Type::write_named_equal(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + // The pointers to the types we are going to compare. These have + // type unsafe.Pointer. + Named_object* key1_arg = gogo->lookup("key1", NULL); + Named_object* key2_arg = gogo->lookup("key2", NULL); + go_assert(key1_arg != NULL && key2_arg != NULL); + + Named_type* base_type = name->real_type()->named_type(); + go_assert(base_type != NULL); + + // Build temporaries with the base type. + Type* pt = Type::make_pointer_type(base_type); + + Expression* ref = Expression::make_var_reference(key1_arg, bloc); + ref = Expression::make_cast(pt, ref, bloc); + Temporary_statement* p1 = Statement::make_temporary(pt, ref, bloc); + gogo->add_statement(p1); + + ref = Expression::make_var_reference(key2_arg, bloc); + ref = Expression::make_cast(pt, ref, bloc); + Temporary_statement* p2 = Statement::make_temporary(pt, ref, bloc); + gogo->add_statement(p2); + + // Compare the values for equality. + Expression* t1 = Expression::make_temporary_reference(p1, bloc); + t1 = Expression::make_unary(OPERATOR_MULT, t1, bloc); + + Expression* t2 = Expression::make_temporary_reference(p2, bloc); + t2 = Expression::make_unary(OPERATOR_MULT, t2, bloc); + + Expression* cond = Expression::make_binary(OPERATOR_EQEQ, t1, t2, bloc); + + // Return the equality comparison. + Expression_list* vals = new Expression_list(); + vals->push_back(cond); + Statement* s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); +} + +// Return a composite literal for the type descriptor for a plain type +// of kind RUNTIME_TYPE_KIND named NAME. + +Expression* +Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, + Named_type* name, const Methods* methods, + bool only_value_methods) +{ + Location bloc = Linemap::predeclared_location(); + + Type* td_type = Type::make_type_descriptor_type(); + const Struct_field_list* fields = td_type->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(9); + + if (!this->has_pointer()) + runtime_type_kind |= RUNTIME_TYPE_KIND_NO_POINTERS; + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("Kind")); + mpz_t iv; + mpz_init_set_ui(iv, runtime_type_kind); + vals->push_back(Expression::make_integer(&iv, p->type(), bloc)); + + ++p; + go_assert(p->is_field_name("align")); + Expression::Type_info type_info = Expression::TYPE_INFO_ALIGNMENT; + vals->push_back(Expression::make_type_info(this, type_info)); + + ++p; + go_assert(p->is_field_name("fieldAlign")); + type_info = Expression::TYPE_INFO_FIELD_ALIGNMENT; + vals->push_back(Expression::make_type_info(this, type_info)); + + ++p; + go_assert(p->is_field_name("size")); + type_info = Expression::TYPE_INFO_SIZE; + vals->push_back(Expression::make_type_info(this, type_info)); + + ++p; + go_assert(p->is_field_name("hash")); + unsigned int h; + if (name != NULL) + h = name->hash_for_method(gogo); + else + h = this->hash_for_method(gogo); + mpz_set_ui(iv, h); + vals->push_back(Expression::make_integer(&iv, p->type(), bloc)); + + ++p; + go_assert(p->is_field_name("hashfn")); + Function_type* hash_fntype = p->type()->function_type(); + + ++p; + go_assert(p->is_field_name("equalfn")); + Function_type* equal_fntype = p->type()->function_type(); + + Named_object* hash_fn; + Named_object* equal_fn; + this->type_functions(gogo, name, hash_fntype, equal_fntype, &hash_fn, + &equal_fn); + vals->push_back(Expression::make_func_code_reference(hash_fn, bloc)); + vals->push_back(Expression::make_func_code_reference(equal_fn, bloc)); + + ++p; + go_assert(p->is_field_name("string")); + Expression* s = Expression::make_string((name != NULL + ? name->reflection(gogo) + : this->reflection(gogo)), + bloc); + vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); + + ++p; + go_assert(p->is_field_name("uncommonType")); + if (name == NULL && methods == NULL) + vals->push_back(Expression::make_nil(bloc)); + else + { + if (methods == NULL) + methods = name->methods(); + vals->push_back(this->uncommon_type_constructor(gogo, + p->type()->deref(), + name, methods, + only_value_methods)); + } + + ++p; + go_assert(p->is_field_name("ptrToThis")); + if (name == NULL) + vals->push_back(Expression::make_nil(bloc)); + else + { + Type* pt = Type::make_pointer_type(name); + vals->push_back(Expression::make_type_descriptor(pt, bloc)); + } + + ++p; + go_assert(p == fields->end()); + + mpz_clear(iv); + + return Expression::make_struct_composite_literal(td_type, vals, bloc); +} + +// Return a composite literal for the uncommon type information for +// this type. UNCOMMON_STRUCT_TYPE is the type of the uncommon type +// struct. If name is not NULL, it is the name of the type. If +// METHODS is not NULL, it is the list of methods. ONLY_VALUE_METHODS +// is true if only value methods should be included. At least one of +// NAME and METHODS must not be NULL. + +Expression* +Type::uncommon_type_constructor(Gogo* gogo, Type* uncommon_type, + Named_type* name, const Methods* methods, + bool only_value_methods) const +{ + Location bloc = Linemap::predeclared_location(); + + const Struct_field_list* fields = uncommon_type->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(3); + + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("name")); + + ++p; + go_assert(p->is_field_name("pkgPath")); + + if (name == NULL) + { + vals->push_back(Expression::make_nil(bloc)); + vals->push_back(Expression::make_nil(bloc)); + } + else + { + Named_object* no = name->named_object(); + std::string n = Gogo::unpack_hidden_name(no->name()); + Expression* s = Expression::make_string(n, bloc); + vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); + + if (name->is_builtin()) + vals->push_back(Expression::make_nil(bloc)); + else + { + const Package* package = no->package(); + const std::string& pkgpath(package == NULL + ? gogo->pkgpath() + : package->pkgpath()); + n.assign(pkgpath); + 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(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)); + } + } + + ++p; + go_assert(p->is_field_name("methods")); + vals->push_back(this->methods_constructor(gogo, p->type(), methods, + only_value_methods)); + + ++p; + go_assert(p == fields->end()); + + Expression* r = Expression::make_struct_composite_literal(uncommon_type, + vals, bloc); + return Expression::make_unary(OPERATOR_AND, r, bloc); +} + +// Sort methods by name. + +class Sort_methods +{ + public: + bool + operator()(const std::pair& m1, + const std::pair& m2) const + { return m1.first < m2.first; } +}; + +// Return a composite literal for the type method table for this type. +// METHODS_TYPE is the type of the table, and is a slice type. +// METHODS is the list of methods. If ONLY_VALUE_METHODS is true, +// then only value methods are used. + +Expression* +Type::methods_constructor(Gogo* gogo, Type* methods_type, + const Methods* methods, + bool only_value_methods) const +{ + Location bloc = Linemap::predeclared_location(); + + std::vector > smethods; + if (methods != NULL) + { + smethods.reserve(methods->count()); + for (Methods::const_iterator p = methods->begin(); + p != methods->end(); + ++p) + { + if (p->second->is_ambiguous()) + continue; + if (only_value_methods && !p->second->is_value_method()) + continue; + + // This is where we implement the magic //go:nointerface + // comment. If we saw that comment, we don't add this + // method to the type descriptor. + if (p->second->nointerface()) + continue; + + smethods.push_back(std::make_pair(p->first, p->second)); + } + } + + if (smethods.empty()) + return Expression::make_slice_composite_literal(methods_type, NULL, bloc); + + std::sort(smethods.begin(), smethods.end(), Sort_methods()); + + Type* method_type = methods_type->array_type()->element_type(); + + Expression_list* vals = new Expression_list(); + vals->reserve(smethods.size()); + for (std::vector >::const_iterator p + = smethods.begin(); + p != smethods.end(); + ++p) + vals->push_back(this->method_constructor(gogo, method_type, p->first, + p->second, only_value_methods)); + + return Expression::make_slice_composite_literal(methods_type, vals, bloc); +} + +// Return a composite literal for a single method. METHOD_TYPE is the +// type of the entry. METHOD_NAME is the name of the method and M is +// the method information. + +Expression* +Type::method_constructor(Gogo*, Type* method_type, + const std::string& method_name, + const Method* m, + bool only_value_methods) const +{ + Location bloc = Linemap::predeclared_location(); + + const Struct_field_list* fields = method_type->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(5); + + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("name")); + const std::string n = Gogo::unpack_hidden_name(method_name); + Expression* s = Expression::make_string(n, bloc); + vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); + + ++p; + go_assert(p->is_field_name("pkgPath")); + if (!Gogo::is_hidden_name(method_name)) + vals->push_back(Expression::make_nil(bloc)); + else + { + s = Expression::make_string(Gogo::hidden_name_pkgpath(method_name), + bloc); + vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); + } + + Named_object* no = (m->needs_stub_method() + ? m->stub_object() + : m->named_object()); + + Function_type* mtype; + if (no->is_function()) + mtype = no->func_value()->type(); + else + mtype = no->func_declaration_value()->type(); + go_assert(mtype->is_method()); + Type* nonmethod_type = mtype->copy_without_receiver(); + + ++p; + go_assert(p->is_field_name("mtyp")); + vals->push_back(Expression::make_type_descriptor(nonmethod_type, bloc)); + + ++p; + go_assert(p->is_field_name("typ")); + bool want_pointer_receiver = !only_value_methods && m->is_value_method(); + nonmethod_type = mtype->copy_with_receiver_as_param(want_pointer_receiver); + vals->push_back(Expression::make_type_descriptor(nonmethod_type, bloc)); + + ++p; + go_assert(p->is_field_name("tfn")); + vals->push_back(Expression::make_func_code_reference(no, bloc)); + + ++p; + go_assert(p == fields->end()); + + return Expression::make_struct_composite_literal(method_type, vals, bloc); +} + +// Return a composite literal for the type descriptor of a plain type. +// RUNTIME_TYPE_KIND is the value of the kind field. If NAME is not +// NULL, it is the name to use as well as the list of methods. + +Expression* +Type::plain_type_descriptor(Gogo* gogo, int runtime_type_kind, + Named_type* name) +{ + return this->type_descriptor_constructor(gogo, runtime_type_kind, + name, NULL, true); +} + +// Return the type reflection string for this type. + +std::string +Type::reflection(Gogo* gogo) const +{ + std::string ret; + + // The do_reflection virtual function should set RET to the + // reflection string. + this->do_reflection(gogo, &ret); + + return ret; +} + +// Return a mangled name for the type. + +std::string +Type::mangled_name(Gogo* gogo) const +{ + std::string ret; + + // The do_mangled_name virtual function should set RET to the + // mangled name. For a composite type it should append a code for + // the composition and then call do_mangled_name on the components. + this->do_mangled_name(gogo, &ret); + + return ret; +} + +// Return whether the backend size of the type is known. + +bool +Type::is_backend_type_size_known(Gogo* gogo) +{ + switch (this->classification_) + { + case TYPE_ERROR: + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_STRING: + case TYPE_FUNCTION: + case TYPE_POINTER: + case TYPE_NIL: + case TYPE_MAP: + case TYPE_CHANNEL: + case TYPE_INTERFACE: + return true; + + case TYPE_STRUCT: + { + const Struct_field_list* fields = this->struct_type()->fields(); + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + if (!pf->type()->is_backend_type_size_known(gogo)) + return false; + return true; + } + + case TYPE_ARRAY: + { + const Array_type* at = this->array_type(); + if (at->length() == NULL) + return true; + else + { + Numeric_constant nc; + if (!at->length()->numeric_constant_value(&nc)) + return false; + mpz_t ival; + if (!nc.to_int(&ival)) + return false; + mpz_clear(ival); + return at->element_type()->is_backend_type_size_known(gogo); + } + } + + case TYPE_NAMED: + this->named_type()->convert(gogo); + return this->named_type()->is_named_backend_type_size_known(); + + case TYPE_FORWARD: + { + Forward_declaration_type* fdt = this->forward_declaration_type(); + return fdt->real_type()->is_backend_type_size_known(gogo); + } + + case TYPE_SINK: + case TYPE_CALL_MULTIPLE_RESULT: + go_unreachable(); + + default: + go_unreachable(); + } +} + +// If the size of the type can be determined, set *PSIZE to the size +// in bytes and return true. Otherwise, return false. This queries +// the backend. + +bool +Type::backend_type_size(Gogo* gogo, unsigned int *psize) +{ + if (!this->is_backend_type_size_known(gogo)) + return false; + Btype* bt = this->get_backend_placeholder(gogo); + size_t size = gogo->backend()->type_size(bt); + *psize = static_cast(size); + if (*psize != size) + return false; + return true; +} + +// If the alignment of the type can be determined, set *PALIGN to +// the alignment in bytes and return true. Otherwise, return false. + +bool +Type::backend_type_align(Gogo* gogo, unsigned int *palign) +{ + if (!this->is_backend_type_size_known(gogo)) + return false; + Btype* bt = this->get_backend_placeholder(gogo); + size_t align = gogo->backend()->type_alignment(bt); + *palign = static_cast(align); + if (*palign != align) + return false; + return true; +} + +// Like backend_type_align, but return the alignment when used as a +// field. + +bool +Type::backend_type_field_align(Gogo* gogo, unsigned int *palign) +{ + if (!this->is_backend_type_size_known(gogo)) + return false; + Btype* bt = this->get_backend_placeholder(gogo); + size_t a = gogo->backend()->type_field_alignment(bt); + *palign = static_cast(a); + if (*palign != a) + return false; + return true; +} + +// Default function to export a type. + +void +Type::do_export(Export*) const +{ + go_unreachable(); +} + +// Import a type. + +Type* +Type::import_type(Import* imp) +{ + if (imp->match_c_string("(")) + return Function_type::do_import(imp); + else if (imp->match_c_string("*")) + return Pointer_type::do_import(imp); + else if (imp->match_c_string("struct ")) + return Struct_type::do_import(imp); + else if (imp->match_c_string("[")) + return Array_type::do_import(imp); + else if (imp->match_c_string("map ")) + return Map_type::do_import(imp); + else if (imp->match_c_string("chan ")) + return Channel_type::do_import(imp); + else if (imp->match_c_string("interface")) + return Interface_type::do_import(imp); + else + { + error_at(imp->location(), "import error: expected type"); + return Type::make_error_type(); + } +} + +// A type used to indicate a parsing error. This exists to simplify +// later error detection. + +class Error_type : public Type +{ + public: + Error_type() + : Type(TYPE_ERROR) + { } + + protected: + bool + do_compare_is_identity(Gogo*) + { return false; } + + Btype* + do_get_backend(Gogo* gogo) + { return gogo->backend()->error_type(); } + + Expression* + do_type_descriptor(Gogo*, Named_type*) + { return Expression::make_error(Linemap::predeclared_location()); } + + void + do_reflection(Gogo*, std::string*) const + { go_assert(saw_errors()); } + + void + do_mangled_name(Gogo*, std::string* ret) const + { ret->push_back('E'); } +}; + +Type* +Type::make_error_type() +{ + static Error_type singleton_error_type; + return &singleton_error_type; +} + +// The void type. + +class Void_type : public Type +{ + public: + Void_type() + : Type(TYPE_VOID) + { } + + protected: + bool + do_compare_is_identity(Gogo*) + { return false; } + + Btype* + do_get_backend(Gogo* gogo) + { return gogo->backend()->void_type(); } + + Expression* + do_type_descriptor(Gogo*, Named_type*) + { go_unreachable(); } + + void + do_reflection(Gogo*, std::string*) const + { } + + void + do_mangled_name(Gogo*, std::string* ret) const + { ret->push_back('v'); } +}; + +Type* +Type::make_void_type() +{ + static Void_type singleton_void_type; + return &singleton_void_type; +} + +// The boolean type. + +class Boolean_type : public Type +{ + public: + Boolean_type() + : Type(TYPE_BOOLEAN) + { } + + protected: + bool + do_compare_is_identity(Gogo*) + { return true; } + + Btype* + do_get_backend(Gogo* gogo) + { return gogo->backend()->bool_type(); } + + Expression* + do_type_descriptor(Gogo*, Named_type* name); + + // We should not be asked for the reflection string of a basic type. + void + do_reflection(Gogo*, std::string* ret) const + { ret->append("bool"); } + + void + do_mangled_name(Gogo*, std::string* ret) const + { ret->push_back('b'); } +}; + +// Make the type descriptor. + +Expression* +Boolean_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + if (name != NULL) + return this->plain_type_descriptor(gogo, RUNTIME_TYPE_KIND_BOOL, name); + else + { + Named_object* no = gogo->lookup_global("bool"); + go_assert(no != NULL); + return Type::type_descriptor(gogo, no->type_value()); + } +} + +Type* +Type::make_boolean_type() +{ + static Boolean_type boolean_type; + return &boolean_type; +} + +// The named type "bool". + +static Named_type* named_bool_type; + +// Get the named type "bool". + +Named_type* +Type::lookup_bool_type() +{ + return named_bool_type; +} + +// Make the named type "bool". + +Named_type* +Type::make_named_bool_type() +{ + Type* bool_type = Type::make_boolean_type(); + Named_object* named_object = + Named_object::make_type("bool", NULL, bool_type, + Linemap::predeclared_location()); + Named_type* named_type = named_object->type_value(); + named_bool_type = named_type; + return named_type; +} + +// Class Integer_type. + +Integer_type::Named_integer_types Integer_type::named_integer_types; + +// Create a new integer type. Non-abstract integer types always have +// names. + +Named_type* +Integer_type::create_integer_type(const char* name, bool is_unsigned, + int bits, int runtime_type_kind) +{ + Integer_type* integer_type = new Integer_type(false, is_unsigned, bits, + runtime_type_kind); + std::string sname(name); + Named_object* named_object = + Named_object::make_type(sname, NULL, integer_type, + Linemap::predeclared_location()); + Named_type* named_type = named_object->type_value(); + std::pair ins = + Integer_type::named_integer_types.insert(std::make_pair(sname, named_type)); + go_assert(ins.second); + return named_type; +} + +// Look up an existing integer type. + +Named_type* +Integer_type::lookup_integer_type(const char* name) +{ + Named_integer_types::const_iterator p = + Integer_type::named_integer_types.find(name); + go_assert(p != Integer_type::named_integer_types.end()); + return p->second; +} + +// Create a new abstract integer type. + +Integer_type* +Integer_type::create_abstract_integer_type() +{ + static Integer_type* abstract_type; + if (abstract_type == NULL) + { + Type* int_type = Type::lookup_integer_type("int"); + abstract_type = new Integer_type(true, false, + int_type->integer_type()->bits(), + RUNTIME_TYPE_KIND_INT); + } + return abstract_type; +} + +// Create a new abstract character type. + +Integer_type* +Integer_type::create_abstract_character_type() +{ + static Integer_type* abstract_type; + if (abstract_type == NULL) + { + abstract_type = new Integer_type(true, false, 32, + RUNTIME_TYPE_KIND_INT32); + abstract_type->set_is_rune(); + } + return abstract_type; +} + +// Integer type compatibility. + +bool +Integer_type::is_identical(const Integer_type* t) const +{ + if (this->is_unsigned_ != t->is_unsigned_ || this->bits_ != t->bits_) + return false; + return this->is_abstract_ == t->is_abstract_; +} + +// Hash code. + +unsigned int +Integer_type::do_hash_for_method(Gogo*) const +{ + return ((this->bits_ << 4) + + ((this->is_unsigned_ ? 1 : 0) << 8) + + ((this->is_abstract_ ? 1 : 0) << 9)); +} + +// Convert an Integer_type to the backend representation. + +Btype* +Integer_type::do_get_backend(Gogo* gogo) +{ + if (this->is_abstract_) + { + go_assert(saw_errors()); + return gogo->backend()->error_type(); + } + return gogo->backend()->integer_type(this->is_unsigned_, this->bits_); +} + +// The type descriptor for an integer type. Integer types are always +// named. + +Expression* +Integer_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + go_assert(name != NULL || saw_errors()); + return this->plain_type_descriptor(gogo, this->runtime_type_kind_, name); +} + +// We should not be asked for the reflection string of a basic type. + +void +Integer_type::do_reflection(Gogo*, std::string*) const +{ + go_assert(saw_errors()); +} + +// Mangled name. + +void +Integer_type::do_mangled_name(Gogo*, std::string* ret) const +{ + char buf[100]; + snprintf(buf, sizeof buf, "i%s%s%de", + this->is_abstract_ ? "a" : "", + this->is_unsigned_ ? "u" : "", + this->bits_); + ret->append(buf); +} + +// Make an integer type. + +Named_type* +Type::make_integer_type(const char* name, bool is_unsigned, int bits, + int runtime_type_kind) +{ + return Integer_type::create_integer_type(name, is_unsigned, bits, + runtime_type_kind); +} + +// Make an abstract integer type. + +Integer_type* +Type::make_abstract_integer_type() +{ + return Integer_type::create_abstract_integer_type(); +} + +// Make an abstract character type. + +Integer_type* +Type::make_abstract_character_type() +{ + return Integer_type::create_abstract_character_type(); +} + +// Look up an integer type. + +Named_type* +Type::lookup_integer_type(const char* name) +{ + return Integer_type::lookup_integer_type(name); +} + +// Class Float_type. + +Float_type::Named_float_types Float_type::named_float_types; + +// Create a new float type. Non-abstract float types always have +// names. + +Named_type* +Float_type::create_float_type(const char* name, int bits, + int runtime_type_kind) +{ + Float_type* float_type = new Float_type(false, bits, runtime_type_kind); + std::string sname(name); + Named_object* named_object = + Named_object::make_type(sname, NULL, float_type, + Linemap::predeclared_location()); + Named_type* named_type = named_object->type_value(); + std::pair ins = + Float_type::named_float_types.insert(std::make_pair(sname, named_type)); + go_assert(ins.second); + return named_type; +} + +// Look up an existing float type. + +Named_type* +Float_type::lookup_float_type(const char* name) +{ + Named_float_types::const_iterator p = + Float_type::named_float_types.find(name); + go_assert(p != Float_type::named_float_types.end()); + return p->second; +} + +// Create a new abstract float type. + +Float_type* +Float_type::create_abstract_float_type() +{ + static Float_type* abstract_type; + if (abstract_type == NULL) + abstract_type = new Float_type(true, 64, RUNTIME_TYPE_KIND_FLOAT64); + return abstract_type; +} + +// Whether this type is identical with T. + +bool +Float_type::is_identical(const Float_type* t) const +{ + if (this->bits_ != t->bits_) + return false; + return this->is_abstract_ == t->is_abstract_; +} + +// Hash code. + +unsigned int +Float_type::do_hash_for_method(Gogo*) const +{ + return (this->bits_ << 4) + ((this->is_abstract_ ? 1 : 0) << 8); +} + +// Convert to the backend representation. + +Btype* +Float_type::do_get_backend(Gogo* gogo) +{ + return gogo->backend()->float_type(this->bits_); +} + +// The type descriptor for a float type. Float types are always named. + +Expression* +Float_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + go_assert(name != NULL || saw_errors()); + return this->plain_type_descriptor(gogo, this->runtime_type_kind_, name); +} + +// We should not be asked for the reflection string of a basic type. + +void +Float_type::do_reflection(Gogo*, std::string*) const +{ + go_assert(saw_errors()); +} + +// Mangled name. + +void +Float_type::do_mangled_name(Gogo*, std::string* ret) const +{ + char buf[100]; + snprintf(buf, sizeof buf, "f%s%de", + this->is_abstract_ ? "a" : "", + this->bits_); + ret->append(buf); +} + +// Make a floating point type. + +Named_type* +Type::make_float_type(const char* name, int bits, int runtime_type_kind) +{ + return Float_type::create_float_type(name, bits, runtime_type_kind); +} + +// Make an abstract float type. + +Float_type* +Type::make_abstract_float_type() +{ + return Float_type::create_abstract_float_type(); +} + +// Look up a float type. + +Named_type* +Type::lookup_float_type(const char* name) +{ + return Float_type::lookup_float_type(name); +} + +// Class Complex_type. + +Complex_type::Named_complex_types Complex_type::named_complex_types; + +// Create a new complex type. Non-abstract complex types always have +// names. + +Named_type* +Complex_type::create_complex_type(const char* name, int bits, + int runtime_type_kind) +{ + Complex_type* complex_type = new Complex_type(false, bits, + runtime_type_kind); + std::string sname(name); + Named_object* named_object = + Named_object::make_type(sname, NULL, complex_type, + Linemap::predeclared_location()); + Named_type* named_type = named_object->type_value(); + std::pair ins = + Complex_type::named_complex_types.insert(std::make_pair(sname, + named_type)); + go_assert(ins.second); + return named_type; +} + +// Look up an existing complex type. + +Named_type* +Complex_type::lookup_complex_type(const char* name) +{ + Named_complex_types::const_iterator p = + Complex_type::named_complex_types.find(name); + go_assert(p != Complex_type::named_complex_types.end()); + return p->second; +} + +// Create a new abstract complex type. + +Complex_type* +Complex_type::create_abstract_complex_type() +{ + static Complex_type* abstract_type; + if (abstract_type == NULL) + abstract_type = new Complex_type(true, 128, RUNTIME_TYPE_KIND_COMPLEX128); + return abstract_type; +} + +// Whether this type is identical with T. + +bool +Complex_type::is_identical(const Complex_type *t) const +{ + if (this->bits_ != t->bits_) + return false; + return this->is_abstract_ == t->is_abstract_; +} + +// Hash code. + +unsigned int +Complex_type::do_hash_for_method(Gogo*) const +{ + return (this->bits_ << 4) + ((this->is_abstract_ ? 1 : 0) << 8); +} + +// Convert to the backend representation. + +Btype* +Complex_type::do_get_backend(Gogo* gogo) +{ + return gogo->backend()->complex_type(this->bits_); +} + +// The type descriptor for a complex type. Complex types are always +// named. + +Expression* +Complex_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + go_assert(name != NULL || saw_errors()); + return this->plain_type_descriptor(gogo, this->runtime_type_kind_, name); +} + +// We should not be asked for the reflection string of a basic type. + +void +Complex_type::do_reflection(Gogo*, std::string*) const +{ + go_assert(saw_errors()); +} + +// Mangled name. + +void +Complex_type::do_mangled_name(Gogo*, std::string* ret) const +{ + char buf[100]; + snprintf(buf, sizeof buf, "c%s%de", + this->is_abstract_ ? "a" : "", + this->bits_); + ret->append(buf); +} + +// Make a complex type. + +Named_type* +Type::make_complex_type(const char* name, int bits, int runtime_type_kind) +{ + return Complex_type::create_complex_type(name, bits, runtime_type_kind); +} + +// Make an abstract complex type. + +Complex_type* +Type::make_abstract_complex_type() +{ + return Complex_type::create_abstract_complex_type(); +} + +// Look up a complex type. + +Named_type* +Type::lookup_complex_type(const char* name) +{ + return Complex_type::lookup_complex_type(name); +} + +// Class String_type. + +// Convert String_type to the backend representation. A string is a +// struct with two fields: a pointer to the characters and a length. + +Btype* +String_type::do_get_backend(Gogo* gogo) +{ + static Btype* backend_string_type; + if (backend_string_type == NULL) + { + std::vector fields(2); + + Type* b = gogo->lookup_global("byte")->type_value(); + Type* pb = Type::make_pointer_type(b); + + // We aren't going to get back to this field to finish the + // backend representation, so force it to be finished now. + if (!gogo->named_types_are_converted()) + { + Btype* bt = pb->get_backend_placeholder(gogo); + pb->finish_backend(gogo, bt); + } + + fields[0].name = "__data"; + fields[0].btype = pb->get_backend(gogo); + fields[0].location = Linemap::predeclared_location(); + + Type* int_type = Type::lookup_integer_type("int"); + fields[1].name = "__length"; + fields[1].btype = int_type->get_backend(gogo); + fields[1].location = fields[0].location; + + backend_string_type = gogo->backend()->struct_type(fields); + } + return backend_string_type; +} + +// Return a tree for the length of STRING. + +tree +String_type::length_tree(Gogo*, tree string) +{ + tree string_type = TREE_TYPE(string); + go_assert(TREE_CODE(string_type) == RECORD_TYPE); + tree length_field = DECL_CHAIN(TYPE_FIELDS(string_type)); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(length_field)), + "__length") == 0); + return fold_build3(COMPONENT_REF, TREE_TYPE(length_field), string, + length_field, NULL_TREE); +} + +// Return a tree for a pointer to the bytes of STRING. + +tree +String_type::bytes_tree(Gogo*, tree string) +{ + tree string_type = TREE_TYPE(string); + go_assert(TREE_CODE(string_type) == RECORD_TYPE); + tree bytes_field = TYPE_FIELDS(string_type); + go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(bytes_field)), + "__data") == 0); + return fold_build3(COMPONENT_REF, TREE_TYPE(bytes_field), string, + bytes_field, NULL_TREE); +} + +// The type descriptor for the string type. + +Expression* +String_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + if (name != NULL) + return this->plain_type_descriptor(gogo, RUNTIME_TYPE_KIND_STRING, name); + else + { + Named_object* no = gogo->lookup_global("string"); + go_assert(no != NULL); + return Type::type_descriptor(gogo, no->type_value()); + } +} + +// We should not be asked for the reflection string of a basic type. + +void +String_type::do_reflection(Gogo*, std::string* ret) const +{ + ret->append("string"); +} + +// Mangled name of a string type. + +void +String_type::do_mangled_name(Gogo*, std::string* ret) const +{ + ret->push_back('z'); +} + +// Make a string type. + +Type* +Type::make_string_type() +{ + static String_type string_type; + return &string_type; +} + +// The named type "string". + +static Named_type* named_string_type; + +// Get the named type "string". + +Named_type* +Type::lookup_string_type() +{ + return named_string_type; +} + +// Make the named type string. + +Named_type* +Type::make_named_string_type() +{ + Type* string_type = Type::make_string_type(); + Named_object* named_object = + Named_object::make_type("string", NULL, string_type, + Linemap::predeclared_location()); + Named_type* named_type = named_object->type_value(); + named_string_type = named_type; + return named_type; +} + +// The sink type. This is the type of the blank identifier _. Any +// type may be assigned to it. + +class Sink_type : public Type +{ + public: + Sink_type() + : Type(TYPE_SINK) + { } + + protected: + bool + do_compare_is_identity(Gogo*) + { return false; } + + Btype* + do_get_backend(Gogo*) + { go_unreachable(); } + + Expression* + do_type_descriptor(Gogo*, Named_type*) + { go_unreachable(); } + + void + do_reflection(Gogo*, std::string*) const + { go_unreachable(); } + + void + do_mangled_name(Gogo*, std::string*) const + { go_unreachable(); } +}; + +// Make the sink type. + +Type* +Type::make_sink_type() +{ + static Sink_type sink_type; + return &sink_type; +} + +// Class Function_type. + +// Traversal. + +int +Function_type::do_traverse(Traverse* traverse) +{ + if (this->receiver_ != NULL + && Type::traverse(this->receiver_->type(), traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->parameters_ != NULL + && this->parameters_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->results_ != NULL + && this->results_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Returns whether T is a valid redeclaration of this type. If this +// returns false, and REASON is not NULL, *REASON may be set to a +// brief explanation of why it returned false. + +bool +Function_type::is_valid_redeclaration(const Function_type* t, + std::string* reason) const +{ + if (!this->is_identical(t, false, true, reason)) + return false; + + // A redeclaration of a function is required to use the same names + // for the receiver and parameters. + if (this->receiver() != NULL + && this->receiver()->name() != t->receiver()->name()) + { + if (reason != NULL) + *reason = "receiver name changed"; + return false; + } + + const Typed_identifier_list* parms1 = this->parameters(); + const Typed_identifier_list* parms2 = t->parameters(); + if (parms1 != NULL) + { + Typed_identifier_list::const_iterator p1 = parms1->begin(); + for (Typed_identifier_list::const_iterator p2 = parms2->begin(); + p2 != parms2->end(); + ++p2, ++p1) + { + if (p1->name() != p2->name()) + { + if (reason != NULL) + *reason = "parameter name changed"; + return false; + } + + // This is called at parse time, so we may have unknown + // types. + Type* t1 = p1->type()->forwarded(); + Type* t2 = p2->type()->forwarded(); + if (t1 != t2 + && t1->forward_declaration_type() != NULL + && (t2->forward_declaration_type() == NULL + || (t1->forward_declaration_type()->named_object() + != t2->forward_declaration_type()->named_object()))) + return false; + } + } + + const Typed_identifier_list* results1 = this->results(); + const Typed_identifier_list* results2 = t->results(); + if (results1 != NULL) + { + Typed_identifier_list::const_iterator res1 = results1->begin(); + for (Typed_identifier_list::const_iterator res2 = results2->begin(); + res2 != results2->end(); + ++res2, ++res1) + { + if (res1->name() != res2->name()) + { + if (reason != NULL) + *reason = "result name changed"; + return false; + } + + // This is called at parse time, so we may have unknown + // types. + Type* t1 = res1->type()->forwarded(); + Type* t2 = res2->type()->forwarded(); + if (t1 != t2 + && t1->forward_declaration_type() != NULL + && (t2->forward_declaration_type() == NULL + || (t1->forward_declaration_type()->named_object() + != t2->forward_declaration_type()->named_object()))) + return false; + } + } + + return true; +} + +// Check whether T is the same as this type. + +bool +Function_type::is_identical(const Function_type* t, bool ignore_receiver, + bool errors_are_identical, + std::string* reason) const +{ + if (!ignore_receiver) + { + const Typed_identifier* r1 = this->receiver(); + const Typed_identifier* r2 = t->receiver(); + if ((r1 != NULL) != (r2 != NULL)) + { + if (reason != NULL) + *reason = _("different receiver types"); + return false; + } + if (r1 != NULL) + { + if (!Type::are_identical(r1->type(), r2->type(), errors_are_identical, + reason)) + { + if (reason != NULL && !reason->empty()) + *reason = "receiver: " + *reason; + return false; + } + } + } + + const Typed_identifier_list* parms1 = this->parameters(); + const Typed_identifier_list* parms2 = t->parameters(); + if ((parms1 != NULL) != (parms2 != NULL)) + { + if (reason != NULL) + *reason = _("different number of parameters"); + return false; + } + if (parms1 != NULL) + { + Typed_identifier_list::const_iterator p1 = parms1->begin(); + for (Typed_identifier_list::const_iterator p2 = parms2->begin(); + p2 != parms2->end(); + ++p2, ++p1) + { + if (p1 == parms1->end()) + { + if (reason != NULL) + *reason = _("different number of parameters"); + return false; + } + + if (!Type::are_identical(p1->type(), p2->type(), + errors_are_identical, NULL)) + { + if (reason != NULL) + *reason = _("different parameter types"); + return false; + } + } + if (p1 != parms1->end()) + { + if (reason != NULL) + *reason = _("different number of parameters"); + return false; + } + } + + if (this->is_varargs() != t->is_varargs()) + { + if (reason != NULL) + *reason = _("different varargs"); + return false; + } + + const Typed_identifier_list* results1 = this->results(); + const Typed_identifier_list* results2 = t->results(); + if ((results1 != NULL) != (results2 != NULL)) + { + if (reason != NULL) + *reason = _("different number of results"); + return false; + } + if (results1 != NULL) + { + Typed_identifier_list::const_iterator res1 = results1->begin(); + for (Typed_identifier_list::const_iterator res2 = results2->begin(); + res2 != results2->end(); + ++res2, ++res1) + { + if (res1 == results1->end()) + { + if (reason != NULL) + *reason = _("different number of results"); + return false; + } + + if (!Type::are_identical(res1->type(), res2->type(), + errors_are_identical, NULL)) + { + if (reason != NULL) + *reason = _("different result types"); + return false; + } + } + if (res1 != results1->end()) + { + if (reason != NULL) + *reason = _("different number of results"); + return false; + } + } + + return true; +} + +// Hash code. + +unsigned int +Function_type::do_hash_for_method(Gogo* gogo) const +{ + unsigned int ret = 0; + // We ignore the receiver type for hash codes, because we need to + // get the same hash code for a method in an interface and a method + // declared for a type. The former will not have a receiver. + if (this->parameters_ != NULL) + { + int shift = 1; + for (Typed_identifier_list::const_iterator p = this->parameters_->begin(); + p != this->parameters_->end(); + ++p, ++shift) + ret += p->type()->hash_for_method(gogo) << shift; + } + if (this->results_ != NULL) + { + int shift = 2; + for (Typed_identifier_list::const_iterator p = this->results_->begin(); + p != this->results_->end(); + ++p, ++shift) + ret += p->type()->hash_for_method(gogo) << shift; + } + if (this->is_varargs_) + ret += 1; + ret <<= 4; + return ret; +} + +// Hash result parameters. + +unsigned int +Function_type::Results_hash::operator()(const Typed_identifier_list* t) const +{ + unsigned int hash = 0; + for (Typed_identifier_list::const_iterator p = t->begin(); + p != t->end(); + ++p) + { + hash <<= 2; + hash = Type::hash_string(p->name(), hash); + hash += p->type()->hash_for_method(NULL); + } + return hash; +} + +// Compare result parameters so that can map identical result +// parameters to a single struct type. + +bool +Function_type::Results_equal::operator()(const Typed_identifier_list* a, + const Typed_identifier_list* b) const +{ + if (a->size() != b->size()) + return false; + Typed_identifier_list::const_iterator pa = a->begin(); + for (Typed_identifier_list::const_iterator pb = b->begin(); + pb != b->end(); + ++pa, ++pb) + { + if (pa->name() != pb->name() + || !Type::are_identical(pa->type(), pb->type(), true, NULL)) + return false; + } + return true; +} + +// Hash from results to a backend struct type. + +Function_type::Results_structs Function_type::results_structs; + +// Get the backend representation for a function type. + +Btype* +Function_type::get_backend_fntype(Gogo* gogo) +{ + if (this->fnbtype_ == NULL) + { + Backend::Btyped_identifier breceiver; + if (this->receiver_ != NULL) + { + breceiver.name = Gogo::unpack_hidden_name(this->receiver_->name()); + + // We always pass the address of the receiver parameter, in + // order to make interface calls work with unknown types. + Type* rtype = this->receiver_->type(); + if (rtype->points_to() == NULL) + rtype = Type::make_pointer_type(rtype); + breceiver.btype = rtype->get_backend(gogo); + breceiver.location = this->receiver_->location(); + } + + std::vector bparameters; + if (this->parameters_ != NULL) + { + bparameters.resize(this->parameters_->size()); + size_t i = 0; + for (Typed_identifier_list::const_iterator p = + this->parameters_->begin(); p != this->parameters_->end(); + ++p, ++i) + { + bparameters[i].name = Gogo::unpack_hidden_name(p->name()); + bparameters[i].btype = p->type()->get_backend(gogo); + bparameters[i].location = p->location(); + } + go_assert(i == bparameters.size()); + } + + std::vector bresults; + Btype* bresult_struct = NULL; + if (this->results_ != NULL) + { + bresults.resize(this->results_->size()); + size_t i = 0; + for (Typed_identifier_list::const_iterator p = + this->results_->begin(); + p != this->results_->end(); + ++p, ++i) + { + bresults[i].name = Gogo::unpack_hidden_name(p->name()); + bresults[i].btype = p->type()->get_backend(gogo); + bresults[i].location = p->location(); + } + go_assert(i == bresults.size()); + + if (this->results_->size() > 1) + { + // Use the same results struct for all functions that + // return the same set of results. This is useful to + // unify calls to interface methods with other calls. + std::pair val; + val.first = this->results_; + val.second = NULL; + std::pair ins = + Function_type::results_structs.insert(val); + if (ins.second) + { + // Build a new struct type. + Struct_field_list* sfl = new Struct_field_list; + for (Typed_identifier_list::const_iterator p = + this->results_->begin(); + p != this->results_->end(); + ++p) + { + Typed_identifier tid = *p; + if (tid.name().empty()) + tid = Typed_identifier("UNNAMED", tid.type(), + tid.location()); + sfl->push_back(Struct_field(tid)); + } + Struct_type* st = Type::make_struct_type(sfl, + this->location()); + ins.first->second = st->get_backend(gogo); + } + bresult_struct = ins.first->second; + } + } + + this->fnbtype_ = gogo->backend()->function_type(breceiver, bparameters, + bresults, bresult_struct, + this->location()); + + } + + return this->fnbtype_; +} + +// Get the backend representation for a Go function type. + +Btype* +Function_type::do_get_backend(Gogo* gogo) +{ + // When we do anything with a function value other than call it, it + // is represented as a pointer to a struct whose first field is the + // actual function. So that is what we return as the type of a Go + // function. + + Location loc = this->location(); + Btype* struct_type = + gogo->backend()->placeholder_struct_type("__go_descriptor", loc); + Btype* ptr_struct_type = gogo->backend()->pointer_type(struct_type); + + std::vector fields(1); + fields[0].name = "code"; + fields[0].btype = this->get_backend_fntype(gogo); + fields[0].location = loc; + if (!gogo->backend()->set_placeholder_struct_type(struct_type, fields)) + return gogo->backend()->error_type(); + return ptr_struct_type; +} + +// The type of a function type descriptor. + +Type* +Function_type::make_function_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* tdt = Type::make_type_descriptor_type(); + Type* ptdt = Type::make_type_descriptor_ptr_type(); + + Type* bool_type = Type::lookup_bool_type(); + + Type* slice_type = Type::make_array_type(ptdt, NULL); + + Struct_type* s = Type::make_builtin_struct_type(4, + "", tdt, + "dotdotdot", bool_type, + "in", slice_type, + "out", slice_type); + + ret = Type::make_builtin_named_type("FuncType", s); + } + + return ret; +} + +// The type descriptor for a function type. + +Expression* +Function_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + Type* ftdt = Function_type::make_function_type_descriptor_type(); + + const Struct_field_list* fields = ftdt->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(4); + + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("commonType")); + vals->push_back(this->type_descriptor_constructor(gogo, + RUNTIME_TYPE_KIND_FUNC, + name, NULL, true)); + + ++p; + go_assert(p->is_field_name("dotdotdot")); + vals->push_back(Expression::make_boolean(this->is_varargs(), bloc)); + + ++p; + go_assert(p->is_field_name("in")); + vals->push_back(this->type_descriptor_params(p->type(), this->receiver(), + this->parameters())); + + ++p; + go_assert(p->is_field_name("out")); + vals->push_back(this->type_descriptor_params(p->type(), NULL, + this->results())); + + ++p; + go_assert(p == fields->end()); + + return Expression::make_struct_composite_literal(ftdt, vals, bloc); +} + +// Return a composite literal for the parameters or results of a type +// descriptor. + +Expression* +Function_type::type_descriptor_params(Type* params_type, + const Typed_identifier* receiver, + const Typed_identifier_list* params) +{ + Location bloc = Linemap::predeclared_location(); + + if (receiver == NULL && params == NULL) + return Expression::make_slice_composite_literal(params_type, NULL, bloc); + + Expression_list* vals = new Expression_list(); + vals->reserve((params == NULL ? 0 : params->size()) + + (receiver != NULL ? 1 : 0)); + + if (receiver != NULL) + vals->push_back(Expression::make_type_descriptor(receiver->type(), bloc)); + + if (params != NULL) + { + for (Typed_identifier_list::const_iterator p = params->begin(); + p != params->end(); + ++p) + vals->push_back(Expression::make_type_descriptor(p->type(), bloc)); + } + + return Expression::make_slice_composite_literal(params_type, vals, bloc); +} + +// The reflection string. + +void +Function_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + // FIXME: Turn this off until we straighten out the type of the + // struct field used in a go statement which calls a method. + // go_assert(this->receiver_ == NULL); + + ret->append("func"); + + if (this->receiver_ != NULL) + { + ret->push_back('('); + this->append_reflection(this->receiver_->type(), gogo, ret); + ret->push_back(')'); + } + + ret->push_back('('); + const Typed_identifier_list* params = this->parameters(); + if (params != NULL) + { + bool is_varargs = this->is_varargs_; + for (Typed_identifier_list::const_iterator p = params->begin(); + p != params->end(); + ++p) + { + if (p != params->begin()) + ret->append(", "); + if (!is_varargs || p + 1 != params->end()) + this->append_reflection(p->type(), gogo, ret); + else + { + ret->append("..."); + this->append_reflection(p->type()->array_type()->element_type(), + gogo, ret); + } + } + } + ret->push_back(')'); + + const Typed_identifier_list* results = this->results(); + if (results != NULL && !results->empty()) + { + if (results->size() == 1) + ret->push_back(' '); + else + ret->append(" ("); + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p) + { + if (p != results->begin()) + ret->append(", "); + this->append_reflection(p->type(), gogo, ret); + } + if (results->size() > 1) + ret->push_back(')'); + } +} + +// Mangled name. + +void +Function_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + ret->push_back('F'); + + if (this->receiver_ != NULL) + { + ret->push_back('m'); + this->append_mangled_name(this->receiver_->type(), gogo, ret); + } + + const Typed_identifier_list* params = this->parameters(); + if (params != NULL) + { + ret->push_back('p'); + for (Typed_identifier_list::const_iterator p = params->begin(); + p != params->end(); + ++p) + this->append_mangled_name(p->type(), gogo, ret); + if (this->is_varargs_) + ret->push_back('V'); + ret->push_back('e'); + } + + const Typed_identifier_list* results = this->results(); + if (results != NULL) + { + ret->push_back('r'); + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p) + this->append_mangled_name(p->type(), gogo, ret); + ret->push_back('e'); + } + + ret->push_back('e'); +} + +// Export a function type. + +void +Function_type::do_export(Export* exp) const +{ + // We don't write out the receiver. The only function types which + // should have a receiver are the ones associated with explicitly + // defined methods. For those the receiver type is written out by + // Function::export_func. + + exp->write_c_string("("); + bool first = true; + if (this->parameters_ != NULL) + { + bool is_varargs = this->is_varargs_; + for (Typed_identifier_list::const_iterator p = + this->parameters_->begin(); + p != this->parameters_->end(); + ++p) + { + if (first) + first = false; + else + exp->write_c_string(", "); + exp->write_name(p->name()); + exp->write_c_string(" "); + if (!is_varargs || p + 1 != this->parameters_->end()) + exp->write_type(p->type()); + else + { + exp->write_c_string("..."); + exp->write_type(p->type()->array_type()->element_type()); + } + } + } + exp->write_c_string(")"); + + const Typed_identifier_list* results = this->results_; + if (results != NULL) + { + exp->write_c_string(" "); + if (results->size() == 1 && results->begin()->name().empty()) + exp->write_type(results->begin()->type()); + else + { + first = true; + exp->write_c_string("("); + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p) + { + if (first) + first = false; + else + exp->write_c_string(", "); + exp->write_name(p->name()); + exp->write_c_string(" "); + exp->write_type(p->type()); + } + exp->write_c_string(")"); + } + } +} + +// Import a function type. + +Function_type* +Function_type::do_import(Import* imp) +{ + imp->require_c_string("("); + Typed_identifier_list* parameters; + bool is_varargs = false; + if (imp->peek_char() == ')') + parameters = NULL; + else + { + parameters = new Typed_identifier_list(); + while (true) + { + std::string name = imp->read_name(); + imp->require_c_string(" "); + + if (imp->match_c_string("...")) + { + imp->advance(3); + is_varargs = true; + } + + Type* ptype = imp->read_type(); + if (is_varargs) + ptype = Type::make_array_type(ptype, NULL); + parameters->push_back(Typed_identifier(name, ptype, + imp->location())); + if (imp->peek_char() != ',') + break; + go_assert(!is_varargs); + imp->require_c_string(", "); + } + } + imp->require_c_string(")"); + + Typed_identifier_list* results; + if (imp->peek_char() != ' ') + results = NULL; + else + { + imp->advance(1); + results = new Typed_identifier_list; + if (imp->peek_char() != '(') + { + Type* rtype = imp->read_type(); + results->push_back(Typed_identifier("", rtype, imp->location())); + } + else + { + imp->advance(1); + while (true) + { + std::string name = imp->read_name(); + imp->require_c_string(" "); + Type* rtype = imp->read_type(); + results->push_back(Typed_identifier(name, rtype, + imp->location())); + if (imp->peek_char() != ',') + break; + imp->require_c_string(", "); + } + imp->require_c_string(")"); + } + } + + Function_type* ret = Type::make_function_type(NULL, parameters, results, + imp->location()); + if (is_varargs) + ret->set_is_varargs(); + return ret; +} + +// Make a copy of a function type without a receiver. + +Function_type* +Function_type::copy_without_receiver() const +{ + go_assert(this->is_method()); + Function_type *ret = Type::make_function_type(NULL, this->parameters_, + this->results_, + this->location_); + if (this->is_varargs()) + ret->set_is_varargs(); + if (this->is_builtin()) + ret->set_is_builtin(); + return ret; +} + +// Make a copy of a function type with a receiver. + +Function_type* +Function_type::copy_with_receiver(Type* receiver_type) const +{ + go_assert(!this->is_method()); + Typed_identifier* receiver = new Typed_identifier("", receiver_type, + this->location_); + Function_type* ret = Type::make_function_type(receiver, this->parameters_, + this->results_, + this->location_); + if (this->is_varargs_) + ret->set_is_varargs(); + return ret; +} + +// Make a copy of a function type with the receiver as the first +// parameter. + +Function_type* +Function_type::copy_with_receiver_as_param(bool want_pointer_receiver) const +{ + go_assert(this->is_method()); + Typed_identifier_list* new_params = new Typed_identifier_list(); + Type* rtype = this->receiver_->type(); + if (want_pointer_receiver) + rtype = Type::make_pointer_type(rtype); + Typed_identifier receiver(this->receiver_->name(), rtype, + this->receiver_->location()); + new_params->push_back(receiver); + const Typed_identifier_list* orig_params = this->parameters_; + if (orig_params != NULL && !orig_params->empty()) + { + for (Typed_identifier_list::const_iterator p = orig_params->begin(); + p != orig_params->end(); + ++p) + new_params->push_back(*p); + } + return Type::make_function_type(NULL, new_params, this->results_, + this->location_); +} + +// Make a copy of a function type ignoring any receiver and adding a +// closure parameter. + +Function_type* +Function_type::copy_with_names() const +{ + Typed_identifier_list* new_params = new Typed_identifier_list(); + const Typed_identifier_list* orig_params = this->parameters_; + if (orig_params != NULL && !orig_params->empty()) + { + static int count; + char buf[50]; + for (Typed_identifier_list::const_iterator p = orig_params->begin(); + p != orig_params->end(); + ++p) + { + snprintf(buf, sizeof buf, "pt.%u", count); + ++count; + new_params->push_back(Typed_identifier(buf, p->type(), + p->location())); + } + } + + const Typed_identifier_list* orig_results = this->results_; + Typed_identifier_list* new_results; + if (orig_results == NULL || orig_results->empty()) + new_results = NULL; + else + { + new_results = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator p = orig_results->begin(); + p != orig_results->end(); + ++p) + new_results->push_back(Typed_identifier("", p->type(), + p->location())); + } + + return Type::make_function_type(NULL, new_params, new_results, + this->location()); +} + +// Make a function type. + +Function_type* +Type::make_function_type(Typed_identifier* receiver, + Typed_identifier_list* parameters, + Typed_identifier_list* results, + Location location) +{ + return new Function_type(receiver, parameters, results, location); +} + +// Make a backend function type. + +Backend_function_type* +Type::make_backend_function_type(Typed_identifier* receiver, + Typed_identifier_list* parameters, + Typed_identifier_list* results, + Location location) +{ + return new Backend_function_type(receiver, parameters, results, location); +} + +// Class Pointer_type. + +// Traversal. + +int +Pointer_type::do_traverse(Traverse* traverse) +{ + return Type::traverse(this->to_type_, traverse); +} + +// Hash code. + +unsigned int +Pointer_type::do_hash_for_method(Gogo* gogo) const +{ + return this->to_type_->hash_for_method(gogo) << 4; +} + +// Get the backend representation for a pointer type. + +Btype* +Pointer_type::do_get_backend(Gogo* gogo) +{ + Btype* to_btype = this->to_type_->get_backend(gogo); + return gogo->backend()->pointer_type(to_btype); +} + +// The type of a pointer type descriptor. + +Type* +Pointer_type::make_pointer_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* tdt = Type::make_type_descriptor_type(); + Type* ptdt = Type::make_type_descriptor_ptr_type(); + + Struct_type* s = Type::make_builtin_struct_type(2, + "", tdt, + "elem", ptdt); + + ret = Type::make_builtin_named_type("PtrType", s); + } + + return ret; +} + +// The type descriptor for a pointer type. + +Expression* +Pointer_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + if (this->is_unsafe_pointer_type()) + { + go_assert(name != NULL); + return this->plain_type_descriptor(gogo, + RUNTIME_TYPE_KIND_UNSAFE_POINTER, + name); + } + else + { + Location bloc = Linemap::predeclared_location(); + + const Methods* methods; + Type* deref = this->points_to(); + if (deref->named_type() != NULL) + methods = deref->named_type()->methods(); + else if (deref->struct_type() != NULL) + methods = deref->struct_type()->methods(); + else + methods = NULL; + + Type* ptr_tdt = Pointer_type::make_pointer_type_descriptor_type(); + + const Struct_field_list* fields = ptr_tdt->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(2); + + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("commonType")); + vals->push_back(this->type_descriptor_constructor(gogo, + RUNTIME_TYPE_KIND_PTR, + name, methods, false)); + + ++p; + go_assert(p->is_field_name("elem")); + vals->push_back(Expression::make_type_descriptor(deref, bloc)); + + return Expression::make_struct_composite_literal(ptr_tdt, vals, bloc); + } +} + +// Reflection string. + +void +Pointer_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + ret->push_back('*'); + this->append_reflection(this->to_type_, gogo, ret); +} + +// Mangled name. + +void +Pointer_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + ret->push_back('p'); + this->append_mangled_name(this->to_type_, gogo, ret); +} + +// Export. + +void +Pointer_type::do_export(Export* exp) const +{ + exp->write_c_string("*"); + if (this->is_unsafe_pointer_type()) + exp->write_c_string("any"); + else + exp->write_type(this->to_type_); +} + +// Import. + +Pointer_type* +Pointer_type::do_import(Import* imp) +{ + imp->require_c_string("*"); + if (imp->match_c_string("any")) + { + imp->advance(3); + return Type::make_pointer_type(Type::make_void_type()); + } + Type* to = imp->read_type(); + return Type::make_pointer_type(to); +} + +// Make a pointer type. + +Pointer_type* +Type::make_pointer_type(Type* to_type) +{ + typedef Unordered_map(Type*, Pointer_type*) Hashtable; + static Hashtable pointer_types; + Hashtable::const_iterator p = pointer_types.find(to_type); + if (p != pointer_types.end()) + return p->second; + Pointer_type* ret = new Pointer_type(to_type); + pointer_types[to_type] = ret; + return ret; +} + +// The nil type. We use a special type for nil because it is not the +// same as any other type. In C term nil has type void*, but there is +// no such type in Go. + +class Nil_type : public Type +{ + public: + Nil_type() + : Type(TYPE_NIL) + { } + + protected: + bool + do_compare_is_identity(Gogo*) + { return false; } + + Btype* + do_get_backend(Gogo* gogo) + { return gogo->backend()->pointer_type(gogo->backend()->void_type()); } + + Expression* + do_type_descriptor(Gogo*, Named_type*) + { go_unreachable(); } + + void + do_reflection(Gogo*, std::string*) const + { go_unreachable(); } + + void + do_mangled_name(Gogo*, std::string* ret) const + { ret->push_back('n'); } +}; + +// Make the nil type. + +Type* +Type::make_nil_type() +{ + static Nil_type singleton_nil_type; + return &singleton_nil_type; +} + +// The type of a function call which returns multiple values. This is +// really a struct, but we don't want to confuse a function call which +// returns a struct with a function call which returns multiple +// values. + +class Call_multiple_result_type : public Type +{ + public: + Call_multiple_result_type(Call_expression* call) + : Type(TYPE_CALL_MULTIPLE_RESULT), + call_(call) + { } + + protected: + bool + do_has_pointer() const + { + go_assert(saw_errors()); + return false; + } + + bool + do_compare_is_identity(Gogo*) + { return false; } + + Btype* + do_get_backend(Gogo* gogo) + { + go_assert(saw_errors()); + return gogo->backend()->error_type(); + } + + Expression* + do_type_descriptor(Gogo*, Named_type*) + { + go_assert(saw_errors()); + return Expression::make_error(Linemap::unknown_location()); + } + + void + do_reflection(Gogo*, std::string*) const + { go_assert(saw_errors()); } + + void + do_mangled_name(Gogo*, std::string*) const + { go_assert(saw_errors()); } + + private: + // The expression being called. + Call_expression* call_; +}; + +// Make a call result type. + +Type* +Type::make_call_multiple_result_type(Call_expression* call) +{ + return new Call_multiple_result_type(call); +} + +// Class Struct_field. + +// Get the name of a field. + +const std::string& +Struct_field::field_name() const +{ + const std::string& name(this->typed_identifier_.name()); + if (!name.empty()) + return name; + else + { + // This is called during parsing, before anything is lowered, so + // we have to be pretty careful to avoid dereferencing an + // unknown type name. + Type* t = this->typed_identifier_.type(); + Type* dt = t; + if (t->classification() == Type::TYPE_POINTER) + { + // Very ugly. + Pointer_type* ptype = static_cast(t); + dt = ptype->points_to(); + } + if (dt->forward_declaration_type() != NULL) + return dt->forward_declaration_type()->name(); + else if (dt->named_type() != NULL) + return dt->named_type()->name(); + else if (t->is_error_type() || dt->is_error_type()) + { + static const std::string error_string = "*error*"; + return error_string; + } + else + { + // Avoid crashing in the erroneous case where T is named but + // DT is not. + go_assert(t != dt); + if (t->forward_declaration_type() != NULL) + return t->forward_declaration_type()->name(); + else if (t->named_type() != NULL) + return t->named_type()->name(); + else + go_unreachable(); + } + } +} + +// Return whether this field is named NAME. + +bool +Struct_field::is_field_name(const std::string& name) const +{ + const std::string& me(this->typed_identifier_.name()); + if (!me.empty()) + return me == name; + else + { + Type* t = this->typed_identifier_.type(); + if (t->points_to() != NULL) + t = t->points_to(); + Named_type* nt = t->named_type(); + if (nt != NULL && nt->name() == name) + return true; + + // This is a horrible hack caused by the fact that we don't pack + // the names of builtin types. FIXME. + if (!this->is_imported_ + && nt != NULL + && nt->is_builtin() + && nt->name() == Gogo::unpack_hidden_name(name)) + return true; + + return false; + } +} + +// Return whether this field is an unexported field named NAME. + +bool +Struct_field::is_unexported_field_name(Gogo* gogo, + const std::string& name) const +{ + const std::string& field_name(this->field_name()); + if (Gogo::is_hidden_name(field_name) + && name == Gogo::unpack_hidden_name(field_name) + && gogo->pack_hidden_name(name, false) != field_name) + return true; + + // Check for the name of a builtin type. This is like the test in + // is_field_name, only there we return false if this->is_imported_, + // and here we return true. + if (this->is_imported_ && this->is_anonymous()) + { + Type* t = this->typed_identifier_.type(); + if (t->points_to() != NULL) + t = t->points_to(); + Named_type* nt = t->named_type(); + if (nt != NULL + && nt->is_builtin() + && nt->name() == Gogo::unpack_hidden_name(name)) + return true; + } + + return false; +} + +// Return whether this field is an embedded built-in type. + +bool +Struct_field::is_embedded_builtin(Gogo* gogo) const +{ + const std::string& name(this->field_name()); + // We know that a field is an embedded type if it is anonymous. + // We can decide if it is a built-in type by checking to see if it is + // registered globally under the field's name. + // This allows us to distinguish between embedded built-in types and + // embedded types that are aliases to built-in types. + return (this->is_anonymous() + && !Gogo::is_hidden_name(name) + && gogo->lookup_global(name.c_str()) != NULL); +} + +// Class Struct_type. + +// A hash table used to find identical unnamed structs so that they +// share method tables. + +Struct_type::Identical_structs Struct_type::identical_structs; + +// A hash table used to merge method sets for identical unnamed +// structs. + +Struct_type::Struct_method_tables Struct_type::struct_method_tables; + +// Traversal. + +int +Struct_type::do_traverse(Traverse* traverse) +{ + Struct_field_list* fields = this->fields_; + if (fields != NULL) + { + for (Struct_field_list::iterator p = fields->begin(); + p != fields->end(); + ++p) + { + if (Type::traverse(p->type(), traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + return TRAVERSE_CONTINUE; +} + +// Verify that the struct type is complete and valid. + +bool +Struct_type::do_verify() +{ + Struct_field_list* fields = this->fields_; + if (fields == NULL) + return true; + for (Struct_field_list::iterator p = fields->begin(); + p != fields->end(); + ++p) + { + Type* t = p->type(); + if (p->is_anonymous()) + { + if (t->named_type() != NULL && t->points_to() != NULL) + { + error_at(p->location(), "embedded type may not be a pointer"); + p->set_type(Type::make_error_type()); + } + else if (t->points_to() != NULL + && t->points_to()->interface_type() != NULL) + { + error_at(p->location(), + "embedded type may not be pointer to interface"); + p->set_type(Type::make_error_type()); + } + } + } + return true; +} + +// Whether this contains a pointer. + +bool +Struct_type::do_has_pointer() const +{ + const Struct_field_list* fields = this->fields(); + if (fields == NULL) + return false; + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + if (p->type()->has_pointer()) + return true; + } + return false; +} + +// Whether this type is identical to T. + +bool +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(); + if (fields1 == NULL || fields2 == NULL) + return fields1 == fields2; + Struct_field_list::const_iterator pf2 = fields2->begin(); + for (Struct_field_list::const_iterator pf1 = fields1->begin(); + pf1 != fields1->end(); + ++pf1, ++pf2) + { + if (pf2 == fields2->end()) + return false; + if (pf1->field_name() != pf2->field_name()) + return false; + if (pf1->is_anonymous() != pf2->is_anonymous() + || !Type::are_identical(pf1->type(), pf2->type(), + errors_are_identical, NULL)) + return false; + if (!pf1->has_tag()) + { + if (pf2->has_tag()) + return false; + } + else + { + if (!pf2->has_tag()) + return false; + if (pf1->tag() != pf2->tag()) + return false; + } + } + if (pf2 != fields2->end()) + return false; + return true; +} + +// Whether this struct type has any hidden fields. + +bool +Struct_type::struct_has_hidden_fields(const Named_type* within, + std::string* reason) const +{ + const Struct_field_list* fields = this->fields(); + if (fields == NULL) + return false; + const Package* within_package = (within == NULL + ? NULL + : within->named_object()->package()); + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (within_package != NULL + && !pf->is_anonymous() + && Gogo::is_hidden_name(pf->field_name())) + { + if (reason != NULL) + { + std::string within_name = within->named_object()->message_name(); + std::string name = Gogo::message_name(pf->field_name()); + size_t bufsize = 200 + within_name.length() + name.length(); + char* buf = new char[bufsize]; + snprintf(buf, bufsize, + _("implicit assignment of %s%s%s hidden field %s%s%s"), + open_quote, within_name.c_str(), close_quote, + open_quote, name.c_str(), close_quote); + reason->assign(buf); + delete[] buf; + } + return true; + } + + if (pf->type()->has_hidden_fields(within, reason)) + return true; + } + + return false; +} + +// Whether comparisons of this struct type are simple identity +// comparisons. + +bool +Struct_type::do_compare_is_identity(Gogo* gogo) +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL) + return true; + unsigned int offset = 0; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (Gogo::is_sink_name(pf->field_name())) + return false; + + if (!pf->type()->compare_is_identity(gogo)) + return false; + + unsigned int field_align; + if (!pf->type()->backend_type_align(gogo, &field_align)) + return false; + if ((offset & (field_align - 1)) != 0) + { + // This struct has padding. We don't guarantee that that + // padding is zero-initialized for a stack variable, so we + // can't use memcmp to compare struct values. + return false; + } + + unsigned int field_size; + if (!pf->type()->backend_type_size(gogo, &field_size)) + 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; +} + +// Build identity and hash functions for this struct. + +// Hash code. + +unsigned int +Struct_type::do_hash_for_method(Gogo* gogo) const +{ + unsigned int ret = 0; + if (this->fields() != NULL) + { + for (Struct_field_list::const_iterator pf = this->fields()->begin(); + pf != this->fields()->end(); + ++pf) + ret = (ret << 1) + pf->type()->hash_for_method(gogo); + } + return ret <<= 2; +} + +// Find the local field NAME. + +const Struct_field* +Struct_type::find_local_field(const std::string& name, + unsigned int *pindex) const +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL) + return NULL; + unsigned int i = 0; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf, ++i) + { + if (pf->is_field_name(name)) + { + if (pindex != NULL) + *pindex = i; + return &*pf; + } + } + return NULL; +} + +// Return an expression for field NAME in STRUCT_EXPR, or NULL. + +Field_reference_expression* +Struct_type::field_reference(Expression* struct_expr, const std::string& name, + Location location) const +{ + unsigned int depth; + return this->field_reference_depth(struct_expr, name, location, NULL, + &depth); +} + +// Return an expression for a field, along with the depth at which it +// was found. + +Field_reference_expression* +Struct_type::field_reference_depth(Expression* struct_expr, + const std::string& name, + Location location, + Saw_named_type* saw, + unsigned int* depth) const +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL) + return NULL; + + // Look for a field with this name. + unsigned int i = 0; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf, ++i) + { + if (pf->is_field_name(name)) + { + *depth = 0; + return Expression::make_field_reference(struct_expr, i, location); + } + } + + // Look for an anonymous field which contains a field with this + // name. + unsigned int found_depth = 0; + Field_reference_expression* ret = NULL; + i = 0; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf, ++i) + { + if (!pf->is_anonymous()) + continue; + + Struct_type* st = pf->type()->deref()->struct_type(); + if (st == NULL) + continue; + + Saw_named_type* hold_saw = saw; + Saw_named_type saw_here; + Named_type* nt = pf->type()->named_type(); + if (nt == NULL) + nt = pf->type()->deref()->named_type(); + if (nt != NULL) + { + Saw_named_type* q; + for (q = saw; q != NULL; q = q->next) + { + if (q->nt == nt) + { + // If this is an error, it will be reported + // elsewhere. + break; + } + } + if (q != NULL) + continue; + saw_here.next = saw; + saw_here.nt = nt; + saw = &saw_here; + } + + // Look for a reference using a NULL struct expression. If we + // find one, fill in the struct expression with a reference to + // this field. + unsigned int subdepth; + Field_reference_expression* sub = st->field_reference_depth(NULL, name, + location, + saw, + &subdepth); + + saw = hold_saw; + + if (sub == NULL) + continue; + + if (ret == NULL || subdepth < found_depth) + { + if (ret != NULL) + delete ret; + ret = sub; + found_depth = subdepth; + Expression* here = Expression::make_field_reference(struct_expr, i, + location); + if (pf->type()->points_to() != NULL) + here = Expression::make_unary(OPERATOR_MULT, here, location); + while (sub->expr() != NULL) + { + sub = sub->expr()->deref()->field_reference_expression(); + go_assert(sub != NULL); + } + sub->set_struct_expression(here); + sub->set_implicit(true); + } + else if (subdepth > found_depth) + delete sub; + else + { + // We do not handle ambiguity here--it should be handled by + // Type::bind_field_or_method. + delete sub; + found_depth = 0; + ret = NULL; + } + } + + if (ret != NULL) + *depth = found_depth + 1; + + return ret; +} + +// Return the total number of fields, including embedded fields. + +unsigned int +Struct_type::total_field_count() const +{ + if (this->fields_ == NULL) + return 0; + unsigned int ret = 0; + for (Struct_field_list::const_iterator pf = this->fields_->begin(); + pf != this->fields_->end(); + ++pf) + { + if (!pf->is_anonymous() || pf->type()->struct_type() == NULL) + ++ret; + else + ret += pf->type()->struct_type()->total_field_count(); + } + return ret; +} + +// Return whether NAME is an unexported field, for better error reporting. + +bool +Struct_type::is_unexported_local_field(Gogo* gogo, + const std::string& name) const +{ + const Struct_field_list* fields = this->fields_; + if (fields != NULL) + { + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + if (pf->is_unexported_field_name(gogo, name)) + return true; + } + return false; +} + +// Finalize the methods of an unnamed struct. + +void +Struct_type::finalize_methods(Gogo* gogo) +{ + if (this->all_methods_ != NULL) + return; + + // It is possible to have multiple identical structs that have + // methods. We want them to share method tables. Otherwise we will + // emit identical methods more than once, which is bad since they + // will even have the same names. + std::pair ins = + Struct_type::identical_structs.insert(std::make_pair(this, this)); + if (!ins.second) + { + // An identical struct was already entered into the hash table. + // Note that finalize_methods is, fortunately, not recursive. + this->all_methods_ = ins.first->second->all_methods_; + return; + } + + Type::finalize_methods(gogo, this, this->location_, &this->all_methods_); +} + +// Return the method NAME, or NULL if there isn't one or if it is +// ambiguous. Set *IS_AMBIGUOUS if the method exists but is +// ambiguous. + +Method* +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) +{ + std::pair + val(this, NULL); + std::pair ins = + Struct_type::struct_method_tables.insert(val); + + Struct_method_table_pair* smtp; + if (!ins.second) + smtp = ins.first->second; + else + { + smtp = new Struct_method_table_pair(); + smtp->first = NULL; + smtp->second = NULL; + ins.first->second = smtp; + } + + return Type::interface_method_table(gogo, this, interface, is_pointer, + &smtp->first, &smtp->second); +} + +// 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. + +static void +get_backend_struct_fields(Gogo* gogo, const Struct_field_list* fields, + bool use_placeholder, + std::vector* bfields) +{ + bfields->resize(fields->size()); + size_t i = 0; + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p, ++i) + { + (*bfields)[i].name = Gogo::unpack_hidden_name(p->field_name()); + (*bfields)[i].btype = (use_placeholder + ? p->type()->get_backend_placeholder(gogo) + : p->type()->get_backend(gogo)); + (*bfields)[i].location = p->location(); + } + go_assert(i == fields->size()); +} + +// Get the tree for a struct type. + +Btype* +Struct_type::do_get_backend(Gogo* gogo) +{ + std::vector bfields; + get_backend_struct_fields(gogo, this->fields_, false, &bfields); + return gogo->backend()->struct_type(bfields); +} + +// Finish the backend representation of the fields of a struct. + +void +Struct_type::finish_backend_fields(Gogo* gogo) +{ + const Struct_field_list* fields = this->fields_; + if (fields != NULL) + { + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + p->type()->get_backend(gogo); + } +} + +// The type of a struct type descriptor. + +Type* +Struct_type::make_struct_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* tdt = Type::make_type_descriptor_type(); + Type* ptdt = Type::make_type_descriptor_ptr_type(); + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* string_type = Type::lookup_string_type(); + Type* pointer_string_type = Type::make_pointer_type(string_type); + + Struct_type* sf = + Type::make_builtin_struct_type(5, + "name", pointer_string_type, + "pkgPath", pointer_string_type, + "typ", ptdt, + "tag", pointer_string_type, + "offset", uintptr_type); + Type* nsf = Type::make_builtin_named_type("structField", sf); + + Type* slice_type = Type::make_array_type(nsf, NULL); + + Struct_type* s = Type::make_builtin_struct_type(2, + "", tdt, + "fields", slice_type); + + ret = Type::make_builtin_named_type("StructType", s); + } + + return ret; +} + +// Build a type descriptor for a struct type. + +Expression* +Struct_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + Type* stdt = Struct_type::make_struct_type_descriptor_type(); + + const Struct_field_list* fields = stdt->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(2); + + const Methods* methods = this->methods(); + // A named struct should not have methods--the methods should attach + // to the named type. + go_assert(methods == NULL || name == NULL); + + Struct_field_list::const_iterator ps = fields->begin(); + go_assert(ps->is_field_name("commonType")); + vals->push_back(this->type_descriptor_constructor(gogo, + RUNTIME_TYPE_KIND_STRUCT, + name, methods, true)); + + ++ps; + go_assert(ps->is_field_name("fields")); + + Expression_list* elements = new Expression_list(); + elements->reserve(this->fields_->size()); + Type* element_type = ps->type()->array_type()->element_type(); + for (Struct_field_list::const_iterator pf = this->fields_->begin(); + pf != this->fields_->end(); + ++pf) + { + const Struct_field_list* f = element_type->struct_type()->fields(); + + Expression_list* fvals = new Expression_list(); + fvals->reserve(5); + + Struct_field_list::const_iterator q = f->begin(); + go_assert(q->is_field_name("name")); + if (pf->is_anonymous()) + fvals->push_back(Expression::make_nil(bloc)); + else + { + std::string n = Gogo::unpack_hidden_name(pf->field_name()); + Expression* s = Expression::make_string(n, bloc); + fvals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); + } + + ++q; + go_assert(q->is_field_name("pkgPath")); + bool is_embedded_builtin = pf->is_embedded_builtin(gogo); + if (!Gogo::is_hidden_name(pf->field_name()) && !is_embedded_builtin) + fvals->push_back(Expression::make_nil(bloc)); + else + { + std::string n; + if (is_embedded_builtin) + n = gogo->package_name(); + else + n = Gogo::hidden_name_pkgpath(pf->field_name()); + Expression* s = Expression::make_string(n, bloc); + fvals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); + } + + ++q; + go_assert(q->is_field_name("typ")); + fvals->push_back(Expression::make_type_descriptor(pf->type(), bloc)); + + ++q; + go_assert(q->is_field_name("tag")); + if (!pf->has_tag()) + fvals->push_back(Expression::make_nil(bloc)); + else + { + Expression* s = Expression::make_string(pf->tag(), bloc); + fvals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); + } + + ++q; + go_assert(q->is_field_name("offset")); + fvals->push_back(Expression::make_struct_field_offset(this, &*pf)); + + Expression* v = Expression::make_struct_composite_literal(element_type, + fvals, bloc); + elements->push_back(v); + } + + vals->push_back(Expression::make_slice_composite_literal(ps->type(), + elements, bloc)); + + return Expression::make_struct_composite_literal(stdt, vals, bloc); +} + +// Write the hash function for a struct which can not use the identity +// function. + +void +Struct_type::write_hash_function(Gogo* gogo, Named_type*, + Function_type* hash_fntype, + Function_type* equal_fntype) +{ + Location bloc = Linemap::predeclared_location(); + + // The pointer to the struct that we are going to hash. This is an + // argument to the hash function we are implementing here. + Named_object* key_arg = gogo->lookup("key", NULL); + go_assert(key_arg != NULL); + Type* key_arg_type = key_arg->var_value()->type(); + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + // Get a 0. + mpz_t ival; + mpz_init_set_ui(ival, 0); + Expression* zero = Expression::make_integer(&ival, uintptr_type, bloc); + mpz_clear(ival); + + // Make a temporary to hold the return value, initialized to 0. + Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero, + bloc); + gogo->add_statement(retval); + + // Make a temporary to hold the key as a uintptr. + Expression* ref = Expression::make_var_reference(key_arg, bloc); + ref = Expression::make_cast(uintptr_type, ref, bloc); + Temporary_statement* key = Statement::make_temporary(uintptr_type, ref, + bloc); + gogo->add_statement(key); + + // Loop over the struct fields. + bool first = true; + const Struct_field_list* fields = this->fields_; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (Gogo::is_sink_name(pf->field_name())) + continue; + + if (first) + first = false; + else + { + // Multiply retval by 33. + mpz_init_set_ui(ival, 33); + Expression* i33 = Expression::make_integer(&ival, uintptr_type, + bloc); + mpz_clear(ival); + + ref = Expression::make_temporary_reference(retval, bloc); + Statement* s = Statement::make_assignment_operation(OPERATOR_MULTEQ, + ref, i33, bloc); + gogo->add_statement(s); + } + + // Get a pointer to the value of this field. + Expression* offset = Expression::make_struct_field_offset(this, &*pf); + ref = Expression::make_temporary_reference(key, bloc); + Expression* subkey = Expression::make_binary(OPERATOR_PLUS, ref, offset, + bloc); + subkey = Expression::make_cast(key_arg_type, subkey, bloc); + + // Get the size of this field. + Expression* size = Expression::make_type_info(pf->type(), + Expression::TYPE_INFO_SIZE); + + // Get the hash function to use for the type of this field. + Named_object* hash_fn; + Named_object* equal_fn; + pf->type()->type_functions(gogo, pf->type()->named_type(), hash_fntype, + equal_fntype, &hash_fn, &equal_fn); + + // Call the hash function for the field. + Expression_list* args = new Expression_list(); + args->push_back(subkey); + args->push_back(size); + Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); + Expression* call = Expression::make_call(func, args, false, bloc); + + // Add the field's hash value to retval. + Temporary_reference_expression* tref = + Expression::make_temporary_reference(retval, bloc); + tref->set_is_lvalue(); + Statement* s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, + tref, call, bloc); + gogo->add_statement(s); + } + + // Return retval to the caller of the hash function. + Expression_list* vals = new Expression_list(); + ref = Expression::make_temporary_reference(retval, bloc); + vals->push_back(ref); + Statement* s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); +} + +// Write the equality function for a struct which can not use the +// identity function. + +void +Struct_type::write_equal_function(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + // The pointers to the structs we are going to compare. + Named_object* key1_arg = gogo->lookup("key1", NULL); + Named_object* key2_arg = gogo->lookup("key2", NULL); + go_assert(key1_arg != NULL && key2_arg != NULL); + + // Build temporaries with the right types. + Type* pt = Type::make_pointer_type(name != NULL + ? static_cast(name) + : static_cast(this)); + + Expression* ref = Expression::make_var_reference(key1_arg, bloc); + ref = Expression::make_unsafe_cast(pt, ref, bloc); + Temporary_statement* p1 = Statement::make_temporary(pt, ref, bloc); + gogo->add_statement(p1); + + ref = Expression::make_var_reference(key2_arg, bloc); + ref = Expression::make_unsafe_cast(pt, ref, bloc); + Temporary_statement* p2 = Statement::make_temporary(pt, ref, bloc); + gogo->add_statement(p2); + + const Struct_field_list* fields = this->fields_; + unsigned int field_index = 0; + for (Struct_field_list::const_iterator pf = fields->begin(); + 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); + f1 = Expression::make_field_reference(f1, field_index, bloc); + + Expression* f2 = Expression::make_temporary_reference(p2, bloc); + f2 = Expression::make_unary(OPERATOR_MULT, f2, bloc); + f2 = Expression::make_field_reference(f2, field_index, bloc); + + Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, f1, f2, bloc); + + // If the values are not equal, return false. + gogo->start_block(bloc); + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_boolean(false, bloc)); + Statement* s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); + Block* then_block = gogo->finish_block(bloc); + + s = Statement::make_if_statement(cond, then_block, NULL, bloc); + gogo->add_statement(s); + } + + // All the fields are equal, so return true. + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_boolean(true, bloc)); + Statement* s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); +} + +// Reflection string. + +void +Struct_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + ret->append("struct {"); + + for (Struct_field_list::const_iterator p = this->fields_->begin(); + p != this->fields_->end(); + ++p) + { + if (p != this->fields_->begin()) + ret->push_back(';'); + ret->push_back(' '); + if (p->is_anonymous()) + ret->push_back('?'); + else + ret->append(Gogo::unpack_hidden_name(p->field_name())); + ret->push_back(' '); + this->append_reflection(p->type(), gogo, ret); + + if (p->has_tag()) + { + const std::string& tag(p->tag()); + ret->append(" \""); + for (std::string::const_iterator p = tag.begin(); + p != tag.end(); + ++p) + { + if (*p == '\0') + ret->append("\\x00"); + else if (*p == '\n') + ret->append("\\n"); + else if (*p == '\t') + ret->append("\\t"); + else if (*p == '"') + ret->append("\\\""); + else if (*p == '\\') + ret->append("\\\\"); + else + ret->push_back(*p); + } + ret->push_back('"'); + } + } + + if (!this->fields_->empty()) + ret->push_back(' '); + + ret->push_back('}'); +} + +// Mangled name. + +void +Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + ret->push_back('S'); + + const Struct_field_list* fields = this->fields_; + if (fields != NULL) + { + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + if (p->is_anonymous()) + ret->append("0_"); + else + { + std::string n = Gogo::unpack_hidden_name(p->field_name()); + char buf[20]; + snprintf(buf, sizeof buf, "%u_", + static_cast(n.length())); + ret->append(buf); + ret->append(n); + } + this->append_mangled_name(p->type(), gogo, ret); + if (p->has_tag()) + { + const std::string& tag(p->tag()); + std::string out; + for (std::string::const_iterator p = tag.begin(); + p != tag.end(); + ++p) + { + if (ISALNUM(*p) || *p == '_') + out.push_back(*p); + else + { + char buf[20]; + snprintf(buf, sizeof buf, ".%x.", + static_cast(*p)); + out.append(buf); + } + } + char buf[20]; + snprintf(buf, sizeof buf, "T%u_", + static_cast(out.length())); + ret->append(buf); + ret->append(out); + } + } + } + + ret->push_back('e'); +} + +// If the offset of field INDEX in the backend implementation can be +// determined, set *POFFSET to the offset in bytes and return true. +// Otherwise, return false. + +bool +Struct_type::backend_field_offset(Gogo* gogo, unsigned int index, + unsigned int* poffset) +{ + if (!this->is_backend_type_size_known(gogo)) + return false; + Btype* bt = this->get_backend_placeholder(gogo); + size_t offset = gogo->backend()->type_field_offset(bt, index); + *poffset = static_cast(offset); + if (*poffset != offset) + return false; + return true; +} + +// Export. + +void +Struct_type::do_export(Export* exp) const +{ + exp->write_c_string("struct { "); + const Struct_field_list* fields = this->fields_; + go_assert(fields != NULL); + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + if (p->is_anonymous()) + exp->write_string("? "); + else + { + exp->write_string(p->field_name()); + exp->write_c_string(" "); + } + exp->write_type(p->type()); + + if (p->has_tag()) + { + exp->write_c_string(" "); + Expression* expr = + Expression::make_string(p->tag(), Linemap::predeclared_location()); + expr->export_expression(exp); + delete expr; + } + + exp->write_c_string("; "); + } + exp->write_c_string("}"); +} + +// Import. + +Struct_type* +Struct_type::do_import(Import* imp) +{ + imp->require_c_string("struct { "); + Struct_field_list* fields = new Struct_field_list; + if (imp->peek_char() != '}') + { + while (true) + { + std::string name; + if (imp->match_c_string("? ")) + imp->advance(2); + else + { + name = imp->read_identifier(); + imp->require_c_string(" "); + } + Type* ftype = imp->read_type(); + + Struct_field sf(Typed_identifier(name, ftype, imp->location())); + sf.set_is_imported(); + + if (imp->peek_char() == ' ') + { + imp->advance(1); + Expression* expr = Expression::import_expression(imp); + String_expression* sexpr = expr->string_expression(); + go_assert(sexpr != NULL); + sf.set_tag(sexpr->val()); + delete sexpr; + } + + imp->require_c_string("; "); + fields->push_back(sf); + if (imp->peek_char() == '}') + break; + } + } + imp->require_c_string("}"); + + return Type::make_struct_type(fields, imp->location()); +} + +// Make a struct type. + +Struct_type* +Type::make_struct_type(Struct_field_list* fields, + Location location) +{ + return new Struct_type(fields, location); +} + +// Class Array_type. + +// Whether two array types are identical. + +bool +Array_type::is_identical(const Array_type* t, bool errors_are_identical) const +{ + if (!Type::are_identical(this->element_type(), t->element_type(), + errors_are_identical, NULL)) + return false; + + Expression* l1 = this->length(); + Expression* l2 = t->length(); + + // Slices of the same element type are identical. + if (l1 == NULL && l2 == NULL) + return true; + + // Arrays of the same element type are identical if they have the + // same length. + if (l1 != NULL && l2 != NULL) + { + if (l1 == l2) + return true; + + // Try to determine the lengths. If we can't, assume the arrays + // are not identical. + bool ret = false; + Numeric_constant nc1, nc2; + if (l1->numeric_constant_value(&nc1) + && l2->numeric_constant_value(&nc2)) + { + mpz_t v1; + if (nc1.to_int(&v1)) + { + mpz_t v2; + if (nc2.to_int(&v2)) + { + ret = mpz_cmp(v1, v2) == 0; + mpz_clear(v2); + } + mpz_clear(v1); + } + } + return ret; + } + + // Otherwise the arrays are not identical. + return false; +} + +// Traversal. + +int +Array_type::do_traverse(Traverse* traverse) +{ + if (Type::traverse(this->element_type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->length_ != NULL + && Expression::traverse(&this->length_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Check that the length is valid. + +bool +Array_type::verify_length() +{ + if (this->length_ == NULL) + return true; + + Type_context context(Type::lookup_integer_type("int"), false); + this->length_->determine_type(&context); + + if (!this->length_->is_constant()) + { + error_at(this->length_->location(), "array bound is not constant"); + return false; + } + + Numeric_constant nc; + if (!this->length_->numeric_constant_value(&nc)) + { + if (this->length_->type()->integer_type() != NULL + || this->length_->type()->float_type() != NULL) + error_at(this->length_->location(), "array bound is not constant"); + else + error_at(this->length_->location(), "array bound is not numeric"); + return false; + } + + unsigned long val; + switch (nc.to_unsigned_long(&val)) + { + case Numeric_constant::NC_UL_VALID: + break; + case Numeric_constant::NC_UL_NOTINT: + error_at(this->length_->location(), "array bound truncated to integer"); + return false; + case Numeric_constant::NC_UL_NEGATIVE: + error_at(this->length_->location(), "negative array bound"); + return false; + case Numeric_constant::NC_UL_BIG: + error_at(this->length_->location(), "array bound overflows"); + return false; + default: + go_unreachable(); + } + + Type* int_type = Type::lookup_integer_type("int"); + unsigned int tbits = int_type->integer_type()->bits(); + if (sizeof(val) <= tbits * 8 + && val >> (tbits - 1) != 0) + { + error_at(this->length_->location(), "array bound overflows"); + return false; + } + + return true; +} + +// Verify the type. + +bool +Array_type::do_verify() +{ + if (!this->verify_length()) + this->length_ = Expression::make_error(this->length_->location()); + return true; +} + +// Whether we can use memcmp to compare this array. + +bool +Array_type::do_compare_is_identity(Gogo* gogo) +{ + if (this->length_ == NULL) + return false; + + // Check for [...], which indicates that this is not a real type. + if (this->length_->is_nil_expression()) + return false; + + if (!this->element_type_->compare_is_identity(gogo)) + return false; + + // If there is any padding, then we can't use memcmp. + unsigned int size; + unsigned int align; + if (!this->element_type_->backend_type_size(gogo, &size) + || !this->element_type_->backend_type_align(gogo, &align)) + return false; + if ((size & (align - 1)) != 0) + return false; + + return true; +} + +// Array type hash code. + +unsigned int +Array_type::do_hash_for_method(Gogo* gogo) const +{ + // There is no very convenient way to get a hash code for the + // length. + return this->element_type_->hash_for_method(gogo) + 1; +} + +// Write the hash function for an array which can not use the identify +// function. + +void +Array_type::write_hash_function(Gogo* gogo, Named_type* name, + Function_type* hash_fntype, + Function_type* equal_fntype) +{ + Location bloc = Linemap::predeclared_location(); + + // The pointer to the array that we are going to hash. This is an + // argument to the hash function we are implementing here. + Named_object* key_arg = gogo->lookup("key", NULL); + go_assert(key_arg != NULL); + Type* key_arg_type = key_arg->var_value()->type(); + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + // Get a 0. + mpz_t ival; + mpz_init_set_ui(ival, 0); + Expression* zero = Expression::make_integer(&ival, uintptr_type, bloc); + mpz_clear(ival); + + // Make a temporary to hold the return value, initialized to 0. + Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero, + bloc); + gogo->add_statement(retval); + + // Make a temporary to hold the key as a uintptr. + Expression* ref = Expression::make_var_reference(key_arg, bloc); + ref = Expression::make_cast(uintptr_type, ref, bloc); + Temporary_statement* key = Statement::make_temporary(uintptr_type, ref, + bloc); + gogo->add_statement(key); + + // Loop over the array elements. + // for i = range a + Type* int_type = Type::lookup_integer_type("int"); + Temporary_statement* index = Statement::make_temporary(int_type, NULL, bloc); + gogo->add_statement(index); + + Expression* iref = Expression::make_temporary_reference(index, bloc); + Expression* aref = Expression::make_var_reference(key_arg, bloc); + Type* pt = Type::make_pointer_type(name != NULL + ? static_cast(name) + : static_cast(this)); + aref = Expression::make_cast(pt, aref, bloc); + For_range_statement* for_range = Statement::make_for_range_statement(iref, + NULL, + aref, + bloc); + + gogo->start_block(bloc); + + // Multiply retval by 33. + mpz_init_set_ui(ival, 33); + Expression* i33 = Expression::make_integer(&ival, uintptr_type, bloc); + mpz_clear(ival); + + ref = Expression::make_temporary_reference(retval, bloc); + Statement* s = Statement::make_assignment_operation(OPERATOR_MULTEQ, ref, + i33, bloc); + gogo->add_statement(s); + + // Get the hash function for the element type. + Named_object* hash_fn; + Named_object* equal_fn; + this->element_type_->type_functions(gogo, this->element_type_->named_type(), + hash_fntype, equal_fntype, &hash_fn, + &equal_fn); + + // Get a pointer to this element in the loop. + Expression* subkey = Expression::make_temporary_reference(key, bloc); + subkey = Expression::make_cast(key_arg_type, subkey, bloc); + + // Get the size of each element. + Expression* ele_size = Expression::make_type_info(this->element_type_, + Expression::TYPE_INFO_SIZE); + + // Get the hash of this element. + Expression_list* args = new Expression_list(); + args->push_back(subkey); + args->push_back(ele_size); + Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); + Expression* call = Expression::make_call(func, args, false, bloc); + + // Add the element's hash value to retval. + Temporary_reference_expression* tref = + Expression::make_temporary_reference(retval, bloc); + tref->set_is_lvalue(); + s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, tref, call, bloc); + gogo->add_statement(s); + + // Increase the element pointer. + tref = Expression::make_temporary_reference(key, bloc); + tref->set_is_lvalue(); + s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, tref, ele_size, + bloc); + + Block* statements = gogo->finish_block(bloc); + + for_range->add_statements(statements); + gogo->add_statement(for_range); + + // Return retval to the caller of the hash function. + Expression_list* vals = new Expression_list(); + ref = Expression::make_temporary_reference(retval, bloc); + vals->push_back(ref); + s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); +} + +// Write the equality function for an array which can not use the +// identity function. + +void +Array_type::write_equal_function(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + // The pointers to the arrays we are going to compare. + Named_object* key1_arg = gogo->lookup("key1", NULL); + Named_object* key2_arg = gogo->lookup("key2", NULL); + go_assert(key1_arg != NULL && key2_arg != NULL); + + // Build temporaries for the keys with the right types. + Type* pt = Type::make_pointer_type(name != NULL + ? static_cast(name) + : static_cast(this)); + + Expression* ref = Expression::make_var_reference(key1_arg, bloc); + ref = Expression::make_unsafe_cast(pt, ref, bloc); + Temporary_statement* p1 = Statement::make_temporary(pt, ref, bloc); + gogo->add_statement(p1); + + ref = Expression::make_var_reference(key2_arg, bloc); + ref = Expression::make_unsafe_cast(pt, ref, bloc); + Temporary_statement* p2 = Statement::make_temporary(pt, ref, bloc); + gogo->add_statement(p2); + + // Loop over the array elements. + // for i = range a + Type* int_type = Type::lookup_integer_type("int"); + Temporary_statement* index = Statement::make_temporary(int_type, NULL, bloc); + gogo->add_statement(index); + + Expression* iref = Expression::make_temporary_reference(index, bloc); + Expression* aref = Expression::make_temporary_reference(p1, bloc); + For_range_statement* for_range = Statement::make_for_range_statement(iref, + NULL, + aref, + bloc); + + gogo->start_block(bloc); + + // Compare element in P1 and P2. + Expression* e1 = Expression::make_temporary_reference(p1, bloc); + e1 = Expression::make_unary(OPERATOR_MULT, e1, bloc); + ref = Expression::make_temporary_reference(index, bloc); + e1 = Expression::make_array_index(e1, ref, NULL, NULL, bloc); + + Expression* e2 = Expression::make_temporary_reference(p2, bloc); + e2 = Expression::make_unary(OPERATOR_MULT, e2, bloc); + ref = Expression::make_temporary_reference(index, bloc); + e2 = Expression::make_array_index(e2, ref, NULL, NULL, bloc); + + Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, e1, e2, bloc); + + // If the elements are not equal, return false. + gogo->start_block(bloc); + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_boolean(false, bloc)); + Statement* s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); + Block* then_block = gogo->finish_block(bloc); + + s = Statement::make_if_statement(cond, then_block, NULL, bloc); + gogo->add_statement(s); + + Block* statements = gogo->finish_block(bloc); + + for_range->add_statements(statements); + gogo->add_statement(for_range); + + // All the elements are equal, so return true. + vals = new Expression_list(); + vals->push_back(Expression::make_boolean(true, bloc)); + s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); +} + +// Get a tree for the length of a fixed array. The length may be +// computed using a function call, so we must only evaluate it once. + +tree +Array_type::get_length_tree(Gogo* gogo) +{ + go_assert(this->length_ != NULL); + if (this->length_tree_ == NULL_TREE) + { + Numeric_constant nc; + mpz_t val; + if (this->length_->numeric_constant_value(&nc) && nc.to_int(&val)) + { + if (mpz_sgn(val) < 0) + { + this->length_tree_ = error_mark_node; + return this->length_tree_; + } + Type* t = nc.type(); + if (t == NULL) + t = Type::lookup_integer_type("int"); + else if (t->is_abstract()) + t = t->make_non_abstract_type(); + Btype* btype = t->get_backend(gogo); + Bexpression* iexpr = + gogo->backend()->integer_constant_expression(btype, val); + this->length_tree_ = expr_to_tree(iexpr); + mpz_clear(val); + } + else + { + // Make up a translation context for the array length + // expression. FIXME: This won't work in general. + Translate_context context(gogo, NULL, NULL, NULL); + tree len = this->length_->get_tree(&context); + if (len != error_mark_node) + { + Type* int_type = Type::lookup_integer_type("int"); + tree int_type_tree = type_to_tree(int_type->get_backend(gogo)); + len = convert_to_integer(int_type_tree, len); + len = save_expr(len); + } + this->length_tree_ = len; + } + } + return this->length_tree_; +} + +// Get the backend representation of the fields of a slice. This is +// not declared in types.h so that types.h doesn't have to #include +// backend.h. +// +// We use int for the count and capacity fields. This matches 6g. +// The language more or less assumes that we can't allocate space of a +// size which does not fit in int. + +static void +get_backend_slice_fields(Gogo* gogo, Array_type* type, bool use_placeholder, + std::vector* bfields) +{ + bfields->resize(3); + + Type* pet = Type::make_pointer_type(type->element_type()); + Btype* pbet = (use_placeholder + ? pet->get_backend_placeholder(gogo) + : pet->get_backend(gogo)); + Location ploc = Linemap::predeclared_location(); + + Backend::Btyped_identifier* p = &(*bfields)[0]; + p->name = "__values"; + p->btype = pbet; + p->location = ploc; + + Type* int_type = Type::lookup_integer_type("int"); + + p = &(*bfields)[1]; + p->name = "__count"; + p->btype = int_type->get_backend(gogo); + p->location = ploc; + + p = &(*bfields)[2]; + p->name = "__capacity"; + p->btype = int_type->get_backend(gogo); + p->location = ploc; +} + +// Get a tree for the type of this array. A fixed array is simply +// represented as ARRAY_TYPE with the appropriate index--i.e., it is +// just like an array in C. An open array is a struct with three +// fields: a data pointer, the length, and the capacity. + +Btype* +Array_type::do_get_backend(Gogo* gogo) +{ + if (this->length_ == NULL) + { + std::vector bfields; + get_backend_slice_fields(gogo, this, false, &bfields); + return gogo->backend()->struct_type(bfields); + } + else + { + Btype* element = this->get_backend_element(gogo, false); + Bexpression* len = this->get_backend_length(gogo); + return gogo->backend()->array_type(element, len); + } +} + +// Return the backend representation of the element type. + +Btype* +Array_type::get_backend_element(Gogo* gogo, bool use_placeholder) +{ + if (use_placeholder) + return this->element_type_->get_backend_placeholder(gogo); + else + return this->element_type_->get_backend(gogo); +} + +// Return the backend representation of the length. + +Bexpression* +Array_type::get_backend_length(Gogo* gogo) +{ + return tree_to_expr(this->get_length_tree(gogo)); +} + +// Finish backend representation of the array. + +void +Array_type::finish_backend_element(Gogo* gogo) +{ + Type* et = this->array_type()->element_type(); + et->get_backend(gogo); + if (this->is_slice_type()) + { + // This relies on the fact that we always use the same + // structure for a pointer to any given type. + Type* pet = Type::make_pointer_type(et); + pet->get_backend(gogo); + } +} + +// Return an expression for a pointer to the values in ARRAY. + +Expression* +Array_type::get_value_pointer(Gogo*, Expression* array) const +{ + if (this->length() != NULL) + { + // Fixed array. + go_assert(array->type()->array_type() != NULL); + Type* etype = array->type()->array_type()->element_type(); + array = Expression::make_unary(OPERATOR_AND, array, array->location()); + return Expression::make_cast(Type::make_pointer_type(etype), array, + array->location()); + } + + // Open array. + return Expression::make_slice_info(array, + Expression::SLICE_INFO_VALUE_POINTER, + array->location()); +} + +// Return an expression for the length of the array ARRAY which has this +// type. + +Expression* +Array_type::get_length(Gogo*, Expression* array) const +{ + if (this->length_ != NULL) + return this->length_; + + // This is an open array. We need to read the length field. + return Expression::make_slice_info(array, Expression::SLICE_INFO_LENGTH, + array->location()); +} + +// Return an expression for the capacity of the array ARRAY which has this +// type. + +Expression* +Array_type::get_capacity(Gogo*, Expression* array) const +{ + if (this->length_ != NULL) + return this->length_; + + // This is an open array. We need to read the capacity field. + return Expression::make_slice_info(array, Expression::SLICE_INFO_CAPACITY, + array->location()); +} + +// Export. + +void +Array_type::do_export(Export* exp) const +{ + exp->write_c_string("["); + if (this->length_ != NULL) + this->length_->export_expression(exp); + exp->write_c_string("] "); + exp->write_type(this->element_type_); +} + +// Import. + +Array_type* +Array_type::do_import(Import* imp) +{ + imp->require_c_string("["); + Expression* length; + if (imp->peek_char() == ']') + length = NULL; + else + length = Expression::import_expression(imp); + imp->require_c_string("] "); + Type* element_type = imp->read_type(); + return Type::make_array_type(element_type, length); +} + +// The type of an array type descriptor. + +Type* +Array_type::make_array_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* tdt = Type::make_type_descriptor_type(); + Type* ptdt = Type::make_type_descriptor_ptr_type(); + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + Struct_type* sf = + Type::make_builtin_struct_type(4, + "", tdt, + "elem", ptdt, + "slice", ptdt, + "len", uintptr_type); + + ret = Type::make_builtin_named_type("ArrayType", sf); + } + + return ret; +} + +// The type of an slice type descriptor. + +Type* +Array_type::make_slice_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* tdt = Type::make_type_descriptor_type(); + Type* ptdt = Type::make_type_descriptor_ptr_type(); + + Struct_type* sf = + Type::make_builtin_struct_type(2, + "", tdt, + "elem", ptdt); + + ret = Type::make_builtin_named_type("SliceType", sf); + } + + return ret; +} + +// Build a type descriptor for an array/slice type. + +Expression* +Array_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + if (this->length_ != NULL) + return this->array_type_descriptor(gogo, name); + else + return this->slice_type_descriptor(gogo, name); +} + +// Build a type descriptor for an array type. + +Expression* +Array_type::array_type_descriptor(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + Type* atdt = Array_type::make_array_type_descriptor_type(); + + const Struct_field_list* fields = atdt->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(3); + + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("commonType")); + vals->push_back(this->type_descriptor_constructor(gogo, + RUNTIME_TYPE_KIND_ARRAY, + name, NULL, true)); + + ++p; + go_assert(p->is_field_name("elem")); + vals->push_back(Expression::make_type_descriptor(this->element_type_, bloc)); + + ++p; + go_assert(p->is_field_name("slice")); + Type* slice_type = Type::make_array_type(this->element_type_, NULL); + vals->push_back(Expression::make_type_descriptor(slice_type, bloc)); + + ++p; + go_assert(p->is_field_name("len")); + vals->push_back(Expression::make_cast(p->type(), this->length_, bloc)); + + ++p; + go_assert(p == fields->end()); + + return Expression::make_struct_composite_literal(atdt, vals, bloc); +} + +// Build a type descriptor for a slice type. + +Expression* +Array_type::slice_type_descriptor(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + Type* stdt = Array_type::make_slice_type_descriptor_type(); + + const Struct_field_list* fields = stdt->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(2); + + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("commonType")); + vals->push_back(this->type_descriptor_constructor(gogo, + RUNTIME_TYPE_KIND_SLICE, + name, NULL, true)); + + ++p; + go_assert(p->is_field_name("elem")); + vals->push_back(Expression::make_type_descriptor(this->element_type_, bloc)); + + ++p; + go_assert(p == fields->end()); + + return Expression::make_struct_composite_literal(stdt, vals, bloc); +} + +// Reflection string. + +void +Array_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + ret->push_back('['); + if (this->length_ != NULL) + { + Numeric_constant nc; + unsigned long val; + if (!this->length_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&val) != Numeric_constant::NC_UL_VALID) + error_at(this->length_->location(), "invalid array length"); + else + { + char buf[50]; + snprintf(buf, sizeof buf, "%lu", val); + ret->append(buf); + } + } + ret->push_back(']'); + + this->append_reflection(this->element_type_, gogo, ret); +} + +// Mangled name. + +void +Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + ret->push_back('A'); + this->append_mangled_name(this->element_type_, gogo, ret); + if (this->length_ != NULL) + { + Numeric_constant nc; + unsigned long val; + if (!this->length_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&val) != Numeric_constant::NC_UL_VALID) + error_at(this->length_->location(), "invalid array length"); + else + { + char buf[50]; + snprintf(buf, sizeof buf, "%lu", val); + ret->append(buf); + } + } + ret->push_back('e'); +} + +// Make an array type. + +Array_type* +Type::make_array_type(Type* element_type, Expression* length) +{ + return new Array_type(element_type, length); +} + +// Class Map_type. + +// Traversal. + +int +Map_type::do_traverse(Traverse* traverse) +{ + if (Type::traverse(this->key_type_, traverse) == TRAVERSE_EXIT + || Type::traverse(this->val_type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Check that the map type is OK. + +bool +Map_type::do_verify() +{ + // The runtime support uses "map[void]void". + if (!this->key_type_->is_comparable() && !this->key_type_->is_void_type()) + error_at(this->location_, "invalid map key type"); + return true; +} + +// Whether two map types are identical. + +bool +Map_type::is_identical(const Map_type* t, bool errors_are_identical) const +{ + 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. + +unsigned int +Map_type::do_hash_for_method(Gogo* gogo) const +{ + return (this->key_type_->hash_for_method(gogo) + + this->val_type_->hash_for_method(gogo) + + 2); +} + +// Get the backend representation for a map type. A map type is +// represented as a pointer to a struct. The struct is __go_map in +// libgo/map.h. + +Btype* +Map_type::do_get_backend(Gogo* gogo) +{ + static Btype* backend_map_type; + if (backend_map_type == NULL) + { + std::vector bfields(4); + + Location bloc = Linemap::predeclared_location(); + + Type* pdt = Type::make_type_descriptor_ptr_type(); + bfields[0].name = "__descriptor"; + bfields[0].btype = pdt->get_backend(gogo); + bfields[0].location = bloc; + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + bfields[1].name = "__element_count"; + bfields[1].btype = uintptr_type->get_backend(gogo); + bfields[1].location = bloc; + + bfields[2].name = "__bucket_count"; + bfields[2].btype = bfields[1].btype; + bfields[2].location = bloc; + + Btype* bvt = gogo->backend()->void_type(); + Btype* bpvt = gogo->backend()->pointer_type(bvt); + Btype* bppvt = gogo->backend()->pointer_type(bpvt); + bfields[3].name = "__buckets"; + bfields[3].btype = bppvt; + bfields[3].location = bloc; + + Btype *bt = gogo->backend()->struct_type(bfields); + bt = gogo->backend()->named_type("__go_map", bt, bloc); + backend_map_type = gogo->backend()->pointer_type(bt); + } + return backend_map_type; +} + +// The type of a map type descriptor. + +Type* +Map_type::make_map_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* tdt = Type::make_type_descriptor_type(); + Type* ptdt = Type::make_type_descriptor_ptr_type(); + + Struct_type* sf = + Type::make_builtin_struct_type(3, + "", tdt, + "key", ptdt, + "elem", ptdt); + + ret = Type::make_builtin_named_type("MapType", sf); + } + + return ret; +} + +// Build a type descriptor for a map type. + +Expression* +Map_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + Type* mtdt = Map_type::make_map_type_descriptor_type(); + + const Struct_field_list* fields = mtdt->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(3); + + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("commonType")); + vals->push_back(this->type_descriptor_constructor(gogo, + RUNTIME_TYPE_KIND_MAP, + name, NULL, true)); + + ++p; + go_assert(p->is_field_name("key")); + vals->push_back(Expression::make_type_descriptor(this->key_type_, bloc)); + + ++p; + go_assert(p->is_field_name("elem")); + vals->push_back(Expression::make_type_descriptor(this->val_type_, bloc)); + + ++p; + go_assert(p == fields->end()); + + return Expression::make_struct_composite_literal(mtdt, vals, bloc); +} + +// A mapping from map types to map descriptors. + +Map_type::Map_descriptors Map_type::map_descriptors; + +// Build a map descriptor for this type. Return a pointer to it. + +Bexpression* +Map_type::map_descriptor_pointer(Gogo* gogo, Location location) +{ + Bvariable* bvar = this->map_descriptor(gogo); + Bexpression* var_expr = gogo->backend()->var_expression(bvar, location); + return gogo->backend()->address_expression(var_expr, location); +} + +// Build a map descriptor for this type. + +Bvariable* +Map_type::map_descriptor(Gogo* gogo) +{ + std::pair val(this, NULL); + std::pair ins = + Map_type::map_descriptors.insert(val); + if (!ins.second) + return ins.first->second; + + Type* key_type = this->key_type_; + Type* val_type = this->val_type_; + + // The map entry type is a struct with three fields. Build that + // struct so that we can get the offsets of the key and value within + // a map entry. The first field should technically be a pointer to + // this type itself, but since we only care about field offsets we + // just use pointer to bool. + Type* pbool = Type::make_pointer_type(Type::make_boolean_type()); + Struct_type* map_entry_type = + Type::make_builtin_struct_type(3, + "__next", pbool, + "__key", key_type, + "__val", val_type); + + Type* map_descriptor_type = Map_type::make_map_descriptor_type(); + + const Struct_field_list* fields = + map_descriptor_type->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(4); + + Location bloc = Linemap::predeclared_location(); + + Struct_field_list::const_iterator p = fields->begin(); + + go_assert(p->is_field_name("__map_descriptor")); + vals->push_back(Expression::make_type_descriptor(this, bloc)); + + ++p; + go_assert(p->is_field_name("__entry_size")); + Expression::Type_info type_info = Expression::TYPE_INFO_SIZE; + vals->push_back(Expression::make_type_info(map_entry_type, type_info)); + + Struct_field_list::const_iterator pf = map_entry_type->fields()->begin(); + ++pf; + go_assert(pf->is_field_name("__key")); + + ++p; + go_assert(p->is_field_name("__key_offset")); + vals->push_back(Expression::make_struct_field_offset(map_entry_type, &*pf)); + + ++pf; + go_assert(pf->is_field_name("__val")); + + ++p; + go_assert(p->is_field_name("__val_offset")); + vals->push_back(Expression::make_struct_field_offset(map_entry_type, &*pf)); + + ++p; + go_assert(p == fields->end()); + + Expression* initializer = + Expression::make_struct_composite_literal(map_descriptor_type, vals, bloc); + + std::string mangled_name = "__go_map_" + this->mangled_name(gogo); + Btype* map_descriptor_btype = map_descriptor_type->get_backend(gogo); + Bvariable* bvar = gogo->backend()->immutable_struct(mangled_name, false, + true, + map_descriptor_btype, + bloc); + + Translate_context context(gogo, NULL, NULL, NULL); + context.set_is_const(); + Bexpression* binitializer = tree_to_expr(initializer->get_tree(&context)); + + gogo->backend()->immutable_struct_set_init(bvar, mangled_name, false, true, + map_descriptor_btype, bloc, + binitializer); + + ins.first->second = bvar; + return bvar; +} + +// Build the type of a map descriptor. This must match the struct +// __go_map_descriptor in libgo/runtime/map.h. + +Type* +Map_type::make_map_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* ptdt = Type::make_type_descriptor_ptr_type(); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Struct_type* sf = + Type::make_builtin_struct_type(4, + "__map_descriptor", ptdt, + "__entry_size", uintptr_type, + "__key_offset", uintptr_type, + "__val_offset", uintptr_type); + ret = Type::make_builtin_named_type("__go_map_descriptor", sf); + } + return ret; +} + +// Reflection string for a map. + +void +Map_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + ret->append("map["); + this->append_reflection(this->key_type_, gogo, ret); + ret->append("]"); + this->append_reflection(this->val_type_, gogo, ret); +} + +// Mangled name for a map. + +void +Map_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + ret->push_back('M'); + this->append_mangled_name(this->key_type_, gogo, ret); + ret->append("__"); + this->append_mangled_name(this->val_type_, gogo, ret); +} + +// Export a map type. + +void +Map_type::do_export(Export* exp) const +{ + exp->write_c_string("map ["); + exp->write_type(this->key_type_); + exp->write_c_string("] "); + exp->write_type(this->val_type_); +} + +// Import a map type. + +Map_type* +Map_type::do_import(Import* imp) +{ + imp->require_c_string("map ["); + Type* key_type = imp->read_type(); + imp->require_c_string("] "); + Type* val_type = imp->read_type(); + return Type::make_map_type(key_type, val_type, imp->location()); +} + +// Make a map type. + +Map_type* +Type::make_map_type(Type* key_type, Type* val_type, Location location) +{ + return new Map_type(key_type, val_type, location); +} + +// Class Channel_type. + +// Hash code. + +unsigned int +Channel_type::do_hash_for_method(Gogo* gogo) const +{ + unsigned int ret = 0; + if (this->may_send_) + ret += 1; + if (this->may_receive_) + ret += 2; + if (this->element_type_ != NULL) + ret += this->element_type_->hash_for_method(gogo) << 2; + return ret << 3; +} + +// Whether this type is the same as T. + +bool +Channel_type::is_identical(const Channel_type* t, + bool errors_are_identical) const +{ + 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_); +} + +// Return the tree for a channel type. A channel is a pointer to a +// __go_channel struct. The __go_channel struct is defined in +// libgo/runtime/channel.h. + +Btype* +Channel_type::do_get_backend(Gogo* gogo) +{ + static Btype* backend_channel_type; + if (backend_channel_type == NULL) + { + std::vector bfields; + Btype* bt = gogo->backend()->struct_type(bfields); + bt = gogo->backend()->named_type("__go_channel", bt, + Linemap::predeclared_location()); + backend_channel_type = gogo->backend()->pointer_type(bt); + } + return backend_channel_type; +} + +// Build a type descriptor for a channel type. + +Type* +Channel_type::make_chan_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* tdt = Type::make_type_descriptor_type(); + Type* ptdt = Type::make_type_descriptor_ptr_type(); + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + Struct_type* sf = + Type::make_builtin_struct_type(3, + "", tdt, + "elem", ptdt, + "dir", uintptr_type); + + ret = Type::make_builtin_named_type("ChanType", sf); + } + + return ret; +} + +// Build a type descriptor for a map type. + +Expression* +Channel_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + Type* ctdt = Channel_type::make_chan_type_descriptor_type(); + + const Struct_field_list* fields = ctdt->struct_type()->fields(); + + Expression_list* vals = new Expression_list(); + vals->reserve(3); + + Struct_field_list::const_iterator p = fields->begin(); + go_assert(p->is_field_name("commonType")); + vals->push_back(this->type_descriptor_constructor(gogo, + RUNTIME_TYPE_KIND_CHAN, + name, NULL, true)); + + ++p; + go_assert(p->is_field_name("elem")); + vals->push_back(Expression::make_type_descriptor(this->element_type_, bloc)); + + ++p; + go_assert(p->is_field_name("dir")); + // These bits must match the ones in libgo/runtime/go-type.h. + int val = 0; + if (this->may_receive_) + val |= 1; + if (this->may_send_) + val |= 2; + mpz_t iv; + mpz_init_set_ui(iv, val); + vals->push_back(Expression::make_integer(&iv, p->type(), bloc)); + mpz_clear(iv); + + ++p; + go_assert(p == fields->end()); + + return Expression::make_struct_composite_literal(ctdt, vals, bloc); +} + +// Reflection string. + +void +Channel_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + if (!this->may_send_) + ret->append("<-"); + ret->append("chan"); + if (!this->may_receive_) + ret->append("<-"); + ret->push_back(' '); + this->append_reflection(this->element_type_, gogo, ret); +} + +// Mangled name. + +void +Channel_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + ret->push_back('C'); + this->append_mangled_name(this->element_type_, gogo, ret); + if (this->may_send_) + ret->push_back('s'); + if (this->may_receive_) + ret->push_back('r'); + ret->push_back('e'); +} + +// Export. + +void +Channel_type::do_export(Export* exp) const +{ + exp->write_c_string("chan "); + if (this->may_send_ && !this->may_receive_) + exp->write_c_string("-< "); + else if (this->may_receive_ && !this->may_send_) + exp->write_c_string("<- "); + exp->write_type(this->element_type_); +} + +// Import. + +Channel_type* +Channel_type::do_import(Import* imp) +{ + imp->require_c_string("chan "); + + bool may_send; + bool may_receive; + if (imp->match_c_string("-< ")) + { + imp->advance(3); + may_send = true; + may_receive = false; + } + else if (imp->match_c_string("<- ")) + { + imp->advance(3); + may_receive = true; + may_send = false; + } + else + { + may_send = true; + may_receive = true; + } + + Type* element_type = imp->read_type(); + + return Type::make_channel_type(may_send, may_receive, element_type); +} + +// Make a new channel type. + +Channel_type* +Type::make_channel_type(bool send, bool receive, Type* element_type) +{ + return new Channel_type(send, receive, element_type); +} + +// Class Interface_type. + +// Return the list of methods. + +const Typed_identifier_list* +Interface_type::methods() const +{ + go_assert(this->methods_are_finalized_ || saw_errors()); + return this->all_methods_; +} + +// Return the number of methods. + +size_t +Interface_type::method_count() const +{ + go_assert(this->methods_are_finalized_ || saw_errors()); + return this->all_methods_ == NULL ? 0 : this->all_methods_->size(); +} + +// Traversal. + +int +Interface_type::do_traverse(Traverse* traverse) +{ + Typed_identifier_list* methods = (this->methods_are_finalized_ + ? this->all_methods_ + : this->parse_methods_); + if (methods == NULL) + return TRAVERSE_CONTINUE; + return methods->traverse(traverse); +} + +// Finalize the methods. This handles interface inheritance. + +void +Interface_type::finalize_methods() +{ + if (this->methods_are_finalized_) + return; + this->methods_are_finalized_ = true; + if (this->parse_methods_ == NULL) + return; + + this->all_methods_ = new Typed_identifier_list(); + this->all_methods_->reserve(this->parse_methods_->size()); + Typed_identifier_list inherit; + for (Typed_identifier_list::const_iterator pm = + this->parse_methods_->begin(); + pm != this->parse_methods_->end(); + ++pm) + { + const Typed_identifier* p = &*pm; + if (p->name().empty()) + inherit.push_back(*p); + else if (this->find_method(p->name()) == NULL) + this->all_methods_->push_back(*p); + else + error_at(p->location(), "duplicate method %qs", + Gogo::message_name(p->name()).c_str()); + } + + std::vector seen; + seen.reserve(inherit.size()); + bool issued_recursive_error = false; + while (!inherit.empty()) + { + Type* t = inherit.back().type(); + Location tl = inherit.back().location(); + inherit.pop_back(); + + Interface_type* it = t->interface_type(); + if (it == NULL) + { + if (!t->is_error()) + error_at(tl, "interface contains embedded non-interface"); + continue; + } + if (it == this) + { + if (!issued_recursive_error) + { + error_at(tl, "invalid recursive interface"); + issued_recursive_error = true; + } + continue; + } + + Named_type* nt = t->named_type(); + if (nt != NULL && it->parse_methods_ != NULL) + { + std::vector::const_iterator q; + for (q = seen.begin(); q != seen.end(); ++q) + { + if (*q == nt) + { + error_at(tl, "inherited interface loop"); + break; + } + } + if (q != seen.end()) + continue; + seen.push_back(nt); + } + + const Typed_identifier_list* imethods = it->parse_methods_; + if (imethods == NULL) + continue; + for (Typed_identifier_list::const_iterator q = imethods->begin(); + q != imethods->end(); + ++q) + { + if (q->name().empty()) + inherit.push_back(*q); + else if (this->find_method(q->name()) == NULL) + this->all_methods_->push_back(Typed_identifier(q->name(), + q->type(), tl)); + else + error_at(tl, "inherited method %qs is ambiguous", + Gogo::message_name(q->name()).c_str()); + } + } + + if (!this->all_methods_->empty()) + this->all_methods_->sort_by_name(); + else + { + delete this->all_methods_; + this->all_methods_ = NULL; + } +} + +// Return the method NAME, or NULL. + +const Typed_identifier* +Interface_type::find_method(const std::string& name) const +{ + go_assert(this->methods_are_finalized_); + if (this->all_methods_ == NULL) + return NULL; + for (Typed_identifier_list::const_iterator p = this->all_methods_->begin(); + p != this->all_methods_->end(); + ++p) + if (p->name() == name) + return &*p; + return NULL; +} + +// Return the method index. + +size_t +Interface_type::method_index(const std::string& name) const +{ + go_assert(this->methods_are_finalized_ && this->all_methods_ != NULL); + size_t ret = 0; + for (Typed_identifier_list::const_iterator p = this->all_methods_->begin(); + p != this->all_methods_->end(); + ++p, ++ret) + if (p->name() == name) + return ret; + go_unreachable(); +} + +// Return whether NAME is an unexported method, for better error +// reporting. + +bool +Interface_type::is_unexported_method(Gogo* gogo, const std::string& name) const +{ + go_assert(this->methods_are_finalized_); + if (this->all_methods_ == NULL) + return false; + for (Typed_identifier_list::const_iterator p = this->all_methods_->begin(); + p != this->all_methods_->end(); + ++p) + { + const std::string& method_name(p->name()); + if (Gogo::is_hidden_name(method_name) + && name == Gogo::unpack_hidden_name(method_name) + && gogo->pack_hidden_name(name, false) != method_name) + return true; + } + return false; +} + +// Whether this type is identical with T. + +bool +Interface_type::is_identical(const Interface_type* t, + bool errors_are_identical) const +{ + // If methods have not been finalized, then we are asking whether + // func redeclarations are the same. This is an error, so for + // simplicity we say they are never the same. + if (!this->methods_are_finalized_ || !t->methods_are_finalized_) + return false; + + // We require the same methods with the same types. The methods + // have already been sorted. + if (this->all_methods_ == NULL || t->all_methods_ == NULL) + return this->all_methods_ == t->all_methods_; + + if (this->assume_identical(this, t) || t->assume_identical(t, this)) + return true; + + Assume_identical* hold_ai = this->assume_identical_; + Assume_identical ai; + ai.t1 = this; + ai.t2 = t; + ai.next = hold_ai; + this->assume_identical_ = &ai; + + Typed_identifier_list::const_iterator p1 = this->all_methods_->begin(); + Typed_identifier_list::const_iterator p2; + for (p2 = t->all_methods_->begin(); p2 != t->all_methods_->end(); ++p1, ++p2) + { + if (p1 == this->all_methods_->end()) + break; + if (p1->name() != p2->name() + || !Type::are_identical(p1->type(), p2->type(), + errors_are_identical, NULL)) + break; + } + + this->assume_identical_ = hold_ai; + + return p1 == this->all_methods_->end() && p2 == t->all_methods_->end(); +} + +// Return true if T1 and T2 are assumed to be identical during a type +// comparison. + +bool +Interface_type::assume_identical(const Interface_type* t1, + const Interface_type* t2) const +{ + for (Assume_identical* p = this->assume_identical_; + p != NULL; + p = p->next) + if ((p->t1 == t1 && p->t2 == t2) || (p->t1 == t2 && p->t2 == t1)) + return true; + return false; +} + +// Whether we can assign the interface type T to this type. The types +// are known to not be identical. An interface assignment is only +// permitted if T is known to implement all methods in THIS. +// Otherwise a type guard is required. + +bool +Interface_type::is_compatible_for_assign(const Interface_type* t, + std::string* reason) const +{ + go_assert(this->methods_are_finalized_ && t->methods_are_finalized_); + if (this->all_methods_ == NULL) + return true; + for (Typed_identifier_list::const_iterator p = this->all_methods_->begin(); + p != this->all_methods_->end(); + ++p) + { + const Typed_identifier* m = t->find_method(p->name()); + if (m == NULL) + { + if (reason != NULL) + { + char buf[200]; + snprintf(buf, sizeof buf, + _("need explicit conversion; missing method %s%s%s"), + open_quote, Gogo::message_name(p->name()).c_str(), + close_quote); + reason->assign(buf); + } + return false; + } + + std::string subreason; + if (!Type::are_identical(p->type(), m->type(), true, &subreason)) + { + if (reason != NULL) + { + std::string n = Gogo::message_name(p->name()); + size_t len = 100 + n.length() + subreason.length(); + char* buf = new char[len]; + if (subreason.empty()) + snprintf(buf, len, _("incompatible type for method %s%s%s"), + open_quote, n.c_str(), close_quote); + else + snprintf(buf, len, + _("incompatible type for method %s%s%s (%s)"), + open_quote, n.c_str(), close_quote, + subreason.c_str()); + reason->assign(buf); + delete[] buf; + } + return false; + } + } + + return true; +} + +// Hash code. + +unsigned int +Interface_type::do_hash_for_method(Gogo*) const +{ + go_assert(this->methods_are_finalized_); + unsigned int ret = 0; + if (this->all_methods_ != NULL) + { + for (Typed_identifier_list::const_iterator p = + this->all_methods_->begin(); + p != this->all_methods_->end(); + ++p) + { + ret = Type::hash_string(p->name(), ret); + // We don't use the method type in the hash, to avoid + // infinite recursion if an interface method uses a type + // which is an interface which inherits from the interface + // itself. + // type T interface { F() interface {T}} + ret <<= 1; + } + } + return ret; +} + +// Return true if T implements the interface. If it does not, and +// REASON is not NULL, set *REASON to a useful error message. + +bool +Interface_type::implements_interface(const Type* t, std::string* reason) const +{ + go_assert(this->methods_are_finalized_); + if (this->all_methods_ == NULL) + return true; + + bool is_pointer = false; + const Named_type* nt = t->named_type(); + const Struct_type* st = t->struct_type(); + // If we start with a named type, we don't dereference it to find + // methods. + if (nt == NULL) + { + const Type* pt = t->points_to(); + if (pt != NULL) + { + // If T is a pointer to a named type, then we need to look at + // the type to which it points. + is_pointer = true; + nt = pt->named_type(); + st = pt->struct_type(); + } + } + + // If we have a named type, get the methods from it rather than from + // any struct type. + if (nt != NULL) + st = NULL; + + // Only named and struct types have methods. + if (nt == NULL && st == NULL) + { + if (reason != NULL) + { + if (t->points_to() != NULL + && t->points_to()->interface_type() != NULL) + reason->assign(_("pointer to interface type has no methods")); + else + reason->assign(_("type has no methods")); + } + return false; + } + + if (nt != NULL ? !nt->has_any_methods() : !st->has_any_methods()) + { + if (reason != NULL) + { + if (t->points_to() != NULL + && t->points_to()->interface_type() != NULL) + reason->assign(_("pointer to interface type has no methods")); + else + reason->assign(_("type has no methods")); + } + return false; + } + + for (Typed_identifier_list::const_iterator p = this->all_methods_->begin(); + p != this->all_methods_->end(); + ++p) + { + bool is_ambiguous = false; + Method* m = (nt != NULL + ? nt->method_function(p->name(), &is_ambiguous) + : st->method_function(p->name(), &is_ambiguous)); + if (m == NULL) + { + if (reason != NULL) + { + std::string n = Gogo::message_name(p->name()); + size_t len = n.length() + 100; + char* buf = new char[len]; + if (is_ambiguous) + snprintf(buf, len, _("ambiguous method %s%s%s"), + open_quote, n.c_str(), close_quote); + else + snprintf(buf, len, _("missing method %s%s%s"), + open_quote, n.c_str(), close_quote); + reason->assign(buf); + delete[] buf; + } + return false; + } + + Function_type *p_fn_type = p->type()->function_type(); + Function_type* m_fn_type = m->type()->function_type(); + go_assert(p_fn_type != NULL && m_fn_type != NULL); + std::string subreason; + if (!p_fn_type->is_identical(m_fn_type, true, true, &subreason)) + { + if (reason != NULL) + { + std::string n = Gogo::message_name(p->name()); + size_t len = 100 + n.length() + subreason.length(); + char* buf = new char[len]; + if (subreason.empty()) + snprintf(buf, len, _("incompatible type for method %s%s%s"), + open_quote, n.c_str(), close_quote); + else + snprintf(buf, len, + _("incompatible type for method %s%s%s (%s)"), + open_quote, n.c_str(), close_quote, + subreason.c_str()); + reason->assign(buf); + delete[] buf; + } + return false; + } + + if (!is_pointer && !m->is_value_method()) + { + if (reason != NULL) + { + 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 receiver"), + open_quote, n.c_str(), close_quote); + reason->assign(buf); + delete[] buf; + } + return false; + } + + // If the magic //go:nointerface comment was used, the method + // may not be used to implement interfaces. + if (m->nointerface()) + { + if (reason != NULL) + { + 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 is marked go:nointerface"), + open_quote, n.c_str(), close_quote); + reason->assign(buf); + delete[] buf; + } + return false; + } + } + + return true; +} + +// Return the backend representation of the empty interface type. We +// use the same struct for all empty interfaces. + +Btype* +Interface_type::get_backend_empty_interface_type(Gogo* gogo) +{ + static Btype* empty_interface_type; + if (empty_interface_type == NULL) + { + std::vector bfields(2); + + Location bloc = Linemap::predeclared_location(); + + Type* pdt = Type::make_type_descriptor_ptr_type(); + bfields[0].name = "__type_descriptor"; + bfields[0].btype = pdt->get_backend(gogo); + bfields[0].location = bloc; + + Type* vt = Type::make_pointer_type(Type::make_void_type()); + bfields[1].name = "__object"; + bfields[1].btype = vt->get_backend(gogo); + bfields[1].location = bloc; + + empty_interface_type = gogo->backend()->struct_type(bfields); + } + return empty_interface_type; +} + +// Return a pointer to the backend representation of the method table. + +Btype* +Interface_type::get_backend_methods(Gogo* gogo) +{ + if (this->bmethods_ != NULL && !this->bmethods_is_placeholder_) + return this->bmethods_; + + Location loc = this->location(); + + std::vector + mfields(this->all_methods_->size() + 1); + + Type* pdt = Type::make_type_descriptor_ptr_type(); + mfields[0].name = "__type_descriptor"; + mfields[0].btype = pdt->get_backend(gogo); + mfields[0].location = loc; + + std::string last_name = ""; + size_t i = 1; + for (Typed_identifier_list::const_iterator p = this->all_methods_->begin(); + p != this->all_methods_->end(); + ++p, ++i) + { + // The type of the method in Go only includes the parameters. + // The actual method also has a receiver, which is always a + // pointer. We need to add that pointer type here in order to + // generate the correct type for the backend. + Function_type* ft = p->type()->function_type(); + go_assert(ft->receiver() == NULL); + + const Typed_identifier_list* params = ft->parameters(); + Typed_identifier_list* mparams = new Typed_identifier_list(); + if (params != NULL) + mparams->reserve(params->size() + 1); + Type* vt = Type::make_pointer_type(Type::make_void_type()); + mparams->push_back(Typed_identifier("", vt, ft->location())); + if (params != NULL) + { + for (Typed_identifier_list::const_iterator pp = params->begin(); + pp != params->end(); + ++pp) + mparams->push_back(*pp); + } + + Typed_identifier_list* mresults = (ft->results() == NULL + ? NULL + : ft->results()->copy()); + Function_type* mft = Type::make_function_type(NULL, mparams, mresults, + ft->location()); + + mfields[i].name = Gogo::unpack_hidden_name(p->name()); + mfields[i].btype = mft->get_backend_fntype(gogo); + mfields[i].location = loc; + + // Sanity check: the names should be sorted. + go_assert(p->name() > last_name); + last_name = p->name(); + } + + Btype* st = gogo->backend()->struct_type(mfields); + Btype* ret = gogo->backend()->pointer_type(st); + + if (this->bmethods_ != NULL && this->bmethods_is_placeholder_) + gogo->backend()->set_placeholder_pointer_type(this->bmethods_, ret); + this->bmethods_ = ret; + this->bmethods_is_placeholder_ = false; + return ret; +} + +// Return a placeholder for the pointer to the backend methods table. + +Btype* +Interface_type::get_backend_methods_placeholder(Gogo* gogo) +{ + if (this->bmethods_ == NULL) + { + Location loc = this->location(); + this->bmethods_ = gogo->backend()->placeholder_pointer_type("", loc, + false); + this->bmethods_is_placeholder_ = true; + } + return this->bmethods_; +} + +// Return the fields of a non-empty interface type. This is not +// declared in types.h so that types.h doesn't have to #include +// backend.h. + +static void +get_backend_interface_fields(Gogo* gogo, Interface_type* type, + bool use_placeholder, + std::vector* bfields) +{ + Location loc = type->location(); + + bfields->resize(2); + + (*bfields)[0].name = "__methods"; + (*bfields)[0].btype = (use_placeholder + ? type->get_backend_methods_placeholder(gogo) + : type->get_backend_methods(gogo)); + (*bfields)[0].location = loc; + + Type* vt = Type::make_pointer_type(Type::make_void_type()); + (*bfields)[1].name = "__object"; + (*bfields)[1].btype = vt->get_backend(gogo); + (*bfields)[1].location = Linemap::predeclared_location(); +} + +// Return a tree for an interface type. An interface is a pointer to +// a struct. The struct has three fields. The first field is a +// pointer to the type descriptor for the dynamic type of the object. +// The second field is a pointer to a table of methods for the +// interface to be used with the object. The third field is the value +// of the object itself. + +Btype* +Interface_type::do_get_backend(Gogo* gogo) +{ + if (this->is_empty()) + return Interface_type::get_backend_empty_interface_type(gogo); + else + { + if (this->interface_btype_ != NULL) + return this->interface_btype_; + this->interface_btype_ = + gogo->backend()->placeholder_struct_type("", this->location_); + std::vector bfields; + get_backend_interface_fields(gogo, this, false, &bfields); + if (!gogo->backend()->set_placeholder_struct_type(this->interface_btype_, + bfields)) + this->interface_btype_ = gogo->backend()->error_type(); + return this->interface_btype_; + } +} + +// Finish the backend representation of the methods. + +void +Interface_type::finish_backend_methods(Gogo* gogo) +{ + if (!this->is_empty()) + { + const Typed_identifier_list* methods = this->methods(); + if (methods != NULL) + { + for (Typed_identifier_list::const_iterator p = methods->begin(); + p != methods->end(); + ++p) + p->type()->get_backend(gogo); + } + + // Getting the backend methods now will set the placeholder + // pointer. + this->get_backend_methods(gogo); + } +} + +// The type of an interface type descriptor. + +Type* +Interface_type::make_interface_type_descriptor_type() +{ + static Type* ret; + if (ret == NULL) + { + Type* tdt = Type::make_type_descriptor_type(); + Type* ptdt = Type::make_type_descriptor_ptr_type(); + + Type* string_type = Type::lookup_string_type(); + Type* pointer_string_type = Type::make_pointer_type(string_type); + + Struct_type* sm = + Type::make_builtin_struct_type(3, + "name", pointer_string_type, + "pkgPath", pointer_string_type, + "typ", ptdt); + + Type* nsm = Type::make_builtin_named_type("imethod", sm); + + Type* slice_nsm = Type::make_array_type(nsm, NULL); + + Struct_type* s = Type::make_builtin_struct_type(2, + "", tdt, + "methods", slice_nsm); + + ret = Type::make_builtin_named_type("InterfaceType", s); + } + + return ret; +} + +// Build a type descriptor for an interface type. + +Expression* +Interface_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + Location bloc = Linemap::predeclared_location(); + + Type* itdt = Interface_type::make_interface_type_descriptor_type(); + + const Struct_field_list* ifields = itdt->struct_type()->fields(); + + Expression_list* ivals = new Expression_list(); + ivals->reserve(2); + + Struct_field_list::const_iterator pif = ifields->begin(); + go_assert(pif->is_field_name("commonType")); + const int rt = RUNTIME_TYPE_KIND_INTERFACE; + ivals->push_back(this->type_descriptor_constructor(gogo, rt, name, NULL, + true)); + + ++pif; + go_assert(pif->is_field_name("methods")); + + Expression_list* methods = new Expression_list(); + if (this->all_methods_ != NULL) + { + Type* elemtype = pif->type()->array_type()->element_type(); + + methods->reserve(this->all_methods_->size()); + for (Typed_identifier_list::const_iterator pm = + this->all_methods_->begin(); + pm != this->all_methods_->end(); + ++pm) + { + const Struct_field_list* mfields = elemtype->struct_type()->fields(); + + Expression_list* mvals = new Expression_list(); + mvals->reserve(3); + + Struct_field_list::const_iterator pmf = mfields->begin(); + go_assert(pmf->is_field_name("name")); + std::string s = Gogo::unpack_hidden_name(pm->name()); + Expression* e = Expression::make_string(s, bloc); + mvals->push_back(Expression::make_unary(OPERATOR_AND, e, bloc)); + + ++pmf; + go_assert(pmf->is_field_name("pkgPath")); + if (!Gogo::is_hidden_name(pm->name())) + mvals->push_back(Expression::make_nil(bloc)); + else + { + s = Gogo::hidden_name_pkgpath(pm->name()); + e = Expression::make_string(s, bloc); + mvals->push_back(Expression::make_unary(OPERATOR_AND, e, bloc)); + } + + ++pmf; + go_assert(pmf->is_field_name("typ")); + mvals->push_back(Expression::make_type_descriptor(pm->type(), bloc)); + + ++pmf; + go_assert(pmf == mfields->end()); + + e = Expression::make_struct_composite_literal(elemtype, mvals, + bloc); + methods->push_back(e); + } + } + + ivals->push_back(Expression::make_slice_composite_literal(pif->type(), + methods, bloc)); + + ++pif; + go_assert(pif == ifields->end()); + + return Expression::make_struct_composite_literal(itdt, ivals, bloc); +} + +// Reflection string. + +void +Interface_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + ret->append("interface {"); + const Typed_identifier_list* methods = this->parse_methods_; + if (methods != NULL) + { + ret->push_back(' '); + for (Typed_identifier_list::const_iterator p = methods->begin(); + p != methods->end(); + ++p) + { + if (p != methods->begin()) + ret->append("; "); + if (p->name().empty()) + this->append_reflection(p->type(), gogo, ret); + else + { + if (!Gogo::is_hidden_name(p->name())) + ret->append(p->name()); + else if (gogo->pkgpath_from_option()) + ret->append(p->name().substr(1)); + else + { + // If no -fgo-pkgpath option, backward compatibility + // for how this used to work before -fgo-pkgpath was + // introduced. + std::string pkgpath = Gogo::hidden_name_pkgpath(p->name()); + ret->append(pkgpath.substr(pkgpath.find('.') + 1)); + ret->push_back('.'); + ret->append(Gogo::unpack_hidden_name(p->name())); + } + std::string sub = p->type()->reflection(gogo); + go_assert(sub.compare(0, 4, "func") == 0); + sub = sub.substr(4); + ret->append(sub); + } + } + ret->push_back(' '); + } + ret->append("}"); +} + +// Mangled name. + +void +Interface_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + go_assert(this->methods_are_finalized_); + + ret->push_back('I'); + + const Typed_identifier_list* methods = this->all_methods_; + if (methods != NULL && !this->seen_) + { + this->seen_ = true; + for (Typed_identifier_list::const_iterator p = methods->begin(); + p != methods->end(); + ++p) + { + if (!p->name().empty()) + { + 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(n.length())); + ret->append(buf); + ret->append(n); + } + this->append_mangled_name(p->type(), gogo, ret); + } + this->seen_ = false; + } + + ret->push_back('e'); +} + +// Export. + +void +Interface_type::do_export(Export* exp) const +{ + exp->write_c_string("interface { "); + + const Typed_identifier_list* methods = this->parse_methods_; + if (methods != NULL) + { + for (Typed_identifier_list::const_iterator pm = methods->begin(); + pm != methods->end(); + ++pm) + { + if (pm->name().empty()) + { + exp->write_c_string("? "); + exp->write_type(pm->type()); + } + else + { + exp->write_string(pm->name()); + exp->write_c_string(" ("); + + const Function_type* fntype = pm->type()->function_type(); + + bool first = true; + const Typed_identifier_list* parameters = fntype->parameters(); + if (parameters != NULL) + { + bool is_varargs = fntype->is_varargs(); + for (Typed_identifier_list::const_iterator pp = + parameters->begin(); + pp != parameters->end(); + ++pp) + { + if (first) + first = false; + else + exp->write_c_string(", "); + exp->write_name(pp->name()); + exp->write_c_string(" "); + if (!is_varargs || pp + 1 != parameters->end()) + exp->write_type(pp->type()); + else + { + exp->write_c_string("..."); + Type *pptype = pp->type(); + exp->write_type(pptype->array_type()->element_type()); + } + } + } + + exp->write_c_string(")"); + + const Typed_identifier_list* results = fntype->results(); + if (results != NULL) + { + exp->write_c_string(" "); + if (results->size() == 1 && results->begin()->name().empty()) + exp->write_type(results->begin()->type()); + else + { + first = true; + exp->write_c_string("("); + for (Typed_identifier_list::const_iterator p = + results->begin(); + p != results->end(); + ++p) + { + if (first) + first = false; + else + exp->write_c_string(", "); + exp->write_name(p->name()); + exp->write_c_string(" "); + exp->write_type(p->type()); + } + exp->write_c_string(")"); + } + } + } + + exp->write_c_string("; "); + } + } + + exp->write_c_string("}"); +} + +// Import an interface type. + +Interface_type* +Interface_type::do_import(Import* imp) +{ + imp->require_c_string("interface { "); + + Typed_identifier_list* methods = new Typed_identifier_list; + while (imp->peek_char() != '}') + { + std::string name = imp->read_identifier(); + + if (name == "?") + { + imp->require_c_string(" "); + Type* t = imp->read_type(); + methods->push_back(Typed_identifier("", t, imp->location())); + imp->require_c_string("; "); + continue; + } + + imp->require_c_string(" ("); + + Typed_identifier_list* parameters; + bool is_varargs = false; + if (imp->peek_char() == ')') + parameters = NULL; + else + { + parameters = new Typed_identifier_list; + while (true) + { + std::string name = imp->read_name(); + imp->require_c_string(" "); + + if (imp->match_c_string("...")) + { + imp->advance(3); + is_varargs = true; + } + + Type* ptype = imp->read_type(); + if (is_varargs) + ptype = Type::make_array_type(ptype, NULL); + parameters->push_back(Typed_identifier(name, ptype, + imp->location())); + if (imp->peek_char() != ',') + break; + go_assert(!is_varargs); + imp->require_c_string(", "); + } + } + imp->require_c_string(")"); + + Typed_identifier_list* results; + if (imp->peek_char() != ' ') + results = NULL; + else + { + results = new Typed_identifier_list; + imp->advance(1); + if (imp->peek_char() != '(') + { + Type* rtype = imp->read_type(); + results->push_back(Typed_identifier("", rtype, imp->location())); + } + else + { + imp->advance(1); + while (true) + { + std::string name = imp->read_name(); + imp->require_c_string(" "); + Type* rtype = imp->read_type(); + results->push_back(Typed_identifier(name, rtype, + imp->location())); + if (imp->peek_char() != ',') + break; + imp->require_c_string(", "); + } + imp->require_c_string(")"); + } + } + + Function_type* fntype = Type::make_function_type(NULL, parameters, + results, + imp->location()); + if (is_varargs) + fntype->set_is_varargs(); + methods->push_back(Typed_identifier(name, fntype, imp->location())); + + imp->require_c_string("; "); + } + + imp->require_c_string("}"); + + if (methods->empty()) + { + delete methods; + methods = NULL; + } + + return Type::make_interface_type(methods, imp->location()); +} + +// Make an interface type. + +Interface_type* +Type::make_interface_type(Typed_identifier_list* methods, + Location location) +{ + return new Interface_type(methods, location); +} + +// Make an empty interface type. + +Interface_type* +Type::make_empty_interface_type(Location location) +{ + Interface_type* ret = new Interface_type(NULL, location); + ret->finalize_methods(); + return ret; +} + +// Class Method. + +// Bind a method to an object. + +Expression* +Method::bind_method(Expression* expr, Location location) const +{ + if (this->stub_ == NULL) + { + // When there is no stub object, the binding is determined by + // the child class. + return this->do_bind_method(expr, location); + } + return Expression::make_bound_method(expr, this, this->stub_, location); +} + +// Return the named object associated with a method. This may only be +// called after methods are finalized. + +Named_object* +Method::named_object() const +{ + if (this->stub_ != NULL) + return this->stub_; + return this->do_named_object(); +} + +// Class Named_method. + +// The type of the method. + +Function_type* +Named_method::do_type() const +{ + if (this->named_object_->is_function()) + return this->named_object_->func_value()->type(); + else if (this->named_object_->is_function_declaration()) + return this->named_object_->func_declaration_value()->type(); + else + go_unreachable(); +} + +// Return the location of the method receiver. + +Location +Named_method::do_receiver_location() const +{ + return this->do_type()->receiver()->location(); +} + +// Bind a method to an object. + +Expression* +Named_method::do_bind_method(Expression* expr, Location location) const +{ + Named_object* no = this->named_object_; + Bound_method_expression* bme = Expression::make_bound_method(expr, this, + no, location); + // If this is not a local method, and it does not use a stub, then + // the real method expects a different type. We need to cast the + // first argument. + if (this->depth() > 0 && !this->needs_stub_method()) + { + Function_type* ftype = this->do_type(); + go_assert(ftype->is_method()); + Type* frtype = ftype->receiver()->type(); + bme->set_first_argument_type(frtype); + } + return bme; +} + +// Return whether this method should not participate in interfaces. + +bool +Named_method::do_nointerface() const +{ + Named_object* no = this->named_object_; + return no->is_function() && no->func_value()->nointerface(); +} + +// Class Interface_method. + +// Bind a method to an object. + +Expression* +Interface_method::do_bind_method(Expression* expr, + Location location) const +{ + return Expression::make_interface_field_reference(expr, this->name_, + location); +} + +// Class Methods. + +// Insert a new method. Return true if it was inserted, false +// otherwise. + +bool +Methods::insert(const std::string& name, Method* m) +{ + std::pair ins = + this->methods_.insert(std::make_pair(name, m)); + if (ins.second) + return true; + else + { + Method* old_method = ins.first->second; + if (m->depth() < old_method->depth()) + { + delete old_method; + ins.first->second = m; + return true; + } + else + { + if (m->depth() == old_method->depth()) + old_method->set_is_ambiguous(); + return false; + } + } +} + +// Return the number of unambiguous methods. + +size_t +Methods::count() const +{ + size_t ret = 0; + for (Method_map::const_iterator p = this->methods_.begin(); + p != this->methods_.end(); + ++p) + if (!p->second->is_ambiguous()) + ++ret; + return ret; +} + +// Class Named_type. + +// Return the name of the type. + +const std::string& +Named_type::name() const +{ + return this->named_object_->name(); +} + +// Return the name of the type to use in an error message. + +std::string +Named_type::message_name() const +{ + return this->named_object_->message_name(); +} + +// Whether this is an alias. There are currently only two aliases so +// we just recognize them by name. + +bool +Named_type::is_alias() const +{ + if (!this->is_builtin()) + return false; + const std::string& name(this->name()); + return name == "byte" || name == "rune"; +} + +// Return the base type for this type. We have to be careful about +// circular type definitions, which are invalid but may be seen here. + +Type* +Named_type::named_base() +{ + if (this->seen_) + return this; + this->seen_ = true; + Type* ret = this->type_->base(); + this->seen_ = false; + return ret; +} + +const Type* +Named_type::named_base() const +{ + if (this->seen_) + return this; + this->seen_ = true; + const Type* ret = this->type_->base(); + this->seen_ = false; + return ret; +} + +// Return whether this is an error type. We have to be careful about +// circular type definitions, which are invalid but may be seen here. + +bool +Named_type::is_named_error_type() const +{ + if (this->seen_) + return false; + this->seen_ = true; + bool ret = this->type_->is_error_type(); + this->seen_ = false; + return ret; +} + +// Whether this type is comparable. We have to be careful about +// circular type definitions. + +bool +Named_type::named_type_is_comparable(std::string* reason) const +{ + if (this->seen_) + return false; + this->seen_ = true; + bool ret = Type::are_compatible_for_comparison(true, this->type_, + this->type_, reason); + this->seen_ = false; + return ret; +} + +// Add a method to this type. + +Named_object* +Named_type::add_method(const std::string& name, Function* function) +{ + if (this->local_methods_ == NULL) + this->local_methods_ = new Bindings(NULL); + return this->local_methods_->add_function(name, NULL, function); +} + +// Add a method declaration to this type. + +Named_object* +Named_type::add_method_declaration(const std::string& name, Package* package, + Function_type* type, + Location location) +{ + if (this->local_methods_ == NULL) + this->local_methods_ = new Bindings(NULL); + return this->local_methods_->add_function_declaration(name, package, type, + location); +} + +// Add an existing method to this type. + +void +Named_type::add_existing_method(Named_object* no) +{ + if (this->local_methods_ == NULL) + this->local_methods_ = new Bindings(NULL); + this->local_methods_->add_named_object(no); +} + +// Look for a local method NAME, and returns its named object, or NULL +// if not there. + +Named_object* +Named_type::find_local_method(const std::string& name) const +{ + if (this->local_methods_ == NULL) + return NULL; + return this->local_methods_->lookup(name); +} + +// Return whether NAME is an unexported field or method, for better +// error reporting. + +bool +Named_type::is_unexported_local_method(Gogo* gogo, + const std::string& name) const +{ + Bindings* methods = this->local_methods_; + if (methods != NULL) + { + for (Bindings::const_declarations_iterator p = + methods->begin_declarations(); + p != methods->end_declarations(); + ++p) + { + if (Gogo::is_hidden_name(p->first) + && name == Gogo::unpack_hidden_name(p->first) + && gogo->pack_hidden_name(name, false) != p->first) + return true; + } + } + return false; +} + +// Build the complete list of methods for this type, which means +// recursively including all methods for anonymous fields. Create all +// stub methods. + +void +Named_type::finalize_methods(Gogo* gogo) +{ + if (this->all_methods_ != NULL) + return; + + if (this->local_methods_ != NULL + && (this->points_to() != NULL || this->interface_type() != NULL)) + { + const Bindings* lm = this->local_methods_; + for (Bindings::const_declarations_iterator p = lm->begin_declarations(); + p != lm->end_declarations(); + ++p) + error_at(p->second->location(), + "invalid pointer or interface receiver type"); + delete this->local_methods_; + this->local_methods_ = NULL; + return; + } + + Type::finalize_methods(gogo, this, this->location_, &this->all_methods_); +} + +// Return the method NAME, or NULL if there isn't one or if it is +// ambiguous. Set *IS_AMBIGUOUS if the method exists but is +// ambiguous. + +Method* +Named_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 +Named_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_); +} + +// Return whether a named type has any hidden fields. + +bool +Named_type::named_type_has_hidden_fields(std::string* reason) const +{ + if (this->seen_) + return false; + this->seen_ = true; + bool ret = this->type_->has_hidden_fields(this, reason); + this->seen_ = false; + return ret; +} + +// Look for a use of a complete type within another type. This is +// used to check that we don't try to use a type within itself. + +class Find_type_use : public Traverse +{ + public: + Find_type_use(Named_type* find_type) + : Traverse(traverse_types), + find_type_(find_type), found_(false) + { } + + // Whether we found the type. + bool + found() const + { return this->found_; } + + protected: + int + type(Type*); + + private: + // The type we are looking for. + Named_type* find_type_; + // Whether we found the type. + bool found_; +}; + +// Check for FIND_TYPE in TYPE. + +int +Find_type_use::type(Type* type) +{ + if (type->named_type() != NULL && this->find_type_ == type->named_type()) + { + this->found_ = true; + return TRAVERSE_EXIT; + } + + // It's OK if we see a reference to the type in any type which is + // essentially a pointer: a pointer, a slice, a function, a map, or + // a channel. + if (type->points_to() != NULL + || type->is_slice_type() + || type->function_type() != NULL + || type->map_type() != NULL + || type->channel_type() != NULL) + return TRAVERSE_SKIP_COMPONENTS; + + // For an interface, a reference to the type in a method type should + // be ignored, but we have to consider direct inheritance. When + // this is called, there may be cases of direct inheritance + // represented as a method with no name. + if (type->interface_type() != NULL) + { + const Typed_identifier_list* methods = type->interface_type()->methods(); + if (methods != NULL) + { + for (Typed_identifier_list::const_iterator p = methods->begin(); + p != methods->end(); + ++p) + { + if (p->name().empty()) + { + if (Type::traverse(p->type(), this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + } + return TRAVERSE_SKIP_COMPONENTS; + } + + // Otherwise, FIND_TYPE_ depends on TYPE, in the sense that we need + // to convert TYPE to the backend representation before we convert + // FIND_TYPE_. + if (type->named_type() != NULL) + { + switch (type->base()->classification()) + { + case Type::TYPE_ERROR: + case Type::TYPE_BOOLEAN: + case Type::TYPE_INTEGER: + case Type::TYPE_FLOAT: + case Type::TYPE_COMPLEX: + case Type::TYPE_STRING: + case Type::TYPE_NIL: + break; + + case Type::TYPE_ARRAY: + case Type::TYPE_STRUCT: + this->find_type_->add_dependency(type->named_type()); + break; + + case Type::TYPE_NAMED: + case Type::TYPE_FORWARD: + go_assert(saw_errors()); + break; + + case Type::TYPE_VOID: + case Type::TYPE_SINK: + case Type::TYPE_FUNCTION: + case Type::TYPE_POINTER: + case Type::TYPE_CALL_MULTIPLE_RESULT: + case Type::TYPE_MAP: + case Type::TYPE_CHANNEL: + case Type::TYPE_INTERFACE: + default: + go_unreachable(); + } + } + + return TRAVERSE_CONTINUE; +} + +// Verify that a named type does not refer to itself. + +bool +Named_type::do_verify() +{ + if (this->is_verified_) + return true; + this->is_verified_ = true; + + Find_type_use find(this); + Type::traverse(this->type_, &find); + if (find.found()) + { + error_at(this->location_, "invalid recursive type %qs", + this->message_name().c_str()); + this->is_error_ = true; + return false; + } + + // Check whether any of the local methods overloads an existing + // struct field or interface method. We don't need to check the + // list of methods against itself: that is handled by the Bindings + // code. + if (this->local_methods_ != NULL) + { + Struct_type* st = this->type_->struct_type(); + if (st != NULL) + { + for (Bindings::const_declarations_iterator p = + this->local_methods_->begin_declarations(); + p != this->local_methods_->end_declarations(); + ++p) + { + const std::string& name(p->first); + if (st != NULL && st->find_local_field(name, NULL) != NULL) + { + error_at(p->second->location(), + "method %qs redeclares struct field name", + Gogo::message_name(name).c_str()); + } + } + } + } + + return true; +} + +// Return whether this type is or contains a pointer. + +bool +Named_type::do_has_pointer() const +{ + if (this->seen_) + return false; + this->seen_ = true; + bool ret = this->type_->has_pointer(); + this->seen_ = false; + return ret; +} + +// Return whether comparisons for this type can use the identity +// function. + +bool +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. + if (this->seen_in_compare_is_identity_) + return false; + this->seen_in_compare_is_identity_ = true; + bool ret = this->type_->compare_is_identity(gogo); + this->seen_in_compare_is_identity_ = false; + return ret; +} + +// Return a hash code. This is used for method lookup. We simply +// hash on the name itself. + +unsigned int +Named_type::do_hash_for_method(Gogo* gogo) const +{ + if (this->is_alias()) + return this->type_->named_type()->do_hash_for_method(gogo); + + const std::string& name(this->named_object()->name()); + unsigned int ret = Type::hash_string(name, 0); + + // GOGO will be NULL here when called from Type_hash_identical. + // That is OK because that is only used for internal hash tables + // where we are going to be comparing named types for equality. In + // other cases, which are cases where the runtime is going to + // compare hash codes to see if the types are the same, we need to + // include the pkgpath in the hash. + if (gogo != NULL && !Gogo::is_hidden_name(name) && !this->is_builtin()) + { + const Package* package = this->named_object()->package(); + if (package == NULL) + ret = Type::hash_string(gogo->pkgpath(), ret); + else + ret = Type::hash_string(package->pkgpath(), ret); + } + + return ret; +} + +// Convert a named type to the backend representation. In order to +// get dependencies right, we fill in a dummy structure for this type, +// then convert all the dependencies, then complete this type. When +// this function is complete, the size of the type is known. + +void +Named_type::convert(Gogo* gogo) +{ + if (this->is_error_ || this->is_converted_) + return; + + this->create_placeholder(gogo); + + // If we are called to turn unsafe.Sizeof into a constant, we may + // not have verified the type yet. We have to make sure it is + // verified, since that sets the list of dependencies. + this->verify(); + + // Convert all the dependencies. If they refer indirectly back to + // this type, they will pick up the intermediate tree we just + // created. + for (std::vector::const_iterator p = this->dependencies_.begin(); + p != this->dependencies_.end(); + ++p) + (*p)->convert(gogo); + + // Complete this type. + Btype* bt = this->named_btype_; + Type* base = this->type_->base(); + switch (base->classification()) + { + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_STRING: + case TYPE_NIL: + break; + + case TYPE_MAP: + case TYPE_CHANNEL: + break; + + case TYPE_FUNCTION: + case TYPE_POINTER: + // The size of these types is already correct. We don't worry + // about filling them in until later, when we also track + // circular references. + break; + + case TYPE_STRUCT: + { + std::vector bfields; + get_backend_struct_fields(gogo, base->struct_type()->fields(), + true, &bfields); + if (!gogo->backend()->set_placeholder_struct_type(bt, bfields)) + bt = gogo->backend()->error_type(); + } + break; + + case TYPE_ARRAY: + // Slice types were completed in create_placeholder. + if (!base->is_slice_type()) + { + Btype* bet = base->array_type()->get_backend_element(gogo, true); + Bexpression* blen = base->array_type()->get_backend_length(gogo); + if (!gogo->backend()->set_placeholder_array_type(bt, bet, blen)) + bt = gogo->backend()->error_type(); + } + break; + + case TYPE_INTERFACE: + // Interface types were completed in create_placeholder. + break; + + case TYPE_ERROR: + return; + + default: + case TYPE_SINK: + case TYPE_CALL_MULTIPLE_RESULT: + case TYPE_NAMED: + case TYPE_FORWARD: + go_unreachable(); + } + + this->named_btype_ = bt; + this->is_converted_ = true; + this->is_placeholder_ = false; +} + +// Create the placeholder for a named type. This is the first step in +// converting to the backend representation. + +void +Named_type::create_placeholder(Gogo* gogo) +{ + if (this->is_error_) + this->named_btype_ = gogo->backend()->error_type(); + + if (this->named_btype_ != NULL) + return; + + // Create the structure for this type. Note that because we call + // base() here, we don't attempt to represent a named type defined + // as another named type. Instead both named types will point to + // different base representations. + Type* base = this->type_->base(); + Btype* bt; + bool set_name = true; + switch (base->classification()) + { + case TYPE_ERROR: + this->is_error_ = true; + this->named_btype_ = gogo->backend()->error_type(); + return; + + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_STRING: + case TYPE_NIL: + // These are simple basic types, we can just create them + // directly. + bt = Type::get_named_base_btype(gogo, base); + break; + + case TYPE_MAP: + case TYPE_CHANNEL: + // All maps and channels have the same backend representation. + bt = Type::get_named_base_btype(gogo, base); + break; + + case TYPE_FUNCTION: + case TYPE_POINTER: + { + bool for_function = base->classification() == TYPE_FUNCTION; + bt = gogo->backend()->placeholder_pointer_type(this->name(), + this->location_, + for_function); + set_name = false; + } + break; + + case TYPE_STRUCT: + bt = gogo->backend()->placeholder_struct_type(this->name(), + this->location_); + this->is_placeholder_ = true; + set_name = false; + break; + + case TYPE_ARRAY: + if (base->is_slice_type()) + bt = gogo->backend()->placeholder_struct_type(this->name(), + this->location_); + else + { + bt = gogo->backend()->placeholder_array_type(this->name(), + this->location_); + this->is_placeholder_ = true; + } + set_name = false; + break; + + case TYPE_INTERFACE: + if (base->interface_type()->is_empty()) + bt = Interface_type::get_backend_empty_interface_type(gogo); + else + { + bt = gogo->backend()->placeholder_struct_type(this->name(), + this->location_); + set_name = false; + } + break; + + default: + case TYPE_SINK: + case TYPE_CALL_MULTIPLE_RESULT: + case TYPE_NAMED: + case TYPE_FORWARD: + go_unreachable(); + } + + if (set_name) + bt = gogo->backend()->named_type(this->name(), bt, this->location_); + + this->named_btype_ = bt; + + if (base->is_slice_type()) + { + // We do not record slices as dependencies of other types, + // because we can fill them in completely here with the final + // size. + std::vector bfields; + get_backend_slice_fields(gogo, base->array_type(), true, &bfields); + if (!gogo->backend()->set_placeholder_struct_type(bt, bfields)) + this->named_btype_ = gogo->backend()->error_type(); + } + else if (base->interface_type() != NULL + && !base->interface_type()->is_empty()) + { + // We do not record interfaces as dependencies of other types, + // because we can fill them in completely here with the final + // size. + std::vector bfields; + get_backend_interface_fields(gogo, base->interface_type(), true, + &bfields); + if (!gogo->backend()->set_placeholder_struct_type(bt, bfields)) + this->named_btype_ = gogo->backend()->error_type(); + } +} + +// Get a tree for a named type. + +Btype* +Named_type::do_get_backend(Gogo* gogo) +{ + if (this->is_error_) + return gogo->backend()->error_type(); + + Btype* bt = this->named_btype_; + + if (!gogo->named_types_are_converted()) + { + // We have not completed converting named types. NAMED_BTYPE_ + // is a placeholder and we shouldn't do anything further. + if (bt != NULL) + return bt; + + // We don't build dependencies for types whose sizes do not + // change or are not relevant, so we may see them here while + // converting types. + this->create_placeholder(gogo); + bt = this->named_btype_; + go_assert(bt != NULL); + return bt; + } + + // We are not converting types. This should only be called if the + // type has already been converted. + if (!this->is_converted_) + { + go_assert(saw_errors()); + return gogo->backend()->error_type(); + } + + go_assert(bt != NULL); + + // Complete the tree. + Type* base = this->type_->base(); + Btype* bt1; + switch (base->classification()) + { + case TYPE_ERROR: + return gogo->backend()->error_type(); + + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_STRING: + case TYPE_NIL: + case TYPE_MAP: + case TYPE_CHANNEL: + return bt; + + case TYPE_STRUCT: + if (!this->seen_in_get_backend_) + { + this->seen_in_get_backend_ = true; + base->struct_type()->finish_backend_fields(gogo); + this->seen_in_get_backend_ = false; + } + return bt; + + case TYPE_ARRAY: + if (!this->seen_in_get_backend_) + { + this->seen_in_get_backend_ = true; + base->array_type()->finish_backend_element(gogo); + this->seen_in_get_backend_ = false; + } + return bt; + + case TYPE_INTERFACE: + if (!this->seen_in_get_backend_) + { + this->seen_in_get_backend_ = true; + base->interface_type()->finish_backend_methods(gogo); + this->seen_in_get_backend_ = false; + } + return bt; + + case TYPE_FUNCTION: + // Don't build a circular data structure. GENERIC can't handle + // it. + if (this->seen_in_get_backend_) + { + this->is_circular_ = true; + return gogo->backend()->circular_pointer_type(bt, false); + } + this->seen_in_get_backend_ = true; + bt1 = Type::get_named_base_btype(gogo, base); + this->seen_in_get_backend_ = false; + if (this->is_circular_) + bt1 = gogo->backend()->circular_pointer_type(bt, false); + if (!gogo->backend()->set_placeholder_pointer_type(bt, bt1)) + bt = gogo->backend()->error_type(); + return bt; + + case TYPE_POINTER: + // Don't build a circular data structure. GENERIC can't handle + // it. + if (this->seen_in_get_backend_) + { + this->is_circular_ = true; + return gogo->backend()->circular_pointer_type(bt, false); + } + this->seen_in_get_backend_ = true; + bt1 = Type::get_named_base_btype(gogo, base); + this->seen_in_get_backend_ = false; + if (this->is_circular_) + bt1 = gogo->backend()->circular_pointer_type(bt, false); + if (!gogo->backend()->set_placeholder_pointer_type(bt, bt1)) + bt = gogo->backend()->error_type(); + return bt; + + default: + case TYPE_SINK: + case TYPE_CALL_MULTIPLE_RESULT: + case TYPE_NAMED: + case TYPE_FORWARD: + go_unreachable(); + } + + go_unreachable(); +} + +// Build a type descriptor for a named type. + +Expression* +Named_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + if (name == NULL && this->is_alias()) + return this->type_->type_descriptor(gogo, this->type_); + + // If NAME is not NULL, then we don't really want the type + // descriptor for this type; we want the descriptor for the + // underlying type, giving it the name NAME. + return this->named_type_descriptor(gogo, this->type_, + name == NULL ? this : name); +} + +// Add to the reflection string. This is used mostly for the name of +// the type used in a type descriptor, not for actual reflection +// strings. + +void +Named_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + if (this->is_alias()) + { + this->append_reflection(this->type_, gogo, ret); + return; + } + if (!this->is_builtin()) + { + // We handle -fgo-prefix and -fgo-pkgpath differently here for + // compatibility with how the compiler worked before + // -fgo-pkgpath was introduced. When -fgo-pkgpath is specified, + // we use it to make a unique reflection string, so that the + // type canonicalization in the reflect package will work. In + // order to be compatible with the gc compiler, we put tabs into + // the package path, so that the reflect methods can discard it. + const Package* package = this->named_object_->package(); + if (gogo->pkgpath_from_option()) + { + ret->push_back('\t'); + ret->append(package != NULL + ? package->pkgpath_symbol() + : gogo->pkgpath_symbol()); + ret->push_back('\t'); + } + ret->append(package != NULL + ? package->package_name() + : gogo->package_name()); + ret->push_back('.'); + } + 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())); +} + +// Get the mangled name. + +void +Named_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + if (this->is_alias()) + { + this->append_mangled_name(this->type_, gogo, ret); + return; + } + Named_object* no = this->named_object_; + std::string name; + if (this->is_builtin()) + go_assert(this->in_function_ == NULL); + else + { + const std::string& pkgpath(no->package() == NULL + ? gogo->pkgpath_symbol() + : no->package()->pkgpath_symbol()); + name = pkgpath; + name.append(1, '.'); + if (this->in_function_ != NULL) + { + 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())); + char buf[20]; + snprintf(buf, sizeof buf, "N%u_", static_cast(name.length())); + ret->append(buf); + ret->append(name); +} + +// Export the type. This is called to export a global type. + +void +Named_type::export_named_type(Export* exp, const std::string&) const +{ + // We don't need to write the name of the type here, because it will + // be written by Export::write_type anyhow. + exp->write_c_string("type "); + exp->write_type(this); + exp->write_c_string(";\n"); +} + +// Import a named type. + +void +Named_type::import_named_type(Import* imp, Named_type** ptype) +{ + imp->require_c_string("type "); + Type *type = imp->read_type(); + *ptype = type->named_type(); + go_assert(*ptype != NULL); + imp->require_c_string(";\n"); +} + +// Export the type when it is referenced by another type. In this +// case Export::export_type will already have issued the name. + +void +Named_type::do_export(Export* exp) const +{ + exp->write_type(this->type_); + + // To save space, we only export the methods directly attached to + // this type. + Bindings* methods = this->local_methods_; + if (methods == NULL) + return; + + exp->write_c_string("\n"); + for (Bindings::const_definitions_iterator p = methods->begin_definitions(); + p != methods->end_definitions(); + ++p) + { + exp->write_c_string(" "); + (*p)->export_named_object(exp); + } + + for (Bindings::const_declarations_iterator p = methods->begin_declarations(); + p != methods->end_declarations(); + ++p) + { + if (p->second->is_function_declaration()) + { + exp->write_c_string(" "); + p->second->export_named_object(exp); + } + } +} + +// Make a named type. + +Named_type* +Type::make_named_type(Named_object* named_object, Type* type, + Location location) +{ + return new Named_type(named_object, type, location); +} + +// Finalize the methods for TYPE. It will be a named type or a struct +// type. This sets *ALL_METHODS to the list of methods, and builds +// all required stubs. + +void +Type::finalize_methods(Gogo* gogo, const Type* type, Location location, + Methods** all_methods) +{ + *all_methods = NULL; + Types_seen types_seen; + Type::add_methods_for_type(type, NULL, 0, false, false, &types_seen, + all_methods); + Type::build_stub_methods(gogo, type, *all_methods, location); +} + +// Add the methods for TYPE to *METHODS. FIELD_INDEXES is used to +// build up the struct field indexes as we go. DEPTH is the depth of +// the field within TYPE. IS_EMBEDDED_POINTER is true if we are +// adding these methods for an anonymous field with pointer type. +// NEEDS_STUB_METHOD is true if we need to use a stub method which +// calls the real method. TYPES_SEEN is used to avoid infinite +// recursion. + +void +Type::add_methods_for_type(const Type* type, + const Method::Field_indexes* field_indexes, + unsigned int depth, + bool is_embedded_pointer, + bool needs_stub_method, + Types_seen* types_seen, + Methods** methods) +{ + // Pointer types may not have methods. + if (type->points_to() != NULL) + return; + + const Named_type* nt = type->named_type(); + if (nt != NULL) + { + std::pair ins = types_seen->insert(nt); + if (!ins.second) + return; + } + + if (nt != NULL) + Type::add_local_methods_for_type(nt, field_indexes, depth, + is_embedded_pointer, needs_stub_method, + methods); + + Type::add_embedded_methods_for_type(type, field_indexes, depth, + is_embedded_pointer, needs_stub_method, + types_seen, methods); + + // If we are called with depth > 0, then we are looking at an + // anonymous field of a struct. If such a field has interface type, + // then we need to add the interface methods. We don't want to add + // them when depth == 0, because we will already handle them + // following the usual rules for an interface type. + if (depth > 0) + Type::add_interface_methods_for_type(type, field_indexes, depth, methods); +} + +// Add the local methods for the named type NT to *METHODS. The +// parameters are as for add_methods_to_type. + +void +Type::add_local_methods_for_type(const Named_type* nt, + const Method::Field_indexes* field_indexes, + unsigned int depth, + bool is_embedded_pointer, + bool needs_stub_method, + Methods** methods) +{ + const Bindings* local_methods = nt->local_methods(); + if (local_methods == NULL) + return; + + if (*methods == NULL) + *methods = new Methods(); + + for (Bindings::const_declarations_iterator p = + local_methods->begin_declarations(); + p != local_methods->end_declarations(); + ++p) + { + Named_object* no = p->second; + bool is_value_method = (is_embedded_pointer + || !Type::method_expects_pointer(no)); + Method* m = new Named_method(no, field_indexes, depth, is_value_method, + (needs_stub_method || depth > 0)); + if (!(*methods)->insert(no->name(), m)) + delete m; + } +} + +// Add the embedded methods for TYPE to *METHODS. These are the +// methods attached to anonymous fields. The parameters are as for +// add_methods_to_type. + +void +Type::add_embedded_methods_for_type(const Type* type, + const Method::Field_indexes* field_indexes, + unsigned int depth, + bool is_embedded_pointer, + bool needs_stub_method, + Types_seen* types_seen, + Methods** methods) +{ + // Look for anonymous fields in TYPE. TYPE has fields if it is a + // struct. + const Struct_type* st = type->struct_type(); + if (st == NULL) + return; + + const Struct_field_list* fields = st->fields(); + if (fields == NULL) + return; + + unsigned int i = 0; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf, ++i) + { + if (!pf->is_anonymous()) + continue; + + Type* ftype = pf->type(); + bool is_pointer = false; + if (ftype->points_to() != NULL) + { + ftype = ftype->points_to(); + is_pointer = true; + } + Named_type* fnt = ftype->named_type(); + if (fnt == NULL) + { + // This is an error, but it will be diagnosed elsewhere. + continue; + } + + Method::Field_indexes* sub_field_indexes = new Method::Field_indexes(); + sub_field_indexes->next = field_indexes; + sub_field_indexes->field_index = i; + + Type::add_methods_for_type(fnt, sub_field_indexes, depth + 1, + (is_embedded_pointer || is_pointer), + (needs_stub_method + || is_pointer + || i > 0), + types_seen, + methods); + } +} + +// If TYPE is an interface type, then add its method to *METHODS. +// This is for interface methods attached to an anonymous field. The +// parameters are as for add_methods_for_type. + +void +Type::add_interface_methods_for_type(const Type* type, + const Method::Field_indexes* field_indexes, + unsigned int depth, + Methods** methods) +{ + const Interface_type* it = type->interface_type(); + if (it == NULL) + return; + + const Typed_identifier_list* imethods = it->methods(); + if (imethods == NULL) + return; + + if (*methods == NULL) + *methods = new Methods(); + + for (Typed_identifier_list::const_iterator pm = imethods->begin(); + pm != imethods->end(); + ++pm) + { + Function_type* fntype = pm->type()->function_type(); + if (fntype == NULL) + { + // This is an error, but it should be reported elsewhere + // when we look at the methods for IT. + continue; + } + go_assert(!fntype->is_method()); + fntype = fntype->copy_with_receiver(const_cast(type)); + Method* m = new Interface_method(pm->name(), pm->location(), fntype, + field_indexes, depth); + if (!(*methods)->insert(pm->name(), m)) + delete m; + } +} + +// Build stub methods for TYPE as needed. METHODS is the set of +// methods for the type. A stub method may be needed when a type +// inherits a method from an anonymous field. When we need the +// address of the method, as in a type descriptor, we need to build a +// little stub which does the required field dereferences and jumps to +// the real method. LOCATION is the location of the type definition. + +void +Type::build_stub_methods(Gogo* gogo, const Type* type, const Methods* methods, + Location location) +{ + if (methods == NULL) + return; + for (Methods::const_iterator p = methods->begin(); + p != methods->end(); + ++p) + { + Method* m = p->second; + if (m->is_ambiguous() || !m->needs_stub_method()) + continue; + + const std::string& name(p->first); + + // Build a stub method. + + const Function_type* fntype = m->type(); + + static unsigned int counter; + char buf[100]; + snprintf(buf, sizeof buf, "$this%u", counter); + ++counter; + + Type* receiver_type = const_cast(type); + if (!m->is_value_method()) + receiver_type = Type::make_pointer_type(receiver_type); + Location receiver_location = m->receiver_location(); + Typed_identifier* receiver = new Typed_identifier(buf, receiver_type, + receiver_location); + + const Typed_identifier_list* fnparams = fntype->parameters(); + Typed_identifier_list* stub_params; + if (fnparams == NULL || fnparams->empty()) + stub_params = NULL; + else + { + // We give each stub parameter a unique name. + stub_params = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator pp = fnparams->begin(); + pp != fnparams->end(); + ++pp) + { + char pbuf[100]; + snprintf(pbuf, sizeof pbuf, "$p%u", counter); + stub_params->push_back(Typed_identifier(pbuf, pp->type(), + pp->location())); + ++counter; + } + } + + const Typed_identifier_list* fnresults = fntype->results(); + Typed_identifier_list* stub_results; + if (fnresults == NULL || fnresults->empty()) + stub_results = NULL; + else + { + // We create the result parameters without any names, since + // we won't refer to them. + stub_results = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator pr = fnresults->begin(); + pr != fnresults->end(); + ++pr) + stub_results->push_back(Typed_identifier("", pr->type(), + pr->location())); + } + + Function_type* stub_type = Type::make_function_type(receiver, + stub_params, + stub_results, + fntype->location()); + if (fntype->is_varargs()) + stub_type->set_is_varargs(); + + // We only create the function in the package which creates the + // type. + const Package* package; + if (type->named_type() == NULL) + package = NULL; + else + package = type->named_type()->named_object()->package(); + Named_object* stub; + if (package != NULL) + stub = Named_object::make_function_declaration(name, package, + stub_type, location); + else + { + stub = gogo->start_function(name, stub_type, false, + fntype->location()); + Type::build_one_stub_method(gogo, m, buf, stub_params, + fntype->is_varargs(), location); + gogo->finish_function(fntype->location()); + + if (type->named_type() == NULL && stub->is_function()) + stub->func_value()->set_is_unnamed_type_stub_method(); + if (m->nointerface() && stub->is_function()) + stub->func_value()->set_nointerface(); + } + + m->set_stub_object(stub); + } +} + +// Build a stub method which adjusts the receiver as required to call +// METHOD. RECEIVER_NAME is the name we used for the receiver. +// PARAMS is the list of function parameters. + +void +Type::build_one_stub_method(Gogo* gogo, Method* method, + const char* receiver_name, + const Typed_identifier_list* params, + bool is_varargs, + Location location) +{ + Named_object* receiver_object = gogo->lookup(receiver_name, NULL); + go_assert(receiver_object != NULL); + + Expression* expr = Expression::make_var_reference(receiver_object, location); + expr = Type::apply_field_indexes(expr, method->field_indexes(), location); + if (expr->type()->points_to() == NULL) + expr = Expression::make_unary(OPERATOR_AND, expr, location); + + Expression_list* arguments; + if (params == NULL || params->empty()) + arguments = NULL; + else + { + arguments = new Expression_list(); + for (Typed_identifier_list::const_iterator p = params->begin(); + p != params->end(); + ++p) + { + Named_object* param = gogo->lookup(p->name(), NULL); + go_assert(param != NULL); + Expression* param_ref = Expression::make_var_reference(param, + location); + arguments->push_back(param_ref); + } + } + + Expression* func = method->bind_method(expr, location); + go_assert(func != NULL); + Call_expression* call = Expression::make_call(func, arguments, is_varargs, + location); + call->set_hidden_fields_are_ok(); + + Statement* s = Statement::make_return_from_call(call, location); + Return_statement* retstat = s->return_statement(); + if (retstat != NULL) + { + // We can return values with hidden fields from a stub. This is + // necessary if the method is itself hidden. + retstat->set_hidden_fields_are_ok(); + } + gogo->add_statement(s); +} + +// Apply FIELD_INDEXES to EXPR. The field indexes have to be applied +// in reverse order. + +Expression* +Type::apply_field_indexes(Expression* expr, + const Method::Field_indexes* field_indexes, + Location location) +{ + if (field_indexes == NULL) + return expr; + expr = Type::apply_field_indexes(expr, field_indexes->next, location); + Struct_type* stype = expr->type()->deref()->struct_type(); + go_assert(stype != NULL + && field_indexes->field_index < stype->field_count()); + if (expr->type()->struct_type() == NULL) + { + go_assert(expr->type()->points_to() != NULL); + expr = Expression::make_unary(OPERATOR_MULT, expr, location); + go_assert(expr->type()->struct_type() == stype); + } + return Expression::make_field_reference(expr, field_indexes->field_index, + location); +} + +// Return whether NO is a method for which the receiver is a pointer. + +bool +Type::method_expects_pointer(const Named_object* no) +{ + const Function_type *fntype; + if (no->is_function()) + fntype = no->func_value()->type(); + else if (no->is_function_declaration()) + fntype = no->func_declaration_value()->type(); + else + go_unreachable(); + return fntype->receiver()->type()->points_to() != NULL; +} + +// Given a set of methods for a type, METHODS, return the method NAME, +// or NULL if there isn't one or if it is ambiguous. If IS_AMBIGUOUS +// is not NULL, then set *IS_AMBIGUOUS to true if the method exists +// but is ambiguous (and return NULL). + +Method* +Type::method_function(const Methods* methods, const std::string& name, + bool* is_ambiguous) +{ + if (is_ambiguous != NULL) + *is_ambiguous = false; + if (methods == NULL) + return NULL; + Methods::const_iterator p = methods->find(name); + if (p == methods->end()) + return NULL; + Method* m = p->second; + if (m->is_ambiguous()) + { + if (is_ambiguous != NULL) + *is_ambiguous = true; + return NULL; + } + 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 val(interface, NULL_TREE); + std::pair 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. + +Expression* +Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, + const std::string& name, + Location location) +{ + if (type->deref()->is_error_type()) + return Expression::make_error(location); + + const Named_type* nt = type->deref()->named_type(); + const Struct_type* st = type->deref()->struct_type(); + const Interface_type* it = type->interface_type(); + + // If this is a pointer to a pointer, then it is possible that the + // pointed-to type has methods. + bool dereferenced = false; + if (nt == NULL + && st == NULL + && it == NULL + && type->points_to() != NULL + && type->points_to()->points_to() != NULL) + { + expr = Expression::make_unary(OPERATOR_MULT, expr, location); + type = type->points_to(); + if (type->deref()->is_error_type()) + return Expression::make_error(location); + nt = type->points_to()->named_type(); + st = type->points_to()->struct_type(); + dereferenced = true; + } + + bool receiver_can_be_pointer = (expr->type()->points_to() != NULL + || expr->is_addressable()); + std::vector seen; + bool is_method = false; + bool found_pointer_method = false; + std::string ambig1; + std::string ambig2; + if (Type::find_field_or_method(type, name, receiver_can_be_pointer, + &seen, NULL, &is_method, + &found_pointer_method, &ambig1, &ambig2)) + { + Expression* ret; + if (!is_method) + { + go_assert(st != NULL); + if (type->struct_type() == NULL) + { + go_assert(type->points_to() != NULL); + expr = Expression::make_unary(OPERATOR_MULT, expr, + location); + go_assert(expr->type()->struct_type() == st); + } + ret = st->field_reference(expr, name, location); + } + else if (it != NULL && it->find_method(name) != NULL) + ret = Expression::make_interface_field_reference(expr, name, + location); + else + { + Method* m; + if (nt != NULL) + m = nt->method_function(name, NULL); + else if (st != NULL) + m = st->method_function(name, NULL); + else + go_unreachable(); + go_assert(m != NULL); + if (dereferenced && m->is_value_method()) + { + error_at(location, + "calling value method requires explicit dereference"); + return Expression::make_error(location); + } + if (!m->is_value_method() && expr->type()->points_to() == NULL) + expr = Expression::make_unary(OPERATOR_AND, expr, location); + ret = m->bind_method(expr, location); + } + go_assert(ret != NULL); + return ret; + } + else + { + if (Gogo::is_erroneous_name(name)) + { + // An error was already reported. + } + else if (!ambig1.empty()) + error_at(location, "%qs is ambiguous via %qs and %qs", + Gogo::message_name(name).c_str(), ambig1.c_str(), + ambig2.c_str()); + else if (found_pointer_method) + 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 " + "has no fields or methods"), + Gogo::message_name(name).c_str()); + else + { + bool is_unexported; + // The test for 'a' and 'z' is to handle builtin names, + // which are not hidden. + if (!Gogo::is_hidden_name(name) && (name[0] < 'a' || name[0] > 'z')) + is_unexported = false; + else + { + std::string unpacked = Gogo::unpack_hidden_name(name); + seen.clear(); + is_unexported = Type::is_unexported_field_or_method(gogo, type, + unpacked, + &seen); + } + if (is_unexported) + error_at(location, "reference to unexported field or method %qs", + Gogo::message_name(name).c_str()); + else + error_at(location, "reference to undefined field or method %qs", + Gogo::message_name(name).c_str()); + } + return Expression::make_error(location); + } +} + +// Look in TYPE for a field or method named NAME, return true if one +// is found. This looks through embedded anonymous fields and handles +// ambiguity. If a method is found, sets *IS_METHOD to true; +// otherwise, if a field is found, set it to false. If +// RECEIVER_CAN_BE_POINTER is false, then the receiver is a value +// whose address can not be taken. SEEN is used to avoid infinite +// recursion on invalid types. + +// When returning false, this sets *FOUND_POINTER_METHOD if we found a +// method we couldn't use because it requires a pointer. LEVEL is +// used for recursive calls, and can be NULL for a non-recursive call. +// When this function returns false because it finds that the name is +// ambiguous, it will store a path to the ambiguous names in *AMBIG1 +// and *AMBIG2. If the name is not found at all, *AMBIG1 and *AMBIG2 +// will be unchanged. + +// This function just returns whether or not there is a field or +// method, and whether it is a field or method. It doesn't build an +// expression to refer to it. If it is a method, we then look in the +// list of all methods for the type. If it is a field, the search has +// to be done again, looking only for fields, and building up the +// expression as we go. + +bool +Type::find_field_or_method(const Type* type, + const std::string& name, + bool receiver_can_be_pointer, + std::vector* seen, + int* level, + bool* is_method, + bool* found_pointer_method, + std::string* ambig1, + std::string* ambig2) +{ + // Named types can have locally defined methods. + const Named_type* nt = type->named_type(); + if (nt == NULL && type->points_to() != NULL) + nt = type->points_to()->named_type(); + if (nt != NULL) + { + Named_object* no = nt->find_local_method(name); + if (no != NULL) + { + if (receiver_can_be_pointer || !Type::method_expects_pointer(no)) + { + *is_method = true; + return true; + } + + // Record that we have found a pointer method in order to + // give a better error message if we don't find anything + // else. + *found_pointer_method = true; + } + + for (std::vector::const_iterator p = seen->begin(); + p != seen->end(); + ++p) + { + if (*p == nt) + { + // We've already seen this type when searching for methods. + return false; + } + } + } + + // Interface types can have methods. + const Interface_type* it = type->interface_type(); + if (it != NULL && it->find_method(name) != NULL) + { + *is_method = true; + return true; + } + + // Struct types can have fields. They can also inherit fields and + // methods from anonymous fields. + const Struct_type* st = type->deref()->struct_type(); + if (st == NULL) + return false; + const Struct_field_list* fields = st->fields(); + if (fields == NULL) + return false; + + if (nt != NULL) + seen->push_back(nt); + + int found_level = 0; + bool found_is_method = false; + std::string found_ambig1; + std::string found_ambig2; + const Struct_field* found_parent = NULL; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (pf->is_field_name(name)) + { + *is_method = false; + if (nt != NULL) + seen->pop_back(); + return true; + } + + if (!pf->is_anonymous()) + continue; + + if (pf->type()->deref()->is_error_type() + || pf->type()->deref()->is_undefined()) + continue; + + Named_type* fnt = pf->type()->named_type(); + if (fnt == NULL) + fnt = pf->type()->deref()->named_type(); + go_assert(fnt != NULL); + + // Methods with pointer receivers on embedded field are + // inherited by the pointer to struct, and also by the struct + // type if the field itself is a pointer. + bool can_be_pointer = (receiver_can_be_pointer + || pf->type()->points_to() != NULL); + int sublevel = level == NULL ? 1 : *level + 1; + bool sub_is_method; + std::string subambig1; + std::string subambig2; + bool subfound = Type::find_field_or_method(fnt, + name, + can_be_pointer, + seen, + &sublevel, + &sub_is_method, + found_pointer_method, + &subambig1, + &subambig2); + if (!subfound) + { + if (!subambig1.empty()) + { + // The name was found via this field, but is ambiguous. + // if the ambiguity is lower or at the same level as + // anything else we have already found, then we want to + // pass the ambiguity back to the caller. + if (found_level == 0 || sublevel <= found_level) + { + found_ambig1 = (Gogo::message_name(pf->field_name()) + + '.' + subambig1); + found_ambig2 = (Gogo::message_name(pf->field_name()) + + '.' + subambig2); + found_level = sublevel; + } + } + } + else + { + // The name was found via this field. Use the level to see + // if we want to use this one, or whether it introduces an + // ambiguity. + if (found_level == 0 || sublevel < found_level) + { + found_level = sublevel; + found_is_method = sub_is_method; + found_ambig1.clear(); + found_ambig2.clear(); + found_parent = &*pf; + } + else if (sublevel > found_level) + ; + else if (found_ambig1.empty()) + { + // We found an ambiguity. + go_assert(found_parent != NULL); + found_ambig1 = Gogo::message_name(found_parent->field_name()); + found_ambig2 = Gogo::message_name(pf->field_name()); + } + else + { + // We found an ambiguity, but we already know of one. + // Just report the earlier one. + } + } + } + + // Here if we didn't find anything FOUND_LEVEL is 0. If we found + // something ambiguous, FOUND_LEVEL is not 0 and FOUND_AMBIG1 and + // FOUND_AMBIG2 are not empty. If we found the field, FOUND_LEVEL + // is not 0 and FOUND_AMBIG1 and FOUND_AMBIG2 are empty. + + if (nt != NULL) + seen->pop_back(); + + if (found_level == 0) + return false; + else if (!found_ambig1.empty()) + { + go_assert(!found_ambig1.empty()); + ambig1->assign(found_ambig1); + ambig2->assign(found_ambig2); + if (level != NULL) + *level = found_level; + return false; + } + else + { + if (level != NULL) + *level = found_level; + *is_method = found_is_method; + return true; + } +} + +// Return whether NAME is an unexported field or method for TYPE. + +bool +Type::is_unexported_field_or_method(Gogo* gogo, const Type* type, + const std::string& name, + std::vector* seen) +{ + const Named_type* nt = type->named_type(); + if (nt == NULL) + nt = type->deref()->named_type(); + if (nt != NULL) + { + if (nt->is_unexported_local_method(gogo, name)) + return true; + + for (std::vector::const_iterator p = seen->begin(); + p != seen->end(); + ++p) + { + if (*p == nt) + { + // We've already seen this type. + return false; + } + } + } + + const Interface_type* it = type->interface_type(); + if (it != NULL && it->is_unexported_method(gogo, name)) + return true; + + type = type->deref(); + + const Struct_type* st = type->struct_type(); + if (st != NULL && st->is_unexported_local_field(gogo, name)) + return true; + + if (st == NULL) + return false; + + const Struct_field_list* fields = st->fields(); + if (fields == NULL) + return false; + + if (nt != NULL) + seen->push_back(nt); + + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (pf->is_anonymous() + && !pf->type()->deref()->is_error_type() + && !pf->type()->deref()->is_undefined()) + { + Named_type* subtype = pf->type()->named_type(); + if (subtype == NULL) + subtype = pf->type()->deref()->named_type(); + if (subtype == NULL) + { + // This is an error, but it will be diagnosed elsewhere. + continue; + } + if (Type::is_unexported_field_or_method(gogo, subtype, name, seen)) + { + if (nt != NULL) + seen->pop_back(); + return true; + } + } + } + + if (nt != NULL) + seen->pop_back(); + + return false; +} + +// Class Forward_declaration. + +Forward_declaration_type::Forward_declaration_type(Named_object* named_object) + : Type(TYPE_FORWARD), + named_object_(named_object->resolve()), warned_(false) +{ + go_assert(this->named_object_->is_unknown() + || this->named_object_->is_type_declaration()); +} + +// Return the named object. + +Named_object* +Forward_declaration_type::named_object() +{ + return this->named_object_->resolve(); +} + +const Named_object* +Forward_declaration_type::named_object() const +{ + return this->named_object_->resolve(); +} + +// Return the name of the forward declared type. + +const std::string& +Forward_declaration_type::name() const +{ + return this->named_object()->name(); +} + +// Warn about a use of a type which has been declared but not defined. + +void +Forward_declaration_type::warn() const +{ + Named_object* no = this->named_object_->resolve(); + if (no->is_unknown()) + { + // The name was not defined anywhere. + if (!this->warned_) + { + error_at(this->named_object_->location(), + "use of undefined type %qs", + no->message_name().c_str()); + this->warned_ = true; + } + } + else if (no->is_type_declaration()) + { + // The name was seen as a type, but the type was never defined. + if (no->type_declaration_value()->using_type()) + { + error_at(this->named_object_->location(), + "use of undefined type %qs", + no->message_name().c_str()); + this->warned_ = true; + } + } + else + { + // The name was defined, but not as a type. + if (!this->warned_) + { + error_at(this->named_object_->location(), "expected type"); + this->warned_ = true; + } + } +} + +// Get the base type of a declaration. This gives an error if the +// type has not yet been defined. + +Type* +Forward_declaration_type::real_type() +{ + if (this->is_defined()) + return this->named_object()->type_value(); + else + { + this->warn(); + return Type::make_error_type(); + } +} + +const Type* +Forward_declaration_type::real_type() const +{ + if (this->is_defined()) + return this->named_object()->type_value(); + else + { + this->warn(); + return Type::make_error_type(); + } +} + +// Return whether the base type is defined. + +bool +Forward_declaration_type::is_defined() const +{ + return this->named_object()->is_type(); +} + +// Add a method. This is used when methods are defined before the +// type. + +Named_object* +Forward_declaration_type::add_method(const std::string& name, + Function* function) +{ + Named_object* no = this->named_object(); + if (no->is_unknown()) + no->declare_as_type(); + return no->type_declaration_value()->add_method(name, function); +} + +// Add a method declaration. This is used when methods are declared +// before the type. + +Named_object* +Forward_declaration_type::add_method_declaration(const std::string& name, + Package* package, + Function_type* type, + Location location) +{ + Named_object* no = this->named_object(); + if (no->is_unknown()) + no->declare_as_type(); + Type_declaration* td = no->type_declaration_value(); + return td->add_method_declaration(name, package, type, location); +} + +// Traversal. + +int +Forward_declaration_type::do_traverse(Traverse* traverse) +{ + if (this->is_defined() + && Type::traverse(this->real_type(), traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Verify the type. + +bool +Forward_declaration_type::do_verify() +{ + if (!this->is_defined() && !this->is_nil_constant_as_type()) + { + this->warn(); + return false; + } + return true; +} + +// Get the backend representation for the type. + +Btype* +Forward_declaration_type::do_get_backend(Gogo* gogo) +{ + if (this->is_defined()) + return Type::get_named_base_btype(gogo, this->real_type()); + + if (this->warned_) + return gogo->backend()->error_type(); + + // We represent an undefined type as a struct with no fields. That + // should work fine for the backend, since the same case can arise + // in C. + std::vector fields; + Btype* bt = gogo->backend()->struct_type(fields); + return gogo->backend()->named_type(this->name(), bt, + this->named_object()->location()); +} + +// Build a type descriptor for a forwarded type. + +Expression* +Forward_declaration_type::do_type_descriptor(Gogo* gogo, Named_type* name) +{ + Location ploc = Linemap::predeclared_location(); + if (!this->is_defined()) + return Expression::make_error(ploc); + else + { + Type* t = this->real_type(); + if (name != NULL) + return this->named_type_descriptor(gogo, t, name); + else + return Expression::make_type_descriptor(t, ploc); + } +} + +// The reflection string. + +void +Forward_declaration_type::do_reflection(Gogo* gogo, std::string* ret) const +{ + this->append_reflection(this->real_type(), gogo, ret); +} + +// The mangled name. + +void +Forward_declaration_type::do_mangled_name(Gogo* gogo, std::string* ret) const +{ + if (this->is_defined()) + this->append_mangled_name(this->real_type(), gogo, ret); + else + { + const Named_object* no = this->named_object(); + std::string name; + if (no->package() == NULL) + name = gogo->pkgpath_symbol(); + else + name = no->package()->pkgpath_symbol(); + name += '.'; + name += Gogo::unpack_hidden_name(no->name()); + char buf[20]; + snprintf(buf, sizeof buf, "N%u_", + static_cast(name.length())); + ret->append(buf); + ret->append(name); + } +} + +// Export a forward declaration. This can happen when a defined type +// refers to a type which is only declared (and is presumably defined +// in some other file in the same package). + +void +Forward_declaration_type::do_export(Export*) const +{ + // If there is a base type, that should be exported instead of this. + go_assert(!this->is_defined()); + + // We don't output anything. +} + +// Make a forward declaration. + +Type* +Type::make_forward_declaration(Named_object* named_object) +{ + return new Forward_declaration_type(named_object); +} + +// Class Typed_identifier_list. + +// Sort the entries by name. + +struct Typed_identifier_list_sort +{ + public: + bool + operator()(const Typed_identifier& t1, const Typed_identifier& t2) const + { return t1.name() < t2.name(); } +}; + +void +Typed_identifier_list::sort_by_name() +{ + std::sort(this->entries_.begin(), this->entries_.end(), + Typed_identifier_list_sort()); +} + +// Traverse types. + +int +Typed_identifier_list::traverse(Traverse* traverse) +{ + for (Typed_identifier_list::const_iterator p = this->begin(); + p != this->end(); + ++p) + { + if (Type::traverse(p->type(), traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Copy the list. + +Typed_identifier_list* +Typed_identifier_list::copy() const +{ + Typed_identifier_list* ret = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator p = this->begin(); + p != this->end(); + ++p) + ret->push_back(Typed_identifier(p->name(), p->type(), p->location())); + return ret; +} diff --git a/gcc-4.9/gcc/go/gofrontend/types.h b/gcc-4.9/gcc/go/gofrontend/types.h new file mode 100644 index 000000000..5fda4e728 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/types.h @@ -0,0 +1,3189 @@ +// types.h -- Go frontend types. -*- C++ -*- + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef GO_TYPES_H +#define GO_TYPES_H + +#include "go-linemap.h" + +class Gogo; +class Package; +class Traverse; +class Typed_identifier; +class Typed_identifier_list; +class Integer_type; +class Float_type; +class Complex_type; +class String_type; +class Function_type; +class Backend_function_type; +class Struct_field; +class Struct_field_list; +class Struct_type; +class Pointer_type; +class Array_type; +class Map_type; +class Channel_type; +class Interface_type; +class Named_type; +class Forward_declaration_type; +class Method; +class Methods; +class Type_hash_identical; +class Type_identical; +class Expression; +class Expression_list; +class Call_expression; +class Field_reference_expression; +class Bound_method_expression; +class Bindings; +class Named_object; +class Function; +class Translate_context; +class Export; +class Import; +class Btype; +class Bexpression; +class Bvariable; + +// Type codes used in type descriptors. These must match the values +// in libgo/runtime/go-type.h. They also match the values in the gc +// compiler in src/cmd/gc/reflect.c and src/pkg/runtime/type.go, +// although this is not required. + +static const int RUNTIME_TYPE_KIND_BOOL = 1; +static const int RUNTIME_TYPE_KIND_INT = 2; +static const int RUNTIME_TYPE_KIND_INT8 = 3; +static const int RUNTIME_TYPE_KIND_INT16 = 4; +static const int RUNTIME_TYPE_KIND_INT32 = 5; +static const int RUNTIME_TYPE_KIND_INT64 = 6; +static const int RUNTIME_TYPE_KIND_UINT = 7; +static const int RUNTIME_TYPE_KIND_UINT8 = 8; +static const int RUNTIME_TYPE_KIND_UINT16 = 9; +static const int RUNTIME_TYPE_KIND_UINT32 = 10; +static const int RUNTIME_TYPE_KIND_UINT64 = 11; +static const int RUNTIME_TYPE_KIND_UINTPTR = 12; +static const int RUNTIME_TYPE_KIND_FLOAT32 = 13; +static const int RUNTIME_TYPE_KIND_FLOAT64 = 14; +static const int RUNTIME_TYPE_KIND_COMPLEX64 = 15; +static const int RUNTIME_TYPE_KIND_COMPLEX128 = 16; +static const int RUNTIME_TYPE_KIND_ARRAY = 17; +static const int RUNTIME_TYPE_KIND_CHAN = 18; +static const int RUNTIME_TYPE_KIND_FUNC = 19; +static const int RUNTIME_TYPE_KIND_INTERFACE = 20; +static const int RUNTIME_TYPE_KIND_MAP = 21; +static const int RUNTIME_TYPE_KIND_PTR = 22; +static const int RUNTIME_TYPE_KIND_SLICE = 23; +static const int RUNTIME_TYPE_KIND_STRING = 24; +static const int RUNTIME_TYPE_KIND_STRUCT = 25; +static const int RUNTIME_TYPE_KIND_UNSAFE_POINTER = 26; + +static const int RUNTIME_TYPE_KIND_NO_POINTERS = (1 << 7); + +// To build the complete list of methods for a named type we need to +// gather all methods from anonymous fields. Those methods may +// require an arbitrary set of indirections and field offsets. There +// is also the possibility of ambiguous methods, which we could ignore +// except that we want to give a better error message for that case. +// This is a base class. There are two types of methods: named +// methods, and methods which are inherited from an anonymous field of +// interface type. + +class Method +{ + public: + // For methods in anonymous types we need to know the sequence of + // field references used to extract the pointer to pass to the + // method. Since each method for a particular anonymous field will + // have the sequence of field indexes, and since the indexes can be + // shared going down the chain, we use a manually managed linked + // list. The first entry in the list is the field index for the + // last field, the one passed to the method. + + struct Field_indexes + { + const Field_indexes* next; + unsigned int field_index; + }; + + virtual ~Method() + { } + + // Get the list of field indexes. + const Field_indexes* + field_indexes() const + { return this->field_indexes_; } + + // Get the depth. + unsigned int + depth() const + { return this->depth_; } + + // Return whether this is a value method--a method which does not + // require a pointer expression. + bool + is_value_method() const + { return this->is_value_method_; } + + // Return whether we need a stub method--this is true if we can't + // just pass the main object to the method. + bool + needs_stub_method() const + { return this->needs_stub_method_; } + + // Return whether this is an ambiguous method name. + bool + is_ambiguous() const + { return this->is_ambiguous_; } + + // Note that this method is ambiguous. + void + set_is_ambiguous() + { this->is_ambiguous_ = true; } + + // Return the type of the method. + Function_type* + type() const + { return this->do_type(); } + + // Return the location of the method receiver. + Location + receiver_location() const + { return this->do_receiver_location(); } + + // Return an expression which binds this method to EXPR. This is + // something which can be used with a function call. + Expression* + bind_method(Expression* expr, Location location) const; + + // Return the named object for this method. This may only be called + // after methods are finalized. + Named_object* + named_object() const; + + // Get the stub object. + Named_object* + stub_object() const + { + go_assert(this->stub_ != NULL); + return this->stub_; + } + + // Set the stub object. + void + set_stub_object(Named_object* no) + { + go_assert(this->stub_ == NULL); + this->stub_ = no; + } + + // Return true if this method should not participate in any + // interfaces. + bool + nointerface() const + { return this->do_nointerface(); } + + protected: + // These objects are only built by the child classes. + Method(const Field_indexes* field_indexes, unsigned int depth, + bool is_value_method, bool needs_stub_method) + : field_indexes_(field_indexes), depth_(depth), stub_(NULL), + is_value_method_(is_value_method), needs_stub_method_(needs_stub_method), + is_ambiguous_(false) + { } + + // The named object for this method. + virtual Named_object* + do_named_object() const = 0; + + // The type of the method. + virtual Function_type* + do_type() const = 0; + + // Return the location of the method receiver. + virtual Location + do_receiver_location() const = 0; + + // Bind a method to an object. + virtual Expression* + do_bind_method(Expression* expr, Location location) const = 0; + + // Return whether this method should not participate in interfaces. + virtual bool + do_nointerface() const = 0; + + private: + // The sequence of field indexes used for this method. If this is + // NULL, then the method is defined for the current type. + const Field_indexes* field_indexes_; + // The depth at which this method was found. + unsigned int depth_; + // If a stub method is required, this is its object. This is only + // set after stub methods are built in finalize_methods. + Named_object* stub_; + // Whether this is a value method--a method that does not require a + // pointer. + bool is_value_method_; + // Whether a stub method is required. + bool needs_stub_method_; + // Whether this method is ambiguous. + bool is_ambiguous_; +}; + +// A named method. This is what you get with a method declaration, +// either directly on the type, or inherited from some anonymous +// embedded field. + +class Named_method : public Method +{ + public: + Named_method(Named_object* named_object, const Field_indexes* field_indexes, + unsigned int depth, bool is_value_method, + bool needs_stub_method) + : Method(field_indexes, depth, is_value_method, needs_stub_method), + named_object_(named_object) + { } + + protected: + // Get the Named_object for the method. + Named_object* + do_named_object() const + { return this->named_object_; } + + // The type of the method. + Function_type* + do_type() const; + + // Return the location of the method receiver. + Location + do_receiver_location() const; + + // Bind a method to an object. + Expression* + do_bind_method(Expression* expr, Location location) const; + + // Return whether this method should not participate in interfaces. + bool + do_nointerface() const; + + private: + // The method itself. For a method which needs a stub, this starts + // out as the underlying method, and is later replaced with the stub + // method. + Named_object* named_object_; +}; + +// An interface method. This is used when an interface appears as an +// anonymous field in a named struct. + +class Interface_method : public Method +{ + public: + Interface_method(const std::string& name, Location location, + Function_type* fntype, const Field_indexes* field_indexes, + unsigned int depth) + : Method(field_indexes, depth, true, true), + name_(name), location_(location), fntype_(fntype) + { } + + protected: + // Get the Named_object for the method. This should never be + // called, as we always create a stub. + Named_object* + do_named_object() const + { go_unreachable(); } + + // The type of the method. + Function_type* + do_type() const + { return this->fntype_; } + + // Return the location of the method receiver. + Location + do_receiver_location() const + { return this->location_; } + + // Bind a method to an object. + Expression* + do_bind_method(Expression* expr, Location location) const; + + // Return whether this method should not participate in interfaces. + bool + do_nointerface() const + { return false; } + + private: + // The name of the interface method to call. + std::string name_; + // The location of the definition of the interface method. + Location location_; + // The type of the interface method. + Function_type* fntype_; +}; + +// A mapping from method name to Method. This is a wrapper around a +// hash table. + +class Methods +{ + private: + typedef Unordered_map(std::string, Method*) Method_map; + + public: + typedef Method_map::const_iterator const_iterator; + + Methods() + : methods_() + { } + + // Insert a new method. Returns true if it was inserted, false if + // it was overidden or ambiguous. + bool + insert(const std::string& name, Method* m); + + // The number of (unambiguous) methods. + size_t + count() const; + + // Iterate. + const_iterator + begin() const + { return this->methods_.begin(); } + + const_iterator + end() const + { return this->methods_.end(); } + + // Lookup. + const_iterator + find(const std::string& name) const + { return this->methods_.find(name); } + + private: + Method_map methods_; +}; + +// The base class for all types. + +class Type +{ + public: + // The types of types. + enum Type_classification + { + TYPE_ERROR, + TYPE_VOID, + TYPE_BOOLEAN, + TYPE_INTEGER, + TYPE_FLOAT, + TYPE_COMPLEX, + TYPE_STRING, + TYPE_SINK, + TYPE_FUNCTION, + TYPE_POINTER, + TYPE_NIL, + TYPE_CALL_MULTIPLE_RESULT, + TYPE_STRUCT, + TYPE_ARRAY, + TYPE_MAP, + TYPE_CHANNEL, + TYPE_INTERFACE, + TYPE_NAMED, + TYPE_FORWARD + }; + + virtual ~Type(); + + // Creators. + + static Type* + make_error_type(); + + static Type* + make_void_type(); + + // Get the unnamed bool type. + static Type* + make_boolean_type(); + + // Get the named type "bool". + static Named_type* + lookup_bool_type(); + + // Make the named type "bool". + static Named_type* + make_named_bool_type(); + + // Make an abstract integer type. + static Integer_type* + make_abstract_integer_type(); + + // Make an abstract type for a character constant. + static Integer_type* + make_abstract_character_type(); + + // Make a named integer type with a specified size. + // RUNTIME_TYPE_KIND is the code to use in reflection information, + // to distinguish int and int32. + static Named_type* + make_integer_type(const char* name, bool is_unsigned, int bits, + int runtime_type_kind); + + // Look up a named integer type. + static Named_type* + lookup_integer_type(const char* name); + + // Make an abstract floating point type. + static Float_type* + make_abstract_float_type(); + + // Make a named floating point type with a specific size. + // RUNTIME_TYPE_KIND is the code to use in reflection information, + // to distinguish float and float32. + static Named_type* + make_float_type(const char* name, int bits, int runtime_type_kind); + + // Look up a named float type. + static Named_type* + lookup_float_type(const char* name); + + // Make an abstract complex type. + static Complex_type* + make_abstract_complex_type(); + + // Make a named complex type with a specific size. + // RUNTIME_TYPE_KIND is the code to use in reflection information, + // to distinguish complex and complex64. + static Named_type* + make_complex_type(const char* name, int bits, int runtime_type_kind); + + // Look up a named complex type. + static Named_type* + lookup_complex_type(const char* name); + + // Get the unnamed string type. + static Type* + make_string_type(); + + // Get the named type "string". + static Named_type* + lookup_string_type(); + + // Make the named type "string". + static Named_type* + make_named_string_type(); + + static Type* + make_sink_type(); + + static Function_type* + make_function_type(Typed_identifier* receiver, + Typed_identifier_list* parameters, + Typed_identifier_list* results, + Location); + + static Backend_function_type* + make_backend_function_type(Typed_identifier* receiver, + Typed_identifier_list* parameters, + Typed_identifier_list* results, + Location); + + static Pointer_type* + make_pointer_type(Type*); + + static Type* + make_nil_type(); + + static Type* + make_call_multiple_result_type(Call_expression*); + + static Struct_type* + make_struct_type(Struct_field_list* fields, Location); + + static Array_type* + make_array_type(Type* element_type, Expression* length); + + static Map_type* + make_map_type(Type* key_type, Type* value_type, Location); + + static Channel_type* + make_channel_type(bool send, bool receive, Type*); + + static Interface_type* + make_interface_type(Typed_identifier_list* methods, Location); + + static Interface_type* + make_empty_interface_type(Location); + + static Type* + make_type_descriptor_type(); + + static Type* + make_type_descriptor_ptr_type(); + + static Named_type* + make_named_type(Named_object*, Type*, Location); + + static Type* + make_forward_declaration(Named_object*); + + // Make a builtin struct type from a list of fields. + static Struct_type* + make_builtin_struct_type(int nfields, ...); + + // Make a builtin named type. + static Named_type* + make_builtin_named_type(const char* name, Type* type); + + // Traverse a type. + static int + traverse(Type*, Traverse*); + + // Verify the type. This is called after parsing, and verifies that + // types are complete and meet the language requirements. This + // returns false if the type is invalid and we should not continue + // traversing it. + bool + verify() + { return this->do_verify(); } + + // 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, 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 + // is an equivalence relation. + static bool + are_compatible_for_binop(const Type* t1, const Type* t2); + + // Return true if two types are compatible for use with the + // comparison operator. IS_EQUALITY_OP is true if this is an + // equality comparison, false if it is an ordered comparison. This + // is an equivalence relation. If this returns false, and REASON is + // not NULL, it sets *REASON. + static bool + are_compatible_for_comparison(bool is_equality_op, const Type *t1, + const Type *t2, std::string* reason); + + // Return true if a type is comparable with itself. This is true of + // most types, but false for, e.g., function types. + bool + is_comparable() const + { return Type::are_compatible_for_comparison(true, this, this, NULL); } + + // Return true if a value with type RHS is assignable to a variable + // with type LHS. This is not an equivalence relation. If this + // returns false, and REASON is not NULL, it sets *REASON. + static bool + are_assignable(const Type* lhs, const Type* rhs, std::string* reason); + + // Return true if a value with type RHS is assignable to a variable + // with type LHS, ignoring any assignment of hidden fields + // (unexported fields of a type imported from another package). + // This is like the are_assignable method. + static bool + are_assignable_hidden_ok(const Type* lhs, const Type* rhs, + std::string* reason); + + // Return true if a value with type RHS may be converted to type + // LHS. If this returns false, and REASON is not NULL, it sets + // *REASON. + static bool + are_convertible(const Type* lhs, const Type* rhs, std::string* reason); + + // Whether this type has any hidden fields which are not visible in + // the current compilation, such as a field whose name begins with a + // lower case letter in a struct imported from a different package. + // WITHIN is not NULL if we are looking at fields in a named type. + bool + has_hidden_fields(const Named_type* within, std::string* reason) const; + + // Return true if values of this type can be compared using an + // identity function which gets nothing but a pointer to the value + // and a size. + bool + compare_is_identity(Gogo* gogo) + { return this->do_compare_is_identity(gogo); } + + // Return a hash code for this type for the method hash table. + // Types which are equivalent according to are_identical will have + // the same hash code. + unsigned int + hash_for_method(Gogo*) const; + + // Return the type classification. + Type_classification + classification() const + { return this->classification_; } + + // Return the base type for this type. This looks through forward + // declarations and names. Using this with a forward declaration + // which has not been defined will return an error type. + Type* + base(); + + const Type* + base() const; + + // Return the type skipping defined forward declarations. If this + // type is a forward declaration which has not been defined, it will + // return the Forward_declaration_type. This differs from base() in + // that it will return a Named_type, and for a + // Forward_declaration_type which is not defined it will return that + // type rather than an error type. + Type* + forwarded(); + + const Type* + forwarded() const; + + // Return true if this is a basic type: a type which is not composed + // of other types, and is not void. + bool + is_basic_type() const; + + // Return true if this is an abstract type--an integer, floating + // point, or complex type whose size has not been determined. + bool + is_abstract() const; + + // Return a non-abstract version of an abstract type. + Type* + make_non_abstract_type(); + + // Return true if this type is or contains a pointer. This + // determines whether the garbage collector needs to look at a value + // of this type. + bool + has_pointer() const + { return this->do_has_pointer(); } + + // Return true if this is the error type. This returns false for a + // type which is not defined, as it is called by the parser before + // all types are defined. + bool + is_error_type() const; + + // Return true if this is the error type or if the type is + // undefined. If the type is undefined, this will give an error. + // This should only be called after parsing is complete. + bool + is_error() const + { return this->base()->is_error_type(); } + + // Return true if this is a void type. + bool + is_void_type() const + { return this->classification_ == TYPE_VOID; } + + // If this is an integer type, return the Integer_type. Otherwise, + // return NULL. This is a controlled dynamic_cast. + Integer_type* + integer_type() + { return this->convert(); } + + const Integer_type* + integer_type() const + { return this->convert(); } + + // If this is a floating point type, return the Float_type. + // Otherwise, return NULL. This is a controlled dynamic_cast. + Float_type* + float_type() + { return this->convert(); } + + const Float_type* + float_type() const + { return this->convert(); } + + // If this is a complex type, return the Complex_type. Otherwise, + // return NULL. + Complex_type* + complex_type() + { return this->convert(); } + + const Complex_type* + complex_type() const + { return this->convert(); } + + // Return whether this is a numeric type. + bool + is_numeric_type() const + { + Type_classification tc = this->base()->classification_; + return tc == TYPE_INTEGER || tc == TYPE_FLOAT || tc == TYPE_COMPLEX; + } + + // Return true if this is a boolean type. + bool + is_boolean_type() const + { return this->base()->classification_ == TYPE_BOOLEAN; } + + // Return true if this is an abstract boolean type. + bool + is_abstract_boolean_type() const + { return this->classification_ == TYPE_BOOLEAN; } + + // Return true if this is a string type. + bool + is_string_type() const + { return this->base()->classification_ == TYPE_STRING; } + + // Return true if this is an abstract string type. + bool + is_abstract_string_type() const + { return this->classification_ == TYPE_STRING; } + + // Return true if this is the sink type. This is the type of the + // blank identifier _. + bool + is_sink_type() const + { return this->base()->classification_ == TYPE_SINK; } + + // If this is a function type, return it. Otherwise, return NULL. + Function_type* + function_type() + { return this->convert(); } + + const Function_type* + function_type() const + { return this->convert(); } + + // If this is a pointer type, return the type to which it points. + // Otherwise, return NULL. + Type* + points_to() const; + + // If this is a pointer type, return the type to which it points. + // Otherwise, return the type itself. + Type* + deref() + { + Type* pt = this->points_to(); + return pt != NULL ? pt : this; + } + + const Type* + deref() const + { + const Type* pt = this->points_to(); + return pt != NULL ? pt : this; + } + + // Return true if this is the nil type. We don't use base() here, + // because this can be called during parse, and there is no way to + // name the nil type anyhow. + bool + is_nil_type() const + { return this->classification_ == TYPE_NIL; } + + // Return true if this is the predeclared constant nil being used as + // a type. This is what the parser produces for type switches which + // use "case nil". + bool + is_nil_constant_as_type() const; + + // Return true if this is the return type of a function which + // returns multiple values. + bool + is_call_multiple_result_type() const + { return this->base()->classification_ == TYPE_CALL_MULTIPLE_RESULT; } + + // If this is a struct type, return it. Otherwise, return NULL. + Struct_type* + struct_type() + { return this->convert(); } + + const Struct_type* + struct_type() const + { return this->convert(); } + + // If this is an array type, return it. Otherwise, return NULL. + Array_type* + array_type() + { return this->convert(); } + + const Array_type* + array_type() const + { return this->convert(); } + + // Return whether if this is a slice type. + bool + is_slice_type() const; + + // If this is a map type, return it. Otherwise, return NULL. + Map_type* + map_type() + { return this->convert(); } + + const Map_type* + map_type() const + { return this->convert(); } + + // If this is a channel type, return it. Otherwise, return NULL. + Channel_type* + channel_type() + { return this->convert(); } + + const Channel_type* + channel_type() const + { return this->convert(); } + + // If this is an interface type, return it. Otherwise, return NULL. + Interface_type* + interface_type() + { return this->convert(); } + + const Interface_type* + interface_type() const + { return this->convert(); } + + // If this is a named type, return it. Otherwise, return NULL. + Named_type* + named_type(); + + const Named_type* + named_type() const; + + // If this is a forward declaration, return it. Otherwise, return + // NULL. + Forward_declaration_type* + forward_declaration_type() + { return this->convert_no_base(); } + + const Forward_declaration_type* + forward_declaration_type() const + { + return this->convert_no_base(); + } + + // Return true if this type is not yet defined. + bool + is_undefined() const; + + // Return true if this is the unsafe.pointer type. We currently + // represent that as pointer-to-void. + bool + is_unsafe_pointer_type() const + { return this->points_to() != NULL && this->points_to()->is_void_type(); } + + // Look for field or method NAME for TYPE. Return an expression for + // it, bound to EXPR. + static Expression* + bind_field_or_method(Gogo*, const Type* type, Expression* expr, + const std::string& name, Location); + + // Return true if NAME is an unexported field or method of TYPE. + static bool + is_unexported_field_or_method(Gogo*, const Type*, const std::string&, + std::vector*); + + // Convert the builtin named types. + static void + convert_builtin_named_types(Gogo*); + + // Return the backend representation of this type. + Btype* + get_backend(Gogo*); + + // Return a placeholder for the backend representation of the type. + // This will return a type of the correct size, but for which some + // of the fields may still need to be completed. + Btype* + get_backend_placeholder(Gogo*); + + // Finish the backend representation of a placeholder. + void + 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 + // entry. + Bexpression* + type_descriptor_pointer(Gogo* gogo, Location); + + // Return the type reflection string for this type. + std::string + reflection(Gogo*) const; + + // Return a mangled name for the type. This is a name which can be + // used in assembler code. Identical types should have the same + // manged name. + std::string + mangled_name(Gogo*) const; + + // If the size of the type can be determined, set *PSIZE to the size + // in bytes and return true. Otherwise, return false. This queries + // the backend. + bool + backend_type_size(Gogo*, unsigned int* psize); + + // If the alignment of the type can be determined, set *PALIGN to + // the alignment in bytes and return true. Otherwise, return false. + bool + backend_type_align(Gogo*, unsigned int* palign); + + // If the alignment of a struct field of this type can be + // determined, set *PALIGN to the alignment in bytes and return + // true. Otherwise, return false. + bool + backend_type_field_align(Gogo*, unsigned int* palign); + + // Whether the backend size is known. + bool + is_backend_type_size_known(Gogo*); + + // Get the hash and equality functions for a type. + void + type_functions(Gogo*, Named_type* name, Function_type* hash_fntype, + Function_type* equal_fntype, Named_object** hash_fn, + Named_object** equal_fn); + + // Write the hash and equality type functions. + void + write_specific_type_functions(Gogo*, Named_type*, + const std::string& hash_name, + Function_type* hash_fntype, + const std::string& equal_name, + Function_type* equal_fntype); + + // Export the type. + void + export_type(Export* exp) const + { this->do_export(exp); } + + // Import a type. + static Type* + import_type(Import*); + + protected: + Type(Type_classification); + + // Functions implemented by the child class. + + // Traverse the subtypes. + virtual int + do_traverse(Traverse*); + + // Verify the type. + virtual bool + do_verify() + { return true; } + + virtual bool + do_has_pointer() const + { return false; } + + virtual bool + do_compare_is_identity(Gogo*) = 0; + + virtual unsigned int + do_hash_for_method(Gogo*) const; + + virtual Btype* + do_get_backend(Gogo*) = 0; + + virtual Expression* + do_type_descriptor(Gogo*, Named_type* name) = 0; + + virtual void + do_reflection(Gogo*, std::string*) const = 0; + + virtual void + do_mangled_name(Gogo*, std::string*) const = 0; + + virtual void + do_export(Export*) const; + + // Return whether a method expects a pointer as the receiver. + static bool + method_expects_pointer(const Named_object*); + + // Finalize the methods for a type. + static void + finalize_methods(Gogo*, const Type*, Location, Methods**); + + // Return a method from a set of methods. + static Method* + 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* + type_descriptor(Gogo*, Type*); + + // Return a composite literal for the type descriptor entry for + // TYPE, using NAME as the name of the type. + static Expression* + named_type_descriptor(Gogo*, Type* type, Named_type* name); + + // Return a composite literal for a plain type descriptor for this + // type with the given kind and name. + Expression* + plain_type_descriptor(Gogo*, int runtime_type_kind, Named_type* name); + + // Build a composite literal for the basic type descriptor. + Expression* + type_descriptor_constructor(Gogo*, int runtime_type_kind, Named_type*, + const Methods*, bool only_value_methods); + + // For the benefit of child class reflection string generation. + void + append_reflection(const Type* type, Gogo* gogo, std::string* ret) const + { type->do_reflection(gogo, ret); } + + // For the benefit of child class mangling. + void + append_mangled_name(const Type* type, Gogo* gogo, std::string* ret) const + { type->do_mangled_name(gogo, ret); } + + // Incorporate a string into a hash code. + static unsigned int + hash_string(const std::string&, unsigned int); + + // Return the backend representation for the underlying type of a + // named type. + static Btype* + get_named_base_btype(Gogo* gogo, Type* base_type) + { return base_type->get_btype_without_hash(gogo); } + + private: + // Convert to the desired type classification, or return NULL. This + // is a controlled dynamic_cast. + template + Type_class* + convert() + { + Type* base = this->base(); + return (base->classification_ == type_classification + ? static_cast(base) + : NULL); + } + + template + const Type_class* + convert() const + { + const Type* base = this->base(); + return (base->classification_ == type_classification + ? static_cast(base) + : NULL); + } + + template + Type_class* + convert_no_base() + { + return (this->classification_ == type_classification + ? static_cast(this) + : NULL); + } + + template + const Type_class* + convert_no_base() const + { + return (this->classification_ == type_classification + ? static_cast(this) + : NULL); + } + + // Support for are_assignable and are_assignable_hidden_ok. + static bool + are_assignable_check_hidden(const Type* lhs, const Type* rhs, + bool check_hidden_fields, std::string* reason); + + // Map unnamed types to type descriptor decls. + typedef Unordered_map_hash(const Type*, Bvariable*, Type_hash_identical, + Type_identical) Type_descriptor_vars; + + static Type_descriptor_vars type_descriptor_vars; + + // Build the type descriptor variable for this type. + void + make_type_descriptor_var(Gogo*); + + // Return the name of the type descriptor variable. If NAME is not + // NULL, it is the name to use. + std::string + type_descriptor_var_name(Gogo*, Named_type* name); + + // Return true if the type descriptor for this type should be + // defined in some other package. If NAME is not NULL, it is the + // name of this type. If this returns true it sets *PACKAGE to the + // package where the type descriptor is defined. + bool + type_descriptor_defined_elsewhere(Named_type* name, const Package** package); + + // Build the hash and equality type functions for a type which needs + // specific functions. + void + specific_type_functions(Gogo*, Named_type*, Function_type* hash_fntype, + Function_type* equal_fntype, Named_object** hash_fn, + Named_object** equal_fn); + + void + write_named_hash(Gogo*, Named_type*, Function_type* hash_fntype, + Function_type* equal_fntype); + + void + write_named_equal(Gogo*, Named_type*); + + // Build a composite literal for the uncommon type information. + Expression* + uncommon_type_constructor(Gogo*, Type* uncommon_type, + Named_type*, const Methods*, + bool only_value_methods) const; + + // Build a composite literal for the methods. + Expression* + methods_constructor(Gogo*, Type* methods_type, const Methods*, + bool only_value_methods) const; + + // Build a composite literal for one method. + Expression* + method_constructor(Gogo*, Type* method_type, const std::string& name, + const Method*, bool only_value_methods) const; + + static tree + build_receive_return_type(tree type); + + // A hash table we use to avoid infinite recursion. + typedef Unordered_set_hash(const Named_type*, Type_hash_identical, + Type_identical) Types_seen; + + // Add all methods for TYPE to the list of methods for THIS. + static void + add_methods_for_type(const Type* type, const Method::Field_indexes*, + unsigned int depth, bool, bool, Types_seen*, + Methods**); + + static void + add_local_methods_for_type(const Named_type* type, + const Method::Field_indexes*, + unsigned int depth, bool, bool, Methods**); + + static void + add_embedded_methods_for_type(const Type* type, + const Method::Field_indexes*, + unsigned int depth, bool, bool, Types_seen*, + Methods**); + + static void + add_interface_methods_for_type(const Type* type, + const Method::Field_indexes*, + unsigned int depth, Methods**); + + // Build stub methods for a type. + static void + build_stub_methods(Gogo*, const Type* type, const Methods* methods, + Location); + + static void + build_one_stub_method(Gogo*, Method*, const char* receiver_name, + const Typed_identifier_list*, bool is_varargs, + Location); + + static Expression* + apply_field_indexes(Expression*, const Method::Field_indexes*, + Location); + + // Look for a field or method named NAME in TYPE. + static bool + find_field_or_method(const Type* type, const std::string& name, + bool receiver_can_be_pointer, + std::vector*, int* level, + bool* is_method, bool* found_pointer_method, + std::string* ambig1, std::string* ambig2); + + // Get the backend representation for a type without looking in the + // hash table for identical types. + 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. 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; + + // A list of builtin named types. + static std::vector named_builtin_types; + + // A map from types which need specific type functions to the type + // functions themselves. + typedef std::pair Hash_equal_fn; + typedef Unordered_map_hash(const Type*, Hash_equal_fn, Type_hash_identical, + Type_identical) Type_functions; + + static Type_functions type_functions_table; + + // The type classification. + Type_classification classification_; + // The backend representation of the type, once it has been + // determined. + Btype* btype_; + // The type descriptor for this type. This starts out as NULL and + // is filled in as needed. + Bvariable* type_descriptor_var_; +}; + +// Type hash table operations. + +class Type_hash_identical +{ + public: + unsigned int + operator()(const Type* type) const + { return type->hash_for_method(NULL); } +}; + +class Type_identical +{ + public: + bool + operator()(const Type* t1, const Type* t2) const + { return Type::are_identical(t1, t2, false, NULL); } +}; + +// An identifier with a type. + +class Typed_identifier +{ + public: + Typed_identifier(const std::string& name, Type* type, + Location location) + : name_(name), type_(type), location_(location) + { } + + // Get the name. + const std::string& + name() const + { return this->name_; } + + // Get the type. + Type* + type() const + { return this->type_; } + + // Return the location where the name was seen. This is not always + // meaningful. + Location + location() const + { return this->location_; } + + // Set the type--sometimes we see the identifier before the type. + void + set_type(Type* type) + { + go_assert(this->type_ == NULL || type->is_error_type()); + this->type_ = type; + } + + private: + // Identifier name. + std::string name_; + // Type. + Type* type_; + // The location where the name was seen. + Location location_; +}; + +// A list of Typed_identifiers. + +class Typed_identifier_list +{ + public: + Typed_identifier_list() + : entries_() + { } + + // Whether the list is empty. + bool + empty() const + { return this->entries_.empty(); } + + // Return the number of entries in the list. + size_t + size() const + { return this->entries_.size(); } + + // Add an entry to the end of the list. + void + push_back(const Typed_identifier& td) + { this->entries_.push_back(td); } + + // Remove an entry from the end of the list. + void + pop_back() + { this->entries_.pop_back(); } + + // Set the type of entry I to TYPE. + void + set_type(size_t i, Type* type) + { + go_assert(i < this->entries_.size()); + this->entries_[i].set_type(type); + } + + // Sort the entries by name. + void + sort_by_name(); + + // Traverse types. + int + traverse(Traverse*); + + // Return the first and last elements. + Typed_identifier& + front() + { return this->entries_.front(); } + + const Typed_identifier& + front() const + { return this->entries_.front(); } + + Typed_identifier& + back() + { return this->entries_.back(); } + + const Typed_identifier& + back() const + { return this->entries_.back(); } + + const Typed_identifier& + at(size_t i) const + { return this->entries_.at(i); } + + void + set(size_t i, const Typed_identifier& t) + { this->entries_.at(i) = t; } + + void + resize(size_t c) + { + go_assert(c <= this->entries_.size()); + this->entries_.resize(c, Typed_identifier("", NULL, + Linemap::unknown_location())); + } + + void + reserve(size_t c) + { this->entries_.reserve(c); } + + // Iterators. + + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + + iterator + begin() + { return this->entries_.begin(); } + + const_iterator + begin() const + { return this->entries_.begin(); } + + iterator + end() + { return this->entries_.end(); } + + const_iterator + end() const + { return this->entries_.end(); } + + // Return a copy of this list. This returns an independent copy of + // the vector, but does not copy the types. + Typed_identifier_list* + copy() const; + + private: + std::vector entries_; +}; + +// The type of an integer. + +class Integer_type : public Type +{ + public: + // Create a new integer type. + static Named_type* + create_integer_type(const char* name, bool is_unsigned, int bits, + int runtime_type_kind); + + // Look up an existing integer type. + static Named_type* + lookup_integer_type(const char* name); + + // Create an abstract integer type. + static Integer_type* + create_abstract_integer_type(); + + // Create an abstract character type. + static Integer_type* + create_abstract_character_type(); + + // Whether this is an abstract integer type. + bool + is_abstract() const + { return this->is_abstract_; } + + // Whether this is an unsigned type. + bool + is_unsigned() const + { return this->is_unsigned_; } + + // The number of bits. + int + bits() const + { return this->bits_; } + + // Whether this type is the same as T. + bool + is_identical(const Integer_type* t) const; + + // Whether this is the type "byte" or another name for "byte". + bool + is_byte() const + { return this->is_byte_; } + + // Mark this as the "byte" type. + void + set_is_byte() + { this->is_byte_ = true; } + + // Whether this is the type "rune" or another name for "rune". + bool + is_rune() const + { return this->is_rune_; } + + // Mark this as the "rune" type. + void + set_is_rune() + { this->is_rune_ = true; } + +protected: + bool + do_compare_is_identity(Gogo*) + { return true; } + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + private: + Integer_type(bool is_abstract, bool is_unsigned, int bits, + int runtime_type_kind) + : Type(TYPE_INTEGER), + is_abstract_(is_abstract), is_unsigned_(is_unsigned), is_byte_(false), + is_rune_(false), bits_(bits), runtime_type_kind_(runtime_type_kind) + { } + + // Map names of integer types to the types themselves. + typedef std::map Named_integer_types; + static Named_integer_types named_integer_types; + + // True if this is an abstract type. + bool is_abstract_; + // True if this is an unsigned type. + bool is_unsigned_; + // True if this is the byte type. + bool is_byte_; + // True if this is the rune type. + bool is_rune_; + // The number of bits. + int bits_; + // The runtime type code used in the type descriptor for this type. + int runtime_type_kind_; +}; + +// The type of a floating point number. + +class Float_type : public Type +{ + public: + // Create a new float type. + static Named_type* + create_float_type(const char* name, int bits, int runtime_type_kind); + + // Look up an existing float type. + static Named_type* + lookup_float_type(const char* name); + + // Create an abstract float type. + static Float_type* + create_abstract_float_type(); + + // Whether this is an abstract float type. + bool + is_abstract() const + { return this->is_abstract_; } + + // The number of bits. + int + bits() const + { return this->bits_; } + + // Whether this type is the same as T. + bool + is_identical(const Float_type* t) const; + + protected: + bool + do_compare_is_identity(Gogo*) + { return false; } + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + private: + Float_type(bool is_abstract, int bits, int runtime_type_kind) + : Type(TYPE_FLOAT), + is_abstract_(is_abstract), bits_(bits), + runtime_type_kind_(runtime_type_kind) + { } + + // Map names of float types to the types themselves. + typedef std::map Named_float_types; + static Named_float_types named_float_types; + + // True if this is an abstract type. + bool is_abstract_; + // The number of bits in the floating point value. + int bits_; + // The runtime type code used in the type descriptor for this type. + int runtime_type_kind_; +}; + +// The type of a complex number. + +class Complex_type : public Type +{ + public: + // Create a new complex type. + static Named_type* + create_complex_type(const char* name, int bits, int runtime_type_kind); + + // Look up an existing complex type. + static Named_type* + lookup_complex_type(const char* name); + + // Create an abstract complex type. + static Complex_type* + create_abstract_complex_type(); + + // Whether this is an abstract complex type. + bool + is_abstract() const + { return this->is_abstract_; } + + // The number of bits: 64 or 128. + int bits() const + { return this->bits_; } + + // Whether this type is the same as T. + bool + is_identical(const Complex_type* t) const; + + protected: + bool + do_compare_is_identity(Gogo*) + { return false; } + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + private: + Complex_type(bool is_abstract, int bits, int runtime_type_kind) + : Type(TYPE_COMPLEX), + is_abstract_(is_abstract), bits_(bits), + runtime_type_kind_(runtime_type_kind) + { } + + // Map names of complex types to the types themselves. + typedef std::map Named_complex_types; + static Named_complex_types named_complex_types; + + // True if this is an abstract type. + bool is_abstract_; + // The number of bits in the complex value--64 or 128. + int bits_; + // The runtime type code used in the type descriptor for this type. + int runtime_type_kind_; +}; + +// The type of a string. + +class String_type : public Type +{ + public: + String_type() + : Type(TYPE_STRING) + { } + + // Return a tree for the length of STRING. + static tree + length_tree(Gogo*, tree string); + + // Return a tree which points to the bytes of STRING. + static tree + bytes_tree(Gogo*, tree string); + + protected: + bool + do_has_pointer() const + { return true; } + + bool + do_compare_is_identity(Gogo*) + { return false; } + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string* ret) const; + + private: + // The named string type. + static Named_type* string_type_; +}; + +// The type of a function. + +class Function_type : public Type +{ + public: + Function_type(Typed_identifier* receiver, Typed_identifier_list* parameters, + Typed_identifier_list* results, Location location) + : Type(TYPE_FUNCTION), + receiver_(receiver), parameters_(parameters), results_(results), + location_(location), is_varargs_(false), is_builtin_(false), + fnbtype_(NULL) + { } + + // Get the receiver. + const Typed_identifier* + receiver() const + { return this->receiver_; } + + // Get the return names and types. + const Typed_identifier_list* + results() const + { return this->results_; } + + // Get the parameter names and types. + const Typed_identifier_list* + parameters() const + { return this->parameters_; } + + // Whether this is a varargs function. + bool + is_varargs() const + { return this->is_varargs_; } + + // Whether this is a builtin function. + bool + is_builtin() const + { return this->is_builtin_; } + + // The location where this type was defined. + Location + location() const + { return this->location_; } + + // Return whether this is a method type. + bool + is_method() const + { return this->receiver_ != NULL; } + + // Whether T is a valid redeclaration of this type. This is called + // when a function is declared more than once. + bool + is_valid_redeclaration(const Function_type* t, std::string*) const; + + // Whether this type is the same as T. + bool + is_identical(const Function_type* t, bool ignore_receiver, + bool errors_are_identical, std::string*) const; + + // Record that this is a varargs function. + void + set_is_varargs() + { this->is_varargs_ = true; } + + // Record that this is a builtin function. + void + set_is_builtin() + { this->is_builtin_ = true; } + + // Import a function type. + static Function_type* + do_import(Import*); + + // Return a copy of this type without a receiver. This is only + // valid for a method type. + Function_type* + copy_without_receiver() const; + + // Return a copy of this type with a receiver. This is used when an + // interface method is attached to a named or struct type. + Function_type* + copy_with_receiver(Type*) const; + + // Return a copy of this type with the receiver treated as the first + // parameter. If WANT_POINTER_RECEIVER is true, the receiver is + // forced to be a pointer. + Function_type* + copy_with_receiver_as_param(bool want_pointer_receiver) const; + + // Return a copy of this type ignoring any receiver and using dummy + // names for all parameters. This is used for thunks for method + // values. + Function_type* + copy_with_names() const; + + static Type* + make_function_type_descriptor_type(); + + // Return the backend representation of this function type. This is used + // as the real type of a backend function declaration or defintion. + Btype* + get_backend_fntype(Gogo*); + + protected: + int + do_traverse(Traverse*); + + // A function descriptor may be allocated on the heap. + bool + do_has_pointer() const + { return true; } + + bool + do_compare_is_identity(Gogo*) + { return false; } + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + void + do_export(Export*) const; + + private: + Expression* + type_descriptor_params(Type*, const Typed_identifier*, + const Typed_identifier_list*); + + // A mapping from a list of result types to a backend struct type. + class Results_hash + { + public: + unsigned int + operator()(const Typed_identifier_list*) const; + }; + + class Results_equal + { + public: + bool + operator()(const Typed_identifier_list*, + const Typed_identifier_list*) const; + }; + + typedef Unordered_map_hash(Typed_identifier_list*, Btype*, + Results_hash, Results_equal) Results_structs; + + static Results_structs results_structs; + + // The receiver name and type. This will be NULL for a normal + // function, non-NULL for a method. + Typed_identifier* receiver_; + // The parameter names and types. + Typed_identifier_list* parameters_; + // The result names and types. This will be NULL if no result was + // specified. + Typed_identifier_list* results_; + // The location where this type was defined. This exists solely to + // give a location for the fields of the struct if this function + // returns multiple values. + Location location_; + // Whether this function takes a variable number of arguments. + bool is_varargs_; + // Whether this is a special builtin function which can not simply + // be called. This is used for len, cap, etc. + bool is_builtin_; + // The backend representation of this type for backend function + // declarations and definitions. + Btype* fnbtype_; +}; + +// The type of a function's backend representation. + +class Backend_function_type : public Function_type +{ + public: + Backend_function_type(Typed_identifier* receiver, + Typed_identifier_list* parameters, + Typed_identifier_list* results, Location location) + : Function_type(receiver, parameters, results, location) + { } + + protected: + Btype* + do_get_backend(Gogo* gogo) + { return this->get_backend_fntype(gogo); } +}; + +// The type of a pointer. + +class Pointer_type : public Type +{ + public: + Pointer_type(Type* to_type) + : Type(TYPE_POINTER), + to_type_(to_type) + {} + + Type* + points_to() const + { return this->to_type_; } + + // Import a pointer type. + static Pointer_type* + do_import(Import*); + + static Type* + make_pointer_type_descriptor_type(); + + protected: + int + do_traverse(Traverse*); + + bool + do_has_pointer() const + { return true; } + + bool + do_compare_is_identity(Gogo*) + { return true; } + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + void + do_export(Export*) const; + + private: + // The type to which this type points. + Type* to_type_; +}; + +// The type of a field in a struct. + +class Struct_field +{ + public: + explicit Struct_field(const Typed_identifier& typed_identifier) + : typed_identifier_(typed_identifier), tag_(NULL), is_imported_(false) + { } + + // The field name. + const std::string& + field_name() const; + + // Return whether this struct field is named NAME. + bool + is_field_name(const std::string& name) const; + + // Return whether this struct field is an unexported field named NAME. + bool + is_unexported_field_name(Gogo*, const std::string& name) const; + + // Return whether this struct field is an embedded built-in type. + bool + is_embedded_builtin(Gogo*) const; + + // The field type. + Type* + type() const + { return this->typed_identifier_.type(); } + + // The field location. + Location + location() const + { return this->typed_identifier_.location(); } + + // Whether the field has a tag. + bool + has_tag() const + { return this->tag_ != NULL; } + + // The tag. + const std::string& + tag() const + { + go_assert(this->tag_ != NULL); + return *this->tag_; + } + + // Whether this is an anonymous field. + bool + is_anonymous() const + { return this->typed_identifier_.name().empty(); } + + // Set the tag. FIXME: This is never freed. + void + set_tag(const std::string& tag) + { this->tag_ = new std::string(tag); } + + // Record that this field is defined in an imported struct. + void + set_is_imported() + { this->is_imported_ = true; } + + // Set the type. This is only used in error cases. + void + set_type(Type* type) + { this->typed_identifier_.set_type(type); } + + private: + // The field name, type, and location. + Typed_identifier typed_identifier_; + // The field tag. This is NULL if the field has no tag. + std::string* tag_; + // Whether this field is defined in an imported struct. + bool is_imported_; +}; + +// A list of struct fields. + +class Struct_field_list +{ + public: + Struct_field_list() + : entries_() + { } + + // Whether the list is empty. + bool + empty() const + { return this->entries_.empty(); } + + // Return the number of entries. + size_t + size() const + { return this->entries_.size(); } + + // Add an entry to the end of the list. + void + push_back(const Struct_field& sf) + { this->entries_.push_back(sf); } + + // Index into the list. + const Struct_field& + at(size_t i) const + { return this->entries_.at(i); } + + // Last entry in list. + Struct_field& + back() + { return this->entries_.back(); } + + // Iterators. + + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + + iterator + begin() + { return this->entries_.begin(); } + + const_iterator + begin() const + { return this->entries_.begin(); } + + iterator + end() + { return this->entries_.end(); } + + const_iterator + end() const + { return this->entries_.end(); } + + private: + std::vector entries_; +}; + +// The type of a struct. + +class Struct_type : public Type +{ + public: + Struct_type(Struct_field_list* fields, Location location) + : Type(TYPE_STRUCT), + fields_(fields), location_(location), all_methods_(NULL) + { } + + // Return the field NAME. This only looks at local fields, not at + // embedded types. If the field is found, and PINDEX is not NULL, + // this sets *PINDEX to the field index. If the field is not found, + // this returns NULL. + const Struct_field* + find_local_field(const std::string& name, unsigned int *pindex) const; + + // Return the field number INDEX. + const Struct_field* + field(unsigned int index) const + { return &this->fields_->at(index); } + + // Get the struct fields. + const Struct_field_list* + fields() const + { return this->fields_; } + + // Return the number of fields. + size_t + field_count() const + { return this->fields_->size(); } + + // Push a new field onto the end of the struct. This is used when + // building a closure variable. + void + push_field(const Struct_field& sf) + { this->fields_->push_back(sf); } + + // Return an expression referring to field NAME in STRUCT_EXPR, or + // NULL if there is no field with that name. + Field_reference_expression* + field_reference(Expression* struct_expr, const std::string& name, + Location) const; + + // Return the total number of fields, including embedded fields. + // This is the number of values that can appear in a conversion to + // this type. + unsigned int + total_field_count() const; + + // Whether this type is identical with T. + bool + 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 + // anonymous fields have types with hidden fields. + bool + struct_has_hidden_fields(const Named_type* within, std::string*) const; + + // Return whether NAME is a local field which is not exported. This + // is only used for better error reporting. + bool + is_unexported_local_field(Gogo*, const std::string& name) const; + + // If this is an unnamed struct, build the complete list of methods, + // including those from anonymous fields, and build methods stubs if + // needed. + void + finalize_methods(Gogo*); + + // Return whether this type has any methods. This should only be + // called after the finalize_methods pass. + bool + has_any_methods() const + { return this->all_methods_ != NULL; } + + // Return the methods for tihs type. This should only be called + // after the finalize_methods pass. + const Methods* + methods() const + { return this->all_methods_; } + + // Return the method to use for NAME. This returns NULL if there is + // no such method or if the method is ambiguous. When it returns + // NULL, this sets *IS_AMBIGUOUS if the method name is ambiguous. + 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) + { return this->do_traverse(traverse); } + + // If the offset of field INDEX in the backend implementation can be + // determined, set *POFFSET to the offset in bytes and return true. + // Otherwise, return false. + bool + backend_field_offset(Gogo*, unsigned int index, unsigned int* poffset); + + // Finish the backend representation of all the fields. + void + finish_backend_fields(Gogo*); + + // Import a struct type. + static Struct_type* + do_import(Import*); + + static Type* + make_struct_type_descriptor_type(); + + // Write the hash function for this type. + void + write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*); + + // Write the equality function for this type. + void + write_equal_function(Gogo*, Named_type*); + + protected: + int + do_traverse(Traverse*); + + bool + do_verify(); + + bool + do_has_pointer() const; + + bool + do_compare_is_identity(Gogo*); + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + void + do_export(Export*) const; + + private: + // Used to merge method sets of identical unnamed structs. + typedef Unordered_map_hash(Struct_type*, Struct_type*, Type_hash_identical, + Type_identical) Identical_structs; + + static Identical_structs identical_structs; + + // Used to manage method tables for identical unnamed structs. + typedef std::pair + Struct_method_table_pair; + + typedef Unordered_map_hash(Struct_type*, Struct_method_table_pair*, + Type_hash_identical, Type_identical) + Struct_method_tables; + + static Struct_method_tables struct_method_tables; + + // Used to avoid infinite loops in field_reference_depth. + struct Saw_named_type + { + Saw_named_type* next; + Named_type* nt; + }; + + Field_reference_expression* + field_reference_depth(Expression* struct_expr, const std::string& name, + Location, Saw_named_type*, + unsigned int* depth) const; + + // The fields of the struct. + Struct_field_list* fields_; + // The place where the struct was declared. + Location location_; + // If this struct is unnamed, a list of methods. + Methods* all_methods_; +}; + +// The type of an array. + +class Array_type : public Type +{ + public: + Array_type(Type* element_type, Expression* length) + : Type(TYPE_ARRAY), + element_type_(element_type), length_(length), length_tree_(NULL) + { } + + // Return the element type. + Type* + element_type() const + { return this->element_type_; } + + // Return the length. This will return NULL for an open array. + Expression* + length() const + { return this->length_; } + + // Whether this type is identical with T. + bool + is_identical(const Array_type* t, bool errors_are_identical) const; + + // Whether this type has any hidden fields. + bool + array_has_hidden_fields(const Named_type* within, std::string* reason) const + { return this->element_type_->has_hidden_fields(within, reason); } + + // Return an expression for the pointer to the values in an array. + Expression* + get_value_pointer(Gogo*, Expression* array) const; + + // Return an expression for the length of an array with this type. + Expression* + get_length(Gogo*, Expression* array) const; + + // Return an expression for the capacity of an array with this type. + Expression* + get_capacity(Gogo*, Expression* array) const; + + // Import an array type. + static Array_type* + do_import(Import*); + + // Return the backend representation of the element type. + Btype* + get_backend_element(Gogo*, bool use_placeholder); + + // Return the backend representation of the length. + Bexpression* + get_backend_length(Gogo*); + + // Finish the backend representation of the element type. + void + finish_backend_element(Gogo*); + + static Type* + make_array_type_descriptor_type(); + + static Type* + make_slice_type_descriptor_type(); + + // Write the hash function for this type. + void + write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*); + + // Write the equality function for this type. + void + write_equal_function(Gogo*, Named_type*); + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_verify(); + + bool + do_has_pointer() const + { + return this->length_ == NULL || this->element_type_->has_pointer(); + } + + bool + do_compare_is_identity(Gogo*); + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + void + do_export(Export*) const; + + private: + bool + verify_length(); + + tree + get_length_tree(Gogo*); + + Expression* + array_type_descriptor(Gogo*, Named_type*); + + Expression* + slice_type_descriptor(Gogo*, Named_type*); + + // The type of elements of the array. + Type* element_type_; + // The number of elements. This may be NULL. + Expression* length_; + // The length as a tree. We only want to compute this once. + tree length_tree_; +}; + +// The type of a map. + +class Map_type : public Type +{ + public: + Map_type(Type* key_type, Type* val_type, Location location) + : Type(TYPE_MAP), + key_type_(key_type), val_type_(val_type), location_(location) + { } + + // Return the key type. + Type* + key_type() const + { return this->key_type_; } + + // Return the value type. + Type* + val_type() const + { return this->val_type_; } + + // Whether this type is identical with T. + bool + is_identical(const Map_type* t, bool errors_are_identical) const; + + // Import a map type. + static Map_type* + do_import(Import*); + + static Type* + make_map_type_descriptor_type(); + + static Type* + make_map_descriptor_type(); + + // Build a map descriptor for this type. Return a pointer to it. + // The location is the location which causes us to need the + // descriptor. + Bexpression* + map_descriptor_pointer(Gogo* gogo, Location); + + protected: + int + do_traverse(Traverse*); + + bool + do_verify(); + + bool + do_has_pointer() const + { return true; } + + bool + do_compare_is_identity(Gogo*) + { return false; } + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + void + do_export(Export*) const; + + private: + // Mapping from map types to map descriptors. + typedef Unordered_map_hash(const Map_type*, Bvariable*, Type_hash_identical, + Type_identical) Map_descriptors; + static Map_descriptors map_descriptors; + + Bvariable* + map_descriptor(Gogo*); + + // The key type. + Type* key_type_; + // The value type. + Type* val_type_; + // Where the type was defined. + Location location_; +}; + +// The type of a channel. + +class Channel_type : public Type +{ + public: + Channel_type(bool may_send, bool may_receive, Type* element_type) + : Type(TYPE_CHANNEL), + may_send_(may_send), may_receive_(may_receive), + element_type_(element_type) + { go_assert(may_send || may_receive); } + + // Whether this channel can send data. + bool + may_send() const + { return this->may_send_; } + + // Whether this channel can receive data. + bool + may_receive() const + { return this->may_receive_; } + + // The type of the values that may be sent on this channel. This is + // NULL if any type may be sent. + Type* + element_type() const + { return this->element_type_; } + + // Whether this type is identical with T. + bool + is_identical(const Channel_type* t, bool errors_are_identical) const; + + // Import a channel type. + static Channel_type* + do_import(Import*); + + static Type* + make_chan_type_descriptor_type(); + + protected: + int + do_traverse(Traverse* traverse) + { return Type::traverse(this->element_type_, traverse); } + + bool + do_has_pointer() const + { return true; } + + bool + do_compare_is_identity(Gogo*) + { return true; } + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + void + do_export(Export*) const; + + private: + // Whether this channel can send data. + bool may_send_; + // Whether this channel can receive data. + bool may_receive_; + // The types of elements which may be sent on this channel. If this + // is NULL, it means that any type may be sent. + Type* element_type_; +}; + +// An interface type. + +class Interface_type : public Type +{ + public: + Interface_type(Typed_identifier_list* methods, Location location) + : Type(TYPE_INTERFACE), + parse_methods_(methods), all_methods_(NULL), location_(location), + interface_btype_(NULL), bmethods_(NULL), assume_identical_(NULL), + methods_are_finalized_(false), bmethods_is_placeholder_(false), + seen_(false) + { go_assert(methods == NULL || !methods->empty()); } + + // The location where the interface type was defined. + Location + location() const + { return this->location_; } + + // Return whether this is an empty interface. + bool + is_empty() const + { + go_assert(this->methods_are_finalized_); + return this->all_methods_ == NULL; + } + + // Return the list of methods. This will return NULL for an empty + // interface. + const Typed_identifier_list* + methods() const; + + // Return the number of methods. + size_t + method_count() const; + + // Return the method NAME, or NULL. + const Typed_identifier* + find_method(const std::string& name) const; + + // Return the zero-based index of method NAME. + size_t + method_index(const std::string& name) const; + + // Finalize the methods. This sets all_methods_. This handles + // interface inheritance. + void + finalize_methods(); + + // Return true if T implements this interface. If this returns + // false, and REASON is not NULL, it sets *REASON to the reason that + // it fails. + bool + implements_interface(const Type* t, std::string* reason) const; + + // Whether this type is identical with T. REASON is as in + // implements_interface. + bool + 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. + bool + is_compatible_for_assign(const Interface_type*, std::string* reason) const; + + // Return whether NAME is a method which is not exported. This is + // only used for better error reporting. + bool + is_unexported_method(Gogo*, const std::string& name) const; + + // Import an interface type. + static Interface_type* + do_import(Import*); + + // Make a struct for an empty interface type. + static Btype* + get_backend_empty_interface_type(Gogo*); + + // Get a pointer to the backend representation of the method table. + Btype* + get_backend_methods(Gogo*); + + // Return a placeholder for the backend representation of the + // pointer to the method table. + Btype* + get_backend_methods_placeholder(Gogo*); + + // Finish the backend representation of the method types. + void + finish_backend_methods(Gogo*); + + static Type* + make_interface_type_descriptor_type(); + + protected: + int + do_traverse(Traverse*); + + bool + do_has_pointer() const + { return true; } + + bool + do_compare_is_identity(Gogo*) + { return false; } + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string*) const; + + void + do_export(Export*) const; + + private: + // This type guards against infinite recursion when comparing + // interface types. We keep a list of interface types assumed to be + // identical during comparison. We just keep the list on the stack. + // This permits us to compare cases like + // type I1 interface { F() interface{I1} } + // type I2 interface { F() interface{I2} } + struct Assume_identical + { + Assume_identical* next; + const Interface_type* t1; + const Interface_type* t2; + }; + + bool + assume_identical(const Interface_type*, const Interface_type*) const; + + // The list of methods associated with the interface from the + // parser. This will be NULL for the empty interface. This may + // include unnamed interface types. + Typed_identifier_list* parse_methods_; + // The list of all methods associated with the interface. This + // expands any interface types listed in methods_. It is set by + // finalize_methods. This will be NULL for the empty interface. + Typed_identifier_list* all_methods_; + // The location where the interface was defined. + Location location_; + // The backend representation of this type during backend conversion. + Btype* interface_btype_; + // The backend representation of the pointer to the method table. + Btype* bmethods_; + // A list of interface types assumed to be identical during + // interface comparison. + mutable Assume_identical* assume_identical_; + // Whether the methods have been finalized. + bool methods_are_finalized_; + // Whether the bmethods_ field is a placeholder. + bool bmethods_is_placeholder_; + // Used to avoid endless recursion in do_mangled_name. + mutable bool seen_; +}; + +// The value we keep for a named type. This lets us get the right +// name when we convert to trees. Note that we don't actually keep +// the name here; the name is in the Named_object which points to +// this. This object exists to hold a unique tree which represents +// the type. + +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), 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), + is_converted_(false), is_circular_(false), is_verified_(false), + seen_(false), seen_in_compare_is_identity_(false), + seen_in_get_backend_(false) + { } + + // Return the associated Named_object. This holds the actual name. + Named_object* + named_object() + { return this->named_object_; } + + const Named_object* + named_object() const + { return this->named_object_; } + + // Set the Named_object. This is used when we see a type + // declaration followed by a type. + void + set_named_object(Named_object* no) + { this->named_object_ = no; } + + // 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(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, unsigned int index) + { + this->in_function_ = f; + this->in_function_index_ = index; + } + + // Return the name of the type. + const std::string& + name() const; + + // Return the name of the type for an error message. The difference + // is that if the type is defined in a different package, this will + // return PACKAGE.NAME. + std::string + message_name() const; + + // Return the underlying type. + Type* + real_type() + { return this->type_; } + + const Type* + real_type() const + { return this->type_; } + + // Return the location. + Location + location() const + { return this->location_; } + + // Whether this type is visible. This only matters when parsing. + bool + is_visible() const + { return this->is_visible_; } + + // Mark this type as visible. + void + set_is_visible() + { this->is_visible_ = true; } + + // Mark this type as invisible. + void + clear_is_visible() + { this->is_visible_ = false; } + + // Whether this is a builtin type. + bool + is_builtin() const + { return Linemap::is_predeclared_location(this->location_); } + + // Whether this is an alias. There are currently two aliases: byte + // and rune. + bool + is_alias() const; + + // Whether this is a circular type: a pointer or function type that + // refers to itself, which is not possible in C. + bool + is_circular() const + { return this->is_circular_; } + + // Return the base type for this type. + Type* + named_base(); + + const Type* + named_base() const; + + // Return whether this is an error type. + bool + is_named_error_type() const; + + // Return whether this type is comparable. If REASON is not NULL, + // set *REASON when returning false. + bool + named_type_is_comparable(std::string* reason) const; + + // Add a method to this type. + Named_object* + add_method(const std::string& name, Function*); + + // Add a method declaration to this type. + Named_object* + add_method_declaration(const std::string& name, Package* package, + Function_type* type, Location location); + + // Add an existing method--one defined before the type itself was + // defined--to a type. + void + add_existing_method(Named_object*); + + // Look up a local method. + Named_object* + find_local_method(const std::string& name) const; + + // Return the list of local methods. + const Bindings* + local_methods() const + { return this->local_methods_; } + + // Build the complete list of methods, including those from + // anonymous fields, and build method stubs if needed. + void + finalize_methods(Gogo*); + + // Return whether this type has any methods. This should only be + // called after the finalize_methods pass. + bool + has_any_methods() const + { return this->all_methods_ != NULL; } + + // Return the methods for this type. This should only be called + // after the finalized_methods pass. + const Methods* + methods() const + { return this->all_methods_; } + + // Return the method to use for NAME. This returns NULL if there is + // no such method or if the method is ambiguous. When it returns + // NULL, this sets *IS_AMBIGUOUS if the method name is ambiguous. + Method* + method_function(const std::string& name, bool *is_ambiguous) const; + + // Return whether NAME is a known field or method which is not + // exported. This is only used for better error reporting. + bool + is_unexported_local_method(Gogo*, const std::string& name) 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); + + // Whether this type has any hidden fields. + bool + named_type_has_hidden_fields(std::string* reason) const; + + // Note that a type must be converted to the backend representation + // before we convert this type. + void + add_dependency(Named_type* nt) + { this->dependencies_.push_back(nt); } + + // Return true if the size and alignment of the backend + // representation of this type is known. This is always true after + // types have been converted, but may be false beforehand. + bool + is_named_backend_type_size_known() const + { return this->named_btype_ != NULL && !this->is_placeholder_; } + + // Export the type. + void + export_named_type(Export*, const std::string& name) const; + + // Import a named type. + static void + import_named_type(Import*, Named_type**); + + // Initial conversion to backend representation. + void + convert(Gogo*); + + protected: + int + do_traverse(Traverse* traverse) + { return Type::traverse(this->type_, traverse); } + + bool + do_verify(); + + bool + do_has_pointer() const; + + bool + do_compare_is_identity(Gogo*); + + unsigned int + do_hash_for_method(Gogo*) const; + + Btype* + do_get_backend(Gogo*); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string* ret) const; + + void + do_export(Export*) const; + + private: + // Create the placeholder during conversion. + void + create_placeholder(Gogo*); + + // 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 + // have methods. + Bindings* local_methods_; + // The full list of methods for this type, including methods + // declared for anonymous fields. + Methods* all_methods_; + // A mapping from interfaces to the associated interface method + // tables for this type. + Interface_method_tables* interface_method_tables_; + // A mapping from interfaces to the associated interface method + // tables for pointers to this type. + Interface_method_tables* pointer_interface_method_tables_; + // The location where this type was defined. + Location location_; + // The backend representation of this type during backend + // conversion. This is used to avoid endless recursion when a named + // type refers to itself. + Btype* named_btype_; + // A list of types which must be converted to the backend + // representation before this type can be converted. This is for + // cases like + // type S1 { p *S2 } + // type S2 { s S1 } + // where we can't convert S2 to the backend representation unless we + // have converted S1. + std::vector dependencies_; + // Whether this type is visible. This is false if this type was + // created because it was referenced by an imported object, but the + // type itself was not exported. This will always be true for types + // created in the current package. + bool is_visible_; + // Whether this type is erroneous. + bool is_error_; + // Whether the current value of named_btype_ is a placeholder for + // which the final size of the type is not known. + bool is_placeholder_; + // Whether this type has been converted to the backend + // representation. Implies that is_placeholder_ is false. + bool is_converted_; + // Whether this is a pointer or function type which refers to the + // type itself. + bool is_circular_; + // Whether this type has been verified. + bool is_verified_; + // In a recursive operation such as has_hidden_fields, this flag is + // used to prevent infinite recursion when a type refers to itself. + // This is mutable because it is always reset to false when the + // function exits. + mutable bool seen_; + // Like seen_, but used only by do_compare_is_identity. + bool seen_in_compare_is_identity_; + // Like seen_, but used only by do_get_backend. + bool seen_in_get_backend_; +}; + +// A forward declaration. This handles a type which has been declared +// but not defined. + +class Forward_declaration_type : public Type +{ + public: + Forward_declaration_type(Named_object* named_object); + + // The named object associated with this type declaration. This + // will be resolved. + Named_object* + named_object(); + + const Named_object* + named_object() const; + + // Return the name of the type. + const std::string& + name() const; + + // Return the type to which this points. Give an error if the type + // has not yet been defined. + Type* + real_type(); + + const Type* + real_type() const; + + // Whether the base type has been defined. + bool + is_defined() const; + + // Add a method to this type. + Named_object* + add_method(const std::string& name, Function*); + + // Add a method declaration to this type. + Named_object* + add_method_declaration(const std::string& name, Package*, Function_type*, + Location); + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_verify(); + + bool + do_has_pointer() const + { return this->real_type()->has_pointer(); } + + bool + do_compare_is_identity(Gogo* gogo) + { return this->real_type()->compare_is_identity(gogo); } + + unsigned int + do_hash_for_method(Gogo* gogo) const + { return this->real_type()->hash_for_method(gogo); } + + Btype* + do_get_backend(Gogo* gogo); + + Expression* + do_type_descriptor(Gogo*, Named_type*); + + void + do_reflection(Gogo*, std::string*) const; + + void + do_mangled_name(Gogo*, std::string* ret) const; + + void + do_export(Export*) const; + + private: + // Issue a warning about a use of an undefined type. + void + warn() const; + + // The type declaration. + Named_object* named_object_; + // Whether we have issued a warning about this type. + mutable bool warned_; +}; + +// The Type_context struct describes what we expect for the type of an +// expression. + +struct Type_context +{ + // The exact type we expect, if known. This may be NULL. + Type* type; + // Whether an abstract type is permitted. + bool may_be_abstract; + + // Constructors. + Type_context() + : type(NULL), may_be_abstract(false) + { } + + Type_context(Type* a_type, bool a_may_be_abstract) + : type(a_type), may_be_abstract(a_may_be_abstract) + { } +}; + +#endif // !defined(GO_TYPES_H) diff --git a/gcc-4.9/gcc/go/gofrontend/unsafe.cc b/gcc-4.9/gcc/go/gofrontend/unsafe.cc new file mode 100644 index 000000000..e7c61f023 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/unsafe.cc @@ -0,0 +1,96 @@ +// unsafe.cc -- Go frontend builtin unsafe package. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go-system.h" + +#include "go-c.h" +#include "types.h" +#include "gogo.h" + +// Set up the builtin unsafe package. This should probably be driven +// by a table. + +void +Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported, + Location location) +{ + Location bloc = Linemap::predeclared_location(); + + bool add_to_globals; + Package* package = this->add_imported_package("unsafe", local_name, + is_local_name_exported, + "unsafe", location, + &add_to_globals); + + if (package == NULL) + { + go_assert(saw_errors()); + return; + } + + package->set_location(location); + package->set_is_imported(); + + this->imports_.insert(std::make_pair("unsafe", package)); + + Bindings* bindings = package->bindings(); + + // The type may have already been created by an import. + Named_object* no = package->bindings()->lookup("Pointer"); + if (no == NULL) + { + Type* type = Type::make_pointer_type(Type::make_void_type()); + no = bindings->add_type("Pointer", package, type, + Linemap::unknown_location()); + } + else + { + go_assert(no->package() == package); + go_assert(no->is_type()); + go_assert(no->type_value()->is_unsafe_pointer_type()); + no->type_value()->set_is_visible(); + } + Named_type* pointer_type = no->type_value(); + if (add_to_globals) + this->add_named_type(pointer_type); + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + // Sizeof. + Typed_identifier_list* results = new Typed_identifier_list; + results->push_back(Typed_identifier("", uintptr_type, bloc)); + Function_type* fntype = Type::make_function_type(NULL, NULL, results, bloc); + fntype->set_is_builtin(); + no = bindings->add_function_declaration("Sizeof", package, fntype, bloc); + if (add_to_globals) + this->add_named_object(no); + + // Offsetof. + results = new Typed_identifier_list; + results->push_back(Typed_identifier("", uintptr_type, bloc)); + fntype = Type::make_function_type(NULL, NULL, results, bloc); + fntype->set_is_varargs(); + fntype->set_is_builtin(); + no = bindings->add_function_declaration("Offsetof", package, fntype, bloc); + if (add_to_globals) + this->add_named_object(no); + + // Alignof. + results = new Typed_identifier_list; + results->push_back(Typed_identifier("", uintptr_type, bloc)); + fntype = Type::make_function_type(NULL, NULL, results, bloc); + fntype->set_is_varargs(); + fntype->set_is_builtin(); + no = bindings->add_function_declaration("Alignof", package, fntype, bloc); + if (add_to_globals) + this->add_named_object(no); + + if (!this->imported_unsafe_) + { + go_imported_unsafe(); + this->imported_unsafe_ = true; + } +} diff --git a/gcc-4.9/gcc/go/gospec.c b/gcc-4.9/gcc/go/gospec.c new file mode 100644 index 000000000..02d584235 --- /dev/null +++ b/gcc-4.9/gcc/go/gospec.c @@ -0,0 +1,410 @@ +/* gospec.c -- Specific flags and argument handling of the gcc Go front end. + Copyright (C) 2009-2014 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "gcc.h" +#include "opts.h" + +/* This bit is set if we saw a `-xfoo' language specification. */ +#define LANGSPEC (1<<1) +/* This bit is set if they did `-lm' or `-lmath'. */ +#define MATHLIB (1<<2) +/* This bit is set if they did `-lpthread'. */ +#define THREADLIB (1<<3) +/* This bit is set if they did `-lc'. */ +#define WITHLIBC (1<<4) +/* Skip this option. */ +#define SKIPOPT (1<<5) + +#ifndef MATH_LIBRARY +#define MATH_LIBRARY "m" +#endif +#ifndef MATH_LIBRARY_PROFILE +#define MATH_LIBRARY_PROFILE MATH_LIBRARY +#endif + +#define THREAD_LIBRARY "pthread" +#define THREAD_LIBRARY_PROFILE THREAD_LIBRARY + +#define LIBGO "go" +#define LIBGO_PROFILE LIBGO +#define LIBGOBEGIN "gobegin" + +void +lang_specific_driver (struct cl_decoded_option **in_decoded_options, + unsigned int *in_decoded_options_count, + int *in_added_libraries) +{ + unsigned int i, j; + + /* If true, the user gave us the `-p' or `-pg' flag. */ + bool saw_profile_flag = false; + + /* This is a tristate: + -1 means we should not link in libgo + 0 means we should link in libgo if it is needed + 1 means libgo is needed and should be linked in. + 2 means libgo is needed and should be linked statically. */ + int library = 0; + + /* The new argument list will be contained in this. */ + struct cl_decoded_option *new_decoded_options; + + /* "-lm" or "-lmath" if it appears on the command line. */ + const struct cl_decoded_option *saw_math = 0; + + /* "-lpthread" if it appears on the command line. */ + const struct cl_decoded_option *saw_thread = 0; + + /* "-lc" if it appears on the command line. */ + const struct cl_decoded_option *saw_libc = 0; + + /* An array used to flag each argument that needs a bit set for + LANGSPEC, MATHLIB, or WITHLIBC. */ + int *args; + + /* Whether we need the thread library. */ + int need_thread = 0; + + /* By default, we throw on the math library if we have one. */ + int need_math = (MATH_LIBRARY[0] != '\0'); + + /* True if we saw -static. */ + int static_link = 0; + + /* True if we should add -shared-libgcc to the command-line. */ + int shared_libgcc = 1; + + /* The total number of arguments with the new stuff. */ + unsigned int argc; + + /* The argument list. */ + struct cl_decoded_option *decoded_options; + + /* The number of libraries added in. */ + int added_libraries; + + /* The total number of arguments with the new stuff. */ + int num_args = 1; + + /* Whether the -o option was used. */ + bool saw_opt_o = false; + + /* Whether the -c option was used. Also used for -E, -fsyntax-only, + in general anything which implies only compilation and not + linking. */ + bool saw_opt_c = false; + + /* Whether the -S option was used. */ + bool saw_opt_S = false; + + /* The first input file with an extension of .go. */ + const char *first_go_file = NULL; + + argc = *in_decoded_options_count; + decoded_options = *in_decoded_options; + added_libraries = *in_added_libraries; + + args = XCNEWVEC (int, argc); + + for (i = 1; i < argc; i++) + { + const char *arg = decoded_options[i].arg; + + switch (decoded_options[i].opt_index) + { + case OPT_nostdlib: + case OPT_nodefaultlibs: + library = -1; + break; + + case OPT_l: + if (strcmp (arg, MATH_LIBRARY) == 0) + { + args[i] |= MATHLIB; + need_math = 0; + } + else if (strcmp (arg, THREAD_LIBRARY) == 0) + args[i] |= THREADLIB; + else if (strcmp (arg, "c") == 0) + args[i] |= WITHLIBC; + else + /* Unrecognized libraries (e.g. -lfoo) may require libgo. */ + library = (library == 0) ? 1 : library; + break; + + case OPT_pg: + case OPT_p: + saw_profile_flag = true; + break; + + case OPT_x: + if (library == 0 && strcmp (arg, "go") == 0) + library = 1; + break; + + case OPT_Xlinker: + case OPT_Wl_: + /* Arguments that go directly to the linker might be .o files, + or something, and so might cause libgo to be needed. */ + if (library == 0) + library = 1; + break; + + case OPT_c: + case OPT_E: + case OPT_M: + case OPT_MM: + case OPT_fsyntax_only: + /* Don't specify libraries if we won't link, since that would + cause a warning. */ + saw_opt_c = true; + library = -1; + break; + + case OPT_S: + saw_opt_S = true; + library = -1; + break; + + case OPT_o: + saw_opt_o = true; + break; + + case OPT_static: + static_link = 1; + break; + + case OPT_static_libgcc: + shared_libgcc = 0; + break; + + case OPT_static_libgo: + library = library >= 0 ? 2 : library; + args[i] |= SKIPOPT; + break; + + case OPT_SPECIAL_input_file: + if (library == 0) + library = 1; + + if (first_go_file == NULL) + { + int len; + + len = strlen (arg); + if (len > 3 && strcmp (arg + len - 3, ".go") == 0) + first_go_file = arg; + } + + break; + } + } + + /* There's no point adding -shared-libgcc if we don't have a shared + libgcc. */ +#ifndef ENABLE_SHARED_LIBGCC + shared_libgcc = 0; +#endif + + /* Make sure to have room for the trailing NULL argument. */ + num_args = argc + need_math + shared_libgcc + (library > 0) * 5 + 10; + new_decoded_options = XNEWVEC (struct cl_decoded_option, num_args); + + i = 0; + j = 0; + + /* Copy the 0th argument, i.e., the name of the program itself. */ + new_decoded_options[j++] = decoded_options[i++]; + + /* If we are linking, pass -fsplit-stack if it is supported. */ +#ifdef TARGET_CAN_SPLIT_STACK + if (library >= 0) + { + generate_option (OPT_fsplit_stack, NULL, 1, CL_DRIVER, + &new_decoded_options[j]); + j++; + } +#endif + + /* NOTE: We start at 1 now, not 0. */ + while (i < argc) + { + new_decoded_options[j] = decoded_options[i]; + + /* Make sure -lgo is before the math library, since libgo itself + uses those math routines. */ + if (!saw_math && (args[i] & MATHLIB) && library > 0) + { + --j; + saw_math = &decoded_options[i]; + } + + if (!saw_thread && (args[i] & THREADLIB) && library > 0) + { + --j; + saw_thread = &decoded_options[i]; + } + + if (!saw_libc && (args[i] & WITHLIBC) && library > 0) + { + --j; + saw_libc = &decoded_options[i]; + } + + if ((args[i] & SKIPOPT) != 0) + --j; + + i++; + j++; + } + + /* If we didn't see a -o option, add one. This is because we need + the driver to pass all .go files to go1. Without a -o option the + driver will invoke go1 separately for each input file. FIXME: + This should probably use some other interface to force the driver + to set combine_inputs. */ + if (first_go_file != NULL && !saw_opt_o) + { + if (saw_opt_c || saw_opt_S) + { + const char *base; + int baselen; + int alen; + char *out; + + base = lbasename (first_go_file); + baselen = strlen (base) - 3; + alen = baselen + 3; + out = XNEWVEC (char, alen); + memcpy (out, base, baselen); + /* The driver will convert .o to some other suffix (e.g., + .obj) if appropriate. */ + out[baselen] = '.'; + if (saw_opt_S) + out[baselen + 1] = 's'; + else + out[baselen + 1] = 'o'; + out[baselen + 2] = '\0'; + generate_option (OPT_o, out, 1, CL_DRIVER, + &new_decoded_options[j]); + } + else + generate_option (OPT_o, "a.out", 1, CL_DRIVER, + &new_decoded_options[j]); + j++; + } + + /* Add `-lgo' if we haven't already done so. */ + if (library > 0) + { + generate_option (OPT_l, LIBGOBEGIN, 1, CL_DRIVER, + &new_decoded_options[j]); + added_libraries++; + j++; + +#ifdef HAVE_LD_STATIC_DYNAMIC + if (library > 1 && !static_link) + { + generate_option (OPT_Wl_, LD_STATIC_OPTION, 1, CL_DRIVER, + &new_decoded_options[j]); + j++; + } +#endif + + generate_option (OPT_l, saw_profile_flag ? LIBGO_PROFILE : LIBGO, 1, + CL_DRIVER, &new_decoded_options[j]); + added_libraries++; + j++; + +#ifdef HAVE_LD_STATIC_DYNAMIC + if (library > 1 && !static_link) + { + generate_option (OPT_Wl_, LD_DYNAMIC_OPTION, 1, CL_DRIVER, + &new_decoded_options[j]); + j++; + } +#endif + + /* When linking libgo statically we also need to link with the + pthread library. */ + if (library > 1 || static_link) + need_thread = 1; + } + + if (saw_thread) + new_decoded_options[j++] = *saw_thread; + else if (library > 0 && need_thread) + { + generate_option (OPT_l, + (saw_profile_flag + ? THREAD_LIBRARY_PROFILE + : THREAD_LIBRARY), + 1, CL_DRIVER, &new_decoded_options[j]); + added_libraries++; + j++; + } + + if (saw_math) + new_decoded_options[j++] = *saw_math; + else if (library > 0 && need_math) + { + generate_option (OPT_l, + saw_profile_flag ? MATH_LIBRARY_PROFILE : MATH_LIBRARY, + 1, CL_DRIVER, &new_decoded_options[j]); + added_libraries++; + j++; + } + + if (saw_libc) + new_decoded_options[j++] = *saw_libc; + if (shared_libgcc && !static_link) + generate_option (OPT_shared_libgcc, NULL, 1, CL_DRIVER, + &new_decoded_options[j++]); + +#ifdef TARGET_CAN_SPLIT_STACK + /* libgcc wraps pthread_create to support split stack, however, due to + relative ordering of -lpthread and -lgcc, we can't just mark + __real_pthread_create in libgcc as non-weak. But we need to link in + pthread_create from pthread if we are statically linking, so we work- + around by passing -u pthread_create to to the linker. */ + if (static_link) + { + generate_option (OPT_Wl_, "-u,pthread_create", 1, CL_DRIVER, + &new_decoded_options[j]); + j++; + } +#endif + + *in_decoded_options_count = j; + *in_decoded_options = new_decoded_options; + *in_added_libraries = added_libraries; +} + +/* Called before linking. Returns 0 on success and -1 on failure. */ +int lang_specific_pre_link (void) /* Not used for Go. */ +{ + return 0; +} + +/* Number of extra output files that lang_specific_pre_link may generate. */ +int lang_specific_extra_outfiles = 0; /* Not used for Go. */ diff --git a/gcc-4.9/gcc/go/lang-specs.h b/gcc-4.9/gcc/go/lang-specs.h new file mode 100644 index 000000000..463e05007 --- /dev/null +++ b/gcc-4.9/gcc/go/lang-specs.h @@ -0,0 +1,25 @@ +/* lang-specs.h -- gcc driver specs for Go frontend. + Copyright (C) 2009-2014 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* This is the contribution to the `default_compilers' array in gcc.c + for the Go language. */ + +{".go", "@go", 0, 1, 0}, +{"@go", "go1 %i %(cc1_options) %{I*} %{L*} %D %{!fsyntax-only:%(invoke_as)}", + 0, 1, 0}, diff --git a/gcc-4.9/gcc/go/lang.opt b/gcc-4.9/gcc/go/lang.opt new file mode 100644 index 000000000..6f6b4418a --- /dev/null +++ b/gcc-4.9/gcc/go/lang.opt @@ -0,0 +1,76 @@ +; lang.opt -- Options for the gcc Go front end. + +; Copyright (C) 2009-2014 Free Software Foundation, Inc. +; +; This file is part of GCC. +; +; GCC is free software; you can redistribute it and/or modify it under +; the terms of the GNU General Public License as published by the Free +; Software Foundation; either version 3, or (at your option) any later +; version. +; +; GCC is distributed in the hope that it will be useful, but WITHOUT ANY +; WARRANTY; without even the implied warranty of MERCHANTABILITY or +; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +; for more details. +; +; You should have received a copy of the GNU General Public License +; along with GCC; see the file COPYING3. If not see +; . + +; See the GCC internals manual for a description of this file's format. + +; Please try to keep this file in ASCII collating order. + +Language +Go + +I +Go Joined Separate +; Documented in c.opt + +L +Go Joined Separate +; Not documented + +Wall +Go +; Documented in c.opt + +fgo-check-divide-zero +Go Var(go_check_divide_zero) Init(1) +Add explicit checks for division by zero + +fgo-check-divide-overflow +Go Var(go_check_divide_overflow) Init(1) +Add explicit checks for division overflow in INT_MIN / -1 + +fgo-dump- +Go Joined RejectNegative +-fgo-dump- Dump Go frontend internal information + +fgo-optimize- +Go Joined RejectNegative +-fgo-optimize- Turn on optimization passes in the frontend + +fgo-pkgpath= +Go Joined RejectNegative +-fgo-pkgpath= Set Go package path + +fgo-prefix= +Go Joined RejectNegative +-fgo-prefix= Set package-specific prefix for exported Go names + +fgo-relative-import-path= +Go Joined RejectNegative +-fgo-relative-import-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 + +o +Go Joined Separate +; Documented in common.opt + +; This comment is to ensure we retain the blank line above. -- cgit v1.2.3