This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[gccgo] For *(*T)&x don't move x to the heap
- From: Ian Lance Taylor <iant at google dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Wed, 18 Nov 2009 21:49:30 -0800
- Subject: [gccgo] For *(*T)&x don't move x to the heap
Normally when you take the address of a variable in Go the variable is
allocated on the heap. However, this is not necessary if the address
of the variable does not escape the function. I didn't implement real
escape analysis, but I did implement the case of *(*T)&x. This is
important because it is actually used in libgo/go/math/unsafe.go.
Most of this patch is just moving class Type_conversion_expression
before class Unary_expression.
Committed to gccgo branch.
Ian
Index: gcc/go/expressions.h
===================================================================
--- gcc/go/expressions.h (revision 154273)
+++ gcc/go/expressions.h (working copy)
@@ -514,10 +514,11 @@ class Expression
{ return this->do_is_lvalue(); }
// We are taking the address of this expression. If this is
- // invalid, report an error and return false.
+ // invalid, report an error and return false. ESCAPES is true if
+ // this address escapes the current function.
bool
- address_taken(source_location location)
- { return this->do_address_taken(location); }
+ address_taken(source_location location, bool escapes)
+ { return this->do_address_taken(location, escapes); }
// This is called when the value of an expression is being stored
// somewhere. In some cases this requires that the reference count
@@ -652,7 +653,7 @@ class Expression
// Child class implements taking the address of an expression.
virtual bool
- do_address_taken(source_location);
+ do_address_taken(source_location, bool);
// Child class implements incrementing reference count.
virtual Expression*
@@ -885,7 +886,7 @@ class Var_expression : public Expression
{ return true; }
bool
- do_address_taken(source_location);
+ do_address_taken(source_location, bool);
Expression*
do_being_copied(Refcounts*, bool);
@@ -1405,7 +1406,7 @@ class Map_index_expression : public Expr
{ return this->is_lvalue_; }
bool
- do_address_taken(source_location);
+ do_address_taken(source_location, bool);
Expression*
do_being_copied(Refcounts*, bool);
@@ -1557,7 +1558,7 @@ class Field_reference_expression : publi
{ return true; }
bool
- do_address_taken(source_location);
+ do_address_taken(source_location, bool);
Expression*
do_being_copied(Refcounts*, bool);
@@ -1634,7 +1635,7 @@ class Interface_field_reference_expressi
}
bool
- do_address_taken(source_location)
+ do_address_taken(source_location, bool)
{ return true; }
tree
Index: gcc/go/expressions.cc
===================================================================
--- gcc/go/expressions.cc (revision 154273)
+++ gcc/go/expressions.cc (working copy)
@@ -121,7 +121,7 @@ Expression::do_discarding_value()
// address of an expression.
bool
-Expression::do_address_taken(source_location location)
+Expression::do_address_taken(source_location location, bool)
{
this->report_address_taken_error(location);
return false;
@@ -738,7 +738,7 @@ class Error_expression : public Expressi
{ return true; }
bool
- do_address_taken(source_location)
+ do_address_taken(source_location, bool)
{ return true; }
Expression*
@@ -844,9 +844,11 @@ Var_expression::do_type()
// may want to move the variable onto the heap.
bool
-Var_expression::do_address_taken(source_location location)
+Var_expression::do_address_taken(source_location location, bool escapes)
{
- if (this->variable_->is_variable())
+ if (!escapes)
+ return true;
+ else if (this->variable_->is_variable())
{
this->variable_->var_value()->set_address_taken();
return true;
@@ -1892,8 +1894,11 @@ class Const_expression : public Expressi
{ return this; }
bool
- do_address_taken(source_location location)
- { return this->constant_->const_value()->expr()->address_taken(location); }
+ do_address_taken(source_location location, bool escapes)
+ {
+ return this->constant_->const_value()->expr()->address_taken(location,
+ escapes);
+ }
Expression*
do_being_copied(Refcounts*, bool);
@@ -2260,50 +2265,40 @@ Expression::make_iota()
return &iota_expression;
}
-// Unary expressions.
+// A type conversion expression.
-class Unary_expression : public Expression
+class Type_conversion_expression : public Expression
{
public:
- Unary_expression(Operator op, Expression* expr, source_location location)
- : Expression(EXPRESSION_UNARY, location),
- op_(op), expr_(expr)
+ Type_conversion_expression(Type* type, Expression* expr,
+ source_location location)
+ : Expression(EXPRESSION_CONVERSION, location),
+ type_(type), expr_(expr), is_being_copied_(false)
{ }
- // Return the operator.
- Operator
- op() const
- { return this->op_; }
+ // Return the type to which we are converting.
+ Type*
+ type() const
+ { return this->type_; }
- // Return the operand.
+ // Return the expression which we are converting.
Expression*
- operand() const
+ expr() const
{ return this->expr_; }
- // Apply unary opcode OP to UVAL, setting VAL. Return true if this
- // could be done, false if not.
- static bool
- eval_integer(Operator op, Type* utype, mpz_t uval, mpz_t val,
- source_location);
-
- // Apply unary opcode OP to UVAL, setting VAL. Return true if this
- // could be done, false if not.
- static bool
- eval_float(Operator op, mpfr_t uval, mpfr_t val);
-
static Expression*
do_import(Import*);
protected:
int
- do_traverse(Traverse* traverse)
- { return Expression::traverse(&this->expr_, traverse); }
+ do_traverse(Traverse* traverse);
Expression*
do_lower(Gogo*, int);
bool
- do_is_constant() const;
+ do_is_constant() const
+ { return this->expr_->is_constant(); }
bool
do_integer_constant_value(bool, mpz_t, Type**) const;
@@ -2311,11 +2306,19 @@ class Unary_expression : public Expressi
bool
do_float_constant_value(mpfr_t, Type**) const;
+ bool
+ do_string_constant_value(std::string*) const;
+
Type*
- do_type();
+ do_type()
+ { return this->type_; }
void
- do_determine_type(const Type_context*);
+ do_determine_type(const Type_context*)
+ {
+ Type_context subcontext(this->type_, true);
+ this->expr_->determine_type(&subcontext);
+ }
void
do_check_types(Gogo*);
@@ -2323,1180 +2326,1256 @@ class Unary_expression : public Expressi
Expression*
do_copy()
{
- return Expression::make_unary(this->op_, this->expr_->copy(),
- this->location());
+ return new Type_conversion_expression(this->type_, this->expr_->copy(),
+ this->location());
}
- bool
- do_is_lvalue() const
- { return this->op_ == OPERATOR_MULT; }
-
- bool
- do_address_taken(source_location);
-
Expression*
do_being_copied(Refcounts*, bool);
Expression*
do_note_decrements(Refcounts*);
- Expression*
- do_being_set(Refcounts*);
-
tree
- do_get_tree(Translate_context*);
+ do_get_tree(Translate_context* context);
void
do_export(Export*) const;
private:
- // The unary operator to apply.
- Operator op_;
- // The operand.
+ // The type to convert to.
+ Type* type_;
+ // The expression to convert.
Expression* expr_;
+ // Whether this expression is being copied.
+ bool is_being_copied_;
};
-// 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.
+// Traversal.
-Expression*
-Unary_expression::do_lower(Gogo*, int)
+int
+Type_conversion_expression::do_traverse(Traverse* traverse)
{
- source_location loc = this->location();
- Operator op = this->op_;
- Expression* expr = this->expr_;
+ if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT
+ || Type::traverse(this->type_, traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ return TRAVERSE_CONTINUE;
+}
- if (op == OPERATOR_MULT && expr->is_type_expression())
- return Expression::make_type(Type::make_pointer_type(expr->type()), loc);
+// Convert to a constant at lowering time.
- if (op == OPERATOR_MULT && expr->classification() == EXPRESSION_UNARY)
- {
- Unary_expression* uexpr = static_cast<Unary_expression*>(expr);
- if (uexpr->op_ == OPERATOR_AND)
- return uexpr->expr_;
- }
+Expression*
+Type_conversion_expression::do_lower(Gogo*, int)
+{
+ Type* type = this->type_;
+ Expression* val = this->expr_;
+ source_location location = this->location();
- if (op == OPERATOR_PLUS || op == OPERATOR_MINUS
- || op == OPERATOR_NOT || op == OPERATOR_XOR)
+ if (type->integer_type() != NULL)
{
- Expression* ret = NULL;
-
- mpz_t eval;
- mpz_init(eval);
- Type* etype;
- if (expr->integer_constant_value(false, eval, &etype))
+ mpz_t ival;
+ mpz_init(ival);
+ Type* dummy;
+ if (val->integer_constant_value(false, ival, &dummy))
{
- mpz_t val;
- mpz_init(val);
- if (Unary_expression::eval_integer(op, etype, eval, val, loc))
- ret = Expression::make_integer(&val, etype, loc);
- mpz_clear(val);
+ if (!Integer_expression::check_constant(ival, type, location))
+ mpz_set_ui(ival, 0);
+ return Expression::make_integer(&ival, type, location);
}
- mpz_clear(eval);
- if (ret != NULL)
- return ret;
- if (op == OPERATOR_PLUS || op == OPERATOR_MINUS)
+ mpfr_t fval;
+ mpfr_init(fval);
+ if (val->float_constant_value(fval, &dummy))
{
- mpfr_t fval;
- mpfr_init(fval);
- Type* ftype;
- if (expr->float_constant_value(fval, &ftype))
+ if (!mpfr_integer_p(fval))
{
- mpfr_t val;
- mpfr_init(val);
- if (Unary_expression::eval_float(op, fval, val))
- ret = Expression::make_float(&val, ftype, loc);
- mpfr_clear(val);
+ error_at(location,
+ "floating point constant truncated to integer");
+ return Expression::make_error(location);
}
- mpfr_clear(fval);
- if (ret != NULL)
- return ret;
+ mpfr_get_z(ival, fval, GMP_RNDN);
+ if (!Integer_expression::check_constant(ival, type, location))
+ mpz_set_ui(ival, 0);
+ return Expression::make_integer(&ival, type, location);
+ }
+ mpfr_clear(fval);
+ mpz_clear(ival);
+ }
+
+ if (type->float_type() != NULL)
+ {
+ mpfr_t fval;
+ mpfr_init(fval);
+ Type* dummy;
+ if (val->float_constant_value(fval, &dummy))
+ {
+ if (!Float_expression::check_constant(fval, type, location))
+ mpfr_set_ui(fval, 0, GMP_RNDN);
+ Float_expression::constrain_float(fval, type);
+ return Expression::make_float(&fval, type, location);
}
+ mpfr_clear(fval);
}
return this;
}
-// Return whether a unary expression is a constant.
+// Return the constant integer value if there is one.
bool
-Unary_expression::do_is_constant() const
+Type_conversion_expression::do_integer_constant_value(bool iota_is_constant,
+ mpz_t val,
+ Type** ptype) 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)
+ if (this->type_->integer_type() == NULL)
+ return false;
+
+ mpz_t ival;
+ mpz_init(ival);
+ Type* dummy;
+ if (this->expr_->integer_constant_value(iota_is_constant, ival, &dummy))
{
- // 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)
+ if (!Integer_expression::check_constant(ival, this->type_,
+ this->location()))
{
- Named_object* no = ve->named_object();
- return no->is_variable() && no->var_value()->is_global();
+ mpz_clear(ival);
+ return false;
}
- return false;
+ mpz_set(val, ival);
+ mpz_clear(ival);
+ *ptype = this->type_;
+ return true;
}
- else
- return this->expr_->is_constant();
+ mpz_clear(ival);
+
+ mpfr_t fval;
+ mpfr_init(fval);
+ if (this->expr_->float_constant_value(fval, &dummy))
+ {
+ mpfr_get_z(val, fval, GMP_RNDN);
+ mpfr_clear(fval);
+ if (!Integer_expression::check_constant(val, this->type_,
+ this->location()))
+ return false;
+ *ptype = this->type_;
+ return true;
+ }
+ mpfr_clear(fval);
+
+ return false;
}
-// Apply unary opcode OP to UVAL, setting VAL. UTYPE is the type of
-// UVAL, if known; it may be NULL. Return true if this could be done,
-// false if not.
+// Return the constant floating point value if there is one.
bool
-Unary_expression::eval_integer(Operator op, Type* utype, mpz_t uval, mpz_t val,
- source_location location)
+Type_conversion_expression::do_float_constant_value(mpfr_t val,
+ Type** ptype) const
{
- switch (op)
+ if (this->type_->float_type() == NULL)
+ return false;
+
+ mpfr_t fval;
+ mpfr_init(fval);
+ Type* dummy;
+ if (this->expr_->float_constant_value(fval, &dummy))
{
- case OPERATOR_PLUS:
- mpz_set(val, uval);
+ if (!Float_expression::check_constant(fval, this->type_,
+ this->location()))
+ {
+ mpfr_clear(fval);
+ return false;
+ }
+ mpfr_set(val, fval, GMP_RNDN);
+ mpfr_clear(fval);
+ Float_expression::constrain_float(val, this->type_);
+ *ptype = this->type_;
return true;
- case OPERATOR_MINUS:
- mpz_neg(val, uval);
- return Integer_expression::check_constant(val, utype, location);
- case OPERATOR_NOT:
- mpz_set_ui(val, mpz_cmp_si(uval, 0) == 0 ? 1 : 0);
+ }
+ mpfr_clear(fval);
+
+ mpz_t ival;
+ mpz_init(ival);
+ if (this->expr_->integer_constant_value(false, ival, &dummy))
+ {
+ mpfr_set_z(val, ival, GMP_RNDN);
+ mpz_clear(ival);
+ if (!Float_expression::check_constant(val, this->type_,
+ this->location()))
+ return false;
+ Float_expression::constrain_float(val, this->type_);
+ *ptype = this->type_;
return true;
- case OPERATOR_XOR:
- if (utype == NULL
- || 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);
+ }
+ mpz_clear(ival);
- unsigned HOST_WIDE_INT* phwi = new unsigned HOST_WIDE_INT[count];
- memset(phwi, 0, count * sizeof(HOST_WIDE_INT));
+ return false;
+}
- size_t ecount;
- mpz_export(phwi, &ecount, -1, sizeof(HOST_WIDE_INT), 0, 0, uval);
- gcc_assert(ecount <= count);
+// Return the constant string value if there is one.
- // Trim down to the number of words required by the type.
- size_t obits = utype->integer_type()->bits();
- if (!utype->integer_type()->is_unsigned())
- ++obits;
- size_t ocount = ((obits + HOST_BITS_PER_WIDE_INT - 1)
- / HOST_BITS_PER_WIDE_INT);
- gcc_assert(ocount <= ocount);
+bool
+Type_conversion_expression::do_string_constant_value(std::string* val) const
+{
+ if (this->type_->is_string_type()
+ && this->expr_->type()->integer_type() != NULL)
+ {
+ mpz_t ival;
+ mpz_init(ival);
+ Type* dummy;
+ if (this->expr_->integer_constant_value(false, ival, &dummy))
+ {
+ unsigned long ulval = mpz_get_ui(ival);
+ if (mpz_cmp_ui(ival, ulval) == 0)
+ {
+ Lex::append_char(ulval, true, val, this->location());
+ mpz_clear(ival);
+ return true;
+ }
+ }
+ mpz_clear(ival);
+ }
- for (size_t i = 0; i < ocount; ++i)
- phwi[i] = ~phwi[i];
+ // FIXME: Could handle conversion from const []int here.
- 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);
+ return false;
+}
- mpz_import(val, ocount, -1, sizeof(HOST_WIDE_INT), 0, 0, phwi);
+// Check that types are convertible.
- delete[] phwi;
+void
+Type_conversion_expression::do_check_types(Gogo*)
+{
+ Type* type = this->type_;
+ Type* expr_type = this->expr_->type();
+ std::string reason;
+
+ if (Type::are_compatible_for_conversion(type, expr_type, &reason))
+ return;
+
+ bool ok = false;
+ if ((type->integer_type() != NULL || type->float_type() != NULL)
+ && (expr_type->integer_type() != NULL
+ || expr_type->float_type() != NULL))
+ ok = true;
+ else if (type->is_string_type())
+ {
+ if (expr_type->integer_type() != NULL)
+ ok = true;
+ else
+ {
+ Type* t = expr_type->deref();
+ if (t->array_type() != NULL)
+ {
+ Type* e = t->array_type()->element_type()->forwarded();
+ if (e->integer_type() != NULL
+ && (e == Type::lookup_integer_type("uint8")
+ || e == Type::lookup_integer_type("int")))
+ ok = true;
+ }
}
- return Integer_expression::check_constant(val, utype, location);
- case OPERATOR_AND:
- case OPERATOR_MULT:
- return false;
- default:
- gcc_unreachable();
}
-}
+ else if ((type->is_unsafe_pointer_type()
+ && (expr_type->points_to() != NULL
+ || (expr_type->integer_type() != NULL
+ && expr_type->integer_type()->bits() == POINTER_SIZE
+ && expr_type->integer_type()->is_unsigned())))
+ || (expr_type->is_unsafe_pointer_type()
+ && (type->points_to() != NULL
+ || (type->integer_type() != NULL
+ && type->integer_type()->bits() == POINTER_SIZE
+ && type->integer_type()->is_unsigned()))))
+ {
+ // Conversions between unsafe pointers and other pointers or
+ // integers of appropriate size are permitted.
+ ok = true;
+ }
-// Apply unary opcode OP to UVAL, setting VAL. Return true if this
-// could be done, false if not.
+ // FIXME: Conversions between interfaces and pointers are supported.
-bool
-Unary_expression::eval_float(Operator op, mpfr_t uval, mpfr_t val)
-{
- switch (op)
+ if (!ok)
{
- case OPERATOR_PLUS:
- mpfr_set(val, uval, GMP_RNDN);
- return true;
- case OPERATOR_MINUS:
- mpfr_neg(val, uval, GMP_RNDN);
- return true;
- case OPERATOR_NOT:
- case OPERATOR_XOR:
- case OPERATOR_AND:
- case OPERATOR_MULT:
- return false;
- default:
- gcc_unreachable();
+ if (reason.empty())
+ this->report_error(_("invalid type conversion"));
+ else
+ {
+ error_at(this->location(), "invalid type conversion (%s)",
+ reason.c_str());
+ this->set_is_error();
+ }
}
}
-// Return the integral constant value of a unary expression, if it has one.
+// The type conversion is being copied elsewhere. If we do not call a
+// function which creates a new reference, then we need to pass this
+// on to the subsidiary expression.
-bool
-Unary_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
- Type** ptype) const
+Expression*
+Type_conversion_expression::do_being_copied(Refcounts* refcounts,
+ bool for_local)
{
- mpz_t uval;
- mpz_init(uval);
- bool ret;
- if (!this->expr_->integer_constant_value(iota_is_constant, uval, ptype))
- ret = false;
+ this->is_being_copied_ = true;
+ Type* type = this->type_;
+ Type* expr_type = this->expr_->type();
+ bool copy_base;
+ if (type == expr_type)
+ copy_base = true;
+ else if (type->interface_type() != NULL
+ || expr_type->interface_type() != NULL)
+ copy_base = false;
+ else if (type->is_string_type()
+ && (expr_type->integer_type() != NULL
+ || expr_type->deref()->array_type() != NULL))
+ copy_base = false;
else
- ret = Unary_expression::eval_integer(this->op_, *ptype, uval, val,
- this->location());
- mpz_clear(uval);
- return ret;
+ copy_base = true;
+ if (copy_base && expr_type->has_refcounted_component())
+ this->expr_ = this->expr_->being_copied(refcounts, for_local);
+ return this;
}
-// Return the floating point constant value of a unary expression, if
-// it has one.
+// A type conversion may introduce a reference count.
-bool
-Unary_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
+Expression*
+Type_conversion_expression::do_note_decrements(Refcounts* refcounts)
{
- mpfr_t uval;
- mpfr_init(uval);
- bool ret;
- if (!this->expr_->float_constant_value(uval, ptype))
- ret = false;
+ if (this->is_being_copied_)
+ return this;
+ Type* type = this->type_;
+ Type* expr_type = this->expr_->type();
+ bool need_decrement;
+ if (type == expr_type)
+ need_decrement = false;
+ else if (type->interface_type() != NULL)
+ need_decrement = true;
+ else if (expr_type->interface_type() != NULL)
+ need_decrement = type->has_refcounted_component();
+ else if (type->is_string_type()
+ && (expr_type->integer_type() != NULL
+ || expr_type->deref()->array_type() != NULL))
+ need_decrement = true;
else
- ret = Unary_expression::eval_float(this->op_, uval, val);
- mpfr_clear(uval);
- return ret;
+ need_decrement = false;
+ if (!need_decrement)
+ return this;
+ return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
+ this, false);
}
-// Return the type of a unary expression.
+// Get a tree for a type conversion.
-Type*
-Unary_expression::do_type()
+tree
+Type_conversion_expression::do_get_tree(Translate_context* context)
{
- switch (this->op_)
- {
- case OPERATOR_PLUS:
- case OPERATOR_MINUS:
- case OPERATOR_NOT:
- case OPERATOR_XOR:
- return this->expr_->type();
+ Gogo* gogo = context->gogo();
+ tree type_tree = this->type_->get_tree(gogo);
+ tree expr_tree = this->expr_->get_tree(context);
- case OPERATOR_AND:
- return Type::make_pointer_type(this->expr_->type());
+ if (type_tree == error_mark_node
+ || expr_tree == error_mark_node
+ || TREE_TYPE(expr_tree) == error_mark_node)
+ return error_mark_node;
- 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;
- }
+ if (TYPE_MAIN_VARIANT(type_tree) == TYPE_MAIN_VARIANT(TREE_TYPE(expr_tree)))
+ return fold_convert(type_tree, expr_tree);
- default:
- gcc_unreachable();
+ 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
+ gcc_unreachable();
}
-}
-
-// Determine abstract types for a unary expression.
-
-void
-Unary_expression::do_determine_type(const Type_context* context)
-{
- switch (this->op_)
+ else if (type->float_type() != NULL)
{
- 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:
- gcc_unreachable();
+ if (expr_type->integer_type() != NULL
+ || expr_type->float_type() != NULL)
+ ret = fold(convert_to_real(type_tree, expr_tree));
+ else
+ gcc_unreachable();
}
-}
-
-// Check types for a unary expression.
-
-void
-Unary_expression::do_check_types(Gogo*)
-{
- switch (this->op_)
+ else if (type->is_string_type()
+ && expr_type->integer_type() != NULL)
{
- case OPERATOR_PLUS:
- case OPERATOR_MINUS:
- {
- Type* type = this->expr_->type();
- if (type->integer_type() == NULL
- && type->float_type() == NULL
- && !type->is_error_type())
- this->report_error(_("expected numeric type"));
- }
- break;
-
- case OPERATOR_NOT:
- case OPERATOR_XOR:
- {
- Type* type = this->expr_->type();
- if (type->integer_type() == NULL
- && !type->is_boolean_type()
- && !type->is_error_type())
- this->report_error(_("expected integer or boolean type"));
- }
- break;
-
- case OPERATOR_AND:
- if (!this->expr_->address_taken(this->location()))
- this->set_is_error();
- break;
-
- case OPERATOR_MULT:
- // Indirecting through a pointer.
- {
- Type* type = this->expr_->type();
- if (type->points_to() == NULL
- && !type->is_error_type())
- this->report_error(_("expected pointer"));
- }
- break;
+ expr_tree = fold_convert(integer_type_node, expr_tree);
+ if (host_integerp(expr_tree, 0))
+ {
+ HOST_WIDE_INT intval = tree_low_cst(expr_tree, 0);
+ std::string s;
+ Lex::append_char(intval, true, &s, this->location());
+ Expression* se = Expression::make_string(s, this->location());
+ return se->get_tree(context);
+ }
- default:
- gcc_unreachable();
+ static tree int_to_string_fndecl;
+ ret = Gogo::call_builtin(&int_to_string_fndecl,
+ this->location(),
+ "__go_int_to_string",
+ 1,
+ type_tree,
+ integer_type_node,
+ fold_convert(integer_type_node, expr_tree));
+ }
+ else if (type->is_string_type()
+ && (expr_type->array_type() != NULL
+ || (expr_type->points_to() != NULL
+ && expr_type->points_to()->array_type() != NULL)))
+ {
+ Type* t = expr_type;
+ if (t->points_to() != NULL)
+ {
+ t = t->points_to();
+ expr_tree = build_fold_indirect_ref(expr_tree);
+ }
+ if (!DECL_P(expr_tree))
+ expr_tree = save_expr(expr_tree);
+ Array_type* a = t->array_type();
+ Type* e = a->element_type()->forwarded();
+ gcc_assert(e->integer_type() != NULL);
+ tree valptr = fold_convert(const_ptr_type_node,
+ a->value_pointer_tree(gogo, expr_tree));
+ tree len = a->length_tree(gogo, expr_tree);
+ if (e->integer_type()->is_unsigned()
+ && e->integer_type()->bits() == 8)
+ {
+ static tree byte_array_to_string_fndecl;
+ ret = Gogo::call_builtin(&byte_array_to_string_fndecl,
+ this->location(),
+ "__go_byte_array_to_string",
+ 2,
+ type_tree,
+ const_ptr_type_node,
+ valptr,
+ size_type_node,
+ len);
+ }
+ else
+ {
+ gcc_assert(e == Type::lookup_integer_type("int"));
+ static tree int_array_to_string_fndecl;
+ ret = Gogo::call_builtin(&int_array_to_string_fndecl,
+ this->location(),
+ "__go_int_array_to_string",
+ 2,
+ type_tree,
+ const_ptr_type_node,
+ valptr,
+ size_type_node,
+ len);
+ }
}
+ 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
+ ret = Expression::convert_for_assignment(context, type, expr_type,
+ expr_tree, this->location());
+
+ return ret;
}
-// &*p is OK.
+// Output a type conversion in a constant expression.
-bool
-Unary_expression::do_address_taken(source_location location)
+void
+Type_conversion_expression::do_export(Export* exp) const
{
- if (this->op_ == OPERATOR_MULT)
- return true;
- this->report_address_taken_error(location);
- return false;
+ exp->write_c_string("convert(");
+ exp->write_type(this->type_);
+ exp->write_c_string(", ");
+ this->expr_->export_expression(exp);
+ exp->write_c_string(")");
}
-// Copying a unary expression may require incrementing a reference
-// count.
+// Import a type conversion or a struct construction.
Expression*
-Unary_expression::do_being_copied(Refcounts* refcounts, bool for_local)
+Type_conversion_expression::do_import(Import* imp)
{
- if (!this->type()->has_refcounted_component())
- return this;
- if (this->op_ == OPERATOR_PLUS)
- {
- this->expr_ = this->expr_->being_copied(refcounts, for_local);
- return this;
- }
- else if (this->op_ == OPERATOR_AND && this->is_constant())
- {
- // No need to increment the reference count when the address is
- // a constant.
- return this;
- }
- else
- return Expression::make_refcount_adjust(refcounts,
- REFCOUNT_INCREMENT_COPIED,
- this, for_local);
+ 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());
}
-// A unary expression does not increment any reference counts, so
-// there are no reference counts to decrement afterward.
+// Make a type cast expression.
Expression*
-Unary_expression::do_note_decrements(Refcounts*)
+Expression::make_cast(Type* type, Expression* val, source_location location)
{
- return this;
+ if (type->is_error_type() || val->is_error_expression())
+ return Expression::make_error(location);
+ return new Type_conversion_expression(type, val, location);
}
-// Assigning to *p requires decrementing the reference count of the
-// old value.
+// Unary expressions.
-Expression*
-Unary_expression::do_being_set(Refcounts* refcounts)
+class Unary_expression : public Expression
{
- gcc_assert(this->op_ == OPERATOR_MULT);
- if (!this->type()->has_refcounted_component())
- return this;
- return Expression::make_refcount_decrement_lvalue(refcounts, this);
-}
+ public:
+ Unary_expression(Operator op, Expression* expr, source_location location)
+ : Expression(EXPRESSION_UNARY, location),
+ op_(op), expr_(expr), escapes_(true)
+ { }
-// Get a tree for a unary expression.
+ // Return the operator.
+ Operator
+ op() const
+ { return this->op_; }
-tree
-Unary_expression::do_get_tree(Translate_context* context)
-{
- tree expr = this->expr_->get_tree(context);
- if (expr == error_mark_node)
- return error_mark_node;
+ // Return the operand.
+ Expression*
+ operand() const
+ { return this->expr_; }
- source_location loc = this->location();
- switch (this->op_)
- {
- case OPERATOR_PLUS:
- return expr;
+ // Record that an address expression does not escape.
+ void
+ set_does_not_escape()
+ {
+ gcc_assert(this->op_ == OPERATOR_AND);
+ this->escapes_ = false;
+ }
- case OPERATOR_MINUS:
- {
- tree type = TREE_TYPE(expr);
- tree compute_type = excess_precision_type(type);
- if (compute_type != NULL_TREE)
- expr = convert_to_real(compute_type, expr);
- tree ret = fold_build1_loc(loc, NEGATE_EXPR,
- (compute_type != NULL_TREE
- ? compute_type
- : type),
- expr);
- if (compute_type != NULL_TREE)
- ret = convert_to_real(type, ret);
- return ret;
- }
+ // Apply unary opcode OP to UVAL, setting VAL. Return true if this
+ // could be done, false if not.
+ static bool
+ eval_integer(Operator op, Type* utype, mpz_t uval, mpz_t val,
+ source_location);
- case OPERATOR_NOT:
- if (TREE_CODE(TREE_TYPE(expr)) == BOOLEAN_TYPE)
- return fold_build1_loc(loc, TRUTH_NOT_EXPR, TREE_TYPE(expr), expr);
- else
- return fold_build2_loc(loc, NE_EXPR, boolean_type_node, expr,
- build_int_cst(TREE_TYPE(expr), 0));
+ // Apply unary opcode OP to UVAL, setting VAL. Return true if this
+ // could be done, false if not.
+ static bool
+ eval_float(Operator op, mpfr_t uval, mpfr_t val);
- case OPERATOR_XOR:
- return fold_build1_loc(loc, BIT_NOT_EXPR, TREE_TYPE(expr), expr);
+ static Expression*
+ do_import(Import*);
- 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.
- gcc_assert(TREE_CODE(expr) != CONSTRUCTOR || TREE_CONSTANT(expr));
+ protected:
+ int
+ do_traverse(Traverse* traverse)
+ { return Expression::traverse(&this->expr_, traverse); }
- // Build a decl for a constant constructor.
- if (TREE_CODE(expr) == CONSTRUCTOR && TREE_CONSTANT(expr))
- {
- tree decl = build_decl(this->location(), VAR_DECL,
- create_tmp_var_name("C"), TREE_TYPE(expr));
- DECL_EXTERNAL(decl) = 0;
- TREE_PUBLIC(decl) = 0;
- TREE_READONLY(decl) = 1;
- TREE_CONSTANT(decl) = 1;
- TREE_STATIC(decl) = 1;
- DECL_ARTIFICIAL(decl) = 1;
- DECL_INITIAL(decl) = expr;
- rest_of_decl_compilation(decl, 1, 0);
- expr = decl;
- }
+ Expression*
+ do_lower(Gogo*, int);
- return build_fold_addr_expr_loc(loc, expr);
+ bool
+ do_is_constant() const;
- case OPERATOR_MULT:
- gcc_assert(POINTER_TYPE_P(TREE_TYPE(expr)));
- return build_fold_indirect_ref_loc(loc, expr);
+ bool
+ do_integer_constant_value(bool, mpz_t, Type**) const;
- default:
- gcc_unreachable();
- }
-}
+ bool
+ do_float_constant_value(mpfr_t, Type**) const;
-// Export a unary expression.
+ Type*
+ do_type();
-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:
- gcc_unreachable();
- }
- this->expr_->export_expression(exp);
-}
+ void
+ do_determine_type(const Type_context*);
-// Import a unary expression.
+ void
+ do_check_types(Gogo*);
-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:
- gcc_unreachable();
- }
- imp->require_c_string(" ");
- Expression* expr = Expression::import_expression(imp);
- return Expression::make_unary(op, expr, imp->location());
-}
+ Expression*
+ do_copy()
+ {
+ return Expression::make_unary(this->op_, this->expr_->copy(),
+ this->location());
+ }
-// Make a unary expression.
+ bool
+ do_is_lvalue() const
+ { return this->op_ == OPERATOR_MULT; }
-Expression*
-Expression::make_unary(Operator op, Expression* expr, source_location location)
-{
- return new Unary_expression(op, expr, location);
-}
+ bool
+ do_address_taken(source_location, bool);
-// Class Binary_expression.
+ Expression*
+ do_being_copied(Refcounts*, bool);
-// Traversal.
+ Expression*
+ do_note_decrements(Refcounts*);
-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);
-}
+ Expression*
+ do_being_set(Refcounts*);
-// Compare integer constants according to OP.
+ tree
+ do_get_tree(Translate_context*);
-bool
-Binary_expression::compare_integer(Operator op, mpz_t left_val,
- mpz_t right_val)
+ void
+ do_export(Export*) const;
+
+ 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_;
+ // The operand.
+ Expression* expr_;
+};
+
+// 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*, int)
{
- int i = mpz_cmp(left_val, right_val);
- switch (op)
+ source_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)
{
- case OPERATOR_EQEQ:
- return i == 0;
- case OPERATOR_NOTEQ:
- return i != 0;
- case OPERATOR_LT:
- return i < 0;
- case OPERATOR_LE:
- return i <= 0;
- case OPERATOR_GT:
- return i > 0;
- case OPERATOR_GE:
- return i >= 0;
- default:
- gcc_unreachable();
+ Expression* e = expr;
+ while (e->classification() == EXPRESSION_CONVERSION)
+ {
+ Type_conversion_expression* te
+ = static_cast<Type_conversion_expression*>(e);
+ e = te->expr();
+ }
+
+ if (e->classification() == EXPRESSION_UNARY)
+ {
+ Unary_expression* ue = static_cast<Unary_expression*>(e);
+ if (ue->op_ == OPERATOR_AND)
+ {
+ if (e == expr)
+ {
+ // *&x == x.
+ return ue->expr_;
+ }
+ ue->set_does_not_escape();
+ }
+ }
}
-}
-// Compare floating point constants according to OP.
+ if (op == OPERATOR_PLUS || op == OPERATOR_MINUS
+ || op == OPERATOR_NOT || op == OPERATOR_XOR)
+ {
+ Expression* ret = NULL;
-bool
-Binary_expression::compare_float(Operator op, Type* type, mpfr_t left_val,
- mpfr_t right_val)
-{
- int i;
- if (type == NULL)
- i = mpfr_cmp(left_val, right_val);
- else
+ mpz_t eval;
+ mpz_init(eval);
+ Type* etype;
+ if (expr->integer_constant_value(false, eval, &etype))
+ {
+ mpz_t val;
+ mpz_init(val);
+ if (Unary_expression::eval_integer(op, etype, eval, val, loc))
+ ret = Expression::make_integer(&val, etype, loc);
+ mpz_clear(val);
+ }
+ mpz_clear(eval);
+ if (ret != NULL)
+ return ret;
+
+ if (op == OPERATOR_PLUS || op == OPERATOR_MINUS)
+ {
+ mpfr_t fval;
+ mpfr_init(fval);
+ Type* ftype;
+ if (expr->float_constant_value(fval, &ftype))
+ {
+ mpfr_t val;
+ mpfr_init(val);
+ if (Unary_expression::eval_float(op, fval, val))
+ ret = Expression::make_float(&val, ftype, loc);
+ mpfr_clear(val);
+ }
+ mpfr_clear(fval);
+ if (ret != NULL)
+ return ret;
+ }
+ }
+
+ return this;
+}
+
+// Return whether a unary expression is a constant.
+
+bool
+Unary_expression::do_is_constant() const
+{
+ if (this->op_ == OPERATOR_MULT)
{
- mpfr_t lv;
- mpfr_init_set(lv, left_val, GMP_RNDN);
- mpfr_t rv;
- mpfr_init_set(rv, right_val, GMP_RNDN);
- Float_expression::constrain_float(lv, type);
- Float_expression::constrain_float(rv, type);
- i = mpfr_cmp(lv, rv);
- mpfr_clear(lv);
- mpfr_clear(rv);
+ // 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;
}
- switch (op)
+ else if (this->op_ == OPERATOR_AND)
{
- case OPERATOR_EQEQ:
- return i == 0;
- case OPERATOR_NOTEQ:
- return i != 0;
- case OPERATOR_LT:
- return i < 0;
- case OPERATOR_LE:
- return i <= 0;
- case OPERATOR_GT:
- return i > 0;
- case OPERATOR_GE:
- return i >= 0;
- default:
- gcc_unreachable();
+ // 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 binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
-// LEFT_TYPE is the type of LEFT_VAL, RIGHT_TYPE is the type of
-// RIGHT_VAL; LEFT_TYPE and/or RIGHT_TYPE may be NULL. Return true if
-// this could be done, false if not.
+// Apply unary opcode OP to UVAL, setting VAL. UTYPE is the type of
+// UVAL, if known; it may be NULL. Return true if this could be done,
+// false if not.
bool
-Binary_expression::eval_integer(Operator op, Type* left_type, mpz_t left_val,
- Type* right_type, mpz_t right_val,
- source_location location, mpz_t val)
+Unary_expression::eval_integer(Operator op, Type* utype, mpz_t uval, mpz_t val,
+ source_location location)
{
- bool is_shift_op = false;
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. We should probably handle them
- // anyhow in case a type conversion is used on the result.
- return false;
case OPERATOR_PLUS:
- mpz_add(val, left_val, right_val);
- break;
+ mpz_set(val, uval);
+ return true;
case OPERATOR_MINUS:
- mpz_sub(val, left_val, right_val);
- break;
- case OPERATOR_OR:
- mpz_ior(val, left_val, right_val);
- break;
+ mpz_neg(val, uval);
+ return Integer_expression::check_constant(val, utype, location);
+ case OPERATOR_NOT:
+ mpz_set_ui(val, mpz_cmp_si(uval, 0) == 0 ? 1 : 0);
+ return true;
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);
- return true;
- }
- break;
- case OPERATOR_MOD:
- if (mpz_sgn(right_val) != 0)
- mpz_tdiv_r(val, left_val, right_val);
+ if (utype == NULL
+ || utype->integer_type() == NULL
+ || utype->integer_type()->is_abstract())
+ mpz_com(val, uval);
else
{
- error_at(location, "division by zero");
- mpz_set_ui(val, 0);
- return true;
+ // 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 ecount;
+ mpz_export(phwi, &ecount, -1, sizeof(HOST_WIDE_INT), 0, 0, uval);
+ gcc_assert(ecount <= count);
+
+ // Trim down to the number of words required by the type.
+ size_t obits = utype->integer_type()->bits();
+ if (!utype->integer_type()->is_unsigned())
+ ++obits;
+ size_t ocount = ((obits + HOST_BITS_PER_WIDE_INT - 1)
+ / HOST_BITS_PER_WIDE_INT);
+ gcc_assert(ocount <= ocount);
+
+ 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);
+
+ delete[] phwi;
}
- break;
- case OPERATOR_LSHIFT:
- {
- 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);
- return true;
- }
- mpz_mul_2exp(val, left_val, shift);
- is_shift_op = true;
- 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);
- return true;
- }
- 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);
- is_shift_op = true;
- break;
- }
- break;
+ return Integer_expression::check_constant(val, utype, location);
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;
+ case OPERATOR_MULT:
+ return false;
default:
gcc_unreachable();
}
+}
- Type* type = left_type;
- if (!is_shift_op)
- {
- if (type == NULL)
- type = right_type;
- else if (type != right_type && right_type != NULL)
- {
- if (type->is_abstract())
- type = right_type;
- else if (!right_type->is_abstract())
- {
- // This look like a type error which should be diagnosed
- // elsewhere. Don't do anything here, to avoid an
- // unhelpful chain of error messages.
- return true;
- }
- }
- }
+// Apply unary opcode OP to UVAL, setting VAL. Return true if this
+// could be done, false if not.
- if (type != NULL && !type->is_abstract())
+bool
+Unary_expression::eval_float(Operator op, mpfr_t uval, mpfr_t val)
+{
+ switch (op)
{
- // We have to check the operands too, as we have implicitly
- // coerced them to TYPE.
- if ((type != left_type
- && !Integer_expression::check_constant(left_val, type, location))
- || (!is_shift_op
- && type != right_type
- && !Integer_expression::check_constant(right_val, type,
- location))
- || !Integer_expression::check_constant(val, type, location))
- mpz_set_ui(val, 0);
+ case OPERATOR_PLUS:
+ mpfr_set(val, uval, GMP_RNDN);
+ return true;
+ case OPERATOR_MINUS:
+ mpfr_neg(val, uval, GMP_RNDN);
+ return true;
+ case OPERATOR_NOT:
+ case OPERATOR_XOR:
+ case OPERATOR_AND:
+ case OPERATOR_MULT:
+ return false;
+ default:
+ gcc_unreachable();
}
+}
- return true;
+// Return the integral constant value of a unary expression, if it has one.
+
+bool
+Unary_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
+ Type** ptype) const
+{
+ mpz_t uval;
+ mpz_init(uval);
+ bool ret;
+ if (!this->expr_->integer_constant_value(iota_is_constant, uval, ptype))
+ ret = false;
+ else
+ ret = Unary_expression::eval_integer(this->op_, *ptype, uval, val,
+ this->location());
+ mpz_clear(uval);
+ return ret;
}
-// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
-// Return true if this could be done, false if not.
+// Return the floating point constant value of a unary expression, if
+// it has one.
bool
-Binary_expression::eval_float(Operator op, Type* left_type, mpfr_t left_val,
- Type* right_type, mpfr_t right_val,
- mpfr_t val, source_location location)
+Unary_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
{
- switch (op)
+ mpfr_t uval;
+ mpfr_init(uval);
+ bool ret;
+ if (!this->expr_->float_constant_value(uval, ptype))
+ ret = false;
+ else
+ ret = Unary_expression::eval_float(this->op_, uval, val);
+ mpfr_clear(uval);
+ return ret;
+}
+
+// Return the type of a unary expression.
+
+Type*
+Unary_expression::do_type()
+{
+ switch (this->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. We should probably handle them
- // anyhow in case a type conversion is used on the result.
- return false;
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_NOT:
case OPERATOR_XOR:
+ return this->expr_->type();
+
case OPERATOR_AND:
- case OPERATOR_BITCLEAR:
- return false;
+ return Type::make_pointer_type(this->expr_->type());
+
case OPERATOR_MULT:
- mpfr_mul(val, left_val, right_val, GMP_RNDN);
- break;
- case OPERATOR_DIV:
- if (mpfr_zero_p(right_val))
- error_at(location, "division by zero");
- mpfr_div(val, left_val, right_val, GMP_RNDN);
- break;
- case OPERATOR_MOD:
- return false;
- case OPERATOR_LSHIFT:
- case OPERATOR_RSHIFT:
- return false;
+ {
+ Type* subtype = this->expr_->type();
+ Type* points_to = subtype->points_to();
+ if (points_to == NULL)
+ return Type::make_error_type();
+ return points_to;
+ }
+
default:
gcc_unreachable();
}
+}
- Type* type = left_type;
- if (type == NULL)
- type = right_type;
- else if (type != right_type && right_type != NULL)
+// Determine abstract types for a unary expression.
+
+void
+Unary_expression::do_determine_type(const Type_context* context)
+{
+ switch (this->op_)
{
- if (type->is_abstract())
- type = right_type;
- else if (!right_type->is_abstract())
- {
- // This looks like a type error which should be diagnosed
- // elsewhere. Don't do anything here, to avoid an unhelpful
- // chain of error messages.
- return true;
- }
+ 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:
+ gcc_unreachable();
}
+}
- if (type != NULL && !type->is_abstract())
+// Check types for a unary expression.
+
+void
+Unary_expression::do_check_types(Gogo*)
+{
+ switch (this->op_)
{
- if ((type != left_type
- && !Float_expression::check_constant(left_val, type, location))
- || (type != right_type
- && !Float_expression::check_constant(right_val, type,
- location))
- || !Float_expression::check_constant(val, type, location))
- mpfr_set_ui(val, 0, GMP_RNDN);
+ case OPERATOR_PLUS:
+ case OPERATOR_MINUS:
+ {
+ Type* type = this->expr_->type();
+ if (type->integer_type() == NULL
+ && type->float_type() == NULL
+ && !type->is_error_type())
+ this->report_error(_("expected numeric type"));
+ }
+ break;
+
+ case OPERATOR_NOT:
+ case OPERATOR_XOR:
+ {
+ Type* type = this->expr_->type();
+ if (type->integer_type() == NULL
+ && !type->is_boolean_type()
+ && !type->is_error_type())
+ this->report_error(_("expected integer or boolean type"));
+ }
+ break;
+
+ case OPERATOR_AND:
+ if (!this->expr_->address_taken(this->location(), this->escapes_))
+ this->set_is_error();
+ break;
+
+ case OPERATOR_MULT:
+ // Indirecting through a pointer.
+ {
+ Type* type = this->expr_->type();
+ if (type->points_to() == NULL
+ && !type->is_error_type())
+ this->report_error(_("expected pointer"));
+ }
+ break;
+
+ default:
+ gcc_unreachable();
}
+}
- return true;
+// &*p is OK.
+
+bool
+Unary_expression::do_address_taken(source_location location, bool)
+{
+ if (this->op_ == OPERATOR_MULT)
+ return true;
+ this->report_address_taken_error(location);
+ return false;
}
-// Lower a binary expression. We have to evaluate constant
-// expressions now, in order t implemented Go's unlimited precision
-// constants.
+// Copying a unary expression may require incrementing a reference
+// count.
Expression*
-Binary_expression::do_lower(Gogo*, int)
+Unary_expression::do_being_copied(Refcounts* refcounts, bool for_local)
{
- source_location location = this->location();
- Operator op = this->op_;
- Expression* left = this->left_;
- Expression* right = this->right_;
+ if (!this->type()->has_refcounted_component())
+ return this;
+ if (this->op_ == OPERATOR_PLUS)
+ {
+ this->expr_ = this->expr_->being_copied(refcounts, for_local);
+ return this;
+ }
+ else if (this->op_ == OPERATOR_AND && this->is_constant())
+ {
+ // No need to increment the reference count when the address is
+ // a constant.
+ return this;
+ }
+ else
+ return Expression::make_refcount_adjust(refcounts,
+ REFCOUNT_INCREMENT_COPIED,
+ this, for_local);
+}
- const bool is_comparison = (op == OPERATOR_EQEQ
- || op == OPERATOR_NOTEQ
- || op == OPERATOR_LT
- || op == OPERATOR_LE
- || op == OPERATOR_GT
- || op == OPERATOR_GE);
+// A unary expression does not increment any reference counts, so
+// there are no reference counts to decrement afterward.
- // Integer constant expressions.
- {
- mpz_t left_val;
- mpz_init(left_val);
- Type* left_type;
- mpz_t right_val;
- mpz_init(right_val);
- Type* right_type;
- if (left->integer_constant_value(false, left_val, &left_type)
- && right->integer_constant_value(false, right_val, &right_type))
- {
- Expression* ret = NULL;
- if (left_type != right_type
- && left_type != NULL
- && right_type != NULL
- && left_type->base() != right_type->base()
- && op != OPERATOR_LSHIFT
- && op != OPERATOR_RSHIFT)
- {
- // May be a type error--let it be diagnosed later.
- }
- else if (is_comparison)
- {
- bool b = Binary_expression::compare_integer(op, left_val,
- right_val);
- ret = Expression::make_cast(Type::lookup_bool_type(),
- Expression::make_boolean(b, location),
- location);
- }
- else
- {
- mpz_t val;
- mpz_init(val);
+Expression*
+Unary_expression::do_note_decrements(Refcounts*)
+{
+ return this;
+}
- if (Binary_expression::eval_integer(op, left_type, left_val,
- right_type, right_val,
- location, val))
- {
- gcc_assert(op != OPERATOR_OROR && op != OPERATOR_ANDAND);
- Type* type;
- if (op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT)
- type = left_type;
- else if (left_type == NULL)
- type = right_type;
- else if (right_type == NULL)
- type = left_type;
- else if (!left_type->is_abstract()
- && left_type->named_type() != NULL)
- type = left_type;
- else if (!right_type->is_abstract()
- && right_type->named_type() != NULL)
- type = right_type;
- else if (!left_type->is_abstract())
- type = left_type;
- else if (!right_type->is_abstract())
- type = right_type;
- else if (left_type->float_type() != NULL)
- type = left_type;
- else if (right_type->float_type() != NULL)
- type = right_type;
- else
- type = left_type;
- ret = Expression::make_integer(&val, type, location);
- }
+// Assigning to *p requires decrementing the reference count of the
+// old value.
- mpz_clear(val);
- }
+Expression*
+Unary_expression::do_being_set(Refcounts* refcounts)
+{
+ gcc_assert(this->op_ == OPERATOR_MULT);
+ if (!this->type()->has_refcounted_component())
+ return this;
+ return Expression::make_refcount_decrement_lvalue(refcounts, this);
+}
- if (ret != NULL)
- {
- mpz_clear(right_val);
- mpz_clear(left_val);
- return ret;
- }
- }
- mpz_clear(right_val);
- mpz_clear(left_val);
- }
+// Get a tree for a unary expression.
- // Floating point constant expressions.
- {
- mpfr_t left_val;
- mpfr_init(left_val);
- Type* left_type;
- mpfr_t right_val;
- mpfr_init(right_val);
- Type* right_type;
- if (left->float_constant_value(left_val, &left_type)
- && right->float_constant_value(right_val, &right_type))
+tree
+Unary_expression::do_get_tree(Translate_context* context)
+{
+ tree expr = this->expr_->get_tree(context);
+ if (expr == error_mark_node)
+ return error_mark_node;
+
+ source_location loc = this->location();
+ switch (this->op_)
+ {
+ case OPERATOR_PLUS:
+ return expr;
+
+ case OPERATOR_MINUS:
{
- Expression* ret = NULL;
- if (left_type != right_type
- && left_type != NULL
- && right_type != NULL
- && left_type->base() != right_type->base()
- && op != OPERATOR_LSHIFT
- && op != OPERATOR_RSHIFT)
- {
- // May be a type error--let it be diagnosed later.
- }
- else if (is_comparison)
- {
- bool b = Binary_expression::compare_float(op,
- (left_type != NULL
- ? left_type
- : right_type),
- left_val, right_val);
- ret = Expression::make_boolean(b, location);
- }
- else
- {
- mpfr_t val;
- mpfr_init(val);
+ tree type = TREE_TYPE(expr);
+ tree compute_type = excess_precision_type(type);
+ if (compute_type != NULL_TREE)
+ expr = convert_to_real(compute_type, expr);
+ tree ret = fold_build1_loc(loc, NEGATE_EXPR,
+ (compute_type != NULL_TREE
+ ? compute_type
+ : type),
+ expr);
+ if (compute_type != NULL_TREE)
+ ret = convert_to_real(type, ret);
+ return ret;
+ }
- if (Binary_expression::eval_float(op, left_type, left_val,
- right_type, right_val, val,
- location))
- {
- gcc_assert(op != OPERATOR_OROR && op != OPERATOR_ANDAND
- && op != OPERATOR_LSHIFT && op != OPERATOR_RSHIFT);
- Type* type;
- if (left_type == NULL)
- type = right_type;
- else if (right_type == NULL)
- type = left_type;
- else if (!left_type->is_abstract()
- && left_type->named_type() != NULL)
- type = left_type;
- else if (!right_type->is_abstract()
- && right_type->named_type() != NULL)
- type = right_type;
- else if (!left_type->is_abstract())
- type = left_type;
- else if (!right_type->is_abstract())
- type = right_type;
- else if (left_type->float_type() != NULL)
- type = left_type;
- else if (right_type->float_type() != NULL)
- type = right_type;
- else
- type = left_type;
- ret = Expression::make_float(&val, type, location);
- }
+ case OPERATOR_NOT:
+ if (TREE_CODE(TREE_TYPE(expr)) == BOOLEAN_TYPE)
+ return fold_build1_loc(loc, TRUTH_NOT_EXPR, TREE_TYPE(expr), expr);
+ else
+ return fold_build2_loc(loc, NE_EXPR, boolean_type_node, expr,
+ build_int_cst(TREE_TYPE(expr), 0));
- mpfr_clear(val);
- }
+ case OPERATOR_XOR:
+ return fold_build1_loc(loc, BIT_NOT_EXPR, TREE_TYPE(expr), expr);
- if (ret != NULL)
- {
- mpfr_clear(right_val);
- mpfr_clear(left_val);
- return ret;
- }
- }
- mpfr_clear(right_val);
- mpfr_clear(left_val);
- }
+ 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.
+ gcc_assert(TREE_CODE(expr) != CONSTRUCTOR || TREE_CONSTANT(expr));
- // String constant expressions.
- if (op == OPERATOR_PLUS
- && 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))
- return Expression::make_string(left_string + right_string, location);
- }
+ // Build a decl for a constant constructor.
+ if (TREE_CODE(expr) == CONSTRUCTOR && TREE_CONSTANT(expr))
+ {
+ tree decl = build_decl(this->location(), VAR_DECL,
+ create_tmp_var_name("C"), TREE_TYPE(expr));
+ DECL_EXTERNAL(decl) = 0;
+ TREE_PUBLIC(decl) = 0;
+ TREE_READONLY(decl) = 1;
+ TREE_CONSTANT(decl) = 1;
+ TREE_STATIC(decl) = 1;
+ DECL_ARTIFICIAL(decl) = 1;
+ DECL_INITIAL(decl) = expr;
+ rest_of_decl_compilation(decl, 1, 0);
+ expr = decl;
+ }
- return this;
+ return build_fold_addr_expr_loc(loc, expr);
+
+ case OPERATOR_MULT:
+ gcc_assert(POINTER_TYPE_P(TREE_TYPE(expr)));
+ return build_fold_indirect_ref_loc(loc, expr);
+
+ default:
+ gcc_unreachable();
+ }
}
-// Return the integer constant value, if it has one.
+// Export a unary expression.
-bool
-Binary_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
- Type** ptype) const
+void
+Unary_expression::do_export(Export* exp) const
{
- mpz_t left_val;
- mpz_init(left_val);
- Type* left_type;
- if (!this->left_->integer_constant_value(iota_is_constant, left_val,
- &left_type))
+ switch (this->op_)
{
- mpz_clear(left_val);
- return false;
+ 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:
+ gcc_unreachable();
}
+ this->expr_->export_expression(exp);
+}
- mpz_t right_val;
- mpz_init(right_val);
- Type* right_type;
- if (!this->right_->integer_constant_value(iota_is_constant, right_val,
- &right_type))
+// Import a unary expression.
+
+Expression*
+Unary_expression::do_import(Import* imp)
+{
+ Operator op;
+ switch (imp->get_char())
{
- mpz_clear(right_val);
- mpz_clear(left_val);
- return false;
+ case '+':
+ op = OPERATOR_PLUS;
+ break;
+ case '-':
+ op = OPERATOR_MINUS;
+ break;
+ case '!':
+ op = OPERATOR_NOT;
+ break;
+ case '^':
+ op = OPERATOR_XOR;
+ break;
+ default:
+ gcc_unreachable();
}
+ imp->require_c_string(" ");
+ Expression* expr = Expression::import_expression(imp);
+ return Expression::make_unary(op, expr, imp->location());
+}
- bool ret;
- if (left_type != right_type
- && left_type != NULL
- && right_type != NULL
- && left_type->base() != right_type->base()
- && this->op_ != OPERATOR_RSHIFT
- && this->op_ != OPERATOR_LSHIFT)
- ret = false;
- else
- ret = Binary_expression::eval_integer(this->op_, left_type, left_val,
- right_type, right_val,
- this->location(), val);
+// Make a unary expression.
- mpz_clear(right_val);
- mpz_clear(left_val);
+Expression*
+Expression::make_unary(Operator op, Expression* expr, source_location location)
+{
+ return new Unary_expression(op, expr, location);
+}
- if (ret)
- *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
- ? right_type
- : left_type);
+// Class Binary_expression.
- return ret;
+// 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 floating point constant value, if it has one.
+// Compare integer constants according to OP.
bool
-Binary_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
+Binary_expression::compare_integer(Operator op, mpz_t left_val,
+ mpz_t right_val)
{
- mpfr_t left_val;
- mpfr_init(left_val);
- Type* left_type;
- if (!this->left_->float_constant_value(left_val, &left_type))
- {
- mpfr_clear(left_val);
- return false;
- }
-
- mpfr_t right_val;
- mpfr_init(right_val);
- Type* right_type;
- if (!this->right_->float_constant_value(right_val, &right_type))
+ int i = mpz_cmp(left_val, right_val);
+ switch (op)
{
- mpfr_clear(right_val);
- mpfr_clear(left_val);
- return false;
+ case OPERATOR_EQEQ:
+ return i == 0;
+ case OPERATOR_NOTEQ:
+ return i != 0;
+ case OPERATOR_LT:
+ return i < 0;
+ case OPERATOR_LE:
+ return i <= 0;
+ case OPERATOR_GT:
+ return i > 0;
+ case OPERATOR_GE:
+ return i >= 0;
+ default:
+ gcc_unreachable();
}
-
- bool ret;
- if (left_type != right_type
- && left_type != NULL
- && right_type != NULL
- && left_type->base() != right_type->base())
- ret = false;
- else
- ret = Binary_expression::eval_float(this->op_, left_type, left_val,
- right_type, right_val,
- val, this->location());
-
- mpfr_clear(left_val);
- mpfr_clear(right_val);
-
- if (ret)
- *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
- ? right_type
- : left_type);
-
- return ret;
}
-// Note that the value is being discarded.
+// Compare floating point constants according to OP.
-void
-Binary_expression::do_discarding_value()
+bool
+Binary_expression::compare_float(Operator op, Type* type, mpfr_t left_val,
+ mpfr_t right_val)
{
- if (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_ANDAND)
- this->right_->discarding_value();
+ int i;
+ if (type == NULL)
+ i = mpfr_cmp(left_val, right_val);
else
- this->warn_unused_value();
+ {
+ mpfr_t lv;
+ mpfr_init_set(lv, left_val, GMP_RNDN);
+ mpfr_t rv;
+ mpfr_init_set(rv, right_val, GMP_RNDN);
+ Float_expression::constrain_float(lv, type);
+ Float_expression::constrain_float(rv, type);
+ i = mpfr_cmp(lv, rv);
+ mpfr_clear(lv);
+ mpfr_clear(rv);
+ }
+ switch (op)
+ {
+ case OPERATOR_EQEQ:
+ return i == 0;
+ case OPERATOR_NOTEQ:
+ return i != 0;
+ case OPERATOR_LT:
+ return i < 0;
+ case OPERATOR_LE:
+ return i <= 0;
+ case OPERATOR_GT:
+ return i > 0;
+ case OPERATOR_GE:
+ return i >= 0;
+ default:
+ gcc_unreachable();
+ }
}
-// Get type.
+// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
+// LEFT_TYPE is the type of LEFT_VAL, RIGHT_TYPE is the type of
+// RIGHT_VAL; LEFT_TYPE and/or RIGHT_TYPE may be NULL. Return true if
+// this could be done, false if not.
-Type*
-Binary_expression::do_type()
+bool
+Binary_expression::eval_integer(Operator op, Type* left_type, mpz_t left_val,
+ Type* right_type, mpz_t right_val,
+ source_location location, mpz_t val)
{
- switch (this->op_)
+ bool is_shift_op = false;
+ switch (op)
{
case OPERATOR_OROR:
case OPERATOR_ANDAND:
@@ -3506,2991 +3585,3098 @@ Binary_expression::do_type()
case OPERATOR_LE:
case OPERATOR_GT:
case OPERATOR_GE:
- return Type::lookup_bool_type();
-
+ // These return boolean values. We should probably handle them
+ // anyhow in case a type conversion is used on the result.
+ return false;
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);
+ return true;
+ }
+ break;
case OPERATOR_MOD:
- case OPERATOR_AND:
- case OPERATOR_BITCLEAR:
+ 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);
+ return true;
+ }
+ break;
+ case OPERATOR_LSHIFT:
{
- Type* left_type = this->left_->type();
- Type* right_type = this->right_->type();
- if (!left_type->is_abstract() && left_type->named_type() != NULL)
- return left_type;
- else if (!right_type->is_abstract() && right_type->named_type() != NULL)
- return right_type;
- else if (!left_type->is_abstract())
- return left_type;
- else if (!right_type->is_abstract())
- return right_type;
- else if (left_type->float_type() != NULL)
- return left_type;
- else if (right_type->float_type() != NULL)
- return right_type;
- else
- return left_type;
+ 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);
+ return true;
+ }
+ mpz_mul_2exp(val, left_val, shift);
+ is_shift_op = true;
+ break;
}
-
- case OPERATOR_LSHIFT:
+ break;
case OPERATOR_RSHIFT:
- return this->left_->type();
-
+ {
+ 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);
+ return true;
+ }
+ 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);
+ is_shift_op = true;
+ 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:
gcc_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;
- }
- // Set the context for the left hand operand.
- if (is_shift_op)
- {
- // The right hand operand plays no role in determining the type
- // of the left hand operand. A shift of an abstract integer in
- // a string context gets special treatment, which may be a
- // language bug.
- if (subcontext.type != NULL
- && subcontext.type->is_string_type()
- && tleft->is_abstract())
- error_at(this->location(), "shift of non-integer operand");
- }
- else if (!tleft->is_abstract())
- subcontext.type = tleft;
- else if (!tright->is_abstract())
- subcontext.type = tright;
- else if (subcontext.type == NULL)
+ Type* type = left_type;
+ if (!is_shift_op)
{
- if ((tleft->integer_type() != NULL && tright->integer_type() != NULL)
- || (tleft->float_type() != NULL && tright->float_type() != NULL))
- {
- // Both sides have an abstract integer type or both sides
- // have an abstract float type. Just let CONTEXT determine
- // whether they may remain abstract or not.
- }
- else
+ if (type == NULL)
+ type = right_type;
+ else if (type != right_type && right_type != NULL)
{
- // Both sides are abstract, but one is integer and one is
- // floating point. Convert the abstract integer to floating
- // point.
- subcontext.type = tleft->float_type() != NULL ? tleft : tright;
+ if (type->is_abstract())
+ type = right_type;
+ else if (!right_type->is_abstract())
+ {
+ // This look like a type error which should be diagnosed
+ // elsewhere. Don't do anything here, to avoid an
+ // unhelpful chain of error messages.
+ return true;
+ }
}
}
- this->left_->determine_type(&subcontext);
-
- // The context for the right hand operand is the same as for the
- // left hand operand, except for a shift operator.
- if (is_shift_op)
+ if (type != NULL && !type->is_abstract())
{
- subcontext.type = Type::lookup_integer_type("uint");
- subcontext.may_be_abstract = false;
+ // We have to check the operands too, as we have implicitly
+ // coerced them to TYPE.
+ if ((type != left_type
+ && !Integer_expression::check_constant(left_val, type, location))
+ || (!is_shift_op
+ && type != right_type
+ && !Integer_expression::check_constant(right_val, type,
+ location))
+ || !Integer_expression::check_constant(val, type, location))
+ mpz_set_ui(val, 0);
}
- this->right_->determine_type(&subcontext);
+ return true;
}
-// Report an error if the binary operator OP does not support TYPE.
-// Return whether the operation is OK. This should not be used for
-// shift.
+// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
+// Return true if this could be done, false if not.
bool
-Binary_expression::check_operator_type(Operator op, Type* type,
- source_location location)
+Binary_expression::eval_float(Operator op, Type* left_type, mpfr_t left_val,
+ Type* right_type, mpfr_t right_val,
+ mpfr_t val, source_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:
- if (type->integer_type() == NULL
- && type->float_type() == NULL
- && !type->is_string_type()
- && type->points_to() == NULL
- && !type->is_nil_type()
- && !type->is_boolean_type()
- && type->interface_type() == NULL
- && (type->array_type() == NULL
- || type->array_type()->length() != NULL)
- && type->map_type() == NULL
- && type->channel_type() == NULL
- && type->function_type() == NULL)
- {
- error_at(location,
- _("expected integer, floating, string, pointer, boolean, "
- "interface, slice, map, channel, or function type"));
- return false;
- }
- break;
-
case OPERATOR_LT:
case OPERATOR_LE:
case OPERATOR_GT:
case OPERATOR_GE:
+ // These return boolean values. We should probably handle them
+ // anyhow in case a type conversion is used on the result.
+ return false;
case OPERATOR_PLUS:
- case OPERATOR_PLUSEQ:
- if (type->integer_type() == NULL
- && type->float_type() == NULL
- && !type->is_string_type())
- {
- error_at(location,
- _("expected integer, floating, or string type"));
- return false;
- }
+ mpfr_add(val, left_val, right_val, GMP_RNDN);
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)
- {
- error_at(location, _("expected integer or floating type"));
- return false;
- }
+ mpfr_sub(val, left_val, right_val, GMP_RNDN);
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_AND:
case OPERATOR_BITCLEAR:
- case OPERATOR_BITCLEAREQ:
- if (type->integer_type() == NULL)
- {
- error_at(location, _("expected integer type"));
- return false;
- }
+ return false;
+ case OPERATOR_MULT:
+ mpfr_mul(val, left_val, right_val, GMP_RNDN);
break;
-
+ case OPERATOR_DIV:
+ if (mpfr_zero_p(right_val))
+ error_at(location, "division by zero");
+ mpfr_div(val, left_val, right_val, GMP_RNDN);
+ break;
+ case OPERATOR_MOD:
+ return false;
+ case OPERATOR_LSHIFT:
+ case OPERATOR_RSHIFT:
+ return false;
default:
gcc_unreachable();
}
- return true;
-}
-
-// Check types.
-
-void
-Binary_expression::do_check_types(Gogo*)
-{
- if (this->op_ != OPERATOR_LSHIFT && this->op_ != OPERATOR_RSHIFT)
+ Type* type = left_type;
+ if (type == NULL)
+ type = right_type;
+ else if (type != right_type && right_type != NULL)
{
- Type* type = this->left_->type();
-
- if (!Type::are_compatible_for_binop(type, this->right_->type()))
+ if (type->is_abstract())
+ type = right_type;
+ else if (!right_type->is_abstract())
{
- this->report_error(_("incompatible types in binary expression"));
- return;
+ // This looks like a type error which should be diagnosed
+ // elsewhere. Don't do anything here, to avoid an unhelpful
+ // chain of error messages.
+ return true;
}
+ }
- if (!type->is_error_type()
- && !this->right_->type()->is_error_type())
- {
- if (!Binary_expression::check_operator_type(this->op_, type,
- this->location()))
- this->set_is_error();
- }
- }
- else
+ if (type != NULL && !type->is_abstract())
{
- if (this->left_->type()->integer_type() == NULL
- && !this->left_->type()->is_error_type())
- this->report_error(_("shift of non-integer operand"));
-
- Type* shift_type = this->right_->type();
- if (!shift_type->is_error_type())
- {
- if (!shift_type->is_abstract()
- && (shift_type->integer_type() == NULL
- || !shift_type->integer_type()->is_unsigned()))
- this->report_error(_("shift count not unsigned integer"));
- else
- {
- mpz_t val;
- mpz_init(val);
- Type* type;
- if (this->right_->integer_constant_value(true, val, &type))
- {
- if (mpz_sgn(val) < 0)
- this->report_error(_("negative shift count"));
- }
- mpz_clear(val);
- }
- }
+ if ((type != left_type
+ && !Float_expression::check_constant(left_val, type, location))
+ || (type != right_type
+ && !Float_expression::check_constant(right_val, type,
+ location))
+ || !Float_expression::check_constant(val, type, location))
+ mpfr_set_ui(val, 0, GMP_RNDN);
}
-}
-
-// Copying a binary expression never requires incrementing a reference
-// count, but we do have to disable incrementing the reference count.
-Expression*
-Binary_expression::do_being_copied(Refcounts*, bool)
-{
- this->is_being_copied_ = true;
- return this;
+ return true;
}
-// A binary expression increments a reference count when adding
-// strings.
+// Lower a binary expression. We have to evaluate constant
+// expressions now, in order t implemented Go's unlimited precision
+// constants.
Expression*
-Binary_expression::do_note_decrements(Refcounts* refcounts)
-{
- if (this->op_ != OPERATOR_PLUS
- || !this->left_->type()->is_string_type()
- || this->is_being_copied_)
- return this;
- return Expression::make_refcount_adjust(refcounts,
- REFCOUNT_DECREMENT_NEW,
- this, false);
-}
-
-// Get a tree for a binary expression.
-
-tree
-Binary_expression::do_get_tree(Translate_context* context)
+Binary_expression::do_lower(Gogo*, int)
{
- tree left = this->left_->get_tree(context);
- tree right = this->right_->get_tree(context);
-
- if (left == error_mark_node || right == error_mark_node)
- return error_mark_node;
+ source_location location = this->location();
+ Operator op = this->op_;
+ Expression* left = this->left_;
+ Expression* right = this->right_;
- enum tree_code code;
- bool use_left_type = true;
- bool is_shift_op = false;
- switch (this->op_)
- {
- case OPERATOR_EQEQ:
- case OPERATOR_NOTEQ:
- case OPERATOR_LT:
- case OPERATOR_LE:
- case OPERATOR_GT:
- case OPERATOR_GE:
- return Expression::comparison_tree(context, this->op_,
- this->left_->type(), left,
- this->right_->type(), right,
- this->location());
+ const bool is_comparison = (op == OPERATOR_EQEQ
+ || op == OPERATOR_NOTEQ
+ || op == OPERATOR_LT
+ || op == OPERATOR_LE
+ || op == OPERATOR_GT
+ || op == OPERATOR_GE);
- case OPERATOR_OROR:
- code = TRUTH_ORIF_EXPR;
- use_left_type = false;
- break;
- case OPERATOR_ANDAND:
- code = TRUTH_ANDIF_EXPR;
- use_left_type = false;
- 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:
- // FIXME: Code depends on whether integer or floating point.
- code = TRUNC_DIV_EXPR;
- break;
- case OPERATOR_MOD:
- // FIXME: Code depends on whether integer or floating point.
- code = TRUNC_MOD_EXPR;
- break;
- case OPERATOR_LSHIFT:
- code = LSHIFT_EXPR;
- is_shift_op = true;
- break;
- case OPERATOR_RSHIFT:
- code = RSHIFT_EXPR;
- is_shift_op = true;
- break;
- case OPERATOR_AND:
- code = BIT_AND_EXPR;
- break;
- case OPERATOR_BITCLEAR:
- right = fold_build1(BIT_NOT_EXPR, TREE_TYPE(right), right);
- code = BIT_AND_EXPR;
- break;
- default:
- gcc_unreachable();
- }
+ // Integer constant expressions.
+ {
+ mpz_t left_val;
+ mpz_init(left_val);
+ Type* left_type;
+ mpz_t right_val;
+ mpz_init(right_val);
+ Type* right_type;
+ if (left->integer_constant_value(false, left_val, &left_type)
+ && right->integer_constant_value(false, right_val, &right_type))
+ {
+ Expression* ret = NULL;
+ if (left_type != right_type
+ && left_type != NULL
+ && right_type != NULL
+ && left_type->base() != right_type->base()
+ && op != OPERATOR_LSHIFT
+ && op != OPERATOR_RSHIFT)
+ {
+ // May be a type error--let it be diagnosed later.
+ }
+ else if (is_comparison)
+ {
+ bool b = Binary_expression::compare_integer(op, left_val,
+ right_val);
+ ret = Expression::make_cast(Type::lookup_bool_type(),
+ Expression::make_boolean(b, location),
+ location);
+ }
+ else
+ {
+ mpz_t val;
+ mpz_init(val);
- tree type = use_left_type ? TREE_TYPE(left) : TREE_TYPE(right);
+ if (Binary_expression::eval_integer(op, left_type, left_val,
+ right_type, right_val,
+ location, val))
+ {
+ gcc_assert(op != OPERATOR_OROR && op != OPERATOR_ANDAND);
+ Type* type;
+ if (op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT)
+ type = left_type;
+ else if (left_type == NULL)
+ type = right_type;
+ else if (right_type == NULL)
+ type = left_type;
+ else if (!left_type->is_abstract()
+ && left_type->named_type() != NULL)
+ type = left_type;
+ else if (!right_type->is_abstract()
+ && right_type->named_type() != NULL)
+ type = right_type;
+ else if (!left_type->is_abstract())
+ type = left_type;
+ else if (!right_type->is_abstract())
+ type = right_type;
+ else if (left_type->float_type() != NULL)
+ type = left_type;
+ else if (right_type->float_type() != NULL)
+ type = right_type;
+ else
+ type = left_type;
+ ret = Expression::make_integer(&val, type, location);
+ }
- if (this->left_->type()->is_string_type())
- {
- gcc_assert(this->op_ == OPERATOR_PLUS);
- tree string_type = Type::make_string_type()->get_tree(context->gogo());
- static tree string_plus_decl;
- return Gogo::call_builtin(&string_plus_decl,
- this->location(),
- "__go_string_plus",
- 2,
- string_type,
- string_type,
- left,
- string_type,
- right);
- }
+ mpz_clear(val);
+ }
- tree compute_type = excess_precision_type(type);
- if (compute_type != NULL_TREE)
- {
- left = convert_to_real(compute_type, left);
- right = convert_to_real(compute_type, right);
- }
+ if (ret != NULL)
+ {
+ mpz_clear(right_val);
+ mpz_clear(left_val);
+ return ret;
+ }
+ }
+ mpz_clear(right_val);
+ mpz_clear(left_val);
+ }
- tree eval_saved = NULL_TREE;
- if (is_shift_op)
- {
- if (!DECL_P(left))
- left = save_expr(left);
- if (!DECL_P(right))
- right = save_expr(right);
- // Make sure the values are evaluated.
- eval_saved = fold_build2_loc(this->location(), COMPOUND_EXPR,
- void_type_node, left, right);
+ // Floating point constant expressions.
+ {
+ mpfr_t left_val;
+ mpfr_init(left_val);
+ Type* left_type;
+ mpfr_t right_val;
+ mpfr_init(right_val);
+ Type* right_type;
+ if (left->float_constant_value(left_val, &left_type)
+ && right->float_constant_value(right_val, &right_type))
+ {
+ Expression* ret = NULL;
+ if (left_type != right_type
+ && left_type != NULL
+ && right_type != NULL
+ && left_type->base() != right_type->base()
+ && op != OPERATOR_LSHIFT
+ && op != OPERATOR_RSHIFT)
+ {
+ // May be a type error--let it be diagnosed later.
+ }
+ else if (is_comparison)
+ {
+ bool b = Binary_expression::compare_float(op,
+ (left_type != NULL
+ ? left_type
+ : right_type),
+ left_val, right_val);
+ ret = Expression::make_boolean(b, location);
+ }
+ else
+ {
+ mpfr_t val;
+ mpfr_init(val);
+
+ if (Binary_expression::eval_float(op, left_type, left_val,
+ right_type, right_val, val,
+ location))
+ {
+ gcc_assert(op != OPERATOR_OROR && op != OPERATOR_ANDAND
+ && op != OPERATOR_LSHIFT && op != OPERATOR_RSHIFT);
+ Type* type;
+ if (left_type == NULL)
+ type = right_type;
+ else if (right_type == NULL)
+ type = left_type;
+ else if (!left_type->is_abstract()
+ && left_type->named_type() != NULL)
+ type = left_type;
+ else if (!right_type->is_abstract()
+ && right_type->named_type() != NULL)
+ type = right_type;
+ else if (!left_type->is_abstract())
+ type = left_type;
+ else if (!right_type->is_abstract())
+ type = right_type;
+ else if (left_type->float_type() != NULL)
+ type = left_type;
+ else if (right_type->float_type() != NULL)
+ type = right_type;
+ else
+ type = left_type;
+ ret = Expression::make_float(&val, type, location);
+ }
+
+ mpfr_clear(val);
+ }
+
+ if (ret != NULL)
+ {
+ mpfr_clear(right_val);
+ mpfr_clear(left_val);
+ return ret;
+ }
+ }
+ mpfr_clear(right_val);
+ mpfr_clear(left_val);
+ }
+
+ // String constant expressions.
+ if (op == OPERATOR_PLUS
+ && 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))
+ return Expression::make_string(left_string + right_string, location);
}
- tree ret = fold_build2_loc(this->location(),
- code,
- compute_type != NULL_TREE ? compute_type : type,
- left, right);
+ return this;
+}
- if (compute_type != NULL_TREE)
- ret = convert_to_real(type, ret);
+// Return the integer constant value, if it has one.
- // In Go, a shift larger than the size of the type is well-defined.
- // This is not true in GENERIC, so we need to insert a conditional.
- if (is_shift_op)
+bool
+Binary_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
+ Type** ptype) const
+{
+ mpz_t left_val;
+ mpz_init(left_val);
+ Type* left_type;
+ if (!this->left_->integer_constant_value(iota_is_constant, left_val,
+ &left_type))
{
- gcc_assert(INTEGRAL_TYPE_P(TREE_TYPE(left)));
- gcc_assert(this->left_->type()->integer_type() != NULL);
- int bits = TYPE_PRECISION(TREE_TYPE(left));
+ mpz_clear(left_val);
+ return false;
+ }
- tree compare = fold_build2(LT_EXPR, boolean_type_node, right,
- build_int_cst_type(TREE_TYPE(right), bits));
+ mpz_t right_val;
+ mpz_init(right_val);
+ Type* right_type;
+ if (!this->right_->integer_constant_value(iota_is_constant, right_val,
+ &right_type))
+ {
+ mpz_clear(right_val);
+ mpz_clear(left_val);
+ return false;
+ }
- tree overflow_result = fold_convert_loc(this->location(),
- TREE_TYPE(left),
- integer_zero_node);
- if (this->op_ == OPERATOR_RSHIFT
- && !this->left_->type()->integer_type()->is_unsigned())
- {
- tree neg = fold_build2_loc(this->location(), LT_EXPR,
- boolean_type_node, left,
- fold_convert_loc(this->location(),
- TREE_TYPE(left),
- integer_zero_node));
- tree neg_one = fold_build2_loc(this->location(),
- MINUS_EXPR, TREE_TYPE(left),
- fold_convert_loc(this->location(),
- TREE_TYPE(left),
- integer_zero_node),
- fold_convert_loc(this->location(),
- TREE_TYPE(left),
- integer_one_node));
- overflow_result = fold_build3_loc(this->location(), COND_EXPR,
- TREE_TYPE(left), neg, neg_one,
- overflow_result);
- }
+ bool ret;
+ if (left_type != right_type
+ && left_type != NULL
+ && right_type != NULL
+ && left_type->base() != right_type->base()
+ && this->op_ != OPERATOR_RSHIFT
+ && this->op_ != OPERATOR_LSHIFT)
+ ret = false;
+ else
+ ret = Binary_expression::eval_integer(this->op_, left_type, left_val,
+ right_type, right_val,
+ this->location(), val);
- ret = fold_build3_loc(this->location(), COND_EXPR, TREE_TYPE(left),
- compare, ret, overflow_result);
+ mpz_clear(right_val);
+ mpz_clear(left_val);
- ret = fold_build2_loc(this->location(), COMPOUND_EXPR,
- TREE_TYPE(ret), eval_saved, ret);
+ if (ret)
+ *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
+ ? right_type
+ : left_type);
+
+ return ret;
+}
+
+// Return the floating point constant value, if it has one.
+
+bool
+Binary_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
+{
+ mpfr_t left_val;
+ mpfr_init(left_val);
+ Type* left_type;
+ if (!this->left_->float_constant_value(left_val, &left_type))
+ {
+ mpfr_clear(left_val);
+ return false;
+ }
+
+ mpfr_t right_val;
+ mpfr_init(right_val);
+ Type* right_type;
+ if (!this->right_->float_constant_value(right_val, &right_type))
+ {
+ mpfr_clear(right_val);
+ mpfr_clear(left_val);
+ return false;
}
+ bool ret;
+ if (left_type != right_type
+ && left_type != NULL
+ && right_type != NULL
+ && left_type->base() != right_type->base())
+ ret = false;
+ else
+ ret = Binary_expression::eval_float(this->op_, left_type, left_val,
+ right_type, right_val,
+ val, this->location());
+
+ mpfr_clear(left_val);
+ mpfr_clear(right_val);
+
+ if (ret)
+ *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
+ ? right_type
+ : left_type);
+
return ret;
}
-// Export a binary expression.
+// Note that the value is being discarded.
void
-Binary_expression::do_export(Export* exp) const
+Binary_expression::do_discarding_value()
+{
+ if (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_ANDAND)
+ this->right_->discarding_value();
+ else
+ this->warn_unused_value();
+}
+
+// Get type.
+
+Type*
+Binary_expression::do_type()
{
- 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;
+ return Type::lookup_bool_type();
+
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;
+ {
+ Type* left_type = this->left_->type();
+ Type* right_type = this->right_->type();
+ if (!left_type->is_abstract() && left_type->named_type() != NULL)
+ return left_type;
+ else if (!right_type->is_abstract() && right_type->named_type() != NULL)
+ return right_type;
+ else if (!left_type->is_abstract())
+ return left_type;
+ else if (!right_type->is_abstract())
+ return right_type;
+ else if (left_type->float_type() != NULL)
+ return left_type;
+ else if (right_type->float_type() != NULL)
+ return right_type;
+ else
+ return left_type;
+ }
+
+ case OPERATOR_LSHIFT:
+ case OPERATOR_RSHIFT:
+ return this->left_->type();
+
default:
gcc_unreachable();
}
- this->right_->export_expression(exp);
- exp->write_c_string(")");
}
-// Import a binary expression.
+// Set type for a binary expression.
-Expression*
-Binary_expression::do_import(Import* imp)
+void
+Binary_expression::do_determine_type(const Type_context* context)
{
- imp->require_c_string("(");
+ Type* tleft = this->left_->type();
+ Type* tright = this->right_->type();
- Expression* left = Expression::import_expression(imp);
+ // Both sides should have the same type, except for the shift
+ // operations. For a comparison, we should ignore the incoming
+ // type.
- 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(" & "))
+ 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)
{
- op = OPERATOR_AND;
- imp->advance(3);
+ // In a comparison, the context does not determine the types of
+ // the operands.
+ subcontext.type = NULL;
}
- else if (imp->match_c_string(" &^ "))
+
+ // Set the context for the left hand operand.
+ if (is_shift_op)
{
- op = OPERATOR_BITCLEAR;
- imp->advance(4);
+ // The right hand operand plays no role in determining the type
+ // of the left hand operand. A shift of an abstract integer in
+ // a string context gets special treatment, which may be a
+ // language bug.
+ if (subcontext.type != NULL
+ && subcontext.type->is_string_type()
+ && tleft->is_abstract())
+ error_at(this->location(), "shift of non-integer operand");
}
- else
+ else if (!tleft->is_abstract())
+ subcontext.type = tleft;
+ else if (!tright->is_abstract())
+ subcontext.type = tright;
+ else if (subcontext.type == NULL)
{
- error_at(imp->location(), "unrecognized binary operator");
- return Expression::make_error(imp->location());
+ if ((tleft->integer_type() != NULL && tright->integer_type() != NULL)
+ || (tleft->float_type() != NULL && tright->float_type() != NULL))
+ {
+ // Both sides have an abstract integer type or both sides
+ // have an abstract float type. Just let CONTEXT determine
+ // whether they may remain abstract or not.
+ }
+ else
+ {
+ // Both sides are abstract, but one is integer and one is
+ // floating point. Convert the abstract integer to floating
+ // point.
+ subcontext.type = tleft->float_type() != NULL ? tleft : tright;
+ }
}
- Expression* right = Expression::import_expression(imp);
-
- imp->require_c_string(")");
-
- return Expression::make_binary(op, left, right, imp->location());
-}
+ this->left_->determine_type(&subcontext);
-// Make a binary expression.
+ // The context for the right hand operand is the same as for the
+ // left hand operand, except for a shift operator.
+ if (is_shift_op)
+ {
+ subcontext.type = Type::lookup_integer_type("uint");
+ subcontext.may_be_abstract = false;
+ }
-Expression*
-Expression::make_binary(Operator op, Expression* left, Expression* right,
- source_location location)
-{
- return new Binary_expression(op, left, right, location);
+ this->right_->determine_type(&subcontext);
}
-// Implement a comparison.
+// Report an error if the binary operator OP does not support TYPE.
+// Return whether the operation is OK. This should not be used for
+// shift.
-tree
-Expression::comparison_tree(Translate_context* context, Operator op,
- Type* left_type, tree left_tree,
- Type* right_type, tree right_tree,
- source_location location)
+bool
+Binary_expression::check_operator_type(Operator op, Type* type,
+ source_location location)
{
- enum tree_code code;
switch (op)
{
- case OPERATOR_EQEQ:
- code = EQ_EXPR;
+ 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:
- code = NE_EXPR;
- break;
- case OPERATOR_LT:
- code = LT_EXPR;
- break;
- case OPERATOR_LE:
- code = LE_EXPR;
- break;
- case OPERATOR_GT:
- code = GT_EXPR;
+ if (type->integer_type() == NULL
+ && type->float_type() == NULL
+ && !type->is_string_type()
+ && type->points_to() == NULL
+ && !type->is_nil_type()
+ && !type->is_boolean_type()
+ && type->interface_type() == NULL
+ && (type->array_type() == NULL
+ || type->array_type()->length() != NULL)
+ && type->map_type() == NULL
+ && type->channel_type() == NULL
+ && type->function_type() == NULL)
+ {
+ error_at(location,
+ _("expected integer, floating, string, pointer, boolean, "
+ "interface, slice, map, channel, or function type"));
+ return false;
+ }
break;
+
+ case OPERATOR_LT:
+ case OPERATOR_LE:
+ case OPERATOR_GT:
case OPERATOR_GE:
- code = GE_EXPR;
+ case OPERATOR_PLUS:
+ case OPERATOR_PLUSEQ:
+ if (type->integer_type() == NULL
+ && type->float_type() == NULL
+ && !type->is_string_type())
+ {
+ error_at(location,
+ _("expected integer, floating, or string type"));
+ return false;
+ }
break;
- default:
- gcc_unreachable();
- }
-
- if (left_type->is_string_type())
- {
- gcc_assert(right_type->is_string_type());
- tree string_type = Type::make_string_type()->get_tree(context->gogo());
- static tree string_compare_decl;
- left_tree = Gogo::call_builtin(&string_compare_decl,
- location,
- "__go_strcmp",
- 2,
- integer_type_node,
- string_type,
- left_tree,
- string_type,
- right_tree);
- right_tree = build_int_cst_type(integer_type_node, 0);
- }
-
- if (left_type->interface_type() != NULL
- && right_type->interface_type() != NULL)
- {
- static tree interface_compare_decl;
- left_tree = Gogo::call_builtin(&interface_compare_decl,
- location,
- "__go_interface_compare",
- 2,
- integer_type_node,
- const_ptr_type_node,
- fold_convert(const_ptr_type_node,
- left_tree),
- const_ptr_type_node,
- fold_convert(const_ptr_type_node,
- right_tree));
- right_tree = build_int_cst_type(integer_type_node, 0);
- }
-
- if (left_type->is_nil_type()
- && (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ))
- {
- std::swap(left_type, right_type);
- std::swap(left_tree, right_tree);
- }
- if (right_type->is_nil_type())
- {
- if (left_type->array_type() != NULL
- && left_type->array_type()->length() == NULL)
+ 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)
{
- Array_type* at = left_type->array_type();
- left_tree = at->value_pointer_tree(context->gogo(), left_tree);
- right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
+ error_at(location, _("expected integer or floating type"));
+ return false;
}
- else
+ 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)
{
- gcc_assert(POINTER_TYPE_P(TREE_TYPE(left_tree)));
- right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
+ error_at(location, _("expected integer type"));
+ return false;
}
- }
-
- tree ret = fold_build2(code, boolean_type_node, left_tree, right_tree);
- if (CAN_HAVE_LOCATION_P(ret))
- SET_EXPR_LOCATION(ret, location);
- return ret;
-}
-
-// Class Bound_method_expression.
-
-// Traversal.
-
-int
-Bound_method_expression::do_traverse(Traverse* traverse)
-{
- if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- return Expression::traverse(&this->method_, traverse);
-}
+ break;
-// Return the type of a bound method expression. The type of this
-// object is really the type of the method with no receiver. We
-// should be able to get away with just returning the type of the
-// method.
+ default:
+ gcc_unreachable();
+ }
-Type*
-Bound_method_expression::do_type()
-{
- return this->method_->type();
+ return true;
}
-// Determine the types of a method expression.
+// Check types.
void
-Bound_method_expression::do_determine_type(const Type_context*)
+Binary_expression::do_check_types(Gogo*)
{
- this->method_->determine_type_no_context();
- Type* mtype = this->method_->type();
- Function_type* fntype = mtype == NULL ? NULL : mtype->function_type();
- if (fntype == NULL || !fntype->is_method())
- this->expr_->determine_type_no_context();
- else
+ if (this->op_ != OPERATOR_LSHIFT && this->op_ != OPERATOR_RSHIFT)
{
- Type_context subcontext(fntype->receiver()->type(), false);
- this->expr_->determine_type(&subcontext);
- }
-}
+ Type* type = this->left_->type();
-// Check the types of a method expression.
+ if (!Type::are_compatible_for_binop(type, this->right_->type()))
+ {
+ this->report_error(_("incompatible types in binary expression"));
+ return;
+ }
-void
-Bound_method_expression::do_check_types(Gogo*)
-{
- Type* type = this->method_->type()->deref();
- if (type == NULL
- || type->function_type() == NULL
- || !type->function_type()->is_method())
- this->report_error(_("object is not a method"));
+ if (!type->is_error_type()
+ && !this->right_->type()->is_error_type())
+ {
+ if (!Binary_expression::check_operator_type(this->op_, type,
+ this->location()))
+ this->set_is_error();
+ }
+ }
else
{
- Type* rtype = type->function_type()->receiver()->type()->deref();
- Type* etype = (this->expr_type_ != NULL
- ? this->expr_type_
- : this->expr_->type());
- etype = etype->deref();
- if (!Type::are_identical(rtype, etype))
- this->report_error(_("method type does not match object type"));
+ if (this->left_->type()->integer_type() == NULL
+ && !this->left_->type()->is_error_type())
+ this->report_error(_("shift of non-integer operand"));
+
+ Type* shift_type = this->right_->type();
+ if (!shift_type->is_error_type())
+ {
+ if (!shift_type->is_abstract()
+ && (shift_type->integer_type() == NULL
+ || !shift_type->integer_type()->is_unsigned()))
+ this->report_error(_("shift count not unsigned integer"));
+ else
+ {
+ mpz_t val;
+ mpz_init(val);
+ Type* type;
+ if (this->right_->integer_constant_value(true, val, &type))
+ {
+ if (mpz_sgn(val) < 0)
+ this->report_error(_("negative shift count"));
+ }
+ mpz_clear(val);
+ }
+ }
}
}
-// Get the tree for a method expression. There is no standard tree
-// representation for this. The only places it may currently be used
-// are in a Call_expression or a Go_statement, which will take it
-// apart directly. So this has nothing to do at present.
+// Copying a binary expression never requires incrementing a reference
+// count, but we do have to disable incrementing the reference count.
-tree
-Bound_method_expression::do_get_tree(Translate_context*)
+Expression*
+Binary_expression::do_being_copied(Refcounts*, bool)
{
- gcc_unreachable();
+ this->is_being_copied_ = true;
+ return this;
}
-// Make a method expression.
+// A binary expression increments a reference count when adding
+// strings.
-Bound_method_expression*
-Expression::make_bound_method(Expression* expr, Expression* method,
- source_location location)
+Expression*
+Binary_expression::do_note_decrements(Refcounts* refcounts)
{
- return new Bound_method_expression(expr, method, location);
+ if (this->op_ != OPERATOR_PLUS
+ || !this->left_->type()->is_string_type()
+ || this->is_being_copied_)
+ return this;
+ return Expression::make_refcount_adjust(refcounts,
+ REFCOUNT_DECREMENT_NEW,
+ this, false);
}
-// Class Builtin_call_expression. This is used for a call to a
-// builtin function.
+// Get a tree for a binary expression.
-class Builtin_call_expression : public Call_expression
+tree
+Binary_expression::do_get_tree(Translate_context* context)
{
- public:
- Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
- source_location location);
+ tree left = this->left_->get_tree(context);
+ tree right = this->right_->get_tree(context);
- protected:
- // This overrides Call_expression::do_lower.
- Expression*
- do_lower(Gogo*, int);
+ if (left == error_mark_node || right == error_mark_node)
+ return error_mark_node;
- bool
- do_is_constant() const;
+ enum tree_code code;
+ bool use_left_type = true;
+ bool is_shift_op = false;
+ switch (this->op_)
+ {
+ case OPERATOR_EQEQ:
+ case OPERATOR_NOTEQ:
+ case OPERATOR_LT:
+ case OPERATOR_LE:
+ case OPERATOR_GT:
+ case OPERATOR_GE:
+ return Expression::comparison_tree(context, this->op_,
+ this->left_->type(), left,
+ this->right_->type(), right,
+ this->location());
- bool
- do_integer_constant_value(bool, mpz_t, Type**) const;
+ case OPERATOR_OROR:
+ code = TRUTH_ORIF_EXPR;
+ use_left_type = false;
+ break;
+ case OPERATOR_ANDAND:
+ code = TRUTH_ANDIF_EXPR;
+ use_left_type = false;
+ 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:
+ // FIXME: Code depends on whether integer or floating point.
+ code = TRUNC_DIV_EXPR;
+ break;
+ case OPERATOR_MOD:
+ // FIXME: Code depends on whether integer or floating point.
+ code = TRUNC_MOD_EXPR;
+ break;
+ case OPERATOR_LSHIFT:
+ code = LSHIFT_EXPR;
+ is_shift_op = true;
+ break;
+ case OPERATOR_RSHIFT:
+ code = RSHIFT_EXPR;
+ is_shift_op = true;
+ break;
+ case OPERATOR_AND:
+ code = BIT_AND_EXPR;
+ break;
+ case OPERATOR_BITCLEAR:
+ right = fold_build1(BIT_NOT_EXPR, TREE_TYPE(right), right);
+ code = BIT_AND_EXPR;
+ break;
+ default:
+ gcc_unreachable();
+ }
- Type*
- do_type();
+ tree type = use_left_type ? TREE_TYPE(left) : TREE_TYPE(right);
- void
- do_determine_type(const Type_context*);
+ if (this->left_->type()->is_string_type())
+ {
+ gcc_assert(this->op_ == OPERATOR_PLUS);
+ tree string_type = Type::make_string_type()->get_tree(context->gogo());
+ static tree string_plus_decl;
+ return Gogo::call_builtin(&string_plus_decl,
+ this->location(),
+ "__go_string_plus",
+ 2,
+ string_type,
+ string_type,
+ left,
+ string_type,
+ right);
+ }
- void
- do_check_types(Gogo*);
+ tree compute_type = excess_precision_type(type);
+ if (compute_type != NULL_TREE)
+ {
+ left = convert_to_real(compute_type, left);
+ right = convert_to_real(compute_type, right);
+ }
- Expression*
- do_copy()
- {
- return new Builtin_call_expression(this->gogo_, this->fn()->copy(),
- this->args()->copy(),
- this->location());
- }
+ tree eval_saved = NULL_TREE;
+ if (is_shift_op)
+ {
+ if (!DECL_P(left))
+ left = save_expr(left);
+ if (!DECL_P(right))
+ right = save_expr(right);
+ // Make sure the values are evaluated.
+ eval_saved = fold_build2_loc(this->location(), COMPOUND_EXPR,
+ void_type_node, left, right);
+ }
- tree
- do_get_tree(Translate_context*);
+ tree ret = fold_build2_loc(this->location(),
+ code,
+ compute_type != NULL_TREE ? compute_type : type,
+ left, right);
- void
- do_export(Export*) const;
+ if (compute_type != NULL_TREE)
+ ret = convert_to_real(type, ret);
- private:
- // The builtin functions.
- enum Builtin_function_code
+ // In Go, a shift larger than the size of the type is well-defined.
+ // This is not true in GENERIC, so we need to insert a conditional.
+ if (is_shift_op)
{
- BUILTIN_INVALID,
-
- // Predeclared builtin functions.
- BUILTIN_CAP,
- BUILTIN_CLOSE,
- BUILTIN_CLOSED,
- BUILTIN_LEN,
- BUILTIN_MAKE,
- BUILTIN_NEW,
- BUILTIN_PANIC,
- BUILTIN_PANICLN,
- BUILTIN_PRINT,
- BUILTIN_PRINTLN,
+ gcc_assert(INTEGRAL_TYPE_P(TREE_TYPE(left)));
+ gcc_assert(this->left_->type()->integer_type() != NULL);
+ int bits = TYPE_PRECISION(TREE_TYPE(left));
- // Builtin functions from the unsafe package.
- BUILTIN_ALIGNOF,
- BUILTIN_OFFSETOF,
- BUILTIN_SIZEOF
- };
+ tree compare = fold_build2(LT_EXPR, boolean_type_node, right,
+ build_int_cst_type(TREE_TYPE(right), bits));
- Expression*
- one_arg() const;
+ tree overflow_result = fold_convert_loc(this->location(),
+ TREE_TYPE(left),
+ integer_zero_node);
+ if (this->op_ == OPERATOR_RSHIFT
+ && !this->left_->type()->integer_type()->is_unsigned())
+ {
+ tree neg = fold_build2_loc(this->location(), LT_EXPR,
+ boolean_type_node, left,
+ fold_convert_loc(this->location(),
+ TREE_TYPE(left),
+ integer_zero_node));
+ tree neg_one = fold_build2_loc(this->location(),
+ MINUS_EXPR, TREE_TYPE(left),
+ fold_convert_loc(this->location(),
+ TREE_TYPE(left),
+ integer_zero_node),
+ fold_convert_loc(this->location(),
+ TREE_TYPE(left),
+ integer_one_node));
+ overflow_result = fold_build3_loc(this->location(), COND_EXPR,
+ TREE_TYPE(left), neg, neg_one,
+ overflow_result);
+ }
- bool
- check_one_arg();
+ ret = fold_build3_loc(this->location(), COND_EXPR, TREE_TYPE(left),
+ compare, ret, overflow_result);
- // 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_;
-};
+ ret = fold_build2_loc(this->location(), COMPOUND_EXPR,
+ TREE_TYPE(ret), eval_saved, ret);
+ }
-Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
- Expression* fn,
- Expression_list* args,
- source_location location)
- : Call_expression(fn, args, 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")
- this->code_ = BUILTIN_CAP;
- else if (name == "close")
- this->code_ = BUILTIN_CLOSE;
- else if (name == "closed")
- this->code_ = BUILTIN_CLOSED;
- 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 == "panicln")
- this->code_ = BUILTIN_PANICLN;
- else if (name == "print")
- this->code_ = BUILTIN_PRINT;
- else if (name == "println")
- this->code_ = BUILTIN_PRINTLN;
- 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
- gcc_unreachable();
+ return ret;
}
-// Lower a builtin call expression. This turns new and make into
-// specific expressions.
+// Export a binary expression.
-Expression*
-Builtin_call_expression::do_lower(Gogo*, int)
+void
+Binary_expression::do_export(Export* exp) const
{
- if (this->code_ == BUILTIN_NEW)
+ exp->write_c_string("(");
+ this->left_->export_expression(exp);
+ switch (this->op_)
{
- 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(), this->location());
- }
- }
- else if (this->code_ == BUILTIN_MAKE)
- {
- const Expression_list* args = this->args();
- if (args == NULL || args->size() < 1)
- this->report_error(_("not enough arguments"));
- else
- {
- Expression* arg = args->front();
- if (!arg->is_type_expression())
- {
- error_at(arg->location(), _("expected type"));
- this->set_is_error();
- }
- else
- {
- Expression_list* newargs;
- if (args->size() == 1)
- newargs = NULL;
- else
- {
- newargs = new Expression_list();
- Expression_list::const_iterator p = args->begin();
- ++p;
- for (; p != args->end(); ++p)
- newargs->push_back(*p);
- }
- return Expression::make_make(arg->type(), newargs,
- this->location());
- }
- }
+ 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:
+ gcc_unreachable();
}
-
- return this;
+ this->right_->export_expression(exp);
+ exp->write_c_string(")");
}
-// Return a single argument, or NULL if there isn't one.
+// Import a binary expression.
Expression*
-Builtin_call_expression::one_arg() const
+Binary_expression::do_import(Import* imp)
{
- const Expression_list* args = this->args();
- if (args->size() != 1)
- return NULL;
- return args->front();
-}
+ imp->require_c_string("(");
-// Return whether this is constant: len of a string, or len or cap of
-// a fixed array, or unsafe.Sizeof, unsafe.Offsetof, unsafe.Alignof.
+ Expression* left = Expression::import_expression(imp);
-bool
-Builtin_call_expression::do_is_constant() const
-{
- if (this->code_ == BUILTIN_LEN
- || this->code_ == BUILTIN_CAP)
+ Operator op;
+ if (imp->match_c_string(" || "))
{
- 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_open_array_type())
- arg_type = arg_type->points_to();
-
- if (arg_type->array_type() != NULL
- && arg_type->array_type()->length() != NULL)
- return arg_type->array_type()->length()->is_constant();
-
- if (this->code_ == BUILTIN_LEN && arg_type->is_string_type())
- return arg->is_constant();
+ op = OPERATOR_OROR;
+ imp->advance(4);
}
- else if (this->code_ == BUILTIN_SIZEOF
- || this->code_ == BUILTIN_ALIGNOF)
- return this->one_arg() != NULL;
- else if (this->code_ == BUILTIN_OFFSETOF)
+ else if (imp->match_c_string(" && "))
{
- Expression* arg = this->one_arg();
- if (arg == NULL)
- return false;
- return arg->field_reference_expression() != NULL;
+ op = OPERATOR_ANDAND;
+ imp->advance(4);
}
- return false;
-}
-
-// Return an integer constant value if possible.
-
-bool
-Builtin_call_expression::do_integer_constant_value(bool iota_is_constant,
- mpz_t val,
- Type** ptype) const
-{
- if (this->code_ == BUILTIN_LEN
- || this->code_ == BUILTIN_CAP)
+ else if (imp->match_c_string(" == "))
{
- 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()
- && arg->classification() == EXPRESSION_STRING)
- {
- String_expression* se = static_cast<String_expression*>(arg);
- mpz_set_ui(val, se->val().length());
- *ptype = Type::lookup_integer_type("int");
- return true;
- }
-
- if (arg_type->points_to() != NULL
- && arg_type->points_to()->array_type() != NULL
- && !arg_type->points_to()->is_open_array_type())
- arg_type = arg_type->points_to();
-
- if (arg_type->array_type() != NULL
- && arg_type->array_type()->length() != NULL)
- {
- Expression* e = arg_type->array_type()->length();
- if (e->integer_constant_value(iota_is_constant, val, ptype))
- {
- *ptype = Type::lookup_integer_type("int");
- return true;
- }
- }
+ op = OPERATOR_EQEQ;
+ imp->advance(4);
}
- else if (this->code_ == BUILTIN_SIZEOF
- || this->code_ == BUILTIN_ALIGNOF)
+ else if (imp->match_c_string(" != "))
{
- Expression* arg = this->one_arg();
- if (arg == NULL)
- return false;
- Type* arg_type = arg->type();
- if (arg_type->is_error_type())
- return false;
- if (arg_type->is_abstract())
- return false;
- tree arg_type_tree = arg_type->get_tree(this->gogo_);
- unsigned long val_long;
- if (this->code_ == BUILTIN_SIZEOF)
- {
- tree type_size = TYPE_SIZE_UNIT(arg_type_tree);
- gcc_assert(TREE_CODE(type_size) == INTEGER_CST);
- if (TREE_INT_CST_HIGH(type_size) != 0)
- return false;
- unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW(type_size);
- val_long = static_cast<unsigned long>(val_wide);
- if (val_long != val_wide)
- return false;
- }
- else if (this->code_ == BUILTIN_ALIGNOF)
- {
- val_long = TYPE_ALIGN(arg_type_tree);
- if (arg->field_reference_expression() != NULL)
- {
- // Calling unsafe.Alignof(s.f) returns the alignment of
- // the type of f when it is used as a field in a struct.
-#ifdef BIGGEST_FIELD_ALIGNMENT
- if (val_long > BIGGEST_FIELD_ALIGNMENT)
- val_long = BIGGEST_FIELD_ALIGNMENT;
-#endif
-#ifdef ADJUST_FIELD_ALIGN
- tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, NULL,
- arg_type_tree);
- val_long = ADJUST_FIELD_ALIGN(field, val_long);
-#endif
- }
- val_long /= BITS_PER_UNIT;
- }
- else
- gcc_unreachable();
- mpz_set_ui(val, val_long);
- *ptype = NULL;
- return true;
+ op = OPERATOR_NOTEQ;
+ imp->advance(4);
}
- else if (this->code_ == BUILTIN_OFFSETOF)
+ else if (imp->match_c_string(" < "))
{
- Expression* arg = this->one_arg();
- if (arg == NULL)
- return false;
- Field_reference_expression* farg = arg->field_reference_expression();
- if (farg == NULL)
- return false;
- Expression* struct_expr = farg->expr();
- Type* st = struct_expr->type();
- if (st->struct_type() == NULL)
- return false;
- tree struct_tree = st->get_tree(this->gogo_);
- gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE);
- tree field = TYPE_FIELDS(struct_tree);
- for (unsigned int index = farg->field_index(); index > 0; --index)
- {
- field = TREE_CHAIN(field);
- gcc_assert(field != NULL_TREE);
- }
- HOST_WIDE_INT offset_wide = int_byte_position (field);
- if (offset_wide < 0)
- return false;
- unsigned long offset_long = static_cast<unsigned long>(offset_wide);
- if (offset_long != static_cast<unsigned HOST_WIDE_INT>(offset_wide))
- return false;
- mpz_set_ui(val, offset_long);
- return true;
+ op = OPERATOR_LT;
+ imp->advance(3);
}
- return false;
-}
-
-// Return the type.
-
-Type*
-Builtin_call_expression::do_type()
-{
- switch (this->code_)
+ else if (imp->match_c_string(" <= "))
{
- case BUILTIN_INVALID:
- default:
- gcc_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());
- }
+ 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());
+ }
- case BUILTIN_CAP:
- case BUILTIN_LEN:
- case BUILTIN_ALIGNOF:
- case BUILTIN_OFFSETOF:
- case BUILTIN_SIZEOF:
- return Type::lookup_integer_type("int");
+ Expression* right = Expression::import_expression(imp);
- case BUILTIN_CLOSE:
- case BUILTIN_PANIC:
- case BUILTIN_PANICLN:
- case BUILTIN_PRINT:
- case BUILTIN_PRINTLN:
- return Type::make_void_type();
+ imp->require_c_string(")");
- case BUILTIN_CLOSED:
- return Type::lookup_bool_type();
- }
+ return Expression::make_binary(op, left, right, imp->location());
}
-// Determine the type.
+// Make a binary expression.
-void
-Builtin_call_expression::do_determine_type(const Type_context*)
+Expression*
+Expression::make_binary(Operator op, Expression* left, Expression* right,
+ source_location location)
{
- this->fn()->determine_type_no_context();
-
- Type_context subcontext(NULL, true);
- const Expression_list* args = this->args();
- if (args != NULL)
- {
- for (Expression_list::const_iterator pa = args->begin();
- pa != args->end();
- ++pa)
- (*pa)->determine_type(&subcontext);
- }
+ return new Binary_expression(op, left, right, location);
}
-// If there is exactly one argument, return true. Otherwise give an
-// error message and return false.
+// Implement a comparison.
-bool
-Builtin_call_expression::check_one_arg()
+tree
+Expression::comparison_tree(Translate_context* context, Operator op,
+ Type* left_type, tree left_tree,
+ Type* right_type, tree right_tree,
+ source_location location)
{
- const Expression_list* args = this->args();
- if (args == NULL || args->size() < 1)
+ enum tree_code code;
+ switch (op)
{
- this->report_error(_("not enough arguments"));
- return false;
+ 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;
+ default:
+ gcc_unreachable();
}
- else if (args->size() > 1)
+
+ if (left_type->is_string_type())
{
- this->report_error(_("too many arguments"));
- return false;
+ gcc_assert(right_type->is_string_type());
+ tree string_type = Type::make_string_type()->get_tree(context->gogo());
+ static tree string_compare_decl;
+ left_tree = Gogo::call_builtin(&string_compare_decl,
+ location,
+ "__go_strcmp",
+ 2,
+ integer_type_node,
+ string_type,
+ left_tree,
+ string_type,
+ right_tree);
+ right_tree = build_int_cst_type(integer_type_node, 0);
}
- if (args->front()->is_error_expression()
- || args->front()->type()->is_error_type())
+
+ if (left_type->interface_type() != NULL
+ && right_type->interface_type() != NULL)
{
- this->set_is_error();
- return false;
+ static tree interface_compare_decl;
+ left_tree = Gogo::call_builtin(&interface_compare_decl,
+ location,
+ "__go_interface_compare",
+ 2,
+ integer_type_node,
+ const_ptr_type_node,
+ fold_convert(const_ptr_type_node,
+ left_tree),
+ const_ptr_type_node,
+ fold_convert(const_ptr_type_node,
+ right_tree));
+ right_tree = build_int_cst_type(integer_type_node, 0);
}
- return true;
-}
-// Check argument types for a builtin function.
+ if (left_type->is_nil_type()
+ && (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ))
+ {
+ std::swap(left_type, right_type);
+ std::swap(left_tree, right_tree);
+ }
-void
-Builtin_call_expression::do_check_types(Gogo*)
-{
- switch (this->code_)
+ if (right_type->is_nil_type())
{
- case BUILTIN_INVALID:
- case BUILTIN_NEW:
- case BUILTIN_MAKE:
- return;
+ if (left_type->array_type() != NULL
+ && left_type->array_type()->length() == NULL)
+ {
+ Array_type* at = left_type->array_type();
+ left_tree = at->value_pointer_tree(context->gogo(), left_tree);
+ right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
+ }
+ else
+ {
+ gcc_assert(POINTER_TYPE_P(TREE_TYPE(left_tree)));
+ right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
+ }
+ }
- 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_open_array_type())
- arg_type = arg_type->points_to();
- if (this->code_ == BUILTIN_CAP)
- {
- if (!arg_type->is_error_type()
- && 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_type()
- && !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;
+ tree ret = fold_build2(code, boolean_type_node, left_tree, right_tree);
+ if (CAN_HAVE_LOCATION_P(ret))
+ SET_EXPR_LOCATION(ret, location);
+ return ret;
+}
- case BUILTIN_PANIC:
- case BUILTIN_PANICLN:
- 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()
- || type->is_string_type()
- || type->integer_type() != NULL
- || type->float_type() != NULL
- || type->is_boolean_type()
- || type->points_to() != NULL
- || type->array_type() != NULL
- || type->interface_type() != NULL)
- ;
- else
- this->report_error(_("unsupported argument type to "
- "builtin function"));
- }
- }
- }
- break;
+// Class Bound_method_expression.
- case BUILTIN_CLOSE:
- case BUILTIN_CLOSED:
- if (this->check_one_arg())
- {
- if (this->one_arg()->type()->channel_type() == NULL)
- this->report_error(_("argument must be channel"));
- }
- break;
+// Traversal.
- case BUILTIN_SIZEOF:
- case BUILTIN_ALIGNOF:
- this->check_one_arg();
- break;
+int
+Bound_method_expression::do_traverse(Traverse* traverse)
+{
+ if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ return Expression::traverse(&this->method_, traverse);
+}
- 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;
+// Return the type of a bound method expression. The type of this
+// object is really the type of the method with no receiver. We
+// should be able to get away with just returning the type of the
+// method.
- default:
- gcc_unreachable();
- }
+Type*
+Bound_method_expression::do_type()
+{
+ return this->method_->type();
}
-// Return the tree for a builtin function.
+// Determine the types of a method expression.
-tree
-Builtin_call_expression::do_get_tree(Translate_context* context)
+void
+Bound_method_expression::do_determine_type(const Type_context*)
{
- Gogo* gogo = context->gogo();
- switch (this->code_)
+ this->method_->determine_type_no_context();
+ Type* mtype = this->method_->type();
+ Function_type* fntype = mtype == NULL ? NULL : mtype->function_type();
+ if (fntype == NULL || !fntype->is_method())
+ this->expr_->determine_type_no_context();
+ else
{
- case BUILTIN_INVALID:
- case BUILTIN_NEW:
- case BUILTIN_MAKE:
- gcc_unreachable();
+ Type_context subcontext(fntype->receiver()->type(), false);
+ this->expr_->determine_type(&subcontext);
+ }
+}
- case BUILTIN_LEN:
- case BUILTIN_CAP:
- {
- const Expression_list* args = this->args();
- gcc_assert(args != NULL && args->size() == 1);
- Expression* arg = *args->begin();
- Type* arg_type = arg->type();
- tree arg_tree = arg->get_tree(context);
- if (arg_tree == error_mark_node)
- return error_mark_node;
+// Check the types of a method expression.
- if (arg_type->points_to() != NULL)
- {
- arg_type = arg_type->points_to();
- gcc_assert(arg_type->array_type() != NULL
- && !arg_type->is_open_array_type());
- gcc_assert(POINTER_TYPE_P(TREE_TYPE(arg_tree)));
- arg_tree = build_fold_indirect_ref(arg_tree);
- }
+void
+Bound_method_expression::do_check_types(Gogo*)
+{
+ Type* type = this->method_->type()->deref();
+ if (type == NULL
+ || type->function_type() == NULL
+ || !type->function_type()->is_method())
+ this->report_error(_("object is not a method"));
+ else
+ {
+ Type* rtype = type->function_type()->receiver()->type()->deref();
+ Type* etype = (this->expr_type_ != NULL
+ ? this->expr_type_
+ : this->expr_->type());
+ etype = etype->deref();
+ if (!Type::are_identical(rtype, etype))
+ this->report_error(_("method type does not match object type"));
+ }
+}
- 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)
- val_tree = arg_type->array_type()->length_tree(gogo, arg_tree);
- else if (arg_type->map_type() != NULL)
- {
- static tree map_len_fndecl;
- val_tree = Gogo::call_builtin(&map_len_fndecl,
- this->location(),
- "__go_map_len",
- 1,
- sizetype,
- arg_type->get_tree(gogo),
- arg_tree);
- }
- else if (arg_type->channel_type() != NULL)
- {
- static tree chan_len_fndecl;
- val_tree = Gogo::call_builtin(&chan_len_fndecl,
- this->location(),
- "__go_chan_len",
- 1,
- sizetype,
- arg_type->get_tree(gogo),
- arg_tree);
- }
- else
- gcc_unreachable();
- }
- else
- {
- if (arg_type->array_type() != NULL)
- val_tree = arg_type->array_type()->capacity_tree(gogo, arg_tree);
- else if (arg_type->channel_type() != NULL)
- {
- static tree chan_cap_fndecl;
- val_tree = Gogo::call_builtin(&chan_cap_fndecl,
- this->location(),
- "__go_chan_cap",
- 1,
- sizetype,
- arg_type->get_tree(gogo),
- arg_tree);
- }
- else
- gcc_unreachable();
- }
+// Get the tree for a method expression. There is no standard tree
+// representation for this. The only places it may currently be used
+// are in a Call_expression or a Go_statement, which will take it
+// apart directly. So this has nothing to do at present.
- tree type_tree = Type::lookup_integer_type("int")->get_tree(gogo);
- if (type_tree == TREE_TYPE(val_tree))
- return val_tree;
- else
- return fold(convert_to_integer(type_tree, val_tree));
- }
+tree
+Bound_method_expression::do_get_tree(Translate_context*)
+{
+ gcc_unreachable();
+}
- case BUILTIN_PANIC:
- case BUILTIN_PANICLN:
- case BUILTIN_PRINT:
- case BUILTIN_PRINTLN:
- {
- const bool is_panic = (this->code_ == BUILTIN_PANIC
- || this->code_ == BUILTIN_PANICLN);
- const bool is_ln = (this->code_ == BUILTIN_PANICLN
- || this->code_ == BUILTIN_PRINTLN);
- const Expression_list* call_args = this->args();
+// Make a method expression.
- int nargs;
- tree* args;
- std::string format;
- if (call_args == NULL)
- {
- if (!is_panic && !is_ln)
- {
- // A call to print with no arguments. There is nothing
- // to do.
- return integer_zero_node;
- }
- nargs = 1;
- args = new tree[1];
- }
- else
- {
- nargs = call_args->size() + 1;
-
- // We allocate extra space because we use three arguments
- // to print arrays.
- args = new tree[nargs * 3];
+Bound_method_expression*
+Expression::make_bound_method(Expression* expr, Expression* method,
+ source_location location)
+{
+ return new Bound_method_expression(expr, method, location);
+}
- int i = 1;
- for (Expression_list::const_iterator p = call_args->begin();
- p != call_args->end();
- ++p, ++i)
- {
- if (is_ln && i > 1)
- format += " ";
+// Class Builtin_call_expression. This is used for a call to a
+// builtin function.
- args[i] = (*p)->get_tree(context);
+class Builtin_call_expression : public Call_expression
+{
+ public:
+ Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
+ source_location location);
- if (args[i] == error_mark_node)
- {
- args[i] = integer_zero_node;
- format += "%d";
- }
- else if ((*p)->type()->is_string_type())
- {
- // We use a precision to print the right number of
- // characters. FIXME: If the string has embedded
- // null bytes, it won't be printed correctly.
- tree string = args[i];
- tree len = String_type::length_tree(gogo, string);
- args[i] = convert_to_integer(integer_type_node, len);
- ++i;
- ++nargs;
- args[i] = String_type::bytes_tree(gogo, string);
- format += "%.*s";
- }
- else if ((*p)->type()->integer_type() != NULL)
- {
- const Integer_type* itype = (*p)->type()->integer_type();
- int bits = TYPE_PRECISION(TREE_TYPE(args[i]));
- if (bits <= INT_TYPE_SIZE)
- {
- args[i] = fold_convert((itype->is_unsigned()
- ? unsigned_type_node
- : integer_type_node),
- args[i]);
- format += itype->is_unsigned() ? "%u" : "%d";
- }
- else if (bits <= LONG_TYPE_SIZE)
- {
- args[i] = fold_convert((itype->is_unsigned()
- ? long_unsigned_type_node
- : long_integer_type_node),
- args[i]);
- format += itype->is_unsigned() ? "%lu" : "%ld";
- }
- else if (bits <= LONG_LONG_TYPE_SIZE)
- {
- args[i] = fold_convert((itype->is_unsigned()
- ? long_long_unsigned_type_node
- : long_long_integer_type_node),
- args[i]);
- format += itype->is_unsigned() ? "%llu" : "%lld";
- }
- else
- gcc_unreachable();
- }
- else if ((*p)->type()->float_type() != NULL)
- {
- args[i] = fold_convert(double_type_node, args[i]);
- format += "%.24g";
- }
- else if ((*p)->type()->is_boolean_type())
- {
- tree string_type = Gogo::const_char_pointer_type_tree();
- tree true_string = Gogo::string_constant_tree("true");
- true_string = build_fold_addr_expr(true_string);
- true_string = fold_convert(string_type, true_string);
- tree false_string = Gogo::string_constant_tree("false");
- false_string = build_fold_addr_expr(false_string);
- false_string = fold_convert(string_type, false_string);
- args[i] = fold_build3(COND_EXPR, string_type, args[i],
- true_string, false_string);
- format += "%s";
- }
- else if ((*p)->type()->points_to() != NULL
- || (*p)->type()->interface_type() != NULL)
- {
- args[i] = fold_convert(ptr_type_node, args[i]);
- format += "%p";
- }
- else if ((*p)->type()->array_type() != NULL)
- {
- Array_type* at = (*p)->type()->array_type();
- tree v = save_expr(args[i]);
- args[i] = at->length_tree(gogo, v);
- ++i;
- ++nargs;
- args[i] = at->capacity_tree(gogo, v);
- ++i;
- ++nargs;
- args[i] = at->value_pointer_tree(gogo, v);
- format += "[%zu/%zu]%p";
- }
- else
- {
- args[i] = integer_zero_node;
- format += "%d";
- }
- }
- gcc_assert(i == nargs);
- }
+ protected:
+ // This overrides Call_expression::do_lower.
+ Expression*
+ do_lower(Gogo*, int);
- if (is_ln)
- format += "\n";
+ bool
+ do_is_constant() const;
- tree string_val = Gogo::string_constant_tree(format);
- args[0] = build_fold_addr_expr(string_val);
+ bool
+ do_integer_constant_value(bool, mpz_t, Type**) const;
- static tree panic_fndecl;
- static tree print_fndecl;
- static tree* pfndecl;
- if (is_panic)
- pfndecl = &panic_fndecl;
- else
- pfndecl = &print_fndecl;
- if (*pfndecl == NULL_TREE)
- {
- tree fnid = get_identifier(is_panic ? "__go_panic" : "__go_print");
- tree argtypes = tree_cons(NULL_TREE,
- Gogo::const_char_pointer_type_tree(),
- NULL_TREE);
- tree fntype = build_function_type(void_type_node, argtypes);
+ Type*
+ do_type();
- *pfndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL, fnid,
- fntype);
- Gogo::mark_fndecl_as_builtin_library(*pfndecl);
- if (is_panic)
- {
- // Mark the function as noreturn.
- TREE_THIS_VOLATILE(*pfndecl) = 1;
- }
- go_preserve_from_gc(*pfndecl);
- }
+ void
+ do_determine_type(const Type_context*);
- tree fnptr = build_fold_addr_expr(*pfndecl);
- tree call = build_call_array(void_type_node, fnptr, nargs, args);
- delete[] args;
+ void
+ do_check_types(Gogo*);
- SET_EXPR_LOCATION(call, this->location());
+ Expression*
+ do_copy()
+ {
+ return new Builtin_call_expression(this->gogo_, this->fn()->copy(),
+ this->args()->copy(),
+ this->location());
+ }
- return call;
- }
+ tree
+ do_get_tree(Translate_context*);
- case BUILTIN_CLOSE:
- case BUILTIN_CLOSED:
- {
- const Expression_list* args = this->args();
- gcc_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;
- if (this->code_ == BUILTIN_CLOSE)
- {
- static tree close_fndecl;
- return Gogo::call_builtin(&close_fndecl,
- this->location(),
- "__go_builtin_close",
- 1,
- void_type_node,
- TREE_TYPE(arg_tree),
- arg_tree);
- }
- else
- {
- static tree closed_fndecl;
- return Gogo::call_builtin(&closed_fndecl,
- this->location(),
- "__go_builtin_closed",
- 1,
- boolean_type_node,
- TREE_TYPE(arg_tree),
- arg_tree);
- }
- }
+ void
+ do_export(Export*) const;
- case BUILTIN_SIZEOF:
- case BUILTIN_OFFSETOF:
- case BUILTIN_ALIGNOF:
- {
- mpz_t val;
- mpz_init(val);
- Type* dummy;
- bool b = this->integer_constant_value(true, val, &dummy);
- gcc_assert(b);
- tree type = Type::lookup_integer_type("int")->get_tree(gogo);
- tree ret = Expression::integer_constant_tree(val, type);
- mpz_clear(val);
- return ret;
- }
+ private:
+ // The builtin functions.
+ enum Builtin_function_code
+ {
+ BUILTIN_INVALID,
- default:
- gcc_unreachable();
- }
-}
+ // Predeclared builtin functions.
+ BUILTIN_CAP,
+ BUILTIN_CLOSE,
+ BUILTIN_CLOSED,
+ BUILTIN_LEN,
+ BUILTIN_MAKE,
+ BUILTIN_NEW,
+ BUILTIN_PANIC,
+ BUILTIN_PANICLN,
+ BUILTIN_PRINT,
+ BUILTIN_PRINTLN,
-// We have to support exporting a builtin call expression, because
-// code can set a constant to the result of a builtin expression.
+ // Builtin functions from the unsafe package.
+ BUILTIN_ALIGNOF,
+ BUILTIN_OFFSETOF,
+ BUILTIN_SIZEOF
+ };
-void
-Builtin_call_expression::do_export(Export* exp) const
-{
- mpz_t val;
- mpz_init(val);
- Type* dummy;
- bool b = this->integer_constant_value(true, val, &dummy);
- if (!b)
- error_at(this->location(), "value is not constant");
- char* s = mpz_get_str(NULL, 10, val);
- exp->write_c_string(s);
- free(s);
- // A trailing space lets us reliably identify the end of the number.
- exp->write_c_string(" ");
- mpz_clear(val);
-}
+ Expression*
+ one_arg() const;
-// Class Call_expression.
+ bool
+ check_one_arg();
-// Traversal.
+ // 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_;
+};
-int
-Call_expression::do_traverse(Traverse* traverse)
+Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
+ Expression* fn,
+ Expression_list* args,
+ source_location location)
+ : Call_expression(fn, args, location),
+ gogo_(gogo), code_(BUILTIN_INVALID)
{
- 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;
+ Func_expression* fnexp = this->fn()->func_expression();
+ gcc_assert(fnexp != NULL);
+ const std::string& name(fnexp->named_object()->name());
+ if (name == "cap")
+ this->code_ = BUILTIN_CAP;
+ else if (name == "close")
+ this->code_ = BUILTIN_CLOSE;
+ else if (name == "closed")
+ this->code_ = BUILTIN_CLOSED;
+ 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 == "panicln")
+ this->code_ = BUILTIN_PANICLN;
+ else if (name == "print")
+ this->code_ = BUILTIN_PRINT;
+ else if (name == "println")
+ this->code_ = BUILTIN_PRINTLN;
+ 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
+ gcc_unreachable();
}
-// Lower a call statement. This is where we handle an argument which
-// is a call to a function which returns multiple results. We also
-// may discover that we thought was a call is actually a type cast.
+// Lower a builtin call expression. This turns new and make into
+// specific expressions.
Expression*
-Call_expression::do_lower(Gogo* gogo, int)
+Builtin_call_expression::do_lower(Gogo*, int)
{
- 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());
-
- Func_expression* fne = this->fn_->func_expression();
- if (fne != NULL
- && 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());
-
- if (this->args_ != NULL
- && this->args_->size() == 1
- && this->args_->front()->call_expression() != NULL
- && this->fn_->type()->function_type() != NULL)
+ if (this->code_ == BUILTIN_NEW)
{
- Function_type* fntype = this->fn_->type()->function_type();
- 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)))
+ 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
{
- 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. FIXME.
- delete this->args_;
- this->args_ = args;
+ 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(), this->location());
+ }
+ }
+ else if (this->code_ == BUILTIN_MAKE)
+ {
+ const Expression_list* args = this->args();
+ if (args == NULL || args->size() < 1)
+ this->report_error(_("not enough arguments"));
+ else
+ {
+ Expression* arg = args->front();
+ if (!arg->is_type_expression())
+ {
+ error_at(arg->location(), _("expected type"));
+ this->set_is_error();
+ }
+ else
+ {
+ Expression_list* newargs;
+ if (args->size() == 1)
+ newargs = NULL;
+ else
+ {
+ newargs = new Expression_list();
+ Expression_list::const_iterator p = args->begin();
+ ++p;
+ for (; p != args->end(); ++p)
+ newargs->push_back(*p);
+ }
+ return Expression::make_make(arg->type(), newargs,
+ this->location());
+ }
}
}
return this;
}
-// Get the function type. Returns NULL if we don't know the type. If
-// this returns NULL, and if_ERROR is true, issues an error.
-
-Function_type*
-Call_expression::get_function_type(bool issue_error)
-{
- Type* type = this->fn_->type()->deref();
- Function_type* fntype = type->function_type();
- if (fntype != NULL)
- return fntype;
- if (issue_error && !type->is_error_type())
- this->report_error("expected function");
- return NULL;
-}
-
-// Return the number of values which this call will return.
+// Return a single argument, or NULL if there isn't one.
-size_t
-Call_expression::result_count()
+Expression*
+Builtin_call_expression::one_arg() const
{
- Function_type* fntype = this->get_function_type(false);
- if (fntype == NULL)
- return 0;
- if (fntype->results() == NULL)
- return 0;
- return fntype->results()->size();
+ const Expression_list* args = this->args();
+ if (args->size() != 1)
+ return NULL;
+ return args->front();
}
-// Get the type.
+// Return whether this is constant: len of a string, or len or cap of
+// a fixed array, or unsafe.Sizeof, unsafe.Offsetof, unsafe.Alignof.
-Type*
-Call_expression::do_type()
+bool
+Builtin_call_expression::do_is_constant() const
{
- if (this->type_ != NULL)
- return this->type_;
-
- Type* ret;
- Function_type* fntype = this->get_function_type(false);
- if (fntype == NULL)
- return Type::make_error_type();
+ if (this->code_ == BUILTIN_LEN
+ || this->code_ == BUILTIN_CAP)
+ {
+ Expression* arg = this->one_arg();
+ if (arg == NULL)
+ return false;
+ Type* arg_type = arg->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);
+ if (arg_type->points_to() != NULL
+ && arg_type->points_to()->array_type() != NULL
+ && !arg_type->points_to()->is_open_array_type())
+ arg_type = arg_type->points_to();
- this->type_ = ret;
+ if (arg_type->array_type() != NULL
+ && arg_type->array_type()->length() != NULL)
+ return arg_type->array_type()->length()->is_constant();
- return this->type_;
+ if (this->code_ == BUILTIN_LEN && arg_type->is_string_type())
+ return arg->is_constant();
+ }
+ else if (this->code_ == BUILTIN_SIZEOF
+ || this->code_ == BUILTIN_ALIGNOF)
+ return this->one_arg() != NULL;
+ else if (this->code_ == BUILTIN_OFFSETOF)
+ {
+ Expression* arg = this->one_arg();
+ if (arg == NULL)
+ return false;
+ return arg->field_reference_expression() != NULL;
+ }
+ return false;
}
-// Determine types for a call expression. We can use the function
-// parameter types to set the types of the arguments.
+// Return an integer constant value if possible.
-void
-Call_expression::do_determine_type(const Type_context*)
-{
- this->fn_->determine_type_no_context();
- Function_type* fntype = this->get_function_type(false);
- const Typed_identifier_list* parameters = NULL;
- if (fntype != NULL)
- parameters = fntype->parameters();
- if (this->args_ != NULL)
+bool
+Builtin_call_expression::do_integer_constant_value(bool iota_is_constant,
+ mpz_t val,
+ Type** ptype) const
+{
+ if (this->code_ == BUILTIN_LEN
+ || this->code_ == BUILTIN_CAP)
{
- Typed_identifier_list::const_iterator pt;
- if (parameters != NULL)
- pt = parameters->begin();
- for (Expression_list::const_iterator pa = this->args_->begin();
- pa != this->args_->end();
- ++pa)
+ 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()
+ && arg->classification() == EXPRESSION_STRING)
{
- if (parameters != NULL
- && pt != parameters->end()
- && (!fntype->is_varargs() || pt + 1 != parameters->end()))
+ String_expression* se = static_cast<String_expression*>(arg);
+ mpz_set_ui(val, se->val().length());
+ *ptype = Type::lookup_integer_type("int");
+ return true;
+ }
+
+ if (arg_type->points_to() != NULL
+ && arg_type->points_to()->array_type() != NULL
+ && !arg_type->points_to()->is_open_array_type())
+ arg_type = arg_type->points_to();
+
+ if (arg_type->array_type() != NULL
+ && arg_type->array_type()->length() != NULL)
+ {
+ Expression* e = arg_type->array_type()->length();
+ if (e->integer_constant_value(iota_is_constant, val, ptype))
{
- Type_context subcontext(pt->type(), false);
- (*pa)->determine_type(&subcontext);
- ++pt;
+ *ptype = Type::lookup_integer_type("int");
+ return true;
}
- else
- (*pa)->determine_type_no_context();
}
}
+ 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_type())
+ return false;
+ if (arg_type->is_abstract())
+ return false;
+ tree arg_type_tree = arg_type->get_tree(this->gogo_);
+ unsigned long val_long;
+ if (this->code_ == BUILTIN_SIZEOF)
+ {
+ tree type_size = TYPE_SIZE_UNIT(arg_type_tree);
+ gcc_assert(TREE_CODE(type_size) == INTEGER_CST);
+ if (TREE_INT_CST_HIGH(type_size) != 0)
+ return false;
+ unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW(type_size);
+ val_long = static_cast<unsigned long>(val_wide);
+ if (val_long != val_wide)
+ return false;
+ }
+ else if (this->code_ == BUILTIN_ALIGNOF)
+ {
+ val_long = TYPE_ALIGN(arg_type_tree);
+ if (arg->field_reference_expression() != NULL)
+ {
+ // Calling unsafe.Alignof(s.f) returns the alignment of
+ // the type of f when it is used as a field in a struct.
+#ifdef BIGGEST_FIELD_ALIGNMENT
+ if (val_long > BIGGEST_FIELD_ALIGNMENT)
+ val_long = BIGGEST_FIELD_ALIGNMENT;
+#endif
+#ifdef ADJUST_FIELD_ALIGN
+ tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, NULL,
+ arg_type_tree);
+ val_long = ADJUST_FIELD_ALIGN(field, val_long);
+#endif
+ }
+ val_long /= BITS_PER_UNIT;
+ }
+ else
+ gcc_unreachable();
+ mpz_set_ui(val, val_long);
+ *ptype = NULL;
+ 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;
+ Expression* struct_expr = farg->expr();
+ Type* st = struct_expr->type();
+ if (st->struct_type() == NULL)
+ return false;
+ tree struct_tree = st->get_tree(this->gogo_);
+ gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE);
+ tree field = TYPE_FIELDS(struct_tree);
+ for (unsigned int index = farg->field_index(); index > 0; --index)
+ {
+ field = TREE_CHAIN(field);
+ gcc_assert(field != NULL_TREE);
+ }
+ HOST_WIDE_INT offset_wide = int_byte_position (field);
+ if (offset_wide < 0)
+ return false;
+ unsigned long offset_long = static_cast<unsigned long>(offset_wide);
+ if (offset_long != static_cast<unsigned HOST_WIDE_INT>(offset_wide))
+ return false;
+ mpz_set_ui(val, offset_long);
+ return true;
+ }
+ return false;
}
-// Check types for parameter I.
+// Return the type.
-void
-Call_expression::check_argument_type(int i, const Type* parameter_type,
- const Type* argument_type,
- source_location argument_location)
+Type*
+Builtin_call_expression::do_type()
{
- std::string reason;
- if (!Type::are_compatible_for_assign(parameter_type, argument_type, &reason))
+ switch (this->code_)
{
- 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();
+ case BUILTIN_INVALID:
+ default:
+ gcc_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_LEN:
+ case BUILTIN_ALIGNOF:
+ case BUILTIN_OFFSETOF:
+ case BUILTIN_SIZEOF:
+ return Type::lookup_integer_type("int");
+
+ case BUILTIN_CLOSE:
+ case BUILTIN_PANIC:
+ case BUILTIN_PANICLN:
+ case BUILTIN_PRINT:
+ case BUILTIN_PRINTLN:
+ return Type::make_void_type();
+
+ case BUILTIN_CLOSED:
+ return Type::lookup_bool_type();
}
}
-// Check types.
+// Determine the type.
void
-Call_expression::do_check_types(Gogo*)
+Builtin_call_expression::do_determine_type(const Type_context*)
{
- Function_type* fntype = this->get_function_type(true);
- if (fntype == NULL)
- return;
+ this->fn()->determine_type_no_context();
- if (fntype->is_method())
+ Type_context subcontext(NULL, true);
+ const Expression_list* args = this->args();
+ if (args != NULL)
{
- // We don't support pointers to methods, so the function has to
- // be a bound method expression.
- if (this->fn_->bound_method_expression() == NULL)
- this->report_error(_("method call without object"));
+ for (Expression_list::const_iterator pa = args->begin();
+ pa != args->end();
+ ++pa)
+ (*pa)->determine_type(&subcontext);
}
+}
- const Typed_identifier_list* parameters = fntype->parameters();
- if (this->args_ == NULL)
+// 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)
{
- // A single varargs parameter can accept zero arguments.
- if (parameters != NULL
- && (!fntype->is_varargs() || parameters->size() > 1))
- this->report_error(_("not enough arguments"));
+ this->report_error(_("not enough arguments"));
+ return false;
}
- else if (parameters == NULL)
- this->report_error(_("too many arguments"));
- else
+ else if (args->size() > 1)
{
- 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 (fntype->is_varargs() && pt + 1 == parameters->end())
- {
- // A varargs parameter can accept any number of
- // arguments, but you can't pass nil.
- for (; pa != this->args_->end(); ++pa)
- {
- if ((*pa)->type()->is_nil_type())
- {
- this->report_error(_("invalid use of %<nil%> for "
- "%<...%> argument"));
- return;
- }
- }
- break;
- }
- if (pt == parameters->end())
- {
- this->report_error(_("too many arguments"));
- return;
- }
- this->check_argument_type(i + 1, pt->type(), (*pa)->type(),
- (*pa)->location());
- }
- // The varargs parameter can accept zero arguments.
- if (pt != parameters->end()
- && (!fntype->is_varargs() || pt + 1 != parameters->end()))
- this->report_error(_("not enough arguments"));
+ this->report_error(_("too many arguments"));
+ return false;
}
-}
-
-// 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)
-{
- 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(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(COMPOUND_EXPR, pointer_to_arg_type,
- build1(DECL_EXPR, void_type_node, tmp),
- build_fold_addr_expr(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 (args->front()->is_error_expression()
+ || args->front()->type()->is_error_type())
{
- if (fatype->points_to() == NULL)
- fatype = Type::make_pointer_type(fatype);
- first_arg = fold_convert(fatype->get_tree(context->gogo()), first_arg);
- if (first_arg == error_mark_node
- || TREE_TYPE(first_arg) == error_mark_node)
- return error_mark_node;
+ this->set_is_error();
+ return false;
}
-
- *first_arg_ptr = first_arg;
-
- return bound_method->method()->get_tree(context);
+ return true;
}
-// Get the function and the first argument to use when calling an
-// interface method.
+// Check argument types for a builtin function.
-tree
-Call_expression::interface_method_function(
- Translate_context* context,
- Interface_field_reference_expression* interface_method,
- tree* first_arg_ptr)
+void
+Builtin_call_expression::do_check_types(Gogo*)
{
- tree expr = interface_method->expr()->get_tree(context);
- if (expr == error_mark_node)
- return error_mark_node;
- expr = save_expr(expr);
- tree first_arg = interface_method->get_underlying_object_tree(context, expr);
- if (first_arg == error_mark_node)
- return error_mark_node;
- *first_arg_ptr = first_arg;
- return interface_method->get_function_tree(context, expr);
-}
+ switch (this->code_)
+ {
+ case BUILTIN_INVALID:
+ case BUILTIN_NEW:
+ case BUILTIN_MAKE:
+ return;
-// The call expression is being copied. There is nothing to do here
-// except to disable any decrements--the function should return with
-// an appropriate reference count.
+ 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_open_array_type())
+ arg_type = arg_type->points_to();
+ if (this->code_ == BUILTIN_CAP)
+ {
+ if (!arg_type->is_error_type()
+ && 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_type()
+ && !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;
-Expression*
-Call_expression::do_being_copied(Refcounts*, bool)
-{
- this->is_being_copied_ = true;
- return this;
-}
+ case BUILTIN_PANIC:
+ case BUILTIN_PANICLN:
+ 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()
+ || type->is_string_type()
+ || type->integer_type() != NULL
+ || type->float_type() != NULL
+ || type->is_boolean_type()
+ || type->points_to() != NULL
+ || type->array_type() != NULL
+ || type->interface_type() != NULL)
+ ;
+ else
+ this->report_error(_("unsupported argument type to "
+ "builtin function"));
+ }
+ }
+ }
+ break;
-// Where needed, decrement the reference counts of any values returned
-// by this call.
+ case BUILTIN_CLOSE:
+ case BUILTIN_CLOSED:
+ if (this->check_one_arg())
+ {
+ if (this->one_arg()->type()->channel_type() == NULL)
+ this->report_error(_("argument must be channel"));
+ }
+ break;
-Expression*
-Call_expression::do_note_decrements(Refcounts* refcounts)
-{
- if (this->is_being_copied_)
- return this;
- Function_type* fntype = this->get_function_type(false);
- if (fntype == NULL)
- return this;
- const Typed_identifier_list* results = fntype->results();
- if (results == NULL)
- return this;
- if (results->size() == 1)
- {
- if (!results->begin()->type()->has_refcounted_component())
- return this;
- return Expression::make_refcount_adjust(refcounts,
- REFCOUNT_DECREMENT_COMPUTED,
- this, false);
- }
- else if (!this->is_value_discarded_)
- {
- // If the value is not discarded, each result will be handled
- // separately via Call_expression_result.
- return this;
- }
- else
- {
- for (Typed_identifier_list::const_iterator p = results->begin();
- p != results->end();
- p++)
+ case BUILTIN_SIZEOF:
+ case BUILTIN_ALIGNOF:
+ this->check_one_arg();
+ break;
+
+ case BUILTIN_OFFSETOF:
+ if (this->check_one_arg())
{
- if (p->type()->has_refcounted_component())
- {
- if (this->refcount_entries_ == NULL)
- this->refcount_entries_ = new std::vector<Refcount_entry>;
- Refcount_entry re = refcounts->add(REFCOUNT_DECREMENT_COMPUTED,
- p->type());
- this->refcount_entries_->push_back(re);
- }
+ Expression* arg = this->one_arg();
+ if (arg->field_reference_expression() == NULL)
+ this->report_error(_("argument must be a field reference"));
}
- return this;
+ break;
+
+ default:
+ gcc_unreachable();
}
}
-// Build the call expression.
+// Return the tree for a builtin function.
tree
-Call_expression::do_get_tree(Translate_context* context)
+Builtin_call_expression::do_get_tree(Translate_context* context)
{
- if (this->tree_ != NULL_TREE)
- return this->tree_;
-
- Function_type* fntype = this->get_function_type(false);
- if (fntype == NULL)
- return error_mark_node;
-
- if (this->fn_->is_error_expression())
- return error_mark_node;
-
Gogo* gogo = context->gogo();
- 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;
- gcc_assert(!fntype->is_method() || is_method);
-
- bool is_varargs = fntype->is_varargs();
-
- int nargs;
- tree* args;
- if (this->args_ == NULL || this->args_->empty())
+ switch (this->code_)
{
- nargs = is_method ? 1 : 0;
- if (is_varargs)
- ++nargs;
- args = nargs == 0 ? NULL : new tree[nargs];
- if (is_varargs)
- {
- Type* varargs_type = Function_type::varargs_type();
- // The old way, passing array[]interface{}.
- // args[nargs - 1] = varargs_type->get_init_tree(gogo, false);
- Struct_field_list* fields = new Struct_field_list();
- Type* struct_type = Type::make_struct_type(fields, location);
- tree val = struct_type->get_init_tree(gogo, false);
- args[nargs - 1] = Expression::convert_for_assignment(context,
- varargs_type,
- struct_type,
- val,
- location);
- }
- }
- else
- {
- const Typed_identifier_list* params = fntype->parameters();
- gcc_assert(params != NULL);
+ case BUILTIN_INVALID:
+ case BUILTIN_NEW:
+ case BUILTIN_MAKE:
+ gcc_unreachable();
- if (is_varargs)
- nargs = params->size();
- else
- nargs = this->args_->size();
- int i = is_method ? 1 : 0;
- nargs += i;
- args = new tree[nargs];
+ case BUILTIN_LEN:
+ case BUILTIN_CAP:
+ {
+ const Expression_list* args = this->args();
+ gcc_assert(args != NULL && args->size() == 1);
+ Expression* arg = *args->begin();
+ Type* arg_type = arg->type();
+ tree arg_tree = arg->get_tree(context);
+ if (arg_tree == error_mark_node)
+ return error_mark_node;
- 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)
- {
- Type* pp_type = pp->type();
- if (is_varargs && pp + 1 == params->end())
- {
- // If the last argument is itself a varargs argument for
- // the caller, we pass it without further encapsulation.
- if (pe + 1 == this->args_->end()
- && (*pe)->var_expression() != NULL
- && (*pe)->var_expression()->named_object()->is_variable()
- && ((*pe)->var_expression()->named_object()->var_value()->
- is_varargs_parameter()))
- {
- pp_type = Function_type::varargs_type();
- is_varargs = false;
- }
- else
- break;
- }
- 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;
- }
+ if (arg_type->points_to() != NULL)
+ {
+ arg_type = arg_type->points_to();
+ gcc_assert(arg_type->array_type() != NULL
+ && !arg_type->is_open_array_type());
+ gcc_assert(POINTER_TYPE_P(TREE_TYPE(arg_tree)));
+ arg_tree = build_fold_indirect_ref(arg_tree);
+ }
- if (is_varargs)
- {
- Type* varargs_type = Function_type::varargs_type();
+ 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)
+ val_tree = arg_type->array_type()->length_tree(gogo, arg_tree);
+ else if (arg_type->map_type() != NULL)
+ {
+ static tree map_len_fndecl;
+ val_tree = Gogo::call_builtin(&map_len_fndecl,
+ this->location(),
+ "__go_map_len",
+ 1,
+ sizetype,
+ arg_type->get_tree(gogo),
+ arg_tree);
+ }
+ else if (arg_type->channel_type() != NULL)
+ {
+ static tree chan_len_fndecl;
+ val_tree = Gogo::call_builtin(&chan_len_fndecl,
+ this->location(),
+ "__go_chan_len",
+ 1,
+ sizetype,
+ arg_type->get_tree(gogo),
+ arg_tree);
+ }
+ else
+ gcc_unreachable();
+ }
+ else
+ {
+ if (arg_type->array_type() != NULL)
+ val_tree = arg_type->array_type()->capacity_tree(gogo, arg_tree);
+ else if (arg_type->channel_type() != NULL)
+ {
+ static tree chan_cap_fndecl;
+ val_tree = Gogo::call_builtin(&chan_cap_fndecl,
+ this->location(),
+ "__go_chan_cap",
+ 1,
+ sizetype,
+ arg_type->get_tree(gogo),
+ arg_tree);
+ }
+ else
+ gcc_unreachable();
+ }
-#if 0
- // The old way, passing array[]interface{}.
- Type* element_type = varargs_type->array_type()->element_type();
- VEC(constructor_elt,gc)* values = VEC_alloc(constructor_elt, gc,
- this->args_->size());
- size_t j = 0;
- for (; pe != this->args_->end(); ++pe, ++j)
- {
- constructor_elt* elt = VEC_quick_push(constructor_elt, values,
- NULL);
- elt->index = size_int(j);
- tree expr_tree = (*pe)->get_tree(context);
- elt->value = Expression::convert_for_assignment(context,
- element_type,
- (*pe)->type(),
- expr_tree,
- location);
- }
+ tree type_tree = Type::lookup_integer_type("int")->get_tree(gogo);
+ if (type_tree == TREE_TYPE(val_tree))
+ return val_tree;
+ else
+ return fold(convert_to_integer(type_tree, val_tree));
+ }
- mpz_t val;
- mpz_init_set_ui(val, j);
- Type* array_type =
- Type::make_array_type(element_type,
- Expression::make_integer(&val, NULL,
- location));
- mpz_clear(val);
+ case BUILTIN_PANIC:
+ case BUILTIN_PANICLN:
+ case BUILTIN_PRINT:
+ case BUILTIN_PRINTLN:
+ {
+ const bool is_panic = (this->code_ == BUILTIN_PANIC
+ || this->code_ == BUILTIN_PANICLN);
+ const bool is_ln = (this->code_ == BUILTIN_PANICLN
+ || this->code_ == BUILTIN_PRINTLN);
+ const Expression_list* call_args = this->args();
- tree varargs_val = build_constructor(array_type->get_tree(gogo),
- values);
+ int nargs;
+ tree* args;
+ std::string format;
+ if (call_args == NULL)
+ {
+ if (!is_panic && !is_ln)
+ {
+ // A call to print with no arguments. There is nothing
+ // to do.
+ return integer_zero_node;
+ }
+ nargs = 1;
+ args = new tree[1];
+ }
+ else
+ {
+ nargs = call_args->size() + 1;
- args[i] = Expression::convert_for_assignment(context, varargs_type,
- array_type,
- varargs_val,
- location);
-#else
- // The new way, passing a struct converted to an empty
- // interface.
- Struct_field_list* fields = new Struct_field_list();
- Expression_list::const_iterator pehold = pe;
- for (; pe != this->args_->end(); ++pe)
- fields->push_back(Struct_field(Typed_identifier("UNNAMED",
- (*pe)->type(),
- location)));
- Type* struct_type = Type::make_struct_type(fields, location);
- tree struct_tree = struct_type->get_tree(gogo);
- if (struct_tree == error_mark_node)
- return error_mark_node;
+ // We allocate extra space because we use three arguments
+ // to print arrays.
+ args = new tree[nargs * 3];
- VEC(constructor_elt,gc)* values = VEC_alloc(constructor_elt, gc,
- this->args_->size());
- tree field = TYPE_FIELDS(struct_tree);
- for (pe = pehold; pe != this->args_->end(); ++pe)
- {
- gcc_assert(field != NULL_TREE);
- constructor_elt* elt = VEC_quick_push(constructor_elt, values,
- NULL);
- elt->index = field;
- elt->value = (*pe)->get_tree(context);
- field = TREE_CHAIN(field);
- }
- gcc_assert(field == NULL_TREE);
+ int i = 1;
+ for (Expression_list::const_iterator p = call_args->begin();
+ p != call_args->end();
+ ++p, ++i)
+ {
+ if (is_ln && i > 1)
+ format += " ";
- tree varargs_val = build_constructor(struct_tree, values);
+ args[i] = (*p)->get_tree(context);
- args[i] = Expression::convert_for_assignment(context, varargs_type,
- struct_type,
- varargs_val,
- location);
-#endif
+ if (args[i] == error_mark_node)
+ {
+ args[i] = integer_zero_node;
+ format += "%d";
+ }
+ else if ((*p)->type()->is_string_type())
+ {
+ // We use a precision to print the right number of
+ // characters. FIXME: If the string has embedded
+ // null bytes, it won't be printed correctly.
+ tree string = args[i];
+ tree len = String_type::length_tree(gogo, string);
+ args[i] = convert_to_integer(integer_type_node, len);
+ ++i;
+ ++nargs;
+ args[i] = String_type::bytes_tree(gogo, string);
+ format += "%.*s";
+ }
+ else if ((*p)->type()->integer_type() != NULL)
+ {
+ const Integer_type* itype = (*p)->type()->integer_type();
+ int bits = TYPE_PRECISION(TREE_TYPE(args[i]));
+ if (bits <= INT_TYPE_SIZE)
+ {
+ args[i] = fold_convert((itype->is_unsigned()
+ ? unsigned_type_node
+ : integer_type_node),
+ args[i]);
+ format += itype->is_unsigned() ? "%u" : "%d";
+ }
+ else if (bits <= LONG_TYPE_SIZE)
+ {
+ args[i] = fold_convert((itype->is_unsigned()
+ ? long_unsigned_type_node
+ : long_integer_type_node),
+ args[i]);
+ format += itype->is_unsigned() ? "%lu" : "%ld";
+ }
+ else if (bits <= LONG_LONG_TYPE_SIZE)
+ {
+ args[i] = fold_convert((itype->is_unsigned()
+ ? long_long_unsigned_type_node
+ : long_long_integer_type_node),
+ args[i]);
+ format += itype->is_unsigned() ? "%llu" : "%lld";
+ }
+ else
+ gcc_unreachable();
+ }
+ else if ((*p)->type()->float_type() != NULL)
+ {
+ args[i] = fold_convert(double_type_node, args[i]);
+ format += "%.24g";
+ }
+ else if ((*p)->type()->is_boolean_type())
+ {
+ tree string_type = Gogo::const_char_pointer_type_tree();
+ tree true_string = Gogo::string_constant_tree("true");
+ true_string = build_fold_addr_expr(true_string);
+ true_string = fold_convert(string_type, true_string);
+ tree false_string = Gogo::string_constant_tree("false");
+ false_string = build_fold_addr_expr(false_string);
+ false_string = fold_convert(string_type, false_string);
+ args[i] = fold_build3(COND_EXPR, string_type, args[i],
+ true_string, false_string);
+ format += "%s";
+ }
+ else if ((*p)->type()->points_to() != NULL
+ || (*p)->type()->interface_type() != NULL)
+ {
+ args[i] = fold_convert(ptr_type_node, args[i]);
+ format += "%p";
+ }
+ else if ((*p)->type()->array_type() != NULL)
+ {
+ Array_type* at = (*p)->type()->array_type();
+ tree v = save_expr(args[i]);
+ args[i] = at->length_tree(gogo, v);
+ ++i;
+ ++nargs;
+ args[i] = at->capacity_tree(gogo, v);
+ ++i;
+ ++nargs;
+ args[i] = at->value_pointer_tree(gogo, v);
+ format += "[%zu/%zu]%p";
+ }
+ else
+ {
+ args[i] = integer_zero_node;
+ format += "%d";
+ }
+ }
+ gcc_assert(i == nargs);
+ }
- ++i;
- ++pp;
- }
+ if (is_ln)
+ format += "\n";
- gcc_assert(pp == params->end());
- gcc_assert(i == nargs);
- }
+ tree string_val = Gogo::string_constant_tree(format);
+ args[0] = build_fold_addr_expr(string_val);
- tree rettype = TREE_TYPE(TREE_TYPE(fntype->get_tree(gogo)));
- if (rettype == error_mark_node)
- return error_mark_node;
+ static tree panic_fndecl;
+ static tree print_fndecl;
+ static tree* pfndecl;
+ if (is_panic)
+ pfndecl = &panic_fndecl;
+ else
+ pfndecl = &print_fndecl;
+ if (*pfndecl == NULL_TREE)
+ {
+ tree fnid = get_identifier(is_panic ? "__go_panic" : "__go_print");
+ tree argtypes = tree_cons(NULL_TREE,
+ Gogo::const_char_pointer_type_tree(),
+ NULL_TREE);
+ tree fntype = build_function_type(void_type_node, argtypes);
+
+ *pfndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL, fnid,
+ fntype);
+ Gogo::mark_fndecl_as_builtin_library(*pfndecl);
+ if (is_panic)
+ {
+ // Mark the function as noreturn.
+ TREE_THIS_VOLATILE(*pfndecl) = 1;
+ }
+ go_preserve_from_gc(*pfndecl);
+ }
+
+ tree fnptr = build_fold_addr_expr(*pfndecl);
+ tree call = build_call_array(void_type_node, fnptr, nargs, args);
+ delete[] args;
+
+ SET_EXPR_LOCATION(call, this->location());
+
+ return call;
+ }
+
+ case BUILTIN_CLOSE:
+ case BUILTIN_CLOSED:
+ {
+ const Expression_list* args = this->args();
+ gcc_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;
+ if (this->code_ == BUILTIN_CLOSE)
+ {
+ static tree close_fndecl;
+ return Gogo::call_builtin(&close_fndecl,
+ this->location(),
+ "__go_builtin_close",
+ 1,
+ void_type_node,
+ TREE_TYPE(arg_tree),
+ arg_tree);
+ }
+ else
+ {
+ static tree closed_fndecl;
+ return Gogo::call_builtin(&closed_fndecl,
+ this->location(),
+ "__go_builtin_closed",
+ 1,
+ boolean_type_node,
+ TREE_TYPE(arg_tree),
+ arg_tree);
+ }
+ }
+
+ case BUILTIN_SIZEOF:
+ case BUILTIN_OFFSETOF:
+ case BUILTIN_ALIGNOF:
+ {
+ mpz_t val;
+ mpz_init(val);
+ Type* dummy;
+ bool b = this->integer_constant_value(true, val, &dummy);
+ gcc_assert(b);
+ tree type = Type::lookup_integer_type("int")->get_tree(gogo);
+ tree ret = Expression::integer_constant_tree(val, type);
+ mpz_clear(val);
+ return ret;
+ }
+
+ default:
+ gcc_unreachable();
+ }
+}
- tree fn;
- if (has_closure)
- fn = func->get_tree_without_closure(gogo);
- else if (!is_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
- gcc_unreachable();
+// We have to support exporting a builtin call expression, because
+// code can set a constant to the result of a builtin expression.
- if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node)
- return error_mark_node;
+void
+Builtin_call_expression::do_export(Export* exp) const
+{
+ mpz_t val;
+ mpz_init(val);
+ Type* dummy;
+ bool b = this->integer_constant_value(true, val, &dummy);
+ if (!b)
+ error_at(this->location(), "value is not constant");
+ char* s = mpz_get_str(NULL, 10, val);
+ exp->write_c_string(s);
+ free(s);
+ // A trailing space lets us reliably identify the end of the number.
+ exp->write_c_string(" ");
+ mpz_clear(val);
+}
- tree ret = build_call_array(rettype, fn, nargs, args);
- delete[] args;
+// Class Call_expression.
- SET_EXPR_LOCATION(ret, location);
+// Traversal.
- if (has_closure)
+int
+Call_expression::do_traverse(Traverse* traverse)
+{
+ if (Expression::traverse(&this->fn_, traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ if (this->args_ != NULL)
{
- tree closure_tree = func->closure()->get_tree(context);
- if (closure_tree != error_mark_node)
- CALL_EXPR_STATIC_CHAIN(ret) = closure_tree;
+ if (this->args_->traverse(traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
}
-
- // If there is more than one result, we will refer to the call
- // multiple times.
- if (fntype->results() != NULL && fntype->results()->size() > 1)
- ret = save_expr(ret);
-
- if (this->refcount_entries_ != NULL)
- ret = this->set_refcount_queue_entries(context, ret);
-
- this->tree_ = ret;
-
- return ret;
+ return TRAVERSE_CONTINUE;
}
-// Set entries in the decrement queue as needed.
+// Lower a call statement. This is where we handle an argument which
+// is a call to a function which returns multiple results. We also
+// may discover that we thought was a call is actually a type cast.
-tree
-Call_expression::set_refcount_queue_entries(Translate_context* context,
- tree ret)
+Expression*
+Call_expression::do_lower(Gogo* gogo, int)
{
- Gogo* gogo = context->gogo();
- Refcounts* refcounts = context->function()->func_value()->refcounts();
- tree val = ret;
- Function_type* fntype = this->get_function_type(false);
- const Typed_identifier_list* results = fntype->results();
- gcc_assert(TREE_CODE(TREE_TYPE(val)) == RECORD_TYPE);
- std::vector<Refcount_entry>::const_iterator pre =
- this->refcount_entries_->begin();
- tree field = TYPE_FIELDS(TREE_TYPE(val));
- for (Typed_identifier_list::const_iterator pr = results->begin();
- pr != results->end();
- ++pr, field = TREE_CHAIN(field))
+ 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());
+
+ Func_expression* fne = this->fn_->func_expression();
+ if (fne != NULL
+ && 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());
+
+ if (this->args_ != NULL
+ && this->args_->size() == 1
+ && this->args_->front()->call_expression() != NULL
+ && this->fn_->type()->function_type() != NULL)
{
- gcc_assert(field != NULL_TREE);
- if (pr->type()->has_refcounted_component())
+ Function_type* fntype = this->fn_->type()->function_type();
+ 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)))
{
- gcc_assert(pre != this->refcount_entries_->end());
- Refcount_entry re = *pre;
- tree f = fold_build3(COMPONENT_REF, TREE_TYPE(field), val, field,
- NULL_TREE);
- pr->type()->set_refcount_queue_entry(gogo, refcounts, &re, f);
- ++pre;
+ 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. FIXME.
+ delete this->args_;
+ this->args_ = args;
}
}
- gcc_assert(pre == this->refcount_entries_->end());
- gcc_assert(field == NULL_TREE);
- return ret;
+
+ return this;
}
-// Make a call expression.
+// Get the function type. Returns NULL if we don't know the type. If
+// this returns NULL, and if_ERROR is true, issues an error.
-Call_expression*
-Expression::make_call(Expression* fn, Expression_list* args,
- source_location location)
+Function_type*
+Call_expression::get_function_type(bool issue_error)
{
- return new Call_expression(fn, args, location);
+ Type* type = this->fn_->type()->deref();
+ Function_type* fntype = type->function_type();
+ if (fntype != NULL)
+ return fntype;
+ if (issue_error && !type->is_error_type())
+ this->report_error("expected function");
+ return NULL;
}
-// A single result from a call which returns multiple results.
+// Return the number of values which this call will return.
-class Call_result_expression : public Expression
+size_t
+Call_expression::result_count()
{
- public:
- Call_result_expression(Call_expression* call, unsigned int index)
- : Expression(EXPRESSION_CALL_RESULT, call->location()),
- call_(call), index_(index), is_being_copied_(false)
- { }
-
- protected:
- int
- do_traverse(Traverse*);
-
- Type*
- do_type();
-
- void
- do_determine_type(const Type_context*);
-
- Expression*
- do_copy()
- {
- return new Call_result_expression(this->call_->call_expression(),
- this->index_);
- }
+ Function_type* fntype = this->get_function_type(false);
+ if (fntype == NULL)
+ return 0;
+ if (fntype->results() == NULL)
+ return 0;
+ return fntype->results()->size();
+}
- Expression*
- do_being_copied(Refcounts*, bool);
+// Get the type.
- Expression*
- do_note_decrements(Refcounts*);
+Type*
+Call_expression::do_type()
+{
+ if (this->type_ != NULL)
+ return this->type_;
- tree
- do_get_tree(Translate_context*);
+ Type* ret;
+ Function_type* fntype = this->get_function_type(false);
+ if (fntype == NULL)
+ return Type::make_error_type();
- private:
- // The underlying call expression.
- Expression* call_;
- // Which result we want.
- unsigned int index_;
- // Whether the result is being copied.
- bool is_being_copied_;
-};
+ 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);
-// Traverse a call result. We only traverse the call expression for
-// index 0, to avoid traversing it multiple times.
+ this->type_ = ret;
-int
-Call_result_expression::do_traverse(Traverse* traverse)
-{
- if (this->index_ > 0)
- return TRAVERSE_CONTINUE;
- return Expression::traverse(&this->call_, traverse);
+ return this->type_;
}
-// Get the type.
+// Determine types for a call expression. We can use the function
+// parameter types to set the types of the arguments.
-Type*
-Call_result_expression::do_type()
+void
+Call_expression::do_determine_type(const Type_context*)
{
- if (this->call_->is_error_expression())
- return Type::make_error_type();
- Function_type* fntype =
- this->call_->call_expression()->get_function_type(false);
- if (fntype == NULL)
- return Type::make_error_type();
- const Typed_identifier_list* results = fntype->results();
- Typed_identifier_list::const_iterator pr = results->begin();
- for (unsigned int i = 0; i < this->index_; ++i)
+ this->fn_->determine_type_no_context();
+ Function_type* fntype = this->get_function_type(false);
+ const Typed_identifier_list* parameters = NULL;
+ if (fntype != NULL)
+ parameters = fntype->parameters();
+ if (this->args_ != NULL)
{
- gcc_assert(pr != results->end());
- ++pr;
+ Typed_identifier_list::const_iterator pt;
+ if (parameters != NULL)
+ pt = parameters->begin();
+ for (Expression_list::const_iterator pa = this->args_->begin();
+ pa != this->args_->end();
+ ++pa)
+ {
+ if (parameters != NULL
+ && pt != parameters->end()
+ && (!fntype->is_varargs() || pt + 1 != parameters->end()))
+ {
+ Type_context subcontext(pt->type(), false);
+ (*pa)->determine_type(&subcontext);
+ ++pt;
+ }
+ else
+ (*pa)->determine_type_no_context();
+ }
}
- gcc_assert(pr != results->end());
- return pr->type();
}
-// Determine the type. We have nothing to do here, but the 0 result
-// needs to pass down to the caller.
+// Check types for parameter I.
void
-Call_result_expression::do_determine_type(const Type_context*)
+Call_expression::check_argument_type(int i, const Type* parameter_type,
+ const Type* argument_type,
+ source_location argument_location)
{
- if (this->index_ == 0)
- this->call_->determine_type_no_context();
+ std::string reason;
+ if (!Type::are_compatible_for_assign(parameter_type, argument_type, &reason))
+ {
+ 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();
+ }
}
-// The result will come back with a reference, so we don't need to do
-// anything to copy it.
+// Check types.
-Expression*
-Call_result_expression::do_being_copied(Refcounts*, bool)
+void
+Call_expression::do_check_types(Gogo*)
{
- this->is_being_copied_ = true;
- return this;
-}
+ Function_type* fntype = this->get_function_type(true);
+ if (fntype == NULL)
+ return;
-// Decrement the reference count if necessary.
+ if (fntype->is_method())
+ {
+ // We don't support pointers to methods, so the function has to
+ // be a bound method expression.
+ if (this->fn_->bound_method_expression() == NULL)
+ this->report_error(_("method call without object"));
+ }
-Expression*
-Call_result_expression::do_note_decrements(Refcounts* refcounts)
-{
- if (this->is_being_copied_ || !this->type()->has_refcounted_component())
- return this;
- return Expression::make_refcount_adjust(refcounts,
- REFCOUNT_DECREMENT_COMPUTED,
- this, false);
+ const Typed_identifier_list* parameters = fntype->parameters();
+ if (this->args_ == NULL)
+ {
+ // A single varargs parameter can accept zero arguments.
+ if (parameters != NULL
+ && (!fntype->is_varargs() || parameters->size() > 1))
+ this->report_error(_("not enough arguments"));
+ }
+ else if (parameters == NULL)
+ 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 (fntype->is_varargs() && pt + 1 == parameters->end())
+ {
+ // A varargs parameter can accept any number of
+ // arguments, but you can't pass nil.
+ for (; pa != this->args_->end(); ++pa)
+ {
+ if ((*pa)->type()->is_nil_type())
+ {
+ this->report_error(_("invalid use of %<nil%> for "
+ "%<...%> argument"));
+ return;
+ }
+ }
+ break;
+ }
+ if (pt == parameters->end())
+ {
+ this->report_error(_("too many arguments"));
+ return;
+ }
+ this->check_argument_type(i + 1, pt->type(), (*pa)->type(),
+ (*pa)->location());
+ }
+ // The varargs parameter can accept zero arguments.
+ if (pt != parameters->end()
+ && (!fntype->is_varargs() || pt + 1 != parameters->end()))
+ this->report_error(_("not enough arguments"));
+ }
}
-// Return the tree.
+// Get the function and the first argument to use when calling a bound
+// method.
tree
-Call_result_expression::do_get_tree(Translate_context* context)
+Call_expression::bound_method_function(Translate_context* context,
+ Bound_method_expression* bound_method,
+ tree* first_arg_ptr)
{
- tree call_tree = this->call_->get_tree(context);
- if (call_tree == error_mark_node)
+ 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;
- gcc_assert(TREE_CODE(TREE_TYPE(call_tree)) == RECORD_TYPE);
- tree field = TYPE_FIELDS(TREE_TYPE(call_tree));
- for (unsigned int i = 0; i < this->index_; ++i)
+
+ // We always pass a pointer to the first argument when calling a
+ // method.
+ if (first_argument->type()->points_to() == NULL)
{
- gcc_assert(field != NULL_TREE);
- field = TREE_CHAIN(field);
+ 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(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(COMPOUND_EXPR, pointer_to_arg_type,
+ build1(DECL_EXPR, void_type_node, tmp),
+ build_fold_addr_expr(tmp));
+ TREE_ADDRESSABLE(tmp) = 1;
+ }
+ if (first_arg == error_mark_node)
+ return error_mark_node;
}
- gcc_assert(field != NULL_TREE);
- return build3(COMPONENT_REF, TREE_TYPE(field), call_tree, field, NULL_TREE);
+
+ Type* fatype = bound_method->first_argument_type();
+ if (fatype != NULL)
+ {
+ if (fatype->points_to() == NULL)
+ fatype = Type::make_pointer_type(fatype);
+ first_arg = fold_convert(fatype->get_tree(context->gogo()), first_arg);
+ if (first_arg == error_mark_node
+ || TREE_TYPE(first_arg) == error_mark_node)
+ return error_mark_node;
+ }
+
+ *first_arg_ptr = first_arg;
+
+ return bound_method->method()->get_tree(context);
}
-// Make a reference to a single result of a call which returns
-// multiple results.
+// Get the function and the first argument to use when calling an
+// interface method.
-Expression*
-Expression::make_call_result(Call_expression* call, unsigned int index)
+tree
+Call_expression::interface_method_function(
+ Translate_context* context,
+ Interface_field_reference_expression* interface_method,
+ tree* first_arg_ptr)
{
- return new Call_result_expression(call, index);
+ tree expr = interface_method->expr()->get_tree(context);
+ if (expr == error_mark_node)
+ return error_mark_node;
+ expr = save_expr(expr);
+ tree first_arg = interface_method->get_underlying_object_tree(context, expr);
+ if (first_arg == error_mark_node)
+ return error_mark_node;
+ *first_arg_ptr = first_arg;
+ return interface_method->get_function_tree(context, expr);
}
-// Class Index_expression.
-
-// Traversal.
+// The call expression is being copied. There is nothing to do here
+// except to disable any decrements--the function should return with
+// an appropriate reference count.
-int
-Index_expression::do_traverse(Traverse* traverse)
+Expression*
+Call_expression::do_being_copied(Refcounts*, bool)
{
- 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))
- return TRAVERSE_EXIT;
- return TRAVERSE_CONTINUE;
+ this->is_being_copied_ = true;
+ return this;
}
-// Lower an index expression. This converts the generic index
-// expression into an array index, a string index, or a map index.
+// Where needed, decrement the reference counts of any values returned
+// by this call.
Expression*
-Index_expression::do_lower(Gogo*, int)
+Call_expression::do_note_decrements(Refcounts* refcounts)
{
- source_location location = this->location();
- Expression* left = this->left_;
- Expression* start = this->start_;
- Expression* end = this->end_;
-
- Type* type = left->type();
- if (type->is_error_type())
- return Expression::make_error(location);
- else if (type->array_type() != NULL
- || (type->deref()->array_type() != NULL
- && !type->deref()->array_type()->is_open_array_type()))
- return Expression::make_array_index(left, start, end, location);
- else if (type->is_string_type())
- return Expression::make_string_index(left, start, end, location);
- else if (type->map_type() != NULL)
+ if (this->is_being_copied_)
+ return this;
+ Function_type* fntype = this->get_function_type(false);
+ if (fntype == NULL)
+ return this;
+ const Typed_identifier_list* results = fntype->results();
+ if (results == NULL)
+ return this;
+ if (results->size() == 1)
{
- if (end != NULL)
+ if (!results->begin()->type()->has_refcounted_component())
+ return this;
+ return Expression::make_refcount_adjust(refcounts,
+ REFCOUNT_DECREMENT_COMPUTED,
+ this, false);
+ }
+ else if (!this->is_value_discarded_)
+ {
+ // If the value is not discarded, each result will be handled
+ // separately via Call_expression_result.
+ return this;
+ }
+ else
+ {
+ for (Typed_identifier_list::const_iterator p = results->begin();
+ p != results->end();
+ p++)
{
- error_at(location, "invalid slice of map");
- return Expression::make_error(location);
+ if (p->type()->has_refcounted_component())
+ {
+ if (this->refcount_entries_ == NULL)
+ this->refcount_entries_ = new std::vector<Refcount_entry>;
+ Refcount_entry re = refcounts->add(REFCOUNT_DECREMENT_COMPUTED,
+ p->type());
+ this->refcount_entries_->push_back(re);
+ }
}
- 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);
+ return this;
}
}
-// Make an index expression.
-
-Expression*
-Expression::make_index(Expression* left, Expression* start, Expression* end,
- source_location location)
-{
- return new Index_expression(left, start, end, location);
-}
-
-// An array index. This is used for both indexing and slicing.
+// Build the call expression.
-class Array_index_expression : public Expression
+tree
+Call_expression::do_get_tree(Translate_context* context)
{
- public:
- Array_index_expression(Expression* array, Expression* start,
- Expression* end, source_location location)
- : Expression(EXPRESSION_ARRAY_INDEX, location),
- array_(array), start_(start), end_(end), type_(NULL)
- { }
-
- protected:
- int
- do_traverse(Traverse*);
-
- Type*
- do_type();
+ if (this->tree_ != NULL_TREE)
+ return this->tree_;
- void
- do_determine_type(const Type_context*);
+ Function_type* fntype = this->get_function_type(false);
+ if (fntype == NULL)
+ return error_mark_node;
- void
- do_check_types(Gogo*);
+ if (this->fn_->is_error_expression())
+ return error_mark_node;
- Expression*
- do_copy()
- {
- return Expression::make_array_index(this->array_->copy(),
- this->start_->copy(),
- (this->end_ == NULL
- ? NULL
- : this->end_->copy()),
- this->location());
- }
+ Gogo* gogo = context->gogo();
+ source_location location = this->location();
- bool
- do_is_lvalue() const
- { return this->end_ == NULL; }
+ 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;
+ gcc_assert(!fntype->is_method() || is_method);
- bool
- do_address_taken(source_location);
+ bool is_varargs = fntype->is_varargs();
- Expression*
- do_being_copied(Refcounts*, bool);
+ int nargs;
+ tree* args;
+ if (this->args_ == NULL || this->args_->empty())
+ {
+ nargs = is_method ? 1 : 0;
+ if (is_varargs)
+ ++nargs;
+ args = nargs == 0 ? NULL : new tree[nargs];
+ if (is_varargs)
+ {
+ Type* varargs_type = Function_type::varargs_type();
+ // The old way, passing array[]interface{}.
+ // args[nargs - 1] = varargs_type->get_init_tree(gogo, false);
+ Struct_field_list* fields = new Struct_field_list();
+ Type* struct_type = Type::make_struct_type(fields, location);
+ tree val = struct_type->get_init_tree(gogo, false);
+ args[nargs - 1] = Expression::convert_for_assignment(context,
+ varargs_type,
+ struct_type,
+ val,
+ location);
+ }
+ }
+ else
+ {
+ const Typed_identifier_list* params = fntype->parameters();
+ gcc_assert(params != NULL);
- Expression*
- do_note_decrements(Refcounts*);
+ if (is_varargs)
+ nargs = params->size();
+ else
+ nargs = this->args_->size();
+ int i = is_method ? 1 : 0;
+ nargs += i;
+ args = new tree[nargs];
- Expression*
- do_being_set(Refcounts*);
+ 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)
+ {
+ Type* pp_type = pp->type();
+ if (is_varargs && pp + 1 == params->end())
+ {
+ // If the last argument is itself a varargs argument for
+ // the caller, we pass it without further encapsulation.
+ if (pe + 1 == this->args_->end()
+ && (*pe)->var_expression() != NULL
+ && (*pe)->var_expression()->named_object()->is_variable()
+ && ((*pe)->var_expression()->named_object()->var_value()->
+ is_varargs_parameter()))
+ {
+ pp_type = Function_type::varargs_type();
+ is_varargs = false;
+ }
+ else
+ break;
+ }
+ 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;
+ }
- tree
- do_get_tree(Translate_context*);
+ if (is_varargs)
+ {
+ Type* varargs_type = Function_type::varargs_type();
- 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.
- Expression* end_;
- // The type of the expression.
- Type* type_;
-};
+#if 0
+ // The old way, passing array[]interface{}.
+ Type* element_type = varargs_type->array_type()->element_type();
+ VEC(constructor_elt,gc)* values = VEC_alloc(constructor_elt, gc,
+ this->args_->size());
+ size_t j = 0;
+ for (; pe != this->args_->end(); ++pe, ++j)
+ {
+ constructor_elt* elt = VEC_quick_push(constructor_elt, values,
+ NULL);
+ elt->index = size_int(j);
+ tree expr_tree = (*pe)->get_tree(context);
+ elt->value = Expression::convert_for_assignment(context,
+ element_type,
+ (*pe)->type(),
+ expr_tree,
+ location);
+ }
-// Array index traversal.
+ mpz_t val;
+ mpz_init_set_ui(val, j);
+ Type* array_type =
+ Type::make_array_type(element_type,
+ Expression::make_integer(&val, NULL,
+ location));
+ mpz_clear(val);
-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;
- }
- return TRAVERSE_CONTINUE;
-}
+ tree varargs_val = build_constructor(array_type->get_tree(gogo),
+ values);
-// Return the type of an array index.
+ args[i] = Expression::convert_for_assignment(context, varargs_type,
+ array_type,
+ varargs_val,
+ location);
+#else
+ // The new way, passing a struct converted to an empty
+ // interface.
+ Struct_field_list* fields = new Struct_field_list();
+ Expression_list::const_iterator pehold = pe;
+ for (; pe != this->args_->end(); ++pe)
+ fields->push_back(Struct_field(Typed_identifier("UNNAMED",
+ (*pe)->type(),
+ location)));
+ Type* struct_type = Type::make_struct_type(fields, location);
+ tree struct_tree = struct_type->get_tree(gogo);
+ if (struct_tree == error_mark_node)
+ return error_mark_node;
-Type*
-Array_index_expression::do_type()
-{
- if (this->type_ == NULL)
- {
- Array_type* type = this->array_->type()->deref()->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_open_array_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);
+ VEC(constructor_elt,gc)* values = VEC_alloc(constructor_elt, gc,
+ this->args_->size());
+ tree field = TYPE_FIELDS(struct_tree);
+ for (pe = pehold; pe != this->args_->end(); ++pe)
+ {
+ gcc_assert(field != NULL_TREE);
+ constructor_elt* elt = VEC_quick_push(constructor_elt, values,
+ NULL);
+ elt->index = field;
+ elt->value = (*pe)->get_tree(context);
+ field = TREE_CHAIN(field);
+ }
+ gcc_assert(field == NULL_TREE);
+
+ tree varargs_val = build_constructor(struct_tree, values);
+
+ args[i] = Expression::convert_for_assignment(context, varargs_type,
+ struct_type,
+ varargs_val,
+ location);
+#endif
+
+ ++i;
+ ++pp;
}
+
+ gcc_assert(pp == params->end());
+ gcc_assert(i == nargs);
}
- return this->type_;
-}
-// Set the type of an array index.
+ tree rettype = TREE_TYPE(TREE_TYPE(fntype->get_tree(gogo)));
+ if (rettype == error_mark_node)
+ return error_mark_node;
-void
-Array_index_expression::do_determine_type(const Type_context*)
-{
- this->array_->determine_type_no_context();
- Type_context subcontext(NULL, true);
- this->start_->determine_type(&subcontext);
- if (this->end_ != NULL)
- this->end_->determine_type(&subcontext);
-}
+ tree fn;
+ if (has_closure)
+ fn = func->get_tree_without_closure(gogo);
+ else if (!is_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
+ gcc_unreachable();
-// Check types of an array index.
+ if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node)
+ return error_mark_node;
-void
-Array_index_expression::do_check_types(Gogo*)
-{
- if (this->start_->type()->integer_type() == NULL)
- this->report_error(_("index must be integer"));
- if (this->end_ != NULL
- && this->end_->type()->integer_type() == NULL)
- this->report_error(_("slice end must be integer"));
+ tree ret = build_call_array(rettype, fn, nargs, args);
+ delete[] args;
- Array_type* array_type = this->array_->type()->deref()->array_type();
- gcc_assert(array_type != NULL);
+ SET_EXPR_LOCATION(ret, location);
- Type* dummy;
- mpz_t lval;
- mpz_init(lval);
- bool lval_valid = (array_type->length() != NULL
- && array_type->length()->integer_constant_value(true,
- lval,
- &dummy));
- mpz_t ival;
- mpz_init(ival);
- if (this->start_->integer_constant_value(true, ival, &dummy))
- {
- if (mpz_sgn(ival) < 0
- || (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)
+ if (has_closure)
{
- if (this->end_->integer_constant_value(true, ival, &dummy))
- {
- if (mpz_sgn(ival) < 0 || (lval_valid && mpz_cmp(ival, lval) > 0))
- {
- error_at(this->end_->location(), "array index out of bounds");
- this->set_is_error();
- }
- }
+ tree closure_tree = func->closure()->get_tree(context);
+ if (closure_tree != error_mark_node)
+ CALL_EXPR_STATIC_CHAIN(ret) = closure_tree;
}
- mpz_clear(ival);
- mpz_clear(lval);
-}
-// We can take the address of an array index. We don't support taking
-// the address of a slice--I'm not sure what the type of that would
-// be. Taking the address of an array index implies taking the
-// address of the array.
+ // If there is more than one result, we will refer to the call
+ // multiple times.
+ if (fntype->results() != NULL && fntype->results()->size() > 1)
+ ret = save_expr(ret);
-bool
-Array_index_expression::do_address_taken(source_location location)
-{
- if (!this->array_->address_taken(location))
- return false;
- if (this->end_ != NULL)
- {
- error_at(location, "may not take address of array slice");
- return false;
- }
- return true;
-}
+ if (this->refcount_entries_ != NULL)
+ ret = this->set_refcount_queue_entries(context, ret);
-// Copying an element of an array may require incrementing a reference
-// count. Copying a slice requires incrementing the reference count
-// of the underlying array.
+ this->tree_ = ret;
-Expression*
-Array_index_expression::do_being_copied(Refcounts* refcounts, bool for_local)
-{
- if (this->end_ == NULL && !this->type()->has_refcounted_component())
- return this;
- return Expression::make_refcount_adjust(refcounts,
- REFCOUNT_INCREMENT_COPIED,
- this, for_local);
+ return ret;
}
-// Referring to an element of an array does not require changing any
-// reference counts. If the array is changed before the value is
-// used, the program is ill-defined. We do not introduce a reference
-// count for a slice, so there is nothing to decrement. FIXME: Is
-// that safe?
+// Set entries in the decrement queue as needed.
-Expression*
-Array_index_expression::do_note_decrements(Refcounts*)
+tree
+Call_expression::set_refcount_queue_entries(Translate_context* context,
+ tree ret)
{
- return this;
+ Gogo* gogo = context->gogo();
+ Refcounts* refcounts = context->function()->func_value()->refcounts();
+ tree val = ret;
+ Function_type* fntype = this->get_function_type(false);
+ const Typed_identifier_list* results = fntype->results();
+ gcc_assert(TREE_CODE(TREE_TYPE(val)) == RECORD_TYPE);
+ std::vector<Refcount_entry>::const_iterator pre =
+ this->refcount_entries_->begin();
+ tree field = TYPE_FIELDS(TREE_TYPE(val));
+ for (Typed_identifier_list::const_iterator pr = results->begin();
+ pr != results->end();
+ ++pr, field = TREE_CHAIN(field))
+ {
+ gcc_assert(field != NULL_TREE);
+ if (pr->type()->has_refcounted_component())
+ {
+ gcc_assert(pre != this->refcount_entries_->end());
+ Refcount_entry re = *pre;
+ tree f = fold_build3(COMPONENT_REF, TREE_TYPE(field), val, field,
+ NULL_TREE);
+ pr->type()->set_refcount_queue_entry(gogo, refcounts, &re, f);
+ ++pre;
+ }
+ }
+ gcc_assert(pre == this->refcount_entries_->end());
+ gcc_assert(field == NULL_TREE);
+ return ret;
}
-// Assigning to an element of an array may require decrementing a
-// reference count of the old value.
+// Make a call expression.
-Expression*
-Array_index_expression::do_being_set(Refcounts* refcounts)
+Call_expression*
+Expression::make_call(Expression* fn, Expression_list* args,
+ source_location location)
{
- gcc_assert(this->end_ == NULL);
- if (!this->type()->has_refcounted_component())
- return this;
- return Expression::make_refcount_decrement_lvalue(refcounts, this);
+ return new Call_expression(fn, args, location);
}
-// Get a tree for an array index.
+// A single result from a call which returns multiple results.
-tree
-Array_index_expression::do_get_tree(Translate_context* context)
+class Call_result_expression : public Expression
{
- Gogo* gogo = context->gogo();
- source_location loc = this->location();
+ public:
+ Call_result_expression(Call_expression* call, unsigned int index)
+ : Expression(EXPRESSION_CALL_RESULT, call->location()),
+ call_(call), index_(index), is_being_copied_(false)
+ { }
- Type* t = this->array_->type();
- Type* pt = t->points_to();
- if (pt != NULL)
- t = pt;
- Array_type* array_type = t->array_type();
- gcc_assert(array_type != NULL);
+ protected:
+ int
+ do_traverse(Traverse*);
- tree type_tree = array_type->get_tree(gogo);
+ Type*
+ do_type();
- tree array_tree = this->array_->get_tree(context);
- if (array_tree == error_mark_node)
- return error_mark_node;
+ void
+ do_determine_type(const Type_context*);
- tree bad_index = boolean_false_node;
- if (pt != NULL)
- {
- gcc_assert(!array_type->is_open_array_type());
- if (!DECL_P(array_tree))
- array_tree = save_expr(array_tree);
- bad_index = build2(EQ_EXPR, boolean_type_node, array_tree,
- fold_convert(TREE_TYPE(array_tree),
- null_pointer_node));
- array_tree = build_fold_indirect_ref(array_tree);
- }
+ Expression*
+ do_copy()
+ {
+ return new Call_result_expression(this->call_->call_expression(),
+ this->index_);
+ }
- tree start_tree = this->start_->get_tree(context);
- if (start_tree == error_mark_node)
- return error_mark_node;
+ Expression*
+ do_being_copied(Refcounts*, bool);
- if (array_type->length() == NULL && !DECL_P(array_tree))
- array_tree = save_expr(array_tree);
- if (!DECL_P(start_tree))
- start_tree = save_expr(start_tree);
- tree length_tree = array_type->length_tree(gogo, array_tree);
- start_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), start_tree);
- bad_index = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node, bad_index,
- fold_build2_loc(loc,
- (this->end_ == NULL
- ? GE_EXPR
- : GT_EXPR),
- boolean_type_node, start_tree,
- length_tree));
+ Expression*
+ do_note_decrements(Refcounts*);
+
+ tree
+ do_get_tree(Translate_context*);
+
+ private:
+ // The underlying call expression.
+ Expression* call_;
+ // Which result we want.
+ unsigned int index_;
+ // Whether the result is being copied.
+ bool is_being_copied_;
+};
- static tree bad_index_fndecl;
- tree crash = Gogo::call_builtin(&bad_index_fndecl,
- loc,
- "__go_bad_index",
- 0,
- void_type_node);
- TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+// Traverse a call result. We only traverse the call expression for
+// index 0, to avoid traversing it multiple times.
- 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);
+int
+Call_result_expression::do_traverse(Traverse* traverse)
+{
+ if (this->index_ > 0)
+ return TRAVERSE_CONTINUE;
+ return Expression::traverse(&this->call_, traverse);
+}
- if (array_type->length() != NULL)
- {
- // Fixed array.
- return build4(ARRAY_REF, TREE_TYPE(type_tree), array_tree,
- start_tree, NULL_TREE, NULL_TREE);
- }
- else
- {
- // Open array.
- tree values = array_type->value_pointer_tree(gogo, array_tree);
- tree element_type_tree = array_type->element_type()->get_tree(gogo);
- tree element_size = TYPE_SIZE_UNIT(element_type_tree);
- tree offset = fold_build2_loc(loc, MULT_EXPR, TREE_TYPE(length_tree),
- start_tree,
- fold_convert_loc(loc,
- TREE_TYPE(length_tree),
- element_size));
- tree ptr = fold_build2_loc(loc, POINTER_PLUS_EXPR,
- TREE_TYPE(values), values,
- fold_convert_loc(loc, sizetype, offset));
- return build_fold_indirect_ref(ptr);
- }
- }
+// Get the type.
- // Array slice.
+Type*
+Call_result_expression::do_type()
+{
+ if (this->call_->is_error_expression())
+ return Type::make_error_type();
+ Function_type* fntype =
+ this->call_->call_expression()->get_function_type(false);
+ if (fntype == NULL)
+ return Type::make_error_type();
+ const Typed_identifier_list* results = fntype->results();
+ Typed_identifier_list::const_iterator pr = results->begin();
+ for (unsigned int i = 0; i < this->index_; ++i)
+ {
+ gcc_assert(pr != results->end());
+ ++pr;
+ }
+ gcc_assert(pr != results->end());
+ return pr->type();
+}
- tree end_tree = this->end_->get_tree(context);
- if (end_tree == error_mark_node)
- return error_mark_node;
- end_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), end_tree);
+// Determine the type. We have nothing to do here, but the 0 result
+// needs to pass down to the caller.
- if (!DECL_P(end_tree))
- end_tree = save_expr(end_tree);
- tree capacity_tree = array_type->capacity_tree(gogo, array_tree);
- capacity_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), capacity_tree);
- capacity_tree = save_expr(capacity_tree);
- tree bad_end = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
- fold_build2_loc(loc, LT_EXPR,
- boolean_type_node,
- end_tree, start_tree),
- fold_build2_loc(loc, GT_EXPR,
- boolean_type_node,
- end_tree, capacity_tree));
- bad_index = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
- bad_index, bad_end);
+void
+Call_result_expression::do_determine_type(const Type_context*)
+{
+ if (this->index_ == 0)
+ this->call_->determine_type_no_context();
+}
- tree element_type_tree = array_type->element_type()->get_tree(gogo);
- tree element_size = TYPE_SIZE_UNIT(element_type_tree);
- element_size = fold_convert_loc(loc, TREE_TYPE(length_tree), element_size);
+// The result will come back with a reference, so we don't need to do
+// anything to copy it.
- tree offset = fold_build2_loc(loc, MULT_EXPR, TREE_TYPE(length_tree),
- start_tree, element_size);
+Expression*
+Call_result_expression::do_being_copied(Refcounts*, bool)
+{
+ this->is_being_copied_ = true;
+ return this;
+}
- tree value_pointer = array_type->value_pointer_tree(gogo, array_tree);
+// Decrement the reference count if necessary.
- value_pointer = fold_build2_loc(loc, POINTER_PLUS_EXPR,
- TREE_TYPE(value_pointer),
- value_pointer,
- fold_convert_loc(loc, sizetype, offset));
+Expression*
+Call_result_expression::do_note_decrements(Refcounts* refcounts)
+{
+ if (this->is_being_copied_ || !this->type()->has_refcounted_component())
+ return this;
+ return Expression::make_refcount_adjust(refcounts,
+ REFCOUNT_DECREMENT_COMPUTED,
+ this, false);
+}
- tree result_length_tree = fold_build2_loc(loc, MINUS_EXPR,
- TREE_TYPE(length_tree),
- end_tree, start_tree);
+// Return the tree.
- tree result_capacity_tree = fold_build2_loc(loc, MINUS_EXPR,
- TREE_TYPE(length_tree),
- capacity_tree, start_tree);
+tree
+Call_result_expression::do_get_tree(Translate_context* context)
+{
+ tree call_tree = this->call_->get_tree(context);
+ if (call_tree == error_mark_node)
+ return error_mark_node;
+ gcc_assert(TREE_CODE(TREE_TYPE(call_tree)) == RECORD_TYPE);
+ tree field = TYPE_FIELDS(TREE_TYPE(call_tree));
+ for (unsigned int i = 0; i < this->index_; ++i)
+ {
+ gcc_assert(field != NULL_TREE);
+ field = TREE_CHAIN(field);
+ }
+ gcc_assert(field != NULL_TREE);
+ return build3(COMPONENT_REF, TREE_TYPE(field), call_tree, field, NULL_TREE);
+}
- tree struct_tree = this->type()->get_tree(gogo);
- gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE);
+// Make a reference to a single result of a call which returns
+// multiple results.
- VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 3);
+Expression*
+Expression::make_call_result(Call_expression* call, unsigned int index)
+{
+ return new Call_result_expression(call, index);
+}
- constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
- tree field = TYPE_FIELDS(struct_tree);
- gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0);
- elt->index = field;
- elt->value = value_pointer;
+// Class Index_expression.
- elt = VEC_quick_push(constructor_elt, init, NULL);
- field = TREE_CHAIN(field);
- gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
- elt->index = field;
- elt->value = fold_convert_loc(loc, TREE_TYPE(field), result_length_tree);
+// Traversal.
- elt = VEC_quick_push(constructor_elt, init, NULL);
- field = TREE_CHAIN(field);
- gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0);
- elt->index = field;
- elt->value = fold_convert_loc(loc, TREE_TYPE(field), result_capacity_tree);
+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))
+ return TRAVERSE_EXIT;
+ return TRAVERSE_CONTINUE;
+}
- tree constructor = build_constructor(struct_tree, init);
+// Lower an index expression. This converts the generic index
+// expression into an array index, a string index, or a map index.
- if (TREE_CONSTANT(value_pointer)
- && TREE_CONSTANT(result_length_tree)
- && TREE_CONSTANT(result_capacity_tree))
- TREE_CONSTANT(constructor) = 1;
+Expression*
+Index_expression::do_lower(Gogo*, int)
+{
+ source_location location = this->location();
+ Expression* left = this->left_;
+ Expression* start = this->start_;
+ Expression* end = this->end_;
- return fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(constructor),
- build3(COND_EXPR, void_type_node,
- bad_index, crash, NULL_TREE),
- constructor);
+ Type* type = left->type();
+ if (type->is_error_type())
+ return Expression::make_error(location);
+ else if (type->array_type() != NULL
+ || (type->deref()->array_type() != NULL
+ && !type->deref()->array_type()->is_open_array_type()))
+ return Expression::make_array_index(left, start, end, location);
+ else if (type->is_string_type())
+ return Expression::make_string_index(left, start, end, location);
+ else if (type->map_type() != NULL)
+ {
+ if (end != 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);
+ }
}
-// Make an array index expression. END may be NULL.
+// Make an index expression.
Expression*
-Expression::make_array_index(Expression* array, Expression* start,
- Expression* end, source_location location)
+Expression::make_index(Expression* left, Expression* start, Expression* end,
+ source_location location)
{
- return new Array_index_expression(array, start, end, location);
+ return new Index_expression(left, start, end, location);
}
-// A string index. This is used for both indexing and slicing.
+// An array index. This is used for both indexing and slicing.
-class String_index_expression : public Expression
+class Array_index_expression : public Expression
{
public:
- String_index_expression(Expression* string, Expression* start,
- Expression* end, source_location location)
- : Expression(EXPRESSION_STRING_INDEX, location),
- string_(string), start_(start), end_(end), is_being_copied_(false)
+ Array_index_expression(Expression* array, Expression* start,
+ Expression* end, source_location location)
+ : Expression(EXPRESSION_ARRAY_INDEX, location),
+ array_(array), start_(start), end_(end), type_(NULL)
{ }
protected:
@@ -6509,17 +6695,20 @@ class String_index_expression : public E
Expression*
do_copy()
{
- return Expression::make_string_index(this->string_->copy(),
- this->start_->copy(),
- (this->end_ == NULL
- ? NULL
- : this->end_->copy()),
- this->location());
+ return Expression::make_array_index(this->array_->copy(),
+ this->start_->copy(),
+ (this->end_ == NULL
+ ? NULL
+ : this->end_->copy()),
+ this->location());
}
bool
- do_address_taken(source_location)
- { return true; }
+ do_is_lvalue() const
+ { return this->end_ == NULL; }
+
+ bool
+ do_address_taken(source_location, bool);
Expression*
do_being_copied(Refcounts*, bool);
@@ -6527,26 +6716,29 @@ class String_index_expression : public E
Expression*
do_note_decrements(Refcounts*);
+ Expression*
+ do_being_set(Refcounts*);
+
tree
do_get_tree(Translate_context*);
private:
- // The string we are getting a value from.
- Expression* string_;
+ // 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.
Expression* end_;
- // Whether the slice is being copied.
- bool is_being_copied_;
+ // The type of the expression.
+ Type* type_;
};
-// String index traversal.
+// Array index traversal.
int
-String_index_expression::do_traverse(Traverse* traverse)
+Array_index_expression::do_traverse(Traverse* traverse)
{
- if (Expression::traverse(&this->string_, traverse) == TRAVERSE_EXIT)
+ if (Expression::traverse(&this->array_, traverse) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
if (Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
@@ -6558,33 +6750,49 @@ String_index_expression::do_traverse(Tra
return TRAVERSE_CONTINUE;
}
-// Return the type of a string index.
+// Return the type of an array index.
Type*
-String_index_expression::do_type()
+Array_index_expression::do_type()
{
- if (this->end_ == NULL)
- return Type::lookup_integer_type("uint8");
- else
- return Type::make_string_type();
+ if (this->type_ == NULL)
+ {
+ Array_type* type = this->array_->type()->deref()->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_open_array_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_;
}
-// Determine the type of a string index.
+// Set the type of an array index.
void
-String_index_expression::do_determine_type(const Type_context*)
+Array_index_expression::do_determine_type(const Type_context*)
{
- this->string_->determine_type_no_context();
+ this->array_->determine_type_no_context();
Type_context subcontext(NULL, true);
this->start_->determine_type(&subcontext);
if (this->end_ != NULL)
this->end_->determine_type(&subcontext);
}
-// Check types of a string index.
+// Check types of an array index.
void
-String_index_expression::do_check_types(Gogo*)
+Array_index_expression::do_check_types(Gogo*)
{
if (this->start_->type()->integer_type() == NULL)
this->report_error(_("index must be integer"));
@@ -6592,1551 +6800,1383 @@ String_index_expression::do_check_types(
&& this->end_->type()->integer_type() == NULL)
this->report_error(_("slice end must be integer"));
- std::string sval;
- bool sval_valid = this->string_->string_constant_value(&sval);
+ Array_type* array_type = this->array_->type()->deref()->array_type();
+ gcc_assert(array_type != NULL);
- mpz_t ival;
- mpz_init(ival);
Type* dummy;
- if (this->start_->integer_constant_value(true, ival, &dummy))
- {
- 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)
- {
- if (this->end_->integer_constant_value(true, ival, &dummy))
- {
- if (mpz_sgn(ival) < 0
- || (sval_valid && mpz_cmp_ui(ival, sval.length()) > 0))
- {
- error_at(this->end_->location(), "string index out of bounds");
- this->set_is_error();
- }
- }
- }
- mpz_clear(ival);
-}
-
-// Copying a string index or a string slice does not require any
-// special action.
-
-Expression*
-String_index_expression::do_being_copied(Refcounts*, bool)
-{
- this->is_being_copied_ = true;
- return this;
-}
-
-// A string slice introduces a new reference which must be decremented
-// if the value is discarded.
-
-Expression*
-String_index_expression::do_note_decrements(Refcounts* refcounts)
-{
- if (this->end_ == NULL || this->is_being_copied_)
- return this;
- return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
- this, false);
-}
-
-// Get a tree for a string index.
-
-tree
-String_index_expression::do_get_tree(Translate_context* context)
-{
- 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);
-
- tree start_tree = this->start_->get_tree(context);
- if (start_tree == error_mark_node)
- return error_mark_node;
- start_tree = fold_convert(sizetype, start_tree);
-
- tree string_type = TREE_TYPE(string_tree);
-
- if (this->end_ == NULL)
- {
- if (!DECL_P(string_tree))
- string_tree = save_expr(string_tree);
- if (!DECL_P(start_tree))
- start_tree = save_expr(start_tree);
-
- tree length_tree = String_type::length_tree(context->gogo(),
- string_tree);
- tree bad_index = fold_build2(GE_EXPR, boolean_type_node,
- start_tree, length_tree);
-
- // FIXME: Duplicates Array_index::do_get_tree.
- static tree bad_index_fndecl;
- tree crash = Gogo::call_builtin(&bad_index_fndecl,
- this->location(),
- "__go_bad_index",
- 0,
- void_type_node);
- TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
-
- tree bytes_tree = String_type::bytes_tree(context->gogo(), string_tree);
- tree ptr = fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(bytes_tree),
- bytes_tree, start_tree);
- tree index = build_fold_indirect_ref(ptr);
-
- return fold_build2(COMPOUND_EXPR, TREE_TYPE(index),
- build3(COND_EXPR, void_type_node,
- bad_index, crash, NULL_TREE),
- index);
- }
- else
- {
- tree end_tree = this->end_->get_tree(context);
- if (end_tree == error_mark_node)
- return error_mark_node;
- end_tree = fold_convert(sizetype, end_tree);
- static tree strslice_fndecl;
- return Gogo::call_builtin(&strslice_fndecl,
- this->location(),
- "__go_string_slice",
- 3,
- string_type,
- string_type,
- string_tree,
- sizetype,
- start_tree,
- sizetype,
- end_tree);
- }
-}
-
-// Make a string index expression. END may be NULL.
-
-Expression*
-Expression::make_string_index(Expression* string, Expression* start,
- Expression* end, source_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();
- gcc_assert(mt != NULL);
- 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()
-{
- Type* type = this->get_map_type()->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();
- Type_context subcontext(this->get_map_type()->key_type(), true);
- this->index_->determine_type(&subcontext);
-}
-
-// Check types of a map index.
-
-void
-Map_index_expression::do_check_types(Gogo*)
-{
- std::string reason;
- if (!Type::are_compatible_for_assign(this->get_map_type()->key_type(),
- this->index_->type(),
- &reason))
+ mpz_t lval;
+ mpz_init(lval);
+ bool lval_valid = (array_type->length() != NULL
+ && array_type->length()->integer_constant_value(true,
+ lval,
+ &dummy));
+ mpz_t ival;
+ mpz_init(ival);
+ if (this->start_->integer_constant_value(true, ival, &dummy))
{
- if (reason.empty())
- this->report_error(_("incompatible type for map index"));
- else
+ if (mpz_sgn(ival) < 0
+ || (lval_valid
+ && (this->end_ == NULL
+ ? mpz_cmp(ival, lval) >= 0
+ : mpz_cmp(ival, lval) > 0)))
{
- error_at(this->location(), "incompatible type for map index (%s)",
- reason.c_str());
+ error_at(this->start_->location(), "array index out of bounds");
this->set_is_error();
}
}
+ if (this->end_ != NULL)
+ {
+ if (this->end_->integer_constant_value(true, ival, &dummy))
+ {
+ if (mpz_sgn(ival) < 0 || (lval_valid && mpz_cmp(ival, lval) > 0))
+ {
+ error_at(this->end_->location(), "array index out of bounds");
+ this->set_is_error();
+ }
+ }
+ }
+ mpz_clear(ival);
+ mpz_clear(lval);
}
-// We can take the address of a map index expression if it is an
-// lvalue.
+// We can take the address of an array index. We don't support taking
+// the address of a slice--I'm not sure what the type of that would
+// be. Taking the address of an array index implies taking the
+// address of the array.
bool
-Map_index_expression::do_address_taken(source_location location)
+Array_index_expression::do_address_taken(source_location location,
+ bool escapes)
{
- if (!this->is_lvalue_)
+ if (!this->array_->address_taken(location, escapes))
+ return false;
+ if (this->end_ != NULL)
{
- this->report_address_taken_error(location);
+ error_at(location, "may not take address of array slice");
return false;
}
return true;
}
-// If we are copying the map index to a variable, we need to increment
-// the reference count.
+// Copying an element of an array may require incrementing a reference
+// count. Copying a slice requires incrementing the reference count
+// of the underlying array.
Expression*
-Map_index_expression::do_being_copied(Refcounts* refcounts, bool for_local)
+Array_index_expression::do_being_copied(Refcounts* refcounts, bool for_local)
{
- if (!this->type()->has_refcounted_component())
+ if (this->end_ == NULL && !this->type()->has_refcounted_component())
return this;
return Expression::make_refcount_adjust(refcounts,
REFCOUNT_INCREMENT_COPIED,
this, for_local);
}
-// We don't do anything with the reference count of a map index if we
-// aren't copying it, so there is nothing to do here. FIXME: Is this
-// safe?
+// Referring to an element of an array does not require changing any
+// reference counts. If the array is changed before the value is
+// used, the program is ill-defined. We do not introduce a reference
+// count for a slice, so there is nothing to decrement. FIXME: Is
+// that safe?
Expression*
-Map_index_expression::do_note_decrements(Refcounts*)
+Array_index_expression::do_note_decrements(Refcounts*)
{
return this;
}
-// If we are changing the map index, we need to decrement the
-// reference count for the old value, and we may need to increment the
-// reference count for the new index.
+// Assigning to an element of an array may require decrementing a
+// reference count of the old value.
Expression*
-Map_index_expression::do_being_set(Refcounts* refcounts)
+Array_index_expression::do_being_set(Refcounts* refcounts)
{
- if (this->get_map_type()->key_type()->has_refcounted_component())
- this->index_ = this->index_->being_copied(refcounts, false);
+ gcc_assert(this->end_ == NULL);
if (!this->type()->has_refcounted_component())
return this;
return Expression::make_refcount_decrement_lvalue(refcounts, this);
}
-// Get a tree for a map index.
+// Get a tree for an array index.
tree
-Map_index_expression::do_get_tree(Translate_context* context)
+Array_index_expression::do_get_tree(Translate_context* context)
{
- Map_type* type = this->get_map_type();
+ Gogo* gogo = context->gogo();
+ source_location loc = this->location();
- tree valptr = this->get_value_pointer(context, this->is_lvalue_);
- if (valptr == error_mark_node)
- return error_mark_node;
- valptr = save_expr(valptr);
+ Type* t = this->array_->type();
+ Type* pt = t->points_to();
+ if (pt != NULL)
+ t = pt;
+ Array_type* array_type = t->array_type();
+ gcc_assert(array_type != NULL);
- tree val_type_tree = TREE_TYPE(TREE_TYPE(valptr));
+ tree type_tree = array_type->get_tree(gogo);
- if (this->is_lvalue_)
- return build_fold_indirect_ref(valptr);
- else if (this->is_in_tuple_assignment_)
+ tree array_tree = this->array_->get_tree(context);
+ if (array_tree == error_mark_node)
+ return error_mark_node;
+
+ tree bad_index = boolean_false_node;
+ if (pt != NULL)
{
- // Tuple_map_assignment_statement is responsible for using this
- // appropriately.
- return valptr;
+ gcc_assert(!array_type->is_open_array_type());
+ if (!DECL_P(array_tree))
+ array_tree = save_expr(array_tree);
+ bad_index = build2(EQ_EXPR, boolean_type_node, array_tree,
+ fold_convert(TREE_TYPE(array_tree),
+ null_pointer_node));
+ array_tree = build_fold_indirect_ref(array_tree);
}
- else
+
+ tree start_tree = this->start_->get_tree(context);
+ if (start_tree == error_mark_node)
+ return error_mark_node;
+
+ if (array_type->length() == NULL && !DECL_P(array_tree))
+ array_tree = save_expr(array_tree);
+ if (!DECL_P(start_tree))
+ start_tree = save_expr(start_tree);
+ tree length_tree = array_type->length_tree(gogo, array_tree);
+ start_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), start_tree);
+ bad_index = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node, bad_index,
+ fold_build2_loc(loc,
+ (this->end_ == NULL
+ ? GE_EXPR
+ : GT_EXPR),
+ boolean_type_node, start_tree,
+ length_tree));
+
+ static tree bad_index_fndecl;
+ tree crash = Gogo::call_builtin(&bad_index_fndecl,
+ loc,
+ "__go_bad_index",
+ 0,
+ void_type_node);
+ TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+
+ if (this->end_ == NULL)
{
- return fold_build3(COND_EXPR, val_type_tree,
- fold_build2(EQ_EXPR, boolean_type_node, valptr,
- fold_convert(TREE_TYPE(valptr),
- null_pointer_node)),
- type->val_type()->get_init_tree(context->gogo(),
- false),
- build_fold_indirect_ref(valptr));
- }
-}
+ // 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);
-// 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.
+ if (array_type->length() != NULL)
+ {
+ // Fixed array.
+ return build4(ARRAY_REF, TREE_TYPE(type_tree), array_tree,
+ start_tree, NULL_TREE, NULL_TREE);
+ }
+ else
+ {
+ // Open array.
+ tree values = array_type->value_pointer_tree(gogo, array_tree);
+ tree element_type_tree = array_type->element_type()->get_tree(gogo);
+ tree element_size = TYPE_SIZE_UNIT(element_type_tree);
+ tree offset = fold_build2_loc(loc, MULT_EXPR, TREE_TYPE(length_tree),
+ start_tree,
+ fold_convert_loc(loc,
+ TREE_TYPE(length_tree),
+ element_size));
+ tree ptr = fold_build2_loc(loc, POINTER_PLUS_EXPR,
+ TREE_TYPE(values), values,
+ fold_convert_loc(loc, sizetype, offset));
+ return build_fold_indirect_ref(ptr);
+ }
+ }
-tree
-Map_index_expression::get_value_pointer(Translate_context* context,
- bool insert)
-{
- Map_type* type = this->get_map_type();
+ // Array slice.
- tree map_tree = this->map_->get_tree(context);
- tree index_tree = this->index_->get_tree(context);
- if (map_tree == error_mark_node || index_tree == error_mark_node)
+ tree end_tree = this->end_->get_tree(context);
+ if (end_tree == error_mark_node)
return error_mark_node;
+ end_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), end_tree);
- if (this->map_->type()->points_to() != NULL)
- map_tree = build_fold_indirect_ref(map_tree);
+ if (!DECL_P(end_tree))
+ end_tree = save_expr(end_tree);
+ tree capacity_tree = array_type->capacity_tree(gogo, array_tree);
+ capacity_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), capacity_tree);
+ capacity_tree = save_expr(capacity_tree);
+ tree bad_end = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
+ fold_build2_loc(loc, LT_EXPR,
+ boolean_type_node,
+ end_tree, start_tree),
+ fold_build2_loc(loc, GT_EXPR,
+ boolean_type_node,
+ end_tree, capacity_tree));
+ bad_index = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
+ bad_index, bad_end);
- // We need to pass in a pointer to the key, so stuff it into a
- // variable.
- tree tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree));
- DECL_IGNORED_P(tmp) = 0;
- DECL_INITIAL(tmp) = index_tree;
- tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
- tree tmpref = fold_convert(const_ptr_type_node, build_fold_addr_expr(tmp));
- TREE_ADDRESSABLE(tmp) = 1;
+ tree element_type_tree = array_type->element_type()->get_tree(gogo);
+ tree element_size = TYPE_SIZE_UNIT(element_type_tree);
+ element_size = fold_convert_loc(loc, TREE_TYPE(length_tree), element_size);
- 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));
+ tree offset = fold_build2_loc(loc, MULT_EXPR, TREE_TYPE(length_tree),
+ start_tree, element_size);
- tree val_type_tree = type->val_type()->get_tree(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 value_pointer = array_type->value_pointer_tree(gogo, array_tree);
- return build2(COMPOUND_EXPR, ptr_val_type_tree,
- make_tmp,
- fold_convert(ptr_val_type_tree, call));
-}
+ value_pointer = fold_build2_loc(loc, POINTER_PLUS_EXPR,
+ TREE_TYPE(value_pointer),
+ value_pointer,
+ fold_convert_loc(loc, sizetype, offset));
+
+ tree result_length_tree = fold_build2_loc(loc, MINUS_EXPR,
+ TREE_TYPE(length_tree),
+ end_tree, start_tree);
+
+ tree result_capacity_tree = fold_build2_loc(loc, MINUS_EXPR,
+ TREE_TYPE(length_tree),
+ capacity_tree, start_tree);
+
+ tree struct_tree = this->type()->get_tree(gogo);
+ gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE);
-// Return a tree to delete this key from the map.
+ VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 3);
-tree
-Map_index_expression::delete_key(Translate_context* context)
-{
- tree map_tree = this->map_->get_tree(context);
- tree index_tree = this->index_->get_tree(context);
- if (map_tree == error_mark_node || index_tree == error_mark_node)
- return error_mark_node;
+ constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
+ tree field = TYPE_FIELDS(struct_tree);
+ gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0);
+ elt->index = field;
+ elt->value = value_pointer;
- if (this->map_->type()->points_to() != NULL)
- map_tree = build_fold_indirect_ref(map_tree);
+ elt = VEC_quick_push(constructor_elt, init, NULL);
+ field = TREE_CHAIN(field);
+ gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
+ elt->index = field;
+ elt->value = fold_convert_loc(loc, TREE_TYPE(field), result_length_tree);
- // We need to pass in a pointer to the key, so stuff it in a
- // variable.
- tree tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree));
- DECL_IGNORED_P(tmp) = 0;
- DECL_INITIAL(tmp) = index_tree;
- tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
- tree tmpref = fold_convert(const_ptr_type_node, build_fold_addr_expr(tmp));
- TREE_ADDRESSABLE(tmp) = 1;
+ elt = VEC_quick_push(constructor_elt, init, NULL);
+ field = TREE_CHAIN(field);
+ gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0);
+ elt->index = field;
+ elt->value = fold_convert_loc(loc, TREE_TYPE(field), result_capacity_tree);
- static tree map_delete_fndecl;
- tree call = Gogo::call_builtin(&map_delete_fndecl,
- this->location(),
- "__go_map_delete",
- 2,
- void_type_node,
- TREE_TYPE(map_tree),
- map_tree,
- const_ptr_type_node,
- tmpref);
+ tree constructor = build_constructor(struct_tree, init);
- return build2(COMPOUND_EXPR, void_type_node, make_tmp, call);
+ if (TREE_CONSTANT(value_pointer)
+ && TREE_CONSTANT(result_length_tree)
+ && TREE_CONSTANT(result_capacity_tree))
+ TREE_CONSTANT(constructor) = 1;
+
+ return fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(constructor),
+ build3(COND_EXPR, void_type_node,
+ bad_index, crash, NULL_TREE),
+ constructor);
}
-// Make a map index expression.
+// Make an array index expression. END may be NULL.
-Map_index_expression*
-Expression::make_map_index(Expression* map, Expression* index,
- source_location location)
+Expression*
+Expression::make_array_index(Expression* array, Expression* start,
+ Expression* end, source_location location)
{
- return new Map_index_expression(map, index, location);
+ return new Array_index_expression(array, start, end, location);
}
-// Class Field_reference_expression.
-
-// Return the type of a field reference.
+// A string index. This is used for both indexing and slicing.
-Type*
-Field_reference_expression::do_type()
+class String_index_expression : public Expression
{
- Type* expr_type = this->expr_->type();
- Type* points_to = expr_type->points_to();
- if (points_to != NULL)
- expr_type = points_to;
- Struct_type* struct_type = expr_type->struct_type();
- gcc_assert(struct_type != NULL);
- return struct_type->field(this->field_index_)->type();
-}
+ public:
+ String_index_expression(Expression* string, Expression* start,
+ Expression* end, source_location location)
+ : Expression(EXPRESSION_STRING_INDEX, location),
+ string_(string), start_(start), end_(end), is_being_copied_(false)
+ { }
-// Check the types for a field reference.
+ protected:
+ int
+ do_traverse(Traverse*);
-void
-Field_reference_expression::do_check_types(Gogo*)
-{
- Type* expr_type = this->expr_->type();
- Type* points_to = expr_type->points_to();
- if (points_to != NULL)
- expr_type = points_to;
- Struct_type* struct_type = expr_type->struct_type();
- gcc_assert(struct_type != NULL);
- gcc_assert(struct_type->field(this->field_index_) != NULL);
-}
+ Type*
+ do_type();
-// We can take the address of a field, and it implies taking the
-// address of the whole structure.
+ void
+ do_determine_type(const Type_context*);
-bool
-Field_reference_expression::do_address_taken(source_location location)
-{
- if (!this->expr_->address_taken(location))
- return false;
- return true;
-}
+ void
+ do_check_types(Gogo*);
-// If we are copying the field to a variable, we need to increment the
-// reference count.
+ Expression*
+ do_copy()
+ {
+ return Expression::make_string_index(this->string_->copy(),
+ this->start_->copy(),
+ (this->end_ == NULL
+ ? NULL
+ : this->end_->copy()),
+ this->location());
+ }
-Expression*
-Field_reference_expression::do_being_copied(Refcounts* refcounts,
- bool for_local)
+ bool
+ do_address_taken(source_location, bool)
+ { return true; }
+
+ Expression*
+ do_being_copied(Refcounts*, bool);
+
+ Expression*
+ do_note_decrements(Refcounts*);
+
+ tree
+ do_get_tree(Translate_context*);
+
+ 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.
+ Expression* end_;
+ // Whether the slice is being copied.
+ bool is_being_copied_;
+};
+
+// String index traversal.
+
+int
+String_index_expression::do_traverse(Traverse* traverse)
{
- if (!this->type()->has_refcounted_component())
- return this;
- return Expression::make_refcount_adjust(refcounts,
- REFCOUNT_INCREMENT_COPIED,
- this, for_local);
+ 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;
}
-// We do not need to bump the reference count; any change to the
-// structure before the value of the field is used would mean an
-// invalid program.
+// Return the type of a string index.
-Expression*
-Field_reference_expression::do_note_decrements(Refcounts*)
+Type*
+String_index_expression::do_type()
{
- return this;
+ if (this->end_ == NULL)
+ return Type::lookup_integer_type("uint8");
+ else
+ return Type::make_string_type();
}
-// If we are changing the field, we need to decrement the reference
-// count for the old value.
+// Determine the type of a string index.
-Expression*
-Field_reference_expression::do_being_set(Refcounts* refcounts)
+void
+String_index_expression::do_determine_type(const Type_context*)
{
- if (!this->type()->has_refcounted_component())
- return this;
- return Expression::make_refcount_decrement_lvalue(refcounts, this);
+ this->string_->determine_type_no_context();
+ Type_context subcontext(NULL, true);
+ this->start_->determine_type(&subcontext);
+ if (this->end_ != NULL)
+ this->end_->determine_type(&subcontext);
}
-// Get a tree for a field reference.
+// Check types of a string index.
-tree
-Field_reference_expression::do_get_tree(Translate_context* context)
+void
+String_index_expression::do_check_types(Gogo*)
{
- tree struct_tree = this->expr_->get_tree(context);
- if (struct_tree == error_mark_node
- || TREE_TYPE(struct_tree) == error_mark_node)
- return error_mark_node;
+ if (this->start_->type()->integer_type() == NULL)
+ this->report_error(_("index must be integer"));
+ if (this->end_ != NULL
+ && this->end_->type()->integer_type() == NULL)
+ this->report_error(_("slice end must be integer"));
- if (POINTER_TYPE_P(TREE_TYPE(struct_tree)))
+ std::string sval;
+ bool sval_valid = this->string_->string_constant_value(&sval);
+
+ mpz_t ival;
+ mpz_init(ival);
+ Type* dummy;
+ if (this->start_->integer_constant_value(true, ival, &dummy))
{
- // 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.
- HOST_WIDE_INT s = int_size_in_bytes(TREE_TYPE(TREE_TYPE(struct_tree)));
- if (s == -1 || s >= 4096)
+ if (mpz_sgn(ival) < 0
+ || (sval_valid && mpz_cmp_ui(ival, sval.length()) >= 0))
{
- if (!DECL_P(struct_tree))
- struct_tree = save_expr(struct_tree);
- tree compare = build2(EQ_EXPR, boolean_type_node, struct_tree,
- fold_convert(TREE_TYPE(struct_tree),
- null_pointer_node));
- // FIXME: This should be a different error message.
- static tree bad_index_fndecl;
- tree crash = Gogo::call_builtin(&bad_index_fndecl,
- this->location(),
- "__go_bad_index",
- 0,
- void_type_node);
- TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
- struct_tree = build2(COMPOUND_EXPR, TREE_TYPE(struct_tree),
- build3(COND_EXPR, void_type_node, compare,
- crash, NULL_TREE),
- struct_tree);
- }
- struct_tree = build_fold_indirect_ref(struct_tree);
- }
-
- gcc_assert(TREE_CODE(TREE_TYPE(struct_tree)) == RECORD_TYPE);
- tree field = TYPE_FIELDS(TREE_TYPE(struct_tree));
- gcc_assert(field != NULL_TREE);
- for (unsigned int i = this->field_index_; i > 0; --i)
+ error_at(this->start_->location(), "string index out of bounds");
+ this->set_is_error();
+ }
+ }
+ if (this->end_ != NULL)
{
- field = TREE_CHAIN(field);
- gcc_assert(field != NULL_TREE);
+ if (this->end_->integer_constant_value(true, ival, &dummy))
+ {
+ if (mpz_sgn(ival) < 0
+ || (sval_valid && mpz_cmp_ui(ival, sval.length()) > 0))
+ {
+ error_at(this->end_->location(), "string index out of bounds");
+ this->set_is_error();
+ }
+ }
}
- return build3(COMPONENT_REF, TREE_TYPE(field), struct_tree, field,
- NULL_TREE);
+ mpz_clear(ival);
}
-// Make a reference to a qualified identifier in an expression.
+// Copying a string index or a string slice does not require any
+// special action.
-Field_reference_expression*
-Expression::make_field_reference(Expression* expr, unsigned int field_index,
- source_location location)
+Expression*
+String_index_expression::do_being_copied(Refcounts*, bool)
{
- return new Field_reference_expression(expr, field_index, location);
+ this->is_being_copied_ = true;
+ return this;
}
-// Class Interface_field_reference_expression.
+// A string slice introduces a new reference which must be decremented
+// if the value is discarded.
-// Return a tree for the pointer to the function to call.
+Expression*
+String_index_expression::do_note_decrements(Refcounts* refcounts)
+{
+ if (this->end_ == NULL || this->is_being_copied_)
+ return this;
+ return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
+ this, false);
+}
+
+// Get a tree for a string index.
tree
-Interface_field_reference_expression::get_function_tree(Translate_context*,
- tree expr)
+String_index_expression::do_get_tree(Translate_context* context)
{
- if (this->expr_->type()->points_to() != NULL)
- expr = build_fold_indirect_ref(expr);
+ tree string_tree = this->string_->get_tree(context);
+ if (string_tree == error_mark_node)
+ return error_mark_node;
- tree expr_type = TREE_TYPE(expr);
- gcc_assert(POINTER_TYPE_P(expr_type));
- expr_type = TREE_TYPE(expr_type);
- expr = build_fold_indirect_ref(expr);
- gcc_assert(TREE_CODE(expr_type) == RECORD_TYPE);
+ if (this->string_->type()->points_to() != NULL)
+ string_tree = build_fold_indirect_ref(string_tree);
- tree field = TREE_CHAIN(TYPE_FIELDS(expr_type));
- gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods") == 0);
+ tree start_tree = this->start_->get_tree(context);
+ if (start_tree == error_mark_node)
+ return error_mark_node;
+ start_tree = fold_convert(sizetype, start_tree);
- tree table = build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
- gcc_assert(POINTER_TYPE_P(TREE_TYPE(table)));
+ tree string_type = TREE_TYPE(string_tree);
- table = build_fold_indirect_ref(table);
- gcc_assert(TREE_CODE(TREE_TYPE(table)) == RECORD_TYPE);
+ if (this->end_ == NULL)
+ {
+ if (!DECL_P(string_tree))
+ string_tree = save_expr(string_tree);
+ if (!DECL_P(start_tree))
+ start_tree = save_expr(start_tree);
- std::string name = Gogo::unpack_hidden_name(this->name_);
- for (field = TYPE_FIELDS(TREE_TYPE(table));
- field != NULL_TREE;
- field = TREE_CHAIN(field))
+ tree length_tree = String_type::length_tree(context->gogo(),
+ string_tree);
+ tree bad_index = fold_build2(GE_EXPR, boolean_type_node,
+ start_tree, length_tree);
+
+ // FIXME: Duplicates Array_index::do_get_tree.
+ static tree bad_index_fndecl;
+ tree crash = Gogo::call_builtin(&bad_index_fndecl,
+ this->location(),
+ "__go_bad_index",
+ 0,
+ void_type_node);
+ TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+
+ tree bytes_tree = String_type::bytes_tree(context->gogo(), string_tree);
+ tree ptr = fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(bytes_tree),
+ bytes_tree, start_tree);
+ tree index = build_fold_indirect_ref(ptr);
+
+ return fold_build2(COMPOUND_EXPR, TREE_TYPE(index),
+ build3(COND_EXPR, void_type_node,
+ bad_index, crash, NULL_TREE),
+ index);
+ }
+ else
{
- if (name == IDENTIFIER_POINTER(DECL_NAME(field)))
- break;
+ tree end_tree = this->end_->get_tree(context);
+ if (end_tree == error_mark_node)
+ return error_mark_node;
+ end_tree = fold_convert(sizetype, end_tree);
+ static tree strslice_fndecl;
+ return Gogo::call_builtin(&strslice_fndecl,
+ this->location(),
+ "__go_string_slice",
+ 3,
+ string_type,
+ string_type,
+ string_tree,
+ sizetype,
+ start_tree,
+ sizetype,
+ end_tree);
}
- gcc_assert(field != NULL_TREE);
-
- return build3(COMPONENT_REF, TREE_TYPE(field), table, field, NULL_TREE);
}
-// Return a tree for the first argument to pass to the interface
-// function.
+// Make a string index expression. END may be NULL.
-tree
-Interface_field_reference_expression::get_underlying_object_tree(
- Translate_context*,
- tree expr)
+Expression*
+Expression::make_string_index(Expression* string, Expression* start,
+ Expression* end, source_location location)
{
- if (this->expr_->type()->points_to() != NULL)
- expr = build_fold_indirect_ref(expr);
+ return new String_index_expression(string, start, end, location);
+}
- tree expr_type = TREE_TYPE(expr);
- gcc_assert(POINTER_TYPE_P(expr_type));
- expr_type = TREE_TYPE(expr_type);
- expr = build_fold_indirect_ref(expr);
- gcc_assert(TREE_CODE(expr_type) == RECORD_TYPE);
+// Class Map_index.
- tree field = TREE_CHAIN(TREE_CHAIN(TYPE_FIELDS(expr_type)));
- gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0);
+// Get the type of the map.
- return build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
+Map_type*
+Map_index_expression::get_map_type() const
+{
+ Map_type* mt = this->map_->type()->deref()->map_type();
+ gcc_assert(mt != NULL);
+ return mt;
}
-// Traversal.
+// Map index traversal.
int
-Interface_field_reference_expression::do_traverse(Traverse* traverse)
+Map_index_expression::do_traverse(Traverse* traverse)
{
- return Expression::traverse(&this->expr_, traverse);
+ if (Expression::traverse(&this->map_, traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ return Expression::traverse(&this->index_, traverse);
}
-// Return the type of an interface field reference.
+// Return the type of a map index.
Type*
-Interface_field_reference_expression::do_type()
+Map_index_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 Type::make_pointer_type(method->type());
+ Type* type = this->get_map_type()->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;
}
-// Determine types.
+// Fix the type of a map index.
void
-Interface_field_reference_expression::do_determine_type(const Type_context*)
+Map_index_expression::do_determine_type(const Type_context*)
{
- this->expr_->determine_type_no_context();
+ this->map_->determine_type_no_context();
+ Type_context subcontext(this->get_map_type()->key_type(), true);
+ this->index_->determine_type(&subcontext);
}
-// Check the types for an interface field reference.
+// Check types of a map index.
void
-Interface_field_reference_expression::do_check_types(Gogo*)
+Map_index_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)
- this->report_error(_("expected interface or pointer to interface"));
- else
+ std::string reason;
+ if (!Type::are_compatible_for_assign(this->get_map_type()->key_type(),
+ this->index_->type(),
+ &reason))
{
- const Typed_identifier* method =
- interface_type->find_method(this->name_);
- if (method == NULL)
+ if (reason.empty())
+ this->report_error(_("incompatible type for map index"));
+ else
{
- error_at(this->location(), "method %qs not in interface",
- this->name_.c_str());
+ error_at(this->location(), "incompatible type for map index (%s)",
+ reason.c_str());
this->set_is_error();
}
}
}
-// Get a tree for a reference to a field in an interface. There is no
-// standard tree type representation for this: it's a function
-// attached to its first argument, like a Bound_method_expression.
-// The only places it may currently be used are in a Call_expression
-// or a Go_statement, which will take it apart directly. So this has
-// nothing to do at present.
+// We can take the address of a map index expression if it is an
+// lvalue.
+
+bool
+Map_index_expression::do_address_taken(source_location location, bool)
+{
+ if (!this->is_lvalue_)
+ {
+ this->report_address_taken_error(location);
+ return false;
+ }
+ return true;
+}
+
+// If we are copying the map index to a variable, we need to increment
+// the reference count.
+
+Expression*
+Map_index_expression::do_being_copied(Refcounts* refcounts, bool for_local)
+{
+ if (!this->type()->has_refcounted_component())
+ return this;
+ return Expression::make_refcount_adjust(refcounts,
+ REFCOUNT_INCREMENT_COPIED,
+ this, for_local);
+}
-tree
-Interface_field_reference_expression::do_get_tree(Translate_context*)
+// We don't do anything with the reference count of a map index if we
+// aren't copying it, so there is nothing to do here. FIXME: Is this
+// safe?
+
+Expression*
+Map_index_expression::do_note_decrements(Refcounts*)
{
- gcc_unreachable();
+ return this;
}
-// Make a reference to a field in an interface.
+// If we are changing the map index, we need to decrement the
+// reference count for the old value, and we may need to increment the
+// reference count for the new index.
Expression*
-Expression::make_interface_field_reference(Expression* expr,
- const std::string& field,
- source_location location)
+Map_index_expression::do_being_set(Refcounts* refcounts)
{
- return new Interface_field_reference_expression(expr, field, location);
+ if (this->get_map_type()->key_type()->has_refcounted_component())
+ this->index_ = this->index_->being_copied(refcounts, false);
+ if (!this->type()->has_refcounted_component())
+ return this;
+ return Expression::make_refcount_decrement_lvalue(refcounts, this);
}
-// A general selector. This is a Parser_expression for LEFT.NAME. It
-// is lowered after we know the type of the left hand side.
+// Get a tree for a map index.
-class Selector_expression : public Parser_expression
+tree
+Map_index_expression::do_get_tree(Translate_context* context)
{
- public:
- Selector_expression(Expression* left, const std::string& name,
- source_location location)
- : Parser_expression(EXPRESSION_SELECTOR, location),
- left_(left), name_(name)
- { }
-
- protected:
- int
- do_traverse(Traverse* traverse)
- { return Expression::traverse(&this->left_, traverse); }
+ Map_type* type = this->get_map_type();
- Expression*
- do_lower(Gogo*, int);
+ tree valptr = this->get_value_pointer(context, this->is_lvalue_);
+ if (valptr == error_mark_node)
+ return error_mark_node;
+ valptr = save_expr(valptr);
- Expression*
- do_copy()
- {
- return new Selector_expression(this->left_->copy(), this->name_,
- this->location());
- }
+ tree val_type_tree = TREE_TYPE(TREE_TYPE(valptr));
- private:
- // The expression on the left hand side.
- Expression* left_;
- // The name on the right hand side.
- std::string name_;
-};
+ 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
+ {
+ return fold_build3(COND_EXPR, val_type_tree,
+ fold_build2(EQ_EXPR, boolean_type_node, valptr,
+ fold_convert(TREE_TYPE(valptr),
+ null_pointer_node)),
+ type->val_type()->get_init_tree(context->gogo(),
+ false),
+ build_fold_indirect_ref(valptr));
+ }
+}
-// Lower a selector expression once we know the real type of the left
-// hand side.
+// 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.
-Expression*
-Selector_expression::do_lower(Gogo*, int)
+tree
+Map_index_expression::get_value_pointer(Translate_context* context,
+ bool insert)
{
- source_location location = this->location();
- Expression* left = this->left_;
- const std::string& name(this->name_);
+ Map_type* type = this->get_map_type();
- Type* type = left->type();
- Type* ptype = type->deref();
- Struct_type* struct_type = ptype->struct_type();
- Interface_type* interface_type = ptype->interface_type();
- Named_type* named_type = type->named_type();
- if (named_type == NULL && type != ptype)
- named_type = ptype->named_type();
+ tree map_tree = this->map_->get_tree(context);
+ tree index_tree = this->index_->get_tree(context);
+ if (map_tree == error_mark_node || index_tree == error_mark_node)
+ return error_mark_node;
- if (type->is_error_type())
- return Expression::make_error(location);
- else if (struct_type != NULL && named_type == NULL)
- {
- Expression* ret = struct_type->field_reference(left, name, location);
- if (ret != NULL)
- return ret;
- }
- else if (interface_type != NULL && interface_type->find_method(name) != NULL)
- return Expression::make_interface_field_reference(left, name, location);
+ if (this->map_->type()->points_to() != NULL)
+ map_tree = build_fold_indirect_ref(map_tree);
- if (named_type != NULL)
- {
- bool found_pointer_method;
- Expression* ret = named_type->bind_field_or_method(left, name, location,
- &found_pointer_method);
- if (ret != NULL)
- return ret;
- if (found_pointer_method)
- {
- error_at(location, "method requires a pointer");
- return Expression::make_error(location);
- }
- }
+ // We need to pass in a pointer to the key, so stuff it into a
+ // variable.
+ tree tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree));
+ DECL_IGNORED_P(tmp) = 0;
+ DECL_INITIAL(tmp) = index_tree;
+ tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
+ tree tmpref = fold_convert(const_ptr_type_node, build_fold_addr_expr(tmp));
+ TREE_ADDRESSABLE(tmp) = 1;
- if (struct_type == NULL && interface_type == NULL && named_type == NULL)
- error_at(location,
- "request for %qs in something which has no fields or methods",
- Gogo::unpack_hidden_name(name).c_str());
- else
- error_at(location, "reference to undefined field or method %qs",
- Gogo::unpack_hidden_name(name).c_str());
- return Expression::make_error(location);
-}
+ 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));
-// Make a selector expression.
+ tree val_type_tree = type->val_type()->get_tree(context->gogo());
+ if (val_type_tree == error_mark_node)
+ return error_mark_node;
+ tree ptr_val_type_tree = build_pointer_type(val_type_tree);
-Expression*
-Expression::make_selector(Expression* left, const std::string& name,
- source_location location)
-{
- return new Selector_expression(left, name, location);
+ return build2(COMPOUND_EXPR, ptr_val_type_tree,
+ make_tmp,
+ fold_convert(ptr_val_type_tree, call));
}
-// Implement the builtin function new.
+// Return a tree to delete this key from the map.
-class Allocation_expression : public Expression
+tree
+Map_index_expression::delete_key(Translate_context* context)
{
- public:
- Allocation_expression(Type* type, source_location location)
- : Expression(EXPRESSION_ALLOCATION, location),
- type_(type), is_being_copied_(false)
- { }
+ tree map_tree = this->map_->get_tree(context);
+ tree index_tree = this->index_->get_tree(context);
+ if (map_tree == error_mark_node || index_tree == error_mark_node)
+ return error_mark_node;
- protected:
- int
- do_traverse(Traverse* traverse)
- { return Type::traverse(this->type_, traverse); }
+ if (this->map_->type()->points_to() != NULL)
+ map_tree = build_fold_indirect_ref(map_tree);
- Type*
- do_type()
- { return Type::make_pointer_type(this->type_); }
+ // We need to pass in a pointer to the key, so stuff it in a
+ // variable.
+ tree tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree));
+ DECL_IGNORED_P(tmp) = 0;
+ DECL_INITIAL(tmp) = index_tree;
+ tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
+ tree tmpref = fold_convert(const_ptr_type_node, build_fold_addr_expr(tmp));
+ TREE_ADDRESSABLE(tmp) = 1;
- void
- do_determine_type(const Type_context*)
- { }
+ static tree map_delete_fndecl;
+ tree call = Gogo::call_builtin(&map_delete_fndecl,
+ this->location(),
+ "__go_map_delete",
+ 2,
+ void_type_node,
+ TREE_TYPE(map_tree),
+ map_tree,
+ const_ptr_type_node,
+ tmpref);
- void
- do_check_types(Gogo*);
+ return build2(COMPOUND_EXPR, void_type_node, make_tmp, call);
+}
- Expression*
- do_copy()
- { return new Allocation_expression(this->type_, this->location()); }
+// Make a map index expression.
- Expression*
- do_being_copied(Refcounts*, bool);
+Map_index_expression*
+Expression::make_map_index(Expression* map, Expression* index,
+ source_location location)
+{
+ return new Map_index_expression(map, index, location);
+}
- Expression*
- do_note_decrements(Refcounts*);
+// Class Field_reference_expression.
+
+// Return the type of a field reference.
+
+Type*
+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;
+ Struct_type* struct_type = expr_type->struct_type();
+ gcc_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* expr_type = this->expr_->type();
+ Type* points_to = expr_type->points_to();
+ if (points_to != NULL)
+ expr_type = points_to;
+ Struct_type* struct_type = expr_type->struct_type();
+ gcc_assert(struct_type != NULL);
+ gcc_assert(struct_type->field(this->field_index_) != NULL);
+}
- tree
- do_get_tree(Translate_context*);
+// We can take the address of a field, and it implies taking the
+// address of the whole structure.
- private:
- // The type we are allocating.
- Type* type_;
- // Whether this value is being copied.
- bool is_being_copied_;
-};
+bool
+Field_reference_expression::do_address_taken(source_location location,
+ bool escapes)
+{
+ if (!this->expr_->address_taken(location, escapes))
+ return false;
+ return true;
+}
-// Check the type of an allocation expression.
+// If we are copying the field to a variable, we need to increment the
+// reference count.
-void
-Allocation_expression::do_check_types(Gogo*)
+Expression*
+Field_reference_expression::do_being_copied(Refcounts* refcounts,
+ bool for_local)
{
- if (this->type_->function_type() != NULL)
- this->report_error(_("invalid new of function type"));
+ if (!this->type()->has_refcounted_component())
+ return this;
+ return Expression::make_refcount_adjust(refcounts,
+ REFCOUNT_INCREMENT_COPIED,
+ this, for_local);
}
-// An allocation expression arrives with a reference count, so nothing
-// special is needed to copy it.
+// We do not need to bump the reference count; any change to the
+// structure before the value of the field is used would mean an
+// invalid program.
Expression*
-Allocation_expression::do_being_copied(Refcounts*, bool)
+Field_reference_expression::do_note_decrements(Refcounts*)
{
- this->is_being_copied_ = true;
return this;
}
-// An allocation expression arrives with a reference count, so if we
-// don't copy the value we must free it.
+// If we are changing the field, we need to decrement the reference
+// count for the old value.
Expression*
-Allocation_expression::do_note_decrements(Refcounts* refcounts)
+Field_reference_expression::do_being_set(Refcounts* refcounts)
{
- if (this->is_being_copied_)
+ if (!this->type()->has_refcounted_component())
return this;
- return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
- this, false);
+ return Expression::make_refcount_decrement_lvalue(refcounts, this);
}
-// Return a tree for an allocation expression.
+// Get a tree for a field reference.
tree
-Allocation_expression::do_get_tree(Translate_context* context)
+Field_reference_expression::do_get_tree(Translate_context* context)
{
- tree type_tree = this->type_->get_tree(context->gogo());
- tree size_tree = TYPE_SIZE_UNIT(type_tree);
- tree space = context->gogo()->allocate_memory(size_tree, this->location());
- return fold_convert(build_pointer_type (type_tree), space);
+ tree struct_tree = this->expr_->get_tree(context);
+ if (struct_tree == error_mark_node
+ || TREE_TYPE(struct_tree) == error_mark_node)
+ return error_mark_node;
+
+ if (POINTER_TYPE_P(TREE_TYPE(struct_tree)))
+ {
+ // 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.
+ HOST_WIDE_INT s = int_size_in_bytes(TREE_TYPE(TREE_TYPE(struct_tree)));
+ if (s == -1 || s >= 4096)
+ {
+ if (!DECL_P(struct_tree))
+ struct_tree = save_expr(struct_tree);
+ tree compare = build2(EQ_EXPR, boolean_type_node, struct_tree,
+ fold_convert(TREE_TYPE(struct_tree),
+ null_pointer_node));
+ // FIXME: This should be a different error message.
+ static tree bad_index_fndecl;
+ tree crash = Gogo::call_builtin(&bad_index_fndecl,
+ this->location(),
+ "__go_bad_index",
+ 0,
+ void_type_node);
+ TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+ struct_tree = build2(COMPOUND_EXPR, TREE_TYPE(struct_tree),
+ build3(COND_EXPR, void_type_node, compare,
+ crash, NULL_TREE),
+ struct_tree);
+ }
+ struct_tree = build_fold_indirect_ref(struct_tree);
+ }
+
+ gcc_assert(TREE_CODE(TREE_TYPE(struct_tree)) == RECORD_TYPE);
+ tree field = TYPE_FIELDS(TREE_TYPE(struct_tree));
+ gcc_assert(field != NULL_TREE);
+ for (unsigned int i = this->field_index_; i > 0; --i)
+ {
+ field = TREE_CHAIN(field);
+ gcc_assert(field != NULL_TREE);
+ }
+ return build3(COMPONENT_REF, TREE_TYPE(field), struct_tree, field,
+ NULL_TREE);
}
-// Make an allocation expression.
+// Make a reference to a qualified identifier in an expression.
-Expression*
-Expression::make_allocation(Type* type, source_location location)
+Field_reference_expression*
+Expression::make_field_reference(Expression* expr, unsigned int field_index,
+ source_location location)
{
- return new Allocation_expression(type, location);
+ return new Field_reference_expression(expr, field_index, location);
}
-// Implement the builtin function make.
+// Class Interface_field_reference_expression.
-class Make_expression : public Expression
+// Return a tree for the pointer to the function to call.
+
+tree
+Interface_field_reference_expression::get_function_tree(Translate_context*,
+ tree expr)
{
- public:
- Make_expression(Type* type, Expression_list* args, source_location location)
- : Expression(EXPRESSION_MAKE, location),
- type_(type), args_(args), is_being_copied_(false)
- { }
+ if (this->expr_->type()->points_to() != NULL)
+ expr = build_fold_indirect_ref(expr);
- protected:
- int
- do_traverse(Traverse* traverse);
+ tree expr_type = TREE_TYPE(expr);
+ gcc_assert(POINTER_TYPE_P(expr_type));
+ expr_type = TREE_TYPE(expr_type);
+ expr = build_fold_indirect_ref(expr);
+ gcc_assert(TREE_CODE(expr_type) == RECORD_TYPE);
- Type*
- do_type()
- { return this->type_; }
+ tree field = TREE_CHAIN(TYPE_FIELDS(expr_type));
+ gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods") == 0);
- void
- do_determine_type(const Type_context*);
+ tree table = build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
+ gcc_assert(POINTER_TYPE_P(TREE_TYPE(table)));
- void
- do_check_types(Gogo*);
+ table = build_fold_indirect_ref(table);
+ gcc_assert(TREE_CODE(TREE_TYPE(table)) == RECORD_TYPE);
- Expression*
- do_copy()
- {
- return new Make_expression(this->type_, this->args_->copy(),
- this->location());
- }
+ std::string name = Gogo::unpack_hidden_name(this->name_);
+ for (field = TYPE_FIELDS(TREE_TYPE(table));
+ field != NULL_TREE;
+ field = TREE_CHAIN(field))
+ {
+ if (name == IDENTIFIER_POINTER(DECL_NAME(field)))
+ break;
+ }
+ gcc_assert(field != NULL_TREE);
- Expression*
- do_being_copied(Refcounts*, bool);
+ return build3(COMPONENT_REF, TREE_TYPE(field), table, field, NULL_TREE);
+}
- Expression*
- do_note_decrements(Refcounts*);
+// Return a tree for the first argument to pass to the interface
+// function.
- tree
- do_get_tree(Translate_context*);
+tree
+Interface_field_reference_expression::get_underlying_object_tree(
+ Translate_context*,
+ tree expr)
+{
+ if (this->expr_->type()->points_to() != NULL)
+ expr = build_fold_indirect_ref(expr);
- private:
- // The type we are making.
- Type* type_;
- // The arguments to pass to the make routine.
- Expression_list* args_;
- // Whether the expression is being copied.
- bool is_being_copied_;
-};
+ tree expr_type = TREE_TYPE(expr);
+ gcc_assert(POINTER_TYPE_P(expr_type));
+ expr_type = TREE_TYPE(expr_type);
+ expr = build_fold_indirect_ref(expr);
+ gcc_assert(TREE_CODE(expr_type) == RECORD_TYPE);
+
+ tree field = TREE_CHAIN(TREE_CHAIN(TYPE_FIELDS(expr_type)));
+ gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0);
+
+ return build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
+}
// Traversal.
int
-Make_expression::do_traverse(Traverse* traverse)
+Interface_field_reference_expression::do_traverse(Traverse* traverse)
{
- if (this->args_ != NULL
- && this->args_->traverse(traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- return TRAVERSE_CONTINUE;
+ return Expression::traverse(&this->expr_, traverse);
}
-// Set types of arguments.
+// Return the type of an interface field reference.
-void
-Make_expression::do_determine_type(const Type_context*)
+Type*
+Interface_field_reference_expression::do_type()
{
- if (this->args_ != NULL)
- {
- for (Expression_list::const_iterator pe = this->args_->begin();
- pe != this->args_->end();
- ++pe)
- (*pe)->determine_type_no_context();
- }
-}
+ Type* expr_type = this->expr_->type();
-// Check types for a make expression.
+ Type* points_to = expr_type->points_to();
+ if (points_to != NULL)
+ expr_type = points_to;
-void
-Make_expression::do_check_types(Gogo*)
-{
- if (this->type_->channel_type() == NULL
- && this->type_->map_type() == NULL
- && (this->type_->array_type() == NULL
- || this->type_->array_type()->length() != NULL))
- this->report_error(_("invalid type for make function"));
- else if (!this->type_->check_make_expression(this->args_, this->location()))
- this->set_is_error();
+ 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 Type::make_pointer_type(method->type());
}
-// An newly created object arrives with a reference count, so nothing
-// special is needed to copy it.
+// Determine types.
-Expression*
-Make_expression::do_being_copied(Refcounts*, bool)
+void
+Interface_field_reference_expression::do_determine_type(const Type_context*)
{
- this->is_being_copied_ = true;
- return this;
+ this->expr_->determine_type_no_context();
}
-// A newly created object arrives with a reference count, so if we
-// don't copy the value we must free it.
+// Check the types for an interface field reference.
-Expression*
-Make_expression::do_note_decrements(Refcounts* refcounts)
+void
+Interface_field_reference_expression::do_check_types(Gogo*)
{
- if (this->is_being_copied_)
- return this;
- return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
- this, false);
+ 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)
+ 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",
+ this->name_.c_str());
+ this->set_is_error();
+ }
+ }
}
-// Return a tree for a make expression.
+// Get a tree for a reference to a field in an interface. There is no
+// standard tree type representation for this: it's a function
+// attached to its first argument, like a Bound_method_expression.
+// The only places it may currently be used are in a Call_expression
+// or a Go_statement, which will take it apart directly. So this has
+// nothing to do at present.
tree
-Make_expression::do_get_tree(Translate_context* context)
+Interface_field_reference_expression::do_get_tree(Translate_context*)
{
- return this->type_->make_expression_tree(context, this->args_,
- this->location());
+ gcc_unreachable();
}
-// Make a make expression.
+// Make a reference to a field in an interface.
Expression*
-Expression::make_make(Type* type, Expression_list* args,
- source_location location)
+Expression::make_interface_field_reference(Expression* expr,
+ const std::string& field,
+ source_location location)
{
- return new Make_expression(type, args, location);
+ return new Interface_field_reference_expression(expr, field, location);
}
-// A type conversion expression.
+// 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 Type_conversion_expression : public Expression
+class Selector_expression : public Parser_expression
{
public:
- Type_conversion_expression(Type* type, Expression* expr,
- source_location location)
- : Expression(EXPRESSION_CONVERSION, location),
- type_(type), expr_(expr), is_being_copied_(false)
+ Selector_expression(Expression* left, const std::string& name,
+ source_location location)
+ : Parser_expression(EXPRESSION_SELECTOR, location),
+ left_(left), name_(name)
{ }
- // 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_; }
-
- static Expression*
- do_import(Import*);
-
protected:
int
- do_traverse(Traverse* traverse);
+ do_traverse(Traverse* traverse)
+ { return Expression::traverse(&this->left_, traverse); }
Expression*
do_lower(Gogo*, int);
- bool
- do_is_constant() const
- { return this->expr_->is_constant(); }
-
- bool
- do_integer_constant_value(bool, mpz_t, Type**) const;
-
- bool
- do_float_constant_value(mpfr_t, Type**) 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_, true);
- 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());
+ return new Selector_expression(this->left_->copy(), this->name_,
+ this->location());
}
- Expression*
- do_being_copied(Refcounts*, bool);
-
- Expression*
- do_note_decrements(Refcounts*);
-
- tree
- do_get_tree(Translate_context* context);
-
- void
- do_export(Export*) const;
-
private:
- // The type to convert to.
- Type* type_;
- // The expression to convert.
- Expression* expr_;
- // Whether this expression is being copied.
- bool is_being_copied_;
+ // The expression on the left hand side.
+ Expression* left_;
+ // The name on the right hand side.
+ std::string name_;
};
-// 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.
+// Lower a selector expression once we know the real type of the left
+// hand side.
Expression*
-Type_conversion_expression::do_lower(Gogo*, int)
+Selector_expression::do_lower(Gogo*, int)
{
- Type* type = this->type_;
- Expression* val = this->expr_;
source_location location = this->location();
+ Expression* left = this->left_;
+ const std::string& name(this->name_);
- if (type->integer_type() != NULL)
+ Type* type = left->type();
+ Type* ptype = type->deref();
+ Struct_type* struct_type = ptype->struct_type();
+ Interface_type* interface_type = ptype->interface_type();
+ Named_type* named_type = type->named_type();
+ if (named_type == NULL && type != ptype)
+ named_type = ptype->named_type();
+
+ if (type->is_error_type())
+ return Expression::make_error(location);
+ else if (struct_type != NULL && named_type == NULL)
{
- mpz_t ival;
- mpz_init(ival);
- Type* dummy;
- if (val->integer_constant_value(false, ival, &dummy))
- {
- if (!Integer_expression::check_constant(ival, type, location))
- mpz_set_ui(ival, 0);
- return Expression::make_integer(&ival, type, location);
- }
+ Expression* ret = struct_type->field_reference(left, name, location);
+ if (ret != NULL)
+ return ret;
+ }
+ else if (interface_type != NULL && interface_type->find_method(name) != NULL)
+ return Expression::make_interface_field_reference(left, name, location);
- mpfr_t fval;
- mpfr_init(fval);
- if (val->float_constant_value(fval, &dummy))
+ if (named_type != NULL)
+ {
+ bool found_pointer_method;
+ Expression* ret = named_type->bind_field_or_method(left, name, location,
+ &found_pointer_method);
+ if (ret != NULL)
+ return ret;
+ if (found_pointer_method)
{
- if (!mpfr_integer_p(fval))
- {
- error_at(location,
- "floating point constant truncated to integer");
- return Expression::make_error(location);
- }
- mpfr_get_z(ival, fval, GMP_RNDN);
- if (!Integer_expression::check_constant(ival, type, location))
- mpz_set_ui(ival, 0);
- return Expression::make_integer(&ival, type, location);
+ error_at(location, "method requires a pointer");
+ return Expression::make_error(location);
}
- mpfr_clear(fval);
- mpz_clear(ival);
}
- if (type->float_type() != NULL)
- {
- mpfr_t fval;
- mpfr_init(fval);
- Type* dummy;
- if (val->float_constant_value(fval, &dummy))
- {
- if (!Float_expression::check_constant(fval, type, location))
- mpfr_set_ui(fval, 0, GMP_RNDN);
- Float_expression::constrain_float(fval, type);
- return Expression::make_float(&fval, type, location);
- }
- mpfr_clear(fval);
- }
+ if (struct_type == NULL && interface_type == NULL && named_type == NULL)
+ error_at(location,
+ "request for %qs in something which has no fields or methods",
+ Gogo::unpack_hidden_name(name).c_str());
+ else
+ error_at(location, "reference to undefined field or method %qs",
+ Gogo::unpack_hidden_name(name).c_str());
+ return Expression::make_error(location);
+}
+
+// Make a selector expression.
+
+Expression*
+Expression::make_selector(Expression* left, const std::string& name,
+ source_location location)
+{
+ return new Selector_expression(left, name, location);
+}
+
+// Implement the builtin function new.
+
+class Allocation_expression : public Expression
+{
+ public:
+ Allocation_expression(Type* type, source_location location)
+ : Expression(EXPRESSION_ALLOCATION, location),
+ type_(type), is_being_copied_(false)
+ { }
+
+ 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*)
+ { }
+
+ void
+ do_check_types(Gogo*);
- return this;
-}
+ Expression*
+ do_copy()
+ { return new Allocation_expression(this->type_, this->location()); }
-// Return the constant integer value if there is one.
+ Expression*
+ do_being_copied(Refcounts*, bool);
-bool
-Type_conversion_expression::do_integer_constant_value(bool iota_is_constant,
- mpz_t val,
- Type** ptype) const
-{
- if (this->type_->integer_type() == NULL)
- return false;
+ Expression*
+ do_note_decrements(Refcounts*);
- mpz_t ival;
- mpz_init(ival);
- Type* dummy;
- if (this->expr_->integer_constant_value(iota_is_constant, ival, &dummy))
- {
- if (!Integer_expression::check_constant(ival, this->type_,
- this->location()))
- {
- mpz_clear(ival);
- return false;
- }
- mpz_set(val, ival);
- mpz_clear(ival);
- *ptype = this->type_;
- return true;
- }
- mpz_clear(ival);
+ tree
+ do_get_tree(Translate_context*);
- mpfr_t fval;
- mpfr_init(fval);
- if (this->expr_->float_constant_value(fval, &dummy))
- {
- mpfr_get_z(val, fval, GMP_RNDN);
- mpfr_clear(fval);
- if (!Integer_expression::check_constant(val, this->type_,
- this->location()))
- return false;
- *ptype = this->type_;
- return true;
- }
- mpfr_clear(fval);
+ private:
+ // The type we are allocating.
+ Type* type_;
+ // Whether this value is being copied.
+ bool is_being_copied_;
+};
- return false;
+// Check the type of an allocation expression.
+
+void
+Allocation_expression::do_check_types(Gogo*)
+{
+ if (this->type_->function_type() != NULL)
+ this->report_error(_("invalid new of function type"));
}
-// Return the constant floating point value if there is one.
+// An allocation expression arrives with a reference count, so nothing
+// special is needed to copy it.
-bool
-Type_conversion_expression::do_float_constant_value(mpfr_t val,
- Type** ptype) const
+Expression*
+Allocation_expression::do_being_copied(Refcounts*, bool)
{
- if (this->type_->float_type() == NULL)
- return false;
-
- mpfr_t fval;
- mpfr_init(fval);
- Type* dummy;
- if (this->expr_->float_constant_value(fval, &dummy))
- {
- if (!Float_expression::check_constant(fval, this->type_,
- this->location()))
- {
- mpfr_clear(fval);
- return false;
- }
- mpfr_set(val, fval, GMP_RNDN);
- mpfr_clear(fval);
- Float_expression::constrain_float(val, this->type_);
- *ptype = this->type_;
- return true;
- }
- mpfr_clear(fval);
+ this->is_being_copied_ = true;
+ return this;
+}
- mpz_t ival;
- mpz_init(ival);
- if (this->expr_->integer_constant_value(false, ival, &dummy))
- {
- mpfr_set_z(val, ival, GMP_RNDN);
- mpz_clear(ival);
- if (!Float_expression::check_constant(val, this->type_,
- this->location()))
- return false;
- Float_expression::constrain_float(val, this->type_);
- *ptype = this->type_;
- return true;
- }
- mpz_clear(ival);
+// An allocation expression arrives with a reference count, so if we
+// don't copy the value we must free it.
- return false;
+Expression*
+Allocation_expression::do_note_decrements(Refcounts* refcounts)
+{
+ if (this->is_being_copied_)
+ return this;
+ return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
+ this, false);
}
-// Return the constant string value if there is one.
+// Return a tree for an allocation expression.
-bool
-Type_conversion_expression::do_string_constant_value(std::string* val) const
+tree
+Allocation_expression::do_get_tree(Translate_context* context)
{
- if (this->type_->is_string_type()
- && this->expr_->type()->integer_type() != NULL)
- {
- mpz_t ival;
- mpz_init(ival);
- Type* dummy;
- if (this->expr_->integer_constant_value(false, ival, &dummy))
- {
- unsigned long ulval = mpz_get_ui(ival);
- if (mpz_cmp_ui(ival, ulval) == 0)
- {
- Lex::append_char(ulval, true, val, this->location());
- mpz_clear(ival);
- return true;
- }
- }
- mpz_clear(ival);
- }
+ tree type_tree = this->type_->get_tree(context->gogo());
+ tree size_tree = TYPE_SIZE_UNIT(type_tree);
+ tree space = context->gogo()->allocate_memory(size_tree, this->location());
+ return fold_convert(build_pointer_type (type_tree), space);
+}
- // FIXME: Could handle conversion from const []int here.
+// Make an allocation expression.
- return false;
+Expression*
+Expression::make_allocation(Type* type, source_location location)
+{
+ return new Allocation_expression(type, location);
}
-// Check that types are convertible.
+// Implement the builtin function make.
-void
-Type_conversion_expression::do_check_types(Gogo*)
+class Make_expression : public Expression
{
- Type* type = this->type_;
- Type* expr_type = this->expr_->type();
- std::string reason;
+ public:
+ Make_expression(Type* type, Expression_list* args, source_location location)
+ : Expression(EXPRESSION_MAKE, location),
+ type_(type), args_(args), is_being_copied_(false)
+ { }
- if (Type::are_compatible_for_conversion(type, expr_type, &reason))
- return;
+ protected:
+ int
+ do_traverse(Traverse* traverse);
- bool ok = false;
- if ((type->integer_type() != NULL || type->float_type() != NULL)
- && (expr_type->integer_type() != NULL
- || expr_type->float_type() != NULL))
- ok = true;
- else if (type->is_string_type())
- {
- if (expr_type->integer_type() != NULL)
- ok = true;
- else
- {
- Type* t = expr_type->deref();
- if (t->array_type() != NULL)
- {
- Type* e = t->array_type()->element_type()->forwarded();
- if (e->integer_type() != NULL
- && (e == Type::lookup_integer_type("uint8")
- || e == Type::lookup_integer_type("int")))
- ok = true;
- }
- }
- }
- else if ((type->is_unsafe_pointer_type()
- && (expr_type->points_to() != NULL
- || (expr_type->integer_type() != NULL
- && expr_type->integer_type()->bits() == POINTER_SIZE
- && expr_type->integer_type()->is_unsigned())))
- || (expr_type->is_unsafe_pointer_type()
- && (type->points_to() != NULL
- || (type->integer_type() != NULL
- && type->integer_type()->bits() == POINTER_SIZE
- && type->integer_type()->is_unsigned()))))
- {
- // Conversions between unsafe pointers and other pointers or
- // integers of appropriate size are permitted.
- ok = true;
- }
+ Type*
+ do_type()
+ { return this->type_; }
+
+ void
+ do_determine_type(const Type_context*);
+
+ void
+ do_check_types(Gogo*);
+
+ Expression*
+ do_copy()
+ {
+ return new Make_expression(this->type_, this->args_->copy(),
+ this->location());
+ }
+
+ Expression*
+ do_being_copied(Refcounts*, bool);
+
+ Expression*
+ do_note_decrements(Refcounts*);
+
+ tree
+ do_get_tree(Translate_context*);
+
+ private:
+ // The type we are making.
+ Type* type_;
+ // The arguments to pass to the make routine.
+ Expression_list* args_;
+ // Whether the expression is being copied.
+ bool is_being_copied_;
+};
- // FIXME: Conversions between interfaces and pointers are supported.
+// Traversal.
- if (!ok)
+int
+Make_expression::do_traverse(Traverse* traverse)
+{
+ if (this->args_ != NULL
+ && this->args_->traverse(traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ return TRAVERSE_CONTINUE;
+}
+
+// Set types of arguments.
+
+void
+Make_expression::do_determine_type(const Type_context*)
+{
+ if (this->args_ != NULL)
{
- if (reason.empty())
- this->report_error(_("invalid type conversion"));
- else
- {
- error_at(this->location(), "invalid type conversion (%s)",
- reason.c_str());
- this->set_is_error();
- }
+ for (Expression_list::const_iterator pe = this->args_->begin();
+ pe != this->args_->end();
+ ++pe)
+ (*pe)->determine_type_no_context();
}
}
-// The type conversion is being copied elsewhere. If we do not call a
-// function which creates a new reference, then we need to pass this
-// on to the subsidiary expression.
+// Check types for a make expression.
+
+void
+Make_expression::do_check_types(Gogo*)
+{
+ if (this->type_->channel_type() == NULL
+ && this->type_->map_type() == NULL
+ && (this->type_->array_type() == NULL
+ || this->type_->array_type()->length() != NULL))
+ this->report_error(_("invalid type for make function"));
+ else if (!this->type_->check_make_expression(this->args_, this->location()))
+ this->set_is_error();
+}
+
+// An newly created object arrives with a reference count, so nothing
+// special is needed to copy it.
Expression*
-Type_conversion_expression::do_being_copied(Refcounts* refcounts,
- bool for_local)
+Make_expression::do_being_copied(Refcounts*, bool)
{
this->is_being_copied_ = true;
- Type* type = this->type_;
- Type* expr_type = this->expr_->type();
- bool copy_base;
- if (type == expr_type)
- copy_base = true;
- else if (type->interface_type() != NULL
- || expr_type->interface_type() != NULL)
- copy_base = false;
- else if (type->is_string_type()
- && (expr_type->integer_type() != NULL
- || expr_type->deref()->array_type() != NULL))
- copy_base = false;
- else
- copy_base = true;
- if (copy_base && expr_type->has_refcounted_component())
- this->expr_ = this->expr_->being_copied(refcounts, for_local);
return this;
}
-// A type conversion may introduce a reference count.
+// A newly created object arrives with a reference count, so if we
+// don't copy the value we must free it.
Expression*
-Type_conversion_expression::do_note_decrements(Refcounts* refcounts)
+Make_expression::do_note_decrements(Refcounts* refcounts)
{
if (this->is_being_copied_)
return this;
- Type* type = this->type_;
- Type* expr_type = this->expr_->type();
- bool need_decrement;
- if (type == expr_type)
- need_decrement = false;
- else if (type->interface_type() != NULL)
- need_decrement = true;
- else if (expr_type->interface_type() != NULL)
- need_decrement = type->has_refcounted_component();
- else if (type->is_string_type()
- && (expr_type->integer_type() != NULL
- || expr_type->deref()->array_type() != NULL))
- need_decrement = true;
- else
- need_decrement = false;
- if (!need_decrement)
- return this;
return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
this, false);
}
-// Get a tree for a type conversion.
+// Return a tree for a make expression.
tree
-Type_conversion_expression::do_get_tree(Translate_context* context)
-{
- Gogo* gogo = context->gogo();
- tree type_tree = this->type_->get_tree(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
- gcc_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
- gcc_unreachable();
- }
- else if (type->is_string_type()
- && expr_type->integer_type() != NULL)
- {
- expr_tree = fold_convert(integer_type_node, expr_tree);
- if (host_integerp(expr_tree, 0))
- {
- HOST_WIDE_INT intval = tree_low_cst(expr_tree, 0);
- std::string s;
- Lex::append_char(intval, true, &s, this->location());
- Expression* se = Expression::make_string(s, this->location());
- return se->get_tree(context);
- }
-
- static tree int_to_string_fndecl;
- ret = Gogo::call_builtin(&int_to_string_fndecl,
- this->location(),
- "__go_int_to_string",
- 1,
- type_tree,
- integer_type_node,
- fold_convert(integer_type_node, expr_tree));
- }
- else if (type->is_string_type()
- && (expr_type->array_type() != NULL
- || (expr_type->points_to() != NULL
- && expr_type->points_to()->array_type() != NULL)))
- {
- Type* t = expr_type;
- if (t->points_to() != NULL)
- {
- t = t->points_to();
- expr_tree = build_fold_indirect_ref(expr_tree);
- }
- if (!DECL_P(expr_tree))
- expr_tree = save_expr(expr_tree);
- Array_type* a = t->array_type();
- Type* e = a->element_type()->forwarded();
- gcc_assert(e->integer_type() != NULL);
- tree valptr = fold_convert(const_ptr_type_node,
- a->value_pointer_tree(gogo, expr_tree));
- tree len = a->length_tree(gogo, expr_tree);
- if (e->integer_type()->is_unsigned()
- && e->integer_type()->bits() == 8)
- {
- static tree byte_array_to_string_fndecl;
- ret = Gogo::call_builtin(&byte_array_to_string_fndecl,
- this->location(),
- "__go_byte_array_to_string",
- 2,
- type_tree,
- const_ptr_type_node,
- valptr,
- size_type_node,
- len);
- }
- else
- {
- gcc_assert(e == Type::lookup_integer_type("int"));
- static tree int_array_to_string_fndecl;
- ret = Gogo::call_builtin(&int_array_to_string_fndecl,
- this->location(),
- "__go_int_array_to_string",
- 2,
- type_tree,
- const_ptr_type_node,
- valptr,
- size_type_node,
- len);
- }
- }
- 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
- 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)
+Make_expression::do_get_tree(Translate_context* context)
{
- 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());
+ return this->type_->make_expression_tree(context, this->args_,
+ this->location());
}
-// Make a type cast expression.
+// Make a make expression.
Expression*
-Expression::make_cast(Type* type, Expression* val, source_location location)
+Expression::make_make(Type* type, Expression_list* args,
+ source_location location)
{
- if (type->is_error_type() || val->is_error_expression())
- return Expression::make_error(location);
- return new Type_conversion_expression(type, val, location);
+ return new Make_expression(type, args, location);
}
// Construct a struct.
@@ -8176,7 +8216,7 @@ class Struct_construction_expression : p
}
bool
- do_address_taken(source_location)
+ do_address_taken(source_location, bool)
{
gcc_assert(this->is_constant_struct());
return true;
@@ -8479,7 +8519,7 @@ protected:
do_check_types(Gogo*);
bool
- do_address_taken(source_location)
+ do_address_taken(source_location, bool)
{
gcc_assert(this->is_constant_array());
return true;
@@ -8922,7 +8962,7 @@ class Map_construction_expression : publ
// Should be turned into Heap_composite_expression.
bool
- do_address_taken()
+ do_address_taken(source_location, bool)
{ gcc_unreachable(); }
Expression*