1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
3 /*
4 * Main authors:
5 * Guido Tack <guido.tack@monash.edu>
6 */
7
8 /* This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11
12 #include <minizinc/flat_exp.hh>
13
14 namespace MiniZinc {
15
flatten_arrayaccess(EnvI & env,const Ctx & ctx,Expression * e,VarDecl * r,VarDecl * b)16 EE flatten_arrayaccess(EnvI& env, const Ctx& ctx, Expression* e, VarDecl* r, VarDecl* b) {
17 CallStackItem _csi(env, e);
18 EE ret;
19 auto* aa = e->cast<ArrayAccess>();
20 KeepAlive aa_ka = aa;
21
22 Ctx nctx = ctx;
23 nctx.b = +nctx.b;
24 nctx.neg = false;
25 EE eev = flat_exp(env, nctx, aa->v(), nullptr, nullptr);
26 std::vector<EE> ees;
27
28 start_flatten_arrayaccess:
29 for (unsigned int i = 0; i < aa->idx().size(); i++) {
30 Expression* tmp = follow_id_to_decl(aa->idx()[i]);
31 if (auto* vd = tmp->dynamicCast<VarDecl>()) {
32 tmp = vd->id();
33 }
34 if (tmp->type().isPar()) {
35 ArrayLit* al;
36 if (eev.r()->isa<ArrayLit>()) {
37 al = eev.r()->cast<ArrayLit>();
38 } else {
39 Id* id = eev.r()->cast<Id>();
40 if (id->decl() == nullptr) {
41 throw InternalError("undefined identifier");
42 }
43 if (id->decl()->e() == nullptr) {
44 throw InternalError("array without initialiser not supported");
45 }
46 Expression* id_e = follow_id(id);
47 if (id_e->isa<ArrayLit>()) {
48 al = id_e->cast<ArrayLit>();
49 } else {
50 throw InternalError("builtin function returning array not supported");
51 }
52 }
53
54 std::vector<KeepAlive> elems;
55 std::vector<IntVal> idx(aa->idx().size());
56 std::vector<std::pair<int, int> > dims;
57 std::vector<Expression*> newaccess;
58 std::vector<int> nonpar;
59 std::vector<int> stack;
60 for (unsigned int j = 0; j < aa->idx().size(); j++) {
61 Expression* tmp = follow_id_to_decl(aa->idx()[j]);
62 if (auto* vd = tmp->dynamicCast<VarDecl>()) {
63 tmp = vd->id();
64 }
65 if (tmp->type().isPar()) {
66 GCLock lock;
67 idx[j] = eval_int(env, tmp).toInt();
68 } else {
69 idx[j] = al->min(j);
70 stack.push_back(static_cast<int>(nonpar.size()));
71 nonpar.push_back(j);
72 dims.emplace_back(al->min(j), al->max(j));
73 newaccess.push_back(aa->idx()[j]);
74 }
75 }
76 if (stack.empty()) {
77 bool success;
78 KeepAlive ka;
79 {
80 GCLock lock;
81 ka = eval_arrayaccess(env, al, idx, success);
82 if (!success && env.inMaybePartial == 0) {
83 ResultUndefinedError warning(env, al->loc(), "array access out of bounds");
84 }
85 }
86 ees.emplace_back(nullptr, constants().boollit(success));
87 ees.emplace_back(nullptr, eev.b());
88 if (aa->type().isbool() && !aa->type().isOpt()) {
89 ret.b = bind(env, Ctx(), b, constants().literalTrue);
90 ees.emplace_back(nullptr, ka());
91 ret.r = conj(env, r, ctx, ees);
92 } else {
93 ret.b = conj(env, b, ctx, ees);
94 ret.r = bind(env, ctx, r, ka());
95 }
96 return ret;
97 }
98 while (!stack.empty()) {
99 int cur = stack.back();
100 if (cur == nonpar.size() - 1) {
101 stack.pop_back();
102 for (int i = al->min(nonpar[cur]); i <= al->max(nonpar[cur]); i++) {
103 idx[nonpar[cur]] = i;
104 bool success;
105 GCLock lock;
106 Expression* al_idx = eval_arrayaccess(env, al, idx, success);
107 if (!success) {
108 if (env.inMaybePartial == 0) {
109 ResultUndefinedError warning(env, al->loc(), "array access out of bounds");
110 }
111 ees.emplace_back(nullptr, constants().literalFalse);
112 ees.emplace_back(nullptr, eev.b());
113 if (aa->type().isbool() && !aa->type().isOpt()) {
114 ret.b = bind(env, Ctx(), b, constants().literalTrue);
115 ret.r = conj(env, r, ctx, ees);
116 } else {
117 ret.b = conj(env, b, ctx, ees);
118 ret.r = bind(env, ctx, r, al_idx);
119 }
120 return ret;
121 }
122 elems.emplace_back(al_idx);
123 }
124 } else {
125 if (idx[nonpar[cur]].toInt() == al->max(nonpar[cur])) {
126 idx[nonpar[cur]] = al->min(nonpar[cur]);
127 stack.pop_back();
128 } else {
129 idx[nonpar[cur]]++;
130 for (unsigned int j = cur + 1; j < nonpar.size(); j++) {
131 stack.push_back(j);
132 }
133 }
134 }
135 }
136 std::vector<Expression*> elems_e(elems.size());
137 for (unsigned int i = 0; i < elems.size(); i++) {
138 elems_e[i] = elems[i]();
139 }
140 {
141 GCLock lock;
142 Expression* newal = new ArrayLit(al->loc(), elems_e, dims);
143 Type t = al->type();
144 t.dim(static_cast<int>(dims.size()));
145 newal->type(t);
146 eev.r = newal;
147 auto* n_aa = new ArrayAccess(aa->loc(), newal, newaccess);
148 n_aa->type(aa->type());
149 aa = n_aa;
150 aa_ka = aa;
151 }
152 }
153 }
154
155 if (aa->idx().size() == 1 && aa->idx()[0]->isa<ArrayAccess>()) {
156 auto* aa_inner = aa->idx()[0]->cast<ArrayAccess>();
157 ArrayLit* al;
158 if (eev.r()->isa<ArrayLit>()) {
159 al = eev.r()->cast<ArrayLit>();
160 } else {
161 Id* id = eev.r()->cast<Id>();
162 if (id->decl() == nullptr) {
163 throw InternalError("undefined identifier");
164 }
165 if (id->decl()->e() == nullptr) {
166 throw InternalError("array without initialiser not supported");
167 }
168 al = follow_id(id)->cast<ArrayLit>();
169 }
170 if (aa_inner->v()->type().isPar()) {
171 KeepAlive ka_al_inner = flat_cv_exp(env, ctx, aa_inner->v());
172 auto* al_inner = ka_al_inner()->cast<ArrayLit>();
173 std::vector<Expression*> composed_e(al_inner->size());
174 for (unsigned int i = 0; i < al_inner->size(); i++) {
175 GCLock lock;
176 IntVal inner_idx = eval_int(env, (*al_inner)[i]);
177 if (inner_idx < al->min(0) || inner_idx > al->max(0)) {
178 goto flatten_arrayaccess;
179 }
180 composed_e[i] = (*al)[static_cast<int>(inner_idx.toInt()) - al->min(0)];
181 }
182 std::vector<std::pair<int, int> > dims(al_inner->dims());
183 for (int i = 0; i < al_inner->dims(); i++) {
184 dims[i] = std::make_pair(al_inner->min(i), al_inner->max(i));
185 }
186 {
187 GCLock lock;
188 Expression* newal = new ArrayLit(al->loc(), composed_e, dims);
189 Type t = al->type();
190 t.dim(static_cast<int>(dims.size()));
191 newal->type(t);
192 eev.r = newal;
193 auto* n_aa = new ArrayAccess(aa->loc(), newal, aa_inner->idx());
194 n_aa->type(aa->type());
195 aa = n_aa;
196 aa_ka = aa;
197 goto start_flatten_arrayaccess;
198 }
199 }
200 }
201 flatten_arrayaccess:
202 Ctx dimctx = ctx;
203 dimctx.neg = false;
204 for (unsigned int i = 0; i < aa->idx().size(); i++) {
205 Expression* tmp = follow_id_to_decl(aa->idx()[i]);
206 if (auto* vd = tmp->dynamicCast<VarDecl>()) {
207 tmp = vd->id();
208 }
209 ees.push_back(flat_exp(env, dimctx, tmp, nullptr, nullptr));
210 }
211 ees.emplace_back(nullptr, eev.b());
212
213 bool parAccess = true;
214 for (unsigned int i = 0; i < aa->idx().size(); i++) {
215 if (!ees[i].r()->type().isPar()) {
216 parAccess = false;
217 break;
218 }
219 }
220
221 if (parAccess) {
222 ArrayLit* al;
223 if (eev.r()->isa<ArrayLit>()) {
224 al = eev.r()->cast<ArrayLit>();
225 } else {
226 Id* id = eev.r()->cast<Id>();
227 if (id->decl() == nullptr) {
228 throw InternalError("undefined identifier");
229 }
230 if (id->decl()->e() == nullptr) {
231 throw InternalError("array without initialiser not supported");
232 }
233 al = follow_id(id)->cast<ArrayLit>();
234 }
235 KeepAlive ka;
236 bool success;
237 {
238 GCLock lock;
239 std::vector<IntVal> dims(aa->idx().size());
240 for (unsigned int i = aa->idx().size(); (i--) != 0U;) {
241 dims[i] = eval_int(env, ees[i].r());
242 }
243 ka = eval_arrayaccess(env, al, dims, success);
244 }
245 if (!success && env.inMaybePartial == 0) {
246 ResultUndefinedError warning(env, al->loc(), "array access out of bounds");
247 }
248 ees.emplace_back(nullptr, constants().boollit(success));
249 if (aa->type().isbool() && !aa->type().isOpt()) {
250 ret.b = bind(env, Ctx(), b, constants().literalTrue);
251 ees.emplace_back(nullptr, ka());
252 ret.r = conj(env, r, ctx, ees);
253 } else {
254 ret.b = conj(env, b, ctx, ees);
255 ret.r = bind(env, ctx, r, ka());
256 }
257 } else {
258 std::vector<Expression*> args(aa->idx().size() + 1);
259 for (unsigned int i = aa->idx().size(); (i--) != 0U;) {
260 args[i] = ees[i].r();
261 }
262 args[aa->idx().size()] = eev.r();
263 KeepAlive ka;
264 {
265 GCLock lock;
266 Call* cc = new Call(e->loc().introduce(), constants().ids.element, args);
267 cc->type(aa->type());
268 FunctionI* fi = env.model->matchFn(env, cc->id(), args, false);
269 if (fi == nullptr) {
270 throw FlatteningError(env, cc->loc(), "cannot find matching declaration");
271 }
272 assert(fi);
273 assert(env.isSubtype(fi->rtype(env, args, false), cc->type(), false));
274 cc->decl(fi);
275 ka = cc;
276 }
277 Ctx elemctx = ctx;
278 elemctx.neg = false;
279 EE ee = flat_exp(env, elemctx, ka(), nullptr, nullptr);
280 ees.push_back(ee);
281 if (aa->type().isbool() && !aa->type().isOpt()) {
282 ee.b = ee.r;
283 ees.push_back(ee);
284 ret.r = conj(env, r, ctx, ees);
285 ret.b = bind(env, ctx, b, constants().boollit(!ctx.neg));
286 } else {
287 ret.r = bind(env, ctx, r, ee.r());
288 ret.b = conj(env, b, ctx, ees);
289 }
290 }
291 return ret;
292 }
293
294 } // namespace MiniZinc
295