/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Guido Tack */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include namespace MiniZinc { bool check_par_domain(EnvI& env, Expression* e, Expression* domain) { if (e->type() == Type::parint()) { IntSetVal* isv = eval_intset(env, domain); if (!isv->contains(eval_int(env, e))) { return false; } } else if (e->type() == Type::parfloat()) { FloatSetVal* fsv = eval_floatset(env, domain); if (!fsv->contains(eval_float(env, e))) { return false; } } else if (e->type() == Type::parsetint()) { IntSetVal* isv = eval_intset(env, domain); IntSetRanges ir(isv); IntSetVal* rsv = eval_intset(env, e); IntSetRanges rr(rsv); if (!Ranges::subset(rr, ir)) { return false; } } else if (e->type() == Type::parsetfloat()) { FloatSetVal* fsv = eval_floatset(env, domain); FloatSetRanges fr(fsv); FloatSetVal* rsv = eval_floatset(env, e); FloatSetRanges rr(rsv); if (!Ranges::subset(rr, fr)) { return false; } } return true; } void check_par_declaration(EnvI& env, VarDecl* vd) { if (vd->type().dim() > 0) { check_index_sets(env, vd, vd->e()); if (vd->ti()->domain() != nullptr) { ArrayLit* al = eval_array_lit(env, vd->e()); for (unsigned int i = 0; i < al->size(); i++) { if (!check_par_domain(env, (*al)[i], vd->ti()->domain())) { throw ResultUndefinedError(env, vd->e()->loc(), "parameter value out of range"); } } } } else { if (vd->ti()->domain() != nullptr) { if (!check_par_domain(env, vd->e(), vd->ti()->domain())) { throw ResultUndefinedError(env, vd->e()->loc(), "parameter value out of range"); } } } } template typename E::Val eval_id(EnvI& env, Expression* e) { Id* id = e->cast(); if (!id->decl()) { throw EvalError(env, e->loc(), "undeclared identifier", id->str()); } VarDecl* vd = id->decl(); while (vd->flat() && vd->flat() != vd) { vd = vd->flat(); } if (!vd->e()) { throw EvalError(env, vd->loc(), "cannot evaluate expression", id->str()); } typename E::Val r = E::e(env, vd->e()); if (!vd->evaluated() && (vd->toplevel() || vd->type().dim() > 0)) { Expression* ne = E::exp(r); vd->e(ne); vd->evaluated(true); } return r; } bool EvalBase::evalBoolCV(EnvI& env, Expression* e) { GCLock lock; if (e->type().cv()) { return eval_bool(env, flat_cv_exp(env, Ctx(), e)()); } return eval_bool(env, e); }; class EvalIntLit : public EvalBase { public: typedef IntLit* Val; typedef Expression* ArrayVal; static IntLit* e(EnvI& env, Expression* e) { return IntLit::a(eval_int(env, e)); } static Expression* exp(IntLit* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalIntVal : public EvalBase { public: typedef IntVal Val; typedef IntVal ArrayVal; static IntVal e(EnvI& env, Expression* e) { return eval_int(env, e); } static Expression* exp(IntVal e) { return IntLit::a(e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { if ((fi->ti()->domain() != nullptr) && !fi->ti()->domain()->isa()) { IntSetVal* isv = eval_intset(env, fi->ti()->domain()); if (!isv->contains(v)) { throw ResultUndefinedError(env, Location().introduce(), "function result violates function type-inst"); } } } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalFloatVal : public EvalBase { public: typedef FloatVal Val; typedef FloatVal ArrayVal; static FloatVal e(EnvI& env, Expression* e) { return eval_float(env, e); } static Expression* exp(FloatVal e) { return FloatLit::a(e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { if ((fi->ti()->domain() != nullptr) && !fi->ti()->domain()->isa()) { FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); if (!fsv->contains(v)) { throw ResultUndefinedError(env, Location().introduce(), "function result violates function type-inst"); } } } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalFloatLit : public EvalBase { public: typedef FloatLit* Val; typedef Expression* ArrayVal; static FloatLit* e(EnvI& env, Expression* e) { return FloatLit::a(eval_float(env, e)); } static Expression* exp(Expression* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalString : public EvalBase { public: typedef std::string Val; typedef std::string ArrayVal; static std::string e(EnvI& env, Expression* e) { return eval_string(env, e); } static Expression* exp(const std::string& e) { return new StringLit(Location(), e); } static void checkRetVal(EnvI& env, const Val& v, FunctionI* fi) {} static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalStringLit : public EvalBase { public: typedef StringLit* Val; typedef Expression* ArrayVal; static StringLit* e(EnvI& env, Expression* e) { return new StringLit(Location(), eval_string(env, e)); } static Expression* exp(Expression* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalBoolLit : public EvalBase { public: typedef BoolLit* Val; typedef Expression* ArrayVal; static BoolLit* e(EnvI& env, Expression* e) { return constants().boollit(eval_bool(env, e)); } static Expression* exp(Expression* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalBoolVal : public EvalBase { public: typedef bool Val; static bool e(EnvI& env, Expression* e) { return eval_bool(env, e); } static Expression* exp(bool e) { return constants().boollit(e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalArrayLit : public EvalBase { public: typedef ArrayLit* Val; typedef Expression* ArrayVal; static ArrayLit* e(EnvI& env, Expression* e) { return eval_array_lit(env, e); } static Expression* exp(Expression* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalArrayLitCopy : public EvalBase { public: typedef ArrayLit* Val; typedef Expression* ArrayVal; static ArrayLit* e(EnvI& env, Expression* e) { return copy(env, eval_array_lit(env, e), true)->cast(); } static Expression* exp(Expression* e) { return e; } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { for (unsigned int i = 0; i < fi->ti()->ranges().size(); i++) { if ((fi->ti()->ranges()[i]->domain() != nullptr) && !fi->ti()->ranges()[i]->domain()->isa()) { IntSetVal* isv = eval_intset(env, fi->ti()->ranges()[i]->domain()); bool bothEmpty = isv->min() > isv->max() && v->min(i) > v->max(i); if (!bothEmpty && (v->min(i) != isv->min() || v->max(i) != isv->max())) { std::ostringstream oss; oss << "array index set " << (i + 1) << " of function result violates function type-inst"; throw ResultUndefinedError(env, fi->e()->loc(), oss.str()); } } } if ((fi->ti()->domain() != nullptr) && !fi->ti()->domain()->isa() && fi->ti()->type().ti() == Type::TI_PAR) { Type base_t = fi->ti()->type(); if (base_t.bt() == Type::BT_INT) { IntSetVal* isv = eval_intset(env, fi->ti()->domain()); if (base_t.st() == Type::ST_PLAIN) { for (unsigned int i = 0; i < v->size(); i++) { IntVal iv = eval_int(env, (*v)[i]); if (!isv->contains(iv)) { std::ostringstream oss; oss << "array contains value " << iv << " which is not contained in " << *isv; throw ResultUndefinedError( env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); } } } else { for (unsigned int i = 0; i < v->size(); i++) { IntSetVal* iv = eval_intset(env, (*v)[i]); IntSetRanges isv_r(isv); IntSetRanges v_r(iv); if (!Ranges::subset(v_r, isv_r)) { std::ostringstream oss; oss << "array contains value " << *iv << " which is not a subset of " << *isv; throw ResultUndefinedError( env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); } } } } else if (base_t.bt() == Type::BT_FLOAT) { FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); if (base_t.st() == Type::ST_PLAIN) { for (unsigned int i = 0; i < v->size(); i++) { FloatVal fv = eval_float(env, (*v)[i]); if (!fsv->contains(fv)) { std::ostringstream oss; oss << "array contains value " << fv << " which is not contained in " << *fsv; throw ResultUndefinedError( env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); } } } else { for (unsigned int i = 0; i < v->size(); i++) { FloatSetVal* fv = eval_floatset(env, (*v)[i]); FloatSetRanges fsv_r(fsv); FloatSetRanges v_r(fv); if (!Ranges::subset(v_r, fsv_r)) { std::ostringstream oss; oss << "array contains value " << *fv << " which is not a subset of " << *fsv; throw ResultUndefinedError( env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); } } } } } } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalIntSet : public EvalBase { public: typedef IntSetVal* Val; static IntSetVal* e(EnvI& env, Expression* e) { return eval_intset(env, e); } static Expression* exp(IntSetVal* e) { return new SetLit(Location(), e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { if ((fi->ti()->domain() != nullptr) && !fi->ti()->domain()->isa()) { IntSetVal* isv = eval_intset(env, fi->ti()->domain()); IntSetRanges isv_r(isv); IntSetRanges v_r(v); if (!Ranges::subset(v_r, isv_r)) { throw ResultUndefinedError(env, Location().introduce(), "function result violates function type-inst"); } } } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalFloatSet : public EvalBase { public: typedef FloatSetVal* Val; static FloatSetVal* e(EnvI& env, Expression* e) { return eval_floatset(env, e); } static Expression* exp(FloatSetVal* e) { return new SetLit(Location(), e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { if ((fi->ti()->domain() != nullptr) && !fi->ti()->domain()->isa()) { FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); FloatSetRanges fsv_r(fsv); FloatSetRanges v_r(v); if (!Ranges::subset(v_r, fsv_r)) { throw ResultUndefinedError(env, Location().introduce(), "function result violates function type-inst"); } } } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalBoolSet : public EvalBase { public: typedef IntSetVal* Val; static IntSetVal* e(EnvI& env, Expression* e) { return eval_boolset(env, e); } static Expression* exp(IntSetVal* e) { auto* sl = new SetLit(Location(), e); sl->type(Type::parsetbool()); return sl; } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalSetLit : public EvalBase { public: typedef SetLit* Val; typedef Expression* ArrayVal; static SetLit* e(EnvI& env, Expression* e) { return eval_set_lit(env, e); } static Expression* exp(Expression* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalFloatSetLit : public EvalBase { public: typedef SetLit* Val; typedef Expression* ArrayVal; static SetLit* e(EnvI& env, Expression* e) { return new SetLit(e->loc(), eval_floatset(env, e)); } static Expression* exp(Expression* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalBoolSetLit : public EvalBase { public: typedef SetLit* Val; typedef Expression* ArrayVal; static SetLit* e(EnvI& env, Expression* e) { auto* sl = new SetLit(e->loc(), eval_boolset(env, e)); sl->type(Type::parsetbool()); return sl; } static Expression* exp(Expression* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalCopy : public EvalBase { public: typedef Expression* Val; typedef Expression* ArrayVal; static Expression* e(EnvI& env, Expression* e) { return copy(env, e, true); } static Expression* exp(Expression* e) { return e; } static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalPar : public EvalBase { public: typedef Expression* Val; typedef Expression* ArrayVal; static Expression* e(EnvI& env, Expression* e) { return eval_par(env, e); } static Expression* exp(Expression* e) { return e; } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} static Expression* flatten(EnvI& /*env*/, Expression* /*e*/) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; void check_dom(EnvI& env, Id* arg, IntSetVal* dom, Expression* e) { bool oob = false; if (!e->type().isOpt()) { if (e->type().isIntSet()) { IntSetVal* ev = eval_intset(env, e); IntSetRanges ev_r(ev); IntSetRanges dom_r(dom); oob = !Ranges::subset(ev_r, dom_r); } else { oob = !dom->contains(eval_int(env, e)); } } if (oob) { std::ostringstream oss; oss << "value for argument `" << *arg << "' out of bounds"; throw EvalError(env, e->loc(), oss.str()); } } void check_dom(EnvI& env, Id* arg, FloatVal dom_min, FloatVal dom_max, Expression* e) { if (!e->type().isOpt()) { FloatVal ev = eval_float(env, e); if (ev < dom_min || ev > dom_max) { std::ostringstream oss; oss << "value for argument `" << *arg << "' out of bounds"; throw EvalError(env, e->loc(), oss.str()); } } } template typename Eval::Val eval_call(EnvI& env, CallClass* ce) { std::vector previousParameters(ce->decl()->params().size()); std::vector params(ce->decl()->params().size()); for (unsigned int i = 0; i < ce->decl()->params().size(); i++) { params[i] = eval_par(env, ce->arg(i)); } for (unsigned int i = ce->decl()->params().size(); i--;) { VarDecl* vd = ce->decl()->params()[i]; if (vd->type().dim() > 0) { // Check array index sets auto* al = params[i]->cast(); for (unsigned int j = 0; j < vd->ti()->ranges().size(); j++) { TypeInst* range_ti = vd->ti()->ranges()[j]; if (range_ti->domain() && !range_ti->domain()->isa()) { IntSetVal* isv = eval_intset(env, range_ti->domain()); if (isv->min() != al->min(j) || isv->max() != al->max(j)) { std::ostringstream oss; oss << "array index set " << (j + 1) << " of argument " << (i + 1) << " does not match declared index set"; throw EvalError(env, ce->loc(), oss.str()); } } } } previousParameters[i] = vd->e(); vd->flat(vd); vd->e(params[i]); if (vd->e()->type().isPar()) { if (Expression* dom = vd->ti()->domain()) { if (!dom->isa()) { if (vd->e()->type().bt() == Type::BT_INT) { IntSetVal* isv = eval_intset(env, dom); if (vd->e()->type().dim() > 0) { ArrayLit* al = eval_array_lit(env, vd->e()); for (unsigned int i = 0; i < al->size(); i++) { check_dom(env, vd->id(), isv, (*al)[i]); } } else { check_dom(env, vd->id(), isv, vd->e()); } } else if (vd->e()->type().bt() == Type::BT_FLOAT) { GCLock lock; FloatSetVal* fsv = eval_floatset(env, dom); FloatVal dom_min = fsv->min(); FloatVal dom_max = fsv->max(); check_dom(env, vd->id(), dom_min, dom_max, vd->e()); } } } } } typename Eval::Val ret = Eval::e(env, ce->decl()->e()); Eval::checkRetVal(env, ret, ce->decl()); for (unsigned int i = ce->decl()->params().size(); i--;) { VarDecl* vd = ce->decl()->params()[i]; vd->e(previousParameters[i]); vd->flat(vd->e() ? vd : nullptr); } return ret; } ArrayLit* eval_array_comp(EnvI& env, Comprehension* e) { ArrayLit* ret; if (e->type() == Type::parint(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else if (e->type() == Type::parbool(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else if (e->type() == Type::parfloat(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else if (e->type().st() == Type::ST_SET) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else if (e->type() == Type::parstring(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } ret->type(e->type()); return ret; } ArrayLit* eval_array_lit(EnvI& env, Expression* e) { CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_BOOLLIT: case Expression::E_STRINGLIT: case Expression::E_SETLIT: case Expression::E_ANON: case Expression::E_TI: case Expression::E_TIID: case Expression::E_VARDECL: throw EvalError(env, e->loc(), "not an array expression"); case Expression::E_ID: return eval_id(env, e); case Expression::E_ARRAYLIT: return e->cast(); case Expression::E_ARRAYACCESS: throw EvalError(env, e->loc(), "arrays of arrays not supported"); case Expression::E_COMP: return eval_array_comp(env, e->cast()); case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->ifExpr(i))) { return eval_array_lit(env, ite->thenExpr(i)); } } return eval_array_lit(env, ite->elseExpr()); } case Expression::E_BINOP: { auto* bo = e->cast(); if (bo->op() == BOT_PLUSPLUS) { ArrayLit* al0 = eval_array_lit(env, bo->lhs()); ArrayLit* al1 = eval_array_lit(env, bo->rhs()); std::vector v(al0->size() + al1->size()); for (unsigned int i = al0->size(); (i--) != 0U;) { v[i] = (*al0)[i]; } for (unsigned int i = al1->size(); (i--) != 0U;) { v[al0->size() + i] = (*al1)[i]; } auto* ret = new ArrayLit(e->loc(), v); ret->flat(al0->flat() && al1->flat()); ret->type(e->type()); return ret; } if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } throw EvalError(env, e->loc(), "not an array expression", bo->opToString()); } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not an array expression"); } case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == nullptr) { throw EvalError(env, e->loc(), "undeclared function", ce->id()); } if (ce->decl()->builtins.e != nullptr) { return eval_array_lit(env, ce->decl()->builtins.e(env, ce)); } if (ce->decl()->e() == nullptr) { std::ostringstream ss; ss << "internal error: missing builtin '" << ce->id() << "'"; throw EvalError(env, ce->loc(), ss.str()); } return eval_call(env, ce); } case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); check_par_declaration(env, vdi); } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } ArrayLit* l_in = eval_array_lit(env, l->in()); auto* ret = copy(env, l_in, true)->cast(); ret->flat(l_in->flat()); l->popbindings(); return ret; } } assert(false); return nullptr; } Expression* eval_arrayaccess(EnvI& env, ArrayLit* al, const std::vector& idx, bool& success) { success = true; assert(al->dims() == idx.size()); IntVal realidx = 0; int realdim = 1; for (int i = 0; i < al->dims(); i++) { realdim *= al->max(i) - al->min(i) + 1; } for (int i = 0; i < al->dims(); i++) { IntVal ix = idx[i]; if (ix < al->min(i) || ix > al->max(i)) { success = false; Type t = al->type(); t.dim(0); if (t.isint()) { return IntLit::a(0); } if (t.isbool()) { return constants().literalFalse; } if (t.isfloat()) { return FloatLit::a(0.0); } if (t.st() == Type::ST_SET || t.isbot()) { auto* ret = new SetLit(Location(), std::vector()); ret->type(t); return ret; } if (t.isstring()) { return new StringLit(Location(), ""); } throw EvalError(env, al->loc(), "Internal error: unexpected type in array access expression"); } realdim /= al->max(i) - al->min(i) + 1; realidx += (ix - al->min(i)) * realdim; } assert(realidx >= 0 && realidx <= al->size()); return (*al)[static_cast(realidx.toInt())]; } Expression* eval_arrayaccess(EnvI& env, ArrayAccess* e, bool& success) { ArrayLit* al = eval_array_lit(env, e->v()); std::vector dims(e->idx().size()); for (unsigned int i = e->idx().size(); (i--) != 0U;) { dims[i] = eval_int(env, e->idx()[i]); } return eval_arrayaccess(env, al, dims, success); } Expression* eval_arrayaccess(EnvI& env, ArrayAccess* e) { bool success; Expression* ret = eval_arrayaccess(env, e, success); if (success) { return ret; } throw ResultUndefinedError(env, e->loc(), "array access out of bounds"); } SetLit* eval_set_lit(EnvI& env, Expression* e) { switch (e->type().bt()) { case Type::BT_INT: case Type::BT_BOT: return new SetLit(e->loc(), eval_intset(env, e)); case Type::BT_BOOL: { auto* sl = new SetLit(e->loc(), eval_boolset(env, e)); sl->type(Type::parsetbool()); return sl; } case Type::BT_FLOAT: return new SetLit(e->loc(), eval_floatset(env, e)); default: throw InternalError("invalid set literal type"); } } IntSetVal* eval_intset(EnvI& env, Expression* e) { if (auto* sl = e->dynamicCast()) { if (sl->isv() != nullptr) { return sl->isv(); } } CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_SETLIT: { auto* sl = e->cast(); std::vector vals; for (unsigned int i = 0; i < sl->v().size(); i++) { Expression* vi = eval_par(env, sl->v()[i]); if (vi != constants().absent) { vals.push_back(eval_int(env, vi)); } } return IntSetVal::a(vals); } case Expression::E_BOOLLIT: case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a set of int expression"); break; case Expression::E_ARRAYLIT: { auto* al = e->cast(); std::vector vals(al->size()); for (unsigned int i = 0; i < al->size(); i++) { vals[i] = eval_int(env, (*al)[i]); } return IntSetVal::a(vals); } break; case Expression::E_COMP: { auto* c = e->cast(); std::vector a = eval_comp(env, c); return IntSetVal::a(a); } case Expression::E_ID: { GCLock lock; return eval_id(env, e)->isv(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_intset(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->ifExpr(i))) { return eval_intset(env, ite->thenExpr(i)); } } return eval_intset(env, ite->elseExpr()); } break; case Expression::E_BINOP: { auto* bo = e->cast(); if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } Expression* lhs = eval_par(env, bo->lhs()); Expression* rhs = eval_par(env, bo->rhs()); if (lhs->type().isIntSet() && rhs->type().isIntSet()) { IntSetVal* v0 = eval_intset(env, lhs); IntSetVal* v1 = eval_intset(env, rhs); IntSetRanges ir0(v0); IntSetRanges ir1(v1); switch (bo->op()) { case BOT_UNION: { Ranges::Union u(ir0, ir1); return IntSetVal::ai(u); } case BOT_DIFF: { Ranges::Diff u(ir0, ir1); return IntSetVal::ai(u); } case BOT_SYMDIFF: { Ranges::Union u(ir0, ir1); Ranges::Inter i(ir0, ir1); Ranges::Diff, Ranges::Inter> sd(u, i); return IntSetVal::ai(sd); } case BOT_INTERSECT: { Ranges::Inter u(ir0, ir1); return IntSetVal::ai(u); } default: throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); } } else if (lhs->type().isint() && rhs->type().isint()) { if (bo->op() != BOT_DOTDOT) { throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); } return IntSetVal::a(eval_int(env, lhs), eval_int(env, rhs)); } else { throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not a set of int expression"); } case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == nullptr) { throw EvalError(env, e->loc(), "undeclared function", ce->id()); } if (ce->decl()->builtins.s != nullptr) { return ce->decl()->builtins.s(env, ce); } if (ce->decl()->builtins.e != nullptr) { return eval_intset(env, ce->decl()->builtins.e(env, ce)); } if (ce->decl()->e() == nullptr) { std::ostringstream ss; ss << "internal error: missing builtin '" << ce->id() << "'"; throw EvalError(env, ce->loc(), ss.str()); } return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); check_par_declaration(env, vdi); } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } IntSetVal* ret = eval_intset(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return nullptr; } } FloatSetVal* eval_floatset(EnvI& env, Expression* e) { if (auto* sl = e->dynamicCast()) { if (sl->fsv() != nullptr) { return sl->fsv(); } if (sl->isv() != nullptr) { IntSetRanges isr(sl->isv()); return FloatSetVal::ai(isr); } } CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_SETLIT: { auto* sl = e->cast(); std::vector vals; for (unsigned int i = 0; i < sl->v().size(); i++) { Expression* vi = eval_par(env, sl->v()[i]); if (vi != constants().absent) { vals.push_back(eval_float(env, vi)); } } return FloatSetVal::a(vals); } case Expression::E_BOOLLIT: case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a set of float expression"); break; case Expression::E_ARRAYLIT: { auto* al = e->cast(); std::vector vals(al->size()); for (unsigned int i = 0; i < al->size(); i++) { vals[i] = eval_float(env, (*al)[i]); } return FloatSetVal::a(vals); } break; case Expression::E_COMP: { auto* c = e->cast(); std::vector a = eval_comp(env, c); return FloatSetVal::a(a); } case Expression::E_ID: { GCLock lock; return eval_floatset(env, eval_id(env, e)); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_floatset(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->ifExpr(i))) { return eval_floatset(env, ite->thenExpr(i)); } } return eval_floatset(env, ite->elseExpr()); } break; case Expression::E_BINOP: { auto* bo = e->cast(); if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } Expression* lhs = eval_par(env, bo->lhs()); Expression* rhs = eval_par(env, bo->rhs()); if (lhs->type().isFloatSet() && rhs->type().isFloatSet()) { FloatSetVal* v0 = eval_floatset(env, lhs); FloatSetVal* v1 = eval_floatset(env, rhs); FloatSetRanges fr0(v0); FloatSetRanges fr1(v1); switch (bo->op()) { case BOT_UNION: { Ranges::Union u(fr0, fr1); return FloatSetVal::ai(u); } case BOT_DIFF: { Ranges::Diff u(fr0, fr1); return FloatSetVal::ai(u); } case BOT_SYMDIFF: { Ranges::Union u(fr0, fr1); Ranges::Inter i(fr0, fr1); Ranges::Diff, Ranges::Inter> sd(u, i); return FloatSetVal::ai(sd); } case BOT_INTERSECT: { Ranges::Inter u(fr0, fr1); return FloatSetVal::ai(u); } default: throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); } } else if (lhs->type().isfloat() && rhs->type().isfloat()) { if (bo->op() != BOT_DOTDOT) { throw EvalError(env, e->loc(), "not a set of float expression", bo->opToString()); } return FloatSetVal::a(eval_float(env, lhs), eval_float(env, rhs)); } else { throw EvalError(env, e->loc(), "not a set of float expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not a set of float expression"); } case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == nullptr) { throw EvalError(env, e->loc(), "undeclared function", ce->id()); } if (ce->decl()->builtins.e != nullptr) { return eval_floatset(env, ce->decl()->builtins.e(env, ce)); } if (ce->decl()->e() == nullptr) { std::ostringstream ss; ss << "internal error: missing builtin '" << ce->id() << "'"; throw EvalError(env, ce->loc(), ss.str()); } return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); check_par_declaration(env, vdi); } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } FloatSetVal* ret = eval_floatset(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return nullptr; } } bool eval_bool(EnvI& env, Expression* e) { CallStackItem csi(env, e); try { if (auto* bl = e->dynamicCast()) { return bl->v(); } switch (e->eid()) { case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_SETLIT: case Expression::E_ARRAYLIT: case Expression::E_COMP: case Expression::E_VARDECL: case Expression::E_TI: assert(false); throw EvalError(env, e->loc(), "not a bool expression"); break; case Expression::E_ID: { GCLock lock; return eval_id(env, e)->v(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_bool(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->ifExpr(i))) { return eval_bool(env, ite->thenExpr(i)); } } return eval_bool(env, ite->elseExpr()); } break; case Expression::E_BINOP: { auto* bo = e->cast(); Expression* lhs = bo->lhs(); if (lhs->type().bt() == Type::BT_TOP) { lhs = eval_par(env, lhs); } Expression* rhs = bo->rhs(); if (rhs->type().bt() == Type::BT_TOP) { rhs = eval_par(env, rhs); } if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } if (lhs->type().isbool() && rhs->type().isbool()) { try { switch (bo->op()) { case BOT_LE: return static_cast(eval_bool(env, lhs)) < static_cast(eval_bool(env, rhs)); case BOT_LQ: return static_cast(eval_bool(env, lhs)) <= static_cast(eval_bool(env, rhs)); case BOT_GR: return static_cast(eval_bool(env, lhs)) > static_cast(eval_bool(env, rhs)); case BOT_GQ: return static_cast(eval_bool(env, lhs)) >= static_cast(eval_bool(env, rhs)); case BOT_EQ: return eval_bool(env, lhs) == eval_bool(env, rhs); case BOT_NQ: return eval_bool(env, lhs) != eval_bool(env, rhs); case BOT_EQUIV: return eval_bool(env, lhs) == eval_bool(env, rhs); case BOT_IMPL: return (!eval_bool(env, lhs)) || eval_bool(env, rhs); case BOT_RIMPL: return (!eval_bool(env, rhs)) || eval_bool(env, lhs); case BOT_OR: return eval_bool(env, lhs) || eval_bool(env, rhs); case BOT_AND: return eval_bool(env, lhs) && eval_bool(env, rhs); case BOT_XOR: return eval_bool(env, lhs) ^ eval_bool(env, rhs); default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isint() && rhs->type().isint()) { try { IntVal v0 = eval_int(env, lhs); IntVal v1 = eval_int(env, rhs); switch (bo->op()) { case BOT_LE: return v0 < v1; case BOT_LQ: return v0 <= v1; case BOT_GR: return v0 > v1; case BOT_GQ: return v0 >= v1; case BOT_EQ: return v0 == v1; case BOT_NQ: return v0 != v1; default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isfloat() && rhs->type().isfloat()) { try { FloatVal v0 = eval_float(env, lhs); FloatVal v1 = eval_float(env, rhs); switch (bo->op()) { case BOT_LE: return v0 < v1; case BOT_LQ: return v0 <= v1; case BOT_GR: return v0 > v1; case BOT_GQ: return v0 >= v1; case BOT_EQ: return v0 == v1; case BOT_NQ: return v0 != v1; default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isint() && rhs->type().isIntSet()) { try { IntVal v0 = eval_int(env, lhs); GCLock lock; IntSetVal* v1 = eval_intset(env, rhs); switch (bo->op()) { case BOT_IN: return v1->contains(v0); default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isfloat() && rhs->type().isFloatSet()) { try { FloatVal v0 = eval_float(env, lhs); GCLock lock; FloatSetVal* v1 = eval_floatset(env, rhs); switch (bo->op()) { case BOT_IN: return v1->contains(v0); default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isSet() && rhs->type().isSet()) { try { GCLock lock; IntSetVal* v0 = eval_intset(env, lhs); IntSetVal* v1 = eval_intset(env, rhs); IntSetRanges ir0(v0); IntSetRanges ir1(v1); switch (bo->op()) { case BOT_LE: return Ranges::less(ir0, ir1); case BOT_LQ: return Ranges::less_eq(ir0, ir1); case BOT_GR: return Ranges::less(ir1, ir0); case BOT_GQ: return Ranges::less_eq(ir1, ir0); case BOT_EQ: return Ranges::equal(ir0, ir1); case BOT_NQ: return !Ranges::equal(ir0, ir1); case BOT_SUBSET: return Ranges::subset(ir0, ir1); case BOT_SUPERSET: return Ranges::subset(ir1, ir0); default: throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isstring() && rhs->type().isstring()) { try { GCLock lock; std::string s0 = eval_string(env, lhs); std::string s1 = eval_string(env, rhs); switch (bo->op()) { case BOT_EQ: return s0 == s1; case BOT_NQ: return s0 != s1; case BOT_LE: return s0 < s1; case BOT_LQ: return s0 <= s1; case BOT_GR: return s0 > s1; case BOT_GQ: return s0 >= s1; default: throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (bo->op() == BOT_EQ && lhs->type().isAnn()) { return Expression::equal(lhs, rhs); } else if (bo->op() == BOT_EQ && lhs->type().dim() > 0 && rhs->type().dim() > 0) { try { ArrayLit* al0 = eval_array_lit(env, lhs); ArrayLit* al1 = eval_array_lit(env, rhs); if (al0->size() != al1->size()) { return false; } for (unsigned int i = 0; i < al0->size(); i++) { if (!Expression::equal(eval_par(env, (*al0)[i]), eval_par(env, (*al1)[i]))) { return false; } } return true; } catch (ResultUndefinedError&) { return false; } } else { throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } bool v0 = eval_bool(env, uo->e()); switch (uo->op()) { case UOT_NOT: return !v0; default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", uo->opToString()); } } break; case Expression::E_CALL: { try { Call* ce = e->cast(); if (ce->decl() == nullptr) { throw EvalError(env, e->loc(), "undeclared function", ce->id()); } if (ce->decl()->builtins.b != nullptr) { return ce->decl()->builtins.b(env, ce); } if (ce->decl()->builtins.e != nullptr) { return eval_bool(env, ce->decl()->builtins.e(env, ce)); } if (ce->decl()->e() == nullptr) { std::ostringstream ss; ss << "internal error: missing builtin '" << ce->id() << "'"; throw EvalError(env, ce->loc(), ss.str()); } return eval_call(env, ce); } catch (ResultUndefinedError&) { return false; } } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); bool ret = true; for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); bool maybe_partial = vdi->ann().contains(constants().ann.maybe_partial); if (maybe_partial) { env.inMaybePartial++; } try { check_par_declaration(env, vdi); } catch (ResultUndefinedError&) { ret = false; } if (maybe_partial) { env.inMaybePartial--; } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is false. if (!eval_bool(env, l->let()[i])) { if (l->let()[i]->ann().contains(constants().ann.maybe_partial)) { ret = false; } else { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } } ret = ret && eval_bool(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return false; } } catch (ResultUndefinedError&) { // undefined means false return false; } } IntSetVal* eval_boolset(EnvI& env, Expression* e) { CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_SETLIT: { auto* sl = e->cast(); if (sl->isv() != nullptr) { return sl->isv(); } std::vector vals; for (unsigned int i = 0; i < sl->v().size(); i++) { Expression* vi = eval_par(env, sl->v()[i]); if (vi != constants().absent) { vals.push_back(eval_int(env, vi)); } } return IntSetVal::a(vals); } case Expression::E_BOOLLIT: case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a set of bool expression"); break; case Expression::E_ARRAYLIT: { auto* al = e->cast(); std::vector vals(al->size()); for (unsigned int i = 0; i < al->size(); i++) { vals[i] = static_cast(eval_bool(env, (*al)[i])); } return IntSetVal::a(vals); } break; case Expression::E_COMP: { auto* c = e->cast(); std::vector a = eval_comp(env, c); return IntSetVal::a(a); } case Expression::E_ID: { GCLock lock; return eval_id(env, e)->isv(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_boolset(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->ifExpr(i))) { return eval_boolset(env, ite->thenExpr(i)); } } return eval_boolset(env, ite->elseExpr()); } break; case Expression::E_BINOP: { auto* bo = e->cast(); if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } Expression* lhs = eval_par(env, bo->lhs()); Expression* rhs = eval_par(env, bo->rhs()); if (lhs->type().isIntSet() && rhs->type().isIntSet()) { IntSetVal* v0 = eval_boolset(env, lhs); IntSetVal* v1 = eval_boolset(env, rhs); IntSetRanges ir0(v0); IntSetRanges ir1(v1); switch (bo->op()) { case BOT_UNION: { Ranges::Union u(ir0, ir1); return IntSetVal::ai(u); } case BOT_DIFF: { Ranges::Diff u(ir0, ir1); return IntSetVal::ai(u); } case BOT_SYMDIFF: { Ranges::Union u(ir0, ir1); Ranges::Inter i(ir0, ir1); Ranges::Diff, Ranges::Inter> sd(u, i); return IntSetVal::ai(sd); } case BOT_INTERSECT: { Ranges::Inter u(ir0, ir1); return IntSetVal::ai(u); } default: throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); } } else if (lhs->type().isbool() && rhs->type().isbool()) { if (bo->op() != BOT_DOTDOT) { throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); } return IntSetVal::a(static_cast(eval_bool(env, lhs)), static_cast(eval_bool(env, rhs))); } else { throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not a set of bool expression"); } case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == nullptr) { throw EvalError(env, e->loc(), "undeclared function", ce->id()); } if (ce->decl()->builtins.s != nullptr) { return ce->decl()->builtins.s(env, ce); } if (ce->decl()->builtins.e != nullptr) { return eval_boolset(env, ce->decl()->builtins.e(env, ce)); } if (ce->decl()->e() == nullptr) { std::ostringstream ss; ss << "internal error: missing builtin '" << ce->id() << "'"; throw EvalError(env, ce->loc(), ss.str()); } return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); check_par_declaration(env, vdi); } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } IntSetVal* ret = eval_boolset(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return nullptr; } } IntVal eval_int(EnvI& env, Expression* e) { if (e->type().isbool()) { return static_cast(eval_bool(env, e)); } if (auto* il = e->dynamicCast()) { return il->v(); } CallStackItem csi(env, e); try { switch (e->eid()) { case Expression::E_FLOATLIT: case Expression::E_BOOLLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_SETLIT: case Expression::E_ARRAYLIT: case Expression::E_COMP: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not an integer expression"); break; case Expression::E_ID: { GCLock lock; return eval_id(env, e)->v(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_int(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->ifExpr(i))) { return eval_int(env, ite->thenExpr(i)); } } return eval_int(env, ite->elseExpr()); } break; case Expression::E_BINOP: { auto* bo = e->cast(); if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } IntVal v0 = eval_int(env, bo->lhs()); IntVal v1 = eval_int(env, bo->rhs()); switch (bo->op()) { case BOT_PLUS: return v0 + v1; case BOT_MINUS: return v0 - v1; case BOT_MULT: return v0 * v1; case BOT_POW: return v0.pow(v1); case BOT_IDIV: if (v1 == 0) { throw ResultUndefinedError(env, e->loc(), "division by zero"); } return v0 / v1; case BOT_MOD: if (v1 == 0) { throw ResultUndefinedError(env, e->loc(), "division by zero"); } return v0 % v1; default: throw EvalError(env, e->loc(), "not an integer expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } IntVal v0 = eval_int(env, uo->e()); switch (uo->op()) { case UOT_PLUS: return v0; case UOT_MINUS: return -v0; default: throw EvalError(env, e->loc(), "not an integer expression", uo->opToString()); } } break; case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == nullptr) { throw EvalError(env, e->loc(), "undeclared function", ce->id()); } if (ce->decl()->builtins.i != nullptr) { return ce->decl()->builtins.i(env, ce); } if (ce->decl()->builtins.e != nullptr) { return eval_int(env, ce->decl()->builtins.e(env, ce)); } if (ce->decl()->e() == nullptr) { std::ostringstream ss; ss << "internal error: missing builtin '" << ce->id() << "'"; throw EvalError(env, ce->loc(), ss.str()); } return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); check_par_declaration(env, vdi); } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } IntVal ret = eval_int(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return 0; } } catch (ArithmeticError& err) { throw EvalError(env, e->loc(), err.msg()); } } FloatVal eval_float(EnvI& env, Expression* e) { if (e->type().isint()) { return static_cast(eval_int(env, e).toInt()); } if (e->type().isbool()) { return static_cast(eval_bool(env, e)); } CallStackItem csi(env, e); try { if (auto* fl = e->dynamicCast()) { return fl->v(); } switch (e->eid()) { case Expression::E_INTLIT: case Expression::E_BOOLLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_SETLIT: case Expression::E_ARRAYLIT: case Expression::E_COMP: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a float expression"); break; case Expression::E_ID: { GCLock lock; return eval_id(env, e)->v(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_float(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->ifExpr(i))) { return eval_float(env, ite->thenExpr(i)); } } return eval_float(env, ite->elseExpr()); } break; case Expression::E_BINOP: { auto* bo = e->cast(); if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } FloatVal v0 = eval_float(env, bo->lhs()); FloatVal v1 = eval_float(env, bo->rhs()); switch (bo->op()) { case BOT_PLUS: return v0 + v1; case BOT_MINUS: return v0 - v1; case BOT_MULT: return v0 * v1; case BOT_POW: return std::pow(v0.toDouble(), v1.toDouble()); case BOT_DIV: if (v1 == 0.0) { throw ResultUndefinedError(env, e->loc(), "division by zero"); } return v0 / v1; default: throw EvalError(env, e->loc(), "not a float expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } FloatVal v0 = eval_float(env, uo->e()); switch (uo->op()) { case UOT_PLUS: return v0; case UOT_MINUS: return -v0; default: throw EvalError(env, e->loc(), "not a float expression", uo->opToString()); } } break; case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == nullptr) { throw EvalError(env, e->loc(), "undeclared function", ce->id()); } if (ce->decl()->builtins.f != nullptr) { return ce->decl()->builtins.f(env, ce); } if (ce->decl()->builtins.e != nullptr) { return eval_float(env, ce->decl()->builtins.e(env, ce)); } if (ce->decl()->e() == nullptr) { std::ostringstream ss; ss << "internal error: missing builtin '" << ce->id() << "'"; throw EvalError(env, ce->loc(), ss.str()); } return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); check_par_declaration(env, vdi); } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } FloatVal ret = eval_float(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return 0.0; } } catch (ArithmeticError& err) { throw EvalError(env, e->loc(), err.msg()); } } std::string eval_string(EnvI& env, Expression* e) { CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_STRINGLIT: { ASTString str = e->cast()->v(); return std::string(str.c_str(), str.size()); } case Expression::E_FLOATLIT: case Expression::E_INTLIT: case Expression::E_BOOLLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_SETLIT: case Expression::E_ARRAYLIT: case Expression::E_COMP: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a string expression"); break; case Expression::E_ID: { GCLock lock; ASTString str = eval_id(env, e)->v(); return std::string(str.c_str(), str.size()); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_string(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->ifExpr(i))) { return eval_string(env, ite->thenExpr(i)); } } return eval_string(env, ite->elseExpr()); } break; case Expression::E_BINOP: { auto* bo = e->cast(); if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } std::string v0 = eval_string(env, bo->lhs()); std::string v1 = eval_string(env, bo->rhs()); switch (bo->op()) { case BOT_PLUSPLUS: return v0 + v1; default: throw EvalError(env, e->loc(), "not a string expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not a string expression"); } break; case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == nullptr) { throw EvalError(env, e->loc(), "undeclared function", ce->id()); } if (ce->decl()->builtins.str != nullptr) { return ce->decl()->builtins.str(env, ce); } if (ce->decl()->builtins.e != nullptr) { return eval_string(env, ce->decl()->builtins.e(env, ce)); } if (ce->decl()->e() == nullptr) { std::ostringstream ss; ss << "internal error: missing builtin '" << ce->id() << "'"; throw EvalError(env, ce->loc(), ss.str()); } return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); check_par_declaration(env, vdi); } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } std::string ret = eval_string(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return ""; } } Expression* eval_par(EnvI& env, Expression* e) { if (e == nullptr) { return nullptr; } switch (e->eid()) { case Expression::E_ANON: case Expression::E_TIID: { return e; } case Expression::E_COMP: if (e->cast()->set()) { return EvalSetLit::e(env, e); } // fall through case Expression::E_ARRAYLIT: { ArrayLit* al = eval_array_lit(env, e); std::vector args(al->size()); for (unsigned int i = al->size(); (i--) != 0U;) { args[i] = eval_par(env, (*al)[i]); } std::vector> dims(al->dims()); for (unsigned int i = al->dims(); (i--) != 0U;) { dims[i].first = al->min(i); dims[i].second = al->max(i); } auto* ret = new ArrayLit(al->loc(), args, dims); Type t = al->type(); if (t.isbot() && ret->size() > 0) { t.bt((*ret)[0]->type().bt()); } ret->type(t); return ret; } case Expression::E_VARDECL: { auto* vd = e->cast(); throw EvalError(env, vd->loc(), "cannot evaluate variable declaration", vd->id()->v()); } case Expression::E_TI: { auto* t = e->cast(); ASTExprVec r; if (t->ranges().size() > 0) { std::vector rv(t->ranges().size()); for (unsigned int i = t->ranges().size(); (i--) != 0U;) { rv[i] = static_cast(eval_par(env, t->ranges()[i])); } r = ASTExprVec(rv); } return new TypeInst(Location(), t->type(), r, eval_par(env, t->domain())); } case Expression::E_ID: { if (e == constants().absent) { return e; } Id* id = e->cast(); if (id->decl() == nullptr) { throw EvalError(env, e->loc(), "undefined identifier", id->v()); } if (id->decl()->ti()->domain() != nullptr) { if (auto* bl = id->decl()->ti()->domain()->dynamicCast()) { return bl; } if (id->decl()->ti()->type().isint()) { if (auto* sl = id->decl()->ti()->domain()->dynamicCast()) { if ((sl->isv() != nullptr) && sl->isv()->min() == sl->isv()->max()) { return IntLit::a(sl->isv()->min()); } } } else if (id->decl()->ti()->type().isfloat()) { if (id->decl()->ti()->domain() != nullptr) { FloatSetVal* fsv = eval_floatset(env, id->decl()->ti()->domain()); if (fsv->min() == fsv->max()) { return FloatLit::a(fsv->min()); } } } } if (id->decl()->e() == nullptr) { return id; } return eval_par(env, id->decl()->e()); } case Expression::E_STRINGLIT: return e; default: { if (e->type().dim() != 0) { ArrayLit* al = eval_array_lit(env, e); std::vector args(al->size()); for (unsigned int i = al->size(); (i--) != 0U;) { args[i] = eval_par(env, (*al)[i]); } std::vector> dims(al->dims()); for (unsigned int i = al->dims(); (i--) != 0U;) { dims[i].first = al->min(i); dims[i].second = al->max(i); } auto* ret = new ArrayLit(al->loc(), args, dims); Type t = al->type(); if ((t.bt() == Type::BT_BOT || t.bt() == Type::BT_TOP) && ret->size() > 0) { t.bt((*ret)[0]->type().bt()); } ret->type(t); return ret; } if (e->type().isPar()) { if (e->type().isSet()) { return EvalSetLit::e(env, e); } if (e->type() == Type::parint()) { return EvalIntLit::e(env, e); } if (e->type() == Type::parbool()) { return EvalBoolLit::e(env, e); } if (e->type() == Type::parfloat()) { return EvalFloatLit::e(env, e); } if (e->type() == Type::parstring()) { return EvalStringLit::e(env, e); } } switch (e->eid()) { case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (ite->ifExpr(i)->type() == Type::parbool()) { if (eval_bool(env, ite->ifExpr(i))) { return eval_par(env, ite->thenExpr(i)); } } else { std::vector e_ifthen(ite->size() * 2); for (int i = 0; i < ite->size(); i++) { e_ifthen[2 * i] = eval_par(env, ite->ifExpr(i)); e_ifthen[2 * i + 1] = eval_par(env, ite->thenExpr(i)); } ITE* n_ite = new ITE(ite->loc(), e_ifthen, eval_par(env, ite->elseExpr())); n_ite->type(ite->type()); return n_ite; } } return eval_par(env, ite->elseExpr()); } case Expression::E_CALL: { Call* c = e->cast(); if (c->decl() != nullptr) { if (c->decl()->builtins.e != nullptr) { return eval_par(env, c->decl()->builtins.e(env, c)); } if (c->decl()->e() == nullptr) { if (c->id() == "deopt" && Expression::equal(c->arg(0), constants().absent)) { throw ResultUndefinedError(env, e->loc(), "deopt(<>) is undefined"); } return c; } return eval_call(env, c); } std::vector args(c->argCount()); for (unsigned int i = 0; i < args.size(); i++) { args[i] = eval_par(env, c->arg(i)); } Call* nc = new Call(c->loc(), c->id(), args); nc->type(c->type()); return nc; } case Expression::E_BINOP: { auto* bo = e->cast(); if ((bo->decl() != nullptr) && (bo->decl()->e() != nullptr)) { return eval_call(env, bo); } auto* nbo = new BinOp(e->loc(), eval_par(env, bo->lhs()), bo->op(), eval_par(env, bo->rhs())); nbo->type(bo->type()); return nbo; } case Expression::E_UNOP: { UnOp* uo = e->cast(); if ((uo->decl() != nullptr) && (uo->decl()->e() != nullptr)) { return eval_call(env, uo); } UnOp* nuo = new UnOp(e->loc(), uo->op(), eval_par(env, uo->e())); nuo->type(uo->type()); return nuo; } case Expression::E_ARRAYACCESS: { auto* aa = e->cast(); for (unsigned int i = 0; i < aa->idx().size(); i++) { if (!aa->idx()[i]->type().isPar()) { std::vector idx(aa->idx().size()); for (unsigned int j = 0; j < aa->idx().size(); j++) { idx[j] = eval_par(env, aa->idx()[j]); } auto* aa_new = new ArrayAccess(e->loc(), eval_par(env, aa->v()), idx); aa_new->type(aa->type()); return aa_new; } } return eval_par(env, eval_arrayaccess(env, aa)); } case Expression::E_LET: { Let* l = e->cast(); assert(l->type().isPar()); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (auto* vdi = l->let()[i]->dynamicCast()) { vdi->e(eval_par(env, vdi->e())); check_par_declaration(env, vdi); } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } Expression* ret = eval_par(env, l->in()); l->popbindings(); return ret; } default: return e; } } } } class ComputeIntBounds : public EVisitor { public: typedef std::pair Bounds; std::vector bounds; bool valid; EnvI& env; ComputeIntBounds(EnvI& env0) : valid(true), env(env0) {} bool enter(Expression* e) { if (e->type().isAnn()) { return false; } if (e->isa()) { return false; } if (e->type().dim() > 0) { return false; } if (e->type().isPar()) { if (e->type().isint()) { Expression* exp = eval_par(env, e); if (exp == constants().absent) { valid = false; } else { IntVal v = exp->cast()->v(); bounds.emplace_back(v, v); } } else { valid = false; } return false; } if (e->type().isint()) { if (ITE* ite = e->dynamicCast()) { Bounds itebounds(IntVal::infinity(), -IntVal::infinity()); for (int i = 0; i < ite->size(); i++) { if (ite->ifExpr(i)->type().isPar() && static_cast(ite->ifExpr(i)->type().cv()) == Type::CV_NO) { if (eval_bool(env, ite->ifExpr(i))) { BottomUpIterator cbi(*this); cbi.run(ite->thenExpr(i)); Bounds& back = bounds.back(); back.first = std::min(itebounds.first, back.first); back.second = std::max(itebounds.second, back.second); return false; } } else { BottomUpIterator cbi(*this); cbi.run(ite->thenExpr(i)); Bounds back = bounds.back(); bounds.pop_back(); itebounds.first = std::min(itebounds.first, back.first); itebounds.second = std::max(itebounds.second, back.second); } } BottomUpIterator cbi(*this); cbi.run(ite->elseExpr()); Bounds& back = bounds.back(); back.first = std::min(itebounds.first, back.first); back.second = std::max(itebounds.second, back.second); return false; } return true; } return false; } /// Visit integer literal void vIntLit(const IntLit& i) { bounds.emplace_back(i.v(), i.v()); } /// Visit floating point literal void vFloatLit(const FloatLit& /*f*/) { valid = false; bounds.emplace_back(0, 0); } /// Visit Boolean literal void vBoolLit(const BoolLit& /*b*/) { valid = false; bounds.emplace_back(0, 0); } /// Visit set literal void vSetLit(const SetLit& /*sl*/) { valid = false; bounds.emplace_back(0, 0); } /// Visit string literal void vStringLit(const StringLit& /*sl*/) { valid = false; bounds.emplace_back(0, 0); } /// Visit identifier void vId(const Id& id) { VarDecl* vd = id.decl(); while ((vd->flat() != nullptr) && vd->flat() != vd) { vd = vd->flat(); } if (vd->ti()->domain() != nullptr) { GCLock lock; IntSetVal* isv = eval_intset(env, vd->ti()->domain()); if (isv->size() == 0) { valid = false; bounds.emplace_back(0, 0); } else { bounds.emplace_back(isv->min(0), isv->max(isv->size() - 1)); } } else { if (vd->e() != nullptr) { BottomUpIterator cbi(*this); cbi.run(vd->e()); } else { bounds.emplace_back(-IntVal::infinity(), IntVal::infinity()); } } } /// Visit anonymous variable void vAnonVar(const AnonVar& /*v*/) { valid = false; bounds.emplace_back(0, 0); } /// Visit array literal void vArrayLit(const ArrayLit& /*al*/) {} /// Visit array access void vArrayAccess(ArrayAccess& aa) { bool parAccess = true; for (unsigned int i = aa.idx().size(); (i--) != 0U;) { bounds.pop_back(); if (!aa.idx()[i]->type().isPar()) { parAccess = false; } } if (Id* id = aa.v()->dynamicCast()) { while ((id->decl()->e() != nullptr) && id->decl()->e()->isa()) { id = id->decl()->e()->cast(); } if (parAccess && (id->decl()->e() != nullptr)) { bool success; Expression* e = eval_arrayaccess(env, &aa, success); if (success) { BottomUpIterator cbi(*this); cbi.run(e); return; } } if (id->decl()->ti()->domain() != nullptr) { GCLock lock; IntSetVal* isv = eval_intset(env, id->decl()->ti()->domain()); if (isv->size() > 0) { bounds.emplace_back(isv->min(0), isv->max(isv->size() - 1)); return; } } } valid = false; bounds.emplace_back(0, 0); } /// Visit array comprehension void vComprehension(const Comprehension& c) { valid = false; bounds.emplace_back(0, 0); } /// Visit if-then-else void vITE(const ITE& /*ite*/) { valid = false; bounds.emplace_back(0, 0); } /// Visit binary operator void vBinOp(const BinOp& bo) { Bounds b1 = bounds.back(); bounds.pop_back(); Bounds b0 = bounds.back(); bounds.pop_back(); if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || !b0.second.isFinite()) { valid = false; bounds.emplace_back(0, 0); } else { switch (bo.op()) { case BOT_PLUS: bounds.emplace_back(b0.first + b1.first, b0.second + b1.second); break; case BOT_MINUS: bounds.emplace_back(b0.first - b1.second, b0.second - b1.first); break; case BOT_MULT: { IntVal x0 = b0.first * b1.first; IntVal x1 = b0.first * b1.second; IntVal x2 = b0.second * b1.first; IntVal x3 = b0.second * b1.second; IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); bounds.emplace_back(m, n); } break; case BOT_IDIV: { IntVal b0f = b0.first == 0 ? 1 : b0.first; IntVal b0s = b0.second == 0 ? -1 : b0.second; IntVal b1f = b1.first == 0 ? 1 : b1.first; IntVal b1s = b1.second == 0 ? -1 : b1.second; IntVal x0 = b0f / b1f; IntVal x1 = b0f / b1s; IntVal x2 = b0s / b1f; IntVal x3 = b0s / b1s; IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); bounds.emplace_back(m, n); } break; case BOT_MOD: { IntVal b0f = b0.first == 0 ? 1 : b0.first; IntVal b0s = b0.second == 0 ? -1 : b0.second; IntVal b1f = b1.first == 0 ? 1 : b1.first; IntVal b1s = b1.second == 0 ? -1 : b1.second; IntVal x0 = b0f % b1f; IntVal x1 = b0f % b1s; IntVal x2 = b0s % b1f; IntVal x3 = b0s % b1s; IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); bounds.emplace_back(m, n); } break; case BOT_POW: { IntVal exp_min = std::min(0, b1.first); IntVal exp_max = std::min(0, b1.second); IntVal x0 = b0.first.pow(exp_min); IntVal x1 = b0.first.pow(exp_max); IntVal x2 = b0.second.pow(exp_min); IntVal x3 = b0.second.pow(exp_max); IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); bounds.emplace_back(m, n); } break; case BOT_DIV: case BOT_LE: case BOT_LQ: case BOT_GR: case BOT_GQ: case BOT_EQ: case BOT_NQ: case BOT_IN: case BOT_SUBSET: case BOT_SUPERSET: case BOT_UNION: case BOT_DIFF: case BOT_SYMDIFF: case BOT_INTERSECT: case BOT_PLUSPLUS: case BOT_EQUIV: case BOT_IMPL: case BOT_RIMPL: case BOT_OR: case BOT_AND: case BOT_XOR: case BOT_DOTDOT: valid = false; bounds.emplace_back(0, 0); } } } /// Visit unary operator void vUnOp(const UnOp& uo) { switch (uo.op()) { case UOT_PLUS: break; case UOT_MINUS: bounds.back().first = -bounds.back().first; bounds.back().second = -bounds.back().second; std::swap(bounds.back().first, bounds.back().second); break; case UOT_NOT: valid = false; } } /// Visit call void vCall(Call& c) { if (c.id() == constants().ids.lin_exp || c.id() == constants().ids.sum) { bool le = c.id() == constants().ids.lin_exp; ArrayLit* coeff = le ? eval_array_lit(env, c.arg(0)) : nullptr; if (c.arg(le ? 1 : 0)->type().isOpt()) { valid = false; bounds.emplace_back(0, 0); return; } ArrayLit* al = eval_array_lit(env, c.arg(le ? 1 : 0)); if (le) { bounds.pop_back(); // remove constant (third arg) from stack } IntVal d = le ? c.arg(2)->cast()->v() : 0; int stacktop = static_cast(bounds.size()); for (unsigned int i = al->size(); (i--) != 0U;) { BottomUpIterator cbi(*this); cbi.run((*al)[i]); if (!valid) { for (unsigned int j = al->size() - 1; j > i; j--) { bounds.pop_back(); } return; } } assert(stacktop + al->size() == bounds.size()); IntVal lb = d; IntVal ub = d; for (unsigned int i = 0; i < al->size(); i++) { Bounds b = bounds.back(); bounds.pop_back(); IntVal cv = le ? eval_int(env, (*coeff)[i]) : 1; if (cv > 0) { if (b.first.isFinite()) { if (lb.isFinite()) { lb += cv * b.first; } } else { lb = b.first; } if (b.second.isFinite()) { if (ub.isFinite()) { ub += cv * b.second; } } else { ub = b.second; } } else { if (b.second.isFinite()) { if (lb.isFinite()) { lb += cv * b.second; } } else { lb = -b.second; } if (b.first.isFinite()) { if (ub.isFinite()) { ub += cv * b.first; } } else { ub = -b.first; } } } bounds.emplace_back(lb, ub); } else if (c.id() == "card") { if (IntSetVal* isv = compute_intset_bounds(env, c.arg(0))) { IntSetRanges isr(isv); bounds.emplace_back(0, Ranges::cardinality(isr)); } else { valid = false; bounds.emplace_back(0, 0); } } else if (c.id() == "int_times") { Bounds b1 = bounds.back(); bounds.pop_back(); Bounds b0 = bounds.back(); bounds.pop_back(); if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || !b0.second.isFinite()) { valid = false; bounds.emplace_back(0, 0); } else { IntVal x0 = b0.first * b1.first; IntVal x1 = b0.first * b1.second; IntVal x2 = b0.second * b1.first; IntVal x3 = b0.second * b1.second; IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); bounds.emplace_back(m, n); } } else if (c.id() == constants().ids.bool2int) { bounds.emplace_back(0, 1); } else if (c.id() == "abs") { Bounds b0 = bounds.back(); if (b0.first < 0) { bounds.pop_back(); if (b0.second < 0) { bounds.emplace_back(-b0.second, -b0.first); } else { bounds.emplace_back(0, std::max(-b0.first, b0.second)); } } } else if (c.id() == "int_uniform") { Bounds b0 = bounds.back(); bounds.pop_back(); Bounds b1 = bounds.back(); bounds.pop_back(); assert(b0.first == b0.second && b1.first == b1.second); bounds.emplace_back(b0.first, b1.first); } else if (c.id() == "int_sol" || c.id() == "int_lastval") { // Take bounds of the argument } else if ((c.decl() != nullptr) && (c.decl()->ti()->domain() != nullptr) && !c.decl()->ti()->domain()->isa()) { for (int i = 0; i < c.argCount(); i++) { if (c.arg(i)->type().isint()) { assert(!bounds.empty()); bounds.pop_back(); } } IntSetVal* isv = eval_intset(env, c.decl()->ti()->domain()); bounds.emplace_back(isv->min(), isv->max()); } else { valid = false; bounds.emplace_back(0, 0); } } /// Visit let void vLet(const Let& l) { valid = false; bounds.emplace_back(0, 0); } /// Visit variable declaration void vVarDecl(const VarDecl& vd) { valid = false; bounds.emplace_back(0, 0); } /// Visit annotation void vAnnotation(const Annotation& e) { valid = false; bounds.emplace_back(0, 0); } /// Visit type inst void vTypeInst(const TypeInst& e) { valid = false; bounds.emplace_back(0, 0); } /// Visit TIId void vTIId(const TIId& e) { valid = false; bounds.emplace_back(0, 0); } }; IntBounds compute_int_bounds(EnvI& env, Expression* e) { try { ComputeIntBounds cb(env); BottomUpIterator cbi(cb); cbi.run(e); if (cb.valid) { assert(cb.bounds.size() == 1); return IntBounds(cb.bounds.back().first, cb.bounds.back().second, true); } return IntBounds(0, 0, false); } catch (ResultUndefinedError&) { return IntBounds(0, 0, false); } } class ComputeFloatBounds : public EVisitor { protected: typedef std::pair FBounds; public: std::vector bounds; bool valid; EnvI& env; ComputeFloatBounds(EnvI& env0) : valid(true), env(env0) {} bool enter(Expression* e) { if (e->type().isAnn()) { return false; } if (e->isa()) { return false; } if (e->type().dim() > 0) { return false; } if (e->type().isPar()) { if (e->type().isfloat()) { Expression* exp = eval_par(env, e); if (exp == constants().absent) { valid = false; } else { FloatVal v = exp->cast()->v(); bounds.emplace_back(v, v); } } return false; } if (e->type().isfloat()) { if (ITE* ite = e->dynamicCast()) { FBounds itebounds(FloatVal::infinity(), -FloatVal::infinity()); for (int i = 0; i < ite->size(); i++) { if (ite->ifExpr(i)->type().isPar() && static_cast(ite->ifExpr(i)->type().cv()) == Type::CV_NO) { if (eval_bool(env, ite->ifExpr(i))) { BottomUpIterator cbi(*this); cbi.run(ite->thenExpr(i)); FBounds& back = bounds.back(); back.first = std::min(itebounds.first, back.first); back.second = std::max(itebounds.second, back.second); return false; } } else { BottomUpIterator cbi(*this); cbi.run(ite->thenExpr(i)); FBounds back = bounds.back(); bounds.pop_back(); itebounds.first = std::min(itebounds.first, back.first); itebounds.second = std::max(itebounds.second, back.second); } } BottomUpIterator cbi(*this); cbi.run(ite->elseExpr()); FBounds& back = bounds.back(); back.first = std::min(itebounds.first, back.first); back.second = std::max(itebounds.second, back.second); return false; } return true; } return false; } /// Visit integer literal void vIntLit(const IntLit& i) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit floating point literal void vFloatLit(const FloatLit& f) { bounds.emplace_back(f.v(), f.v()); } /// Visit Boolean literal void vBoolLit(const BoolLit& /*b*/) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit set literal void vSetLit(const SetLit& /*sl*/) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit string literal void vStringLit(const StringLit& /*sl*/) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit identifier void vId(const Id& id) { VarDecl* vd = id.decl(); while ((vd->flat() != nullptr) && vd->flat() != vd) { vd = vd->flat(); } if (vd->ti()->domain() != nullptr) { GCLock lock; FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain()); if (fsv->size() == 0) { valid = false; bounds.emplace_back(0, 0); } else { bounds.emplace_back(fsv->min(0), fsv->max(fsv->size() - 1)); } } else { if (vd->e() != nullptr) { BottomUpIterator cbi(*this); cbi.run(vd->e()); } else { bounds.emplace_back(-FloatVal::infinity(), FloatVal::infinity()); } } } /// Visit anonymous variable void vAnonVar(const AnonVar& v) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit array literal void vArrayLit(const ArrayLit& al) {} /// Visit array access void vArrayAccess(ArrayAccess& aa) { bool parAccess = true; for (unsigned int i = aa.idx().size(); (i--) != 0U;) { if (!aa.idx()[i]->type().isPar()) { parAccess = false; } } if (Id* id = aa.v()->dynamicCast()) { while ((id->decl()->e() != nullptr) && id->decl()->e()->isa()) { id = id->decl()->e()->cast(); } if (parAccess && (id->decl()->e() != nullptr)) { bool success; Expression* e = eval_arrayaccess(env, &aa, success); if (success) { BottomUpIterator cbi(*this); cbi.run(e); return; } } if (id->decl()->ti()->domain() != nullptr) { FloatSetVal* fsv = eval_floatset(env, id->decl()->ti()->domain()); FBounds b(fsv->min(), fsv->max()); bounds.push_back(b); return; } } valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit array comprehension void vComprehension(const Comprehension& c) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit if-then-else void vITE(const ITE& ite) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit binary operator void vBinOp(const BinOp& bo) { FBounds b1 = bounds.back(); bounds.pop_back(); FBounds b0 = bounds.back(); bounds.pop_back(); if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || !b0.second.isFinite()) { valid = false; bounds.emplace_back(0.0, 0.0); } else { switch (bo.op()) { case BOT_PLUS: bounds.emplace_back(b0.first + b1.first, b0.second + b1.second); break; case BOT_MINUS: bounds.emplace_back(b0.first - b1.second, b0.second - b1.first); break; case BOT_MULT: { FloatVal x0 = b0.first * b1.first; FloatVal x1 = b0.first * b1.second; FloatVal x2 = b0.second * b1.first; FloatVal x3 = b0.second * b1.second; FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); bounds.emplace_back(m, n); } break; case BOT_POW: { FloatVal x0 = std::pow(b0.first.toDouble(), b1.first.toDouble()); FloatVal x1 = std::pow(b0.first.toDouble(), b1.second.toDouble()); FloatVal x2 = std::pow(b0.second.toDouble(), b1.first.toDouble()); FloatVal x3 = std::pow(b0.second.toDouble(), b1.second.toDouble()); FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); bounds.emplace_back(m, n); } break; case BOT_DIV: case BOT_IDIV: case BOT_MOD: case BOT_LE: case BOT_LQ: case BOT_GR: case BOT_GQ: case BOT_EQ: case BOT_NQ: case BOT_IN: case BOT_SUBSET: case BOT_SUPERSET: case BOT_UNION: case BOT_DIFF: case BOT_SYMDIFF: case BOT_INTERSECT: case BOT_PLUSPLUS: case BOT_EQUIV: case BOT_IMPL: case BOT_RIMPL: case BOT_OR: case BOT_AND: case BOT_XOR: case BOT_DOTDOT: valid = false; bounds.emplace_back(0.0, 0.0); } } } /// Visit unary operator void vUnOp(const UnOp& uo) { switch (uo.op()) { case UOT_PLUS: break; case UOT_MINUS: bounds.back().first = -bounds.back().first; bounds.back().second = -bounds.back().second; break; case UOT_NOT: valid = false; bounds.emplace_back(0.0, 0.0); } } /// Visit call void vCall(Call& c) { if (c.id() == constants().ids.lin_exp || c.id() == constants().ids.sum) { bool le = c.id() == constants().ids.lin_exp; ArrayLit* coeff = le ? eval_array_lit(env, c.arg(0)) : nullptr; if (le) { bounds.pop_back(); // remove constant (third arg) from stack } if (c.arg(le ? 1 : 0)->type().isOpt()) { valid = false; bounds.emplace_back(0.0, 0.0); return; } ArrayLit* al = eval_array_lit(env, c.arg(le ? 1 : 0)); FloatVal d = le ? c.arg(2)->cast()->v() : 0.0; int stacktop = static_cast(bounds.size()); for (unsigned int i = al->size(); (i--) != 0U;) { BottomUpIterator cbi(*this); cbi.run((*al)[i]); if (!valid) { return; } } assert(stacktop + al->size() == bounds.size()); FloatVal lb = d; FloatVal ub = d; for (unsigned int i = 0; i < (*al).size(); i++) { FBounds b = bounds.back(); bounds.pop_back(); FloatVal cv = le ? eval_float(env, (*coeff)[i]) : 1.0; if (cv > 0) { if (b.first.isFinite()) { if (lb.isFinite()) { lb += cv * b.first; } } else { lb = b.first; } if (b.second.isFinite()) { if (ub.isFinite()) { ub += cv * b.second; } } else { ub = b.second; } } else { if (b.second.isFinite()) { if (lb.isFinite()) { lb += cv * b.second; } } else { lb = -b.second; } if (b.first.isFinite()) { if (ub.isFinite()) { ub += cv * b.first; } } else { ub = -b.first; } } } bounds.emplace_back(lb, ub); } else if (c.id() == "float_times") { BottomUpIterator cbi(*this); cbi.run(c.arg(0)); cbi.run(c.arg(1)); FBounds b1 = bounds.back(); bounds.pop_back(); FBounds b0 = bounds.back(); bounds.pop_back(); if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || !b0.second.isFinite()) { valid = false; bounds.emplace_back(0, 0); } else { FloatVal x0 = b0.first * b1.first; FloatVal x1 = b0.first * b1.second; FloatVal x2 = b0.second * b1.first; FloatVal x3 = b0.second * b1.second; FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); bounds.emplace_back(m, n); } } else if (c.id() == "int2float") { ComputeIntBounds ib(env); BottomUpIterator cbi(ib); cbi.run(c.arg(0)); if (!ib.valid) { valid = false; } ComputeIntBounds::Bounds result = ib.bounds.back(); if (!result.first.isFinite() || !result.second.isFinite()) { valid = false; bounds.emplace_back(0.0, 0.0); } else { bounds.emplace_back(static_cast(result.first.toInt()), static_cast(result.second.toInt())); } } else if (c.id() == "abs") { BottomUpIterator cbi(*this); cbi.run(c.arg(0)); FBounds b0 = bounds.back(); if (b0.first < 0) { bounds.pop_back(); if (b0.second < 0) { bounds.emplace_back(-b0.second, -b0.first); } else { bounds.emplace_back(0.0, std::max(-b0.first, b0.second)); } } } else if (c.id() == "float_sol" || c.id() == "float_lastval") { // Take bounds of the argument } else if ((c.decl() != nullptr) && (c.decl()->ti()->domain() != nullptr) && !c.decl()->ti()->domain()->isa()) { for (int i = 0; i < c.argCount(); i++) { if (c.arg(i)->type().isfloat()) { assert(!bounds.empty()); bounds.pop_back(); } } FloatSetVal* fsv = eval_floatset(env, c.decl()->ti()->domain()); bounds.emplace_back(fsv->min(), fsv->max()); } else { valid = false; bounds.emplace_back(0.0, 0.0); } } /// Visit let void vLet(const Let& l) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit variable declaration void vVarDecl(const VarDecl& vd) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit annotation void vAnnotation(const Annotation& e) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit type inst void vTypeInst(const TypeInst& e) { valid = false; bounds.emplace_back(0.0, 0.0); } /// Visit TIId void vTIId(const TIId& e) { valid = false; bounds.emplace_back(0.0, 0.0); } }; FloatBounds compute_float_bounds(EnvI& env, Expression* e) { try { ComputeFloatBounds cb(env); BottomUpIterator cbi(cb); cbi.run(e); if (cb.valid) { assert(!cb.bounds.empty()); return FloatBounds(cb.bounds.back().first, cb.bounds.back().second, true); } return FloatBounds(0.0, 0.0, false); } catch (ResultUndefinedError&) { return FloatBounds(0.0, 0.0, false); } } class ComputeIntSetBounds : public EVisitor { public: std::vector bounds; bool valid; EnvI& env; ComputeIntSetBounds(EnvI& env0) : valid(true), env(env0) {} bool enter(Expression* e) { if (e->type().isAnn()) { return false; } if (e->isa()) { return false; } if (e->type().dim() > 0) { return false; } if (!e->type().isIntSet()) { return false; } if (e->type().isPar()) { bounds.push_back(eval_intset(env, e)); return false; } return true; } /// Visit set literal void vSetLit(const SetLit& sl) { assert(sl.type().isvar()); assert(sl.isv() == nullptr); IntSetVal* isv = IntSetVal::a(); for (unsigned int i = 0; i < sl.v().size(); i++) { IntSetRanges i0(isv); IntBounds ib = compute_int_bounds(env, sl.v()[i]); if (!ib.valid || !ib.l.isFinite() || !ib.u.isFinite()) { valid = false; bounds.push_back(nullptr); return; } Ranges::Const cr(ib.l, ib.u); Ranges::Union> u(i0, cr); isv = IntSetVal::ai(u); } bounds.push_back(isv); } /// Visit identifier void vId(const Id& id) { if ((id.decl()->ti()->domain() != nullptr) && !id.decl()->ti()->domain()->isa()) { bounds.push_back(eval_intset(env, id.decl()->ti()->domain())); } else { if (id.decl()->e() != nullptr) { BottomUpIterator cbi(*this); cbi.run(id.decl()->e()); } else { valid = false; bounds.push_back(nullptr); } } } /// Visit anonymous variable void vAnonVar(const AnonVar& v) { valid = false; bounds.push_back(nullptr); } /// Visit array access void vArrayAccess(ArrayAccess& aa) { bool parAccess = true; for (unsigned int i = aa.idx().size(); (i--) != 0U;) { if (!aa.idx()[i]->type().isPar()) { parAccess = false; break; } } if (Id* id = aa.v()->dynamicCast()) { while ((id->decl()->e() != nullptr) && id->decl()->e()->isa()) { id = id->decl()->e()->cast(); } if (parAccess && (id->decl()->e() != nullptr)) { bool success; Expression* e = eval_arrayaccess(env, &aa, success); if (success) { BottomUpIterator cbi(*this); cbi.run(e); return; } } if (id->decl()->ti()->domain() != nullptr) { bounds.push_back(eval_intset(env, id->decl()->ti()->domain())); return; } } valid = false; bounds.push_back(nullptr); } /// Visit array comprehension void vComprehension(const Comprehension& c) { valid = false; bounds.push_back(nullptr); } /// Visit if-then-else void vITE(const ITE& ite) { valid = false; bounds.push_back(nullptr); } /// Visit binary operator void vBinOp(const BinOp& bo) { if (bo.op() == BOT_DOTDOT) { IntBounds lb = compute_int_bounds(env, bo.lhs()); IntBounds ub = compute_int_bounds(env, bo.rhs()); valid = valid && lb.valid && ub.valid; bounds.push_back(IntSetVal::a(lb.l, ub.u)); } else { IntSetVal* b1 = bounds.back(); bounds.pop_back(); IntSetVal* b0 = bounds.back(); bounds.pop_back(); switch (bo.op()) { case BOT_INTERSECT: case BOT_UNION: { IntSetRanges b0r(b0); IntSetRanges b1r(b1); Ranges::Union u(b0r, b1r); bounds.push_back(IntSetVal::ai(u)); } break; case BOT_DIFF: { bounds.push_back(b0); } break; case BOT_SYMDIFF: valid = false; bounds.push_back(nullptr); break; case BOT_PLUS: case BOT_MINUS: case BOT_MULT: case BOT_POW: case BOT_DIV: case BOT_IDIV: case BOT_MOD: case BOT_LE: case BOT_LQ: case BOT_GR: case BOT_GQ: case BOT_EQ: case BOT_NQ: case BOT_IN: case BOT_SUBSET: case BOT_SUPERSET: case BOT_PLUSPLUS: case BOT_EQUIV: case BOT_IMPL: case BOT_RIMPL: case BOT_OR: case BOT_AND: case BOT_XOR: case BOT_DOTDOT: valid = false; bounds.push_back(nullptr); } } } /// Visit unary operator void vUnOp(const UnOp& uo) { valid = false; bounds.push_back(nullptr); } /// Visit call void vCall(Call& c) { if (valid && (c.id() == "set_intersect" || c.id() == "set_union")) { IntSetVal* b0 = bounds.back(); bounds.pop_back(); IntSetVal* b1 = bounds.back(); bounds.pop_back(); IntSetRanges b0r(b0); IntSetRanges b1r(b1); Ranges::Union u(b0r, b1r); bounds.push_back(IntSetVal::ai(u)); } else if (valid && c.id() == "set_diff") { IntSetVal* b0 = bounds.back(); bounds.pop_back(); bounds.pop_back(); // don't need bounds of right hand side bounds.push_back(b0); } else if ((c.decl() != nullptr) && (c.decl()->ti()->domain() != nullptr) && !c.decl()->ti()->domain()->isa()) { for (int i = 0; i < c.argCount(); i++) { if (c.arg(i)->type().isIntSet()) { assert(!bounds.empty()); bounds.pop_back(); } } IntSetVal* fsv = eval_intset(env, c.decl()->ti()->domain()); bounds.push_back(fsv); } else { valid = false; bounds.push_back(nullptr); } } /// Visit let void vLet(const Let& l) { valid = false; bounds.push_back(nullptr); } /// Visit variable declaration void vVarDecl(const VarDecl& vd) { valid = false; bounds.push_back(nullptr); } /// Visit annotation void vAnnotation(const Annotation& e) { valid = false; bounds.push_back(nullptr); } /// Visit type inst void vTypeInst(const TypeInst& e) { valid = false; bounds.push_back(nullptr); } /// Visit TIId void vTIId(const TIId& e) { valid = false; bounds.push_back(nullptr); } }; IntSetVal* compute_intset_bounds(EnvI& env, Expression* e) { try { ComputeIntSetBounds cb(env); BottomUpIterator cbi(cb); cbi.run(e); if (cb.valid) { return cb.bounds.back(); } return nullptr; } catch (ResultUndefinedError&) { return nullptr; } } Expression* follow_id(Expression* e) { for (;;) { if (e == nullptr) { return nullptr; } if (e->eid() == Expression::E_ID && e != constants().absent) { e = e->cast()->decl()->e(); } else { return e; } } } Expression* follow_id_to_decl(Expression* e) { for (;;) { if (e == nullptr) { return nullptr; } if (e == constants().absent) { return e; } switch (e->eid()) { case Expression::E_ID: e = e->cast()->decl(); break; case Expression::E_VARDECL: { Expression* vd_e = e->cast()->e(); if ((vd_e != nullptr) && vd_e->isa() && vd_e != constants().absent) { e = vd_e; } else { return e; } break; } default: return e; } } } Expression* follow_id_to_value(Expression* e) { Expression* decl = follow_id_to_decl(e); if (auto* vd = decl->dynamicCast()) { if ((vd->e() != nullptr) && vd->e()->type().isPar()) { return vd->e(); } return vd->id(); } return decl; } } // namespace MiniZinc