@@ -6075,7 +6075,7 @@
{
public:
Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
- source_location location);
+ bool is_varargs, source_location location);
protected:
// This overrides Call_expression::do_lower.
@@ -6108,6 +6108,7 @@
{
return new Builtin_call_expression(this->gogo_, this->fn()->copy(),
this->args()->copy(),
+ this->is_varargs(),
this->location());
}
@@ -6130,6 +6131,7 @@
BUILTIN_INVALID,
// Predeclared builtin functions.
+ BUILTIN_APPEND,
BUILTIN_CAP,
BUILTIN_CLOSE,
BUILTIN_CLOSED,
@@ -6173,14 +6175,17 @@
Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
Expression* fn,
Expression_list* args,
+ bool is_varargs,
source_location location)
- : Call_expression(fn, args, false, location),
+ : Call_expression(fn, args, is_varargs, location),
gogo_(gogo), code_(BUILTIN_INVALID)
{
Func_expression* fnexp = this->fn()->func_expression();
gcc_assert(fnexp != NULL);
const std::string& name(fnexp->named_object()->name());
- if (name == "cap")
+ if (name == "append")
+ this->code_ = BUILTIN_APPEND;
+ else if (name == "cap")
this->code_ = BUILTIN_CAP;
else if (name == "close")
this->code_ = BUILTIN_CLOSE;
@@ -6277,7 +6282,7 @@
// specific expressions. We also convert to a constant if we can.
Expression*
-Builtin_call_expression::do_lower(Gogo*, Named_object* function, int)
+Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, int)
{
if (this->code_ == BUILTIN_NEW)
{
@@ -6394,6 +6399,22 @@
this->location());
}
}
+ else if (this->code_ == 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_open_array_type())
+ {
+ error_at(args->front()->location(),
+ _("argument 1 must be a slice"));
+ this->set_is_error();
+ return this;
+ }
+ return this->lower_varargs(gogo, function, slice_type, 2);
+ }
return this;
}
@@ -6768,6 +6789,14 @@
case BUILTIN_RECOVER:
return Type::make_interface_type(NULL, BUILTINS_LOCATION);
+ case BUILTIN_APPEND:
+ {
+ const Expression_list* args = this->args();
+ if (args == NULL || args->empty())
+ return Type::make_error_type();
+ return args->front()->type();
+ }
+
case BUILTIN_REAL:
case BUILTIN_IMAG:
{
@@ -7093,6 +7122,33 @@
}
break;
+ case BUILTIN_APPEND:
+ {
+ const Expression_list* args = this->args();
+ if (args == NULL || args->empty())
+ {
+ this->report_error(_("not enough arguments"));
+ break;
+ }
+ /* Lowering varargs should have left us with 2 arguments. */
+ gcc_assert(args->size() == 2);
+ std::string reason;
+ if (!Type::are_assignable(args->front()->type(), args->back()->type(),
+ &reason))
+ {
+ if (reason.empty())
+ this->report_error(_("arguments 1 and 2 have different types"));
+ else
+ {
+ error_at(this->location(),
+ "arguments 1 and 2 have different types (%s)",
+ reason.c_str());
+ this->set_is_error();
+ }
+ }
+ break;
+ }
+
case BUILTIN_REAL:
case BUILTIN_IMAG:
if (this->check_one_arg())
@@ -7559,6 +7615,36 @@
call, len);
}
+ case BUILTIN_APPEND:
+ {
+ const Expression_list* args = this->args();
+ gcc_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;
+
+ tree descriptor_tree = arg1->type()->type_descriptor_pointer(gogo);
+
+ // 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",
+ 3,
+ TREE_TYPE(arg1_tree),
+ TREE_TYPE(descriptor_tree),
+ descriptor_tree,
+ TREE_TYPE(arg1_tree),
+ arg1_tree,
+ TREE_TYPE(arg2_tree),
+ arg2_tree);
+ }
+
case BUILTIN_REAL:
case BUILTIN_IMAG:
{
@@ -7690,7 +7776,7 @@
&& 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->location());
+ this->is_varargs_, this->location());
// Handle an argument which is a call to a function which returns
// multiple results.
@@ -7720,28 +7806,41 @@
// Handle a call to a varargs function by packaging up the extra
// parameters.
- if (!this->varargs_are_lowered_
- && this->fn_->type()->function_type() != NULL
+ if (this->fn_->type()->function_type() != NULL
&& this->fn_->type()->function_type()->is_varargs())
- return this->lower_varargs(gogo, function);
+ {
+ Function_type* fntype = this->fn_->type()->function_type();
+ const Typed_identifier_list* parameters = fntype->parameters();
+ gcc_assert(parameters != NULL && !parameters->empty());
+ Type* varargs_type = parameters->back().type();
+ return this->lower_varargs(gogo, function, varargs_type,
+ parameters->size());
+ }
return this;
}
-// Lower a call to a varargs function.
+// 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.
Expression*
-Call_expression::lower_varargs(Gogo* gogo, Named_object* function)
-{
+Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
+ Type* varargs_type, size_t param_count)
+{
+ if (this->varargs_are_lowered_)
+ return this;
+
source_location loc = this->location();
- Function_type* fntype = this->fn_->type()->function_type();
- const Typed_identifier_list* parameters = fntype->parameters();
- gcc_assert(parameters != NULL && !parameters->empty());
- Type* varargs_type = parameters->back().type();
+ gcc_assert(param_count > 0);
+ gcc_assert(varargs_type->is_open_array_type());
size_t arg_count = this->args_ == NULL ? 0 : this->args_->size();
- if (arg_count < parameters->size() - 1)
+ if (arg_count < param_count - 1)
{
// Not enough arguments; will be caught in check_types.
return this;
@@ -7752,17 +7851,16 @@
bool push_empty_arg = false;
if (old_args == NULL || old_args->empty())
{
- gcc_assert(parameters->size() == 1);
+ gcc_assert(param_count == 1);
push_empty_arg = true;
}
else
{
- Typed_identifier_list::const_iterator pp = parameters->begin();
Expression_list::const_iterator pa;
int i = 1;
- for (pa = old_args->begin(); pa != old_args->end(); ++pa, ++pp, ++i)
- {
- if (pp + 1 == parameters->end())
+ for (pa = old_args->begin(); pa != old_args->end(); ++pa, ++i)
+ {
+ if (static_cast<size_t>(i) == param_count)
break;
new_args->push_back(*pa);
}
@@ -7770,15 +7868,20 @@
// We have reached the varargs parameter.
bool issued_error = false;
- if (pa != old_args->end()
- && pa + 1 == old_args->end()
- && (this->is_varargs_
- || this->is_compatible_varargs_argument(function, *pa,
- varargs_type,
- &issued_error)))
+ 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 (pa == old_args->end())
- push_empty_arg = true;
+ else if (this->is_varargs_)
+ {
+ this->report_error("too many arguments");
+ return this;
+ }
+ else if (pa + 1 == old_args->end()
+ && this->is_compatible_varargs_argument(function, *pa,
+ varargs_type,
+ &issued_error))
+ new_args->push_back(*pa);
else
{
Type* element_type = varargs_type->array_type()->element_type();
@@ -1197,6 +1197,11 @@
void
set_recover_arg(Expression*);
+ // Whether the last argument is a varargs argument (f(a...)).
+ bool
+ is_varargs() const
+ { return this->is_varargs_; }
+
// Whether this call is being deferred.
bool
is_deferred() const
@@ -1251,10 +1256,12 @@
set_args(Expression_list* args)
{ this->args_ = args; }
+ // Let a builtin expression lower varargs.
+ Expression*
+ lower_varargs(Gogo*, Named_object* function, Type* varargs_type,
+ size_t param_count);
+
private:
- Expression*
- lower_varargs(Gogo*, Named_object*);
-
bool
is_compatible_varargs_argument(Named_object*, Expression*, Type*, bool*);
@@ -195,6 +195,11 @@
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* cmplx_type = Type::make_function_type(NULL, NULL, NULL, loc);
cmplx_type->set_is_varargs();
cmplx_type->set_is_builtin();
@@ -292,6 +292,7 @@
endif
runtime_files = \
+ runtime/go-append.c \
runtime/go-assert.c \
runtime/go-assert-interface.c \
runtime/go-byte-array-to-string.c \
@@ -0,0 +1,62 @@
+/* go-append.c -- the go builtin append function.
+
+ Copyright 2010 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-assert.h"
+#include "go-type.h"
+#include "array.h"
+#include "runtime.h"
+#include "malloc.h"
+
+struct __go_open_array
+__go_append (const struct __go_slice_type *type,
+ struct __go_open_array a, struct __go_open_array b)
+{
+ size_t element_size;
+ unsigned int ucount;
+ int count;
+
+ if (b.__values == NULL || b.__count == 0)
+ return a;
+
+ __go_assert (type->__common.__code == GO_SLICE);
+ element_size = type->__element_type->__size;
+
+ ucount = (unsigned int) a.__count + (unsigned int) b.__count;
+ count = (int) ucount;
+ __go_assert (ucount == (unsigned int) count && count >= a.__count);
+ if (count > a.__capacity)
+ {
+ int m;
+ struct __go_open_array n;
+
+ m = a.__capacity;
+ if (m == 0)
+ m = b.__count;
+ else
+ {
+ do
+ {
+ if (a.__count < 1024)
+ m += m;
+ else
+ m += m / 4;
+ }
+ while (m < count);
+ }
+
+ n.__values = __go_alloc (m * element_size);
+ n.__count = a.__count;
+ n.__capacity = m;
+ __builtin_memcpy (n.__values, a.__values, n.__count * element_size);
+
+ a = n;
+ }
+
+ __builtin_memmove ((char *) a.__values + a.__count * element_size,
+ b.__values, b.__count * element_size);
+ a.__count = count;
+ return a;
+}