Elaborate PChainConstructor calls.

It is better to leave the handling of PChainConstructor calls to
the elaboration, instead of stripping them out early. This allows
for handling the arguments of the chain constructor in the correct
scope.
This commit is contained in:
Stephen Williams 2013-11-09 19:45:03 -08:00
parent a7d8c983ba
commit f16fd03dab
11 changed files with 174 additions and 73 deletions

View File

@ -20,7 +20,7 @@
# include "PClass.h" # include "PClass.h"
PClass::PClass(perm_string name, LexicalScope*parent) PClass::PClass(perm_string name, LexicalScope*parent)
: PScopeExtra(name, parent), type(0), chain(0) : PScopeExtra(name, parent), type(0)
{ {
} }

View File

@ -42,7 +42,6 @@ class PClass : public PScopeExtra, public LineInfo {
public: public:
class_type_t*type; class_type_t*type;
PChainConstructor*chain;
}; };
#endif #endif

View File

@ -558,8 +558,7 @@ class PENewClass : public PExpr {
private: private:
NetExpr* elaborate_expr_constructor_(Design*des, NetScope*scope, NetExpr* elaborate_expr_constructor_(Design*des, NetScope*scope,
const netclass_t*ctype, const netclass_t*ctype,
NetExpr*obj, unsigned flags, NetExpr*obj, unsigned flags) const;
const netclass_t*derived) const;
private: private:
std::vector<PExpr*>parms_; std::vector<PExpr*>parms_;

View File

@ -21,6 +21,7 @@
# include "PTask.h" # include "PTask.h"
# include "Statement.h" # include "Statement.h"
# include <cassert> # include <cassert>
# include "ivl_assert.h"
PFunction::PFunction(perm_string name, LexicalScope*parent, bool is_auto__) PFunction::PFunction(perm_string name, LexicalScope*parent, bool is_auto__)
: PTaskFunc(name, parent), statement_(0) : PTaskFunc(name, parent), statement_(0)
@ -33,13 +34,37 @@ PFunction::~PFunction()
{ {
} }
void PFunction::set_statement(Statement*s, bool rewrite_ok) void PFunction::set_statement(Statement*s)
{ {
assert(s != 0); assert(s != 0);
assert(statement_ == 0 || rewrite_ok&&statement_); assert(statement_ == 0);
statement_ = s; statement_ = s;
} }
void PFunction::push_statement_front(Statement*stmt)
{
// This can only happen after the statement is initially set.
ivl_assert(*this, statement_);
// Get the PBlock of the statement. If it is not a PBlock,
// then create one to wrap the existing statement and the new
// statement that we're pushing.
PBlock*blk = dynamic_cast<PBlock*> (statement_);
if (blk == 0) {
PBlock*tmp = new PBlock(PBlock::BL_SEQ);
tmp->set_line(*this);
vector<Statement*>tmp_list(1);
tmp_list[0] = statement_;
tmp->set_statement(tmp_list);
statement_ = tmp;
blk = tmp;
}
// Now do the push.
blk->push_statement_front(stmt);
}
void PFunction::set_return(const data_type_t*t) void PFunction::set_return(const data_type_t*t)
{ {
return_type_ = t; return_type_ = t;

View File

@ -113,11 +113,16 @@ class PFunction : public PTaskFunc {
explicit PFunction(perm_string name, LexicalScope*parent, bool is_auto); explicit PFunction(perm_string name, LexicalScope*parent, bool is_auto);
~PFunction(); ~PFunction();
void set_statement(Statement *s, bool rewrite_flag =false); void set_statement(Statement *s);
void set_return(const data_type_t*t); void set_return(const data_type_t*t);
inline Statement* get_statement() { return statement_; } inline Statement* get_statement() { return statement_; }
// Push this statement to the front of the existing
// definition. If the statement is a simple statement, make a
// block to contain the statements.
void push_statement_front(Statement*stmt);
// This is only used if this function is a constructor. In // This is only used if this function is a constructor. In
// that case, this method looks for a PChainConstructor in the // that case, this method looks for a PChainConstructor in the
// statement and extracts it if found. // statement and extracts it if found.

View File

@ -288,6 +288,7 @@ class PChainConstructor : public Statement {
PChainConstructor(const list<PExpr*>&parms); PChainConstructor(const list<PExpr*>&parms);
~PChainConstructor(); ~PChainConstructor();
virtual NetProc* elaborate(Design*des, NetScope*scope) const;
virtual void dump(ostream&out, unsigned ind) const; virtual void dump(ostream&out, unsigned ind) const;
inline const std::vector<PExpr*>& chain_args(void) const inline const std::vector<PExpr*>& chain_args(void) const

View File

@ -4677,7 +4677,7 @@ unsigned PENewClass::test_width(Design*, NetScope*, width_mode_t&)
/* /*
* This elaborates the constructor for a class. This arranges for the * This elaborates the constructor for a class. This arranges for the
* call of parent class constructors, if present, and also * call of class constructor, if present, and also
* initializers in front of an explicit constructor. * initializers in front of an explicit constructor.
* *
* The derived argument is the type of the class derived from the * The derived argument is the type of the class derived from the
@ -4685,18 +4685,8 @@ unsigned PENewClass::test_width(Design*, NetScope*, width_mode_t&)
*/ */
NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope, NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope,
const netclass_t*ctype, const netclass_t*ctype,
NetExpr*obj, unsigned flags, NetExpr*obj, unsigned flags) const
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, ctype);
}
// If there is an initializer function, then pass the object // If there is an initializer function, then pass the object
// through that function first. Note tha the initializer // through that function first. Note tha the initializer
// function has no arguments other then the object itself. // function has no arguments other then the object itself.
@ -4718,18 +4708,11 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope,
obj = tmp; 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<PExpr*>&use_parms = derived? derived->get_chain_args() : parms_;
NetScope*new_scope = ctype->method_from_name(perm_string::literal("new")); NetScope*new_scope = ctype->method_from_name(perm_string::literal("new"));
if (new_scope == 0) { if (new_scope == 0) {
// No constructor. // No constructor.
if (use_parms.size() > 0) { if (parms_.size() > 0) {
cerr << get_fileline() << ": error: " cerr << get_fileline() << ": error: "
<< "Class " << ctype->get_name() << "Class " << ctype->get_name()
<< " has no constructor, but you passed " << parms_.size() << " has no constructor, but you passed " << parms_.size()
@ -4746,9 +4729,9 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope,
// Are there too many arguments passed to the function. If so, // Are there too many arguments passed to the function. If so,
// generate an error message. The case of too few arguments // generate an error message. The case of too few arguments
// will be handled below, when we run out of arguments. // will be handled below, when we run out of arguments.
if ((use_parms.size()+1) > def->port_count()) { if ((parms_.size()+1) > def->port_count()) {
cerr << get_fileline() << ": error: Parm count mismatch" cerr << get_fileline() << ": error: Parm count mismatch"
<< " passing " << use_parms.size() << " arguments " << " passing " << parms_.size() << " arguments "
<< " to constructor expecting " << (def->port_count()-1) << " to constructor expecting " << (def->port_count()-1)
<< " arguments." << endl; << " arguments." << endl;
des->errors += 1; des->errors += 1;
@ -4761,8 +4744,8 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope,
int parm_errors = 0; int parm_errors = 0;
for (size_t idx = 1 ; idx < parms.size() ; idx += 1) { for (size_t idx = 1 ; idx < parms.size() ; idx += 1) {
// While there are default arguments, check them. // While there are default arguments, check them.
if (idx <= use_parms.size() && use_parms[idx-1]) { if (idx <= parms_.size() && parms_[idx-1]) {
PExpr*tmp = use_parms[idx-1]; PExpr*tmp = parms_[idx-1];
parms[idx] = elaborate_rval_expr(des, scope, parms[idx] = elaborate_rval_expr(des, scope,
def->port(idx)->net_type(), def->port(idx)->net_type(),
def->port(idx)->data_type(), def->port(idx)->data_type(),
@ -4818,7 +4801,7 @@ NetExpr* PENewClass::elaborate_expr(Design*des, NetScope*scope,
// allocation alone. // allocation alone.
const netclass_t*ctype = dynamic_cast<const netclass_t*> (ntype); const netclass_t*ctype = dynamic_cast<const netclass_t*> (ntype);
obj = elaborate_expr_constructor_(des, scope, ctype, obj, flags, 0); obj = elaborate_expr_constructor_(des, scope, ctype, obj, flags);
return obj; return obj;
} }

View File

@ -331,11 +331,19 @@ static void blend_class_constructors(PClass*pclass)
// While we're here, look for a super.new() call. If we find // 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 // it, strip it out of the constructor and set it aside for
// when we actually call the chained constructor. // when we actually call the chained constructor.
if (use_new) PChainConstructor*chain_new = use_new? use_new->extract_chain_constructor() : 0;
pclass->chain = use_new->extract_chain_constructor();
else
pclass->chain = 0;
// If we do not have an explicit constructor chain, but there
// is a parent class, then create an implicit chain.
if (chain_new==0 && pclass->type->base_type!=0) {
list<PExpr*>tmp_parms;
chain_new = new PChainConstructor(tmp_parms);
chain_new->set_line(*pclass);
}
// If there are both an implicit and explicit constructor,
// then blend the implicit constructor into the explicit
// constructor. This eases the task for the elaborator later.
if (use_new && use_new2) { if (use_new && use_new2) {
// These constructors must be methods of the same class. // These constructors must be methods of the same class.
ivl_assert(*use_new, use_new->method_of() == use_new2->method_of()); ivl_assert(*use_new, use_new->method_of() == use_new2->method_of());
@ -351,37 +359,23 @@ static void blend_class_constructors(PClass*pclass)
use_new->set_statement(def_new); use_new->set_statement(def_new);
} }
PBlock*blk_new = dynamic_cast<PBlock*> (def_new); if (def_new2) use_new->push_statement_front(def_new2);
// If the statement for the constructor is not a block,
// then convert it into a block so that we can do other
// manipulations.
if (blk_new==0) {
PBlock*tmp = new PBlock(PBlock::BL_SEQ);
tmp->set_line(*def_new);
vector<Statement*>tmp_list(1);
tmp_list[0] = def_new;
tmp->set_statement(tmp_list);
blk_new = tmp;
use_new->set_statement(blk_new, true);
}
ivl_assert(*blk_new, blk_new ->bl_type()==PBlock::BL_SEQ);
if (def_new2) {
blk_new->push_statement_front(def_new2);
}
// Now the implicit initializations are all built into // Now the implicit initializations are all built into
// the constructor. Delete the "new@" constructor. // the constructor. Delete the "new@" constructor.
pclass->funcs.erase(iter_new2); pclass->funcs.erase(iter_new2);
delete use_new2; delete use_new2;
use_new2 = 0; use_new2 = 0;
} }
if (chain_new) {
if (use_new2) {
use_new2->push_statement_front(chain_new);
} else {
use_new->push_statement_front(chain_new);
}
chain_new = 0;
}
} }
static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass) static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass)
@ -412,10 +406,7 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass)
} }
} }
const static vector<PExpr*>no_args; netclass_t*use_class = new netclass_t(use_type->name, use_base_class);
const vector<PExpr*>&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 // Class scopes have no parent scope, because references are
// not allowed to escape a class method. // not allowed to escape a class method.

View File

@ -3027,6 +3027,109 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
return res; return res;
} }
NetProc* PChainConstructor::elaborate(Design*des, NetScope*scope) const
{
assert(scope);
if (debug_elaborate) {
cerr << get_fileline() << ": PChainConstructor::elaborate: "
<< "Elaborate constructor chain in scope=" << scope_path(scope) << endl;
}
// The scope is the <class>.new function, so scope->parent()
// is the class. Use that to get the class type that we are
// constructing.
NetScope*scope_class = scope->parent();
const netclass_t*class_this = scope_class->class_def();
ivl_assert(*this, class_this);
// We also need the super-class.
const netclass_t*class_super = class_this->get_super();
if (class_super == 0) {
cerr << get_fileline() << ": error: "
<< "Class " << class_this->get_name()
<< " has no parent class for super.new constructor chaining." << endl;
des->errors += 1;
NetBlock*tmp = new NetBlock(NetBlock::SEQU, 0);
tmp->set_line(*this);
return tmp;
}
// Need the "this" variable for the current constructor. We're
// going to pass this to the chained constructor.
NetNet*var_this = scope->find_signal(perm_string::literal("@"));
// If super.new is an implicit constructor, then there are no
// arguments (other then "this" to worry about, so make a
// NetEUFunc and there we go.
if (NetScope*new_scope = class_super->method_from_name(perm_string::literal("new@"))) {
NetESignal*eres = new NetESignal(var_this);
vector<NetExpr*> parms(1);
parms[0] = eres;
NetEUFunc*tmp = new NetEUFunc(scope, new_scope, eres, parms, true);
tmp->set_line(*this);
NetAssign_*lval_this = new NetAssign_(var_this);
NetAssign*stmt = new NetAssign(lval_this, tmp);
stmt->set_line(*this);
return stmt;
}
// If super.new(...) is a user defined constructor, then call
// it. This is a bit more complicated because there may be arguments.
if (NetScope*new_scope = class_super->method_from_name(perm_string::literal("new"))) {
int missing_parms = 0;
NetFuncDef*def = new_scope->func_def();
ivl_assert(*this, def);
NetESignal*eres = new NetESignal(var_this);
vector<NetExpr*> parms (def->port_count());
parms[0] = eres;
for (size_t idx = 1 ; idx < parms.size() ; idx += 1) {
if (idx <= parms_.size() && parms_[idx-1]) {
PExpr*tmp = 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);
continue;
}
if (NetExpr*tmp = def->port_defe(idx)) {
parms[idx] = tmp;
continue;
}
missing_parms += 1;
parms[idx] = 0;
}
if (missing_parms) {
cerr << get_fileline() << ": error: "
<< "Missing " << missing_parms
<< " arguments to constructor " << scope_path(new_scope) << "." << endl;
des->errors += 1;
}
NetEUFunc*tmp = new NetEUFunc(scope, new_scope, eres, parms, true);
tmp->set_line(*this);
NetAssign_*lval_this = new NetAssign_(var_this);
NetAssign*stmt = new NetAssign(lval_this, tmp);
stmt->set_line(*this);
return stmt;
}
// There is no constructor at all in the parent, so skip it.
NetBlock*tmp = new NetBlock(NetBlock::SEQU, 0);
tmp->set_line(*this);
return tmp;
return 0;
}
NetProc* PCondit::elaborate(Design*des, NetScope*scope) const NetProc* PCondit::elaborate(Design*des, NetScope*scope) const
{ {
assert(scope); assert(scope);

View File

@ -23,8 +23,8 @@
using namespace std; using namespace std;
netclass_t::netclass_t(perm_string name, netclass_t*sup, const vector<PExpr*>&chain_args) netclass_t::netclass_t(perm_string name, netclass_t*sup)
: name_(name), super_(sup), chain_args_(chain_args), class_scope_(0) : name_(name), super_(sup), class_scope_(0)
{ {
} }

View File

@ -34,7 +34,7 @@ class PExpr;
class netclass_t : public ivl_type_s { class netclass_t : public ivl_type_s {
public: public:
netclass_t(perm_string class_name, netclass_t*par, const vector<PExpr*>&pchain_args); netclass_t(perm_string class_name, netclass_t*par);
~netclass_t(); ~netclass_t();
// Set the property of the class during elaboration. Set the // Set the property of the class during elaboration. Set the
@ -46,9 +46,6 @@ class netclass_t : public ivl_type_s {
// is used for the elaboration of methods (tasks/functions). // is used for the elaboration of methods (tasks/functions).
void set_class_scope(NetScope*cscope); void set_class_scope(NetScope*cscope);
inline const std::vector<PExpr*>& get_chain_args(void) const
{ return chain_args_; }
// As an ivl_type_s object, the netclass is always an // As an ivl_type_s object, the netclass is always an
// ivl_VT_CLASS object. // ivl_VT_CLASS object.
ivl_variable_type_t base_type() const; ivl_variable_type_t base_type() const;
@ -103,11 +100,9 @@ class netclass_t : public ivl_type_s {
private: private:
perm_string name_; perm_string name_;
// If this is derived from another base class, point to it // If this is derived from another base class, point to it
// here. The super constructor may take arguments, and the // here.
// chain_parms_ are those arguments.
netclass_t*super_; netclass_t*super_;
std::vector<PExpr*> chain_args_; // Map property names to property table index.
// Map properrty names to property table index.
std::map<perm_string,size_t> properties_; std::map<perm_string,size_t> properties_;
// Vector of properties. // Vector of properties.
struct prop_t { struct prop_t {