mirror of
https://github.com/steveicarus/iverilog.git
synced 2026-09-08 12:23:32 +02:00
`typeref_t` stores the `typedef_t` selected by the parser. This binds a type
identifier before the elaborated scope is available and can select the wrong
declaration. Function and task declarations are visible throughout their
scope, so a later subroutine named `T` in the current scope hides an earlier
`T` type in an outer scope even when the type reference appears before the
subroutine declaration. Parser-time lookup can not handle this correctly.
For example:
typedef int T;
module test;
T value;
function T;
T = 0;
endfunction
endmodule
The declaration of `value` must be rejected because the local function named
`T` hides the outer typedef. The same applies to a task named `T`.
Type lookup can also depend on information that is not available to the parser.
For example, a parameterized class can extend a type parameter and inherit `T`
from the selected base class:
class Derived #(type B = DefaultBase) extends B;
T value;
endclass
Parameterized classes are not supported yet, but resolving named types during
elaboration is a prerequisite for supporting this case.
Replace `typeref_t` with `type_identifier_t`, which owns a `PEIdent`. Construct
the identifier in the parser, then resolve it through
`PEIdent::elaborate_type()` and `symbol_search()` while elaborating the data
type. Parser-time type lookup is only used to select grammar paths.
`pform_new_ident()` preserves wildcard import activation for the identifier
expression.
Enforce declaration ordering when an identifier occurs in a grammar position
that requires a data type, even when compatibility options relax it for
ordinary variable lookup. A later variable therefore does not hide an outer
type in a declaration, and a later type declaration can not satisfy an earlier
use. Keep ordinary lookup when an identifier can be either a type or a value,
such as an argument to `$bits()`. Add
`SYMBOL_SEARCH_STRICT_DECLARATION_ORDER` and a required-type elaboration context
for the former case.
For example:
typedef logic [7:0] T;
module test;
T value;
localparam int A = $bits(value);
localparam int B = $bits(T);
integer T;
endmodule
The declaration of `value` uses the outer typedef, so `A` is 8. With relaxed
variable declaration ordering, the ambiguous reference in `$bits(T)` uses the
later integer, so `B` is 32.
Check a constant's declaration position before `get_parameter()` elaborates it
on demand when declaration order is enforced. This keeps a later declaration in
`parameter T T` from recursively selecting itself as the type.
Recover a failed deferred lookup in `type_identifier_t` with a scalar logic type
after reporting the error. This keeps the existing non-null expectations of
type consumers unchanged.
Signed-off-by: Lars-Peter Clausen <[email protected]>
474 lines
13 KiB
C++
474 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2012-2026 Stephen Williams ([email protected])
|
|
*
|
|
* This source code is free software; you can redistribute it
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
* General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
# include "PExpr.h"
|
|
# include "PScope.h"
|
|
# include "pform_types.h"
|
|
# include "netlist.h"
|
|
# include "netclass.h"
|
|
# include "netdarray.h"
|
|
# include "netenum.h"
|
|
# include "netqueue.h"
|
|
# include "netparray.h"
|
|
# include "netscalar.h"
|
|
# include "netstruct.h"
|
|
# include "netvector.h"
|
|
# include "netmisc.h"
|
|
# include <typeinfo>
|
|
# include "ivl_assert.h"
|
|
|
|
using namespace std;
|
|
|
|
/*
|
|
* Elaborations of types may vary depending on the scope that it is
|
|
* done in, so keep a per-scope cache of the results.
|
|
*/
|
|
ivl_type_t data_type_t::elaborate_type(Design*des, NetScope*scope)
|
|
{
|
|
scope = find_scope(des, scope);
|
|
|
|
Definitions*use_definitions = scope;
|
|
|
|
map<Definitions*,ivl_type_t>::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;
|
|
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<NetScope*,ivl_type_t>(scope, tmp));
|
|
return tmp;
|
|
}
|
|
|
|
NetScope *data_type_t::find_scope(Design *, NetScope *scope) const
|
|
{
|
|
return scope;
|
|
}
|
|
|
|
ivl_type_t data_type_t::elaborate_type_raw(Design*des, NetScope*) const
|
|
{
|
|
cerr << get_fileline() << ": internal error: "
|
|
<< "Elaborate method not implemented for " << typeid(*this).name()
|
|
<< "." << endl;
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
|
|
ivl_type_t atom_type_t::elaborate_type_raw(Design*des, NetScope*) const
|
|
{
|
|
switch (type_code) {
|
|
case INTEGER:
|
|
return netvector_t::integer_type(signed_flag);
|
|
|
|
case TIME:
|
|
if (signed_flag)
|
|
return &netvector_t::time_signed;
|
|
else
|
|
return &netvector_t::time_unsigned;
|
|
|
|
case LONGINT:
|
|
if (signed_flag)
|
|
return &netvector_t::atom2s64;
|
|
else
|
|
return &netvector_t::atom2u64;
|
|
|
|
case INT:
|
|
if (signed_flag)
|
|
return &netvector_t::atom2s32;
|
|
else
|
|
return &netvector_t::atom2u32;
|
|
|
|
case SHORTINT:
|
|
if (signed_flag)
|
|
return &netvector_t::atom2s16;
|
|
else
|
|
return &netvector_t::atom2u16;
|
|
|
|
case BYTE:
|
|
if (signed_flag)
|
|
return &netvector_t::atom2s8;
|
|
else
|
|
return &netvector_t::atom2u8;
|
|
|
|
default:
|
|
cerr << get_fileline() << ": internal error: "
|
|
<< "atom_type_t type_code=" << type_code << "." << endl;
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ivl_type_t class_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
|
|
{
|
|
return scope->find_class(des, name);
|
|
}
|
|
|
|
/*
|
|
* elaborate_type_raw for enumerations is actually mostly performed
|
|
* during scope elaboration so that the enumeration literals are
|
|
* 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 *des, NetScope *scope) const
|
|
{
|
|
ivl_type_t base = base_type->elaborate_type(des, scope);
|
|
|
|
const class netvector_t *vec_type = dynamic_cast<const netvector_t*>(base);
|
|
|
|
if (!vec_type && !dynamic_cast<const netparray_t*>(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
|
|
{
|
|
netranges_t packed;
|
|
if (pdims.get())
|
|
evaluate_ranges(des, scope, this, packed, *pdims);
|
|
|
|
netvector_t*tmp = new netvector_t(packed, base_type);
|
|
tmp->set_signed(signed_flag);
|
|
tmp->set_isint(integer_flag);
|
|
tmp->set_implicit(implicit_flag);
|
|
|
|
return tmp;
|
|
}
|
|
|
|
ivl_type_t real_type_t::elaborate_type_raw(Design*, NetScope*) const
|
|
{
|
|
switch (type_code_) {
|
|
case REAL:
|
|
return &netreal_t::type_real;
|
|
case SHORTREAL:
|
|
return &netreal_t::type_shortreal;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ivl_type_t string_type_t::elaborate_type_raw(Design*, NetScope*) const
|
|
{
|
|
return &netstring_t::type_string;
|
|
}
|
|
|
|
ivl_type_t parray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
|
|
{
|
|
netranges_t packed;
|
|
if (dims.get())
|
|
evaluate_ranges(des, scope, this, packed, *dims);
|
|
|
|
ivl_type_t etype = base_type->elaborate_type(des, scope);
|
|
if (!etype->packed()) {
|
|
cerr << this->get_fileline() << " error: Packed array ";
|
|
cerr << "base-type `";
|
|
cerr << *base_type;
|
|
cerr << "` is not packed." << endl;
|
|
des->errors++;
|
|
}
|
|
|
|
return new netparray_t(packed, etype);
|
|
}
|
|
|
|
ivl_type_t struct_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
|
|
{
|
|
netstruct_t*res = new netstruct_t;
|
|
|
|
res->set_line(*this);
|
|
|
|
bool is_packed = packed_flag || (union_flag && soft_flag);
|
|
res->packed(is_packed);
|
|
res->set_signed(signed_flag);
|
|
|
|
if (union_flag) {
|
|
res->union_flag(true, soft_flag);
|
|
}
|
|
|
|
for (list<struct_member_t*>::iterator cur = members->begin()
|
|
; cur != members->end() ; ++ cur) {
|
|
|
|
// Elaborate the type of the member.
|
|
struct_member_t*curp = *cur;
|
|
ivl_type_t mem_vec = curp->type->elaborate_type(des, scope);
|
|
if (mem_vec == 0)
|
|
continue;
|
|
|
|
// There may be several names that are the same type:
|
|
// <data_type> name1, name2, ...;
|
|
// Process all the member, and give them a type.
|
|
for (list<decl_assignment_t*>::iterator cur_name = curp->names->begin()
|
|
; cur_name != curp->names->end() ; ++ cur_name) {
|
|
decl_assignment_t*namep = *cur_name;
|
|
|
|
if (is_packed && namep->expr) {
|
|
cerr << namep->expr->get_fileline() << " error: "
|
|
<< "Packed structs must not have default member values."
|
|
<< endl;
|
|
des->errors++;
|
|
}
|
|
|
|
netstruct_t::member_t memb;
|
|
memb.name = namep->name.first;
|
|
memb.net_type = elaborate_array_type(des, scope, *this,
|
|
mem_vec, namep->index);
|
|
res->append_member(des, memb);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static ivl_type_t elaborate_darray_check_type(Design *des, const LineInfo &li,
|
|
ivl_type_t type,
|
|
const char *darray_type)
|
|
{
|
|
if (dynamic_cast<const netvector_t*>(type) ||
|
|
dynamic_cast<const netparray_t*>(type) ||
|
|
dynamic_cast<const netreal_t*>(type) ||
|
|
dynamic_cast<const netstring_t*>(type))
|
|
return type;
|
|
|
|
cerr << li.get_fileline() << ": Sorry: "
|
|
<< darray_type << " of type `" << *type
|
|
<< "` is not yet supported." << endl;
|
|
des->errors++;
|
|
|
|
// Return something to recover
|
|
return new netvector_t(IVL_VT_LOGIC);
|
|
}
|
|
|
|
static ivl_type_t elaborate_queue_type(Design *des, NetScope *scope,
|
|
const LineInfo &li, ivl_type_t base_type,
|
|
PExpr *ridx)
|
|
{
|
|
base_type = elaborate_darray_check_type(des, li, base_type, "Queue");
|
|
|
|
long max_idx = -1;
|
|
if (ridx) {
|
|
NetExpr*tmp = elab_and_eval(des, scope, ridx, -1, true);
|
|
NetEConst*cv = dynamic_cast<NetEConst*>(tmp);
|
|
if (cv == 0) {
|
|
cerr << li.get_fileline() << ": error: "
|
|
<< "queue bound must be constant."
|
|
<< endl;
|
|
des->errors++;
|
|
} else {
|
|
verinum res = cv->value();
|
|
if (res.is_defined()) {
|
|
max_idx = res.as_long();
|
|
if (max_idx < 0) {
|
|
cerr << li.get_fileline() << ": error: "
|
|
<< "queue bound must be positive ("
|
|
<< max_idx << ")." << endl;
|
|
des->errors++;
|
|
max_idx = -1;
|
|
}
|
|
} else {
|
|
cerr << li.get_fileline() << ": error: "
|
|
<< "queue bound must be defined."
|
|
<< endl;
|
|
des->errors++;
|
|
}
|
|
}
|
|
delete cv;
|
|
}
|
|
|
|
return new netqueue_t(base_type, max_idx);
|
|
}
|
|
|
|
// If dims is not empty create a unpacked array type and clear dims, otherwise
|
|
// return the base type. Also check that we actually support the base type.
|
|
static ivl_type_t elaborate_static_array_type(Design *des, const LineInfo &li,
|
|
ivl_type_t base_type,
|
|
netranges_t &dims)
|
|
{
|
|
if (dims.empty())
|
|
return base_type;
|
|
|
|
if (dynamic_cast<const netqueue_t*>(base_type)) {
|
|
cerr << li.get_fileline() << ": sorry: "
|
|
<< "array of queue type is not yet supported."
|
|
<< endl;
|
|
des->errors++;
|
|
// Recover
|
|
base_type = new netvector_t(IVL_VT_LOGIC);
|
|
} else if (dynamic_cast<const netdarray_t*>(base_type)) {
|
|
cerr << li.get_fileline() << ": sorry: "
|
|
<< "array of dynamic array type is not yet supported."
|
|
<< endl;
|
|
des->errors++;
|
|
// Recover
|
|
base_type = new netvector_t(IVL_VT_LOGIC);
|
|
}
|
|
|
|
ivl_type_t type = new netuarray_t(dims, base_type);
|
|
dims.clear();
|
|
|
|
return type;
|
|
}
|
|
|
|
ivl_type_t elaborate_array_type(Design *des, NetScope *scope,
|
|
const LineInfo &li, ivl_type_t base_type,
|
|
const list<pform_range_t> &dims)
|
|
{
|
|
const long warn_dimension_size = 1 << 30;
|
|
netranges_t dimensions;
|
|
dimensions.reserve(dims.size());
|
|
|
|
ivl_type_t type = base_type;
|
|
|
|
for (list<pform_range_t>::const_iterator cur = dims.begin();
|
|
cur != dims.end() ; ++cur) {
|
|
PExpr *lidx = cur->first;
|
|
PExpr *ridx = cur->second;
|
|
|
|
if (lidx == 0 && ridx == 0) {
|
|
// Special case: If we encounter an undefined dimensions,
|
|
// then turn this into a dynamic array and put all the
|
|
// packed dimensions there.
|
|
type = elaborate_static_array_type(des, li, type, dimensions);
|
|
type = elaborate_darray_check_type(des, li, type, "Dynamic array");
|
|
type = new netdarray_t(type);
|
|
continue;
|
|
} else if (dynamic_cast<PEQueueDimension*>(lidx)) {
|
|
// Special case: Detect the mark for a QUEUE declaration.
|
|
type = elaborate_static_array_type(des, li, type, dimensions);
|
|
type = elaborate_queue_type(des, scope, li, type, ridx);
|
|
continue;
|
|
}
|
|
|
|
long index_l, index_r;
|
|
evaluate_range(des, scope, &li, *cur, index_l, index_r);
|
|
|
|
if (abs(index_r - index_l) > warn_dimension_size) {
|
|
cerr << li.get_fileline() << ": warning: "
|
|
<< "Array dimension is greater than "
|
|
<< warn_dimension_size << "."
|
|
<< endl;
|
|
}
|
|
|
|
dimensions.push_back(netrange_t(index_l, index_r));
|
|
}
|
|
|
|
return elaborate_static_array_type(des, li, type, dimensions);
|
|
}
|
|
|
|
ivl_type_t uarray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
|
|
{
|
|
ivl_type_t btype = base_type->elaborate_type(des, scope);
|
|
|
|
return elaborate_array_type(des, scope, *this, btype, *dims.get());
|
|
}
|
|
|
|
ivl_type_t type_identifier_t::elaborate_type_raw(Design *des,
|
|
NetScope *scope) const
|
|
{
|
|
if (!scope)
|
|
return new netvector_t(IVL_VT_LOGIC);
|
|
|
|
ivl_type_t type = identifier_->elaborate_type(
|
|
des, scope, PExpr::type_elaboration_context_t::REQUIRED_TYPE);
|
|
if (type)
|
|
return type;
|
|
|
|
return new netvector_t(IVL_VT_LOGIC);
|
|
}
|
|
|
|
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 definition `" << 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 = basic_type.matches(elab_type);
|
|
|
|
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;
|
|
}
|
|
|
|
ivl_type_t type_parameter_t::elaborate_type_raw(Design *des, NetScope*scope) const
|
|
{
|
|
ivl_type_t type;
|
|
|
|
scope->get_parameter(des, name, type);
|
|
|
|
// Recover
|
|
if (!type)
|
|
return netvector_t::integer_type();
|
|
|
|
return type;
|
|
}
|