Merge pull request #858 from larsclausen/class-prop-shadow

Consolidate class property handling
This commit is contained in:
Stephen Williams 2023-01-16 11:23:44 -08:00 committed by GitHub
commit 0ac3997142
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 192 additions and 306 deletions

View File

@ -412,7 +412,6 @@ class PEIdent : public PExpr {
bool is_force, bool is_cassign,
NetNet *reg, ivl_type_t data_type,
pform_name_t tail_path) const;
NetAssign_*elaborate_lval_method_class_member_(Design*, NetScope*) const;
NetAssign_*elaborate_lval_net_word_(Design*, NetScope*, NetNet*,
bool need_const_idx) const;
bool elaborate_lval_net_bit_(Design*, NetScope*, NetAssign_*,
@ -511,11 +510,6 @@ class PEIdent : public PExpr {
NetScope*found,
bool need_const) const;
NetExpr*elaborate_expr_class_member_(Design*des,
NetScope*scope,
unsigned expr_wid,
unsigned flags) const;
NetExpr *elaborate_expr_class_field_(Design*des, NetScope*scope,
const symbol_search_results &sr,
unsigned expr_wid,

View File

@ -2601,7 +2601,37 @@ NetExpr* PEIdent::elaborate_expr_class_field_(Design*des, NetScope*scope,
prop_name);
}
NetEProperty*tmp = new NetEProperty(sr.net, pidx);
NetExpr *canon_index = nullptr;
ivl_type_t tmp_type = class_type->get_prop_type(pidx);
if (const netuarray_t *tmp_ua = dynamic_cast<const netuarray_t*>(tmp_type)) {
const std::vector<netrange_t> &dims = tmp_ua->static_dimensions();
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_expr_class_member_: "
<< "Property " << class_type->get_prop_name(pidx)
<< " has " << dims.size() << " dimensions, "
<< " got " << comp.index.size() << " indices." << endl;
}
if (dims.size() != comp.index.size()) {
cerr << get_fileline() << ": error: "
<< "Got " << comp.index.size() << " indices, "
<< "expecting " << dims.size()
<< " to index the property " << class_type->get_prop_name(pidx) << "." << endl;
des->errors++;
} else {
canon_index = make_canonical_index(des, scope, this,
comp.index, tmp_ua, false);
}
}
if (debug_elaborate && canon_index) {
cerr << get_fileline() << ": PEIdent::elaborate_expr_class_member_: "
<< "Property " << class_type->get_prop_name(pidx)
<< " canonical index: " << *canon_index << endl;
}
NetEProperty *tmp = new NetEProperty(sr.net, pidx, canon_index);
tmp->set_line(*this);
return tmp;
}
@ -4166,13 +4196,32 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
return expr_width_;
}
// Look for a class property.
if (gn_system_verilog() && sr.cls_val) {
expr_type_ = sr.cls_val->base_type();
expr_width_ = sr.cls_val->packed_width();
min_width_ = expr_width_;
signed_flag_ = sr.cls_val->get_signed();
return expr_width_;
if (sr.net) {
// Similarly, if this net is an object, the path tail may
// be a class property.
const netclass_t *class_type = dynamic_cast<const netclass_t*>(sr.type);
if (class_type && !sr.path_tail.empty()) {
perm_string pname = peek_tail_name(sr.path_tail);
ivl_type_t par_type;
const NetExpr *par = class_type->get_parameter(des, pname, par_type);
if (par)
return test_width_parameter_(par, mode);
int pidx = class_type->property_idx_from_name(pname);
if (pidx >= 0) {
const name_component_t member_comp = sr.path_tail.front();
ivl_type_t ptype = class_type->get_prop_type(pidx);
if (!member_comp.index.empty()) {
const netuarray_t*tmp_ua = dynamic_cast<const netuarray_t*>(ptype);
if (tmp_ua) ptype = tmp_ua->element_type();
}
expr_type_ = ptype->base_type();
expr_width_ = ptype->packed_width();
min_width_ = expr_width_;
signed_flag_ = ptype->get_signed();
return expr_width_;
}
}
}
if (use_width != UINT_MAX) {
@ -4221,27 +4270,6 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
}
}
// Similarly, if this net is an object, the path tail may
// be a class property.
const netclass_t *class_type = dynamic_cast<const netclass_t*>(sr.type);
if (class_type && !sr.path_tail.empty()) {
perm_string pname = peek_tail_name(sr.path_tail);
ivl_type_t par_type;
const NetExpr *par = class_type->get_parameter(des, pname, par_type);
if (par)
return test_width_parameter_(par, mode);
int pidx = class_type->property_idx_from_name(pname);
if (pidx >= 0) {
ivl_type_t ptype = class_type->get_prop_type(pidx);
expr_type_ = ptype->base_type();
expr_width_ = ptype->packed_width();
min_width_ = expr_width_;
signed_flag_ = ptype->get_signed();
return expr_width_;
}
}
size_t use_depth = name_tail.index.size();
// Account for unpacked dimensions by assuming that the
// unpacked dimensions are consumed first, so subtract
@ -4321,10 +4349,6 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
ivl_assert(*this, use_scope);
}
if (NetExpr* tmp = elaborate_expr_class_member_(des, scope, 0, flags)) {
return tmp;
}
symbol_search_results sr;
symbol_search(this, des, use_scope, path_, &sr);
@ -4454,105 +4478,6 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
return tmp;
}
/*
* Guess that the path_ is the name of a member of a containing class,
* and see how that works. If it turns out that the current scope is
* not a method, or the name is not in the parent class, then
* fail. Otherwise, return a NetEProperty.
*/
NetExpr* PEIdent::elaborate_expr_class_member_(Design*des, NetScope*scope,
unsigned, unsigned) const
{
if (!gn_system_verilog())
return 0;
if (scope->parent() == 0)
return 0;
if (path_.size() != 1)
return 0;
const netclass_t*class_type = find_class_containing_scope(*this, scope);
if (class_type == 0)
return 0;
const name_component_t&name_comp = path_.back();
perm_string member_name = name_comp.name;
int pidx = class_type->property_idx_from_name(member_name);
if (pidx < 0)
return 0;
NetScope*scope_method = find_method_containing_scope(*this, scope);
ivl_assert(*this, scope_method);
NetNet*this_net = scope_method->find_signal(perm_string::literal(THIS_TOKEN));
if (this_net == 0) {
cerr << get_fileline() << ": internal error: "
<< "Unable to find 'this' port of " << scope_path(scope_method)
<< "." << endl;
return 0;
}
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_expr_class_member: "
<< "Found member " << member_name
<< " is a member of class " << class_type->get_name()
<< ", context scope=" << scope_path(scope)
<< ", type=" << *class_type->get_prop_type(pidx)
<< ", so making a NetEProperty." << endl;
}
property_qualifier_t qual = class_type->get_prop_qual(pidx);
if (qual.test_local() && ! class_type->test_scope_is_method(scope)) {
cerr << get_fileline() << ": error: "
<< "Local property " << class_type->get_prop_name(pidx)
<< " is not accessible in this context."
<< " (scope=" << scope_path(scope) << ")" << endl;
des->errors += 1;
}
if (qual.test_static()) {
return class_static_property_expression(this, class_type, member_name);
}
NetExpr*canon_index = 0;
ivl_type_t tmp_type = class_type->get_prop_type(pidx);
if (const netuarray_t*tmp_ua = dynamic_cast<const netuarray_t*>(tmp_type)) {
const std::vector<netrange_t>&dims = tmp_ua->static_dimensions();
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_expr_class_member_: "
<< "Property " << class_type->get_prop_name(pidx)
<< " has " << dims.size() << " dimensions, "
<< " got " << name_comp.index.size() << " indices." << endl;
}
if (dims.size() != name_comp.index.size()) {
cerr << get_fileline() << ": error: "
<< "Got " << name_comp.index.size() << " indices, "
<< "expecting " << dims.size()
<< " to index the property " << class_type->get_prop_name(pidx) << "." << endl;
des->errors += 1;
} else {
canon_index = make_canonical_index(des, scope, this,
name_comp.index, tmp_ua, false);
}
}
if (debug_elaborate && canon_index) {
cerr << get_fileline() << ": PEIdent::elaborate_expr_class_member_: "
<< "Property " << class_type->get_prop_name(pidx)
<< " canonical index: " << *canon_index << endl;
}
NetEProperty*tmp = new NetEProperty(this_net, pidx, canon_index);
tmp->set_line(*this);
return tmp;
}
/*
* Elaborate an identifier in an expression. The identifier can be a
* parameter name, a signal name or a memory name. It can also be a
@ -4587,17 +4512,6 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope,
<< endl;
}
// Special case: Detect the special situation that this name
// is the name of a variable in the class, and this is a class
// method. We sense that this might be the case by noting that
// the parent scope of where we are working is a
// NetScope::CLASS, the path_ is a single component, and the
// name is a property of the class. If that turns out to be
// the case, then handle this specially.
if (NetExpr*tmp = elaborate_expr_class_member_(des, scope, expr_wid, flags)) {
return tmp;
}
if (path_.size() > 1) {
if (NEED_CONST & flags) {
cerr << get_fileline() << ": error: A hierarchical reference"

View File

@ -165,11 +165,6 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
<< "Elaborate l-value ident expression: " << *this << endl;
}
/* Try to detect the special case that we are in a method and
the identifier is a member of the class. */
if (NetAssign_*tmp = elaborate_lval_method_class_member_(des, scope))
return tmp;
/* Normally find the name in the passed scope. But if this is
imported from a package, then located the variable from the
package scope. */
@ -364,132 +359,6 @@ NetAssign_*PEIdent::elaborate_lval_var_(Design *des, NetScope *scope,
return lv;
}
NetAssign_* PEIdent::elaborate_lval_method_class_member_(Design*des,
NetScope*scope) const
{
if (!gn_system_verilog())
return 0;
if (scope->parent() == 0 || scope->type() == NetScope::CLASS)
return 0;
if (path_.size() != 1)
return 0;
const netclass_t*class_type = find_class_containing_scope(*this, scope);
if (class_type == 0)
return 0;
const name_component_t&name_comp = path_.back();
perm_string member_name = name_comp.name;
int pidx = class_type->property_idx_from_name(member_name);
if (pidx < 0)
return 0;
property_qualifier_t qual = class_type->get_prop_qual(pidx);
if (qual.test_static()) {
NetNet *sig = class_type->find_static_property(member_name);
return elaborate_lval_var_(des, scope, false, false, sig,
class_type, {});
}
NetScope*scope_method = find_method_containing_scope(*this, scope);
ivl_assert(*this, scope_method);
NetNet*this_net = scope_method->find_signal(perm_string::literal(THIS_TOKEN));
if (this_net == 0) {
cerr << get_fileline() << ": internal error: "
<< "Unable to find 'this' port of " << scope_path(scope_method)
<< "." << endl;
return 0;
}
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Ident " << member_name
<< " is a property of class " << class_type->get_name() << endl;
}
NetExpr*canon_index = 0;
if (! name_comp.index.empty()) {
ivl_type_t property_type = class_type->get_prop_type(pidx);
if (const netsarray_t* stype = dynamic_cast<const netsarray_t*> (property_type)) {
canon_index = make_canonical_index(des, scope, this,
name_comp.index, stype, false);
} else {
cerr << get_fileline() << ": error: "
<< "Index expressions don't apply to this type of property." << endl;
des->errors += 1;
}
}
// Detect assignment to constant properties. Note that the
// initializer constructor MAY assign to constant properties,
// as this is how the property gets its value.
if (qual.test_const()) {
if (class_type->get_prop_initialized(pidx)) {
cerr << get_fileline() << ": error: "
<< "Property " << class_type->get_prop_name(pidx)
<< " is constant in this method."
<< " (scope=" << scope_path(scope) << ")" << endl;
des->errors += 1;
} else if (scope->basename()!="new" && scope->basename()!="new@") {
cerr << get_fileline() << ": error: "
<< "Property " << class_type->get_prop_name(pidx)
<< " is constant in this method."
<< " (scope=" << scope_path(scope) << ")" << endl;
des->errors += 1;
} else {
// Mark this property as initialized. This is used
// to know that we have initialized the constant
// object so the next assignment will be marked as
// illegal.
class_type->set_prop_initialized(pidx);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Found initializers for property " << class_type->get_prop_name(pidx) << endl;
}
}
}
ivl_type_t tmp_type = class_type->get_prop_type(pidx);
if (const netuarray_t*tmp_ua = dynamic_cast<const netuarray_t*>(tmp_type)) {
const std::vector<netrange_t>&dims = tmp_ua->static_dimensions();
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Property " << class_type->get_prop_name(pidx)
<< " has " << dims.size() << " dimensions, "
<< " got " << name_comp.index.size() << " indices." << endl;
if (canon_index) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Canonical index is:" << *canon_index << endl;
};
}
if (dims.size() != name_comp.index.size()) {
cerr << get_fileline() << ": error: "
<< "Got " << name_comp.index.size() << " indices, "
<< "expecting " << dims.size()
<< " to index the property " << class_type->get_prop_name(pidx) << "." << endl;
des->errors += 1;
}
}
NetAssign_*this_lval = new NetAssign_(this_net);
this_lval->set_property(member_name, pidx);
if (canon_index) this_lval->set_word(canon_index);
return this_lval;
}
NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des,
NetScope*scope,
NetNet*reg,
@ -1151,10 +1020,30 @@ NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope
return lv;
} else if (qual.test_const()) {
cerr << get_fileline() << ": error: "
<< "Property " << class_type->get_prop_name(pidx)
<< " is constant in this context." << endl;
des->errors += 1;
if (class_type->get_prop_initialized(pidx)) {
cerr << get_fileline() << ": error: "
<< "Property " << class_type->get_prop_name(pidx)
<< " is constant in this method."
<< " (scope=" << scope_path(scope) << ")" << endl;
des->errors++;
} else if (scope->basename() != "new" && scope->basename() != "new@") {
cerr << get_fileline() << ": error: "
<< "Property " << class_type->get_prop_name(pidx)
<< " is constant in this method."
<< " (scope=" << scope_path(scope) << ")" << endl;
des->errors++;
} else {
// Mark this property as initialized. This is used
// to know that we have initialized the constant
// object so the next assignment will be marked as
// illegal.
class_type->set_prop_initialized(pidx);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Found initializers for property " << class_type->get_prop_name(pidx) << endl;
}
}
}
lv = lv? new NetAssign_(lv) : new NetAssign_(sig);
@ -1171,6 +1060,44 @@ NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope
des->errors += 1;
}
}
NetExpr *canon_index = nullptr;
if (!member_cur.index.empty()) {
if (const netsarray_t *stype = dynamic_cast<const netsarray_t*>(ptype)) {
canon_index = make_canonical_index(des, scope, this,
member_cur.index, stype, false);
} else {
cerr << get_fileline() << ": error: "
<< "Index expressions don't apply to this type of property." << endl;
des->errors++;
}
}
if (const netuarray_t *tmp_ua = dynamic_cast<const netuarray_t*>(ptype)) {
const std::vector<netrange_t> &dims = tmp_ua->static_dimensions();
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Property " << class_type->get_prop_name(pidx)
<< " has " << dims.size() << " dimensions, "
<< " got " << member_cur.index.size() << " indices." << endl;
if (canon_index) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Canonical index is:" << *canon_index << endl;
}
}
if (dims.size() != member_cur.index.size()) {
cerr << get_fileline() << ": error: "
<< "Got " << member_cur.index.size() << " indices, "
<< "expecting " << dims.size()
<< " to index the property " << class_type->get_prop_name(pidx) << "." << endl;
des->errors++;
}
}
if (canon_index)
lv->set_word(canon_index);
// If the current member is a class object, then get the
// type. We may wind up iterating, and need the proper

