Consider types during symbol lookup
SystemVerilog places user-defined types and data identifiers in the same namespace. Symbol lookup cannot currently return a type, so a value lookup can skip a nearer typedef and incorrectly bind a value in an outer scope when an ambiguous identifier is resolved during elaboration. Add typedef results to `symbol_search()` and check visible typedefs before continuing to enclosing scopes. Use the lexical visibility limit selected by the caller. This is normally the reference position, while function and task call lookup uses the end of the current scope as required by the LRM section 26.3. Let callers that require a value or another non-type symbol diagnose the type result and report its declaration location. This includes function and task calls, expressions with and without a required type, procedural l-values, named-event triggers, and disable targets. Callers that only probe for a particular kind of symbol, including port-connection matching, ignore a type result. Also ensure scope-only callers do not confuse the typedef's owning scope with the resolved object. This prepares expression elaboration to defer the type-versus-value decision while preserving lexical shadowing. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
parent
3ed306bab8
commit
c5ab989dc7
3
PExpr.cc
3
PExpr.cc
|
|
@ -488,6 +488,9 @@ bool PEIdent::has_aa_term(Design*des, NetScope*scope) const
|
|||
if (!symbol_search(this, des, scope, path_, lexical_pos(), &sr))
|
||||
return false;
|
||||
|
||||
if (sr.type_def)
|
||||
return false;
|
||||
|
||||
// Class properties are not considered automatic since a non-blocking
|
||||
// assignment to an object stored in an automatic variable is supposed to
|
||||
// capture a reference to the object, not the variable.
|
||||
|
|
|
|||
|
|
@ -3271,6 +3271,9 @@ NetExpr* PECallFunction::elaborate_expr_(Design*des, NetScope*scope,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!search_results.require_non_type(this, des, "in a function call"))
|
||||
return 0;
|
||||
|
||||
// If the symbol is found, but is not a scope...
|
||||
if (! search_results.is_scope() && !test_function_return_value(search_results)) {
|
||||
|
||||
|
|
@ -5146,6 +5149,9 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
|
|||
symbol_search_results sr;
|
||||
symbol_search(this, des, scope, path_, lexical_pos(), &sr);
|
||||
|
||||
if (!sr.require_non_type(this, des, "in an expression"))
|
||||
return nullptr;
|
||||
|
||||
if (!sr.net) {
|
||||
cerr << get_fileline() << ": error: Unable to bind variable `"
|
||||
<< path_ << "' in `" << scope_path(scope) << "'" << endl;
|
||||
|
|
@ -5466,6 +5472,9 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope,
|
|||
symbol_search_results sr;
|
||||
symbol_search(this, des, scope, path_, lexical_pos(), &sr);
|
||||
|
||||
if (!sr.require_non_type(this, des, "in an expression"))
|
||||
return 0;
|
||||
|
||||
// If the identifier name is a parameter name, then return
|
||||
// the parameter value.
|
||||
if (sr.par_val != 0) {
|
||||
|
|
|
|||
|
|
@ -178,6 +178,9 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
|||
symbol_search_results sr;
|
||||
symbol_search(this, des, scope, path_, lexical_pos(), &sr);
|
||||
|
||||
if (!sr.require_non_type(this, des, "as a procedural l-value"))
|
||||
return nullptr;
|
||||
|
||||
NetNet *reg = sr.net;
|
||||
const pform_name_t &member_path = sr.path_tail;
|
||||
|
||||
|
|
|
|||
20
elaborate.cc
20
elaborate.cc
|
|
@ -1274,7 +1274,7 @@ bool PGModule::match_module_ports_(Design*des, const Module*rmod,
|
|||
wildcard.lexical_pos(), &sr);
|
||||
if (sr.net != 0 ||
|
||||
(rmod->ports[j]->is_interface_port() &&
|
||||
sr.scope != 0 && sr.scope->is_interface())) {
|
||||
sr.is_scope() && sr.scope->is_interface())) {
|
||||
pins[j] = new PEIdent(
|
||||
rmod->ports[j]->name,
|
||||
wildcard.lexical_pos(), true);
|
||||
|
|
@ -1450,7 +1450,8 @@ static bool resolve_interface_actual_scope(const PExpr*actual,
|
|||
symbol_search_results sr;
|
||||
symbol_search(actual, des, parent_scope, actual_ident->path(),
|
||||
actual_ident->lexical_pos(), &sr);
|
||||
res.scope = sr.scope;
|
||||
if (sr.is_scope())
|
||||
res.scope = sr.scope;
|
||||
if (sr.through_interface_alias())
|
||||
res.modport = sr.interface_alias_modport;
|
||||
}
|
||||
|
|
@ -1462,7 +1463,8 @@ static bool resolve_interface_actual_scope(const PExpr*actual,
|
|||
symbol_search(actual, des, parent_scope, actual_ident->path(),
|
||||
actual_ident->lexical_pos(), &sr);
|
||||
|
||||
res.scope = sr.scope;
|
||||
if (sr.is_scope())
|
||||
res.scope = sr.scope;
|
||||
if (sr.through_interface_alias())
|
||||
res.modport = sr.interface_alias_modport;
|
||||
else if (NetScope*child = parent_scope->child(hname_t(res.display_name)))
|
||||
|
|
@ -3956,6 +3958,10 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const
|
|||
NetScope *func_scope = nullptr;
|
||||
if (symbol_search(this, des, scope, call_path, lexical_pos(),
|
||||
&search_results, true)) {
|
||||
if (!search_results.require_non_type(
|
||||
this, des, "in a task or function call"))
|
||||
return nullptr;
|
||||
|
||||
if (search_results.is_scope()) {
|
||||
if (search_results.scope->type() == NetScope::TASK)
|
||||
task = search_results.scope;
|
||||
|
|
@ -5264,6 +5270,10 @@ NetProc* PDisable::elaborate(Design*des, NetScope*scope) const
|
|||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
if (!search_results.require_non_type(this, des,
|
||||
"as a disable target"))
|
||||
return nullptr;
|
||||
|
||||
if (!search_results.is_scope()) {
|
||||
cerr << get_fileline() << ": error: Cannot disable "
|
||||
<< search_results.result_type() << " `" << scope_ << "'."
|
||||
|
|
@ -6669,6 +6679,8 @@ NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const
|
|||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
if (!sr.require_non_type(this, des, "as a named event"))
|
||||
return nullptr;
|
||||
|
||||
if (!sr.eve) {
|
||||
cerr << get_fileline() << ": error: <" << event_ << ">"
|
||||
|
|
@ -6698,6 +6710,8 @@ NetProc* PNBTrigger::elaborate(Design*des, NetScope*scope) const
|
|||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
if (!sr.require_non_type(this, des, "as a named event"))
|
||||
return nullptr;
|
||||
|
||||
if (sr.eve == 0) {
|
||||
cerr << get_fileline() << ": error: <" << event_ << ">"
|
||||
|
|
|
|||
13
net_scope.cc
13
net_scope.cc
|
|
@ -244,6 +244,19 @@ void NetScope::add_typedefs(const map<perm_string,typedef_t*>*typedefs)
|
|||
typedefs_ = *typedefs;
|
||||
}
|
||||
|
||||
typedef_t *NetScope::lookup_typedef(perm_string name,
|
||||
unsigned int lexical_pos) const
|
||||
{
|
||||
auto type = typedefs_.find(name);
|
||||
if (type == typedefs_.end())
|
||||
return nullptr;
|
||||
|
||||
if (type->second->lexical_pos() > lexical_pos)
|
||||
return nullptr;
|
||||
|
||||
return type->second;
|
||||
}
|
||||
|
||||
/*
|
||||
* Type names are resolved to typedef_t objects during parsing. Locate the
|
||||
* scope that owns that exact object instead of resolving its name again
|
||||
|
|
|
|||
|
|
@ -1006,6 +1006,11 @@ class NetScope : public Definitions, public Attrib {
|
|||
|
||||
void add_typedefs(const std::map<perm_string,typedef_t*>*typedefs);
|
||||
|
||||
/* Find a type declared in this scope and visible at the given source
|
||||
position. */
|
||||
typedef_t *lookup_typedef(perm_string name,
|
||||
unsigned int lexical_pos) const;
|
||||
|
||||
/* Locate the scope that owns the resolved typedef object. */
|
||||
NetScope*find_typedef_scope(const Design*des, const typedef_t*type_i);
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ struct symbol_search_results {
|
|||
net = 0;
|
||||
par_val = 0;
|
||||
type = 0;
|
||||
type_def = nullptr;
|
||||
eve = 0;
|
||||
decl_after_use = 0;
|
||||
interface_alias_scope = 0;
|
||||
|
|
@ -59,6 +60,7 @@ struct symbol_search_results {
|
|||
if (net) return false;
|
||||
if (eve) return false;
|
||||
if (par_val) return false;
|
||||
if (type_def) return false;
|
||||
if (scope) return true;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -67,6 +69,7 @@ struct symbol_search_results {
|
|||
if (net) return true;
|
||||
if (eve) return true;
|
||||
if (par_val) return true;
|
||||
if (type_def) return true;
|
||||
if (scope) return true;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -75,10 +78,14 @@ struct symbol_search_results {
|
|||
if (net) return "net";
|
||||
if (eve) return "named event";
|
||||
if (par_val) return "parameter";
|
||||
if (type_def) return "type";
|
||||
if (scope) return "scope";
|
||||
return "nothing found";
|
||||
}
|
||||
|
||||
bool require_non_type(const LineInfo *li, Design *des,
|
||||
const char *use) const;
|
||||
|
||||
inline bool through_interface_alias() const {
|
||||
return interface_alias_target != 0;
|
||||
}
|
||||
|
|
@ -92,6 +99,8 @@ struct symbol_search_results {
|
|||
// optional value dimensions.
|
||||
const NetExpr*par_val;
|
||||
ivl_type_t type;
|
||||
// If this is a type, the parsed type declaration.
|
||||
typedef_t *type_def;
|
||||
// If this is a named event, ...
|
||||
NetEvent*eve;
|
||||
// If a symbol was located but skipped because its lexical position
|
||||
|
|
|
|||
|
|
@ -30,6 +30,21 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
bool symbol_search_results::require_non_type(const LineInfo *li,
|
||||
Design *des,
|
||||
const char *use) const
|
||||
{
|
||||
if (!type_def)
|
||||
return true;
|
||||
|
||||
cerr << li->get_fileline() << ": error: Type name `" << path_head
|
||||
<< "' cannot be used " << use << "." << endl;
|
||||
cerr << type_def->get_fileline() << ": : The type was declared here."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
enum class scope_object_search_result_t {
|
||||
not_found,
|
||||
found,
|
||||
|
|
@ -39,8 +54,8 @@ enum class scope_object_search_result_t {
|
|||
static scope_object_search_result_t symbol_search_scope_objects(
|
||||
const LineInfo *li, Design *des, NetScope *scope,
|
||||
NetScope *start_scope, const pform_name_t &path,
|
||||
const name_component_t &path_tail, unsigned int lexical_pos,
|
||||
bool check_lexical_order, struct symbol_search_results *res)
|
||||
const name_component_t &path_tail, unsigned int visibility_pos,
|
||||
struct symbol_search_results *res)
|
||||
{
|
||||
// Special case `super` keyword. Return the `this` object, but
|
||||
// with the type of the base class.
|
||||
|
|
@ -70,8 +85,7 @@ static scope_object_search_result_t symbol_search_scope_objects(
|
|||
}
|
||||
|
||||
if (NetNet *net = scope->find_signal(path_tail.name)) {
|
||||
bool decl_after_use = check_lexical_order
|
||||
&& !(net->lexical_pos() <= lexical_pos);
|
||||
bool decl_after_use = !(net->lexical_pos() <= visibility_pos);
|
||||
if (!gn_strict_net_var_declaration || !decl_after_use) {
|
||||
pform_name_t path_head = path;
|
||||
path_head.push_back(path_tail);
|
||||
|
|
@ -87,7 +101,7 @@ static scope_object_search_result_t symbol_search_scope_objects(
|
|||
<< ": : the net/variable is declared here."
|
||||
<< endl;
|
||||
// suppress further warnings for this net
|
||||
net->lexical_pos(lexical_pos);
|
||||
net->lexical_pos(visibility_pos);
|
||||
}
|
||||
return scope_object_search_result_t::found;
|
||||
} else if (!res->decl_after_use) {
|
||||
|
|
@ -96,8 +110,7 @@ static scope_object_search_result_t symbol_search_scope_objects(
|
|||
}
|
||||
|
||||
if (NetEvent *eve = scope->find_event(path_tail.name)) {
|
||||
bool decl_after_use = check_lexical_order
|
||||
&& !(eve->lexical_pos() <= lexical_pos);
|
||||
bool decl_after_use = !(eve->lexical_pos() <= visibility_pos);
|
||||
if (!gn_strict_net_var_declaration || !decl_after_use) {
|
||||
pform_name_t path_head = path;
|
||||
path_head.push_back(path_tail);
|
||||
|
|
@ -111,7 +124,7 @@ static scope_object_search_result_t symbol_search_scope_objects(
|
|||
cerr << eve->get_fileline()
|
||||
<< ": : the event is declared here." << endl;
|
||||
// suppress further warnings for this event
|
||||
eve->lexical_pos(lexical_pos);
|
||||
eve->lexical_pos(visibility_pos);
|
||||
}
|
||||
return scope_object_search_result_t::found;
|
||||
} else if (!res->decl_after_use) {
|
||||
|
|
@ -119,14 +132,26 @@ static scope_object_search_result_t symbol_search_scope_objects(
|
|||
}
|
||||
}
|
||||
|
||||
// Types and data objects share a namespace. Return a visible
|
||||
// type even when the caller requires a value so it cannot
|
||||
// incorrectly continue searching an outer scope.
|
||||
if (auto type_def = scope->lookup_typedef(
|
||||
path_tail.name, visibility_pos)) {
|
||||
pform_name_t path_head = path;
|
||||
path_head.push_back(path_tail);
|
||||
res->scope = scope;
|
||||
res->type_def = type_def;
|
||||
res->path_head = path_head;
|
||||
return scope_object_search_result_t::found;
|
||||
}
|
||||
|
||||
if (const NetExpr *par = scope->get_parameter(
|
||||
des, path_tail.name, res->type)) {
|
||||
bool is_enum_name;
|
||||
unsigned int declaration_pos =
|
||||
scope->get_constant_lexical_pos(path_tail.name,
|
||||
is_enum_name);
|
||||
bool decl_after_use = check_lexical_order
|
||||
&& !(declaration_pos <= lexical_pos);
|
||||
bool decl_after_use = !(declaration_pos <= visibility_pos);
|
||||
if (!gn_strict_parameter_declaration || !decl_after_use) {
|
||||
pform_name_t path_head = path;
|
||||
path_head.push_back(path_tail);
|
||||
|
|
@ -145,7 +170,7 @@ static scope_object_search_result_t symbol_search_scope_objects(
|
|||
<< " is declared here." << endl;
|
||||
// suppress further warnings for this constant
|
||||
scope->set_constant_lexical_pos(
|
||||
path_tail.name, lexical_pos);
|
||||
path_tail.name, visibility_pos);
|
||||
}
|
||||
return scope_object_search_result_t::found;
|
||||
} else if (!res->decl_after_use) {
|
||||
|
|
@ -180,7 +205,7 @@ static scope_object_search_result_t symbol_search_scope_objects(
|
|||
// Finally check the rare case of a signal that hasn't
|
||||
// been elaborated yet.
|
||||
if (PWire *wire = scope->find_signal_placeholder(path_tail.name)) {
|
||||
if (!check_lexical_order || (wire->lexical_pos() <= lexical_pos)) {
|
||||
if (wire->lexical_pos() <= visibility_pos) {
|
||||
NetNet *net = wire->elaborate_sig(des, scope);
|
||||
if (!net)
|
||||
return scope_object_search_result_t::failed;
|
||||
|
|
@ -413,8 +438,8 @@ static bool symbol_search_(const LineInfo *li, Design *des, NetScope *scope,
|
|||
|
||||
// An explicit $unit:: prefix only disambiguates the name and does
|
||||
// not allow forward references (LRM 3.12.1).
|
||||
const bool check_lexical_order =
|
||||
!scope_is_bound || scope->is_unit();
|
||||
const unsigned int visibility_pos =
|
||||
(!scope_is_bound || scope->is_unit()) ? lexical_pos : UINT_MAX;
|
||||
|
||||
if (scope->genvar_tmp.str() && path_tail.name == scope->genvar_tmp)
|
||||
return false;
|
||||
|
|
@ -435,7 +460,7 @@ static bool symbol_search_(const LineInfo *li, Design *des, NetScope *scope,
|
|||
scope_object_search_result_t object_result =
|
||||
symbol_search_scope_objects(
|
||||
li, des, scope, start_scope, path, path_tail,
|
||||
lexical_pos, check_lexical_order, res);
|
||||
visibility_pos, res);
|
||||
if (object_result == scope_object_search_result_t::found)
|
||||
return true;
|
||||
if (object_result == scope_object_search_result_t::failed)
|
||||
|
|
|
|||
Loading…
Reference in New Issue