Improve handling of type identifier references

Currently when referencing a typedef this gets replaced with the
`data_type_t` that the typedef points to. This works for most cases, but
there are some corner cases where it breaks down.

E.g. it is possible to have a scoped type identifier which references a
type defined in a package. For such type identifiers, only the data_type_t
itself is remembered, but not the package scope. This will cause the type
identifier to be elaborated in the wrong scope.

Furthermore type identifiers of vector types used for module or task port
might not be elaborated in the correct scope.

Introduce a new `typeref_t` which has `data_type_t` as a base type and can
be used as the data type for a signal. A new instance of a `typeref_t` is
created when referencing a type identifier. The `typeref_t` remembers both
the data type and the scope of the type identifier.

When elaborating the `typeref_t` the elaboration is passed through to the
referenced `data_type_t`. But special care is taken to lookup the right
scope first.

With the new approach also typedefs of typedefs are supported. This
previously did not work because chained typedefs all reference the same
`data_type_t`, but each typedef sets the `name` field of the `data_type_t`.
So the second typedef overwrites the first typedef and a lookup of the
scope of the first typedef by name will fail as it will return the scope of
the second typedef.

This refactoring also allows to define clear ownership of a data_type_t
instance. This e.g. means that an array type owns its base type and the
base type can be freed when the array type itself is freed. The same is
true for signals and class properties, they now own their data type and the
data type can be freed when the signal or property is freed.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-03-24 11:05:33 +01:00
parent 20d82bbdcb
commit b7fce4a66e
9 changed files with 84 additions and 47 deletions

View File

