diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index cb5c45c2eb19..39baf9d1c156 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -2285,6 +2285,32 @@ Expression::make_complex(const mpfr_t* real, const mpfr_t* imag, Type* type, return new Complex_expression(real, imag, type, location); } +// Find a named object in an expression. + +class Find_named_object : public Traverse +{ + public: + Find_named_object(Named_object* no) + : Traverse(traverse_expressions), + no_(no), found_(false) + { } + + // Whether we found the object. + bool + found() const + { return this->found_; } + + protected: + int + expression(Expression**); + + private: + // The object we are looking for. + Named_object* no_; + // Whether we found it. + bool found_; +}; + // A reference to a const in an expression. class Const_expression : public Expression @@ -2295,6 +2321,10 @@ class Const_expression : public Expression constant_(constant), type_(NULL), seen_(false) { } + Named_object* + named_object() + { return this->constant_; } + const std::string& name() const { return this->constant_->name(); } @@ -2565,6 +2595,19 @@ Const_expression::do_determine_type(const Type_context* context) void Const_expression::do_check_types(Gogo*) { + if (this->type_ != NULL && this->type_->is_error_type()) + return; + + Expression* init = this->constant_->const_value()->expr(); + Find_named_object find_named_object(this->constant_); + Expression::traverse(&init, &find_named_object); + if (find_named_object.found()) + { + this->report_error(_("constant refers to itself")); + this->type_ = Type::make_error_type(); + return; + } + if (this->type_ == NULL || this->type_->is_abstract()) return; @@ -2682,6 +2725,32 @@ Expression::make_const_reference(Named_object* constant, return new Const_expression(constant, location); } +// Find a named object in an expression. + +int +Find_named_object::expression(Expression** pexpr) +{ + switch ((*pexpr)->classification()) + { + case Expression::EXPRESSION_CONST_REFERENCE: + if (static_cast(*pexpr)->named_object() == this->no_) + break; + return TRAVERSE_CONTINUE; + case Expression::EXPRESSION_VAR_REFERENCE: + if ((*pexpr)->var_expression()->named_object() == this->no_) + break; + return TRAVERSE_CONTINUE; + case Expression::EXPRESSION_FUNC_REFERENCE: + if ((*pexpr)->func_expression()->named_object() == this->no_) + break; + return TRAVERSE_CONTINUE; + default: + return TRAVERSE_CONTINUE; + } + this->found_ = true; + return TRAVERSE_EXIT; +} + // The nil value. class Nil_expression : public Expression diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 29462998836d..d8d704d956e9 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -3056,7 +3056,7 @@ Variable::Variable(Type* type, Expression* init, bool is_global, : type_(type), init_(init), preinit_(NULL), location_(location), is_global_(is_global), is_parameter_(is_parameter), is_receiver_(is_receiver), is_varargs_parameter_(false), - is_address_taken_(false), init_is_lowered_(false), + is_address_taken_(false), seen_(false), init_is_lowered_(false), type_from_init_tuple_(false), type_from_range_index_(false), type_from_range_value_(false), type_from_chan_element_(false), is_type_switch_var_(false) @@ -3090,7 +3090,18 @@ Variable::lower_init_expression(Gogo* gogo, Named_object* function) { if (this->init_ != NULL && !this->init_is_lowered_) { + if (this->seen_) + { + // We will give an error elsewhere, this is just to prevent + // an infinite loop. + return; + } + this->seen_ = true; + gogo->lower_expression(function, &this->init_); + + this->seen_ = false; + this->init_is_lowered_ = true; } } @@ -3209,7 +3220,7 @@ Variable::type_from_chan_element(Expression* expr, bool report_error) const // with type determination, then this should be unnecessary. Type* -Variable::type() const +Variable::type() { // A variable in a type switch with a nil case will have the wrong // type here. This gets fixed up in determine_type, below. @@ -3224,14 +3235,26 @@ Variable::type() const type = NULL; } + if (this->seen_) + { + if (this->type_ == NULL || !this->type_->is_error_type()) + { + error_at(this->location_, "variable initializer refers to itself"); + this->type_ = Type::make_error_type(); + } + return this->type_; + } + + this->seen_ = true; + if (type != NULL) - return type; + ; else if (this->type_from_init_tuple_) - return this->type_from_tuple(init, false); + type = this->type_from_tuple(init, false); else if (this->type_from_range_index_ || this->type_from_range_value_) - return this->type_from_range(init, this->type_from_range_index_, false); + type = this->type_from_range(init, this->type_from_range_index_, false); else if (this->type_from_chan_element_) - return this->type_from_chan_element(init, false); + type = this->type_from_chan_element(init, false); else { gcc_assert(init != NULL); @@ -3244,9 +3267,21 @@ Variable::type() const if (type->is_void_type()) type = Type::make_error_type(); - - return type; } + + this->seen_ = false; + + return type; +} + +// Fetch the type from a const pointer, in which case it should have +// been set already. + +Type* +Variable::type() const +{ + gcc_assert(this->type_ != NULL); + return this->type_; } // Set the type if necessary. diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 49f1be514685..552a64344857 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -1038,6 +1038,9 @@ class Variable bool is_receiver, source_location); // Get the type of the variable. + Type* + type(); + Type* type() const; @@ -1258,6 +1261,8 @@ class Variable bool is_varargs_parameter_ : 1; // Whether something takes the address of this variable. bool is_address_taken_ : 1; + // True if we have seen this variable in a traversal. + bool seen_ : 1; // True if we have lowered the initialization expression. bool init_is_lowered_ : 1; // True if init is a tuple used to set the type.