mirror of
https://github.com/steveicarus/iverilog.git
synced 2026-09-08 04:20:49 +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]>
127 lines
2.9 KiB
C++
127 lines
2.9 KiB
C++
/*
|
|
* Copyright (c) 2007-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 "pform_types.h"
|
|
# include "PExpr.h"
|
|
# include "netclass.h"
|
|
# include "netenum.h"
|
|
|
|
data_type_t::~data_type_t()
|
|
{
|
|
}
|
|
|
|
type_identifier_t::type_identifier_t(PEIdent *identifier)
|
|
: identifier_(identifier)
|
|
{
|
|
}
|
|
|
|
type_identifier_t::~type_identifier_t() = default;
|
|
|
|
PNamedItem::SymbolType data_type_t::symbol_type() const
|
|
{
|
|
return TYPE;
|
|
}
|
|
|
|
string_type_t::~string_type_t()
|
|
{
|
|
}
|
|
|
|
atom_type_t size_type (atom_type_t::INT, true);
|
|
|
|
PNamedItem::SymbolType enum_type_t::symbol_type() const
|
|
{
|
|
return ENUM;
|
|
}
|
|
|
|
PNamedItem::SymbolType class_type_t::symbol_type() const
|
|
{
|
|
return CLASS;
|
|
}
|
|
|
|
bool typedef_t::set_data_type(data_type_t *t)
|
|
{
|
|
if (data_type.get())
|
|
return false;
|
|
|
|
data_type.reset(t);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool typedef_t::set_basic_type(type_restrict_t type)
|
|
{
|
|
return basic_type.merge(type);
|
|
}
|
|
|
|
bool type_restrict_t::merge(type_restrict_t other)
|
|
{
|
|
if (other.type == ANY)
|
|
return true;
|
|
if (this->type != ANY && other.type != this->type)
|
|
return false;
|
|
|
|
this->type = other.type;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool type_restrict_t::matches(ivl_type_t ivl_type) const
|
|
{
|
|
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 type_restrict_t::ENUM:
|
|
out << "enum";
|
|
break;
|
|
case type_restrict_t::STRUCT:
|
|
out << "struct";
|
|
break;
|
|
case type_restrict_t::UNION:
|
|
out << "union";
|
|
break;
|
|
case type_restrict_t::CLASS:
|
|
out << "class";
|
|
break;
|
|
}
|
|
|
|
return out;
|
|
}
|