/***** * fundec.h * Andy Hammerlindl 2002/8/29 * * Defines the semantics for defining functions. Both the newexp syntax, and * the abbreviated C-style function definition. *****/ #include "fundec.h" #include "errormsg.h" #include "coenv.h" #include "stm.h" #include "runtime.h" namespace absyntax { using namespace trans; using namespace types; using mem::list; varinit *Default=new definit(nullPos); void formal::prettyprint(ostream &out, Int indent) { prettyname( out, keywordOnly ? "formal (keyword only)" : "formal", indent, getPos() ); base->prettyprint(out, indent+1); if (start) start->prettyprint(out, indent+1); if (defval) defval->prettyprint(out, indent+1); } types::formal formal::trans(coenv &e, bool encodeDefVal, bool tacit) { return types::formal(getType(e,tacit), getName(), encodeDefVal ? (bool) getDefaultValue() : 0, getExplicit()); } types::ty *formal::getType(coenv &e, bool tacit) { types::ty *bt = base->trans(e, tacit); types::ty *t = start ? start->getType(bt, e, tacit) : bt; if (t->kind == ty_void && !tacit) { em.error(getPos()); em << "cannot declare parameters of type void"; return primError(); } return t; } void formal::addOps(coenv &e, record *r) { base->addOps(e, r); if (start) start->addOps(base->trans(e, true), e, r); } void formals::prettyprint(ostream &out, Int indent) { prettyname(out, "formals",indent, getPos()); for (list::iterator p = fields.begin(); p != fields.end(); ++p) (*p)->prettyprint(out, indent+1); } void formals::addToSignature(signature& sig, coenv &e, bool encodeDefVal, bool tacit) { for (list::iterator p = fields.begin(); p != fields.end(); ++p) { formal& f=**p; types::formal tf = f.trans(e, encodeDefVal, tacit); if (f.isKeywordOnly()) sig.addKeywordOnly(tf); else sig.add(tf); } if (rest) { if (!tacit && rest->getDefaultValue()) { em.error(rest->getPos()); em << "rest parameters cannot have default values"; } sig.addRest(rest->trans(e, encodeDefVal, tacit)); } } // Returns the types of each parameter as a signature. // encodeDefVal means that it will also encode information regarding // the default values into the signature signature *formals::getSignature(coenv &e, bool encodeDefVal, bool tacit) { signature *sig = new signature; addToSignature(*sig,e,encodeDefVal,tacit); return sig; } // Returns the corresponding function type, assuming it has a return // value of types::ty *result. function *formals::getType(types::ty *result, coenv &e, bool encodeDefVal, bool tacit) { function *ft = new function(result); addToSignature(ft->sig,e,encodeDefVal,tacit); return ft; } void formals::addOps(coenv &e, record *r) { for(list::iterator p = fields.begin(); p != fields.end(); ++p) (*p)->addOps(e, r); if (rest) rest->addOps(e, r); } mem::vector *formals::getFields() { auto *lst = new mem::vector(); for (auto frml : fields) { lst->emplace_back(frml->getAbsyntaxType(), frml->getName()); } return lst; } // Another helper class. Does an assignment, but relying only on the // destination for the type. class basicAssignExp : public exp { varEntry *dest; varinit *value; public: basicAssignExp(position pos, varEntry *dest, varinit *value) : exp(pos), dest(dest), value(value) {} void prettyprint(ostream &out, Int indent) { prettyname(out, "basicAssignExp", indent, getPos()); } types::ty *getType(coenv &) { return dest->getType(); } types::ty *trans(coenv &e) { // This doesn't handle overloaded types for the destination. value->transToType(e, getType(e)); dest->encode(WRITE, pos, e.c); return getType(e); } }; void transDefault(coenv &e, position pos, varEntry *v, varinit *init) { // This roughly translates into the statement // if (isDefault(x)) // x=init; // where x is the variable in v and isDefault is a function that tests // whether x is the default argument token. v->encode(READ, pos, e.c); label end = e.c.fwdLabel(); e.c.useLabel(inst::jump_if_not_default, end); init->transToType(e, v->getType()); v->encode(WRITE, pos, e.c); e.c.encodePop(); e.c.defLabel(end); } void formal::transAsVar(coenv &e, Int index) { symbol name = getName(); if (name) { trans::access *a = e.c.accessFormal(index); assert(a); // Suppress error messages because they will already be reported // when the formals are translated to yield the type earlier. types::ty *t = getType(e, true); varEntry *v = new varEntry(t, a, 0, getPos()); // Translate the default argument before adding the formal to the // environment, consistent with the initializers of variables. if (defval) transDefault(e, getPos(), v, defval); e.e.addVar(name, v); } } void formal::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP if (start) { start->createSymMap(symContext); } #endif } #ifdef HAVE_LSP std::pair> formal::fnInfo() const { std::string typeName(static_cast(*base)); return start != nullptr ? std::make_pair(typeName, make_optional(static_cast(start->getName())) ) : std::make_pair(typeName, nullopt); } #endif void formals::trans(coenv &e) { Int index = 0; for (list::iterator p=fields.begin(); p!=fields.end(); ++p) { (*p)->transAsVar(e, index); ++index; } if (rest) { rest->transAsVar(e, index); ++index; } } void formals::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP for (auto& field: fields) { field->createSymMap(symContext); } if (rest) { rest->createSymMap(symContext); } #endif } void formals::addArgumentsToFnInfo(AsymptoteLsp::FunctionInfo& fnInfo) { #ifdef HAVE_LSP for (auto const& field: fields) { fnInfo.arguments.emplace_back(field->fnInfo()); } if (rest) { fnInfo.restArgs=rest->fnInfo(); } // handle rest case as well #endif } void fundef::prettyprint(ostream &out, Int indent) { result->prettyprint(out, indent+1); params->prettyprint(out, indent+1); body->prettyprint(out, indent+1); } function *fundef::transType(coenv &e, bool tacit) { bool encodeDefVal=true; return params->getType(result->trans(e, tacit), e, encodeDefVal, tacit); } function *fundef::transTypeAndAddOps(coenv &e, record *r, bool tacit) { result->addOps(e,r); params->addOps(e,r); function *ft=transType(e, tacit); e.e.addFunctionOps(ft); if (r) r->e.addFunctionOps(ft); return ft; } void fundef::addArgumentsToFnInfo(AsymptoteLsp::FunctionInfo& fnInfo) { #ifdef HAVE_LSP params->addArgumentsToFnInfo(fnInfo); // handle rest case as well #endif } varinit *fundef::makeVarInit(function *ft) { struct initializer : public varinit { fundef *f; function *ft; initializer(fundef *f, function *ft) : varinit(f->getPos()), f(f), ft(ft) {} void prettyprint(ostream &out, Int indent) { prettyname(out, "initializer", indent, getPos()); } void transToType(coenv &e, types::ty *target) { assert(ft==target); f->baseTrans(e, ft); } }; return new initializer(this, ft); } void fundef::baseTrans(coenv &e, types::function *ft) { string name = id ? string(id) : string(""); // Create a new function environment. coder fc = e.c.newFunction(getPos(), name, ft); coenv fe(fc,e.e); // Translate the function. fe.e.beginScope(); params->trans(fe); body->trans(fe); types::ty *rt = ft->result; if (rt->kind != ty_void && rt->kind != ty_error && !body->returns()) { em.error(body->getPos()); em << "function must return a value"; } fe.e.endScope(); // Put an instance of the new function on the stack. vm::lambda *l = fe.c.close(); e.c.encode(inst::pushclosure); e.c.encode(inst::makefunc, l); } types::ty *fundef::trans(coenv &e) { // I don't see how addFunctionOps can be useful here. // For instance, in // // new void() {} == null // // callExp has to search for == before translating either argument, and the // operator cannot be added before translation. (getType() is not allowed to // manipulate the environment.) // A new function expression is assigned to a variable, given as a return // value, or used as an argument to a function. In any of these // // We must still addOps though, for the return type and formals. ex: // // new guide[] (guide f(int)) { // return sequence(f, 10); // }; function *ft=transTypeAndAddOps(e, (record *)0, false); assert(ft); baseTrans(e, ft); return ft; } void fundef::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP auto* declCtx(symContext->newContext( getPos().LineColumn() )); params->createSymMap(declCtx); body->createSymMap(declCtx); #endif } void fundec::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "fundec '" << id << "'\n"; fun.prettyprint(out, indent); } void fundec::trans(coenv &e) { transAsField(e,0); } void fundec::transAsField(coenv &e, record *r) { function *ft = fun.transTypeAndAddOps(e, r, false); assert(ft); createVar(getPos(), e, r, id, ft, fun.makeVarInit(ft)); } void fundec::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP AsymptoteLsp::FunctionInfo& fnInfo= symContext->symMap.addFunDef(static_cast(id), getPos().LineColumn(), static_cast(*fun.result) ); fun.addArgumentsToFnInfo(fnInfo); fun.createSymMap(symContext); #endif } } // namespace absyntax