View File

@ -3812,7 +3812,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
use_path.pop_back();
NetNet *net;
ivl_type_t cls_val = 0;
const NetExpr *par;
ivl_type_t par_type = 0;
NetEvent *eve;
@ -3838,7 +3837,7 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
// resolve to a class object. Note that the "this" symbol
// (internally represented as "@") is handled by there being a
// "this" object in the instance scope.
symbol_search(this, des, scope, use_path, net, par, eve, par_type, cls_val);
symbol_search(this, des, scope, use_path, net, par, eve, par_type);
if (net == 0)
return 0;

View File

@ -0,0 +1,25 @@
// Check that class properties can be shadowed by a local symbol
module test;
class C;
int x = 0;
task check;
int x; // This should shadow the class property
x = 10;
if (this.x == 0 && x === 10) begin
$display("PASSED");
end else begin
$display("FAILED");
end
endtask
endclass
initial begin
C c;
c = new;
c.check;
end
endmodule

View File

@ -0,0 +1,28 @@
// Check that it is possible to access a package scoped identifier of the same
// name of a class property inside a class method
package P;
int x = 10;
endpackage
module test;
class C;
int x = 0;
task check;
if (P::x === 10) begin
$display("PASSED");
end else begin
$display("FAILED");
end
endtask
endclass
initial begin
C c;
c = new;
c.check;
end
endmodule