@ -33,7 +33,7 @@ PWire::PWire(perm_string n,
: name_(n), type_(t), port_type_(pt), data_type_(dt),
signed_(false),
port_set_(false), net_set_(false), is_scalar_(false),
error_cnt_(0), set_data_type_(0), discipline_(0)
error_cnt_(0), discipline_(0)
{
switch (rt) {
case SR_PORT:
@ -195,8 +195,11 @@ void PWire::set_unpacked_idx(const list<pform_range_t>&ranges)
void PWire::set_data_type(data_type_t*type)
{
assert(set_data_type_ == 0 || set_data_type_ == type);
set_data_type_ = type;
if (set_data_type_.get() == type)
return;
assert(!set_data_type_.get());
set_data_type_.reset(type);
}
void PWire::set_discipline(ivl_discipline_t d)

View File

@ -123,7 +123,7 @@ class PWire : public PNamedItem {
// This is the complex type of the wire. the data_type_ may
// modify how this is interpreted.
data_type_t*set_data_type_;
std::unique_ptr<data_type_t> set_data_type_;
ivl_discipline_t discipline_;

View File

@ -444,7 +444,7 @@ static void blend_class_constructors(PClass*pclass)
// If we do not have an explicit constructor chain, but there
// is a parent class, then create an implicit chain.
if (chain_new==0 && pclass->type->base_type!=0) {
if (chain_new==0 && pclass->type->base_type) {
chain_new = new PChainConstructor(pclass->type->base_args);
chain_new->set_line(*pclass);
}
@ -512,16 +512,6 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass)
netclass_t*use_class = new netclass_t(use_type->name, use_base_class);
// If this is a package we need to remember the elaborated type so that
// scoped type references work. Since there is only one instance for each
// package this works. For classes defined in modules there might be
// multiple instances though. Each module instance will have its own class
// type instance, so the same does not work there.
if (scope->type() == NetScope::PACKAGE) {
ivl_assert(*pclass, use_type->save_elaborated_type == 0);
use_type->save_elaborated_type = use_class;
}
NetScope*class_scope = new NetScope(scope, hname_t(pclass->pscope_name()),
NetScope::CLASS, scope->unit());
class_scope->set_line(pclass);

View File

@ -939,10 +939,10 @@ bool test_ranges_eeq(const vector<netrange_t>&lef, const vector<netrange_t>&rig)
ivl_type_t PWire::elaborate_type(Design*des, NetScope*scope,
const std::vector<netrange_t>&packed_dimensions) const
{
if (set_data_type_ && !dynamic_cast<vector_type_t*>(set_data_type_)) {
ivl_type_t use_type = set_data_type_->elaborate_type(des, scope);
vector_type_t *vec_type = dynamic_cast<vector_type_t*>(set_data_type_.get());
if (set_data_type_ && !vec_type) {
ivl_assert(*this, packed_dimensions.empty());
return use_type;
return set_data_type_->elaborate_type(des, scope);
}
// Fallback method. Create vector type.
@ -1003,10 +1003,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
unsigned wid = 1;
vector<netrange_t>packed_dimensions;
NetScope *type_scope = scope;
if (set_data_type_ && !set_data_type_->name.nil())
type_scope = type_scope->find_typedef_scope(des, set_data_type_);
des->errors += error_cnt_;
if (port_set_ || net_set_) {
@ -1046,7 +1042,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
cerr << get_fileline() << ": PWire::elaborate_sig: "
<< "Evaluate ranges for net " << basename() << endl;
}
dimensions_ok &= evaluate_ranges(des, type_scope, this, nlist, net_);
dimensions_ok &= evaluate_ranges(des, scope, this, nlist, net_);
}
assert(net_set_ || net_.empty());
@ -1147,10 +1143,10 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
wtype = NetNet::WIRE;
}
ivl_type_t type = elaborate_type(des, type_scope, packed_dimensions);
ivl_type_t type = elaborate_type(des, scope, packed_dimensions);
// Create the type for the unpacked dimensions. If the
// unpacked_dimensions are empty this will just return the base type.
type = elaborate_array_type(des, type_scope, *this, type, unpacked_);
type = elaborate_array_type(des, scope, *this, type, unpacked_);
list<netrange_t> unpacked_dimensions;
// If this is an unpacked array extract the base type and unpacked

View File

@ -18,6 +18,7 @@
*/
# include "PExpr.h"
# include "PScope.h"
# include "pform_types.h"
# include "netlist.h"
# include "netclass.h"
@ -40,10 +41,7 @@ using namespace std;
*/
ivl_type_t data_type_t::elaborate_type(Design*des, NetScope*scope)
{
// User-defined types must be elaborated in the context
// where they were defined.
if (!name.nil())
scope = scope->find_typedef_scope(des, this);
scope = find_scope(des, scope);
ivl_assert(*this, scope);
Definitions*use_definitions = scope;
@ -57,6 +55,11 @@ ivl_type_t data_type_t::elaborate_type(Design*des, NetScope*scope)
return tmp;
}
NetScope *data_type_t::find_scope(Design *, NetScope *scope) const
{
return scope;
}
ivl_type_t data_type_t::elaborate_type_raw(Design*des, NetScope*) const
{
cerr << get_fileline() << ": internal error: "
@ -112,8 +115,6 @@ ivl_type_t atom_type_t::elaborate_type_raw(Design*des, NetScope*) const
ivl_type_t class_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
{
if (save_elaborated_type)
return save_elaborated_type;
return scope->find_class(des, name);
}
@ -366,3 +367,26 @@ ivl_type_t uarray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
return elaborate_array_type(des, scope, *this, btype, *dims.get());
}
ivl_type_t typeref_t::elaborate_type_raw(Design*des, NetScope*s) const
{
if (!s) {
// Try to recover
return new netvector_t(IVL_VT_LOGIC);
}
return type->elaborate_type(des, s);
}
NetScope *typeref_t::find_scope(Design *des, NetScope *s) const
{
// If a scope has been specified use that as a starting point for the
// search
if (scope)
s = des->find_package(scope->pscope_name());
if (!type->name.nil())
s = s->find_typedef_scope(des, type);
return s;
}

View File

@ -27,6 +27,7 @@
# include "pform.h"
# include "Statement.h"
# include "PSpec.h"
# include "PPackage.h"
# include <stack>
# include <cstring>
# include <sstream>
@ -1167,12 +1168,14 @@ ps_type_identifier /* IEEE1800-2017: A.9.3 */
: TYPE_IDENTIFIER
{ pform_set_type_referenced(@1, $1.text);
delete[]$1.text;
$$ = $1.type;
$$ = new typeref_t($1.type);
FILE_NAME($$, @1);
}
| package_scope TYPE_IDENTIFIER
{ lex_in_package_scope(0);
$$ = $2.type;
delete[]$2.text;
$$ = new typeref_t($2.type, $1);
FILE_NAME($$, @2);
delete[] $2.text;
}
;

View File

@ -173,6 +173,15 @@ ostream& data_type_t::debug_dump(ostream&out) const
return out;
}
std::ostream& typeref_t::debug_dump(ostream&out) const
{
if (scope)
out << scope->pscope_name() << "::";
out << type->name;
return out;
}
ostream& atom_type_t::debug_dump(ostream&out) const
{
if (signed_flag)

View File

@ -50,7 +50,7 @@ void pform_start_class_declaration(const struct vlltype&loc,
pform_cur_class = class_scope;
assert(type->base_type == 0);
type->base_type = base_type;
type->base_type.reset(base_type);
assert(type->base_args.empty());
if (base_exprs) {

View File

@ -41,6 +41,7 @@ class Design;
class NetScope;
class Definitions;
class PExpr;
class PScope;
class PWire;
class Statement;
class netclass_t;
@ -161,6 +162,8 @@ class data_type_t : public PNamedItem {
virtual SymbolType symbol_type() const;
virtual NetScope *find_scope(Design* des, NetScope *scope) const;
perm_string name;
private:
@ -171,6 +174,19 @@ class data_type_t : public PNamedItem {
std::map<Definitions*,ivl_type_t> cache_type_elaborate_;
};
struct typeref_t : public data_type_t {
explicit typeref_t(data_type_t *t, PScope *s = 0) : scope(s), type(t) {}
ivl_type_t elaborate_type_raw(Design*des, NetScope*scope) const;
NetScope *find_scope(Design* des, NetScope *scope) const;
std::ostream& debug_dump(std::ostream&out) const;
private:
PScope *scope;
data_type_t *type;
};
struct void_type_t : public data_type_t {
virtual void pform_dump(std::ostream&out, unsigned indent) const;
};
@ -189,7 +205,7 @@ struct enum_type_t : public data_type_t {
SymbolType symbol_type() const;
data_type_t *base_type;
std::unique_ptr<data_type_t> base_type;
std::unique_ptr< std::list<named_pexpr_t> > names;
};
@ -266,7 +282,7 @@ struct array_base_t : public data_type_t {
inline explicit array_base_t(data_type_t*btype, std::list<pform_range_t>*pd)
: base_type(btype), dims(pd) { }
data_type_t*base_type;
std::unique_ptr<data_type_t> base_type;
std::unique_ptr< std::list<pform_range_t> > dims;
};
@ -319,8 +335,7 @@ struct string_type_t : public data_type_t {
struct class_type_t : public data_type_t {
inline explicit class_type_t(perm_string n)
: base_type(0), save_elaborated_type(0) { name = n; }
inline explicit class_type_t(perm_string n) { name = n; }
void pform_dump(std::ostream&out, unsigned indent) const;
void pform_dump_init(std::ostream&out, unsigned indent) const;
@ -329,15 +344,17 @@ struct class_type_t : public data_type_t {
// class that we are extending. This is nil if there is no
// hierarchy. If there are arguments to the base class, then
// put them in the base_args vector.
data_type_t*base_type;
std::unique_ptr<data_type_t> base_type;
std::list<PExpr*>base_args;
// This is a map of the properties. Map the name to the type.
struct prop_info_t : public LineInfo {
inline prop_info_t() : qual(property_qualifier_t::make_none()), type(0) { }
inline prop_info_t() : qual(property_qualifier_t::make_none()) { }
inline prop_info_t(property_qualifier_t q, data_type_t*t) : qual(q), type(t) { }
prop_info_t(prop_info_t&&) = default;
prop_info_t& operator=(prop_info_t&&) = default;
property_qualifier_t qual;
data_type_t* type;
std::unique_ptr<data_type_t> type;
};
std::map<perm_string, struct prop_info_t> properties;
@ -352,11 +369,6 @@ struct class_type_t : public data_type_t {
std::vector<Statement*> initialize_static;
ivl_type_t elaborate_type_raw(Design*, NetScope*) const;
// The save_elaborated_type member must be set to the pointer
// to the netclass_t object that is created to represent this
// type. The elaborate_type_raw() method uses this pointer,
// and it is used in some other situations as well.
netclass_t* save_elaborated_type;
virtual SymbolType symbol_type() const;
};