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