View File

@ -584,6 +584,8 @@ sv_class_method_lt_static2 CE,-g2009 ivltests
sv_class_method_signed1 normal,-g2009 ivltests
sv_class_method_signed2 normal,-g2009 ivltests
sv_class_method_var_init normal,-g2009 ivltests
sv_class_prop_shadow1 normal,-g2009 ivltests
sv_class_prop_shadow2 normal,-g2009 ivltests
sv_class_property_signed1 normal,-g2009 ivltests
sv_class_property_signed2 normal,-g2009 ivltests
sv_class_property_signed3 normal,-g2009 ivltests

View File

@ -432,6 +432,8 @@ sv_class_method_default2 CE,-g2009 ivltests
sv_class_method_signed1 CE,-g2009,-pallowsigned=1 ivltests
sv_class_method_signed2 CE,-g2009,-pallowsigned=1 ivltests
sv_class_method_var_init CE,-g2009,-pallowsigned=1 ivltests
sv_class_prop_shadow1 CE,-g2009 ivltests
sv_class_prop_shadow2 CE,-g2009 ivltests
sv_class_property_signed1 CE,-g2009,-pallowsigned=1 ivltests
sv_class_property_signed2 CE,-g2009,-pallowsigned=1 ivltests
sv_class_property_signed3 CE,-g2009,-pallowsigned=1 ivltests

