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/flatten_internal.hh>
15 #include <minizinc/hash.hh>
16 #include <minizinc/prettyprinter.hh>
17 #include <minizinc/typecheck.hh>
18 
19 #include <sstream>
20 #include <string>
21 #include <unordered_map>
22 #include <utility>
23 
24 namespace MiniZinc {
25 
Scopes()26 Scopes::Scopes() { _s.emplace_back(ST_TOPLEVEL); }
27 
add(EnvI & env,VarDecl * vd)28 void Scopes::add(EnvI& env, VarDecl* vd) {
29   if (!_s.back().toplevel() && vd->ti()->isEnum() && (vd->e() != nullptr)) {
30     throw TypeError(env, vd->loc(), "enums are only allowed at top level");
31   }
32   if (vd->id()->idn() == -1 && vd->id()->v() == "") {
33     return;
34   }
35   // If the current scope is ST_INNER, check if vd shadows another
36   // declaration from the same functional or toplevel scope
37   if (_s.back().st == ST_INNER) {
38     assert(_s.size() > 1);  // at least toplevel scope above
39     for (int i = static_cast<int>(_s.size()) - 2; i >= 0; i--) {
40       auto previous = _s[i].m.find(vd->id());
41       if (previous != _s[i].m.end()) {
42         std::ostringstream oss;
43         ASTString warnloc_f = vd->loc().filename();
44         unsigned int warnloc_l = vd->id()->loc().firstLine();
45         unsigned int warnloc_c = vd->id()->loc().firstColumn();
46         unsigned int earlier_l = previous->second->id()->loc().firstLine();
47         unsigned int earlier_c = previous->second->id()->loc().firstColumn();
48         oss << "\n  " << warnloc_f << ":" << warnloc_l << "." << warnloc_c << ":\n";
49         oss << "  Variable `" << *vd->id() << "' shadows variable with the same name in line "
50             << earlier_l << "." << earlier_c;
51         env.addWarning(oss.str());
52         break;
53       }
54       if (_s[i].st != ST_INNER) {
55         break;
56       }
57     }
58   }
59 
60   auto vdi = _s.back().m.find(vd->id());
61   if (vdi == _s.back().m.end()) {
62     _s.back().m.insert(vd->id(), vd);
63   } else {
64     std::ostringstream ss;
65     ss << "identifier `" << vd->id()->str() << "' already defined";
66     throw TypeError(env, vd->loc(), ss.str());
67   }
68 }
69 
pushToplevel()70 void Scopes::pushToplevel() { _s.emplace_back(ST_TOPLEVEL); }
71 
pushFun()72 void Scopes::pushFun() { _s.emplace_back(ST_FUN); }
73 
push()74 void Scopes::push() { _s.emplace_back(ST_INNER); }
75 
pop()76 void Scopes::pop() { _s.pop_back(); }
77 
find(Id * ident)78 VarDecl* Scopes::find(Id* ident) {
79   int cur = static_cast<int>(_s.size()) - 1;
80   for (;;) {
81     auto vdi = _s[cur].m.find(ident);
82     if (vdi == _s[cur].m.end()) {
83       if (_s[cur].toplevel()) {
84         if (cur > 0) {
85           cur = 0;
86         } else {
87           return nullptr;
88         }
89       } else {
90         cur--;
91       }
92     } else {
93       return vdi->second;
94     }
95   }
96 }
97 
findSimilar(Id * ident)98 VarDecl* Scopes::findSimilar(Id* ident) {
99   VarDecl* mostSimilar = nullptr;
100   int cur = static_cast<int>(_s.size()) - 1;
101   int minEdits = 3;
102   for (;;) {
103     for (auto decls : _s[cur].m) {
104       int edits = ident->levenshteinDistance(decls.first);
105       if (edits < minEdits && std::abs(static_cast<int>(ident->v().size()) -
106                                        static_cast<int>(decls.first->v().size())) <= 3) {
107         minEdits = edits;
108         mostSimilar = decls.second;
109       }
110     }
111     if (_s[cur].toplevel()) {
112       if (cur > 0) {
113         cur = 0;
114       } else {
115         break;
116       }
117     } else {
118       cur--;
119     }
120   }
121   return mostSimilar;
122 }
123 
124 class VarDeclCmp {
125 private:
126   std::unordered_map<VarDecl*, int>& _pos;
127 
128 public:
VarDeclCmp(std::unordered_map<VarDecl *,int> & pos)129   VarDeclCmp(std::unordered_map<VarDecl*, int>& pos) : _pos(pos) {}
operator ()(Expression * e0,Expression * e1)130   bool operator()(Expression* e0, Expression* e1) {
131     if (auto* vd0 = Expression::dynamicCast<VarDecl>(e0)) {
132       if (auto* vd1 = Expression::dynamicCast<VarDecl>(e1)) {
133         return _pos[vd0] < _pos[vd1];
134       }
135       return true;
136     }
137     return false;
138   }
139 };
140 class ItemCmp {
141 private:
142   std::unordered_map<VarDecl*, int>& _pos;
143 
144 public:
ItemCmp(std::unordered_map<VarDecl *,int> & pos)145   ItemCmp(std::unordered_map<VarDecl*, int>& pos) : _pos(pos) {}
operator ()(Item * i0,Item * i1)146   bool operator()(Item* i0, Item* i1) {
147     if (auto* vd0 = i0->cast<VarDeclI>()) {
148       if (auto* vd1 = i1->cast<VarDeclI>()) {
149         return _pos[vd0->e()] < _pos[vd1->e()];
150       }
151       return true;
152     }
153     return false;
154   }
155 };
156 
157 // Create all required mapping functions for a new enum
158 // (mapping enum identifiers to strings, and mapping between different enums)
create_enum_mapper(EnvI & env,Model * m,unsigned int enumId,VarDecl * vd,Model * enumItems)159 void create_enum_mapper(EnvI& env, Model* m, unsigned int enumId, VarDecl* vd, Model* enumItems) {
160   GCLock lock;
161 
162   Id* ident = vd->id();
163 
164   if (vd->e() == nullptr) {
165     // Enum without right hand side (may be supplied later in an assignment
166     // item, or we may be runnint in --model-interface-only mode).
167     // Need to create stub function declarations, so that the type checker
168     // is happy.
169     Type tx = Type::parint();
170     tx.ot(Type::OT_OPTIONAL);
171     auto* ti_aa = new TypeInst(Location().introduce(), tx);
172     auto* vd_aa = new VarDecl(Location().introduce(), ti_aa, "x");
173     vd_aa->toplevel(false);
174 
175     auto* ti_ab = new TypeInst(Location().introduce(), Type::parbool());
176     auto* vd_ab = new VarDecl(Location().introduce(), ti_ab, "b");
177     vd_ab->toplevel(false);
178 
179     auto* ti_aj = new TypeInst(Location().introduce(), Type::parbool());
180     auto* vd_aj = new VarDecl(Location().introduce(), ti_aj, "json");
181     vd_aj->toplevel(false);
182 
183     auto* ti_fi = new TypeInst(Location().introduce(), Type::parstring());
184     std::vector<VarDecl*> fi_params(3);
185     fi_params[0] = vd_aa;
186     fi_params[1] = vd_ab;
187     fi_params[2] = vd_aj;
188     auto* fi =
189         new FunctionI(Location().introduce(), create_enum_to_string_name(ident, "_toString_"),
190                       ti_fi, fi_params, nullptr);
191     enumItems->addItem(fi);
192 
193     return;
194   }
195 
196   Call* c = vd->e()->dynamicCast<Call>();
197   auto* al = vd->e()->dynamicCast<ArrayLit>();
198 
199   std::vector<Expression*> parts;
200   if (vd->e()->isa<SetLit>()) {
201     parts.push_back(vd->e());
202   } else if ((al != nullptr) || ((c != nullptr) && c->id() == "anon_enum" && c->argCount() == 1 &&
203                                  c->arg(0)->isa<ArrayLit>())) {
204     if (c != nullptr) {
205       al = c->arg(0)->cast<ArrayLit>();
206     }
207     std::vector<Expression*> enumIds(al->size());
208     for (unsigned int i = 0; i < al->size(); i++) {
209       if (Id* eid = (*al)[i]->dynamicCast<Id>()) {
210         enumIds[i] = eid;
211       } else {
212         std::ostringstream ss;
213         ss << "invalid initialisation for enum `" << ident->v() << "'";
214         throw TypeError(env, vd->e()->loc(), ss.str());
215       }
216     }
217     parts.push_back(new SetLit(vd->e()->loc(), enumIds));
218   } else if (c != nullptr) {
219     if (c->id() == "enumFromConstructors") {
220       if (c->argCount() != 1 || !c->arg(0)->isa<ArrayLit>()) {
221         throw TypeError(env, c->loc(),
222                         "enumFromConstructors used with incorrect argument type (only supports "
223                         "array literals)");
224       }
225       auto* al = c->arg(0)->cast<ArrayLit>();
226       for (unsigned int i = 0; i < al->size(); i++) {
227         parts.push_back((*al)[i]);
228       }
229     } else {
230       parts.push_back(c);
231     }
232   } else {
233     throw TypeError(env, vd->e()->loc(),
234                     std::string("invalid initialisation for enum `") + ident->v().c_str() + "'");
235   }
236 
237   std::vector<Expression*> partCardinality;
238   for (unsigned int p = 0; p < parts.size(); p++) {
239     if (auto* sl = parts[p]->dynamicCast<SetLit>()) {
240       for (unsigned int i = 0; i < sl->v().size(); i++) {
241         if (!sl->v()[i]->isa<Id>()) {
242           throw TypeError(
243               env, sl->v()[i]->loc(),
244               std::string("invalid initialisation for enum `") + ident->v().c_str() + "'");
245         }
246         auto* ti_id = new TypeInst(sl->v()[i]->loc(), Type::parenum(enumId));
247 
248         std::vector<Expression*> toEnumArgs(2);
249         toEnumArgs[0] = vd->id();
250         if (partCardinality.empty()) {
251           toEnumArgs[1] = IntLit::a(i + 1);
252         } else {
253           toEnumArgs[1] =
254               new BinOp(Location().introduce(), partCardinality.back(), BOT_PLUS, IntLit::a(i + 1));
255         }
256         Call* toEnum = new Call(sl->v()[i]->loc(), ASTString("to_enum"), toEnumArgs);
257         auto* vd_id = new VarDecl(ti_id->loc(), ti_id, sl->v()[i]->cast<Id>()->str(), toEnum);
258         auto* vdi_id = new VarDeclI(vd_id->loc(), vd_id);
259         std::string str(sl->v()[i]->cast<Id>()->str().c_str());
260         env.reverseEnum[str] = vdi_id;
261         enumItems->addItem(vdi_id);
262         if (i == sl->v().size() - 1) {
263           // remember the last identifier
264           partCardinality.push_back(toEnumArgs[1]);
265         }
266       }
267 
268       std::string name =
269           create_enum_to_string_name(ident, "_enum_to_string_" + std::to_string(p) + "_");
270       std::vector<Expression*> al_args(sl->v().size());
271       for (unsigned int i = 0; i < sl->v().size(); i++) {
272         std::string str(sl->v()[i]->cast<Id>()->str().c_str());
273         if (str.size() >= 2 && str[0] == '\'' && str[str.size() - 1] == '\'') {
274           al_args[i] =
275               new StringLit(Location().introduce(), ASTString(str.substr(1, str.size() - 2)));
276         } else {
277           al_args[i] = new StringLit(Location().introduce(), ASTString(str));
278         }
279         /// TODO: reimplement reverseEnum with a symbol table into the model (so you can evalPar an
280         /// expression)
281       }
282       auto* al = new ArrayLit(Location().introduce(), al_args);
283 
284       std::vector<TypeInst*> ranges(1);
285       ranges[0] = new TypeInst(Location().introduce(), Type::parint());
286       auto* ti = new TypeInst(Location().introduce(), Type::parstring(1));
287       ti->setRanges(ranges);
288       auto* vd_enumToString = new VarDecl(Location().introduce(), ti, name, al);
289       enumItems->addItem(new VarDeclI(Location().introduce(), vd_enumToString));
290 
291       Type tx = Type::parint();
292       tx.ot(Type::OT_OPTIONAL);
293       auto* ti_aa = new TypeInst(Location().introduce(), tx);
294       auto* vd_aa = new VarDecl(Location().introduce(), ti_aa, "x");
295       vd_aa->toplevel(false);
296       auto* ti_ab = new TypeInst(Location().introduce(), Type::parbool());
297       auto* vd_ab = new VarDecl(Location().introduce(), ti_ab, "b");
298       vd_ab->toplevel(false);
299       auto* ti_aj = new TypeInst(Location().introduce(), Type::parbool());
300       auto* vd_aj = new VarDecl(Location().introduce(), ti_aj, "json");
301       vd_aj->toplevel(false);
302       auto* ti_fi = new TypeInst(Location().introduce(), Type::parstring());
303       std::vector<VarDecl*> fi_params(3);
304       fi_params[0] = vd_aa;
305       fi_params[1] = vd_ab;
306       fi_params[2] = vd_aj;
307 
308       std::vector<Expression*> deopt_args(1);
309       deopt_args[0] = vd_aa->id();
310       Call* deopt = new Call(Location().introduce(), "deopt", deopt_args);
311       Call* occurs = new Call(Location().introduce(), "occurs", deopt_args);
312       std::vector<Expression*> aa_args(1);
313       aa_args[0] = deopt;
314       auto* aa = new ArrayAccess(Location().introduce(), vd_enumToString->id(), aa_args);
315 
316       auto* sl_absent = new StringLit(Location().introduce(), "<>");
317 
318       ITE* if_absent = new ITE(
319           Location().introduce(),
320           {vd_aj->id(), new StringLit(Location().introduce(), ASTString("null"))}, sl_absent);
321 
322       auto* json_e_quote = new StringLit(Location().introduce(), ASTString("{\"e\":\""));
323       auto* json_e_quote_end = new StringLit(Location().introduce(), ASTString("\"}"));
324       auto* quote_aa = new BinOp(Location().introduce(), json_e_quote, BOT_PLUSPLUS, aa);
325       auto* quote_aa2 = new BinOp(Location().introduce(), quote_aa, BOT_PLUSPLUS, json_e_quote_end);
326 
327       Call* quote_dzn = new Call(Location().introduce(), ASTString("showDznId"), {aa});
328 
329       std::vector<Expression*> ite_ifelse(2);
330       ite_ifelse[0] = occurs;
331       ite_ifelse[1] =
332           new ITE(Location().introduce(), {vd_ab->id(), quote_dzn, vd_aj->id(), quote_aa2}, aa);
333 
334       ITE* ite = new ITE(Location().introduce(), ite_ifelse, if_absent);
335 
336       std::string toString = "_toString_";
337       if (parts.size() > 1) {
338         toString += std::to_string(p) + "_";
339       }
340 
341       auto* fi = new FunctionI(Location().introduce(), create_enum_to_string_name(ident, toString),
342                                ti_fi, fi_params, ite);
343       enumItems->addItem(fi);
344     } else if (Call* c = parts[p]->dynamicCast<Call>()) {
345       if (c->id() == "anon_enum") {
346         Type tx = Type::parint();
347         tx.ot(Type::OT_OPTIONAL);
348         auto* ti_aa = new TypeInst(Location().introduce(), tx);
349         auto* vd_aa = new VarDecl(Location().introduce(), ti_aa, "x");
350         vd_aa->toplevel(false);
351 
352         auto* ti_ab = new TypeInst(Location().introduce(), Type::parbool());
353         auto* vd_ab = new VarDecl(Location().introduce(), ti_ab, "b");
354         vd_ab->toplevel(false);
355 
356         auto* ti_aj = new TypeInst(Location().introduce(), Type::parbool());
357         auto* vd_aj = new VarDecl(Location().introduce(), ti_aj, "json");
358         vd_aj->toplevel(false);
359 
360         std::vector<Expression*> deopt_args(1);
361         deopt_args[0] = vd_aa->id();
362         Call* deopt = new Call(Location().introduce(), "deopt", deopt_args);
363         Call* if_absent = new Call(Location().introduce(), "absent", deopt_args);
364         auto* sl_absent_dzn = new StringLit(Location().introduce(), "<>");
365         ITE* sl_absent = new ITE(
366             Location().introduce(),
367             {vd_aj->id(), new StringLit(Location().introduce(), ASTString("null"))}, sl_absent_dzn);
368 
369         auto* sl_dzn = new StringLit(Location().introduce(), ASTString(std::string("to_enum(") +
370                                                                        ident->str().c_str() + ","));
371 
372         std::vector<Expression*> showIntArgs(1);
373         if (partCardinality.empty()) {
374           showIntArgs[0] = deopt;
375           partCardinality.push_back(c->arg(0));
376         } else {
377           showIntArgs[0] =
378               new BinOp(Location().introduce(), partCardinality.back(), BOT_PLUS, deopt);
379           partCardinality.push_back(
380               new BinOp(Location().introduce(), partCardinality.back(), BOT_PLUS, c->arg(0)));
381         }
382 
383         Call* showInt = new Call(Location().introduce(), constants().ids.show, showIntArgs);
384         auto* construct_string_dzn =
385             new BinOp(Location().introduce(), sl_dzn, BOT_PLUSPLUS, showInt);
386         auto* closing_bracket = new StringLit(Location().introduce(), ASTString(")"));
387         auto* construct_string_dzn_2 =
388             new BinOp(Location().introduce(), construct_string_dzn, BOT_PLUSPLUS, closing_bracket);
389 
390         auto* sl = new StringLit(Location().introduce(),
391                                  ASTString(std::string(ident->str().c_str()) + "_"));
392         auto* construct_string = new BinOp(Location().introduce(), sl, BOT_PLUSPLUS, showInt);
393 
394         auto* json_e_quote = new StringLit(Location().introduce(), ASTString("{\"e\":\""));
395         auto* json_e_quote_mid = new StringLit(Location().introduce(), ASTString("\", \"i\":"));
396         auto* json_e_quote_end = new StringLit(Location().introduce(), ASTString("}"));
397         auto* construct_string_json =
398             new BinOp(Location().introduce(), json_e_quote, BOT_PLUSPLUS,
399                       new StringLit(Location().introduce(), ident->str()));
400         auto* construct_string_json_1a = new BinOp(Location().introduce(), construct_string_json,
401                                                    BOT_PLUSPLUS, json_e_quote_mid);
402         auto* construct_string_json_1b =
403             new BinOp(Location().introduce(), construct_string_json_1a, BOT_PLUSPLUS, showInt);
404         auto* construct_string_json_2 = new BinOp(Location().introduce(), construct_string_json_1b,
405                                                   BOT_PLUSPLUS, json_e_quote_end);
406 
407         std::vector<Expression*> if_then(6);
408         if_then[0] = if_absent;
409         if_then[1] = sl_absent;
410         if_then[2] = vd_ab->id();
411         if_then[3] = construct_string_dzn_2;
412         if_then[4] = vd_aj->id();
413         if_then[5] = construct_string_json_2;
414         ITE* ite = new ITE(Location().introduce(), if_then, construct_string);
415 
416         auto* ti_fi = new TypeInst(Location().introduce(), Type::parstring());
417         std::vector<VarDecl*> fi_params(3);
418         fi_params[0] = vd_aa;
419         fi_params[1] = vd_ab;
420         fi_params[2] = vd_aj;
421         std::string toString = "_toString_";
422         if (parts.size() > 1) {
423           toString += std::to_string(p) + "_";
424         }
425 
426         auto* fi =
427             new FunctionI(Location().introduce(), create_enum_to_string_name(ident, toString),
428                           ti_fi, fi_params, ite);
429         enumItems->addItem(fi);
430       } else {
431         // This is an enum constructor C(E)
432 
433         if (c->argCount() != 1 || !c->arg(0)->isa<Id>()) {
434           throw TypeError(env, c->loc(),
435                           "enum constructors must have a single identifier as argument");
436         }
437         Id* otherEnumId = c->arg(0)->cast<Id>();
438 
439         // Generate:
440         /*
441          function X: C(E: x) = to_enum(X,partCardinality.back()+x)
442          function var X: C(var E: x) = to_enum(X,partCardinality.back()+x)
443          function opt X: C(opt E: x) = if occurs(x) then C(deopt(x)) else to_enum(x,<>) endif
444          function var opt X: C(var opt E: x) = if occurs(x) then C(deopt(x)) else to_enum(x,<>)
445          endif
446          function set of X: C(set of E: x) = { C(i) | i in x }
447          function var set of X: C(var set of E: x) = { C(i) | i in x }
448          */
449         {
450           Type Xt = Type::parint();
451           Xt.enumId(enumId);
452           auto* Cfn_ti = new TypeInst(Location().introduce(), Xt);
453           auto* Cfn_x_ti = new TypeInst(Location().introduce(), Type(), otherEnumId);
454           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
455           vd_x->toplevel(false);
456           Expression* realX;
457           if (partCardinality.empty()) {
458             realX = vd_x->id();
459           } else {
460             realX = new BinOp(Location().introduce(), partCardinality.back(), BOT_PLUS, vd_x->id());
461           }
462           auto* Cfn_body = new Call(Location().introduce(), "to_enum", {vd->id(), realX});
463 
464           std::string Cfn_id(c->id().c_str());
465           auto* Cfn = new FunctionI(Location().introduce(), Cfn_id, Cfn_ti, {vd_x}, Cfn_body);
466           env.reverseEnum[Cfn_id] = Cfn;
467           enumItems->addItem(Cfn);
468         }
469         {
470           Type Xt = Type::varint();
471           Xt.enumId(enumId);
472           auto* Cfn_ti = new TypeInst(Location().introduce(), Xt);
473           Type argT;
474           argT.ti(Type::TI_VAR);
475           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, otherEnumId);
476           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
477           vd_x->toplevel(false);
478           Expression* realX;
479           if (partCardinality.empty()) {
480             realX = vd_x->id();
481           } else {
482             realX = new BinOp(Location().introduce(), partCardinality.back(), BOT_PLUS, vd_x->id());
483           }
484           auto* Cfn_body = new Call(Location().introduce(), "to_enum", {vd->id(), realX});
485 
486           std::string Cfn_id(c->id().c_str());
487           auto* Cfn = new FunctionI(Location().introduce(), Cfn_id, Cfn_ti, {vd_x}, Cfn_body);
488           enumItems->addItem(Cfn);
489         }
490         {
491           Type Xt = Type::parint();
492           Xt.ot(Type::OT_OPTIONAL);
493           Xt.enumId(enumId);
494           auto* Cfn_ti = new TypeInst(Location().introduce(), Xt);
495           Type argT;
496           argT.ot(Type::OT_OPTIONAL);
497           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, otherEnumId);
498           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
499           std::string Cfn_id(c->id().c_str());
500           vd_x->toplevel(false);
501           auto* occurs = new Call(Location().introduce(), "occurs", {vd_x->id()});
502           auto* deopt = new Call(Location().introduce(), "deopt", {vd_x->id()});
503           auto* inv = new Call(Location().introduce(), Cfn_id, {deopt});
504           auto* toEnumAbsent =
505               new Call(Location().introduce(), "to_enum", {vd->id(), constants().absent});
506           auto* ite = new ITE(Location().introduce(), {occurs, inv}, toEnumAbsent);
507           auto* Cfn = new FunctionI(Location().introduce(), Cfn_id, Cfn_ti, {vd_x}, ite);
508           enumItems->addItem(Cfn);
509         }
510         {
511           Type Xt = Type::varint();
512           Xt.ot(Type::OT_OPTIONAL);
513           Xt.enumId(enumId);
514           auto* Cfn_ti = new TypeInst(Location().introduce(), Xt);
515           Type argT;
516           argT.ti(Type::TI_VAR);
517           argT.ot(Type::OT_OPTIONAL);
518           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, otherEnumId);
519           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
520           std::string Cfn_id(c->id().c_str());
521           vd_x->toplevel(false);
522           auto* occurs = new Call(Location().introduce(), "occurs", {vd_x->id()});
523           auto* deopt = new Call(Location().introduce(), "deopt", {vd_x->id()});
524           auto* toEnumAbsent =
525               new Call(Location().introduce(), "to_enum", {vd->id(), constants().absent});
526           auto* inv = new Call(Location().introduce(), Cfn_id, {deopt});
527           auto* ite = new ITE(Location().introduce(), {occurs, inv}, toEnumAbsent);
528           auto* Cfn = new FunctionI(Location().introduce(), Cfn_id, Cfn_ti, {vd_x}, ite);
529           enumItems->addItem(Cfn);
530         }
531         {
532           Type Xt = Type::parint();
533           Xt.st(Type::ST_SET);
534           Xt.enumId(enumId);
535           auto* Cfn_ti = new TypeInst(Location().introduce(), Xt);
536           Type argT;
537           argT.st(Type::ST_SET);
538           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, otherEnumId);
539           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
540           std::string Cfn_id(c->id().c_str());
541           vd_x->toplevel(false);
542           auto* s_ti = new TypeInst(Location().introduce(), Type::parint());
543           auto* s = new VarDecl(Location().introduce(), s_ti, "s", nullptr);
544           s->toplevel(false);
545           auto* inv = new Call(Location().introduce(), Cfn_id, {s->id()});
546           Generator gen({s}, vd_x->id(), nullptr);
547           Generators gens;
548           gens.g = {gen};
549           auto* comprehension = new Comprehension(Location().introduce(), inv, gens, true);
550           auto* Cfn = new FunctionI(Location().introduce(), Cfn_id, Cfn_ti, {vd_x}, comprehension);
551           enumItems->addItem(Cfn);
552         }
553         {
554           Type Xt = Type::varint();
555           Xt.st(Type::ST_SET);
556           Xt.enumId(enumId);
557           auto* Cfn_ti = new TypeInst(Location().introduce(), Xt);
558           Type argT;
559           argT.ti(Type::TI_VAR);
560           argT.st(Type::ST_SET);
561           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, otherEnumId);
562           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
563           std::string Cfn_id(c->id().c_str());
564           vd_x->toplevel(false);
565           auto* s_ti = new TypeInst(Location().introduce(), Type::parint());
566           auto* s = new VarDecl(Location().introduce(), s_ti, "s", nullptr);
567           s->toplevel(false);
568           auto* inv = new Call(Location().introduce(), Cfn_id, {s->id()});
569           Generator gen({s}, vd_x->id(), nullptr);
570           Generators gens;
571           gens.g = {gen};
572           auto* comprehension = new Comprehension(Location().introduce(), inv, gens, true);
573           auto* Cfn = new FunctionI(Location().introduce(), Cfn_id, Cfn_ti, {vd_x}, comprehension);
574           enumItems->addItem(Cfn);
575         }
576         /*
577          function E: C⁻¹(X: x) = to_enum(E,x-partCardinality.back())
578          function var E: C⁻¹(var X: x) = to_enum(E,x-partCardinality.back())
579          function opt E: C⁻¹(opt X: x) = if occurs(x) then C⁻¹(deopt(x)) else to_enum(x,<>) endif
580          function var opt E: C⁻¹(var opt X: x) = if occurs(x) then C⁻¹(deopt(x)) else to_enum(x,<>)
581          endif
582          function set of E: C⁻¹(set of X: x) = { C⁻¹(i) | i in x }
583          function var set of E: C⁻¹(var set of X: x) = { C⁻¹(i) | i in x }
584          */
585         {
586           auto* toEfn_ti = new TypeInst(Location().introduce(), Type(), otherEnumId);
587           Type Xt = Type::parint();
588           Xt.enumId(enumId);
589           auto* toEfn_x_ti = new TypeInst(Location().introduce(), Xt, vd->id());
590           auto* vd_x = new VarDecl(Location().introduce(), toEfn_x_ti, "x");
591           vd_x->toplevel(false);
592           Expression* realX;
593           if (partCardinality.empty()) {
594             realX = vd_x->id();
595           } else {
596             realX =
597                 new BinOp(Location().introduce(), vd_x->id(), BOT_MINUS, partCardinality.back());
598           }
599           auto* toEfn_body = new Call(Location().introduce(), "to_enum", {otherEnumId, realX});
600 
601           std::string Cinv_id(std::string(c->id().c_str()) + "⁻¹");
602           auto* toEfn =
603               new FunctionI(Location().introduce(), Cinv_id, toEfn_ti, {vd_x}, toEfn_body);
604           enumItems->addItem(toEfn);
605         }
606         {
607           Type rT;
608           rT.ti(Type::TI_VAR);
609           auto* toEfn_ti = new TypeInst(Location().introduce(), rT, otherEnumId);
610           Type Xt = Type::varint();
611           Xt.enumId(enumId);
612           auto* toEfn_x_ti = new TypeInst(Location().introduce(), Xt, vd->id());
613           auto* vd_x = new VarDecl(Location().introduce(), toEfn_x_ti, "x");
614           vd_x->toplevel(false);
615           Expression* realX;
616           if (partCardinality.empty()) {
617             realX = vd_x->id();
618           } else {
619             realX =
620                 new BinOp(Location().introduce(), vd_x->id(), BOT_MINUS, partCardinality.back());
621           }
622           auto* toEfn_body = new Call(Location().introduce(), "to_enum", {otherEnumId, realX});
623 
624           std::string Cinv_id(std::string(c->id().c_str()) + "⁻¹");
625           auto* toEfn =
626               new FunctionI(Location().introduce(), Cinv_id, toEfn_ti, {vd_x}, toEfn_body);
627           enumItems->addItem(toEfn);
628         }
629         {
630           Type rt;
631           rt.ot(Type::OT_OPTIONAL);
632           auto* Cfn_ti = new TypeInst(Location().introduce(), rt, otherEnumId);
633           Type argT;
634           argT.ot(Type::OT_OPTIONAL);
635           argT.enumId(enumId);
636           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, vd->id());
637           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
638           std::string Cinv_id(std::string(c->id().c_str()) + "⁻¹");
639           vd_x->toplevel(false);
640           auto* occurs = new Call(Location().introduce(), "occurs", {vd_x->id()});
641           auto* deopt = new Call(Location().introduce(), "deopt", {vd_x->id()});
642           auto* inv = new Call(Location().introduce(), Cinv_id, {deopt});
643           auto* toEnumAbsent =
644               new Call(Location().introduce(), "to_enum", {otherEnumId, constants().absent});
645           auto* ite = new ITE(Location().introduce(), {occurs, inv}, toEnumAbsent);
646           auto* Cfn = new FunctionI(Location().introduce(), Cinv_id, Cfn_ti, {vd_x}, ite);
647           enumItems->addItem(Cfn);
648         }
649         {
650           Type rt;
651           rt.ti(Type::TI_VAR);
652           rt.ot(Type::OT_OPTIONAL);
653           auto* Cfn_ti = new TypeInst(Location().introduce(), rt, otherEnumId);
654           Type argT = Type::varint();
655           argT.ot(Type::OT_OPTIONAL);
656           argT.enumId(enumId);
657           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, vd->id());
658           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
659           std::string Cinv_id(std::string(c->id().c_str()) + "⁻¹");
660           vd_x->toplevel(false);
661           auto* occurs = new Call(Location().introduce(), "occurs", {vd_x->id()});
662           auto* deopt = new Call(Location().introduce(), "deopt", {vd_x->id()});
663           auto* inv = new Call(Location().introduce(), Cinv_id, {deopt});
664           auto* toEnumAbsent =
665               new Call(Location().introduce(), "to_enum", {otherEnumId, constants().absent});
666           auto* ite = new ITE(Location().introduce(), {occurs, inv}, toEnumAbsent);
667           auto* Cfn = new FunctionI(Location().introduce(), Cinv_id, Cfn_ti, {vd_x}, ite);
668           enumItems->addItem(Cfn);
669         }
670         {
671           Type Xt;
672           Xt.st(Type::ST_SET);
673           auto* Cfn_ti = new TypeInst(Location().introduce(), Xt, otherEnumId);
674           Type argT = Type::parint();
675           argT.st(Type::ST_SET);
676           argT.enumId(enumId);
677           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, vd->id());
678           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
679           vd_x->toplevel(false);
680           std::string Cinv_id(std::string(c->id().c_str()) + "⁻¹");
681           auto* s_ti = new TypeInst(Location().introduce(), Type::parint());
682           auto* s = new VarDecl(Location().introduce(), s_ti, "s", nullptr);
683           s->toplevel(false);
684           auto* inv = new Call(Location().introduce(), Cinv_id, {s->id()});
685           Generator gen({s}, vd_x->id(), nullptr);
686           Generators gens;
687           gens.g = {gen};
688           auto* comprehension = new Comprehension(Location().introduce(), inv, gens, true);
689           auto* Cfn = new FunctionI(Location().introduce(), Cinv_id, Cfn_ti, {vd_x}, comprehension);
690           enumItems->addItem(Cfn);
691         }
692         {
693           Type Xt;
694           Xt.ti(Type::TI_VAR);
695           Xt.st(Type::ST_SET);
696           auto* Cfn_ti = new TypeInst(Location().introduce(), Xt, otherEnumId);
697           Type argT = Type::varint();
698           argT.st(Type::ST_SET);
699           argT.enumId(enumId);
700           auto* Cfn_x_ti = new TypeInst(Location().introduce(), argT, vd->id());
701           auto* vd_x = new VarDecl(Location().introduce(), Cfn_x_ti, "x");
702           vd_x->toplevel(false);
703           std::string Cinv_id(std::string(c->id().c_str()) + "⁻¹");
704           auto* s_ti = new TypeInst(Location().introduce(), Type::varint());
705           auto* s = new VarDecl(Location().introduce(), s_ti, "s", nullptr);
706           s->toplevel(false);
707           auto* inv = new Call(Location().introduce(), Cinv_id, {s->id()});
708           Generator gen({s}, vd_x->id(), nullptr);
709           Generators gens;
710           gens.g = {gen};
711           auto* comprehension = new Comprehension(Location().introduce(), inv, gens, true);
712           auto* Cfn = new FunctionI(Location().introduce(), Cinv_id, Cfn_ti, {vd_x}, comprehension);
713           enumItems->addItem(Cfn);
714         }
715 
716         /*
717          function string: _toString_p_X(opt X: x, bool: b, bool: json) =
718            if absent(x) then "<>" else
719            if json then "{ \"c\": \"C\", \"e\":" else "C(" endif
720            ++_toString_E(to_enum(E,deopt(x)),b,json)
721            ++ if json then "}" else ")" endif
722            endif
723          */
724 
725         {
726           Type tx = Type::parint();
727           tx.ot(Type::OT_OPTIONAL);
728           auto* ti_aa = new TypeInst(Location().introduce(), tx);
729           auto* vd_aa = new VarDecl(Location().introduce(), ti_aa, "x");
730           vd_aa->toplevel(false);
731 
732           auto* ti_ab = new TypeInst(Location().introduce(), Type::parbool());
733           auto* vd_ab = new VarDecl(Location().introduce(), ti_ab, "b");
734           vd_ab->toplevel(false);
735 
736           auto* ti_aj = new TypeInst(Location().introduce(), Type::parbool());
737           auto* vd_aj = new VarDecl(Location().introduce(), ti_aj, "json");
738           vd_aj->toplevel(false);
739 
740           std::vector<Expression*> deopt_args(1);
741           deopt_args[0] = vd_aa->id();
742           Call* deopt = new Call(Location().introduce(), "deopt", deopt_args);
743           Call* if_absent = new Call(Location().introduce(), "absent", deopt_args);
744           auto* sl_absent_dzn = new StringLit(Location().introduce(), "<>");
745           ITE* sl_absent =
746               new ITE(Location().introduce(),
747                       {vd_aj->id(), new StringLit(Location().introduce(), ASTString("null"))},
748                       sl_absent_dzn);
749 
750           Call* toEnumE = new Call(Location().introduce(), "to_enum", {otherEnumId, deopt});
751           Call* toString = new Call(Location().introduce(),
752                                     create_enum_to_string_name(otherEnumId, "_toString_"),
753                                     {toEnumE, vd_ab->id(), vd_aj->id()});
754 
755           auto* openOther =
756               new StringLit(Location().introduce(), std::string(c->id().c_str()) + "(");
757           auto* openJson =
758               new StringLit(Location().introduce(),
759                             "{ \"c\" : \"" + std::string(c->id().c_str()) + "\", \"e\" : ");
760           ITE* openConstr = new ITE(Location().introduce(), {vd_aj->id(), openJson}, openOther);
761           auto* closeJson = new StringLit(Location().introduce(), "}");
762           auto* closeOther = new StringLit(Location().introduce(), ")");
763           ITE* closeConstr = new ITE(Location().introduce(), {vd_aj->id(), closeJson}, closeOther);
764 
765           auto* concat1 = new BinOp(Location().introduce(), openConstr, BOT_PLUSPLUS, toString);
766           auto* concat2 = new BinOp(Location().introduce(), concat1, BOT_PLUSPLUS, closeConstr);
767 
768           ITE* ite = new ITE(Location().introduce(), {if_absent, sl_absent}, concat2);
769           auto* ti_fi = new TypeInst(Location().introduce(), Type::parstring());
770           std::vector<VarDecl*> fi_params(3);
771           fi_params[0] = vd_aa;
772           fi_params[1] = vd_ab;
773           fi_params[2] = vd_aj;
774           std::string XtoString = "_toString_";
775           if (parts.size() > 1) {
776             XtoString += std::to_string(p) + "_";
777           }
778 
779           auto* fi =
780               new FunctionI(Location().introduce(), create_enum_to_string_name(ident, XtoString),
781                             ti_fi, fi_params, ite);
782           enumItems->addItem(fi);
783         }
784 
785         Call* cardE = new Call(Location().introduce(), "card", {otherEnumId});
786         if (partCardinality.empty()) {
787           partCardinality.push_back(cardE);
788         } else {
789           partCardinality.push_back(
790               new BinOp(Location().introduce(), partCardinality.back(), BOT_PLUS, cardE));
791         }
792       }
793     } else {
794       assert(false);
795     }
796   }
797 
798   // Create set literal for overall enum
799   Expression* upperBound;
800   if (!partCardinality.empty()) {
801     upperBound = partCardinality.back();
802   } else {
803     // For empty enums, just create 1..0.
804     upperBound = IntLit::a(0);
805   }
806   auto* rhs = new BinOp(vd->loc(), IntLit::a(1), BOT_DOTDOT, upperBound);
807   vd->e(rhs);
808 
809   if (parts.size() > 1) {
810     Type tx = Type::parint();
811     tx.ot(Type::OT_OPTIONAL);
812     auto* ti_aa = new TypeInst(Location().introduce(), tx);
813     auto* vd_aa = new VarDecl(Location().introduce(), ti_aa, "x");
814     vd_aa->toplevel(false);
815 
816     auto* ti_ab = new TypeInst(Location().introduce(), Type::parbool());
817     auto* vd_ab = new VarDecl(Location().introduce(), ti_ab, "b");
818     vd_ab->toplevel(false);
819 
820     auto* ti_aj = new TypeInst(Location().introduce(), Type::parbool());
821     auto* vd_aj = new VarDecl(Location().introduce(), ti_aj, "json");
822     vd_aj->toplevel(false);
823 
824     std::vector<Expression*> deopt_args(1);
825     deopt_args[0] = vd_aa->id();
826     Call* deopt = new Call(Location().introduce(), "deopt", deopt_args);
827     Call* if_absent = new Call(Location().introduce(), "absent", deopt_args);
828     auto* sl_absent_dzn = new StringLit(Location().introduce(), "<>");
829     ITE* sl_absent = new ITE(
830         Location().introduce(),
831         {vd_aj->id(), new StringLit(Location().introduce(), ASTString("null"))}, sl_absent_dzn);
832 
833     std::vector<Expression*> ite_cases_a;
834     Expression* ite_cases_else;
835     for (unsigned int i = 0; i < parts.size(); i++) {
836       std::string toString = "_toString_" + std::to_string(i) + "_";
837       Expression* aa;
838       if (i == 0) {
839         aa = vd_aa->id();
840       } else {
841         auto* bo = new BinOp(Location().introduce(), deopt, BOT_MINUS, partCardinality[i - 1]);
842         Call* c = new Call(Location().introduce(), "to_enum", {vd->id(), bo});
843         aa = c;
844       }
845       Call* c = new Call(Location().introduce(), create_enum_to_string_name(ident, toString),
846                          {aa, vd_ab->id(), vd_aj->id()});
847       if (i < parts.size() - 1) {
848         auto* bo = new BinOp(Location().introduce(), deopt, BOT_LQ, partCardinality[i]);
849         ite_cases_a.push_back(bo);
850         ite_cases_a.push_back(c);
851       } else {
852         ite_cases_else = c;
853       }
854     }
855 
856     ITE* ite_cases = new ITE(Location().introduce(), ite_cases_a, ite_cases_else);
857 
858     ITE* ite = new ITE(Location().introduce(), {if_absent, sl_absent}, ite_cases);
859 
860     auto* ti_fi = new TypeInst(Location().introduce(), Type::parstring());
861     std::vector<VarDecl*> fi_params(3);
862     fi_params[0] = vd_aa;
863     fi_params[1] = vd_ab;
864     fi_params[2] = vd_aj;
865     auto* fi =
866         new FunctionI(Location().introduce(), create_enum_to_string_name(ident, "_toString_"),
867                       ti_fi, fi_params, ite);
868     enumItems->addItem(fi);
869 
870     /*
871      function string: _toString_ENUM(opt Foo: x, bool: b, bool: json) =
872        if occurs(x) then
873          if deopt(x)<=partCardinality[1] then _toString_1_ENUM(x,b,json)
874          elseif deopt(x)<=partCardinality[2] then _toString_2_ENUM(x,b,json)
875          ...
876          endif
877        else "<>" endif
878      */
879   }
880 
881   {
882     /*
883 
884      function _toString_ENUM(array[$U] of opt Foo: x, bool: b, bool: json) =
885        let {
886          array[int] of opt ENUM: xx = array1d(x)
887        } in "[" ++ join(", ", [ _toString_ENUM(xx[i],b,json) | i in index_set(xx) ]) ++ "]";
888 
889      */
890 
891     TIId* tiid = new TIId(Location().introduce(), "U");
892     auto* ti_range = new TypeInst(Location().introduce(), Type::parint(), tiid);
893     std::vector<TypeInst*> ranges(1);
894     ranges[0] = ti_range;
895 
896     Type tx = Type::parint(-1);
897     tx.ot(Type::OT_OPTIONAL);
898     auto* x_ti = new TypeInst(Location().introduce(), tx, ranges, ident);
899     auto* vd_x = new VarDecl(Location().introduce(), x_ti, "x");
900     vd_x->toplevel(false);
901 
902     auto* b_ti = new TypeInst(Location().introduce(), Type::parbool());
903     auto* vd_b = new VarDecl(Location().introduce(), b_ti, "b");
904     vd_b->toplevel(false);
905 
906     auto* j_ti = new TypeInst(Location().introduce(), Type::parbool());
907     auto* vd_j = new VarDecl(Location().introduce(), j_ti, "json");
908     vd_j->toplevel(false);
909 
910     auto* xx_range = new TypeInst(Location().introduce(), Type::parint(), nullptr);
911     std::vector<TypeInst*> xx_ranges(1);
912     xx_ranges[0] = xx_range;
913     auto* xx_ti = new TypeInst(Location().introduce(), tx, xx_ranges, ident);
914 
915     std::vector<Expression*> array1dArgs(1);
916     array1dArgs[0] = vd_x->id();
917     Call* array1dCall = new Call(Location().introduce(), "array1d", array1dArgs);
918 
919     auto* vd_xx = new VarDecl(Location().introduce(), xx_ti, "xx", array1dCall);
920     vd_xx->toplevel(false);
921 
922     auto* idx_i_ti = new TypeInst(Location().introduce(), Type::parint());
923     auto* idx_i = new VarDecl(Location().introduce(), idx_i_ti, "i");
924     idx_i->toplevel(false);
925 
926     std::vector<Expression*> aa_xxi_idx(1);
927     aa_xxi_idx[0] = idx_i->id();
928     auto* aa_xxi = new ArrayAccess(Location().introduce(), vd_xx->id(), aa_xxi_idx);
929 
930     std::vector<Expression*> _toString_ENUMArgs(3);
931     _toString_ENUMArgs[0] = aa_xxi;
932     _toString_ENUMArgs[1] = vd_b->id();
933     _toString_ENUMArgs[2] = vd_j->id();
934     Call* _toString_ENUM =
935         new Call(Location().introduce(), create_enum_to_string_name(ident, "_toString_"),
936                  _toString_ENUMArgs);
937 
938     std::vector<Expression*> index_set_xx_args(1);
939     index_set_xx_args[0] = vd_xx->id();
940     Call* index_set_xx = new Call(Location().introduce(), "index_set", index_set_xx_args);
941     std::vector<VarDecl*> gen_exps(1);
942     gen_exps[0] = idx_i;
943     Generator gen(gen_exps, index_set_xx, nullptr);
944 
945     Generators generators;
946     generators.g.push_back(gen);
947     auto* comp = new Comprehension(Location().introduce(), _toString_ENUM, generators, false);
948 
949     std::vector<Expression*> join_args(2);
950     join_args[0] = new StringLit(Location().introduce(), ", ");
951     join_args[1] = comp;
952     Call* join = new Call(Location().introduce(), "join", join_args);
953 
954     auto* sl_open = new StringLit(Location().introduce(), "[");
955     auto* bopp0 = new BinOp(Location().introduce(), sl_open, BOT_PLUSPLUS, join);
956     auto* sl_close = new StringLit(Location().introduce(), "]");
957     auto* bopp1 = new BinOp(Location().introduce(), bopp0, BOT_PLUSPLUS, sl_close);
958 
959     std::vector<Expression*> let_args(1);
960     let_args[0] = vd_xx;
961     Let* let = new Let(Location().introduce(), let_args, bopp1);
962 
963     auto* ti_fi = new TypeInst(Location().introduce(), Type::parstring());
964     std::vector<VarDecl*> fi_params(3);
965     fi_params[0] = vd_x;
966     fi_params[1] = vd_b;
967     fi_params[2] = vd_j;
968     auto* fi =
969         new FunctionI(Location().introduce(), create_enum_to_string_name(ident, "_toString_"),
970                       ti_fi, fi_params, let);
971     enumItems->addItem(fi);
972   }
973 
974   {
975     /*
976 
977      function _toString_ENUM(opt set of ENUM: x, bool: b, bool: json) =
978        if absent(x) then "<>" else "{" ++ join(", ", [ _toString_ENUM(i,b,json) | i in x ]) ++ "}"
979      endif;
980 
981      */
982 
983     Type argType = Type::parsetenum(ident->type().enumId());
984     argType.ot(Type::OT_OPTIONAL);
985     auto* x_ti = new TypeInst(Location().introduce(), argType, ident);
986     auto* vd_x = new VarDecl(Location().introduce(), x_ti, "x");
987     vd_x->toplevel(false);
988 
989     auto* b_ti = new TypeInst(Location().introduce(), Type::parbool());
990     auto* vd_b = new VarDecl(Location().introduce(), b_ti, "b");
991     vd_b->toplevel(false);
992 
993     auto* j_ti = new TypeInst(Location().introduce(), Type::parbool());
994     auto* vd_j = new VarDecl(Location().introduce(), j_ti, "json");
995     vd_j->toplevel(false);
996 
997     auto* idx_i_ti = new TypeInst(Location().introduce(), Type::parint());
998     auto* idx_i = new VarDecl(Location().introduce(), idx_i_ti, "i");
999     idx_i->toplevel(false);
1000 
1001     std::vector<Expression*> _toString_ENUMArgs(3);
1002     _toString_ENUMArgs[0] = idx_i->id();
1003     _toString_ENUMArgs[1] = vd_b->id();
1004     _toString_ENUMArgs[2] = vd_j->id();
1005     Call* _toString_ENUM =
1006         new Call(Location().introduce(), create_enum_to_string_name(ident, "_toString_"),
1007                  _toString_ENUMArgs);
1008 
1009     std::vector<Expression*> deopt_args(1);
1010     deopt_args[0] = vd_x->id();
1011     Call* deopt = new Call(Location().introduce(), "deopt", deopt_args);
1012     Call* if_absent = new Call(Location().introduce(), "absent", deopt_args);
1013     auto* sl_absent_dzn = new StringLit(Location().introduce(), "<>");
1014     ITE* sl_absent = new ITE(Location().introduce(),
1015                              {vd_j->id(), new StringLit(Location().introduce(), ASTString("null"))},
1016                              sl_absent_dzn);
1017 
1018     std::vector<VarDecl*> gen_exps(1);
1019     gen_exps[0] = idx_i;
1020     Generator gen(gen_exps, deopt, nullptr);
1021 
1022     Generators generators;
1023     generators.g.push_back(gen);
1024     auto* comp = new Comprehension(Location().introduce(), _toString_ENUM, generators, false);
1025 
1026     std::vector<Expression*> join_args(2);
1027     join_args[0] = new StringLit(Location().introduce(), ", ");
1028     join_args[1] = comp;
1029     Call* join = new Call(Location().introduce(), "join", join_args);
1030 
1031     ITE* json_set =
1032         new ITE(Location().introduce(),
1033                 {vd_j->id(), new StringLit(Location().introduce(), ASTString("\"set\":["))},
1034                 new StringLit(Location().introduce(), ASTString("")));
1035     ITE* json_set_close = new ITE(
1036         Location().introduce(), {vd_j->id(), new StringLit(Location().introduce(), ASTString("]"))},
1037         new StringLit(Location().introduce(), ASTString("")));
1038 
1039     auto* sl_open = new StringLit(Location().introduce(), "{");
1040     auto* bopp0 = new BinOp(Location().introduce(), sl_open, BOT_PLUSPLUS, json_set);
1041     auto* bopp1 = new BinOp(Location().introduce(), bopp0, BOT_PLUSPLUS, join);
1042     auto* bopp2 = new BinOp(Location().introduce(), bopp1, BOT_PLUSPLUS, json_set_close);
1043     auto* sl_close = new StringLit(Location().introduce(), "}");
1044     auto* bopp3 = new BinOp(Location().introduce(), bopp2, BOT_PLUSPLUS, sl_close);
1045 
1046     std::vector<Expression*> if_then(2);
1047     if_then[0] = if_absent;
1048     if_then[1] = sl_absent;
1049     ITE* ite = new ITE(Location().introduce(), if_then, bopp3);
1050 
1051     auto* ti_fi = new TypeInst(Location().introduce(), Type::parstring());
1052     std::vector<VarDecl*> fi_params(3);
1053     fi_params[0] = vd_x;
1054     fi_params[1] = vd_b;
1055     fi_params[2] = vd_j;
1056     auto* fi =
1057         new FunctionI(Location().introduce(), create_enum_to_string_name(ident, "_toString_"),
1058                       ti_fi, fi_params, ite);
1059     enumItems->addItem(fi);
1060   }
1061 
1062   {
1063     /*
1064 
1065      function _toString_ENUM(array[$U] of opt set of ENUM: x, bool: b, bool: json) =
1066      let {
1067      array[int] of opt set of ENUM: xx = array1d(x)
1068      } in "[" ++ join(", ", [ _toString_ENUM(xx[i],b,json) | i in index_set(xx) ]) ++ "]";
1069 
1070      */
1071 
1072     TIId* tiid = new TIId(Location().introduce(), "U");
1073     auto* ti_range = new TypeInst(Location().introduce(), Type::parint(), tiid);
1074     std::vector<TypeInst*> ranges(1);
1075     ranges[0] = ti_range;
1076 
1077     Type tx = Type::parsetint(-1);
1078     tx.ot(Type::OT_OPTIONAL);
1079     auto* x_ti = new TypeInst(Location().introduce(), tx, ranges, ident);
1080     auto* vd_x = new VarDecl(Location().introduce(), x_ti, "x");
1081     vd_x->toplevel(false);
1082 
1083     auto* b_ti = new TypeInst(Location().introduce(), Type::parbool());
1084     auto* vd_b = new VarDecl(Location().introduce(), b_ti, "b");
1085     vd_b->toplevel(false);
1086 
1087     auto* j_ti = new TypeInst(Location().introduce(), Type::parbool());
1088     auto* vd_j = new VarDecl(Location().introduce(), j_ti, "json");
1089     vd_j->toplevel(false);
1090 
1091     auto* xx_range = new TypeInst(Location().introduce(), Type::parint(), nullptr);
1092     std::vector<TypeInst*> xx_ranges(1);
1093     xx_ranges[0] = xx_range;
1094     auto* xx_ti = new TypeInst(Location().introduce(), tx, xx_ranges, ident);
1095 
1096     std::vector<Expression*> array1dArgs(1);
1097     array1dArgs[0] = vd_x->id();
1098     Call* array1dCall = new Call(Location().introduce(), "array1d", array1dArgs);
1099 
1100     auto* vd_xx = new VarDecl(Location().introduce(), xx_ti, "xx", array1dCall);
1101     vd_xx->toplevel(false);
1102 
1103     auto* idx_i_ti = new TypeInst(Location().introduce(), Type::parint());
1104     auto* idx_i = new VarDecl(Location().introduce(), idx_i_ti, "i");
1105     idx_i->toplevel(false);
1106 
1107     std::vector<Expression*> aa_xxi_idx(1);
1108     aa_xxi_idx[0] = idx_i->id();
1109     auto* aa_xxi = new ArrayAccess(Location().introduce(), vd_xx->id(), aa_xxi_idx);
1110 
1111     std::vector<Expression*> _toString_ENUMArgs(3);
1112     _toString_ENUMArgs[0] = aa_xxi;
1113     _toString_ENUMArgs[1] = vd_b->id();
1114     _toString_ENUMArgs[2] = vd_j->id();
1115     Call* _toString_ENUM =
1116         new Call(Location().introduce(), create_enum_to_string_name(ident, "_toString_"),
1117                  _toString_ENUMArgs);
1118 
1119     std::vector<Expression*> index_set_xx_args(1);
1120     index_set_xx_args[0] = vd_xx->id();
1121     Call* index_set_xx = new Call(Location().introduce(), "index_set", index_set_xx_args);
1122     std::vector<VarDecl*> gen_exps(1);
1123     gen_exps[0] = idx_i;
1124     Generator gen(gen_exps, index_set_xx, nullptr);
1125 
1126     Generators generators;
1127     generators.g.push_back(gen);
1128     auto* comp = new Comprehension(Location().introduce(), _toString_ENUM, generators, false);
1129 
1130     std::vector<Expression*> join_args(2);
1131     join_args[0] = new StringLit(Location().introduce(), ", ");
1132     join_args[1] = comp;
1133     Call* join = new Call(Location().introduce(), "join", join_args);
1134 
1135     auto* sl_open = new StringLit(Location().introduce(), "[");
1136     auto* bopp0 = new BinOp(Location().introduce(), sl_open, BOT_PLUSPLUS, join);
1137     auto* sl_close = new StringLit(Location().introduce(), "]");
1138     auto* bopp1 = new BinOp(Location().introduce(), bopp0, BOT_PLUSPLUS, sl_close);
1139 
1140     std::vector<Expression*> let_args(1);
1141     let_args[0] = vd_xx;
1142     Let* let = new Let(Location().introduce(), let_args, bopp1);
1143 
1144     auto* ti_fi = new TypeInst(Location().introduce(), Type::parstring());
1145     std::vector<VarDecl*> fi_params(3);
1146     fi_params[0] = vd_x;
1147     fi_params[1] = vd_b;
1148     fi_params[2] = vd_j;
1149     auto* fi =
1150         new FunctionI(Location().introduce(), create_enum_to_string_name(ident, "_toString_"),
1151                       ti_fi, fi_params, let);
1152     enumItems->addItem(fi);
1153   }
1154 }
1155 
add(EnvI & env,VarDeclI * vdi,bool handleEnums,Model * enumItems)1156 void TopoSorter::add(EnvI& env, VarDeclI* vdi, bool handleEnums, Model* enumItems) {
1157   VarDecl* vd = vdi->e();
1158   if (handleEnums && vd->ti()->isEnum()) {
1159     unsigned int enumId = env.registerEnum(vdi);
1160     Type vdt = vd->type();
1161     vdt.enumId(enumId);
1162     vd->ti()->type(vdt);
1163     vd->type(vdt);
1164 
1165     create_enum_mapper(env, model, enumId, vd, enumItems);
1166   }
1167   scopes.add(env, vd);
1168 }
1169 
get(EnvI & env,const ASTString & id_v,const Location & loc)1170 VarDecl* TopoSorter::get(EnvI& env, const ASTString& id_v, const Location& loc) {
1171   GCLock lock;
1172   Id* ident = new Id(Location(), id_v, nullptr);
1173   VarDecl* decl = scopes.find(ident);
1174   if (decl == nullptr) {
1175     std::ostringstream ss;
1176     ss << "undefined identifier `" << ident->str() << "'";
1177     VarDecl* similar = scopes.findSimilar(ident);
1178     if (similar != nullptr) {
1179       ss << ", did you mean `" << *similar->id() << "'?";
1180     }
1181     throw TypeError(env, loc, ss.str());
1182   }
1183   return decl;
1184 }
1185 
checkId(EnvI & env,Id * ident,const Location & loc)1186 VarDecl* TopoSorter::checkId(EnvI& env, Id* ident, const Location& loc) {
1187   VarDecl* decl = scopes.find(ident);
1188   if (decl == nullptr) {
1189     std::ostringstream ss;
1190     ss << "undefined identifier `" << ident->str() << "'";
1191     VarDecl* similar = scopes.findSimilar(ident);
1192     if (similar != nullptr) {
1193       ss << ", did you mean `" << *similar->id() << "'?";
1194     }
1195     throw TypeError(env, loc, ss.str());
1196   }
1197   auto pi = pos.find(decl);
1198   if (pi == pos.end()) {
1199     // new id
1200     scopes.pushToplevel();
1201     run(env, decl);
1202     scopes.pop();
1203   } else {
1204     // previously seen, check if circular
1205     if (pi->second == -1) {
1206       std::ostringstream ss;
1207       ss << "circular definition of `" << ident->str() << "'";
1208       throw TypeError(env, loc, ss.str());
1209     }
1210   }
1211   return decl;
1212 }
1213 
checkId(EnvI & env,const ASTString & id_v,const Location & loc)1214 VarDecl* TopoSorter::checkId(EnvI& env, const ASTString& id_v, const Location& loc) {
1215   GCLock lock;
1216   Id* id = new Id(loc, id_v, nullptr);
1217   return checkId(env, id, loc);
1218 }
1219 
run(EnvI & env,Expression * e)1220 void TopoSorter::run(EnvI& env, Expression* e) {
1221   if (e == nullptr) {
1222     return;
1223   }
1224   switch (e->eid()) {
1225     case Expression::E_INTLIT:
1226     case Expression::E_FLOATLIT:
1227     case Expression::E_BOOLLIT:
1228     case Expression::E_STRINGLIT:
1229     case Expression::E_ANON:
1230       break;
1231     case Expression::E_SETLIT: {
1232       auto* sl = e->cast<SetLit>();
1233       if (sl->isv() == nullptr && sl->fsv() == nullptr) {
1234         for (unsigned int i = 0; i < sl->v().size(); i++) {
1235           run(env, sl->v()[i]);
1236         }
1237       }
1238     } break;
1239     case Expression::E_ID: {
1240       if (e != constants().absent) {
1241         VarDecl* vd = checkId(env, e->cast<Id>(), e->loc());
1242         e->cast<Id>()->decl(vd);
1243       }
1244     } break;
1245     case Expression::E_ARRAYLIT: {
1246       auto* al = e->cast<ArrayLit>();
1247       for (unsigned int i = 0; i < al->size(); i++) {
1248         run(env, (*al)[i]);
1249       }
1250     } break;
1251     case Expression::E_ARRAYACCESS: {
1252       auto* ae = e->cast<ArrayAccess>();
1253       run(env, ae->v());
1254       for (unsigned int i = 0; i < ae->idx().size(); i++) {
1255         run(env, ae->idx()[i]);
1256       }
1257     } break;
1258     case Expression::E_COMP: {
1259       auto* ce = e->cast<Comprehension>();
1260       scopes.push();
1261       for (int i = 0; i < ce->numberOfGenerators(); i++) {
1262         run(env, ce->in(i));
1263         for (int j = 0; j < ce->numberOfDecls(i); j++) {
1264           run(env, ce->decl(i, j));
1265           scopes.add(env, ce->decl(i, j));
1266         }
1267         if (ce->where(i) != nullptr) {
1268           run(env, ce->where(i));
1269         }
1270       }
1271       run(env, ce->e());
1272       scopes.pop();
1273     } break;
1274     case Expression::E_ITE: {
1275       ITE* ite = e->cast<ITE>();
1276       for (int i = 0; i < ite->size(); i++) {
1277         run(env, ite->ifExpr(i));
1278         run(env, ite->thenExpr(i));
1279       }
1280       run(env, ite->elseExpr());
1281     } break;
1282     case Expression::E_BINOP: {
1283       auto* be = e->cast<BinOp>();
1284       std::vector<Expression*> todo;
1285       todo.push_back(be->lhs());
1286       todo.push_back(be->rhs());
1287       while (!todo.empty()) {
1288         Expression* be = todo.back();
1289         todo.pop_back();
1290         if (auto* e_bo = be->dynamicCast<BinOp>()) {
1291           todo.push_back(e_bo->lhs());
1292           todo.push_back(e_bo->rhs());
1293           for (ExpressionSetIter it = e_bo->ann().begin(); it != e_bo->ann().end(); ++it) {
1294             run(env, *it);
1295           }
1296         } else {
1297           run(env, be);
1298         }
1299       }
1300     } break;
1301     case Expression::E_UNOP: {
1302       UnOp* ue = e->cast<UnOp>();
1303       run(env, ue->e());
1304     } break;
1305     case Expression::E_CALL: {
1306       Call* ce = e->cast<Call>();
1307       for (unsigned int i = 0; i < ce->argCount(); i++) {
1308         run(env, ce->arg(i));
1309       }
1310     } break;
1311     case Expression::E_VARDECL: {
1312       auto* ve = e->cast<VarDecl>();
1313       auto pi = pos.find(ve);
1314       if (pi == pos.end()) {
1315         pos.insert(std::pair<VarDecl*, int>(ve, -1));
1316         run(env, ve->ti());
1317         run(env, ve->e());
1318         ve->payload(static_cast<int>(decls.size()));
1319         decls.push_back(ve);
1320         pi = pos.find(ve);
1321         pi->second = static_cast<int>(decls.size()) - 1;
1322       } else {
1323         assert(pi->second != -1);
1324       }
1325     } break;
1326     case Expression::E_TI: {
1327       auto* ti = e->cast<TypeInst>();
1328       for (unsigned int i = 0; i < ti->ranges().size(); i++) {
1329         run(env, ti->ranges()[i]);
1330       }
1331       run(env, ti->domain());
1332     } break;
1333     case Expression::E_TIID:
1334       break;
1335     case Expression::E_LET: {
1336       Let* let = e->cast<Let>();
1337       scopes.push();
1338       for (unsigned int i = 0; i < let->let().size(); i++) {
1339         run(env, let->let()[i]);
1340         if (auto* vd = let->let()[i]->dynamicCast<VarDecl>()) {
1341           scopes.add(env, vd);
1342         }
1343       }
1344       run(env, let->in());
1345       VarDeclCmp poscmp(pos);
1346       std::stable_sort(let->let().begin(), let->let().end(), poscmp);
1347       for (unsigned int i = 0, j = 0; i < let->let().size(); i++) {
1348         if (auto* vd = let->let()[i]->dynamicCast<VarDecl>()) {
1349           let->letOrig()[j++] = vd->e();
1350           for (unsigned int k = 0; k < vd->ti()->ranges().size(); k++) {
1351             let->letOrig()[j++] = vd->ti()->ranges()[k]->domain();
1352           }
1353         }
1354       }
1355       scopes.pop();
1356     } break;
1357   }
1358   if (env.ignoreUnknownIds) {
1359     std::vector<Expression*> toDelete;
1360     for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) {
1361       try {
1362         run(env, *it);
1363       } catch (TypeError&) {
1364         toDelete.push_back(*it);
1365       }
1366       for (Expression* de : toDelete) {
1367         e->ann().remove(de);
1368       }
1369     }
1370   } else {
1371     for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) {
1372       run(env, *it);
1373     }
1374   }
1375 }
1376 
add_coercion(EnvI & env,Model * m,Expression * e,const Type & funarg_t)1377 KeepAlive add_coercion(EnvI& env, Model* m, Expression* e, const Type& funarg_t) {
1378   if (e->isa<ArrayAccess>() && e->type().dim() > 0) {
1379     auto* aa = e->cast<ArrayAccess>();
1380     // Turn ArrayAccess into a slicing operation
1381     std::vector<Expression*> args;
1382     args.push_back(aa->v());
1383     args.push_back(nullptr);
1384     std::vector<Expression*> slice;
1385     GCLock lock;
1386     for (unsigned int i = 0; i < aa->idx().size(); i++) {
1387       if (aa->idx()[i]->type().isSet()) {
1388         bool needIdxSet = true;
1389         bool needInter = true;
1390         if (auto* sl = aa->idx()[i]->dynamicCast<SetLit>()) {
1391           if ((sl->isv() != nullptr) && sl->isv()->size() == 1) {
1392             if (sl->isv()->min().isFinite() && sl->isv()->max().isFinite()) {
1393               args.push_back(sl);
1394               needIdxSet = false;
1395             } else if (sl->isv()->min() == -IntVal::infinity() &&
1396                        sl->isv()->max() == IntVal::infinity()) {
1397               needInter = false;
1398             }
1399           }
1400         }
1401         if (needIdxSet) {
1402           std::ostringstream oss;
1403           oss << "index_set";
1404           if (aa->idx().size() > 1) {
1405             oss << "_" << (i + 1) << "of" << aa->idx().size();
1406           }
1407           std::vector<Expression*> origIdxsetArgs(1);
1408           origIdxsetArgs[0] = aa->v();
1409           Call* origIdxset = new Call(aa->v()->loc(), ASTString(oss.str()), origIdxsetArgs);
1410           FunctionI* fi = m->matchFn(env, origIdxset, false);
1411           if (fi == nullptr) {
1412             throw TypeError(env, e->loc(), "missing builtin " + oss.str());
1413           }
1414           origIdxset->type(fi->rtype(env, origIdxsetArgs, false));
1415           origIdxset->decl(fi);
1416           if (needInter) {
1417             auto* inter = new BinOp(aa->idx()[i]->loc(), aa->idx()[i], BOT_INTERSECT, origIdxset);
1418             inter->type(Type::parsetint());
1419             args.push_back(inter);
1420           } else {
1421             args.push_back(origIdxset);
1422           }
1423         }
1424         slice.push_back(aa->idx()[i]);
1425       } else {
1426         auto* bo = new BinOp(aa->idx()[i]->loc(), aa->idx()[i], BOT_DOTDOT, aa->idx()[i]);
1427         bo->type(Type::parsetint());
1428         slice.push_back(bo);
1429       }
1430     }
1431     auto* a_slice = new ArrayLit(e->loc(), slice);
1432     a_slice->type(Type::parsetint(1));
1433     args[1] = a_slice;
1434     std::ostringstream oss;
1435     oss << "slice_" << (args.size() - 2) << "d";
1436     Call* c = new Call(e->loc(), ASTString(oss.str()), args);
1437     FunctionI* fi = m->matchFn(env, c, false);
1438     if (fi == nullptr) {
1439       throw TypeError(env, e->loc(), "missing builtin " + oss.str());
1440     }
1441     c->type(fi->rtype(env, args, false));
1442     c->decl(fi);
1443     e = c;
1444   }
1445   if (e->type().dim() == funarg_t.dim() &&
1446       (funarg_t.bt() == Type::BT_BOT || funarg_t.bt() == Type::BT_TOP ||
1447        e->type().bt() == funarg_t.bt() || e->type().bt() == Type::BT_BOT)) {
1448     return e;
1449   }
1450   GCLock lock;
1451   Call* c = nullptr;
1452   if (e->type().dim() == 0 && funarg_t.dim() != 0) {
1453     if (e->type().isvar()) {
1454       throw TypeError(env, e->loc(), "cannot coerce var set into array");
1455     }
1456     if (e->type().isOpt()) {
1457       throw TypeError(env, e->loc(), "cannot coerce opt set into array");
1458     }
1459     std::vector<Expression*> set2a_args(1);
1460     set2a_args[0] = e;
1461     Call* set2a = new Call(e->loc(), ASTString("set2array"), set2a_args);
1462     FunctionI* fi = m->matchFn(env, set2a, false);
1463     if (fi != nullptr) {
1464       set2a->type(fi->rtype(env, set2a_args, false));
1465       set2a->decl(fi);
1466       e = set2a;
1467     }
1468   }
1469   if (funarg_t.bt() == Type::BT_TOP || e->type().bt() == funarg_t.bt() ||
1470       e->type().bt() == Type::BT_BOT) {
1471     KeepAlive ka(e);
1472     return ka;
1473   }
1474   std::vector<Expression*> args(1);
1475   args[0] = e;
1476   if (e->type().bt() == Type::BT_BOOL) {
1477     if (funarg_t.bt() == Type::BT_INT) {
1478       c = new Call(e->loc(), constants().ids.bool2int, args);
1479     } else if (funarg_t.bt() == Type::BT_FLOAT) {
1480       c = new Call(e->loc(), constants().ids.bool2float, args);
1481     }
1482   } else if (e->type().bt() == Type::BT_INT) {
1483     if (funarg_t.bt() == Type::BT_FLOAT) {
1484       c = new Call(e->loc(), constants().ids.int2float, args);
1485     }
1486   }
1487   if (c != nullptr) {
1488     FunctionI* fi = m->matchFn(env, c, false);
1489     assert(fi);
1490     Type ct = fi->rtype(env, args, false);
1491     ct.cv(e->type().cv() || ct.cv());
1492     c->type(ct);
1493     c->decl(fi);
1494     KeepAlive ka(c);
1495     return ka;
1496   }
1497   throw TypeError(env, e->loc(),
1498                   "cannot determine coercion from type " + e->type().toString(env) + " to type " +
1499                       funarg_t.toString(env));
1500 }
add_coercion(EnvI & env,Model * m,Expression * e,Expression * funarg)1501 KeepAlive add_coercion(EnvI& env, Model* m, Expression* e, Expression* funarg) {
1502   return add_coercion(env, m, e, funarg->type());
1503 }
1504 
1505 template <bool ignoreVarDecl>
1506 class Typer {
1507 private:
1508   EnvI& _env;
1509   Model* _model;
1510   std::vector<TypeError>& _typeErrors;
1511   bool _ignoreUndefined;
1512 
1513 public:
Typer(EnvI & env,Model * model,std::vector<TypeError> & typeErrors,bool ignoreUndefined)1514   Typer(EnvI& env, Model* model, std::vector<TypeError>& typeErrors, bool ignoreUndefined)
1515       : _env(env), _model(model), _typeErrors(typeErrors), _ignoreUndefined(ignoreUndefined) {}
1516   /// Check annotations when expression is finished
exit(Expression * e)1517   void exit(Expression* e) {
1518     for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) {
1519       if (!(*it)->type().isAnn()) {
1520         throw TypeError(_env, (*it)->loc(),
1521                         "expected annotation, got `" + (*it)->type().toString(_env) + "'");
1522       }
1523     }
1524   }
enter(Expression *)1525   bool enter(Expression* /*e*/) { return true; }
1526   /// Visit integer literal
vIntLit(const IntLit &)1527   void vIntLit(const IntLit& /*i*/) {}
1528   /// Visit floating point literal
vFloatLit(const FloatLit &)1529   void vFloatLit(const FloatLit& /*f*/) {}
1530   /// Visit Boolean literal
vBoolLit(const BoolLit &)1531   void vBoolLit(const BoolLit& /*b*/) {}
1532   /// Visit set literal
vSetLit(SetLit & sl)1533   void vSetLit(SetLit& sl) {
1534     Type ty;
1535     ty.st(Type::ST_SET);
1536     if (sl.isv() != nullptr) {
1537       ty.bt(Type::BT_INT);
1538       ty.enumId(sl.type().enumId());
1539       sl.type(ty);
1540       return;
1541     }
1542     if (sl.fsv() != nullptr) {
1543       ty.bt(Type::BT_FLOAT);
1544       sl.type(ty);
1545       return;
1546     }
1547     unsigned int enumId = sl.v().size() > 0 ? sl.v()[0]->type().enumId() : 0;
1548     for (unsigned int i = 0; i < sl.v().size(); i++) {
1549       Type vi_t = sl.v()[i]->type();
1550       vi_t.ot(Type::OT_PRESENT);
1551       if (sl.v()[i] == constants().absent) {
1552         continue;
1553       }
1554       if (vi_t.dim() > 0) {
1555         throw TypeError(_env, sl.v()[i]->loc(), "set literals cannot contain arrays");
1556       }
1557       if (vi_t.st() == Type::ST_SET) {
1558         throw TypeError(_env, sl.v()[i]->loc(), "set literals cannot contain sets");
1559       }
1560       if (vi_t.isvar()) {
1561         ty.ti(Type::TI_VAR);
1562       }
1563       if (vi_t.cv()) {
1564         ty.cv(true);
1565       }
1566       if (enumId != vi_t.enumId()) {
1567         enumId = 0;
1568       }
1569       if (!Type::btSubtype(vi_t, ty, true)) {
1570         if (ty.bt() == Type::BT_UNKNOWN || Type::btSubtype(ty, vi_t, true)) {
1571           ty.bt(vi_t.bt());
1572         } else {
1573           throw TypeError(_env, sl.loc(), "non-uniform set literal");
1574         }
1575       }
1576     }
1577     ty.enumId(enumId);
1578     if (ty.bt() == Type::BT_UNKNOWN) {
1579       ty.bt(Type::BT_BOT);
1580     } else {
1581       if (ty.isvar() && ty.bt() != Type::BT_INT) {
1582         if (ty.bt() == Type::BT_BOOL) {
1583           ty.bt(Type::BT_INT);
1584         } else {
1585           throw TypeError(_env, sl.loc(), "cannot coerce set literal element to var int");
1586         }
1587       }
1588       for (unsigned int i = 0; i < sl.v().size(); i++) {
1589         sl.v()[i] = add_coercion(_env, _model, sl.v()[i], ty)();
1590       }
1591     }
1592     sl.type(ty);
1593   }
1594   /// Visit string literal
vStringLit(const StringLit &)1595   void vStringLit(const StringLit& /*sl*/) {}
1596   /// Visit identifier
vId(Id & id)1597   void vId(Id& id) {
1598     if (&id != constants().absent) {
1599       assert(!id.decl()->type().isunknown());
1600       id.type(id.decl()->type());
1601     }
1602   }
1603   /// Visit anonymous variable
vAnonVar(const AnonVar &)1604   void vAnonVar(const AnonVar& /*v*/) {}
1605   /// Visit array literal
vArrayLit(ArrayLit & al)1606   void vArrayLit(ArrayLit& al) {
1607     Type ty;
1608     ty.dim(static_cast<int>(al.dims()));
1609     std::vector<AnonVar*> anons;
1610     bool haveAbsents = false;
1611     bool haveInferredType = false;
1612     for (unsigned int i = 0; i < al.size(); i++) {
1613       Expression* vi = al[i];
1614       if (vi->type().dim() > 0) {
1615         throw TypeError(_env, vi->loc(), "arrays cannot be elements of arrays");
1616       }
1617       if (vi == constants().absent) {
1618         haveAbsents = true;
1619       }
1620       auto* av = vi->dynamicCast<AnonVar>();
1621       if (av != nullptr) {
1622         ty.ti(Type::TI_VAR);
1623         anons.push_back(av);
1624       } else if (vi->type().isvar()) {
1625         ty.ti(Type::TI_VAR);
1626       }
1627       if (vi->type().cv()) {
1628         ty.cv(true);
1629       }
1630       if (vi->type().isOpt()) {
1631         ty.ot(Type::OT_OPTIONAL);
1632       }
1633 
1634       if (ty.bt() == Type::BT_UNKNOWN) {
1635         if (av == nullptr) {
1636           if (haveInferredType) {
1637             if (ty.st() != vi->type().st() && vi->type().ot() != Type::OT_OPTIONAL) {
1638               throw TypeError(_env, al.loc(), "non-uniform array literal");
1639             }
1640           } else {
1641             haveInferredType = true;
1642             ty.st(vi->type().st());
1643           }
1644           if (vi->type().bt() != Type::BT_BOT) {
1645             ty.bt(vi->type().bt());
1646             ty.enumId(vi->type().enumId());
1647           }
1648         }
1649       } else {
1650         if (av == nullptr) {
1651           if (vi->type().bt() == Type::BT_BOT) {
1652             if (vi->type().st() != ty.st() && vi->type().ot() != Type::OT_OPTIONAL) {
1653               throw TypeError(_env, al.loc(), "non-uniform array literal");
1654             }
1655             if (vi->type().enumId() != 0 && ty.enumId() != vi->type().enumId()) {
1656               ty.enumId(0);
1657             }
1658           } else {
1659             unsigned int tyEnumId = ty.enumId();
1660             ty.enumId(vi->type().enumId());
1661             if (Type::btSubtype(ty, vi->type(), true)) {
1662               ty.bt(vi->type().bt());
1663             }
1664             if (tyEnumId != vi->type().enumId()) {
1665               ty.enumId(0);
1666             }
1667             if (!Type::btSubtype(vi->type(), ty, true) || ty.st() != vi->type().st()) {
1668               throw TypeError(_env, al.loc(), "non-uniform array literal");
1669             }
1670           }
1671         }
1672       }
1673     }
1674     if (ty.bt() == Type::BT_UNKNOWN) {
1675       ty.bt(Type::BT_BOT);
1676       if (!anons.empty()) {
1677         throw TypeError(_env, al.loc(),
1678                         "array literal must contain at least one non-anonymous variable");
1679       }
1680       if (haveAbsents) {
1681         throw TypeError(_env, al.loc(), "array literal must contain at least one non-absent value");
1682       }
1683     } else {
1684       Type at = ty;
1685       at.dim(0);
1686       if (at.ti() == Type::TI_VAR && at.st() == Type::ST_SET && at.bt() != Type::BT_INT) {
1687         if (at.bt() == Type::BT_BOOL) {
1688           ty.bt(Type::BT_INT);
1689           at.bt(Type::BT_INT);
1690         } else {
1691           throw TypeError(_env, al.loc(), "cannot coerce array element to var set of int");
1692         }
1693       }
1694       for (auto& anon : anons) {
1695         anon->type(at);
1696       }
1697       for (unsigned int i = 0; i < al.size(); i++) {
1698         al.set(i, add_coercion(_env, _model, al[i], at)());
1699       }
1700     }
1701     if (ty.enumId() != 0) {
1702       std::vector<unsigned int> enumIds(ty.dim() + 1);
1703       for (int i = 0; i < ty.dim(); i++) {
1704         enumIds[i] = 0;
1705       }
1706       enumIds[ty.dim()] = ty.enumId();
1707       ty.enumId(_env.registerArrayEnum(enumIds));
1708     }
1709     al.type(ty);
1710   }
1711   /// Visit array access
vArrayAccess(ArrayAccess & aa)1712   void vArrayAccess(ArrayAccess& aa) {
1713     if (aa.v()->type().dim() == 0) {
1714       if (aa.v()->type().st() == Type::ST_SET) {
1715         Type tv = aa.v()->type();
1716         tv.st(Type::ST_PLAIN);
1717         tv.dim(1);
1718         aa.v(add_coercion(_env, _model, aa.v(), tv)());
1719       } else {
1720         std::ostringstream oss;
1721         oss << "array access attempted on expression of type `" << aa.v()->type().toString(_env)
1722             << "'";
1723         throw TypeError(_env, aa.v()->loc(), oss.str());
1724       }
1725     } else if (aa.v()->isa<ArrayAccess>()) {
1726       aa.v(add_coercion(_env, _model, aa.v(), aa.v()->type())());
1727     }
1728     if (aa.v()->type().dim() != aa.idx().size()) {
1729       std::ostringstream oss;
1730       oss << aa.v()->type().dim() << "-dimensional array accessed with " << aa.idx().size()
1731           << (aa.idx().size() == 1 ? " expression" : " expressions");
1732       throw TypeError(_env, aa.v()->loc(), oss.str());
1733     }
1734     Type tt = aa.v()->type();
1735     if (tt.enumId() != 0) {
1736       const std::vector<unsigned int>& arrayEnumIds = _env.getArrayEnum(tt.enumId());
1737       std::vector<unsigned int> newArrayEnumids;
1738 
1739       for (unsigned int i = 0; i < arrayEnumIds.size() - 1; i++) {
1740         Expression* aai = aa.idx()[i];
1741         // Check if index is slice operator, and convert to correct enum type
1742         if (auto* aai_sl = aai->dynamicCast<SetLit>()) {
1743           if (IntSetVal* aai_isv = aai_sl->isv()) {
1744             if (aai_isv->min() == -IntVal::infinity() && aai_isv->max() == IntVal::infinity()) {
1745               Type aai_sl_t = aai_sl->type();
1746               aai_sl_t.enumId(arrayEnumIds[i]);
1747               aai_sl->type(aai_sl_t);
1748             }
1749           }
1750         } else if (auto* aai_bo = aai->dynamicCast<BinOp>()) {
1751           if (aai_bo->op() == BOT_DOTDOT) {
1752             Type aai_bo_t = aai_bo->type();
1753             if (auto* il = aai_bo->lhs()->dynamicCast<IntLit>()) {
1754               if (il->v() == -IntVal::infinity()) {
1755                 // Expression is ..X, so result gets enum type of X
1756                 aai_bo_t.enumId(aai_bo->rhs()->type().enumId());
1757               }
1758             } else if (auto* il = aai_bo->rhs()->dynamicCast<IntLit>()) {
1759               if (il->v() == IntVal::infinity()) {
1760                 // Expression is X.., so result gets enum type of X
1761                 aai_bo_t.enumId(aai_bo->lhs()->type().enumId());
1762               }
1763             }
1764             aai_bo->type(aai_bo_t);
1765           }
1766         }
1767         if (aai->type().isSet()) {
1768           newArrayEnumids.push_back(arrayEnumIds[i]);
1769         }
1770 
1771         if (arrayEnumIds[i] != 0) {
1772           if (aa.idx()[i]->type().enumId() != arrayEnumIds[i]) {
1773             std::ostringstream oss;
1774             oss << "array index ";
1775             if (aa.idx().size() > 1) {
1776               oss << (i + 1) << " ";
1777             }
1778             oss << "must be `" << _env.getEnum(arrayEnumIds[i])->e()->id()->str() << "', but is `"
1779                 << aa.idx()[i]->type().toString(_env) << "'";
1780             throw TypeError(_env, aa.loc(), oss.str());
1781           }
1782         }
1783       }
1784       if (newArrayEnumids.empty()) {
1785         tt.enumId(arrayEnumIds[arrayEnumIds.size() - 1]);
1786       } else {
1787         newArrayEnumids.push_back(arrayEnumIds[arrayEnumIds.size() - 1]);
1788         int newEnumId = _env.registerArrayEnum(newArrayEnumids);
1789         tt.enumId(newEnumId);
1790       }
1791     }
1792     int n_dimensions = 0;
1793     bool isVarAccess = false;
1794     bool isSlice = false;
1795     for (unsigned int i = 0; i < aa.idx().size(); i++) {
1796       Expression* aai = aa.idx()[i];
1797       if (aai->isa<AnonVar>()) {
1798         aai->type(Type::varint());
1799       }
1800       if ((aai->type().bt() != Type::BT_INT && aai->type().bt() != Type::BT_BOOL) ||
1801           aai->type().dim() != 0) {
1802         throw TypeError(_env, aa.loc(),
1803                         "array index must be `int' or `set of int', but is `" +
1804                             aai->type().toString(_env) + "'");
1805       }
1806       if (aai->type().isSet()) {
1807         if (isVarAccess || aai->type().isvar()) {
1808           throw TypeError(_env, aa.loc(),
1809                           "array slicing with variable range or index not supported");
1810         }
1811         isSlice = true;
1812         aa.idx()[i] = add_coercion(_env, _model, aai, Type::varsetint())();
1813         n_dimensions++;
1814       } else {
1815         aa.idx()[i] = add_coercion(_env, _model, aai, Type::varint())();
1816       }
1817 
1818       if (aai->type().isOpt()) {
1819         tt.ot(Type::OT_OPTIONAL);
1820       }
1821       if (aai->type().isvar()) {
1822         isVarAccess = true;
1823         if (isSlice) {
1824           throw TypeError(_env, aa.loc(),
1825                           "array slicing with variable range or index not supported");
1826         }
1827         tt.ti(Type::TI_VAR);
1828         if (tt.bt() == Type::BT_ANN || tt.bt() == Type::BT_STRING) {
1829           throw TypeError(_env, aai->loc(),
1830                           std::string("array access using a variable not supported for array of ") +
1831                               (tt.bt() == Type::BT_ANN ? "ann" : "string"));
1832         }
1833       }
1834       tt.dim(n_dimensions);
1835       if (aai->type().cv()) {
1836         tt.cv(true);
1837       }
1838     }
1839     aa.type(tt);
1840   }
1841   /// Visit array comprehension
vComprehension(Comprehension & c)1842   void vComprehension(Comprehension& c) {
1843     Type tt = c.e()->type();
1844     typedef std::unordered_map<VarDecl*, std::pair<int, int>> genMap_t;
1845     typedef std::unordered_map<VarDecl*, std::vector<Expression*>> whereMap_t;
1846     genMap_t generatorMap;
1847     whereMap_t whereMap;
1848     int declCount = 0;
1849 
1850     for (int i = 0; i < c.numberOfGenerators(); i++) {
1851       for (int j = 0; j < c.numberOfDecls(i); j++) {
1852         generatorMap[c.decl(i, j)] = std::pair<int, int>(i, declCount++);
1853         whereMap[c.decl(i, j)] = std::vector<Expression*>();
1854       }
1855       Expression* g_in = c.in(i);
1856       if (g_in != nullptr) {
1857         const Type& ty_in = g_in->type();
1858         if (ty_in == Type::varsetint()) {
1859           if (!c.set()) {
1860             tt.ot(Type::OT_OPTIONAL);
1861           }
1862           tt.ti(Type::TI_VAR);
1863           tt.cv(true);
1864         }
1865         if (ty_in.cv()) {
1866           tt.cv(true);
1867         }
1868         if (c.where(i) != nullptr) {
1869           if (c.where(i)->type() == Type::varbool()) {
1870             if (!c.set()) {
1871               tt.ot(Type::OT_OPTIONAL);
1872             }
1873             tt.ti(Type::TI_VAR);
1874             tt.cv(true);
1875           } else if (c.where(i)->type() != Type::parbool()) {
1876             throw TypeError(
1877                 _env, c.where(i)->loc(),
1878                 "where clause must be bool, but is `" + c.where(i)->type().toString(_env) + "'");
1879           }
1880           if (c.where(i)->type().cv()) {
1881             tt.cv(true);
1882           }
1883 
1884           // Try to move parts of the where clause to earlier generators
1885           std::vector<Expression*> wherePartsStack;
1886           std::vector<Expression*> whereParts;
1887           wherePartsStack.push_back(c.where(i));
1888           while (!wherePartsStack.empty()) {
1889             Expression* e = wherePartsStack.back();
1890             wherePartsStack.pop_back();
1891             if (auto* bo = e->dynamicCast<BinOp>()) {
1892               if (bo->op() == BOT_AND) {
1893                 wherePartsStack.push_back(bo->rhs());
1894                 wherePartsStack.push_back(bo->lhs());
1895               } else {
1896                 whereParts.push_back(e);
1897               }
1898             } else {
1899               whereParts.push_back(e);
1900             }
1901           }
1902 
1903           for (auto* wp : whereParts) {
1904             class FindLatestGen : public EVisitor {
1905             public:
1906               int declIndex;
1907               VarDecl* decl;
1908               const genMap_t& generatorMap;
1909               Comprehension* comp;
1910               FindLatestGen(const genMap_t& generatorMap0, Comprehension* comp0)
1911                   : declIndex(-1),
1912                     decl(comp0->decl(0, 0)),
1913                     generatorMap(generatorMap0),
1914                     comp(comp0) {}
1915               void vId(const Id& ident) {
1916                 auto it = generatorMap.find(ident.decl());
1917                 if (it != generatorMap.end() && it->second.second > declIndex) {
1918                   declIndex = it->second.second;
1919                   decl = ident.decl();
1920                   int gen = it->second.first;
1921                   while (comp->in(gen) == nullptr && gen < comp->numberOfGenerators() - 1) {
1922                     declIndex++;
1923                     gen++;
1924                     decl = comp->decl(gen, 0);
1925                   }
1926                 }
1927               }
1928             } flg(generatorMap, &c);
1929             top_down(flg, wp);
1930             whereMap[flg.decl].push_back(wp);
1931           }
1932         }
1933       } else {
1934         assert(c.where(i) != nullptr);
1935         whereMap[c.decl(i, 0)].push_back(c.where(i));
1936       }
1937     }
1938 
1939     {
1940       GCLock lock;
1941       Generators generators;
1942       for (int i = 0; i < c.numberOfGenerators(); i++) {
1943         std::vector<VarDecl*> decls;
1944         for (int j = 0; j < c.numberOfDecls(i); j++) {
1945           decls.push_back(c.decl(i, j));
1946           KeepAlive c_in =
1947               c.in(i) != nullptr ? add_coercion(_env, _model, c.in(i), c.in(i)->type()) : nullptr;
1948           if (!whereMap[c.decl(i, j)].empty()) {
1949             // need a generator for all the decls up to this point
1950             Expression* whereExpr = whereMap[c.decl(i, j)][0];
1951             for (unsigned int k = 1; k < whereMap[c.decl(i, j)].size(); k++) {
1952               GCLock lock;
1953               auto* bo =
1954                   new BinOp(Location().introduce(), whereExpr, BOT_AND, whereMap[c.decl(i, j)][k]);
1955               Type bo_t = whereMap[c.decl(i, j)][k]->type().isPar() && whereExpr->type().isPar()
1956                               ? Type::parbool()
1957                               : Type::varbool();
1958               if (whereMap[c.decl(i, j)][k]->type().cv() || whereExpr->type().cv()) {
1959                 bo_t.cv(true);
1960               }
1961               bo->type(bo_t);
1962               whereExpr = bo;
1963             }
1964             generators.g.emplace_back(decls, c_in(), whereExpr);
1965             decls.clear();
1966           } else if (j == c.numberOfDecls(i) - 1) {
1967             generators.g.emplace_back(decls, c_in(), nullptr);
1968             decls.clear();
1969           }
1970         }
1971       }
1972       c.init(c.e(), generators);
1973     }
1974 
1975     if (c.set()) {
1976       if (c.e()->type().dim() != 0 || c.e()->type().st() == Type::ST_SET) {
1977         throw TypeError(_env, c.e()->loc(),
1978                         "set comprehension expression must be scalar, but is `" +
1979                             c.e()->type().toString(_env) + "'");
1980       }
1981       tt.st(Type::ST_SET);
1982       if (tt.isvar()) {
1983         c.e(add_coercion(_env, _model, c.e(), Type::varint())());
1984         tt.bt(Type::BT_INT);
1985       }
1986     } else {
1987       if (c.e()->type().dim() != 0) {
1988         throw TypeError(_env, c.e()->loc(), "array comprehension expression cannot be an array");
1989       }
1990       tt.dim(1);
1991       if (tt.enumId() != 0) {
1992         std::vector<unsigned int> enumIds(2);
1993         enumIds[0] = 0;
1994         enumIds[1] = tt.enumId();
1995         tt.enumId(_env.registerArrayEnum(enumIds));
1996       }
1997     }
1998     c.type(tt);
1999   }
2000   /// Visit array comprehension generator
vComprehensionGenerator(Comprehension & c,int gen_i)2001   void vComprehensionGenerator(Comprehension& c, int gen_i) {
2002     Expression* g_in = c.in(gen_i);
2003     if (g_in == nullptr) {
2004       // This is an "assignment generator" (i = expr)
2005       assert(c.where(gen_i) != nullptr);
2006       assert(c.numberOfDecls(gen_i) == 1);
2007       const Type& ty_where = c.where(gen_i)->type();
2008       c.decl(gen_i, 0)->type(ty_where);
2009       c.decl(gen_i, 0)->ti()->type(ty_where);
2010     } else {
2011       const Type& ty_in = g_in->type();
2012       if (ty_in != Type::varsetint() && ty_in != Type::parsetint() && ty_in.dim() != 1) {
2013         throw TypeError(_env, g_in->loc(),
2014                         "generator expression must be (par or var) set of int or one-dimensional "
2015                         "array, but is `" +
2016                             ty_in.toString(_env) + "'");
2017       }
2018       Type ty_id;
2019       if (ty_in.dim() == 0) {
2020         ty_id = Type::parint();
2021         ty_id.enumId(ty_in.enumId());
2022       } else {
2023         ty_id = ty_in;
2024         if (ty_in.enumId() != 0) {
2025           const std::vector<unsigned int>& enumIds = _env.getArrayEnum(ty_in.enumId());
2026           ty_id.enumId(enumIds.back());
2027         }
2028         ty_id.dim(0);
2029       }
2030       for (int j = 0; j < c.numberOfDecls(gen_i); j++) {
2031         c.decl(gen_i, j)->type(ty_id);
2032         c.decl(gen_i, j)->ti()->type(ty_id);
2033       }
2034     }
2035   }
2036   /// Visit if-then-else
vITE(ITE & ite)2037   void vITE(ITE& ite) {
2038     bool mustBeBool = false;
2039     if (ite.elseExpr() == nullptr) {
2040       // this is an "if <cond> then <expr> endif" so the <expr> must be bool
2041       ite.elseExpr(constants().boollit(true));
2042       mustBeBool = true;
2043     }
2044     Type tret = ite.elseExpr()->type();
2045     std::vector<AnonVar*> anons;
2046     bool allpar = !(tret.isvar());
2047     if (tret.isunknown()) {
2048       if (auto* av = ite.elseExpr()->dynamicCast<AnonVar>()) {
2049         allpar = false;
2050         anons.push_back(av);
2051       } else {
2052         throw TypeError(_env, ite.elseExpr()->loc(),
2053                         "cannot infer type of expression in `else' branch of conditional");
2054       }
2055     }
2056     bool allpresent = !(tret.isOpt());
2057     bool varcond = false;
2058     for (int i = 0; i < ite.size(); i++) {
2059       Expression* eif = ite.ifExpr(i);
2060       Expression* ethen = ite.thenExpr(i);
2061       varcond = varcond || (eif->type() == Type::varbool());
2062       if (eif->type() != Type::parbool() && eif->type() != Type::varbool()) {
2063         throw TypeError(
2064             _env, eif->loc(),
2065             "expected bool conditional expression, got `" + eif->type().toString(_env) + "'");
2066       }
2067       if (eif->type().cv()) {
2068         tret.cv(true);
2069       }
2070       if (ethen->type().isunknown()) {
2071         if (auto* av = ethen->dynamicCast<AnonVar>()) {
2072           allpar = false;
2073           anons.push_back(av);
2074         } else {
2075           throw TypeError(_env, ethen->loc(),
2076                           "cannot infer type of expression in `then' branch of conditional");
2077         }
2078       } else {
2079         if (tret.isbot() || tret.isunknown()) {
2080           tret.bt(ethen->type().bt());
2081         }
2082         if (mustBeBool &&
2083             (ethen->type().bt() != Type::BT_BOOL || ethen->type().dim() > 0 ||
2084              ethen->type().st() != Type::ST_PLAIN || ethen->type().ot() != Type::OT_PRESENT)) {
2085           throw TypeError(_env, ite.loc(),
2086                           std::string("conditional without `else' branch must have bool type, ") +
2087                               "but `then' branch has type `" + ethen->type().toString(_env) + "'");
2088         }
2089         if ((!ethen->type().isbot() && !Type::btSubtype(ethen->type(), tret, true) &&
2090              !Type::btSubtype(tret, ethen->type(), true)) ||
2091             ethen->type().st() != tret.st() || ethen->type().dim() != tret.dim()) {
2092           throw TypeError(_env, ethen->loc(),
2093                           "type mismatch in branches of conditional. `then' branch has type `" +
2094                               ethen->type().toString(_env) + "', but `else' branch has type `" +
2095                               tret.toString(_env) + "'");
2096         }
2097         if (Type::btSubtype(tret, ethen->type(), true)) {
2098           tret.bt(ethen->type().bt());
2099         }
2100         if (tret.enumId() != 0 && ethen->type().enumId() == 0) {
2101           tret.enumId(0);
2102         }
2103         if (ethen->type().isvar()) {
2104           allpar = false;
2105         }
2106         if (ethen->type().isOpt()) {
2107           allpresent = false;
2108         }
2109         if (ethen->type().cv()) {
2110           tret.cv(true);
2111         }
2112       }
2113     }
2114     Type tret_var(tret);
2115     tret_var.ti(Type::TI_VAR);
2116     for (auto& anon : anons) {
2117       anon->type(tret_var);
2118     }
2119     for (int i = 0; i < ite.size(); i++) {
2120       ite.thenExpr(i, add_coercion(_env, _model, ite.thenExpr(i), tret)());
2121     }
2122     ite.elseExpr(add_coercion(_env, _model, ite.elseExpr(), tret)());
2123     /// TODO: perhaps extend flattener to array types, but for now throw an error
2124     if (varcond && tret.dim() > 0) {
2125       throw TypeError(_env, ite.loc(), "conditional with var condition cannot have array type");
2126     }
2127     if (varcond || !allpar) {
2128       tret.ti(Type::TI_VAR);
2129     }
2130     if (!allpresent) {
2131       tret.ot(Type::OT_OPTIONAL);
2132     }
2133     ite.type(tret);
2134   }
2135   /// Visit binary operator
vBinOp(BinOp & bop)2136   void vBinOp(BinOp& bop) {
2137     std::vector<Expression*> args(2);
2138     args[0] = bop.lhs();
2139     args[1] = bop.rhs();
2140     if (FunctionI* fi = _model->matchFn(_env, bop.opToString(), args, true)) {
2141       bop.lhs(add_coercion(_env, _model, bop.lhs(), fi->argtype(_env, args, 0))());
2142       bop.rhs(add_coercion(_env, _model, bop.rhs(), fi->argtype(_env, args, 1))());
2143       args[0] = bop.lhs();
2144       args[1] = bop.rhs();
2145       Type ty = fi->rtype(_env, args, true);
2146       ty.cv(bop.lhs()->type().cv() || bop.rhs()->type().cv() || ty.cv());
2147       bop.type(ty);
2148 
2149       if (fi->e() != nullptr) {
2150         bop.decl(fi);
2151       } else {
2152         bop.decl(nullptr);
2153       }
2154 
2155       if (bop.lhs()->type().isint() && bop.rhs()->type().isint() &&
2156           (bop.op() == BOT_EQ || bop.op() == BOT_GQ || bop.op() == BOT_GR || bop.op() == BOT_NQ ||
2157            bop.op() == BOT_LE || bop.op() == BOT_LQ)) {
2158         Call* call = bop.lhs()->dynamicCast<Call>();
2159         Expression* rhs = bop.rhs();
2160         BinOpType bot = bop.op();
2161         if (call == nullptr) {
2162           call = bop.rhs()->dynamicCast<Call>();
2163           rhs = bop.lhs();
2164           switch (bop.op()) {
2165             case BOT_LQ:
2166               bot = BOT_GQ;
2167               break;
2168             case BOT_LE:
2169               bot = BOT_GR;
2170               break;
2171             case BOT_GQ:
2172               bot = BOT_LQ;
2173               break;
2174             case BOT_GR:
2175               bot = BOT_LE;
2176               break;
2177             default:
2178               break;
2179           }
2180         }
2181         if ((call != nullptr) && (call->id() == "count" || call->id() == "sum") &&
2182             call->type().isvar()) {
2183           if (call->argCount() == 1 && call->arg(0)->isa<Comprehension>()) {
2184             auto* comp = call->arg(0)->cast<Comprehension>();
2185             auto* inner_bo = comp->e()->dynamicCast<BinOp>();
2186             if (inner_bo != nullptr) {
2187               if (inner_bo->op() == BOT_EQ && inner_bo->lhs()->type().isint()) {
2188                 Expression* generated = inner_bo->lhs();
2189                 Expression* comparedTo = inner_bo->rhs();
2190                 if (comp->containsBoundVariable(comparedTo)) {
2191                   if (comp->containsBoundVariable(generated)) {
2192                     comparedTo = nullptr;
2193                   } else {
2194                     std::swap(generated, comparedTo);
2195                   }
2196                 }
2197                 if (comparedTo != nullptr) {
2198                   GCLock lock;
2199                   ASTString cid;
2200                   switch (bot) {
2201                     case BOT_EQ:
2202                       cid = ASTString("count_eq");
2203                       break;
2204                     case BOT_GQ:
2205                       cid = ASTString("count_leq");
2206                       break;
2207                     case BOT_GR:
2208                       cid = ASTString("count_lt");
2209                       break;
2210                     case BOT_LQ:
2211                       cid = ASTString("count_geq");
2212                       break;
2213                     case BOT_LE:
2214                       cid = ASTString("count_gt");
2215                       break;
2216                     case BOT_NQ:
2217                       cid = ASTString("count_neq");
2218                       break;
2219                     default:
2220                       assert(false);
2221                   }
2222 
2223                   comp->e(generated);
2224                   Type ct = comp->type();
2225                   ct.bt(generated->type().bt());
2226                   comp->type(ct);
2227 
2228                   std::vector<Expression*> args({comp, comparedTo, rhs});
2229                   FunctionI* newCall_decl = _model->matchFn(_env, cid, args, true);
2230                   if (newCall_decl == nullptr) {
2231                     std::ostringstream ss;
2232                     ss << "could not replace binary operator by call to " << cid;
2233                     throw InternalError(ss.str());
2234                   }
2235                   Call* newCall = bop.morph(cid, args);
2236                   newCall->decl(newCall_decl);
2237                 }
2238               }
2239             }
2240           } else if (call->argCount() == 2 && call->arg(0)->type().isIntArray() &&
2241                      call->arg(1)->type().isint()) {
2242             GCLock lock;
2243             ASTString cid;
2244             switch (bot) {
2245               case BOT_EQ:
2246                 cid = ASTString("count_eq");
2247                 break;
2248               case BOT_GQ:
2249                 cid = ASTString("count_leq");
2250                 break;
2251               case BOT_GR:
2252                 cid = ASTString("count_lt");
2253                 break;
2254               case BOT_LQ:
2255                 cid = ASTString("count_geq");
2256                 break;
2257               case BOT_LE:
2258                 cid = ASTString("count_gt");
2259                 break;
2260               case BOT_NQ:
2261                 cid = ASTString("count_neq");
2262                 break;
2263               default:
2264                 assert(false);
2265             }
2266             std::vector<Expression*> args({call->arg(0), call->arg(1), rhs});
2267             FunctionI* newCall_decl = _model->matchFn(_env, cid, args, true);
2268             if (newCall_decl == nullptr) {
2269               std::ostringstream ss;
2270               ss << "could not replace binary operator by call to " << cid;
2271               throw InternalError(ss.str());
2272             }
2273             Call* newCall = bop.morph(cid, args);
2274             newCall->decl(newCall_decl);
2275           }
2276         }
2277       }
2278     } else {
2279       std::ostringstream ss;
2280       ss << "type error in operator application for `" << bop.opToString()
2281          << "'. No matching operator found with left-hand side type `"
2282          << bop.lhs()->type().toString(_env) << "' and right-hand side type `"
2283          << bop.rhs()->type().toString(_env) << "'";
2284       throw TypeError(_env, bop.loc(), ss.str());
2285     }
2286   }
2287   /// Visit unary operator
vUnOp(UnOp & uop)2288   void vUnOp(UnOp& uop) {
2289     std::vector<Expression*> args(1);
2290     args[0] = uop.e();
2291     if (FunctionI* fi = _model->matchFn(_env, uop.opToString(), args, true)) {
2292       uop.e(add_coercion(_env, _model, uop.e(), fi->argtype(_env, args, 0))());
2293       args[0] = uop.e();
2294       Type ty = fi->rtype(_env, args, true);
2295       ty.cv(uop.e()->type().cv() || ty.cv());
2296       uop.type(ty);
2297       if (fi->e() != nullptr) {
2298         uop.decl(fi);
2299       }
2300     } else {
2301       std::ostringstream ss;
2302       ss << "type error in operator application for `" << uop.opToString()
2303          << "'. No matching operator found with type `" << uop.e()->type().toString(_env) << "'";
2304       throw TypeError(_env, uop.loc(), ss.str());
2305     }
2306   }
2307 
2308   /// Visit call
vCall(Call & call)2309   void vCall(Call& call) {
2310     std::vector<Expression*> args(call.argCount());
2311     for (auto i = static_cast<unsigned int>(args.size()); (i--) != 0U;) {
2312       args[i] = call.arg(i);
2313     }
2314     FunctionI* fi = _model->matchFn(_env, &call, true, true);
2315 
2316     if (fi != nullptr && fi->id() == "symmetry_breaking_constraint" && fi->params().size() == 1 &&
2317         fi->params()[0]->type().isbool()) {
2318       GCLock lock;
2319       call.id(ASTString("mzn_symmetry_breaking_constraint"));
2320       fi = _model->matchFn(_env, &call, true, true);
2321     } else if (fi != nullptr && fi->id() == "redundant_constraint" && fi->params().size() == 1 &&
2322                fi->params()[0]->type().isbool()) {
2323       GCLock lock;
2324       call.id(ASTString("mzn_redundant_constraint"));
2325       fi = _model->matchFn(_env, &call, true, true);
2326     }
2327 
2328     if ((fi->e() != nullptr) && fi->e()->isa<Call>()) {
2329       Call* next_call = fi->e()->cast<Call>();
2330       if ((next_call->decl() != nullptr) && next_call->argCount() == fi->params().size() &&
2331           _model->sameOverloading(_env, args, fi, next_call->decl())) {
2332         bool macro = true;
2333         for (unsigned int i = 0; i < fi->params().size(); i++) {
2334           if (!Expression::equal(next_call->arg(i), fi->params()[i]->id())) {
2335             macro = false;
2336             break;
2337           }
2338         }
2339         if (macro) {
2340           // Call is not a macro if it has a reification implementation
2341           GCLock lock;
2342           ASTString reif_id = _env.reifyId(fi->id());
2343           std::vector<Type> tt(fi->params().size() + 1);
2344           for (unsigned int i = 0; i < fi->params().size(); i++) {
2345             tt[i] = fi->params()[i]->type();
2346           }
2347           tt[fi->params().size()] = Type::varbool();
2348 
2349           macro = _model->matchFn(_env, reif_id, tt, true) == nullptr;
2350         }
2351         if (macro) {
2352           call.decl(next_call->decl());
2353           for (ExpressionSetIter esi = next_call->ann().begin(); esi != next_call->ann().end();
2354                ++esi) {
2355             call.addAnnotation(*esi);
2356           }
2357           call.rehash();
2358           fi = next_call->decl();
2359         }
2360       }
2361     }
2362 
2363     bool cv = false;
2364     for (unsigned int i = 0; i < args.size(); i++) {
2365       if (auto* c = call.arg(i)->dynamicCast<Comprehension>()) {
2366         Type t_before = c->e()->type();
2367         Type t = fi->argtype(_env, args, i);
2368         t.dim(0);
2369         c->e(add_coercion(_env, _model, c->e(), t)());
2370         Type t_after = c->e()->type();
2371         if (t_before != t_after) {
2372           Type ct = c->type();
2373           ct.bt(t_after.bt());
2374           c->type(ct);
2375         }
2376       } else {
2377         args[i] = add_coercion(_env, _model, call.arg(i), fi->argtype(_env, args, i))();
2378         call.arg(i, args[i]);
2379       }
2380       cv = cv || args[i]->type().cv();
2381     }
2382     // Replace par enums with their string versions
2383     if (call.id() == "format" || call.id() == "show" || call.id() == "showDzn" ||
2384         call.id() == "showJSON") {
2385       if (call.arg(call.argCount() - 1)->type().isPar()) {
2386         unsigned int enumId = call.arg(call.argCount() - 1)->type().enumId();
2387         if (enumId != 0U && call.arg(call.argCount() - 1)->type().dim() != 0) {
2388           const std::vector<unsigned int>& enumIds = _env.getArrayEnum(enumId);
2389           enumId = enumIds[enumIds.size() - 1];
2390         }
2391         if (enumId > 0) {
2392           VarDecl* enumDecl = _env.getEnum(enumId)->e();
2393           if (enumDecl->e() != nullptr) {
2394             Id* ti_id = _env.getEnum(enumId)->e()->id();
2395             GCLock lock;
2396             std::vector<Expression*> args(3);
2397             args[0] = call.arg(call.argCount() - 1);
2398             if (args[0]->type().dim() > 1) {
2399               std::vector<Expression*> a1dargs(1);
2400               a1dargs[0] = args[0];
2401               Call* array1d = new Call(Location().introduce(), ASTString("array1d"), a1dargs);
2402               Type array1dt = args[0]->type();
2403               array1dt.dim(1);
2404               array1d->type(array1dt);
2405               args[0] = array1d;
2406             }
2407             args[1] = constants().boollit(call.id() == "showDzn");
2408             args[2] = constants().boollit(call.id() == "showJSON");
2409             ASTString enumName(create_enum_to_string_name(ti_id, "_toString_"));
2410             call.id(enumName);
2411             call.args(args);
2412             if (call.id() == "showDzn") {
2413               call.id(constants().ids.show);
2414             }
2415             fi = _model->matchFn(_env, &call, false, true);
2416           }
2417         }
2418       }
2419     }
2420 
2421     // Set type and decl
2422     Type ty = fi->rtype(_env, args, true);
2423     ty.cv(cv || ty.cv());
2424     call.type(ty);
2425 
2426     if (Call* deprecated = fi->ann().getCall(constants().ann.mzn_deprecated)) {
2427       // rewrite this call into a call to mzn_deprecate(..., e)
2428       GCLock lock;
2429       std::vector<Expression*> params(call.argCount());
2430       for (unsigned int i = 0; i < params.size(); i++) {
2431         params[i] = call.arg(i);
2432       }
2433       Call* origCall = new Call(call.loc(), call.id(), params);
2434       origCall->type(ty);
2435       origCall->decl(fi);
2436       call.id(constants().ids.mzn_deprecate);
2437       std::vector<Expression*> args(
2438           {new StringLit(Location(), fi->id()), deprecated->arg(0), deprecated->arg(1), origCall});
2439       call.args(args);
2440       FunctionI* deprecated_fi = _model->matchFn(_env, &call, false, true);
2441       call.decl(deprecated_fi);
2442     } else {
2443       call.decl(fi);
2444     }
2445   }
2446   /// Visit let
vLet(Let & let)2447   void vLet(Let& let) {
2448     bool cv = false;
2449     bool isVar = false;
2450     for (unsigned int i = 0, j = 0; i < let.let().size(); i++) {
2451       Expression* li = let.let()[i];
2452       cv = cv || li->type().cv();
2453       if (auto* vdi = li->dynamicCast<VarDecl>()) {
2454         if (vdi->e() == nullptr && vdi->type().isSet() && vdi->type().isvar() &&
2455             vdi->ti()->domain() == nullptr) {
2456           std::ostringstream ss;
2457           ss << "set element type for `" << vdi->id()->str() << "' is not finite";
2458           _typeErrors.emplace_back(_env, vdi->loc(), ss.str());
2459         }
2460         if (vdi->type().isPar() && (vdi->e() == nullptr)) {
2461           std::ostringstream ss;
2462           ss << "let variable `" << vdi->id()->v() << "' must be initialised";
2463           throw TypeError(_env, vdi->loc(), ss.str());
2464         }
2465         if (vdi->ti()->hasTiVariable()) {
2466           std::ostringstream ss;
2467           ss << "type-inst variables not allowed in type-inst for let variable `"
2468              << vdi->id()->str() << "'";
2469           _typeErrors.emplace_back(_env, vdi->loc(), ss.str());
2470         }
2471         let.letOrig()[j++] = vdi->e();
2472         for (unsigned int k = 0; k < vdi->ti()->ranges().size(); k++) {
2473           let.letOrig()[j++] = vdi->ti()->ranges()[k]->domain();
2474         }
2475       }
2476       isVar |= li->type().isvar();
2477     }
2478     Type ty = let.in()->type();
2479     ty.cv(cv || ty.cv());
2480     if (isVar && ty.bt() == Type::BT_BOOL && ty.dim() == 0) {
2481       ty.ti(Type::TI_VAR);
2482     }
2483     let.type(ty);
2484   }
2485   /// Visit variable declaration
vVarDecl(VarDecl & vd)2486   void vVarDecl(VarDecl& vd) {
2487     if (ignoreVarDecl) {
2488       if (vd.e() != nullptr) {
2489         Type vdt = vd.ti()->type();
2490         Type vet = vd.e()->type();
2491         if (vdt.enumId() != 0 && vdt.dim() > 0 &&
2492             (vd.e()->isa<ArrayLit>() || vd.e()->isa<Comprehension>() ||
2493              (vd.e()->isa<BinOp>() && vd.e()->cast<BinOp>()->op() == BOT_PLUSPLUS))) {
2494           // Special case: index sets of array literals and comprehensions automatically
2495           // coerce to any enum index set
2496           const std::vector<unsigned int>& enumIds = _env.getArrayEnum(vdt.enumId());
2497           if (enumIds[enumIds.size() - 1] == 0) {
2498             vdt.enumId(0);
2499           } else {
2500             std::vector<unsigned int> nEnumIds(enumIds.size());
2501             for (unsigned int i = 0; i < nEnumIds.size() - 1; i++) {
2502               nEnumIds[i] = 0;
2503             }
2504             nEnumIds[nEnumIds.size() - 1] = enumIds[enumIds.size() - 1];
2505             vdt.enumId(_env.registerArrayEnum(nEnumIds));
2506           }
2507         } else if (vd.ti()->isEnum() && vd.e()->isa<Call>()) {
2508           if (vd.e()->cast<Call>()->id() == "anon_enum") {
2509             vet.enumId(vdt.enumId());
2510           }
2511         }
2512 
2513         if (vd.type().isunknown()) {
2514           vd.ti()->type(vet);
2515           vd.type(vet);
2516         } else if (!_env.isSubtype(vet, vdt, true)) {
2517           if (vet == Type::bot(1) && vd.e()->isa<ArrayLit>() &&
2518               vd.e()->cast<ArrayLit>()->size() == 0 &&
2519               vdt.dim() != 0) {  // NOLINT(bugprone-branch-clone): see TODO in other branch
2520             // this is okay: assigning an empty array (one-dimensional) to an array variable
2521           } else if (vd.ti()->isEnum() && vet == Type::parsetint()) {
2522             // let's ignore this for now (TODO: add an annotation to make sure only
2523             // compiler-generated ones are accepted)
2524           } else {
2525             const Location& loc = vd.e()->loc().isNonAlloc() ? vd.loc() : vd.e()->loc();
2526             std::ostringstream ss;
2527             ss << "initialisation value for `" << vd.id()->str()
2528                << "' has invalid type-inst: expected `" << vd.ti()->type().toString(_env)
2529                << "', actual `" << vd.e()->type().toString(_env) << "'";
2530             _typeErrors.emplace_back(_env, loc, ss.str());
2531           }
2532         } else {
2533           vd.e(add_coercion(_env, _model, vd.e(), vd.ti()->type())());
2534         }
2535       } else {
2536         assert(!vd.type().isunknown());
2537       }
2538     } else {
2539       vd.type(vd.ti()->type());
2540       vd.id()->type(vd.type());
2541     }
2542   }
2543   /// Visit type inst
vTypeInst(TypeInst & ti)2544   void vTypeInst(TypeInst& ti) {
2545     Type tt = ti.type();
2546     bool foundEnum =
2547         ti.ranges().size() > 0 && (ti.domain() != nullptr) && ti.domain()->type().enumId() != 0;
2548     if (ti.ranges().size() > 0) {
2549       bool foundTIId = false;
2550       for (unsigned int i = 0; i < ti.ranges().size(); i++) {
2551         TypeInst* ri = ti.ranges()[i];
2552         assert(ri != nullptr);
2553         if (ri->type().cv()) {
2554           tt.cv(true);
2555         }
2556         if (ri->type().enumId() != 0) {
2557           foundEnum = true;
2558         }
2559         if (ri->type() == Type::top()) {
2560           //            if (foundTIId) {
2561           //              throw TypeError(_env,ri->loc(),
2562           //                "only one type-inst variable allowed in array index");
2563           //            } else {
2564           foundTIId = true;
2565           //            }
2566         } else if (ri->type() != Type::parint()) {
2567           assert(ri->isa<TypeInst>());
2568           auto* riti = ri->cast<TypeInst>();
2569           if (riti->domain() != nullptr) {
2570             throw TypeError(_env, ri->loc(),
2571                             "array index set expression has invalid type, expected `set of int', "
2572                             "actual `set of " +
2573                                 ri->type().toString(_env) + "'");
2574           }
2575           throw TypeError(_env, ri->loc(),
2576                           "cannot use `" + ri->type().toString(_env) +
2577                               "' as array index set (did you mean `int'?)");
2578         }
2579       }
2580       tt.dim(foundTIId ? -1 : static_cast<int>(ti.ranges().size()));
2581     }
2582     if ((ti.domain() != nullptr) && ti.domain()->type().cv()) {
2583       tt.cv(true);
2584     }
2585     if (ti.domain() != nullptr) {
2586       if (TIId* tiid = ti.domain()->dynamicCast<TIId>()) {
2587         if (tiid->isEnum()) {
2588           tt.bt(Type::BT_INT);
2589         }
2590       } else {
2591         if (ti.domain()->type().ti() != Type::TI_PAR || ti.domain()->type().st() != Type::ST_SET) {
2592           throw TypeError(
2593               _env, ti.domain()->loc().isNonAlloc() ? ti.loc() : ti.domain()->loc(),
2594               "type-inst must be par set but is `" + ti.domain()->type().toString(_env) + "'");
2595         }
2596         if (ti.domain()->type().dim() != 0) {
2597           throw TypeError(_env, ti.domain()->loc(), "type-inst cannot be an array");
2598         }
2599       }
2600     }
2601     if (tt.isunknown() && (ti.domain() != nullptr)) {
2602       assert(ti.domain());
2603       switch (ti.domain()->type().bt()) {
2604         case Type::BT_INT:
2605         case Type::BT_FLOAT:
2606           break;
2607         case Type::BT_BOT: {
2608           Type tidt = ti.domain()->type();
2609           tidt.bt(Type::BT_INT);
2610           ti.domain()->type(tidt);
2611         } break;
2612         default:
2613           throw TypeError(_env, ti.domain()->loc(), "type-inst must be int or float");
2614       }
2615       tt.bt(ti.domain()->type().bt());
2616       tt.enumId(ti.domain()->type().enumId());
2617     } else {
2618       //        assert(ti.domain()==NULL || ti.domain()->isa<TIId>());
2619     }
2620     if (foundEnum) {
2621       std::vector<unsigned int> enumIds(ti.ranges().size() + 1);
2622       for (unsigned int i = 0; i < ti.ranges().size(); i++) {
2623         enumIds[i] = ti.ranges()[i]->type().enumId();
2624       }
2625       enumIds[ti.ranges().size()] = ti.domain() != nullptr ? ti.domain()->type().enumId() : 0;
2626       int arrayEnumId = _env.registerArrayEnum(enumIds);
2627       tt.enumId(arrayEnumId);
2628     }
2629 
2630     if (tt.st() == Type::ST_SET && tt.ti() == Type::TI_VAR && tt.bt() != Type::BT_INT &&
2631         tt.bt() != Type::BT_TOP) {
2632       throw TypeError(_env, ti.loc(), "var set element types other than `int' not allowed");
2633     }
2634     ti.type(tt);
2635   }
vTIId(TIId & id)2636   void vTIId(TIId& id) {}
2637 };
2638 
typecheck(Env & env,Model * origModel,std::vector<TypeError> & typeErrors,bool ignoreUndefinedParameters,bool allowMultiAssignment,bool isFlatZinc)2639 void typecheck(Env& env, Model* origModel, std::vector<TypeError>& typeErrors,
2640                bool ignoreUndefinedParameters, bool allowMultiAssignment, bool isFlatZinc) {
2641   Model* m;
2642   if (!isFlatZinc && origModel == env.model()) {
2643     // Combine all items into single model
2644     auto* combinedModel = new Model;
2645     class Combiner : public ItemVisitor {
2646     public:
2647       Model* m;
2648       Combiner(Model* m0) : m(m0) {}
2649       bool enter(Item* i) const {
2650         if (!i->isa<IncludeI>()) {
2651           m->addItem(i);
2652         }
2653         return true;
2654       }
2655     } _combiner(combinedModel);
2656     iter_items(_combiner, origModel);
2657     env.envi().originalModel = origModel;
2658     env.envi().model = combinedModel;
2659     m = combinedModel;
2660   } else {
2661     m = origModel;
2662   }
2663 
2664   // Topological sorting
2665   TopoSorter ts(m);
2666 
2667   std::vector<FunctionI*> functionItems;
2668   std::vector<AssignI*> assignItems;
2669   auto* enumItems = new Model;
2670 
2671   class TSVFuns : public ItemVisitor {
2672   public:
2673     EnvI& env;
2674     Model* model;
2675     std::vector<FunctionI*>& fis;
2676     TSVFuns(EnvI& env0, Model* model0, std::vector<FunctionI*>& fis0)
2677         : env(env0), model(model0), fis(fis0) {}
2678     void vFunctionI(FunctionI* i) {
2679       (void)model->registerFn(env, i);
2680       fis.push_back(i);
2681     }
2682   } _tsvf(env.envi(), m, functionItems);
2683   iter_items(_tsvf, m);
2684 
2685   class TSV0 : public ItemVisitor {
2686   public:
2687     EnvI& env;
2688     TopoSorter& ts;
2689     Model* model;
2690     bool hadSolveItem;
2691     std::vector<AssignI*>& ais;
2692     VarDeclI* objective;
2693     Model* enumis;
2694     bool isFlatZinc;
2695     TSV0(EnvI& env0, TopoSorter& ts0, Model* model0, std::vector<AssignI*>& ais0, Model* enumis0,
2696          bool isFlatZinc0)
2697         : env(env0),
2698           ts(ts0),
2699           model(model0),
2700           hadSolveItem(false),
2701           ais(ais0),
2702           objective(nullptr),
2703           enumis(enumis0),
2704           isFlatZinc(isFlatZinc0) {}
2705     void vAssignI(AssignI* i) { ais.push_back(i); }
2706     void vVarDeclI(VarDeclI* i) {
2707       ts.add(env, i, true, enumis);
2708       // initialise new identifier counter to be larger than existing identifier
2709       if (i->e()->id()->idn() >= 0) {
2710         env.minId(i->e()->id()->idn());
2711       } else if (i->e()->id()->v().beginsWith("X_INTRODUCED_") && i->e()->id()->v().endsWith("_")) {
2712         std::string numId = i->e()->id()->v().substr(std::string("X_INTRODUCED_").size());
2713         if (!numId.empty()) {
2714           numId = numId.substr(0, numId.size() - 1);
2715           if (!numId.empty()) {
2716             int vId = -1;
2717             try {
2718               vId = std::stoi(numId);
2719             } catch (std::exception&) {
2720             }
2721             if (vId >= 0) {
2722               env.minId(vId);
2723             }
2724           }
2725         }
2726       }
2727     }
2728     void vSolveI(SolveI* si) {
2729       if (hadSolveItem) {
2730         throw TypeError(env, si->loc(), "Only one solve item allowed");
2731       }
2732       hadSolveItem = true;
2733       if (!isFlatZinc && (si->e() != nullptr)) {
2734         GCLock lock;
2735         auto* ti = new TypeInst(Location().introduce(), Type());
2736         auto* obj = new VarDecl(Location().introduce(), ti, "_objective", si->e());
2737         si->e(obj->id());
2738         objective = new VarDeclI(Location().introduce(), obj);
2739       }
2740     }
2741   } _tsv0(env.envi(), ts, m, assignItems, enumItems, isFlatZinc);
2742   iter_items(_tsv0, m);
2743   if (_tsv0.objective != nullptr) {
2744     m->addItem(_tsv0.objective);
2745     ts.add(env.envi(), _tsv0.objective, true, enumItems);
2746   }
2747 
2748   for (unsigned int i = 0; i < enumItems->size(); i++) {
2749     if (auto* ai = (*enumItems)[i]->dynamicCast<AssignI>()) {
2750       assignItems.push_back(ai);
2751     } else if (auto* vdi = (*enumItems)[i]->dynamicCast<VarDeclI>()) {
2752       m->addItem(vdi);
2753       ts.add(env.envi(), vdi, false, enumItems);
2754     } else {
2755       auto* fi = (*enumItems)[i]->dynamicCast<FunctionI>();
2756       m->addItem(fi);
2757       (void)m->registerFn(env.envi(), fi);
2758       functionItems.push_back(fi);
2759     }
2760   }
2761 
2762   auto* enumItems2 = new Model;
2763 
2764   for (auto* ai : assignItems) {
2765     VarDecl* vd = nullptr;
2766     if (env.envi().ignoreUnknownIds) {
2767       try {
2768         vd = ts.get(env.envi(), ai->id(), ai->loc());
2769       } catch (TypeError&) {
2770       }
2771     } else {
2772       vd = ts.get(env.envi(), ai->id(), ai->loc());
2773     }
2774     if (vd != nullptr) {
2775       if (vd->e() != nullptr) {
2776         if (allowMultiAssignment) {
2777           GCLock lock;
2778           m->addItem(new ConstraintI(
2779               ai->loc(),
2780               new BinOp(ai->loc(), new Id(Location().introduce(), ai->id(), vd), BOT_EQ, ai->e())));
2781         } else {
2782           throw TypeError(env.envi(), ai->loc(), "multiple assignment to the same variable");
2783         }
2784       } else {
2785         vd->e(ai->e());
2786         vd->ann().add(constants().ann.rhs_from_assignment);
2787         if (vd->ti()->isEnum()) {
2788           create_enum_mapper(env.envi(), m, vd->ti()->type().enumId(), vd, enumItems2);
2789         }
2790       }
2791     }
2792     ai->remove();
2793   }
2794 
2795   for (auto& i : *enumItems2) {
2796     if (auto* vdi = i->dynamicCast<VarDeclI>()) {
2797       m->addItem(vdi);
2798       ts.add(env.envi(), vdi, false, enumItems);
2799     } else {
2800       auto* fi = i->cast<FunctionI>();
2801       m->addItem(fi);
2802       (void)m->registerFn(env.envi(), fi);
2803       functionItems.push_back(fi);
2804     }
2805   }
2806 
2807   delete enumItems;
2808   delete enumItems2;
2809 
2810   class TSV1 : public ItemVisitor {
2811   public:
2812     EnvI& env;
2813     TopoSorter& ts;
2814     TSV1(EnvI& env0, TopoSorter& ts0) : env(env0), ts(ts0) {}
2815     void vVarDeclI(VarDeclI* i) { ts.run(env, i->e()); }
2816     void vAssignI(AssignI* i) {}
2817     void vConstraintI(ConstraintI* i) { ts.run(env, i->e()); }
2818     void vSolveI(SolveI* i) {
2819       for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) {
2820         ts.run(env, *it);
2821       }
2822       ts.run(env, i->e());
2823     }
2824     void vOutputI(OutputI* i) { ts.run(env, i->e()); }
2825     void vFunctionI(FunctionI* fi) {
2826       ts.run(env, fi->ti());
2827       for (unsigned int i = 0; i < fi->params().size(); i++) {
2828         ts.run(env, fi->params()[i]);
2829       }
2830       for (ExpressionSetIter it = fi->ann().begin(); it != fi->ann().end(); ++it) {
2831         ts.run(env, *it);
2832       }
2833       ts.scopes.pushFun();
2834       for (unsigned int i = 0; i < fi->params().size(); i++) {
2835         ts.scopes.add(env, fi->params()[i]);
2836       }
2837       ts.run(env, fi->e());
2838       ts.scopes.pop();
2839     }
2840   } _tsv1(env.envi(), ts);
2841   iter_items(_tsv1, m);
2842 
2843   m->sortFn();
2844 
2845   {
2846     struct SortByPayload {
2847       bool operator()(Item* i0, Item* i1) {
2848         if (i0->isa<IncludeI>()) {
2849           return !i1->isa<IncludeI>();
2850         }
2851         if (auto* vdi0 = i0->dynamicCast<VarDeclI>()) {
2852           if (auto* vdi1 = i1->dynamicCast<VarDeclI>()) {
2853             return vdi0->e()->payload() < vdi1->e()->payload();
2854           }
2855           return !i1->isa<IncludeI>();
2856         }
2857         return false;
2858       }
2859     } _sbp;
2860 
2861     std::stable_sort(m->begin(), m->end(), _sbp);
2862   }
2863 
2864   {
2865     Typer<false> ty(env.envi(), m, typeErrors, ignoreUndefinedParameters);
2866     BottomUpIterator<Typer<false>> bottomUpTyper(ty);
2867     for (auto& decl : ts.decls) {
2868       decl->payload(0);
2869       bottomUpTyper.run(decl->ti());
2870       ty.vVarDecl(*decl);
2871     }
2872     for (auto& functionItem : functionItems) {
2873       bottomUpTyper.run(functionItem->ti());
2874       for (unsigned int j = 0; j < functionItem->params().size(); j++) {
2875         bottomUpTyper.run(functionItem->params()[j]);
2876       }
2877     }
2878   }
2879 
2880   m->fixFnMap();
2881 
2882   {
2883     Typer<true> ty(env.envi(), m, typeErrors, ignoreUndefinedParameters);
2884     BottomUpIterator<Typer<true>> bottomUpTyper(ty);
2885 
2886     class TSV2 : public ItemVisitor {
2887     private:
2888       EnvI& _env;
2889       Model* _m;
2890       BottomUpIterator<Typer<true>>& _bottomUpTyper;
2891       std::vector<TypeError>& _typeErrors;
2892 
2893     public:
2894       TSV2(EnvI& env0, Model* m0, BottomUpIterator<Typer<true>>& b,
2895            std::vector<TypeError>& typeErrors)
2896           : _env(env0), _m(m0), _bottomUpTyper(b), _typeErrors(typeErrors) {}
2897       void vVarDeclI(VarDeclI* i) {
2898         _bottomUpTyper.run(i->e());
2899         if (i->e()->ti()->hasTiVariable()) {
2900           std::ostringstream ss;
2901           ss << "type-inst variables not allowed in type-inst for `" << i->e()->id()->str() << "'";
2902           _typeErrors.emplace_back(_env, i->e()->loc(), ss.str());
2903         }
2904         VarDecl* vdi = i->e();
2905         if (vdi->e() == nullptr && vdi->type().isSet() && vdi->type().isvar() &&
2906             vdi->ti()->domain() == nullptr) {
2907           std::ostringstream ss;
2908           ss << "set element type for `" << vdi->id()->str() << "' is not finite";
2909           _typeErrors.emplace_back(_env, vdi->loc(), ss.str());
2910         }
2911         if (i->e()->ann().contains(constants().ann.output_only)) {
2912           if (vdi->e() == nullptr) {
2913             _typeErrors.emplace_back(
2914                 _env, vdi->loc(),
2915                 "variables annotated with ::output_only must have a right hand side");
2916           } else if (vdi->e()->type().isvar()) {
2917             _typeErrors.emplace_back(_env, vdi->loc(),
2918                                      "variables annotated with ::output_only must be par");
2919           }
2920         }
2921       }
2922       void vAssignI(AssignI* i) {
2923         _bottomUpTyper.run(i->e());
2924         if (!_env.isSubtype(i->e()->type(), i->decl()->ti()->type(), true)) {
2925           std::ostringstream ss;
2926           ss << "assignment value for `" << i->decl()->id()->str()
2927              << "' has invalid type-inst: expected `" << i->decl()->ti()->type().toString(_env)
2928              << "', actual `" << i->e()->type().toString(_env) << "'";
2929           _typeErrors.emplace_back(_env, i->loc(), ss.str());
2930           // Assign to "true" constant to avoid generating further errors that the parameter
2931           // is undefined
2932           i->decl()->e(constants().literalTrue);
2933         }
2934       }
2935       void vConstraintI(ConstraintI* i) {
2936         _bottomUpTyper.run(i->e());
2937         if (!_env.isSubtype(i->e()->type(), Type::varbool(), true)) {
2938           throw TypeError(_env, i->loc(),
2939                           "invalid type of constraint, expected `" +
2940                               Type::varbool().toString(_env) + "', actual `" +
2941                               i->e()->type().toString(_env) + "'");
2942         }
2943       }
2944       void vSolveI(SolveI* i) {
2945         for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) {
2946           _bottomUpTyper.run(*it);
2947           if (!(*it)->type().isAnn()) {
2948             throw TypeError(_env, (*it)->loc(),
2949                             "expected annotation, got `" + (*it)->type().toString(_env) + "'");
2950           }
2951         }
2952         _bottomUpTyper.run(i->e());
2953         if (i->e() != nullptr) {
2954           Type et = i->e()->type();
2955           if (et.isbool()) {
2956             Type target_t = Type::varint();
2957             if (et.isOpt()) {
2958               target_t.ot(Type::OT_OPTIONAL);
2959             }
2960             i->e(add_coercion(_env, _env.model, i->e(), target_t)());
2961           }
2962 
2963           bool needOptCoercion = et.isOpt() && et.isint();
2964           if (needOptCoercion) {
2965             et.ot(Type::OT_PRESENT);
2966           }
2967 
2968           if (!(_env.isSubtype(et, Type::varint(), true) ||
2969                 _env.isSubtype(et, Type::varfloat(), true))) {
2970             throw TypeError(_env, i->e()->loc(),
2971                             "objective has invalid type, expected int or float, actual `" +
2972                                 et.toString(_env) + "'");
2973           }
2974 
2975           if (needOptCoercion) {
2976             GCLock lock;
2977             std::vector<Expression*> args(2);
2978             args[0] = i->e();
2979             args[1] = constants().boollit(i->st() == SolveI::ST_MAX);
2980             Call* c = new Call(Location().introduce(), ASTString("objective_deopt_"), args);
2981             c->decl(_env.model->matchFn(_env, c, false));
2982             assert(c->decl());
2983             c->type(et);
2984             i->e(c);
2985           }
2986         }
2987       }
2988       void vOutputI(OutputI* i) {
2989         _bottomUpTyper.run(i->e());
2990         if (i->e()->type() != Type::parstring(1) && i->e()->type() != Type::bot(1)) {
2991           throw TypeError(_env, i->e()->loc(),
2992                           "invalid type in output item, expected `" +
2993                               Type::parstring(1).toString(_env) + "', actual `" +
2994                               i->e()->type().toString(_env) + "'");
2995         }
2996       }
2997       void vFunctionI(FunctionI* i) {
2998         for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) {
2999           _bottomUpTyper.run(*it);
3000           if (!(*it)->type().isAnn()) {
3001             throw TypeError(_env, (*it)->loc(),
3002                             "expected annotation, got `" + (*it)->type().toString(_env) + "'");
3003           }
3004         }
3005         _bottomUpTyper.run(i->ti());
3006         _bottomUpTyper.run(i->e());
3007         if ((i->e() != nullptr) && !_env.isSubtype(i->e()->type(), i->ti()->type(), true)) {
3008           throw TypeError(_env, i->e()->loc(),
3009                           "return type of function does not match body, declared type is `" +
3010                               i->ti()->type().toString(_env) + "', body type is `" +
3011                               i->e()->type().toString(_env) + "'");
3012         }
3013         if ((i->e() != nullptr) && i->e()->type().isPar() && i->ti()->type().isvar()) {
3014           // this is a par function declared as var, so change declared return type
3015           Type i_t = i->ti()->type();
3016           i_t.ti(Type::TI_PAR);
3017           i->ti()->type(i_t);
3018         }
3019         if (i->e() != nullptr) {
3020           i->e(add_coercion(_env, _m, i->e(), i->ti()->type())());
3021         }
3022       }
3023     } _tsv2(env.envi(), m, bottomUpTyper, typeErrors);
3024     iter_items(_tsv2, m);
3025   }
3026 
3027   class TSV3 : public ItemVisitor {
3028   public:
3029     EnvI& env;
3030     Model* m;
3031     OutputI* outputItem;
3032     TSV3(EnvI& env0, Model* m0) : env(env0), m(m0), outputItem(nullptr) {}
3033     void vAssignI(AssignI* i) { i->decl()->e(add_coercion(env, m, i->e(), i->decl()->type())()); }
3034     void vOutputI(OutputI* oi) {
3035       if (outputItem == nullptr) {
3036         outputItem = oi;
3037       } else {
3038         GCLock lock;
3039         auto* bo = new BinOp(Location().introduce(), outputItem->e(), BOT_PLUSPLUS, oi->e());
3040         bo->type(Type::parstring(1));
3041         outputItem->e(bo);
3042         oi->remove();
3043         m->setOutputItem(outputItem);
3044       }
3045     }
3046   } _tsv3(env.envi(), m);
3047   if (typeErrors.empty()) {
3048     iter_items(_tsv3, m);
3049   }
3050 
3051   // Create a par version of each function that returns par and
3052   // that has a body that can be made par
3053   std::unordered_map<FunctionI*, std::pair<bool, std::vector<FunctionI*>>> fnsToMakePar;
3054   for (auto& f : m->functions()) {
3055     if (f.id() == "mzn_reverse_map_var") {
3056       continue;
3057     }
3058     if (f.e() != nullptr && f.ti()->type().bt() != Type::BT_ANN) {
3059       bool foundVar = false;
3060       for (auto* p : f.params()) {
3061         if (p->type().isvar()) {
3062           foundVar = true;
3063           break;
3064         }
3065       }
3066       if (foundVar) {
3067         // create par version of parameter types
3068         std::vector<Type> tv;
3069         for (auto* p : f.params()) {
3070           Type t = p->type();
3071           t.cv(false);
3072           t.ti(Type::TI_PAR);
3073           tv.push_back(t);
3074         }
3075         // check if specialised par version of function already exists
3076         FunctionI* fi_par = m->matchFn(env.envi(), f.id(), tv, false);
3077         bool parIsUsable = false;
3078         if (fi_par != nullptr) {
3079           bool foundVar = false;
3080           for (auto* p : fi_par->params()) {
3081             if (p->type().isvar()) {
3082               foundVar = true;
3083               break;
3084             }
3085           }
3086           parIsUsable = !foundVar;
3087         }
3088         if (!parIsUsable) {
3089           // check if body of f doesn't contain any free variables in lets,
3090           // all calls in the body have par versions available,
3091           // and all toplevel identifiers used in the body of f are par
3092           class CheckParBody : public EVisitor {
3093           public:
3094             EnvI& env;
3095             Model* m;
3096             CheckParBody(EnvI& env0, Model* m0) : env(env0), m(m0) {}
3097             bool isPar = true;
3098             std::vector<FunctionI*> deps;
3099             bool enter(Expression* e) const {
3100               // if we have already found a var, don't continue
3101               return isPar;
3102             }
3103             void vId(const Id& ident) {
3104               if (ident.decl() != nullptr && ident.type().isvar() && ident.decl()->toplevel()) {
3105                 isPar = false;
3106               }
3107             }
3108             void vLet(const Let& let) {
3109               // check if any of the declared variables does not have a RHS
3110               for (auto* e : let.let()) {
3111                 if (auto* vd = e->dynamicCast<VarDecl>()) {
3112                   if (vd->e() == nullptr) {
3113                     isPar = false;
3114                     break;
3115                   }
3116                 }
3117               }
3118             }
3119             void vCall(const Call& c) {
3120               if (!c.type().isAnn()) {
3121                 FunctionI* decl = c.decl();
3122                 // create par version of parameter types
3123                 std::vector<Type> tv;
3124                 for (auto* p : decl->params()) {
3125                   Type t = p->type();
3126                   t.cv(false);
3127                   t.ti(Type::TI_PAR);
3128                   tv.push_back(t);
3129                 }
3130                 // check if specialised par version of function already exists
3131                 FunctionI* decl_par = m->matchFn(env, decl->id(), tv, false);
3132                 bool parIsUsable = decl_par->ti()->type().isPar();
3133                 if (parIsUsable && decl_par->e() == nullptr && decl_par->fromStdLib()) {
3134                   parIsUsable = true;
3135                 } else if (parIsUsable) {
3136                   bool foundVar = false;
3137                   for (auto* p : decl_par->params()) {
3138                     if (p->type().isvar()) {
3139                       foundVar = true;
3140                       break;
3141                     }
3142                   }
3143                   parIsUsable = !foundVar;
3144                 }
3145                 if (!parIsUsable) {
3146                   deps.push_back(decl_par);
3147                 }
3148               }
3149             }
3150           } cpb(env.envi(), m);
3151           top_down(cpb, f.e());
3152           if (cpb.isPar) {
3153             fnsToMakePar.insert({&f, {false, cpb.deps}});
3154           }
3155         } else {
3156           fnsToMakePar.insert({fi_par, {true, std::vector<FunctionI*>()}});
3157         }
3158       }
3159     }
3160   }
3161 
3162   // Repeatedly remove functions whose dependencies cannot be made par
3163   bool didRemove;
3164   do {
3165     didRemove = false;
3166     std::vector<FunctionI*> toRemove;
3167     for (auto& p : fnsToMakePar) {
3168       for (auto* dep : p.second.second) {
3169         if (fnsToMakePar.find(dep) == fnsToMakePar.end()) {
3170           toRemove.push_back(p.first);
3171         }
3172       }
3173     }
3174     if (!toRemove.empty()) {
3175       didRemove = true;
3176       for (auto* p : toRemove) {
3177         fnsToMakePar.erase(p);
3178       }
3179     }
3180   } while (didRemove);
3181 
3182   // Create par versions of remaining functions
3183   if (!fnsToMakePar.empty()) {
3184     // First step: copy and register functions
3185     std::vector<FunctionI*> parFunctions;
3186     CopyMap parCopyMap;
3187     // Step 1a: enter all global declarations into copy map
3188     class EnterGlobalDecls : public EVisitor {
3189     public:
3190       CopyMap& cm;
3191       EnterGlobalDecls(CopyMap& cm0) : cm(cm0) {}
3192       void vId(Id& ident) {
3193         if (ident.decl() != nullptr && ident.decl()->toplevel()) {
3194           cm.insert(ident.decl(), ident.decl());
3195         }
3196       }
3197     } _egd(parCopyMap);
3198     for (auto& p : fnsToMakePar) {
3199       if (!p.second.first) {
3200         for (auto* param : p.first->params()) {
3201           top_down(_egd, param);
3202         }
3203         for (ExpressionSetIter i = p.first->ann().begin(); i != p.first->ann().end(); ++i) {
3204           top_down(_egd, *i);
3205         }
3206         top_down(_egd, p.first->e());
3207       }
3208     }
3209 
3210     // Step 1b: copy functions
3211     for (auto& p : fnsToMakePar) {
3212       if (!p.second.first) {
3213         GCLock lock;
3214         auto* cp = copy(env.envi(), parCopyMap, p.first)->cast<FunctionI>();
3215         for (auto* v : cp->params()) {
3216           Type vt = v->ti()->type();
3217           vt.ti(Type::TI_PAR);
3218           v->ti()->type(vt);
3219           v->type(vt);
3220         }
3221         Type cpt(cp->ti()->type());
3222         cpt.ti(Type::TI_PAR);
3223         cp->ti()->type(cpt);
3224         bool didRegister = m->registerFn(env.envi(), cp, true, false);
3225         if (didRegister) {
3226           m->addItem(cp);
3227           parFunctions.push_back(cp);
3228         }
3229       }
3230     }
3231 
3232     // Second step: make function bodies par
3233     // (needs to happen in a separate second step so that
3234     //  matchFn will find the correct par function from first step)
3235     class MakeFnPar : public EVisitor {
3236     public:
3237       EnvI& env;
3238       Model* m;
3239       MakeFnPar(EnvI& env0, Model* m0) : env(env0), m(m0) {}
3240       static bool enter(Expression* e) {
3241         Type t(e->type());
3242         t.ti(Type::TI_PAR);
3243         t.cv(false);
3244         e->type(t);
3245         return true;
3246       }
3247       void vCall(Call& c) {
3248         FunctionI* decl = m->matchFn(env, &c, false);
3249         c.decl(decl);
3250       }
3251       void vBinOp(BinOp& bo) {
3252         if (bo.decl() != nullptr) {
3253           std::vector<Type> ta(2);
3254           ta[0] = bo.lhs()->type();
3255           ta[1] = bo.rhs()->type();
3256           FunctionI* decl = m->matchFn(env, bo.opToString(), ta, false);
3257           bo.decl(decl);
3258         }
3259       }
3260       void vUnOp(UnOp& uo) {
3261         if (uo.decl() != nullptr) {
3262           std::vector<Type> ta(1);
3263           ta[0] = uo.e()->type();
3264           FunctionI* decl = m->matchFn(env, uo.opToString(), ta, false);
3265           uo.decl(decl);
3266         }
3267       }
3268     } _mfp(env.envi(), m);
3269 
3270     for (auto* p : parFunctions) {
3271       bottom_up(_mfp, p->e());
3272     }
3273   }
3274 
3275   try {
3276     m->checkFnOverloading(env.envi());
3277   } catch (TypeError& e) {
3278     typeErrors.push_back(e);
3279   }
3280 
3281   for (auto& decl : ts.decls) {
3282     if (decl->toplevel() && decl->type().isPar() && !decl->type().isAnn() && decl->e() == nullptr) {
3283       if (decl->type().isOpt() && decl->type().dim() == 0) {
3284         decl->e(constants().absent);
3285         decl->addAnnotation(constants().ann.mzn_was_undefined);
3286       } else if (!ignoreUndefinedParameters) {
3287         std::ostringstream ss;
3288         ss << "  symbol error: variable `" << decl->id()->str()
3289            << "' must be defined (did you forget to specify a data file?)";
3290         typeErrors.emplace_back(env.envi(), decl->loc(), ss.str());
3291       }
3292     }
3293     if (decl->ti()->isEnum()) {
3294       decl->ti()->setIsEnum(false);
3295       Type vdt = decl->ti()->type();
3296       vdt.enumId(0);
3297       decl->ti()->type(vdt);
3298     }
3299   }
3300 
3301   for (auto vd_k : env.envi().checkVars) {
3302     try {
3303       VarDecl* vd;
3304       try {
3305         vd = ts.get(env.envi(), vd_k()->cast<VarDecl>()->id()->str(),
3306                     vd_k()->cast<VarDecl>()->loc());
3307       } catch (TypeError&) {
3308         if (vd_k()->cast<VarDecl>()->type().isvar()) {
3309           continue;  // var can be undefined
3310         }
3311         throw;
3312       }
3313       vd->ann().add(constants().ann.mzn_check_var);
3314       if (vd->type().enumId() != 0) {
3315         GCLock lock;
3316         std::vector<unsigned int> enumIds({vd->type().enumId()});
3317         if (vd->type().dim() > 0) {
3318           enumIds = env.envi().getArrayEnum(vd->type().enumId());
3319         }
3320         std::vector<Expression*> enumIds_a(enumIds.size());
3321         for (unsigned int i = 0; i < enumIds.size(); i++) {
3322           if (enumIds[i] != 0) {
3323             enumIds_a[i] = env.envi().getEnum(enumIds[i])->e()->id();
3324           } else {
3325             enumIds_a[i] = new SetLit(Location().introduce(), std::vector<Expression*>());
3326           }
3327         }
3328         auto* enumIds_al = new ArrayLit(Location().introduce(), enumIds_a);
3329         enumIds_al->type(Type::parsetint(1));
3330         std::vector<Expression*> args({enumIds_al});
3331         Call* checkEnum =
3332             new Call(Location().introduce(), constants().ann.mzn_check_enum_var, args);
3333         checkEnum->type(Type::ann());
3334         checkEnum->decl(env.envi().model->matchFn(env.envi(), checkEnum, false));
3335         vd->ann().add(checkEnum);
3336       }
3337       Type vdktype = vd_k()->type();
3338       vdktype.ti(Type::TI_VAR);
3339       if (!vd_k()->type().isSubtypeOf(vd->type(), false)) {
3340         std::ostringstream ss;
3341         ss << "Solution checker requires `" << vd->id()->str() << "' to be of type `"
3342            << vdktype.toString(env.envi()) << "'";
3343         typeErrors.emplace_back(env.envi(), vd->loc(), ss.str());
3344       }
3345     } catch (TypeError& e) {
3346       typeErrors.emplace_back(env.envi(), e.loc(),
3347                               e.msg() + " (required by solution checker model)");
3348     }
3349   }
3350 }
3351 
typecheck(Env & env,Model * m,AssignI * ai)3352 void typecheck(Env& env, Model* m, AssignI* ai) {
3353   std::vector<TypeError> typeErrors;
3354   Typer<true> ty(env.envi(), m, typeErrors, false);
3355   BottomUpIterator<Typer<true>> bottomUpTyper(ty);
3356   bottomUpTyper.run(ai->e());
3357   if (!typeErrors.empty()) {
3358     throw typeErrors[0];
3359   }
3360   if (!env.envi().isSubtype(ai->e()->type(), ai->decl()->ti()->type(), true)) {
3361     std::ostringstream ss;
3362     ss << "assignment value for `" << ai->decl()->id()->str()
3363        << "' has invalid type-inst: expected `" << ai->decl()->ti()->type().toString(env.envi())
3364        << "', actual `" << ai->e()->type().toString(env.envi()) << "'";
3365     throw TypeError(env.envi(), ai->e()->loc(), ss.str());
3366   }
3367 }
3368 
output_var_desc_json(Env & env,VarDecl * vd,std::ostream & os,bool extra=false)3369 void output_var_desc_json(Env& env, VarDecl* vd, std::ostream& os, bool extra = false) {
3370   os << "    \"" << *vd->id() << "\" : {";
3371   os << "\"type\" : ";
3372   switch (vd->type().bt()) {
3373     case Type::BT_INT:
3374       os << "\"int\"";
3375       break;
3376     case Type::BT_BOOL:
3377       os << "\"bool\"";
3378       break;
3379     case Type::BT_FLOAT:
3380       os << "\"float\"";
3381       break;
3382     case Type::BT_STRING:
3383       os << "\"string\"";
3384       break;
3385     case Type::BT_ANN:
3386       os << "\"ann\"";
3387       break;
3388     default:
3389       os << "\"?\"";
3390       break;
3391   }
3392   if (vd->type().ot() == Type::OT_OPTIONAL) {
3393     os << ", \"optional\" : true";
3394   }
3395   if (vd->type().st() == Type::ST_SET) {
3396     os << ", \"set\" : true";
3397   }
3398   if (vd->type().dim() > 0) {
3399     os << ", \"dim\" : " << vd->type().dim();
3400 
3401     if (extra) {
3402       os << ", \"dims\" : [";
3403       bool had_dim = false;
3404       ASTExprVec<TypeInst> ranges = vd->ti()->ranges();
3405       for (auto& range : ranges) {
3406         if (range->type().enumId() > 0) {
3407           os << (had_dim ? "," : "") << "\""
3408              << *env.envi().getEnum(range->type().enumId())->e()->id() << "\"";
3409         } else {
3410           os << (had_dim ? "," : "") << "\"int\"";
3411         }
3412         had_dim = true;
3413       }
3414       os << "]";
3415 
3416       if (vd->type().enumId() > 0) {
3417         const std::vector<unsigned int>& enumIds = env.envi().getArrayEnum(vd->type().enumId());
3418         if (enumIds.back() > 0) {
3419           os << ", \"enum_type\" : \"" << *env.envi().getEnum(enumIds.back())->e()->id() << "\"";
3420         }
3421       }
3422     }
3423 
3424   } else {
3425     if (extra) {
3426       if (vd->type().enumId() > 0) {
3427         os << ", \"enum_type\" : \"" << *env.envi().getEnum(vd->type().enumId())->e()->id() << "\"";
3428       }
3429     }
3430   }
3431   os << "}";
3432 }
3433 
output_model_variable_types(Env & env,Model * m,std::ostream & os,const std::vector<std::string> & skipDirs)3434 void output_model_variable_types(Env& env, Model* m, std::ostream& os,
3435                                  const std::vector<std::string>& skipDirs) {
3436   class VInfVisitor : public ItemVisitor {
3437   public:
3438     Env& env;
3439     const std::vector<std::string>& skipDirs;
3440     bool hadVar;
3441     bool hadEnum;
3442     std::ostringstream ossVars;
3443     std::ostringstream ossEnums;
3444     VInfVisitor(Env& env0, const std::vector<std::string>& skipDirs0)
3445         : env(env0), skipDirs(skipDirs0), hadVar(false), hadEnum(false) {}
3446     bool enter(Item* i) {
3447       if (auto* ii = i->dynamicCast<IncludeI>()) {
3448         std::string prefix =
3449             ii->m()->filepath().substr(0, ii->m()->filepath().size() - ii->f().size());
3450         for (const auto& skip_dir : skipDirs) {
3451           if (prefix.substr(0, skip_dir.size()) == skip_dir) {
3452             return false;
3453           }
3454         }
3455       }
3456       return true;
3457     }
3458     void vVarDeclI(VarDeclI* vdi) {
3459       if (!vdi->e()->type().isAnn() && !vdi->e()->ti()->isEnum()) {
3460         if (hadVar) {
3461           ossVars << ",\n";
3462         }
3463         output_var_desc_json(env, vdi->e(), ossVars, true);
3464         hadVar = true;
3465       } else if (vdi->e()->type().st() == Type::ST_SET && vdi->e()->type().enumId() != 0 &&
3466                  !vdi->e()->type().isAnn()) {
3467         if (hadEnum) {
3468           ossEnums << ", ";
3469         }
3470         ossEnums << "\"" << *env.envi().getEnum(vdi->e()->type().enumId())->e()->id() << "\"";
3471         hadEnum = true;
3472       }
3473     }
3474   } _vinf(env, skipDirs);
3475   iter_items(_vinf, m);
3476   os << "{\"var_types\": {";
3477   os << "\n  \"vars\": {\n" << _vinf.ossVars.str() << "\n  },";
3478   os << "\n  \"enums\": [" << _vinf.ossEnums.str() << "]\n";
3479   os << "}}\n";
3480 }
3481 
output_model_interface(Env & env,Model * m,std::ostream & os,const std::vector<std::string> & skipDirs)3482 void output_model_interface(Env& env, Model* m, std::ostream& os,
3483                             const std::vector<std::string>& skipDirs) {
3484   class IfcVisitor : public ItemVisitor {
3485   public:
3486     Env& env;
3487     const std::vector<std::string>& skipDirs;
3488     bool hadInput;
3489     bool hadOutput;
3490     bool hadIncludedFiles;
3491     bool hadAddToOutput = false;
3492     std::ostringstream ossInput;
3493     std::ostringstream ossOutput;
3494     std::ostringstream ossIncludedFiles;
3495     std::string method;
3496     bool outputItem;
3497     IfcVisitor(Env& env0, const std::vector<std::string>& skipDirs0)
3498         : env(env0),
3499           skipDirs(skipDirs0),
3500           hadInput(false),
3501           hadOutput(false),
3502           hadIncludedFiles(false),
3503           method("sat"),
3504           outputItem(false) {}
3505     bool enter(Item* i) {
3506       if (auto* ii = i->dynamicCast<IncludeI>()) {
3507         std::string prefix =
3508             ii->m()->filepath().substr(0, ii->m()->filepath().size() - ii->f().size());
3509         for (const auto& skip_dir : skipDirs) {
3510           if (prefix.substr(0, skip_dir.size()) == skip_dir) {
3511             return false;
3512           }
3513         }
3514         if (hadIncludedFiles) {
3515           ossIncludedFiles << ",\n";
3516         }
3517         ossIncludedFiles << "    \"" << Printer::escapeStringLit(ii->m()->filepath()) << "\"";
3518         hadIncludedFiles = true;
3519       }
3520       return true;
3521     }
3522     void vVarDeclI(VarDeclI* vdi) {
3523       VarDecl* vd = vdi->e();
3524       if (vd->type().isPar() && !vd->type().isAnn() &&
3525           (vd->e() == nullptr || (vd->e() == constants().absent &&
3526                                   vd->ann().contains(constants().ann.mzn_was_undefined)))) {
3527         if (hadInput) {
3528           ossInput << ",\n";
3529         }
3530         output_var_desc_json(env, vd, ossInput);
3531         hadInput = true;
3532       } else {
3533         bool process_var = false;
3534         if (vd->ann().contains(constants().ann.add_to_output)) {
3535           if (!hadAddToOutput) {
3536             ossOutput.str("");
3537             hadOutput = false;
3538           }
3539           hadAddToOutput = true;
3540           process_var = true;
3541         } else if (!hadAddToOutput) {
3542           process_var =
3543               vd->type().isvar() &&
3544               (vd->e() == nullptr || vd->ann().contains(constants().ann.rhs_from_assignment));
3545         }
3546         if (process_var) {
3547           if (hadOutput) {
3548             ossOutput << ",\n";
3549           }
3550           output_var_desc_json(env, vd, ossOutput);
3551           hadOutput = true;
3552         }
3553       }
3554     }
3555     void vSolveI(SolveI* si) {
3556       switch (si->st()) {
3557         case SolveI::ST_MIN:
3558           method = "min";
3559           break;
3560         case SolveI::ST_MAX:
3561           method = "max";
3562           break;
3563         case SolveI::ST_SAT:
3564           method = "sat";
3565           break;
3566       }
3567     }
3568     void vOutputI(OutputI* oi) { outputItem = true; }
3569   } _ifc(env, skipDirs);
3570   iter_items(_ifc, m);
3571   os << "{\n  \"input\" : {\n"
3572      << _ifc.ossInput.str() << "\n  },\n  \"output\" : {\n"
3573      << _ifc.ossOutput.str() << "\n  }";
3574   os << ",\n  \"method\": \"";
3575   os << _ifc.method;
3576   os << "\"";
3577   os << ",\n  \"has_output_item\": " << (_ifc.outputItem ? "true" : "false");
3578   os << ",\n  \"included_files\": [\n" << _ifc.ossIncludedFiles.str() << "\n  ]";
3579   os << "\n}\n";
3580 }
3581 
create_enum_to_string_name(Id * ident,const std::string & prefix)3582 std::string create_enum_to_string_name(Id* ident, const std::string& prefix) {
3583   std::ostringstream ss;
3584   if (ident->str().c_str()[0] == '\'') {
3585     ss << "'" << prefix << ident->str().substr(1);
3586   } else {
3587     ss << prefix << *ident;
3588   }
3589   return ss.str();
3590 }
3591 
3592 }  // namespace MiniZinc
3593