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 <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-03-31 15:08:51 +02:00
parent 699ceb15a5
commit cdc9629ce7
6 changed files with 161 additions and 67 deletions

View File

@ -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<const netenum_t *>(elab_type);
break;
case STRUCT: {
const netstruct_t *struct_type = dynamic_cast<const netstruct_t *>(elab_type);
type_ok = struct_type && !struct_type->union_flag();
break;
}
case UNION: {
const netstruct_t *struct_type = dynamic_cast<const netstruct_t *>(elab_type);
type_ok = struct_type && struct_type->union_flag();
break;
}
case CLASS:
type_ok = dynamic_cast<const netclass_t *>(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;
}

104
parse.y
View File

@ -464,6 +464,8 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector<Statem
std::list<index_component_t> *dimensions;
LexicalScope::lifetime_t lifetime;
enum typedef_t::basic_type typedef_basic_type;
};
%token <text> IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL
@ -589,6 +591,7 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector<Statem
%type <text> event_variable label_opt class_declaration_endlabel_opt
%type <text> block_identifier_opt
%type <text> identifier_name
%type <perm_strings> event_variable_list
%type <perm_strings> list_of_identifiers loop_variables
%type <port_list> list_of_port_identifiers list_of_variable_port_identifiers
@ -646,7 +649,6 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector<Statem
%type <data_type> packed_array_data_type
%type <data_type> ps_type_identifier
%type <data_type> simple_packed_type
%type <class_type> class_identifier
%type <struct_member> struct_union_member
%type <struct_members> struct_union_member_list
%type <struct_type> struct_data_type
@ -706,6 +708,8 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector<Statem
%type <letter> compressed_operator
%type <typedef_basic_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<class_type_t*>($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 <TYPE_IDENTIFIER>` 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;

View File

@ -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<pform_range_t>*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<pform_range_t>*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

View File

@ -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<pform_range_t>*unp_ranges);
std::list<pform_range_t>*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);

View File

@ -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;
}

View File

@ -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_t> 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 */