Add support for restricted type parameters

SystemVerilog 2023 allows type parameters to be restricted to a
specific kind of type, e.g. `parameter type struct T = T0`.

This is very similar to the type restrictions that can be applied to
forward typedefs.

Factor the support code from the typedefs into a standalone helper and
reuse it for both.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2026-05-16 16:13:19 -07:00
parent 0f75156d37
commit 46c0526dab
10 changed files with 115 additions and 61 deletions

View File

@ -1,7 +1,7 @@
#ifndef IVL_PScope_H
#define IVL_PScope_H
/*
* Copyright (c) 2008-2025 Stephen Williams (steve@icarus.com)
* Copyright (c) 2008-2026 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -121,6 +121,8 @@ class LexicalScope {
bool overridable;
// Whether the parameter is a type parameter
bool type_flag = false;
// Type restriction for a type parameter
type_restrict_t type_restrict;
// The lexical position of the declaration
unsigned lexical_pos = 0;

View File

@ -452,27 +452,7 @@ ivl_type_t typedef_t::elaborate_type(Design *des, NetScope *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;
}
bool type_ok = basic_type.matches(elab_type);
if (!type_ok) {
cerr << data_type->get_fileline() << " error: "

View File

@ -838,6 +838,16 @@ void NetScope::evaluate_type_parameter_(Design *des, param_ref_t cur)
data_type_t *ptype = type_expr->get_type();
NetScope *type_scope = cur->second.val_scope;
cur->second.ivl_type = ptype->elaborate_type(des, type_scope);
if (!cur->second.ivl_type)
return;
if (!cur->second.type_restrict.matches(cur->second.ivl_type)) {
cerr << type_expr->get_fileline() << ": error: "
<< "Type parameter `" << cur->first << "` expects a `"
<< cur->second.type_restrict << "` type, got `"
<< *cur->second.ivl_type << "`." << endl;
des->errors++;
}
}
void NetScope::evaluate_parameter_(Design*des, param_ref_t cur)

View File

@ -281,6 +281,7 @@ void NetScope::set_parameter(perm_string key, bool is_annotatable,
ref.local_flag = param.local_flag;
ref.overridable = param.overridable;
ref.type_flag = param.type_flag;
ref.type_restrict = param.type_restrict;
ref.lexical_pos = param.lexical_pos;
ivl_assert(param, !ref.range);
ref.range = range_list;

View File

@ -1279,6 +1279,8 @@ class NetScope : public Definitions, public Attrib {
bool overridable = false;
// Is it a type parameter
bool type_flag = false;
// Type restriction for a type parameter
type_restrict_t type_restrict;
// The lexical position of the declaration
unsigned lexical_pos = 0;
// range constraints

43
parse.y
View File

@ -45,6 +45,7 @@ extern void lex_end_table();
static data_type_t* param_data_type = 0;
static bool param_is_local = false;
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;
@ -598,7 +599,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
LexicalScope::lifetime_t lifetime;
enum typedef_t::basic_type typedef_basic_type;
enum type_restrict_t::type_t type_restrict;
};
%token <text> IDENTIFIER INTERFACE_IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL
@ -858,7 +859,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
%type <letter> compressed_operator
%type <typedef_basic_type> typedef_basic_type
%type <type_restrict> forward_type forward_type_without_enum
%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
@ -2882,11 +2883,16 @@ block_item_decls_opt
* `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; }
* forward_type_without_enum. */
forward_type_without_enum
: K_struct { $$ = type_restrict_t::STRUCT; }
| K_union { $$ = type_restrict_t::UNION; }
| K_class { $$ = type_restrict_t::CLASS; }
;
forward_type
: K_enum { $$ = type_restrict_t::ENUM; }
| forward_type_without_enum
;
/* Type declarations are parsed here. The rule actions call pform
@ -2902,17 +2908,17 @@ type_declaration
| K_typedef identifier_name ';'
{ perm_string name = lex_strings.make($2);
pform_forward_typedef(@2, name, typedef_t::ANY);
pform_forward_typedef(@2, name, type_restrict_t::ANY);
delete[]$2;
}
| K_typedef typedef_basic_type identifier_name ';'
| K_typedef forward_type_without_enum 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);
pform_forward_typedef(@3, name, type_restrict_t::ENUM);
delete[]$3;
}
| K_typedef error ';'
@ -4924,7 +4930,16 @@ module_parameter_port_list_opt
;
type_param
: K_type { param_is_type = true; }
: K_type
{ param_is_type = true;
param_type_restrict = {};
}
| K_type forward_type
{ if (generation_flag < GN_VER2023)
yyerror(@1, "error: Restricted type parameters require SystemVerilog 2023 or later.");
param_is_type = true;
param_type_restrict = $2;
}
;
module_parameter
@ -4940,6 +4955,7 @@ module_parameter_port_list
{ param_data_type = $1;
param_is_local = false;
param_is_type = false;
param_type_restrict = {};
}
parameter_assign
{ pform_requires_sv(@3, "Omitting initial `parameter` in parameter port "
@ -4955,6 +4971,7 @@ module_parameter_port_list
"data type in parameter port list");
param_data_type = $3;
param_is_type = false;
param_type_restrict = {};
}
}
parameter_assign
@ -5598,6 +5615,7 @@ param_type
: data_type_or_implicit
{ param_is_type = false;
param_data_type = $1;
param_type_restrict = {};
}
| type_param
@ -5632,7 +5650,8 @@ parameter_assign_list
parameter_assign
: IDENTIFIER dimensions_opt initializer_opt parameter_value_ranges_opt
{ pform_set_parameter(@1, lex_strings.make($1), param_is_local,
param_is_type, param_data_type, $2, $3, $4);
param_is_type, param_type_restrict,
param_data_type, $2, $3, $4);
delete[]$1;
}
;

View File

@ -841,7 +841,7 @@ static typedef_t *pform_get_typedef(const struct vlltype&loc, perm_string name)
}
void pform_forward_typedef(const struct vlltype&loc, perm_string name,
enum typedef_t::basic_type basic_type)
type_restrict_t basic_type)
{
typedef_t *td = pform_get_typedef(loc, name);
@ -2948,6 +2948,7 @@ static void pform_set_type_parameter(const struct vlltype&loc, perm_string name,
void pform_set_parameter(const struct vlltype&loc,
perm_string name, bool is_local, bool is_type,
type_restrict_t type_restrict,
data_type_t*data_type, const list<pform_range_t>*udims,
PExpr*expr, LexicalScope::range_t*value_range)
{
@ -3020,6 +3021,7 @@ void pform_set_parameter(const struct vlltype&loc,
parm->local_flag = is_local;
parm->overridable = overridable;
parm->type_flag = is_type;
parm->type_restrict = type_restrict;
parm->lexical_pos = loc.lexical_pos;
scope->parameters[name] = parm;

View File

@ -323,7 +323,7 @@ extern void pform_set_typedef(const struct vlltype&loc, perm_string name,
data_type_t*data_type,
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);
type_restrict_t basic_type);
extern void pform_set_type_referenced(const struct vlltype&loc, const char*name);
@ -412,6 +412,7 @@ extern LexicalScope::range_t* pform_parameter_value_range(bool exclude_flag,
extern void pform_set_parameter(const struct vlltype&loc,
perm_string name,
bool is_local, bool is_type,
type_restrict_t type_restrict,
data_type_t*data_type, const std::list<pform_range_t>*udims,
PExpr*expr, LexicalScope::range_t*value_range);
extern void pform_set_specparam(const struct vlltype&loc,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007-2019 Stephen Williams (steve@icarus.com)
* Copyright (c) 2007-2026 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -19,6 +19,8 @@
# include "pform_types.h"
# include "netclass.h"
# include "netenum.h"
data_type_t::~data_type_t()
{
@ -55,34 +57,59 @@ bool typedef_t::set_data_type(data_type_t *t)
return true;
}
bool typedef_t::set_basic_type(enum basic_type bt)
bool typedef_t::set_basic_type(type_restrict_t type)
{
if (bt == ANY)
return basic_type.merge(type);
}
bool type_restrict_t::merge(type_restrict_t other)
{
if (other.type == ANY)
return true;
if (basic_type != ANY && bt != basic_type)
if (this->type != ANY && other.type != this->type)
return false;
basic_type = bt;
this->type = other.type;
return true;
}
std::ostream& operator<< (std::ostream&out, enum typedef_t::basic_type bt)
bool type_restrict_t::matches(ivl_type_t ivl_type) const
{
switch (bt) {
case typedef_t::ANY:
switch (this->type) {
case ENUM:
return dynamic_cast<const netenum_t *>(ivl_type);
case STRUCT: {
const netstruct_t *struct_type = dynamic_cast<const netstruct_t *>(ivl_type);
return struct_type && !struct_type->union_flag();
}
case UNION: {
const netstruct_t *struct_type = dynamic_cast<const netstruct_t *>(ivl_type);
return struct_type && struct_type->union_flag();
}
case CLASS:
return dynamic_cast<const netclass_t *>(ivl_type);
default:
return true;
}
}
std::ostream& operator<< (std::ostream&out, const type_restrict_t& type)
{
switch (type.type) {
case type_restrict_t::ANY:
out << "any";
break;
case typedef_t::ENUM:
case type_restrict_t::ENUM:
out << "enum";
break;
case typedef_t::STRUCT:
case type_restrict_t::STRUCT:
out << "struct";
break;
case typedef_t::UNION:
case type_restrict_t::UNION:
out << "union";
break;
case typedef_t::CLASS:
case type_restrict_t::CLASS:
out << "class";
break;
}

View File

@ -49,6 +49,24 @@ class netclass_t;
class netenum_t;
typedef named<PExpr*> named_pexpr_t;
struct type_restrict_t {
enum type_t {
ANY,
ENUM,
STRUCT,
UNION,
CLASS
};
type_restrict_t() = default;
type_restrict_t(type_t kind) : type(kind) { }
bool merge(type_restrict_t other);
bool matches(ivl_type_t type) const;
enum type_t type = ANY;
};
/*
* The pform_ident_t holds the identifier name and its lexical position
* (the lexical_pos supplied by the scanner).
@ -180,26 +198,18 @@ class data_type_t : public PNamedItem {
};
struct typedef_t : public PNamedItem {
explicit typedef_t(perm_string n) : basic_type(ANY), name(n) { };
explicit typedef_t(perm_string n) : 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; }
bool set_basic_type(type_restrict_t type);
type_restrict_t get_basic_type() const { return basic_type; }
protected:
enum basic_type basic_type;
type_restrict_t basic_type;
std::unique_ptr<data_type_t> data_type;
public:
perm_string name;
@ -498,6 +508,6 @@ extern std::ostream& operator<< (std::ostream&out, const pform_name_t&);
extern std::ostream& operator<< (std::ostream&out, const pform_scoped_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);
extern std::ostream& operator<< (std::ostream&out, const type_restrict_t& type);
#endif /* IVL_pform_types_H */