return true;
}
+// Report whether this is a type expression.
+
+bool
+Expression::is_type_expression() const
+{
+ if (this->classification_ == EXPRESSION_TYPE)
+ return true;
+ if (this->unknown_expression() != NULL)
+ {
+ Named_object* no = this->unknown_expression()->named_object();
+ if (no->is_unknown())
+ {
+ no = no->unknown_value()->real_named_object();
+ if (no == NULL)
+ return false;
+ }
+ return no->is_type();
+ }
+ if (this->unary_expression() != NULL
+ && this->unary_expression()->op() == OPERATOR_MULT
+ && this->unary_expression()->operand()->is_type_expression())
+ return true;
+ return false;
+}
+
// Set types of variables and constants. This is implemented by the
// child class.
(Type::COMPARE_ERRORS
| Type::COMPARE_TAGS),
NULL);
+ Expression* ret;
if (!are_identical && lhs_type->interface_type() != NULL)
{
// Type to interface conversions have been made explicit early.
go_assert(rhs_type->interface_type() != NULL);
- return Expression::convert_interface_to_interface(gogo, lhs_type, rhs,
- false, location);
+ ret = Expression::convert_interface_to_interface(gogo, lhs_type, rhs,
+ false, location);
}
else if (!are_identical && rhs_type->interface_type() != NULL)
- return Expression::convert_interface_to_type(gogo, lhs_type, rhs, location);
+ ret = Expression::convert_interface_to_type(gogo, lhs_type, rhs, location);
else if (lhs_type->is_slice_type() && rhs_type->is_nil_type())
{
// Assigning nil to a slice.
Expression* nil = Expression::make_nil(location);
Expression* zero = Expression::make_integer_ul(0, NULL, location);
- return Expression::make_slice_value(lhs_type, nil, zero, zero, location);
+ ret = Expression::make_slice_value(lhs_type, nil, zero, zero, location);
}
else if (rhs_type->is_nil_type())
- return Expression::make_nil(location);
+ ret = Expression::make_nil(location);
else if (are_identical)
{
if (lhs_type->forwarded() != rhs_type->forwarded())
return rhs;
}
else if (lhs_type->points_to() != NULL)
- return Expression::make_unsafe_cast(lhs_type, rhs, location);
+ ret = Expression::make_unsafe_cast(lhs_type, rhs, location);
else if (lhs_type->is_numeric_type())
- return Expression::make_cast(lhs_type, rhs, location);
+ ret = Expression::make_cast(lhs_type, rhs, location);
else if ((lhs_type->struct_type() != NULL
&& rhs_type->struct_type() != NULL)
|| (lhs_type->array_type() != NULL
{
// This conversion must be permitted by Go, or we wouldn't have
// gotten here.
- return Expression::make_unsafe_cast(lhs_type, rhs, location);
+ ret = Expression::make_unsafe_cast(lhs_type, rhs, location);
}
else
return rhs;
+
+ Type_context context(lhs_type, false);
+ ret->determine_type(gogo, &context);
+ return ret;
}
// Return an expression for a conversion from a non-interface type to an
// The second field is simply the object pointer.
Expression* obj =
Expression::make_interface_info(rhs, INTERFACE_INFO_OBJECT, location);
- return Expression::make_interface_value(lhs_type, first_field, obj, location);
+ Expression* ret = Expression::make_interface_value(lhs_type, first_field,
+ obj, location);
+ Type_context context(lhs_type, false);
+ ret->determine_type(gogo, &context);
+ return ret;
}
// Return an expression for the conversion of an interface type to a
Expression* crash = Runtime::make_call(gogo, c, loc, 2,
val->copy(), bound->copy());
Expression* cond = Expression::make_conditional(check, ignore, crash, loc);
- inserter->insert(Statement::make_statement(cond, true));
+ Statement* s = Statement::make_statement(cond, true);
+ s->determine_types(gogo);
+ inserter->insert(s);
}
void
{ return false; }
bool
- do_numeric_constant_value(Numeric_constant* nc)
- {
- nc->set_unsigned_long(NULL, 0);
- return true;
- }
+ do_numeric_constant_value(Numeric_constant*)
+ { return false; }
bool
do_discarding_value()
// An expression which is really a type. This is used during parsing.
// It is an error if these survive after lowering.
-class
-Type_expression : public Expression
+class Type_expression : public Expression
{
public:
Type_expression(Type* type, Location location)
{ return this; }
Bexpression*
- do_get_backend(Translate_context*)
- { go_unreachable(); }
+ do_get_backend(Translate_context*);
void do_dump_expression(Ast_dump_context*) const;
go_assert(saw_errors());
this->set_is_error();
}
- else
- this->report_error(_("invalid use of type"));
+}
+
+Bexpression*
+Type_expression::do_get_backend(Translate_context* context)
+{
+ if (!this->is_error_expression())
+ this->report_error("invalid use of type");
+ return context->backend()->error_expression();
}
void
return new Type_expression(type, location);
}
-// Class Parser_expression.
-
-Type*
-Parser_expression::do_type()
-{
- // We should never really ask for the type of a Parser_expression.
- // However, it can happen, at least when we have an invalid const
- // whose initializer refers to the const itself. In that case we
- // may ask for the type when lowering the const itself.
- go_assert(saw_errors());
- return Type::make_error_type();
-}
-
// Class Var_expression.
// Lower a variable expression. Here we just make sure that the
{
if (var->is_sink())
return Expression::make_sink(location);
+ if (var->is_redefinition())
+ return Expression::make_error(location);
// FIXME: Creating a new object for each reference to a variable is
// wasteful.
return this->named_object_->name();
}
-// Lower a reference to an unknown name.
+// Set the iota value if this could be a reference to iota.
-Expression*
-Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
+void
+Unknown_expression::set_iota_value(int iota_value)
{
- Location location = this->location();
+ this->iota_value_ = iota_value;
+ this->is_iota_ = true;
+}
+
+// Traversal.
+
+int
+Unknown_expression::do_traverse(Traverse* traverse)
+{
+ if (this->lowered_ != NULL)
+ {
+ if (Expression::traverse(&this->lowered_, traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
+ return TRAVERSE_CONTINUE;
+}
+
+// Determine the type of a reference to an unknown name. At this
+// point we have to figure out what the name refers to.
+
+void
+Unknown_expression::do_determine_type(Gogo* gogo, const Type_context* context)
+{
+ if (this->is_error_expression())
+ return;
+
+ if (this->lowered_ != NULL)
+ {
+ this->lowered_->determine_type(gogo, context);
+ return;
+ }
+
+ Location loc = this->location();
+
Named_object* no = this->named_object_;
- Named_object* real;
- if (!no->is_unknown())
- real = no;
- else
+ if (no->is_unknown())
{
- real = no->unknown_value()->real_named_object();
+ Named_object* real = no->unknown_value()->real_named_object();
if (real == NULL)
{
if (!this->no_error_message_)
- go_error_at(location, "reference to undefined name %qs",
- this->named_object_->message_name().c_str());
- return Expression::make_error(location);
+ go_error_at(loc, "reference to undefined name %qs",
+ no->message_name().c_str());
+ this->set_is_error();
+ return;
}
+ no = real;
+ this->named_object_ = real;
}
- switch (real->classification())
+
+ switch (no->classification())
{
- case Named_object::NAMED_OBJECT_CONST:
- return Expression::make_const_reference(real, location);
case Named_object::NAMED_OBJECT_TYPE:
- return Expression::make_type(real->type_value(), location);
- case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
- if (!this->no_error_message_)
- go_error_at(location, "reference to undefined type %qs",
- real->message_name().c_str());
- return Expression::make_error(location);
- case Named_object::NAMED_OBJECT_VAR:
- real->var_value()->set_is_used();
- return Expression::make_var_reference(real, location);
+ this->lowered_ = Expression::make_type(no->type_value(), loc);
+ break;
case Named_object::NAMED_OBJECT_FUNC:
case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
- return Expression::make_func_reference(real, NULL, location);
+ this->lowered_ = Expression::make_func_reference(no, NULL, loc);
+ break;
+ case Named_object::NAMED_OBJECT_CONST:
+ this->lowered_ = Expression::make_const_reference(no, loc);
+ this->lowered_->determine_type(gogo, context);
+ if (this->is_iota_)
+ this->lowered_->const_expression()->set_iota_value(this->iota_value_);
+ break;
+ case Named_object::NAMED_OBJECT_VAR:
+ this->lowered_ = Expression::make_var_reference(no, loc);
+ no->var_value()->set_is_used();
+ this->lowered_->determine_type(gogo, context);
+ break;
+ case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
+ if (!this->no_error_message_)
+ go_error_at(this->location(), "reference to undefined type %qs",
+ no->message_name().c_str());
+ this->set_is_error();
+ break;
case Named_object::NAMED_OBJECT_PACKAGE:
if (!this->no_error_message_)
- go_error_at(location, "unexpected reference to package");
- return Expression::make_error(location);
+ this->report_error(_("unexpected reference to package"));
+ this->set_is_error();
+ break;
default:
go_unreachable();
}
}
+Type*
+Unknown_expression::do_type()
+{
+ if (this->is_error_expression())
+ return Type::make_error_type();
+ go_assert(this->lowered_ != NULL);
+ return this->lowered_->type();
+}
+
+bool
+Unknown_expression::do_is_constant() const
+{
+ if (this->is_error_expression())
+ return true;
+ if (this->lowered_ != NULL)
+ return this->lowered_->is_constant();
+
+ // This can be called before do_determine_types by
+ // Binary_expression::do_determine_type, which needs to know which
+ // values are constant before it works out the appropriate
+ // Type_context to pass down.
+ Named_object* no = this->named_object_;
+ if (no->is_unknown())
+ {
+ no = no->unknown_value()->real_named_object();
+ if (no == NULL)
+ return true;
+ }
+ return no->is_const();
+}
+
+bool
+Unknown_expression::do_is_untyped(Type** ptype) const
+{
+ if (this->is_error_expression())
+ return false;
+ if (this->lowered_ != NULL)
+ return this->lowered_->is_untyped(ptype);
+
+ Named_object* no = this->named_object_;
+ if (no->is_unknown())
+ {
+ no = no->unknown_value()->real_named_object();
+ if (no == NULL)
+ return false;
+ }
+
+ if (!no->is_const())
+ return false;
+ Type* t = no->const_value()->type();
+ if (t != NULL)
+ return Expression::is_untyped_type(t, ptype);
+ return no->const_value()->expr()->is_untyped(ptype);
+}
+
+bool
+Unknown_expression::do_numeric_constant_value(Numeric_constant* nc)
+{
+ if (this->is_error_expression())
+ return false;
+ if (this->lowered_ != NULL)
+ return this->lowered_->numeric_constant_value(nc);
+
+ // This can be called before the determine_types pass.
+ Named_object* no = this->named_object_;
+ if (no->is_unknown())
+ {
+ no = no->unknown_value()->real_named_object();
+ if (no == NULL)
+ return false;
+ }
+ if (!no->is_const())
+ return false;
+ return no->const_value()->expr()->numeric_constant_value(nc);
+}
+
+bool
+Unknown_expression::do_string_constant_value(std::string* val)
+{
+ if (this->is_error_expression())
+ return false;
+ go_assert(this->lowered_ != NULL);
+ return this->lowered_->string_constant_value(val);
+}
+
+bool
+Unknown_expression::do_boolean_constant_value(bool* val)
+{
+ if (this->is_error_expression())
+ return false;
+ go_assert(this->lowered_ != NULL);
+ return this->lowered_->boolean_constant_value(val);
+}
+
+bool
+Unknown_expression::do_is_addressable() const
+{
+ if (this->is_error_expression())
+ return true;
+ go_assert(this->lowered_ != NULL);
+ return this->lowered_->is_addressable();
+}
+
+// Lower a reference to an unknown name.
+
+Expression*
+Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
+{
+ if (this->is_error_expression())
+ return Expression::make_error(this->location());
+ go_assert(this->lowered_ != NULL);
+ return this->lowered_;
+}
+
// Dump the ast representation for an unknown expression to a dump context.
void
Unknown_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
- ast_dump_context->ostream() << "_Unknown_(" << this->named_object_->name()
- << ")";
+ if (this->lowered_ != NULL)
+ this->lowered_->dump_expression(ast_dump_context);
+ else
+ ast_dump_context->ostream() << "_Unknown_(" << this->named_object_->name()
+ << ")";
}
// Make a reference to an unknown name.
void
do_determine_type(Gogo*, const Type_context*)
- { go_unreachable(); }
+ { }
Expression*
do_copy()
else
{
if (!saw_errors())
- go_error_at(this->location(),
- "unknown type for large integer constant");
+ go_error_at(this->location(), "integer constant overflow");
return context->gogo()->backend()->error_expression();
}
}
// predeclared constant iota into an integer value.
Expression*
-Const_expression::do_lower(Gogo* gogo, Named_object*,
- Statement_inserter*, int iota_value)
+Const_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*, int)
{
- if (this->constant_->const_value()->expr()->classification()
- == EXPRESSION_IOTA)
- {
- if (iota_value == -1)
- {
- go_error_at(this->location(),
- "iota is only defined in const declarations");
- iota_value = 0;
- }
- return Expression::make_integer_ul(iota_value, NULL, this->location());
- }
+ Location loc = this->location();
+
+ if (this->is_error_expression())
+ return Expression::make_error(loc);
+ if (this->constant_->const_value()->expr()->is_error_expression())
+ return Expression::make_error(loc);
+
+ if (this->is_iota_)
+ return Expression::make_integer_ul(this->iota_value_, NULL, loc);
// Make sure that the constant itself has been lowered.
gogo->lower_constant(this->constant_);
if (this->seen_)
return false;
+ Type* ctype;
+ if (this->type_ != NULL)
+ ctype = this->type_;
+ else
+ ctype = this->constant_->const_value()->type();
+
+ if (this->is_iota_)
+ {
+ nc->set_unsigned_long(ctype,
+ static_cast<unsigned long>(this->iota_value_));
+ return true;
+ }
+
Expression* e = this->constant_->const_value()->expr();
this->seen_ = true;
this->seen_ = false;
- Type* ctype;
- if (this->type_ != NULL)
- ctype = this->type_;
- else
- ctype = this->constant_->const_value()->type();
if (r && ctype != NULL)
{
if (!nc->set_type(ctype, false, this->location()))
{
if (this->seen_)
return false;
+ if (this->is_iota_)
+ return false;
Expression* e = this->constant_->const_value()->expr();
{
if (this->seen_)
return false;
+ if (this->is_iota_)
+ return false;
Expression* e = this->constant_->const_value()->expr();
Type*
Const_expression::do_type()
{
- if (this->type_ != NULL)
- return this->type_;
+ if (this->type_ == NULL)
+ {
+ go_assert(saw_errors());
+ return Type::make_error_type();
+ }
- Named_constant* nc = this->constant_->const_value();
+ return this->type_;
+}
- if (this->seen_ || nc->lowering())
+// Set the type of the const reference.
+
+void
+Const_expression::do_determine_type(Gogo* gogo, const Type_context* context)
+{
+ if (this->type_ != NULL)
+ return;
+
+ // The type may depend on the type of other constants. Avoid an
+ // endless loop.
+ if (this->seen_)
{
- if (nc->type() == NULL || !nc->type()->is_error_type())
- {
- Location loc = this->location();
- if (!this->seen_)
- loc = nc->location();
- go_error_at(loc, "constant refers to itself");
- }
+ if (!saw_errors())
+ go_error_at(this->location(), "constant refers to itself");
this->set_is_error();
this->type_ = Type::make_error_type();
- nc->set_type(this->type_);
- return this->type_;
+ return;
}
this->seen_ = true;
- Type* ret = nc->type();
-
- if (ret != NULL)
- {
- this->seen_ = false;
- return ret;
- }
+ Named_constant* nc = this->constant_->const_value();
+ nc->determine_type(gogo);
- // During parsing, a named constant may have a NULL type, but we
- // must not return a NULL type here.
- ret = nc->expr()->type();
+ Type* ctype = nc->type();
this->seen_ = false;
- if (ret->is_error_type())
- nc->set_type(ret);
-
- return ret;
-}
-
-// Set the type of the const reference.
-
-void
-Const_expression::do_determine_type(Gogo*, const Type_context* context)
-{
- Type* ctype = this->constant_->const_value()->type();
- Type* cetype = (ctype != NULL
- ? ctype
- : this->constant_->const_value()->expr()->type());
- if (ctype != NULL && !ctype->is_abstract())
- ;
+ if (ctype == NULL)
+ {
+ go_error_at(nc->expr()->location(), "constant refers to itself");
+ this->set_is_error();
+ this->type_ = Type::make_error_type();
+ }
+ else if (!ctype->is_abstract())
+ this->type_ = ctype;
else if (context->type != NULL
&& context->type->is_numeric_type()
- && cetype->is_numeric_type())
+ && ctype->is_numeric_type())
this->type_ = context->type;
else if (context->type != NULL
&& context->type->is_string_type()
- && cetype->is_string_type())
+ && ctype->is_string_type())
this->type_ = context->type;
else if (context->type != NULL
&& context->type->is_boolean_type()
- && cetype->is_boolean_type())
+ && ctype->is_boolean_type())
this->type_ = context->type;
else if (!context->may_be_abstract)
{
- if (cetype->is_abstract())
- cetype = cetype->make_non_abstract_type();
- this->type_ = cetype;
+ if (ctype->is_abstract())
+ ctype = ctype->make_non_abstract_type();
+ this->type_ = ctype;
}
+ else
+ this->type_ = ctype;
}
// Check for a loop in which the initializer of a constant refers to
void
Const_expression::check_for_init_loop()
{
+ if (this->is_error_expression())
+ return;
if (this->type_ != NULL && this->type_->is_error())
return;
+ if (this->constant_->const_value()->expr()->is_error_expression())
+ {
+ this->set_is_error();
+ return;
+ }
if (this->seen_)
{
}
}
+// Set the iota value if this is a reference to iota.
+
+void
+Const_expression::set_iota_value(int iota_value)
+{
+ Named_constant* nc = this->constant_->const_value();
+ if (nc->expr()->classification() == EXPRESSION_IOTA)
+ {
+ this->is_iota_ = true;
+ this->iota_value_ = iota_value;
+ }
+}
+
// Check types of a const reference.
void
Const_expression::do_check_types(Gogo*)
{
+ if (this->is_error_expression())
+ return;
if (this->type_ != NULL && this->type_->is_error())
return;
+ if (this->constant_->const_value()->expr()->is_error_expression())
+ {
+ this->set_is_error();
+ return;
+ }
- this->check_for_init_loop();
+ Expression* expr = this->constant_->const_value()->expr();
+ if (expr->classification() == EXPRESSION_IOTA && !this->is_iota_)
+ {
+ go_error_at(this->location(),
+ "iota is only defined in const declarations");
+ // Avoid knock-on errors.
+ this->is_iota_ = true;
+ this->iota_value_ = 0;
+ }
- // Check that numeric constant fits in type.
- if (this->type_ != NULL && this->type_->is_numeric_type())
+ if (this->is_iota_ && this->type_->is_numeric_type())
{
Numeric_constant nc;
- if (this->constant_->const_value()->expr()->numeric_constant_value(&nc))
+ nc.set_unsigned_long(Type::make_abstract_integer_type(),
+ static_cast<unsigned long>(this->iota_value_));
+ if (!nc.set_type(this->type_, true, this->location()))
+ this->set_is_error();
+ return;
+ }
+
+ this->check_for_init_loop();
+
+ // Check that numeric constant fits in type.
+ if (this->type_->is_numeric_type())
+ {
+ Numeric_constant nc;
+ if (expr->numeric_constant_value(&nc))
{
if (!nc.set_type(this->type_, true, this->location()))
this->set_is_error();
return context->backend()->error_expression();
}
+ go_assert(!this->is_iota_);
+
// If the type has been set for this expression, but the underlying
// object is an abstract int or float, we try to get the abstract
// value. Otherwise we may lose something in the conversion.
Const_expression* ce = static_cast<Const_expression*>(*pexpr);
if (ce->named_object() == this->no_)
break;
-
- // We need to check a constant initializer explicitly, as
- // loops here will not be caught by the loop checking for
- // variable initializers.
- ce->check_for_init_loop();
-
return TRAVERSE_CONTINUE;
}
if ((*pexpr)->func_expression()->named_object() == this->no_)
break;
return TRAVERSE_CONTINUE;
+ case Expression::EXPRESSION_ERROR:
+ return TRAVERSE_EXIT;
default:
return TRAVERSE_CONTINUE;
}
{ }
protected:
+ Type*
+ do_type()
+ { return Type::make_abstract_integer_type(); }
+
+ void
+ do_determine_type(Gogo*, const Type_context*)
+ { }
+
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{ go_unreachable(); }
return TRAVERSE_CONTINUE;
}
+// Return the type of the expression.
+
+Type*
+Type_conversion_expression::do_type()
+{
+ if (this->is_error_expression() || this->expr_->is_error_expression())
+ return Type::make_error_type();
+ return this->type_;
+}
+
// Convert to a constant at lowering time. Also lower conversions
// from slice to pointer-to-array, as they can panic.
location);
vallen = Expression::make_temporary_reference(vallen_temp, location);
- Expression* panic = Runtime::make_call(gogo, Runtime::PANIC_SLICE_CONVERT,
+ Expression* panic = Runtime::make_call(gogo,
+ Runtime::PANIC_SLICE_CONVERT,
location, 2, arrlen, vallen);
Expression* nil = Expression::make_nil(location);
location);
ptr = Expression::make_unsafe_cast(type, ptr, location);
- return Expression::make_compound(check, ptr, location);
+ Expression* ret = Expression::make_compound(check, ptr, location);
+ ret->determine_type_no_context(gogo);
+ return ret;
}
return this;
if (Type::are_convertible(type, expr_type, &reason))
return;
+ // We can convert all numeric types if the value is a constant.
+ if (type->is_numeric_type()
+ && expr_type->is_numeric_type()
+ && this->expr_->is_constant())
+ return;
+
go_error_at(this->location(), "%s", reason.c_str());
this->set_is_error();
}
{
Type* type = this->type_;
Type* expr_type = this->expr_->type();
+ Type_context tcontext(type, false);
Gogo* gogo = context->gogo();
Btype* btype = type->get_backend(gogo);
Expression* conversion =
Expression::convert_type_to_interface(type, this->expr_,
this->no_escape_, loc);
+ conversion->determine_type(gogo, &tcontext);
return conversion->get_backend(context);
}
else if (type->interface_type() != NULL
Expression* conversion =
Expression::convert_for_assignment(gogo, type, this->expr_,
loc);
+ conversion->determine_type(gogo, &tcontext);
return conversion->get_backend(context);
}
else if (type->is_string_type()
Lex::append_char(x, true, &s, loc);
mpz_clear(intval);
Expression* se = Expression::make_string(s, loc);
+ se->determine_type(gogo, &tcontext);
return se->get_backend(context);
}
buf = Expression::make_nil(loc);
Expression* i2s_expr =
Runtime::make_call(gogo, Runtime::INTSTRING, loc, 2, buf, this->expr_);
- return Expression::make_cast(type, i2s_expr, loc)->get_backend(context);
+ Expression* ret = Expression::make_cast(type, i2s_expr, loc);
+ Type_context tcontext(type, false);
+ ret->determine_type(gogo, &tcontext);
+ return ret->get_backend(context);
}
else if (type->is_string_type() && expr_type->is_slice_type())
{
Expression* str = Expression::make_string_value(ptr, len, loc);
return str->get_backend(context);
}
- return Runtime::make_call(gogo, Runtime::SLICEBYTETOSTRING, loc, 3,
- buf, ptr, len)->get_backend(context);
+ Expression* ret = Runtime::make_call(gogo, Runtime::SLICEBYTETOSTRING,
+ loc, 3, buf, ptr, len);
+ Type_context tcontext(type, false);
+ ret->determine_type(gogo, &tcontext);
+ return ret->get_backend(context);
}
else
{
go_assert(e->integer_type()->is_rune());
- return Runtime::make_call(gogo, Runtime::SLICERUNETOSTRING, loc, 2,
- buf, this->expr_)->get_backend(context);
+ Expression* ret = Runtime::make_call(gogo, Runtime::SLICERUNETOSTRING,
+ loc, 2, buf, this->expr_);
+ Type_context tcontext(type, false);
+ ret->determine_type(gogo, &tcontext);
+ return ret->get_backend(context);
}
}
else if (type->is_slice_type() && expr_type->is_string_type())
buf = Expression::make_nil(loc);
Expression* s2a = Runtime::make_call(gogo, code, loc, 2, buf,
this->expr_);
- return Expression::make_unsafe_cast(type, s2a, loc)->get_backend(context);
+ Expression* ret = Expression::make_unsafe_cast(type, s2a, loc);
+ Type_context tcontext(type, false);
+ ret->determine_type(gogo, &tcontext);
+ return ret->get_backend(context);
}
else if (type->is_numeric_type())
{
{
Expression* conversion =
Expression::convert_for_assignment(gogo, type, this->expr_, loc);
+ conversion->determine_type(gogo, &tcontext);
return conversion->get_backend(context);
}
}
// instead.
Expression*
-Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
+Unary_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*, int)
{
Location loc = this->location();
+
+ if (this->is_error_expression())
+ return Expression::make_error(loc);
+
Operator op = this->op_;
Expression* expr = this->expr_;
+ if (expr->is_error_expression())
+ return Expression::make_error(loc);
+
if (op == OPERATOR_MULT && expr->is_type_expression())
- return Expression::make_type(Type::make_pointer_type(expr->type()), loc);
+ {
+ Expression* ret =
+ Expression::make_type(Type::make_pointer_type(expr->type()), loc);
+ ret->determine_type_no_context(gogo);
+ return ret;
+ }
// *&x simplifies to x. *(*T)(unsafe.Pointer)(&x) does not require
// moving x to the heap. FIXME: Is it worth doing a real escape
}
}
- // Catching an invalid indirection of unsafe.Pointer here avoid
- // having to deal with TYPE_VOID in other places.
- if (op == OPERATOR_MULT && expr->type()->is_unsafe_pointer_type())
- {
- go_error_at(this->location(), "invalid indirect of %<unsafe.Pointer%>");
- return Expression::make_error(this->location());
- }
-
// Check for an invalid pointer dereference. We need to do this
// here because Unary_expression::do_type will return an error type
// in this case. That can cause code to appear erroneous, and
{
Numeric_constant result;
bool issued_error;
- if (Unary_expression::eval_constant(op, &nc, loc, &result,
- &issued_error))
- return result.expression(loc);
+ if (Unary_expression::eval_constant(this->type_, op, &nc, loc,
+ &result, &issued_error))
+ {
+ Expression* ret = result.expression(loc);
+ Type_context subcontext(this->type_, this->type_->is_abstract());
+ ret->determine_type(gogo, &subcontext);
+ ret->check_types(gogo);
+ return ret;
+ }
else if (issued_error)
return Expression::make_error(this->location());
}
bool
Unary_expression::do_is_constant() const
{
- if (this->op_ == OPERATOR_MULT)
- {
- // Indirecting through a pointer is only constant if the object
- // to which the expression points is constant, but we currently
- // have no way to determine that.
- return false;
- }
- else if (this->op_ == OPERATOR_AND)
+ if (this->op_ == OPERATOR_MULT || this->op_ == OPERATOR_AND)
{
- // Taking the address of a variable is constant if it is a
- // global variable, not constant otherwise. In other cases taking the
- // address is probably not a constant.
- Var_expression* ve = this->expr_->var_expression();
- if (ve != NULL)
- {
- Named_object* no = ve->named_object();
- return no->is_variable() && no->var_value()->is_global();
- }
+ // These are not constant by Go language rules.
return false;
}
else
bool
Unary_expression::do_is_untyped(Type** ptype) const
{
+ if (this->type_ != NULL)
+ return Expression::is_untyped_type(this->type_, ptype);
+
if (this->op_ == OPERATOR_MULT || this->op_ == OPERATOR_AND)
return false;
return this->expr_->is_untyped(ptype);
// *ISSUED_ERROR.
bool
-Unary_expression::eval_constant(Operator op, const Numeric_constant* unc,
+Unary_expression::eval_constant(Type* type, Operator op,
+ const Numeric_constant* unc,
Location location, Numeric_constant* nc,
bool* issued_error)
{
mpfr_t val;
mpfr_init(val);
mpfr_neg(val, uval, MPFR_RNDN);
- nc->set_float(unc->type(), val);
+ Type* utype = unc->type();
+ if (type != NULL
+ && type->is_abstract()
+ && type->is_numeric_type())
+ utype = type;
+ nc->set_float(utype, val);
mpfr_clear(uval);
mpfr_clear(val);
return true;
mpc_t val;
mpc_init2(val, mpc_precision);
mpc_neg(val, uval, MPC_RNDNN);
- nc->set_complex(unc->type(), val);
+ Type* utype = unc->type();
+ if (type != NULL
+ && type->is_abstract()
+ && type->is_numeric_type())
+ utype = type;
+ nc->set_complex(utype, val);
mpc_clear(uval);
mpc_clear(val);
return true;
bool
Unary_expression::do_numeric_constant_value(Numeric_constant* nc)
{
+ if (this->is_error_expression())
+ return false;
+
Numeric_constant unc;
if (!this->expr_->numeric_constant_value(&unc))
return false;
bool issued_error;
- return Unary_expression::eval_constant(this->op_, &unc, this->location(),
- nc, &issued_error);
+ bool r = Unary_expression::eval_constant(this->type_, this->op_, &unc,
+ this->location(), nc,
+ &issued_error);
+ if (issued_error)
+ this->set_is_error();
+ return r;
}
// Return the boolean constant value of a unary expression, if it has one.
Type*
Unary_expression::do_type()
{
- switch (this->op_)
+ if (this->type_ == NULL)
{
- case OPERATOR_PLUS:
- case OPERATOR_MINUS:
- case OPERATOR_NOT:
- case OPERATOR_XOR:
- return this->expr_->type();
+ switch (this->op_)
+ {
+ case OPERATOR_AND:
+ return Type::make_pointer_type(this->expr_->type());
- case OPERATOR_AND:
- return Type::make_pointer_type(this->expr_->type());
+ case OPERATOR_MULT:
+ {
+ if (this->expr_->is_type_expression())
+ return Type::make_pointer_type(this->expr_->type());
- case OPERATOR_MULT:
- {
- Type* subtype = this->expr_->type();
- Type* points_to = subtype->points_to();
- if (points_to == NULL)
- return Type::make_error_type();
- return points_to;
- }
+ Type* subtype = this->expr_->type();
+ Type* points_to = subtype->points_to();
+ if (points_to == NULL)
+ {
+ this->report_error(_("expected pointer"));
+ this->type_ = Type::make_error_type();
+ return this->type_;
+ }
+ return points_to;
+ }
- default:
- go_unreachable();
+ default:
+ go_assert(saw_errors());
+ return Type::make_error_type();
+ }
}
+
+ return this->type_;
}
// Determine abstract types for a unary expression.
case OPERATOR_MINUS:
case OPERATOR_NOT:
case OPERATOR_XOR:
- this->expr_->determine_type(gogo, context);
+ {
+ if (this->type_ != NULL)
+ return;
+
+ Type* dummy;
+ Type_context subcontext(*context);
+ if (this->expr_->is_untyped(&dummy) && this->expr_->is_constant())
+ {
+ // We evaluate an untyped operator as untyped. Then we
+ // convert it to the desired type. Otherwise we may, for
+ // example, give a useless error for one more than the
+ // most positive integer when it is the operand of a unary
+ // minus.
+ subcontext.type = NULL;
+ subcontext.may_be_abstract = true;
+ }
+ this->expr_->determine_type(gogo, &subcontext);
+
+ this->type_ = this->expr_->type();
+
+ // If this is an untyped expression in a typed context, use
+ // the context type. If this doesn't work we'll report an
+ // error later.
+ if (this->type_->is_abstract()
+ && !context->may_be_abstract
+ && context->type != NULL)
+ {
+ if (context->type->interface_type() == NULL)
+ this->type_ = context->type;
+ else
+ this->type_ = this->type_->make_non_abstract_type();
+ }
+ }
break;
case OPERATOR_AND:
break;
case OPERATOR_MULT:
- // Indirecting through a pointer.
{
+ if (this->expr_->is_type_expression())
+ {
+ this->expr_->determine_type_no_context(gogo);
+ return;
+ }
+
+ // Indirecting through a pointer.
Type* subtype = (context->type == NULL
? NULL
: Type::make_pointer_type(context->type));
void
Unary_expression::do_check_types(Gogo*)
{
+ if (this->is_error_expression())
+ return;
+
Type* type = this->expr_->type();
if (type->is_error())
{
break;
case OPERATOR_MULT:
+ if (this->expr_->is_type_expression())
+ break;
+
+ // Catching an invalid indirection of unsafe.Pointer here avoid
+ // having to deal with TYPE_VOID in other places.
+ if (this->expr_->type()->is_unsafe_pointer_type())
+ {
+ go_error_at(this->location(),
+ "invalid indirect of %<unsafe.Pointer%>");
+ this->set_is_error();
+ return;
+ }
+
// Indirecting through a pointer.
if (type->points_to() == NULL)
this->report_error(_("expected pointer"));
Bexpression* ret;
Bexpression* bexpr = this->expr_->get_backend(context);
- Btype* btype = this->expr_->type()->get_backend(gogo);
+ Btype* btype = (this->type_ == NULL
+ ? this->expr_->type()->get_backend(gogo)
+ : this->type_->get_backend(gogo));
switch (this->op_)
{
case OPERATOR_PLUS:
- ret = bexpr;
+ ret = gogo->backend()->convert_expression(btype, bexpr, loc);
break;
case OPERATOR_MINUS:
case OPERATOR_NOT:
case OPERATOR_XOR:
ret = gogo->backend()->unary_expression(this->op_, bexpr, loc);
+ ret = gogo->backend()->convert_expression(btype, ret, loc);
break;
case OPERATOR_AND:
nil, loc);
Expression* crash = Runtime::make_call(gogo, Runtime::PANIC_MEM,
loc, 0);
+ crash->determine_type_no_context(gogo);
Bexpression* bcrash = crash->get_backend(context);
Bfunction* bfn = context->function()->func_value()->get_decl();
bexpr = gogo->backend()->conditional_expression(bfn, btype,
if (!is_shift && !right_nc->set_type(type, true, location))
return false;
if (is_shift
- && ((left_type->integer_type() == NULL
- && !left_type->is_abstract())
- || (right_type->integer_type() == NULL
- && !right_type->is_abstract())))
+ && right_type->integer_type() == NULL
+ && !right_type->is_abstract())
return false;
bool r;
if (type->complex_type() != NULL)
- r = Binary_expression::eval_complex(op, left_nc, right_nc, location, nc);
+ r = Binary_expression::eval_complex(op, left_nc, right_nc, location, nc,
+ issued_error);
else if (type->float_type() != NULL)
- r = Binary_expression::eval_float(op, left_nc, right_nc, location, nc);
+ r = Binary_expression::eval_float(op, left_nc, right_nc, location, nc,
+ issued_error);
else
- r = Binary_expression::eval_integer(op, left_nc, right_nc, location, nc);
+ r = Binary_expression::eval_integer(op, left_nc, right_nc, location, nc,
+ issued_error);
if (r)
{
bool
Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc,
const Numeric_constant* right_nc,
- Location location, Numeric_constant* nc)
+ Location location, Numeric_constant* nc,
+ bool* issued_error)
{
mpz_t left_val;
if (!left_nc->to_int(&left_val))
go_error_at(location, "constant addition overflow");
nc->set_invalid();
mpz_set_ui(val, 1);
+ *issued_error = true;
}
break;
case OPERATOR_MINUS:
go_error_at(location, "constant subtraction overflow");
nc->set_invalid();
mpz_set_ui(val, 1);
+ *issued_error = true;
}
break;
case OPERATOR_OR:
go_error_at(location, "constant multiplication overflow");
nc->set_invalid();
mpz_set_ui(val, 1);
+ *issued_error = true;
}
break;
case OPERATOR_DIV:
go_error_at(location, "division by zero");
nc->set_invalid();
mpz_set_ui(val, 0);
+ *issued_error = true;
}
break;
case OPERATOR_MOD:
go_error_at(location, "division by zero");
nc->set_invalid();
mpz_set_ui(val, 0);
+ *issued_error = true;
}
break;
case OPERATOR_LSHIFT:
go_error_at(location, "shift count overflow");
nc->set_invalid();
mpz_set_ui(val, 1);
+ *issued_error = true;
}
break;
}
go_error_at(location, "shift count overflow");
nc->set_invalid();
mpz_set_ui(val, 1);
+ *issued_error = true;
}
else
{
bool
Binary_expression::eval_float(Operator op, const Numeric_constant* left_nc,
const Numeric_constant* right_nc,
- Location location, Numeric_constant* nc)
+ Location location, Numeric_constant* nc,
+ bool* issued_error)
{
mpfr_t left_val;
if (!left_nc->to_float(&left_val))
go_error_at(location, "division by zero");
nc->set_invalid();
mpfr_set_ui(val, 0, MPFR_RNDN);
+ *issued_error = true;
}
break;
default:
bool
Binary_expression::eval_complex(Operator op, const Numeric_constant* left_nc,
const Numeric_constant* right_nc,
- Location location, Numeric_constant* nc)
+ Location location, Numeric_constant* nc,
+ bool* issued_error)
{
mpc_t left_val;
if (!left_nc->to_complex(&left_val))
go_error_at(location, "division by zero");
nc->set_invalid();
mpc_set_ui(val, 0, MPC_RNDNN);
+ *issued_error = true;
break;
}
mpc_div(val, left_val, right_val, MPC_RNDNN);
Statement_inserter* inserter, int)
{
Location location = this->location();
+
+ if (this->is_error_expression())
+ return Expression::make_error(location);
+
Operator op = this->op_;
Expression* left = this->left_;
Expression* right = this->right_;
+ if (left->is_error_expression() || right->is_error_expression())
+ return Expression::make_error(location);
+
const bool is_comparison = (op == OPERATOR_EQEQ
|| op == OPERATOR_NOTEQ
|| op == OPERATOR_LT
if (left->numeric_constant_value(&left_nc)
&& right->numeric_constant_value(&right_nc))
{
+ Expression* ret;
if (is_comparison)
{
bool result;
&right_nc, location,
&result))
return this;
- return Expression::make_boolean(result, location);
+ ret = Expression::make_boolean(result, location);
}
else
{
return Expression::make_error(location);
return this;
}
- return nc.expression(location);
+ ret = nc.expression(location);
}
+
+ Type_context subcontext(this->type_, this->type_->is_abstract());
+ ret->determine_type(gogo, &subcontext);
+ ret->check_types(gogo);
+ return ret;
}
}
if (left->string_constant_value(&left_string)
&& right->string_constant_value(&right_string))
{
+ Expression* ret = NULL;
if (op == OPERATOR_PLUS)
{
- Type* result_type = (left->type()->named_type() != NULL
- ? left->type()
- : right->type());
delete left;
delete right;
- return Expression::make_string_typed(left_string + right_string,
- result_type, location);
+ ret = Expression::make_string_typed(left_string + right_string,
+ this->type_, location);
}
else if (is_comparison)
{
bool r = Binary_expression::cmp_to_bool(op, cmp);
delete left;
delete right;
- return Expression::make_boolean(r, location);
+ ret = Expression::make_boolean(r, location);
+ }
+
+ if (ret != NULL)
+ {
+ Type_context subcontext(this->type_, this->type_->is_abstract());
+ ret->determine_type(gogo, &subcontext);
+ ret->check_types(gogo);
+ return ret;
}
}
}
if (this->op_ == OPERATOR_NOTEQ)
ret = Expression::make_unary(OPERATOR_NOT, ret, loc);
+ ret->determine_type_no_context(gogo);
+
return ret;
}
if (this->op_ == OPERATOR_NOTEQ)
ret = Expression::make_unary(OPERATOR_NOT, ret, loc);
+ ret->determine_type_no_context(gogo);
+
return ret;
}
a1, a2, len);
Type* int32_type = Type::lookup_integer_type("int32");
Expression* zero = Expression::make_integer_ul(0, int32_type, loc);
- return Expression::make_binary(this->op_, call, zero, loc);
+ Expression* ret = Expression::make_binary(this->op_, call, zero, loc);
+ Type_context context(this->type_, this->type_->is_abstract());
+ ret->determine_type(gogo, &context);
+ return ret;
}
Expression*
bool
Binary_expression::do_numeric_constant_value(Numeric_constant* nc)
{
+ if (this->is_error_expression())
+ return false;
+
Numeric_constant left_nc;
if (!this->left_->numeric_constant_value(&left_nc))
return false;
if (!this->right_->numeric_constant_value(&right_nc))
return false;
bool issued_error;
- return Binary_expression::eval_constant(this->op_, &left_nc, &right_nc,
- this->location(), nc, &issued_error);
+ bool r = Binary_expression::eval_constant(this->op_, &left_nc, &right_nc,
+ this->location(), nc,
+ &issued_error);
+ if (issued_error)
+ this->set_is_error();
+ return r;
}
// Return the boolean constant value, if it has one.
bool
Binary_expression::do_boolean_constant_value(bool* val)
{
+ if (this->is_error_expression())
+ return false;
+
bool is_comparison = false;
switch (this->op_)
{
Type*
Binary_expression::do_type()
{
- if (this->classification() == EXPRESSION_ERROR)
- return Type::make_error_type();
-
- switch (this->op_)
+ if (this->type_ == NULL)
{
- case OPERATOR_EQEQ:
- case OPERATOR_NOTEQ:
- case OPERATOR_LT:
- case OPERATOR_LE:
- case OPERATOR_GT:
- case OPERATOR_GE:
- if (this->type_ == NULL)
- this->type_ = Type::make_boolean_type();
- return this->type_;
-
- case OPERATOR_PLUS:
- case OPERATOR_MINUS:
- case OPERATOR_OR:
- case OPERATOR_XOR:
- case OPERATOR_MULT:
- case OPERATOR_DIV:
- case OPERATOR_MOD:
- case OPERATOR_AND:
- case OPERATOR_BITCLEAR:
- case OPERATOR_OROR:
- case OPERATOR_ANDAND:
- {
- Type* type;
- if (!Binary_expression::operation_type(this->op_,
- this->left_->type(),
- this->right_->type(),
- &type))
- return Type::make_error_type();
- return type;
- }
-
- case OPERATOR_LSHIFT:
- case OPERATOR_RSHIFT:
- return this->left_->type();
-
- default:
- go_unreachable();
+ go_assert(saw_errors());
+ return Type::make_error_type();
}
+
+ return this->type_;
}
// Set type for a binary expression.
void
Binary_expression::do_determine_type(Gogo* gogo, 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.
+ if (this->type_ != NULL)
+ return;
+ // For a shift operation, the type of the binary expression is the
+ // type of the left operand. If the left operand is a constant,
+ // then it gets its type from the context.
bool is_shift_op = (this->op_ == OPERATOR_LSHIFT
|| this->op_ == OPERATOR_RSHIFT);
+ // For a comparison operation, the type of the binary expression is
+ // a boolean type.
bool is_comparison = (this->op_ == OPERATOR_EQEQ
|| this->op_ == OPERATOR_NOTEQ
|| this->op_ == OPERATOR_LT
// boolean, numeric, and string constants as operands where it is legal to
// use non-abstract boolean, numeric, and string constants, respectively.
// Any issues with the operation will be resolved in the check_types pass.
- bool is_constant_expr = (this->left_->is_constant()
- && this->right_->is_constant());
+ bool left_is_constant = this->left_->is_constant();
+ bool right_is_constant = this->right_->is_constant();
+ bool is_constant_expr = left_is_constant && right_is_constant;
Type_context subcontext(*context);
+ if (is_comparison)
+ subcontext.type = NULL;
+
+ Type* tleft;
+ bool left_is_untyped = this->left_->is_untyped(&tleft);
+ if (!left_is_untyped)
+ {
+ this->left_->determine_type(gogo, &subcontext);
+ tleft = this->left_->type();
+ }
+
+ Type* tright;
+ bool right_is_untyped = this->right_->is_untyped(&tright);
+ if (!right_is_untyped)
+ {
+ // For a shift operation, the right operand should always be an
+ // integer.
+ if (is_shift_op)
+ {
+ subcontext.type = Type::lookup_integer_type("uint");
+ subcontext.may_be_abstract = false;
+ }
- if (is_constant_expr && !is_shift_op)
+ this->right_->determine_type(gogo, &subcontext);
+ tright = this->right_->type();
+ }
+
+ // For each operand we have the real type or, if the operand is a
+ // untyped, a guess at the type. Use this to determine the types of
+ // untyped operands.
+
+ subcontext = *context;
+ if (left_is_untyped && (right_is_untyped || is_shift_op) && is_constant_expr)
{
+ // We evaluate the operands of an untyped expression as untyped
+ // values. Then we convert to the desired type. Otherwise we
+ // may, for example, mishandle a floating-point constant
+ // division as an integer division.
subcontext.type = NULL;
subcontext.may_be_abstract = true;
}
}
// Set the context for the left hand operand.
+
if (is_shift_op)
{
// The right hand operand of a shift plays no role in
// determining the type of the left hand operand.
+ if (subcontext.type == NULL
+ && right_is_constant
+ && context->may_be_abstract)
+ subcontext.type = Type::make_abstract_integer_type();
}
else if (!tleft->is_abstract())
subcontext.type = tleft;
subcontext.type = tright;
else
subcontext.type = tleft;
-
- if (subcontext.type != NULL && !context->may_be_abstract)
- subcontext.type = subcontext.type->make_non_abstract_type();
}
- this->left_->determine_type(gogo, &subcontext);
+ if (left_is_untyped)
+ {
+ this->left_->determine_type(gogo, &subcontext);
+ tleft = this->left_->type();
+ }
if (is_shift_op)
{
// We may have inherited an unusable type for the shift operand.
// Give a useful error if that happened.
- if (tleft->is_abstract()
+ if (left_is_untyped
+ && !is_constant_expr
&& subcontext.type != NULL
&& !subcontext.may_be_abstract
&& subcontext.type->interface_type() == NULL
- && subcontext.type->integer_type() == NULL)
+ && subcontext.type->integer_type() == NULL
+ && !tleft->is_error()
+ && !tright->is_error())
this->report_error(("invalid context-determined non-integer type "
"for left operand of shift"));
subcontext.may_be_abstract = false;
}
- this->right_->determine_type(gogo, &subcontext);
+ if (right_is_untyped)
+ {
+ this->right_->determine_type(gogo, &subcontext);
+ tright = this->right_->type();
+ }
+
+ if (this->left_->is_error_expression()
+ || tleft->is_error()
+ || this->right_->is_error_expression()
+ || tright->is_error())
+ {
+ this->set_is_error();
+ return;
+ }
if (is_comparison)
{
- if (this->type_ != NULL && !this->type_->is_abstract())
- ;
- else if (context->type != NULL && context->type->is_boolean_type())
+ if (context->type != NULL && context->type->is_boolean_type())
this->type_ = context->type;
else if (!context->may_be_abstract)
this->type_ = Type::lookup_bool_type();
+ else
+ this->type_ = Type::make_boolean_type();
+ }
+ else
+ {
+ if (is_shift_op)
+ {
+ // Shifts only work with integers, so force an abstract
+ // floating-point type (such as 1.0 << 1) into an integer.
+ if (tleft->is_abstract()
+ && tleft->integer_type() == NULL
+ && context->type == NULL)
+ {
+ this->type_ = Type::make_abstract_integer_type();
+ if (!context->may_be_abstract)
+ this->type_ = this->type_->make_non_abstract_type();
+ }
+ else
+ this->type_ = tleft;
+ }
+ else
+ {
+ if (!Binary_expression::operation_type(this->op_, tleft, tright,
+ &this->type_))
+ {
+ this->report_error("incompatible types in binary expression");
+ this->type_ = Type::make_error_type();
+ return;
+ }
+ }
+
+ // If this is an untyped expression in a typed context, use the
+ // context type. If this doesn't work we'll report an error
+ // later.
+ if (this->type_->is_abstract()
+ && !context->may_be_abstract
+ && context->type != NULL)
+ {
+ if (context->type->interface_type() == NULL
+ && ((this->type_->is_numeric_type()
+ && context->type->is_numeric_type())
+ || (this->type_->is_string_type()
+ && context->type->is_string_type())
+ || (this->type_->is_boolean_type()
+ && context->type->is_boolean_type())))
+ this->type_ = context->type;
+ else if (context->type->interface_type() != NULL)
+ this->type_ = this->type_->make_non_abstract_type();
+ }
}
}
}
else
{
- if (left_type->integer_type() == NULL)
+ if (left_type->integer_type() == NULL
+ && !left_type->is_abstract()
+ && !this->is_constant())
this->report_error(_("shift of non-integer operand"));
if (right_type->is_string_type())
loc);
Expression* crash = Runtime::make_call(gogo, Runtime::PANIC_SHIFT,
loc, 0);
+ crash->determine_type_no_context(gogo);
Bexpression* bcrash = crash->get_backend(context);
Bfunction* bfn = context->function()->func_value()->get_decl();
ret = gogo->backend()->conditional_expression(bfn, btype, compare,
Expression* crash = Runtime::make_call(gogo, Runtime::PANIC_DIVIDE,
loc, 0);
+ crash->determine_type_no_context(gogo);
Bexpression* bcrash = crash->get_backend(context);
// right == 0 ? (panicdivide(), 0) : ret
Expression* descriptor =
Expression::make_type_descriptor(right_type, location);
- left =
- Runtime::make_call(gogo,
- (left_type->interface_type()->is_empty()
- ? Runtime::EFACEVALEQ
- : Runtime::IFACEVALEQ),
- location, 3, left, descriptor,
- pointer_arg);
+ left = Runtime::make_call(gogo,
+ (left_type->interface_type()->is_empty()
+ ? Runtime::EFACEVALEQ
+ : Runtime::IFACEVALEQ),
+ location, 3, left, descriptor,
+ pointer_arg);
go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ);
right = Expression::make_boolean(true, location);
}
}
}
+ left->determine_type_no_context(gogo);
+ right->determine_type_no_context(gogo);
+
Bexpression* left_bexpr = left->get_backend(context);
Bexpression* right_bexpr = right->get_backend(context);
loc);
Temporary_statement* ts =
Statement::make_temporary(array_type, array, loc);
+ ts->determine_types(gogo);
inserter->insert(ts);
Expression* ref = Expression::make_temporary_reference(ts, loc);
ref = Expression::make_unary(OPERATOR_AND, ref, loc);
- Expression* call =
- Runtime::make_call(gogo, Runtime::CONCATSTRINGS, loc, 3,
- buf, ref, len->copy());
- return Expression::make_cast(type, call, loc);
+ Expression* call =
+ Runtime::make_call(gogo, Runtime::CONCATSTRINGS, loc, 3, buf,
+ ref, len->copy());
+ Expression* ret = Expression::make_cast(type, call, loc);
+ Type_context context(type, false);
+ ret->determine_type(gogo, &context);
+ return ret;
}
void
void
Bound_method_expression::do_check_types(Gogo*)
{
- Named_object* fn = this->method_->named_object();
+ Named_object* fn = this->function();
if (!fn->is_function() && !fn->is_function_declaration())
{
this->report_error(_("object is not a method"));
loc);
call->set_varargs_are_lowered();
- Statement* s = Statement::make_return_from_call(call, loc);
+ Statement* s = Statement::make_return_from_call(new_no, call, loc);
+ s->determine_types(gogo);
gogo->add_statement(s);
Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc);
- // This is called after lowering but before determine_types.
+ // This is called after lowering.
gogo->lower_block(new_no, b);
gogo->finish_function(loc);
Expression* crash = Runtime::make_call(gogo, Runtime::PANIC_MEM, loc, 0);
// Fix the type of the conditional expression by pretending to
// evaluate to RET either way through the conditional.
+ crash->determine_type_no_context(gogo);
crash = Expression::make_compound(crash, ret, loc);
ret = Expression::make_conditional(nil_check, crash, ret, loc);
}
// RET is a pointer to a struct, but we want a function type.
ret = Expression::make_unsafe_cast(this->type(), ret, loc);
+ ret->determine_type_no_context(gogo);
+
return ret;
}
Selector_expression(Expression* left, const std::string& name,
Location location)
: Parser_expression(EXPRESSION_SELECTOR, location),
- left_(left), name_(name)
+ left_(left), name_(name), resolved_(NULL)
{ }
+ // Return the resolved selector. This will typically be a
+ // Field_reference_expression or a Bound_method_expression or an
+ // Interface_field_reference_expression.
+ Expression*
+ resolved()
+ { return this->resolved_; }
+
protected:
int
do_traverse(Traverse* traverse)
{ return Expression::traverse(&this->left_, traverse); }
+ Type*
+ do_type();
+
+ void
+ do_determine_type(Gogo*, const Type_context*);
+
+ bool
+ do_is_addressable() const;
+
+ void
+ do_issue_nil_check();
+
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Expression* left_;
// The name on the right hand side.
std::string name_;
+ // The resolved expression.
+ Expression* resolved_;
};
-// Lower a selector expression once we know the real type of the left
-// hand side.
-
-Expression*
-Selector_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*,
- int)
+void
+Selector_expression::do_determine_type(Gogo* gogo, const Type_context* context)
{
+ if (this->is_error_expression() || this->resolved_ != NULL)
+ return;
Expression* left = this->left_;
- if (left->is_type_expression())
- return this->lower_method_expression(gogo);
- return Type::bind_field_or_method(gogo, left->type(), left, this->name_,
- this->location());
+ left->determine_type_no_context(gogo);
+ if (left->is_error_expression())
+ this->set_is_error();
+ else
+ {
+ if (left->is_type_expression())
+ this->resolved_ = this->lower_method_expression(gogo);
+ else
+ this->resolved_ = Type::bind_field_or_method(gogo, left->type(), left,
+ this->name_,
+ this->location());
+ this->resolved_->determine_type(gogo, context);
+ }
}
-// Lower a method expression T.M or (*T).M. We turn this into a
-// function literal.
-
-Expression*
-Selector_expression::lower_method_expression(Gogo* gogo)
+Type*
+Selector_expression::do_type()
+{
+ if (this->is_error_expression())
+ return Type::make_error_type();
+ go_assert(this->resolved_ != NULL);
+ return this->resolved_->type();
+}
+
+bool
+Selector_expression::do_is_addressable() const
+{
+ if (this->is_error_expression())
+ return true;
+ go_assert(this->resolved_ != NULL);
+ return this->resolved_->is_addressable();
+}
+
+void
+Selector_expression::do_issue_nil_check()
+{
+ if (this->is_error_expression())
+ return;
+ go_assert(this->resolved_ != NULL);
+ this->resolved_->issue_nil_check();
+}
+
+// Lower a selector expression to the resolved value.
+
+Expression*
+Selector_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
+{
+ if (this->is_error_expression() || this->resolved_ == NULL)
+ return Expression::make_error(this->location());
+ return this->resolved_;
+}
+
+// Lower a method expression T.M or (*T).M. We turn this into a
+// function literal.
+
+Expression*
+Selector_expression::lower_method_expression(Gogo* gogo)
{
Location location = this->location();
Type* left_type = this->left_->type();
method_type->is_varargs(),
location);
- Statement* s = Statement::make_return_from_call(call, location);
+ Statement* s = Statement::make_return_from_call(no, call, location);
+ s->determine_types(gogo);
gogo->add_statement(s);
Block* b = gogo->finish_block(location);
gogo->add_block(b, location);
- // Lower the call in case there are multiple results.
- gogo->lower_block(no, b);
- gogo->flatten_block(no, b);
-
gogo->finish_function(location);
return Expression::make_func_reference(no, NULL, location);
gogo_(gogo), code_(BUILTIN_INVALID), seen_(false),
recover_arg_is_set_(false)
{
- Func_expression* fnexp = this->fn()->func_expression();
- if (fnexp == NULL)
+ const Named_object* no;
+ if (fn->is_error_expression())
{
this->code_ = BUILTIN_INVALID;
return;
}
- const std::string& name(fnexp->named_object()->name());
+ else if (fn->func_expression() != NULL)
+ no = fn->func_expression()->named_object();
+ else if (fn->unknown_expression() != NULL)
+ no = fn->unknown_expression()->named_object();
+ else
+ go_unreachable();
+
+ const std::string& name(no->name());
if (name == "append")
this->code_ = BUILTIN_APPEND;
else if (name == "cap")
Location loc = this->location();
- if (this->is_varargs() && this->code_ != BUILTIN_APPEND)
- {
- this->report_error(_("invalid use of %<...%> with builtin function"));
- return Expression::make_error(loc);
- }
-
if (this->code_ == BUILTIN_OFFSETOF)
{
Expression* arg = this->one_arg();
-
- if (arg->bound_method_expression() != NULL
- || arg->interface_field_reference_expression() != NULL)
- {
- this->report_error(_("invalid use of method value as argument "
- "of Offsetof"));
- return this;
- }
-
Field_reference_expression* farg = arg->field_reference_expression();
while (farg != NULL)
{
{
Numeric_constant nc;
if (this->numeric_constant_value(&nc))
- return nc.expression(loc);
+ {
+ Expression* ret = nc.expression(loc);
+ Type_context subcontext;
+ if (this->type() != NULL)
+ subcontext = Type_context(this->type(),
+ this->type()->is_abstract());
+ ret->determine_type(gogo, &subcontext);
+ return ret;
+ }
}
switch (this->code_)
break;
case BUILTIN_NEW:
- {
- const Expression_list* args = this->args();
- if (args == NULL || args->size() < 1)
- this->report_error(_("not enough arguments"));
- else if (args->size() > 1)
- this->report_error(_("too many arguments"));
- else
- {
- Expression* arg = args->front();
- if (!arg->is_type_expression())
- {
- go_error_at(arg->location(), "expected type");
- this->set_is_error();
- }
- else
- return Expression::make_allocation(arg->type(), loc);
- }
- }
- break;
+ return Expression::make_allocation(this->one_arg()->type(), loc);
case BUILTIN_MAKE:
return this->lower_make(gogo, inserter);
case BUILTIN_DELETE:
{
const Expression_list* args = this->args();
- if (args == NULL || args->size() < 2)
- this->report_error(_("not enough arguments"));
- else if (args->size() > 2)
- this->report_error(_("too many arguments"));
- else if (args->front()->type()->map_type() == NULL)
- this->report_error(_("argument 1 must be a map"));
- else
- {
- Type* key_type =
- args->front()->type()->map_type()->key_type();
- Expression_list::iterator pa = this->args()->begin();
- pa++;
- Type* arg_type = (*pa)->type();
- std::string reason;
- if (!Type::are_assignable(key_type, arg_type, &reason))
- {
- if (reason.empty())
- go_error_at(loc, "argument 2 has incompatible type");
- else
- go_error_at(loc, "argument 2 has incompatible type (%s)",
- reason.c_str());
- this->set_is_error();
- }
- else if (!Type::are_identical(key_type, arg_type, 0, NULL))
- *pa = Expression::make_cast(key_type, *pa, loc);
- }
+ Type* key_type =
+ args->front()->type()->map_type()->key_type();
+ Expression_list::iterator pa = this->args()->begin();
+ pa++;
+ Type* arg_type = (*pa)->type();
+ if (!Type::are_identical(key_type, arg_type, 0, NULL))
+ *pa = Expression::make_cast(key_type, *pa, loc);
}
break;
gogo->lower_expression(function, inserter, &len1);
gogo->flatten_expression(function, inserter, &len1);
Temporary_statement* l1tmp = Statement::make_temporary(int_type, len1, loc);
+ l1tmp->determine_types(gogo);
inserter->insert(l1tmp);
// l2 = len(arg2)
gogo->lower_expression(function, inserter, &len2);
gogo->flatten_expression(function, inserter, &len2);
Temporary_statement* l2tmp = Statement::make_temporary(int_type, len2, loc);
+ l2tmp->determine_types(gogo);
inserter->insert(l2tmp);
// n = (l1 < l2 ? l1 : l2)
l2ref->copy(),
loc);
Temporary_statement* ntmp = Statement::make_temporary(NULL, n, loc);
+ ntmp->determine_types(gogo);
inserter->insert(ntmp);
// sz = n * sizeof(elem_type)
nref = Expression::make_temporary_reference(ntmp, loc);
ret = Expression::make_compound(call, nref, loc);
}
+ ret->determine_type_no_context(gogo);
return ret;
}
break;
loc);
Temporary_statement* atemp =
Statement::make_temporary(NULL, a, loc);
+ atemp->determine_types(gogo);
inserter->insert(atemp);
a = Expression::make_temporary_reference(atemp, loc);
a = Expression::make_dereference(a, NIL_CHECK_NOT_NEEDED, loc);
Statement* s = Statement::make_assignment(a, e3, loc);
+ s->determine_types(gogo);
inserter->insert(s);
e3 = Expression::make_temporary_reference(atemp, loc);
}
}
- return Runtime::make_call(gogo, code, loc, 3, e1, e2, e3);
+ Expression* ret = Runtime::make_call(gogo, code, loc, 3, e1, e2, e3);
+ ret->determine_type_no_context(gogo);
+ return ret;
}
case BUILTIN_ADD:
len = Expression::make_cast(uintptr_type, len, loc);
Expression* add = Expression::make_binary(OPERATOR_PLUS, ptr, len,
loc);
- return Expression::make_cast(this->args()->front()->type(), add, loc);
+ Expression* ret = Expression::make_cast(this->args()->front()->type(),
+ add, loc);
+ ret->determine_type_no_context(gogo);
+ return ret;
}
case BUILTIN_SLICE:
slice = Expression::make_conditional(cond, nil_slice, slice, loc);
- return Expression::make_compound(check, slice, loc);
+ Expression* ret = Expression::make_compound(check, slice, loc);
+ ret->determine_type_no_context(gogo);
+ return ret;
}
}
Location loc = this->location();
const Expression_list* args = this->args();
- if (args == NULL || args->size() < 1)
- {
- this->report_error(_("not enough arguments"));
- return Expression::make_error(this->location());
- }
Expression_list::const_iterator parg = args->begin();
Expression* first_arg = *parg;
- if (!first_arg->is_type_expression())
- {
- go_error_at(first_arg->location(), "expected type");
- this->set_is_error();
- return Expression::make_error(this->location());
- }
+ go_assert(first_arg->is_type_expression());
Type* type = first_arg->type();
- if (!type->in_heap())
- go_error_at(first_arg->location(),
- "cannot make slice of go:notinheap type");
-
bool is_slice = false;
bool is_map = false;
bool is_chan = false;
else if (type->channel_type() != NULL)
is_chan = true;
else
- {
- this->report_error(_("invalid type for make function"));
- return Expression::make_error(this->location());
- }
-
- Type_context int_context(Type::lookup_integer_type("int"), false);
+ go_unreachable();
++parg;
Expression* len_arg;
bool len_small = false;
if (parg == args->end())
{
- if (is_slice)
- {
- this->report_error(_("length required when allocating a slice"));
- return Expression::make_error(this->location());
- }
+ go_assert(!is_slice);
len_arg = Expression::make_integer_ul(0, NULL, loc);
len_small = true;
}
else
{
len_arg = *parg;
- len_arg->determine_type(gogo, &int_context);
- if (len_arg->type()->integer_type() == NULL)
- {
- go_error_at(len_arg->location(), "non-integer len argument in make");
- return Expression::make_error(this->location());
- }
if (!this->check_int_value(len_arg, true, &len_small))
return Expression::make_error(this->location());
++parg;
if (is_slice && parg != args->end())
{
cap_arg = *parg;
- cap_arg->determine_type(gogo, &int_context);
- if (cap_arg->type()->integer_type() == NULL)
- {
- go_error_at(cap_arg->location(), "non-integer cap argument in make");
- return Expression::make_error(this->location());
- }
if (!this->check_int_value(cap_arg, false, &cap_small))
return Expression::make_error(this->location());
++parg;
}
- if (parg != args->end())
- {
- this->report_error(_("too many arguments to make"));
- return Expression::make_error(this->location());
- }
+ go_assert(parg == args->end());
Location type_loc = first_arg->location();
else
go_unreachable();
- return Expression::make_unsafe_cast(type, call, loc);
+ Expression* ret = Expression::make_unsafe_cast(type, call, loc);
+ ret->determine_type_no_context(gogo);
+ return ret;
}
// Flatten a call to the predeclared append function. We do this in
}
Type* int_type = Type::lookup_integer_type("int");
+ Type_context int_context(int_type, false);
Type* uint_type = Type::lookup_integer_type("uint");
// Implementing
Expression_list* call_args = new Expression_list();
call_args->push_back(Expression::make_temporary_reference(s1tmp, loc));
Expression* len = Expression::make_call(lenref, call_args, false, loc);
+ len->determine_type(gogo, &int_context);
gogo->lower_expression(function, inserter, &len);
gogo->flatten_expression(function, inserter, &len);
Temporary_statement* l1tmp = Statement::make_temporary(int_type, len, loc);
Expression* len_arg = makecall->args()->at(1);
len_arg = Expression::make_cast(int_type, len_arg, loc);
l2tmp = Statement::make_temporary(int_type, len_arg, loc);
+ l2tmp->determine_types(gogo);
inserter->insert(l2tmp);
Expression* cap_arg = makecall->args()->at(2);
cap_arg = Expression::make_cast(int_type, cap_arg, loc);
Temporary_statement* c2tmp =
Statement::make_temporary(int_type, cap_arg, loc);
+ c2tmp->determine_types(gogo);
inserter->insert(c2tmp);
// Check bad len/cap here.
Expression* check = Runtime::make_call(gogo,
Runtime::CHECK_MAKE_SLICE,
loc, 3, elem, len2, cap2);
+ check->determine_type_no_context(gogo);
gogo->lower_expression(function, inserter, &check);
gogo->flatten_expression(function, inserter, &check);
Statement* s = Statement::make_statement(check, false);
call_args = new Expression_list();
call_args->push_back(Expression::make_temporary_reference(s2tmp, loc));
len = Expression::make_call(lenref, call_args, false, loc);
+ len->determine_type(gogo, &int_context);
gogo->lower_expression(function, inserter, &len);
gogo->flatten_expression(function, inserter, &len);
l2tmp = Statement::make_temporary(int_type, len, loc);
+ l2tmp->determine_types(gogo);
inserter->insert(l2tmp);
}
// ntmp := l1tmp + len2
Expression* ref = Expression::make_temporary_reference(l1tmp, loc);
Expression* sum = Expression::make_binary(OPERATOR_PLUS, ref, len2, loc);
+ sum->determine_type(gogo, &int_context);
gogo->lower_expression(function, inserter, &sum);
gogo->flatten_expression(function, inserter, &sum);
Temporary_statement* ntmp = Statement::make_temporary(int_type, sum, loc);
+ ntmp->determine_types(gogo);
inserter->insert(ntmp);
// s1tmp = uint(ntmp) > uint(cap(s1tmp)) ?
call_args = new Expression_list();
call_args->push_back(Expression::make_temporary_reference(s1tmp, loc));
Expression* cap = Expression::make_call(capref, call_args, false, loc);
+ cap->determine_type(gogo, &int_context);
gogo->lower_expression(function, inserter, &cap);
gogo->flatten_expression(function, inserter, &cap);
Temporary_statement* c1tmp = Statement::make_temporary(int_type, cap, loc);
+ c1tmp->determine_types(gogo);
inserter->insert(c1tmp);
Expression* left = Expression::make_temporary_reference(ntmp, loc);
{
Expression* rhs = Expression::make_conditional(cond, call, ref, loc);
+ rhs->determine_type_no_context(gogo);
gogo->lower_expression(function, inserter, &rhs);
gogo->flatten_expression(function, inserter, &rhs);
ref = Expression::make_temporary_reference(s1tmp, loc);
Statement* assign = Statement::make_assignment(ref, rhs, loc);
+ assign->determine_types(gogo);
inserter->insert(assign);
}
else
{
+ cond->determine_type_no_context(gogo);
gogo->lower_expression(function, inserter, &cond);
gogo->flatten_expression(function, inserter, &cond);
+ call->determine_type_no_context(gogo);
gogo->lower_expression(function, inserter, &call);
gogo->flatten_expression(function, inserter, &call);
+ ref->determine_type_no_context(gogo);
gogo->lower_expression(function, inserter, &ref);
gogo->flatten_expression(function, inserter, &ref);
Block* then_block = new Block(enclosing, loc);
Assignment_statement* assign =
Statement::make_assignment(assign_lhs, call, loc);
+ assign->determine_types(gogo);
then_block->add_statement(assign);
Block* else_block = new Block(enclosing, loc);
// This assignment will not change the pointer value, so it does
// not need a write barrier.
assign->set_omit_write_barrier();
+ assign->determine_types(gogo);
else_block->add_statement(assign);
Statement* s = Statement::make_if_statement(cond, then_block,
else_block, loc);
+ s->determine_types(gogo);
inserter->insert(s);
ref = Expression::make_temporary_reference(s1tmp, loc);
assign = Statement::make_assignment(ref, assign_lhs->copy(), loc);
+ assign->determine_types(gogo);
inserter->insert(assign);
}
a1, a2, a3);
}
}
+ call->determine_type_no_context(gogo);
gogo->lower_expression(function, inserter, &call);
gogo->flatten_expression(function, inserter, &call);
inserter->insert(Statement::make_statement(call, false));
Expression* lhs = Expression::make_array_index(ref, ref2, NULL,
NULL, loc);
lhs->array_index_expression()->set_needs_bounds_check(false);
+ lhs->determine_type_no_context(gogo);
gogo->lower_expression(function, inserter, &lhs);
gogo->flatten_expression(function, inserter, &lhs);
Expression* elem = *pa;
assign = gogo->assign_with_write_barrier(f, NULL, inserter,
lhs, elem, loc);
}
+ assign->determine_types(gogo);
inserter->insert(assign);
}
}
Expression* arg = this->one_arg();
if (arg == NULL)
return false;
+
+ // We may be called before the determine_types pass.
+ arg->determine_type_no_context(this->gogo_);
+
Type* arg_type = arg->type();
if (arg_type->is_error())
return true;
Expression* arg = this->one_arg();
if (arg == NULL)
return false;
- return arg->field_reference_expression() != NULL;
+ return (arg->field_reference_expression() != NULL
+ || arg->classification() == Expression::EXPRESSION_SELECTOR);
}
case BUILTIN_COMPLEX:
Expression* arg = this->one_arg();
if (arg == NULL)
return false;
+
+ // We may be called before the determine_types pass.
+ arg->determine_type_no_context(this->gogo_);
+
Type* arg_type = arg->type();
if (arg_type->is_error())
return false;
if (this->seen_)
return false;
- // We may be replacing this expression with a constant
- // during lowering, so verify the type to report any errors.
- // It's OK to verify an array type more than once.
- // FIXME: Remove this reference to go_get_gogo.
- arg_type->verify(go_get_gogo());
if (!arg_type->is_error())
{
Expression* e = arg_type->array_type()->length();
Expression* arg = this->one_arg();
if (arg == NULL)
return false;
+
+ // We may be called before the determine_types pass.
+ arg->determine_type_no_context(this->gogo_);
+
Type* arg_type = arg->type();
if (arg_type->is_error())
return false;
Expression* arg = this->one_arg();
if (arg == NULL)
return false;
+
+ // We may be called before the determine_types pass.
+ arg->determine_type_no_context(this->gogo_);
+
Field_reference_expression* farg = arg->field_reference_expression();
if (farg == NULL)
return false;
{
if (this->is_error_expression())
return Type::make_error_type();
+
+ Type* type = this->type();
+ if (type != NULL)
+ return type;
+
switch (this->code_)
{
case BUILTIN_INVALID:
this->fn()->determine_type_no_context(gogo);
+ this->simplify_multiple_results(gogo);
+
const Expression_list* args = this->args();
bool is_print;
Type* trailing_arg_types = NULL;
switch (this->code_)
{
+ case BUILTIN_MAKE:
+ trailing_arg_types = Type::lookup_integer_type("int");
+ is_print = false;
+ break;
+
case BUILTIN_PRINT:
case BUILTIN_PRINTLN:
// Do not force a large integer constant to "int".
case BUILTIN_REAL:
case BUILTIN_IMAG:
- arg_type = Builtin_call_expression::complex_type(context->type);
- if (arg_type == NULL)
- arg_type = Type::lookup_complex_type("complex128");
- is_print = false;
+ {
+ // We need the argument to determine the type, so check it now
+ // before any call to the do_type method.
+ const Expression_list* args = this->args();
+ if (args == NULL || args->size() < 1)
+ {
+ this->report_error(_("not enough arguments"));
+ return;
+ }
+ else if (args->size() > 1)
+ {
+ this->report_error(_("too many arguments"));
+ return;
+ }
+
+ Type* dummy;
+ if (context->type != NULL
+ && context->type->is_numeric_type()
+ && this->is_untyped(&dummy))
+ {
+ Type* type = context->type;
+ if (type->is_abstract() && !context->may_be_abstract)
+ type = type->make_non_abstract_type();
+ this->set_type(type);
+ }
+ else if (context->may_be_abstract && this->is_constant())
+ this->set_type(Type::make_abstract_float_type());
+
+ arg_type = Builtin_call_expression::complex_type(context->type);
+ if (arg_type == NULL)
+ {
+ if (context->may_be_abstract)
+ arg_type = Type::make_abstract_complex_type();
+ else
+ arg_type = Type::lookup_complex_type("complex128");
+ }
+
+ if (!args->front()->is_untyped(&dummy))
+ {
+ Type_context subcontext(arg_type, context->may_be_abstract);
+ args->front()->determine_type(gogo, &subcontext);
+ }
+
+ is_print = false;
+ }
break;
case BUILTIN_COMPLEX:
{
+ // We need the arguments to determine the type, so check them
+ // now before any call to the do_type method.
+ const Expression_list* args = this->args();
+ if (args == NULL || args->size() < 2)
+ {
+ this->report_error(_("not enough arguments"));
+ return;
+ }
+ else if (args->size() > 2)
+ {
+ this->report_error(_("too many arguments"));
+ return;
+ }
+
+ Type* dummy;
+ if (context->type != NULL
+ && context->type->is_numeric_type()
+ && this->is_untyped(&dummy))
+ {
+ Type* type = context->type;
+ if (type->is_abstract() && !context->may_be_abstract)
+ type = type->make_non_abstract_type();
+ this->set_type(type);
+ }
+ else if (context->may_be_abstract && this->is_constant())
+ this->set_type(Type::make_abstract_complex_type());
+
// For the complex function the type of one operand can
// determine the type of the other, as in a binary expression.
arg_type = Builtin_call_expression::real_imag_type(context->type);
if (arg_type == NULL)
- arg_type = Type::lookup_float_type("float64");
- if (args != NULL && args->size() == 2)
{
- Type* t1 = args->front()->type();
- Type* t2 = args->back()->type();
- if (!t1->is_abstract())
- arg_type = t1;
- else if (!t2->is_abstract())
- arg_type = t2;
+ if (context->may_be_abstract)
+ arg_type = Type::make_abstract_float_type();
+ else
+ arg_type = Type::lookup_float_type("float64");
+ }
+
+ Type_context subcontext(arg_type, context->may_be_abstract);
+ if (!args->front()->is_untyped(&dummy))
+ {
+ args->front()->determine_type(gogo, &subcontext);
+ arg_type = args->front()->type();
+ }
+ else if (!args->back()->is_untyped(&dummy))
+ {
+ args->back()->determine_type(gogo, &subcontext);
+ arg_type = args->back()->type();
}
+
is_print = false;
}
break;
case BUILTIN_APPEND:
if (!this->is_varargs()
&& args != NULL
- && !args->empty()
- && args->front()->type()->is_slice_type())
- trailing_arg_types =
- args->front()->type()->array_type()->element_type();
+ && !args->empty())
+ {
+ args->front()->determine_type_no_context(gogo);
+ if (args->front()->type()->is_slice_type())
+ trailing_arg_types =
+ args->front()->type()->array_type()->element_type();
+ }
is_print = false;
break;
is_print = false;
break;
+ case BUILTIN_DELETE:
+ if (args != NULL && args->size() == 2)
+ {
+ args->front()->determine_type_no_context(gogo);
+ Map_type* mt = args->front()->type()->map_type();
+ if (mt != NULL)
+ trailing_arg_types = mt->key_type();
+ }
+ is_print = false;
+ break;
+
default:
is_print = false;
break;
Type_context subcontext;
subcontext.type = arg_type;
- if (is_print)
+ if (is_print && (*pa)->is_constant())
{
// We want to print large constants, we so can't just
// use the appropriate nonabstract type. Use uint64 for
// an integer if we know it is nonnegative, otherwise
// use int64 for a integer, otherwise use float64 for a
// float or complex128 for a complex.
- Type* want_type = NULL;
- Type* atype = (*pa)->type();
- if (atype->is_abstract())
+ Type* atype;
+ if ((*pa)->is_untyped(&atype))
{
+ Type* want_type = NULL;
if (atype->integer_type() != NULL)
{
Numeric_constant nc;
// Check argument types for a builtin function.
void
-Builtin_call_expression::do_check_types(Gogo*)
+Builtin_call_expression::do_check_types(Gogo* gogo)
{
if (this->is_error_expression())
return;
+
+ if (this->is_varargs() && this->code_ != BUILTIN_APPEND)
+ {
+ go_error_at(this->location(),
+ "invalid use of %<...%> with built-in function");
+ this->set_is_error();
+ return;
+ }
+
switch (this->code_)
{
case BUILTIN_INVALID:
- case BUILTIN_NEW:
- case BUILTIN_MAKE:
- case BUILTIN_DELETE:
return;
- case BUILTIN_LEN:
- case BUILTIN_CAP:
+ case BUILTIN_NEW:
+ if (this->check_one_arg())
+ {
+ Expression* arg = this->one_arg();
+ if (!arg->is_type_expression())
+ {
+ go_error_at(arg->location(), "expected type");
+ this->set_is_error();
+ }
+ }
+ break;
+
+ case BUILTIN_MAKE:
{
- // 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())
+ const Expression_list* args = this->args();
+ if (args == NULL || args->size() < 1)
{
- Type* arg_type = this->one_arg()->type();
- if (arg_type->points_to() != NULL
- && arg_type->points_to()->array_type() != NULL
- && !arg_type->points_to()->is_slice_type())
- arg_type = arg_type->points_to();
- if (this->code_ == BUILTIN_CAP)
+ this->report_error(_("not enough arguments"));
+ return;
+ }
+
+ Expression* first_arg = args->front();
+ if (!first_arg->is_type_expression())
+ {
+ go_error_at(first_arg->location(), "expected type");
+ this->set_is_error();
+ return;
+ }
+
+ Type* type = first_arg->type();
+ if (!type->in_heap())
+ go_error_at(first_arg->location(),
+ "cannot make slice of go:notinheap type");
+
+ bool is_slice = type->is_slice_type();
+ if (!is_slice
+ && type->map_type() == NULL
+ && type->channel_type() == NULL)
+ {
+ this->report_error(_("invalid type for make function"));
+ return;
+ }
+
+ Expression_list::const_iterator parg = args->begin();
+ ++parg;
+ if (parg == args->end())
+ {
+ if (is_slice)
{
- if (!arg_type->is_error()
- && arg_type->array_type() == NULL
- && arg_type->channel_type() == NULL)
- this->report_error(_("argument must be array or slice "
- "or channel"));
+ this->report_error(_("length required when "
+ "allocating a slice"));
+ return;
}
- else
+ }
+ else
+ {
+ if ((*parg)->type()->integer_type() == NULL)
{
- if (!arg_type->is_error()
- && !arg_type->is_string_type()
- && arg_type->array_type() == NULL
- && arg_type->map_type() == NULL
- && arg_type->channel_type() == NULL)
- this->report_error(_("argument must be string or "
- "array or slice or map or channel"));
+ go_error_at((*parg)->location(),
+ "non-integer len argument in make");
+ return;
+ }
+ ++parg;
+
+ if (is_slice && parg != args->end())
+ {
+ if ((*parg)->type()->integer_type() == NULL)
+ {
+ go_error_at((*parg)->location(),
+ "non-integer cap argument in make");
+ return;
+ }
+ ++parg;
}
}
+
+ if (parg != args->end())
+ {
+ this->report_error(_("too many arguments to make"));
+ return;
+ }
}
break;
- case BUILTIN_PRINT:
- case BUILTIN_PRINTLN:
+ case BUILTIN_DELETE:
+ {
+ const Expression_list* args = this->args();
+ if (args == NULL || args->size() < 2)
+ this->report_error(_("not enough arguments"));
+ else if (args->size() > 2)
+ this->report_error(_("too many arguments"));
+ else if (args->front()->type()->map_type() == NULL)
+ this->report_error(_("argument 1 must be a map"));
+ else
+ {
+ Type* key_type =
+ args->front()->type()->map_type()->key_type();
+ Expression_list::iterator pa = this->args()->begin();
+ pa++;
+ Type* arg_type = (*pa)->type();
+ std::string reason;
+ if (!Type::are_assignable(key_type, arg_type, &reason))
+ {
+ if (reason.empty())
+ go_error_at(this->location(),
+ "argument 2 has incompatible type");
+ else
+ go_error_at(this->location(),
+ "argument 2 has incompatible type (%s)",
+ reason.c_str());
+ this->set_is_error();
+ }
+ }
+ }
+ break;
+
+ case BUILTIN_LEN:
+ case BUILTIN_CAP:
+ {
+ // The single argument may be either a string or an array or a
+ // map or a channel, or a pointer to a closed array.
+ if (this->check_one_arg())
+ {
+ Type* arg_type = this->one_arg()->type();
+ if (arg_type->points_to() != NULL
+ && arg_type->points_to()->array_type() != NULL
+ && !arg_type->points_to()->is_slice_type())
+ arg_type = arg_type->points_to();
+ if (this->code_ == BUILTIN_CAP)
+ {
+ if (!arg_type->is_error()
+ && arg_type->array_type() == NULL
+ && arg_type->channel_type() == NULL)
+ this->report_error(_("argument must be array or slice "
+ "or channel"));
+ }
+ else
+ {
+ if (!arg_type->is_error()
+ && !arg_type->is_string_type()
+ && arg_type->array_type() == NULL
+ && arg_type->map_type() == NULL
+ && arg_type->channel_type() == NULL)
+ this->report_error(_("argument must be string or "
+ "array or slice or map or channel"));
+ }
+ }
+ }
+ break;
+
+ case BUILTIN_PRINT:
+ case BUILTIN_PRINTLN:
{
const Expression_list* args = this->args();
if (args != NULL)
// an error anyhow, so we don't need one here.
}
else
- this->report_error(_("unsupported argument type to "
- "builtin function"));
+ {
+ // Report errors in the expression first.
+ (*p)->check_types(gogo);
+ if (!(*p)->is_error_expression())
+ this->report_error(_("unsupported argument type to "
+ "builtin function"));
+ }
}
}
}
if (this->check_one_arg())
{
Expression* arg = this->one_arg();
- if (arg->field_reference_expression() == NULL)
+ if (arg->classification() == Expression::EXPRESSION_SELECTOR)
+ {
+ Selector_expression* se = static_cast<Selector_expression*>(arg);
+ Expression* resolved = se->resolved();
+ if (resolved != NULL)
+ arg = resolved;
+ }
+ if (arg->is_error_expression())
+ ;
+ else if (arg->bound_method_expression() != NULL
+ || arg->interface_field_reference_expression() != NULL)
+ this->report_error(_("invalid use of method value as "
+ "argument of Offsetof"));
+ else if (arg->field_reference_expression() == NULL)
this->report_error(_("argument must be a field reference"));
}
break;
case BUILTIN_COMPLEX:
{
const Expression_list* args = this->args();
- if (args == NULL || args->size() < 2)
- this->report_error(_("not enough arguments"));
- else if (args->size() > 2)
- this->report_error(_("too many arguments"));
- else if (args->front()->is_error_expression()
- || args->front()->type()->is_error()
- || args->back()->is_error_expression()
- || args->back()->type()->is_error())
+ go_assert(args != NULL && args->size() == 2);
+ if (args->front()->is_error_expression()
+ || args->front()->type()->is_error()
+ || args->back()->is_error_expression()
+ || args->back()->type()->is_error())
this->set_is_error();
else if (!Type::are_identical(args->front()->type(),
args->back()->type(),
go_unreachable();
}
- return Expression::make_cast(int_type, val,
- location)->get_backend(context);
+ Expression* e = Expression::make_cast(int_type, val, location);
+ e->determine_type_no_context(gogo);
+ return e->get_backend(context);
}
case BUILTIN_PRINT:
if (is_ln)
{
Expression* print_nl =
- Runtime::make_call(gogo, Runtime::PRINTNL, location, 0);
+ Runtime::make_call(gogo, Runtime::PRINTNL, location, 0);
print_stmts = Expression::make_compound(print_stmts, print_nl,
location);
}
location, 0);
print_stmts = Expression::make_compound(print_stmts, unlock, location);
+ print_stmts->determine_type_no_context(gogo);
+
return print_stmts->get_backend(context);
}
arg = Expression::convert_for_assignment(gogo, empty, arg, location);
Expression* panic =
- Runtime::make_call(gogo, Runtime::GOPANIC, location, 1, arg);
+ Runtime::make_call(gogo, Runtime::GOPANIC, location, 1, arg);
+ panic->determine_type_no_context(gogo);
return panic->get_backend(context);
}
location, 0);
Expression* cond =
Expression::make_conditional(arg, recover, nil, location);
+ cond->determine_type_no_context(gogo);
return cond->get_backend(context);
}
Expression* arg = args->front();
Expression* close = Runtime::make_call(gogo, Runtime::CLOSE, location,
1, arg);
+ close->determine_type_no_context(gogo);
return close->get_backend(context);
}
int
Call_expression::do_traverse(Traverse* traverse)
{
+ if (this->lowered_ != NULL)
+ return Expression::traverse(&this->lowered_, traverse);
+
// If we are calling a function in a different package that returns
// an unnamed type, this may be the only chance we get to traverse
// that type. We don't traverse this->type_ because it may be a
return TRAVERSE_CONTINUE;
}
-// Lower a call statement.
+// Lower a Call_expression to a Builtin_call_expression. This happens
+// early on, before determine_types.
Expression*
-Call_expression::do_lower(Gogo* gogo, Named_object* function,
- Statement_inserter* inserter, int)
-{
- Location loc = this->location();
+Call_expression::lower_builtin(Gogo* gogo)
+{
+ // This is called before determine_types, so we can't call
+ // this->fn_->type(). Fortunately builtin calls require a direct
+ // reference to the builtin.
+ Expression* fn = this->fn_;
+ Named_object* no;
+ if (fn->func_expression() != NULL)
+ no = fn->func_expression()->named_object();
+ else if (fn->unknown_expression() != NULL)
+ {
+ no = fn->unknown_expression()->named_object();
+ if (no->is_unknown())
+ {
+ no = no->unknown_value()->real_named_object();
+ if (no == NULL)
+ return this;
+ }
+ }
+ else
+ return this;
- if (this->is_error_expression())
- return Expression::make_error(loc);
+ if (!no->is_function_declaration())
+ return this;
+ if (!no->func_declaration_value()->type()->is_builtin())
+ return this;
+
+ if (fn->unknown_expression() != NULL)
+ fn = Expression::make_func_reference(no, NULL, fn->location());
+
+ Builtin_call_expression* bce = new Builtin_call_expression(gogo, fn,
+ this->args_,
+ this->is_varargs_,
+ this->location());
+ if (this->is_deferred_)
+ bce->set_is_deferred();
+ if (this->is_concurrent_)
+ bce->set_is_concurrent();
+ return bce;
+}
+
+// A type conversion can be a constant.
- // A type cast can look like a function call.
+bool
+Call_expression::do_is_constant() const
+{
+ if (this->lowered_ != NULL)
+ return this->lowered_->is_constant();
if (this->fn_->is_type_expression()
&& this->args_ != NULL
&& this->args_->size() == 1)
- {
- if (this->expected_result_count_ != 0
- && this->expected_result_count_ != 1)
- {
- this->report_error(_("type conversion result count mismatch"));
- return Expression::make_error(loc);
- }
- return Expression::make_cast(this->fn_->type(), this->args_->front(),
- loc);
- }
+ return this->args_->front()->is_constant();
+ return false;
+}
- // Because do_type will return an error type and thus prevent future
- // errors, check for that case now to ensure that the error gets
- // reported.
- Function_type* fntype = this->get_function_type();
- if (fntype == NULL)
+bool
+Call_expression::do_is_untyped(Type** ptype) const
+{
+ if (this->lowered_ != NULL)
+ return this->lowered_->is_untyped(ptype);
+ return false;
+}
+
+bool
+Call_expression::do_numeric_constant_value(Numeric_constant* nc)
+{
+ if (this->lowered_ != NULL)
+ return this->lowered_->numeric_constant_value(nc);
+ if (this->fn_->is_type_expression()
+ && this->args_ != NULL
+ && this->args_->size() == 1)
{
- if (!this->fn_->type()->is_error())
- this->report_error(_("expected function"));
- this->set_is_error();
- return this;
- }
+ // If we get here, it's before the determine_types pass, so we
+ // have to pull apart the type carefully. This is a hack that
+ // is needed because the finalize_methods needs to be able to
+ // determine whether the length of an array is 1.
- // Handle an argument which is a call to a function which returns
- // multiple results.
- if (this->args_ != NULL
- && this->args_->size() == 1
- && this->args_->front()->call_expression() != NULL)
- {
- size_t rc = this->args_->front()->call_expression()->result_count();
- if (rc > 1
- && ((fntype->parameters() != NULL
- && (fntype->parameters()->size() == rc
- || (fntype->is_varargs()
- && fntype->parameters()->size() - 1 <= rc)))
- || fntype->is_builtin()))
+ Type* type;
+ if (this->fn_->classification() == EXPRESSION_TYPE)
+ type = this->fn_->type();
+ else if (this->fn_->unknown_expression() != NULL)
{
- Call_expression* call = this->args_->front()->call_expression();
- call->set_is_multi_value_arg();
- if (this->is_varargs_)
+ Named_object* no = this->fn_->unknown_expression()->named_object();
+ if (no->is_unknown())
{
- // It is not clear which result of a multiple result call
- // the ellipsis operator should be applied to. If we unpack the
- // the call into its individual results here, the ellipsis will be
- // applied to the last result.
- go_error_at(call->location(),
- _("multiple-value argument in single-value context"));
- return Expression::make_error(call->location());
+ no = no->unknown_value()->real_named_object();
+ go_assert(no != NULL);
}
-
- Expression_list* args = new Expression_list;
- for (size_t i = 0; i < rc; ++i)
- args->push_back(Expression::make_call_result(call, i));
- // We can't return a new call expression here, because this
- // one may be referenced by Call_result expressions. We
- // also can't delete the old arguments, because we may still
- // traverse them somewhere up the call stack. FIXME.
- this->args_ = args;
+ type = no->type_value();
}
+ else
+ return false;
+
+ if (!type->is_numeric_type())
+ return false;
+ if (!this->args_->front()->numeric_constant_value(nc))
+ return false;
+ return nc->set_type(type, false, this->location());
}
+ return false;
+}
- // Recognize a call to a builtin function.
- if (fntype->is_builtin())
+bool
+Call_expression::do_discarding_value()
+{
+ if (this->fn_->is_type_expression())
{
- Builtin_call_expression* bce =
- new Builtin_call_expression(gogo, this->fn_, this->args_,
- this->is_varargs_, loc);
- if (this->is_deferred_)
- bce->set_is_deferred();
- if (this->is_concurrent_)
- bce->set_is_concurrent();
- return bce;
+ this->unused_value_error();
+ return false;
}
+ return true;
+}
+
+// Lower a call statement.
+
+Expression*
+Call_expression::do_lower(Gogo* gogo, Named_object*,
+ Statement_inserter* inserter, int)
+{
+ if (this->lowered_ != NULL)
+ return this->lowered_;
+
+ Location loc = this->location();
+
+ if (this->is_error_expression())
+ return Expression::make_error(loc);
+
+ // Although we've already lowered calls to builtin functions, we may
+ // still see calls generated to builtins elsewhere in the lowering
+ // pass. It's simpler to handle them here.
+ Expression* builtin = this->lower_builtin(gogo);
+ if (builtin != this)
+ return builtin;
// If this call returns multiple results, create a temporary
// variable to hold them.
if (this->result_count() > 1 && this->call_temp_ == NULL)
{
Struct_field_list* sfl = new Struct_field_list();
- const Typed_identifier_list* results = fntype->results();
+ const Typed_identifier_list* results =
+ this->get_function_type()->results();
int i = 0;
char buf[20];
inserter->insert(this->call_temp_);
}
- // Handle a call to a varargs function by packaging up the extra
- // parameters.
- if (fntype->is_varargs())
- {
- const Typed_identifier_list* parameters = fntype->parameters();
- go_assert(parameters != NULL && !parameters->empty());
- Type* varargs_type = parameters->back().type();
- this->lower_varargs(gogo, function, inserter, varargs_type,
- parameters->size(), SLICE_STORAGE_MAY_ESCAPE);
- }
-
// If this is call to a method, call the method directly passing the
// object as the first parameter.
Bound_method_expression* bme = this->fn_->bound_method_expression();
first_arg = Expression::make_unsafe_cast(fatype, first_arg, loc);
}
+ first_arg->determine_type_no_context(gogo);
+ first_arg->check_types(gogo);
+
Expression_list* new_args = new Expression_list();
new_args->push_back(first_arg);
if (this->args_ != NULL)
return this;
}
-// Lower a call to a varargs function. FUNCTION is the function in
-// which the call occurs--it's not the function we are calling.
-// VARARGS_TYPE is the type of the varargs parameter, a slice type.
-// PARAM_COUNT is the number of parameters of the function we are
-// calling; the last of these parameters will be the varargs
-// parameter.
-
-void
-Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
- Statement_inserter* inserter,
- Type* varargs_type, size_t param_count,
- Slice_storage_escape_disp escape_disp)
-{
- if (this->varargs_are_lowered_)
- return;
-
- Location loc = this->location();
-
- go_assert(param_count > 0);
- go_assert(varargs_type->is_slice_type());
-
- size_t arg_count = this->args_ == NULL ? 0 : this->args_->size();
- if (arg_count < param_count - 1)
- {
- // Not enough arguments; will be caught in check_types.
- return;
- }
-
- Expression_list* old_args = this->args_;
- Expression_list* new_args = new Expression_list();
- bool push_empty_arg = false;
- if (old_args == NULL || old_args->empty())
- {
- go_assert(param_count == 1);
- push_empty_arg = true;
- }
- else
- {
- Expression_list::const_iterator pa;
- int i = 1;
- for (pa = old_args->begin(); pa != old_args->end(); ++pa, ++i)
- {
- if (static_cast<size_t>(i) == param_count)
- break;
- new_args->push_back(*pa);
- }
-
- // We have reached the varargs parameter.
-
- bool issued_error = false;
- if (pa == old_args->end())
- push_empty_arg = true;
- else if (pa + 1 == old_args->end() && this->is_varargs_)
- new_args->push_back(*pa);
- else if (this->is_varargs_)
- {
- if ((*pa)->type()->is_slice_type())
- this->report_error(_("too many arguments"));
- else
- {
- go_error_at(this->location(),
- _("invalid use of %<...%> with non-slice"));
- this->set_is_error();
- }
- return;
- }
- else
- {
- Type* element_type = varargs_type->array_type()->element_type();
- Expression_list* vals = new Expression_list;
- for (; pa != old_args->end(); ++pa, ++i)
- {
- // Check types here so that we get a better message.
- Type* patype = (*pa)->type();
- Location paloc = (*pa)->location();
- if (!this->check_argument_type(i, element_type, patype,
- paloc, issued_error))
- continue;
- vals->push_back(*pa);
- }
- Slice_construction_expression* sce =
- Expression::make_slice_composite_literal(varargs_type, vals, loc);
- if (escape_disp == SLICE_STORAGE_DOES_NOT_ESCAPE)
- sce->set_storage_does_not_escape();
- Expression* val = sce;
- gogo->lower_expression(function, inserter, &val);
- new_args->push_back(val);
- }
- }
-
- if (push_empty_arg)
- new_args->push_back(Expression::make_nil(loc));
-
- // We can't return a new call expression here, because this one may
- // be referenced by Call_result expressions. FIXME. We can't
- // delete OLD_ARGS because we may have both a Call_expression and a
- // Builtin_call_expression which refer to them. FIXME.
- this->args_ = new_args;
- this->varargs_are_lowered_ = true;
-}
-
// Flatten a call with multiple results into a temporary.
Expression*
{
Expression* ret = this->intrinsify(gogo, inserter);
if (ret != NULL)
- return ret;
+ {
+ ret->determine_type_no_context(gogo);
+ return ret;
+ }
}
// Add an implicit conversion to a boolean type, if needed. See the
Call_expression::set_expected_result_count(size_t count)
{
go_assert(this->expected_result_count_ == 0);
+ go_assert(!this->types_are_determined_);
this->expected_result_count_ = count;
}
{
if (this->is_error_expression())
return Type::make_error_type();
- if (this->type_ != NULL)
- return this->type_;
-
- Type* ret;
- Function_type* fntype = this->get_function_type();
- if (fntype == NULL)
- return Type::make_error_type();
-
- const Typed_identifier_list* results = fntype->results();
- if (results == NULL)
- ret = Type::make_void_type();
- else if (results->size() == 1)
- ret = results->begin()->type();
- else
- ret = Type::make_call_multiple_result_type();
-
- this->type_ = ret;
-
+ if (this->lowered_ != NULL)
+ return this->lowered_->type();
+ go_assert(this->type_ != NULL);
return this->type_;
}
// Determine types for a call expression. We can use the function
-// parameter types to set the types of the arguments.
+// parameter types to set the types of the arguments. We simplify
+// some of the call cases here, storing the result in the lowered_
+// field.
void
Call_expression::do_determine_type(Gogo* gogo, const Type_context* context)
if (!this->determining_types())
return;
- this->fn_->determine_type_no_context(gogo);
- Function_type* fntype = this->get_function_type();
- const Typed_identifier_list* parameters = NULL;
- if (fntype != NULL)
- parameters = fntype->parameters();
- if (this->args_ != NULL)
+ if (this->lowered_== NULL)
{
- Typed_identifier_list::const_iterator pt;
- if (parameters != NULL)
- pt = parameters->begin();
- bool first = true;
- for (Expression_list::const_iterator pa = this->args_->begin();
- pa != this->args_->end();
- ++pa)
- {
- if (first)
- {
- first = false;
- // If this is a method, the first argument is the
- // receiver.
- if (fntype != NULL && fntype->is_method())
- {
- Type* rtype = fntype->receiver()->type();
- // The receiver is always passed as a pointer.
- if (rtype->points_to() == NULL)
- rtype = Type::make_pointer_type(rtype);
- Type_context subcontext(rtype, false);
- (*pa)->determine_type(gogo, &subcontext);
- continue;
- }
- }
+ Expression* builtin = this->lower_builtin(gogo);
+ if (builtin != this)
+ this->lowered_ = builtin;
+ }
- if (parameters != NULL && pt != parameters->end())
- {
- Type_context subcontext(pt->type(), false);
- (*pa)->determine_type(gogo, &subcontext);
- ++pt;
+ if (this->lowered_ != NULL)
+ {
+ this->lowered_->determine_type(gogo, context);
+ return;
+ }
+
+ this->fn_->determine_type_no_context(gogo);
+
+ // Simplify a type conversion.
+
+ if (this->fn_->is_type_expression()
+ && this->args_ != NULL
+ && this->args_->size() == 1
+ && (this->expected_result_count_ == 0
+ || this->expected_result_count_ == 1))
+ {
+ this->lowered_ = Expression::make_cast(this->fn_->type(),
+ this->args_->front(),
+ this->location());
+ this->lowered_->determine_type(gogo, context);
+ return;
+ }
+
+ // Get the type of the function we are calling.
+
+ Function_type* fntype = this->get_function_type();
+ if (fntype == NULL)
+ {
+ // We report the error here so that we can reasonably return an
+ // error type in do_type.
+ if (!this->fn_->type()->is_error())
+ this->report_error(_("expected function"));
+ else
+ this->set_is_error();
+ if (this->args_ != NULL)
+ {
+ for (Expression_list::iterator p = this->args_->begin();
+ p != this->args_->end();
+ ++p)
+ (*p)->determine_type_no_context(gogo);
+ }
+ return;
+ }
+
+ // Simplify f(g()) where g() returns multiple results.
+
+ this->simplify_multiple_results(gogo);
+
+ // Set the type of this expression.
+
+ go_assert(this->type_ == NULL);
+ const Typed_identifier_list* results = fntype->results();
+ if (results == NULL || results->empty())
+ this->type_ = Type::make_void_type();
+ else if (results->size() == 1)
+ {
+ // If this is a call to a generated equality function, we
+ // determine the type based on the context. See the comment in
+ // Binary_expression::lower_array_comparison.
+ if (this->is_equal_function_
+ && !context->may_be_abstract
+ && context->type != NULL
+ && context->type->is_boolean_type())
+ this->type_ = context->type;
+ else
+ this->type_ = results->begin()->type();
+ }
+ else
+ this->type_ = Type::make_call_multiple_result_type();
+
+ // Determine the types of the arguments.
+
+ if (this->args_ == NULL)
+ {
+ if (fntype->is_varargs())
+ {
+ if (!this->rewrite_varargs())
+ this->set_is_error();
+ }
+ return;
+ }
+
+ const Typed_identifier_list* parameters = fntype->parameters();
+ Typed_identifier_list::const_iterator pt;
+ if (parameters != NULL)
+ pt = parameters->begin();
+ bool first = true;
+ for (Expression_list::const_iterator pa = this->args_->begin();
+ pa != this->args_->end();
+ ++pa)
+ {
+ if (first)
+ {
+ first = false;
+ // If this is a method, the first argument is the
+ // receiver.
+ if (fntype != NULL && fntype->is_method())
+ {
+ Type* rtype = fntype->receiver()->type();
+ // The receiver is always passed as a pointer.
+ if (rtype->points_to() == NULL)
+ rtype = Type::make_pointer_type(rtype);
+ Type_context subcontext(rtype, false);
+ (*pa)->determine_type(gogo, &subcontext);
+ continue;
}
- else
- (*pa)->determine_type_no_context(gogo);
}
+
+ if ((this->is_varargs_ || this->varargs_are_lowered_)
+ && fntype->is_varargs()
+ && pa + 1 == this->args_->end()
+ && parameters != NULL
+ && pt + 1 == parameters->end())
+ {
+ Type_context subcontext(pt->type(), false);
+ (*pa)->determine_type(gogo, &subcontext);
+ continue;
+ }
+
+ if (!this->is_varargs_
+ && fntype->is_varargs()
+ && parameters != NULL
+ && pt + 1 == parameters->end())
+ {
+ go_assert(pt->type()->is_slice_type());
+ Type_context subcontext(pt->type()->array_type()->element_type(),
+ false);
+ (*pa)->determine_type(gogo, &subcontext);
+ continue;
+ }
+
+ if (parameters != NULL && pt != parameters->end())
+ {
+ Type_context subcontext(pt->type(), false);
+ (*pa)->determine_type(gogo, &subcontext);
+ if (!fntype->is_varargs() || pt + 1 != parameters->end())
+ ++pt;
+ }
+ else
+ (*pa)->determine_type_no_context(gogo);
}
- // If this is a call to a generated equality function, we determine
- // the type based on the context. See the comment in
- // Binary_expression::lower_array_comparison.
- if (this->is_equal_function_
- && !context->may_be_abstract
- && context->type != NULL
- && context->type->is_boolean_type()
- && context->type != Type::lookup_bool_type())
+ if (fntype->is_varargs())
{
- go_assert(this->type_ == NULL
- || this->type_ == Type::lookup_bool_type()
- || this->type_ == context->type
- || this->type_->is_error());
- this->type_ = context->type;
+ if (!this->rewrite_varargs())
+ this->set_is_error();
}
}
}
}
+// Simplify f(g()) where g() returns multiple results. Called by
+// do_determine_types of both Call_expression and
+// Builtin_call_expression.
+
+void
+Call_expression::simplify_multiple_results(Gogo* gogo)
+{
+ if (this->args_ == NULL || this->args_->size() != 1)
+ return;
+
+ Call_expression* call = this->args_->front()->call_expression();
+ if (call == NULL || call->is_builtin())
+ return;
+
+ call->determine_type_no_context(gogo);
+ size_t rc = call->result_count();
+ Function_type* fntype = this->get_function_type();
+ if (rc > 1
+ && ((fntype->parameters() != NULL
+ && (fntype->parameters()->size() == rc
+ || (fntype->is_varargs()
+ && fntype->parameters()->size() - 1 <= rc)))
+ || fntype->is_builtin()))
+ {
+ if (this->is_varargs_)
+ {
+ go_error_at(call->location(),
+ "multiple-value argument in single-value context");
+ this->set_is_error();
+ }
+
+ call->set_is_multi_value_arg();
+ 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 create a new Call_expression here because this
+ // one may be referred to by Call_result expressions.
+ this->args_ = args;
+ }
+}
+
+// Lower a call to a varargs function by rewriting the value(s) passed
+// to the varargs argument into a slice. Called during the
+// determine_types pass.
+
+bool
+Call_expression::rewrite_varargs()
+{
+ if (this->varargs_are_lowered_)
+ return true;
+ this->varargs_are_lowered_ = true;
+
+ Function_type* fntype = this->get_function_type();
+
+ const Typed_identifier_list* parameters = fntype->parameters();
+ go_assert(parameters != NULL && !parameters->empty());
+ size_t param_count = parameters->size();
+
+ Type* varargs_type = parameters->back().type();
+ go_assert(varargs_type->is_slice_type());
+
+ size_t arg_count = this->args_ == NULL ? 0 : this->args_->size();
+ if (arg_count < param_count - 1)
+ {
+ if (!this->is_error_expression())
+ this->report_error(_("not enough arguments"));
+ return false;
+ }
+
+ bool ret = true;
+ Expression_list* old_args = this->args_;
+ Expression_list* new_args = new Expression_list();
+ bool push_empty_arg = false;
+ if (old_args == NULL || old_args->empty())
+ {
+ go_assert(param_count == 1);
+ push_empty_arg = true;
+ }
+ else
+ {
+ Expression_list::const_iterator pa;
+ size_t i = 1;
+ for (pa = old_args->begin(); pa != old_args->end(); ++pa, ++i)
+ {
+ if (i == param_count)
+ break;
+ new_args->push_back(*pa);
+ }
+
+ // We have reached the varargs parameter.
+
+ if (pa == old_args->end())
+ push_empty_arg = true;
+ else if (pa + 1 == old_args->end() && this->is_varargs_)
+ new_args->push_back(*pa);
+ else if (this->is_varargs_)
+ {
+ if (!this->is_error_expression())
+ {
+ if ((*pa)->type()->is_slice_type())
+ this->report_error(_("too many arguments"));
+ else
+ {
+ go_error_at(this->location(),
+ "invalid use of %<...%> with non-slice");
+ this->set_is_error();
+ }
+ }
+ return false;
+ }
+ else
+ {
+ Type* element_type = varargs_type->array_type()->element_type();
+ Expression_list* vals = new Expression_list;
+ for (; pa != old_args->end(); ++pa, ++i)
+ {
+ Type* patype = (*pa)->type();
+ Location paloc = (*pa)->location();
+ if (!this->check_argument_type(i, element_type, patype, paloc))
+ {
+ ret = false;
+ continue;
+ }
+ vals->push_back(*pa);
+ }
+ Expression* val =
+ Expression::make_slice_composite_literal(varargs_type, vals,
+ this->location());
+ new_args->push_back(val);
+ }
+ }
+
+ if (push_empty_arg)
+ new_args->push_back(Expression::make_nil(this->location()));
+
+ // We can't create a new Call_expression here because this
+ // one may be referred to by Call_result expressions.
+ this->args_ = new_args;
+
+ return ret;
+}
+
// Check types for parameter I.
bool
Call_expression::check_argument_type(int i, const Type* parameter_type,
const Type* argument_type,
- Location argument_location,
- bool issued_error)
+ Location argument_location)
{
std::string reason;
if (!Type::are_assignable(parameter_type, argument_type, &reason))
{
- if (!issued_error)
- {
- if (reason.empty())
- go_error_at(argument_location, "argument %d has incompatible type", i);
- else
- go_error_at(argument_location,
- "argument %d has incompatible type (%s)",
- i, reason.c_str());
- }
+ if (reason.empty())
+ go_error_at(argument_location, "argument %d has incompatible type", i);
+ else
+ go_error_at(argument_location,
+ "argument %d has incompatible type (%s)",
+ i, reason.c_str());
this->set_is_error();
return false;
}
void
Call_expression::do_check_types(Gogo*)
{
- if (this->classification() == EXPRESSION_ERROR)
+ if (this->is_error_expression()
+ || this->fn_->is_error_expression()
+ || this->fn_->type()->is_error())
+ return;
+ if (this->lowered_ != NULL)
return;
Function_type* fntype = this->get_function_type();
- if (fntype == NULL)
- {
- if (!this->fn_->type()->is_error())
- this->report_error(_("expected function"));
- return;
- }
+ go_assert(fntype != NULL);
if (this->expected_result_count_ != 0
&& this->expected_result_count_ != this->result_count())
return;
}
+ if (this->is_varargs_ && !fntype->is_varargs())
+ {
+ go_error_at(this->location(),
+ "invalid use of %<...%> calling non-variadic function");
+ this->set_is_error();
+ return;
+ }
+
bool is_method = fntype->is_method();
if (is_method)
{
}
}
- // Note that varargs was handled by the lower_varargs() method, so
- // we don't have to worry about it here unless something is wrong.
- if (this->is_varargs_ && !this->varargs_are_lowered_)
- {
- if (!fntype->is_varargs())
- {
- go_error_at(this->location(),
- _("invalid use of %<...%> calling non-variadic function"));
- this->set_is_error();
- return;
- }
- }
-
const Typed_identifier_list* parameters = fntype->parameters();
- if (this->args_ == NULL || this->args_->size() == 0)
+ if (this->args_ == NULL || this->args_->empty())
{
if (parameters != NULL && !parameters->empty())
this->report_error(_("not enough arguments"));
{
// This is F(G()) when G returns more than one result. If the
// results can be matched to parameters, it would have been
- // lowered in do_lower. If we get here we know there is a
- // mismatch.
+ // rewritten in determine_types. If we get here we know there
+ // is a mismatch.
if (this->args_->front()->call_expression()->result_count()
< parameters->size())
this->report_error(_("not enough arguments"));
return;
}
this->check_argument_type(i + 1, pt->type(), (*pa)->type(),
- (*pa)->location(), false);
+ (*pa)->location());
}
if (pa != this->args_->end())
this->report_error(_("too many arguments"));
// Class Index_expression.
+// Report whether EXPR is a map index expression. This is called when
+// types are determined but before lowering.
+
+bool
+Index_expression::is_map_index(Expression* expr)
+{
+ if (expr->map_index_expression() != NULL)
+ return true;
+ if (expr->index_expression() != NULL)
+ return expr->index_expression()->left_->type()->map_type() != NULL;
+ return false;
+}
+
// Traversal.
int
return TRAVERSE_CONTINUE;
}
+void
+Index_expression::do_determine_type(Gogo* gogo, const Type_context*)
+{
+ this->left_->determine_type_no_context(gogo);
+
+ Type_context end_context;
+ Type* type = this->left_->type();
+ if (type->array_type() != NULL
+ || (type->points_to() != NULL
+ && type->points_to()->array_type() != NULL
+ && !type->points_to()->is_slice_type())
+ || type->is_string_type())
+ {
+ Type_context index_context(Type::lookup_integer_type("int"), false);
+ this->start_->determine_type(gogo, &index_context);
+ end_context = index_context;
+ }
+ else if (type->map_type() != NULL)
+ {
+ Type_context key_context(type->map_type()->key_type(), false);
+ this->start_->determine_type(gogo, &key_context);
+ }
+ else if (type->is_error())
+ this->set_is_error();
+ else
+ {
+ if (this->cap_ != NULL)
+ this->report_error(_("invalid 3-index slice of object "
+ "that is not a slice"));
+ else if (this->end_ != NULL)
+ this->report_error(_("attempt to slice object that is not "
+ "array, slice, or string"));
+ else
+ this->report_error(_("attempt to index object that is not "
+ "array, slice, string, or map"));
+
+ this->start_->determine_type_no_context(gogo);
+ }
+
+ if (this->end_ != NULL)
+ this->end_->determine_type(gogo, &end_context);
+ if (this->cap_ != NULL)
+ this->cap_->determine_type(gogo, &end_context);
+}
+
+Type*
+Index_expression::do_type()
+{
+ if (this->is_error_expression())
+ return Type::make_error_type();
+
+ Type* type = this->left_->type();
+ if (this->end_ != NULL)
+ {
+ // A slice of an array is a slice type. A slice of a slice or
+ // string type is that same type.
+ Array_type* at = type->deref()->array_type();
+ if (at != NULL && !at->is_slice_type())
+ return Type::make_array_type(at->element_type(), NULL);
+ return type;
+ }
+ if (type->deref()->array_type() != NULL)
+ return type->deref()->array_type()->element_type();
+ else if (type->is_string_type())
+ return Type::lookup_integer_type("byte");
+ else if (type->map_type() != NULL)
+ return type->map_type()->val_type();
+ else
+ return Type::make_error_type();
+}
+
+void
+Index_expression::do_check_types(Gogo*)
+{
+ if (this->is_error_expression())
+ return;
+
+ Type* ltype = this->left_->type();
+ if (ltype->is_error())
+ {
+ go_assert(saw_errors());
+ return;
+ }
+
+ if (this->left_->is_type_expression())
+ {
+ this->report_error(_("attempt to index type expression"));
+ return;
+ }
+
+ if (ltype->array_type() != NULL)
+ {
+ if (!Array_index_expression::check_indexes(this->left_, this->start_,
+ this->end_, this->cap_,
+ this->location()))
+ this->set_is_error();
+ }
+ else if (ltype->points_to() != NULL
+ && ltype->points_to()->array_type() != NULL
+ && !ltype->points_to()->is_slice_type())
+ {
+ Expression* deref = Expression::make_dereference(this->left_,
+ NIL_CHECK_DEFAULT,
+ this->location());
+ if (!Array_index_expression::check_indexes(deref, this->start_,
+ this->end_, this->cap_,
+ this->location()))
+ this->set_is_error();
+ delete deref;
+ }
+ else if (ltype->is_string_type())
+ {
+ if (this->cap_ != NULL)
+ this->report_error(_("invalid 3-index slice of string"));
+ else
+ {
+ if (!String_index_expression::check_indexes(this->left_,
+ this->start_,
+ this->end_,
+ this->location()))
+ this->set_is_error();
+ }
+ }
+ else if (ltype->map_type() != NULL)
+ {
+ if (this->end_ != NULL || this->cap_ != NULL)
+ this->report_error(_("invalid slice of map"));
+ else
+ {
+ Map_type* mt = ltype->map_type();
+ std::string reason;
+ if (!Type::are_assignable(mt->key_type(), this->start_->type(),
+ &reason))
+ {
+ if (reason.empty())
+ this->report_error(_("incompatible type for map index"));
+ else
+ {
+ go_error_at(this->location(),
+ "incompatible type for map index (%s)",
+ reason.c_str());
+ this->set_is_error();
+ }
+ }
+ }
+ }
+ else
+ go_unreachable();
+}
+
+bool
+Index_expression::do_is_addressable() const
+{
+ if (this->is_error_expression())
+ return true;
+
+ Type* type = this->left_->type();
+ if (type->is_error())
+ return true;
+
+ // A slice index is addressable, and an index of an addressable
+ // array is addressable.
+
+ bool is_pointer = false;
+ if (type->points_to() != NULL
+ && type->points_to()->array_type() != NULL
+ && !type->points_to()->is_slice_type())
+ {
+ type = type->points_to();
+ is_pointer = true;
+ }
+
+ if (type->array_type() == NULL)
+ return false;
+
+ if (this->end_ != NULL)
+ return false;
+ if (type->is_slice_type())
+ return true;
+ return is_pointer || this->left_->is_addressable();
+}
+
+// We need to do a nil check of any pointer dereference.
+
+void
+Index_expression::do_issue_nil_check()
+{
+ this->left_->issue_nil_check();
+ this->needs_nil_check_ = true;
+}
+
// Lower an index expression. This converts the generic index
// expression into an array index, a string index, or a map index.
Expression*
Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{
+ if (this->is_error_expression())
+ return Expression::make_error(this->location());
+
Location location = this->location();
Expression* left = this->left_;
Expression* start = this->start_;
go_assert(saw_errors());
return Expression::make_error(location);
}
- else if (left->is_type_expression())
- {
- go_error_at(location, "attempt to index type expression");
- return Expression::make_error(location);
- }
else if (type->array_type() != NULL)
return Expression::make_array_index(left, start, end, cap, location);
else if (type->points_to() != NULL
// For an ordinary index into the array, the pointer will be
// dereferenced. For a slice it will not--the resulting slice
// will simply reuse the pointer, which is incorrect if that
- // pointer is nil.
- if (end != NULL || cap != NULL)
+ // pointer is nil. We may also be in a context that requires a
+ // nil check.
+ if (end != NULL || cap != NULL || this->needs_nil_check_)
deref->issue_nil_check();
return Expression::make_array_index(deref, start, end, cap, location);
}
else if (type->is_string_type())
{
- if (cap != NULL)
- {
- go_error_at(location, "invalid 3-index slice of string");
- return Expression::make_error(location);
- }
+ go_assert(cap == NULL);
return Expression::make_string_index(left, start, end, location);
}
else if (type->map_type() != NULL)
{
- if (end != NULL || cap != NULL)
- {
- go_error_at(location, "invalid slice of map");
- return Expression::make_error(location);
- }
+ go_assert(end == NULL && cap == NULL);
return Expression::make_map_index(left, start, location);
}
- else if (cap != NULL)
- {
- go_error_at(location,
- "invalid 3-index slice of object that is not a slice");
- return Expression::make_error(location);
- }
- else if (end != NULL)
- {
- go_error_at(location,
- ("attempt to slice object that is not "
- "array, slice, or string"));
- return Expression::make_error(location);
- }
else
- {
- go_error_at(location,
- ("attempt to index object that is not "
- "array, slice, string, or map"));
- return Expression::make_error(location);
- }
+ go_unreachable();
}
// Write an indexed expression
this->cap_->determine_type(gogo, &index_context);
}
-// Check types of an array index.
+// Check types of an array index.
+
+void
+Array_index_expression::do_check_types(Gogo*)
+{
+ if (!Array_index_expression::check_indexes(this->array_, this->start_,
+ this->end_, this->cap_,
+ this->location()))
+ this->set_is_error();
+}
+
+// A static function to check array index types. This is also called
+// by Index_expression::do_check_types. It reports whether type
+// checking succeeded.
-void
-Array_index_expression::do_check_types(Gogo*)
+bool
+Array_index_expression::check_indexes(Expression* array, Expression* start,
+ Expression* end, Expression* cap,
+ Location loc)
{
+ bool ret = true;
+
Numeric_constant nc;
unsigned long v;
- if (this->start_->type()->integer_type() == NULL
- && !this->start_->type()->is_error()
- && (!this->start_->type()->is_abstract()
- || !this->start_->numeric_constant_value(&nc)
+ if (start->type()->integer_type() == NULL
+ && !start->type()->is_error()
+ && (!start->type()->is_abstract()
+ || !start->numeric_constant_value(&nc)
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
- this->report_error(_("index must be integer"));
- if (this->end_ != NULL
- && this->end_->type()->integer_type() == NULL
- && !this->end_->type()->is_error()
- && !this->end_->is_nil_expression()
- && !this->end_->is_error_expression()
- && (!this->end_->type()->is_abstract()
- || !this->end_->numeric_constant_value(&nc)
+ {
+ go_error_at(loc, "index must be integer");
+ ret = false;
+ }
+
+ if (end != NULL
+ && end->type()->integer_type() == NULL
+ && !end->type()->is_error()
+ && !end->is_nil_expression()
+ && !end->is_error_expression()
+ && (!end->type()->is_abstract()
+ || !end->numeric_constant_value(&nc)
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
- this->report_error(_("slice end must be integer"));
- if (this->cap_ != NULL
- && this->cap_->type()->integer_type() == NULL
- && !this->cap_->type()->is_error()
- && !this->cap_->is_nil_expression()
- && !this->cap_->is_error_expression()
- && (!this->cap_->type()->is_abstract()
- || !this->cap_->numeric_constant_value(&nc)
+ {
+ go_error_at(loc, "slice end must be integer");
+ ret = false;
+ }
+
+ if (cap != NULL
+ && cap->type()->integer_type() == NULL
+ && !cap->type()->is_error()
+ && !cap->is_nil_expression()
+ && !cap->is_error_expression()
+ && (!cap->type()->is_abstract()
+ || !cap->numeric_constant_value(&nc)
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
- this->report_error(_("slice capacity must be integer"));
+ {
+ go_error_at(loc, "slice capacity must be integer");
+ ret = false;
+ }
- Array_type* array_type = this->array_->type()->array_type();
+ Array_type* array_type = array->type()->array_type();
if (array_type == NULL)
{
- go_assert(this->array_->type()->is_error());
- this->set_is_error();
- return;
+ go_assert(array->type()->is_error());
+ return false;
}
unsigned int int_bits =
Numeric_constant inc;
mpz_t ival;
bool ival_valid = false;
- if (this->start_->numeric_constant_value(&inc) && inc.to_int(&ival))
+ if (start->numeric_constant_value(&inc) && inc.to_int(&ival))
{
ival_valid = true;
if (mpz_sgn(ival) < 0
|| mpz_sizeinbase(ival, 2) >= int_bits
|| (lval_valid
- && (this->end_ == NULL
+ && (end == NULL
? mpz_cmp(ival, lval) >= 0
: mpz_cmp(ival, lval) > 0)))
{
- go_error_at(this->start_->location(), "array index out of bounds");
- this->set_is_error();
+ go_error_at(start->location(), "array index out of bounds");
+ ret = false;
}
}
- if (this->end_ != NULL && !this->end_->is_nil_expression())
+
+ if (end != NULL && !end->is_nil_expression())
{
Numeric_constant enc;
mpz_t eval;
bool eval_valid = false;
- if (this->end_->numeric_constant_value(&enc) && enc.to_int(&eval))
+ if (end->numeric_constant_value(&enc) && enc.to_int(&eval))
{
eval_valid = true;
if (mpz_sgn(eval) < 0
|| mpz_sizeinbase(eval, 2) >= int_bits
|| (lval_valid && mpz_cmp(eval, lval) > 0))
{
- go_error_at(this->end_->location(), "array index out of bounds");
- this->set_is_error();
+ go_error_at(end->location(), "array index out of bounds");
+ ret = false;
}
else if (ival_valid && mpz_cmp(ival, eval) > 0)
- this->report_error(_("inverted slice range"));
+ {
+ go_error_at(loc, "inverted slice range");
+ ret = false;
+ }
}
Numeric_constant cnc;
mpz_t cval;
- if (this->cap_ != NULL
- && this->cap_->numeric_constant_value(&cnc) && cnc.to_int(&cval))
+ if (cap != NULL
+ && cap->numeric_constant_value(&cnc) && cnc.to_int(&cval))
{
if (mpz_sgn(cval) < 0
|| mpz_sizeinbase(cval, 2) >= int_bits
|| (lval_valid && mpz_cmp(cval, lval) > 0))
{
- go_error_at(this->cap_->location(), "array index out of bounds");
- this->set_is_error();
+ go_error_at(cap->location(), "array index out of bounds");
+ ret = false;
}
else if (ival_valid && mpz_cmp(ival, cval) > 0)
{
- go_error_at(this->cap_->location(),
+ go_error_at(cap->location(),
"invalid slice index: capacity less than start");
- this->set_is_error();
+ ret = false;
}
else if (eval_valid && mpz_cmp(eval, cval) > 0)
{
- go_error_at(this->cap_->location(),
+ go_error_at(cap->location(),
"invalid slice index: capacity less than length");
- this->set_is_error();
+ ret = false;
}
mpz_clear(cval);
}
// A slice of an array requires an addressable array. A slice of a
// slice is always possible.
- if (this->end_ != NULL && !array_type->is_slice_type())
+ if (end != NULL && !array_type->is_slice_type())
{
- if (!this->array_->is_addressable())
- this->report_error(_("slice of unaddressable value"));
+ if (!array->is_addressable())
+ {
+ go_error_at(loc, "slice of unaddressable value");
+ ret = false;
+ }
else
- // Set the array address taken but not escape. The escape
- // analysis will make it escape to heap when needed.
- this->array_->address_taken(false);
+ {
+ // Set the array address taken but not escape. The escape
+ // analysis will make it escape to heap when needed.
+ array->address_taken(false);
+ }
}
+
+ return ret;
}
// The subexpressions of an array index must be evaluated in order.
{
len = array_type->get_length(gogo, this->array_->copy());
temp = Statement::make_temporary(NULL, len, loc);
+ temp->determine_types(gogo);
inserter->insert(temp);
len = Expression::make_temporary_reference(temp, loc);
}
{
scap = array_type->get_capacity(gogo, this->array_->copy());
temp = Statement::make_temporary(NULL, scap, loc);
+ temp->determine_types(gogo);
inserter->insert(temp);
scap = Expression::make_temporary_reference(temp, loc);
}
Expression* len = Expression::make_string_info(string->copy(),
STRING_INFO_LENGTH, loc);
temp = Statement::make_temporary(NULL, len, loc);
+ temp->determine_types(gogo);
inserter->insert(temp);
len = Expression::make_temporary_reference(temp, loc);
void
String_index_expression::do_check_types(Gogo*)
{
+ if (!String_index_expression::check_indexes(this->string_, this->start_,
+ this->end_, this->location()))
+ this->set_is_error();
+}
+
+// A static function to check string index types. This is also called
+// by Index_expression::do_check_types. It reports whether type
+// checking succeeded.
+
+bool
+String_index_expression::check_indexes(Expression* string, Expression* start,
+ Expression* end, Location loc)
+{
+ bool ret = true;
+
Numeric_constant nc;
unsigned long v;
- if (this->start_->type()->integer_type() == NULL
- && !this->start_->type()->is_error()
- && (!this->start_->type()->is_abstract()
- || !this->start_->numeric_constant_value(&nc)
+ if (start->type()->integer_type() == NULL
+ && !start->type()->is_error()
+ && (!start->type()->is_abstract()
+ || !start->numeric_constant_value(&nc)
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
- this->report_error(_("index must be integer"));
- if (this->end_ != NULL
- && this->end_->type()->integer_type() == NULL
- && !this->end_->type()->is_error()
- && !this->end_->is_nil_expression()
- && !this->end_->is_error_expression()
- && (!this->end_->type()->is_abstract()
- || !this->end_->numeric_constant_value(&nc)
+ {
+ go_error_at(loc, "index must be integer");
+ ret = false;
+ }
+
+ if (end != NULL
+ && end->type()->integer_type() == NULL
+ && !end->type()->is_error()
+ && !end->is_nil_expression()
+ && !end->is_error_expression()
+ && (!end->type()->is_abstract()
+ || !end->numeric_constant_value(&nc)
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
- this->report_error(_("slice end must be integer"));
+ {
+ go_error_at(loc, "slice end must be integer");
+ ret = false;
+ }
std::string sval;
- bool sval_valid = this->string_->string_constant_value(&sval);
+ bool sval_valid = string->string_constant_value(&sval);
Numeric_constant inc;
mpz_t ival;
bool ival_valid = false;
- if (this->start_->numeric_constant_value(&inc) && inc.to_int(&ival))
+ if (start->numeric_constant_value(&inc) && inc.to_int(&ival))
{
ival_valid = true;
if (mpz_sgn(ival) < 0
|| (sval_valid
- && (this->end_ == NULL
+ && (end == NULL
? mpz_cmp_ui(ival, sval.length()) >= 0
: mpz_cmp_ui(ival, sval.length()) > 0)))
{
- go_error_at(this->start_->location(), "string index out of bounds");
- this->set_is_error();
+ go_error_at(start->location(), "string index out of bounds");
+ ret = false;
}
}
- if (this->end_ != NULL && !this->end_->is_nil_expression())
+
+ if (end != NULL && !end->is_nil_expression())
{
Numeric_constant enc;
mpz_t eval;
- if (this->end_->numeric_constant_value(&enc) && enc.to_int(&eval))
+ if (end->numeric_constant_value(&enc) && enc.to_int(&eval))
{
if (mpz_sgn(eval) < 0
|| (sval_valid && mpz_cmp_ui(eval, sval.length()) > 0))
{
- go_error_at(this->end_->location(), "string index out of bounds");
- this->set_is_error();
+ go_error_at(end->location(), "string index out of bounds");
+ ret = false;
}
else if (ival_valid && mpz_cmp(ival, eval) > 0)
- this->report_error(_("inverted slice range"));
+ {
+ go_error_at(loc, "inverted slice range");
+ ret = false;
+ }
mpz_clear(eval);
}
}
if (ival_valid)
mpz_clear(ival);
+
+ return ret;
}
// Get the backend representation for a string index.
return Expression::traverse(&this->index_, traverse);
}
+void
+Map_index_expression::do_determine_type(Gogo* gogo, const Type_context*)
+{
+ this->map_->determine_type_no_context(gogo);
+ Map_type* mt = this->map_->type()->map_type();
+ go_assert(mt != NULL);
+ Type_context index_context(mt->key_type(), false);
+ this->index_->determine_type(gogo, &index_context);
+ if (this->value_pointer_ != NULL)
+ this->value_pointer_->determine_type_no_context(gogo);
+}
+
+void
+Map_index_expression::do_check_types(Gogo*)
+{
+ // Types should have been checked before this was created, so this
+ // will probably never be called. Check it anyhow to be sure.
+ Map_type* mt = this->map_->type()->map_type();
+ go_assert(mt != NULL);
+ if (!Type::are_assignable(mt->key_type(), this->index_->type(), NULL))
+ this->report_error(_("incompatible type for map index"));
+}
+
// We need to pass in a pointer to the key, so flatten the index into a
// temporary variable if it isn't already. The value pointer will be
// dereferenced and checked for nil, so flatten into a temporary to avoid
return mt->val_type();
}
-// Fix the type of a map index.
-
-void
-Map_index_expression::do_determine_type(Gogo* gogo, const Type_context*)
-{
- this->map_->determine_type_no_context(gogo);
- Map_type* mt = this->get_map_type();
- Type* key_type = mt == NULL ? NULL : mt->key_type();
- Type_context subcontext(key_type, false);
- this->index_->determine_type(gogo, &subcontext);
-}
-
-// Check types of a map index.
-
-void
-Map_index_expression::do_check_types(Gogo*)
-{
- std::string reason;
- Map_type* mt = this->get_map_type();
- if (mt == NULL)
- return;
- if (!Type::are_assignable(mt->key_type(), this->index_->type(), &reason))
- {
- if (reason.empty())
- this->report_error(_("incompatible type for map index"));
- else
- {
- go_error_at(this->location(), "incompatible type for map index (%s)",
- reason.c_str());
- this->set_is_error();
- }
- }
-}
-
// Add explicit type conversions.
void
this->value_pointer_ =
Expression::make_unsafe_cast(Type::make_pointer_type(val_type),
map_index, this->location());
+ this->value_pointer_->determine_type_no_context(gogo);
}
return this->value_pointer_;
e = Expression::make_unary(OPERATOR_AND, e, loc);
Expression* call = Runtime::make_call(gogo, Runtime::FIELDTRACK, loc, 1, e);
+ call->determine_type_no_context(gogo);
gogo->lower_expression(function, inserter, &call);
inserter->insert(Statement::make_statement(call, false));
loc);
call->set_varargs_are_lowered();
- Statement* s = Statement::make_return_from_call(call, loc);
+ Statement* s = Statement::make_return_from_call(new_no, call, loc);
+ s->determine_types(gogo);
gogo->add_statement(s);
Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc);
- // This is called after lowering but before determine_types.
+ // This is called after lowering.
gogo->lower_block(new_no, b);
gogo->finish_function(loc);
Bexpression* bnil_check = nil_check->get_backend(context);
Expression* crash = Runtime::make_call(gogo, Runtime::PANIC_MEM, loc, 0);
+ crash->determine_type_no_context(gogo);
Bexpression* bcrash = crash->get_backend(context);
Bfunction* bfn = context->function()->func_value()->get_decl();
// Check types.
void
-Struct_construction_expression::do_check_types(Gogo*)
+Struct_construction_expression::do_check_types(Gogo* gogo)
{
- if (this->vals() == NULL)
- return;
+ Struct_construction_expression::check_value_types(gogo, this->type_,
+ this->vals(),
+ this->location());
+}
- Struct_type* st = this->type_->struct_type();
- if (this->vals()->size() > st->field_count())
+// Check types. This static function is also called by
+// Composite_literal_expression::do_check_types. Reports whether type
+// checking succeeded.
+
+bool
+Struct_construction_expression::check_value_types(Gogo* gogo,
+ Type* type,
+ Expression_list* vals,
+ Location loc)
+{
+ if (vals == NULL || vals->empty())
+ return true;
+
+ Struct_type* st = type->struct_type();
+ if (vals->size() > st->field_count())
{
- this->report_error(_("too many expressions for struct"));
- return;
+ go_error_at(loc, "too many expressions for struct");
+ return false;
}
+ bool imported_type =
+ (type->named_type() != NULL
+ && type->named_type()->named_object()->package() != NULL);
+
+ bool ret = true;
const Struct_field_list* fields = st->fields();
- Expression_list::const_iterator pv = this->vals()->begin();
+ Expression_list::const_iterator pv = vals->begin();
int i = 0;
for (Struct_field_list::const_iterator pf = fields->begin();
pf != fields->end();
++pf, ++pv, ++i)
{
- if (pv == this->vals()->end())
+ if (pv == vals->end())
{
- this->report_error(_("too few expressions for struct"));
- break;
+ go_error_at(loc, "too few expressions for struct");
+ return false;
}
if (*pv == NULL)
continue;
+ if (imported_type
+ && (Gogo::is_hidden_name(pf->field_name())
+ || pf->is_embedded_builtin(gogo)))
+ {
+ go_error_at(loc,
+ "assignment of unexported field %qs in %qs literal",
+ Gogo::message_name(pf->field_name()).c_str(),
+ type->named_type()->message_name().c_str());
+ ret = false;
+ }
+
std::string reason;
if (!Type::are_assignable(pf->type(), (*pv)->type(), &reason))
{
("incompatible type for field %d in "
"struct construction (%s)"),
i + 1, reason.c_str());
- this->set_is_error();
+ ret = false;
}
}
- go_assert(pv == this->vals()->end());
+ go_assert(pv == vals->end());
+ return ret;
}
// Copy.
return TRAVERSE_CONTINUE;
}
+void
+Map_construction_expression::do_determine_type(Gogo* gogo, const Type_context*)
+{
+ Map_type* mt = this->type_->map_type();
+ go_assert(mt != NULL);
+ Type_context key_context(mt->key_type(), false);
+ Type_context val_context(mt->val_type(), false);
+ if (this->vals_ != NULL)
+ {
+ for (Expression_list::const_iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv)
+ {
+ (*pv)->determine_type(gogo, &key_context);
+ ++pv;
+ (*pv)->determine_type(gogo, &val_context);
+ }
+ }
+}
+
+void
+Map_construction_expression::do_check_types(Gogo*)
+{
+ // Types should have been checked before this was created, so this
+ // will probably never be called. Check it anyhow to be sure.
+
+ if (this->vals_ == NULL)
+ return;
+
+ Map_type* mt = this->type_->map_type();
+ go_assert(mt != NULL);
+ Type* key_type = mt->key_type();
+ Type* val_type = mt->val_type();
+ for (Expression_list::const_iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv)
+ {
+ if (!Type::are_assignable(key_type, (*pv)->type(), NULL))
+ {
+ go_error_at((*pv)->location(),
+ "incompatible type for key in map composite literal");
+ this->set_is_error();
+ }
+ ++pv;
+ if (!Type::are_assignable(val_type, (*pv)->type(), NULL))
+ {
+ go_error_at((*pv)->location(),
+ "incompatible type for value in map composite literal");
+ this->set_is_error();
+ }
+ }
+}
+
// Flatten constructor initializer into a temporary variable since
// we need to take its address for __go_construct_map.
Statement::make_temporary(NULL, constructor, loc);
constructor->issue_nil_check();
this->constructor_temp_->set_is_address_taken();
+ this->constructor_temp_->determine_types(gogo);
inserter->insert(this->constructor_temp_);
}
return this;
}
-// Final type determination.
-
-void
-Map_construction_expression::do_determine_type(Gogo* gogo, const Type_context*)
-{
- if (this->vals_ == NULL)
- return;
-
- Map_type* mt = this->type_->map_type();
- Type_context key_context(mt->key_type(), false);
- Type_context val_context(mt->val_type(), false);
- for (Expression_list::const_iterator pv = this->vals_->begin();
- pv != this->vals_->end();
- ++pv)
- {
- (*pv)->determine_type(gogo, &key_context);
- ++pv;
- (*pv)->determine_type(gogo, &val_context);
- }
-}
-
-// Check types.
-
-void
-Map_construction_expression::do_check_types(Gogo*)
-{
- if (this->vals_ == NULL)
- return;
-
- Map_type* mt = this->type_->map_type();
- int i = 0;
- Type* key_type = mt->key_type();
- Type* val_type = mt->val_type();
- for (Expression_list::const_iterator pv = this->vals_->begin();
- pv != this->vals_->end();
- ++pv, ++i)
- {
- if (!Type::are_assignable(key_type, (*pv)->type(), NULL))
- {
- go_error_at((*pv)->location(),
- "incompatible type for element %d key in map construction",
- i + 1);
- this->set_is_error();
- }
- ++pv;
- if (!Type::are_assignable(val_type, (*pv)->type(), NULL))
- {
- go_error_at((*pv)->location(),
- ("incompatible type for element %d value "
- "in map construction"),
- i + 1);
- this->set_is_error();
- }
- }
-}
-
// Copy.
Expression*
Expression* val_offset =
Expression::make_struct_field_offset(this->element_type_, valfield);
+ Gogo* gogo = context->gogo();
Expression* map_ctor =
- Runtime::make_call(context->gogo(), Runtime::CONSTRUCT_MAP, loc, 5,
+ Runtime::make_call(gogo, Runtime::CONSTRUCT_MAP, loc, 5,
descriptor, count, entry_size, val_offset, ventries);
+ map_ctor->determine_type_no_context(gogo);
return map_ctor->get_backend(context);
}
public:
Composite_literal_key_expression(const std::string& name, Location location)
: Parser_expression(EXPRESSION_COMPOSITE_LITERAL_KEY, location),
- name_(name)
+ name_(name), expr_(NULL)
{ }
const std::string&
{ return this->name_; }
protected:
+ Type*
+ do_type();
+
+ void
+ do_determine_type(Gogo*, const Type_context*);
+
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
private:
// The name.
std::string name_;
+ // After we look up the name, a corresponding expression.
+ Expression* expr_;
};
-// Lower a composite literal key. We will never get here for keys in
-// composite literals of struct types, because that is prevented by
-// Composite_literal_expression::do_traverse. So if we do get here,
-// this must be a regular name reference after all.
+// Determine the type of a composite literal key. We will never get
+// here for keys in composite literals of struct types, because they
+// will be handled by Composite_literal_expression::do_determine_type.
+// So if we get here, this must be a regular name reference after all.
-Expression*
-Composite_literal_key_expression::do_lower(Gogo* gogo, Named_object*,
- Statement_inserter*, int)
+void
+Composite_literal_key_expression::do_determine_type(
+ Gogo* gogo,
+ const Type_context* context)
{
+ if (this->expr_ != NULL)
+ {
+ // Already resolved.
+ return;
+ }
+
Named_object* no = gogo->lookup(this->name_, NULL);
if (no == NULL)
{
{
go_error_at(this->location(), "reference to undefined name %qs",
Gogo::message_name(this->name_).c_str());
- return Expression::make_error(this->location());
+ this->set_is_error();
+ return;
}
}
- return Expression::make_unknown_reference(no, this->location());
+
+ this->expr_ = Expression::make_unknown_reference(no, this->location());
+ this->expr_->determine_type(gogo, context);
+}
+
+Type*
+Composite_literal_key_expression::do_type()
+{
+ if (this->is_error_expression())
+ return Type::make_error_type();
+ go_assert(this->expr_ != NULL);
+ return this->expr_->type();
+}
+
+Expression*
+Composite_literal_key_expression::do_lower(Gogo*, Named_object*,
+ Statement_inserter*, int)
+{
+ if (this->is_error_expression())
+ return Expression::make_error(this->location());
+ go_assert(this->expr_ != NULL);
+ return this->expr_;
}
// Dump a composite literal key.
return TRAVERSE_CONTINUE;
}
-// Lower a generic composite literal into a specific version based on
-// the type.
-
-Expression*
-Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function,
- Statement_inserter* inserter, int)
+void
+Composite_literal_expression::do_determine_type(Gogo* gogo,
+ const Type_context*)
{
+ // Resolve the type if this composite literal is within another
+ // composite literal and has omitted the type. The DEPTH_ field
+ // tells us how deeply this one is embedded.
Type* type = this->type_;
-
for (int depth = 0; depth < this->depth_; ++depth)
{
type = type->deref();
if (type->array_type() != NULL)
type = type->array_type()->element_type();
else if (type->map_type() != NULL)
- {
- if (this->key_path_[depth])
- type = type->map_type()->key_type();
- else
- type = type->map_type()->val_type();
- }
+ {
+ if (this->key_path_[depth])
+ type = type->map_type()->key_type();
+ else
+ type = type->map_type()->val_type();
+ }
else
{
if (!type->is_error())
- go_error_at(this->location(),
- ("may only omit types within composite literals "
- "of slice, array, or map type"));
- return Expression::make_error(this->location());
+ this->report_error(_("may only omit types within composite "
+ "literals of slice, array, or map type"));
+ else
+ {
+ go_assert(saw_errors());
+ this->set_is_error();
+ }
+ return;
}
}
- Type *pt = type->points_to();
- bool is_pointer = false;
- if (pt != NULL)
+ this->type_ = type;
+ this->depth_ = 0;
+
+ type = type->deref();
+
+ if (this->vals_ == NULL || this->vals_->empty())
{
- is_pointer = true;
- type = pt;
+ // No value types to determine.
+ if (type->array_type() != NULL
+ && type->array_type()->length() != NULL
+ && type->array_type()->length()->is_nil_expression())
+ this->resolve_array_length(type);
+ return;
}
- Expression* ret;
- if (type->is_error())
- return Expression::make_error(this->location());
- else if (type->struct_type() != NULL)
- ret = this->lower_struct(gogo, type);
- else if (type->array_type() != NULL)
- ret = this->lower_array(type);
- else if (type->map_type() != NULL)
- ret = this->lower_map(gogo, function, inserter, type);
- else
+ if (type->struct_type() != NULL && this->has_keys_)
{
- go_error_at(this->location(),
- ("expected struct, slice, array, or map type "
- "for composite literal"));
- return Expression::make_error(this->location());
+ // Rewrite the value list by removing the struct field keys. We
+ // do this now rather than during lowering because handling
+ // struct keys is painful. We don't need to do this for
+ // slice/array/map literals, because it's easy to determine
+ // their types with or without the keys.
+ if (!this->resolve_struct_keys(gogo, type))
+ {
+ this->set_is_error();
+ delete this->vals_;
+ this->vals_ = NULL;
+ this->has_keys_ = false;
+ return;
+ }
}
- if (is_pointer)
- ret = Expression::make_heap_expression(ret, this->location());
-
- return ret;
-}
-
-// Lower a struct composite literal.
-
-Expression*
-Composite_literal_expression::lower_struct(Gogo* gogo, Type* type)
-{
- Location location = this->location();
- Struct_type* st = type->struct_type();
- if (this->vals_ == NULL || !this->has_keys_)
+ if (type->struct_type() != NULL)
{
- if (this->vals_ != NULL
- && !this->vals_->empty()
- && type->named_type() != NULL
- && type->named_type()->named_object()->package() != NULL)
+ const Struct_field_list* fields = type->struct_type()->fields();
+ Expression_list::const_iterator pv = this->vals_->begin();
+ for (Struct_field_list::const_iterator pf = fields->begin();
+ pf != fields->end();
+ ++pf, ++pv)
{
- for (Struct_field_list::const_iterator pf = st->fields()->begin();
- pf != st->fields()->end();
- ++pf)
+ if (pv == this->vals_->end())
+ return;
+ if (*pv != NULL)
{
- if (Gogo::is_hidden_name(pf->field_name())
- || pf->is_embedded_builtin(gogo))
- go_error_at(this->location(),
- "assignment of unexported field %qs in %qs literal",
- Gogo::message_name(pf->field_name()).c_str(),
- type->named_type()->message_name().c_str());
+ Type_context subcontext(pf->type(), false);
+ (*pv)->determine_type(gogo, &subcontext);
+ }
+ }
+ // Extra values are an error we will report in the check_types
+ // pass; we still want to determine the type to avoid knockon
+ // errors.
+ for (; pv != this->vals_->end(); ++pv)
+ (*pv)->determine_type_no_context(gogo);
+ }
+ else if (type->array_type() != NULL)
+ {
+ Array_type* at = type->array_type();
+ Type_context intcontext(Type::lookup_integer_type("int"), false);
+ Type_context subcontext(at->element_type(), false);
+ for (Expression_list::const_iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv)
+ {
+ if (this->has_keys_)
+ {
+ if (*pv != NULL)
+ (*pv)->determine_type(gogo, &intcontext);
+ ++pv;
+ if (pv == this->vals_->end())
+ break;
}
+
+ if (*pv != NULL)
+ (*pv)->determine_type(gogo, &subcontext);
+ }
+
+ if (at->length() != NULL && at->length()->is_nil_expression())
+ this->resolve_array_length(type);
+ }
+ else if (type->map_type() != NULL)
+ {
+ if (!this->has_keys_)
+ {
+ this->report_error(_("map composite literal must have keys"));
+ return;
}
- return new Struct_construction_expression(type, this->vals_, location);
+ Map_type* mt = type->map_type();
+ Type_context key_context(mt->key_type(), false);
+ Type_context val_context(mt->val_type(), false);
+ for (Expression_list::const_iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv)
+ {
+ if (*pv != NULL)
+ (*pv)->determine_type(gogo, &key_context);
+ ++pv;
+ if (*pv != NULL)
+ (*pv)->determine_type(gogo, &val_context);
+ }
+ }
+ else
+ {
+ // We'll report this as an error in the check_types pass.
+ // Determine types to avoid knockon errors.
+ for (Expression_list::const_iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv)
+ {
+ if (*pv != NULL)
+ (*pv)->determine_type_no_context(gogo);
+ }
}
+}
+
+Type*
+Composite_literal_expression::do_type()
+{
+ if (this->is_error_expression())
+ return Type::make_error_type();
+ go_assert(this->depth_ == 0);
+ return this->type_;
+}
+// Resolve the field keys of a struct composite literal.
+
+bool
+Composite_literal_expression::resolve_struct_keys(Gogo* gogo, Type* type)
+{
+ Struct_type* st = type->struct_type();
size_t field_count = st->field_count();
std::vector<Expression*> vals(field_count);
std::vector<unsigned long>* traverse_order = new(std::vector<unsigned long>);
if (name_expr == NULL)
{
go_error_at(val->location(),
- "mixture of field and value initializers");
- return Expression::make_error(location);
+ "mixture of field and value initializers");
+ return false;
}
bool bad_key = false;
// this incorrect lookup as a usage of the object's package.
no = name_expr->unknown_expression()->named_object();
if (no->package() != NULL
- && no->package() != type->named_type()->named_object()->package())
+ && (no->package()
+ != type->named_type()->named_object()->package()))
no->package()->forget_usage(name_expr);
}
break;
if (bad_key)
{
go_error_at(name_expr->location(), "expected struct field name");
- return Expression::make_error(location);
+ return false;
}
if (no != NULL)
if (sf == NULL)
{
go_error_at(name_expr->location(), "unknown field %qs in %qs",
- Gogo::message_name(name).c_str(),
- (type->named_type() != NULL
- ? type->named_type()->message_name().c_str()
- : "unnamed struct"));
- return Expression::make_error(location);
+ Gogo::message_name(name).c_str(),
+ (type->named_type() != NULL
+ ? type->named_type()->message_name().c_str()
+ : "unnamed struct"));
+ return false;
}
if (vals[index] != NULL)
{
go_error_at(name_expr->location(),
- "duplicate value for field %qs in %qs",
- Gogo::message_name(name).c_str(),
- (type->named_type() != NULL
- ? type->named_type()->message_name().c_str()
- : "unnamed struct"));
- return Expression::make_error(location);
+ "duplicate value for field %qs in %qs",
+ Gogo::message_name(name).c_str(),
+ (type->named_type() != NULL
+ ? type->named_type()->message_name().c_str()
+ : "unnamed struct"));
+ return false;
}
- if (type->named_type() != NULL
- && type->named_type()->named_object()->package() != NULL
- && (Gogo::is_hidden_name(sf->field_name())
- || sf->is_embedded_builtin(gogo)))
- go_error_at(name_expr->location(),
- "assignment of unexported field %qs in %qs literal",
- Gogo::message_name(sf->field_name()).c_str(),
- type->named_type()->message_name().c_str());
-
vals[index] = val;
traverse_order->push_back(static_cast<unsigned long>(index));
}
(type->named_type() != NULL
? type->named_type()->message_name().c_str()
: "unnamed struct"));
- return Expression::make_error(location);
+ return false;
}
Expression_list* list = new Expression_list;
for (size_t i = 0; i < field_count; ++i)
list->push_back(vals[i]);
+ this->vals_ = list;
+ this->traverse_order_ = traverse_order;
+ this->has_keys_ = false;
+
+ return true;
+}
+
+// Handle [...]{...}
+
+void
+Composite_literal_expression::resolve_array_length(Type* type)
+{
+ size_t size;
+ if (this->vals_ == NULL || this->vals_->empty())
+ size = 0;
+ else if (!this->has_keys_)
+ size = this->vals_->size();
+ else
+ {
+ unsigned long index = 0;
+ size = 0;
+ for (Expression_list::const_iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ pv += 2)
+ {
+ Expression* index_expr = *pv;
+ if (index_expr != NULL)
+ {
+ Numeric_constant nc;
+ unsigned long iv;
+ if (!index_expr->numeric_constant_value(&nc)
+ || nc.to_unsigned_long(&iv) != Numeric_constant::NC_UL_VALID)
+ {
+ // We will report an error when lowering.
+ break;
+ }
+ index = iv;
+ }
+
+ if (index >= size)
+ size = index + 1;
+
+ ++index;
+ }
+ }
+
+ Expression* elen = Expression::make_integer_ul(size, NULL, this->location());
+ this->type_ = Type::make_array_type(type->array_type()->element_type(),
+ elen);
+}
+
+void
+Composite_literal_expression::do_check_types(Gogo* gogo)
+{
+ if (this->is_error_expression() || this->type_->is_error())
+ {
+ go_assert(saw_errors());
+ return;
+ }
+ go_assert(this->depth_ == 0);
+
+ Type* type = this->type_->deref();
+ if (type->struct_type() != NULL)
+ {
+ go_assert(!this->has_keys_);
+ if (!Struct_construction_expression::check_value_types(gogo, type,
+ this->vals_,
+ this->location()))
+ this->set_is_error();
+ }
+ else if (type->array_type() != NULL)
+ {
+ Type* element_type = type->array_type()->element_type();
+ if (element_type->is_error())
+ {
+ go_assert(saw_errors());
+ return;
+ }
+ if (this->vals_ == NULL || this->vals_->empty())
+ return;
+ int i = 0;
+ for (Expression_list::const_iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv, ++i)
+ {
+ if (this->has_keys_)
+ {
+ // We check the key type in the lowering pass.
+ ++pv;
+ if (pv == this->vals_->end())
+ break;
+ }
+ if (*pv != NULL
+ && !Type::are_assignable(element_type, (*pv)->type(), NULL))
+ {
+ go_error_at((*pv)->location(),
+ ("incompatible type for element %d "
+ "in composite literal"),
+ i + 1);
+ this->set_is_error();
+ }
+ }
+ }
+ else if (type->map_type() != NULL)
+ {
+ if (this->vals_ == NULL || this->vals_->empty())
+ return;
+ if (!this->has_keys_)
+ {
+ this->report_error(_("map composite literal must have keys"));
+ return;
+ }
+ int i = 0;
+ Map_type* mt = type->map_type();
+ Type* key_type = mt->key_type();
+ Type* val_type = mt->val_type();
+ for (Expression_list::const_iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv, ++i)
+ {
+ if (*pv != NULL
+ && !Type::are_assignable(key_type, (*pv)->type(), NULL))
+ {
+ go_error_at((*pv)->location(),
+ ("incompatible type for element %d key "
+ "in map construction"),
+ i + 1);
+ this->set_is_error();
+ }
+ ++pv;
+ if (!Type::are_assignable(val_type, (*pv)->type(), NULL))
+ {
+ go_error_at((*pv)->location(),
+ ("incompatible type for element %d value "
+ "in map construction"),
+ i + 1);
+ this->set_is_error();
+ }
+ }
+ }
+ else
+ {
+ this->report_error(_("expected struct, slice, array, or map type "
+ "for composite literal"));
+ }
+}
+
+// Lower a generic composite literal into a specific version based on
+// the type.
+
+Expression*
+Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function,
+ Statement_inserter* inserter, int)
+{
+ if (this->is_error_expression() || this->type_->is_error())
+ return Expression::make_error(this->location());
+ go_assert(this->depth_ == 0);
+
+ Type* type = this->type_;
+ Type *pt = type->points_to();
+ bool is_pointer = false;
+ if (pt != NULL)
+ {
+ is_pointer = true;
+ type = pt;
+ }
+
+ Expression* ret;
+ if (type->is_error())
+ return Expression::make_error(this->location());
+ else if (type->struct_type() != NULL)
+ ret = this->lower_struct(type);
+ else if (type->array_type() != NULL)
+ ret = this->lower_array(type);
+ else if (type->map_type() != NULL)
+ ret = this->lower_map(gogo, function, inserter, type);
+ else
+ go_unreachable();
+
+ if (is_pointer)
+ ret = Expression::make_heap_expression(ret, this->location());
+
+ return ret;
+}
+
+// Lower a struct composite literal.
+
+Expression*
+Composite_literal_expression::lower_struct(Type* type)
+{
+ go_assert(!this->has_keys_);
Struct_construction_expression* ret =
- new Struct_construction_expression(type, list, location);
- ret->set_traverse_order(traverse_order);
+ new Struct_construction_expression(type, this->vals_, this->location());
+ ret->set_traverse_order(this->traverse_order_);
return ret;
}
Location location = this->location();
Array_type* at = type->array_type();
- if (at->length() != NULL && at->length()->is_nil_expression())
- {
- size_t size;
- if (vals == NULL)
- size = 0;
- else if (indexes != NULL)
- size = indexes->back() + 1;
- else
- {
- size = vals->size();
- Integer_type* it = Type::lookup_integer_type("int")->integer_type();
- if (sizeof(size) <= static_cast<size_t>(it->bits() * 8)
- && size >> (it->bits() - 1) != 0)
- {
- go_error_at(location, "too many elements in composite literal");
- return Expression::make_error(location);
- }
- }
-
- Expression* elen = Expression::make_integer_ul(size, NULL, location);
- at = Type::make_array_type(at->element_type(), elen);
- type = at;
- }
- else if (at->length() != NULL
- && !at->length()->is_error_expression()
- && this->vals_ != NULL)
+ if (at->length() != NULL
+ && !at->length()->is_error_expression()
+ && this->vals_ != NULL)
{
Numeric_constant nc;
unsigned long val;
bool saw_true = false;
if (this->vals_ != NULL)
{
- if (!this->has_keys_)
- {
- go_error_at(location, "map composite literal must have keys");
- return Expression::make_error(location);
- }
+ go_assert(this->has_keys_);
for (Expression_list::iterator p = this->vals_->begin();
p != this->vals_->end();
mit->second.push_back(*p);
}
}
- else if ((*p)->numeric_constant_value(&nval)) // Check numeric keys.
+ else if ((*p)->numeric_constant_value(&nval))
{
+ // Check numeric keys.
unsigned int h = nval.hash(0);
Unordered_map(unsigned int, std::vector<Expression*>)::iterator mit;
mit = nt.find(h);
recv_addr = Expression::make_unary(OPERATOR_AND, recv_addr, loc);
Expression* recv = Runtime::make_call(gogo, Runtime::CHANRECV1, loc, 2,
this->channel_, recv_addr);
- return Expression::make_compound(recv, recv_ref, loc)->get_backend(context);
+ Expression* ret = Expression::make_compound(recv, recv_ref, loc);
+ ret->determine_type_no_context(gogo);
+ return ret->get_backend(context);
}
// Export a receive expression.
{ return this->type_; }
void
- do_determine_type(Gogo*, const Type_context*)
- { go_unreachable(); }
+ do_determine_type(Gogo*, const Type_context*);
Expression*
do_copy()
return TRAVERSE_CONTINUE;
}
+void
+Interface_value_expression::do_determine_type(Gogo* gogo, const Type_context*)
+{
+ this->first_field_->determine_type_no_context(gogo);
+ this->obj_->determine_type_no_context(gogo);
+}
+
Bexpression*
Interface_value_expression::do_get_backend(Translate_context* context)
{
void
do_determine_type(Gogo*, const Type_context*)
- { go_unreachable(); }
+ { }
Expression*
do_copy()
// After lowering return statements have no expressions. The
// return expressions are assigned to result parameters.
ifb->advance(6);
- return Statement::make_return_statement(NULL, loc);
+ return Statement::make_return_statement(ifb->function(), NULL, loc);
}
else if (ifb->match_c_string("var $t"))
return Temporary_statement::do_import(ifb, loc);
return TRAVERSE_CONTINUE;
}
+void
+Variable_declaration_statement::do_determine_types(Gogo* gogo)
+{
+ this->var_->var_value()->determine_type(gogo);
+}
+
// Lower the variable's initialization expression.
Statement*
Bstatement*
Variable_declaration_statement::do_get_backend(Translate_context* context)
{
+ if (this->var_->is_redefinition())
+ return context->backend()->error_statement();
Bfunction* bfunction = context->function()->func_value()->get_decl();
Variable* var = this->var_->var_value();
Bvariable* bvar = this->var_->get_backend_variable(context->gogo(),
Expression::make_dereference(call, Expression::NIL_CHECK_NOT_NEEDED,
loc);
ref = Expression::make_temporary_reference(val_temp, loc);
- b->add_statement(Statement::make_assignment(indir, ref, loc));
+ Statement* s = Statement::make_assignment(indir, ref, loc);
+ s->determine_types(gogo);
+ b->add_statement(s);
return Statement::make_block_statement(b, loc);
}
this->rhs_->determine_type(gogo, &context);
}
-// Check types for an assignment.
+// Check types for an assignment from RHS to LHS. Returns true if the
+// assignment is OK.
-void
-Assignment_statement::do_check_types(Gogo*)
+bool
+Assignment_statement::check_assignment_types(Expression* lhs,
+ Type* rhs_type,
+ Location loc)
{
// The left hand side must be either addressable, a map index
// expression, or the blank identifier.
- if (!this->lhs_->is_addressable()
- && this->lhs_->map_index_expression() == NULL
- && !this->lhs_->is_sink_expression())
+ if (!lhs->is_addressable()
+ && !Index_expression::is_map_index(lhs)
+ && !lhs->is_sink_expression())
{
- if (!this->lhs_->type()->is_error())
- this->report_error(_("invalid left hand side of assignment"));
- return;
+ if (!lhs->type()->is_error())
+ go_error_at(lhs->location(), "invalid left hand side of assignment");
+ return false;
}
- Type* lhs_type = this->lhs_->type();
- Type* rhs_type = this->rhs_->type();
+ Type* lhs_type = lhs->type();
- // Invalid assignment of nil to the blank identifier.
- if (lhs_type->is_sink_type()
- && rhs_type->is_nil_type())
+ // Check for invalid assignment of nil to the blank identifier.
+ if (lhs_type->is_sink_type() && rhs_type->is_nil_type())
{
- this->report_error(_("use of untyped nil"));
- return;
+ go_error_at(loc, "use of untyped nil");
+ return false;
}
std::string reason;
if (!Type::are_assignable(lhs_type, rhs_type, &reason))
{
if (reason.empty())
- go_error_at(this->location(), "incompatible types in assignment");
+ go_error_at(loc, "incompatible types in assignment");
else
- go_error_at(this->location(), "incompatible types in assignment (%s)",
+ go_error_at(loc, "incompatible types in assignment (%s)",
reason.c_str());
- this->set_is_error();
+ return false;
}
- if (lhs_type->is_error() || rhs_type->is_error())
+ if (lhs_type->is_error_type() || rhs_type->is_error_type())
+ return false;
+
+ return true;
+}
+
+// Check types for an assignment.
+
+void
+Assignment_statement::do_check_types(Gogo*)
+{
+ if (!Assignment_statement::check_assignment_types(this->lhs_,
+ this->rhs_->type(),
+ this->location()))
this->set_is_error();
}
int
do_traverse(Traverse*);
+ void
+ do_determine_types(Gogo*);
+
+ void
+ do_check_types(Gogo*);
+
Statement*
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
do_dump_statement(Ast_dump_context*) const;
private:
+ Operator
+ get_binop();
+
// The operator (OPERATOR_PLUSEQ, etc.).
Operator op_;
// Left hand side.
return this->traverse_expression(traverse, &this->rhs_);
}
+void
+Assignment_operation_statement::do_determine_types(Gogo* gogo)
+{
+ this->lhs_->determine_type_no_context(gogo);
+ Type* rhs_context_type = this->lhs_->type();
+ if (rhs_context_type->is_sink_type())
+ rhs_context_type = NULL;
+ Type_context context(rhs_context_type, false);
+ this->rhs_->determine_type(gogo, &context);
+}
+
+// Get the binary operator from the assignment operator.
+
+Operator
+Assignment_operation_statement::get_binop()
+{
+ switch (this->op_)
+ {
+ case OPERATOR_PLUSEQ:
+ return OPERATOR_PLUS;
+ case OPERATOR_MINUSEQ:
+ return OPERATOR_MINUS;
+ case OPERATOR_OREQ:
+ return OPERATOR_OR;
+ case OPERATOR_XOREQ:
+ return OPERATOR_XOR;
+ case OPERATOR_MULTEQ:
+ return OPERATOR_MULT;
+ case OPERATOR_DIVEQ:
+ return OPERATOR_DIV;
+ case OPERATOR_MODEQ:
+ return OPERATOR_MOD;
+ case OPERATOR_LSHIFTEQ:
+ return OPERATOR_LSHIFT;
+ case OPERATOR_RSHIFTEQ:
+ return OPERATOR_RSHIFT;
+ case OPERATOR_ANDEQ:
+ return OPERATOR_AND;
+ case OPERATOR_BITCLEAREQ:
+ return OPERATOR_BITCLEAR;
+ default:
+ go_unreachable();
+ }
+}
+
+void
+Assignment_operation_statement::do_check_types(Gogo*)
+{
+ if (this->lhs_->is_sink_expression())
+ {
+ this->report_error(_("cannot use %<_%> as value"));
+ return;
+ }
+
+
+ Type* lhs_type = this->lhs_->type();
+ Type* rhs_type = this->rhs_->type();
+
+ if (!this->lhs_->is_addressable()
+ && !Index_expression::is_map_index(this->lhs_))
+ {
+ if (!lhs_type->is_error())
+ this->report_error(_("invalid left hand side of assignment"));
+ this->set_is_error();
+ return;
+ }
+
+ if (this->op_ != OPERATOR_LSHIFTEQ && this->op_ != OPERATOR_RSHIFTEQ)
+ {
+ if (!Type::are_compatible_for_binop(lhs_type, rhs_type))
+ {
+ this->report_error(_("incompatible type in binary expression"));
+ return;
+ }
+ if (!Binary_expression::check_operator_type(this->get_binop(), lhs_type,
+ rhs_type, this->location()))
+ {
+ this->set_is_error();
+ return;
+ }
+ if (this->op_ == OPERATOR_DIVEQ || this->op_ == OPERATOR_MODEQ)
+ {
+ Numeric_constant rconst;
+ unsigned long rval;
+ if (lhs_type->integer_type() != NULL
+ && this->rhs_->numeric_constant_value(&rconst)
+ && rconst.to_unsigned_long(&rval) == Numeric_constant::NC_UL_VALID
+ && rval == 0)
+ {
+ this->report_error(_("integer division by zero"));
+ return;
+ }
+ }
+ }
+ else
+ {
+ if (lhs_type->integer_type() == NULL)
+ {
+ this->report_error(_("shift of non-integer operand"));
+ return;
+ }
+
+ if (rhs_type->is_string_type()
+ || (!rhs_type->is_abstract()
+ && rhs_type->integer_type() == NULL))
+ {
+ this->report_error(_("shift count not integer"));
+ return;
+ }
+
+ Numeric_constant nc;
+ if (this->rhs_->numeric_constant_value(&nc))
+ {
+ mpz_t val;
+ if (!nc.to_int(&val))
+ {
+ this->report_error(_("shift count not integer"));
+ return;
+ }
+ if (mpz_sgn(val) < 0)
+ this->report_error(_("negative shift count"));
+ mpz_clear(val);
+ }
+ }
+}
+
// Lower an assignment operation statement to a regular assignment
// statement.
Statement*
-Assignment_operation_statement::do_lower(Gogo*, Named_object*,
+Assignment_operation_statement::do_lower(Gogo* gogo, Named_object*,
Block* enclosing, Statement_inserter*)
{
Location loc = this->location();
Expression* lval = this->lhs_->copy();
- Operator op;
- switch (this->op_)
- {
- case OPERATOR_PLUSEQ:
- op = OPERATOR_PLUS;
- break;
- case OPERATOR_MINUSEQ:
- op = OPERATOR_MINUS;
- break;
- case OPERATOR_OREQ:
- op = OPERATOR_OR;
- break;
- case OPERATOR_XOREQ:
- op = OPERATOR_XOR;
- break;
- case OPERATOR_MULTEQ:
- op = OPERATOR_MULT;
- break;
- case OPERATOR_DIVEQ:
- op = OPERATOR_DIV;
- break;
- case OPERATOR_MODEQ:
- op = OPERATOR_MOD;
- break;
- case OPERATOR_LSHIFTEQ:
- op = OPERATOR_LSHIFT;
- break;
- case OPERATOR_RSHIFTEQ:
- op = OPERATOR_RSHIFT;
- break;
- case OPERATOR_ANDEQ:
- op = OPERATOR_AND;
- break;
- case OPERATOR_BITCLEAREQ:
- op = OPERATOR_BITCLEAR;
- break;
- default:
- go_unreachable();
- }
-
- Expression* binop = Expression::make_binary(op, lval, this->rhs_, loc);
+ Expression* binop = Expression::make_binary(this->get_binop(), lval,
+ this->rhs_, loc);
Statement* s = Statement::make_assignment(this->lhs_, binop, loc);
+ s->determine_types(gogo);
if (b->statements()->empty())
{
delete b;
int
do_traverse(Traverse* traverse);
+ void
+ do_determine_types(Gogo*);
+
+ void
+ do_check_types(Gogo*);
+
Statement*
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
return this->traverse_expression_list(traverse, this->rhs_);
}
+void
+Tuple_assignment_statement::do_determine_types(Gogo* gogo)
+{
+ Expression_list::iterator pr = this->rhs_->begin();
+ for (Expression_list::iterator pl = this->lhs_->begin();
+ pl != this->lhs_->end();
+ ++pl, ++pr)
+ {
+ go_assert(pr != this->rhs_->end());
+ (*pl)->determine_type_no_context(gogo);
+ Type* rhs_context_type = (*pl)->type();
+ if (rhs_context_type->is_sink_type())
+ rhs_context_type = NULL;
+ Type_context context(rhs_context_type, false);
+ (*pr)->determine_type(gogo, &context);
+ }
+ go_assert(pr == this->rhs_->end());
+}
+
+void
+Tuple_assignment_statement::do_check_types(Gogo*)
+{
+ Expression_list::iterator pr = this->rhs_->begin();
+ for (Expression_list::iterator pl = this->lhs_->begin();
+ pl != this->lhs_->end();
+ ++pl, ++pr)
+ {
+ go_assert(pr != this->rhs_->end());
+ if (!Assignment_statement::check_assignment_types(*pl, (*pr)->type(),
+ this->location()))
+ this->set_is_error();
+ }
+ go_assert(pr == this->rhs_->end());
+}
+
// Lower a tuple assignment. We use temporary variables to split it
// up into a set of single assignments.
Statement*
-Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
- Statement_inserter*)
+Tuple_assignment_statement::do_lower(Gogo* gogo, Named_object*,
+ Block* enclosing, Statement_inserter*)
{
Location loc = this->location();
+ if (this->classification() == STATEMENT_ERROR)
+ return Statement::make_error_statement(loc);
+
Block* b = new Block(enclosing, loc);
// First move out any subexpressions on the left hand side. The
continue;
Expression* ref = Expression::make_temporary_reference(*ptemp, loc);
- b->add_statement(Statement::make_assignment(*plhs, ref, loc));
+ Statement* s = Statement::make_assignment(*plhs, ref, loc);
+ s->determine_types(gogo);
+ b->add_statement(s);
++ptemp;
}
go_assert(ptemp == temps.end() || saw_errors());
int
do_traverse(Traverse* traverse);
+ void
+ do_determine_types(Gogo*);
+
+ void
+ do_check_types(Gogo*);
+
Statement*
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
return this->traverse_expression(traverse, &this->map_index_);
}
+void
+Tuple_map_assignment_statement::do_determine_types(Gogo* gogo)
+{
+ this->val_->determine_type_no_context(gogo);
+ this->present_->determine_type_no_context(gogo);
+ this->map_index_->determine_type_no_context(gogo);
+}
+
+void
+Tuple_map_assignment_statement::do_check_types(Gogo*)
+{
+ Expression* map_expr = this->map_index_;
+ Map_type* map_type;
+ if (map_expr->map_index_expression() != NULL)
+ map_type = map_expr->map_index_expression()->get_map_type();
+ else if (map_expr->index_expression() != NULL)
+ map_type = map_expr->index_expression()->left()->type()->map_type();
+ else
+ {
+ this->report_error(_("expected map index on right hand side"));
+ return;
+ }
+ if (map_type == NULL || map_type->is_error())
+ {
+ go_assert(saw_errors());
+ this->set_is_error();
+ return;
+ }
+
+ if (!Assignment_statement::check_assignment_types(this->val_,
+ map_type->val_type(),
+ this->location()))
+ this->set_is_error();
+ if (!Assignment_statement::check_assignment_types(this->present_,
+ Type::make_boolean_type(),
+ this->location()))
+ this->set_is_error();
+}
+
// Lower a tuple map assignment.
Statement*
{
Location loc = this->location();
+ if (this->classification() == STATEMENT_ERROR)
+ return Statement::make_error_statement(loc);
+
Map_index_expression* map_index = this->map_index_->map_index_expression();
- if (map_index == NULL)
- {
- this->report_error(_("expected map index on right hand side"));
- return Statement::make_error_statement(loc);
- }
+ go_assert(map_index != NULL);
Map_type* map_type = map_index->get_map_type();
- if (map_type == NULL)
- return Statement::make_error_statement(loc);
+ go_assert(map_type != NULL);
// Avoid copy for string([]byte) conversions used in map keys.
// mapaccess doesn't keep the reference, so this is safe.
Expression* res = Expression::make_call_result(call, 0);
res = Expression::make_unsafe_cast(val_ptr_type, res, loc);
Statement* s = Statement::make_assignment(ref, res, loc);
+ s->determine_types(gogo);
b->add_statement(s);
ref = Expression::make_temporary_reference(present_temp, loc);
ref->set_is_lvalue();
res = Expression::make_call_result(call, 1);
s = Statement::make_assignment(ref, res, loc);
+ s->determine_types(gogo);
b->add_statement(s);
// val = *val__ptr_temp
Expression* ind =
Expression::make_dereference(ref, Expression::NIL_CHECK_NOT_NEEDED, loc);
s = Statement::make_assignment(this->val_, ind, loc);
+ s->determine_types(gogo);
b->add_statement(s);
// present = present_temp
ref = Expression::make_temporary_reference(present_temp, loc);
s = Statement::make_assignment(this->present_, ref, loc);
+ s->determine_types(gogo);
b->add_statement(s);
return Statement::make_block_statement(b, loc);
int
do_traverse(Traverse* traverse);
+ void
+ do_determine_types(Gogo*);
+
+ void
+ do_check_types(Gogo*);
+
Statement*
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
return this->traverse_expression(traverse, &this->channel_);
}
+void
+Tuple_receive_assignment_statement::do_determine_types(Gogo* gogo)
+{
+ this->val_->determine_type_no_context(gogo);
+ this->closed_->determine_type_no_context(gogo);
+ this->channel_->determine_type_no_context(gogo);
+}
+
+void
+Tuple_receive_assignment_statement::do_check_types(Gogo*)
+{
+ Channel_type* ct = this->channel_->type()->channel_type();
+ if (ct == NULL)
+ {
+ this->report_error(_("expected channel"));
+ return;
+ }
+ if (!ct->may_receive())
+ {
+ this->report_error(_("invalid receive on send-only channel"));
+ return;
+ }
+
+ if (!Assignment_statement::check_assignment_types(this->val_,
+ ct->element_type(),
+ this->location()))
+ this->set_is_error();
+ if (!Assignment_statement::check_assignment_types(this->closed_,
+ Type::make_boolean_type(),
+ this->location()))
+ this->set_is_error();
+}
+
// Lower to a function call.
Statement*
{
Location loc = this->location();
+ if (this->classification() == STATEMENT_ERROR)
+ return Statement::make_error_statement(loc);
+
Channel_type* channel_type = this->channel_->type()->channel_type();
- if (channel_type == NULL)
- {
- this->report_error(_("expected channel"));
- return Statement::make_error_statement(loc);
- }
- if (!channel_type->may_receive())
- {
- this->report_error(_("invalid receive on send-only channel"));
- return Statement::make_error_statement(loc);
- }
+ go_assert(channel_type != NULL && channel_type->may_receive());
Block* b = new Block(enclosing, loc);
ref = Expression::make_temporary_reference(closed_temp, loc);
ref->set_is_lvalue();
Statement* s = Statement::make_assignment(ref, call, loc);
+ s->determine_types(gogo);
b->add_statement(s);
// val = val_temp
ref = Expression::make_temporary_reference(val_temp, loc);
s = Statement::make_assignment(this->val_, ref, loc);
+ s->determine_types(gogo);
b->add_statement(s);
// closed = closed_temp
ref = Expression::make_temporary_reference(closed_temp, loc);
s = Statement::make_assignment(this->closed_, ref, loc);
+ s->determine_types(gogo);
b->add_statement(s);
return Statement::make_block_statement(b, loc);
int
do_traverse(Traverse*);
+ void
+ do_determine_types(Gogo*);
+
+ void
+ do_check_types(Gogo*);
+
Statement*
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
return this->traverse_expression(traverse, &this->expr_);
}
+void
+Tuple_type_guard_assignment_statement::do_determine_types(Gogo* gogo)
+{
+ this->val_->determine_type_no_context(gogo);
+ this->ok_->determine_type_no_context(gogo);
+ this->expr_->determine_type_no_context(gogo);
+}
+
+void
+Tuple_type_guard_assignment_statement::do_check_types(Gogo*)
+{
+ Type* expr_type = this->expr_->type();
+ if (expr_type->interface_type() == NULL)
+ {
+ if (!expr_type->is_error() && !this->type_->is_error())
+ this->report_error(_("type assertion only valid for interface types"));
+ return;
+ }
+
+ if (!Assignment_statement::check_assignment_types(this->val_, this->type_,
+ this->location()))
+ this->set_is_error();
+ if (!Assignment_statement::check_assignment_types(this->ok_,
+ Type::make_boolean_type(),
+ this->location()))
+ this->set_is_error();
+}
+
// Lower to a function call.
Statement*
{
Location loc = this->location();
+ if (this->classification() == STATEMENT_ERROR)
+ return Statement::make_error_statement(loc);
+
Type* expr_type = this->expr_->type();
- if (expr_type->interface_type() == NULL)
- {
- if (!expr_type->is_error() && !this->type_->is_error())
- this->report_error(_("type assertion only valid for interface types"));
- return Statement::make_error_statement(loc);
- }
+ go_assert(expr_type->interface_type() != NULL);
Block* b = new Block(enclosing, loc);
Expression* res = Expression::make_call_result(call, 0);
res = Expression::make_unsafe_cast(this->type_, res, loc);
Statement* s = Statement::make_assignment(this->val_, res, loc);
+ s->determine_types(gogo);
b->add_statement(s);
res = Expression::make_call_result(call, 1);
if (!this->ok_->type()->is_boolean_type())
res = Expression::make_cast(Type::lookup_bool_type(), res, loc);
s = Statement::make_assignment(this->ok_, res, loc);
+ s->determine_types(gogo);
b->add_statement(s);
}
Expression* ok_ref = Expression::make_temporary_reference(ok_temp, loc);
s = Statement::make_assignment(ok_ref, call, loc);
}
+ s->determine_types(gogo);
b->add_statement(s);
// val = val_temp
ref = Expression::make_temporary_reference(val_temp, loc);
s = Statement::make_assignment(this->val_, ref, loc);
+ s->determine_types(gogo);
b->add_statement(s);
// ok = ok_temp
{
ref = Expression::make_temporary_reference(ok_temp, loc);
s = Statement::make_assignment(this->ok_, ref, loc);
+ s->determine_types(gogo);
b->add_statement(s);
}
}
bool
Expression_statement::do_may_fall_through() const
{
+ // The builtin function panic does not return.
const Call_expression* call = this->expr_->call_expression();
- if (call != NULL)
- {
- const Expression* fn = call->fn();
- // panic is still an unknown named object.
- const Unknown_expression* ue = fn->unknown_expression();
- if (ue != NULL)
- {
- Named_object* no = ue->named_object();
-
- if (no->is_unknown())
- no = no->unknown_value()->real_named_object();
- if (no != NULL)
- {
- Function_type* fntype;
- if (no->is_function())
- fntype = no->func_value()->type();
- else if (no->is_function_declaration())
- fntype = no->func_declaration_value()->type();
- else
- fntype = NULL;
-
- // The builtin function panic does not return.
- if (fntype != NULL && fntype->is_builtin() && no->name() == "panic")
- return false;
- }
- }
- }
- return true;
+ if (call == NULL)
+ return true;
+ const Builtin_call_expression* bce = call->builtin_call_expression();
+ return bce == NULL || bce->code() != Builtin_call_expression::BUILTIN_PANIC;
}
// Export an expression statement.
do_traverse(Traverse* traverse)
{ return this->traverse_expression(traverse, &this->expr_); }
+ void
+ do_determine_types(Gogo*);
+
+ void
+ do_check_types(Gogo*);
+
Statement*
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
bool is_inc_;
};
+void
+Inc_dec_statement::do_determine_types(Gogo* gogo)
+{
+ this->expr_->determine_type_no_context(gogo);
+}
+
+void
+Inc_dec_statement::do_check_types(Gogo*)
+{
+ if (!this->expr_->is_addressable()
+ && !Index_expression::is_map_index(this->expr_))
+ {
+ if (!this->expr_->type()->is_error())
+ this->report_error(_("invalid left hand side of assignment"));
+ this->set_is_error();
+ return;
+ }
+ if (!this->expr_->type()->is_numeric_type())
+ {
+ this->report_error(_("increment or decrement of non-numeric type"));
+ return;
+ }
+}
+
// Lower to += or -=.
Statement*
Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*, Statement_inserter*)
{
Location loc = this->location();
- if (!this->expr_->type()->is_numeric_type())
- {
- this->report_error("increment or decrement of non-numeric type");
- return Statement::make_error_statement(loc);
- }
Expression* oexpr = Expression::make_integer_ul(1, this->expr_->type(), loc);
Operator op = this->is_inc_ ? OPERATOR_PLUSEQ : OPERATOR_MINUSEQ;
return Statement::make_assignment_operation(op, this->expr_, oexpr, loc);
Thunk_statement::do_check_types(Gogo*)
{
if (!this->call_->discarding_value())
- return;
+ {
+ this->set_is_error();
+ return;
+ }
Call_expression* ce = this->call_->call_expression();
if (ce == NULL)
{
if (!this->call_->is_error_expression())
this->report_error("expected call expression");
+ this->set_is_error();
return;
}
}
Statement* call_statement = Statement::make_statement(call, true);
+ call_statement->determine_types(gogo);
gogo->add_statement(call_statement);
// If this is a defer statement, the label comes immediately after
Expression_list* vals = new Expression_list();
vals->push_back(Expression::make_boolean(false, location));
- gogo->add_statement(Statement::make_return_statement(vals, location));
+ Statement* s = Statement::make_return_statement(function, vals,
+ location);
+ s->determine_types(gogo);
+ gogo->add_statement(s);
}
Block* b = gogo->finish_block(location);
Gogo* gogo = context->gogo();
Expression* call = Runtime::make_call(gogo, Runtime::GO, this->location(), 2,
fn, arg);
+ call->determine_type_no_context(gogo);
Bexpression* bcall = call->get_backend(context);
Bfunction* bfunction = context->function()->func_value()->get_decl();
return context->backend()->expression_statement(bfunction, bcall);
Expression* call;
if (this->on_stack_)
{
- if (context->gogo()->debug_optimization())
+ if (gogo->debug_optimization())
go_debug(loc, "stack allocated defer");
Type* defer_type = Defer_statement::defer_struct_type();
else
call = Runtime::make_call(gogo, Runtime::DEFERPROC, loc, 3,
ds, fn, arg);
+ call->determine_type_no_context(gogo);
Bexpression* bcall = call->get_backend(context);
Bfunction* bfunction = context->function()->func_value()->get_decl();
return context->backend()->expression_statement(bfunction, bcall);
// Class Return_statement.
-// Lower a return statement. If we are returning a function call
-// which returns multiple values which match the current function,
-// split up the call's results. If the return statement lists
-// explicit values, implement this statement by assigning the values
-// to the result variables and change this statement to a naked
-// return. This lets panic/recover work correctly.
-
-Statement*
-Return_statement::do_lower(Gogo* gogo, Named_object* function,
- Block* enclosing, Statement_inserter*)
+void
+Return_statement::do_determine_types(Gogo* gogo)
{
- if (this->is_lowered_)
- return this;
+ if (this->types_are_determined_)
+ return;
+ this->types_are_determined_ = true;
- Expression_list* vals = this->vals_;
- this->vals_ = NULL;
- this->is_lowered_ = true;
+ size_t vals_count = this->vals_ == NULL ? 0 : this->vals_->size();
+ if (vals_count == 0)
+ return;
- Location loc = this->location();
+ Function::Results* results =
+ this->function_->func_value()->result_variables();
+ size_t results_count = results == NULL ? 0 : results->size();
- size_t vals_count = vals == NULL ? 0 : vals->size();
- Function::Results* results = function->func_value()->result_variables();
+ // If the current function has multiple return values, and we are
+ // returning a single call expression, split up the call expression.
+ if (results_count > 1
+ && vals_count == 1
+ && this->vals_->front()->call_expression() != NULL)
+ {
+ Call_expression* call = this->vals_->front()->call_expression();
+ call->set_expected_result_count(results_count);
+ call->determine_type_no_context(gogo);
+ delete this->vals_;
+ this->vals_ = new Expression_list();
+ for (size_t i = 0; i < results_count; ++i)
+ this->vals_->push_back(Expression::make_call_result(call, i));
+ vals_count = results_count;
+ }
+
+ if (vals_count != results_count)
+ {
+ // This is an error which we will report later. Determine all
+ // types to avoid knockon errors.
+ for (Expression_list::const_iterator pe = this->vals_->begin();
+ pe != this->vals_->end();
+ ++pe)
+ (*pe)->determine_type_no_context(gogo);
+ return;
+ }
+
+ Expression_list::const_iterator pe = this->vals_->begin();
+ for (Function::Results::const_iterator pr = results->begin();
+ pr != results->end();
+ ++pr, ++pe)
+ {
+ Type* rvtype = (*pr)->result_var_value()->type();
+ Type_context context(rvtype, false);
+ (*pe)->determine_type(gogo, &context);
+ }
+}
+
+void
+Return_statement::do_check_types(Gogo*)
+{
+ size_t vals_count = this->vals_ == NULL ? 0 : this->vals_->size();
+ Function::Results* results =
+ this->function_->func_value()->result_variables();
size_t results_count = results == NULL ? 0 : results->size();
if (vals_count == 0)
{
- if (results_count > 0 && !function->func_value()->results_are_named())
- {
- this->report_error(_("not enough arguments to return"));
- return this;
- }
- return this;
+ if (results_count > 0
+ && !this->function_->func_value()->results_are_named())
+ this->report_error(_("not enough arguments to return"));
+ return;
}
if (results_count == 0)
{
this->report_error(_("return with value in function "
"with no return type"));
- return this;
- }
-
- // If the current function has multiple return values, and we are
- // returning a single call expression, split up the call expression.
- if (results_count > 1
- && vals->size() == 1
- && vals->front()->call_expression() != NULL)
- {
- Call_expression* call = vals->front()->call_expression();
- call->set_expected_result_count(results_count);
- delete vals;
- vals = new Expression_list;
- for (size_t i = 0; i < results_count; ++i)
- vals->push_back(Expression::make_call_result(call, i));
- vals_count = results_count;
+ delete this->vals_;
+ this->vals_ = NULL;
+ return;
}
if (vals_count < results_count)
{
this->report_error(_("not enough arguments to return"));
- return this;
+ return;
}
if (vals_count > results_count)
{
this->report_error(_("too many values in return statement"));
- return this;
+ return;
}
- Block* b = new Block(enclosing, loc);
-
- Expression_list* lhs = new Expression_list();
- Expression_list* rhs = new Expression_list();
-
- Expression_list::const_iterator pe = vals->begin();
+ Expression_list::const_iterator pe = this->vals_->begin();
int i = 1;
for (Function::Results::const_iterator pr = results->begin();
pr != results->end();
Named_object* rv = *pr;
Expression* e = *pe;
- // Check types now so that we give a good error message. The
- // result type is known. We determine the expression type
- // early.
-
- Type *rvtype = rv->result_var_value()->type();
- Type_context type_context(rvtype, false);
- e->determine_type(gogo, &type_context);
-
std::string reason;
- if (Type::are_assignable(rvtype, e->type(), &reason))
- {
- Expression* ve = Expression::make_var_reference(rv, e->location());
- lhs->push_back(ve);
- rhs->push_back(e);
- }
- else
+ if (!Type::are_assignable(rv->result_var_value()->type(), e->type(),
+ &reason))
{
if (reason.empty())
go_error_at(e->location(),
go_error_at(e->location(),
"incompatible type for return value %d (%s)",
i, reason.c_str());
+ this->set_is_error();
}
}
- go_assert(lhs->size() == rhs->size());
+}
+
+// Lower a return statement. If we are returning a function call
+// which returns multiple values which match the current function,
+// split up the call's results. If the return statement lists
+// explicit values, implement this statement by assigning the values
+// to the result variables and change this statement to a naked
+// return. This lets panic/recover work correctly.
+
+Statement*
+Return_statement::do_lower(Gogo* gogo, Named_object*,
+ Block* enclosing, Statement_inserter*)
+{
+ Location loc = this->location();
+
+ if (this->classification() == STATEMENT_ERROR)
+ return Statement::make_error_statement(loc);
+
+ if (this->is_lowered_)
+ return this;
+
+ Expression_list* vals = this->vals_;
+ this->vals_ = NULL;
+ this->is_lowered_ = true;
+
+ size_t vals_count = vals == NULL ? 0 : vals->size();
+
+ if (vals_count == 0)
+ return this;
+
+ Function::Results* results =
+ this->function_->func_value()->result_variables();
+ size_t results_count = results == NULL ? 0 : results->size();
+
+ go_assert(vals_count == results_count);
+
+ Block* b = new Block(enclosing, loc);
+
+ Expression_list* lhs = new Expression_list();
+ Expression_list* rhs = new Expression_list();
+
+ Expression_list::const_iterator pe = vals->begin();
+ for (Function::Results::const_iterator pr = results->begin();
+ pr != results->end();
+ ++pr, ++pe)
+ {
+ Named_object* rv = *pr;
+ Expression* e = *pe;
+ Expression* ve = Expression::make_var_reference(rv, e->location());
+ lhs->push_back(ve);
+ rhs->push_back(e);
+ }
- if (lhs->empty())
- ;
- else if (lhs->size() == 1)
+ if (lhs->size() == 1)
{
- b->add_statement(Statement::make_assignment(lhs->front(), rhs->front(),
- loc));
+ Statement* s = Statement::make_assignment(lhs->front(), rhs->front(),
+ loc);
+ s->determine_types(gogo);
+ b->add_statement(s);
delete lhs;
delete rhs;
}
else
- b->add_statement(Statement::make_tuple_assignment(lhs, rhs, loc));
+ {
+ Statement* s = Statement::make_tuple_assignment(lhs, rhs, loc);
+ s->determine_types(gogo);
+ b->add_statement(s);
+ }
b->add_statement(this);
// Make a return statement.
Return_statement*
-Statement::make_return_statement(Expression_list* vals,
+Statement::make_return_statement(Named_object* function, Expression_list* vals,
Location location)
{
- return new Return_statement(vals, location);
+ return new Return_statement(function, vals, location);
}
// Make a statement that returns the result of a call expression.
Statement*
-Statement::make_return_from_call(Call_expression* call, Location location)
+Statement::make_return_from_call(Named_object* function, Call_expression* call,
+ Location location)
{
size_t rc = call->result_count();
if (rc == 0)
for (size_t i = 0; i < rc; ++i)
vals->push_back(Expression::make_call_result(call, i));
}
- return Statement::make_return_statement(vals, location);
+ return Statement::make_return_statement(function, vals, location);
}
}
// test. We branch to FINISH_LABEL at the end of the statements.
void
-Case_clauses::Case_clause::lower(Block* b, Temporary_statement* val_temp,
+Case_clauses::Case_clause::lower(Gogo* gogo, Block* b,
+ Temporary_statement* val_temp,
Unnamed_label* start_label,
Unnamed_label* finish_label) const
{
// if !COND { goto NEXT_CASE_LABEL }
cond = Expression::make_unary(OPERATOR_NOT, cond, loc);
s = Statement::make_if_statement(cond, then_block, NULL, loc);
+ s->determine_types(gogo);
b->add_statement(s);
}
p != this->cases_->end();
++p)
{
- if (!Type::are_assignable(type, (*p)->type(), NULL)
- && !Type::are_assignable((*p)->type(), type, NULL))
+ std::string reason;
+ if (!Type::are_compatible_for_comparison(true, type, (*p)->type(),
+ &reason))
{
- go_error_at((*p)->location(),
- "type mismatch between switch value and case clause");
+ go_error_at(this->location_, "%s", reason.c_str());
return false;
}
}
// Lower case clauses for a nonconstant switch.
void
-Case_clauses::lower(Block* b, Temporary_statement* val_temp,
+Case_clauses::lower(Gogo* gogo, Block* b, Temporary_statement* val_temp,
Unnamed_label* break_label) const
{
// The default case.
}
if (!p->is_default())
- p->lower(b, val_temp, start_label, finish_label);
+ p->lower(gogo, b, val_temp, start_label, finish_label);
else
{
// We have to move the default case to the end, so that we
}
if (default_case != NULL)
- default_case->lower(b, val_temp, default_start_label,
+ default_case->lower(gogo, b, val_temp, default_start_label,
default_finish_label);
}
int
do_traverse(Traverse*);
- void
- do_determine_types(Gogo*);
-
- void
- do_check_types(Gogo*);
-
Bstatement*
do_get_backend(Translate_context*);
int
Constant_switch_statement::do_traverse(Traverse* traverse)
{
- if (this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- return this->clauses_->traverse(traverse);
-}
-
-// Determine types.
-
-void
-Constant_switch_statement::do_determine_types(Gogo* gogo)
-{
- this->val_->determine_type_no_context(gogo);
- this->clauses_->determine_types(gogo, this->val_->type());
-}
-
-// Check types.
-
-void
-Constant_switch_statement::do_check_types(Gogo*)
-{
- if (!this->clauses_->check_types(this->val_->type()))
- this->set_is_error();
+ if (this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ return this->clauses_->traverse(traverse);
}
// Convert to GENERIC.
return this->clauses_->traverse(traverse);
}
+void
+Switch_statement::do_determine_types(Gogo* gogo)
+{
+ if (this->val_ != NULL)
+ this->val_->determine_type_no_context(gogo);
+ this->clauses_->determine_types(gogo,
+ (this->val_ == NULL
+ ? NULL
+ : this->val_->type()));
+}
+
+void
+Switch_statement::do_check_types(Gogo*)
+{
+ if (this->val_ != NULL
+ && (this->val_->is_error_expression()
+ || this->val_->type()->is_error()))
+ return;
+
+ if (this->val_ != NULL
+ && !this->val_->type()->is_comparable()
+ && !Type::are_compatible_for_comparison(true, this->val_->type(),
+ Type::make_nil_type(), NULL))
+ {
+ go_error_at(this->val_->location(),
+ "cannot switch on value whose type may not be compared");
+ this->set_is_error();
+ return;
+ }
+
+ Type* type;
+ if (this->val_ != NULL)
+ type = this->val_->type();
+ else
+ type = Type::make_boolean_type();
+ if (!this->clauses_->check_types(type))
+ this->set_is_error();
+}
+
// Lower a Switch_statement to a Constant_switch_statement or a series
// of if statements.
Statement*
-Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+Switch_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
Statement_inserter*)
{
Location loc = this->location();
return new Constant_switch_statement(this->val_, this->clauses_,
this->break_label_, loc);
- if (this->val_ != NULL
- && !this->val_->type()->is_comparable()
- && !Type::are_compatible_for_comparison(true, this->val_->type(),
- Type::make_nil_type(), NULL))
- {
- go_error_at(this->val_->location(),
- "cannot switch on value whose type may not be compared");
- return Statement::make_error_statement(loc);
- }
-
Block* b = new Block(enclosing, loc);
if (this->clauses_->empty())
Temporary_statement* val_temp = Statement::make_temporary(type, val, loc);
b->add_statement(val_temp);
- this->clauses_->lower(b, val_temp, this->break_label());
+ this->clauses_->lower(gogo, b, val_temp, this->break_label());
Statement* s = Statement::make_unnamed_label_statement(this->break_label_);
b->add_statement(s);
return TRAVERSE_CONTINUE;
}
-// Lower one clause in a type switch. Add statements to the block B.
-// The type descriptor we are switching on is in DESCRIPTOR_TEMP.
-// BREAK_LABEL is the label at the end of the type switch.
-// *STMTS_LABEL, if not NULL, is a label to put at the start of the
-// statements.
-
void
-Type_case_clauses::Type_case_clause::lower(Gogo* gogo,
- Type* switch_val_type,
- Block* b,
- Temporary_statement* descriptor_temp,
- Unnamed_label* break_label,
- Unnamed_label** stmts_label) const
+Type_case_clauses::Type_case_clause::determine_types(Gogo* gogo)
{
- Location loc = this->location_;
+ if (this->statements_ != NULL)
+ this->statements_->determine_types(gogo);
+}
- Unnamed_label* next_case_label = NULL;
+bool
+Type_case_clauses::Type_case_clause::check_types(Type* switch_val_type)
+{
if (!this->is_default_)
{
Type* type = this->type_;
-
std::string reason;
if (switch_val_type->interface_type() != NULL
&& !type->is_nil_constant_as_type()
else
go_error_at(this->location_, "impossible type switch case (%s)",
reason.c_str());
+ return false;
}
+ }
+ return true;
+}
+
+// Lower one clause in a type switch. Add statements to the block B.
+// The type descriptor we are switching on is in DESCRIPTOR_TEMP.
+// BREAK_LABEL is the label at the end of the type switch.
+// *STMTS_LABEL, if not NULL, is a label to put at the start of the
+// statements.
+
+void
+Type_case_clauses::Type_case_clause::lower(Gogo* gogo,
+ Block* b,
+ Temporary_statement* descriptor_temp,
+ Unnamed_label* break_label,
+ Unnamed_label** stmts_label) const
+{
+ Location loc = this->location_;
+
+ Unnamed_label* next_case_label = NULL;
+ if (!this->is_default_)
+ {
+ Type* type = this->type_;
Expression* ref = Expression::make_temporary_reference(descriptor_temp,
loc);
Statement* s = Statement::make_goto_unnamed_statement(dest, loc);
then_block->add_statement(s);
s = Statement::make_if_statement(cond, then_block, NULL, loc);
+ s->determine_types(gogo);
b->add_statement(s);
}
return TRAVERSE_CONTINUE;
}
+void
+Type_case_clauses::determine_types(Gogo* gogo)
+{
+ for (Type_clauses::iterator p = this->clauses_.begin();
+ p != this->clauses_.end();
+ ++p)
+ p->determine_types(gogo);
+}
+
+bool
+Type_case_clauses::check_types(Type* switch_val_type)
+{
+ bool ret = true;
+ for (Type_clauses::iterator p = this->clauses_.begin();
+ p != this->clauses_.end();
+ ++p)
+ {
+ if (!p->check_types(switch_val_type))
+ ret = false;
+ }
+ return ret;
+}
+
// Check for duplicate types.
void
// BREAK_LABEL is the label at the end of the type switch.
void
-Type_case_clauses::lower(Gogo* gogo, Type* switch_val_type,
- Block* b,
+Type_case_clauses::lower(Gogo* gogo, Block* b,
Temporary_statement* descriptor_temp,
Unnamed_label* break_label) const
{
++p)
{
if (!p->is_default())
- p->lower(gogo, switch_val_type, b, descriptor_temp, break_label,
- &stmts_label);
+ p->lower(gogo, b, descriptor_temp, break_label, &stmts_label);
else
{
// We are generating a series of tests, which means that we
go_assert(stmts_label == NULL);
if (default_case != NULL)
- default_case->lower(gogo, switch_val_type, b, descriptor_temp, break_label,
- NULL);
+ default_case->lower(gogo, b, descriptor_temp, break_label, NULL);
}
// Return true if these clauses may fall through to the statements
return TRAVERSE_CONTINUE;
}
+void
+Type_switch_statement::do_determine_types(Gogo* gogo)
+{
+ this->expr_->determine_type_no_context(gogo);
+ this->clauses_->determine_types(gogo);
+}
+
+void
+Type_switch_statement::do_check_types(Gogo*)
+{
+ if (this->clauses_ != NULL)
+ this->clauses_->check_duplicates();
+
+ Type* expr_type = this->expr_->type();
+ if (expr_type->interface_type() == NULL)
+ {
+ if (!expr_type->is_error())
+ this->report_error(_("cannot type switch on non-interface value"));
+ this->set_is_error();
+ }
+
+ if (!this->clauses_->check_types(expr_type))
+ this->set_is_error();
+}
+
// Lower a type switch statement to a series of if statements. The gc
// compiler is able to generate a table in some cases. However, that
// does not work for us because we may have type descriptors in
{
const Location loc = this->location();
- if (this->clauses_ != NULL)
- this->clauses_->check_duplicates();
+ if (this->classification() == STATEMENT_ERROR)
+ return Statement::make_error_statement(loc);
Block* b = new Block(enclosing, loc);
- Type* val_type = this->expr_->type();
- if (val_type->interface_type() == NULL)
- {
- if (!val_type->is_error())
- this->report_error(_("cannot type switch on non-interface value"));
- return Statement::make_error_statement(loc);
- }
-
Temporary_statement* val_temp =
Statement::make_temporary(NULL, this->expr_, loc);
b->add_statement(val_temp);
Expression::make_temporary_reference(descriptor_temp, loc);
lhs->set_is_lvalue();
Statement* s = Statement::make_assignment(lhs, td, loc);
+ s->determine_types(gogo);
b->add_statement(s);
if (this->clauses_ != NULL)
- this->clauses_->lower(gogo, val_type, b, descriptor_temp, this->break_label());
+ this->clauses_->lower(gogo, b, descriptor_temp, this->break_label());
s = Statement::make_unnamed_label_statement(this->break_label_);
b->add_statement(s);
Channel_type* channel_type = this->channel_->type()->channel_type();
Type* element_type = channel_type->element_type();
- Expression* val = Expression::convert_for_assignment(context->gogo(),
+ Expression* val = Expression::convert_for_assignment(gogo,
element_type,
this->val_, loc);
Expression* call = Runtime::make_call(gogo, Runtime::CHANSEND, loc, 2,
this->channel_, val);
-
- context->gogo()->lower_expression(context->function(), NULL, &call);
+ call->determine_type_no_context(gogo);
+ gogo->lower_expression(context->function(), NULL, &call);
Bexpression* bcall = call->get_backend(context);
Bfunction* bfunction = context->function()->func_value()->get_decl();
Bstatement* s = context->backend()->expression_statement(bfunction, bcall);
loc);
if (this->is_send_)
- this->lower_send(b, scase, chanref);
+ this->lower_send(gogo, b, scase, chanref);
else
this->lower_recv(gogo, function, b, scase, chanref, recvok);
// Lower a send clause in a select statement.
void
-Select_clauses::Select_clause::lower_send(Block* b, Expression* scase,
+Select_clauses::Select_clause::lower_send(Gogo* gogo, Block* b,
+ Expression* scase,
Expression* chanref)
{
Location loc = this->location_;
Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type());
valaddr = Expression::make_cast(unsafe_pointer_type, valaddr, loc);
- this->set_case(b, scase, chanref, valaddr);
+ this->set_case(gogo, b, scase, chanref, valaddr);
}
// Lower a receive clause in a select statement.
Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type());
valaddr = Expression::make_cast(unsafe_pointer_type, valaddr, loc);
- this->set_case(b, scase, chanref, valaddr);
+ this->set_case(gogo, b, scase, chanref, valaddr);
// If the block of statements is executed, arrange for the received
// value to move from VAL to the place where the statements expect
{
init = new Block(b, loc);
valref = Expression::make_temporary_reference(val, loc);
- init->add_statement(Statement::make_assignment(this->val_, valref, loc));
+ Statement* s = Statement::make_assignment(this->val_, valref, loc);
+ s->determine_types(gogo);
+ init->add_statement(s);
}
if (this->closedvar_ != NULL)
if (init == NULL)
init = new Block(b, loc);
Expression* cref = Expression::make_temporary_reference(recvok, loc);
- init->add_statement(Statement::make_assignment(this->closed_, cref,
- loc));
+ Statement* s = Statement::make_assignment(this->closed_, cref, loc);
+ s->determine_types(gogo);
+ init->add_statement(s);
}
if (init != NULL)
// pass to the runtime function selectgo.
void
-Select_clauses::Select_clause::set_case(Block* b,
+Select_clauses::Select_clause::set_case(Gogo* gogo,
+ Block* b,
Expression* scase,
Expression* chanref,
Expression* elem)
Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type());
chanref = Expression::make_unsafe_cast(unsafe_pointer_type, chanref, loc);
Statement* s = Statement::make_assignment(ref, chanref, loc);
+ s->determine_types(gogo);
b->add_statement(s);
if (elem != NULL)
go_assert(scase_type->field(field_index)->is_field_name("elem"));
ref = Expression::make_field_reference(scase->copy(), field_index, loc);
s = Statement::make_assignment(ref, elem, loc);
+ s->determine_types(gogo);
b->add_statement(s);
}
}
void
Select_clauses::Select_clause::determine_types(Gogo* gogo)
{
- go_assert(this->is_lowered_);
+ if (this->channel_ != NULL)
+ this->channel_->determine_type_no_context(gogo);
+ if (this->val_ != NULL)
+ this->val_->determine_type_no_context(gogo);
+ if (this->closed_ != NULL)
+ this->closed_->determine_type_no_context(gogo);
+ if (this->var_ != NULL && this->var_->is_variable())
+ this->var_->var_value()->determine_type(gogo);
+ if (this->closedvar_ != NULL && this->closedvar_->is_variable())
+ this->closedvar_->var_value()->determine_type(gogo);
if (this->statements_ != NULL)
this->statements_->determine_types(gogo);
}
Gogo* gogo = context->gogo();
Expression* crash = Runtime::make_call(gogo, Runtime::UNREACHABLE,
location, 0);
+ crash->determine_type_no_context(gogo);
Bexpression* bcrash = crash->get_backend(context);
clauses[count] = context->backend()->expression_statement(bfunction, bcrash);
{
Expression* call = Runtime::make_call(gogo, Runtime::BLOCK, loc, 0);
Statement *s = Statement::make_statement(call, false);
+ s->determine_types(gogo);
b->add_statement(s);
this->is_lowered_ = true;
return Statement::make_block_statement(b, loc);
Expression* result = Expression::make_call_result(call, 0);
Expression* ref = Expression::make_temporary_reference(this->index_, loc);
Statement* s = Statement::make_assignment(ref, result, loc);
+ s->determine_types(gogo);
b->add_statement(s);
result = Expression::make_call_result(call, 1);
ref = Expression::make_temporary_reference(recvok, loc);
s = Statement::make_assignment(ref, result, loc);
+ s->determine_types(gogo);
b->add_statement(s);
this->is_lowered_ = true;
Block* bnil = new Block(b, loc);
Expression* call = Runtime::make_call(gogo, Runtime::BLOCK, loc, 0);
Statement* s = Statement::make_statement(call, false);
+ s->determine_types(gogo);
bnil->add_statement(s);
Statement* ifs = Statement::make_if_statement(cond, bnil, NULL, loc);
+ ifs->determine_types(gogo);
b->add_statement(ifs);
chanref = chanref->copy();
if (scase.is_send())
{
s = Statement::make_send_statement(chanref, scase.val(), cloc);
+ s->determine_types(gogo);
b->add_statement(s);
}
else
}
else
s = Statement::make_statement(recv, false);
+ s->determine_types(gogo);
b->add_statement(s);
}
else
}
s = Statement::make_tuple_receive_assignment(lhs, lhs2, chanref, cloc);
+ s->determine_types(gogo);
b->add_statement(s);
if (scase.var() != NULL)
Statement::make_temporary(Type::make_boolean_type(),
Expression::make_call_result(call, 0),
loc);
+ selected_temp->determine_types(gogo);
b->add_statement(selected_temp);
Temporary_statement* ok_temp =
Statement::make_temporary(Type::make_boolean_type(),
Expression::make_call_result(call, 1),
loc);
+ ok_temp->determine_types(gogo);
b->add_statement(ok_temp);
cond = Expression::make_temporary_reference(selected_temp, loc);
Statement* as = Statement::make_assignment(chancase.val(),
ref->copy(),
cloc);
+ as->determine_types(gogo);
bchan->add_statement(as);
}
else if (chancase.var() != NULL)
cloc);
Statement* as = Statement::make_assignment(chancase.closed(),
okref, cloc);
+ as->determine_types(gogo);
bchan->add_statement(as);
}
else if (chancase.closedvar() != NULL)
Statement* ifs =
Statement::make_if_statement(cond, bchan, defcase.statements(), loc);
+ ifs->determine_types(gogo);
b->add_statement(ifs);
Statement* label =
return this->statements_->traverse(traverse);
}
+void
+For_statement::do_determine_types(Gogo* gogo)
+{
+ if (this->init_ != NULL)
+ this->init_->determine_types(gogo);
+ if (this->cond_ != NULL)
+ this->cond_->determine_type_no_context(gogo);
+ if (this->post_ != NULL)
+ this->post_->determine_types(gogo);
+ this->statements_->determine_types(gogo);
+}
+
+void
+For_statement::do_check_types(Gogo*)
+{
+ if (this->cond_ != NULL)
+ {
+ Type* type = this->cond_->type();
+ if (type->is_error())
+ this->set_is_error();
+ else if (!type->is_boolean_type())
+ {
+ go_error_at(this->cond_->location(), "expected boolean expression");
+ this->set_is_error();
+ }
+ }
+}
+
// Lower a For_statement into if statements and gotos. Getting rid of
// complex statements make it easier to handle garbage collection.
Statement*
-For_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+For_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
Statement_inserter*)
{
- Statement* s;
Location loc = this->location();
+ if (this->classification() == STATEMENT_ERROR)
+ return Statement::make_error_statement(loc);
+
+ Statement* s;
Block* b = new Block(enclosing, this->location());
if (this->init_ != NULL)
{
then_block->add_statement(s);
s = Statement::make_if_statement(this->cond_, then_block, NULL, cond_loc);
+ s->determine_types(gogo);
b->add_statement(s);
}
return this->statements_->traverse(traverse);
}
-// Lower a for range statement. For simplicity we lower this into a
-// for statement, which will then be lowered in turn to goto
-// statements.
+void
+For_range_statement::do_determine_types(Gogo* gogo)
+{
+ if (this->index_var_ != NULL)
+ this->index_var_->determine_type_no_context(gogo);
+ if (this->value_var_ != NULL)
+ this->value_var_->determine_type_no_context(gogo);
+ this->range_->determine_type_no_context(gogo);
+ this->statements_->determine_types(gogo);
+}
-Statement*
-For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
- Statement_inserter*)
+void
+For_range_statement::do_check_types(Gogo*)
{
Type* range_type = this->range_->type();
+
+ Type* index_type;
+ Type* value_type = NULL;
+
if (range_type->points_to() != NULL
&& range_type->points_to()->array_type() != NULL
&& !range_type->points_to()->is_slice_type())
range_type = range_type->points_to();
- Type* index_type;
- Type* value_type = NULL;
if (range_type->array_type() != NULL)
{
index_type = Type::lookup_integer_type("int");
}
else if (range_type->channel_type() != NULL)
{
+ if (!range_type->channel_type()->may_receive())
+ this->report_error(_("invalid receive on send-only channel"));
index_type = range_type->channel_type()->element_type();
if (this->value_var_ != NULL)
{
if (!this->value_var_->type()->is_error())
this->report_error(_("too many variables for range clause "
"with channel"));
- return Statement::make_error_statement(this->location());
+ this->set_is_error();
+ return;
}
}
else
{
this->report_error(_("range clause must have "
"array, slice, string, map, or channel type"));
- return Statement::make_error_statement(this->location());
+ return;
+ }
+
+ if (this->index_var_ != NULL
+ && !Assignment_statement::check_assignment_types(this->index_var_,
+ index_type,
+ this->location()))
+ this->set_is_error();
+ if (this->value_var_ != NULL
+ && !Assignment_statement::check_assignment_types(this->value_var_,
+ value_type,
+ this->location()))
+ this->set_is_error();
+}
+
+// Lower a for range statement. For simplicity we lower this into a
+// for statement, which will then be lowered in turn to goto
+// statements.
+
+Statement*
+For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
+ Statement_inserter*)
+{
+ if (this->classification() == STATEMENT_ERROR)
+ return Statement::make_error_statement(this->location());
+
+ Type* range_type = this->range_->type();
+ if (range_type->points_to() != NULL
+ && range_type->points_to()->array_type() != NULL
+ && !range_type->points_to()->is_slice_type())
+ range_type = range_type->points_to();
+
+ Type* index_type;
+ Type* value_type = NULL;
+ if (range_type->array_type() != NULL)
+ {
+ index_type = Type::lookup_integer_type("int");
+ value_type = range_type->array_type()->element_type();
+ }
+ else if (range_type->is_string_type())
+ {
+ index_type = Type::lookup_integer_type("int");
+ value_type = Type::lookup_integer_type("rune");
}
+ else if (range_type->map_type() != NULL)
+ {
+ index_type = range_type->map_type()->key_type();
+ value_type = range_type->map_type()->val_type();
+ }
+ else if (range_type->channel_type() != NULL)
+ {
+ index_type = range_type->channel_type()->element_type();
+ go_assert(this->value_var_ == NULL);
+ }
+ else
+ go_unreachable();
// If there is only one iteration variable, and len(this->range_) is
// constant, then we do not evaluate the range variable. len(x) is
{
if (gogo->debug_optimization())
go_debug(loc, "map range clear");
+ clear->determine_types(gogo);
temp_block->add_statement(clear);
return Statement::make_block_statement(temp_block, loc);
}
{
if (gogo->debug_optimization())
go_debug(loc, "array range clear");
+ clear->determine_types(gogo);
temp_block->add_statement(clear);
return Statement::make_block_statement(temp_block, loc);
}
assign = Statement::make_tuple_assignment(lhs, rhs, loc);
}
+ assign->determine_types(gogo);
body->add_statement(assign);
}
For_statement* loop = Statement::make_for_statement(init, cond, post,
this->location());
loop->add_statements(body);
+ loop->determine_types(gogo);
loop->set_break_continue_labels(this->break_label_, this->continue_label_);
temp_block->add_statement(loop);
{
Expression* ref = this->make_range_ref(range_object, range_temp, loc);
range_temp = Statement::make_temporary(NULL, ref, loc);
+ range_temp->determine_types(gogo);
init->add_statement(range_temp);
len_arg = ref;
}
Expression* len_call = this->call_builtin(gogo, "len", len_arg, loc);
Temporary_statement* len_temp = Statement::make_temporary(index_temp->type(),
len_call, loc);
+ len_temp->determine_types(gogo);
init->add_statement(len_temp);
Expression* zexpr = Expression::make_integer_ul(0, NULL, loc);
Expression::make_temporary_reference(index_temp, loc);
tref->set_is_lvalue();
Statement* s = Statement::make_assignment(tref, zexpr, loc);
+ s->determine_types(gogo);
init->add_statement(s);
*pinit = init;
tref = Expression::make_temporary_reference(value_temp, loc);
tref->set_is_lvalue();
s = Statement::make_assignment(tref, index, loc);
+ s->determine_types(gogo);
iter_init->add_statement(s);
}
tref = Expression::make_temporary_reference(index_temp, loc);
tref->set_is_lvalue();
s = Statement::make_inc_statement(tref);
+ s->determine_types(gogo);
post->add_statement(s);
*ppost = post;
}
Expression* ref = this->make_range_ref(range_object, range_temp, loc);
Temporary_statement* for_temp = Statement::make_temporary(NULL, ref, loc);
+ for_temp->determine_types(gogo);
init->add_statement(for_temp);
ref = Expression::make_temporary_reference(for_temp, loc);
Expression* len_call = this->call_builtin(gogo, "len", ref, loc);
Temporary_statement* len_temp = Statement::make_temporary(index_temp->type(),
len_call, loc);
+ len_temp->determine_types(gogo);
init->add_statement(len_temp);
Expression* zexpr = Expression::make_integer_ul(0, NULL, loc);
Expression::make_temporary_reference(index_temp, loc);
tref->set_is_lvalue();
Statement* s = Statement::make_assignment(tref, zexpr, loc);
+ s->determine_types(gogo);
init->add_statement(s);
*pinit = init;
tref = Expression::make_temporary_reference(value_temp, loc);
tref->set_is_lvalue();
s = Statement::make_assignment(tref, index, loc);
+ s->determine_types(gogo);
iter_init->add_statement(s);
}
tref = Expression::make_temporary_reference(index_temp, loc);
tref->set_is_lvalue();
s = Statement::make_inc_statement(tref);
+ s->determine_types(gogo);
post->add_statement(s);
*ppost = post;
}
Call_expression* call = this->call_builtin(gogo, "len", ref, loc);
Temporary_statement* len_temp =
Statement::make_temporary(index_temp->type(), call, loc);
+ len_temp->determine_types(gogo);
init->add_statement(len_temp);
Temporary_statement* next_index_temp =
index_ref->set_is_lvalue();
Expression* zexpr = Expression::make_integer_ul(0, index_temp->type(), loc);
Statement* s = Statement::make_assignment(index_ref, zexpr, loc);
+ s->determine_types(gogo);
init->add_statement(s);
Type* rune_type;
Expression::make_temporary_reference(value_temp, loc);
value_ref->set_is_lvalue();
s = Statement::make_assignment(value_ref, ref, loc);
+ s->determine_types(gogo);
iter_init->add_statement(s);
value_ref = Expression::make_temporary_reference(value_temp, loc);
Expression* sum = Expression::make_binary(OPERATOR_PLUS, index_ref, one,
loc);
s = Statement::make_assignment(lhs, sum, loc);
+ s->determine_types(gogo);
then_block->add_statement(s);
Block* else_block = new Block(iter_init, loc);
value_ref->set_is_lvalue();
Expression* res = Expression::make_call_result(call, 0);
s = Statement::make_assignment(value_ref, res, loc);
+ s->determine_types(gogo);
else_block->add_statement(s);
lhs = Expression::make_temporary_reference(next_index_temp, loc);
lhs->set_is_lvalue();
res = Expression::make_call_result(call, 1);
s = Statement::make_assignment(lhs, res, loc);
+ s->determine_types(gogo);
else_block->add_statement(s);
s = Statement::make_if_statement(cond, then_block, else_block, loc);
+ s->determine_types(gogo);
iter_init->add_statement(s);
*piter_init = iter_init;
index_ref->set_is_lvalue();
ref = Expression::make_temporary_reference(next_index_temp, loc);
s = Statement::make_assignment(index_ref, ref, loc);
+ s->determine_types(gogo);
post->add_statement(s);
*ppost = post;
Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc);
Expression* call = Runtime::make_call(gogo, Runtime::MAPITERINIT, loc, 3,
p1, p2, p3);
- init->add_statement(Statement::make_statement(call, true));
+ Statement* s = Statement::make_statement(call, true);
+ s->determine_types(gogo);
+ init->add_statement(s);
*pinit = init;
rhs = Expression::make_dereference(ref, Expression::NIL_CHECK_NOT_NEEDED,
loc);
Statement* set = Statement::make_assignment(lhs, rhs, loc);
+ set->determine_types(gogo);
iter_init->add_statement(set);
if (value_temp != NULL)
rhs = Expression::make_dereference(rhs, Expression::NIL_CHECK_NOT_NEEDED,
loc);
set = Statement::make_assignment(lhs, rhs, loc);
+ set->determine_types(gogo);
iter_init->add_statement(set);
}
ref = Expression::make_temporary_reference(hiter, loc);
p1 = Expression::make_unary(OPERATOR_AND, ref, loc);
call = Runtime::make_call(gogo, Runtime::MAPITERNEXT, loc, 1, p1);
- post->add_statement(Statement::make_statement(call, true));
+ s = Statement::make_statement(call, true);
+ s->determine_types(gogo);
+ post->add_statement(s);
*ppost = post;
}
// Lower a for range over a channel.
void
-For_range_statement::lower_range_channel(Gogo*,
+For_range_statement::lower_range_channel(Gogo* gogo,
Block*,
Block* body_block,
Named_object* range_object,
oref->set_is_lvalue();
Statement* s = Statement::make_tuple_receive_assignment(iref, oref, cref,
loc);
+ s->determine_types(gogo);
iter_init->add_statement(s);
Block* then_block = new Block(iter_init, loc);
oref = Expression::make_temporary_reference(ok_temp, loc);
Expression* cond = Expression::make_unary(OPERATOR_NOT, oref, loc);
s = Statement::make_if_statement(cond, then_block, NULL, loc);
+ s->determine_types(gogo);
iter_init->add_statement(s);
*piter_init = iter_init;
Expression* e1 = Expression::make_type_descriptor(map_type, loc);
Expression* e2 = this->make_range_ref(range_object, range_temp, loc);
call = Runtime::make_call(gogo, Runtime::MAPCLEAR, loc, 2, e1, e2);
- return Statement::make_statement(call, true);
+ Statement* s = Statement::make_statement(call, true);
+ s->determine_types(gogo);
+ return s;
}
// Match
ref = this->make_range_ref(range_object, range_temp, loc);
Expression* len = this->call_builtin(gogo, "len", ref, loc);
Temporary_statement* tslen = Statement::make_temporary(NULL, len, loc);
+ tslen->determine_types(gogo);
temp_block->add_statement(tslen);
Expression* zero = Expression::make_integer_ul(0, this->index_var_->type(), loc);
elem->array_index_expression()->set_needs_bounds_check(false);
Expression* e1 = Expression::make_unary(OPERATOR_AND, elem, loc);
Temporary_statement* ts1 = Statement::make_temporary(NULL, e1, loc);
+ ts1->determine_types(gogo);
b->add_statement(ts1);
len = Expression::make_temporary_reference(tslen, loc);
Expression* sz = Expression::make_integer_int64(elme_sz, len->type(), loc);
Expression* e2 = Expression::make_binary(OPERATOR_MULT, len, sz, loc);
Temporary_statement* ts2 = Statement::make_temporary(NULL, e2, loc);
+ ts2->determine_types(gogo);
b->add_statement(ts2);
Expression* ptr_arg = Expression::make_temporary_reference(ts1, loc);
zero32, sz_arg);
}
Statement* cs3 = Statement::make_statement(call, true);
+ cs3->determine_types(gogo);
b->add_statement(cs3);
len = Expression::make_temporary_reference(tslen, loc);
Expression* rhs = Expression::make_binary(OPERATOR_MINUS, len, one, loc);
Expression* lhs = this->index_var_->copy();
Statement* as4 = Statement::make_assignment(lhs, rhs, loc);
+ as4->determine_types(gogo);
b->add_statement(as4);
len = Expression::make_temporary_reference(tslen, loc);
zero = zero->copy();
Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, len, zero, loc);
- return Statement::make_if_statement(cond, b, NULL, loc);
+ Statement* ret = Statement::make_if_statement(cond, b, NULL, loc);
+ ret->determine_types(gogo);
+ return ret;
}
// Return the break LABEL_EXPR.