compiler: check repeated const expressions in new scope
Test case is const8.go in https://go.dev/cl/414795. Fixes golang/go#53585 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/414914
This commit is contained in:
parent
329bef49da
commit
3183acc8e0
5 changed files with 215 additions and 92 deletions
|
@ -1,4 +1,4 @@
|
|||
c7238f58a26131b7611eff6f555cab02af8a623c
|
||||
63782f8a318e9eebfdc983f171a920c7a937c759
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
|
|
@ -3352,97 +3352,7 @@ class Find_named_object : public Traverse
|
|||
bool found_;
|
||||
};
|
||||
|
||||
// A reference to a const in an expression.
|
||||
|
||||
class Const_expression : public Expression
|
||||
{
|
||||
public:
|
||||
Const_expression(Named_object* constant, Location location)
|
||||
: Expression(EXPRESSION_CONST_REFERENCE, location),
|
||||
constant_(constant), type_(NULL), seen_(false)
|
||||
{ }
|
||||
|
||||
Named_object*
|
||||
named_object()
|
||||
{ return this->constant_; }
|
||||
|
||||
const Named_object*
|
||||
named_object() const
|
||||
{ return this->constant_; }
|
||||
|
||||
// Check that the initializer does not refer to the constant itself.
|
||||
void
|
||||
check_for_init_loop();
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse*);
|
||||
|
||||
Expression*
|
||||
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
|
||||
|
||||
bool
|
||||
do_is_constant() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_zero_value() const
|
||||
{ return this->constant_->const_value()->expr()->is_zero_value(); }
|
||||
|
||||
bool
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
do_numeric_constant_value(Numeric_constant* nc) const;
|
||||
|
||||
bool
|
||||
do_string_constant_value(std::string* val) const;
|
||||
|
||||
bool
|
||||
do_boolean_constant_value(bool* val) const;
|
||||
|
||||
Type*
|
||||
do_type();
|
||||
|
||||
// The type of a const is set by the declaration, not the use.
|
||||
void
|
||||
do_determine_type(const Type_context*);
|
||||
|
||||
void
|
||||
do_check_types(Gogo*);
|
||||
|
||||
Expression*
|
||||
do_copy()
|
||||
{ return this; }
|
||||
|
||||
Bexpression*
|
||||
do_get_backend(Translate_context* context);
|
||||
|
||||
int
|
||||
do_inlining_cost() const
|
||||
{ return 1; }
|
||||
|
||||
// When exporting a reference to a const as part of a const
|
||||
// expression, we export the value. We ignore the fact that it has
|
||||
// a name.
|
||||
void
|
||||
do_export(Export_function_body* efb) const
|
||||
{ this->constant_->const_value()->expr()->export_expression(efb); }
|
||||
|
||||
void
|
||||
do_dump_expression(Ast_dump_context*) const;
|
||||
|
||||
private:
|
||||
// The constant.
|
||||
Named_object* constant_;
|
||||
// The type of this reference. This is used if the constant has an
|
||||
// abstract type.
|
||||
Type* type_;
|
||||
// Used to prevent infinite recursion when a constant incorrectly
|
||||
// refers to itself.
|
||||
mutable bool seen_;
|
||||
};
|
||||
// Class Const_expression.
|
||||
|
||||
// Traversal.
|
||||
|
||||
|
@ -3454,6 +3364,14 @@ Const_expression::do_traverse(Traverse* traverse)
|
|||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
// Whether this is the zero value.
|
||||
|
||||
bool
|
||||
Const_expression::do_is_zero_value() const
|
||||
{
|
||||
return this->constant_->const_value()->expr()->is_zero_value();
|
||||
}
|
||||
|
||||
// Lower a constant expression. This is where we convert the
|
||||
// predeclared constant iota into an integer value.
|
||||
|
||||
|
@ -3708,6 +3626,16 @@ Const_expression::do_get_backend(Translate_context* context)
|
|||
return expr->get_backend(context);
|
||||
}
|
||||
|
||||
// When exporting a reference to a const as part of a const
|
||||
// expression, we export the value. We ignore the fact that it has
|
||||
// a name.
|
||||
|
||||
void
|
||||
Const_expression::do_export(Export_function_body* efb) const
|
||||
{
|
||||
this->constant_->const_value()->expr()->export_expression(efb);
|
||||
}
|
||||
|
||||
// Dump ast representation for constant expression.
|
||||
|
||||
void
|
||||
|
|
|
@ -28,6 +28,7 @@ class Map_type;
|
|||
class Struct_type;
|
||||
class Struct_field;
|
||||
class Expression_list;
|
||||
class Const_expression;
|
||||
class Var_expression;
|
||||
class Enclosed_var_expression;
|
||||
class Temporary_reference_expression;
|
||||
|
@ -626,6 +627,20 @@ class Expression
|
|||
is_type_expression() const
|
||||
{ return this->classification_ == EXPRESSION_TYPE; }
|
||||
|
||||
// If this is a const reference, return the Const_expression
|
||||
// structure. Otherwise, return NULL. This is a controlled dynamic
|
||||
// cast.
|
||||
Const_expression*
|
||||
const_expression()
|
||||
{ return this->convert<Const_expression, EXPRESSION_CONST_REFERENCE>(); }
|
||||
|
||||
const Const_expression*
|
||||
const_expression() const
|
||||
{
|
||||
return this->convert<const Const_expression,
|
||||
EXPRESSION_CONST_REFERENCE>();
|
||||
}
|
||||
|
||||
// If this is a variable reference, return the Var_expression
|
||||
// structure. Otherwise, return NULL. This is a controlled dynamic
|
||||
// cast.
|
||||
|
@ -1453,6 +1468,96 @@ class Parser_expression : public Expression
|
|||
{ go_unreachable(); }
|
||||
};
|
||||
|
||||
// A reference to a const in an expression.
|
||||
|
||||
class Const_expression : public Expression
|
||||
{
|
||||
public:
|
||||
Const_expression(Named_object* constant, Location location)
|
||||
: Expression(EXPRESSION_CONST_REFERENCE, location),
|
||||
constant_(constant), type_(NULL), seen_(false)
|
||||
{ }
|
||||
|
||||
Named_object*
|
||||
named_object()
|
||||
{ return this->constant_; }
|
||||
|
||||
const Named_object*
|
||||
named_object() const
|
||||
{ return this->constant_; }
|
||||
|
||||
// Check that the initializer does not refer to the constant itself.
|
||||
void
|
||||
check_for_init_loop();
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse*);
|
||||
|
||||
Expression*
|
||||
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
|
||||
|
||||
bool
|
||||
do_is_constant() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_zero_value() const;
|
||||
|
||||
bool
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
do_numeric_constant_value(Numeric_constant* nc) const;
|
||||
|
||||
bool
|
||||
do_string_constant_value(std::string* val) const;
|
||||
|
||||
bool
|
||||
do_boolean_constant_value(bool* val) const;
|
||||
|
||||
Type*
|
||||
do_type();
|
||||
|
||||
// The type of a const is set by the declaration, not the use.
|
||||
void
|
||||
do_determine_type(const Type_context*);
|
||||
|
||||
void
|
||||
do_check_types(Gogo*);
|
||||
|
||||
Expression*
|
||||
do_copy()
|
||||
{ return this; }
|
||||
|
||||
Bexpression*
|
||||
do_get_backend(Translate_context* context);
|
||||
|
||||
int
|
||||
do_inlining_cost() const
|
||||
{ return 1; }
|
||||
|
||||
// When exporting a reference to a const as part of a const
|
||||
// expression, we export the value. We ignore the fact that it has
|
||||
// a name.
|
||||
void
|
||||
do_export(Export_function_body* efb) const;
|
||||
|
||||
void
|
||||
do_dump_expression(Ast_dump_context*) const;
|
||||
|
||||
private:
|
||||
// The constant.
|
||||
Named_object* constant_;
|
||||
// The type of this reference. This is used if the constant has an
|
||||
// abstract type.
|
||||
Type* type_;
|
||||
// Used to prevent infinite recursion when a constant incorrectly
|
||||
// refers to itself.
|
||||
mutable bool seen_;
|
||||
};
|
||||
|
||||
// An expression which is simply a variable.
|
||||
|
||||
class Var_expression : public Expression
|
||||
|
|
|
@ -1468,6 +1468,7 @@ Parse::const_spec(int iota, Type** last_type, Expression_list** last_expr_list)
|
|||
{
|
||||
Expression* copy = (*p)->copy();
|
||||
copy->set_location(loc);
|
||||
this->update_references(©);
|
||||
expr_list->push_back(copy);
|
||||
}
|
||||
}
|
||||
|
@ -1513,6 +1514,94 @@ Parse::const_spec(int iota, Type** last_type, Expression_list** last_expr_list)
|
|||
return;
|
||||
}
|
||||
|
||||
// Update any references to names to refer to the current names,
|
||||
// for weird cases like
|
||||
//
|
||||
// const X = 1
|
||||
// func F() {
|
||||
// const (
|
||||
// X = X + X
|
||||
// Y
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// where the X + X for the first X is the outer X, but the X + X
|
||||
// copied for Y is the inner X.
|
||||
|
||||
class Update_references : public Traverse
|
||||
{
|
||||
public:
|
||||
Update_references(Gogo* gogo)
|
||||
: Traverse(traverse_expressions),
|
||||
gogo_(gogo)
|
||||
{ }
|
||||
|
||||
int
|
||||
expression(Expression**);
|
||||
|
||||
private:
|
||||
Gogo* gogo_;
|
||||
};
|
||||
|
||||
int
|
||||
Update_references::expression(Expression** pexpr)
|
||||
{
|
||||
Named_object* old_no;
|
||||
switch ((*pexpr)->classification())
|
||||
{
|
||||
case Expression::EXPRESSION_CONST_REFERENCE:
|
||||
old_no = (*pexpr)->const_expression()->named_object();
|
||||
break;
|
||||
case Expression::EXPRESSION_VAR_REFERENCE:
|
||||
old_no = (*pexpr)->var_expression()->named_object();
|
||||
break;
|
||||
case Expression::EXPRESSION_ENCLOSED_VAR_REFERENCE:
|
||||
old_no = (*pexpr)->enclosed_var_expression()->variable();
|
||||
break;
|
||||
case Expression::EXPRESSION_FUNC_REFERENCE:
|
||||
old_no = (*pexpr)->func_expression()->named_object();
|
||||
break;
|
||||
case Expression::EXPRESSION_UNKNOWN_REFERENCE:
|
||||
old_no = (*pexpr)->unknown_expression()->named_object();
|
||||
break;
|
||||
default:
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
if (old_no->package() != NULL)
|
||||
{
|
||||
// This is a qualified reference, so it can't have changed in
|
||||
// scope. FIXME: This probably doesn't handle dot imports
|
||||
// correctly.
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
Named_object* in_function;
|
||||
Named_object* new_no = this->gogo_->lookup(old_no->name(), &in_function);
|
||||
if (new_no == old_no)
|
||||
return TRAVERSE_CONTINUE;
|
||||
|
||||
// The new name must be a constant, since that is all we have
|
||||
// introduced into scope.
|
||||
if (!new_no->is_const())
|
||||
{
|
||||
go_assert(saw_errors());
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
*pexpr = Expression::make_const_reference(new_no, (*pexpr)->location());
|
||||
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
void
|
||||
Parse::update_references(Expression** pexpr)
|
||||
{
|
||||
Update_references ur(this->gogo_);
|
||||
ur.expression(pexpr);
|
||||
(*pexpr)->traverse_subexpressions(&ur);
|
||||
}
|
||||
|
||||
// TypeDecl = "type" Decl<TypeSpec> .
|
||||
|
||||
void
|
||||
|
|
|
@ -185,6 +185,7 @@ class Parse
|
|||
void list(void (Parse::*)(), bool);
|
||||
void const_decl();
|
||||
void const_spec(int, Type**, Expression_list**);
|
||||
void update_references(Expression**);
|
||||
void type_decl();
|
||||
void type_spec();
|
||||
void var_decl();
|
||||
|
|
Loading…
Add table
Reference in a new issue