this repo has no description
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/astexception.hh>
13#include <minizinc/astiterator.hh>
14#include <minizinc/copy.hh>
15#include <minizinc/eval_par.hh>
16#include <minizinc/flat_exp.hh>
17#include <minizinc/flatten.hh>
18#include <minizinc/flatten_internal.hh>
19#include <minizinc/hash.hh>
20#include <minizinc/optimize.hh>
21#include <minizinc/output.hh>
22
23#include <map>
24#include <unordered_map>
25#include <unordered_set>
26
27namespace MiniZinc {
28
29// Create domain constraints. Return true if successful.
30bool create_explicit_domain_constraints(EnvI& envi, VarDecl* vd, Expression* domain) {
31 std::vector<Call*> calls;
32 Location iloc = Location().introduce();
33
34 if (vd->type().isIntSet()) {
35 assert(domain->type().isint() || domain->type().isIntSet());
36 IntSetVal* isv = eval_intset(envi, domain);
37 calls.push_back(new Call(iloc, constants().ids.set_subset, {vd->id(), new SetLit(iloc, isv)}));
38 } else if (domain->type().isbool()) {
39 calls.push_back(new Call(iloc, constants().ids.bool_eq, {vd->id(), domain}));
40 } else if (domain->type().isBoolSet()) {
41 IntSetVal* bsv = eval_boolset(envi, domain);
42 assert(bsv->size() == 1);
43 if (bsv->min() != bsv->max()) {
44 return true; // Both values are still possible, no change
45 }
46 calls.push_back(
47 new Call(iloc, constants().ids.bool_eq, {vd->id(), constants().boollit(bsv->min() > 0)}));
48 } else if (domain->type().isfloat() || domain->type().isFloatSet()) {
49 FloatSetVal* fsv = eval_floatset(envi, domain);
50 if (fsv->size() == 1) { // Range based
51 if (fsv->min() == fsv->max()) {
52 calls.push_back(
53 new Call(iloc, constants().ids.float_.eq, {vd->id(), FloatLit::a(fsv->min())}));
54 } else {
55 FloatSetVal* cfsv;
56 if (vd->ti()->domain() != nullptr) {
57 cfsv = eval_floatset(envi, vd->ti()->domain());
58 } else {
59 cfsv = FloatSetVal::a(-FloatVal::infinity(), FloatVal::infinity());
60 }
61 if (cfsv->min() < fsv->min()) {
62 calls.push_back(
63 new Call(iloc, constants().ids.float_.le, {FloatLit::a(fsv->min()), vd->id()}));
64 }
65 if (cfsv->max() > fsv->max()) {
66 calls.push_back(
67 new Call(iloc, constants().ids.float_.le, {vd->id(), FloatLit::a(fsv->max())}));
68 }
69 }
70 } else {
71 calls.push_back(new Call(iloc, constants().ids.set_in, {vd->id(), new SetLit(iloc, fsv)}));
72 }
73 } else if (domain->type().isint() || domain->type().isIntSet()) {
74 IntSetVal* isv = eval_intset(envi, domain);
75 if (isv->size() == 1) { // Range based
76 if (isv->min() == isv->max()) {
77 calls.push_back(new Call(iloc, constants().ids.int_.eq, {vd->id(), IntLit::a(isv->min())}));
78 } else {
79 IntSetVal* cisv;
80 if (vd->ti()->domain() != nullptr) {
81 cisv = eval_intset(envi, vd->ti()->domain());
82 } else {
83 cisv = IntSetVal::a(-IntVal::infinity(), IntVal::infinity());
84 }
85 if (cisv->min() < isv->min()) {
86 calls.push_back(
87 new Call(iloc, constants().ids.int_.le, {IntLit::a(isv->min()), vd->id()}));
88 }
89 if (cisv->max() > isv->max()) {
90 calls.push_back(
91 new Call(iloc, constants().ids.int_.le, {vd->id(), IntLit::a(isv->max())}));
92 }
93 }
94 } else {
95 calls.push_back(new Call(iloc, constants().ids.set_in, {vd->id(), new SetLit(iloc, isv)}));
96 }
97 } else {
98 return false;
99 }
100
101 int counter = 0;
102 for (Call* c : calls) {
103 CallStackItem csi(envi, IntLit::a(counter++));
104 c->ann().add(constants().ann.domain_change_constraint);
105 c->type(Type::varbool());
106 c->decl(envi.model->matchFn(envi, c, true));
107 flat_exp(envi, Ctx(), c, constants().varTrue, constants().varTrue);
108 }
109 return true;
110}
111
112void set_computed_domain(EnvI& envi, VarDecl* vd, Expression* domain, bool is_computed) {
113 if (envi.hasReverseMapper(vd->id())) {
114 if (!create_explicit_domain_constraints(envi, vd, domain)) {
115 std::ostringstream ss;
116 ss << "Unable to create domain constraint for reverse mapped variable: " << *vd->id() << " = "
117 << *domain << std::endl;
118 throw EvalError(envi, domain->loc(), ss.str());
119 }
120 vd->ti()->domain(domain);
121 return;
122 }
123 if (envi.fopts.recordDomainChanges && !vd->ann().contains(constants().ann.is_defined_var) &&
124 !vd->introduced() && !(vd->type().dim() > 0)) {
125 if (create_explicit_domain_constraints(envi, vd, domain)) {
126 return;
127 }
128 std::cerr << "Warning: domain change not handled by -g mode: " << *vd->id() << " = " << *domain
129 << std::endl;
130 }
131 vd->ti()->domain(domain);
132 vd->ti()->setComputedDomain(is_computed);
133}
134
135/// Output operator for contexts
136template <class Char, class Traits>
137std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, Ctx& ctx) {
138 switch (ctx.b) {
139 case C_ROOT:
140 os << "R";
141 break;
142 case C_POS:
143 os << "+";
144 break;
145 case C_NEG:
146 os << "-";
147 break;
148 case C_MIX:
149 os << "M";
150 break;
151 default:
152 assert(false);
153 break;
154 }
155 switch (ctx.i) {
156 case C_ROOT:
157 os << "R";
158 break;
159 case C_POS:
160 os << "+";
161 break;
162 case C_NEG:
163 os << "-";
164 break;
165 case C_MIX:
166 os << "M";
167 break;
168 default:
169 assert(false);
170 break;
171 }
172 if (ctx.neg) {
173 os << "!";
174 }
175 return os;
176}
177
178BCtx operator+(const BCtx& c) {
179 switch (c) {
180 case C_ROOT:
181 case C_POS:
182 return C_POS;
183 case C_NEG:
184 return C_NEG;
185 case C_MIX:
186 return C_MIX;
187 default:
188 assert(false);
189 return C_ROOT;
190 }
191}
192
193BCtx operator-(const BCtx& c) {
194 switch (c) {
195 case C_ROOT:
196 case C_POS:
197 return C_NEG;
198 case C_NEG:
199 return C_POS;
200 case C_MIX:
201 return C_MIX;
202 default:
203 assert(false);
204 return C_ROOT;
205 }
206}
207
208/// Check if \a c is non-positive
209bool nonpos(const BCtx& c) { return c == C_NEG || c == C_MIX; }
210/// Check if \a c is non-negative
211bool nonneg(const BCtx& c) { return c == C_ROOT || c == C_POS; }
212
213void dump_ee_b(const std::vector<EE>& ee) {
214 for (const auto& i : ee) {
215 std::cerr << *i.b() << "\n";
216 }
217}
218void dump_ee_r(const std::vector<EE>& ee) {
219 for (const auto& i : ee) {
220 std::cerr << *i.r() << "\n";
221 }
222}
223
224std::tuple<BCtx, bool> ann_to_ctx(VarDecl* vd) {
225 if (vd->ann().contains(constants().ctx.root)) {
226 return std::make_tuple(C_ROOT, true);
227 }
228 if (vd->ann().contains(constants().ctx.mix)) {
229 return std::make_tuple(C_MIX, true);
230 }
231 if (vd->ann().contains(constants().ctx.pos)) {
232 return std::make_tuple(C_POS, true);
233 }
234 if (vd->ann().contains(constants().ctx.neg)) {
235 return std::make_tuple(C_NEG, true);
236 }
237 return std::make_tuple(C_MIX, false);
238}
239
240void add_ctx_ann(VarDecl* vd, BCtx& c) {
241 if (vd != nullptr) {
242 Id* ctx_id = nullptr;
243 BCtx nc;
244 bool annotated;
245 std::tie(nc, annotated) = ann_to_ctx(vd);
246 // If previously annotated
247 if (annotated) {
248 // Early exit
249 if (nc == c || nc == C_ROOT || (nc == C_MIX && c != C_ROOT)) {
250 return;
251 }
252 // Remove old annotation
253 switch (nc) {
254 case C_ROOT:
255 vd->ann().remove(constants().ctx.root);
256 break;
257 case C_MIX:
258 vd->ann().remove(constants().ctx.mix);
259 break;
260 case C_POS:
261 vd->ann().remove(constants().ctx.pos);
262 break;
263 case C_NEG:
264 vd->ann().remove(constants().ctx.neg);
265 break;
266 default:
267 assert(false);
268 break;
269 }
270 // Determine new context
271 if (c == C_ROOT) {
272 nc = C_ROOT;
273 } else {
274 nc = C_MIX;
275 }
276 } else {
277 nc = c;
278 }
279 switch (nc) {
280 case C_ROOT:
281 ctx_id = constants().ctx.root;
282 break;
283 case C_POS:
284 ctx_id = constants().ctx.pos;
285 break;
286 case C_NEG:
287 ctx_id = constants().ctx.neg;
288 break;
289 case C_MIX:
290 ctx_id = constants().ctx.mix;
291 break;
292 default:
293 assert(false);
294 break;
295 }
296 vd->addAnnotation(ctx_id);
297 }
298}
299
300void make_defined_var(VarDecl* vd, Call* c) {
301 if (!vd->ann().contains(constants().ann.is_defined_var)) {
302 std::vector<Expression*> args(1);
303 args[0] = vd->id();
304 Call* dv = new Call(Location().introduce(), constants().ann.defines_var, args);
305 dv->type(Type::ann());
306 vd->addAnnotation(constants().ann.is_defined_var);
307 c->addAnnotation(dv);
308 }
309}
310
311bool is_defines_var_ann(Expression* e) {
312 return e->isa<Call>() && e->cast<Call>()->id() == constants().ann.defines_var;
313}
314
315/// Check if \a e is NULL or true
316bool istrue(EnvI& env, Expression* e) {
317 if (e == nullptr) {
318 return true;
319 }
320 if (e->type() == Type::parbool()) {
321 if (e->type().cv()) {
322 Ctx ctx;
323 ctx.b = C_MIX;
324 KeepAlive r = flat_cv_exp(env, ctx, e);
325 return eval_bool(env, r());
326 }
327 GCLock lock;
328 return eval_bool(env, e);
329 }
330 return false;
331}
332/// Check if \a e is non-NULL and false
333bool isfalse(EnvI& env, Expression* e) {
334 if (e == nullptr) {
335 return false;
336 }
337 if (e->type() == Type::parbool()) {
338 if (e->type().cv()) {
339 Ctx ctx;
340 ctx.b = C_MIX;
341 KeepAlive r = flat_cv_exp(env, ctx, e);
342 return !eval_bool(env, r());
343 }
344 GCLock lock;
345 return !eval_bool(env, e);
346 }
347 return false;
348}
349
350/// Use bounds from ovd for vd if they are better.
351/// Returns true if ovd's bounds are better.
352bool update_bounds(EnvI& envi, VarDecl* ovd, VarDecl* vd) {
353 bool tighter = false;
354 bool fixed = false;
355 if ((ovd->ti()->domain() != nullptr) || (ovd->e() != nullptr)) {
356 IntVal intval;
357 FloatVal doubleval;
358 bool boolval;
359
360 if (vd->type().isint()) {
361 IntBounds oldbounds = compute_int_bounds(envi, ovd->id());
362 IntBounds bounds(0, 0, false);
363 if ((vd->ti()->domain() != nullptr) || (vd->e() != nullptr)) {
364 bounds = compute_int_bounds(envi, vd->id());
365 }
366
367 if (((vd->ti()->domain() != nullptr) || (vd->e() != nullptr)) && bounds.valid &&
368 bounds.l.isFinite() && bounds.u.isFinite()) {
369 if (oldbounds.valid && oldbounds.l.isFinite() && oldbounds.u.isFinite()) {
370 fixed = oldbounds.u == oldbounds.l || bounds.u == bounds.l;
371 if (fixed) {
372 tighter = true;
373 intval = oldbounds.u == oldbounds.l ? oldbounds.u : bounds.l;
374 ovd->ti()->domain(new SetLit(ovd->loc(), IntSetVal::a(intval, intval)));
375 } else {
376 IntSetVal* olddom =
377 ovd->ti()->domain() != nullptr ? eval_intset(envi, ovd->ti()->domain()) : nullptr;
378 IntSetVal* newdom =
379 vd->ti()->domain() != nullptr ? eval_intset(envi, vd->ti()->domain()) : nullptr;
380
381 if (olddom != nullptr) {
382 if (newdom == nullptr) {
383 tighter = true;
384 } else {
385 IntSetRanges oisr(olddom);
386 IntSetRanges nisr(newdom);
387 IntSetRanges nisr_card(newdom);
388
389 Ranges::Inter<IntVal, IntSetRanges, IntSetRanges> inter(oisr, nisr);
390
391 if (Ranges::cardinality(inter) < Ranges::cardinality(nisr_card)) {
392 IntSetRanges oisr_inter(olddom);
393 IntSetRanges nisr_inter(newdom);
394 Ranges::Inter<IntVal, IntSetRanges, IntSetRanges> inter_card(oisr_inter,
395 nisr_inter);
396 tighter = true;
397 ovd->ti()->domain(new SetLit(ovd->loc(), IntSetVal::ai(inter_card)));
398 }
399 }
400 }
401 }
402 }
403 } else {
404 if (oldbounds.valid && oldbounds.l.isFinite() && oldbounds.u.isFinite()) {
405 tighter = true;
406 fixed = oldbounds.u == oldbounds.l;
407 if (fixed) {
408 intval = oldbounds.u;
409 ovd->ti()->domain(new SetLit(ovd->loc(), IntSetVal::a(intval, intval)));
410 }
411 }
412 }
413 } else if (vd->type().isfloat()) {
414 FloatBounds oldbounds = compute_float_bounds(envi, ovd->id());
415 FloatBounds bounds(0.0, 0.0, false);
416 if ((vd->ti()->domain() != nullptr) || (vd->e() != nullptr)) {
417 bounds = compute_float_bounds(envi, vd->id());
418 }
419 if (((vd->ti()->domain() != nullptr) || (vd->e() != nullptr)) && bounds.valid) {
420 if (oldbounds.valid) {
421 fixed = oldbounds.u == oldbounds.l || bounds.u == bounds.l;
422 if (fixed) {
423 doubleval = oldbounds.u == oldbounds.l ? oldbounds.u : bounds.l;
424 }
425 tighter = fixed || (oldbounds.u - oldbounds.l < bounds.u - bounds.l);
426 }
427 } else {
428 if (oldbounds.valid) {
429 tighter = true;
430 fixed = oldbounds.u == oldbounds.l;
431 if (fixed) {
432 doubleval = oldbounds.u;
433 }
434 }
435 }
436 } else if (vd->type().isbool()) {
437 if (ovd->ti()->domain() != nullptr) {
438 fixed = tighter = true;
439 boolval = eval_bool(envi, ovd->ti()->domain());
440 } else {
441 fixed = tighter = ((ovd->e() != nullptr) && ovd->e()->isa<BoolLit>());
442 if (fixed) {
443 boolval = ovd->e()->cast<BoolLit>()->v();
444 }
445 }
446 }
447
448 if (tighter) {
449 vd->ti()->domain(ovd->ti()->domain());
450 if (vd->e() == nullptr && fixed) {
451 if (vd->ti()->type().isvarint()) {
452 vd->type(Type::parint());
453 vd->ti(new TypeInst(vd->loc(), Type::parint()));
454 vd->e(IntLit::a(intval));
455 } else if (vd->ti()->type().isvarfloat()) {
456 vd->type(Type::parfloat());
457 vd->ti(new TypeInst(vd->loc(), Type::parfloat()));
458 vd->e(FloatLit::a(doubleval));
459 } else if (vd->ti()->type().isvarbool()) {
460 vd->type(Type::parbool());
461 vd->ti(new TypeInst(vd->loc(), Type::parbool()));
462 vd->ti()->domain(boolval ? constants().literalTrue : constants().literalFalse);
463 vd->e(new BoolLit(vd->loc(), boolval));
464 }
465 }
466 }
467 }
468
469 return tighter;
470}
471
472std::string get_path(EnvI& env) {
473 std::string path;
474 std::stringstream ss;
475 if (env.dumpPath(ss)) {
476 path = ss.str();
477 }
478
479 return path;
480}
481
482inline Location get_loc(EnvI& env, Expression* e1, Expression* e2) {
483 if (e1 != nullptr) {
484 return e1->loc().introduce();
485 }
486 if (e2 != nullptr) {
487 return e2->loc().introduce();
488 }
489 return Location().introduce();
490}
491inline Id* get_id(EnvI& env, Id* origId) {
492 return origId != nullptr ? origId : new Id(Location().introduce(), env.genId(), nullptr);
493}
494
495StringLit* get_longest_mzn_path_annotation(EnvI& env, const Expression* e) {
496 StringLit* sl = nullptr;
497
498 if (const auto* vd = e->dynamicCast<const VarDecl>()) {
499 EnvI::ReversePathMap& reversePathMap = env.getReversePathMap();
500 KeepAlive vd_decl_ka(vd->id()->decl());
501 auto it = reversePathMap.find(vd_decl_ka);
502 if (it != reversePathMap.end()) {
503 sl = new StringLit(Location(), it->second);
504 }
505 } else {
506 for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) {
507 if (Call* ca = (*it)->dynamicCast<Call>()) {
508 if (ca->id() == constants().ann.mzn_path) {
509 auto* sl1 = ca->arg(0)->cast<StringLit>();
510 if (sl != nullptr) {
511 if (sl1->v().size() > sl->v().size()) {
512 sl = sl1;
513 }
514 } else {
515 sl = sl1;
516 }
517 }
518 }
519 }
520 }
521 return sl;
522}
523
524void add_path_annotation(EnvI& env, Expression* e) {
525 if (!(e->type().isAnn() || e->isa<Id>()) && e->type().dim() == 0) {
526 GCLock lock;
527 Annotation& ann = e->ann();
528 if (ann.containsCall(constants().ann.mzn_path)) {
529 return;
530 }
531
532 EnvI::ReversePathMap& reversePathMap = env.getReversePathMap();
533
534 std::vector<Expression*> path_args(1);
535 std::string p;
536 KeepAlive e_ka(e);
537 auto it = reversePathMap.find(e_ka);
538 if (it == reversePathMap.end()) {
539 p = get_path(env);
540 } else {
541 p = it->second;
542 }
543
544 if (!p.empty()) {
545 path_args[0] = new StringLit(Location(), p);
546 Call* path_call = new Call(e->loc(), constants().ann.mzn_path, path_args);
547 path_call->type(Type::ann());
548 e->addAnnotation(path_call);
549 }
550 }
551}
552
553VarDecl* new_vardecl(EnvI& env, const Ctx& ctx, TypeInst* ti, Id* origId, VarDecl* origVd,
554 Expression* rhs) {
555 VarDecl* vd = nullptr;
556
557 // Is this vardecl already in the FlatZinc (for unification)
558 bool hasBeenAdded = false;
559
560 // Don't use paths for arrays or annotations
561 if (ti->type().dim() == 0 && !ti->type().isAnn()) {
562 std::string path = get_path(env);
563 if (!path.empty()) {
564 EnvI::ReversePathMap& reversePathMap = env.getReversePathMap();
565 EnvI::PathMap& pathMap = env.getPathMap();
566 auto it = pathMap.find(path);
567
568 if (it != pathMap.end()) {
569 auto* ovd = Expression::cast<VarDecl>((it->second.decl)());
570 unsigned int ovd_pass = it->second.passNumber;
571
572 if (ovd != nullptr) {
573 // If ovd was introduced during the same pass, we can unify
574 if (env.currentPassNumber == ovd_pass) {
575 vd = ovd;
576 if (origId != nullptr) {
577 origId->decl(vd);
578 }
579 hasBeenAdded = true;
580 } else {
581 vd = new VarDecl(get_loc(env, origVd, rhs), ti, get_id(env, origId));
582 hasBeenAdded = false;
583 update_bounds(env, ovd, vd);
584 }
585
586 // Check whether ovd was unified in a previous pass
587 if (ovd->id() != ovd->id()->decl()->id()) {
588 // We may not have seen the pointed to decl just yet
589 KeepAlive ovd_decl_ka(ovd->id()->decl());
590 auto path2It = reversePathMap.find(ovd_decl_ka);
591 if (path2It != reversePathMap.end()) {
592 std::string path2 = path2It->second;
593 EnvI::PathVar vd_tup{vd, env.currentPassNumber};
594
595 pathMap[path] = vd_tup;
596 pathMap[path2] = vd_tup;
597 KeepAlive vd_ka(vd);
598 reversePathMap.insert(vd_ka, path);
599 }
600 }
601 }
602 } else {
603 // Create new VarDecl and add it to the maps
604 vd = new VarDecl(get_loc(env, origVd, rhs), ti, get_id(env, origId));
605 hasBeenAdded = false;
606 EnvI::PathVar vd_tup{vd, env.currentPassNumber};
607 pathMap[path] = vd_tup;
608 KeepAlive vd_ka(vd);
609 reversePathMap.insert(vd_ka, path);
610 }
611 }
612 }
613 if (vd == nullptr) {
614 vd = new VarDecl(get_loc(env, origVd, rhs), ti, get_id(env, origId));
615 hasBeenAdded = false;
616 }
617
618 // If vd has an e() use bind to turn rhs into a constraint
619 if (vd->e() != nullptr) {
620 if (rhs != nullptr) {
621 bind(env, ctx, vd, rhs);
622 }
623 } else {
624 vd->e(rhs);
625 if ((rhs != nullptr) && hasBeenAdded) {
626 // This variable is being reused, so it won't be added to the model below.
627 // Therefore, we need to register that we changed the RHS, in order
628 // for the reference counts to be accurate.
629 env.voAddExp(vd);
630 }
631 }
632 assert(!vd->type().isbot());
633 if ((origVd != nullptr) && (origVd->id()->idn() != -1 || origVd->toplevel())) {
634 vd->introduced(origVd->introduced());
635 } else {
636 vd->introduced(true);
637 }
638
639 vd->flat(vd);
640
641 // Copy annotations from origVd
642 if (origVd != nullptr) {
643 for (ExpressionSetIter it = origVd->ann().begin(); it != origVd->ann().end(); ++it) {
644 EE ee_ann = flat_exp(env, Ctx(), *it, nullptr, constants().varTrue);
645 vd->addAnnotation(ee_ann.r());
646 }
647 }
648
649 if (!hasBeenAdded) {
650 if (FunctionI* fi = env.model->matchRevMap(env, vd->type())) {
651 // We need to introduce a reverse mapper
652 Call* revmap = new Call(Location().introduce(), fi->id(), {vd->id()});
653 revmap->decl(fi);
654 revmap->type(Type::varbool());
655 env.flatAddItem(new ConstraintI(Location().introduce(), revmap));
656 }
657
658 auto* ni = new VarDeclI(Location().introduce(), vd);
659 env.flatAddItem(ni);
660 EE ee(vd, nullptr);
661 env.cseMapInsert(vd->id(), ee);
662 }
663
664 return vd;
665}
666
667#define MZN_FILL_REIFY_MAP(T, ID) \
668 _reifyMap.insert( \
669 std::pair<ASTString, ASTString>(constants().ids.T.ID, constants().ids.T##reif.ID));
670
671EnvI::EnvI(Model* model0, std::ostream& outstream0, std::ostream& errstream0)
672 : model(model0),
673 originalModel(nullptr),
674 output(new Model),
675 outstream(outstream0),
676 errstream(errstream0),
677 currentPassNumber(0),
678 finalPassNumber(1),
679 maxPathDepth(0),
680 ignorePartial(false),
681 ignoreUnknownIds(false),
682 maxCallStack(0),
683 inRedundantConstraint(0),
684 inMaybePartial(0),
685 inReverseMapVar(false),
686 counters({0, 0, 0, 0}),
687 pathUse(0),
688 _flat(new Model),
689 _failed(false),
690 _ids(0),
691 _collectVardecls(false) {
692 MZN_FILL_REIFY_MAP(int_, lin_eq);
693 MZN_FILL_REIFY_MAP(int_, lin_le);
694 MZN_FILL_REIFY_MAP(int_, lin_ne);
695 MZN_FILL_REIFY_MAP(int_, plus);
696 MZN_FILL_REIFY_MAP(int_, minus);
697 MZN_FILL_REIFY_MAP(int_, times);
698 MZN_FILL_REIFY_MAP(int_, div);
699 MZN_FILL_REIFY_MAP(int_, mod);
700 MZN_FILL_REIFY_MAP(int_, lt);
701 MZN_FILL_REIFY_MAP(int_, le);
702 MZN_FILL_REIFY_MAP(int_, gt);
703 MZN_FILL_REIFY_MAP(int_, ge);
704 MZN_FILL_REIFY_MAP(int_, eq);
705 MZN_FILL_REIFY_MAP(int_, ne);
706 MZN_FILL_REIFY_MAP(float_, lin_eq);
707 MZN_FILL_REIFY_MAP(float_, lin_le);
708 MZN_FILL_REIFY_MAP(float_, lin_lt);
709 MZN_FILL_REIFY_MAP(float_, lin_ne);
710 MZN_FILL_REIFY_MAP(float_, plus);
711 MZN_FILL_REIFY_MAP(float_, minus);
712 MZN_FILL_REIFY_MAP(float_, times);
713 MZN_FILL_REIFY_MAP(float_, div);
714 MZN_FILL_REIFY_MAP(float_, mod);
715 MZN_FILL_REIFY_MAP(float_, lt);
716 MZN_FILL_REIFY_MAP(float_, le);
717 MZN_FILL_REIFY_MAP(float_, gt);
718 MZN_FILL_REIFY_MAP(float_, ge);
719 MZN_FILL_REIFY_MAP(float_, eq);
720 MZN_FILL_REIFY_MAP(float_, ne);
721 _reifyMap.insert(
722 std::pair<ASTString, ASTString>(constants().ids.forall, constants().ids.forallReif));
723 _reifyMap.insert(
724 std::pair<ASTString, ASTString>(constants().ids.bool_eq, constants().ids.bool_eq_reif));
725 _reifyMap.insert(std::pair<ASTString, ASTString>(constants().ids.bool_clause,
726 constants().ids.bool_clause_reif));
727 _reifyMap.insert(
728 std::pair<ASTString, ASTString>(constants().ids.clause, constants().ids.bool_clause_reif));
729 _reifyMap.insert({constants().ids.bool_not, constants().ids.bool_not});
730}
731EnvI::~EnvI() {
732 delete _flat;
733 delete output;
734 delete model;
735 delete originalModel;
736}
737long long int EnvI::genId() { return _ids++; }
738void EnvI::cseMapInsert(Expression* e, const EE& ee) {
739 KeepAlive ka(e);
740 _cseMap.insert(ka, WW(ee.r(), ee.b()));
741 Call* c = e->dynamicCast<Call>();
742 if (c != nullptr && c->decl() != nullptr &&
743 get_annotation(c->decl()->ann(), constants().ann.impure) != nullptr) {
744 return;
745 }
746 if ((c != nullptr) && c->id() == constants().ids.bool_not && c->arg(0)->isa<Id>() &&
747 ee.r()->isa<Id>() && ee.b() == constants().boollit(true)) {
748 Call* neg_c = new Call(Location().introduce(), c->id(), {ee.r()});
749 neg_c->type(c->type());
750 neg_c->decl(c->decl());
751 KeepAlive neg_ka(neg_c);
752 _cseMap.insert(neg_ka, WW(c->arg(0), ee.b()));
753 }
754}
755EnvI::CSEMap::iterator EnvI::cseMapFind(Expression* e) {
756 KeepAlive ka(e);
757 auto it = _cseMap.find(ka);
758 if (it != _cseMap.end()) {
759 if (it->second.r() != nullptr) {
760 VarDecl* it_vd = it->second.r()->isa<Id>() ? it->second.r()->cast<Id>()->decl()
761 : it->second.r()->dynamicCast<VarDecl>();
762 if (it_vd != nullptr) {
763 int idx = varOccurrences.find(it_vd);
764 if (idx == -1 || (*_flat)[idx]->removed()) {
765 return _cseMap.end();
766 }
767 }
768 } else {
769 return _cseMap.end();
770 }
771 }
772 return it;
773}
774void EnvI::cseMapRemove(Expression* e) {
775 KeepAlive ka(e);
776 _cseMap.remove(ka);
777}
778EnvI::CSEMap::iterator EnvI::cseMapEnd() { return _cseMap.end(); }
779void EnvI::dump() {
780 struct EED {
781 static std::string k(Expression* e) {
782 std::ostringstream oss;
783 oss << *e;
784 return oss.str();
785 }
786 static std::string d(const WW& ee) {
787 std::ostringstream oss;
788 oss << *ee.r() << " " << ee.b();
789 return oss.str();
790 }
791 };
792 _cseMap.dump<EED>();
793}
794
795void EnvI::flatAddItem(Item* i) {
796 assert(_flat);
797 if (_failed) {
798 return;
799 }
800 _flat->addItem(i);
801
802 Expression* toAnnotate = nullptr;
803 Expression* toAdd = nullptr;
804 switch (i->iid()) {
805 case Item::II_VD: {
806 auto* vd = i->cast<VarDeclI>();
807 add_path_annotation(*this, vd->e());
808 toAnnotate = vd->e()->e();
809 varOccurrences.addIndex(vd, static_cast<int>(_flat->size()) - 1);
810 toAdd = vd->e();
811 break;
812 }
813 case Item::II_CON: {
814 auto* ci = i->cast<ConstraintI>();
815
816 if (ci->e()->isa<BoolLit>() && !ci->e()->cast<BoolLit>()->v()) {
817 fail();
818 } else {
819 toAnnotate = ci->e();
820 add_path_annotation(*this, ci->e());
821 toAdd = ci->e();
822 }
823 break;
824 }
825 case Item::II_SOL: {
826 auto* si = i->cast<SolveI>();
827 CollectOccurrencesE ce(varOccurrences, si);
828 top_down(ce, si->e());
829 for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++it) {
830 top_down(ce, *it);
831 }
832 break;
833 }
834 case Item::II_OUT: {
835 auto* si = i->cast<OutputI>();
836 toAdd = si->e();
837 break;
838 }
839 default:
840 break;
841 }
842 if ((toAnnotate != nullptr) && toAnnotate->isa<Call>()) {
843 annotateFromCallStack(toAnnotate);
844 }
845 if (toAdd != nullptr) {
846 CollectOccurrencesE ce(varOccurrences, i);
847 top_down(ce, toAdd);
848 }
849}
850
851void EnvI::annotateFromCallStack(Expression* e) {
852 int prev = !idStack.empty() ? idStack.back() : 0;
853 bool allCalls = true;
854 for (int i = static_cast<int>(callStack.size()) - 1; i >= prev; i--) {
855 Expression* ee = callStack[i]->untag();
856 allCalls = allCalls && (i == callStack.size() - 1 || ee->isa<Call>() || ee->isa<BinOp>());
857 for (ExpressionSetIter it = ee->ann().begin(); it != ee->ann().end(); ++it) {
858 EE ee_ann = flat_exp(*this, Ctx(), *it, nullptr, constants().varTrue);
859 if (allCalls || !is_defines_var_ann(ee_ann.r())) {
860 e->addAnnotation(ee_ann.r());
861 }
862 }
863 }
864}
865
866void EnvI::copyPathMapsAndState(EnvI& env) {
867 finalPassNumber = env.finalPassNumber;
868 maxPathDepth = env.maxPathDepth;
869 currentPassNumber = env.currentPassNumber;
870 _filenameSet = env._filenameSet;
871 maxPathDepth = env.maxPathDepth;
872 _pathMap = env.getPathMap();
873 _reversePathMap = env.getReversePathMap();
874}
875
876void EnvI::flatRemoveExpr(Expression* e, Item* i) {
877 std::vector<VarDecl*> toRemove;
878 CollectDecls cd(varOccurrences, toRemove, i);
879 top_down(cd, e);
880
881 Model& flat = (*_flat);
882 while (!toRemove.empty()) {
883 VarDecl* cur = toRemove.back();
884 toRemove.pop_back();
885 assert(varOccurrences.occurrences(cur) == 0 && CollectDecls::varIsFree(cur));
886
887 auto cur_idx = varOccurrences.idx.find(cur->id());
888 if (cur_idx != varOccurrences.idx.end()) {
889 auto* vdi = flat[cur_idx->second]->cast<VarDeclI>();
890
891 if (!is_output(vdi->e()) && !vdi->removed()) {
892 CollectDecls cd(varOccurrences, toRemove, vdi);
893 top_down(cd, vdi->e()->e());
894 vdi->remove();
895 }
896 }
897 }
898}
899
900void EnvI::flatRemoveItem(ConstraintI* ci) {
901 flatRemoveExpr(ci->e(), ci);
902 ci->e(constants().literalTrue);
903 ci->remove();
904}
905void EnvI::flatRemoveItem(VarDeclI* vdi) {
906 flatRemoveExpr(vdi->e(), vdi);
907 vdi->remove();
908}
909
910void EnvI::fail(const std::string& msg) {
911 if (!_failed) {
912 addWarning(std::string("model inconsistency detected") +
913 (msg.empty() ? std::string() : (": " + msg)));
914 _failed = true;
915 for (auto& i : *_flat) {
916 i->remove();
917 }
918 auto* failedConstraint = new ConstraintI(Location().introduce(), constants().literalFalse);
919 _flat->addItem(failedConstraint);
920 _flat->addItem(SolveI::sat(Location().introduce()));
921 for (auto& i : *output) {
922 i->remove();
923 }
924 output->addItem(
925 new OutputI(Location().introduce(), new ArrayLit(Location(), std::vector<Expression*>())));
926 throw ModelInconsistent(*this, Location().introduce());
927 }
928}
929
930bool EnvI::failed() const { return _failed; }
931
932unsigned int EnvI::registerEnum(VarDeclI* vdi) {
933 auto it = _enumMap.find(vdi);
934 unsigned int ret;
935 if (it == _enumMap.end()) {
936 ret = static_cast<unsigned int>(_enumVarDecls.size());
937 _enumVarDecls.push_back(vdi);
938 _enumMap.insert(std::make_pair(vdi, ret));
939 } else {
940 ret = it->second;
941 }
942 return ret + 1;
943}
944VarDeclI* EnvI::getEnum(unsigned int i) const {
945 assert(i > 0 && i <= _enumVarDecls.size());
946 return _enumVarDecls[i - 1];
947}
948unsigned int EnvI::registerArrayEnum(const std::vector<unsigned int>& arrayEnum) {
949 std::ostringstream oss;
950 for (unsigned int i : arrayEnum) {
951 assert(i <= _enumVarDecls.size());
952 oss << i << ".";
953 }
954 auto it = _arrayEnumMap.find(oss.str());
955 unsigned int ret;
956 if (it == _arrayEnumMap.end()) {
957 ret = static_cast<unsigned int>(_arrayEnumDecls.size());
958 _arrayEnumDecls.push_back(arrayEnum);
959 _arrayEnumMap.insert(std::make_pair(oss.str(), ret));
960 } else {
961 ret = it->second;
962 }
963 return ret + 1;
964}
965const std::vector<unsigned int>& EnvI::getArrayEnum(unsigned int i) const {
966 assert(i > 0 && i <= _arrayEnumDecls.size());
967 return _arrayEnumDecls[i - 1];
968}
969bool EnvI::isSubtype(const Type& t1, const Type& t2, bool strictEnums) const {
970 if (!t1.isSubtypeOf(t2, strictEnums)) {
971 return false;
972 }
973 if (strictEnums && t1.dim() == 0 && t2.dim() != 0 && t2.enumId() != 0) {
974 // set assigned to an array
975 const std::vector<unsigned int>& t2enumIds = getArrayEnum(t2.enumId());
976 if (t2enumIds[t2enumIds.size() - 1] != 0 && t1.enumId() != t2enumIds[t2enumIds.size() - 1]) {
977 return false;
978 }
979 }
980 if (strictEnums && t1.dim() > 0 && t1.enumId() != t2.enumId()) {
981 if (t1.enumId() == 0) {
982 return t1.isbot();
983 }
984 if (t2.enumId() != 0) {
985 const std::vector<unsigned int>& t1enumIds = getArrayEnum(t1.enumId());
986 const std::vector<unsigned int>& t2enumIds = getArrayEnum(t2.enumId());
987 assert(t1enumIds.size() == t2enumIds.size());
988 for (unsigned int i = 0; i < t1enumIds.size() - 1; i++) {
989 if (t2enumIds[i] != 0 && t1enumIds[i] != t2enumIds[i]) {
990 return false;
991 }
992 }
993 if (!t1.isbot() && t2enumIds[t1enumIds.size() - 1] != 0 &&
994 t1enumIds[t1enumIds.size() - 1] != t2enumIds[t2enumIds.size() - 1]) {
995 return false;
996 }
997 }
998 }
999 return true;
1000}
1001
1002void EnvI::collectVarDecls(bool b) { _collectVardecls = b; }
1003void EnvI::voAddExp(VarDecl* vd) {
1004 if ((vd->e() != nullptr) && vd->e()->isa<Call>() && !vd->e()->type().isAnn()) {
1005 int prev = !idStack.empty() ? idStack.back() : 0;
1006 for (int i = static_cast<int>(callStack.size()) - 1; i >= prev; i--) {
1007 Expression* ee = callStack[i]->untag();
1008 for (ExpressionSetIter it = ee->ann().begin(); it != ee->ann().end(); ++it) {
1009 Expression* ann = *it;
1010 if (ann != constants().ann.add_to_output && ann != constants().ann.rhs_from_assignment) {
1011 bool needAnnotation = true;
1012 if (Call* ann_c = ann->dynamicCast<Call>()) {
1013 if (ann_c->id() == constants().ann.defines_var) {
1014 // only add defines_var annotation if vd is the defined variable
1015 if (Id* defined_var = ann_c->arg(0)->dynamicCast<Id>()) {
1016 if (defined_var->decl() != vd->id()->decl()) {
1017 needAnnotation = false;
1018 }
1019 }
1020 }
1021 }
1022 if (needAnnotation) {
1023 EE ee_ann = flat_exp(*this, Ctx(), *it, nullptr, constants().varTrue);
1024 vd->e()->addAnnotation(ee_ann.r());
1025 }
1026 }
1027 }
1028 }
1029 }
1030 int idx = varOccurrences.find(vd);
1031 CollectOccurrencesE ce(varOccurrences, (*_flat)[idx]);
1032 top_down(ce, vd->e());
1033 if (_collectVardecls) {
1034 modifiedVarDecls.push_back(idx);
1035 }
1036}
1037Model* EnvI::flat() { return _flat; }
1038void EnvI::swap() {
1039 Model* tmp = model;
1040 model = _flat;
1041 _flat = tmp;
1042}
1043ASTString EnvI::reifyId(const ASTString& id) {
1044 auto it = _reifyMap.find(id);
1045 if (it == _reifyMap.end()) {
1046 std::ostringstream ss;
1047 ss << id << "_reif";
1048 return {ss.str()};
1049 }
1050 return it->second;
1051}
1052#undef MZN_FILL_REIFY_MAP
1053ASTString EnvI::halfReifyId(const ASTString& id) {
1054 std::ostringstream ss;
1055 ss << id << "_imp";
1056 return {ss.str()};
1057}
1058
1059void EnvI::addWarning(const std::string& msg) {
1060 if (warnings.size() > 20) {
1061 return;
1062 }
1063 if (warnings.size() == 20) {
1064 warnings.emplace_back("Further warnings have been suppressed.\n");
1065 } else {
1066 std::ostringstream oss;
1067 createErrorStack();
1068 dumpStack(oss, true);
1069 warnings.push_back(msg + "\n" + oss.str());
1070 }
1071}
1072
1073void EnvI::createErrorStack() {
1074 errorStack.clear();
1075 for (auto i = static_cast<unsigned int>(callStack.size()); (i--) != 0U;) {
1076 Expression* e = callStack[i]->untag();
1077 bool isCompIter = callStack[i]->isTagged();
1078 KeepAlive ka(e);
1079 errorStack.emplace_back(ka, isCompIter);
1080 }
1081}
1082
1083Call* EnvI::surroundingCall() const {
1084 if (callStack.size() >= 2) {
1085 return callStack[callStack.size() - 2]->untag()->dynamicCast<Call>();
1086 }
1087 return nullptr;
1088}
1089
1090void EnvI::cleanupExceptOutput() {
1091 cmap.clear();
1092 _cseMap.clear();
1093 delete _flat;
1094 delete model;
1095 delete originalModel;
1096 _flat = nullptr;
1097 model = nullptr;
1098}
1099
1100CallStackItem::CallStackItem(EnvI& env0, Expression* e) : env(env0) {
1101 if (e->isa<VarDecl>()) {
1102 env.idStack.push_back(static_cast<int>(env.callStack.size()));
1103 }
1104 if (e->isa<Call>() && e->cast<Call>()->id() == "redundant_constraint") {
1105 env.inRedundantConstraint++;
1106 }
1107 if (e->ann().contains(constants().ann.maybe_partial)) {
1108 env.inMaybePartial++;
1109 }
1110 env.callStack.push_back(e);
1111 env.maxCallStack = std::max(env.maxCallStack, static_cast<unsigned int>(env.callStack.size()));
1112}
1113CallStackItem::CallStackItem(EnvI& env0, Id* ident, IntVal i) : env(env0) {
1114 Expression* ee = ident->tag();
1115 env.callStack.push_back(ee);
1116 env.maxCallStack = std::max(env.maxCallStack, static_cast<unsigned int>(env.callStack.size()));
1117}
1118CallStackItem::~CallStackItem() {
1119 try {
1120 Expression* e = env.callStack.back()->untag();
1121 if (e->isa<VarDecl>()) {
1122 env.idStack.pop_back();
1123 }
1124 if (e->isa<Call>() && e->cast<Call>()->id() == "redundant_constraint") {
1125 env.inRedundantConstraint--;
1126 }
1127 if (e->ann().contains(constants().ann.maybe_partial)) {
1128 env.inMaybePartial--;
1129 }
1130 env.callStack.pop_back();
1131 } catch (std::exception&) {
1132 assert(false); // Invariant: These Env vector operations will never throw an exception
1133 }
1134}
1135
1136FlatteningError::FlatteningError(EnvI& env, const Location& loc, const std::string& msg)
1137 : LocationException(env, loc, msg) {}
1138
1139Env::Env(Model* m, std::ostream& outstream, std::ostream& errstream)
1140 : _e(new EnvI(m, outstream, errstream)) {}
1141Env::~Env() { delete _e; }
1142
1143Model* Env::model() { return _e->model; }
1144void Env::model(Model* m) { _e->model = m; }
1145Model* Env::flat() { return _e->flat(); }
1146void Env::swap() { _e->swap(); }
1147Model* Env::output() { return _e->output; }
1148
1149std::ostream& Env::evalOutput(std::ostream& os, std::ostream& log) {
1150 return _e->evalOutput(os, log);
1151}
1152EnvI& Env::envi() { return *_e; }
1153const EnvI& Env::envi() const { return *_e; }
1154std::ostream& Env::dumpErrorStack(std::ostream& os) { return _e->dumpStack(os, true); }
1155
1156bool EnvI::dumpPath(std::ostream& os, bool force) {
1157 force = force ? force : fopts.collectMznPaths;
1158 if (callStack.size() > maxPathDepth) {
1159 if (!force && currentPassNumber >= finalPassNumber - 1) {
1160 return false;
1161 }
1162 maxPathDepth = static_cast<int>(callStack.size());
1163 }
1164
1165 auto lastError = static_cast<unsigned int>(callStack.size());
1166
1167 std::string major_sep = ";";
1168 std::string minor_sep = "|";
1169 for (unsigned int i = 0; i < lastError; i++) {
1170 Expression* e = callStack[i]->untag();
1171 bool isCompIter = callStack[i]->isTagged();
1172 Location loc = e->loc();
1173 auto findFilename = _filenameSet.find(loc.filename());
1174 if (findFilename == _filenameSet.end()) {
1175 if (!force && currentPassNumber >= finalPassNumber - 1) {
1176 return false;
1177 }
1178 _filenameSet.insert(loc.filename());
1179 }
1180
1181 // If this call is not a dummy StringLit with empty Location (so that deferred compilation
1182 // doesn't drop the paths)
1183 if (e->eid() != Expression::E_STRINGLIT || (loc.firstLine() != 0U) ||
1184 (loc.firstColumn() != 0U) || (loc.lastLine() != 0U) || (loc.lastColumn() != 0U)) {
1185 os << loc.filename() << minor_sep << loc.firstLine() << minor_sep << loc.firstColumn()
1186 << minor_sep << loc.lastLine() << minor_sep << loc.lastColumn() << minor_sep;
1187 switch (e->eid()) {
1188 case Expression::E_INTLIT:
1189 os << "il" << minor_sep << *e;
1190 break;
1191 case Expression::E_FLOATLIT:
1192 os << "fl" << minor_sep << *e;
1193 break;
1194 case Expression::E_SETLIT:
1195 os << "sl" << minor_sep << *e;
1196 break;
1197 case Expression::E_BOOLLIT:
1198 os << "bl" << minor_sep << *e;
1199 break;
1200 case Expression::E_STRINGLIT:
1201 os << "stl" << minor_sep << *e;
1202 break;
1203 case Expression::E_ID:
1204 if (isCompIter) {
1205 // if (e->cast<Id>()->decl()->e()->type().isPar())
1206 os << *e << "=" << *e->cast<Id>()->decl()->e();
1207 // else
1208 // os << *e << "=?";
1209 } else {
1210 os << "id" << minor_sep << *e;
1211 }
1212 break;
1213 case Expression::E_ANON:
1214 os << "anon";
1215 break;
1216 case Expression::E_ARRAYLIT:
1217 os << "al";
1218 break;
1219 case Expression::E_ARRAYACCESS:
1220 os << "aa";
1221 break;
1222 case Expression::E_COMP: {
1223 const Comprehension* cmp = e->cast<Comprehension>();
1224 if (cmp->set()) {
1225 os << "sc";
1226 } else {
1227 os << "ac";
1228 }
1229 } break;
1230 case Expression::E_ITE:
1231 os << "ite";
1232 break;
1233 case Expression::E_BINOP:
1234 os << "bin" << minor_sep << e->cast<BinOp>()->opToString();
1235 break;
1236 case Expression::E_UNOP:
1237 os << "un" << minor_sep << e->cast<UnOp>()->opToString();
1238 break;
1239 case Expression::E_CALL:
1240 if (fopts.onlyToplevelPaths) {
1241 return false;
1242 }
1243 os << "ca" << minor_sep << e->cast<Call>()->id();
1244 break;
1245 case Expression::E_VARDECL:
1246 os << "vd";
1247 break;
1248 case Expression::E_LET:
1249 os << "l";
1250 break;
1251 case Expression::E_TI:
1252 os << "ti";
1253 break;
1254 case Expression::E_TIID:
1255 os << "ty";
1256 break;
1257 default:
1258 assert(false);
1259 os << "unknown expression (internal error)";
1260 break;
1261 }
1262 os << major_sep;
1263 } else {
1264 os << e->cast<StringLit>()->v() << major_sep;
1265 }
1266 }
1267 return true;
1268}
1269
1270std::ostream& EnvI::dumpStack(std::ostream& os, bool errStack) {
1271 int lastError = 0;
1272
1273 std::vector<Expression*> errStackCopy;
1274 if (errStack) {
1275 errStackCopy.resize(errorStack.size());
1276 for (unsigned int i = 0; i < errorStack.size(); i++) {
1277 Expression* e = errorStack[i].first();
1278 if (errorStack[i].second) {
1279 e = e->tag();
1280 }
1281 errStackCopy[i] = e;
1282 }
1283 }
1284
1285 std::vector<Expression*>& stack = errStack ? errStackCopy : callStack;
1286
1287 for (; lastError < stack.size(); lastError++) {
1288 Expression* e = stack[lastError]->untag();
1289 bool isCompIter = stack[lastError]->isTagged();
1290 if (e->loc().isIntroduced()) {
1291 continue;
1292 }
1293 if (!isCompIter && e->isa<Id>()) {
1294 break;
1295 }
1296 }
1297
1298 if (lastError == 0 && !stack.empty() && stack[0]->untag()->isa<Id>()) {
1299 Expression* e = stack[0]->untag();
1300 ASTString newloc_f = e->loc().filename();
1301 if (!e->loc().isIntroduced()) {
1302 unsigned int newloc_l = e->loc().firstLine();
1303 os << " " << newloc_f << ":" << newloc_l << ":" << std::endl;
1304 os << " in variable declaration " << *e << std::endl;
1305 }
1306 } else {
1307 ASTString curloc_f;
1308 long long int curloc_l = -1;
1309
1310 for (int i = lastError - 1; i >= 0; i--) {
1311 Expression* e = stack[i]->untag();
1312 bool isCompIter = stack[i]->isTagged();
1313 ASTString newloc_f = e->loc().filename();
1314 if (e->loc().isIntroduced()) {
1315 continue;
1316 }
1317 auto newloc_l = static_cast<long long int>(e->loc().firstLine());
1318 if (newloc_f != curloc_f || newloc_l != curloc_l) {
1319 os << " " << newloc_f << ":" << newloc_l << ":" << std::endl;
1320 curloc_f = newloc_f;
1321 curloc_l = newloc_l;
1322 }
1323 if (isCompIter) {
1324 os << " with ";
1325 } else {
1326 os << " in ";
1327 }
1328 switch (e->eid()) {
1329 case Expression::E_INTLIT:
1330 os << "integer literal" << std::endl;
1331 break;
1332 case Expression::E_FLOATLIT:
1333 os << "float literal" << std::endl;
1334 break;
1335 case Expression::E_SETLIT:
1336 os << "set literal" << std::endl;
1337 break;
1338 case Expression::E_BOOLLIT:
1339 os << "bool literal" << std::endl;
1340 break;
1341 case Expression::E_STRINGLIT:
1342 os << "string literal" << std::endl;
1343 break;
1344 case Expression::E_ID:
1345 if (isCompIter) {
1346 if ((e->cast<Id>()->decl()->e() != nullptr) &&
1347 e->cast<Id>()->decl()->e()->type().isPar()) {
1348 os << *e << " = " << *e->cast<Id>()->decl()->e() << std::endl;
1349 } else {
1350 os << *e << " = <expression>" << std::endl;
1351 }
1352 } else {
1353 os << "identifier" << *e << std::endl;
1354 }
1355 break;
1356 case Expression::E_ANON:
1357 os << "anonymous variable" << std::endl;
1358 break;
1359 case Expression::E_ARRAYLIT:
1360 os << "array literal" << std::endl;
1361 break;
1362 case Expression::E_ARRAYACCESS:
1363 os << "array access" << std::endl;
1364 break;
1365 case Expression::E_COMP: {
1366 const Comprehension* cmp = e->cast<Comprehension>();
1367 if (cmp->set()) {
1368 os << "set ";
1369 } else {
1370 os << "array ";
1371 }
1372 os << "comprehension expression" << std::endl;
1373 } break;
1374 case Expression::E_ITE:
1375 os << "if-then-else expression" << std::endl;
1376 break;
1377 case Expression::E_BINOP:
1378 os << "binary " << e->cast<BinOp>()->opToString() << " operator expression" << std::endl;
1379 break;
1380 case Expression::E_UNOP:
1381 os << "unary " << e->cast<UnOp>()->opToString() << " operator expression" << std::endl;
1382 break;
1383 case Expression::E_CALL:
1384 os << "call '" << e->cast<Call>()->id() << "'" << std::endl;
1385 break;
1386 case Expression::E_VARDECL: {
1387 GCLock lock;
1388 os << "variable declaration for '" << e->cast<VarDecl>()->id()->str() << "'" << std::endl;
1389 } break;
1390 case Expression::E_LET:
1391 os << "let expression" << std::endl;
1392 break;
1393 case Expression::E_TI:
1394 os << "type-inst expression" << std::endl;
1395 break;
1396 case Expression::E_TIID:
1397 os << "type identifier" << std::endl;
1398 break;
1399 default:
1400 assert(false);
1401 os << "unknown expression (internal error)" << std::endl;
1402 break;
1403 }
1404 }
1405 }
1406 return os;
1407}
1408
1409void populate_output(Env& env) {
1410 EnvI& envi = env.envi();
1411 Model* _flat = envi.flat();
1412 Model* _output = envi.output;
1413 std::vector<Expression*> outputVars;
1414 for (VarDeclIterator it = _flat->vardecls().begin(); it != _flat->vardecls().end(); ++it) {
1415 VarDecl* vd = it->e();
1416 Annotation& ann = vd->ann();
1417 ArrayLit* dims = nullptr;
1418 bool has_output_ann = false;
1419 if (!ann.isEmpty()) {
1420 for (ExpressionSetIter ait = ann.begin(); ait != ann.end(); ++ait) {
1421 if (Call* c = (*ait)->dynamicCast<Call>()) {
1422 if (c->id() == constants().ann.output_array) {
1423 dims = c->arg(0)->cast<ArrayLit>();
1424 has_output_ann = true;
1425 break;
1426 }
1427 } else if ((*ait)->isa<Id>() &&
1428 (*ait)->cast<Id>()->str() == constants().ann.output_var->str()) {
1429 has_output_ann = true;
1430 }
1431 }
1432 if (has_output_ann) {
1433 std::ostringstream s;
1434 s << vd->id()->str() << " = ";
1435
1436 auto* vd_output = copy(env.envi(), vd)->cast<VarDecl>();
1437 Type vd_t = vd_output->type();
1438 vd_t.ti(Type::TI_PAR);
1439 vd_output->type(vd_t);
1440 vd_output->ti()->type(vd_t);
1441
1442 if (dims != nullptr) {
1443 std::vector<TypeInst*> ranges(dims->size());
1444 s << "array" << dims->size() << "d(";
1445 for (unsigned int i = 0; i < dims->size(); i++) {
1446 IntSetVal* idxset = eval_intset(envi, (*dims)[i]);
1447 ranges[i] = new TypeInst(Location().introduce(), Type(),
1448 new SetLit(Location().introduce(), idxset));
1449 s << *idxset << ",";
1450 }
1451 Type t = vd_t;
1452 vd_t.dim(static_cast<int>(dims->size()));
1453 vd_output->type(t);
1454 vd_output->ti(new TypeInst(Location().introduce(), vd_t, ranges));
1455 }
1456 _output->addItem(new VarDeclI(Location().introduce(), vd_output));
1457
1458 auto* sl = new StringLit(Location().introduce(), s.str());
1459 outputVars.push_back(sl);
1460
1461 std::vector<Expression*> showArgs(1);
1462 showArgs[0] = vd_output->id();
1463 Call* show = new Call(Location().introduce(), constants().ids.show, showArgs);
1464 show->type(Type::parstring());
1465 FunctionI* fi = _flat->matchFn(envi, show, false);
1466 assert(fi);
1467 show->decl(fi);
1468 outputVars.push_back(show);
1469 std::string ends = dims != nullptr ? ")" : "";
1470 ends += ";\n";
1471 auto* eol = new StringLit(Location().introduce(), ends);
1472 outputVars.push_back(eol);
1473 }
1474 }
1475 }
1476 auto* newOutputItem =
1477 new OutputI(Location().introduce(), new ArrayLit(Location().introduce(), outputVars));
1478 _output->addItem(newOutputItem);
1479 envi.flat()->mergeStdLib(envi, _output);
1480}
1481
1482std::ostream& EnvI::evalOutput(std::ostream& os, std::ostream& log) {
1483 GCLock lock;
1484 warnings.clear();
1485 ArrayLit* al = eval_array_lit(*this, output->outputItem()->e());
1486 bool fLastEOL = true;
1487 for (unsigned int i = 0; i < al->size(); i++) {
1488 std::string s = eval_string(*this, (*al)[i]);
1489 if (!s.empty()) {
1490 os << s;
1491 fLastEOL = ('\n' == s.back());
1492 }
1493 }
1494 if (!fLastEOL) {
1495 os << '\n';
1496 }
1497 for (auto w : warnings) {
1498 log << " WARNING: " << w << "\n";
1499 }
1500 return os;
1501}
1502
1503const std::vector<std::string>& Env::warnings() { return envi().warnings; }
1504
1505void Env::clearWarnings() { envi().warnings.clear(); }
1506
1507unsigned int Env::maxCallStack() const { return envi().maxCallStack; }
1508
1509void check_index_sets(EnvI& env, VarDecl* vd, Expression* e) {
1510 ASTExprVec<TypeInst> tis = vd->ti()->ranges();
1511 std::vector<TypeInst*> newtis(tis.size());
1512 bool needNewTypeInst = false;
1513 GCLock lock;
1514 switch (e->eid()) {
1515 case Expression::E_ID: {
1516 Id* id = e->cast<Id>();
1517 ASTExprVec<TypeInst> e_tis = id->decl()->ti()->ranges();
1518 assert(tis.size() == e_tis.size());
1519 for (unsigned int i = 0; i < tis.size(); i++) {
1520 if (tis[i]->domain() == nullptr) {
1521 newtis[i] = e_tis[i];
1522 needNewTypeInst = true;
1523 } else {
1524 IntSetVal* isv0 = eval_intset(env, tis[i]->domain());
1525 IntSetVal* isv1 = eval_intset(env, e_tis[i]->domain());
1526 if (!isv0->equal(isv1)) {
1527 std::ostringstream oss;
1528 oss << "Index set mismatch. Declared index " << (tis.size() == 1 ? "set" : "sets");
1529 oss << " of `" << *vd->id() << "' " << (tis.size() == 1 ? "is [" : "are [");
1530 for (unsigned int j = 0; j < tis.size(); j++) {
1531 if (tis[j]->domain() != nullptr) {
1532 oss << *eval_intset(env, tis[j]->domain());
1533 } else {
1534 oss << "int";
1535 }
1536 if (j < tis.size() - 1) {
1537 oss << ", ";
1538 }
1539 }
1540 oss << "], but is assigned to array `" << *id << "' with index "
1541 << (tis.size() == 1 ? "set [" : "sets [");
1542 for (unsigned int j = 0; j < e_tis.size(); j++) {
1543 oss << *eval_intset(env, e_tis[j]->domain());
1544 if (j < e_tis.size() - 1) {
1545 oss << ", ";
1546 }
1547 }
1548 oss << "]. You may need to coerce the index sets using the array" << tis.size()
1549 << "d function.";
1550 throw EvalError(env, vd->loc(), oss.str());
1551 }
1552 newtis[i] = tis[i];
1553 }
1554 }
1555 } break;
1556 case Expression::E_ARRAYLIT: {
1557 auto* al = e->cast<ArrayLit>();
1558 for (unsigned int i = 0; i < tis.size(); i++) {
1559 if (tis[i]->domain() == nullptr) {
1560 newtis[i] = new TypeInst(
1561 Location().introduce(), Type(),
1562 new SetLit(Location().introduce(), IntSetVal::a(al->min(i), al->max(i))));
1563 needNewTypeInst = true;
1564 } else if (i == 0 || al->size() != 0) {
1565 IntSetVal* isv = eval_intset(env, tis[i]->domain());
1566 assert(isv->size() <= 1);
1567 if ((isv->size() == 0 && al->min(i) <= al->max(i)) ||
1568 (isv->size() != 0 && (isv->min(0) != al->min(i) || isv->max(0) != al->max(i)))) {
1569 std::ostringstream oss;
1570 oss << "Index set mismatch. Declared index " << (tis.size() == 1 ? "set" : "sets");
1571 oss << " of `" << *vd->id() << "' " << (tis.size() == 1 ? "is [" : "are [");
1572 for (unsigned int j = 0; j < tis.size(); j++) {
1573 if (tis[j]->domain() != nullptr) {
1574 oss << *eval_intset(env, tis[j]->domain());
1575 } else {
1576 oss << "int";
1577 }
1578 if (j < tis.size() - 1) {
1579 oss << ",";
1580 }
1581 }
1582 oss << "], but is assigned to array with index "
1583 << (tis.size() == 1 ? "set [" : "sets [");
1584 for (unsigned int j = 0; j < al->dims(); j++) {
1585 oss << al->min(j) << ".." << al->max(j);
1586 if (j < al->dims() - 1) {
1587 oss << ", ";
1588 }
1589 }
1590 oss << "]. You may need to coerce the index sets using the array" << tis.size()
1591 << "d function.";
1592 throw EvalError(env, vd->loc(), oss.str());
1593 }
1594 newtis[i] = tis[i];
1595 }
1596 }
1597 } break;
1598 default:
1599 throw InternalError("not supported yet");
1600 }
1601 if (needNewTypeInst) {
1602 auto* tic = copy(env, vd->ti())->cast<TypeInst>();
1603 tic->setRanges(newtis);
1604 vd->ti(tic);
1605 }
1606}
1607
1608/// Turn \a c into domain constraints if possible.
1609/// Return whether \a c is still required in the model.
1610bool check_domain_constraints(EnvI& env, Call* c) {
1611 if (env.fopts.recordDomainChanges) {
1612 return true;
1613 }
1614 if (c->id() == constants().ids.int_.le) {
1615 Expression* e0 = c->arg(0);
1616 Expression* e1 = c->arg(1);
1617 if (e0->type().isPar() && e1->isa<Id>()) {
1618 // greater than
1619 Id* id = e1->cast<Id>();
1620 IntVal lb = eval_int(env, e0);
1621 if (id->decl()->ti()->domain() != nullptr) {
1622 IntSetVal* domain = eval_intset(env, id->decl()->ti()->domain());
1623 if (domain->min() >= lb) {
1624 return false;
1625 }
1626 if (domain->max() < lb) {
1627 env.fail();
1628 return false;
1629 }
1630 IntSetRanges dr(domain);
1631 Ranges::Const<IntVal> cr(lb, IntVal::infinity());
1632 Ranges::Inter<IntVal, IntSetRanges, Ranges::Const<IntVal>> i(dr, cr);
1633 IntSetVal* newibv = IntSetVal::ai(i);
1634 id->decl()->ti()->domain(new SetLit(Location().introduce(), newibv));
1635 id->decl()->ti()->setComputedDomain(false);
1636 } else {
1637 id->decl()->ti()->domain(
1638 new SetLit(Location().introduce(), IntSetVal::a(lb, IntVal::infinity())));
1639 }
1640 return false;
1641 }
1642 if (e1->type().isPar() && e0->isa<Id>()) {
1643 // less than
1644 Id* id = e0->cast<Id>();
1645 IntVal ub = eval_int(env, e1);
1646 if (id->decl()->ti()->domain() != nullptr) {
1647 IntSetVal* domain = eval_intset(env, id->decl()->ti()->domain());
1648 if (domain->max() <= ub) {
1649 return false;
1650 }
1651 if (domain->min() > ub) {
1652 env.fail();
1653 return false;
1654 }
1655 IntSetRanges dr(domain);
1656 Ranges::Const<IntVal> cr(-IntVal::infinity(), ub);
1657 Ranges::Inter<IntVal, IntSetRanges, Ranges::Const<IntVal>> i(dr, cr);
1658 IntSetVal* newibv = IntSetVal::ai(i);
1659 id->decl()->ti()->domain(new SetLit(Location().introduce(), newibv));
1660 id->decl()->ti()->setComputedDomain(false);
1661 } else {
1662 id->decl()->ti()->domain(
1663 new SetLit(Location().introduce(), IntSetVal::a(-IntVal::infinity(), ub)));
1664 }
1665 }
1666 } else if (c->id() == constants().ids.int_.lin_le) {
1667 auto* al_c = follow_id(c->arg(0))->cast<ArrayLit>();
1668 if (al_c->size() == 1) {
1669 auto* al_x = follow_id(c->arg(1))->cast<ArrayLit>();
1670 IntVal coeff = eval_int(env, (*al_c)[0]);
1671 IntVal y = eval_int(env, c->arg(2));
1672 IntVal lb = -IntVal::infinity();
1673 IntVal ub = IntVal::infinity();
1674 IntVal r = y % coeff;
1675 if (coeff >= 0) {
1676 ub = y / coeff;
1677 if (r < 0) {
1678 --ub;
1679 }
1680 } else {
1681 lb = y / coeff;
1682 if (r < 0) {
1683 ++lb;
1684 }
1685 }
1686 if (Id* id = (*al_x)[0]->dynamicCast<Id>()) {
1687 if (id->decl()->ti()->domain() != nullptr) {
1688 IntSetVal* domain = eval_intset(env, id->decl()->ti()->domain());
1689 if (domain->max() <= ub && domain->min() >= lb) {
1690 return false;
1691 }
1692 if (domain->min() > ub || domain->max() < lb) {
1693 env.fail();
1694 return false;
1695 }
1696 IntSetRanges dr(domain);
1697 Ranges::Const<IntVal> cr(lb, ub);
1698 Ranges::Inter<IntVal, IntSetRanges, Ranges::Const<IntVal>> i(dr, cr);
1699 IntSetVal* newibv = IntSetVal::ai(i);
1700 id->decl()->ti()->domain(new SetLit(Location().introduce(), newibv));
1701 id->decl()->ti()->setComputedDomain(false);
1702 } else {
1703 id->decl()->ti()->domain(new SetLit(Location().introduce(), IntSetVal::a(lb, ub)));
1704 }
1705 return false;
1706 }
1707 }
1708 }
1709 return true;
1710}
1711
1712KeepAlive bind(EnvI& env, Ctx ctx, VarDecl* vd, Expression* e) {
1713 assert(e == nullptr || !e->isa<VarDecl>());
1714 if (vd == constants().varIgnore) {
1715 return e;
1716 }
1717 if (Id* ident = e->dynamicCast<Id>()) {
1718 if (ident->decl() != nullptr) {
1719 auto* e_vd = follow_id_to_decl(ident)->cast<VarDecl>();
1720 e = e_vd->id();
1721 if (!env.inReverseMapVar && ctx.b != C_ROOT && e->type() == Type::varbool()) {
1722 add_ctx_ann(e_vd, ctx.b);
1723 if (e_vd != ident->decl()) {
1724 add_ctx_ann(ident->decl(), ctx.b);
1725 }
1726 }
1727 }
1728 }
1729 if (ctx.neg) {
1730 assert(e->type().bt() == Type::BT_BOOL);
1731 if (vd == constants().varTrue) {
1732 if (!isfalse(env, e)) {
1733 if (Id* id = e->dynamicCast<Id>()) {
1734 while (id != nullptr) {
1735 assert(id->decl() != nullptr);
1736 if ((id->decl()->ti()->domain() != nullptr) &&
1737 istrue(env, id->decl()->ti()->domain())) {
1738 GCLock lock;
1739 env.flatAddItem(new ConstraintI(Location().introduce(), constants().literalFalse));
1740 } else {
1741 GCLock lock;
1742 std::vector<Expression*> args(2);
1743 args[0] = id;
1744 args[1] = constants().literalFalse;
1745 Call* c = new Call(Location().introduce(), constants().ids.bool_eq, args);
1746 c->decl(env.model->matchFn(env, c, false));
1747 c->type(c->decl()->rtype(env, args, false));
1748 if (c->decl()->e() != nullptr) {
1749 flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
1750 }
1751 set_computed_domain(env, id->decl(), constants().literalFalse,
1752 id->decl()->ti()->computedDomain());
1753 }
1754 id = id->decl()->e() != nullptr ? id->decl()->e()->dynamicCast<Id>() : nullptr;
1755 }
1756 return constants().literalTrue;
1757 }
1758 GC::lock();
1759 Call* bn = new Call(e->loc(), constants().ids.bool_not, {e});
1760 bn->type(e->type());
1761 bn->decl(env.model->matchFn(env, bn, false));
1762 KeepAlive ka(bn);
1763 GC::unlock();
1764 EE ee = flat_exp(env, Ctx(), bn, vd, constants().varTrue);
1765 return ee.r;
1766 }
1767 return constants().literalTrue;
1768 }
1769 GC::lock();
1770 Call* bn = new Call(e->loc(), constants().ids.bool_not, {e});
1771 bn->type(e->type());
1772 bn->decl(env.model->matchFn(env, bn, false));
1773 KeepAlive ka(bn);
1774 GC::unlock();
1775 EE ee = flat_exp(env, Ctx(), bn, vd, constants().varTrue);
1776 return ee.r;
1777 }
1778 if (vd == constants().varTrue) {
1779 if (!istrue(env, e)) {
1780 if (Id* id = e->dynamicCast<Id>()) {
1781 assert(id->decl() != nullptr);
1782 while (id != nullptr) {
1783 if ((id->decl()->ti()->domain() != nullptr) && isfalse(env, id->decl()->ti()->domain())) {
1784 GCLock lock;
1785 env.flatAddItem(new ConstraintI(Location().introduce(), constants().literalFalse));
1786 } else if (id->decl()->ti()->domain() == nullptr) {
1787 GCLock lock;
1788 std::vector<Expression*> args(2);
1789 args[0] = id;
1790 args[1] = constants().literalTrue;
1791 Call* c = new Call(Location().introduce(), constants().ids.bool_eq, args);
1792 c->decl(env.model->matchFn(env, c, false));
1793 c->type(c->decl()->rtype(env, args, false));
1794 if (c->decl()->e() != nullptr) {
1795 flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
1796 }
1797 set_computed_domain(env, id->decl(), constants().literalTrue,
1798 id->decl()->ti()->computedDomain());
1799 }
1800 id = id->decl()->e() != nullptr ? id->decl()->e()->dynamicCast<Id>() : nullptr;
1801 }
1802 } else {
1803 GCLock lock;
1804 // extract domain information from added constraint if possible
1805 if (!e->isa<Call>() || check_domain_constraints(env, e->cast<Call>())) {
1806 env.flatAddItem(new ConstraintI(Location().introduce(), e));
1807 }
1808 }
1809 }
1810 return constants().literalTrue;
1811 }
1812 if (vd == constants().varFalse) {
1813 if (!isfalse(env, e)) {
1814 throw InternalError("not supported yet");
1815 }
1816 return constants().literalTrue;
1817 }
1818 if (vd == nullptr) {
1819 if (e == nullptr) {
1820 return nullptr;
1821 }
1822 switch (e->eid()) {
1823 case Expression::E_INTLIT:
1824 case Expression::E_FLOATLIT:
1825 case Expression::E_BOOLLIT:
1826 case Expression::E_STRINGLIT:
1827 case Expression::E_ANON:
1828 case Expression::E_ID:
1829 case Expression::E_TIID:
1830 case Expression::E_SETLIT:
1831 case Expression::E_VARDECL:
1832 case Expression::E_BINOP: // TODO: should not happen once operators are evaluated
1833 case Expression::E_UNOP: // TODO: should not happen once operators are evaluated
1834 return e;
1835 case Expression::E_ARRAYACCESS:
1836 case Expression::E_COMP:
1837 case Expression::E_ITE:
1838 case Expression::E_LET:
1839 case Expression::E_TI:
1840 throw InternalError("unevaluated expression");
1841 case Expression::E_ARRAYLIT: {
1842 GCLock lock;
1843 auto* al = e->cast<ArrayLit>();
1844 /// TODO: review if limit of 10 is a sensible choice
1845 if (al->type().bt() == Type::BT_ANN || al->size() <= 10) {
1846 return e;
1847 }
1848
1849 auto it = env.cseMapFind(al);
1850 if (it != env.cseMapEnd()) {
1851 return it->second.r()->cast<VarDecl>()->id();
1852 }
1853
1854 std::vector<TypeInst*> ranges(al->dims());
1855 for (unsigned int i = 0; i < ranges.size(); i++) {
1856 ranges[i] = new TypeInst(
1857 e->loc(), Type(),
1858 new SetLit(Location().introduce(), IntSetVal::a(al->min(i), al->max(i))));
1859 }
1860 ASTExprVec<TypeInst> ranges_v(ranges);
1861 assert(!al->type().isbot());
1862 Expression* domain = nullptr;
1863 if (al->size() > 0 && (*al)[0]->type().isint()) {
1864 IntVal min = IntVal::infinity();
1865 IntVal max = -IntVal::infinity();
1866 for (unsigned int i = 0; i < al->size(); i++) {
1867 IntBounds ib = compute_int_bounds(env, (*al)[i]);
1868 if (!ib.valid) {
1869 min = -IntVal::infinity();
1870 max = IntVal::infinity();
1871 break;
1872 }
1873 min = std::min(min, ib.l);
1874 max = std::max(max, ib.u);
1875 }
1876 if (min != -IntVal::infinity() && max != IntVal::infinity()) {
1877 domain = new SetLit(Location().introduce(), IntSetVal::a(min, max));
1878 }
1879 }
1880 auto* ti = new TypeInst(e->loc(), al->type(), ranges_v, domain);
1881 if (domain != nullptr) {
1882 ti->setComputedDomain(true);
1883 }
1884
1885 VarDecl* nvd = new_vardecl(env, ctx, ti, nullptr, nullptr, al);
1886 EE ee(nvd, nullptr);
1887 env.cseMapInsert(al, ee);
1888 env.cseMapInsert(nvd->e(), ee);
1889 return nvd->id();
1890 }
1891 case Expression::E_CALL: {
1892 if (e->type().isAnn()) {
1893 return e;
1894 }
1895 GCLock lock;
1896 /// TODO: handle array types
1897 auto* ti = new TypeInst(Location().introduce(), e->type());
1898 VarDecl* nvd = new_vardecl(env, ctx, ti, nullptr, nullptr, e);
1899 if (nvd->e()->type().bt() == Type::BT_INT && nvd->e()->type().dim() == 0) {
1900 IntSetVal* ibv = nullptr;
1901 if (nvd->e()->type().isSet()) {
1902 ibv = compute_intset_bounds(env, nvd->e());
1903 } else {
1904 IntBounds ib = compute_int_bounds(env, nvd->e());
1905 if (ib.valid) {
1906 ibv = IntSetVal::a(ib.l, ib.u);
1907 }
1908 }
1909 if (ibv != nullptr) {
1910 Id* id = nvd->id();
1911 while (id != nullptr) {
1912 bool is_computed = id->decl()->ti()->computedDomain();
1913 if (id->decl()->ti()->domain() != nullptr) {
1914 IntSetVal* domain = eval_intset(env, id->decl()->ti()->domain());
1915 IntSetRanges dr(domain);
1916 IntSetRanges ibr(ibv);
1917 Ranges::Inter<IntVal, IntSetRanges, IntSetRanges> i(dr, ibr);
1918 IntSetVal* newibv = IntSetVal::ai(i);
1919 if (ibv->card() == newibv->card()) {
1920 is_computed = true;
1921 } else {
1922 ibv = newibv;
1923 }
1924 } else {
1925 is_computed = true;
1926 }
1927 if (id->type().st() == Type::ST_PLAIN && ibv->size() == 0) {
1928 env.fail();
1929 } else {
1930 set_computed_domain(env, id->decl(), new SetLit(Location().introduce(), ibv),
1931 is_computed);
1932 }
1933 id = id->decl()->e() != nullptr ? id->decl()->e()->dynamicCast<Id>() : nullptr;
1934 }
1935 }
1936 } else if (nvd->e()->type().isbool()) {
1937 add_ctx_ann(nvd, ctx.b);
1938 } else if (nvd->e()->type().bt() == Type::BT_FLOAT && nvd->e()->type().dim() == 0) {
1939 FloatBounds fb = compute_float_bounds(env, nvd->e());
1940 FloatSetVal* ibv = LinearTraits<FloatLit>::intersectDomain(nullptr, fb.l, fb.u);
1941 if (fb.valid) {
1942 Id* id = nvd->id();
1943 while (id != nullptr) {
1944 bool is_computed = id->decl()->ti()->computedDomain();
1945 if (id->decl()->ti()->domain() != nullptr) {
1946 FloatSetVal* domain = eval_floatset(env, id->decl()->ti()->domain());
1947 FloatSetVal* ndomain = LinearTraits<FloatLit>::intersectDomain(domain, fb.l, fb.u);
1948 if ((ibv != nullptr) && ndomain == domain) {
1949 is_computed = true;
1950 } else {
1951 ibv = ndomain;
1952 }
1953 } else {
1954 is_computed = true;
1955 }
1956 if (LinearTraits<FloatLit>::domainEmpty(ibv)) {
1957 env.fail();
1958 } else {
1959 set_computed_domain(env, id->decl(), new SetLit(Location().introduce(), ibv),
1960 is_computed);
1961 }
1962 id = id->decl()->e() != nullptr ? id->decl()->e()->dynamicCast<Id>() : nullptr;
1963 }
1964 }
1965 }
1966
1967 return nvd->id();
1968 }
1969 default:
1970 assert(false);
1971 return nullptr;
1972 }
1973 } else {
1974 if (vd->e() == nullptr) {
1975 Expression* ret = e;
1976 if (e == nullptr || (e->type().isPar() && e->type().isbool())) {
1977 GCLock lock;
1978 bool isTrue = (e == nullptr || eval_bool(env, e));
1979
1980 // Check if redefinition of bool_eq exists, if yes call it
1981 std::vector<Expression*> args(2);
1982 args[0] = vd->id();
1983 args[1] = constants().boollit(isTrue);
1984 Call* c = new Call(Location().introduce(), constants().ids.bool_eq, args);
1985 c->decl(env.model->matchFn(env, c, false));
1986 c->type(c->decl()->rtype(env, args, false));
1987 bool didRewrite = false;
1988 if (c->decl()->e() != nullptr) {
1989 flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
1990 didRewrite = true;
1991 }
1992
1993 vd->e(constants().boollit(isTrue));
1994 if (vd->ti()->domain() != nullptr) {
1995 if (vd->ti()->domain() != vd->e()) {
1996 env.fail();
1997 return vd->id();
1998 }
1999 } else {
2000 set_computed_domain(env, vd, vd->e(), true);
2001 }
2002 if (didRewrite) {
2003 return vd->id();
2004 }
2005 } else {
2006 if (e->type().dim() > 0) {
2007 // Check that index sets match
2008 env.errorStack.clear();
2009 check_index_sets(env, vd, e);
2010 auto* al =
2011 Expression::dynamicCast<ArrayLit>(e->isa<Id>() ? e->cast<Id>()->decl()->e() : e);
2012 if ((al != nullptr) && (vd->ti()->domain() != nullptr) &&
2013 !vd->ti()->domain()->isa<TIId>()) {
2014 if (e->type().bt() == Type::BT_INT) {
2015 IntSetVal* isv = eval_intset(env, vd->ti()->domain());
2016 for (unsigned int i = 0; i < al->size(); i++) {
2017 if (Id* id = (*al)[i]->dynamicCast<Id>()) {
2018 if (id == constants().absent) {
2019 continue;
2020 }
2021 VarDecl* vdi = id->decl();
2022 if (vdi->ti()->domain() == nullptr) {
2023 set_computed_domain(env, vdi, vd->ti()->domain(), vdi->ti()->computedDomain());
2024 } else {
2025 IntSetVal* vdi_dom = eval_intset(env, vdi->ti()->domain());
2026 IntSetRanges isvr(isv);
2027 IntSetRanges vdi_domr(vdi_dom);
2028 Ranges::Inter<IntVal, IntSetRanges, IntSetRanges> inter(isvr, vdi_domr);
2029 IntSetVal* newdom = IntSetVal::ai(inter);
2030 if (newdom->size() == 0) {
2031 env.fail();
2032 } else {
2033 IntSetRanges vdi_domr2(vdi_dom);
2034 IntSetRanges newdomr(newdom);
2035 if (!Ranges::equal(vdi_domr2, newdomr)) {
2036 set_computed_domain(env, vdi, new SetLit(Location().introduce(), newdom),
2037 false);
2038 }
2039 }
2040 }
2041 } else {
2042 // at this point, can only be a constant
2043 assert((*al)[i]->type().isPar());
2044 if (e->type().st() == Type::ST_PLAIN) {
2045 IntVal iv = eval_int(env, (*al)[i]);
2046 if (!isv->contains(iv)) {
2047 std::ostringstream oss;
2048 oss << "value " << iv << " outside declared array domain " << *isv;
2049 env.fail(oss.str());
2050 }
2051 } else {
2052 IntSetVal* aisv = eval_intset(env, (*al)[i]);
2053 IntSetRanges aisv_r(aisv);
2054 IntSetRanges isv_r(isv);
2055 if (!Ranges::subset(aisv_r, isv_r)) {
2056 std::ostringstream oss;
2057 oss << "value " << *aisv << " outside declared array domain " << *isv;
2058 env.fail(oss.str());
2059 }
2060 }
2061 }
2062 }
2063 vd->ti()->setComputedDomain(true);
2064 } else if (e->type().bt() == Type::BT_FLOAT) {
2065 FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain());
2066 for (unsigned int i = 0; i < al->size(); i++) {
2067 if (Id* id = (*al)[i]->dynamicCast<Id>()) {
2068 VarDecl* vdi = id->decl();
2069 if (vdi->ti()->domain() == nullptr) {
2070 set_computed_domain(env, vdi, vd->ti()->domain(), vdi->ti()->computedDomain());
2071 } else {
2072 FloatSetVal* vdi_dom = eval_floatset(env, vdi->ti()->domain());
2073 FloatSetRanges fsvr(fsv);
2074 FloatSetRanges vdi_domr(vdi_dom);
2075 Ranges::Inter<FloatVal, FloatSetRanges, FloatSetRanges> inter(fsvr, vdi_domr);
2076 FloatSetVal* newdom = FloatSetVal::ai(inter);
2077 if (newdom->size() == 0) {
2078 env.fail();
2079 } else {
2080 FloatSetRanges vdi_domr2(vdi_dom);
2081 FloatSetRanges newdomr(newdom);
2082 if (!Ranges::equal(vdi_domr2, newdomr)) {
2083 set_computed_domain(env, vdi, new SetLit(Location().introduce(), newdom),
2084 false);
2085 }
2086 }
2087 }
2088 } else {
2089 // at this point, can only be a constant
2090 assert((*al)[i]->type().isPar());
2091 FloatVal fv = eval_float(env, (*al)[i]);
2092 if (!fsv->contains(fv)) {
2093 std::ostringstream oss;
2094 oss << "value " << fv << " outside declared array domain " << *fsv;
2095 env.fail(oss.str());
2096 }
2097 }
2098 }
2099 vd->ti()->setComputedDomain(true);
2100 }
2101 }
2102 } else if (Id* e_id = e->dynamicCast<Id>()) {
2103 if (e_id == vd->id()) {
2104 ret = vd->id();
2105 } else {
2106 ASTString cid;
2107 if (e->type().isint()) {
2108 if (e->type().isOpt()) {
2109 cid = ASTString("int_opt_eq");
2110 } else {
2111 cid = constants().ids.int_.eq;
2112 }
2113 } else if (e->type().isbool()) {
2114 if (e->type().isOpt()) {
2115 cid = ASTString("bool_opt_eq");
2116 } else {
2117 cid = constants().ids.bool_eq;
2118 }
2119 } else if (e->type().isSet()) {
2120 cid = constants().ids.set_eq;
2121 } else if (e->type().isfloat()) {
2122 cid = constants().ids.float_.eq;
2123 }
2124 if (cid != "" && env.hasReverseMapper(vd->id())) {
2125 GCLock lock;
2126 std::vector<Expression*> args(2);
2127 args[0] = vd->id();
2128 args[1] = e_id;
2129 Call* c = new Call(Location().introduce(), cid, args);
2130 c->decl(env.model->matchFn(env, c, false));
2131 c->type(c->decl()->rtype(env, args, false));
2132 if (c->type().isbool() && ctx.b != C_ROOT) {
2133 add_ctx_ann(vd, ctx.b);
2134 add_ctx_ann(e_id->decl(), ctx.b);
2135 }
2136 if (c->decl()->e() != nullptr) {
2137 flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
2138 ret = vd->id();
2139 vd->e(e);
2140 env.voAddExp(vd);
2141 }
2142 }
2143 }
2144 }
2145
2146 if (ret != vd->id()) {
2147 vd->e(ret);
2148 add_path_annotation(env, ret);
2149 env.voAddExp(vd);
2150 ret = vd->id();
2151 }
2152 Id* vde_id = Expression::dynamicCast<Id>(vd->e());
2153 if (vde_id == constants().absent) {
2154 // no need to do anything
2155 } else if ((vde_id != nullptr) && vde_id->decl()->ti()->domain() == nullptr) {
2156 if (vd->ti()->domain() != nullptr) {
2157 GCLock lock;
2158 Expression* vd_dom = eval_par(env, vd->ti()->domain());
2159 set_computed_domain(env, vde_id->decl(), vd_dom,
2160 vde_id->decl()->ti()->computedDomain());
2161 }
2162 } else if ((vd->e() != nullptr) && vd->e()->type().bt() == Type::BT_INT &&
2163 vd->e()->type().dim() == 0) {
2164 GCLock lock;
2165 IntSetVal* ibv = nullptr;
2166 if (vd->e()->type().isSet()) {
2167 ibv = compute_intset_bounds(env, vd->e());
2168 } else {
2169 IntBounds ib = compute_int_bounds(env, vd->e());
2170 if (ib.valid) {
2171 Call* call = vd->e()->dynamicCast<Call>();
2172 if ((call != nullptr) && call->id() == constants().ids.lin_exp) {
2173 ArrayLit* al = eval_array_lit(env, call->arg(1));
2174 if (al->size() == 1) {
2175 IntBounds check_zeroone = compute_int_bounds(env, (*al)[0]);
2176 if (check_zeroone.l == 0 && check_zeroone.u == 1) {
2177 ArrayLit* coeffs = eval_array_lit(env, call->arg(0));
2178 std::vector<IntVal> newdom(2);
2179 newdom[0] = 0;
2180 newdom[1] = eval_int(env, (*coeffs)[0]) + eval_int(env, call->arg(2));
2181 ibv = IntSetVal::a(newdom);
2182 }
2183 }
2184 }
2185 if (ibv == nullptr) {
2186 ibv = IntSetVal::a(ib.l, ib.u);
2187 }
2188 }
2189 }
2190 if (ibv != nullptr) {
2191 if (vd->ti()->domain() != nullptr) {
2192 IntSetVal* domain = eval_intset(env, vd->ti()->domain());
2193 IntSetRanges dr(domain);
2194 IntSetRanges ibr(ibv);
2195 Ranges::Inter<IntVal, IntSetRanges, IntSetRanges> i(dr, ibr);
2196 IntSetVal* newibv = IntSetVal::ai(i);
2197 if (newibv->card() == 0) {
2198 env.fail();
2199 } else if (ibv->card() == newibv->card()) {
2200 vd->ti()->setComputedDomain(true);
2201 } else {
2202 ibv = newibv;
2203 }
2204 } else {
2205 vd->ti()->setComputedDomain(true);
2206 }
2207 SetLit* ibv_l = nullptr;
2208 if (Id* rhs_ident = vd->e()->dynamicCast<Id>()) {
2209 if (rhs_ident->decl()->ti()->domain() != nullptr) {
2210 IntSetVal* rhs_domain = eval_intset(env, rhs_ident->decl()->ti()->domain());
2211 IntSetRanges dr(rhs_domain);
2212 IntSetRanges ibr(ibv);
2213 Ranges::Inter<IntVal, IntSetRanges, IntSetRanges> i(dr, ibr);
2214 IntSetVal* rhs_newibv = IntSetVal::ai(i);
2215 if (rhs_domain->card() != rhs_newibv->card()) {
2216 ibv_l = new SetLit(Location().introduce(), rhs_newibv);
2217 set_computed_domain(env, rhs_ident->decl(), ibv_l, false);
2218 if (rhs_ident->decl()->type().isOpt()) {
2219 std::vector<Expression*> args(2);
2220 args[0] = rhs_ident;
2221 args[1] = ibv_l;
2222 Call* c = new Call(Location().introduce(), "var_dom", args);
2223 c->type(Type::varbool());
2224 c->decl(env.model->matchFn(env, c, false));
2225 (void)flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
2226 }
2227 } else if (ibv->card() != rhs_newibv->card()) {
2228 ibv_l = new SetLit(Location().introduce(), rhs_newibv);
2229 }
2230 }
2231 }
2232 if (ibv_l == nullptr) {
2233 ibv_l = new SetLit(Location().introduce(), ibv);
2234 }
2235 set_computed_domain(env, vd, ibv_l, vd->ti()->computedDomain());
2236
2237 if (vd->type().isOpt()) {
2238 std::vector<Expression*> args(2);
2239 args[0] = vd->id();
2240 args[1] = ibv_l;
2241 Call* c = new Call(Location().introduce(), "var_dom", args);
2242 c->type(Type::varbool());
2243 c->decl(env.model->matchFn(env, c, false));
2244 (void)flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
2245 }
2246 }
2247 } else if ((vd->e() != nullptr) && vd->e()->type().bt() == Type::BT_FLOAT &&
2248 vd->e()->type().dim() == 0) {
2249 GCLock lock;
2250 FloatSetVal* fbv = nullptr;
2251 FloatBounds fb = compute_float_bounds(env, vd->e());
2252 if (fb.valid) {
2253 fbv = FloatSetVal::a(fb.l, fb.u);
2254 }
2255 if (fbv != nullptr) {
2256 if (vd->ti()->domain() != nullptr) {
2257 FloatSetVal* domain = eval_floatset(env, vd->ti()->domain());
2258 FloatSetRanges dr(domain);
2259 FloatSetRanges fbr(fbv);
2260 Ranges::Inter<FloatVal, FloatSetRanges, FloatSetRanges> i(dr, fbr);
2261 FloatSetVal* newfbv = FloatSetVal::ai(i);
2262 if (newfbv->size() == 0) {
2263 env.fail();
2264 }
2265 FloatSetRanges dr_eq(domain);
2266 FloatSetRanges newfbv_eq(newfbv);
2267 if (Ranges::equal(dr_eq, newfbv_eq)) {
2268 vd->ti()->setComputedDomain(true);
2269 } else {
2270 fbv = newfbv;
2271 }
2272 } else {
2273 vd->ti()->setComputedDomain(true);
2274 }
2275 SetLit* fbv_l = nullptr;
2276 if (Id* rhs_ident = vd->e()->dynamicCast<Id>()) {
2277 if (rhs_ident->decl()->ti()->domain() != nullptr) {
2278 FloatSetVal* rhs_domain = eval_floatset(env, rhs_ident->decl()->ti()->domain());
2279 FloatSetRanges dr(rhs_domain);
2280 FloatSetRanges ibr(fbv);
2281 Ranges::Inter<FloatVal, FloatSetRanges, FloatSetRanges> i(dr, ibr);
2282 FloatSetVal* rhs_newfbv = FloatSetVal::ai(i);
2283 if (rhs_domain->card() != rhs_newfbv->card()) {
2284 fbv_l = new SetLit(Location().introduce(), rhs_newfbv);
2285 set_computed_domain(env, rhs_ident->decl(), fbv_l, false);
2286 if (rhs_ident->decl()->type().isOpt()) {
2287 std::vector<Expression*> args(2);
2288 args[0] = rhs_ident;
2289 args[1] = fbv_l;
2290 Call* c = new Call(Location().introduce(), "var_dom", args);
2291 c->type(Type::varbool());
2292 c->decl(env.model->matchFn(env, c, false));
2293 (void)flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
2294 }
2295 } else if (fbv->card() != rhs_newfbv->card()) {
2296 fbv_l = new SetLit(Location().introduce(), rhs_newfbv);
2297 }
2298 }
2299 }
2300 fbv_l = new SetLit(Location().introduce(), fbv);
2301 set_computed_domain(env, vd, fbv_l, vd->ti()->computedDomain());
2302
2303 if (vd->type().isOpt()) {
2304 std::vector<Expression*> args(2);
2305 args[0] = vd->id();
2306 args[1] = fbv_l;
2307 Call* c = new Call(Location().introduce(), "var_dom", args);
2308 c->type(Type::varbool());
2309 c->decl(env.model->matchFn(env, c, false));
2310 (void)flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
2311 }
2312 }
2313 }
2314 }
2315 return ret;
2316 }
2317 if (vd == e) {
2318 return vd->id();
2319 }
2320 if (vd->e() != e) {
2321 e = follow_id_to_decl(e);
2322 if (vd == e) {
2323 return vd->id();
2324 }
2325 switch (e->eid()) {
2326 case Expression::E_BOOLLIT: {
2327 Id* id = vd->id();
2328 while (id != nullptr) {
2329 if ((id->decl()->ti()->domain() != nullptr) &&
2330 eval_bool(env, id->decl()->ti()->domain()) == e->cast<BoolLit>()->v()) {
2331 return constants().literalTrue;
2332 }
2333 if ((id->decl()->ti()->domain() != nullptr) &&
2334 eval_bool(env, id->decl()->ti()->domain()) != e->cast<BoolLit>()->v()) {
2335 GCLock lock;
2336 env.flatAddItem(new ConstraintI(Location().introduce(), constants().literalFalse));
2337 } else {
2338 GCLock lock;
2339 std::vector<Expression*> args(2);
2340 args[0] = id;
2341 args[1] = e;
2342 Call* c = new Call(Location().introduce(), constants().ids.bool_eq, args);
2343 c->decl(env.model->matchFn(env, c, false));
2344 c->type(c->decl()->rtype(env, args, false));
2345 if (c->decl()->e() != nullptr) {
2346 flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
2347 }
2348 set_computed_domain(env, id->decl(), e, id->decl()->ti()->computedDomain());
2349 }
2350 id = id->decl()->e() != nullptr ? id->decl()->e()->dynamicCast<Id>() : nullptr;
2351 }
2352 return constants().literalTrue;
2353 }
2354 case Expression::E_VARDECL: {
2355 auto* e_vd = e->cast<VarDecl>();
2356 if (vd->e() == e_vd->id() || e_vd->e() == vd->id()) {
2357 return vd->id();
2358 }
2359 if (e->type().dim() != 0) {
2360 throw InternalError("not supported yet");
2361 }
2362 GCLock lock;
2363 ASTString cid;
2364 if (e->type().isint()) {
2365 cid = constants().ids.int_.eq;
2366 } else if (e->type().isbool()) {
2367 cid = constants().ids.bool_eq;
2368 } else if (e->type().isSet()) {
2369 cid = constants().ids.set_eq;
2370 } else if (e->type().isfloat()) {
2371 cid = constants().ids.float_.eq;
2372 } else {
2373 throw InternalError("not yet implemented");
2374 }
2375 std::vector<Expression*> args(2);
2376 args[0] = vd->id();
2377 args[1] = e_vd->id();
2378 Call* c = new Call(vd->loc().introduce(), cid, args);
2379 c->decl(env.model->matchFn(env, c, false));
2380 c->type(c->decl()->rtype(env, args, false));
2381 flat_exp(env, Ctx(), c, constants().varTrue, constants().varTrue);
2382 return vd->id();
2383 }
2384 case Expression::E_CALL: {
2385 Call* c = e->cast<Call>();
2386 GCLock lock;
2387 Call* nc;
2388 std::vector<Expression*> args;
2389 if (c->id() == constants().ids.lin_exp) {
2390 auto* le_c = follow_id(c->arg(0))->cast<ArrayLit>();
2391 std::vector<Expression*> ncoeff(le_c->size());
2392 for (auto i = static_cast<unsigned int>(ncoeff.size()); (i--) != 0U;) {
2393 ncoeff[i] = (*le_c)[i];
2394 }
2395 ncoeff.push_back(IntLit::a(-1));
2396 args.push_back(new ArrayLit(Location().introduce(), ncoeff));
2397 args[0]->type(le_c->type());
2398 auto* le_x = follow_id(c->arg(1))->cast<ArrayLit>();
2399 std::vector<Expression*> nx(le_x->size());
2400 for (auto i = static_cast<unsigned int>(nx.size()); (i--) != 0U;) {
2401 nx[i] = (*le_x)[i];
2402 }
2403 nx.push_back(vd->id());
2404 args.push_back(new ArrayLit(Location().introduce(), nx));
2405 args[1]->type(le_x->type());
2406 args.push_back(c->arg(2));
2407 nc = new Call(c->loc().introduce(), constants().ids.lin_exp, args);
2408 nc->decl(env.model->matchFn(env, nc, false));
2409 if (nc->decl() == nullptr) {
2410 std::ostringstream ss;
2411 ss << "undeclared function or predicate " << nc->id();
2412 throw InternalError(ss.str());
2413 }
2414 nc->type(nc->decl()->rtype(env, args, false));
2415 auto* bop = new BinOp(nc->loc(), nc, BOT_EQ, IntLit::a(0));
2416 bop->type(Type::varbool());
2417 flat_exp(env, Ctx(), bop, constants().varTrue, constants().varTrue);
2418 return vd->id();
2419 }
2420 args.resize(c->argCount());
2421 for (auto i = static_cast<unsigned int>(args.size()); (i--) != 0U;) {
2422 args[i] = c->arg(i);
2423 }
2424 args.push_back(vd->id());
2425 ASTString nid = c->id();
2426
2427 if (c->id() == constants().ids.exists) {
2428 nid = constants().ids.array_bool_or;
2429 } else if (c->id() == constants().ids.forall) {
2430 nid = constants().ids.array_bool_and;
2431 } else if (vd->type().isbool()) {
2432 if (env.fopts.enableHalfReification && vd->ann().contains(constants().ctx.pos)) {
2433 nid = env.halfReifyId(c->id());
2434 if (env.model->matchFn(env, nid, args, false) == nullptr) {
2435 nid = env.reifyId(c->id());
2436 }
2437 } else {
2438 nid = env.reifyId(c->id());
2439 }
2440 }
2441 nc = new Call(c->loc().introduce(), nid, args);
2442 FunctionI* nc_decl = env.model->matchFn(env, nc, false);
2443 if (nc_decl == nullptr) {
2444 std::ostringstream ss;
2445 ss << "undeclared function or predicate " << nc->id();
2446 throw InternalError(ss.str());
2447 }
2448 nc->decl(nc_decl);
2449 nc->type(nc->decl()->rtype(env, args, false));
2450 make_defined_var(vd, nc);
2451 flat_exp(env, Ctx(), nc, constants().varTrue, constants().varTrue);
2452 return vd->id();
2453 } break;
2454 default:
2455 throw InternalError("not supported yet");
2456 }
2457 } else {
2458 return e;
2459 }
2460 }
2461}
2462
2463KeepAlive conj(EnvI& env, VarDecl* b, const Ctx& ctx, const std::vector<EE>& e) {
2464 if (!ctx.neg) {
2465 std::vector<Expression*> nontrue;
2466 for (const auto& i : e) {
2467 if (istrue(env, i.b())) {
2468 continue;
2469 }
2470 if (isfalse(env, i.b())) {
2471 return bind(env, Ctx(), b, constants().literalFalse);
2472 }
2473 nontrue.push_back(i.b());
2474 }
2475 if (nontrue.empty()) {
2476 return bind(env, Ctx(), b, constants().literalTrue);
2477 }
2478 if (nontrue.size() == 1) {
2479 return bind(env, ctx, b, nontrue[0]);
2480 }
2481 if (b == constants().varTrue) {
2482 for (auto& i : nontrue) {
2483 bind(env, ctx, b, i);
2484 }
2485 return constants().literalTrue;
2486 }
2487 GC::lock();
2488 std::vector<Expression*> args;
2489 auto* al = new ArrayLit(Location().introduce(), nontrue);
2490 al->type(Type::varbool(1));
2491 args.push_back(al);
2492 Call* ret = new Call(nontrue[0]->loc().introduce(), constants().ids.forall, args);
2493 ret->decl(env.model->matchFn(env, ret, false));
2494 ret->type(ret->decl()->rtype(env, args, false));
2495 KeepAlive ka(ret);
2496 GC::unlock();
2497 return flat_exp(env, ctx, ret, b, constants().varTrue).r;
2498 }
2499 Ctx nctx = ctx;
2500 nctx.neg = false;
2501 nctx.b = -nctx.b;
2502 // negated
2503 std::vector<Expression*> nonfalse;
2504 for (const auto& i : e) {
2505 if (istrue(env, i.b())) {
2506 continue;
2507 }
2508 if (isfalse(env, i.b())) {
2509 return bind(env, Ctx(), b, constants().literalTrue);
2510 }
2511 nonfalse.push_back(i.b());
2512 }
2513 if (nonfalse.empty()) {
2514 return bind(env, Ctx(), b, constants().literalFalse);
2515 }
2516 if (nonfalse.size() == 1) {
2517 GC::lock();
2518 UnOp* uo = new UnOp(nonfalse[0]->loc(), UOT_NOT, nonfalse[0]);
2519 uo->type(Type::varbool());
2520 KeepAlive ka(uo);
2521 GC::unlock();
2522 return flat_exp(env, nctx, uo, b, constants().varTrue).r;
2523 }
2524 if (b == constants().varFalse) {
2525 for (auto& i : nonfalse) {
2526 bind(env, nctx, b, i);
2527 }
2528 return constants().literalFalse;
2529 }
2530 GC::lock();
2531 std::vector<Expression*> args;
2532 for (auto& i : nonfalse) {
2533 UnOp* uo = new UnOp(i->loc(), UOT_NOT, i);
2534 uo->type(Type::varbool());
2535 i = uo;
2536 }
2537 auto* al = new ArrayLit(Location().introduce(), nonfalse);
2538 al->type(Type::varbool(1));
2539 args.push_back(al);
2540 Call* ret = new Call(Location().introduce(), constants().ids.exists, args);
2541 ret->decl(env.model->matchFn(env, ret, false));
2542 ret->type(ret->decl()->rtype(env, args, false));
2543 assert(ret->decl());
2544 KeepAlive ka(ret);
2545 GC::unlock();
2546 return flat_exp(env, nctx, ret, b, constants().varTrue).r;
2547}
2548
2549TypeInst* eval_typeinst(EnvI& env, const Ctx& ctx, VarDecl* vd) {
2550 bool hasTiVars = (vd->ti()->domain() != nullptr) && vd->ti()->domain()->isa<TIId>();
2551 for (unsigned int i = 0; i < vd->ti()->ranges().size(); i++) {
2552 hasTiVars = hasTiVars || ((vd->ti()->ranges()[i]->domain() != nullptr) &&
2553 vd->ti()->ranges()[i]->domain()->isa<TIId>());
2554 }
2555 if (hasTiVars) {
2556 assert(vd->e());
2557 if (vd->e()->type().dim() == 0) {
2558 return new TypeInst(Location().introduce(), vd->e()->type());
2559 }
2560 ArrayLit* al = eval_array_lit(env, vd->e());
2561 std::vector<TypeInst*> dims(al->dims());
2562 for (unsigned int i = 0; i < dims.size(); i++) {
2563 dims[i] =
2564 new TypeInst(Location().introduce(), Type(),
2565 new SetLit(Location().introduce(), IntSetVal::a(al->min(i), al->max(i))));
2566 }
2567 return new TypeInst(Location().introduce(), vd->e()->type(), dims,
2568 flat_cv_exp(env, ctx, vd->ti()->domain())());
2569 }
2570 std::vector<TypeInst*> dims(vd->ti()->ranges().size());
2571 for (unsigned int i = 0; i < vd->ti()->ranges().size(); i++) {
2572 if (vd->ti()->ranges()[i]->domain() != nullptr) {
2573 KeepAlive range = flat_cv_exp(env, ctx, vd->ti()->ranges()[i]->domain());
2574 IntSetVal* isv = eval_intset(env, range());
2575 if (isv->size() > 1) {
2576 throw EvalError(env, vd->ti()->ranges()[i]->domain()->loc(),
2577 "array index set must be contiguous range");
2578 }
2579 auto* sl = new SetLit(vd->ti()->ranges()[i]->loc(), isv);
2580 sl->type(Type::parsetint());
2581 dims[i] = new TypeInst(vd->ti()->ranges()[i]->loc(), Type(), sl);
2582 } else {
2583 dims[i] = new TypeInst(vd->ti()->ranges()[i]->loc(), Type(), nullptr);
2584 }
2585 }
2586 Type t = ((vd->e() != nullptr) && !vd->e()->type().isbot()) ? vd->e()->type() : vd->ti()->type();
2587 return new TypeInst(vd->ti()->loc(), t, dims, flat_cv_exp(env, ctx, vd->ti()->domain())());
2588}
2589
2590KeepAlive flat_cv_exp(EnvI& env, Ctx ctx, Expression* e) {
2591 if (e == nullptr) {
2592 return nullptr;
2593 }
2594 GCLock lock;
2595 if (e->type().isPar() && !e->type().cv()) {
2596 return eval_par(env, e);
2597 }
2598 if (e->type().isvar()) {
2599 EE ee = flat_exp(env, ctx, e, nullptr, nullptr);
2600 if (isfalse(env, ee.b())) {
2601 throw ResultUndefinedError(env, e->loc(), "");
2602 }
2603 return ee.r();
2604 }
2605 switch (e->eid()) {
2606 case Expression::E_INTLIT:
2607 case Expression::E_FLOATLIT:
2608 case Expression::E_BOOLLIT:
2609 case Expression::E_STRINGLIT:
2610 case Expression::E_TIID:
2611 case Expression::E_VARDECL:
2612 case Expression::E_TI:
2613 case Expression::E_ANON:
2614 assert(false);
2615 return nullptr;
2616 case Expression::E_ID: {
2617 Id* id = e->cast<Id>();
2618 return flat_cv_exp(env, ctx, id->decl()->e());
2619 }
2620 case Expression::E_SETLIT: {
2621 auto* sl = e->cast<SetLit>();
2622 if ((sl->isv() != nullptr) || (sl->fsv() != nullptr)) {
2623 return sl;
2624 }
2625 std::vector<Expression*> es(sl->v().size());
2626 GCLock lock;
2627 for (unsigned int i = 0; i < sl->v().size(); i++) {
2628 es[i] = flat_cv_exp(env, ctx, sl->v()[i])();
2629 }
2630 auto* sl_ret = new SetLit(Location().introduce(), es);
2631 Type t = sl->type();
2632 t.cv(false);
2633 sl_ret->type(t);
2634 return eval_par(env, sl_ret);
2635 }
2636 case Expression::E_ARRAYLIT: {
2637 auto* al = e->cast<ArrayLit>();
2638 std::vector<Expression*> es(al->size());
2639 GCLock lock;
2640 for (unsigned int i = 0; i < al->size(); i++) {
2641 es[i] = flat_cv_exp(env, ctx, (*al)[i])();
2642 }
2643 std::vector<std::pair<int, int>> dims(al->dims());
2644 for (int i = 0; i < al->dims(); i++) {
2645 dims[i] = std::make_pair(al->min(i), al->max(i));
2646 }
2647 Expression* al_ret = eval_par(env, new ArrayLit(Location().introduce(), es, dims));
2648 Type t = al->type();
2649 t.cv(false);
2650 al_ret->type(t);
2651 return al_ret;
2652 }
2653 case Expression::E_ARRAYACCESS: {
2654 auto* aa = e->cast<ArrayAccess>();
2655 GCLock lock;
2656 Expression* av = flat_cv_exp(env, ctx, aa->v())();
2657 std::vector<Expression*> idx(aa->idx().size());
2658 for (unsigned int i = 0; i < aa->idx().size(); i++) {
2659 idx[i] = flat_cv_exp(env, ctx, aa->idx()[i])();
2660 }
2661 auto* aa_ret = new ArrayAccess(Location().introduce(), av, idx);
2662 Type t = aa->type();
2663 t.cv(false);
2664 aa_ret->type(t);
2665 return eval_par(env, aa_ret);
2666 }
2667 case Expression::E_COMP: {
2668 auto* c = e->cast<Comprehension>();
2669 GCLock lock;
2670 class EvalFlatCvExp : public EvalBase {
2671 public:
2672 Ctx ctx;
2673 EvalFlatCvExp(Ctx& ctx0) : ctx(ctx0) {}
2674 typedef Expression* ArrayVal;
2675 Expression* e(EnvI& env, Expression* e) const { return flat_cv_exp(env, ctx, e)(); }
2676 static Expression* exp(Expression* e) { return e; }
2677 static Expression* flatten(EnvI& env, Expression* e0) {
2678 return flat_exp(env, Ctx(), e0, nullptr, constants().varTrue).r();
2679 }
2680
2681 } eval(ctx);
2682 std::vector<Expression*> a = eval_comp<EvalFlatCvExp>(env, eval, c);
2683
2684 Type t = Type::bot();
2685 bool allPar = true;
2686 for (auto& i : a) {
2687 if (t == Type::bot()) {
2688 t = i->type();
2689 }
2690 if (!i->type().isPar()) {
2691 allPar = false;
2692 }
2693 }
2694 if (!allPar) {
2695 t.ti(Type::TI_VAR);
2696 }
2697 if (c->set()) {
2698 t.st(Type::ST_SET);
2699 } else {
2700 t.dim(c->type().dim());
2701 }
2702 t.cv(false);
2703 if (c->set()) {
2704 if (c->type().isPar() && allPar) {
2705 auto* sl = new SetLit(c->loc().introduce(), a);
2706 sl->type(t);
2707 Expression* slr = eval_par(env, sl);
2708 slr->type(t);
2709 return slr;
2710 }
2711 throw InternalError("var set comprehensions not supported yet");
2712 }
2713 auto* alr = new ArrayLit(Location().introduce(), a);
2714 alr->type(t);
2715 alr->flat(true);
2716 return alr;
2717 }
2718 case Expression::E_ITE: {
2719 ITE* ite = e->cast<ITE>();
2720 for (int i = 0; i < ite->size(); i++) {
2721 KeepAlive ka = flat_cv_exp(env, ctx, ite->ifExpr(i));
2722 if (eval_bool(env, ka())) {
2723 return flat_cv_exp(env, ctx, ite->thenExpr(i));
2724 }
2725 }
2726 return flat_cv_exp(env, ctx, ite->elseExpr());
2727 }
2728 case Expression::E_BINOP: {
2729 auto* bo = e->cast<BinOp>();
2730 if (bo->op() == BOT_AND) {
2731 GCLock lock;
2732 Expression* lhs = flat_cv_exp(env, ctx, bo->lhs())();
2733 if (!eval_bool(env, lhs)) {
2734 return constants().literalFalse;
2735 }
2736 return eval_par(env, flat_cv_exp(env, ctx, bo->rhs())());
2737 }
2738 if (bo->op() == BOT_OR) {
2739 GCLock lock;
2740 Expression* lhs = flat_cv_exp(env, ctx, bo->lhs())();
2741 if (eval_bool(env, lhs)) {
2742 return constants().literalTrue;
2743 }
2744 return eval_par(env, flat_cv_exp(env, ctx, bo->rhs())());
2745 }
2746 GCLock lock;
2747 auto* nbo = new BinOp(bo->loc().introduce(), flat_cv_exp(env, ctx, bo->lhs())(), bo->op(),
2748 flat_cv_exp(env, ctx, bo->rhs())());
2749 nbo->type(bo->type());
2750 nbo->decl(bo->decl());
2751 return eval_par(env, nbo);
2752 }
2753 case Expression::E_UNOP: {
2754 UnOp* uo = e->cast<UnOp>();
2755 GCLock lock;
2756 UnOp* nuo = new UnOp(uo->loc(), uo->op(), flat_cv_exp(env, ctx, uo->e())());
2757 nuo->type(uo->type());
2758 return eval_par(env, nuo);
2759 }
2760 case Expression::E_CALL: {
2761 Call* c = e->cast<Call>();
2762 if (c->id() == "mzn_in_root_context") {
2763 return constants().boollit(ctx.b == C_ROOT);
2764 }
2765 if (ctx.b == C_ROOT && (c->decl()->e() != nullptr) && c->decl()->e()->isa<BoolLit>()) {
2766 bool allBool = true;
2767 for (unsigned int i = 0; i < c->argCount(); i++) {
2768 if (c->arg(i)->type().bt() != Type::BT_BOOL) {
2769 allBool = false;
2770 break;
2771 }
2772 }
2773 if (allBool) {
2774 return c->decl()->e();
2775 }
2776 }
2777 std::vector<Expression*> args(c->argCount());
2778 GCLock lock;
2779 for (unsigned int i = 0; i < c->argCount(); i++) {
2780 Ctx c_mix;
2781 c_mix.b = C_MIX;
2782 c_mix.i = C_MIX;
2783 args[i] = flat_cv_exp(env, c_mix, c->arg(i))();
2784 }
2785 Call* nc = new Call(c->loc(), c->id(), args);
2786 nc->decl(c->decl());
2787 Type nct(c->type());
2788 if ((nc->decl()->e() != nullptr) && nc->decl()->e()->type().cv()) {
2789 nct.cv(false);
2790 nct.ti(Type::TI_VAR);
2791 nc->type(nct);
2792 EE ee = flat_exp(env, ctx, nc, nullptr, nullptr);
2793 if (isfalse(env, ee.b())) {
2794 std::ostringstream ss;
2795 ss << "evaluation of `" << nc->id() << "was undefined";
2796 throw ResultUndefinedError(env, e->loc(), ss.str());
2797 }
2798 return ee.r();
2799 }
2800 nc->type(nct);
2801 return eval_par(env, nc);
2802 }
2803 case Expression::E_LET: {
2804 Let* l = e->cast<Let>();
2805 EE ee = flat_exp(env, ctx, l, nullptr, nullptr);
2806 if (isfalse(env, ee.b())) {
2807 throw ResultUndefinedError(env, e->loc(), "evaluation of let expression was undefined");
2808 }
2809 return ee.r();
2810 }
2811 }
2812 throw InternalError("internal error");
2813}
2814
2815class ItemTimer {
2816public:
2817 using TimingMap =
2818 std::map<std::pair<ASTString, unsigned int>, std::chrono::high_resolution_clock::duration>;
2819 ItemTimer(const Location& loc, TimingMap* tm)
2820 : _loc(loc), _tm(tm), _start(std::chrono::high_resolution_clock::now()) {}
2821
2822 ~ItemTimer() {
2823 try {
2824 if (_tm != nullptr) {
2825 std::chrono::high_resolution_clock::time_point end =
2826 std::chrono::high_resolution_clock::now();
2827 unsigned int line = _loc.firstLine();
2828 auto it = _tm->find(std::make_pair(_loc.filename(), line));
2829 if (it != _tm->end()) {
2830 it->second += end - _start;
2831 } else {
2832 _tm->insert(std::make_pair(std::make_pair(_loc.filename(), line), end - _start));
2833 }
2834 }
2835 } catch (std::exception& e) {
2836 assert(false); // Invariant: Operations on the TimingMap will not throw an exception
2837 }
2838 }
2839
2840private:
2841 const Location& _loc;
2842 TimingMap* _tm;
2843 std::chrono::high_resolution_clock::time_point _start;
2844};
2845
2846void flatten(Env& e, FlatteningOptions opt) {
2847 ItemTimer::TimingMap timingMap_o;
2848 ItemTimer::TimingMap* timingMap = opt.detailedTiming ? &timingMap_o : nullptr;
2849 try {
2850 EnvI& env = e.envi();
2851 env.fopts = opt;
2852
2853 bool onlyRangeDomains = false;
2854 if (opt.onlyRangeDomains) {
2855 onlyRangeDomains = true; // compulsory
2856 } else {
2857 GCLock lock;
2858 Call* check_only_range =
2859 new Call(Location(), "mzn_check_only_range_domains", std::vector<Expression*>());
2860 check_only_range->type(Type::parbool());
2861 check_only_range->decl(env.model->matchFn(e.envi(), check_only_range, false));
2862 onlyRangeDomains = eval_bool(e.envi(), check_only_range);
2863 }
2864
2865 bool hadSolveItem = false;
2866 // Flatten main model
2867 class FV : public ItemVisitor {
2868 public:
2869 EnvI& env;
2870 bool& hadSolveItem;
2871 ItemTimer::TimingMap* timingMap;
2872 FV(EnvI& env0, bool& hadSolveItem0, ItemTimer::TimingMap* timingMap0)
2873 : env(env0), hadSolveItem(hadSolveItem0), timingMap(timingMap0) {}
2874 bool enter(Item* i) const { return !(i->isa<ConstraintI>() && env.failed()); }
2875 void vVarDeclI(VarDeclI* v) {
2876 ItemTimer item_timer(v->loc(), timingMap);
2877 v->e()->ann().remove(constants().ann.output_var);
2878 v->e()->ann().removeCall(constants().ann.output_array);
2879 if (v->e()->ann().contains(constants().ann.output_only)) {
2880 return;
2881 }
2882 if (v->e()->type().isPar() && !v->e()->type().isOpt() && !v->e()->type().cv() &&
2883 v->e()->type().dim() > 0 && v->e()->ti()->domain() == nullptr &&
2884 (v->e()->type().bt() == Type::BT_INT || v->e()->type().bt() == Type::BT_FLOAT)) {
2885 // Compute bounds for array literals
2886 CallStackItem csi(env, v->e());
2887 GCLock lock;
2888 ArrayLit* al = eval_array_lit(env, v->e()->e());
2889 v->e()->e(al);
2890 check_index_sets(env, v->e(), v->e()->e());
2891 if (al->size() > 0) {
2892 if (v->e()->type().bt() == Type::BT_INT && v->e()->type().st() == Type::ST_PLAIN) {
2893 IntVal lb = IntVal::infinity();
2894 IntVal ub = -IntVal::infinity();
2895 for (unsigned int i = 0; i < al->size(); i++) {
2896 IntVal vi = eval_int(env, (*al)[i]);
2897 lb = std::min(lb, vi);
2898 ub = std::max(ub, vi);
2899 }
2900 GCLock lock;
2901 set_computed_domain(env, v->e(),
2902 new SetLit(Location().introduce(), IntSetVal::a(lb, ub)), true);
2903 } else if (v->e()->type().bt() == Type::BT_FLOAT &&
2904 v->e()->type().st() == Type::ST_PLAIN) {
2905 FloatVal lb = FloatVal::infinity();
2906 FloatVal ub = -FloatVal::infinity();
2907 for (unsigned int i = 0; i < al->size(); i++) {
2908 FloatVal vi = eval_float(env, (*al)[i]);
2909 lb = std::min(lb, vi);
2910 ub = std::max(ub, vi);
2911 }
2912 GCLock lock;
2913 set_computed_domain(env, v->e(),
2914 new SetLit(Location().introduce(), FloatSetVal::a(lb, ub)), true);
2915 }
2916 }
2917 } else if (v->e()->type().isvar() || v->e()->type().isAnn()) {
2918 (void)flatten_id(env, Ctx(), v->e()->id(), nullptr, constants().varTrue, true);
2919 } else {
2920 if (v->e()->e() == nullptr) {
2921 if (!v->e()->type().isAnn()) {
2922 throw EvalError(env, v->e()->loc(), "Undefined parameter", v->e()->id()->v());
2923 }
2924 } else {
2925 CallStackItem csi(env, v->e());
2926 GCLock lock;
2927 Location v_loc = v->e()->e()->loc();
2928 if (!v->e()->e()->type().cv()) {
2929 v->e()->e(eval_par(env, v->e()->e()));
2930 } else {
2931 EE ee = flat_exp(env, Ctx(), v->e()->e(), nullptr, constants().varTrue);
2932 v->e()->e(ee.r());
2933 }
2934 check_par_declaration(env, v->e());
2935 }
2936 }
2937 }
2938 void vConstraintI(ConstraintI* ci) {
2939 ItemTimer item_timer(ci->loc(), timingMap);
2940 (void)flat_exp(env, Ctx(), ci->e(), constants().varTrue, constants().varTrue);
2941 }
2942 void vSolveI(SolveI* si) {
2943 if (hadSolveItem) {
2944 throw FlatteningError(env, si->loc(), "Only one solve item allowed");
2945 }
2946 ItemTimer item_timer(si->loc(), timingMap);
2947 hadSolveItem = true;
2948 GCLock lock;
2949 SolveI* nsi = nullptr;
2950 switch (si->st()) {
2951 case SolveI::ST_SAT:
2952 nsi = SolveI::sat(Location());
2953 break;
2954 case SolveI::ST_MIN: {
2955 Ctx ctx;
2956 ctx.i = C_NEG;
2957 nsi = SolveI::min(Location().introduce(),
2958 flat_exp(env, ctx, si->e(), nullptr, constants().varTrue).r());
2959 } break;
2960 case SolveI::ST_MAX: {
2961 Ctx ctx;
2962 ctx.i = C_POS;
2963 nsi = SolveI::max(Location().introduce(),
2964 flat_exp(env, Ctx(), si->e(), nullptr, constants().varTrue).r());
2965 } break;
2966 }
2967 for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++it) {
2968 auto* c = (*it)->dynamicCast<Call>();
2969 if (c != nullptr && c->id() == "on_restart") {
2970 assert(c->argCount() == 1);
2971 auto* pred = c->arg(0)->cast<StringLit>();
2972 Call* nc = new Call(c->loc().introduce(), pred->v(), std::vector<Expression*>());
2973 nc->type(Type::varbool());
2974 FunctionI* decl = env.model->matchFn(env, nc, false);
2975 if (decl == nullptr) {
2976 std::ostringstream ss;
2977 ss << "Unknown predicate `" << pred->v()
2978 << "' found while evaluating on_restart annotation";
2979 throw FlatteningError(env, pred->loc(), ss.str());
2980 }
2981 nc->decl(decl);
2982 env.flatAddItem(new ConstraintI(c->loc().introduce(), nc));
2983 } else {
2984 nsi->ann().add(flat_exp(env, Ctx(), *it, nullptr, constants().varTrue).r());
2985 }
2986 }
2987 env.flatAddItem(nsi);
2988 }
2989 } _fv(env, hadSolveItem, timingMap);
2990 iter_items<FV>(_fv, e.model());
2991
2992 if (!hadSolveItem) {
2993 GCLock lock;
2994 e.envi().flatAddItem(SolveI::sat(Location().introduce()));
2995 }
2996
2997 // Create output model
2998 if (opt.keepOutputInFzn) {
2999 copy_output(env);
3000 } else {
3001 create_output(env, opt.outputMode, opt.outputObjective, opt.outputOutputItem, opt.hasChecker);
3002 }
3003
3004 // Flatten remaining redefinitions
3005 Model& m = *e.flat();
3006 int startItem = 0;
3007 int endItem = static_cast<int>(m.size()) - 1;
3008
3009 FunctionI* int_lin_eq;
3010 {
3011 std::vector<Type> int_lin_eq_t(3);
3012 int_lin_eq_t[0] = Type::parint(1);
3013 int_lin_eq_t[1] = Type::varint(1);
3014 int_lin_eq_t[2] = Type::parint(0);
3015 GCLock lock;
3016 FunctionI* fi = env.model->matchFn(env, constants().ids.int_.lin_eq, int_lin_eq_t, false);
3017 int_lin_eq = ((fi != nullptr) && (fi->e() != nullptr)) ? fi : nullptr;
3018 }
3019 FunctionI* float_lin_eq;
3020 {
3021 std::vector<Type> float_lin_eq_t(3);
3022 float_lin_eq_t[0] = Type::parfloat(1);
3023 float_lin_eq_t[1] = Type::varfloat(1);
3024 float_lin_eq_t[2] = Type::parfloat(0);
3025 GCLock lock;
3026 FunctionI* fi = env.model->matchFn(env, constants().ids.float_.lin_eq, float_lin_eq_t, false);
3027 float_lin_eq = ((fi != nullptr) && (fi->e() != nullptr)) ? fi : nullptr;
3028 }
3029 FunctionI* array_bool_and;
3030 FunctionI* array_bool_or;
3031 FunctionI* array_bool_clause;
3032 FunctionI* array_bool_clause_reif;
3033 FunctionI* bool_xor;
3034 {
3035 std::vector<Type> array_bool_andor_t(2);
3036 array_bool_andor_t[0] = Type::varbool(1);
3037 array_bool_andor_t[1] = Type::varbool(0);
3038 GCLock lock;
3039 FunctionI* fi =
3040 env.model->matchFn(env, ASTString("array_bool_and"), array_bool_andor_t, false);
3041 array_bool_and = ((fi != nullptr) && (fi->e() != nullptr)) ? fi : nullptr;
3042 fi = env.model->matchFn(env, ASTString("array_bool_or"), array_bool_andor_t, false);
3043 array_bool_or = ((fi != nullptr) && (fi->e() != nullptr)) ? fi : nullptr;
3044
3045 array_bool_andor_t[1] = Type::varbool(1);
3046 fi = env.model->matchFn(env, ASTString("bool_clause"), array_bool_andor_t, false);
3047 array_bool_clause = ((fi != nullptr) && (fi->e() != nullptr)) ? fi : nullptr;
3048
3049 array_bool_andor_t.push_back(Type::varbool());
3050 fi = env.model->matchFn(env, ASTString("bool_clause_reif"), array_bool_andor_t, false);
3051 array_bool_clause_reif = ((fi != nullptr) && (fi->e() != nullptr)) ? fi : nullptr;
3052
3053 std::vector<Type> bool_xor_t(3);
3054 bool_xor_t[0] = Type::varbool();
3055 bool_xor_t[1] = Type::varbool();
3056 bool_xor_t[2] = Type::varbool();
3057 fi = env.model->matchFn(env, constants().ids.bool_xor, bool_xor_t, false);
3058 bool_xor = ((fi != nullptr) && (fi->e() != nullptr)) ? fi : nullptr;
3059 }
3060
3061 std::vector<int> convertToRangeDomain;
3062 env.collectVarDecls(true);
3063
3064 while (startItem <= endItem || !env.modifiedVarDecls.empty() || !convertToRangeDomain.empty()) {
3065 if (env.failed()) {
3066 return;
3067 }
3068 std::vector<int> agenda;
3069 for (int i = startItem; i <= endItem; i++) {
3070 agenda.push_back(i);
3071 }
3072 for (int modifiedVarDecl : env.modifiedVarDecls) {
3073 agenda.push_back(modifiedVarDecl);
3074 }
3075 env.modifiedVarDecls.clear();
3076
3077 bool doConvertToRangeDomain = false;
3078 if (agenda.empty()) {
3079 for (auto i : convertToRangeDomain) {
3080 agenda.push_back(i);
3081 }
3082 convertToRangeDomain.clear();
3083 doConvertToRangeDomain = true;
3084 }
3085
3086 for (int i : agenda) {
3087 if (auto* vdi = m[i]->dynamicCast<VarDeclI>()) {
3088 if (vdi->removed()) {
3089 continue;
3090 }
3091 /// Look at constraints
3092 if (!is_output(vdi->e())) {
3093 if (0 < env.varOccurrences.occurrences(vdi->e())) {
3094 const auto it = env.varOccurrences.itemMap.find(vdi->e()->id());
3095 if (env.varOccurrences.itemMap.end() != it) {
3096 bool hasRedundantOccurrenciesOnly = true;
3097 for (const auto& c : it->second) {
3098 if (auto* constrI = c->dynamicCast<ConstraintI>()) {
3099 if (auto* call = constrI->e()->dynamicCast<Call>()) {
3100 if (call->id() == "mzn_reverse_map_var") {
3101 continue; // all good
3102 }
3103 }
3104 }
3105 hasRedundantOccurrenciesOnly = false;
3106 break;
3107 }
3108 if (hasRedundantOccurrenciesOnly) {
3109 env.flatRemoveItem(vdi);
3110 env.varOccurrences.removeAllOccurrences(vdi->e());
3111 for (const auto& c : it->second) {
3112 c->remove();
3113 }
3114 continue;
3115 }
3116 }
3117 } else { // 0 occurrencies
3118 if ((vdi->e()->e() != nullptr) && (vdi->e()->ti()->domain() != nullptr)) {
3119 if (vdi->e()->type().isvar() && vdi->e()->type().isbool() &&
3120 !vdi->e()->type().isOpt() &&
3121 Expression::equal(vdi->e()->ti()->domain(), constants().literalTrue)) {
3122 GCLock lock;
3123 auto* ci = new ConstraintI(vdi->loc(), vdi->e()->e());
3124 if (vdi->e()->introduced()) {
3125 env.flatAddItem(ci);
3126 env.flatRemoveItem(vdi);
3127 continue;
3128 }
3129 vdi->e()->e(nullptr);
3130 env.flatAddItem(ci);
3131 } else if (vdi->e()->type().isPar() || vdi->e()->ti()->computedDomain()) {
3132 env.flatRemoveItem(vdi);
3133 continue;
3134 }
3135 } else {
3136 env.flatRemoveItem(vdi);
3137 continue;
3138 }
3139 }
3140 }
3141 if (vdi->e()->type().dim() > 0 && vdi->e()->type().isvar()) {
3142 vdi->e()->ti()->domain(nullptr);
3143 }
3144 if (vdi->e()->type().isint() && vdi->e()->type().isvar() &&
3145 vdi->e()->ti()->domain() != nullptr) {
3146 GCLock lock;
3147 IntSetVal* dom = eval_intset(env, vdi->e()->ti()->domain());
3148
3149 bool needRangeDomain = onlyRangeDomains;
3150 if (!needRangeDomain && dom->size() > 0) {
3151 if (dom->min(0).isMinusInfinity() || dom->max(dom->size() - 1).isPlusInfinity()) {
3152 needRangeDomain = true;
3153 }
3154 }
3155 if (needRangeDomain) {
3156 if (doConvertToRangeDomain) {
3157 if (dom->min(0).isMinusInfinity() || dom->max(dom->size() - 1).isPlusInfinity()) {
3158 auto* nti = copy(env, vdi->e()->ti())->cast<TypeInst>();
3159 nti->domain(nullptr);
3160 vdi->e()->ti(nti);
3161 if (dom->min(0).isFinite()) {
3162 std::vector<Expression*> args(2);
3163 args[0] = IntLit::a(dom->min(0));
3164 args[1] = vdi->e()->id();
3165 Call* call = new Call(Location().introduce(), constants().ids.int_.le, args);
3166 call->type(Type::varbool());
3167 call->decl(env.model->matchFn(env, call, false));
3168 env.flatAddItem(new ConstraintI(Location().introduce(), call));
3169 } else if (dom->max(dom->size() - 1).isFinite()) {
3170 std::vector<Expression*> args(2);
3171 args[0] = vdi->e()->id();
3172 args[1] = IntLit::a(dom->max(dom->size() - 1));
3173 Call* call = new Call(Location().introduce(), constants().ids.int_.le, args);
3174 call->type(Type::varbool());
3175 call->decl(env.model->matchFn(env, call, false));
3176 env.flatAddItem(new ConstraintI(Location().introduce(), call));
3177 }
3178 } else if (dom->size() > 1) {
3179 auto* newDom = new SetLit(Location().introduce(),
3180 IntSetVal::a(dom->min(0), dom->max(dom->size() - 1)));
3181 auto* nti = copy(env, vdi->e()->ti())->cast<TypeInst>();
3182 nti->domain(newDom);
3183 vdi->e()->ti(nti);
3184 }
3185 if (dom->size() > 1) {
3186 std::vector<Expression*> args(2);
3187 args[0] = vdi->e()->id();
3188 args[1] = new SetLit(vdi->e()->loc(), dom);
3189 Call* call = new Call(vdi->e()->loc(), constants().ids.set_in, args);
3190 call->type(Type::varbool());
3191 call->decl(env.model->matchFn(env, call, false));
3192 // Give distinct call stack
3193 Annotation& ann = vdi->e()->ann();
3194 Expression* tmp = call;
3195 if (Expression* mznpath_ann = ann.getCall(constants().ann.mzn_path)) {
3196 tmp = mznpath_ann->cast<Call>()->arg(0);
3197 }
3198 CallStackItem csi(env, tmp);
3199 env.flatAddItem(new ConstraintI(Location().introduce(), call));
3200 }
3201 } else {
3202 convertToRangeDomain.push_back(i);
3203 }
3204 }
3205 }
3206 if (vdi->e()->type().isfloat() && vdi->e()->type().isvar() &&
3207 vdi->e()->ti()->domain() != nullptr) {
3208 GCLock lock;
3209 FloatSetVal* vdi_dom = eval_floatset(env, vdi->e()->ti()->domain());
3210 FloatVal vmin = vdi_dom->min();
3211 FloatVal vmax = vdi_dom->max();
3212 if (vmin == -FloatVal::infinity() && vmax == FloatVal::infinity()) {
3213 vdi->e()->ti()->domain(nullptr);
3214 } else if (vmin == -FloatVal::infinity()) {
3215 vdi->e()->ti()->domain(nullptr);
3216 std::vector<Expression*> args(2);
3217 args[0] = vdi->e()->id();
3218 args[1] = FloatLit::a(vmax);
3219 Call* call = new Call(Location().introduce(), constants().ids.float_.le, args);
3220 call->type(Type::varbool());
3221 call->decl(env.model->matchFn(env, call, false));
3222 env.flatAddItem(new ConstraintI(Location().introduce(), call));
3223 } else if (vmax == FloatVal::infinity()) {
3224 vdi->e()->ti()->domain(nullptr);
3225 std::vector<Expression*> args(2);
3226 args[0] = FloatLit::a(vmin);
3227 args[1] = vdi->e()->id();
3228 Call* call = new Call(Location().introduce(), constants().ids.float_.le, args);
3229 call->type(Type::varbool());
3230 call->decl(env.model->matchFn(env, call, false));
3231 env.flatAddItem(new ConstraintI(Location().introduce(), call));
3232 } else if (vdi_dom->size() > 1) {
3233 auto* dom_ranges = new BinOp(vdi->e()->ti()->domain()->loc().introduce(),
3234 FloatLit::a(vmin), BOT_DOTDOT, FloatLit::a(vmax));
3235 vdi->e()->ti()->domain(dom_ranges);
3236
3237 std::vector<Expression*> ranges;
3238 for (FloatSetRanges vdi_r(vdi_dom); vdi_r(); ++vdi_r) {
3239 ranges.push_back(FloatLit::a(vdi_r.min()));
3240 ranges.push_back(FloatLit::a(vdi_r.max()));
3241 }
3242 auto* al = new ArrayLit(Location().introduce(), ranges);
3243 al->type(Type::parfloat(1));
3244 std::vector<Expression*> args(2);
3245 args[0] = vdi->e()->id();
3246 args[1] = al;
3247 Call* call = new Call(Location().introduce(), constants().ids.float_.dom, args);
3248 call->type(Type::varbool());
3249 call->decl(env.model->matchFn(env, call, false));
3250 env.flatAddItem(new ConstraintI(Location().introduce(), call));
3251 }
3252 }
3253 }
3254 }
3255
3256 // rewrite some constraints if there are redefinitions
3257 for (int i : agenda) {
3258 if (m[i]->removed()) {
3259 continue;
3260 }
3261 if (auto* vdi = m[i]->dynamicCast<VarDeclI>()) {
3262 VarDecl* vd = vdi->e();
3263 if (vd->e() != nullptr) {
3264 bool isTrueVar = vd->type().isbool() &&
3265 Expression::equal(vd->ti()->domain(), constants().literalTrue);
3266 if (Call* c = vd->e()->dynamicCast<Call>()) {
3267 GCLock lock;
3268 Call* nc = nullptr;
3269 if (c->id() == constants().ids.lin_exp) {
3270 if (c->type().isfloat() && (float_lin_eq != nullptr)) {
3271 std::vector<Expression*> args(c->argCount());
3272 auto* le_c = follow_id(c->arg(0))->cast<ArrayLit>();
3273 std::vector<Expression*> nc_c(le_c->size());
3274 for (auto ii = static_cast<unsigned int>(nc_c.size()); (ii--) != 0U;) {
3275 nc_c[ii] = (*le_c)[ii];
3276 }
3277 nc_c.push_back(FloatLit::a(-1));
3278 args[0] = new ArrayLit(Location().introduce(), nc_c);
3279 args[0]->type(Type::parfloat(1));
3280 auto* le_x = follow_id(c->arg(1))->cast<ArrayLit>();
3281 std::vector<Expression*> nx(le_x->size());
3282 for (auto ii = static_cast<unsigned int>(nx.size()); (ii--) != 0U;) {
3283 nx[ii] = (*le_x)[ii];
3284 }
3285 nx.push_back(vd->id());
3286 args[1] = new ArrayLit(Location().introduce(), nx);
3287 args[1]->type(Type::varfloat(1));
3288 FloatVal d = c->arg(2)->cast<FloatLit>()->v();
3289 args[2] = FloatLit::a(-d);
3290 args[2]->type(Type::parfloat(0));
3291 nc = new Call(c->loc().introduce(), ASTString("float_lin_eq"), args);
3292 nc->type(Type::varbool());
3293 nc->decl(float_lin_eq);
3294 } else if (int_lin_eq != nullptr) {
3295 assert(c->type().isint());
3296 std::vector<Expression*> args(c->argCount());
3297 auto* le_c = follow_id(c->arg(0))->cast<ArrayLit>();
3298 std::vector<Expression*> nc_c(le_c->size());
3299 for (auto ii = static_cast<unsigned int>(nc_c.size()); (ii--) != 0U;) {
3300 nc_c[ii] = (*le_c)[ii];
3301 }
3302 nc_c.push_back(IntLit::a(-1));
3303 args[0] = new ArrayLit(Location().introduce(), nc_c);
3304 args[0]->type(Type::parint(1));
3305 auto* le_x = follow_id(c->arg(1))->cast<ArrayLit>();
3306 std::vector<Expression*> nx(le_x->size());
3307 for (auto ii = static_cast<unsigned int>(nx.size()); (ii--) != 0U;) {
3308 nx[ii] = (*le_x)[ii];
3309 }
3310 nx.push_back(vd->id());
3311 args[1] = new ArrayLit(Location().introduce(), nx);
3312 args[1]->type(Type::varint(1));
3313 IntVal d = c->arg(2)->cast<IntLit>()->v();
3314 args[2] = IntLit::a(-d);
3315 args[2]->type(Type::parint(0));
3316 nc = new Call(c->loc().introduce(), ASTString("int_lin_eq"), args);
3317 nc->type(Type::varbool());
3318 nc->decl(int_lin_eq);
3319 }
3320 } else if (c->id() == constants().ids.exists) {
3321 if (array_bool_or != nullptr) {
3322 std::vector<Expression*> args(2);
3323 args[0] = c->arg(0);
3324 args[1] = vd->id();
3325 nc = new Call(c->loc().introduce(), array_bool_or->id(), args);
3326 nc->type(Type::varbool());
3327 nc->decl(array_bool_or);
3328 }
3329 } else if (!isTrueVar && c->id() == constants().ids.forall) {
3330 if (array_bool_and != nullptr) {
3331 std::vector<Expression*> args(2);
3332 args[0] = c->arg(0);
3333 args[1] = vd->id();
3334 nc = new Call(c->loc().introduce(), array_bool_and->id(), args);
3335 nc->type(Type::varbool());
3336 nc->decl(array_bool_and);
3337 }
3338 } else if (isTrueVar && c->id() == constants().ids.clause &&
3339 (array_bool_clause != nullptr)) {
3340 std::vector<Expression*> args(2);
3341 args[0] = c->arg(0);
3342 args[1] = c->arg(1);
3343 nc = new Call(c->loc().introduce(), array_bool_clause->id(), args);
3344 nc->type(Type::varbool());
3345 nc->decl(array_bool_clause);
3346 } else if (c->id() == constants().ids.clause && (array_bool_clause_reif != nullptr)) {
3347 std::vector<Expression*> args(3);
3348 args[0] = c->arg(0);
3349 args[1] = c->arg(1);
3350 args[2] = vd->id();
3351 nc = new Call(c->loc().introduce(), array_bool_clause_reif->id(), args);
3352 nc->type(Type::varbool());
3353 nc->decl(array_bool_clause_reif);
3354
3355 } else if (c->id() == constants().ids.bool_not && c->argCount() == 1 &&
3356 c->decl()->e() == nullptr) {
3357 bool isFalseVar = Expression::equal(vd->ti()->domain(), constants().literalFalse);
3358 if (c->arg(0) == constants().boollit(true)) {
3359 if (isTrueVar) {
3360 env.fail();
3361 } else {
3362 env.flatRemoveExpr(c, vdi);
3363 env.cseMapRemove(c);
3364 vd->e(constants().literalFalse);
3365 vd->ti()->domain(constants().literalFalse);
3366 }
3367 } else if (c->arg(0) == constants().boollit(false)) {
3368 if (isFalseVar) {
3369 env.fail();
3370 } else {
3371 env.flatRemoveExpr(c, vdi);
3372 env.cseMapRemove(c);
3373 vd->e(constants().literalTrue);
3374 vd->ti()->domain(constants().literalTrue);
3375 }
3376 } else if (c->arg(0)->isa<Id>() && (isTrueVar || isFalseVar)) {
3377 VarDecl* arg_vd = c->arg(0)->cast<Id>()->decl();
3378 if (arg_vd->ti()->domain() == nullptr) {
3379 arg_vd->e(constants().boollit(!isTrueVar));
3380 arg_vd->ti()->domain(constants().boollit(!isTrueVar));
3381 } else if (arg_vd->ti()->domain() == constants().boollit(isTrueVar)) {
3382 env.fail();
3383 } else {
3384 arg_vd->e(arg_vd->ti()->domain());
3385 }
3386 env.flatRemoveExpr(c, vdi);
3387 vd->e(nullptr);
3388 // Need to remove right hand side from CSE map, otherwise
3389 // flattening of nc could assume c has already been flattened
3390 // to vd
3391 env.cseMapRemove(c);
3392 } else {
3393 // don't know how to handle, use bool_not/2
3394 nc = new Call(c->loc().introduce(), c->id(), {c->arg(0), vd->id()});
3395 nc->type(Type::varbool());
3396 nc->decl(env.model->matchFn(env, nc, false));
3397 }
3398 } else {
3399 if (isTrueVar) {
3400 FunctionI* decl = env.model->matchFn(env, c, false);
3401 env.cseMapRemove(c);
3402 if ((decl->e() != nullptr) || c->id() == constants().ids.forall) {
3403 if (decl->e() != nullptr) {
3404 add_path_annotation(env, decl->e());
3405 }
3406 c->decl(decl);
3407 nc = c;
3408 }
3409 } else {
3410 std::vector<Expression*> args(c->argCount());
3411 for (auto i = static_cast<unsigned int>(args.size()); (i--) != 0U;) {
3412 args[i] = c->arg(i);
3413 }
3414 args.push_back(vd->id());
3415 ASTString cid = c->id();
3416 if (cid == constants().ids.clause && (array_bool_clause_reif != nullptr)) {
3417 nc = new Call(c->loc().introduce(), array_bool_clause_reif->id(), args);
3418 nc->type(Type::varbool());
3419 nc->decl(array_bool_clause_reif);
3420 } else {
3421 FunctionI* decl = nullptr;
3422 if (c->type().isbool() && vd->type().isbool()) {
3423 if (env.fopts.enableHalfReification &&
3424 vd->ann().contains(constants().ctx.pos)) {
3425 cid = env.halfReifyId(c->id());
3426 decl = env.model->matchFn(env, cid, args, false);
3427 if (decl == nullptr) {
3428 cid = env.reifyId(c->id());
3429 decl = env.model->matchFn(env, cid, args, false);
3430 }
3431 } else {
3432 cid = env.reifyId(c->id());
3433 decl = env.model->matchFn(env, cid, args, false);
3434 }
3435 if (decl == nullptr) {
3436 std::ostringstream ss;
3437 ss << "'" << c->id()
3438 << "' is used in a reified context but no reified version is "
3439 "available";
3440 throw FlatteningError(env, c->loc(), ss.str());
3441 }
3442 } else {
3443 decl = env.model->matchFn(env, cid, args, false);
3444 }
3445 if ((decl != nullptr) && (decl->e() != nullptr)) {
3446 add_path_annotation(env, decl->e());
3447 nc = new Call(c->loc().introduce(), cid, args);
3448 nc->type(Type::varbool());
3449 nc->decl(decl);
3450 }
3451 }
3452 }
3453 }
3454 if (nc != nullptr) {
3455 // Note: Removal of VarDecl's referenced by c must be delayed
3456 // until nc is flattened
3457 std::vector<VarDecl*> toRemove;
3458 CollectDecls cd(env.varOccurrences, toRemove, vdi);
3459 top_down(cd, c);
3460 vd->e(nullptr);
3461 // Need to remove right hand side from CSE map, otherwise
3462 // flattening of nc could assume c has already been flattened
3463 // to vd
3464 env.cseMapRemove(c);
3465 /// TODO: check if removing variables here makes sense:
3466 // if (!is_output(vd) && env.varOccurrences.occurrences(vd)==0) {
3467 // removedItems.push_back(vdi);
3468 // }
3469 if (nc != c) {
3470 make_defined_var(vd, nc);
3471 for (ExpressionSetIter it = c->ann().begin(); it != c->ann().end(); ++it) {
3472 EE ee_ann = flat_exp(env, Ctx(), *it, nullptr, constants().varTrue);
3473 nc->addAnnotation(ee_ann.r());
3474 }
3475 }
3476 StringLit* vsl = get_longest_mzn_path_annotation(env, vdi->e());
3477 StringLit* csl = get_longest_mzn_path_annotation(env, c);
3478 CallStackItem* vsi = nullptr;
3479 CallStackItem* csi = nullptr;
3480 if (vsl != nullptr) {
3481 vsi = new CallStackItem(env, vsl);
3482 }
3483 if (csl != nullptr) {
3484 csi = new CallStackItem(env, csl);
3485 }
3486 Location orig_loc = nc->loc();
3487 if (csl != nullptr) {
3488 ASTString loc = csl->v();
3489 size_t sep = loc.find('|');
3490 std::string filename = loc.substr(0, sep);
3491 std::string start_line_s = loc.substr(sep + 1, loc.find('|', sep + 1) - sep - 1);
3492 int start_line = std::stoi(start_line_s);
3493 Location new_loc(ASTString(filename), start_line, 0, start_line, 0);
3494 orig_loc = new_loc;
3495 }
3496 ItemTimer item_timer(orig_loc, timingMap);
3497 (void)flat_exp(env, Ctx(), nc, constants().varTrue, constants().varTrue);
3498
3499 delete csi;
3500
3501 delete vsi;
3502
3503 // Remove VarDecls becoming unused through the removal of c
3504 // because they are not used by nc
3505 while (!toRemove.empty()) {
3506 VarDecl* cur = toRemove.back();
3507 toRemove.pop_back();
3508 if (env.varOccurrences.occurrences(cur) == 0 && CollectDecls::varIsFree(cur)) {
3509 auto cur_idx = env.varOccurrences.idx.find(cur->id());
3510 if (cur_idx != env.varOccurrences.idx.end()) {
3511 auto* vdi = m[cur_idx->second]->cast<VarDeclI>();
3512 if (!is_output(cur) && !m[cur_idx->second]->removed()) {
3513 CollectDecls cd(env.varOccurrences, toRemove, vdi);
3514 top_down(cd, vdi->e()->e());
3515 vdi->remove();
3516 }
3517 }
3518 }
3519 }
3520 }
3521 }
3522 }
3523 } else if (auto* ci = m[i]->dynamicCast<ConstraintI>()) {
3524 if (Call* c = ci->e()->dynamicCast<Call>()) {
3525 GCLock lock;
3526 Call* nc = nullptr;
3527 if (c->id() == constants().ids.exists) {
3528 if (array_bool_or != nullptr) {
3529 std::vector<Expression*> args(2);
3530 args[0] = c->arg(0);
3531 args[1] = constants().literalTrue;
3532 nc = new Call(c->loc().introduce(), array_bool_or->id(), args);
3533 nc->type(Type::varbool());
3534 nc->decl(array_bool_or);
3535 }
3536 } else if (c->id() == constants().ids.forall) {
3537 if (array_bool_and != nullptr) {
3538 std::vector<Expression*> args(2);
3539 args[0] = c->arg(0);
3540 args[1] = constants().literalTrue;
3541 nc = new Call(c->loc().introduce(), array_bool_and->id(), args);
3542 nc->type(Type::varbool());
3543 nc->decl(array_bool_and);
3544 }
3545 } else if (c->id() == constants().ids.clause) {
3546 if (array_bool_clause != nullptr) {
3547 std::vector<Expression*> args(2);
3548 args[0] = c->arg(0);
3549 args[1] = c->arg(1);
3550 nc = new Call(c->loc().introduce(), array_bool_clause->id(), args);
3551 nc->type(Type::varbool());
3552 nc->decl(array_bool_clause);
3553 }
3554 } else if (c->id() == constants().ids.bool_xor) {
3555 if (bool_xor != nullptr) {
3556 std::vector<Expression*> args(3);
3557 args[0] = c->arg(0);
3558 args[1] = c->arg(1);
3559 args[2] = c->argCount() == 2 ? constants().literalTrue : c->arg(2);
3560 nc = new Call(c->loc().introduce(), bool_xor->id(), args);
3561 nc->type(Type::varbool());
3562 nc->decl(bool_xor);
3563 }
3564 } else if (c->id() == constants().ids.bool_not && c->argCount() == 1 &&
3565 c->decl()->e() == nullptr) {
3566 if (c->arg(0) == constants().boollit(true)) {
3567 env.fail();
3568 } else if (c->arg(0) == constants().boollit(false)) {
3569 // nothing to do, not false = true
3570 } else if (c->arg(0)->isa<Id>()) {
3571 VarDecl* vd = c->arg(0)->cast<Id>()->decl();
3572 if (vd->ti()->domain() == nullptr) {
3573 vd->ti()->domain(constants().boollit(false));
3574 } else if (vd->ti()->domain() == constants().boollit(true)) {
3575 env.fail();
3576 }
3577 } else {
3578 // don't know how to handle, use bool_not/2
3579 nc =
3580 new Call(c->loc().introduce(), c->id(), {c->arg(0), constants().boollit(true)});
3581 nc->type(Type::varbool());
3582 nc->decl(env.model->matchFn(env, nc, false));
3583 }
3584 if (nc == nullptr) {
3585 env.flatRemoveItem(ci);
3586 }
3587 } else {
3588 FunctionI* decl = env.model->matchFn(env, c, false);
3589 if ((decl != nullptr) && (decl->e() != nullptr)) {
3590 nc = c;
3591 nc->decl(decl);
3592 }
3593 }
3594 if (nc != nullptr) {
3595 if (nc != c) {
3596 for (ExpressionSetIter it = c->ann().begin(); it != c->ann().end(); ++it) {
3597 EE ee_ann = flat_exp(env, Ctx(), *it, nullptr, constants().varTrue);
3598 nc->addAnnotation(ee_ann.r());
3599 }
3600 }
3601 StringLit* sl = get_longest_mzn_path_annotation(env, c);
3602 CallStackItem* csi = nullptr;
3603 if (sl != nullptr) {
3604 csi = new CallStackItem(env, sl);
3605 }
3606 ItemTimer item_timer(nc->loc(), timingMap);
3607 (void)flat_exp(env, Ctx(), nc, constants().varTrue, constants().varTrue);
3608 env.flatRemoveItem(ci);
3609
3610 delete csi;
3611 }
3612 }
3613 }
3614 }
3615
3616 startItem = endItem + 1;
3617 endItem = static_cast<int>(m.size()) - 1;
3618 }
3619
3620 // Add redefinitions for output variables that may have been redefined since create_output
3621 for (unsigned int i = 0; i < env.output->size(); i++) {
3622 if (auto* vdi = (*env.output)[i]->dynamicCast<VarDeclI>()) {
3623 IdMap<KeepAlive>::iterator it;
3624 if (vdi->e()->e() == nullptr &&
3625 (it = env.reverseMappers.find(vdi->e()->id())) != env.reverseMappers.end()) {
3626 GCLock lock;
3627 Call* rhs = copy(env, env.cmap, it->second())->cast<Call>();
3628 check_output_par_fn(env, rhs);
3629 output_vardecls(env, vdi, rhs);
3630
3631 remove_is_output(vdi->e()->flat());
3632 vdi->e()->e(rhs);
3633 }
3634 }
3635 }
3636
3637 if (!opt.keepOutputInFzn) {
3638 finalise_output(env);
3639 }
3640
3641 for (auto& i : m) {
3642 if (auto* ci = i->dynamicCast<ConstraintI>()) {
3643 if (Call* c = ci->e()->dynamicCast<Call>()) {
3644 if (c->decl() == constants().varRedef) {
3645 env.flatRemoveItem(ci);
3646 }
3647 }
3648 }
3649 }
3650
3651 cleanup_output(env);
3652 } catch (ModelInconsistent&) {
3653 }
3654
3655 if (opt.detailedTiming) {
3656 e.envi().outstream << "% Compilation profile (file,line,milliseconds)\n";
3657 if (opt.collectMznPaths) {
3658 e.envi().outstream << "% (time is allocated to toplevel item)\n";
3659 } else {
3660 e.envi().outstream << "% (locations are approximate, use --keep-paths to allocate times to "
3661 "toplevel items)\n";
3662 }
3663 for (auto& entry : *timingMap) {
3664 std::chrono::milliseconds time_taken =
3665 std::chrono::duration_cast<std::chrono::milliseconds>(entry.second);
3666 if (time_taken > std::chrono::milliseconds(0)) {
3667 e.envi().outstream << "%%%mzn-stat: profiling=[\"" << entry.first.first << "\","
3668 << entry.first.second << "," << time_taken.count() << "]\n";
3669 }
3670 }
3671 e.envi().outstream << "%%%mzn-stat-end\n";
3672 }
3673}
3674
3675void clear_internal_annotations(Expression* e, bool keepDefinesVar) {
3676 e->ann().remove(constants().ann.promise_total);
3677 e->ann().remove(constants().ann.maybe_partial);
3678 e->ann().remove(constants().ann.add_to_output);
3679 e->ann().remove(constants().ann.rhs_from_assignment);
3680 e->ann().remove(constants().ann.mzn_was_undefined);
3681 // Remove defines_var(x) annotation where x is par
3682 std::vector<Expression*> removeAnns;
3683 for (ExpressionSetIter anns = e->ann().begin(); anns != e->ann().end(); ++anns) {
3684 if (Call* c = (*anns)->dynamicCast<Call>()) {
3685 if (c->id() == constants().ann.defines_var &&
3686 (!keepDefinesVar || c->arg(0)->type().isPar())) {
3687 removeAnns.push_back(c);
3688 }
3689 }
3690 }
3691 for (auto& removeAnn : removeAnns) {
3692 e->ann().remove(removeAnn);
3693 }
3694}
3695
3696std::vector<Expression*> cleanup_vardecl(EnvI& env, VarDeclI* vdi, VarDecl* vd,
3697 bool keepDefinesVar) {
3698 std::vector<Expression*> added_constraints;
3699
3700 // In FlatZinc par variables have RHSs, not domains
3701 if (vd->type().isPar()) {
3702 vd->ann().clear();
3703 vd->introduced(false);
3704 vd->ti()->domain(nullptr);
3705 }
3706
3707 // In FlatZinc the RHS of a VarDecl must be a literal, Id or empty
3708 // Example:
3709 // var 1..5: x = function(y)
3710 // becomes:
3711 // var 1..5: x;
3712 // relation(x, y);
3713 if (vd->type().isvar() && vd->type().isbool()) {
3714 bool is_fixed = (vd->ti()->domain() != nullptr);
3715 if (Expression::equal(vd->ti()->domain(), constants().literalTrue)) {
3716 // Ex: var true: b = e()
3717
3718 // Store RHS
3719 Expression* ve = vd->e();
3720 vd->e(constants().literalTrue);
3721 vd->ti()->domain(nullptr);
3722 // Ex: var bool: b = true
3723
3724 // If vd had a RHS
3725 if (ve != nullptr) {
3726 if (Call* vcc = ve->dynamicCast<Call>()) {
3727 // Convert functions to relations:
3728 // exists([x]) => array_bool_or([x],true)
3729 // forall([x]) => array_bool_and([x],true)
3730 // clause([x]) => bool_clause([x])
3731 ASTString cid;
3732 std::vector<Expression*> args;
3733 if (vcc->id() == constants().ids.exists) {
3734 cid = constants().ids.array_bool_or;
3735 args.push_back(vcc->arg(0));
3736 args.push_back(constants().literalTrue);
3737 } else if (vcc->id() == constants().ids.forall) {
3738 cid = constants().ids.array_bool_and;
3739 args.push_back(vcc->arg(0));
3740 args.push_back(constants().literalTrue);
3741 } else if (vcc->id() == constants().ids.clause) {
3742 cid = constants().ids.bool_clause;
3743 args.push_back(vcc->arg(0));
3744 args.push_back(vcc->arg(1));
3745 }
3746
3747 if (args.empty()) {
3748 // Post original RHS as stand alone constraint
3749 ve = vcc;
3750 } else {
3751 // Create new call, retain annotations from original RHS
3752 Call* nc = new Call(vcc->loc().introduce(), cid, args);
3753 nc->type(vcc->type());
3754 nc->ann().merge(vcc->ann());
3755 ve = nc;
3756 }
3757 } else if (Id* id = ve->dynamicCast<Id>()) {
3758 if (id->decl()->ti()->domain() != constants().literalTrue) {
3759 // Inconsistent assignment: post bool_eq(y, true)
3760 std::vector<Expression*> args(2);
3761 args[0] = id;
3762 args[1] = constants().literalTrue;
3763 GCLock lock;
3764 ve = new Call(Location().introduce(), constants().ids.bool_eq, args);
3765 } else {
3766 // Don't post this
3767 ve = constants().literalTrue;
3768 }
3769 }
3770 // Post new constraint
3771 if (ve != constants().literalTrue) {
3772 clear_internal_annotations(ve, keepDefinesVar);
3773 added_constraints.push_back(ve);
3774 }
3775 }
3776 } else {
3777 // Ex: var false: b = e()
3778 if (vd->e() != nullptr) {
3779 if (vd->e()->eid() == Expression::E_CALL) {
3780 // Convert functions to relations:
3781 // var false: b = exists([x]) => array_bool_or([x], b)
3782 // var false: b = forall([x]) => array_bool_and([x], b)
3783 // var false: b = clause([x]) => bool_clause_reif([x], b)
3784 const Call* c = vd->e()->cast<Call>();
3785 GCLock lock;
3786 vd->e(nullptr);
3787 ASTString cid;
3788 std::vector<Expression*> args(c->argCount());
3789 for (unsigned int i = args.size(); (i--) != 0U;) {
3790 args[i] = c->arg(i);
3791 }
3792 if (is_fixed) {
3793 args.push_back(constants().literalFalse);
3794 } else {
3795 args.push_back(vd->id());
3796 }
3797 if (c->id() == constants().ids.exists) {
3798 cid = constants().ids.array_bool_or;
3799 } else if (c->id() == constants().ids.forall) {
3800 cid = constants().ids.array_bool_and;
3801 } else if (c->id() == constants().ids.clause) {
3802 cid = constants().ids.bool_clause_reif;
3803 } else {
3804 if (env.fopts.enableHalfReification && vd->ann().contains(constants().ctx.pos)) {
3805 cid = env.halfReifyId(c->id());
3806 if (env.model->matchFn(env, cid, args, false) == nullptr) {
3807 cid = env.reifyId(c->id());
3808 }
3809 } else {
3810 cid = env.reifyId(c->id());
3811 }
3812 }
3813 Call* nc = new Call(c->loc().introduce(), cid, args);
3814 nc->type(c->type());
3815 FunctionI* decl = env.model->matchFn(env, nc, false);
3816 if (decl == nullptr) {
3817 std::ostringstream ss;
3818 ss << "'" << c->id()
3819 << "' is used in a reified context but no reified version is available";
3820 throw FlatteningError(env, c->loc(), ss.str());
3821 }
3822 nc->decl(decl);
3823 if (!is_fixed) {
3824 make_defined_var(vd, nc);
3825 }
3826 nc->ann().merge(c->ann());
3827 clear_internal_annotations(nc, keepDefinesVar);
3828 added_constraints.push_back(nc);
3829 } else {
3830 assert(vd->e()->eid() == Expression::E_ID || vd->e()->eid() == Expression::E_BOOLLIT);
3831 }
3832 }
3833 if (Expression::equal(vd->ti()->domain(), constants().literalFalse)) {
3834 vd->ti()->domain(nullptr);
3835 vd->e(constants().literalFalse);
3836 }
3837 }
3838 if (vdi != nullptr && is_fixed && env.varOccurrences.occurrences(vd) == 0) {
3839 if (is_output(vd)) {
3840 VarDecl* vd_output =
3841 (*env.output)[env.outputFlatVarOccurrences.find(vd)]->cast<VarDeclI>()->e();
3842 if (vd_output->e() == nullptr) {
3843 vd_output->e(vd->e());
3844 }
3845 }
3846 env.flatRemoveItem(vdi);
3847 }
3848 } else if (vd->type().isvar() && vd->type().dim() == 0) {
3849 // Int or Float var
3850 if (vd->e() != nullptr) {
3851 if (const Call* cc = vd->e()->dynamicCast<Call>()) {
3852 // Remove RHS from vd
3853 vd->e(nullptr);
3854
3855 std::vector<Expression*> args(cc->argCount());
3856 ASTString cid;
3857 if (cc->id() == constants().ids.lin_exp) {
3858 // a = lin_exp([1],[b],5) => int_lin_eq([1,-1],[b,a],-5):: defines_var(a)
3859 auto* le_c = follow_id(cc->arg(0))->cast<ArrayLit>();
3860 std::vector<Expression*> nc(le_c->size());
3861 for (auto i = static_cast<unsigned int>(nc.size()); (i--) != 0U;) {
3862 nc[i] = (*le_c)[i];
3863 }
3864 if (le_c->type().bt() == Type::BT_INT) {
3865 cid = constants().ids.int_.lin_eq;
3866 nc.push_back(IntLit::a(-1));
3867 args[0] = new ArrayLit(Location().introduce(), nc);
3868 args[0]->type(Type::parint(1));
3869 auto* le_x = follow_id(cc->arg(1))->cast<ArrayLit>();
3870 std::vector<Expression*> nx(le_x->size());
3871 for (auto i = static_cast<unsigned int>(nx.size()); (i--) != 0U;) {
3872 nx[i] = (*le_x)[i];
3873 }
3874 nx.push_back(vd->id());
3875 args[1] = new ArrayLit(Location().introduce(), nx);
3876 args[1]->type(le_x->type());
3877 IntVal d = cc->arg(2)->cast<IntLit>()->v();
3878 args[2] = IntLit::a(-d);
3879 } else {
3880 // float
3881 cid = constants().ids.float_.lin_eq;
3882 nc.push_back(FloatLit::a(-1.0));
3883 args[0] = new ArrayLit(Location().introduce(), nc);
3884 args[0]->type(Type::parfloat(1));
3885 auto* le_x = follow_id(cc->arg(1))->cast<ArrayLit>();
3886 std::vector<Expression*> nx(le_x->size());
3887 for (auto i = static_cast<unsigned int>(nx.size()); (i--) != 0U;) {
3888 nx[i] = (*le_x)[i];
3889 }
3890 nx.push_back(vd->id());
3891 args[1] = new ArrayLit(Location().introduce(), nx);
3892 args[1]->type(le_x->type());
3893 FloatVal d = cc->arg(2)->cast<FloatLit>()->v();
3894 args[2] = FloatLit::a(-d);
3895 }
3896 } else {
3897 if (cc->id() == "card") {
3898 // card is 'set_card' in old FlatZinc
3899 cid = constants().ids.set_card;
3900 } else {
3901 cid = cc->id();
3902 }
3903 for (auto i = static_cast<unsigned int>(args.size()); (i--) != 0U;) {
3904 args[i] = cc->arg(i);
3905 }
3906 args.push_back(vd->id());
3907 }
3908 Call* nc = new Call(cc->loc().introduce(), cid, args);
3909 nc->type(cc->type());
3910 make_defined_var(vd, nc);
3911 nc->ann().merge(cc->ann());
3912
3913 clear_internal_annotations(nc, keepDefinesVar);
3914 added_constraints.push_back(nc);
3915 } else {
3916 // RHS must be literal or Id
3917 assert(vd->e()->eid() == Expression::E_ID || vd->e()->eid() == Expression::E_INTLIT ||
3918 vd->e()->eid() == Expression::E_FLOATLIT ||
3919 vd->e()->eid() == Expression::E_BOOLLIT || vd->e()->eid() == Expression::E_SETLIT);
3920 }
3921 }
3922 } else if (vd->type().dim() > 0) {
3923 // vd is an array
3924
3925 // If RHS is an Id, follow id to RHS
3926 // a = [1,2,3]; b = a;
3927 // vd = b => vd = [1,2,3]
3928 if (!vd->e()->isa<ArrayLit>()) {
3929 vd->e(follow_id(vd->e()));
3930 }
3931
3932 // If empty array or 1 indexed, continue
3933 if (vd->ti()->ranges().size() == 1 && vd->ti()->ranges()[0]->domain() != nullptr &&
3934 vd->ti()->ranges()[0]->domain()->isa<SetLit>()) {
3935 IntSetVal* isv = vd->ti()->ranges()[0]->domain()->cast<SetLit>()->isv();
3936 if ((isv != nullptr) && (isv->size() == 0 || isv->min(0) == 1)) {
3937 return added_constraints;
3938 }
3939 }
3940
3941 // Array should be 1 indexed since ArrayLit is 1 indexed
3942 assert(vd->e() != nullptr);
3943 ArrayLit* al = nullptr;
3944 Expression* e = vd->e();
3945 while (al == nullptr) {
3946 switch (e->eid()) {
3947 case Expression::E_ARRAYLIT:
3948 al = e->cast<ArrayLit>();
3949 break;
3950 case Expression::E_ID:
3951 e = e->cast<Id>()->decl()->e();
3952 break;
3953 default:
3954 assert(false);
3955 }
3956 }
3957 al->make1d();
3958 IntSetVal* isv = IntSetVal::a(1, al->length());
3959 if (vd->ti()->ranges().size() == 1) {
3960 vd->ti()->ranges()[0]->domain(new SetLit(Location().introduce(), isv));
3961 } else {
3962 std::vector<TypeInst*> r(1);
3963 r[0] = new TypeInst(vd->ti()->ranges()[0]->loc(), vd->ti()->ranges()[0]->type(),
3964 new SetLit(Location().introduce(), isv));
3965 ASTExprVec<TypeInst> ranges(r);
3966 auto* ti = new TypeInst(vd->ti()->loc(), vd->ti()->type(), ranges, vd->ti()->domain());
3967 vd->ti(ti);
3968 }
3969 }
3970
3971 // Remove boolean context annotations used only on compilation
3972 vd->ann().remove(constants().ctx.mix);
3973 vd->ann().remove(constants().ctx.pos);
3974 vd->ann().remove(constants().ctx.neg);
3975 vd->ann().remove(constants().ctx.root);
3976 vd->ann().remove(constants().ann.promise_total);
3977 vd->ann().remove(constants().ann.add_to_output);
3978 vd->ann().remove(constants().ann.mzn_check_var);
3979 vd->ann().remove(constants().ann.rhs_from_assignment);
3980 vd->ann().remove(constants().ann.mzn_was_undefined);
3981 vd->ann().removeCall(constants().ann.mzn_check_enum_var);
3982
3983 return added_constraints;
3984}
3985
3986Expression* cleanup_constraint(EnvI& env, std::unordered_set<Item*>& globals, Expression* ce,
3987 bool keepDefinesVar) {
3988 clear_internal_annotations(ce, keepDefinesVar);
3989
3990 if (Call* vc = ce->dynamicCast<Call>()) {
3991 for (unsigned int i = 0; i < vc->argCount(); i++) {
3992 // Change array indicies to be 1 indexed
3993 if (auto* al = vc->arg(i)->dynamicCast<ArrayLit>()) {
3994 if (al->dims() > 1 || al->min(0) != 1) {
3995 al->make1d();
3996 }
3997 }
3998 }
3999 // Convert functions to relations:
4000 // exists([x]) => array_bool_or([x],true)
4001 // forall([x]) => array_bool_and([x],true)
4002 // clause([x]) => bool_clause([x])
4003 // bool_xor([x],[y]) => bool_xor([x],[y],true)
4004 if (vc->id() == constants().ids.exists) {
4005 GCLock lock;
4006 vc->id(constants().ids.array_bool_or);
4007 std::vector<Expression*> args(2);
4008 args[0] = vc->arg(0);
4009 args[1] = constants().literalTrue;
4010 ASTExprVec<Expression> argsv(args);
4011 vc->args(argsv);
4012 vc->decl(env.model->matchFn(env, vc, false));
4013 } else if (vc->id() == constants().ids.forall) {
4014 GCLock lock;
4015 vc->id(constants().ids.array_bool_and);
4016 std::vector<Expression*> args(2);
4017 args[0] = vc->arg(0);
4018 args[1] = constants().literalTrue;
4019 ASTExprVec<Expression> argsv(args);
4020 vc->args(argsv);
4021 vc->decl(env.model->matchFn(env, vc, false));
4022 } else if (vc->id() == constants().ids.clause) {
4023 GCLock lock;
4024 vc->id(constants().ids.bool_clause);
4025 vc->decl(env.model->matchFn(env, vc, false));
4026 } else if (vc->id() == constants().ids.bool_xor && vc->argCount() == 2) {
4027 GCLock lock;
4028 std::vector<Expression*> args(3);
4029 args[0] = vc->arg(0);
4030 args[1] = vc->arg(1);
4031 args[2] = constants().literalTrue;
4032 ASTExprVec<Expression> argsv(args);
4033 vc->args(argsv);
4034 vc->decl(env.model->matchFn(env, vc, false));
4035 }
4036
4037 // If vc->decl() is a solver builtin and has not been added to the
4038 // FlatZinc, add it
4039 if ((vc->decl() != nullptr) && vc->decl() != constants().varRedef &&
4040 !vc->decl()->fromStdLib() && globals.find(vc->decl()) == globals.end()) {
4041 std::vector<VarDecl*> params(vc->decl()->params().size());
4042 for (unsigned int i = 0; i < params.size(); i++) {
4043 params[i] = vc->decl()->params()[i];
4044 }
4045 GCLock lock;
4046 auto* vc_decl_copy = new FunctionI(vc->decl()->loc(), vc->decl()->id(), vc->decl()->ti(),
4047 params, vc->decl()->e());
4048 env.flatAddItem(vc_decl_copy);
4049 globals.insert(vc->decl());
4050 }
4051 return ce;
4052 }
4053 if (Id* id = ce->dynamicCast<Id>()) {
4054 // Ex: constraint b; => constraint bool_eq(b, true);
4055 std::vector<Expression*> args(2);
4056 args[0] = id;
4057 args[1] = constants().literalTrue;
4058 GCLock lock;
4059 return new Call(Location().introduce(), constants().ids.bool_eq, args);
4060 }
4061 if (auto* bl = ce->dynamicCast<BoolLit>()) {
4062 // Ex: true => delete; false => bool_eq(false, true);
4063 if (!bl->v()) {
4064 GCLock lock;
4065 std::vector<Expression*> args(2);
4066 args[0] = constants().literalFalse;
4067 args[1] = constants().literalTrue;
4068 Call* neq = new Call(Location().introduce(), constants().ids.bool_eq, args);
4069 return neq;
4070 }
4071 return nullptr;
4072 }
4073 return ce;
4074}
4075
4076void oldflatzinc(Env& e) {
4077 Model* m = e.flat();
4078
4079 // Check wheter we need to keep defines_var annotations
4080 bool keepDefinesVar = true;
4081 {
4082 GCLock lock;
4083 Call* c = new Call(Location().introduce(), "mzn_check_annotate_defines_var", {});
4084 c->type(Type::parbool());
4085 FunctionI* fi = e.model()->matchFn(e.envi(), c, true);
4086 if (fi != nullptr) {
4087 c->decl(fi);
4088 keepDefinesVar = eval_bool(e.envi(), c);
4089 }
4090 }
4091
4092 // Mark annotations and optional variables for removal, and clear flags
4093 for (auto& vdi : m->vardecls()) {
4094 if (vdi.e()->type().ot() == Type::OT_OPTIONAL || vdi.e()->type().bt() == Type::BT_ANN) {
4095 vdi.remove();
4096 }
4097 }
4098
4099 EnvI& env = e.envi();
4100
4101 unsigned int msize = m->size();
4102
4103 // Predicate declarations of solver builtins
4104 std::unordered_set<Item*> globals;
4105
4106 // Variables mapped to the index of the constraint that defines them
4107 enum DFS_STATUS { DFS_UNKNOWN, DFS_SEEN, DFS_DONE };
4108 std::unordered_map<VarDecl*, std::pair<int, DFS_STATUS>> definition_map;
4109
4110 // Record indices of VarDeclIs with Id RHS for sorting & unification
4111 std::vector<int> declsWithIds;
4112 for (int i = 0; i < msize; i++) {
4113 if ((*m)[i]->removed()) {
4114 continue;
4115 }
4116 if (auto* vdi = (*m)[i]->dynamicCast<VarDeclI>()) {
4117 GCLock lock;
4118 VarDecl* vd = vdi->e();
4119 std::vector<Expression*> added_constraints =
4120 cleanup_vardecl(e.envi(), vdi, vd, keepDefinesVar);
4121 // Record whether this VarDecl is equal to an Id (aliasing)
4122 if ((vd->e() != nullptr) && vd->e()->isa<Id>()) {
4123 declsWithIds.push_back(i);
4124 vdi->e()->payload(-static_cast<int>(i) - 1);
4125 } else {
4126 vdi->e()->payload(i);
4127 }
4128 for (auto* nc : added_constraints) {
4129 Expression* new_ce = cleanup_constraint(e.envi(), globals, nc, keepDefinesVar);
4130 if (new_ce != nullptr) {
4131 e.envi().flatAddItem(new ConstraintI(Location().introduce(), new_ce));
4132 }
4133 }
4134 } else if (auto* ci = (*m)[i]->dynamicCast<ConstraintI>()) {
4135 Expression* new_ce = cleanup_constraint(e.envi(), globals, ci->e(), keepDefinesVar);
4136 if (new_ce != nullptr) {
4137 ci->e(new_ce);
4138 if (keepDefinesVar) {
4139 if (Call* defines_var = new_ce->ann().getCall(constants().ann.defines_var)) {
4140 if (Id* ident = defines_var->arg(0)->dynamicCast<Id>()) {
4141 if (definition_map.find(ident->decl()) != definition_map.end()) {
4142 // This is the second definition, remove it
4143 new_ce->ann().removeCall(constants().ann.defines_var);
4144 } else {
4145 definition_map.insert({ident->decl(), {i, DFS_UNKNOWN}});
4146 }
4147 }
4148 }
4149 }
4150 } else {
4151 ci->remove();
4152 }
4153 } else if (auto* fi = (*m)[i]->dynamicCast<FunctionI>()) {
4154 if (Let* let = Expression::dynamicCast<Let>(fi->e())) {
4155 GCLock lock;
4156 std::vector<Expression*> new_let;
4157 for (unsigned int i = 0; i < let->let().size(); i++) {
4158 Expression* let_e = let->let()[i];
4159 if (auto* vd = let_e->dynamicCast<VarDecl>()) {
4160 std::vector<Expression*> added_constraints =
4161 cleanup_vardecl(e.envi(), nullptr, vd, keepDefinesVar);
4162 new_let.push_back(vd);
4163 for (auto* nc : added_constraints) {
4164 new_let.push_back(nc);
4165 }
4166 } else {
4167 Expression* new_ce = cleanup_constraint(e.envi(), globals, let_e, keepDefinesVar);
4168 if (new_ce != nullptr) {
4169 new_let.push_back(new_ce);
4170 }
4171 }
4172 }
4173 fi->e(new Let(let->loc(), new_let, let->in()));
4174 }
4175 } else if (auto* si = (*m)[i]->dynamicCast<SolveI>()) {
4176 if ((si->e() != nullptr) && si->e()->type().isPar()) {
4177 // Introduce VarDecl if objective expression is par
4178 GCLock lock;
4179 auto* ti = new TypeInst(Location().introduce(), si->e()->type(), nullptr);
4180 auto* constantobj = new VarDecl(Location().introduce(), ti, e.envi().genId(), si->e());
4181 si->e(constantobj->id());
4182 e.envi().flatAddItem(new VarDeclI(Location().introduce(), constantobj));
4183 }
4184 }
4185 }
4186
4187 if (keepDefinesVar) {
4188 // Detect and break cycles in defines_var annotations
4189 std::vector<VarDecl*> definesStack;
4190 auto checkId = [&definesStack, &definition_map, &m](VarDecl* cur, Id* ident) {
4191 if (cur == ident->decl()) {
4192 // Never push the variable we're currently looking at
4193 return;
4194 }
4195 auto it = definition_map.find(ident->decl());
4196 if (it != definition_map.end()) {
4197 if (it->second.second == 0) {
4198 // not yet visited, push
4199 definesStack.push_back(it->first);
4200 } else if (it->second.second == 1) {
4201 // Found a cycle through variable ident
4202 // Break cycle by removing annotations
4203 ident->decl()->ann().remove(constants().ann.is_defined_var);
4204 Call* c = (*m)[it->second.first]->cast<ConstraintI>()->e()->cast<Call>();
4205 c->ann().removeCall(constants().ann.defines_var);
4206 }
4207 }
4208 };
4209 for (auto& it : definition_map) {
4210 if (it.second.second == 0) {
4211 // not yet visited
4212 definesStack.push_back(it.first);
4213 while (!definesStack.empty()) {
4214 VarDecl* cur = definesStack.back();
4215 if (definition_map[cur].second != DFS_UNKNOWN) {
4216 // already visited (or already finished), now finished
4217 definition_map[cur].second = DFS_DONE;
4218 definesStack.pop_back();
4219 } else {
4220 // now visited and on stack
4221 definition_map[cur].second = DFS_SEEN;
4222 if (Call* c = (*m)[definition_map[cur].first]
4223 ->cast<ConstraintI>()
4224 ->e()
4225 ->dynamicCast<Call>()) {
4226 // Variable is defined by a call, push all arguments
4227 for (unsigned int i = 0; i < c->argCount(); i++) {
4228 if (c->arg(i)->type().isPar()) {
4229 continue;
4230 }
4231 if (Id* ident = c->arg(i)->dynamicCast<Id>()) {
4232 if (ident->type().dim() > 0) {
4233 if (auto* al = Expression::dynamicCast<ArrayLit>(ident->decl()->e())) {
4234 for (auto* e : al->getVec()) {
4235 if (auto* ident = e->dynamicCast<Id>()) {
4236 checkId(cur, ident);
4237 }
4238 }
4239 }
4240 } else if (ident->type().isvar()) {
4241 checkId(cur, ident);
4242 }
4243 } else if (auto* al = c->arg(i)->dynamicCast<ArrayLit>()) {
4244 for (auto* e : al->getVec()) {
4245 if (auto* ident = e->dynamicCast<Id>()) {
4246 checkId(cur, ident);
4247 }
4248 }
4249 }
4250 }
4251 }
4252 }
4253 }
4254 }
4255 }
4256 }
4257
4258 // Sort VarDecls in FlatZinc so that VarDecls are declared before use
4259 std::vector<VarDeclI*> sortedVarDecls(declsWithIds.size());
4260 int vdCount = 0;
4261 for (int declsWithId : declsWithIds) {
4262 VarDecl* cur = (*m)[declsWithId]->cast<VarDeclI>()->e();
4263 std::vector<int> stack;
4264 while ((cur != nullptr) && cur->payload() < 0) {
4265 stack.push_back(cur->payload());
4266 if (Id* id = cur->e()->dynamicCast<Id>()) {
4267 cur = id->decl();
4268 } else {
4269 cur = nullptr;
4270 }
4271 }
4272 for (auto i = static_cast<unsigned int>(stack.size()); (i--) != 0U;) {
4273 auto* vdi = (*m)[-stack[i] - 1]->cast<VarDeclI>();
4274 vdi->e()->payload(-vdi->e()->payload() - 1);
4275 sortedVarDecls[vdCount++] = vdi;
4276 }
4277 }
4278 for (unsigned int i = 0; i < declsWithIds.size(); i++) {
4279 (*m)[declsWithIds[i]] = sortedVarDecls[i];
4280 }
4281
4282 // Remove marked items
4283 m->compact();
4284 e.envi().output->compact();
4285
4286 for (auto& it : env.varOccurrences.itemMap) {
4287 std::vector<Item*> toRemove;
4288 for (auto* iit : it.second) {
4289 if (iit->removed()) {
4290 toRemove.push_back(iit);
4291 }
4292 }
4293 for (auto& i : toRemove) {
4294 it.second.erase(i);
4295 }
4296 }
4297
4298 class Cmp {
4299 public:
4300 bool operator()(Item* i, Item* j) {
4301 if (i->iid() == Item::II_FUN || j->iid() == Item::II_FUN) {
4302 if (i->iid() == j->iid()) {
4303 return false;
4304 }
4305 return i->iid() == Item::II_FUN;
4306 }
4307 if (i->iid() == Item::II_SOL) {
4308 assert(j->iid() != i->iid());
4309 return false;
4310 }
4311 if (j->iid() == Item::II_SOL) {
4312 assert(j->iid() != i->iid());
4313 return true;
4314 }
4315 if (i->iid() == Item::II_VD) {
4316 if (j->iid() != i->iid()) {
4317 return true;
4318 }
4319 if (i->cast<VarDeclI>()->e()->type().isPar() && j->cast<VarDeclI>()->e()->type().isvar()) {
4320 return true;
4321 }
4322 if (j->cast<VarDeclI>()->e()->type().isPar() && i->cast<VarDeclI>()->e()->type().isvar()) {
4323 return false;
4324 }
4325 if (i->cast<VarDeclI>()->e()->type().dim() == 0 &&
4326 j->cast<VarDeclI>()->e()->type().dim() != 0) {
4327 return true;
4328 }
4329 if (i->cast<VarDeclI>()->e()->type().dim() != 0 &&
4330 j->cast<VarDeclI>()->e()->type().dim() == 0) {
4331 return false;
4332 }
4333 if (i->cast<VarDeclI>()->e()->e() == nullptr && j->cast<VarDeclI>()->e()->e() != nullptr) {
4334 return true;
4335 }
4336 if ((i->cast<VarDeclI>()->e()->e() != nullptr) &&
4337 (j->cast<VarDeclI>()->e()->e() != nullptr) &&
4338 !i->cast<VarDeclI>()->e()->e()->isa<Id>() && j->cast<VarDeclI>()->e()->e()->isa<Id>()) {
4339 return true;
4340 }
4341 }
4342 return false;
4343 }
4344 } _cmp;
4345 // Perform final sorting
4346 std::stable_sort(m->begin(), m->end(), _cmp);
4347}
4348
4349FlatModelStatistics statistics(Env& m) {
4350 Model* flat = m.flat();
4351 FlatModelStatistics stats;
4352 stats.n_reif_ct = m.envi().counters.reifConstraints;
4353 stats.n_imp_ct = m.envi().counters.impConstraints;
4354 stats.n_imp_del = m.envi().counters.impDel;
4355 stats.n_lin_del = m.envi().counters.linDel;
4356 for (auto& i : *flat) {
4357 if (!i->removed()) {
4358 if (auto* vdi = i->dynamicCast<VarDeclI>()) {
4359 Type t = vdi->e()->type();
4360 if (t.isvar() && t.dim() == 0) {
4361 if (t.isSet()) {
4362 stats.n_set_vars++;
4363 } else if (t.isint()) {
4364 stats.n_int_vars++;
4365 } else if (t.isbool()) {
4366 stats.n_bool_vars++;
4367 } else if (t.isfloat()) {
4368 stats.n_float_vars++;
4369 }
4370 }
4371 } else if (auto* ci = i->dynamicCast<ConstraintI>()) {
4372 if (Call* call = ci->e()->dynamicCast<Call>()) {
4373 if (call->id().endsWith("_reif")) {
4374 stats.n_reif_ct++;
4375 } else if (call->id().endsWith("_imp")) {
4376 stats.n_imp_ct++;
4377 }
4378 if (call->argCount() > 0) {
4379 Type all_t;
4380 for (unsigned int i = 0; i < call->argCount(); i++) {
4381 Type t = call->arg(i)->type();
4382 if (t.isvar()) {
4383 if (t.st() == Type::ST_SET ||
4384 (t.bt() == Type::BT_FLOAT && all_t.st() != Type::ST_SET) ||
4385 (t.bt() == Type::BT_INT && all_t.bt() != Type::BT_FLOAT &&
4386 all_t.st() != Type::ST_SET) ||
4387 (t.bt() == Type::BT_BOOL && all_t.bt() != Type::BT_INT &&
4388 all_t.bt() != Type::BT_FLOAT && all_t.st() != Type::ST_SET)) {
4389 all_t = t;
4390 }
4391 }
4392 }
4393 if (all_t.isvar()) {
4394 if (all_t.st() == Type::ST_SET) {
4395 stats.n_set_ct++;
4396 } else if (all_t.bt() == Type::BT_INT) {
4397 stats.n_int_ct++;
4398 } else if (all_t.bt() == Type::BT_BOOL) {
4399 stats.n_bool_ct++;
4400 } else if (all_t.bt() == Type::BT_FLOAT) {
4401 stats.n_float_ct++;
4402 }
4403 }
4404 }
4405 }
4406 }
4407 }
4408 }
4409 return stats;
4410}
4411
4412} // namespace MiniZinc