/***** * dec.cc * Andy Hammerlindl 2002/8/29 * * Represents the abstract syntax tree for declarations in the language. * Also included is an abstract syntax for types as they are most often * used with declarations. *****/ #include "errormsg.h" #include "coenv.h" #include "dec.h" #include "fundec.h" #include "newexp.h" #include "stm.h" #include "exp.h" #include "modifier.h" #include "runtime.h" #include "locate.h" #include "asyparser.h" #include "builtin.h" // for trans::addRecordOps namespace absyntax { using namespace trans; using namespace types; using mem::list; symbol intSymbol() { const static symbol* intSymbol = new symbol(symbol::literalTrans("int")); return *intSymbol; } bool usableInTemplate(ty *t) { assert(t); if (t->primitive()) return true; assert(t->kind != ty_null); assert(t->kind != ty_overloaded); if (t->kind == ty_record) { record* r= dynamic_cast(t); assert(r); assert(r->getLevel()); if (!r->getLevel()->getParent()) return false;// r is actually a module if (!r->getLevel()->getParent()->getParent()) { return true;// r is a top-level record, or all nestings are static } return false; // r is nested non-statically } if (t->kind == ty_function) { function* f= dynamic_cast(t); assert(f); // Check the types of the result and all the parameters. if (!usableInTemplate(f->result)) return false; const signature& sig= *f->getSignature(); for (const types::formal& f : sig.formals) { if (!usableInTemplate(f.t)) return false; } if (sig.hasRest() && !usableInTemplate(sig.getRest().t)) return false; return true; } if (t->kind == ty_array) { array* a= dynamic_cast(t); assert(a); return usableInTemplate(a->celltype); } // We should have already handled all the cases. assert(false); return false; } trans::tyEntry *astType::transAsTyEntry(coenv &e, record *where) { return new trans::tyEntry(trans(e, false), nullptr, where, getPos()); } void nameTy::prettyprint(ostream &out, Int indent) { prettyname(out, "nameTy",indent, getPos()); id->prettyprint(out, indent+1); } void addNameOps(coenv &e, record *r, record *qt, varEntry *qv, position pos) { for (auto au : qt->e.ve.getAutoUnravels()) { symbol auName = au.first; varEntry *v = au.second; if (!v->checkPerm(READ, e.c)) { em.error(pos); em << "cannot access '" << auName << "' in current scope"; continue; } if (r && e.c.getPermission() != PUBLIC) { // Add an additional restriction to v based on c.getPermission(). v = new varEntry(*v, e.c.getPermission(), r); } varEntry *qqv = qualifyVarEntry(qv, v); auto enter= [&](trans::venv& ve) { // Add op only if it does not already exist. if (!ve.lookByType(auName, qqv->getType())) { ve.enter(auName, qqv); } }; if (r) { enter(r->e.ve); } enter(e.e.ve); } } void nameTy::addOps(coenv &e, record *r, AutounravelOption opt) { if (opt == AutounravelOption::Apply) { if (record* qt= dynamic_cast(id->getType(e, true)); qt) { varEntry* qv= id->getVarEntry(e); addNameOps(e, r, qt, qv, getPos()); } } } types::ty *nameTy::trans(coenv &e, bool tacit) { return id->typeTrans(e, tacit); } trans::tyEntry *nameTy::transAsTyEntry(coenv &e, record *) { return id->tyEntryTrans(e); } nameTy::operator string() const { return static_cast(id->getName()); } void dimensions::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "dimensions (" << depth << ")\n"; } types::array *dimensions::truetype(types::ty *base, bool tacit) { if (!tacit && base->kind == ty_void) { em.error(getPos()); em << "cannot declare array of type void"; } assert(depth >= 1); size_t d=depth; types::array *a=new types::array(base); d--; for (; d > 0; d--) { a = new types::array(a); } return a; } void arrayTy::prettyprint(ostream &out, Int indent) { prettyname(out, "arrayTy",indent, getPos()); cell->prettyprint(out, indent+1); dims->prettyprint(out, indent+1); } // NOTE: Can this be merged with trans somehow? void arrayTy::addOps(coenv &e, record *r, AutounravelOption) { types::ty *t=trans(e, true); // Only add ops if it is an array (and not, say, an error) if (t->kind == types::ty_array) { types::array *at=dynamic_cast(t); assert(at); e.e.addArrayOps(at); if (r) r->e.addArrayOps(at); } } types::ty *arrayTy::trans(coenv &e, bool tacit) { types::ty *ct = cell->trans(e, tacit); assert(ct); // Don't make an array of errors. if (ct->kind == types::ty_error) return ct; types::array *t = dims->truetype(ct,tacit); assert(t); return t; } arrayTy::operator string() const { stringstream ss; ss << static_cast(*cell); for (size_t i = 0; i < dims->size(); i++) { ss << "[]"; } return ss.str(); } tyEntryTy::tyEntryTy(position pos, types::ty* t) : astType(pos), ent(new trans::tyEntry(t, nullptr, nullptr, nullPos)) {} void tyEntryTy::prettyprint(ostream &out, Int indent) { prettyindent(out,indent); out << "tyEntryTy: " << *(ent->t) << "\n"; } types::ty *tyEntryTy::trans(coenv &, bool) { return ent->t; } tyEntryTy::operator string() const { return ""; } vm::lambda *runnable::transAsCodelet(coenv &e) { coder c=e.c.newCodelet(getPos()); coenv ce(c, e.e); markTrans(ce); return c.close(); } void block::prettystms(ostream &out, Int indent) { for (list::iterator p = stms.begin(); p != stms.end(); ++p) (*p)->prettyprint(out, indent); } void block::prettyprint(ostream &out, Int indent) { prettyname(out,"block",indent,getPos()); prettystms(out, indent+1); } // Uses RAII to ensure scope is ended when function returns. class Scope { coenv* e; public: Scope(coenv &e, bool scope) : e(scope ? &e : nullptr) { if (this->e) this->e->e.beginScope(); } ~Scope() { if (this->e) e->e.endScope(); } }; void block::trans(coenv &e) { Scope scopeHolder(e, scope); for (list::iterator p = stms.begin(); p != stms.end(); ++p) { (*p)->markTrans(e); } } void block::transAsField(coenv &e, record *r) { Scope scopeHolder(e, scope); for (list::iterator p = stms.begin(); p != stms.end(); ++p) { (*p)->markTransAsField(e, r); if (em.errors() && !settings::debug) break; } } bool block::transAsTemplatedField( coenv &e, record *r, mem::vector* args ) { Scope scopeHolder(e, scope); receiveTypedefDec *dec = getTypedefDec(); if (dec == nullptr) { em.error(getPos()); em << "expected 'typedef import();'"; em.sync(true); return false; } if(!dec->transAsParamMatcher(e, r, args/*, caller*/)) return false; auto p = stms.begin(); // Start with second statement since the first was a receiveTypedefDec. while (++p != stms.end()) { (*p)->markTransAsField(e, r); if (em.errors() && !settings::debug) { return false; } } em.sync(); return true; } receiveTypedefDec* block::getTypedefDec() { auto p= stms.begin(); // Check for an empty file if (p == stms.end()) return nullptr; return dynamic_cast(*p); } record *block::transAsFile(genv& ge, symbol id) { // Create the new module. record *r = new record(id, new frame(id,0,0)); // Create coder and environment to translate the module. // File-level modules have dynamic fields by default. coder c(getPos(), r, 0); env e(ge); coenv ce(c, e); if (settings::getSetting("autoplain")) { autoplainRunnable()->transAsField(ce, r); } // If the file starts with a template declaration and it was accessed // or imported without template arguments, further translation is // likely futile. if (getTypedefDec() != nullptr) { if(!settings::getSetting("listvariables")) { em.error(getPos()); em << "templated module access requires template parameters"; em.sync(); } return r; } // Translate the abstract syntax. transAsField(ce, r); ce.c.closeRecord(); em.sync(); if (em.errors()) return nullptr; return r; } record* block::transAsTemplatedFile( genv& ge, symbol id, mem::vector* args ) { // Create the new module. record *r = new record(id, new frame(id, 0, 0)); // Create coder and environment to translate the module. // File-level modules have dynamic fields by default. coder c(getPos(), r, 0); env e(ge); coenv ce(c, e); // Import `plain` before even translating "typedef import" since the latter // might change the meanings of symbols provided by `plain`. if (settings::getSetting("autoplain")) { autoplainRunnable()->transAsField(ce, r); } // Translate the abstract syntax. bool succeeded = transAsTemplatedField(ce, r, args); ce.c.closeRecord(); if (!succeeded) { return nullptr; } return r; } bool block::returns() { // Search for a returning runnable, starting at the end for efficiency. for (list::reverse_iterator p=stms.rbegin(); p != stms.rend(); ++p) if ((*p)->returns()) return true; return false; } vardec *block::asVardec() { vardec *var = 0; for (list::iterator p=stms.begin(); p != stms.end(); ++p) { vardec *v = dynamic_cast(*p); if (v) { if (var) // Multiple vardecs. return 0; var = v; } else if (!dynamic_cast(*p)) // Failure due to another runnable in the block. return 0; } return var; } void dec::prettyprint(ostream &out, Int indent) { prettyname(out, "dec", indent, getPos()); } void modifierList::prettyprint(ostream &out, Int indent) { prettyindent(out,indent); out << "modifierList ("; for (list::iterator p = mods.begin(); p != mods.end(); ++p) { if (p != mods.begin()) out << ", "; switch (*p) { case EXPLICIT_STATIC: out << "static"; break; #if 0 case EXPLICIT_DYNAMIC: out << "dynamic"; break; #endif default: out << "invalid code"; } } for (list::iterator p = perms.begin(); p != perms.end(); ++p) { if (p != perms.begin() || !mods.empty()) out << ", "; switch (*p) { case PUBLIC: out << "public"; break; case PRIVATE: out << "private"; break; default: out << "invalid code"; } } out << ")\n"; } bool modifierList::staticSet() { return !mods.empty(); } modifier modifierList::getModifier() { assert(staticSet()); int numAutounravel = 0; int numStatic = 0; for (modifier m : mods) { switch (m) { case AUTOUNRAVEL: ++numAutounravel; break; case EXPLICIT_STATIC: case DEFAULT_STATIC: ++numStatic; break; default: em.compiler(getPos()); em << "invalid modifier"; } } if (numAutounravel > 1) { em.error(getPos()); em << "too many autounravel modifiers"; } if (numStatic > 1) { em.error(getPos()); em << "too many static modifiers"; } if (numAutounravel) { return AUTOUNRAVEL; } else { return mods.front(); } } permission modifierList::getPermission() { if (perms.size() > 1) { em.error(getPos()); em << "too many modifiers"; } return perms.empty() ? DEFAULT_PERM : perms.front(); } void modifiedRunnable::prettyprint(ostream &out, Int indent) { prettyname(out, "modifierRunnable",indent, getPos()); mods->prettyprint(out, indent+1); body->prettyprint(out, indent+1); } void modifiedRunnable::transAsField(coenv &e, record *r) { if (mods->staticSet()) { modifier mod = mods->getModifier(); if (e.c.isTopLevel()) { if (mod == AUTOUNRAVEL) { em.error(getPos()); em << "top-level fields cannot be autounraveled"; return; } else { em.warning(getPos()); em << "static modifier is meaningless at top level"; } } e.c.pushModifier(mod); } permission p = mods->getPermission(); #if 0 // This is innocuous if (p != DEFAULT_PERM && (!r || !body->allowPermissions())) { em.warning(pos); em << "permission modifier is meaningless"; } #endif e.c.setPermission(p); body->transAsField(e,r); e.c.clearPermission(); if (mods->staticSet()) e.c.popModifier(); } void decidstart::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "decidstart '" << id << "'\n"; if (dims) dims->prettyprint(out, indent+1); } types::ty *decidstart::getType(types::ty *base, coenv &, bool) { return dims ? dims->truetype(base) : base; } trans::tyEntry *decidstart::getTyEntry(trans::tyEntry *base, coenv &e, record *where) { return dims ? new trans::tyEntry( getType(base->t, e, false), nullptr, where, getPos() ) : base; } void decidstart::addOps(types::ty *base, coenv &e, record *r) { if (dims) { array *a=dims->truetype(base); e.e.addArrayOps(a); if (r) r->e.addArrayOps(a); } } void fundecidstart::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "fundecidstart '" << id << "'\n"; if (dims) dims->prettyprint(out, indent+1); if (params) params->prettyprint(out, indent+1); } types::ty *fundecidstart::getType(types::ty *base, coenv &e, bool tacit) { types::ty *result = decidstart::getType(base, e, tacit); if (params) { return params->getType(result, e, true, tacit); } else { types::ty *t = new function(base); return t; } } trans::tyEntry *fundecidstart::getTyEntry(trans::tyEntry *base, coenv &e, record *where) { return new trans::tyEntry(getType(base->t,e,false), nullptr, where, getPos()); } void fundecidstart::addOps(types::ty *base, coenv &e, record *r) { decidstart::addOps(base, e, r); params->addOps(e, r); types::function *ft=dynamic_cast(getType(base, e, true)); assert(ft); } void decid::prettyprint(ostream &out, Int indent) { prettyname(out, "decid",indent, getPos()); start->prettyprint(out, indent+1); if (init) init->prettyprint(out, indent+1); } varEntry *makeVarEntryWhere(coenv &e, record *r, types::ty *t, record *where, position pos) { access *a = r ? r->allocField(e.c.isStatic()) : e.c.allocLocal(); return r ? new varEntry(t, a, e.c.getPermission(), r, where, pos) : new varEntry(t, a, where, pos); } varEntry *makeVarEntry(position pos, coenv &e, record *r, types::ty *t) { return makeVarEntryWhere(e, r, t, r, pos); } // Defined in constructor.cc. bool definesImplicitConstructor(coenv &e, record *r, varEntry *v, symbol id); void addConstructorFromInitializer(position pos, coenv &e, record *r, varEntry *init); void addVar(coenv &e, record *r, varEntry *v, symbol id) { // Test for 'operator init' definitions that implicitly define constructors: if (definesImplicitConstructor(e, r, v, id)) addConstructorFromInitializer(nullPos, e, r, v); // Add to the record so it can be accessed when qualified; add to the // environment so it can be accessed unqualified in the scope of the // record definition. if (r) { r->e.addVar(id, v); if (e.c.isAutoUnravel()) { r->e.ve.registerAutoUnravel(id, v); } } e.e.addVar(id, v); } void initializeVar(position pos, coenv &e, varEntry *v, varinit *init) { types::ty *t=v->getType(); if (init) init->transToType(e, t); else { definit d(pos); d.transToType(e, t); } v->getLocation()->encode(WRITE, pos, e.c); e.c.encodePop(); } types::ty *inferType(position pos, coenv &e, varinit *init) { if (!init) { em.error(pos); em << "inferred variable declaration without initializer"; return primError(); } exp *base = dynamic_cast(init); bool Void=false; if (base) { types::ty *t = base->cgetType(e); Void=t->kind == ty_void; if (t->kind != ty_overloaded && !Void) return t; } em.error(pos); em << (Void ? "cannot infer from void" : "could not infer type of initializer"); return primError(); } void createVar(position pos, coenv &e, record *r, symbol id, types::ty *t, varinit *init) { // I'm not sure how to handle inferred types in these cases. assert(t->kind != types::ty_inferred); varEntry *v=makeVarEntry(pos, e, r, t); addVar(e, r, v, id); initializeVar(pos, e, v, init); } void createVarOutOfOrder(position pos, coenv &e, record *r, symbol id, types::ty *t, varinit *init) { /* For declarations such as "var x = 5;", infer the type from the * initializer. */ if (t->kind == types::ty_inferred) t = inferType(pos, e, init); varEntry *v=makeVarEntry(pos, e, r, t); initializeVar(pos, e, v, init); addVar(e, r, v, id); } void addTypeWithPermission(coenv &e, record *r, tyEntry *base, symbol id) { // Only bother encoding permissions for private types. tyEntry *ent = (r && e.c.getPermission()==PRIVATE) ? new trans::tyEntry(base, PRIVATE, r) : base; if (r) r->e.addType(id, ent); e.e.addType(id, ent); } void decid::transAsField(coenv &e, record *r, types::ty *base) { types::ty *t = start->getType(base, e); assert(t); if (t->kind == ty_void) { em.error(getPos()); em << "cannot declare variable of type void"; } start->addOps(base, e, r); createVarOutOfOrder(getPos(), e, r, start->getName(), t, init); } void decid::transAsTypedefField(coenv &e, trans::tyEntry *base, record *r) { trans::tyEntry *ent = start->getTyEntry(base, e, r); assert(ent && ent->t); if (init) { em.error(getPos()); em << "type definition cannot have initializer"; } start->addOps(base->t, e, r); addTypeWithPermission(e, r, ent, start->getName()); } void decidlist::prettyprint(ostream &out, Int indent) { prettyname(out, "decidlist",indent, getPos()); for (list::iterator p = decs.begin(); p != decs.end(); ++p) (*p)->prettyprint(out, indent+1); } void decidlist::transAsField(coenv &e, record *r, types::ty *base) { for (list::iterator p = decs.begin(); p != decs.end(); ++p) (*p)->transAsField(e, r, base); } void decidlist::transAsTypedefField(coenv &e, trans::tyEntry *base, record *r) { for (list::iterator p = decs.begin(); p != decs.end(); ++p) (*p)->transAsTypedefField(e, base, r); } void vardec::prettyprint(ostream &out, Int indent) { prettyname(out, "vardec",indent, getPos()); base->prettyprint(out, indent+1); decs->prettyprint(out, indent+1); } void vardec::transAsTypedefField(coenv &e, record *r) { base->addOps(e, r); decs->transAsTypedefField(e, base->transAsTyEntry(e, r), r); } symbol vardec::singleName() { decid *did = decs->singleEntry(); if (!did) return symbol::nullsym; return did->getStart()->getName(); } types::ty *vardec::singleGetType(coenv &e) { decid *did = decs->singleEntry(); if (!did) return 0; return did->getStart()->getType(base->trans(e), e); } // Helper class for imports. This essentially evaluates to the run::loadModule // function. However, that function returns different types of records // depending on the filename given to it, so we cannot add it to the // environment. Instead, we explicitly tell it what types::record it is // returning for each use. class loadModuleExp : public exp { function *ft; public: loadModuleExp(position pos, record* imp) : exp(pos) { ft= new function(imp, primString(), primInt()); } void prettyprint(ostream &out, Int indent) { prettyname(out, "loadModuleExp", indent, getPos()); } types::ty *trans(coenv &) { em.compiler(getPos()); em << "trans called for loadModuleExp"; return primError(); } void transCall(coenv &e, types::ty *t) { assert(equivalent(t, ft)); e.c.encode(inst::builtin, run::loadModule); } types::ty *getType(coenv &) { return ft; } exp *evaluate(coenv &, types::ty *) { // Don't alias. return this; } }; // Creates a local variable to hold the import and translate the accessing of // the import, but doesn't add the import to the environment. varEntry *accessModule(position pos, coenv &e, record *r, symbol id) { string filename=(string) id; bool tainted=settings::debug && em.errors(); record *imp=e.e.getModule(id, filename); if (!imp) { if(!tainted) { em.error(pos); em << "could not load module '" << filename << "'"; em.sync(true); } return 0; } else { // Create a varinit that evaluates to the module. // This is effectively the expression 'loadModule(filename, 0)'. callExp init( pos, new loadModuleExp(pos, imp), new stringExp(pos, filename), new intExp(pos, 0) ); // The varEntry should have whereDefined()==0 as it is not defined inside // the record r. varEntry *v=makeVarEntryWhere(e, r, imp, 0, pos); initializeVar(pos, e, v, &init); return v; } } // Returns the number of items that will be added to the stack once the // translation has been run (always either 0 or 1). Int transPushParent(formal *f, coenv &e) { if (f->getType(e)->kind != types::ty_record) { return 0; } astType *astT = f->getAbsyntaxType(); tyEntry *ent = astT->transAsTyEntry(e, nullptr); bool succeeded = e.c.encodeParent(f->getPos(), ent); if (!succeeded) { em.compiler(f->getPos()); em << "failed to encode parent level"; em.sync(true); } return 1; } // Translates formals into namedTys. mem::vector *computeTemplateArgs(formals *args, coenv &e) { auto *computedArgs = new mem::vector(); for (formal *f : *args) { symbol theName = f->getName(); position sourcePos = f->getPos(); if (theName == symbol::nullsym) { em.error(sourcePos); em << "expected typename="; em.sync(true); return nullptr; } types::ty *t = f->getType(e); if (!usableInTemplate(t)) { em.error(f->getAbsyntaxType()->getPos()); em << "non-statically nested types cannot be used in templates"; em.sync(true); return nullptr; } computedArgs->push_back(new namedTy(sourcePos, theName, t)); } return computedArgs; } // Creates a local variable to hold the import and translate the accessing of // the import, but doesn't add the import to the environment. varEntry *accessTemplatedModule(position pos, coenv &e, record *r, symbol id, formals *args) { string moduleName=(string) id; mem::vector *computedArgs = computeTemplateArgs(args, e); if (!computedArgs) { return nullptr; } record *imp=e.e.getTemplatedModule(moduleName,computedArgs); if (!imp) { em.error(pos); em << "could not load module '" << id << "'"; em.sync(true); return nullptr; } // Encode action: Push parents to the stack. Int numParents = 0; // We push parents in reverse order so that we can later pop them in // order, meaning that if more than one parameter has an error, the first // error will be reported rather than the last. for (auto p = args->rbegin(); p != args->rend(); ++p) { numParents += transPushParent(*p, e); } // Create a varinit that evaluates to the module. // This is effectively the expression 'loadModule(index, numParents)'. callExp init( pos, new loadModuleExp(pos, imp), new stringExp(pos, imp->getTemplateIndex()), new intExp(pos, numParents) ); // The varEntry should have whereDefined()==nullptr as it is not defined // inside the record r. varEntry *v=makeVarEntryWhere(e, r, imp, nullptr, pos); initializeVar(pos, e, v, &init); return v; } void idpair::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "idpair (" << "'" << src << "' as " << dest << ")\n"; } void idpair::transAsAccess(coenv &e, record *r) { checkValidity(); varEntry *v=accessModule(getPos(), e, r, src); if (v) addVar(e, r, v, dest); } tyEntry *idpair::transAsUnravel(coenv &e, record *r, protoenv &source, varEntry *qualifier) { checkValidity(); if (r) { auto fieldsAdded = r->e.add(src, dest, source, qualifier, e.c)->varsAdded; if (e.c.isAutoUnravel()) { for (varEntry *v : fieldsAdded) { r->e.ve.registerAutoUnravel(dest, v); } } } protoenv::Added *added = e.e.add(src, dest, source, qualifier, e.c); if (added->empty()) { em.error(getPos()); em << "no matching types or fields of name '" << src << "'"; } return added->typeAdded; } void idpairlist::prettyprint(ostream &out, Int indent) { for (list::iterator p=base.begin(); p != base.end(); ++p) (*p)->prettyprint(out, indent); } void idpairlist::transAsAccess(coenv &e, record *r) { for (list::iterator p=base.begin(); p != base.end(); ++p) (*p)->transAsAccess(e,r); } mem::vector idpairlist::transAsUnravel( coenv &e, record *r, protoenv &source, varEntry *qualifier ) { mem::vector result; for (idpair *p : base) { tyEntry *typeAdded = p->transAsUnravel(e,r,source,qualifier); result.push_back(typeAdded); } return result; } idpairlist * const WILDCARD = 0; void accessdec::prettyprint(ostream &out, Int indent) { prettyname(out, "accessdec", indent, getPos()); base->prettyprint(out, indent+1); } void templateAccessDec::transAsField(coenv& e, record* r) { if (!this->checkValidity()) return; args->addOps(e, r); varEntry *v=accessTemplatedModule(getPos(), e, r, this->src, args); if (v) addVar(e, r, v, dest); } void typeParam::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "typeParam (" << paramSym << ")\n"; } void recordInitializer(coenv &e, symbol id, record *r, position here) { // This is almost equivalent to the code // autounravel A operator init() { return new A; } // where A is the name of the record. The "almost" is because the code below // does not add the operator init to the environment, since in practice it // would be added to the outer environment, not the record's environment. // Since it is always added *after* any code in the record, we lose nothing // by not adding it to the environment. // Additionally, the "autounravel" is made low-priority (will not override // user-defined operator init) which is possible only for built-in functions. formals formals(here); simpleName recordName(here, id); nameTy result(here, &recordName); newRecordExp exp(here, &result); returnStm stm(here, &exp); fundef fun(here, &result, &formals, &stm); assert(r); { e.c.pushModifier(AUTOUNRAVEL); function *ft = fun.transType(e, false); assert(ft); symbol initSym=symbol::opTrans("init"); varinit *init=fun.makeVarInit(ft); assert(ft->kind != types::ty_inferred); varEntry *v=makeVarEntry(here, e, r, ft); r->e.addVar(initSym, v); r->e.ve.registerAutoUnravel(initSym, v, trans::AutounravelPriority::OFFER); initializeVar(here, e, v, init); e.c.popModifier(); } } bool typeParam::transAsParamMatcher(coenv &e, record *module, namedTy* arg) { symbol name = arg->dest; ty *t = arg->t; if (name != paramSym) { em.error(arg->pos); em << "template argument name does not match module: passed " << name << ", expected " << paramSym; return false; } if (t->kind != types::ty_record) { tyEntry *ent = new tyEntry(t, nullptr, module, getPos()); addTypeWithPermission(e, module, ent, name); return true; } // We can now assume t is a record (i.e., asy struct). record *r = dynamic_cast(t); assert(r); // // Build a varEntry v for the parent level and encode the bytecode // to pop the parent off the stack into v. The varEntry needs a type, // but the only thing we use from the type is the level, so make a // new fake record. string fakeParentName; # ifdef DEBUG_FRAME { ostringstream oss; oss << "getLevel()->getParent()); oss << ">"; fakeParentName = oss.str(); } # else fakeParentName = "(name) + ">"; # endif record* fakeParent = new record( symbol::literalTrans(fakeParentName), r->getLevel()->getParent() ); varEntry *v = makeVarEntryWhere(e, module, fakeParent, nullptr, getPos()); // Encode bytecode to pop the parent off the stack into v. v->encode(WRITE, getPos(), e.c); e.c.encodePop(); // // Build tyEntry, using v as ent->v. tyEntry *ent = new tyEntry(t, v, module, getPos()); addTypeWithPermission(e, module, ent, name); // Add any autounravel fields. addNameOps(e, module, r, v, getPos()); return true; } void typeParamList::prettyprint(ostream &out, Int indent) { for (auto p = params.begin(); p != params.end(); ++p) { (*p)->prettyprint(out, indent); } } void typeParamList::add(typeParam *tp) { params.push_back(tp); } // RAII class to set the permission of a coder to a new value, and then reset it // to the old value when the object goes out of scope. class PermissionSetter { coder &c; permission oldPerm; public: PermissionSetter(coder &c, permission newPerm) : c(c), oldPerm(c.getPermission()) { c.setPermission(newPerm); } ~PermissionSetter() { c.setPermission(oldPerm); } }; bool typeParamList::transAsParamMatcher( coenv &e, record *r, mem::vector *args ) { // Check that the number of arguments passed matches the number of parameters. if (args->size() != params.size()) { position pos = getPos(); if (args->size() >= 1) { pos = (*args)[0]->pos; } em.error(pos); if (args->size() > params.size()) { em << "too many types passed: got " << args->size() << ", expected " << params.size(); } else { em << "too few types passed: got " << args->size() << ", expected " << params.size(); } return false; } // Set the permission to PRIVATE while translating type parameters. PermissionSetter ps(e.c, PRIVATE); // Pop the parents off the stack. They were pushed in reverse order. // With this approach, the first error will be reported, rather than the last. for (size_t i = 0; i < params.size(); ++i) { bool succeeded = params[i]->transAsParamMatcher(e, r, (*args)[i]); if (!succeeded) return false; } return true; } bool receiveTypedefDec::transAsParamMatcher( coenv& e, record *r, mem::vector *args ) { bool succeeded = params->transAsParamMatcher(e, r, args); return succeeded; } void receiveTypedefDec::transAsField(coenv& e, record *r) { em.error(getPos()); em << "unexpected 'typedef import'"; em.sync(); } void fromdec::prettyprint(ostream &out, Int indent) { prettyname(out, "fromdec", indent, getPos()); fields->prettyprint(out, indent+1); } void fromdec::transAsField(coenv &e, record *r) { qualifier q=getQualifier(e,r); if (q.t) { if (fields==WILDCARD) { if (r) r->e.add(q.t->e, q.v, e.c); e.e.add(q.t->e, q.v, e.c); } else { auto typesAdded = fields->transAsUnravel(e, r, q.t->e, q.v); for (tyEntry *te : typesAdded) { if (te) { record *t = dynamic_cast(te->t); if (t) { addNameOps(e, r, t, te->v, getPos()); } } } } } } void unraveldec::prettyprint(ostream &out, Int indent) { prettyname(out, "unraveldec", indent, getPos()); id->prettyprint(out, indent+1); idpairlist *f=this->fields; if(f) f->prettyprint(out, indent+1); } fromdec::qualifier unraveldec::getQualifier(coenv &e, record *) { // getType is where errors in the qualifier are reported. record *qt=dynamic_cast(id->getType(e, false)); if (!qt) { em.error(getPos()); em << "qualifier is not a record"; } return qualifier(qt,id->getVarEntry(e)); } void fromaccessdec::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "fromaccessdec '" << id << "'\n"; idpairlist *f=this->fields; if(f) f->prettyprint(out, indent+1); } fromdec::qualifier fromaccessdec::getQualifier(coenv &e, record *r) { varEntry *v = 0; if (templateArgs) { v = accessTemplatedModule(getPos(), e, r, id, templateArgs); } else { v=accessModule(getPos(), e, r, id); } if (v) { record *qt=dynamic_cast(v->getType()); if (!qt) { em.compiler(getPos()); em << "qualifier is not a record"; } return qualifier(qt, v); } else return qualifier(0,0); } void importdec::prettyprint(ostream &out, Int indent) { prettyname(out, "importdec", indent, getPos()); base.prettyprint(out, indent+1); } void includedec::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "includedec ('" << filename << "')\n"; } void includedec::loadFailed(coenv &) { em.warning(getPos()); em << "could not parse file of name '" << filename << "'"; em.sync(true); } void includedec::transAsField(coenv &e, record *r) { file *ast = parser::parseFile(filename,"Including"); em.sync(); // The runnables will be translated, one at a time, without any additional // scoping. ast->transAsField(e, r); } void typedec::prettyprint(ostream &out, Int indent) { prettyname(out, "typedec",indent, getPos()); body->prettyprint(out, indent+1); } void recorddec::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "structdec '" << id << "'\n"; body->prettyprint(out, indent+1); } void recorddec::transRecordInitializer(coenv &e, record *parent) { recordInitializer(e,id,parent,getPos()); } void recorddec::addPostRecordEnvironment(coenv &e, record *r, record *parent) { if (parent) parent->e.add(r->postdefenv, 0, e.c); e.e.add(r->postdefenv, 0, e.c); // Add the autounravel fields also. addNameOps(e, parent, r, nullptr, getPos()); } void recorddec::transAsField(coenv &e, record *parent) { if (e.c.isAutoUnravel()) { em.error(getPos()); em << "types cannot be autounraveled"; } record *r = parent ? parent->newRecord(id, e.c.isStatic()) : e.c.newRecord(id); addTypeWithPermission( e, parent, new trans::tyEntry(r, nullptr, parent, getPos()), id ); trans::addRecordOps(r); // Start translating the initializer. coder c=e.c.newRecordInit(getPos(), r); coenv re(c,e.e); { // Make sure the autounraveled fields are limited to the record's scope. // If the scope is too broad, then user-provide autounravel overrides will // defer to already-defined ops when they should not. bool useScope = body->scope; // RAII: Close the scope when scopeHolder runs its destructor. Scope scopeHolder(re, useScope); // Autounravel the record ops into the record's environment. addNameOps(re, nullptr, r, nullptr, getPos()); // We've already handled the scope ourselves, so tell `body` not to add an // additional scope when running `transAsField`. body->scope = false; // Translate the main body of the record. body->transAsField(re, r); // Restore the original value of the `scope` boolean. This probably makes no // difference but is included out of an abundance of caution. body->scope = useScope; } // Close the scope. // After the record is translated, add a default initializer so that a // variable of the type of the record is initialized to a new instance by // default. transRecordInitializer(re, r); // This would normally be done right after transAsField, but we needed to add // the default initializer first. re.c.closeRecord(); // Add types and variables defined during the record that should be added to // the enclosing environment. These are the implicit constructors defined by // "operator init", as well as the autounravel fields (both builtin and user // defined). addPostRecordEnvironment(e, r, parent); } runnable *autoplainRunnable() { // Abstract syntax for the code: // private import plain; position pos=nullPos; static importdec ap(pos, new idpair(pos, symbol::literalTrans("plain"))); static modifiedRunnable mr(pos, trans::PRIVATE, &ap); return &mr; } } // namespace absyntax