Honor declaration order for module-body specparams

The LRM section 6.20.5 permits `specparam` declarations both in a
module body and in a `specify` block. It states:

    A specify parameter declared outside a specify block shall be declared
    before it is referenced.

Currently all specparams retain a declaration position of zero, making a
later module-body declaration visible to an earlier expression:

    module test;
      wire value;
      assign value = delay;
      specparam delay = 1;
    endmodule

Record the source position for module-body specparams. Track when the
parser is inside a `specify` block so the outside-block restriction is
not applied there.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2026-08-02 12:36:40 -07:00
parent 2556b5f073
commit dd403e7ea0
3 changed files with 14 additions and 4 deletions

View File

@ -48,6 +48,7 @@ static bool param_is_type = false;
static type_restrict_t param_type_restrict;
static bool in_gen_region = false;
static std::list<pform_range_t>* specparam_active_range = 0;
static bool in_specify_block = false;
/* Port declaration lists use this structure for context. */
static struct {
@ -5887,12 +5888,15 @@ module_item
yyerror(@1, "error: specify blocks are not allowed "
"in interfaces.");
pform_error_in_generate(@1, "specify block");
in_specify_block = true;
}
specify_item_list_opt K_endspecify
{ in_specify_block = false; }
| K_specify error K_endspecify
{ yyerror(@1, "error: Syntax error in specify block");
in_specify_block = false;
yyerrok;
}
@ -7005,7 +7009,8 @@ specify_path_identifiers
specparam
: identifier_name '=' expr_mintypmax
{ pform_set_specparam(@1, lex_strings.make($1), specparam_active_range, $3);
{ pform_set_specparam(@1, lex_strings.make($1), specparam_active_range, $3,
!in_specify_block);
delete[]$1;
}
| PATHPULSE_IDENTIFIER '=' expression

View File

@ -3167,7 +3167,8 @@ void pform_set_parameter(const struct vlltype&loc,
}
void pform_set_specparam(const struct vlltype&loc, perm_string name,
list<pform_range_t>*range, PExpr*expr)
list<pform_range_t> *range, PExpr *expr,
bool check_decl_order)
{
ivl_assert(loc, !pform_cur_module.empty());
Module*scope = pform_cur_module.front();
@ -3187,6 +3188,10 @@ void pform_set_specparam(const struct vlltype&loc, perm_string name,
parm->expr = expr;
parm->range = 0;
// Only module-body specparams follow lexical order.
if (check_decl_order)
parm->lexical_pos = loc.lexical_pos;
if (range) {
ivl_assert(loc, range->size() == 1);
parm->data_type = new vector_type_t(IVL_VT_LOGIC, false, range);

View File

@ -414,8 +414,8 @@ extern void pform_set_parameter(const struct vlltype&loc,
PExpr*expr, LexicalScope::range_t*value_range);
extern void pform_set_specparam(const struct vlltype&loc,
perm_string name,
std::list<pform_range_t>*range,
PExpr*expr);
std::list<pform_range_t> *range,
PExpr *expr, bool check_decl_order);
extern void pform_set_defparam(const pform_name_t&name, PExpr*expr);
extern void pform_make_let(const struct vlltype&loc,