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/astiterator.hh>
13 #include <minizinc/output.hh>
14 #include <minizinc/typecheck.hh>
15 
16 namespace MiniZinc {
17 
18 namespace {
19 
20 // Test if all parameters and the return type are par
is_completely_par(EnvI & env,FunctionI * fi,const std::vector<Type> & tv)21 bool is_completely_par(EnvI& env, FunctionI* fi, const std::vector<Type>& tv) {
22   if (fi->e() != nullptr) {
23     // This is not a builtin, so check parameters
24     for (auto* p : fi->params()) {
25       if (p->type().isvar()) {
26         return false;
27       }
28     }
29   }
30   return fi->rtype(env, tv, false).isPar();
31 }
32 
33 }  // namespace
34 
check_output_par_fn(EnvI & env,Call * rhs)35 void check_output_par_fn(EnvI& env, Call* rhs) {
36   std::vector<Type> tv(rhs->argCount());
37   for (unsigned int i = rhs->argCount(); (i--) != 0U;) {
38     tv[i] = rhs->arg(i)->type();
39     tv[i].ti(Type::TI_PAR);
40   }
41   FunctionI* decl = env.output->matchFn(env, rhs->id(), tv, false);
42   if (decl == nullptr) {
43     FunctionI* origdecl = env.model->matchFn(env, rhs->id(), tv, false);
44     if (origdecl == nullptr || !is_completely_par(env, origdecl, tv)) {
45       std::ostringstream ss;
46       ss << "function " << rhs->id() << " is used in output, par version needed";
47       throw FlatteningError(env, rhs->loc(), ss.str());
48     }
49     if (!origdecl->fromStdLib()) {
50       decl = copy(env, env.cmap, origdecl)->cast<FunctionI>();
51       CollectOccurrencesE ce(env.outputVarOccurrences, decl);
52       top_down(ce, decl->e());
53       top_down(ce, decl->ti());
54       for (unsigned int i = decl->params().size(); (i--) != 0U;) {
55         top_down(ce, decl->params()[i]);
56       }
57       (void)env.output->registerFn(env, decl, true);
58       env.output->addItem(decl);
59     } else {
60       decl = origdecl;
61     }
62   }
63   rhs->type(decl->rtype(env, tv, false));
64   rhs->decl(decl);
65 }
66 
cannot_use_rhs_for_output(EnvI & env,Expression * e,std::unordered_set<FunctionI * > & seen_functions)67 bool cannot_use_rhs_for_output(EnvI& env, Expression* e,
68                                std::unordered_set<FunctionI*>& seen_functions) {
69   if (e == nullptr) {
70     return true;
71   }
72 
73   class V : public EVisitor {
74   public:
75     EnvI& env;
76     std::unordered_set<FunctionI*>& seenFunctions;
77     bool success;
78     V(EnvI& env0, std::unordered_set<FunctionI*>& seenFunctions0)
79         : env(env0), seenFunctions(seenFunctions0), success(true) {}
80     /// Visit anonymous variable
81     void vAnonVar(const AnonVar& /*v*/) { success = false; }
82     /// Visit array literal
83     void vArrayLit(const ArrayLit& /*al*/) {}
84     /// Visit array access
85     void vArrayAccess(const ArrayAccess& /*aa*/) {}
86     /// Visit array comprehension
87     void vComprehension(const Comprehension& /*c*/) {}
88     /// Visit if-then-else
89     void vITE(const ITE& /*ite*/) {}
90     /// Visit binary operator
91     void vBinOp(const BinOp& /*bo*/) {}
92     /// Visit unary operator
93     void vUnOp(const UnOp& /*uo*/) {}
94     /// Visit call
95     void vCall(Call& c) {
96       std::vector<Type> tv(c.argCount());
97       for (unsigned int i = c.argCount(); (i--) != 0U;) {
98         tv[i] = c.arg(i)->type();
99         tv[i].ti(Type::TI_PAR);
100       }
101       FunctionI* decl = env.output->matchFn(env, c.id(), tv, false);
102       Type t;
103       if (decl == nullptr) {
104         FunctionI* origdecl = env.model->matchFn(env, c.id(), tv, false);
105         if (origdecl == nullptr) {
106           std::ostringstream ss;
107           ss << "function " << c.id() << " is used in output, par version needed";
108           throw FlatteningError(env, c.loc(), ss.str());
109         }
110         bool seen = (seenFunctions.find(origdecl) != seenFunctions.end());
111         if (seen) {
112           success = false;
113         } else {
114           seenFunctions.insert(origdecl);
115           if ((origdecl->e() != nullptr) &&
116               cannot_use_rhs_for_output(env, origdecl->e(), seenFunctions)) {
117             success = false;
118           } else {
119             if (!origdecl->fromStdLib()) {
120               decl = copy(env, env.cmap, origdecl)->cast<FunctionI>();
121               CollectOccurrencesE ce(env.outputVarOccurrences, decl);
122               top_down(ce, decl->e());
123               top_down(ce, decl->ti());
124               for (unsigned int i = decl->params().size(); (i--) != 0U;) {
125                 top_down(ce, decl->params()[i]);
126               }
127               (void)env.output->registerFn(env, decl, true);
128               env.output->addItem(decl);
129               output_vardecls(env, origdecl, decl->e());
130               output_vardecls(env, origdecl, decl->ti());
131             } else {
132               decl = origdecl;
133             }
134             c.decl(decl);
135           }
136         }
137       }
138       if (success) {
139         t = decl->rtype(env, tv, false);
140         if (!t.isPar()) {
141           success = false;
142         }
143       }
144     }
145     void vId(const Id& /*id*/) {}
146     /// Visit let
147     void vLet(const Let& /*let*/) { success = false; }
148     /// Visit variable declaration
149     void vVarDecl(const VarDecl& /*vd*/) {}
150     /// Visit type inst
151     void vTypeInst(const TypeInst& /*ti*/) {}
152     /// Visit TIId
153     void vTIId(const TIId& /*tiid*/) {}
154     /// Determine whether to enter node
155     bool enter(Expression* /*e*/) const { return success; }
156   } _v(env, seen_functions);
157   top_down(_v, e);
158 
159   return !_v.success;
160 }
161 
cannot_use_rhs_for_output(EnvI & env,Expression * e)162 bool cannot_use_rhs_for_output(EnvI& env, Expression* e) {
163   std::unordered_set<FunctionI*> seen_functions;
164   return cannot_use_rhs_for_output(env, e, seen_functions);
165 }
166 
remove_is_output(VarDecl * vd)167 void remove_is_output(VarDecl* vd) {
168   if (vd == nullptr) {
169     return;
170   }
171   vd->ann().remove(constants().ann.output_var);
172   vd->ann().removeCall(constants().ann.output_array);
173 }
174 
copy_output(EnvI & e)175 void copy_output(EnvI& e) {
176   struct CopyOutput : public EVisitor {
177     EnvI& env;
178     CopyOutput(EnvI& env0) : env(env0) {}
179     static void vId(Id& _id) { _id.decl(_id.decl()->flat()); }
180     void vCall(Call& c) {
181       std::vector<Type> tv(c.argCount());
182       for (unsigned int i = c.argCount(); (i--) != 0U;) {
183         tv[i] = c.arg(i)->type();
184         tv[i].ti(Type::TI_PAR);
185       }
186       FunctionI* decl = c.decl();
187       if (!decl->fromStdLib()) {
188         env.flatAddItem(decl);
189       }
190     }
191   };
192 
193   if (OutputI* oi = e.model->outputItem()) {
194     GCLock lock;
195     auto* noi = copy(e, oi)->cast<OutputI>();
196     CopyOutput co(e);
197     top_down(co, noi->e());
198     e.flatAddItem(noi);
199   }
200 }
201 
cleanup_output(EnvI & env)202 void cleanup_output(EnvI& env) {
203   for (auto& i : *env.output) {
204     if (auto* vdi = i->dynamicCast<VarDeclI>()) {
205       vdi->e()->flat(nullptr);
206     }
207   }
208 }
209 
make_par(EnvI & env,Expression * e)210 void make_par(EnvI& env, Expression* e) {
211   class OutputJSON : public EVisitor {
212   public:
213     EnvI& env;
214     OutputJSON(EnvI& env0) : env(env0) {}
215     void vCall(Call& c) {
216       if (c.id() == "outputJSON") {
217         bool outputObjective = (c.argCount() == 1 && eval_bool(env, c.arg(0)));
218         c.id(ASTString("array1d"));
219         Expression* json =
220             copy(env, env.cmap, create_json_output(env, outputObjective, false, false));
221         std::vector<Expression*> new_args({json});
222         new_args[0]->type(Type::parstring(1));
223         c.args(new_args);
224       }
225     }
226   } _outputJSON(env);
227   top_down(_outputJSON, e);
228   class Par : public EVisitor {
229   public:
230     /// Visit variable declaration
231     static void vVarDecl(VarDecl& vd) { vd.ti()->type(vd.type()); }
232     /// Determine whether to enter node
233     static bool enter(Expression* e) {
234       Type t = e->type();
235       t.ti(Type::TI_PAR);
236       t.cv(false);
237       e->type(t);
238       return true;
239     }
240   } _par;
241   top_down(_par, e);
242   class Decls : public EVisitor {
243   public:
244     EnvI& env;
245     Decls(EnvI& env0) : env(env0) {}
246     void vCall(Call& c) {
247       if (c.id() == "format" || c.id() == "show" || c.id() == "showDzn" || c.id() == "showJSON") {
248         unsigned int enumId = c.arg(c.argCount() - 1)->type().enumId();
249         if (enumId != 0U && c.arg(c.argCount() - 1)->type().dim() != 0) {
250           const std::vector<unsigned int>& enumIds = env.getArrayEnum(enumId);
251           enumId = enumIds[enumIds.size() - 1];
252         }
253         if (enumId > 0) {
254           GCLock lock;
255           Expression* obj = c.arg(c.argCount() - 1);
256           Id* ti_id = env.getEnum(enumId)->e()->id();
257           std::string enumName = create_enum_to_string_name(ti_id, "_toString_");
258           bool is_json = c.id() == "showJSON";
259           const int dimensions = obj->type().dim();
260           if (is_json && dimensions > 1) {
261             // Create generators for dimensions selection
262             std::vector<Expression*> slice_dimensions(dimensions);
263             std::vector<Generator> generators;
264             generators.reserve(dimensions - 1);
265             auto* idx_ti = new TypeInst(Location().introduce(), Type::parint());
266             for (int i = 0; i < dimensions - 1; ++i) {
267               auto* idx_i = new VarDecl(Location().introduce(), idx_ti, env.genId());
268               idx_i->toplevel(false);
269               Call* index_set_xx = new Call(
270                   Location().introduce(),
271                   "index_set_" + std::to_string(i + 1) + "of" + std::to_string(dimensions), {obj});
272               index_set_xx->type(Type::parsetint());
273               generators.push_back(Generator({idx_i}, index_set_xx, nullptr));
274               slice_dimensions[i] =
275                   new BinOp(Location().introduce(), idx_i->id(), BOT_DOTDOT, idx_i->id());
276               slice_dimensions[i]->type(Type::parsetint());
277             }
278 
279             // Construct innermost slicing operation
280             Call* index_set_n = new Call(
281                 Location().introduce(),
282                 "index_set_" + std::to_string(dimensions) + "of" + std::to_string(dimensions),
283                 {obj});
284             index_set_n->type(Type::parsetint());
285             slice_dimensions[dimensions - 1] = index_set_n;
286             auto* al_slice_dim = new ArrayLit(Location().introduce(), slice_dimensions);
287             al_slice_dim->type(Type::parsetint(1));
288 
289             auto* slice_call =
290                 new Call(Location().introduce(), "slice_1d", {obj, al_slice_dim, index_set_n});
291             Type tt = obj->type();
292             tt.dim(1);
293             slice_call->type(tt);
294             Call* _toString_ENUM =
295                 new Call(Location().introduce(), enumName,
296                          {slice_call, constants().boollit(false), constants().boollit(true)});
297             _toString_ENUM->type(Type::parstring());
298 
299             // Build multi-level JSON Array string
300             auto* comma = new StringLit(Location().introduce(), ", ");
301             comma->type(Type::parstring());
302             auto join = [&](Expression* expr, Generator gen) -> Expression* {
303               Generators generators;
304               generators.g.push_back(gen);
305               auto* comp = new Comprehension(Location().introduce(), expr, generators, false);
306               comp->type(Type::parstring(1));
307               Call* cc = new Call(Location().introduce(), "join", {comma, comp});
308               cc->type(Type::parstring());
309               return cc;
310             };
311             auto* sl_open = new StringLit(Location().introduce(), "[");
312             sl_open->type(Type::parstring());
313             auto* sl_close = new StringLit(Location().introduce(), "]");
314             sl_close->type(Type::parstring());
315 
316             auto* al_concat = new ArrayLit(
317                 Location().introduce(),
318                 std::vector<Expression*>(
319                     {sl_open, join(_toString_ENUM, generators[dimensions - 2]), sl_close}));
320             al_concat->type(Type::parstring(1));
321             for (int i = dimensions - 3; i >= 0; --i) {
322               Call* concat = new Call(Location().introduce(), "concat", {al_concat});
323               concat->type(Type::parstring());
324               al_concat = new ArrayLit(
325                   Location().introduce(),
326                   std::vector<Expression*>({sl_open, join(concat, generators[i]), sl_close}));
327               al_concat->type(Type::parstring(1));
328             }
329             std::vector<Expression*> args = {al_concat};
330             c.args(args);
331             c.id(ASTString("concat"));
332           } else {
333             std::vector<Expression*> args = {obj, constants().boollit(c.id() == "showDzn"),
334                                              constants().boollit(is_json)};
335             c.args(args);
336             c.id(ASTString(enumName));
337           }
338         }
339         if (c.id() == "showDzn" || (c.id() == "showJSON" && enumId > 0)) {
340           c.id(constants().ids.show);
341         }
342       }
343       c.decl(env.model->matchFn(env, &c, false));
344     }
345     void vBinOp(BinOp& bo) {
346       std::vector<Expression*> args = {bo.lhs(), bo.rhs()};
347       bo.decl(env.model->matchFn(env, bo.opToString(), args, false));
348     }
349     void vUnop(UnOp& uo) {
350       std::vector<Expression*> args = {uo.e()};
351       uo.decl(env.model->matchFn(env, uo.opToString(), args, false));
352     }
353   } _decls(env);
354   top_down(_decls, e);
355 }
356 
check_rename_var(EnvI & e,VarDecl * vd)357 void check_rename_var(EnvI& e, VarDecl* vd) {
358   if (vd->id()->idn() != vd->flat()->id()->idn()) {
359     auto* vd_rename_ti = copy(e, e.cmap, vd->ti())->cast<TypeInst>();
360     auto* vd_rename =
361         new VarDecl(Location().introduce(), vd_rename_ti, vd->flat()->id()->idn(), nullptr);
362     vd_rename->flat(vd->flat());
363     make_par(e, vd_rename);
364     vd->e(vd_rename->id());
365     e.output->addItem(new VarDeclI(Location().introduce(), vd_rename));
366   }
367 }
368 
369 class ClearAnnotations {
370 public:
371   /// Push all elements of \a v onto \a stack
372   template <class E>
pushVec(std::vector<Expression * > & stack,ASTExprVec<E> v)373   static void pushVec(std::vector<Expression*>& stack, ASTExprVec<E> v) {
374     for (unsigned int i = 0; i < v.size(); i++) {
375       stack.push_back(v[i]);
376     }
377   }
378 
run(Expression * root)379   static void run(Expression* root) {
380     std::vector<Expression*> stack;
381     stack.push_back(root);
382     while (!stack.empty()) {
383       Expression* e = stack.back();
384       stack.pop_back();
385       if (e == nullptr) {
386         continue;
387       }
388       e->ann().clear();
389       switch (e->eid()) {
390         case Expression::E_INTLIT:
391         case Expression::E_FLOATLIT:
392         case Expression::E_BOOLLIT:
393         case Expression::E_STRINGLIT:
394         case Expression::E_ID:
395         case Expression::E_ANON:
396         case Expression::E_TIID:
397           break;
398         case Expression::E_SETLIT:
399           pushVec(stack, e->template cast<SetLit>()->v());
400           break;
401         case Expression::E_ARRAYLIT:
402           for (unsigned int i = 0; i < e->cast<ArrayLit>()->size(); i++) {
403             stack.push_back((*e->cast<ArrayLit>())[i]);
404           }
405           break;
406         case Expression::E_ARRAYACCESS:
407           pushVec(stack, e->template cast<ArrayAccess>()->idx());
408           stack.push_back(e->template cast<ArrayAccess>()->v());
409           break;
410         case Expression::E_COMP: {
411           auto* comp = e->template cast<Comprehension>();
412           for (unsigned int i = comp->numberOfGenerators(); (i--) != 0U;) {
413             stack.push_back(comp->where(i));
414             stack.push_back(comp->in(i));
415             for (unsigned int j = comp->numberOfDecls(i); (j--) != 0U;) {
416               stack.push_back(comp->decl(i, j));
417             }
418           }
419           stack.push_back(comp->e());
420         } break;
421         case Expression::E_ITE: {
422           ITE* ite = e->template cast<ITE>();
423           stack.push_back(ite->elseExpr());
424           for (int i = 0; i < ite->size(); i++) {
425             stack.push_back(ite->ifExpr(i));
426             stack.push_back(ite->thenExpr(i));
427           }
428         } break;
429         case Expression::E_BINOP:
430           stack.push_back(e->template cast<BinOp>()->rhs());
431           stack.push_back(e->template cast<BinOp>()->lhs());
432           break;
433         case Expression::E_UNOP:
434           stack.push_back(e->template cast<UnOp>()->e());
435           break;
436         case Expression::E_CALL:
437           for (unsigned int i = 0; i < e->template cast<Call>()->argCount(); i++) {
438             stack.push_back(e->template cast<Call>()->arg(i));
439           }
440           break;
441         case Expression::E_VARDECL:
442           stack.push_back(e->template cast<VarDecl>()->e());
443           stack.push_back(e->template cast<VarDecl>()->ti());
444           break;
445         case Expression::E_LET:
446           stack.push_back(e->template cast<Let>()->in());
447           pushVec(stack, e->template cast<Let>()->let());
448           break;
449         case Expression::E_TI:
450           stack.push_back(e->template cast<TypeInst>()->domain());
451           pushVec(stack, e->template cast<TypeInst>()->ranges());
452           break;
453       }
454     }
455   }
456 };
457 
output_vardecls(EnvI & env,Item * ci,Expression * e)458 void output_vardecls(EnvI& env, Item* ci, Expression* e) {
459   class O : public EVisitor {
460   public:
461     EnvI& env;
462     Item* ci;
463     O(EnvI& env0, Item* ci0) : env(env0), ci(ci0) {}
464     void vId(Id& id) {
465       if (&id == constants().absent) {
466         return;
467       }
468       if (!id.decl()->toplevel()) {
469         return;
470       }
471       VarDecl* vd = id.decl();
472       VarDecl* reallyFlat = vd->flat();
473       while (reallyFlat != nullptr && reallyFlat != reallyFlat->flat()) {
474         reallyFlat = reallyFlat->flat();
475       }
476       auto idx = reallyFlat != nullptr ? env.outputFlatVarOccurrences.idx.find(reallyFlat->id())
477                                        : env.outputFlatVarOccurrences.idx.end();
478       auto idx2 = env.outputVarOccurrences.idx.find(vd->id());
479       if (idx == env.outputFlatVarOccurrences.idx.end() &&
480           idx2 == env.outputVarOccurrences.idx.end()) {
481         auto* nvi = new VarDeclI(Location().introduce(), copy(env, env.cmap, vd)->cast<VarDecl>());
482         Type t = nvi->e()->ti()->type();
483         if (t.ti() != Type::TI_PAR) {
484           t.ti(Type::TI_PAR);
485         }
486         make_par(env, nvi->e());
487         nvi->e()->ti()->domain(nullptr);
488         nvi->e()->flat(vd->flat());
489         ClearAnnotations::run(nvi->e());
490         nvi->e()->introduced(false);
491         if (reallyFlat != nullptr) {
492           env.outputFlatVarOccurrences.addIndex(reallyFlat, static_cast<int>(env.output->size()));
493         }
494         env.outputVarOccurrences.addIndex(nvi, static_cast<int>(env.output->size()));
495         env.outputVarOccurrences.add(nvi->e(), ci);
496         env.output->addItem(nvi);
497 
498         IdMap<KeepAlive>::iterator it;
499         if ((it = env.reverseMappers.find(nvi->e()->id())) != env.reverseMappers.end()) {
500           Call* rhs = copy(env, env.cmap, it->second())->cast<Call>();
501           check_output_par_fn(env, rhs);
502           output_vardecls(env, nvi, it->second());
503           nvi->e()->e(rhs);
504         } else if ((reallyFlat != nullptr) && cannot_use_rhs_for_output(env, reallyFlat->e())) {
505           assert(nvi->e()->flat());
506           nvi->e()->e(nullptr);
507           if (nvi->e()->type().dim() == 0) {
508             reallyFlat->addAnnotation(constants().ann.output_var);
509           } else {
510             std::vector<Expression*> args(reallyFlat->e()->type().dim());
511             for (unsigned int i = 0; i < args.size(); i++) {
512               if (nvi->e()->ti()->ranges()[i]->domain() == nullptr) {
513                 args[i] = new SetLit(Location().introduce(),
514                                      eval_intset(env, reallyFlat->ti()->ranges()[i]->domain()));
515               } else {
516                 args[i] = new SetLit(Location().introduce(),
517                                      eval_intset(env, nvi->e()->ti()->ranges()[i]->domain()));
518               }
519             }
520             auto* al = new ArrayLit(Location().introduce(), args);
521             args.resize(1);
522             args[0] = al;
523             reallyFlat->addAnnotation(
524                 new Call(Location().introduce(), constants().ann.output_array, args));
525           }
526           check_rename_var(env, nvi->e());
527         } else {
528           output_vardecls(env, nvi, nvi->e()->ti());
529           output_vardecls(env, nvi, nvi->e()->e());
530         }
531         CollectOccurrencesE ce(env.outputVarOccurrences, nvi);
532         top_down(ce, nvi->e());
533       }
534     }
535   } _o(env, ci);
536   top_down(_o, e);
537 }
538 
process_deletions(EnvI & e)539 void process_deletions(EnvI& e) {
540   std::vector<VarDecl*> deletedVarDecls;
541   for (unsigned int i = 0; i < e.output->size(); i++) {
542     if (auto* vdi = (*e.output)[i]->dynamicCast<VarDeclI>()) {
543       if (!vdi->removed() && e.outputVarOccurrences.occurrences(vdi->e()) == 0 &&
544           !vdi->e()->ann().contains(constants().ann.mzn_check_var) &&
545           !(vdi->e()->id()->idn() == -1 && (vdi->e()->id()->v() == "_mzn_solution_checker" ||
546                                             vdi->e()->id()->v() == "_mzn_stats_checker"))) {
547         CollectDecls cd(e.outputVarOccurrences, deletedVarDecls, vdi);
548         top_down(cd, vdi->e()->e());
549         remove_is_output(vdi->e()->flat());
550         if (e.outputVarOccurrences.find(vdi->e()) != -1) {
551           e.outputVarOccurrences.remove(vdi->e());
552         }
553         vdi->remove();
554       }
555     }
556   }
557   while (!deletedVarDecls.empty()) {
558     VarDecl* cur = deletedVarDecls.back();
559     deletedVarDecls.pop_back();
560     if (e.outputVarOccurrences.occurrences(cur) == 0) {
561       auto cur_idx = e.outputVarOccurrences.idx.find(cur->id());
562       if (cur_idx != e.outputVarOccurrences.idx.end()) {
563         auto* vdi = (*e.output)[cur_idx->second]->cast<VarDeclI>();
564         if (!vdi->removed()) {
565           CollectDecls cd(e.outputVarOccurrences, deletedVarDecls, vdi);
566           top_down(cd, cur->e());
567           remove_is_output(vdi->e()->flat());
568           if (e.outputVarOccurrences.find(vdi->e()) != -1) {
569             e.outputVarOccurrences.remove(vdi->e());
570           }
571           vdi->remove();
572         }
573       }
574     }
575   }
576 
577   for (auto& it : e.outputVarOccurrences.itemMap) {
578     std::vector<Item*> toRemove;
579     for (auto* iit : it.second) {
580       if (iit->removed()) {
581         toRemove.push_back(iit);
582       }
583     }
584     for (auto& i : toRemove) {
585       it.second.erase(i);
586     }
587   }
588 }
589 
create_dzn_output_item(EnvI & e,bool outputObjective,bool includeOutputItem,bool hasChecker,bool outputForChecker)590 void create_dzn_output_item(EnvI& e, bool outputObjective, bool includeOutputItem, bool hasChecker,
591                             bool outputForChecker) {
592   std::vector<Expression*> outputVars;
593 
594   class DZNOVisitor : public ItemVisitor {
595   protected:
596     EnvI& _e;
597     bool _outputObjective;
598     bool _includeOutputItem;
599     bool _outputForChecker;
600     std::vector<Expression*>& _outputVars;
601     bool _hadAddToOutput;
602 
603   public:
604     DZNOVisitor(EnvI& e, bool outputObjective, bool includeOutputItem, bool outputForChecker,
605                 std::vector<Expression*>& outputVars)
606         : _e(e),
607           _outputObjective(outputObjective),
608           _includeOutputItem(includeOutputItem),
609           _outputForChecker(outputForChecker),
610           _outputVars(outputVars),
611           _hadAddToOutput(false) {}
612     void vVarDeclI(VarDeclI* vdi) {
613       VarDecl* vd = vdi->e();
614       bool process_var = false;
615       if (_outputForChecker) {
616         if (vd->ann().contains(constants().ann.mzn_check_var)) {
617           process_var = true;
618         }
619       } else {
620         if (_outputObjective && vd->id()->idn() == -1 && vd->id()->v() == "_objective") {
621           process_var = true;
622         } else {
623           if (vd->ann().contains(constants().ann.add_to_output)) {
624             if (!_hadAddToOutput) {
625               _outputVars.clear();
626             }
627             _hadAddToOutput = true;
628             process_var = true;
629           } else {
630             if (!_hadAddToOutput) {
631               process_var = false;
632               if (vd->type().isvar()) {
633                 if (vd->e() != nullptr) {
634                   if (auto* al = vd->e()->dynamicCast<ArrayLit>()) {
635                     for (unsigned int i = 0; i < al->size(); i++) {
636                       if ((*al)[i]->isa<AnonVar>()) {
637                         process_var = true;
638                         break;
639                       }
640                     }
641                   } else if (vd->ann().contains(constants().ann.rhs_from_assignment)) {
642                     process_var = true;
643                   }
644                 } else {
645                   process_var = true;
646                 }
647               }
648             }
649           }
650         }
651       }
652       if (process_var) {
653         std::ostringstream s;
654         s << vd->id()->str() << " = ";
655         bool needArrayXd = false;
656         if (vd->type().dim() > 0) {
657           ArrayLit* al = nullptr;
658           if (!vd->ann().contains(constants().ann.output_only)) {
659             if ((vd->flat() != nullptr) && (vd->flat()->e() != nullptr)) {
660               al = eval_array_lit(_e, vd->flat()->e());
661             } else if (vd->e() != nullptr) {
662               al = eval_array_lit(_e, vd->e());
663             }
664           }
665           if (al == nullptr || al->size() > 0) {
666             needArrayXd = true;
667             s << "array" << vd->type().dim() << "d(";
668             for (int i = 0; i < vd->type().dim(); i++) {
669               unsigned int enumId =
670                   (vd->type().enumId() != 0 ? _e.getArrayEnum(vd->type().enumId())[i] : 0);
671               if (enumId != 0) {
672                 s << _e.getEnum(enumId)->e()->id()->str() << ", ";
673               } else if (al != nullptr) {
674                 s << al->min(i) << ".." << al->max(i) << ", ";
675               } else if (vd->ti()->ranges()[i]->domain() != nullptr) {
676                 IntSetVal* idxset = eval_intset(_e, vd->ti()->ranges()[i]->domain());
677                 s << *idxset << ", ";
678               } else {
679                 // Don't know index set range - have to compute in solns2out
680                 auto* sl = new StringLit(Location().introduce(), s.str());
681                 _outputVars.push_back(sl);
682 
683                 std::string index_set_fn = "index_set";
684                 if (vd->type().dim() > 1) {
685                   index_set_fn +=
686                       "_" + std::to_string(i + 1) + "of" + std::to_string(vd->type().dim());
687                 }
688                 auto* index_set_xx = new Call(Location().introduce(), index_set_fn, {vd->id()});
689                 index_set_xx->type(Type::parsetint());
690                 auto* i_fi = _e.model->matchFn(_e, index_set_xx, false);
691                 assert(i_fi);
692                 index_set_xx->decl(i_fi);
693 
694                 auto* show = new Call(Location().introduce(), constants().ids.show, {index_set_xx});
695                 show->type(Type::parstring());
696                 FunctionI* s_fi = _e.model->matchFn(_e, show, false);
697                 assert(s_fi);
698                 show->decl(s_fi);
699 
700                 _outputVars.push_back(show);
701                 s.str(", ");
702               }
703             }
704           }
705         }
706         auto* sl = new StringLit(Location().introduce(), s.str());
707         _outputVars.push_back(sl);
708 
709         std::vector<Expression*> showArgs(1);
710         showArgs[0] = vd->id();
711         Call* show = new Call(Location().introduce(), ASTString("showDzn"), showArgs);
712         show->type(Type::parstring());
713         FunctionI* fi = _e.model->matchFn(_e, show, false);
714         assert(fi);
715         show->decl(fi);
716         _outputVars.push_back(show);
717         std::string ends = needArrayXd ? ")" : "";
718         ends += ";\n";
719         auto* eol = new StringLit(Location().introduce(), ends);
720         _outputVars.push_back(eol);
721       }
722     }
723     void vOutputI(OutputI* oi) {
724       if (_includeOutputItem) {
725         _outputVars.push_back(new StringLit(Location().introduce(), "_output = "));
726         Call* concat = new Call(Location().introduce(), ASTString("concat"), {oi->e()});
727         concat->type(Type::parstring());
728         FunctionI* fi = _e.model->matchFn(_e, concat, false);
729         assert(fi);
730         concat->decl(fi);
731         Call* show = new Call(Location().introduce(), ASTString("showDzn"), {concat});
732         show->type(Type::parstring());
733         fi = _e.model->matchFn(_e, show, false);
734         assert(fi);
735         show->decl(fi);
736         _outputVars.push_back(show);
737         _outputVars.push_back(new StringLit(Location().introduce(), ";\n"));
738       }
739 
740       oi->remove();
741     }
742   } dznov(e, outputObjective, includeOutputItem, outputForChecker, outputVars);
743 
744   iter_items(dznov, e.model);
745 
746   if (hasChecker && !outputForChecker) {
747     outputVars.push_back(new StringLit(Location().introduce(), "_checker = "));
748     auto* checker_output = new Call(Location().introduce(), ASTString("showCheckerOutput"), {});
749     checker_output->type(Type::parstring());
750     FunctionI* fi = e.model->matchFn(e, checker_output, false);
751     assert(fi);
752     checker_output->decl(fi);
753     auto* show = new Call(Location().introduce(), ASTString("showDzn"), {checker_output});
754     show->type(Type::parstring());
755     fi = e.model->matchFn(e, show, false);
756     assert(fi);
757     show->decl(fi);
758     outputVars.push_back(show);
759     outputVars.push_back(new StringLit(Location().introduce(), ";\n"));
760   }
761 
762   auto* newOutputItem =
763       new OutputI(Location().introduce(), new ArrayLit(Location().introduce(), outputVars));
764   e.model->addItem(newOutputItem);
765 }
766 
create_json_output(EnvI & e,bool outputObjective,bool includeOutputItem,bool hasChecker)767 ArrayLit* create_json_output(EnvI& e, bool outputObjective, bool includeOutputItem,
768                              bool hasChecker) {
769   std::vector<Expression*> outputVars;
770   outputVars.push_back(new StringLit(Location().introduce(), "{\n"));
771 
772   class JSONOVisitor : public ItemVisitor {
773   protected:
774     EnvI& _e;
775     bool _outputObjective;
776     bool _includeOutputItem;
777     std::vector<Expression*>& _outputVars;
778     bool _hadAddToOutput;
779 
780   public:
781     bool firstVar;
782     JSONOVisitor(EnvI& e, bool outputObjective, bool includeOutputItem,
783                  std::vector<Expression*>& outputVars)
784         : _e(e),
785           _outputObjective(outputObjective),
786           _outputVars(outputVars),
787           _includeOutputItem(includeOutputItem),
788           _hadAddToOutput(false),
789           firstVar(true) {}
790     void vVarDeclI(VarDeclI* vdi) {
791       VarDecl* vd = vdi->e();
792       bool process_var = false;
793       if (_outputObjective && vd->id()->idn() == -1 && vd->id()->v() == "_objective") {
794         process_var = true;
795       } else {
796         if (vd->ann().contains(constants().ann.add_to_output)) {
797           if (!_hadAddToOutput) {
798             _outputVars.clear();
799             _outputVars.push_back(new StringLit(Location().introduce(), "{\n"));
800             firstVar = true;
801           }
802           _hadAddToOutput = true;
803           process_var = true;
804         } else {
805           if (!_hadAddToOutput) {
806             process_var =
807                 vd->type().isvar() &&
808                 (vd->e() == nullptr || vd->ann().contains(constants().ann.rhs_from_assignment));
809           }
810         }
811       }
812       if (process_var) {
813         std::ostringstream s;
814         if (firstVar) {
815           firstVar = false;
816         } else {
817           s << ",\n";
818         }
819         s << "  \"" << vd->id()->str() << "\""
820           << " : ";
821         auto* sl = new StringLit(Location().introduce(), s.str());
822         _outputVars.push_back(sl);
823 
824         std::vector<Expression*> showArgs(1);
825         showArgs[0] = vd->id();
826         Call* show = new Call(Location().introduce(), "showJSON", showArgs);
827         show->type(Type::parstring());
828         FunctionI* fi = _e.model->matchFn(_e, show, false);
829         assert(fi);
830         show->decl(fi);
831         _outputVars.push_back(show);
832       }
833     }
834     void vOutputI(OutputI* oi) {
835       if (_includeOutputItem) {
836         std::ostringstream s;
837         if (firstVar) {
838           firstVar = false;
839         } else {
840           s << ",\n";
841         }
842         s << "  \"_output\""
843           << " : ";
844         auto* sl = new StringLit(Location().introduce(), s.str());
845         _outputVars.push_back(sl);
846         Call* concat = new Call(Location().introduce(), ASTString("concat"), {oi->e()});
847         concat->type(Type::parstring());
848         FunctionI* fi = _e.model->matchFn(_e, concat, false);
849         assert(fi);
850         concat->decl(fi);
851         Call* show = new Call(Location().introduce(), ASTString("showJSON"), {concat});
852         show->type(Type::parstring());
853         fi = _e.model->matchFn(_e, show, false);
854         assert(fi);
855         show->decl(fi);
856         _outputVars.push_back(show);
857       }
858 
859       oi->remove();
860     }
861   } jsonov(e, outputObjective, includeOutputItem, outputVars);
862 
863   iter_items(jsonov, e.model);
864 
865   if (hasChecker) {
866     std::ostringstream s;
867     if (jsonov.firstVar) {
868       jsonov.firstVar = false;
869     } else {
870       s << ",\n";
871     }
872     s << "  \"_checker\""
873       << " : ";
874     auto* sl = new StringLit(Location().introduce(), s.str());
875     outputVars.push_back(sl);
876     Call* checker_output = new Call(Location().introduce(), ASTString("showCheckerOutput"), {});
877     checker_output->type(Type::parstring());
878     FunctionI* fi = e.model->matchFn(e, checker_output, false);
879     assert(fi);
880     checker_output->decl(fi);
881     Call* show = new Call(Location().introduce(), ASTString("showJSON"), {checker_output});
882     show->type(Type::parstring());
883     fi = e.model->matchFn(e, show, false);
884     assert(fi);
885     show->decl(fi);
886     outputVars.push_back(show);
887   }
888 
889   outputVars.push_back(new StringLit(Location().introduce(), "\n}\n"));
890   return new ArrayLit(Location().introduce(), outputVars);
891 }
create_json_output_item(EnvI & e,bool outputObjective,bool includeOutputItem,bool hasChecker)892 void create_json_output_item(EnvI& e, bool outputObjective, bool includeOutputItem,
893                              bool hasChecker) {
894   auto* newOutputItem =
895       new OutputI(Location().introduce(),
896                   create_json_output(e, outputObjective, includeOutputItem, hasChecker));
897   e.model->addItem(newOutputItem);
898 }
899 
create_output(EnvI & e,FlatteningOptions::OutputMode outputMode,bool outputObjective,bool includeOutputItem,bool hasChecker)900 void create_output(EnvI& e, FlatteningOptions::OutputMode outputMode, bool outputObjective,
901                    bool includeOutputItem, bool hasChecker) {
902   // Create new output model
903   OutputI* outputItem = nullptr;
904   GCLock lock;
905 
906   switch (outputMode) {
907     case FlatteningOptions::OUTPUT_DZN:
908       create_dzn_output_item(e, outputObjective, includeOutputItem, hasChecker, false);
909       break;
910     case FlatteningOptions::OUTPUT_JSON:
911       create_json_output_item(e, outputObjective, includeOutputItem, hasChecker);
912       break;
913     case FlatteningOptions::OUTPUT_CHECKER:
914       create_dzn_output_item(e, outputObjective, includeOutputItem, hasChecker, true);
915       break;
916     default:
917       if (e.model->outputItem() == nullptr) {
918         create_dzn_output_item(e, outputObjective, false, false, false);
919       }
920       break;
921   }
922 
923   // Copy output item from model into output model
924   outputItem = copy(e, e.cmap, e.model->outputItem())->cast<OutputI>();
925   make_par(e, outputItem->e());
926   e.output->addItem(outputItem);
927 
928   // Copy all function definitions that are required for output into the output model
929   class CollectFunctions : public EVisitor {
930   public:
931     EnvI& env;
932     CollectFunctions(EnvI& env0) : env(env0) {}
933     static bool enter(Expression* e) {
934       if (e->type().isvar()) {
935         Type t = e->type();
936         t.ti(Type::TI_PAR);
937         e->type(t);
938       }
939       return true;
940     }
941     void vId(Id& i) {
942       // Also collect functions from output_only variables we depend on
943       if ((i.decl() != nullptr) && i.decl()->ann().contains(constants().ann.output_only)) {
944         top_down(*this, i.decl()->e());
945       }
946     }
947     void vCall(Call& c) {
948       std::vector<Type> tv(c.argCount());
949       for (unsigned int i = c.argCount(); (i--) != 0U;) {
950         tv[i] = c.arg(i)->type();
951         tv[i].ti(Type::TI_PAR);
952       }
953       FunctionI* decl = env.output->matchFn(env, c.id(), tv, false);
954       FunctionI* origdecl = env.model->matchFn(env, c.id(), tv, false);
955       bool canReuseDecl = (decl != nullptr);
956       if (canReuseDecl && (origdecl != nullptr)) {
957         // Check if this is the exact same overloaded declaration as in the model
958         for (unsigned int i = 0; i < decl->params().size(); i++) {
959           if (decl->params()[i]->type() != origdecl->params()[i]->type()) {
960             // no, the types don't match, so we have to copy the original decl
961             canReuseDecl = false;
962             break;
963           }
964         }
965       }
966       Type t;
967       if (!canReuseDecl) {
968         if (origdecl == nullptr || !is_completely_par(env, origdecl, tv)) {
969           std::ostringstream ss;
970           ss << "function " << c.id() << " is used in output, par version needed";
971           throw FlatteningError(env, c.loc(), ss.str());
972         }
973         if (!origdecl->fromStdLib()) {
974           auto* decl_copy = copy(env, env.cmap, origdecl)->cast<FunctionI>();
975           if (decl_copy != decl) {
976             decl = decl_copy;
977             (void)env.output->registerFn(env, decl, true);
978             env.output->addItem(decl);
979             if (decl->e() != nullptr) {
980               make_par(env, decl->e());
981               top_down(*this, decl->e());
982             }
983             CollectOccurrencesE ce(env.outputVarOccurrences, decl);
984             top_down(ce, decl->e());
985             top_down(ce, decl->ti());
986             for (unsigned int i = decl->params().size(); (i--) != 0U;) {
987               top_down(ce, decl->params()[i]);
988             }
989           }
990         } else {
991           decl = origdecl;
992         }
993       }
994       c.decl(decl);
995     }
996   } _cf(e);
997   top_down(_cf, outputItem->e());
998 
999   // If we are checking solutions using a checker model, all parameters of the checker model
1000   // have to be made available in the output model
1001   class OV1 : public ItemVisitor {
1002   public:
1003     EnvI& env;
1004     CollectFunctions& cf;
1005     OV1(EnvI& env0, CollectFunctions& cf0) : env(env0), cf(cf0) {}
1006     void vVarDeclI(VarDeclI* vdi) {
1007       if (vdi->e()->ann().contains(constants().ann.mzn_check_var)) {
1008         auto* output_vd = copy(env, env.cmap, vdi->e())->cast<VarDecl>();
1009         top_down(cf, output_vd);
1010       }
1011     }
1012   } _ov1(e, _cf);
1013   iter_items(_ov1, e.model);
1014 
1015   // Copying the output item and the functions it depends on has created copies
1016   // of all dependent VarDecls. However the output model does not contain VarDeclIs for
1017   // these VarDecls yet. This iterator processes all variable declarations of the
1018   // original model, and if they were copied (i.e., if the output model depends on them),
1019   // the corresponding VarDeclI is created in the output model.
1020   class OV2 : public ItemVisitor {
1021   public:
1022     EnvI& env;
1023     OV2(EnvI& env0) : env(env0) {}
1024     void vVarDeclI(VarDeclI* vdi) {
1025       auto idx = env.outputVarOccurrences.idx.find(vdi->e()->id());
1026       if (idx != env.outputVarOccurrences.idx.end()) {
1027         return;
1028       }
1029       if (Expression* vd_e = env.cmap.find(vdi->e())) {
1030         // We found a copied VarDecl, now need to create a VarDeclI
1031         auto* vd = vd_e->cast<VarDecl>();
1032         auto* vdi_copy = copy(env, env.cmap, vdi)->cast<VarDeclI>();
1033 
1034         Type t = vdi_copy->e()->ti()->type();
1035         t.ti(Type::TI_PAR);
1036         vdi_copy->e()->ti()->domain(nullptr);
1037         vdi_copy->e()->flat(vdi->e()->flat());
1038         bool isCheckVar = vdi_copy->e()->ann().contains(constants().ann.mzn_check_var);
1039         Call* checkVarEnum = vdi_copy->e()->ann().getCall(constants().ann.mzn_check_enum_var);
1040         vdi_copy->e()->ann().clear();
1041         if (isCheckVar) {
1042           vdi_copy->e()->ann().add(constants().ann.mzn_check_var);
1043         }
1044         if (checkVarEnum != nullptr) {
1045           vdi_copy->e()->ann().add(checkVarEnum);
1046         }
1047         vdi_copy->e()->introduced(false);
1048         IdMap<KeepAlive>::iterator it;
1049         if (!vdi->e()->type().isPar()) {
1050           if (vd->flat() == nullptr && vdi->e()->e() != nullptr && vdi->e()->e()->type().isPar()) {
1051             // Don't have a flat version of this variable, but the original has a right hand
1052             // side that is par, so we can use that.
1053             Expression* flate = eval_par(env, vdi->e()->e());
1054             output_vardecls(env, vdi_copy, flate);
1055             vd->e(flate);
1056           } else {
1057             vd = follow_id_to_decl(vd->id())->cast<VarDecl>();
1058             VarDecl* reallyFlat = vd->flat();
1059             while ((reallyFlat != nullptr) && reallyFlat != reallyFlat->flat()) {
1060               reallyFlat = reallyFlat->flat();
1061             }
1062             if (reallyFlat == nullptr) {
1063               // The variable doesn't have a flat version. This can only happen if
1064               // the original variable had type-inst var, but a right-hand-side that
1065               // was par, so follow_id_to_decl lead to a par variable.
1066               assert(vd->e() && vd->e()->type().isPar());
1067               Expression* flate = eval_par(env, vd->e());
1068               output_vardecls(env, vdi_copy, flate);
1069               vd->e(flate);
1070             } else if ((vd->flat()->e() != nullptr) && vd->flat()->e()->type().isPar()) {
1071               // We can use the right hand side of the flat version of this variable
1072               Expression* flate = copy(env, env.cmap, follow_id(reallyFlat->id()));
1073               output_vardecls(env, vdi_copy, flate);
1074               vd->e(flate);
1075             } else if ((it = env.reverseMappers.find(vd->id())) != env.reverseMappers.end()) {
1076               // Found a reverse mapper, so we need to add the mapping function to the
1077               // output model to map the FlatZinc value back to the model variable.
1078               Call* rhs = copy(env, env.cmap, it->second())->cast<Call>();
1079               check_output_par_fn(env, rhs);
1080               output_vardecls(env, vdi_copy, rhs);
1081               vd->e(rhs);
1082             } else if (cannot_use_rhs_for_output(env, vd->e())) {
1083               // If the VarDecl does not have a usable right hand side, it needs to be
1084               // marked as output in the FlatZinc
1085               vd->e(nullptr);
1086               assert(vd->flat());
1087               if (vd->type().dim() == 0) {
1088                 vd->flat()->addAnnotation(constants().ann.output_var);
1089                 check_rename_var(env, vd);
1090               } else {
1091                 bool needOutputAnn = true;
1092                 if ((reallyFlat->e() != nullptr) && reallyFlat->e()->isa<ArrayLit>()) {
1093                   auto* al = reallyFlat->e()->cast<ArrayLit>();
1094                   for (unsigned int i = 0; i < al->size(); i++) {
1095                     if (Id* id = (*al)[i]->dynamicCast<Id>()) {
1096                       if (env.reverseMappers.find(id) != env.reverseMappers.end()) {
1097                         needOutputAnn = false;
1098                         break;
1099                       }
1100                     }
1101                   }
1102                   if (!needOutputAnn) {
1103                     output_vardecls(env, vdi_copy, al);
1104                     vd->e(copy(env, env.cmap, al));
1105                   }
1106                 }
1107                 if (needOutputAnn) {
1108                   std::vector<Expression*> args(vdi->e()->type().dim());
1109                   for (unsigned int i = 0; i < args.size(); i++) {
1110                     if (vdi->e()->ti()->ranges()[i]->domain() == nullptr) {
1111                       args[i] =
1112                           new SetLit(Location().introduce(),
1113                                      eval_intset(env, vd->flat()->ti()->ranges()[i]->domain()));
1114                     } else {
1115                       args[i] = new SetLit(Location().introduce(),
1116                                            eval_intset(env, vd->ti()->ranges()[i]->domain()));
1117                     }
1118                   }
1119                   auto* al = new ArrayLit(Location().introduce(), args);
1120                   args.resize(1);
1121                   args[0] = al;
1122                   vd->flat()->addAnnotation(
1123                       new Call(Location().introduce(), constants().ann.output_array, args));
1124                   check_rename_var(env, vd);
1125                 }
1126               }
1127             }
1128             if ((reallyFlat != nullptr) && env.outputFlatVarOccurrences.find(reallyFlat) == -1) {
1129               env.outputFlatVarOccurrences.addIndex(reallyFlat,
1130                                                     static_cast<int>(env.output->size()));
1131             }
1132           }
1133         } else {
1134           if (vd->flat() == nullptr && vdi->e()->e() != nullptr) {
1135             // Need to process right hand side of variable, since it may contain
1136             // identifiers that are only in the FlatZinc and that we would
1137             // therefore fail to copy into the output model
1138             output_vardecls(env, vdi_copy, vdi->e()->e());
1139           }
1140         }
1141         make_par(env, vdi_copy->e());
1142         env.outputVarOccurrences.addIndex(vdi_copy, static_cast<int>(env.output->size()));
1143         CollectOccurrencesE ce(env.outputVarOccurrences, vdi_copy);
1144         top_down(ce, vdi_copy->e());
1145         env.output->addItem(vdi_copy);
1146       }
1147     }
1148   } _ov2(e);
1149   iter_items(_ov2, e.model);
1150 
1151   CollectOccurrencesE ce(e.outputVarOccurrences, outputItem);
1152   top_down(ce, outputItem->e());
1153 
1154   e.model->mergeStdLib(e, e.output);
1155   process_deletions(e);
1156 }
1157 
is_fixed_domain(EnvI & env,VarDecl * vd)1158 Expression* is_fixed_domain(EnvI& env, VarDecl* vd) {
1159   if (vd->type() != Type::varbool() && vd->type() != Type::varint() &&
1160       vd->type() != Type::varfloat()) {
1161     return nullptr;
1162   }
1163   Expression* e = vd->ti()->domain();
1164   if (e == constants().literalTrue || e == constants().literalFalse) {
1165     return e;
1166   }
1167   if (auto* sl = Expression::dynamicCast<SetLit>(e)) {
1168     if (sl->type().bt() == Type::BT_INT) {
1169       IntSetVal* isv = eval_intset(env, sl);
1170       return isv->min() == isv->max() ? IntLit::a(isv->min()) : nullptr;
1171     }
1172     if (sl->type().bt() == Type::BT_FLOAT) {
1173       FloatSetVal* fsv = eval_floatset(env, sl);
1174       return fsv->min() == fsv->max() ? FloatLit::a(fsv->min()) : nullptr;
1175     }
1176   }
1177   return nullptr;
1178 }
1179 
finalise_output(EnvI & e)1180 void finalise_output(EnvI& e) {
1181   if (e.output->size() > 0) {
1182     // Adapt existing output model
1183     // (generated by repeated flattening)
1184     e.outputVarOccurrences.clear();
1185     for (unsigned int i = 0; i < e.output->size(); i++) {
1186       Item* item = (*e.output)[i];
1187       if (item->removed()) {
1188         continue;
1189       }
1190       switch (item->iid()) {
1191         case Item::II_VD: {
1192           VarDecl* vd = item->cast<VarDeclI>()->e();
1193           IdMap<KeepAlive>::iterator it;
1194           GCLock lock;
1195           VarDecl* reallyFlat = vd->flat();
1196           while ((reallyFlat != nullptr) && reallyFlat != reallyFlat->flat()) {
1197             reallyFlat = reallyFlat->flat();
1198           }
1199           if (vd->e() == nullptr) {
1200             if (((vd->flat()->e() != nullptr) && vd->flat()->e()->type().isPar()) ||
1201                 (is_fixed_domain(e, vd->flat()) != nullptr)) {
1202               VarDecl* reallyFlat = vd->flat();
1203               while (reallyFlat != reallyFlat->flat()) {
1204                 reallyFlat = reallyFlat->flat();
1205               }
1206               remove_is_output(reallyFlat);
1207               Expression* flate;
1208               if (Expression* fd = is_fixed_domain(e, vd->flat())) {
1209                 flate = fd;
1210               } else {
1211                 flate = copy(e, e.cmap, follow_id(reallyFlat->id()));
1212               }
1213               output_vardecls(e, item, flate);
1214               vd->e(flate);
1215             } else if ((it = e.reverseMappers.find(vd->id())) != e.reverseMappers.end()) {
1216               Call* rhs = copy(e, e.cmap, it->second())->cast<Call>();
1217               check_output_par_fn(e, rhs);
1218               remove_is_output(reallyFlat);
1219 
1220               output_vardecls(e, item, it->second()->cast<Call>());
1221               vd->e(rhs);
1222 
1223               if (e.varOccurrences.occurrences(reallyFlat) == 0 && reallyFlat->e() == nullptr) {
1224                 auto it = e.varOccurrences.idx.find(reallyFlat->id());
1225                 assert(it != e.varOccurrences.idx.end());
1226                 e.flatRemoveItem((*e.flat())[it->second]->cast<VarDeclI>());
1227               }
1228             } else {
1229               // If the VarDecl does not have a usable right hand side, it needs to be
1230               // marked as output in the FlatZinc
1231               assert(vd->flat());
1232 
1233               bool needOutputAnn = true;
1234               if ((reallyFlat->e() != nullptr) && reallyFlat->e()->isa<Id>()) {
1235                 Id* ident = reallyFlat->e()->cast<Id>();
1236                 if (e.reverseMappers.find(ident) != e.reverseMappers.end()) {
1237                   needOutputAnn = false;
1238                   remove_is_output(vd);
1239                   remove_is_output(reallyFlat);
1240 
1241                   vd->e(copy(e, e.cmap, ident));
1242                   Type al_t(vd->e()->type());
1243                   al_t.ti(Type::TI_PAR);
1244                   vd->e()->type(al_t);
1245 
1246                   output_vardecls(e, item, ident);
1247 
1248                   if (e.varOccurrences.occurrences(reallyFlat) == 0) {
1249                     auto it = e.varOccurrences.idx.find(reallyFlat->id());
1250                     assert(it != e.varOccurrences.idx.end());
1251                     e.flatRemoveItem((*e.flat())[it->second]->cast<VarDeclI>());
1252                   }
1253                 }
1254               } else if ((reallyFlat->e() != nullptr) && reallyFlat->e()->isa<ArrayLit>()) {
1255                 auto* al = reallyFlat->e()->cast<ArrayLit>();
1256                 for (unsigned int i = 0; i < al->size(); i++) {
1257                   if (Id* ident = follow_id_to_value((*al)[i])->dynamicCast<Id>()) {
1258                     if (e.reverseMappers.find(ident) != e.reverseMappers.end()) {
1259                       needOutputAnn = false;
1260                       break;
1261                     }
1262                   }
1263                 }
1264                 if (!needOutputAnn) {
1265                   remove_is_output(vd);
1266                   remove_is_output(reallyFlat);
1267                   if (e.varOccurrences.occurrences(reallyFlat) == 0) {
1268                     auto it = e.varOccurrences.idx.find(reallyFlat->id());
1269                     assert(it != e.varOccurrences.idx.end());
1270                     e.flatRemoveItem((*e.flat())[it->second]->cast<VarDeclI>());
1271                   }
1272 
1273                   output_vardecls(e, item, al);
1274                   vd->e(copy(e, e.cmap, al));
1275                   Type al_t(vd->e()->type());
1276                   al_t.ti(Type::TI_PAR);
1277                   vd->e()->type(al_t);
1278                 }
1279               }
1280               if (needOutputAnn) {
1281                 if (!is_output(vd->flat())) {
1282                   GCLock lock;
1283                   if (vd->type().dim() == 0) {
1284                     vd->flat()->addAnnotation(constants().ann.output_var);
1285                   } else {
1286                     std::vector<Expression*> args(vd->type().dim());
1287                     for (unsigned int i = 0; i < args.size(); i++) {
1288                       if (vd->ti()->ranges()[i]->domain() == nullptr) {
1289                         args[i] =
1290                             new SetLit(Location().introduce(),
1291                                        eval_intset(e, vd->flat()->ti()->ranges()[i]->domain()));
1292                       } else {
1293                         args[i] = new SetLit(Location().introduce(),
1294                                              eval_intset(e, vd->ti()->ranges()[i]->domain()));
1295                       }
1296                     }
1297                     auto* al = new ArrayLit(Location().introduce(), args);
1298                     args.resize(1);
1299                     args[0] = al;
1300                     vd->flat()->addAnnotation(
1301                         new Call(Location().introduce(), constants().ann.output_array, args));
1302                   }
1303                   check_rename_var(e, vd);
1304                 }
1305               }
1306             }
1307             vd->flat(nullptr);
1308             // Remove enum type
1309             Type vdt = vd->type();
1310             vdt.enumId(0);
1311             vd->type(vdt);
1312             vd->ti()->type(vdt);
1313           }
1314           e.outputVarOccurrences.addIndex(item->cast<VarDeclI>(), static_cast<int>(i));
1315           CollectOccurrencesE ce(e.outputVarOccurrences, item);
1316           top_down(ce, vd);
1317         } break;
1318         case Item::II_OUT: {
1319           CollectOccurrencesE ce(e.outputVarOccurrences, item);
1320           top_down(ce, item->cast<OutputI>()->e());
1321         } break;
1322         case Item::II_FUN: {
1323           CollectOccurrencesE ce(e.outputVarOccurrences, item);
1324           top_down(ce, item->cast<FunctionI>()->e());
1325           top_down(ce, item->cast<FunctionI>()->ti());
1326           for (unsigned int i = item->cast<FunctionI>()->params().size(); (i--) != 0U;) {
1327             top_down(ce, item->cast<FunctionI>()->params()[i]);
1328           }
1329         } break;
1330         default:
1331           throw FlatteningError(e, item->loc(), "invalid item in output model");
1332       }
1333     }
1334   }
1335   process_deletions(e);
1336 }
1337 }  // namespace MiniZinc
1338