From cdc9629ce76f8c81b79bc30e2b57cf02500f7f93 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 31 Mar 2022 15:08:51 +0200 Subject: [PATCH] Add support for forward type declarations SystemVerilog supports forward type declarations. This allows to declare a type identifier and use it, e.g. in a signal declaration, before declaring what the actual type is. The type still needs to be fully defined eventually in the same scope as its forward type declaration. E.g. ``` typedef T; T x; typedef int T; ``` The forward type definition can also contain the kind of the type it is going to be. E.g struct, union, class, etc. The LRM calls this the basic type. If the actual type is not of the basic type specified in the forward type declaration this is an error. E.g. ``` typedef struct T; typedef int T; // Error, int is not a struct ``` It is legal to have more than one forward type declaration for the same type name, as long as the basic type is the compatible. It is even legal to have a forward type declaration after the actual type has already been declared. E.g. ``` typedef T; typedef int T; typedef T; ``` Implement support for forward type definitions as part of the new typedef_t. The basic type will be attached to the typedef_t. The compatibility of the basic type for multiple forward type declarations will be checked in the parser. The compatibility of the basic type to the actual type will be checked during elaboration, once the actual type is known. Signed-off-by: Lars-Peter Clausen --- elab_type.cc | 37 +++++++++++++++++- parse.y | 104 ++++++++++++++++++++++--------------------------- pform.cc | 33 +++++++++++++--- pform.h | 4 +- pform_types.cc | 35 +++++++++++++++++ pform_types.h | 15 ++++++- 6 files changed, 161 insertions(+), 67 deletions(-) diff --git a/elab_type.cc b/elab_type.cc index cb0156dfd..695ed5cf6 100644 --- a/elab_type.cc +++ b/elab_type.cc @@ -439,5 +439,40 @@ ivl_type_t typedef_t::elaborate_type(Design *des, NetScope *scope) return netvector_t::integer_type(); } - return data_type->elaborate_type(des, scope); + ivl_type_t elab_type = data_type->elaborate_type(des, scope); + if (!elab_type) + return netvector_t::integer_type(); + + bool type_ok = true; + switch (basic_type) { + case ENUM: + type_ok = dynamic_cast(elab_type); + break; + case STRUCT: { + const netstruct_t *struct_type = dynamic_cast(elab_type); + type_ok = struct_type && !struct_type->union_flag(); + break; + } + case UNION: { + const netstruct_t *struct_type = dynamic_cast(elab_type); + type_ok = struct_type && struct_type->union_flag(); + break; + } + case CLASS: + type_ok = dynamic_cast(elab_type); + break; + default: + break; + } + + if (!type_ok) { + cerr << data_type->get_fileline() << " error: " + << "Unexpected type `" << *elab_type << "` for `" << name + << "`. It was forward declared as `" << basic_type + << "` at " << get_fileline() << "." + << endl; + des->errors++; + } + + return elab_type; } diff --git a/parse.y b/parse.y index 875749ff4..a4d427704 100644 --- a/parse.y +++ b/parse.y @@ -464,6 +464,8 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector *dimensions; LexicalScope::lifetime_t lifetime; + + enum typedef_t::basic_type typedef_basic_type; }; %token IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL @@ -589,6 +591,7 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector event_variable label_opt class_declaration_endlabel_opt %type block_identifier_opt +%type identifier_name %type event_variable_list %type list_of_identifiers loop_variables %type list_of_port_identifiers list_of_variable_port_identifiers @@ -646,7 +649,6 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector packed_array_data_type %type ps_type_identifier %type simple_packed_type -%type class_identifier %type struct_union_member %type struct_union_member_list %type struct_data_type @@ -706,6 +708,8 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector compressed_operator +%type typedef_basic_type + %token K_TAND %nonassoc K_PLUS_EQ K_MINUS_EQ K_MUL_EQ K_DIV_EQ K_MOD_EQ K_AND_EQ K_OR_EQ %nonassoc K_XOR_EQ K_LS_EQ K_RS_EQ K_RSS_EQ K_NB_TRIGGER @@ -786,15 +790,22 @@ block_identifier_opt /* */ ; class_declaration /* IEEE1800-2005: A.1.2 */ - : K_virtual_opt K_class lifetime_opt class_identifier class_declaration_extends_opt ';' - { pform_start_class_declaration(@2, $4, $5.type, $5.exprs, $3); } + : K_virtual_opt K_class lifetime_opt identifier_name class_declaration_extends_opt ';' + { + perm_string name = lex_strings.make($4); + class_type_t *class_type= new class_type_t(name); + FILE_NAME(class_type, @4); + pform_set_typedef(@4, name, class_type, nullptr); + pform_start_class_declaration(@2, class_type, $5.type, $5.exprs, $3); + } class_items_opt K_endclass { // Process a class. pform_end_class_declaration(@9); } class_declaration_endlabel_opt { // Wrap up the class. - check_end_label(@11, "class", $4->name, $11); + check_end_label(@11, "class", $4, $11); + delete[] $4; } ; @@ -803,33 +814,18 @@ class_constraint /* IEEE1800-2005: A.1.8 */ | constraint_declaration ; -class_identifier - : IDENTIFIER - { // Create a synthetic typedef for the class name so that the - // lexor detects the name as a type. - perm_string name = lex_strings.make($1); - class_type_t*tmp = new class_type_t(name); - FILE_NAME(tmp, @1); - pform_set_typedef(@1, name, tmp, NULL); - delete[]$1; - $$ = tmp; - } - | TYPE_IDENTIFIER - { 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); - } - delete[]$1.text; - $$ = tmp; - } + // This is used in places where a new type can be declared or an existig type + // is referenced. E.g. typedefs. +identifier_name + : IDENTIFIER { $$ = $1; } + | TYPE_IDENTIFIER { $$ = $1.text; } ; /* The endlabel after a class declaration is a little tricky because the class name is detected by the lexor as a TYPE_IDENTIFIER if it does indeed match a name. */ class_declaration_endlabel_opt - : ':' TYPE_IDENTIFIER { $$ = $2.text; } - | ':' IDENTIFIER { $$ = $2; } + : ':' identifier_name { $$ = $2; } | { $$ = 0; } ; @@ -2651,51 +2647,43 @@ block_item_decls_opt | { $$ = false; } ; + /* We need to handle K_enum separately because + * `typedef enum ` can either be the start of a enum forward + * declaration or a enum type declaration with a type identifier as its base + * type. And this abmiguity can not be resolved if we reduce the K_enum to + * typedef_basic_type. */ +typedef_basic_type + : K_struct { $$ = typedef_t::STRUCT; } + | K_union { $$ = typedef_t::UNION; } + | K_class { $$ = typedef_t::CLASS; } + ; + /* Type declarations are parsed here. The rule actions call pform functions that add the declaration to the current lexical scope. */ type_declaration - : K_typedef data_type IDENTIFIER dimensions_opt ';' + : K_typedef data_type identifier_name dimensions_opt ';' { perm_string name = lex_strings.make($3); pform_set_typedef(@3, name, $2, $4); delete[]$3; } - /* If the IDENTIFIER already is a typedef, it is possible for this - code to override the definition, but only if the typedef is - inherited from a different scope. */ - | K_typedef data_type TYPE_IDENTIFIER dimensions_opt ';' - { perm_string name = lex_strings.make($3.text); - pform_set_typedef(@3, name, $2, $4); - delete[]$3.text; - } - /* These are forward declarations... */ - | K_typedef K_class IDENTIFIER ';' - { // Create a synthetic typedef for the class name so that the - // lexor detects the name as a type. - perm_string name = lex_strings.make($3); - class_type_t*tmp = new class_type_t(name); - FILE_NAME(tmp, @3); - pform_set_typedef(@3, name, tmp, NULL); - delete[]$3; - } - | K_typedef K_enum IDENTIFIER ';' - { yyerror(@1, "sorry: Enum forward declarations not supported yet."); } - | K_typedef K_struct IDENTIFIER ';' - { yyerror(@1, "sorry: Struct forward declarations not supported yet."); } - | K_typedef K_union IDENTIFIER ';' - { yyerror(@1, "sorry: Union forward declarations not supported yet."); } - | K_typedef IDENTIFIER ';' - { // Create a synthetic typedef for the class name so that the - // lexor detects the name as a type. - perm_string name = lex_strings.make($2); - class_type_t*tmp = new class_type_t(name); - FILE_NAME(tmp, @2); - pform_set_typedef(@3, name, tmp, NULL); + | K_typedef identifier_name ';' + { perm_string name = lex_strings.make($2); + pform_forward_typedef(@2, name, typedef_t::ANY); delete[]$2; } - + | K_typedef typedef_basic_type identifier_name ';' + { perm_string name = lex_strings.make($3); + pform_forward_typedef(@3, name, $2); + delete[]$3; + } + | K_typedef K_enum identifier_name ';' + { perm_string name = lex_strings.make($3); + pform_forward_typedef(@3, name, typedef_t::ENUM); + delete[]$3; + } | K_typedef error ';' { yyerror(@2, "error: Syntax error in typedef clause."); yyerrok; diff --git a/pform.cc b/pform.cc index f1602a589..7405a91c9 100644 --- a/pform.cc +++ b/pform.cc @@ -858,19 +858,40 @@ void pform_put_enum_type_in_scope(enum_type_t*enum_set) lexical_scope->enum_sets.push_back(enum_set); } -void pform_set_typedef(const struct vlltype&loc, perm_string name, - data_type_t*data_type, - std::list*unp_ranges) +static typedef_t *pform_get_typedef(const struct vlltype&loc, perm_string name) { - if(unp_ranges) - data_type = new uarray_type_t(data_type, unp_ranges); - 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); } + return td; +} + +void pform_forward_typedef(const struct vlltype&loc, perm_string name, + enum typedef_t::basic_type basic_type) +{ + typedef_t *td = pform_get_typedef(loc, name); + + if (!td->set_basic_type(basic_type)) { + cout << loc << " error: Incompatible basic type `" << basic_type + << "` for `" << name + << "`. Previously declared in this scope as `" + << td->get_basic_type() << "` at " << td->get_fileline() << "." + << endl; + error_count++; + } +} + +void pform_set_typedef(const struct vlltype&loc, perm_string name, + data_type_t*data_type, + std::list*unp_ranges) +{ + typedef_t *td = pform_get_typedef(loc, name); + + if(unp_ranges) + data_type = new uarray_type_t(data_type, unp_ranges); if (!td->set_data_type(data_type)) { cerr << loc << " error: Type identifier `" << name diff --git a/pform.h b/pform.h index 66c28900c..ed7577616 100644 --- a/pform.h +++ b/pform.h @@ -305,7 +305,9 @@ extern void pform_make_elab_task(const struct vlltype&li, extern void pform_set_typedef(const struct vlltype&loc, perm_string name, data_type_t*data_type, - std::list*unp_ranges); + std::list*unp_ranges = nullptr); +extern void pform_forward_typedef(const struct vlltype&loc, perm_string name, + enum typedef_t::basic_type basic_type); extern void pform_set_type_referenced(const struct vlltype&loc, const char*name); diff --git a/pform_types.cc b/pform_types.cc index 5f538b5e7..f69c5d908 100644 --- a/pform_types.cc +++ b/pform_types.cc @@ -54,3 +54,38 @@ bool typedef_t::set_data_type(data_type_t *t) return true; } + +bool typedef_t::set_basic_type(enum basic_type bt) +{ + if (bt == ANY) + return true; + if (basic_type != ANY && bt != basic_type) + return false; + + basic_type = bt; + + return true; +} + +std::ostream& operator<< (std::ostream&out, enum typedef_t::basic_type bt) +{ + switch (bt) { + case typedef_t::ANY: + out << "any"; + break; + case typedef_t::ENUM: + out << "enum"; + break; + case typedef_t::STRUCT: + out << "struct"; + break; + case typedef_t::UNION: + out << "union"; + break; + case typedef_t::CLASS: + out << "class"; + break; + } + + return out; +} diff --git a/pform_types.h b/pform_types.h index 509e1d5e2..1029e7225 100644 --- a/pform_types.h +++ b/pform_types.h @@ -174,14 +174,26 @@ class data_type_t : public PNamedItem { }; struct typedef_t : public PNamedItem { - explicit typedef_t(perm_string n) : name(n) { }; + explicit typedef_t(perm_string n) : basic_type(ANY), name(n) { }; ivl_type_t elaborate_type(Design*des, NetScope*scope); + enum basic_type { + ANY, + ENUM, + STRUCT, + UNION, + CLASS + }; + bool set_data_type(data_type_t *t); const data_type_t *get_data_type() const { return data_type.get(); } + bool set_basic_type(basic_type bt); + enum basic_type get_basic_type() const { return basic_type; } + protected: + enum basic_type basic_type; std::unique_ptr data_type; public: perm_string name; @@ -447,5 +459,6 @@ static inline std::ostream& operator<< (std::ostream&out, const data_type_t&that extern std::ostream& operator<< (std::ostream&out, const pform_name_t&); extern std::ostream& operator<< (std::ostream&out, const name_component_t&that); extern std::ostream& operator<< (std::ostream&out, const index_component_t&that); +extern std::ostream& operator<< (std::ostream&out, enum typedef_t::basic_type bt); #endif /* IVL_pform_types_H */