From 272ba403f0901b206ff5e14da7770129152e1bcc Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 2 Mar 2024 11:24:01 +0000 Subject: [PATCH] Deferred subprogram instantiation for packages. Fixes #654 --- src/common.c | 13 +++ src/common.h | 1 + src/diag.c | 1 + src/lower.c | 6 ++ src/parse.c | 175 +++++++++++++++++++++++++++++--------- src/sem.c | 11 ++- test/parse/issue654.vhd | 10 +++ test/regress/issue654.vhd | 94 ++++++++++++++++++++ test/regress/testlist.txt | 1 + test/test_parse.c | 19 ++++- test/test_sem.c | 2 +- 11 files changed, 287 insertions(+), 46 deletions(-) create mode 100644 test/regress/issue654.vhd diff --git a/src/common.c b/src/common.c index 04f118b0..6e4e053e 100644 --- a/src/common.c +++ b/src/common.c @@ -744,6 +744,19 @@ bool is_literal(tree_t t) } } +bool is_body(tree_t t) +{ + switch (tree_kind(t)) { + case T_FUNC_BODY: + case T_PROC_BODY: + case T_PACK_BODY: + case T_PROT_BODY: + return true; + default: + return false; + } +} + bool is_guarded_signal(tree_t decl) { return !!(tree_flags(decl) & (TREE_F_BUS | TREE_F_REGISTER)); diff --git a/src/common.h b/src/common.h index c70fd928..2f6c4423 100644 --- a/src/common.h +++ b/src/common.h @@ -64,6 +64,7 @@ tree_t aliased_type_decl(tree_t decl); bool is_loop_stmt(tree_t t); bool is_design_unit(tree_t t); bool is_literal(tree_t t); +bool is_body(tree_t t); bool is_uninstantiated_package(tree_t pack); bool is_uninstantiated_subprogram(tree_t decl); bool is_anonymous_subtype(type_t type); diff --git a/src/diag.c b/src/diag.c index edfe0595..ab476681 100644 --- a/src/diag.c +++ b/src/diag.c @@ -436,6 +436,7 @@ static const struct { { "Type conversions", { [STD_93] = "7.3.5", [STD_08] = "9.3.6" } }, { "External names", { [STD_08] = "8.7" } }, { "Port clauses", { [STD_08] = "6.5.6.3" } }, + { "Subprogram instantiation declarations", { [STD_08] = "4.4" } }, }; diag_t *diag_new(diag_level_t level, const loc_t *loc) diff --git a/src/lower.c b/src/lower.c index a3a1b42d..386825a1 100644 --- a/src/lower.c +++ b/src/lower.c @@ -9824,6 +9824,9 @@ static void lower_decls(lower_unit_t *lu, tree_t scope) tree_t d = tree_decl(scope, i); switch (tree_kind(d)) { case T_FUNC_INST: + if (!is_body(tree_ref(d))) + break; // Deferred instantiation in package body + // Fall-through case T_FUNC_BODY: { ident_t mangled = tree_ident2(d); @@ -9834,6 +9837,9 @@ static void lower_decls(lower_unit_t *lu, tree_t scope) } break; case T_PROC_INST: + if (!is_body(tree_ref(d))) + break; // Deferred instantiation in package body + // Fall-through case T_PROC_BODY: { const bool never_waits = !!(tree_flags(d) & TREE_F_NEVER_WAITS) diff --git a/src/parse.c b/src/parse.c index 5df84f0a..5862d7a5 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1801,36 +1801,37 @@ static void instantiate_helper(tree_t new, tree_t *pdecl, tree_t *pbody) static void instantiate_subprogram(tree_t new, tree_t decl, tree_t body) { - assert(body != NULL); - assert(type_eq(tree_type(body), tree_type(decl))); - tree_t decl_copy = decl, body_copy = body; instantiate_helper(new, &decl_copy, &body_copy); - tree_set_type(new, tree_type(body_copy)); + tree_t src = body_copy ?: decl_copy; + + tree_set_type(new, tree_type(src)); + tree_set_flag(new, tree_flags(decl)); - const int ngenerics = tree_generics(body_copy); + const int ngenerics = tree_generics(src); for (int i = 0; i < ngenerics; i++) - tree_add_generic(new, tree_generic(body_copy, i)); + tree_add_generic(new, tree_generic(src, i)); - const int ndecls = tree_decls(body_copy); - for (int i = 0; i < ndecls; i++) - tree_add_decl(new, tree_decl(body_copy, i)); + const int nports = tree_ports(src); + for (int i = 0; i < nports; i++) + tree_add_port(new, tree_port(src, i)); - const int nstmts = tree_stmts(body_copy); - for (int i = 0; i < nstmts; i++) - tree_add_stmt(new, tree_stmt(body_copy, i)); + if (body != NULL) { + const int ndecls = tree_decls(body_copy); + for (int i = 0; i < ndecls; i++) + tree_add_decl(new, tree_decl(body_copy, i)); - const int nports = tree_ports(body_copy); - for (int i = 0; i < nports; i++) - tree_add_port(new, tree_port(body_copy, i)); + const int nstmts = tree_stmts(body_copy); + for (int i = 0; i < nstmts; i++) + tree_add_stmt(new, tree_stmt(body_copy, i)); - tree_set_flag(new, tree_flags(decl)); - tree_set_flag(new, tree_flags(body)); + tree_set_flag(new, tree_flags(body)); + } // Allow recursive calls to the uninstantiated subprogram map_generic_subprogram(nametab, decl_copy, new); - map_generic_subprogram(nametab, body_copy, new); + if (body != NULL) map_generic_subprogram(nametab, body_copy, new); } static void instantiate_package(tree_t new, tree_t pack, tree_t body) @@ -2468,6 +2469,110 @@ static void find_disconnect_specification(tree_t guard, tree_t target) tree_set_spec(guard, spec); } +static tree_t find_subprogram_body(tree_t decl) +{ + const tree_kind_t decl_kind = tree_kind(decl); + if (decl_kind == T_FUNC_BODY || decl_kind == T_PROC_BODY) + return decl; + + // Attempt to load the package body if available + tree_t pack = tree_container(decl); + if (tree_kind(pack) != T_PACKAGE) + return NULL; + + tree_t du = find_enclosing(nametab, S_DESIGN_UNIT); + if (du == pack) + return NULL; // Avoid referencing old version of current package + + tree_t pack_body, d; + if (tree_kind(du) == T_PACK_BODY && tree_primary(du) == pack) + pack_body = du; + else if ((pack_body = body_of(pack)) == NULL) + return NULL; + + type_t type = tree_type(decl); + ident_t id = tree_ident(decl); + for (int nth = 0; (d = search_decls(pack_body, id, nth)); nth++) { + if (is_subprogram(d) && is_body(d) && type_eq(tree_type(d), type)) + return d; + } + + return NULL; +} + +static void package_body_deferred_instantiation(tree_t pack, tree_t container) +{ + // LRM 08 section 4.4: if the subprogram instantiation declaration + // occurs immediately within an enclosing package declaration, the + // generic-mapped subprogram body occurs at the end of the package + // body corresponding to the enclosing package declaration + + const int ndecls = tree_decls(pack); + for (int i = 0; i < ndecls; i++) { + tree_t decl = tree_decl(pack, i); + + const tree_kind_t dkind = tree_kind(decl); + if (dkind != T_FUNC_INST && dkind != T_PROC_INST) + continue; + else if (!tree_has_ref(decl)) + continue; + + tree_t ref = tree_ref(decl); + if (is_body(ref)) + continue; + + tree_t body = find_subprogram_body(ref); + if (body == NULL) { + diag_t *d = diag_new(DIAG_ERROR, CURRENT_LOC); + diag_printf(d, "subprogram %s cannot be instantiated until its " + "body has been analysed", type_pp(tree_type(decl))); + diag_hint(d, tree_loc(decl), "subprogram instantiation in package " + "declarative part"); + diag_hint(d, NULL, "the instantiated subprogram body occurs at " + "the end of the package body corresponding to the " + "enclosing package declaration"); + diag_lrm(d, STD_08, "4.4"); + diag_emit(d); + } + else { + tree_t inst = tree_new(dkind); + tree_set_ident(inst, tree_ident(decl)); + tree_set_ident2(inst, tree_ident2(decl)); + tree_set_ref(inst, body); + + instantiate_subprogram(inst, ref, body); + + hash_t *gmap = hash_new(16); + const int ngenmaps = tree_genmaps(decl); + for (int i = 0; i < ngenmaps; i++) { + tree_t map = tree_genmap(decl, i); + assert(tree_subkind(map) == P_POS); + + tree_add_genmap(inst, map); + + tree_t g = tree_generic(inst, tree_pos(map)); + tree_t value = tree_value(map); + + switch (tree_class(g)) { + case C_TYPE: + hash_put(gmap, tree_type(g), tree_type(value)); + break; + case C_FUNCTION: + case C_PROCEDURE: + hash_put(gmap, g, tree_ref(value)); + break; + default: + break; + } + } + + instance_fixup(inst, gmap); + + tree_add_decl(container, inst); + } + } +} + //////////////////////////////////////////////////////////////////////////////// // Parser rules @@ -6910,32 +7015,22 @@ static tree_t p_subprogram_instantiation_declaration(void) tree_t body = NULL; if (decl != NULL) { - const tree_kind_t decl_kind = tree_kind(decl); - if (decl_kind == T_FUNC_BODY || decl_kind == T_PROC_BODY) - body = decl; - else { - // Attempt to load the package body if available - type_t type = tree_type(decl); - tree_t pack = tree_container(decl), pack_body, d; - if (tree_kind(pack) == T_PACKAGE && (pack_body = body_of(pack))) { - ident_t id = tree_ident(decl); - for (int nth = 0; (d = search_decls(pack_body, id, nth)); nth++) { - if (is_subprogram(d) && type_eq(tree_type(d), type)) { - body = d; - break; - } - } + if ((body = find_subprogram_body(decl)) == NULL) { + tree_t du = find_enclosing(nametab, S_DESIGN_UNIT); + if (tree_kind(du) != T_PACKAGE) + parse_error(CURRENT_LOC, "subprogram %s cannot be instantiated " + "until its body has been analysed", + istr(tree_ident(decl))); + else { + // Will be instantiated at end of package body + tree_set_ref(inst, decl); } } - - if (body == NULL) - parse_error(CURRENT_LOC, "subprogram %s cannot be instantiated until " - "its body has been analysed", istr(tree_ident(decl))); else tree_set_ref(inst, body); } - if (decl != NULL && body != NULL) + if (decl != NULL) instantiate_subprogram(inst, decl, body); else { // Create a dummy subprogram type to avoid later errors @@ -6943,6 +7038,7 @@ static tree_t p_subprogram_instantiation_declaration(void) type_set_ident(type, tree_ident(name)); if (kind == T_FUNC_INST) type_set_result(type, type_new(T_NONE)); + tree_set_type(inst, type); } @@ -12732,6 +12828,9 @@ static tree_t p_package_body(tree_t unit) p_package_body_declarative_part(body); + if (standard() >= STD_08 && pack != NULL) + package_body_deferred_instantiation(pack, body); + pop_scope(nametab); pop_scope(nametab); diff --git a/src/sem.c b/src/sem.c index 2443cbac..4aa29c3c 100644 --- a/src/sem.c +++ b/src/sem.c @@ -3161,12 +3161,15 @@ static bool sem_check_pcall(tree_t t, nametab_t *tab) tree_t decl = tree_ref(t); - const tree_kind_t kind = tree_kind(decl); - if (kind == T_FUNC_DECL || kind == T_FUNC_BODY) + switch (class_of(decl)) { + case C_PROCEDURE: + break; + case C_FUNCTION: sem_error(t, "function %s cannot be called as a procedure", - istr(tree_ident2(t))); - else if (kind != T_PROC_DECL && kind != T_PROC_BODY) { + type_pp(tree_type(decl))); + default: // All other errors should be caught at parsing stage + assert(error_count() > 0); return false; } diff --git a/test/parse/issue654.vhd b/test/parse/issue654.vhd index 41cbadb0..081330a0 100644 --- a/test/parse/issue654.vhd +++ b/test/parse/issue654.vhd @@ -29,4 +29,14 @@ package frequency is procedure bad3 is new generate_clock; -- Error + procedure test1 generic (type t) (x : t); + + procedure test1 is new test1 generic map (integer); -- OK end package ; + +package body frequency is + procedure test1 generic (type t) (x : t) is + begin + generate_clock(bit, 1 Hz); -- Error + end procedure; +end package body; diff --git a/test/regress/issue654.vhd b/test/regress/issue654.vhd new file mode 100644 index 00000000..e8aec88d --- /dev/null +++ b/test/regress/issue654.vhd @@ -0,0 +1,94 @@ +library ieee ; + use ieee.std_logic_1164.std_logic ; + use ieee.std_logic_1164.std_ulogic ; + use ieee.std_logic_1164."not"; + +package frequency is + + type frequency is range 0 to 2e9 units + Hz ; + kHz = 1000 Hz ; + MHz = 1000 kHz ; + GHz = 1000 MHz ; + THz = 1000 GHz ; + end units ; + + function half_period(freq : frequency) return time ; + function period(freq : frequency) return time ; + + procedure generate_clock generic ( + type t ; + function "not"(x : t) return t is <> + ) parameter (signal clock : inout t ; freq : frequency ; count : natural := 0) ; + + procedure generate_clock is new generate_clock generic map(t => std_ulogic) ; + --procedure generate_clock is new generate_clock generic map(t => std_logic) ; + procedure generate_clock is new generate_clock generic map(t => bit) ; + +end package ; + +package body frequency is + + function period(freq : frequency) return time is + begin + return 1 sec / frequency'pos(freq) ; + end function ; + + function half_period(freq : frequency) return time is + begin + return period(freq) / 2.0 ; + end function ; + + procedure generate_clock generic( + type t ; + function "not"(x : t) return t is <> + ) parameter ( + signal clock : inout t ; + freq : frequency ; + count : natural := 0 + ) is + constant hp : time := half_period(freq) ; + variable downcount : natural := count ; + begin + -- count = 0 means forever, otherwise we look at the downcount + while count = 0 or downcount > 0 loop + clock <= not clock; + wait for hp; + clock <= not clock; + wait for hp; + downcount := downcount - 1 ; + end loop ; + end procedure ; + +end package body ; + +------------------------------------------------------------------------------- + +entity issue654 is +end entity; + +use work.frequency.all; + +architecture test of issue654 is + signal clk : bit; +begin + + clkgen: process is + begin + generate_clock(clk, 1 GHz, 100); + assert now = 100 ns; + wait; + end process; + + check: process is + begin + for i in 1 to 100 loop + wait for 0.5 ns; + assert clk = '1'; + wait for 0.5 ns; + assert clk = '0'; + end loop; + wait; + end process; + +end architecture; diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index d9732cb6..196292ee 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -950,3 +950,4 @@ vlog8 verilog issue852 wave,2019,dump-arrays wave11 wave,2008,dump-arrays vlog9 verilog +issue654 normal,2008 diff --git a/test/test_parse.c b/test/test_parse.c index 0ee4b69f..daec6f7e 100644 --- a/test/test_parse.c +++ b/test/test_parse.c @@ -5409,13 +5409,13 @@ START_TEST(test_issue654) set_standard(STD_08); const error_t expect[] = { - { 19, "subprogram GENERATE_CLOCK cannot be instantiated until its body " - "has been analysed" }, - { 20, "GENERATE_CLOCK has no generic named T" }, { 22, "no visible uninstantiated subprogram GENERATE_CLOCK matches " "signature [return INTEGER]" }, { 23, "no visible uninstantiated subprogram declaration for \"+\"" }, { 30, "multiple visible uninstantiated subprograms with name GENE" }, + { 40, "invalid use of type BIT" }, + { 37, "subprogram WORK.FREQUENCY.GENERATE_CLOCK [BIT, FREQUENCY, " + "NATURAL] cannot be instantiated until its body has been analysed" }, { -1, NULL } }; expect_errors(expect); @@ -5427,6 +5427,19 @@ START_TEST(test_issue654) fail_unless(tree_kind(p) == T_PACKAGE); lib_put(lib_work(), p); + tree_t b = parse(); + fail_if(b == NULL); + fail_unless(tree_kind(b) == T_PACK_BODY); + + fail_unless(tree_decls(b) == 2); + + tree_t d0 = tree_decl(b, 0); + fail_unless(tree_kind(d0) == T_PROC_BODY); + + tree_t d1 = tree_decl(b, 1); + fail_unless(tree_kind(d1) == T_PROC_INST); + fail_unless(tree_ref(d1) == d0); + fail_unless(parse() == NULL); check_expected_errors(); diff --git a/test/test_sem.c b/test/test_sem.c index 53ad582b..eaa32d49 100644 --- a/test/test_sem.c +++ b/test/test_sem.c @@ -399,7 +399,7 @@ START_TEST(test_func) { 245, "class constant of subprogram body TEST26 parameter" }, { 271, "invalid reference to X inside pure function NESTED" }, { 288, "no visible subprogram declaration for FNORK" }, - { 293, "function CONSTPURE cannot be called as a procedure" }, + { 293, "function CONSTPURE [INTEGER return INTEGER] cannot be called " }, { 294, "procedure NOTDEF not allowed in an expression" }, { 297, "no visible declaration for BAD_TYPE" }, { 297, "no visible declaration for FOO" }, -- 2.39.2