/*****
* exp.cc
* andy hammerlindl 2002/8/19
*
* represents the abstract syntax tree for the expressions in the
* language. this is translated into virtual machine code using trans()
* and with the aid of the environment class.
*****/
if (equivalent(target, ct)) {
transAsType(e, target);
return;
}
// See if the cast can be handled by the fastLookupCast method, which does
// less memory allocation.
if (ct->kind != ty_overloaded &&
ct->kind != ty_error &&
target->kind != ty_error) {
access *a = e.e.fastLookupCast(target, ct);
if (a) {
transAsType(e, ct);
a->encode(trans::CALL, getPos(), e.c);
return;
}
}
types::ty *source = e.e.castSource(target, ct, symbol::castsym);
if (source==0) {
if (target->kind != ty_error) {
types::ty *sources=cgetType(e);
em.error(getPos());
em << "cannot cast ";
if (sources->kind==ty_overloaded)
em << "expression";
else
em << "'" << *sources << "'";
em << " to '" << *target << "'";
}
}
else if (source->kind==ty_overloaded) {
if (target->kind != ty_error) {
em.error(getPos());
em << "expression is ambiguous in cast to '" << *target << "'";
}
}
else {
transAsType(e, source);
e.implicitCast(getPos(), target, source);
}
}
void exp::testCachedType(coenv &e) {
if (ct != 0) {
types::ty *t = getType(e);
if (!equivalent(t, ct)) {
em.compiler(getPos());
em << "cached type '" << *ct
<< "' doesn't match actual type '" << *t << "'";
em.sync(true);
}
}
}
void nameExp::prettyprint(ostream &out, Int indent)
{
prettyname(out, "nameExp",indent, getPos());
value->prettyprint(out, indent+1);
}
void fieldExp::pseudoName::prettyprint(ostream &out, Int indent)
{
// This should never be called.
prettyindent(out, indent);
out << "pseudoName" << "\n";
object->prettyprint(out, indent+1);
}
void fieldExp::prettyprint(ostream &out, Int indent)
{
prettyindent(out, indent);
out << "fieldExp '" << field << "'\n";
array *arrayExp::getArrayType(coenv &e)
{
types::ty *a = set->cgetType(e);
if (a->kind == ty_overloaded) {
a = ((overloaded *)a)->signatureless();
if (!a)
return 0;
}
switch (a->kind) {
case ty_array:
return (array *)a;
case ty_error:
return 0;
default:
return 0;
}
}
array *arrayExp::transArray(coenv &e)
{
types::ty *a = set->cgetType(e);
if (a->kind == ty_overloaded) {
a = ((overloaded *)a)->signatureless();
if (!a) {
em.error(set->getPos());
em << "expression is not an array";
return 0;
}
}
set->transAsType(e, a);
switch (a->kind) {
case ty_array:
return (array *)a;
case ty_error:
return 0;
default:
em.error(set->getPos());
em << "expression is not an array";
return 0;
}
}
// Checks if the expression can be translated as an array.
bool isAnArray(coenv &e, exp *x)
{
types::ty *t=x->cgetType(e);
if (t->kind == ty_overloaded)
t=dynamic_cast<overloaded *>(t)->signatureless();
return t && t->kind==ty_array;
}
void subscriptExp::prettyprint(ostream &out, Int indent)
{
prettyindent(out, indent);
out << "subscriptExp\n";
types::ty *subscriptExp::getType(coenv &e)
{
array *a = getArrayType(e);
return a ? (isAnArray(e, index) ? a : a->celltype) :
primError();
}
void subscriptExp::transWrite(coenv &e, types::ty *t, exp *value)
{
// Put array, index, and value on the stack in that order, then call
// arrayWrite.
array *a = transArray(e);
if (!a)
return;
if (!equivalent(a->celltype, t))
{
em.error(getPos());
em << "array expression cannot be used as an address";
// Translate the value for errors.
value->transToType(e, t);
return;
}
void slice::trans(coenv &e)
{
if (left)
left->transToType(e, types::primInt());
else
// If the left index is omitted it can be assumed to be zero.
e.c.encode(inst::intpush, (Int)0);
if (right)
right->transToType(e, types::primInt());
}
types::ty *equalityExp::getType(coenv &e) {
// Try to the resolve the expression as a function call first.
types::ty *t = callExp::getType(e);
assert(t);
if (t->kind != ty_error)
return t;
else
// Either an error or handled by the function equality methods. In the
// first case, we may return whatever we like, and the second case always
// returns bool. In either case, it is safe to return bool.
return primBoolean();
}
// From a possibly overloaded type, if there is a unique function type, return
// it, otherwise 0.
types::ty *uniqueFunction(types::ty *t) {
if (t->kind == types::ty_function)
return t;
if (t->isOverloaded()) {
types::ty *ft = 0;
for (ty_iterator i = t->begin(); i != t->end(); ++i)
{
if ((*i)->kind != types::ty_function)
continue;
if (ft) {
// Multiple function types.
return 0;
}
ft = *i;
}
return ft;
}
// Not a function.
return 0;
}
// From two possibly overloaded types, if there is a unique function type
// common to both, return it, otherwise 0.
types::ty *uniqueFunction(types::ty *t1, types::ty *t2) {
if (t1->kind == types::ty_function)
return equivalent(t1, t2) ? t1 : 0;
if (t1->isOverloaded()) {
types::ty *ft = 0;
for (ty_iterator i = t1->begin(); i != t1->end(); ++i)
{
if ((*i)->kind != types::ty_function)
continue;
types::ty *equalityExp::trans(coenv &e) {
// First, try to handle by normal function resolution.
types::ty *t = callExp::getType(e);
assert(t);
if (t->kind != ty_error)
return callExp::trans(e);
// Then, check for the function equality case.
exp *left = (*this->args)[0].val;
exp *right = (*this->args)[1].val;
// Check for function == null and null == function
types::ty *ft = 0;
if (rt->kind == types::ty_null)
ft = uniqueFunction(lt);
else if (lt->kind == types::ty_null)
ft = uniqueFunction(rt);
else
ft = uniqueFunction(lt, rt);
if (l.empty()) {
//cerr << "l is empty\n";
if (!tacit) {
em.error(getPos());
symbol s = callee->getName();
if (s)
em << "no matching function \'" << s;
else
em << "no matching function for signature \'";
em << *source << "\'";
}
return 0;
}
else if (l.size() > 1) { // This may take O(n) time.
//cerr << "l is full\n";
if (!tacit) {
em.error(getPos());
symbol s = callee->getName();
if(s)
em << "call of function \'" << s;
else
em << "call with signature \'";
em << *source << "\' is ambiguous:\n\n";
for(app_list::iterator p=l.begin(); p != l.end(); ++p)
em << *(*p)->getType() << "\n";
}
bool hasNamedParameters(signature *sig) {
for (size_t i=0; i < sig->getNumFormals(); ++i)
if (sig->getFormal(i).name)
return true;
return false;
}
void callExp::reportMismatch(function *ft, signature *source)
{
symbol s = callee->getName();
const char *separator=ft->getSignature()->getNumFormals() > 1 ? "\n" : " ";
em.error(getPos());
em << "cannot call" << separator << "'" << *ft->getResult() << " ";
if(s)
em << s;
em << *ft->getSignature() << "'" << separator;
if (ft->getSignature()->isOpen && hasNamedParameters(source))
em << "with named parameters";
else
switch(source->getNumFormals()) {
case 0:
em << "without parameters";
break;
case 1:
em << "with parameter '" << toString(*source) << "'";
break;
default:
em << "with parameters\n'" << toString(*source) << "'";
}
}
void callExp::reportArgErrors(coenv &e)
{
// Cycle through the parameters to report all errors.
// NOTE: This may report inappropriate ambiguity errors.
for (size_t i = 0; i < args->size(); i++) {
(*args)[i].val->trans(e);
}
if (args->rest.val)
args->rest.val->trans(e);
}
void callExp::reportNonFunction() {
em.error(getPos());
symbol s = callee->getName();
if (s)
em << "\'" << s << "\' is not a function";
else
em << "called expression is not a function";
}
// An attempt at speeding up compilation: See if the source arguments match
// the (possibly overloaded) function exactly.
if (searchable) {
varEntry *ve = callee->getCallee(e, source);
if (ve) {
cachedVarEntry = ve;
#ifndef DEBUG_CACHE
// Normally DEBUG_CACHE is not defined and we return here for efficiency
// reasons. If DEBUG_CACHE is defined, we instead proceed to resolve
// the function by the normal techniques and make sure we get the same
// result.
return ((function *)ve->getType())->getResult();
#endif
}
}
// Figure out what function types we can call.
types::ty *ft = callee->cgetType(e);
#ifdef DEBUG_GETAPP
string name = callee->getName() ? string(*callee->getName()) :
string("unnamed");
if (!callee->getName())
cout << getPos() << endl;
#endif
switch (ft->kind) {
case ty_error:
if (!tacit)
// Report callee errors.
callee->trans(e);
break;
case ty_function:
//cout << "name " << name << endl;
cachedApp = application::match(e.e, (function *)ft, source, *args);
if (!cachedApp && !tacit)
reportMismatch((function *)ft, source);
break;
case ty_overloaded: {
#ifdef DEBUG_GETAPP
int size = ((overloaded *)ft)->sub.size();
for (int i = 0; i < size; ++i) cout << "name " << name << endl;
#endif
cachedApp = resolve(e, (overloaded *)ft, source, tacit);
break;
}
default:
if (!tacit)
reportNonFunction();
break;
}
#if DEBUG_CACHE
// Make sure cachedVarEntry is giving us the right function.
if (cachedVarEntry)
assert(equivalent(cachedVarEntry->getType(), cachedApp->getType()));
#endif
// getType relies on this method for the type.
return cachedApp ? cachedApp->getType()->getResult() : primError();
}
types::ty *callExp::transPerfectMatch(coenv &e) {
// The varEntry of the callee. (No longer needed after translation.)
varEntry *ve = cachedVarEntry;
cachedVarEntry = 0;
assert(ve);
// Translate the arguments in turn.
for (size_t i = 0; i < args->size(); ++i)
(*args)[i].val->trans(e);
if (args->rest.val)
args->rest.val->trans(e);
// Call the function.
ve->encode(trans::CALL, getPos(), e.c);
// That's it. Return the return type of the function.
return ct ? ct : dynamic_cast<function *>(ve->getType())->getResult();
}
// The cached data is no longer needed after translation, so let it be
// garbage collected.
application *a = cachedApp;
cachedApp=0;
if (!a) {
reportArgErrors(e);
return primError();
}
// To simulate left-to-right order of evaluation, produce the
// side-effects for the callee.
assert(a);
function *t=a->getType();
assert(t);
exp *temp=callee->evaluate(e, t);
// Let the application handle the argument translation.
a->transArgs(e);
// Translate the call.
temp->transCall(e, t);
return t->result;
}
types::ty *callExp::getType(coenv &e)
{
if (cachedApp)
return cachedApp->getType()->getResult();
if (cachedVarEntry) {
function *ft = dynamic_cast<function *>(cachedVarEntry->getType());
assert(ft);
return ft->getResult();
}
return cacheAppOrVarEntry(e, true);
}
if (tt->kind==ty_error)
return onTrue->trans(e);
if (ft->kind==ty_error)
return onFalse->trans(e);
types::ty *t=promote(e, tt, ft);
if (!t) {
em.error(getPos());
em << "types in conditional expression do not match";
return primError();
}
else if (t->kind == ty_overloaded) {
em.error(getPos());
em << "type of conditional expression is ambiguous";
return primError();
}
void assignExp::transAsType(coenv &e, types::ty *target)
{
#if 0
// For left-to-right order, we have to evaluate the side-effects of the
// destination first.
exp *temp=dest->evaluate(e, target);
ultimateValue(temp)->transToType(e, target);
temp->transWrite(e, target);
#endif
// All of the heavy work is handled by transWrite.
dest->transWrite(e, target, value);
}
void selfExp::transAsType(coenv &e, types::ty *target)
{
// Create a temp expression for the destination, so it is not evaluated
// twice.
exp *temp=dest->evaluate(e, target);
temp->transWrite(e, target, ultimateValue(temp));
}
void prefixExp::prettyprint(ostream &out, Int indent)
{
prettyindent(out, indent);
out << "prefixExp '" << op << "'\n";
dest->prettyprint(out, indent+1);
}
types::ty *prefixExp::trans(coenv &e)
{
// Convert into the operation and the assign.
// NOTE: This can cause multiple evaluations.
intExp ie(getPos(), 1);
selfExp se(getPos(), dest, op, &ie);
return se.trans(e);
}
types::ty *prefixExp::getType(coenv &e)
{
// Convert into the operation and the assign.
intExp ie(getPos(), 1);
selfExp se(getPos(), dest, op, &ie);
return se.getType(e);
}
void postfixExp::prettyprint(ostream &out, Int indent)
{
prettyindent(out, indent);
out << "postfixExp <illegal> '" << op << "'\n";
dest->prettyprint(out, indent+1);
}
types::ty *postfixExp::trans(coenv &)
{
em.error(getPos());
em << "postfix expressions are not allowed";
return primError();
}