/* -*- 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 namespace MiniZinc { EE flatten_arrayaccess(EnvI& env, const Ctx& ctx, Expression* e, VarDecl* r, VarDecl* b) { CallStackItem _csi(env, e); EE ret; auto* aa = e->cast(); KeepAlive aa_ka = aa; Ctx nctx = ctx; nctx.b = +nctx.b; nctx.neg = false; EE eev = flat_exp(env, nctx, aa->v(), nullptr, nullptr); std::vector ees; start_flatten_arrayaccess: for (unsigned int i = 0; i < aa->idx().size(); i++) { Expression* tmp = follow_id_to_decl(aa->idx()[i]); if (auto* vd = tmp->dynamicCast()) { tmp = vd->id(); } if (tmp->type().isPar()) { ArrayLit* al; if (eev.r()->isa()) { al = eev.r()->cast(); } else { Id* id = eev.r()->cast(); if (id->decl() == nullptr) { throw InternalError("undefined identifier"); } if (id->decl()->e() == nullptr) { throw InternalError("array without initialiser not supported"); } Expression* id_e = follow_id(id); if (id_e->isa()) { al = id_e->cast(); } else { throw InternalError("builtin function returning array not supported"); } } std::vector elems; std::vector idx(aa->idx().size()); std::vector > dims; std::vector newaccess; std::vector nonpar; std::vector stack; for (unsigned int j = 0; j < aa->idx().size(); j++) { Expression* tmp = follow_id_to_decl(aa->idx()[j]); if (auto* vd = tmp->dynamicCast()) { tmp = vd->id(); } if (tmp->type().isPar()) { GCLock lock; idx[j] = eval_int(env, tmp).toInt(); } else { idx[j] = al->min(j); stack.push_back(static_cast(nonpar.size())); nonpar.push_back(j); dims.emplace_back(al->min(j), al->max(j)); newaccess.push_back(aa->idx()[j]); } } if (stack.empty()) { bool success; KeepAlive ka; { GCLock lock; ka = eval_arrayaccess(env, al, idx, success); if (!success && env.inMaybePartial == 0) { ResultUndefinedError warning(env, al->loc(), "array access out of bounds"); } } ees.emplace_back(nullptr, constants().boollit(success)); ees.emplace_back(nullptr, eev.b()); if (aa->type().isbool() && !aa->type().isOpt()) { ret.b = bind(env, Ctx(), b, constants().literalTrue); ees.emplace_back(nullptr, ka()); ret.r = conj(env, r, ctx, ees); } else { ret.b = conj(env, b, ctx, ees); ret.r = bind(env, ctx, r, ka()); } return ret; } while (!stack.empty()) { int cur = stack.back(); if (cur == nonpar.size() - 1) { stack.pop_back(); for (int i = al->min(nonpar[cur]); i <= al->max(nonpar[cur]); i++) { idx[nonpar[cur]] = i; bool success; GCLock lock; Expression* al_idx = eval_arrayaccess(env, al, idx, success); if (!success) { if (env.inMaybePartial == 0) { ResultUndefinedError warning(env, al->loc(), "array access out of bounds"); } ees.emplace_back(nullptr, constants().literalFalse); ees.emplace_back(nullptr, eev.b()); if (aa->type().isbool() && !aa->type().isOpt()) { ret.b = bind(env, Ctx(), b, constants().literalTrue); ret.r = conj(env, r, ctx, ees); } else { ret.b = conj(env, b, ctx, ees); ret.r = bind(env, ctx, r, al_idx); } return ret; } elems.emplace_back(al_idx); } } else { if (idx[nonpar[cur]].toInt() == al->max(nonpar[cur])) { idx[nonpar[cur]] = al->min(nonpar[cur]); stack.pop_back(); } else { idx[nonpar[cur]]++; for (unsigned int j = cur + 1; j < nonpar.size(); j++) { stack.push_back(j); } } } } std::vector elems_e(elems.size()); for (unsigned int i = 0; i < elems.size(); i++) { elems_e[i] = elems[i](); } { GCLock lock; Expression* newal = new ArrayLit(al->loc(), elems_e, dims); Type t = al->type(); t.dim(static_cast(dims.size())); newal->type(t); eev.r = newal; auto* n_aa = new ArrayAccess(aa->loc(), newal, newaccess); n_aa->type(aa->type()); aa = n_aa; aa_ka = aa; } } } if (aa->idx().size() == 1 && aa->idx()[0]->isa()) { auto* aa_inner = aa->idx()[0]->cast(); ArrayLit* al; if (eev.r()->isa()) { al = eev.r()->cast(); } else { Id* id = eev.r()->cast(); if (id->decl() == nullptr) { throw InternalError("undefined identifier"); } if (id->decl()->e() == nullptr) { throw InternalError("array without initialiser not supported"); } al = follow_id(id)->cast(); } if (aa_inner->v()->type().isPar()) { KeepAlive ka_al_inner = flat_cv_exp(env, ctx, aa_inner->v()); auto* al_inner = ka_al_inner()->cast(); std::vector composed_e(al_inner->size()); for (unsigned int i = 0; i < al_inner->size(); i++) { GCLock lock; IntVal inner_idx = eval_int(env, (*al_inner)[i]); if (inner_idx < al->min(0) || inner_idx > al->max(0)) { goto flatten_arrayaccess; } composed_e[i] = (*al)[static_cast(inner_idx.toInt()) - al->min(0)]; } std::vector > dims(al_inner->dims()); for (int i = 0; i < al_inner->dims(); i++) { dims[i] = std::make_pair(al_inner->min(i), al_inner->max(i)); } { GCLock lock; Expression* newal = new ArrayLit(al->loc(), composed_e, dims); Type t = al->type(); t.dim(static_cast(dims.size())); newal->type(t); eev.r = newal; auto* n_aa = new ArrayAccess(aa->loc(), newal, aa_inner->idx()); n_aa->type(aa->type()); aa = n_aa; aa_ka = aa; goto start_flatten_arrayaccess; } } } flatten_arrayaccess: Ctx dimctx = ctx; dimctx.neg = false; for (unsigned int i = 0; i < aa->idx().size(); i++) { Expression* tmp = follow_id_to_decl(aa->idx()[i]); if (auto* vd = tmp->dynamicCast()) { tmp = vd->id(); } ees.push_back(flat_exp(env, dimctx, tmp, nullptr, nullptr)); } ees.emplace_back(nullptr, eev.b()); bool parAccess = true; for (unsigned int i = 0; i < aa->idx().size(); i++) { if (!ees[i].r()->type().isPar()) { parAccess = false; break; } } if (parAccess) { ArrayLit* al; if (eev.r()->isa()) { al = eev.r()->cast(); } else { Id* id = eev.r()->cast(); if (id->decl() == nullptr) { throw InternalError("undefined identifier"); } if (id->decl()->e() == nullptr) { throw InternalError("array without initialiser not supported"); } al = follow_id(id)->cast(); } KeepAlive ka; bool success; { GCLock lock; std::vector dims(aa->idx().size()); for (unsigned int i = aa->idx().size(); (i--) != 0U;) { dims[i] = eval_int(env, ees[i].r()); } ka = eval_arrayaccess(env, al, dims, success); } if (!success && env.inMaybePartial == 0) { ResultUndefinedError warning(env, al->loc(), "array access out of bounds"); } ees.emplace_back(nullptr, constants().boollit(success)); if (aa->type().isbool() && !aa->type().isOpt()) { ret.b = bind(env, Ctx(), b, constants().literalTrue); ees.emplace_back(nullptr, ka()); ret.r = conj(env, r, ctx, ees); } else { ret.b = conj(env, b, ctx, ees); ret.r = bind(env, ctx, r, ka()); } } else { std::vector args(aa->idx().size() + 1); for (unsigned int i = aa->idx().size(); (i--) != 0U;) { args[i] = ees[i].r(); } args[aa->idx().size()] = eev.r(); KeepAlive ka; { GCLock lock; Call* cc = new Call(e->loc().introduce(), constants().ids.element, args); cc->type(aa->type()); FunctionI* fi = env.model->matchFn(env, cc->id(), args, false); if (fi == nullptr) { throw FlatteningError(env, cc->loc(), "cannot find matching declaration"); } assert(fi); assert(env.isSubtype(fi->rtype(env, args, false), cc->type(), false)); cc->decl(fi); ka = cc; } Ctx elemctx = ctx; elemctx.neg = false; EE ee = flat_exp(env, elemctx, ka(), nullptr, nullptr); ees.push_back(ee); if (aa->type().isbool() && !aa->type().isOpt()) { ee.b = ee.r; ees.push_back(ee); ret.r = conj(env, r, ctx, ees); ret.b = bind(env, ctx, b, constants().boollit(!ctx.neg)); } else { ret.r = bind(env, ctx, r, ee.r()); ret.b = conj(env, b, ctx, ees); } } return ret; } } // namespace MiniZinc