View File

@ -46,7 +46,6 @@ struct symbol_search_results {
inline symbol_search_results() {
scope = 0;
net = 0;
cls_val = 0;
par_val = 0;
type = 0;
eve = 0;
@ -55,7 +54,6 @@ struct symbol_search_results {
inline bool is_scope() const {
if (net) return false;
if (eve) return false;
if (cls_val) return false;
if (par_val) return false;
if (scope) return true;
return false;
@ -64,7 +62,6 @@ struct symbol_search_results {
inline bool is_found() const {
if (net) return true;
if (eve) return true;
if (cls_val) return true;
if (par_val) return true;
if (scope) return true;
return false;
@ -75,8 +72,6 @@ struct symbol_search_results {
NetScope*scope;
// If this was a net, the signal itself.
NetNet*net;
// For a class property we only have type information.
ivl_type_t cls_val;
// If this was a parameter, the value expression and the
// optional value dimensions.
const NetExpr*par_val;
@ -136,8 +131,7 @@ extern NetScope* symbol_search(const LineInfo*li,
NetNet*&net, /* net/reg */
const NetExpr*&par,/* parameter/expr */
NetEvent*&eve, /* named event */
ivl_type_t&par_type,
ivl_type_t&cls_val);
ivl_type_t&par_type);
inline NetScope* symbol_search(const LineInfo*li,
Design*des,
@ -148,9 +142,8 @@ inline NetScope* symbol_search(const LineInfo*li,
NetEvent*&eve /* named event */)
{
ivl_type_t par_type;
ivl_type_t cls_val;
return symbol_search(li, des, start, path, net, par, eve,
par_type, cls_val);
par_type);
}
/*

View File

@ -187,16 +187,21 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
// Static items are just normal signals and are found above.
if (scope->type() == NetScope::CLASS) {
netclass_t*clsnet = scope->find_class(des, scope->basename());
const netclass_t *clsnet = scope->class_def();
int pidx = clsnet->property_idx_from_name(path_tail.name);
if (pidx >= 0) {
ivl_type_t prop_type = clsnet->get_prop_type(pidx);
const netuarray_t*tmp_ua = dynamic_cast<const netuarray_t*>(prop_type);
if (tmp_ua) prop_type = tmp_ua->element_type();
path.push_back(path_tail);
// This is a class property being accessed in a
// class method. Return `this` for the net and the
// property name for the path tail.
NetScope *scope_method = find_method_containing_scope(*li, start_scope);
ivl_assert(*li, scope_method);
res->net = scope_method->find_signal(perm_string::literal(THIS_TOKEN));
ivl_assert(*li, res->net);
res->scope = scope;
res->cls_val = prop_type;
res->path_head = path;
ivl_assert(*li, path.empty());
res->path_head.push_back(name_component_t(perm_string::literal(THIS_TOKEN)));
res->path_tail.push_front(path_tail);
res->type = clsnet;
return true;
}
}
@ -311,14 +316,12 @@ NetScope*symbol_search(const LineInfo*li, Design*des, NetScope*scope,
NetNet*&net,
const NetExpr*&par,
NetEvent*&eve,
ivl_type_t&par_type,
ivl_type_t&cls_val)
ivl_type_t&par_type)
{
symbol_search_results recurse;
bool flag = symbol_search(li, des, scope, path, &recurse);
net = 0;
cls_val = 0;
par = 0;
par_type = 0;
eve = 0;
@ -335,7 +338,6 @@ NetScope*symbol_search(const LineInfo*li, Design*des, NetScope*scope,
// Convert the extended results to the compatible results.
net = recurse.net;
cls_val = recurse.cls_val;
par = recurse.par_val;
par_type = recurse.type;
eve = recurse.eve;