Mercurial > hg > CbC > CbC_gcc
diff gcc/go/gofrontend/gogo.cc @ 131:84e7813d76e9
gcc-8.2
author | mir3636 |
---|---|
date | Thu, 25 Oct 2018 07:37:49 +0900 |
parents | 04ced10e8804 |
children | 1830386684a0 |
line wrap: on
line diff
--- a/gcc/go/gofrontend/gogo.cc Fri Oct 27 22:46:09 2017 +0900 +++ b/gcc/go/gofrontend/gogo.cc Thu Oct 25 07:37:49 2018 +0900 @@ -55,6 +55,7 @@ check_divide_overflow_(true), compiling_runtime_(false), debug_escape_level_(0), + nil_check_size_threshold_(4096), verify_types_(), interface_types_(), specific_type_functions_(), @@ -710,7 +711,7 @@ Bfunction* pfunc = this->backend()->function(fntype, user_name, init_name, true, true, true, false, - false, unknown_loc); + false, false, unknown_loc); Bexpression* pfunc_code = this->backend()->function_code_expression(pfunc, unknown_loc); Bexpression* pfunc_call = @@ -909,43 +910,52 @@ return initfn; } -// 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. +// Given an expression, collect all the global variables defined in +// this package that it references. + +class Find_vars : public Traverse +{ + private: + // The list of variables we accumulate. + typedef Unordered_set(Named_object*) Vars; + + // A hash table we use to avoid looping. The index is a + // Named_object* or a Temporary_statement*. 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) + public: + Find_vars() : Traverse(traverse_expressions), - var_(var), seen_objects_(seen_objects), found_(false) + vars_(), seen_objects_() { } - // Whether the variable was found. - bool - found() const - { return this->found_; } + // An iterator through the variables found, after the traversal. + typedef Vars::const_iterator const_iterator; + + const_iterator + begin() const + { return this->vars_.begin(); } + + const_iterator + end() const + { return this->vars_.end(); } 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_; + // Accumulated variables. + Vars vars_; + // Objects we have already seen. + Seen_objects seen_objects_; }; -// See if EXPR refers to VAR, looking through function calls and -// variable initializations. +// Collect global variables referenced by EXPR. Look through function +// calls and variable initializations. int -Find_var::expression(Expression** pexpr) +Find_vars::expression(Expression** pexpr) { Expression* e = *pexpr; @@ -953,26 +963,29 @@ if (ve != NULL) { Named_object* v = ve->named_object(); - if (v == this->var_) + if (!v->is_variable() || v->package() != NULL) { - this->found_ = true; - return TRAVERSE_EXIT; + // This is a result parameter or a variable defined in a + // different package. Either way we don't care about it. + return TRAVERSE_CONTINUE; } - if (v->is_variable() && v->package() == NULL) + std::pair<Seen_objects::iterator, bool> ins = + this->seen_objects_.insert(v); + if (!ins.second) { - Expression* init = v->var_value()->init(); - if (init != NULL) - { - std::pair<Seen_objects::iterator, bool> 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've seen this variable before. + return TRAVERSE_CONTINUE; + } + + if (v->var_value()->is_global()) + this->vars_.insert(v); + + Expression* init = v->var_value()->init(); + if (init != NULL) + { + if (Expression::traverse(&init, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; } } @@ -987,7 +1000,7 @@ if (f->is_function() && f->package() == NULL) { std::pair<Seen_objects::iterator, bool> ins = - this->seen_objects_->insert(f); + this->seen_objects_.insert(f); if (ins.second) { // This is the first time we have seen this name. @@ -1005,7 +1018,7 @@ if (init != NULL) { std::pair<Seen_objects::iterator, bool> ins = - this->seen_objects_->insert(ts); + this->seen_objects_.insert(ts); if (ins.second) { // This is the first time we have seen this temporary @@ -1025,22 +1038,28 @@ 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); + Find_vars find_vars; if (expr != NULL) - Expression::traverse(&expr, &find_var); + Expression::traverse(&expr, &find_vars); if (preinit != NULL) - preinit->traverse(&find_var); + preinit->traverse(&find_vars); if (dep != NULL) { Expression* init = dep->var_value()->init(); if (init != NULL) - Expression::traverse(&init, &find_var); + Expression::traverse(&init, &find_vars); if (dep->var_value()->has_pre_init()) - dep->var_value()->preinit()->traverse(&find_var); - } - - return find_var.found(); + dep->var_value()->preinit()->traverse(&find_vars); + } + + for (Find_vars::const_iterator p = find_vars.begin(); + p != find_vars.end(); + ++p) + { + if (*p == var) + return true; + } + return false; } // Sort variable initializations. If the initialization expression @@ -1051,11 +1070,11 @@ { public: Var_init() - : var_(NULL), init_(NULL), dep_count_(0) + : var_(NULL), init_(NULL), refs_(NULL), dep_count_(0) { } Var_init(Named_object* var, Bstatement* init) - : var_(var), init_(init), dep_count_(0) + : var_(var), init_(init), refs_(NULL), dep_count_(0) { } // Return the variable. @@ -1068,6 +1087,19 @@ init() const { return this->init_; } + // Add a reference. + void + add_ref(Named_object* var); + + // The variables which this variable's initializers refer to. + const std::vector<Named_object*>* + refs() + { return this->refs_; } + + // Clear the references, if any. + void + clear_refs(); + // Return the number of remaining dependencies. size_t dep_count() const @@ -1086,14 +1118,38 @@ private: // The variable being initialized. Named_object* var_; - // The initialization statement. + // The backend initialization statement. Bstatement* init_; + // Variables this refers to. + std::vector<Named_object*>* refs_; // The number of initializations this is dependent on. A variable // initialization should not be emitted if any of its dependencies // have not yet been resolved. size_t dep_count_; }; +// Add a reference. + +void +Var_init::add_ref(Named_object* var) +{ + if (this->refs_ == NULL) + this->refs_ = new std::vector<Named_object*>; + this->refs_->push_back(var); +} + +// Clear the references, if any. + +void +Var_init::clear_refs() +{ + if (this->refs_ != NULL) + { + delete this->refs_; + this->refs_ = NULL; + } +} + // For comparing Var_init keys in a map. inline bool @@ -1113,63 +1169,106 @@ if (var_inits->empty()) return; - typedef std::pair<Named_object*, Named_object*> No_no; - typedef std::map<No_no, bool> Cache; - Cache cache; + std::map<Named_object*, Var_init*> var_to_init; // A mapping from a variable initialization to a set of // variable initializations that depend on it. typedef std::map<Var_init, std::set<Var_init*> > Init_deps; Init_deps init_deps; bool init_loop = false; - for (Var_inits::iterator p1 = var_inits->begin(); - p1 != var_inits->end(); - ++p1) - { - Named_object* var = p1->var(); + + // Compute all variable references. + for (Var_inits::iterator pvar = var_inits->begin(); + pvar != var_inits->end(); + ++pvar) + { + Named_object* var = pvar->var(); + var_to_init[var] = &*pvar; + + Find_vars find_vars; Expression* init = var->var_value()->init(); - Block* preinit = var->var_value()->preinit(); + if (init != NULL) + Expression::traverse(&init, &find_vars); + if (var->var_value()->has_pre_init()) + var->var_value()->preinit()->traverse(&find_vars); Named_object* dep = gogo->var_depends_on(var->var_value()); - - // Start walking through the list to see which variables VAR - // needs to wait for. - for (Var_inits::iterator p2 = var_inits->begin(); - p2 != var_inits->end(); - ++p2) + if (dep != NULL) + { + Expression* dinit = dep->var_value()->init(); + if (dinit != NULL) + Expression::traverse(&dinit, &find_vars); + if (dep->var_value()->has_pre_init()) + dep->var_value()->preinit()->traverse(&find_vars); + } + for (Find_vars::const_iterator p = find_vars.begin(); + p != find_vars.end(); + ++p) + pvar->add_ref(*p); + } + + // Add dependencies to init_deps, and check for cycles. + for (Var_inits::iterator pvar = var_inits->begin(); + pvar != var_inits->end(); + ++pvar) + { + Named_object* var = pvar->var(); + + const std::vector<Named_object*>* refs = pvar->refs(); + if (refs == NULL) + continue; + for (std::vector<Named_object*>::const_iterator pdep = refs->begin(); + pdep != refs->end(); + ++pdep) { - if (var == p2->var()) + Named_object* dep = *pdep; + if (var == dep) + { + // This is a reference from a variable to itself, which + // may indicate a loop. We only report an error if + // there is an initializer and there is no dependency. + // When there is no initializer, it means that the + // preinitializer sets the variable, which will appear + // to be a loop here. + if (var->var_value()->init() != NULL + && gogo->var_depends_on(var->var_value()) == NULL) + go_error_at(var->location(), + ("initialization expression for %qs " + "depends upon itself"), + var->message_name().c_str()); + + continue; + } + + Var_init* dep_init = var_to_init[dep]; + if (dep_init == NULL) + { + // This is a dependency on some variable that doesn't + // have an initializer, so for purposes of + // initialization ordering this is irrelevant. + continue; + } + + init_deps[*dep_init].insert(&(*pvar)); + pvar->add_dependency(); + + // Check for cycles. + const std::vector<Named_object*>* deprefs = dep_init->refs(); + if (deprefs == NULL) continue; - - Named_object* p2var = p2->var(); - No_no key(var, p2var); - std::pair<Cache::iterator, bool> ins = - cache.insert(std::make_pair(key, false)); - if (ins.second) - ins.first->second = expression_requires(init, preinit, dep, p2var); - if (ins.first->second) + for (std::vector<Named_object*>::const_iterator pdepdep = + deprefs->begin(); + pdepdep != deprefs->end(); + ++pdepdep) { - // VAR depends on P2VAR. - init_deps[*p2].insert(&(*p1)); - p1->add_dependency(); - - // 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) + if (*pdepdep == var) { go_error_at(var->location(), ("initialization expressions for %qs and " "%qs depend upon each other"), var->message_name().c_str(), - p2var->message_name().c_str()); - go_inform(p2->var()->location(), "%qs defined here", - p2var->message_name().c_str()); + dep->message_name().c_str()); + go_inform(dep->location(), "%qs defined here", + dep->message_name().c_str()); init_loop = true; break; } @@ -1177,6 +1276,12 @@ } } + var_to_init.clear(); + for (Var_inits::iterator pvar = var_inits->begin(); + pvar != var_inits->end(); + ++pvar) + pvar->clear_refs(); + // If there are no dependencies then the declaration order is sorted. if (!init_deps.empty() && !init_loop) { @@ -1213,14 +1318,6 @@ var_inits->swap(ready); go_assert(init_deps.empty()); } - - // VAR_INITS is in the correct order. For each VAR in VAR_INITS, - // check for a loop of VAR on itself. - // interpret as a loop. - for (Var_inits::const_iterator p = var_inits->begin(); - p != var_inits->end(); - ++p) - gogo->check_self_dep(p->var()); } // Give an error if the initialization expression for VAR depends on @@ -1394,7 +1491,7 @@ { Location loc = var->location(); Bexpression* var_expr = - this->backend()->var_expression(bvar, VE_lvalue, loc); + this->backend()->var_expression(bvar, loc); var_init_stmt = this->backend()->assignment_statement(init_bfn, var_expr, var_binit, loc); @@ -1438,7 +1535,8 @@ // Avoid putting runtime.gcRoots itself on the list. if (this->compiling_runtime() && this->package_name() == "runtime" - && Gogo::unpack_hidden_name(no->name()) == "gcRoots") + && (Gogo::unpack_hidden_name(no->name()) == "gcRoots" + || Gogo::unpack_hidden_name(no->name()) == "gcRootsIndex")) ; else var_gc.push_back(no); @@ -1757,7 +1855,7 @@ else { // Invent a name for a nested function. - nested_name = this->nested_function_name(); + nested_name = this->nested_function_name(enclosing); pname = &nested_name; } @@ -2750,6 +2848,14 @@ return TRAVERSE_EXIT; *pexpr = enew; } + + // Lower the type of this expression before the parent looks at it, + // in case the type contains an array that has expressions in its + // length. Skip an Unknown_expression, as at this point that means + // a composite literal key that does not have a type. + if ((*pexpr)->unknown_expression() == NULL) + Type::traverse((*pexpr)->type(), this); + return TRAVERSE_SKIP_COMPONENTS; } @@ -3327,210 +3433,6 @@ 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. @@ -3584,6 +3486,18 @@ int Find_eval_ordering::expression(Expression** expression_pointer) { + Binary_expression* binexp = (*expression_pointer)->binary_expression(); + if (binexp != NULL + && (binexp->op() == OPERATOR_ANDAND || binexp->op() == OPERATOR_OROR)) + { + // Shortcut expressions may potentially have side effects which need + // to be ordered, so add them to the list. + // We don't order its subexpressions here since they may be evaluated + // conditionally. This is handled in remove_shortcuts. + this->exprs_.push_back(expression_pointer); + return TRAVERSE_SKIP_COMPONENTS; + } + // We have to look at subexpressions before this one. if ((*expression_pointer)->traverse_subexpressions(this) == TRAVERSE_EXIT) return TRAVERSE_EXIT; @@ -3820,6 +3734,215 @@ this->traverse(&order_eval); } +// 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. + // LEFT and RIGHT were skipped in the top level + // Gogo::order_evaluations. We need to order their + // components first. + Order_eval order_eval(this->gogo_); + retblock->traverse(&order_eval); + 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); +} + // Traversal to flatten parse tree after order of evaluation rules are applied. class Flatten : public Traverse @@ -3875,6 +3998,29 @@ return TRAVERSE_CONTINUE; } + if (!no->var_value()->is_parameter() + && !no->var_value()->is_receiver() + && !no->var_value()->is_closure() + && no->var_value()->is_non_escaping_address_taken() + && !no->var_value()->is_in_heap() + && no->var_value()->toplevel_decl() == NULL) + { + // Local variable that has address taken but not escape. + // It needs to be live beyond its lexical scope. So we + // create a top-level declaration for it. + // No need to do it if it is already in the top level. + Block* top_block = function_->func_value()->block(); + if (top_block->bindings()->lookup_local(no->name()) != no) + { + Variable* var = no->var_value(); + Temporary_statement* ts = + Statement::make_temporary(var->type(), NULL, var->location()); + ts->set_is_address_taken(); + top_block->add_statement_at_front(ts); + var->set_toplevel_decl(ts); + } + } + go_assert(!no->var_value()->has_pre_init()); return TRAVERSE_SKIP_COMPONENTS; @@ -4609,7 +4755,9 @@ if (no->is_type() && no->type_value()->struct_type() != NULL) types.push_back(no); - if (no->is_const() && no->const_value()->type()->integer_type() != NULL) + if (no->is_const() + && no->const_value()->type()->integer_type() != NULL + && !no->const_value()->is_sink()) { Numeric_constant nc; unsigned long val; @@ -4797,9 +4945,9 @@ : 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), - pragmas_(0), is_sink_(false), results_are_named_(false), - is_unnamed_type_stub_method_(false), calls_recover_(false), - is_recover_thunk_(false), has_recover_thunk_(false), + pragmas_(0), nested_functions_(0), is_sink_(false), + results_are_named_(false), is_unnamed_type_stub_method_(false), + calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false), calls_defer_retaddr_(false), is_type_specific_function_(false), in_unique_section_(false) { @@ -4924,7 +5072,7 @@ // 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, + st->push_field(Struct_field(Typed_identifier(".f", voidptr_type, this->location_))); unsigned int index = 1; @@ -5155,17 +5303,24 @@ void Function::export_func(Export* exp, const std::string& name) const { - Function::export_func_with_type(exp, name, this->type_); + Function::export_func_with_type(exp, name, this->type_, + this->is_method() && this->nointerface()); } // Export a function with a type. void Function::export_func_with_type(Export* exp, const std::string& name, - const Function_type* fntype) + const Function_type* fntype, bool nointerface) { exp->write_c_string("func "); + if (nointerface) + { + go_assert(fntype->is_method()); + exp->write_c_string("/*nointerface*/ "); + } + if (fntype->is_method()) { exp->write_c_string("("); @@ -5236,7 +5391,7 @@ exp->write_c_string(")"); } } - exp->write_c_string(";\n"); + exp->write_c_string("\n"); } // Import a function. @@ -5246,10 +5401,21 @@ Typed_identifier** preceiver, Typed_identifier_list** pparameters, Typed_identifier_list** presults, - bool* is_varargs) + bool* is_varargs, + bool* nointerface) { imp->require_c_string("func "); + *nointerface = false; + if (imp->match_c_string("/*")) + { + imp->require_c_string("/*nointerface*/ "); + *nointerface = true; + + // Only a method can be nointerface. + go_assert(imp->peek_char() == '('); + } + *preceiver = NULL; if (imp->peek_char() == '(') { @@ -5332,7 +5498,8 @@ imp->require_c_string(")"); } } - imp->require_c_string(";\n"); + imp->require_semicolon_if_old_version(); + imp->require_c_string("\n"); *presults = results; } @@ -5434,8 +5601,8 @@ 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()); + disable_split_stack, false, + in_unique_section, this->location()); } return this->fndecl_; } @@ -5447,6 +5614,8 @@ { if (this->fndecl_ == NULL) { + bool does_not_return = false; + // Let Go code use an asm declaration to pick up a builtin // function. if (!this->asm_name_.empty()) @@ -5458,6 +5627,10 @@ this->fndecl_ = builtin_decl; return this->fndecl_; } + + if (this->asm_name_ == "runtime.gopanic" + || this->asm_name_ == "__go_runtime_error") + does_not_return = true; } std::string asm_name; @@ -5474,8 +5647,8 @@ 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()); + true, true, true, false, does_not_return, + false, this->location()); } return this->fndecl_; @@ -5538,6 +5711,7 @@ // initial values. std::vector<Bvariable*> vars; std::vector<Bexpression*> var_inits; + std::vector<Statement*> var_decls_stmts; for (Bindings::const_definitions_iterator p = this->block_->bindings()->begin_definitions(); p != this->block_->bindings()->end_definitions(); @@ -5567,7 +5741,10 @@ vars.push_back(bvar); Expression* parm_ref = Expression::make_var_reference(parm_no, loc); - parm_ref = Expression::make_unary(OPERATOR_MULT, parm_ref, loc); + parm_ref = + Expression::make_dereference(parm_ref, + Expression::NIL_CHECK_NEEDED, + loc); if ((*p)->var_value()->is_in_heap()) parm_ref = Expression::make_heap_expression(parm_ref, loc); var_inits.push_back(parm_ref->get_backend(&context)); @@ -5609,6 +5786,24 @@ vars.push_back(bvar); var_inits.push_back(init); } + else if (this->defer_stack_ != NULL + && (*p)->is_variable() + && (*p)->var_value()->is_non_escaping_address_taken() + && !(*p)->var_value()->is_in_heap()) + { + // Local variable captured by deferred closure needs to be live + // until the end of the function. We create a top-level + // declaration for it. + // TODO: we don't need to do this if the variable is not captured + // by the defer closure. There is no easy way to check it here, + // so we do this for all address-taken variables for now. + Variable* var = (*p)->var_value(); + Temporary_statement* ts = + Statement::make_temporary(var->type(), NULL, var->location()); + ts->set_is_address_taken(); + var->set_toplevel_decl(ts); + var_decls_stmts.push_back(ts); + } } if (!gogo->backend()->function_set_parameters(this->fndecl_, param_vars)) { @@ -5628,7 +5823,7 @@ { // Declare variables if necessary. Bblock* var_decls = NULL; - + std::vector<Bstatement*> var_decls_bstmt_list; Bstatement* defer_init = NULL; if (!vars.empty() || this->defer_stack_ != NULL) { @@ -5642,6 +5837,14 @@ Translate_context dcontext(gogo, named_function, this->block_, var_decls); defer_init = this->defer_stack_->get_backend(&dcontext); + var_decls_bstmt_list.push_back(defer_init); + for (std::vector<Statement*>::iterator p = var_decls_stmts.begin(); + p != var_decls_stmts.end(); + ++p) + { + Bstatement* bstmt = (*p)->get_backend(&dcontext); + var_decls_bstmt_list.push_back(bstmt); + } } } @@ -5660,8 +5863,6 @@ var_inits[i]); init.push_back(init_stmt); } - if (defer_init != NULL) - init.push_back(defer_init); Bstatement* var_init = gogo->backend()->statement_list(init); // Initialize all variables before executing this code block. @@ -5689,8 +5890,8 @@ // we built one. if (var_decls != NULL) { - std::vector<Bstatement*> code_stmt_list(1, code_stmt); - gogo->backend()->block_add_statements(var_decls, code_stmt_list); + var_decls_bstmt_list.push_back(code_stmt); + gogo->backend()->block_add_statements(var_decls, var_decls_bstmt_list); code_stmt = gogo->backend()->block_statement(var_decls); } @@ -5798,8 +5999,7 @@ { Named_object* no = (*this->results_)[i]; Bvariable* bvar = no->get_backend_variable(gogo, named_function); - Bexpression* val = gogo->backend()->var_expression(bvar, VE_rvalue, - location); + Bexpression* val = gogo->backend()->var_expression(bvar, location); if (no->result_var_value()->is_in_heap()) { Btype* bt = no->result_var_value()->type()->get_backend(gogo); @@ -6132,14 +6332,46 @@ } go_assert(p != block->bindings()->end_definitions()); - std::string n = (*p)->message_name(); - go_error_at(loc, "goto jumps over declaration of %qs", n.c_str()); - go_inform((*p)->location(), "%qs defined here", n.c_str()); + for (; p != block->bindings()->end_definitions(); ++p) + { + if ((*p)->is_variable()) + { + std::string n = (*p)->message_name(); + go_error_at(loc, "goto jumps over declaration of %qs", n.c_str()); + go_inform((*p)->location(), "%qs defined here", n.c_str()); + } + } } } // Class Function_declaration. +// Whether this declares a method. + +bool +Function_declaration::is_method() const +{ + return this->fntype_->is_method(); +} + +// Whether this method should not be included in the type descriptor. + +bool +Function_declaration::nointerface() const +{ + go_assert(this->is_method()); + return (this->pragmas_ & GOPRAGMA_NOINTERFACE) != 0; +} + +// Record that this method should not be included in the type +// descriptor. + +void +Function_declaration::set_nointerface() +{ + this->pragmas_ |= GOPRAGMA_NOINTERFACE; +} + // Return the function descriptor. Expression* @@ -6165,7 +6397,8 @@ 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), escapes_(true) + in_unique_section_(false), escapes_(true), + toplevel_decl_(NULL) { go_assert(type != NULL || init != NULL); go_assert(!is_parameter || init == NULL); @@ -6259,7 +6492,8 @@ // If an interface conversion is needed, we need a temporary // variable. if (this->type_ != NULL - && !Type::are_identical(this->type_, this->init_->type(), false, + && !Type::are_identical(this->type_, this->init_->type(), + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, NULL) && this->init_->type()->interface_type() != NULL && !this->init_->is_variable()) @@ -6632,7 +6866,7 @@ Expression::make_cast(this->type(), this->init_, loc); Bexpression* val = val_expr->get_backend(&context); Bexpression* var_ref = - gogo->backend()->var_expression(var_decl, VE_lvalue, loc); + gogo->backend()->var_expression(var_decl, loc); decl_init = gogo->backend()->assignment_statement(bfunction, var_ref, val, loc); } @@ -6653,7 +6887,7 @@ exp->write_string(name); exp->write_c_string(" "); exp->write_type(this->type()); - exp->write_c_string(";\n"); + exp->write_c_string("\n"); } // Import a variable. @@ -6665,7 +6899,8 @@ *pname = imp->read_identifier(); imp->require_c_string(" "); *ptype = imp->read_type(); - imp->require_c_string(";\n"); + imp->require_semicolon_if_old_version(); + imp->require_c_string("\n"); } // Convert a variable to the backend representation. @@ -6742,9 +6977,19 @@ is_address_taken, this->location_); else - bvar = backend->local_variable(bfunction, n, btype, - is_address_taken, - this->location_); + { + Bvariable* bvar_decl = NULL; + if (this->toplevel_decl_ != NULL) + { + Translate_context context(gogo, NULL, NULL, NULL); + bvar_decl = this->toplevel_decl_->temporary_statement() + ->get_backend_variable(&context); + } + bvar = backend->local_variable(bfunction, n, btype, + bvar_decl, + is_address_taken, + this->location_); + } } this->backend_ = bvar; } @@ -6776,7 +7021,7 @@ 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, + NULL, is_address_taken, this->location_); } } @@ -6785,6 +7030,16 @@ // Class Named_constant. +// Set the type of a named constant. This is only used to set the +// type to an error type. + +void +Named_constant::set_type(Type* t) +{ + go_assert(this->type_ == NULL || t->is_error_type()); + this->type_ = t; +} + // Traverse the initializer expression. int @@ -6837,7 +7092,7 @@ } exp->write_c_string("= "); this->expr()->export_expression(exp); - exp->write_c_string(";\n"); + exp->write_c_string("\n"); } // Import a constant. @@ -6858,7 +7113,8 @@ } imp->require_c_string("= "); *pexpr = Expression::import_expression(imp); - imp->require_c_string(";\n"); + imp->require_semicolon_if_old_version(); + imp->require_c_string("\n"); } // Get the backend representation. @@ -7255,8 +7511,8 @@ break; case NAMED_OBJECT_TYPE: - this->type_value()->export_named_type(exp, this->name_); - break; + // Types are handled by export::write_types. + go_unreachable(); case NAMED_OBJECT_TYPE_DECLARATION: go_error_at(this->type_declaration_value()->location(), @@ -7370,7 +7626,7 @@ case NAMED_OBJECT_TYPE: { Named_type* named_type = this->u_.type_value; - if (!Gogo::is_erroneous_name(this->name_)) + if (!Gogo::is_erroneous_name(this->name_) && !named_type->is_alias()) type_decls.push_back(named_type->get_backend(gogo)); // We need to produce a type descriptor for every named @@ -7624,33 +7880,29 @@ go_unreachable(); case Named_object::NAMED_OBJECT_FUNC: - if (new_object->is_function_declaration()) - { - if (!new_object->func_declaration_value()->asm_name().empty()) - go_error_at(Linemap::unknown_location(), - ("sorry, not implemented: " - "__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: { - if (new_object->is_function()) + // We declare the hash and equality functions before defining + // them, because we sometimes see that we need the declaration + // while we are in the middle of a different function. We + // declare the main function before the user defines it, to + // give better error messages. + if (new_object->is_function() + && ((Linemap::is_predeclared_location(old_object->location()) + && Linemap::is_predeclared_location(new_object->location())) + || (Gogo::unpack_hidden_name(old_object->name()) == "main" + && Linemap::is_unknown_location(old_object->location())))) { Function_type* old_type = old_object->func_declaration_value()->type(); 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()) - go_error_at(Linemap::unknown_location(), - ("sorry, not implemented: " - "__asm__ for function definitions")); + Function_declaration* fd = + old_object->func_declaration_value(); + go_assert(fd->asm_name().empty()); old_object->set_function_value(new_object->func_value()); this->named_objects_.push_back(old_object); return old_object; @@ -7672,8 +7924,10 @@ old_object->set_is_redefinition(); new_object->set_is_redefinition(); - go_inform(old_object->location(), "previous definition of %qs was here", - n.c_str()); + if (!Linemap::is_unknown_location(old_object->location()) + && !Linemap::is_predeclared_location(old_object->location())) + go_inform(old_object->location(), "previous definition of %qs was here", + n.c_str()); return old_object; } @@ -7869,6 +8123,21 @@ } } + // Traverse function declarations when needed. + if ((traverse_mask & Traverse::traverse_func_declarations) != 0) + { + for (Bindings::const_declarations_iterator p = this->begin_declarations(); + p != this->end_declarations(); + ++p) + { + if (p->second->is_function_declaration()) + { + if (traverse->function_declaration(p->second) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + } + return TRAVERSE_CONTINUE; } @@ -8107,8 +8376,16 @@ // 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} } + // + // type I interface { F() interface{I} } + // + // Similarly it is possible for array types to refer to themselves + // without a name, e.g. + // + // var x [uintptr(unsafe.Sizeof(&x))]byte + // if (type->classification() != Type::TYPE_NAMED + && type->classification() != Type::TYPE_ARRAY && type->classification() != Type::TYPE_INTERFACE) return false; if (this->types_seen_ == NULL) @@ -8178,11 +8455,20 @@ go_unreachable(); } +int +Traverse::function_declaration(Named_object*) +{ + go_unreachable(); +} + // Class Statement_inserter. void Statement_inserter::insert(Statement* s) { + if (this->statements_added_ != NULL) + this->statements_added_->insert(s); + if (this->block_ != NULL) { go_assert(this->pindex_ != NULL);