Skip to content

Commit

Permalink
Improve error checking for configuration specifications
Browse files Browse the repository at this point in the history
  • Loading branch information
nickg committed Aug 12, 2023
1 parent bfd254b commit 43eef5c
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,8 @@ static void dump_binding(tree_t t, int indent)
print_syntax("#use %s", istr(tree_ident(t)));
if (tree_has_ident2(t))
print_syntax("(%s)", istr(tree_ident2(t)));
print_syntax("\n");
if (tree_genmaps(t) > 0 || tree_params(t) > 0)
print_syntax("\n");
dump_generic_map(t, indent + 2, tree_params(t) > 0 ? "\n" : "");
dump_port_map(t, indent + 2, "");
print_syntax(";\n");
Expand Down
155 changes: 146 additions & 9 deletions src/sem.c
Original file line number Diff line number Diff line change
Expand Up @@ -5525,20 +5525,157 @@ static bool sem_check_binding(tree_t t, nametab_t *tab)

static bool sem_check_block_config(tree_t t, nametab_t *tab)
{
bool ok = true;
const int ndecls = tree_decls(t);
for (int i = 0; i < ndecls; i++)
ok &= sem_check(tree_decl(t, i), tab);

return ok;
return true;
}

static bool sem_check_spec(tree_t t, nametab_t *tab)
{
if (tree_has_value(t))
return sem_check(tree_value(t), tab);
else
if (!tree_has_ref(t))
return false;

tree_t comp = tree_ref(t);
assert(tree_kind(comp) == T_COMPONENT);

if (!tree_has_value(t))
return true;

tree_t bind = tree_value(t);
assert(tree_kind(bind) == T_BINDING);

if (!sem_check(bind, tab))
return false;

tree_t entity = primary_unit_of(tree_ref(bind));
assert(tree_kind(entity) == T_ENTITY);

bool ok = true;

const int c_ngenerics = tree_generics(comp);
const int e_ngenerics = tree_generics(entity);
const int b_genmaps = tree_genmaps(bind);

for (int i = 0; i < c_ngenerics; i++) {
tree_t cg = tree_generic(comp, i);

tree_t rebind = NULL;
for (int j = 0; rebind == NULL && j < b_genmaps; j++) {
tree_t value = tree_value(tree_genmap(bind, j));
if (tree_kind(value) == T_REF && tree_ref(value) == cg)
rebind = value;
}

if (rebind != NULL)
continue; // Ignore for now

tree_t match = NULL;
for (int j = 0; match == NULL && j < e_ngenerics; j++) {
tree_t eg = tree_generic(entity, j);
if (tree_ident(eg) == tree_ident(cg))
match = eg;
}

if (match == NULL) {
if (!tree_has_value(cg)) {
diag_t *d = diag_new(DIAG_ERROR, tree_loc(t));
diag_printf(d, "generic %s in component %s without a default value "
"has no corresponding generic in entity %s",
istr(tree_ident(cg)), istr(tree_ident(comp)),
istr(tree_ident(entity)));
diag_hint(d, tree_loc(cg), "generic %s declared here",
istr(tree_ident(cg)));
diag_emit(d);
}

ok = false;
continue;
}

type_t ctype = tree_type(cg);
type_t etype = tree_type(match);
if (!type_eq(ctype, etype)) {
diag_t *d = diag_new(DIAG_ERROR, tree_loc(t));
diag_printf(d, "generic %s in component %s has type %s which is "
"incompatible with type %s in entity %s",
istr(tree_ident(cg)), istr(tree_ident(comp)),
type_pp2(ctype, etype), type_pp2(etype, ctype),
istr(tree_ident(entity)));
diag_hint(d, tree_loc(cg), "declaration of generic %s in component",
istr(tree_ident(cg)));
diag_hint(d, tree_loc(match), "declaration of generic %s in entity",
istr(tree_ident(match)));
diag_emit(d);

ok = false;
continue;
}
}

const int c_nports = tree_ports(comp);
const int e_nports = tree_ports(entity);
const int b_nparams = tree_params(bind);

for (int i = 0; i < c_nports; i++) {
tree_t cp = tree_port(comp, i);

tree_t rebind = NULL;
for (int j = 0; rebind == NULL && j < b_nparams; j++) {
tree_t value = tree_value(tree_param(bind, j));
if (tree_kind(value) == T_REF && tree_ref(value) == cp)
rebind = value;
}

if (rebind != NULL)
continue; // Ignore for now

tree_t match = NULL;
for (int j = 0; match == NULL && j < e_nports; j++) {
tree_t ep = tree_port(entity, j);
if (tree_ident(ep) == tree_ident(cp))
match = ep;
}

if (match == NULL) {
const bool open_ok =
tree_has_value(cp)
|| (tree_subkind(cp) == PORT_OUT
&& !type_is_unconstrained(tree_type(cp)));

if (!open_ok) {
diag_t *d = diag_new(DIAG_ERROR, tree_loc(t));
diag_printf(d, "port %s in component %s without a default value "
"has no corresponding port in entity %s",
istr(tree_ident(cp)), istr(tree_ident(comp)),
istr(tree_ident(entity)));
diag_hint(d, tree_loc(cp), "port %s declared here",
istr(tree_ident(cp)));
diag_emit(d);
}

ok = false;
continue;
}

type_t ctype = tree_type(cp);
type_t etype = tree_type(match);
if (!type_eq(ctype, etype)) {
diag_t *d = diag_new(DIAG_ERROR, tree_loc(t));
diag_printf(d, "port %s in component %s has type %s which is "
"incompatible with type %s in entity %s",
istr(tree_ident(cp)), istr(tree_ident(comp)),
type_pp2(ctype, etype), type_pp2(etype, ctype),
istr(tree_ident(entity)));
diag_hint(d, tree_loc(cp), "declaration of port %s in component",
istr(tree_ident(cp)));
diag_hint(d, tree_loc(match), "declaration of port %s in entity",
istr(tree_ident(match)));
diag_emit(d);

ok = false;
continue;
}
}

return ok;
}

