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_scope.cc b/elab_scope.cc index a03d4df75..716c3bed4 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -169,34 +169,11 @@ static void collect_scope_specparams(Design*des, NetScope*scope, static void elaborate_scope_enumeration(Design*des, NetScope*scope, enum_type_t*enum_type) { - ivl_type_t base_type; bool rc_flag; - base_type = enum_type->base_type->elaborate_type(des, scope); + enum_type->elaborate_type(des, scope); - const struct netvector_t *vec_type = dynamic_cast(base_type); - - if (!vec_type && !dynamic_cast(base_type)) { - cerr << enum_type->get_fileline() << ": error: " - << "Invalid enum base type `" << *base_type << "`." - << endl; - des->errors++; - } else if (base_type->slice_dimensions().size() > 1) { - cerr << enum_type->get_fileline() << ": error: " - << "Enum type must not have more than 1 packed dimension." - << endl; - des->errors++; - } - - bool integer_flag = false; - if (vec_type) - integer_flag = vec_type->get_isint(); - - netenum_t*use_enum = new netenum_t(base_type, enum_type->names->size(), - integer_flag); - - use_enum->set_line(*enum_type); - scope->add_enumeration_set(enum_type, use_enum); + netenum_t *use_enum = scope->enumeration_for_key(enum_type); size_t name_idx = 0; // Find the enumeration width. diff --git a/elab_type.cc b/elab_type.cc index 9c8280c61..695ed5cf6 100644 --- a/elab_type.cc +++ b/elab_type.cc @@ -43,14 +43,26 @@ 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); if (pos != cache_type_elaborate_.end() && pos->first == use_definitions) return pos->second; - ivl_type_t tmp = elaborate_type_raw(des, scope); + ivl_type_t tmp; + if (elaborating) { + des->errors++; + cerr << get_fileline() << ": error: " + << "Circular type definition found involving `" << *this << "`." + << endl; + // Try to recover + tmp = netvector_t::integer_type(); + } else { + elaborating = true; + tmp = elaborate_type_raw(des, scope); + elaborating = false; + } + cache_type_elaborate_.insert(pos, pair(scope, tmp)); return tmp; } @@ -124,13 +136,34 @@ ivl_type_t class_type_t::elaborate_type_raw(Design*des, NetScope*scope) const * available at the right time. At that time, the netenum_t* object is * stashed in the scope so that I can retrieve it here. */ -ivl_type_t enum_type_t::elaborate_type_raw(Design*, NetScope*scope) const +ivl_type_t enum_type_t::elaborate_type_raw(Design *des, NetScope *scope) const { - ivl_assert(*this, scope); - ivl_type_t tmp = scope->enumeration_for_key(this); - if (tmp == 0 && scope->unit()) - tmp = scope->unit()->enumeration_for_key(this); - return tmp; + ivl_type_t base = base_type->elaborate_type(des, scope); + + const struct netvector_t *vec_type = dynamic_cast(base); + + if (!vec_type && !dynamic_cast(base)) { + cerr << get_fileline() << ": error: " + << "Invalid enum base type `" << *base << "`." + << endl; + des->errors++; + } else if (base->slice_dimensions().size() > 1) { + cerr << get_fileline() << ": error: " + << "Enum type must not have more than 1 packed dimension." + << endl; + des->errors++; + } + + bool integer_flag = false; + if (vec_type) + integer_flag = vec_type->get_isint(); + + netenum_t *type = new netenum_t(base, names->size(), integer_flag); + type->set_line(*this); + + scope->add_enumeration_set(this, type); + + return type; } ivl_type_t vector_type_t::elaborate_type_raw(Design*des, NetScope*scope) const @@ -172,13 +205,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 +413,66 @@ 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(); + } + + 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/ivtest/ivltests/sv_typedef_circular1.v b/ivtest/ivltests/sv_typedef_circular1.v new file mode 100644 index 000000000..998a21a50 --- /dev/null +++ b/ivtest/ivltests/sv_typedef_circular1.v @@ -0,0 +1,14 @@ +// Check that circular type definitions are detected and an error is reported. + +module test; + typedef T1; + typedef T1 T2; + typedef T2 T1; + + T2 x; + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_circular2.v b/ivtest/ivltests/sv_typedef_circular2.v new file mode 100644 index 000000000..638744e20 --- /dev/null +++ b/ivtest/ivltests/sv_typedef_circular2.v @@ -0,0 +1,18 @@ +// Check that longer chains of circular type definitions are detected as an +// error. + +module test; + + typedef T1; + + typedef struct packed { + T1 x; + } T2; + + typedef T2 [1:0] T3; + + typedef T3 T1; + + T1 x; + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_base.v b/ivtest/ivltests/sv_typedef_fwd_base.v new file mode 100644 index 000000000..46562aca9 --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_base.v @@ -0,0 +1,47 @@ +// Check that forward typedefs of basic types are supported + +`define check(val, exp) \ + if (val != exp) begin \ + $display("FAILED(%0d). '%s' expected ", `__LINE__, `"val`", exp, " got ", val, ); \ + failed = 1'b1; \ + end + +bit failed = 1'b0; + +module test; + typedef T1; + typedef T1; // Check forward typedef twice for the same type + typedef T2; + typedef T3; + typedef T4; + + T1 x = -1; + T2 y = 1.23; + T3 z = "Hello"; + T4 w; + + typedef integer T1; + // There can be as many forward typedefs as we like, even after the type + // itself has already been declared. + typedef T1; + typedef T1; + + typedef real T2; + typedef string T3; + typedef logic [1:0] T4[3:0]; + + initial begin + `check($bits(x), $bits(integer)) + `check($bits(T1), $bits(integer)) + `check(x, -1) + `check(y, 1.23) + `check(z, "Hello") + `check($unpacked_dimensions(w), 1) + `check($size(w), 4) + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_class.v b/ivtest/ivltests/sv_typedef_fwd_class.v new file mode 100644 index 000000000..4a672f5c5 --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_class.v @@ -0,0 +1,46 @@ +// Check that forward typedefs of classes are supported + +module test; + +`define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %d, got %d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end + + bit failed = 1'b0; + + typedef class C; + typedef C; + + C x; + + class C; + int x; + endclass + + C y; + + // There can be as many forward typedefs as we like, even after the type + // itself has already been declared. + typedef C; + typedef class C; + + C z; + + initial begin + // Check they are all the same type and can be assigned to each other + x = y; + y = z; + z = x; + + `check($bits(x.x), $bits(int)); + `check($bits(y.x), $bits(int)); + `check($bits(z.x), $bits(int)); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_enum1.v b/ivtest/ivltests/sv_typedef_fwd_enum1.v new file mode 100644 index 000000000..6e3a90132 --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_enum1.v @@ -0,0 +1,44 @@ +// Check that forward enum typedefs are supported + +module test; + +`define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end + + bit failed = 1'b0; + + typedef T; + typedef enum T; + + T x; + + typedef enum integer { + A, B + } T; + + T y; + + typedef enum T; + typedef T; + + T z; + + initial begin + // Check that they are all the same type and can be assigned to each other + x = y; + y = z; + z = x; + + `check($bits(x), $bits(integer)) + `check($bits(y), $bits(integer)) + `check($bits(z), $bits(integer)) + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_enum2.v b/ivtest/ivltests/sv_typedef_fwd_enum2.v new file mode 100644 index 000000000..d4fc02400 --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_enum2.v @@ -0,0 +1,23 @@ +// Check that the base type of an enum can be a forward typedef + +module test; + + typedef T1; + + typedef enum T1 { + A, B + } T2; + + typedef logic [31:0] T1; + + T2 z; + + initial begin + if ($bits(z) == 32) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_enum3.v b/ivtest/ivltests/sv_typedef_fwd_enum3.v new file mode 100644 index 000000000..3668d26ba --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_enum3.v @@ -0,0 +1,26 @@ +// Check that a forwarded enum typedef can be referenced in a class + +module test; + + typedef T; + + class C; + T x; + endclass + + typedef enum integer { + X, Y + } T; + + initial begin + C c; + c = new; + + if ($bits(c.x) == 32) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_enum_fail.v b/ivtest/ivltests/sv_typedef_fwd_enum_fail.v new file mode 100644 index 000000000..e05586e4e --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_enum_fail.v @@ -0,0 +1,14 @@ +// Check that a enum can't be its own base type + +module test; + + typedef T; + typedef enum T { + A, B + } T; + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_struct.v b/ivtest/ivltests/sv_typedef_fwd_struct.v new file mode 100644 index 000000000..f3593462b --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_struct.v @@ -0,0 +1,39 @@ +// Check that forward struct typedefs are supported + +module test; + +`define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end + + bit failed = 1'b0; + + typedef T; + typedef struct T; + + T x; + + typedef struct packed { + int x; + } T; + + T y; + + typedef struct T; + typedef T; + + T z; + + initial begin + `check($bits(x), $bits(int)) + `check($bits(y), $bits(int)) + `check($bits(z), $bits(int)) + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_struct_fail.v b/ivtest/ivltests/sv_typedef_fwd_struct_fail.v new file mode 100644 index 000000000..eb680bd96 --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_struct_fail.v @@ -0,0 +1,18 @@ +// Check that it is an error to use a forwarded struct type as the type for the +// member in the struct itself. + +module test; + + typedef T; + + typedef struct packed { + T x; + } T; + + T x; + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_union.v b/ivtest/ivltests/sv_typedef_fwd_union.v new file mode 100644 index 000000000..9356d9065 --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_union.v @@ -0,0 +1,41 @@ +// Check that forward typdes of unions are supported + +module test; + +`define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end + + bit failed = 1'b0; + typedef union T; + typedef T; + + T x; + + typedef union packed { + int x; + logic [3:0][7:0] y; + } T; + + T y; + + // There can be as many forward typedefs as we like, even after the type + // itself has already been declared. + typedef T; + typedef union T; + + T z; + + initial begin + `check($bits(x), $bits(int)); + `check($bits(y), $bits(int)); + `check($bits(z), $bits(int)); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_typedef_fwd_union_fail.v b/ivtest/ivltests/sv_typedef_fwd_union_fail.v new file mode 100644 index 000000000..b77dc13da --- /dev/null +++ b/ivtest/ivltests/sv_typedef_fwd_union_fail.v @@ -0,0 +1,18 @@ +// Check that it is an error to use a forwarded union type as the type for the +// members in the union itself. + +module test; + + typedef T; + + typedef union packed { + T x; + } T; + + T x; + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index cf7a8f06a..f9456c777 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -716,10 +716,22 @@ sv_typedef_array_base2 normal,-g2009 ivltests sv_typedef_array_base3 normal,-g2009 ivltests sv_typedef_array_base4 normal,-g2009 ivltests sv_typedef_chained normal,-g2009 ivltests +sv_typedef_circular1 CE,-g2009 ivltests +sv_typedef_circular2 CE,-g2009 ivltests sv_typedef_darray_base1 normal,-g2009 ivltests sv_typedef_darray_base2 normal,-g2009 ivltests sv_typedef_darray_base3 normal,-g2009 ivltests sv_typedef_darray_base4 normal,-g2009 ivltests +sv_typedef_fwd_base normal,-g2009 ivltests +sv_typedef_fwd_class normal,-g2009 ivltests +sv_typedef_fwd_union normal,-g2009 ivltests +sv_typedef_fwd_union_fail CE,-g2009 ivltests +sv_typedef_fwd_enum1 normal,-g2009 ivltests +sv_typedef_fwd_enum2 normal,-g2009 ivltests +sv_typedef_fwd_enum3 normal,-g2009 ivltests +sv_typedef_fwd_enum_fail CE,-g2009 ivltests +sv_typedef_fwd_struct normal,-g2009 ivltests +sv_typedef_fwd_struct_fail CE,-g2009 ivltests sv_typedef_nested_array normal,-g2009 ivltests sv_typedef_queue_base1 normal,-g2009 ivltests sv_typedef_queue_base2 normal,-g2009 ivltests diff --git a/ivtest/regress-vlog95.list b/ivtest/regress-vlog95.list index 30cbdc3e6..e520e2de3 100644 --- a/ivtest/regress-vlog95.list +++ b/ivtest/regress-vlog95.list @@ -302,6 +302,7 @@ sv_string5 CE,-g2009 ivltests sv_string6 CE,-g2009,-pallowsigned=1 ivltests sv_string7 CE,-g2009,-pallowsigned=1 ivltests sv_string7b CE,-g2009,-pallowsigned=1 ivltests +sv_typedef_fwd_base CE,-g2009 ivltests vhdl_string_lim CE,-g2005-sv,-pallowsigned=1,ivltests/vhdl_string_lim.vhd ivltests vhdl_textio_write CE,-g2005-sv,-pallowsigned=1,ivltests/vhdl_textio_write.vhd ivltests vhdl_textio_read CE,-g2005-sv,-pallowsigned=1,ivltests/vhdl_textio_read.vhd ivltests @@ -440,6 +441,8 @@ sv_port_default8 CE,-g2009,-pallowsigned=1 ivltests sv_port_default9 CE,-g2009 ivltests sv_ps_type_class1 CE,-g2009 ivltests sv_root_class CE,-g2009 ivltests +sv_typedef_fwd_class CE,-g2009 ivltests +sv_typedef_fwd_enum3 CE,-g2009 ivltests sv_typedef_scope3 CE,-g2009 ivltests sv_unit2b CE,-g2009 ivltests sv_unit3b CE,-g2009 ivltests 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..a4d427704 100644 --- a/parse.y +++ b/parse.y @@ -438,7 +438,7 @@ 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(name, tmp, NULL); - delete[]$1; - $$ = tmp; - } - | TYPE_IDENTIFIER - { class_type_t*tmp = dynamic_cast($1.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,56 +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(name, $2, $4); + 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); - 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); - } - 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(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(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/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..7405a91c9 100644 --- a/pform.cc +++ b/pform.cc @@ -858,18 +858,49 @@ 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) +static typedef_t *pform_get_typedef(const struct vlltype&loc, perm_string name) { + 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); - add_local_symbol(lexical_scope, name, data_type); - - 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 +909,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 +955,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..ed7577616 100644 --- a/pform.h +++ b/pform.h @@ -303,8 +303,11 @@ 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, - std::list*unp_ranges); +extern void pform_set_typedef(const struct vlltype&loc, perm_string name, + data_type_t*data_type, + 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_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..f69c5d908 100644 --- a/pform_types.cc +++ b/pform_types.cc @@ -44,3 +44,48 @@ 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; +} + +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 2ef0585c6..1029e7225 100644 --- a/pform_types.h +++ b/pform_types.h @@ -162,20 +162,45 @@ 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; + + bool elaborating = false; // Keep per-scope elaboration results cached. std::map cache_type_elaborate_; }; +struct typedef_t : public PNamedItem { + 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; +}; + 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 +209,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 +360,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 +395,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; }; @@ -432,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 */