diff --git a/PClass.cc b/PClass.cc index 45da536ce..b84d424e1 100644 --- a/PClass.cc +++ b/PClass.cc @@ -20,7 +20,7 @@ # include "PClass.h" PClass::PClass(perm_string name, LexicalScope*parent) -: PScopeExtra(name, parent) +: PScopeExtra(name, parent), type(0), chain(0) { } diff --git a/PClass.h b/PClass.h index ec5440468..edeea0c5c 100644 --- a/PClass.h +++ b/PClass.h @@ -24,6 +24,8 @@ # include "StringHeap.h" # include +class PChainConstructor; + /* * SystemVerilog supports class declarations with their own lexical * scope, etc. The parser arranges for these to be created and @@ -40,6 +42,7 @@ class PClass : public PScopeExtra, public LineInfo { public: class_type_t*type; + PChainConstructor*chain; }; #endif diff --git a/PExpr.h b/PExpr.h index 7121e00f1..88b2a8382 100644 --- a/PExpr.h +++ b/PExpr.h @@ -558,7 +558,8 @@ class PENewClass : public PExpr { private: NetExpr* elaborate_expr_constructor_(Design*des, NetScope*scope, const netclass_t*ctype, - NetExpr*obj, unsigned flags) const; + NetExpr*obj, unsigned flags, + const netclass_t*derived) const; private: std::vectorparms_; diff --git a/PFunction.cc b/PFunction.cc index a80123425..9d7845aa8 100644 --- a/PFunction.cc +++ b/PFunction.cc @@ -19,6 +19,7 @@ # include "config.h" # include "PTask.h" +# include "Statement.h" # include PFunction::PFunction(perm_string name, LexicalScope*parent, bool is_auto__) @@ -43,3 +44,17 @@ void PFunction::set_return(const data_type_t*t) { return_type_ = t; } + +PChainConstructor* PFunction::extract_chain_constructor() +{ + PChainConstructor*res = 0; + + if (res = dynamic_cast (statement_)) { + statement_ = 0; + + } else if (PBlock*blk = dynamic_cast(statement_)) { + res = blk->extract_chain_constructor(); + } + + return res; +} diff --git a/PTask.h b/PTask.h index 69c0723e6..dcc38811e 100644 --- a/PTask.h +++ b/PTask.h @@ -29,6 +29,7 @@ class Design; class NetExpr; class NetNet; class NetScope; +class PChainConstructor; class PWire; class Statement; class PExpr; @@ -117,6 +118,11 @@ class PFunction : public PTaskFunc { inline Statement* get_statement() { return statement_; } + // This is only used if this function is a constructor. In + // that case, this method looks for a PChainConstructor in the + // statement and extracts it if found. + PChainConstructor*extract_chain_constructor(); + void elaborate_scope(Design*des, NetScope*scope) const; /* elaborate the ports and return value. */ diff --git a/Statement.cc b/Statement.cc index 979a11a16..f30b6f931 100644 --- a/Statement.cc +++ b/Statement.cc @@ -115,6 +115,21 @@ PBlock::~PBlock() delete list_[idx]; } +PChainConstructor* PBlock::extract_chain_constructor() +{ + if (list_.empty()) + return 0; + + if (PChainConstructor*res = dynamic_cast (list_[0])) { + for (size_t idx = 0 ; idx < list_.size()-1 ; idx += 1) + list_[idx] = list_[idx+1]; + list_.resize(list_.size()-1); + return res; + } + + return 0; +} + void PBlock::set_join_type(PBlock::BL_TYPE type) { assert(bl_type_ == BL_PAR); @@ -206,6 +221,21 @@ PCAssign::~PCAssign() delete expr_; } +PChainConstructor::PChainConstructor(const list&parms) +: parms_(parms.size()) +{ + list::const_iterator cur = parms.begin(); + for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { + parms_[idx] = *cur; + ++cur; + } + assert(cur == parms.end()); +} + +PChainConstructor::~PChainConstructor() +{ +} + PCondit::PCondit(PExpr*ex, Statement*i, Statement*e) : expr_(ex), if_(i), else_(e) { diff --git a/Statement.h b/Statement.h index 52a3ecf32..7c133f528 100644 --- a/Statement.h +++ b/Statement.h @@ -31,6 +31,7 @@ # include "HName.h" # include "LineInfo.h" class PExpr; +class PChainConstructor; class PPackage; class Statement; class PEventStatement; @@ -181,6 +182,11 @@ class PBlock : public PScope, public Statement { BL_TYPE bl_type() const { return bl_type_; } + // This is only used if this block is the statement list for a + // constructor. We look for a PChainConstructor as the first + // statement, and if it is there, extract it. + PChainConstructor*extract_chain_constructor(); + // If the bl_type() is BL_PAR, it is possible to replace it // with JOIN_NONE or JOIN_ANY. This is to help the parser. void set_join_type(BL_TYPE); @@ -272,6 +278,25 @@ class PCAssign : public Statement { PExpr*expr_; }; +/* + * This represents the syntax "super.new(...)". This is not really an + * executable statement, but the elaborator will handle these + * specially and will remove them from the statement stream. If any + */ +class PChainConstructor : public Statement { + public: + PChainConstructor(const list&parms); + ~PChainConstructor(); + + virtual void dump(ostream&out, unsigned ind) const; + + inline const std::vector& chain_args(void) const + { return parms_; } + + private: + std::vector parms_; +}; + class PCondit : public Statement { public: diff --git a/elab_expr.cc b/elab_expr.cc index e76c0e13c..9578c8eef 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -4679,13 +4679,21 @@ unsigned PENewClass::test_width(Design*, NetScope*, width_mode_t&) * This elaborates the constructor for a class. This arranges for the * call of parent class constructors, if present, and also * initializers in front of an explicit constructor. + * + * The derived argument is the type of the class derived from the + * current one. This is used to get chained constructor arguments, if necessary. */ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope, const netclass_t*ctype, - NetExpr*obj, unsigned flags) const + NetExpr*obj, unsigned flags, + const netclass_t*derived) const { + // If there is a super-class, then call the super-class + // constructors first. The results of the current class + // constructors cannot effect the superclass, so this is the + // right order of events. if (const netclass_t*super_class = ctype->get_super()) { - obj = elaborate_expr_constructor_(des, scope, super_class, obj, flags); + obj = elaborate_expr_constructor_(des, scope, super_class, obj, flags, ctype); } @@ -4710,10 +4718,18 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope, obj = tmp; } + // The parameters for the constructor are the arguments of the + // new(...) expression, unless we are recursing, in which case + // we use the chained arguments. Note that the arguments to + // the chained constructor must be elaborated in the scope of + // the subclass (so that arguments can be passed down) so also + // select which scope we are going to use. + const vector&use_parms = derived? derived->get_chain_args() : parms_; + NetScope*new_scope = ctype->method_from_name(perm_string::literal("new")); if (new_scope == 0) { // No constructor. - if (parms_.size() > 0) { + if (use_parms.size() > 0) { cerr << get_fileline() << ": error: " << "Class " << ctype->get_name() << " has no constructor, but you passed " << parms_.size() @@ -4730,9 +4746,9 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope, // Are there too many arguments passed to the function. If so, // 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()) { + if ((use_parms.size()+1) > def->port_count()) { cerr << get_fileline() << ": error: Parm count mismatch" - << " passing " << parms_.size() << " arguments " + << " passing " << use_parms.size() << " arguments " << " to constructor expecting " << (def->port_count()-1) << " arguments." << endl; des->errors += 1; @@ -4745,9 +4761,10 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope, int parm_errors = 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]; - parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->net_type(), + if (idx <= use_parms.size() && use_parms[idx-1]) { + PExpr*tmp = use_parms[idx-1]; + parms[idx] = elaborate_rval_expr(des, scope, + def->port(idx)->net_type(), def->port(idx)->data_type(), def->port(idx)->vector_width(), tmp, false); @@ -4801,7 +4818,7 @@ NetExpr* PENewClass::elaborate_expr(Design*des, NetScope*scope, // allocation alone. const netclass_t*ctype = dynamic_cast (ntype); - obj = elaborate_expr_constructor_(des, scope, ctype, obj, flags); + obj = elaborate_expr_constructor_(des, scope, ctype, obj, flags, 0); return obj; } diff --git a/elab_scope.cc b/elab_scope.cc index a0f4c10c2..640da0a61 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -311,6 +311,12 @@ static void blend_class_constructors(PClass*pclass) if (iter_new == pclass->funcs.end()) return; + // While we're here, look for a super.new() call. If we find + // it, strip it out of the constructor and set it aside for + // when we actually call the chained constructor. + pclass->chain = iter_new->second->extract_chain_constructor(); + + map::iterator iter_new2 = pclass->funcs.find(new2); if (iter_new2 == pclass->funcs.end()) return; @@ -374,7 +380,10 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass) } } - netclass_t*use_class = new netclass_t(use_type->name, use_base_class); + const static vectorno_args; + const vector&use_chain_args = (pclass&&pclass->chain)? pclass->chain->chain_args() : no_args; + + netclass_t*use_class = new netclass_t(use_type->name, use_base_class, use_chain_args); // Class scopes have no parent scope, because references are // not allowed to escape a class method. diff --git a/netclass.cc b/netclass.cc index f302585d1..c460c24bf 100644 --- a/netclass.cc +++ b/netclass.cc @@ -23,8 +23,8 @@ using namespace std; -netclass_t::netclass_t(perm_string name, netclass_t*sup) -: name_(name), super_(sup), class_scope_(0) +netclass_t::netclass_t(perm_string name, netclass_t*sup, const vector&chain_args) +: name_(name), super_(sup), chain_args_(chain_args), class_scope_(0) { } diff --git a/netclass.h b/netclass.h index 79e912f74..63552753d 100644 --- a/netclass.h +++ b/netclass.h @@ -30,10 +30,11 @@ class Design; class NetNet; class NetScope; class PClass; +class PExpr; class netclass_t : public ivl_type_s { public: - netclass_t(perm_string class_name, netclass_t*par); + netclass_t(perm_string class_name, netclass_t*par, const vector&pchain_args); ~netclass_t(); // Set the property of the class during elaboration. Set the @@ -45,6 +46,9 @@ class netclass_t : public ivl_type_s { // is used for the elaboration of methods (tasks/functions). void set_class_scope(NetScope*cscope); + inline const std::vector& get_chain_args(void) const + { return chain_args_; } + // As an ivl_type_s object, the netclass is always an // ivl_VT_CLASS object. ivl_variable_type_t base_type() const; @@ -99,8 +103,10 @@ class netclass_t : public ivl_type_s { private: perm_string name_; // If this is derived from another base class, point to it - // here. + // here. The super constructor may take arguments, and the + // chain_parms_ are those arguments. netclass_t*super_; + std::vector chain_args_; // Map properrty names to property table index. std::map properties_; // Vector of properties. diff --git a/parse.y b/parse.y index 5baf61dc7..fbcded31f 100644 --- a/parse.y +++ b/parse.y @@ -5853,9 +5853,9 @@ statement_item /* This is roughly statement_item in the LRM */ out. */ | implicit_class_handle '.' K_new '(' expression_list_with_nuls ')' ';' - { yyerror(@1, "sorry: Calls to superclass constructor not supported."); - yyerrok; - $$ = new PNoop; + { PChainConstructor*tmp = new PChainConstructor(*$5); + FILE_NAME(tmp, @3); + $$ = tmp; } | hierarchy_identifier '(' error ')' ';' { yyerror(@3, "error: Syntax error in task arguments."); diff --git a/pform_dump.cc b/pform_dump.cc index 9a02c9d2d..845fa5477 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -821,6 +821,19 @@ void PCase::dump(ostream&out, unsigned ind) const out << setw(ind) << "" << "endcase" << endl; } +void PChainConstructor::dump(ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << "super.new("; + if (parms_.size() > 0) { + if (parms_[0]) out << *parms_[0]; + } + for (size_t idx = 1 ; idx < parms_.size() ; idx += 1) { + out << ", "; + if (parms_[idx]) out << *parms_[idx]; + } + out << ");" << endl; +} + void PCondit::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "if (" << *expr_ << ")" << endl;