From f6a51bc9db575362b7e29031a46f575c25f2d157 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 6 Jan 2023 19:13:29 -0800 Subject: [PATCH] Add support for binding function/task arguments by name In addition to providing positional arguments for task and functions SystemVerilog allows to bind arguments by name. This is similar to how module ports can be bound by name. ``` task t(int a, int b); ... endtask ... t(.b(1), .a(2)); ``` Extend the parser and elaboration stage to be able to handle this. During elaboration the named argument list is transformed into a purely positional list so that later stages like synthesis do not have to care about the names. For system functions and tasks all arguments must be unnamed, otherwise an error will be reported. In addition to functions and tasks arguments can also be bound by name for the various different ways of invoking a class constructor. Signed-off-by: Lars-Peter Clausen --- Makefile.in | 3 +- PExpr.cc | 28 +++--- PExpr.h | 18 ++-- PTask.h | 3 +- Statement.cc | 13 ++- Statement.h | 21 ++-- elab_expr.cc | 188 ++++++++++++++++++++---------------- elab_sig.cc | 13 ++- elaborate.cc | 241 ++++++++++++++++++++++++++++++---------------- map_named_args.cc | 75 +++++++++++++++ map_named_args.h | 22 +++++ netmisc.cc | 3 + parse.y | 95 ++++++++++++------ pform.cc | 6 +- pform.h | 8 +- pform_analog.cc | 16 +-- pform_dump.cc | 10 +- pform_pclass.cc | 13 ++- pform_types.h | 2 +- 19 files changed, 504 insertions(+), 274 deletions(-) create mode 100644 map_named_args.cc create mode 100644 map_named_args.h diff --git a/Makefile.in b/Makefile.in index 65223fb60..ace61b063 100644 --- a/Makefile.in +++ b/Makefile.in @@ -114,7 +114,8 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ net_event.o net_expr.o net_func.o \ net_func_eval.o net_link.o net_modulo.o \ net_nex_input.o net_nex_output.o net_proc.o net_scope.o net_tran.o \ - net_udp.o pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ + net_udp.o map_named_args.o \ + pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ pform_disciplines.o pform_dump.o pform_package.o pform_pclass.o \ pform_types.o \ symbol_search.o sync.o sys_funcs.o verinum.o verireal.o vpi_modules.o target.o \ diff --git a/PExpr.cc b/PExpr.cc index 9ca575264..73679a4b1 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -215,12 +215,12 @@ PEBShift::~PEBShift() { } -PECallFunction::PECallFunction(const pform_name_t&n, const vector &parms) +PECallFunction::PECallFunction(const pform_name_t &n, const vector &parms) : path_(n), parms_(parms), is_overridden_(false) { } -PECallFunction::PECallFunction(PPackage*pkg, const pform_name_t&n, const vector &parms) +PECallFunction::PECallFunction(PPackage *pkg, const pform_name_t &n, const vector &parms) : path_(pkg, n), parms_(parms), is_overridden_(false) { } @@ -233,12 +233,12 @@ static pform_name_t pn_from_ps(perm_string n) return tmp; } -PECallFunction::PECallFunction(PPackage*pkg, const pform_name_t&n, const list &parms) +PECallFunction::PECallFunction(PPackage *pkg, const pform_name_t &n, const list &parms) : path_(pkg, n), parms_(parms.begin(), parms.end()), is_overridden_(false) { } -PECallFunction::PECallFunction(perm_string n, const vector&parms) +PECallFunction::PECallFunction(perm_string n, const vector &parms) : path_(pn_from_ps(n)), parms_(parms), is_overridden_(false) { } @@ -249,13 +249,13 @@ PECallFunction::PECallFunction(perm_string n) } // NOTE: Anachronism. Try to work all use of svector out. -PECallFunction::PECallFunction(const pform_name_t&n, const list &parms) +PECallFunction::PECallFunction(const pform_name_t &n, const list &parms) : path_(n), parms_(parms.begin(), parms.end()), is_overridden_(false) { } -PECallFunction::PECallFunction(perm_string n, const list&parms) -: package_(0), path_(pn_from_ps(n)), parms_(parms.begin(), parms.end()), is_overridden_(false) +PECallFunction::PECallFunction(perm_string n, const list &parms) +: path_(pn_from_ps(n)), parms_(parms.begin(), parms.end()), is_overridden_(false) { } @@ -265,18 +265,18 @@ PECallFunction::~PECallFunction() void PECallFunction::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { - for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - if (parms_[idx]) - parms_[idx]->declare_implicit_nets(scope, type); + for (const auto &parm : parms_) { + if (parm.parm) + parm.parm->declare_implicit_nets(scope, type); } } bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const { bool flag = false; - for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - if (parms_[idx]) - flag |= parms_[idx]->has_aa_term(des, scope); + for (const auto &parm : parms_) { + if (parm.parm) + flag |= parm.parm->has_aa_term(des, scope); } return flag; } @@ -466,7 +466,7 @@ PENewClass::PENewClass(void) { } -PENewClass::PENewClass(const list&p, data_type_t *class_type) +PENewClass::PENewClass(const list &p, data_type_t *class_type) : parms_(p.begin(), p.end()), class_type_(class_type) { } diff --git a/PExpr.h b/PExpr.h index 8958ed62a..a70a6f1e7 100644 --- a/PExpr.h +++ b/PExpr.h @@ -570,7 +570,7 @@ class PENewClass : public PExpr { // New without (or with default) constructor explicit PENewClass (); // New with constructor arguments - explicit PENewClass (const std::list&p, + explicit PENewClass (const std::list &p, data_type_t *class_type = nullptr); ~PENewClass(); @@ -592,7 +592,7 @@ class PENewClass : public PExpr { NetExpr*obj, unsigned flags) const; private: - std::vectorparms_; + std::vector parms_; data_type_t *class_type_; }; @@ -895,20 +895,20 @@ class PETernary : public PExpr { */ class PECallFunction : public PExpr { public: - explicit PECallFunction(const pform_name_t&n, const std::vector &parms); + explicit PECallFunction(const pform_name_t &n, const std::vector &parms); // Call function defined in package. - explicit PECallFunction(PPackage*pkg, const pform_name_t&n, const std::list &parms); + explicit PECallFunction(PPackage *pkg, const pform_name_t &n, const std::list &parms); // Used to convert a user function called as a task - explicit PECallFunction(PPackage*pkg, const pform_name_t&n, const std::vector &parms); + explicit PECallFunction(PPackage *pkg, const pform_name_t &n, const std::vector &parms); // Call of system function (name is not hierarchical) - explicit PECallFunction(perm_string n, const std::vector &parms); + explicit PECallFunction(perm_string n, const std::vector &parms); explicit PECallFunction(perm_string n); // std::list versions. Should be removed! - explicit PECallFunction(const pform_name_t&n, const std::list &parms); - explicit PECallFunction(perm_string n, const std::list &parms); + explicit PECallFunction(const pform_name_t &n, const std::list &parms); + explicit PECallFunction(perm_string n, const std::list &parms); ~PECallFunction(); @@ -929,7 +929,7 @@ class PECallFunction : public PExpr { private: pform_scoped_name_t path_; - std::vector parms_; + std::vector parms_; // For system functions. bool is_overridden_; diff --git a/PTask.h b/PTask.h index 4702d0a12..7ea3e924a 100644 --- a/PTask.h +++ b/PTask.h @@ -63,7 +63,8 @@ class PTaskFunc : public PScope, public PNamedItem { // default value expressions, if any. void elaborate_sig_ports_(Design*des, NetScope*scope, std::vector&ports, - std::vector&pdefs) const; + std::vector &pdefs, + std::vector &port_names) const; void dump_ports_(std::ostream&out, unsigned ind) const; diff --git a/Statement.cc b/Statement.cc index 41d0bbb22..1dda37940 100644 --- a/Statement.cc +++ b/Statement.cc @@ -166,17 +166,17 @@ PNamedItem::SymbolType PBlock::symbol_type() const return BLOCK; } -PCallTask::PCallTask(const pform_name_t&n, const list&p) +PCallTask::PCallTask(const pform_name_t &n, const list &p) : package_(0), path_(n), parms_(p.begin(), p.end()) { } -PCallTask::PCallTask(PPackage*pkg, const pform_name_t&n, const list&p) +PCallTask::PCallTask(PPackage *pkg, const pform_name_t &n, const list &p) : package_(pkg), path_(n), parms_(p.begin(), p.end()) { } -PCallTask::PCallTask(perm_string n, const list&p) +PCallTask::PCallTask(perm_string n, const list &p) : package_(0), parms_(p.begin(), p.end()) { path_.push_back(name_component_t(n)); @@ -216,11 +216,16 @@ PCAssign::~PCAssign() delete expr_; } -PChainConstructor::PChainConstructor(const list&parms) +PChainConstructor::PChainConstructor(const list &parms) : parms_(parms.begin(), parms.end()) { } +PChainConstructor::PChainConstructor(const vector &parms) +: parms_(parms) +{ +} + PChainConstructor::~PChainConstructor() { } diff --git a/Statement.h b/Statement.h index d9b7f0faf..6aa2e9bca 100644 --- a/Statement.h +++ b/Statement.h @@ -223,9 +223,9 @@ class PBreak : public Statement { class PCallTask : public Statement { public: - explicit PCallTask(PPackage*pkg, const pform_name_t&n, const std::list&parms); - explicit PCallTask(const pform_name_t&n, const std::list&parms); - explicit PCallTask(perm_string n, const std::list&parms); + explicit PCallTask(PPackage *pkg, const pform_name_t &n, const std::list &parms); + explicit PCallTask(const pform_name_t &n, const std::list &parms); + explicit PCallTask(perm_string n, const std::list &parms); ~PCallTask(); const pform_name_t& path() const; @@ -253,11 +253,13 @@ class PCallTask : public Statement { NetProc*elaborate_sys_task_method_(Design*des, NetScope*scope, NetNet*net, perm_string method_name, - const char*sys_task_name) const; + const char *sys_task_name, + const std::vector &parm_names = {}) const; NetProc*elaborate_queue_method_(Design*des, NetScope*scope, NetNet*net, perm_string method_name, - const char*sys_task_name) const; + const char *sys_task_name, + const std::vector &parm_names) const; NetProc*elaborate_method_func_(NetScope*scope, NetNet*net, ivl_type_t type, @@ -267,7 +269,7 @@ class PCallTask : public Statement { PPackage*package_; pform_name_t path_; - std::vector parms_; + std::vector parms_; bool void_cast_ = false; }; @@ -320,17 +322,18 @@ class PCAssign : public Statement { */ class PChainConstructor : public Statement { public: - explicit PChainConstructor(const std::list&parms); + explicit PChainConstructor(const std::list &parms); + explicit PChainConstructor(const std::vector &parms); ~PChainConstructor(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void dump(std::ostream&out, unsigned ind) const; - inline const std::vector& chain_args(void) const + inline const std::vector& chain_args(void) const { return parms_; } private: - std::vector parms_; + std::vector parms_; }; class PCondit : public Statement { diff --git a/elab_expr.cc b/elab_expr.cc index 45773cede..1918908cf 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -40,6 +40,7 @@ # include "netscalar.h" # include "util.h" # include "ivl_assert.h" +# include "map_named_args.h" using namespace std; @@ -1374,7 +1375,7 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, // The Icarus Verilog specific $ivlh_to_unsigned() system // task takes a second argument which is the output // size. This can be an arbitrary constant function. - PExpr*pexpr = parms_[1]; + PExpr *pexpr = parms_[1].parm; if (pexpr == 0) { cerr << get_fileline() << ": error: " << "Missing $ivlh_to_unsigned width." << endl; @@ -1396,7 +1397,7 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, // The argument width is self-determined and doesn't // affect the result width. width_mode_t arg_mode = SIZED; - parms_[0]->test_width(des, scope, arg_mode); + parms_[0].parm->test_width(des, scope, arg_mode); expr_type_ = pexpr->expr_type(); expr_width_ = value; @@ -1406,7 +1407,7 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, } if (name=="$signed" || name=="$unsigned") { - PExpr*expr = parms_[0]; + PExpr *expr = parms_[0].parm; if (expr == 0) return 0; @@ -1423,7 +1424,7 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, } if (name=="$sizeof" || name=="$bits") { - PExpr*expr = parms_[0]; + PExpr *expr = parms_[0].parm; if (expr == 0) return 0; @@ -1450,7 +1451,7 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, } if (name=="$is_signed") { - PExpr*expr = parms_[0]; + PExpr *expr = parms_[0].parm; if (expr == 0) return 0; @@ -1887,6 +1888,17 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, { perm_string name = peek_tail_name(path_); + // System functions don't have named parameters + for (const auto &parm : parms_) { + if (!parm.name.nil()) { + des->errors++; + cerr << parm.get_fileline() << ": error: " + << "The system function `" << name + << "` has no argument called `" << parm.name << "`." + << endl; + } + } + /* Catch the special case that the system function is the $ivl_unsigned function. In this case the second argument is the size of the expression, but should already be accounted @@ -1894,7 +1906,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, if (name=="$ivlh_to_unsigned") { ivl_assert(*this, parms_.size()==2); - PExpr*expr = parms_[0]; + PExpr *expr = parms_[0].parm; ivl_assert(*this, expr); NetExpr*sub = expr->elaborate_expr(des, scope, expr->expr_width(), flags); return cast_to_width_(sub, expr_wid); @@ -1904,7 +1916,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, function. Its argument will be evaluated as a self-determined expression. */ if (name=="$signed" || name=="$unsigned") { - if ((parms_.size() != 1) || (parms_[0] == 0)) { + if ((parms_.size() != 1) || !parms_[0].parm) { cerr << get_fileline() << ": error: The " << name << " function takes exactly one(1) argument." << endl; des->errors += 1; @@ -1922,7 +1934,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, cerr << get_fileline() << ": PECallFunction::elaborate_sfunc_: " << name << " expression is the argument cast to expr_wid=" << expr_wid << endl; } - PExpr*expr = parms_[0]; + PExpr *expr = parms_[0].parm; NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, flags); return cast_to_width_(sub, expr_wid); @@ -1933,7 +1945,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, sub-expression is not used, so the expression itself can be deleted. */ if (name=="$sizeof" || name=="$bits") { - if ((parms_.size() != 1) || (parms_[0] == 0)) { + if ((parms_.size() != 1) || !parms_[0].parm) { cerr << get_fileline() << ": error: The " << name << " function takes exactly one(1) argument." << endl; des->errors += 1; @@ -1944,7 +1956,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, cerr << get_fileline() << ": warning: $sizeof is deprecated." << " Use $bits() instead." << endl; - PExpr*expr = parms_[0]; + PExpr *expr = parms_[0].parm; uint64_t use_width = 0; if (PETypename*type_expr = dynamic_cast(expr)) { @@ -1991,14 +2003,14 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, a single bit flag -- 1 if the expression is signed, 0 otherwise. */ if (name=="$is_signed") { - if ((parms_.size() != 1) || (parms_[0] == 0)) { + if ((parms_.size() != 1) || !parms_[0].parm) { cerr << get_fileline() << ": error: The " << name << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } - PExpr*expr = parms_[0]; + PExpr *expr = parms_[0].parm; verinum val (expr->has_sign() ? verinum::V1 : verinum::V0, 1); NetEConst*sub = new NetEConst(val); @@ -2039,7 +2051,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, expression if one is created. */ /* These functions can work in a constant context with a signal expression. */ - if ((nparms == 1) && (dynamic_cast(parms_[0]))) { + if ((nparms == 1) && (dynamic_cast(parms_[0].parm))) { if (strcmp(name, "$dimensions") == 0) need_const = false; else if (strcmp(name, "$high") == 0) need_const = false; else if (strcmp(name, "$increment") == 0) need_const = false; @@ -2053,7 +2065,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, unsigned parm_errors = 0; unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < nparms ; idx += 1) { - PExpr*expr = parms_[idx]; + PExpr *expr = parms_[idx].parm; if (expr) { NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, expr, need_const); @@ -2092,7 +2104,7 @@ NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, NetBranch*branch = 0; if (parms_.size() == 1) { - PExpr*arg1 = parms_[0]; + PExpr *arg1 = parms_[0].parm; PEIdent*arg_ident = dynamic_cast (arg1); ivl_assert(*this, arg_ident); @@ -2147,7 +2159,7 @@ static NetExpr* check_for_enum_methods(const LineInfo*li, const pform_scoped_name_t&use_path, perm_string method_name, NetExpr*expr, - PExpr*parg, unsigned args) + const std::vector &parms) { if (debug_elaborate) { cerr << li->get_fileline() << ": " << __func__ << ": " @@ -2168,9 +2180,9 @@ static NetExpr* check_for_enum_methods(const LineInfo*li, // The "num()" method returns the number of elements. This is // actually a static constant, and can be replaced at compile time // with a constant value. - if (args != 0) { + if (parms.size() != 0) { cerr << li->get_fileline() << ": error: enumeration " - "method " << use_path << ".num() does not " + "method " << use_path << " does not " "take an argument." << endl; des->errors += 1; } @@ -2184,9 +2196,9 @@ static NetExpr* check_for_enum_methods(const LineInfo*li, // The "first()" method returns the first enumeration value. This // doesn't actually care about the constant value, and instead // returns as a constant literal the first value of the enumeration. - if (args != 0) { + if (parms.size() != 0) { cerr << li->get_fileline() << ": error: enumeration " - "method " << use_path << ".first() does not " + "method " << use_path << " does not " "take an argument." << endl; des->errors += 1; } @@ -2201,9 +2213,9 @@ static NetExpr* check_for_enum_methods(const LineInfo*li, // The "last()" method returns the first enumeration value. This // doesn't actually care about the constant value, and instead // returns as a constant literal the last value of the enumeration. - if (args != 0) { + if (parms.size() != 0) { cerr << li->get_fileline() << ": error: enumeration " - "method " << use_path << ".last() does not " + "method " << use_path << " does not " "take an argument." << endl; des->errors += 1; } @@ -2216,34 +2228,14 @@ static NetExpr* check_for_enum_methods(const LineInfo*li, NetESFunc*sys_expr; - // Process the method argument if it is available. - NetExpr* count = 0; - if (args != 0 && parg) { - count = elaborate_rval_expr(des, scope, &netvector_t::atom2u32, - parg); - if (count == 0) { - cerr << li->get_fileline() << ": error: unable to elaborate " - "enumeration method argument " << use_path << "." - << method_name << "(" << parg << ")." << endl; - args = 0; - des->errors += 1; - } else if (NetEEvent*evt = dynamic_cast (count)) { - cerr << evt->get_fileline() << ": error: An event '" - << evt->event()->name() << "' cannot be an enumeration " - "method argument." << endl; - args = 0; - des->errors += 1; - } - } - if (method_name == "name") { // The "name()" method returns the name of the current enumeration // value. The generated system task takes the enumeration // definition and the enumeration value. The return value is the // string name of the enumeration. - if (args != 0) { + if (parms.size() != 0) { cerr << li->get_fileline() << ": error: enumeration " - "method " << use_path << ".name() does not " + "method " << use_path << " does not " "take an argument." << endl; des->errors += 1; } @@ -2257,37 +2249,51 @@ static NetExpr* check_for_enum_methods(const LineInfo*li, sys_expr->parm(0, def); sys_expr->parm(1, expr); - } else if (method_name == "next") { - // The "next()" method returns the next enumeration value. - if (args > 1) { - cerr << li->get_fileline() << ": error: enumeration " - "method " << use_path << ".next() take at " - "most one argument." << endl; - des->errors += 1; - } - sys_expr = new NetESFunc("$ivl_enum_method$next", netenum, - 2 + (args != 0)); - NetENetenum* def = new NetENetenum(netenum); - def->set_line(*li); - sys_expr->parm(0, def); - sys_expr->parm(1, expr); - if (args != 0) sys_expr->parm(2, count); + } else if (method_name == "next" || method_name == "prev") { + static const std::vector parm_names = { + perm_string::literal("N"), + }; + auto args = map_named_args(des, parm_names, parms); - } else if (method_name == "prev") { - // The "prev()" method returns the previous enumeration value. - if (args > 1) { + // Process the method argument if it is available. + NetExpr *count = nullptr; + if (args.size() != 0 && args[0]) { + count = elaborate_rval_expr(des, scope, &netvector_t::atom2u32, + args[0]); + if (!count) { + cerr << li->get_fileline() << ": error: unable to elaborate " + "enumeration method argument " << use_path << "." + << method_name << "(" << args[0] << ")." << endl; + des->errors++; + } else if (NetEEvent *evt = dynamic_cast (count)) { + cerr << evt->get_fileline() << ": error: An event '" + << evt->event()->name() << "' cannot be an enumeration " + "method argument." << endl; + des->errors++; + } + } + + // The "next()" and "prev()" methods returns the next or previous enumeration value. + if (args.size() > 1) { cerr << li->get_fileline() << ": error: enumeration " - "method " << use_path << ".prev() take at " + "method " << use_path << " takes at " "most one argument." << endl; des->errors += 1; } - sys_expr = new NetESFunc("$ivl_enum_method$prev", netenum, - 2 + (args != 0)); + + const char *func_name; + if (method_name == "next") + func_name = "$ivl_enum_method$next"; + else + func_name = "$ivl_enum_method$prev"; + + sys_expr = new NetESFunc(func_name, netenum, + 2 + (count != nullptr)); NetENetenum* def = new NetENetenum(netenum); def->set_line(*li); sys_expr->parm(0, def); sys_expr->parm(1, expr); - if (args != 0) sys_expr->parm(2, count); + if (count) sys_expr->parm(2, count); } else { // This is an unknown enumeration method. @@ -3119,9 +3125,12 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, des->errors += 1; } + auto args = map_named_args(des, def, parms_, parm_off); + for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { unsigned pidx = idx + parm_off; - PExpr*tmp = (idx < actual_count) ? parms_[idx] : NULL; + PExpr *tmp = args[idx]; + if (tmp) { parms[pidx] = elaborate_rval_expr(des, scope, def->port(pidx)->net_type(), @@ -3354,12 +3363,10 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // Get the method name that we are looking for. perm_string method_name = search_results.path_tail.back().name; - - PExpr*tmp = parms_.size() ? parms_[0] : NULL; return check_for_enum_methods(this, des, scope, netenum, path_, method_name, sub_expr, - tmp, parms_.size()); + parms_); } // Class methods. Generate function call to the class method. @@ -3437,6 +3444,17 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, } if (method_name == "substr") { + if (parms_.size() != 2) + cerr << get_fileline() << ": error: Method `substr()`" + << " requires 2 arguments, got " << parms_.size() + << "." << endl; + + static const std::vector parm_names = { + perm_string::literal("i"), + perm_string::literal("j") + }; + auto args = map_named_args(des, parm_names, parms_); + NetESFunc*sys_expr = new NetESFunc("$ivl_string_method$substr", &netstring_t::type_string, 3); sys_expr->set_line(*this); @@ -3444,16 +3462,15 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // First argument is the source string. sys_expr->parm(0, sub_expr); - ivl_assert(*this, parms_.size() == 2); - NetExpr*tmp; + for (int i = 0; i < 2; i++) { + if (!args[i]) + continue; - tmp = elaborate_rval_expr(des, scope, &netvector_t::atom2u32, - parms_[0], false); - sys_expr->parm(1, tmp); - - tmp = elaborate_rval_expr(des, scope, &netvector_t::atom2u32, - parms_[1], false); - sys_expr->parm(2, tmp); + auto expr = elaborate_rval_expr(des, scope, + &netvector_t::atom2u32, + args[i], false); + sys_expr->parm(i + 1, expr); + } return sys_expr; } @@ -4943,7 +4960,7 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope, return check_for_enum_methods(this, des, scope, netenum, sr.path_head, member_comp.name, - expr, NULL, 0); + expr, {}); } ivl_assert(*this, sr.path_tail.empty()); @@ -6661,8 +6678,8 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope, // generate an error message. The case of too few arguments // will be handled below, when we run out of arguments. if ((parms_.size()+1) > def->port_count()) { - cerr << get_fileline() << ": error: Parm count mismatch" - << " passing " << parms_.size() << " arguments " + cerr << get_fileline() << ": error: Argument count mismatch." + << " Passing " << parms_.size() << " arguments" << " to constructor expecting " << (def->port_count()-1) << " arguments." << endl; des->errors += 1; @@ -6671,14 +6688,15 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope, vector parms (def->port_count()); parms[0] = obj; + auto args = map_named_args(des, def, parms_, 1); + int missing_parms = 0; for (size_t idx = 1 ; idx < parms.size() ; idx += 1) { // While there are default arguments, check them. - if (idx <= parms_.size() && parms_[idx-1]) { - PExpr*tmp = parms_[idx-1]; + if (args[idx - 1]) { parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->net_type(), - tmp, false); + args[idx - 1], false); // NOTE: if elaborate_rval_expr fails, it will return a // nullptr, but it will also increment des->errors so there // is nothing we need to do here. diff --git a/elab_sig.cc b/elab_sig.cc index a855d9948..c3a0fc64f 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -688,7 +688,8 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const vectorports; vectorpdef; - elaborate_sig_ports_(des, scope, ports, pdef); + vector port_names; + elaborate_sig_ports_(des, scope, ports, pdef, port_names); NetFuncDef*def = new NetFuncDef(scope, ret_sig, ports, pdef); @@ -722,7 +723,8 @@ void PTask::elaborate_sig(Design*des, NetScope*scope) const vectorports; vectorpdefs; - elaborate_sig_ports_(des, scope, ports, pdefs); + vector port_names; + elaborate_sig_ports_(des, scope, ports, pdefs, port_names); NetTaskDef*def = new NetTaskDef(scope, ports, pdefs); scope->set_task_def(def); @@ -732,11 +734,14 @@ void PTask::elaborate_sig(Design*des, NetScope*scope) const } void PTaskFunc::elaborate_sig_ports_(Design*des, NetScope*scope, - vector&ports, vector&pdefs) const + vector &ports, + vector &pdefs, + vector &port_names) const { if (ports_ == 0) { ports.clear(); pdefs.clear(); + port_names.clear(); /* Make sure the function has at least one input port. If it fails this test, print an error @@ -755,6 +760,7 @@ void PTaskFunc::elaborate_sig_ports_(Design*des, NetScope*scope, ports.resize(ports_->size()); pdefs.resize(ports_->size()); + port_names.resize(ports_->size()); for (size_t idx = 0 ; idx < ports_->size() ; idx += 1) { @@ -817,6 +823,7 @@ void PTaskFunc::elaborate_sig_ports_(Design*des, NetScope*scope, } ports[idx] = tmp; + port_names[idx] = port_name; pdefs[idx] = tmp_def; if (scope->type()==NetScope::FUNC && tmp->port_type()!=NetNet::PINPUT) { cerr << tmp->get_fileline() << ": error: " diff --git a/elaborate.cc b/elaborate.cc index e858d6e1c..16c482f12 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -54,6 +54,7 @@ # include "parse_api.h" # include "compiler.h" # include "ivl_assert.h" +# include "map_named_args.h" using namespace std; @@ -3301,12 +3302,12 @@ NetProc* PChainConstructor::elaborate(Design*des, NetScope*scope) const vector parms (def->port_count()); parms[0] = eres; + auto args = map_named_args(des, def, parms_, 1); for (size_t idx = 1 ; idx < parms.size() ; idx += 1) { - if (idx <= parms_.size() && parms_[idx-1]) { - PExpr*tmp = parms_[idx-1]; + if (args[idx - 1]) { parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->net_type(), - tmp, false); + args[idx - 1], false); continue; } @@ -3480,12 +3481,19 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const perm_string name = peek_tail_name(path_); for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { - PExpr*ex = parms_[idx]; - if (ex != 0) { - eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex); - } else { - eparms[idx] = 0; + auto &parm = parms_[idx]; + + // System functions don't have named parameters + if (!parm.name.nil()) { + cerr << parm.get_fileline() << ": error: " + << "The system task `" << name + << "` has no argument called `" << parm.name + << "`." << endl; + des->errors++; } + + eparms[idx] = elab_sys_task_arg(des, scope, name, idx, + parm.parm); } // Special case: Specify blocks are turned off, and this is an @@ -3615,7 +3623,8 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope, NetNet*net, perm_string method_name, - const char*sys_task_name) const + const char *sys_task_name, + const std::vector &parm_names) const { NetESignal*sig = new NetESignal(net); sig->set_line(*this); @@ -3638,17 +3647,17 @@ NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope, << "method takes no arguments." << endl; des->errors += 1; } + } else if (parm_names.size() != parms_.size()) { + cerr << get_fileline() << ": error: " << method_name + << "() method takes " << parm_names.size() << " arguments, got " + << parms_.size() << "." << endl; + des->errors++; } + auto args = map_named_args(des, parm_names, parms_); for (unsigned idx = 0 ; idx < nparms ; idx += 1) { - PExpr*ex = parms_[idx]; - if (ex != 0) { - argv[idx+1] = elab_sys_task_arg(des, scope, - method_name, - idx, ex); - } else { - argv[idx+1] = 0; - } + argv[idx + 1] = elab_sys_task_arg(des, scope, method_name, + idx, args[idx]); } NetSTask*sys = new NetSTask(sys_task_name, IVL_SFUNC_AS_TASK_IGNORE, argv); @@ -3663,7 +3672,8 @@ NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope, NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, NetNet*net, perm_string method_name, - const char*sys_task_name) const + const char *sys_task_name, + const std::vector &parm_names) const { NetESignal*sig = new NetESignal(net); sig->set_line(*this); @@ -3696,33 +3706,38 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, vectorargv (nparms+1); argv[0] = sig; - if (method_name != "insert") { - if ((nparms == 0) || (parms_[0] == 0)) { - argv[1] = 0; - cerr << get_fileline() << ": error: " << method_name - << "() methods first argument is missing." << endl; - des->errors += 1; - } else - argv[1] = elab_and_eval(des, scope, parms_[0], context_width, - false, false, base_type); - } else { - if ((nparms == 0) || (parms_[0] == 0)) { - argv[1] = 0; - cerr << get_fileline() << ": error: " << method_name - << "() methods first argument is missing." << endl; - des->errors += 1; - } else - argv[1] = elab_and_eval(des, scope, parms_[0], 32, - false, false, IVL_VT_LOGIC); - if ((nparms < 2) || (parms_[1] == 0)) { - argv[2] = 0; + auto args = map_named_args(des, parm_names, parms_); + if (method_name != "insert") { + if (nparms == 0 || !args[0]) { + argv[1] = nullptr; + cerr << get_fileline() << ": error: " << method_name + << "() methods first argument is missing." << endl; + des->errors += 1; + } else { + argv[1] = elab_and_eval(des, scope, args[0], context_width, + false, false, base_type); + } + } else { + if (nparms == 0 || !args[0]) { + argv[1] = nullptr; + cerr << get_fileline() << ": error: " << method_name + << "() methods first argument is missing." << endl; + des->errors += 1; + } else { + argv[1] = elab_and_eval(des, scope, args[0], context_width, + false, false, IVL_VT_LOGIC); + } + + if (nparms < 2 || !args[1]) { + argv[2] = nullptr; cerr << get_fileline() << ": error: " << method_name << "() methods second argument is missing." << endl; des->errors += 1; - } else - argv[2] = elab_and_eval(des, scope, parms_[1], context_width, + } else { + argv[2] = elab_and_eval(des, scope, args[1], context_width, false, false, base_type); + } } NetSTask*sys = new NetSTask(sys_task_name, IVL_SFUNC_AS_TASK_IGNORE, argv); @@ -3814,34 +3829,65 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, // Is this a method of a "string" type? if (dynamic_cast(net->net_type())) { - if (method_name=="itoa") + if (method_name == "itoa") { + static const std::vector parm_names = { + perm_string::literal("i") + }; + return elaborate_sys_task_method_(des, scope, net, method_name, - "$ivl_string_method$itoa"); - else if (method_name=="hextoa") + "$ivl_string_method$itoa", + parm_names); + } else if (method_name == "hextoa") { + static const std::vector parm_names = { + perm_string::literal("i") + }; + return elaborate_sys_task_method_(des, scope, net, method_name, - "$ivl_string_method$hextoa"); - else if (method_name=="octtoa") + "$ivl_string_method$hextoa", + parm_names); + } else if (method_name == "octtoa") { + static const std::vector parm_names = { + perm_string::literal("i") + }; + return elaborate_sys_task_method_(des, scope, net, method_name, - "$ivl_string_method$octtoa"); - else if (method_name=="bintoa") + "$ivl_string_method$octtoa", + parm_names); + } else if (method_name == "bintoa") { + static const std::vector parm_names = { + perm_string::literal("i") + }; + return elaborate_sys_task_method_(des, scope, net, method_name, - "$ivl_string_method$bintoa"); - else if (method_name=="realtoa") + "$ivl_string_method$bintoa", + parm_names); + } else if (method_name == "realtoa") { + static const std::vector parm_names = { + perm_string::literal("r") + }; + return elaborate_sys_task_method_(des, scope, net, method_name, - "$ivl_string_method$realtoa"); + "$ivl_string_method$realtoa", + parm_names); + } } // Is this a delete method for dynamic arrays or queues? if (net->darray_type()) { - if (method_name=="delete") + if (method_name == "delete") { + static const std::vector parm_names = { + perm_string::literal("index") + }; + return elaborate_sys_task_method_(des, scope, net, method_name, - "$ivl_darray_method$delete"); - else if (method_name=="size") + "$ivl_darray_method$delete", + parm_names); + } else if (method_name == "size") { // This returns an int. It could be removed, but keep for now. return elaborate_method_func_(scope, net, &netvector_t::atom2s32, method_name, "$size"); - else if (method_name=="reverse") { + } else if (method_name == "reverse") { cerr << get_fileline() << ": sorry: 'reverse()' " "array sorting method is not currently supported." << endl; @@ -3870,25 +3916,42 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, if (net->queue_type()) { const netdarray_t*use_darray = net->darray_type(); - if (method_name == "push_back") + if (method_name == "push_back") { + static const std::vector parm_names = { + perm_string::literal("item") + }; + return elaborate_queue_method_(des, scope, net, method_name, - "$ivl_queue_method$push_back"); - else if (method_name == "push_front") + "$ivl_queue_method$push_back", + parm_names); + } else if (method_name == "push_front") { + static const std::vector parm_names = { + perm_string::literal("item") + }; + return elaborate_queue_method_(des, scope, net, method_name, - "$ivl_queue_method$push_front"); - else if (method_name == "insert") + "$ivl_queue_method$push_front", + parm_names); + } else if (method_name == "insert") { + static const std::vector parm_names = { + perm_string::literal("index"), + perm_string::literal("item") + }; + return elaborate_queue_method_(des, scope, net, method_name, - "$ivl_queue_method$insert"); - else if (method_name == "pop_front") + "$ivl_queue_method$insert", + parm_names); + } else if (method_name == "pop_front") { return elaborate_method_func_(scope, net, use_darray->element_type(), method_name, "$ivl_queue_method$pop_front"); - else if (method_name == "pop_back") + } else if (method_name == "pop_back") { return elaborate_method_func_(scope, net, use_darray->element_type(), method_name, "$ivl_queue_method$pop_back"); + } } if (const netclass_t*class_type = dynamic_cast(par_type)) { @@ -4096,9 +4159,11 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, expression the r-value. We know by definition that the port is a reg type, so this elaboration is pretty obvious. */ - for (unsigned idx = use_this?1:0 ; idx < parm_count ; idx += 1) { + unsigned int off = use_this ? 1 : 0; - size_t parms_idx = use_this? idx-1 : idx; + auto args = map_named_args(des, def, parms_, off); + for (unsigned int idx = off; idx < parm_count; idx++) { + size_t parms_idx = idx - off; NetNet*port = def->port(idx); ivl_assert(*this, port->port_type() != NetNet::NOT_A_PORT); @@ -4111,9 +4176,9 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, NetExpr*rv = 0; - if (parms_idx < parms_.size() && parms_[parms_idx]) { + if (args[parms_idx]) { rv = elaborate_rval_expr(des, scope, port->net_type(), - parms_ [parms_idx]); + args[parms_idx]); if (NetEEvent*evt = dynamic_cast (rv)) { cerr << evt->get_fileline() << ": error: An event '" << evt->event()->name() << "' can not be a user " @@ -4161,9 +4226,9 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, expression that can be a target to a procedural assignment, including a memory word. */ - for (unsigned idx = use_this?1:0 ; idx < parm_count ; idx += 1) { + for (unsigned int idx = off; idx < parm_count; idx++) { - size_t parms_idx = use_this? idx-1 : idx; + size_t parms_idx = idx - off; NetNet*port = def->port(idx); @@ -4179,12 +4244,12 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, message. Note that the elaborate_lval method already printed a detailed message for the latter case. */ NetAssign_*lv = 0; - if (parms_idx < parms_.size() && parms_[parms_idx]) { - lv = parms_[parms_idx]->elaborate_lval(des, scope, false, false); + if (args[parms_idx]) { + lv = args[parms_idx]->elaborate_lval(des, scope, false, false); if (lv == 0) { - cerr << parms_[parms_idx]->get_fileline() << ": error: " + cerr << args[parms_idx]->get_fileline() << ": error: " << "I give up on task port " << (idx+1) - << " expression: " << *parms_[parms_idx] << endl; + << " expression: " << *args[parms_idx] << endl; } } else if (port->port_type() == NetNet::POUTPUT) { // Output ports were skipped earlier, so @@ -4335,19 +4400,25 @@ bool PCallTask::elaborate_elab(Design*des, NetScope*scope) const bool const_parms = true; for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { - PExpr*ex = parms_[idx]; - if (ex != 0) { - eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex); - if (!check_parm_is_const(eparms[idx])) { - cerr << get_fileline() << ": error: Elaboration task " - << name << "() parameter [" << idx+1 << "] '" - << *eparms[idx] << "' is not constant." << endl; - des->errors += 1; - const_parms = false; - } - } else { - eparms[idx] = 0; - } + auto &parm = parms_[idx]; + + // Elaboration tasks don't have named parameters + if (!parm.name.nil()) { + cerr << parm.get_fileline() << ": error: " + << "The elaboration system task `" << name + << "` has no argument called `" << parm.name + << "`." << endl; + des->errors++; + } + + eparms[idx] = elab_sys_task_arg(des, scope, name, idx, parm.parm); + if (!check_parm_is_const(eparms[idx])) { + cerr << get_fileline() << ": error: Elaboration task " + << name << "() parameter [" << idx+1 << "] '" + << *eparms[idx] << "' is not constant." << endl; + des->errors += 1; + const_parms = false; + } } if (!const_parms) return true; diff --git a/map_named_args.cc b/map_named_args.cc new file mode 100644 index 000000000..fc108feef --- /dev/null +++ b/map_named_args.cc @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2023 Lars-Peter Clausen +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "PExpr.h" +#include "ivl_assert.h" +#include "map_named_args.h" +#include "netlist.h" + +#include + +std::vector map_named_args(Design *des, + const std::vector &names, + const std::vector &parms) +{ + std::vector args(names.size()); + + bool has_named = false; + for (size_t i = 0; i < parms.size(); i++) { + if (parms[i].name.nil()) { + if (!parms[i].parm) + continue; + + if (has_named) { + std::cerr << parms[i].get_fileline() << ": error: " + << "Positional argument must preceded " + << "named arguments." + << std::endl; + } else if (i < args.size()) { + args[i] = parms[i].parm; + } + + continue; + } + has_named = true; + + bool found = false; + for (size_t j = 0; j < names.size(); j++) { + if (names[j] == parms[i].name) { + if (args[j]) { + std::cerr << parms[i].get_fileline() << ": error: " + << "Argument `" + << parms[i].name + << "` has already been specified." + << std::endl; + des->errors++; + } else { + args[j] = parms[i].parm; + } + found = true; + break; + } + } + if (!found) { + std::cerr << parms[i].get_fileline() << ": error: " + << "No argument called `" + << parms[i].name << "`." + << std::endl; + des->errors++; + } + } + + return args; +} + +std::vector map_named_args(Design *des, NetBaseDef *def, + const std::vector &parms, + unsigned int off) +{ + std::vector names; + + for (size_t j = off; j < def->port_count(); j++) + names.push_back(def->port(j)->name()); + + return map_named_args(des, names, parms); +} diff --git a/map_named_args.h b/map_named_args.h new file mode 100644 index 000000000..f0017198f --- /dev/null +++ b/map_named_args.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Lars-Peter Clausen +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef MAP_NAMED_ARGS_H +#define MAP_NAMED_ARGS_H + +#include +#include "pform_types.h" + +class PExpr; +class Design; +class NetBaseDef; + +std::vector map_named_args(Design *des, + const std::vector &names, + const std::vector &parms); + +std::vector map_named_args(Design *des, NetBaseDef *def, + const std::vector &parms, + unsigned int off); + +#endif diff --git a/netmisc.cc b/netmisc.cc index 5954db89f..8f3f89b84 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -1046,6 +1046,9 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, unsigned arg_idx, PExpr*pe, bool need_const) { + if (!pe) + return nullptr; + PExpr::width_mode_t mode = PExpr::SIZED; pe->test_width(des, scope, mode); diff --git a/parse.y b/parse.y index 11e28dcc0..1aa5f0105 100644 --- a/parse.y +++ b/parse.y @@ -172,9 +172,9 @@ template void append(vector&out, const std::vector&in) * The parser parses an empty argument list as an argument list with an single * empty argument. Fix this up here and replace it with an empty list. */ -static void argument_list_fixup(list*lst) +static void argument_list_fixup(list *lst) { - if (lst->size() == 1 && !lst->front()) + if (lst->size() == 1 && lst->front().name.nil() && !lst->front().parm) lst->clear(); } @@ -184,17 +184,20 @@ static void argument_list_fixup(list*lst) */ static PECallFunction*make_call_function(perm_string tn, PExpr*arg) { - std::vector parms(1); - parms[0] = arg; + std::vector parms(1); + parms[0].parm = arg; + parms[0].set_line(*arg); PECallFunction*tmp = new PECallFunction(tn, parms); return tmp; } static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) { - std::vector parms(2); - parms[0] = arg1; - parms[1] = arg2; + std::vector parms(2); + parms[0].parm = arg1; + parms[0].set_line(*arg1); + parms[1].parm = arg2; + parms[1].set_line(*arg2); PECallFunction*tmp = new PECallFunction(tn, parms); return tmp; } @@ -510,7 +513,7 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id, struct { data_type_t*type; - std::list*exprs; + std::list *args; } class_declaration_extends; struct { @@ -690,6 +693,10 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id, %type attribute %type attribute_list attribute_instance_list attribute_list_opt +%type argument +%type argument_list +%type argument_list_parens argument_list_parens_opt + %type case_item %type case_items @@ -717,7 +724,6 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id, %type delay_value delay_value_simple %type delay1 delay3 delay3_opt delay_value_list %type expression_list_with_nuls expression_list_proper -%type argument_list_parens argument_list_parens_opt %type cont_assign cont_assign_list %type variable_decl_assignment @@ -889,7 +895,7 @@ class_declaration /* IEEE1800-2005: A.1.2 */ class_type_t *class_type= new class_type_t(name); FILE_NAME(class_type, @4); pform_set_typedef(@4, name, class_type, nullptr); - pform_start_class_declaration(@2, class_type, $5.type, $5.exprs, $1); + pform_start_class_declaration(@2, class_type, $5.type, $5.args, $1); } class_items_opt K_endclass { // Process a class. @@ -932,11 +938,12 @@ class_declaration_endlabel_opt class_declaration_extends_opt /* IEEE1800-2005: A.1.2 */ : K_extends ps_type_identifier argument_list_parens_opt - { $$.type = $2; - $$.exprs = $3; + { $$.type = $2; + $$.args = $3; } | - { $$.type = 0; $$.exprs = 0; } + { $$ = {nullptr, nullptr}; + } ; /* The class_items_opt and class_items rules together implement the @@ -2251,7 +2258,7 @@ simple_immediate_assertion_statement /* IEEE1800-2012 A.6.10 */ : assert_or_assume '(' expression ')' statement_or_null %prec less_than_K_else { if (gn_supported_assertions_flag) { - std::listarg_list; + std::list arg_list; PCallTask*tmp1 = new PCallTask(lex_strings.make("$error"), arg_list); FILE_NAME(tmp1, @1); PCondit*tmp2 = new PCondit($3, $5, tmp1); @@ -3714,12 +3721,45 @@ expression_list_with_nuls } ; +argument + : expression + { named_pexpr_t *tmp = new named_pexpr_t; + FILE_NAME(tmp, @$); + tmp->name = perm_string(); + tmp->parm = $1; + $$ = tmp; + } + | named_expression_opt + { $$ = $1; + } + | + { named_pexpr_t *tmp = new named_pexpr_t; + tmp->name = perm_string(); + tmp->parm = nullptr; + $$ = tmp; + } + ; + +argument_list + : argument + { std::list *expr = new std::list; + expr->push_back(*$1); + delete $1; + $$ = expr; + } + | argument_list ',' argument + { $1->push_back(*$3); + delete $3; + $$ = $1; + } + ; + /* An argument list enclosed in parenthesis. The parser will parse '()' as a * argument list with an single empty item. We fix this up once the list * parsing is done by replacing it with the empty list. */ argument_list_parens - : '(' expression_list_with_nuls ')' + : '(' argument_list ')' { argument_list_fixup($2); $$ = $2; } ; @@ -3731,7 +3771,8 @@ argument_list_parens_opt : argument_list_parens { $$ = $1; } | - { $$ = new std::list; } + { $$ = new std::list; } + ; expression_list_proper : expression_list_proper ',' expression @@ -3874,12 +3915,14 @@ expr_primary delete $2; $$ = tmp; } - | SYSTEM_IDENTIFIER '(' expression_list_proper ')' + | SYSTEM_IDENTIFIER argument_list_parens { perm_string tn = lex_strings.make($1); - PECallFunction*tmp = new PECallFunction(tn, *$3); + PECallFunction *tmp = new PECallFunction(tn, *$2); + if ($2->empty()) + pform_requires_sv(@1, "Empty function argument list"); FILE_NAME(tmp, @1); delete[]$1; - delete $3; + delete $2; $$ = tmp; } | package_scope hierarchy_identifier { lex_in_package_scope(0); } argument_list_parens @@ -3889,16 +3932,6 @@ expr_primary delete $4; $$ = tmp; } - | SYSTEM_IDENTIFIER '(' ')' - { perm_string tn = lex_strings.make($1); - const std::vectorempty; - PECallFunction*tmp = new PECallFunction(tn, empty); - FILE_NAME(tmp, @1); - delete[]$1; - $$ = tmp; - pform_requires_sv(@1, "Empty function argument list"); - } - | K_this { PEIdent*tmp = new PEIdent(perm_string::literal(THIS_TOKEN)); FILE_NAME(tmp,@1); @@ -6551,7 +6584,7 @@ subroutine_call } | hierarchy_identifier '(' error ')' { yyerror(@3, "error: Syntax error in task arguments."); - listpt; + std::list pt; PCallTask*tmp = pform_make_call_task(@1, *$1, pt); delete $1; $$ = tmp; @@ -6928,7 +6961,7 @@ statement_item /* This is roughly statement_item in the LRM */ } else { yyerror(@2, "error: Constraint block can only be applied to randomize method."); } - listpt; + list pt; PCallTask*tmp = new PCallTask(*$1, pt); FILE_NAME(tmp, @1); delete $1; diff --git a/pform.cc b/pform.cc index c2431ac5a..142df2c66 100644 --- a/pform.cc +++ b/pform.cc @@ -927,7 +927,7 @@ typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt) PECallFunction* pform_make_call_function(const struct vlltype&loc, const pform_name_t&name, - const list&parms) + const list &parms) { if (gn_system_verilog()) check_potential_imports(loc, name.front().name, true); @@ -939,7 +939,7 @@ PECallFunction* pform_make_call_function(const struct vlltype&loc, PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, - const list&parms) + const list &parms) { if (gn_system_verilog()) check_potential_imports(loc, name.front().name, true); @@ -1732,7 +1732,7 @@ void pform_endgenerate(bool end_conditional) void pform_make_elab_task(const struct vlltype&li, perm_string name, - const list¶ms) + const list ¶ms) { PCallTask*elab_task = new PCallTask(name, params); FILE_NAME(elab_task, li); diff --git a/pform.h b/pform.h index 92d3edfb2..bb3dabfef 100644 --- a/pform.h +++ b/pform.h @@ -173,7 +173,7 @@ extern void pform_endmodule(const char*, bool inside_celldefine, extern void pform_start_class_declaration(const struct vlltype&loc, class_type_t*type, data_type_t*base_type, - std::list*base_exprs, + std::list *base_args, bool virtual_class); extern void pform_class_property(const struct vlltype&loc, property_qualifier_t pq, @@ -307,7 +307,7 @@ bool pform_error_in_generate(const vlltype&loc, const char *type); extern void pform_make_elab_task(const struct vlltype&li, perm_string name, - const std::list¶ms); + const std::list ¶ms); extern void pform_set_typedef(const struct vlltype&loc, perm_string name, data_type_t*data_type, @@ -322,10 +322,10 @@ extern void pform_set_type_referenced(const struct vlltype&loc, const char*name) */ extern PECallFunction* pform_make_call_function(const struct vlltype&loc, const pform_name_t&name, - const std::list&parms); + const std::list &parms); extern PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, - const std::list&parms); + const std::list &parms); extern void pform_make_foreach_declarations(const struct vlltype&loc, std::list*loop_vars); diff --git a/pform_analog.cc b/pform_analog.cc index b849f6b82..eb96cce61 100644 --- a/pform_analog.cc +++ b/pform_analog.cc @@ -46,12 +46,12 @@ void pform_make_analog_behavior(const struct vlltype&loc, ivl_process_type_t pt, PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*n1, char*n2) { - vector parms (2); - parms[0] = new PEIdent(lex_strings.make(n1)); - FILE_NAME(parms[0], loc); + vector parms (2); + parms[0].parm = new PEIdent(lex_strings.make(n1)); + FILE_NAME(parms[0].parm, loc); - parms[1] = new PEIdent(lex_strings.make(n2)); - FILE_NAME(parms[1], loc); + parms[1].parm = new PEIdent(lex_strings.make(n2)); + FILE_NAME(parms[1].parm, loc); PECallFunction*res = new PECallFunction(lex_strings.make(name), parms); FILE_NAME(res, loc); @@ -61,9 +61,9 @@ PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*branch_name) { - vector parms (1); - parms[0] = new PEIdent(lex_strings.make(branch_name)); - FILE_NAME(parms[0], loc); + vector parms (1); + parms[0].parm = new PEIdent(lex_strings.make(branch_name)); + FILE_NAME(parms[0].parm, loc); PECallFunction*res = new PECallFunction(lex_strings.make(name), parms); FILE_NAME(res, loc); diff --git a/pform_dump.cc b/pform_dump.cc index 735d0f923..1a43caaa5 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -345,15 +345,7 @@ void class_type_t::pform_dump(ostream&out, unsigned indent) const if (base_type) out << " extends "; if (! base_args.empty()) { - out << " ("; - for (list::const_iterator cur = base_args.begin() - ; cur != base_args.end() ; ++cur) { - const PExpr*curp = *cur; - if (cur != base_args.begin()) - out << ", "; - curp->dump(out); - } - out << ")"; + out << " (" << base_args << ")"; } out << " {"; diff --git a/pform_pclass.cc b/pform_pclass.cc index f70fc8621..b438f4cde 100644 --- a/pform_pclass.cc +++ b/pform_pclass.cc @@ -43,7 +43,7 @@ static PClass*pform_cur_class = 0; void pform_start_class_declaration(const struct vlltype&loc, class_type_t*type, data_type_t*base_type, - list*base_exprs, + list *base_args, bool virtual_class) { PClass*class_scope = pform_push_class_scope(loc, type->name); @@ -55,13 +55,12 @@ void pform_start_class_declaration(const struct vlltype&loc, type->base_type.reset(base_type); type->virtual_class = virtual_class; + ivl_assert(loc, type->base_args.empty()); - if (base_exprs) { - for (list::iterator cur = base_exprs->begin() - ; cur != base_exprs->end() ; ++ cur) { - type->base_args.push_back(*cur); - } - delete base_exprs; + if (base_args) { + type->base_args.insert(type->base_args.begin(), base_args->begin(), + base_args->end()); + delete base_args; } } diff --git a/pform_types.h b/pform_types.h index ebdff6f0b..fb7a03f8a 100644 --- a/pform_types.h +++ b/pform_types.h @@ -377,7 +377,7 @@ struct class_type_t : public data_type_t { // hierarchy. If there are arguments to the base class, then // put them in the base_args vector. std::unique_ptr base_type; - std::listbase_args; + std::vector base_args; bool virtual_class;