Allow to attach data type to lvalue part select

In most cases the type of an lvalue part select is the base type of the
lvalue with the width of the part select. But there are some exceptions.

1) An index into a `string` type is of type `byte`.

2) Packed structs are implemented as packed arrays under the hood. A lvalue
struct member is elaborated as a normal part select on a packed array. The
type of that select should be the type of the member.

For the case 1 there is some special handling for strings that accounts for
this. But for case 2 the type information of the member is lost.

This works fine for most things but there are a few constructs where the
type information is required.
 * Enum type compatibility check
 * Assignment pattern behavior depends on the type of the lvalue

Allow to attach a specific type to a lvalue part select to allow correct
behavior for constructs where the type is required.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2023-01-15 17:34:53 -08:00
parent f9909562fd
commit d0613f24b8
3 changed files with 30 additions and 11 deletions

View File

@ -605,10 +605,9 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des,
cerr << get_fileline() << ": debug: "
<< "Bit select of string becomes character select." << endl;
}
if (mux)
lv->set_part(mux, 8);
else
lv->set_part(new NetEConst(verinum(lsb)), 8);
if (!mux)
mux = new NetEConst(verinum(lsb));
lv->set_part(mux, &netvector_t::atom2s8);
} else if (mux) {
ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE);
@ -1180,6 +1179,7 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
// increases, and use_width shrinks.
unsigned long off = 0;
unsigned long use_width = struct_type->packed_width();
ivl_type_t member_type;
pform_name_t completed_path;
do {
@ -1251,6 +1251,8 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
return false;
}
member_type = member->net_type;
if (const netvector_t*mem_vec = dynamic_cast<const netvector_t*>(member->net_type)) {
// If the member type is a netvector_t, then it is a
// vector of atom or scaler objects. For example, if the
@ -1320,6 +1322,7 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
off += loff;
use_width = lwid * tail_wid;
member_type = nullptr;
}
// The netvector_t only has atom elements, to
@ -1484,7 +1487,11 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
}
if (packed_base == 0) {
lv->set_part(new NetEConst(verinum(off)), use_width);
NetExpr *base = new NetEConst(verinum(off));
if (member_type)
lv->set_part(base, member_type);
else
lv->set_part(base, use_width);
return true;
}

View File

@ -126,10 +126,6 @@ unsigned NetAssign_::lwidth() const
ivl_variable_type_t NetAssign_::expr_type() const
{
ivl_type_t ntype = net_type();
if (sig_ && sig_->data_type()==IVL_VT_STRING && base_!=0)
return IVL_VT_BOOL;
if (ntype)
return ntype->base_type();
@ -139,10 +135,14 @@ ivl_variable_type_t NetAssign_::expr_type() const
ivl_type_t NetAssign_::net_type() const
{
// This is a concatenation or a part select, it does not have a type
if (more || base_)
// This is a concatenation, it does not have a type
if (more)
return nullptr;
// Selected sub-vector can have its own data type
if (base_)
return part_data_type_;
ivl_type_t ntype;
if (nest_) {
ntype = nest_->net_type();
@ -200,6 +200,12 @@ void NetAssign_::set_part(NetExpr*base, unsigned wid,
sel_type_ = sel_type;
}
void NetAssign_::set_part(NetExpr*base, ivl_type_t data_type)
{
part_data_type_ = data_type;
set_part(base, part_data_type_->packed_width());
}
void NetAssign_::set_property(const perm_string&mname, unsigned idx)
{
member_ = mname;

View File

@ -2836,6 +2836,11 @@ class NetAssign_ {
// that the expression calculates a CANONICAL bit address.
void set_part(NetExpr* loff, unsigned wid,
ivl_select_type_t = IVL_SEL_OTHER);
// Set a part select expression for the l-value vector. Note
// that the expression calculates a CANONICAL bit address.
// The part select has a specific type and the width of the select will
// be that of the type.
void set_part(NetExpr *loff, ivl_type_t data_type);
// Set the member or property name if the signal type is a
// class.
void set_property(const perm_string&name, unsigned int idx);
@ -2905,6 +2910,7 @@ class NetAssign_ {
NetExpr*base_;
unsigned lwid_;
ivl_select_type_t sel_type_;
ivl_type_t part_data_type_ = nullptr;
};
class NetAssignBase : public NetProc {