static bool sem_check_configuration(tree_t t, nametab_t *tab)
Expand Down
62 changes: 62 additions & 0 deletions test/sem/config2.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
entity sub is
generic ( g : integer; g2 : bit );
port ( i : in bit_vector;
o : out bit_vector );
end entity;

architecture test of sub is
begin
end architecture;

-------------------------------------------------------------------------------

entity other is
generic ( gg : integer; g2 : real );
port ( ii : in bit_vector;
oo : out bit_vector;
zz : out integer );
end entity;

architecture test of other is
begin
end architecture;

-------------------------------------------------------------------------------

entity top is
end entity;

architecture test of top is
component comp is
generic ( g : integer; g2 : bit );
port ( i : in bit_vector;
o : out bit_vector;
k : out bit;
zz : out real );
end component;

signal x, y : bit_vector(3 downto 0);
begin

u1: component comp -- Error
generic map (5, '1')
port map (x, y);

u2: component comp -- OK
generic map (5, '1')
port map (x, y);

end architecture;

-------------------------------------------------------------------------------

configuration conf of top is
for test
for u1 : comp
use entity work.other(test);
end for;
for u2 : comp
use entity work.sub(test);
end for;
end for;
end configuration;
27 changes: 27 additions & 0 deletions test/test_sem.c
Original file line number Diff line number Diff line change
Expand Up @@ -3202,6 +3202,32 @@ START_TEST(test_lcs2016_23)
}
END_TEST

START_TEST(test_config2)
{
input_from_file(TESTDIR "/sem/config2.vhd");

const error_t expect[] = {
{ 55, "generic G in component COMP without a default value has no "
"corresponding generic in entity WORK.OTHER" },
{ 55, "generic G2 in component COMP has type BIT which is incompatible "
"with type REAL in entity WORK.OTHER" },
{ 55, "port I in component COMP without a default value has no "
"corresponding port in entity WORK.OTHER" },
{ 55, "port O in component COMP without a default value has no "
"corresponding port in entity WORK.OTHER" },
{ 55, "port ZZ in component COMP has type REAL which is incompatible "
"with type INTEGER in entity WORK.OTHER" },
{ -1, NULL }
};
expect_errors(expect);

parse_and_check(T_ENTITY, T_ARCH, T_ENTITY, T_ARCH, T_ENTITY,
T_ARCH, T_CONFIGURATION);

check_expected_errors();
}
END_TEST

Suite *get_sem_tests(void)
{
Suite *s = suite_create("sem");
Expand Down Expand Up @@ -3357,6 +3383,7 @@ Suite *get_sem_tests(void)
tcase_add_test(tc_core, test_lcs2016_19);
tcase_add_test(tc_core, test_lcs2016_07);
tcase_add_loop_test(tc_core, test_lcs2016_23, STD_08, STD_19 + 1);
tcase_add_test(tc_core, test_config2);
suite_add_tcase(s, tc_core);

return s;
Expand Down

0 comments on commit 43eef5c

Please sign in to comment.