From 2e0d6d5af1b217002f9c9c574e12a2d4a14ea718 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 23 Apr 2022 15:05:53 +0200 Subject: [PATCH] Allow to attach additional information to typedefs Currently typedefs are just a pointer to a data_type_t. Currently typedefs are implemented by setting the name field of a data_type_t when a typedef of the type is declared. This works mostly, but there are some corner cases that can't be supported. E.g. a typedef of a typedef does not work as it overwrites the name field of the same data_type_t multiple times. Forward typedefs can also not be supported since forward typedefs allow to reference a type before it has been declared. There are also some problems with type identifier references from a higher-level scope if there is a type identifier in the current scope with the same name, but it is declared after the type identifier has been referenced. E.g. in the following x should be a vector fo width 8, but it will be a vector of width 4, because while the right type is used it is elaborated in the wrong scope. ``` localparam A = 8; typedef logic [A-1:0] T; module M; localparam A = 4; T x; typedef int T; endmodule ``` Furthermore typedefs used for the type of ports are elaborated in the wrong scope. To handle these corner case issues introduce a data_type_t for typedefs. Signed-off-by: Lars-Peter Clausen --- PScope.cc | 4 ++-- PScope.h | 3 ++- elab_type.cc | 37 +++++++++++++++++++++++++++---------- lexor.lex | 6 +++--- net_scope.cc | 7 ++++--- netlist.h | 6 +++--- parse.y | 19 +++++++------------ parse_misc.h | 6 ++---- pform.cc | 46 +++++++++++++++++++--------------------------- pform.h | 3 ++- pform_dump.cc | 4 ++-- pform_package.cc | 6 ++++-- pform_types.cc | 10 ++++++++++ pform_types.h | 27 ++++++++++++++++++++------- 14 files changed, 107 insertions(+), 77 deletions(-) diff --git a/PScope.cc b/PScope.cc index e2f3192f2..5d5c2fb75 100644 --- a/PScope.cc +++ b/PScope.cc @@ -51,8 +51,8 @@ PScope::PScope(perm_string n, LexicalScope*parent) PScope::~PScope() { - for(map::iterator it = typedefs.begin(); - it != typedefs.end(); ++it) + for(typedef_map_t::iterator it = typedefs.begin(); it != typedefs.end(); + ++it) delete it->second; } diff --git a/PScope.h b/PScope.h index 6a22ac51f..d76b1c819 100644 --- a/PScope.h +++ b/PScope.h @@ -120,7 +120,8 @@ class LexicalScope { bool has_parameter_port_list; // Defined types in the scope. - std::maptypedefs; + typedef std::map typedef_map_t; + typedef_map_t typedefs; // Named events in the scope. std::mapevents; diff --git a/elab_type.cc b/elab_type.cc index 9c8280c61..42a13ed43 100644 --- a/elab_type.cc +++ b/elab_type.cc @@ -43,7 +43,6 @@ ivl_type_t data_type_t::elaborate_type(Design*des, NetScope*scope) { scope = find_scope(des, scope); - ivl_assert(*this, scope); Definitions*use_definitions = scope; map::iterator pos = cache_type_elaborate_.lower_bound(use_definitions); @@ -172,13 +171,8 @@ ivl_type_t parray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const ivl_type_t etype = base_type->elaborate_type(des, scope); if (!etype->packed()) { cerr << this->get_fileline() << " error: Packed array "; - if (!name.nil()) - cerr << "`" << name << "` "; cerr << "base-type `"; - if (base_type->name.nil()) - cerr << *base_type; - else - cerr << base_type->name; + cerr << *base_type; cerr << "` is not packed." << endl; des->errors++; } @@ -385,8 +379,31 @@ NetScope *typeref_t::find_scope(Design *des, NetScope *s) const if (scope) s = des->find_package(scope->pscope_name()); - if (!type->name.nil()) - s = s->find_typedef_scope(des, type); - return s; } + +ivl_type_t typedef_t::elaborate_type(Design *des, NetScope *scope) +{ + if (!data_type.get()) { + cerr << get_fileline() << ": error: Undefined type `" << name << "`." + << endl; + des->errors++; + + // Try to recover + return netvector_t::integer_type(); + } + + // Search upwards from where the type was referenced + scope = scope->find_typedef_scope(des, this); + if (!scope) { + cerr << get_fileline() << ": sorry: " + << "Can not find the scope type defintion `" << name << "`." + << endl; + des->errors++; + + // Try to recover + return netvector_t::integer_type(); + } + + return data_type->elaborate_type(des, scope); +} diff --git a/lexor.lex b/lexor.lex index e5ee90a6a..a479f6096 100644 --- a/lexor.lex +++ b/lexor.lex @@ -369,7 +369,7 @@ TU [munpf] identifier here and interpret it in the package scope. */ if (in_package_scope) { if (rc == IDENTIFIER) { - if (data_type_t*type = pform_test_type_identifier(in_package_scope, yylval.text)) { + if (typedef_t*type = pform_test_type_identifier(in_package_scope, yylval.text)) { yylval.type_identifier.text = yylval.text; yylval.type_identifier.type = type; rc = TYPE_IDENTIFIER; @@ -405,7 +405,7 @@ TU [munpf] /* If this identifier names a previously declared type, then return this as a TYPE_IDENTIFIER instead. */ if (rc == IDENTIFIER && gn_system_verilog()) { - if (data_type_t*type = pform_test_type_identifier(yylloc, yylval.text)) { + if (typedef_t*type = pform_test_type_identifier(yylloc, yylval.text)) { yylval.type_identifier.text = yylval.text; yylval.type_identifier.type = type; rc = TYPE_IDENTIFIER; @@ -426,7 +426,7 @@ TU [munpf] } } if (gn_system_verilog()) { - if (data_type_t*type = pform_test_type_identifier(yylloc, yylval.text)) { + if (typedef_t*type = pform_test_type_identifier(yylloc, yylval.text)) { yylval.type_identifier.text = yylval.text; yylval.type_identifier.type = type; return TYPE_IDENTIFIER; diff --git a/net_scope.cc b/net_scope.cc index 70791f094..77a91bc23 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -232,19 +232,20 @@ NetScope*NetScope::find_import(const Design*des, perm_string name) return 0; } -void NetScope::add_typedefs(const map*typedefs) +void NetScope::add_typedefs(const map*typedefs) { if (!typedefs->empty()) typedefs_ = *typedefs; } -NetScope*NetScope::find_typedef_scope(const Design*des, data_type_t*type) +NetScope*NetScope::find_typedef_scope(const Design*des, const typedef_t*type) { assert(type); NetScope *cur_scope = this; while (cur_scope) { - if (cur_scope->typedefs_.find(type->name) != cur_scope->typedefs_.end()) + auto it = cur_scope->typedefs_.find(type->name); + if (it != cur_scope->typedefs_.end() && it->second == type) return cur_scope; NetScope*import_scope = cur_scope->find_import(des, type->name); if (import_scope) diff --git a/netlist.h b/netlist.h index 28d75c2e1..7ad1454f4 100644 --- a/netlist.h +++ b/netlist.h @@ -940,10 +940,10 @@ class NetScope : public Definitions, public Attrib { void add_imports(const std::map*imports); NetScope*find_import(const Design*des, perm_string name); - void add_typedefs(const std::map*typedefs); + void add_typedefs(const std::map*typedefs); /* Search the scope hierarchy for the scope where 'type' was defined. */ - NetScope*find_typedef_scope(const Design*des, data_type_t*type); + NetScope*find_typedef_scope(const Design*des, const typedef_t*type); /* Parameters exist within a scope, and these methods allow one to manipulate the set. In these cases, the name is the @@ -1268,7 +1268,7 @@ class NetScope : public Definitions, public Attrib { const std::map*imports_; - std::maptypedefs_; + std::maptypedefs_; NetEvent *events_; diff --git a/parse.y b/parse.y index 4b7748057..875749ff4 100644 --- a/parse.y +++ b/parse.y @@ -438,7 +438,7 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector($1.type); + { class_type_t*tmp = dynamic_cast($1.type->get_data_type()); if (tmp == 0) { yyerror(@1, "Type name \"%s\"is not a predeclared class name.", $1.text); } @@ -2656,7 +2656,7 @@ block_item_decls_opt type_declaration : K_typedef data_type IDENTIFIER dimensions_opt ';' { perm_string name = lex_strings.make($3); - pform_set_typedef(name, $2, $4); + pform_set_typedef(@3, name, $2, $4); delete[]$3; } @@ -2665,12 +2665,7 @@ type_declaration inherited from a different scope. */ | K_typedef data_type TYPE_IDENTIFIER dimensions_opt ';' { perm_string name = lex_strings.make($3.text); - if (pform_test_type_identifier_local(name)) { - yyerror(@3, "error: Typedef identifier \"%s\" is already a type name.", $3.text); - delete $4; - } else { - pform_set_typedef(name, $2, $4); - } + pform_set_typedef(@3, name, $2, $4); delete[]$3.text; } @@ -2682,7 +2677,7 @@ type_declaration perm_string name = lex_strings.make($3); class_type_t*tmp = new class_type_t(name); FILE_NAME(tmp, @3); - pform_set_typedef(name, tmp, NULL); + pform_set_typedef(@3, name, tmp, NULL); delete[]$3; } | K_typedef K_enum IDENTIFIER ';' @@ -2697,7 +2692,7 @@ type_declaration perm_string name = lex_strings.make($2); class_type_t*tmp = new class_type_t(name); FILE_NAME(tmp, @2); - pform_set_typedef(name, tmp, NULL); + pform_set_typedef(@3, name, tmp, NULL); delete[]$2; } diff --git a/parse_misc.h b/parse_misc.h index c20cc0e4e..e9a1477bb 100644 --- a/parse_misc.h +++ b/parse_misc.h @@ -89,10 +89,8 @@ extern void lex_in_package_scope(PPackage*pkg); * parser detects typedefs and marks the typedef'ed identifiers as * type names. */ -extern data_type_t* pform_test_type_identifier(const YYLTYPE&loc, const char*txt); -extern data_type_t* pform_test_type_identifier(PPackage*pkg, const char*txt); - -extern bool pform_test_type_identifier_local(perm_string txt); +extern typedef_t* pform_test_type_identifier(const YYLTYPE&loc, const char*txt); +extern typedef_t* pform_test_type_identifier(PPackage*pkg, const char*txt); /* * Test if this identifier is a package name. The pform needs to help diff --git a/pform.cc b/pform.cc index a266ab043..f1602a589 100644 --- a/pform.cc +++ b/pform.cc @@ -858,18 +858,28 @@ void pform_put_enum_type_in_scope(enum_type_t*enum_set) lexical_scope->enum_sets.push_back(enum_set); } -void pform_set_typedef(perm_string name, data_type_t*data_type, std::list*unp_ranges) +void pform_set_typedef(const struct vlltype&loc, perm_string name, + data_type_t*data_type, + std::list*unp_ranges) { if(unp_ranges) data_type = new uarray_type_t(data_type, unp_ranges); - add_local_symbol(lexical_scope, name, data_type); + typedef_t *&td = lexical_scope->typedefs[name]; + if (!td) { + td = new typedef_t(name); + FILE_NAME(td, loc); + add_local_symbol(lexical_scope, name, td); + } - data_type_t*&ref = lexical_scope->typedefs[name]; - - ivl_assert(*data_type, ref == 0); - ref = data_type; - ref->name = name; + if (!td->set_data_type(data_type)) { + cerr << loc << " error: Type identifier `" << name + << "` has already been declared in this scope at " + << td->get_data_type()->get_fileline() << "." + << endl; + error_count++; + delete data_type; + } } void pform_set_type_referenced(const struct vlltype&loc, const char*name) @@ -878,13 +888,13 @@ void pform_set_type_referenced(const struct vlltype&loc, const char*name) check_potential_imports(loc, lex_name, false); } -data_type_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt) +typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt) { perm_string name = lex_strings.make(txt); LexicalScope*cur_scope = lexical_scope; do { - map::iterator cur; + LexicalScope::typedef_map_t::iterator cur; // First look to see if this identifier is imported from // a package. If it is, see if it is a type in that @@ -924,24 +934,6 @@ data_type_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt return 0; } -/* - * The parser uses this function to test if the name is a typedef in - * the current scope. We use this to know if we can override the - * definition because it shadows a containing scope. - */ -bool pform_test_type_identifier_local(perm_string name) -{ - LexicalScope*cur_scope = lexical_scope; - - map::iterator cur; - - cur = cur_scope->typedefs.find(name); - if (cur != cur_scope->typedefs.end()) - return true; - - return false; -} - PECallFunction* pform_make_call_function(const struct vlltype&loc, const pform_name_t&name, const list&parms) diff --git a/pform.h b/pform.h index 928dcf97b..66c28900c 100644 --- a/pform.h +++ b/pform.h @@ -303,7 +303,8 @@ extern void pform_make_elab_task(const struct vlltype&li, perm_string name, const std::list¶ms); -extern void pform_set_typedef(perm_string name, data_type_t*data_type, +extern void pform_set_typedef(const struct vlltype&loc, perm_string name, + data_type_t*data_type, std::list*unp_ranges); extern void pform_set_type_referenced(const struct vlltype&loc, const char*name); diff --git a/pform_dump.cc b/pform_dump.cc index f6bc1c1b3..15c7504e9 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1502,10 +1502,10 @@ void PGenerate::dump(ostream&out, unsigned indent) const void LexicalScope::dump_typedefs_(ostream&out, unsigned indent) const { - typedef map::const_iterator iter_t; + typedef typedef_map_t::const_iterator iter_t; for (iter_t cur = typedefs.begin() ; cur != typedefs.end() ; ++ cur) { out << setw(indent) << "" << "typedef of " << cur->first << ":" << endl; - cur->second->pform_dump(out, indent+4); + cur->second->get_data_type()->pform_dump(out, indent+4); } } diff --git a/pform_package.cc b/pform_package.cc index 9e33f2e4e..a772c26ea 100644 --- a/pform_package.cc +++ b/pform_package.cc @@ -143,10 +143,12 @@ PExpr* pform_package_ident(const struct vlltype&loc, return tmp; } -data_type_t* pform_test_type_identifier(PPackage*pkg, const char*txt) +typedef_t* pform_test_type_identifier(PPackage*pkg, const char*txt) { perm_string use_name = lex_strings.make(txt); - map::const_iterator cur = pkg->typedefs.find(use_name); + LexicalScope::typedef_map_t::const_iterator cur; + + cur = pkg->typedefs.find(use_name); if (cur != pkg->typedefs.end()) return cur->second; diff --git a/pform_types.cc b/pform_types.cc index 56509a54c..5f538b5e7 100644 --- a/pform_types.cc +++ b/pform_types.cc @@ -44,3 +44,13 @@ PNamedItem::SymbolType class_type_t::symbol_type() const { return CLASS; } + +bool typedef_t::set_data_type(data_type_t *t) +{ + if (data_type.get()) + return false; + + data_type.reset(t); + + return true; +} diff --git a/pform_types.h b/pform_types.h index 2ef0585c6..2b0d63779 100644 --- a/pform_types.h +++ b/pform_types.h @@ -162,20 +162,31 @@ class data_type_t : public PNamedItem { virtual SymbolType symbol_type() const; - virtual NetScope *find_scope(Design* des, NetScope *scope) const; - - perm_string name; - private: // Elaborate the type to an ivl_type_s type. virtual ivl_type_t elaborate_type_raw(Design*des, NetScope*scope) const; + virtual NetScope *find_scope(Design* des, NetScope *scope) const; // Keep per-scope elaboration results cached. std::map cache_type_elaborate_; }; +struct typedef_t : public PNamedItem { + explicit typedef_t(perm_string n) : name(n) { }; + + ivl_type_t elaborate_type(Design*des, NetScope*scope); + + bool set_data_type(data_type_t *t); + const data_type_t *get_data_type() const { return data_type.get(); } + +protected: + std::unique_ptr data_type; +public: + perm_string name; +}; + struct typeref_t : public data_type_t { - explicit typeref_t(data_type_t *t, PScope *s = 0) : scope(s), type(t) {} + explicit typeref_t(typedef_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; @@ -184,7 +195,7 @@ struct typeref_t : public data_type_t { private: PScope *scope; - data_type_t *type; + typedef_t *type; }; struct void_type_t : public data_type_t { @@ -335,7 +346,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) { 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; @@ -370,6 +381,8 @@ struct class_type_t : public data_type_t { ivl_type_t elaborate_type_raw(Design*, NetScope*) const; + perm_string name; + virtual SymbolType symbol_type() const; };