do_is_constant() const
{ return true; }
- bool
- do_is_immutable() const
- { return true; }
-
bool
do_numeric_constant_value(Numeric_constant* nc) const
{
{ return TRAVERSE_CONTINUE; }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
Type*
{ return true; }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
Type*
{ return true; }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
bool
{ return true; }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
bool
{ return true; }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
bool
{ return true; }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
bool
{ return true; }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
Type*
return true;
}
-// Return whether a type conversion is immutable.
+// Return whether a type conversion can be used in a constant
+// initializer.
bool
-Type_conversion_expression::do_is_immutable() const
+Type_conversion_expression::do_is_static_initializer() const
{
Type* type = this->type_;
Type* expr_type = this->expr_->type();
|| expr_type->interface_type() != NULL)
return false;
- if (!this->expr_->is_immutable())
+ if (!this->expr_->is_static_initializer())
return false;
if (Type::are_identical(type, expr_type, false, NULL))
return TRAVERSE_CONTINUE;
}
-// Return whether an unsafe type conversion is immutable.
+// Return whether an unsafe type conversion can be used as a constant
+// initializer.
bool
-Unsafe_type_conversion_expression::do_is_immutable() const
+Unsafe_type_conversion_expression::do_is_static_initializer() const
{
Type* type = this->type_;
Type* expr_type = this->expr_->type();
|| expr_type->interface_type() != NULL)
return false;
- if (!this->expr_->is_immutable())
+ if (!this->expr_->is_static_initializer())
return false;
if (Type::are_convertible(type, expr_type, NULL))
return this->expr_->is_constant();
}
+// Return whether a unary expression can be used as a constant
+// initializer.
+
+bool
+Unary_expression::do_is_static_initializer() const
+{
+ if (this->op_ == OPERATOR_MULT)
+ return false;
+ else if (this->op_ == OPERATOR_AND)
+ {
+ // The address of a global variable can used as a static
+ // initializer.
+ 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();
+ }
+
+ // The address of a composite literal can be used as a static
+ // initializer if the composite literal is itself usable as a
+ // static initializer.
+ if (this->expr_->is_composite_literal()
+ && this->expr_->is_static_initializer())
+ return true;
+
+ // The address of a string constant can be used as a static
+ // initializer. This can not be written in Go itself but this
+ // is used when building a type descriptor.
+ if (this->expr_->string_expression() != NULL)
+ return true;
+
+ return false;
+ }
+ else
+ return this->expr_->is_static_initializer();
+}
+
// Apply unary opcode OP to UNC, setting NC. Return true if this
// could be done, false if not. Issue errors for overflow.
// constructor will not do what the programmer expects.
go_assert(!this->expr_->is_composite_literal()
- || this->expr_->is_immutable());
+ || this->expr_->is_static_initializer());
if (this->expr_->classification() == EXPRESSION_UNARY)
{
Unary_expression* ue =
// initialize the value once, so we can use this directly
// rather than copying it. In that case we can't make it
// read-only, because the program is permitted to change it.
- copy_to_heap = (at->element_type()->has_pointer()
- && !context->is_const());
+ copy_to_heap = context->function() != NULL;
}
Bvariable* implicit =
gogo->backend()->implicit_variable(buf, btype, true, copy_to_heap,
bexpr = gogo->backend()->var_expression(implicit, loc);
}
else if ((this->expr_->is_composite_literal()
- || this->expr_->string_expression() != NULL)
- && this->expr_->is_immutable())
+ || this->expr_->string_expression() != NULL)
+ && this->expr_->is_static_initializer())
{
// Build a decl for a constant constructor.
snprintf(buf, sizeof buf, "C%u", counter);
return Expression::traverse(&this->right_, traverse);
}
+// Return whether this expression may be used as a static initializer.
+
+bool
+Binary_expression::do_is_static_initializer() const
+{
+ if (!this->left_->is_static_initializer()
+ || !this->right_->is_static_initializer())
+ return false;
+
+ // Addresses can be static initializers, but we can't implement
+ // arbitray binary expressions of them.
+ Unary_expression* lu = this->left_->unary_expression();
+ Unary_expression* ru = this->right_->unary_expression();
+ if (lu != NULL && lu->op() == OPERATOR_AND)
+ {
+ if (ru != NULL && ru->op() == OPERATOR_AND)
+ return this->op_ == OPERATOR_MINUS;
+ else
+ return this->op_ == OPERATOR_PLUS || this->op_ == OPERATOR_MINUS;
+ }
+ else if (ru != NULL && ru->op() == OPERATOR_AND)
+ return this->op_ == OPERATOR_PLUS || this->op_ == OPERATOR_MINUS;
+
+ // Other cases should resolve in the backend.
+ return true;
+}
+
// Return the type to use for a binary operation on operands of
// LEFT_TYPE and RIGHT_TYPE. These are the types of constants and as
// such may be NULL or abstract.
}
bool
-String_concat_expression::do_is_immutable() const
+String_concat_expression::do_is_static_initializer() const
{
for (Expression_list::const_iterator pe = this->exprs_->begin();
pe != this->exprs_->end();
++pe)
{
- if (!(*pe)->is_immutable())
+ if (!(*pe)->is_static_initializer())
return false;
}
return true;
return true;
}
-// Return whether this struct is immutable.
+// Return whether this struct can be used as a constant initializer.
bool
-Struct_construction_expression::do_is_immutable() const
+Struct_construction_expression::do_is_static_initializer() const
{
if (this->vals() == NULL)
return true;
pv != this->vals()->end();
++pv)
{
- if (*pv != NULL && !(*pv)->is_immutable())
+ if (*pv != NULL && !(*pv)->is_static_initializer())
return false;
}
return true;
return true;
}
-// Return whether this is an immutable array initializer.
+// Return whether this can be used a constant initializer.
bool
-Array_construction_expression::do_is_immutable() const
+Array_construction_expression::do_is_static_initializer() const
{
if (this->vals() == NULL)
return true;
pv != this->vals()->end();
++pv)
{
- if (*pv != NULL && !(*pv)->is_immutable())
+ if (*pv != NULL && !(*pv)->is_static_initializer())
return false;
}
return true;
}
Location loc = this->location();
- Array_type* array_type = this->type()->array_type();
- Type* element_type = array_type->element_type();
- bool is_constant_initializer = this->array_val_->is_immutable();
+ bool is_static_initializer = this->array_val_->is_static_initializer();
// We have to copy the initial values into heap memory if we are in
- // a function or if the values are not constants. We also have to
- // copy them if they may contain pointers in a non-constant context,
- // as otherwise the garbage collector won't see them.
- bool copy_to_heap = (context->function() != NULL
- || !is_constant_initializer
- || (element_type->has_pointer()
- && !context->is_const()));
+ // a function or if the values are not constants.
+ bool copy_to_heap = context->function() != NULL || !is_static_initializer;
Expression* space;
{ return Type::make_type_descriptor_ptr_type(); }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
void
{ return Type::lookup_integer_type("uintptr"); }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
void
protected:
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
Type*
do_type();
bool
- is_immutable() const
+ do_is_static_initializer() const
{ return true; }
void
protected:
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
Type*
is_constant() const
{ return this->do_is_constant(); }
- // Return whether this is an immutable expression.
- bool
- is_immutable() const
- { return this->do_is_immutable(); }
+ // Return whether this expression can be used as a static
+ // initializer. This is true for an expression that has only
+ // numbers and pointers to global variables or composite literals
+ // that do not require runtime initialization. It is false if we
+ // must generate code to compute this expression when it is used to
+ // initialize a global variable. This is not a language-level
+ // concept, but an implementation-level one. If this expression is
+ // used to initialize a global variable, this is true if we can pass
+ // an initializer to the backend, false if we must generate code to
+ // initialize the variable. It is always safe for this method to
+ // return false, but the resulting code may be less efficient.
+ bool
+ is_static_initializer() const
+ { return this->do_is_static_initializer(); }
// If this is not a numeric constant, return false. If it is one,
// return true, and set VAL to hold the value.
do_is_constant() const
{ return false; }
- // Return whether this is an immutable expression.
+ // Return whether this expression can be used as a constant
+ // initializer.
virtual bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return false; }
// Return whether this is a constant expression of numeric type, and
{ return true; }
bool
- do_is_immutable() const
+ do_is_static_initializer() const
{ return true; }
bool
do_is_constant() const;
bool
- do_is_immutable() const;
+ do_is_static_initializer() const;
bool
do_numeric_constant_value(Numeric_constant*) const;
do_traverse(Traverse* traverse);
bool
- do_is_immutable() const;
+ do_is_static_initializer() const;
Type*
do_type()
do_is_constant() const;
bool
- do_is_immutable() const
- {
- return (this->expr_->is_immutable()
- || (this->op_ == OPERATOR_AND && this->expr_->is_variable()));
- }
+ do_is_static_initializer() const;
bool
do_numeric_constant_value(Numeric_constant*) const;
{ return this->left_->is_constant() && this->right_->is_constant(); }
bool
- do_is_immutable() const
- { return this->left_->is_immutable() && this->right_->is_immutable(); }
+ do_is_static_initializer() const;
bool
do_numeric_constant_value(Numeric_constant*) const;
do_is_constant() const;
bool
- do_is_immutable() const;
+ do_is_static_initializer() const;
Type*
do_type();
do_traverse(Traverse* traverse);
bool
- do_is_immutable() const;
+ do_is_static_initializer() const;
Type*
do_type()
do_traverse(Traverse* traverse);
bool
- do_is_immutable() const;
+ do_is_static_initializer() const;
Type*
do_type()
// The initializer is constant if it is the zero-value of the
// variable's type or if the initial value is an immutable value
// that is not copied to the heap.
- bool is_constant_initializer = false;
+ bool is_static_initializer = false;
if (var->init() == NULL)
- is_constant_initializer = true;
+ is_static_initializer = true;
else
{
Type* var_type = var->type();
Expression* init = var->init();
Expression* init_cast =
Expression::make_cast(var_type, init, var->location());
- is_constant_initializer =
- init_cast->is_immutable() && !var_type->has_pointer();
+ is_static_initializer = init_cast->is_static_initializer();
}
// Non-constant variable initializations might need to create
// temporary variables, which will need the initialization
// function as context.
- if (!is_constant_initializer && init_fndecl == NULL)
- init_fndecl = this->initialization_function_decl();
- Bexpression* var_binit = var->get_init(this, init_fndecl);
+ Named_object* var_init_fn;
+ if (is_static_initializer)
+ var_init_fn = NULL;
+ else
+ {
+ if (init_fndecl == NULL)
+ init_fndecl = this->initialization_function_decl();
+ var_init_fn = init_fndecl;
+ }
+ Bexpression* var_binit = var->get_init(this, var_init_fn);
if (var_binit == NULL)
;
- else if (is_constant_initializer)
+ else if (is_static_initializer)
{
if (expression_requires(var->init(), NULL,
this->var_depends_on(var), no))