diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-08-29 22:06:19 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-08-29 22:06:19 +0000 |
commit | 1bbf7edba0f3518d91b6622761b634bda2ca8a29 (patch) | |
tree | 723b92c72a0ba175df1059b88a1244ec013d0891 /gcc/go | |
parent | 273113346e85423e88d1352c4acafe41e7dd90b5 (diff) | |
download | gcc-1bbf7edba0f3518d91b6622761b634bda2ca8a29.zip gcc-1bbf7edba0f3518d91b6622761b634bda2ca8a29.tar.gz gcc-1bbf7edba0f3518d91b6622761b634bda2ca8a29.tar.bz2 |
Lower calls to bound method expressions.
From-SVN: r178264
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 336 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 10 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 71 |
3 files changed, 197 insertions, 220 deletions
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index af29a30..8957acb 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3845,7 +3845,7 @@ Unsafe_type_conversion_expression::do_get_tree(Translate_context* context) go_assert((et->points_to() != NULL && et->points_to()->channel_type() != NULL) || et->is_nil_type()); - else if (t->is_unsafe_pointer_type()) + 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); @@ -3908,7 +3908,7 @@ class Unary_expression : public Expression public: Unary_expression(Operator op, Expression* expr, source_location location) : Expression(EXPRESSION_UNARY, location), - op_(op), escapes_(true), expr_(expr) + op_(op), escapes_(true), create_temp_(false), expr_(expr) { } // Return the operator. @@ -3929,6 +3929,15 @@ class Unary_expression : public Expression 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 UVAL, setting VAL. Return true if this // could be done, false if not. static bool @@ -4004,6 +4013,9 @@ class Unary_expression : public Expression // 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_; }; @@ -4428,7 +4440,10 @@ Unary_expression::do_check_types(Gogo*) case OPERATOR_AND: if (!this->expr_->is_addressable()) - this->report_error(_("invalid operand for unary %<&%>")); + { + if (!this->create_temp_) + this->report_error(_("invalid operand for unary %<&%>")); + } else this->expr_->address_taken(this->escapes_); break; @@ -4486,12 +4501,15 @@ Unary_expression::do_get_tree(Translate_context* context) return fold_build1_loc(loc, BIT_NOT_EXPR, TREE_TYPE(expr), expr); case OPERATOR_AND: - // 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(TREE_CODE(expr) != CONSTRUCTOR || TREE_CONSTANT(expr)); - go_assert(TREE_CODE(expr) != ADDR_EXPR); + 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(TREE_CODE(expr) != CONSTRUCTOR || TREE_CONSTANT(expr)); + go_assert(TREE_CODE(expr) != ADDR_EXPR); + } // Build a decl for a constant constructor. if (TREE_CODE(expr) == CONSTRUCTOR && TREE_CONSTANT(expr)) @@ -4510,6 +4528,22 @@ Unary_expression::do_get_tree(Translate_context* context) expr = decl; } + if (this->create_temp_ + && !TREE_ADDRESSABLE(TREE_TYPE(expr)) + && !DECL_P(expr) + && TREE_CODE(expr) != INDIRECT_REF + && TREE_CODE(expr) != COMPONENT_REF) + { + tree tmp = create_tmp_var(TREE_TYPE(expr), get_name(expr)); + DECL_IGNORED_P(tmp) = 1; + DECL_INITIAL(tmp) = expr; + TREE_ADDRESSABLE(tmp) = 1; + return build2_loc(loc, COMPOUND_EXPR, + build_pointer_type(TREE_TYPE(expr)), + build1_loc(loc, DECL_EXPR, void_type_node, tmp), + build_fold_addr_expr_loc(loc, tmp)); + } + return build_fold_addr_expr_loc(loc, expr); case OPERATOR_MULT: @@ -7223,7 +7257,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, this->set_is_error(); return this; } - return this->lower_varargs(gogo, function, inserter, slice_type, 2); + this->lower_varargs(gogo, function, inserter, slice_type, 2); } return this; @@ -8805,12 +8839,14 @@ Expression* Call_expression::do_lower(Gogo* gogo, Named_object* function, Statement_inserter* inserter, int) { + source_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(), - this->location()); + loc); // Recognize a call to a builtin function. Func_expression* fne = this->fn_->func_expression(); @@ -8818,7 +8854,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, && fne->named_object()->is_function_declaration() && fne->named_object()->func_declaration_value()->type()->is_builtin()) return new Builtin_call_expression(gogo, this->fn_, this->args_, - this->is_varargs_, this->location()); + this->is_varargs_, loc); // Handle an argument which is a call to a function which returns // multiple results. @@ -8862,8 +8898,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, ++p) { Temporary_statement* temp = Statement::make_temporary(p->type(), - NULL, - p->location()); + NULL, loc); inserter->insert(temp); temps->push_back(temp); } @@ -8879,8 +8914,58 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, const Typed_identifier_list* parameters = fntype->parameters(); go_assert(parameters != NULL && !parameters->empty()); Type* varargs_type = parameters->back().type(); - return this->lower_varargs(gogo, function, inserter, varargs_type, - parameters->size()); + 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* method = bme->method(); + 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<Unary_expression*>(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(method, NULL, + bme->location()); } return this; @@ -8893,13 +8978,13 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, // calling; the last of these parameters will be the varargs // parameter. -Expression* +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 this; + return; source_location loc = this->location(); @@ -8910,7 +8995,7 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, if (arg_count < param_count - 1) { // Not enough arguments; will be caught in check_types. - return this; + return; } Expression_list* old_args = this->args_; @@ -8942,7 +9027,7 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, else if (this->is_varargs_) { this->report_error(_("too many arguments")); - return this; + return; } else { @@ -8960,6 +9045,7 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, } Expression* val = Expression::make_slice_composite_literal(varargs_type, vals, loc); + gogo->lower_expression(function, inserter, &val); new_args->push_back(val); } } @@ -8973,12 +9059,6 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, // Builtin_call_expression which refer to them. FIXME. this->args_ = new_args; this->varargs_are_lowered_ = true; - - // Lower all the new subexpressions. - Expression* ret = this; - gogo->lower_expression(function, inserter, &ret); - go_assert(ret == this); - return ret; } // Get the function type. This can return NULL in error cases. @@ -9102,10 +9182,28 @@ Call_expression::do_determine_type(const Type_context*) 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); @@ -9172,35 +9270,28 @@ Call_expression::do_check_types(Gogo*) return; } - if (fntype->is_method()) + bool is_method = fntype->is_method(); + if (is_method) { - // We don't support pointers to methods, so the function has to - // be a bound method expression. - Bound_method_expression* bme = this->fn_->bound_method_expression(); - if (bme == NULL) - { - this->report_error(_("method call without object")); - return; - } - Type* first_arg_type = bme->first_argument()->type(); - if (first_arg_type->points_to() == NULL) + 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)) { - // When passing a value, we need to check that we are - // permitted to copy it. The language permits copying - // hidden fields for a method receiver. - std::string reason; - if (!Type::are_assignable_hidden_ok(fntype->receiver()->type(), - first_arg_type, &reason)) + if (reason.empty()) + this->report_error(_("incompatible type for receiver")); + else { - 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(); - } + error_at(this->location(), + "incompatible type for receiver (%s)", + reason.c_str()); + this->set_is_error(); } } } @@ -9215,25 +9306,30 @@ Call_expression::do_check_types(Gogo*) this->report_error(_("not enough arguments")); } else if (parameters == NULL) - this->report_error(_("too many arguments")); + { + if (!is_method || this->args_->size() > 1) + this->report_error(_("too many arguments")); + } else { int i = 0; - Typed_identifier_list::const_iterator pt = parameters->begin(); - for (Expression_list::const_iterator pa = this->args_->begin(); - pa != this->args_->end(); - ++pa, ++pt, ++i) - { - if (pt == parameters->end()) + 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(_("too many arguments")); + this->report_error(_("not enough arguments")); return; } this->check_argument_type(i + 1, pt->type(), (*pa)->type(), (*pa)->location(), false); } - if (pt != parameters->end()) - this->report_error(_("not enough arguments")); + if (pa != this->args_->end()) + this->report_error(_("too many arguments")); } } @@ -9247,83 +9343,6 @@ Call_expression::do_must_eval_in_order() const return this->result_count() > 0; } -// Get the function and the first argument to use when calling a bound -// method. - -tree -Call_expression::bound_method_function(Translate_context* context, - Bound_method_expression* bound_method, - tree* first_arg_ptr) -{ - Gogo* gogo = context->gogo(); - source_location loc = this->location(); - - Expression* first_argument = bound_method->first_argument(); - tree first_arg = first_argument->get_tree(context); - if (first_arg == error_mark_node) - return error_mark_node; - - // We always pass a pointer to the first argument when calling a - // method. - if (first_argument->type()->points_to() == NULL) - { - tree pointer_to_arg_type = build_pointer_type(TREE_TYPE(first_arg)); - if (TREE_ADDRESSABLE(TREE_TYPE(first_arg)) - || DECL_P(first_arg) - || TREE_CODE(first_arg) == INDIRECT_REF - || TREE_CODE(first_arg) == COMPONENT_REF) - { - first_arg = build_fold_addr_expr_loc(loc, first_arg); - if (DECL_P(first_arg)) - TREE_ADDRESSABLE(first_arg) = 1; - } - else - { - tree tmp = create_tmp_var(TREE_TYPE(first_arg), - get_name(first_arg)); - DECL_IGNORED_P(tmp) = 0; - DECL_INITIAL(tmp) = first_arg; - first_arg = build2_loc(loc, COMPOUND_EXPR, pointer_to_arg_type, - build1_loc(loc, DECL_EXPR, void_type_node, - tmp), - build_fold_addr_expr_loc(loc, tmp)); - TREE_ADDRESSABLE(tmp) = 1; - } - if (first_arg == error_mark_node) - return error_mark_node; - } - - Type* fatype = bound_method->first_argument_type(); - if (fatype != NULL) - { - if (fatype->points_to() == NULL) - fatype = Type::make_pointer_type(fatype); - Btype* bfatype = fatype->get_backend(gogo); - first_arg = fold_convert_loc(loc, type_to_tree(bfatype), first_arg); - if (first_arg == error_mark_node - || TREE_TYPE(first_arg) == error_mark_node) - return error_mark_node; - } - - *first_arg_ptr = first_arg; - - Named_object* method = bound_method->method(); - tree id = method->get_id(gogo); - if (id == error_mark_node) - return error_mark_node; - - tree fndecl; - if (method->is_function()) - fndecl = method->func_value()->get_or_make_decl(gogo, method, id); - else if (method->is_function_declaration()) - fndecl = method->func_declaration_value()->get_or_make_decl(gogo, method, - id); - else - go_unreachable(); - - return build_fold_addr_expr_loc(loc, fndecl); -} - // Get the function and the first argument to use when calling an // interface method. @@ -9363,35 +9382,46 @@ Call_expression::do_get_tree(Translate_context* context) source_location location = this->location(); Func_expression* func = this->fn_->func_expression(); - Bound_method_expression* bound_method = this->fn_->bound_method_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_method = bound_method != NULL || interface_method != NULL; - go_assert(!fntype->is_method() || is_method); + const bool is_interface_method = interface_method != NULL; int nargs; tree* args; if (this->args_ == NULL || this->args_->empty()) { - nargs = is_method ? 1 : 0; + 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(); - go_assert(params != NULL); nargs = this->args_->size(); - int i = is_method ? 1 : 0; + 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; - for (pe = this->args_->begin(); - pe != this->args_->end(); - ++pe, ++pp, ++i) + 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); @@ -9420,14 +9450,10 @@ Call_expression::do_get_tree(Translate_context* context) tree fn; if (has_closure) fn = func->get_tree_without_closure(gogo); - else if (!is_method) + else if (!is_interface_method) fn = this->fn_->get_tree(context); - else if (bound_method != NULL) - fn = this->bound_method_function(context, bound_method, &args[0]); - else if (interface_method != NULL) - fn = this->interface_method_function(context, interface_method, &args[0]); else - go_unreachable(); + fn = this->interface_method_function(context, interface_method, &args[0]); if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node) { diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 530ea4e..bb4f23e 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -1244,6 +1244,11 @@ class Call_expression : public Expression is_varargs() const { return this->is_varargs_; } + // Note that varargs have already been lowered. + void + set_varargs_are_lowered() + { this->varargs_are_lowered_ = true; } + // Whether this call is being deferred. bool is_deferred() const @@ -1307,7 +1312,7 @@ class Call_expression : public Expression { this->args_ = args; } // Let a builtin expression lower varargs. - Expression* + void lower_varargs(Gogo*, Named_object* function, Statement_inserter* inserter, Type* varargs_type, size_t param_count); @@ -1324,9 +1329,6 @@ class Call_expression : public Expression check_argument_type(int, const Type*, const Type*, source_location, bool); tree - bound_method_function(Translate_context*, Bound_method_expression*, tree*); - - tree interface_method_function(Translate_context*, Interface_field_reference_expression*, tree*); diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index c2caaa4..e646227 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -1858,8 +1858,7 @@ Thunk_statement::is_simple(Function_type* fntype) const // If this calls something which is not a simple function, then we // need a thunk. Expression* fn = this->call_->call_expression()->fn(); - if (fn->bound_method_expression() != NULL - || fn->interface_field_reference_expression() != NULL) + if (fn->interface_field_reference_expression() != NULL) return false; return true; @@ -1914,14 +1913,6 @@ Thunk_statement::do_check_types(Gogo*) this->report_error("expected call expression"); return; } - Function_type* fntype = ce->get_function_type(); - if (fntype != NULL && fntype->is_method()) - { - Expression* fn = ce->fn(); - if (fn->bound_method_expression() == NULL - && fn->interface_field_reference_expression() == NULL) - this->report_error(_("no object for method call")); - } } // The Traverse class used to find and simplify thunk statements. @@ -2005,8 +1996,7 @@ Thunk_statement::is_constant_function() const Expression* fn = ce->fn(); if (fn->func_expression() != NULL) return fn->func_expression()->closure() == NULL; - if (fn->bound_method_expression() != NULL - || fn->interface_field_reference_expression() != NULL) + if (fn->interface_field_reference_expression() != NULL) return true; return false; } @@ -2048,7 +2038,6 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function, return false; Expression* fn = ce->fn(); - Bound_method_expression* bound_method = fn->bound_method_expression(); Interface_field_reference_expression* interface_method = fn->interface_field_reference_expression(); @@ -2071,30 +2060,6 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function, if (interface_method != NULL) vals->push_back(interface_method->expr()); - if (bound_method != NULL) - { - Expression* first_arg = bound_method->first_argument(); - - // We always pass a pointer when calling a method. - if (first_arg->type()->points_to() == NULL) - first_arg = Expression::make_unary(OPERATOR_AND, first_arg, location); - - // 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 = bound_method->first_argument_type(); - if (fatype != NULL) - { - if (fatype->points_to() == NULL) - fatype = Type::make_pointer_type(fatype); - Type* unsafe = Type::make_pointer_type(Type::make_void_type()); - first_arg = Expression::make_cast(unsafe, first_arg, location); - first_arg = Expression::make_cast(fatype, first_arg, location); - } - - vals->push_back(first_arg); - } - if (ce->args() != NULL) { for (Expression_list::const_iterator p = ce->args()->begin(); @@ -2186,19 +2151,6 @@ Thunk_statement::build_struct(Function_type* fntype) fields->push_back(Struct_field(tid)); } - // If this is a method call, pass down the expression we are - // calling. - if (fn->bound_method_expression() != NULL) - { - go_assert(fntype->is_method()); - Type* rtype = fntype->receiver()->type(); - // We always pass the receiver as a pointer. - if (rtype->points_to() == NULL) - rtype = Type::make_pointer_type(rtype); - Typed_identifier tid("receiver", rtype, 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()) @@ -2317,7 +2269,6 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) thunk_parameter = Expression::make_unary(OPERATOR_MULT, thunk_parameter, location); - Bound_method_expression* bound_method = ce->fn()->bound_method_expression(); Interface_field_reference_expression* interface_method = ce->fn()->interface_field_reference_expression(); @@ -2335,16 +2286,7 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) next_index = 1; } - if (bound_method != NULL) - { - go_assert(next_index == 0); - Expression* r = Expression::make_field_reference(thunk_parameter, 0, - location); - func_to_call = Expression::make_bound_method(r, bound_method->method(), - location); - next_index = 1; - } - else if (interface_method != NULL) + if (interface_method != NULL) { // The main program passes the interface object. go_assert(next_index == 0); @@ -2389,6 +2331,13 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) 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); gogo->add_statement(call_statement); |