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:
Ian Lance Taylor 2022-06-28 17:03:28 -07:00
parent 329bef49da
commit 3183acc8e0
5 changed files with 215 additions and 92 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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(&copy);
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

View file

@ -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();