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:
parent
699ceb15a5
commit
cdc9629ce7
37
elab_type.cc
37
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<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
104
parse.y
|
|
@ -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;
|
||||
|
|
|
|||
33
pform.cc
33
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<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
|
||||
|
|
|
|||
4
pform.h
4
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<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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue