diff --git a/PExpr.h b/PExpr.h index 0aef99878..b934763f6 100644 --- a/PExpr.h +++ b/PExpr.h @@ -128,6 +128,13 @@ class PExpr : public LineInfo { virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); + // Return true if this expression represents a type in this scope. This + // may cache lookup state for a subsequent elaborate_type() call. + virtual bool test_type(Design *des, NetScope *scope); + + // Elaborate this expression as a type. Return null on failure. + virtual ivl_type_t elaborate_type(Design *des, NetScope *scope) const; + // After the test_width method is complete, these methods // return valid results. ivl_variable_type_t expr_type() const { return expr_type_; } @@ -377,6 +384,8 @@ class PEIdent : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const override; + bool test_type(Design *des, NetScope *scope) override; + ivl_type_t elaborate_type(Design *des, NetScope *scope) const override; virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const override; @@ -396,10 +405,22 @@ class PEIdent : public PExpr { const pform_scoped_name_t& path() const { return path_; } private: + // Type testing and elaboration normally happen as a pair. Preserve the + // lookup result between them, but only reuse it in the same scope. + struct type_lookup_t { + const NetScope *lookup_scope = nullptr; + NetScope *declaration_scope = nullptr; + typedef_t *type_def = nullptr; + bool valid = false; + }; + pform_scoped_name_t path_; bool no_implicit_sig_; + type_lookup_t type_lookup_; + + bool find_type_(Design *des, NetScope *scope, + struct symbol_search_results &search_results) const; - private: // Common functions to calculate parts of part/bit // selects. These methods return true if the expressions // elaborate/calculate, or false if there is some sort of @@ -710,8 +731,8 @@ class PETypename : public PExpr { width_mode_t&mode) override; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const override; - - inline data_type_t* get_type() const { return data_type_; } + bool test_type(Design *des, NetScope *scope) override; + ivl_type_t elaborate_type(Design *des, NetScope *scope) const override; private: data_type_t*data_type_; diff --git a/elab_expr.cc b/elab_expr.cc index ec3307ffa..9b381394f 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -488,6 +488,16 @@ unsigned PExpr::test_width(Design*des, NetScope*, width_mode_t&) return 1; } +ivl_type_t PExpr::elaborate_type(Design *, NetScope *) const +{ + return nullptr; +} + +bool PExpr::test_type(Design *, NetScope *) +{ + return false; +} + NetExpr* PExpr::elaborate_expr(Design*des, NetScope*scope, ivl_type_t, unsigned flags) const { // Fall back to the old method. Currently the new method won't be used @@ -1735,7 +1745,7 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, if (expr == 0) return 0; - if (! dynamic_cast(expr)) { + if (!expr->test_type(des, scope)) { // The argument type/width is self-determined and doesn't // affect the result type/width. Note that if the // argument is a type name (a special case) then @@ -2332,9 +2342,10 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, PExpr *expr = parms_[0].parm; uint64_t use_width = 0; - if (const PETypename*type_expr = dynamic_cast(expr)) { - ivl_type_t data_type = type_expr->get_type()->elaborate_type(des, scope); - ivl_assert(*this, data_type); + if (expr->test_type(des, scope)) { + ivl_type_t data_type = expr->elaborate_type(des, scope); + if (!data_type) + return nullptr; use_width = 1; while (const netuarray_t *utype = dynamic_cast(data_type)) { @@ -4991,6 +5002,137 @@ ivl_type_t PEIdent::resolve_type_(Design *des, const symbol_search_results &sr, return type; } +bool PEIdent::find_type_(Design *des, NetScope *scope, + struct symbol_search_results &search_results) const +{ + return symbol_search(this, des, scope, path_, lexical_pos(), + &search_results, + SYMBOL_SEARCH_NO_SIGNAL_ELABORATION) + && search_results.type_def; +} + +bool PEIdent::test_type(Design *des, NetScope *scope) +{ + if (type_lookup_.valid && type_lookup_.lookup_scope == scope) + return type_lookup_.type_def != nullptr; + + symbol_search_results search_results; + find_type_(des, scope, search_results); + + type_lookup_.lookup_scope = scope; + type_lookup_.declaration_scope = search_results.scope; + type_lookup_.type_def = search_results.type_def; + type_lookup_.valid = true; + + return type_lookup_.type_def != nullptr; +} + +static bool elaborate_type_dimensions(Design *des, NetScope *scope, + const std::list &indices, + netranges_t &dimensions) +{ + dimensions.reserve(indices.size()); + bool dimensions_ok = true; + + for (const auto &index : indices) { + PExpr *range_msb = index.msb; + PExpr *range_lsb = nullptr; + + switch (index.sel) { + case index_component_t::SEL_BIT: + break; + case index_component_t::SEL_PART: + range_lsb = index.lsb; + break; + case index_component_t::SEL_NONE: + cerr << index.get_fileline() << ": error: " + << "An unsized dimension is not allowed here." << endl; + des->errors++; + dimensions_ok = false; + continue; + case index_component_t::SEL_BIT_LAST: + case index_component_t::SEL_QUEUE_BOUND: + cerr << index.get_fileline() << ": error: " + << "A queue dimension is not allowed here." << endl; + des->errors++; + dimensions_ok = false; + continue; + case index_component_t::SEL_IDX_UP: + case index_component_t::SEL_IDX_DO: + cerr << index.get_fileline() << ": error: " + << "An indexed part select is not allowed in a dimension." + << endl; + des->errors++; + dimensions_ok = false; + continue; + } + + long range_msb_value = 0; + long range_lsb_value = 0; + pform_range_t range(range_msb, range_lsb); + dimensions_ok &= evaluate_range(des, scope, &index, range, + range_msb_value, range_lsb_value); + dimensions.emplace_back(range_msb_value, range_lsb_value); + } + + return dimensions_ok; +} + +ivl_type_t PEIdent::elaborate_type(Design *des, NetScope *scope) const +{ + symbol_search_results search_results; + NetScope *declaration_scope; + typedef_t *type_def; + + if (type_lookup_.valid && type_lookup_.lookup_scope == scope) { + declaration_scope = type_lookup_.declaration_scope; + type_def = type_lookup_.type_def; + } else { + if (!find_type_(des, scope, search_results)) + return nullptr; + declaration_scope = search_results.scope; + type_def = search_results.type_def; + } + + if (!type_def) + return nullptr; + + // Recognize types at the end of hierarchical paths during lookup so + // they are not mistaken for values, but reject the hierarchical type + // reference during elaboration. + if (path_.name.size() != 1) { + cerr << get_fileline() << ": error: Type name `" << path_ + << "' cannot be referenced through a hierarchical path." + << endl; + cerr << type_def->get_fileline() + << ": : The type was declared here." << endl; + des->errors++; + return nullptr; + } + + ivl_type_t base_type = type_def->elaborate_type(des, declaration_scope); + if (!base_type) + return nullptr; + + const auto &name = path_.name.front(); + netranges_t packed_dimensions; + if (!elaborate_type_dimensions(des, scope, name.index, + packed_dimensions)) + return nullptr; + + if (packed_dimensions.empty()) + return base_type; + + if (!base_type->packed()) { + cerr << get_fileline() << ": error: Packed array base-type `" + << name.name << "` is not packed." << endl; + des->errors++; + return nullptr; + } + + return new netparray_t(packed_dimensions, base_type); +} + unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) { symbol_search_results sr; @@ -7881,6 +8023,16 @@ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, * A typename expression is only legal in very narrow cases. This is * just a placeholder. */ +ivl_type_t PETypename::elaborate_type(Design *des, NetScope *scope) const +{ + return data_type_->elaborate_type(des, scope); +} + +bool PETypename::test_type(Design *, NetScope *) +{ + return true; +} + unsigned PETypename::test_width(Design*des, NetScope*, width_mode_t&) { cerr << get_fileline() << ": error: " diff --git a/net_design.cc b/net_design.cc index 8ea4bf7be..8172cffa1 100644 --- a/net_design.cc +++ b/net_design.cc @@ -822,8 +822,7 @@ void NetScope::evaluate_parameter_string_(Design*des, param_ref_t cur) void NetScope::evaluate_type_parameter_(Design *des, param_ref_t cur) { - const PETypename *type_expr = dynamic_cast(cur->second.val_expr); - if (!type_expr) { + if (!cur->second.val_expr->test_type(des, cur->second.val_scope)) { cerr << this->get_fileline() << ": error: " << "Type parameter `" << cur->first << "` value `" << *cur->second.val_expr << "` is not a type." @@ -835,14 +834,13 @@ void NetScope::evaluate_type_parameter_(Design *des, param_ref_t cur) return; } - 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); + cur->second.ivl_type = cur->second.val_expr->elaborate_type( + des, cur->second.val_scope); if (!cur->second.ivl_type) return; if (!cur->second.type_restrict.matches(cur->second.ivl_type)) { - cerr << type_expr->get_fileline() << ": error: " + cerr << cur->second.val_expr->get_fileline() << ": error: " << "Type parameter `" << cur->first << "` expects a `" << cur->second.type_restrict << "` type, got `" << *cur->second.ivl_type << "`." << endl; diff --git a/parse.y b/parse.y index b4f75eb10..3548977dd 100644 --- a/parse.y +++ b/parse.y @@ -224,7 +224,7 @@ static void validate_hierarchy_index_components( } } -static void append_hierarchy_identifier_component( +static void append_identifier_component( pform_name_t &path, perm_string name, std::list *components) { @@ -234,10 +234,37 @@ static void append_hierarchy_identifier_component( if (!component_list) return; - validate_hierarchy_index_components(*component_list); path.back().index.splice(path.back().index.end(), *component_list); } +static void append_hierarchy_identifier_component( + pform_name_t &path, perm_string name, + std::list *components) +{ + if (components) + validate_hierarchy_index_components(*components); + + append_identifier_component(path, name, components); +} + +static PExpr *make_type_identifier_expression( + const struct vlltype &loc, PPackage *package, + char *text, + std::list *components) +{ + pform_name_t name; + append_identifier_component(name, lex_strings.make(text), components); + + PExpr *expr; + if (package) + expr = pform_package_ident(loc, package, &name); + else + expr = pform_new_ident(loc, name, true); + + delete[]text; + return expr; +} + static std::list * make_dimensions(std::list *components) { @@ -1189,7 +1216,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type timeskew_fullskew_opt_remain_active_flag %type assignment_pattern expression expression_opt expr_mintypmax -%type expr_primary_or_typename expr_primary call_chain_expr +%type expr_primary_or_typename expr_primary call_chain_expr type_value %type class_new dynamic_array_new %type net_decl_initializer_opt var_decl_initializer_opt initializer_opt %type inc_or_dec_expression inside_expression lpvalue @@ -4540,12 +4567,22 @@ expr_primary_or_typename /* There are a few special cases (notably $bits argument) where the expression may be a type name. Let the elaborator sort this out. */ - | data_type - { PETypename*tmp = new PETypename($1); + | type_value + ; + +type_value + : atomic_type + { auto tmp = new PETypename($1); FILE_NAME(tmp, @1); $$ = tmp; } - + | TYPE_IDENTIFIER index_components_opt + { $$ = make_type_identifier_expression(@1, nullptr, $1.text, $2); + } + | package_scope TYPE_IDENTIFIER index_components_opt + { lex_in_package_scope(nullptr); + $$ = make_type_identifier_expression(@2, $1, $2.text, $3); + } ; /* SystemVerilog: a().b() — call a function, then invoke a method on the