The models, scripts, and results of the benchmarks performed for a Half Reification Journal paper
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
24namespace MiniZinc {
25
26Scopes::Scopes() { _s.emplace_back(ST_TOPLEVEL); }
27
28void 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
70void Scopes::pushToplevel() { _s.emplace_back(ST_TOPLEVEL); }
71
72void Scopes::pushFun() { _s.emplace_back(ST_FUN); }
73
74void Scopes::push() { _s.emplace_back(ST_INNER); }
75
76void Scopes::pop() { _s.pop_back(); }
77
78VarDecl* 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
98VarDecl* 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
124class VarDeclCmp {
125private:
126 std::unordered_map<VarDecl*, int>& _pos;
127
128public:
129 VarDeclCmp(std::unordered_map<VarDecl*, int>& pos) : _pos(pos) {}
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};
140class ItemCmp {
141private:
142 std::unordered_map<VarDecl*, int>& _pos;
143
144public:
145 ItemCmp(std::unordered_map<VarDecl*, int>& pos) : _pos(pos) {}
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)
159void 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
1156void 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
1170VarDecl* 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
1186VarDecl* 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
1214VarDecl* 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
1220void 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
1377KeepAlive 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}
1501KeepAlive add_coercion(EnvI& env, Model* m, Expression* e, Expression* funarg) {
1502 return add_coercion(env, m, e, funarg->type());
1503}
1504
1505template <bool ignoreVarDecl>
1506class Typer {
1507private:
1508 EnvI& _env;
1509 Model* _model;
1510 std::vector<TypeError>& _typeErrors;
1511 bool _ignoreUndefined;
1512
1513public:
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
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 }
1525 bool enter(Expression* /*e*/) { return true; }
1526 /// Visit integer literal
1527 void vIntLit(const IntLit& /*i*/) {}
1528 /// Visit floating point literal
1529 void vFloatLit(const FloatLit& /*f*/) {}
1530 /// Visit Boolean literal
1531 void vBoolLit(const BoolLit& /*b*/) {}
1532 /// Visit set literal
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
1595 void vStringLit(const StringLit& /*sl*/) {}
1596 /// Visit identifier
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
1604 void vAnonVar(const AnonVar& /*v*/) {}
1605 /// Visit array literal
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
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
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
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
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
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
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
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->paramCount() == 1 &&
2317 fi->param(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->paramCount() == 1 &&
2322 fi->param(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->paramCount() &&
2331 _model->sameOverloading(_env, args, fi, next_call->decl())) {
2332 bool macro = true;
2333 for (unsigned int i = 0; i < fi->paramCount(); i++) {
2334 if (!Expression::equal(next_call->arg(i), fi->param(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->paramCount() + 1);
2344 for (unsigned int i = 0; i < fi->paramCount(); i++) {
2345 tt[i] = fi->param(i)->type();
2346 }
2347 tt[fi->paramCount()] = 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
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
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
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 }
2636 void vTIId(TIId& id) {}
2637};
2638
2639void 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 std::vector<TypeError>& typeErrors;
2677 TSVFuns(EnvI& env0, Model* model0, std::vector<FunctionI*>& fis0,
2678 std::vector<TypeError>& typeErrors0)
2679 : env(env0), model(model0), fis(fis0), typeErrors(typeErrors0) {}
2680 void vFunctionI(FunctionI* i) {
2681 (void)model->registerFn(env, i);
2682 fis.push_back(i);
2683 // check if one of the arguments is annotated with ::annotated_expression
2684 int reifiedAnnotationIdx = -1;
2685 for (int j = 0; j < i->paramCount(); j++) {
2686 Expression* param = i->param(j);
2687 for (auto* ii : param->ann()) {
2688 if (ii->isa<Id>() && ii->cast<Id>()->v() == constants().ann.annotated_expression->v()) {
2689 if (reifiedAnnotationIdx >= 0) {
2690 typeErrors.emplace_back(
2691 env, param->loc(),
2692 "only one argument can be annotated with annotated_expression");
2693 }
2694 reifiedAnnotationIdx = j;
2695 }
2696 }
2697 }
2698 if (reifiedAnnotationIdx >= 0) {
2699 GCLock lock;
2700 if (i->paramCount() == 1) {
2701 // turn into atomic annotation
2702 auto* ti = new TypeInst(Location().introduce(), Type::ann());
2703 auto* vd = new VarDecl(Location().introduce(), ti, i->id());
2704 vd->ann().add(new Call(Location().introduce(),
2705 constants().ann.mzn_add_annotated_expression, {IntLit::a(0)}));
2706 model->addItem(new VarDeclI(Location().introduce(), vd));
2707 } else {
2708 // turn into annotation function with one argument less
2709 std::vector<VarDecl*> newParams(i->paramCount() - 1);
2710 int j = 0;
2711 for (int k = 0; k < i->paramCount(); k++) {
2712 if (k != reifiedAnnotationIdx) {
2713 newParams[j++] = copy(env, i->param(k))->cast<VarDecl>();
2714 }
2715 }
2716 auto* fi = new FunctionI(Location().introduce(), i->id(), i->ti(), newParams);
2717 fi->ann().add(new Call(Location().introduce(),
2718 constants().ann.mzn_add_annotated_expression,
2719 {IntLit::a(reifiedAnnotationIdx)}));
2720 model->addItem(fi);
2721 (void)model->registerFn(env, fi);
2722 fis.push_back(fi);
2723 }
2724 }
2725 }
2726 } _tsvf(env.envi(), m, functionItems, typeErrors);
2727 iter_items(_tsvf, m);
2728
2729 class TSV0 : public ItemVisitor {
2730 public:
2731 EnvI& env;
2732 TopoSorter& ts;
2733 Model* model;
2734 bool hadSolveItem;
2735 std::vector<AssignI*>& ais;
2736 VarDeclI* objective;
2737 Model* enumis;
2738 bool isFlatZinc;
2739 TSV0(EnvI& env0, TopoSorter& ts0, Model* model0, std::vector<AssignI*>& ais0, Model* enumis0,
2740 bool isFlatZinc0)
2741 : env(env0),
2742 ts(ts0),
2743 model(model0),
2744 hadSolveItem(false),
2745 ais(ais0),
2746 objective(nullptr),
2747 enumis(enumis0),
2748 isFlatZinc(isFlatZinc0) {}
2749 void vAssignI(AssignI* i) { ais.push_back(i); }
2750 void vVarDeclI(VarDeclI* i) {
2751 ts.add(env, i, true, enumis);
2752 // initialise new identifier counter to be larger than existing identifier
2753 if (i->e()->id()->idn() >= 0) {
2754 env.minId(i->e()->id()->idn());
2755 } else if (i->e()->id()->v().beginsWith("X_INTRODUCED_") && i->e()->id()->v().endsWith("_")) {
2756 std::string numId = i->e()->id()->v().substr(std::string("X_INTRODUCED_").size());
2757 if (!numId.empty()) {
2758 numId = numId.substr(0, numId.size() - 1);
2759 if (!numId.empty()) {
2760 int vId = -1;
2761 try {
2762 vId = std::stoi(numId);
2763 } catch (std::exception&) {
2764 }
2765 if (vId >= 0) {
2766 env.minId(vId);
2767 }
2768 }
2769 }
2770 }
2771 }
2772 void vSolveI(SolveI* si) {
2773 if (hadSolveItem) {
2774 throw TypeError(env, si->loc(), "Only one solve item allowed");
2775 }
2776 hadSolveItem = true;
2777 if (!isFlatZinc && (si->e() != nullptr)) {
2778 GCLock lock;
2779 auto* ti = new TypeInst(Location().introduce(), Type());
2780 auto* obj = new VarDecl(Location().introduce(), ti, "_objective", si->e());
2781 si->e(obj->id());
2782 obj->addAnnotation(si->st() == SolveI::ST_MAX ? constants().ctx.pos : constants().ctx.neg);
2783 objective = new VarDeclI(Location().introduce(), obj);
2784 }
2785 }
2786 } _tsv0(env.envi(), ts, m, assignItems, enumItems, isFlatZinc);
2787 iter_items(_tsv0, m);
2788 if (_tsv0.objective != nullptr) {
2789 m->addItem(_tsv0.objective);
2790 ts.add(env.envi(), _tsv0.objective, true, enumItems);
2791 }
2792
2793 for (unsigned int i = 0; i < enumItems->size(); i++) {
2794 if (auto* ai = (*enumItems)[i]->dynamicCast<AssignI>()) {
2795 assignItems.push_back(ai);
2796 } else if (auto* vdi = (*enumItems)[i]->dynamicCast<VarDeclI>()) {
2797 m->addItem(vdi);
2798 ts.add(env.envi(), vdi, false, enumItems);
2799 } else {
2800 auto* fi = (*enumItems)[i]->dynamicCast<FunctionI>();
2801 m->addItem(fi);
2802 (void)m->registerFn(env.envi(), fi);
2803 functionItems.push_back(fi);
2804 }
2805 }
2806
2807 auto* enumItems2 = new Model;
2808
2809 for (auto* ai : assignItems) {
2810 VarDecl* vd = nullptr;
2811 if (env.envi().ignoreUnknownIds) {
2812 try {
2813 vd = ts.get(env.envi(), ai->id(), ai->loc());
2814 } catch (TypeError&) {
2815 }
2816 } else {
2817 vd = ts.get(env.envi(), ai->id(), ai->loc());
2818 }
2819 if (vd != nullptr) {
2820 if (vd->e() != nullptr) {
2821 if (allowMultiAssignment) {
2822 GCLock lock;
2823 m->addItem(new ConstraintI(
2824 ai->loc(),
2825 new BinOp(ai->loc(), new Id(Location().introduce(), ai->id(), vd), BOT_EQ, ai->e())));
2826 } else {
2827 throw TypeError(env.envi(), ai->loc(), "multiple assignment to the same variable");
2828 }
2829 } else {
2830 vd->e(ai->e());
2831 vd->ann().add(constants().ann.rhs_from_assignment);
2832 if (vd->ti()->isEnum()) {
2833 create_enum_mapper(env.envi(), m, vd->ti()->type().enumId(), vd, enumItems2);
2834 }
2835 }
2836 }
2837 ai->remove();
2838 }
2839
2840 for (auto& i : *enumItems2) {
2841 if (auto* vdi = i->dynamicCast<VarDeclI>()) {
2842 m->addItem(vdi);
2843 ts.add(env.envi(), vdi, false, enumItems);
2844 } else {
2845 auto* fi = i->cast<FunctionI>();
2846 m->addItem(fi);
2847 (void)m->registerFn(env.envi(), fi);
2848 functionItems.push_back(fi);
2849 }
2850 }
2851
2852 delete enumItems;
2853 delete enumItems2;
2854
2855 class TSV1 : public ItemVisitor {
2856 public:
2857 EnvI& env;
2858 TopoSorter& ts;
2859 TSV1(EnvI& env0, TopoSorter& ts0) : env(env0), ts(ts0) {}
2860 void vVarDeclI(VarDeclI* i) { ts.run(env, i->e()); }
2861 void vAssignI(AssignI* i) {}
2862 void vConstraintI(ConstraintI* i) { ts.run(env, i->e()); }
2863 void vSolveI(SolveI* i) {
2864 for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) {
2865 ts.run(env, *it);
2866 }
2867 ts.run(env, i->e());
2868 }
2869 void vOutputI(OutputI* i) { ts.run(env, i->e()); }
2870 void vFunctionI(FunctionI* fi) {
2871 ts.run(env, fi->ti());
2872 for (unsigned int i = 0; i < fi->paramCount(); i++) {
2873 ts.run(env, fi->param(i));
2874 }
2875 ts.run(env, fi->capturedAnnotationsVar());
2876 for (ExpressionSetIter it = fi->ann().begin(); it != fi->ann().end(); ++it) {
2877 ts.run(env, *it);
2878 }
2879 ts.scopes.pushFun();
2880 for (unsigned int i = 0; i < fi->paramCount(); i++) {
2881 ts.scopes.add(env, fi->param(i));
2882 }
2883 if (fi->capturedAnnotationsVar() != nullptr) {
2884 ts.scopes.add(env, fi->capturedAnnotationsVar());
2885 }
2886 ts.run(env, fi->e());
2887 ts.scopes.pop();
2888 }
2889 } _tsv1(env.envi(), ts);
2890 iter_items(_tsv1, m);
2891
2892 m->sortFn();
2893
2894 {
2895 struct SortByPayload {
2896 bool operator()(Item* i0, Item* i1) {
2897 if (i0->isa<IncludeI>()) {
2898 return !i1->isa<IncludeI>();
2899 }
2900 if (auto* vdi0 = i0->dynamicCast<VarDeclI>()) {
2901 if (auto* vdi1 = i1->dynamicCast<VarDeclI>()) {
2902 return vdi0->e()->payload() < vdi1->e()->payload();
2903 }
2904 return !i1->isa<IncludeI>();
2905 }
2906 return false;
2907 }
2908 } _sbp;
2909
2910 std::stable_sort(m->begin(), m->end(), _sbp);
2911 }
2912
2913 {
2914 Typer<false> ty(env.envi(), m, typeErrors, ignoreUndefinedParameters);
2915 BottomUpIterator<Typer<false>> bottomUpTyper(ty);
2916 for (auto& decl : ts.decls) {
2917 decl->payload(0);
2918 bottomUpTyper.run(decl->ti());
2919 ty.vVarDecl(*decl);
2920 }
2921 for (auto& functionItem : functionItems) {
2922 bottomUpTyper.run(functionItem->ti());
2923 for (unsigned int j = 0; j < functionItem->paramCount(); j++) {
2924 bottomUpTyper.run(functionItem->param(j));
2925 }
2926 if (functionItem->capturedAnnotationsVar() != nullptr) {
2927 bottomUpTyper.run(functionItem->capturedAnnotationsVar());
2928 }
2929 }
2930 }
2931
2932 m->fixFnMap();
2933
2934 {
2935 Typer<true> ty(env.envi(), m, typeErrors, ignoreUndefinedParameters);
2936 BottomUpIterator<Typer<true>> bottomUpTyper(ty);
2937
2938 class TSV2 : public ItemVisitor {
2939 private:
2940 EnvI& _env;
2941 Model* _m;
2942 BottomUpIterator<Typer<true>>& _bottomUpTyper;
2943 std::vector<TypeError>& _typeErrors;
2944
2945 public:
2946 TSV2(EnvI& env0, Model* m0, BottomUpIterator<Typer<true>>& b,
2947 std::vector<TypeError>& typeErrors)
2948 : _env(env0), _m(m0), _bottomUpTyper(b), _typeErrors(typeErrors) {}
2949 void vVarDeclI(VarDeclI* i) {
2950 _bottomUpTyper.run(i->e());
2951 if (i->e()->ti()->hasTiVariable()) {
2952 std::ostringstream ss;
2953 ss << "type-inst variables not allowed in type-inst for `" << i->e()->id()->str() << "'";
2954 _typeErrors.emplace_back(_env, i->e()->loc(), ss.str());
2955 }
2956 VarDecl* vdi = i->e();
2957 if (vdi->e() == nullptr && vdi->type().isSet() && vdi->type().isvar() &&
2958 vdi->ti()->domain() == nullptr) {
2959 std::ostringstream ss;
2960 ss << "set element type for `" << vdi->id()->str() << "' is not finite";
2961 _typeErrors.emplace_back(_env, vdi->loc(), ss.str());
2962 }
2963 if (i->e()->ann().contains(constants().ann.output_only)) {
2964 if (vdi->e() == nullptr) {
2965 _typeErrors.emplace_back(
2966 _env, vdi->loc(),
2967 "variables annotated with ::output_only must have a right hand side");
2968 } else if (vdi->e()->type().isvar()) {
2969 _typeErrors.emplace_back(_env, vdi->loc(),
2970 "variables annotated with ::output_only must be par");
2971 }
2972 }
2973 }
2974 void vAssignI(AssignI* i) {
2975 _bottomUpTyper.run(i->e());
2976 if (!_env.isSubtype(i->e()->type(), i->decl()->ti()->type(), true)) {
2977 std::ostringstream ss;
2978 ss << "assignment value for `" << i->decl()->id()->str()
2979 << "' has invalid type-inst: expected `" << i->decl()->ti()->type().toString(_env)
2980 << "', actual `" << i->e()->type().toString(_env) << "'";
2981 _typeErrors.emplace_back(_env, i->loc(), ss.str());
2982 // Assign to "true" constant to avoid generating further errors that the parameter
2983 // is undefined
2984 i->decl()->e(constants().literalTrue);
2985 }
2986 }
2987 void vConstraintI(ConstraintI* i) {
2988 _bottomUpTyper.run(i->e());
2989 if (!_env.isSubtype(i->e()->type(), Type::varbool(), true)) {
2990 throw TypeError(_env, i->loc(),
2991 "invalid type of constraint, expected `" +
2992 Type::varbool().toString(_env) + "', actual `" +
2993 i->e()->type().toString(_env) + "'");
2994 }
2995 }
2996 void vSolveI(SolveI* i) {
2997 for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) {
2998 _bottomUpTyper.run(*it);
2999 if (!(*it)->type().isAnn()) {
3000 throw TypeError(_env, (*it)->loc(),
3001 "expected annotation, got `" + (*it)->type().toString(_env) + "'");
3002 }
3003 }
3004 _bottomUpTyper.run(i->e());
3005 if (i->e() != nullptr) {
3006 Type et = i->e()->type();
3007 if (et.isbool()) {
3008 Type target_t = Type::varint();
3009 if (et.isOpt()) {
3010 target_t.ot(Type::OT_OPTIONAL);
3011 }
3012 i->e(add_coercion(_env, _env.model, i->e(), target_t)());
3013 }
3014
3015 bool needOptCoercion = et.isOpt() && et.isint();
3016 if (needOptCoercion) {
3017 et.ot(Type::OT_PRESENT);
3018 }
3019
3020 if (!(_env.isSubtype(et, Type::varint(), true) ||
3021 _env.isSubtype(et, Type::varfloat(), true))) {
3022 throw TypeError(_env, i->e()->loc(),
3023 "objective has invalid type, expected int or float, actual `" +
3024 et.toString(_env) + "'");
3025 }
3026
3027 if (needOptCoercion) {
3028 GCLock lock;
3029 std::vector<Expression*> args(2);
3030 args[0] = i->e();
3031 args[1] = constants().boollit(i->st() == SolveI::ST_MAX);
3032 Call* c = new Call(Location().introduce(), ASTString("objective_deopt_"), args);
3033 c->decl(_env.model->matchFn(_env, c, false));
3034 assert(c->decl());
3035 c->type(et);
3036 i->e(c);
3037 }
3038 }
3039 }
3040 void vOutputI(OutputI* i) {
3041 for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) {
3042 _bottomUpTyper.run(*it);
3043 if (!(*it)->type().isAnn()) {
3044 throw TypeError(_env, (*it)->loc(),
3045 "expected annotation, got `" + (*it)->type().toString(_env) + "'");
3046 }
3047 }
3048 _bottomUpTyper.run(i->e());
3049 if (i->e()->type() != Type::parstring(1) && i->e()->type() != Type::bot(1)) {
3050 throw TypeError(_env, i->e()->loc(),
3051 "invalid type in output item, expected `" +
3052 Type::parstring(1).toString(_env) + "', actual `" +
3053 i->e()->type().toString(_env) + "'");
3054 }
3055 }
3056 void vFunctionI(FunctionI* i) {
3057 for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) {
3058 _bottomUpTyper.run(*it);
3059 if (!(*it)->type().isAnn()) {
3060 throw TypeError(_env, (*it)->loc(),
3061 "expected annotation, got `" + (*it)->type().toString(_env) + "'");
3062 }
3063 }
3064 _bottomUpTyper.run(i->ti());
3065 _bottomUpTyper.run(i->e());
3066 if ((i->e() != nullptr) && !_env.isSubtype(i->e()->type(), i->ti()->type(), true)) {
3067 throw TypeError(_env, i->e()->loc(),
3068 "return type of function does not match body, declared type is `" +
3069 i->ti()->type().toString(_env) + "', body type is `" +
3070 i->e()->type().toString(_env) + "'");
3071 }
3072 if ((i->e() != nullptr) && i->e()->type().isPar() && i->ti()->type().isvar()) {
3073 // this is a par function declared as var, so change declared return type
3074 Type i_t = i->ti()->type();
3075 i_t.ti(Type::TI_PAR);
3076 i->ti()->type(i_t);
3077 }
3078 if (i->e() != nullptr) {
3079 i->e(add_coercion(_env, _m, i->e(), i->ti()->type())());
3080 }
3081 }
3082 } _tsv2(env.envi(), m, bottomUpTyper, typeErrors);
3083 iter_items(_tsv2, m);
3084 }
3085
3086 class TSV3 : public ItemVisitor {
3087 public:
3088 EnvI& env;
3089 Model* m;
3090 OutputI* outputItem;
3091 TSV3(EnvI& env0, Model* m0) : env(env0), m(m0), outputItem(nullptr) {}
3092 void vAssignI(AssignI* i) { i->decl()->e(add_coercion(env, m, i->e(), i->decl()->type())()); }
3093 void vOutputI(OutputI* oi) {
3094 if (outputItem == nullptr) {
3095 outputItem = oi;
3096 } else {
3097 GCLock lock;
3098 auto* bo = new BinOp(Location().introduce(), outputItem->e(), BOT_PLUSPLUS, oi->e());
3099 bo->type(Type::parstring(1));
3100 outputItem->e(bo);
3101 oi->remove();
3102 m->setOutputItem(outputItem);
3103 }
3104 }
3105 } _tsv3(env.envi(), m);
3106 if (typeErrors.empty()) {
3107 iter_items(_tsv3, m);
3108 }
3109
3110 // Create a par version of each function that returns par and
3111 // that has a body that can be made par
3112 std::unordered_map<FunctionI*, std::pair<bool, std::vector<FunctionI*>>> fnsToMakePar;
3113 for (auto& f : m->functions()) {
3114 if (f.id() == "mzn_reverse_map_var") {
3115 continue;
3116 }
3117 if (f.e() != nullptr && f.ti()->type().bt() != Type::BT_ANN) {
3118 bool foundVar = false;
3119 for (int i = 0; i < f.paramCount(); i++) {
3120 if (f.param(i)->type().isvar()) {
3121 foundVar = true;
3122 break;
3123 }
3124 }
3125 if (foundVar) {
3126 // create par version of parameter types
3127 std::vector<Type> tv;
3128 for (int i = 0; i < f.paramCount(); i++) {
3129 Type t = f.param(i)->type();
3130 t.cv(false);
3131 t.ti(Type::TI_PAR);
3132 tv.push_back(t);
3133 }
3134 // check if specialised par version of function already exists
3135 FunctionI* fi_par = m->matchFn(env.envi(), f.id(), tv, false);
3136 bool parIsUsable = false;
3137 if (fi_par != nullptr) {
3138 bool foundVar = false;
3139 for (int i = 0; i < fi_par->paramCount(); i++) {
3140 if (fi_par->param(i)->type().isvar()) {
3141 foundVar = true;
3142 break;
3143 }
3144 }
3145 parIsUsable = !foundVar;
3146 }
3147 if (!parIsUsable) {
3148 // check if body of f doesn't contain any free variables in lets,
3149 // all calls in the body have par versions available,
3150 // and all toplevel identifiers used in the body of f are par
3151 class CheckParBody : public EVisitor {
3152 public:
3153 EnvI& env;
3154 Model* m;
3155 CheckParBody(EnvI& env0, Model* m0) : env(env0), m(m0) {}
3156 bool isPar = true;
3157 std::vector<FunctionI*> deps;
3158 bool enter(Expression* e) const {
3159 // if we have already found a var, don't continue
3160 return isPar;
3161 }
3162 void vId(const Id& ident) {
3163 if (ident.decl() != nullptr && ident.type().isvar() && ident.decl()->toplevel()) {
3164 isPar = false;
3165 }
3166 }
3167 void vLet(const Let& let) {
3168 // check if any of the declared variables does not have a RHS
3169 for (auto* e : let.let()) {
3170 if (auto* vd = e->dynamicCast<VarDecl>()) {
3171 if (vd->e() == nullptr) {
3172 isPar = false;
3173 break;
3174 }
3175 }
3176 }
3177 }
3178 void vCall(const Call& c) {
3179 if (!c.type().isAnn()) {
3180 FunctionI* decl = c.decl();
3181 // create par version of parameter types
3182 std::vector<Type> tv;
3183 for (int i = 0; i < decl->paramCount(); i++) {
3184 Type t = decl->param(i)->type();
3185 t.cv(false);
3186 t.ti(Type::TI_PAR);
3187 tv.push_back(t);
3188 }
3189 // check if specialised par version of function already exists
3190 FunctionI* decl_par = m->matchFn(env, decl->id(), tv, false);
3191 bool parIsUsable = decl_par->ti()->type().isPar();
3192 if (parIsUsable && decl_par->e() == nullptr && decl_par->fromStdLib()) {
3193 parIsUsable = true;
3194 } else if (parIsUsable) {
3195 bool foundVar = false;
3196 for (int i = 0; i < decl_par->paramCount(); i++) {
3197 if (decl_par->param(i)->type().isvar()) {
3198 foundVar = true;
3199 break;
3200 }
3201 }
3202 parIsUsable = !foundVar;
3203 }
3204 if (!parIsUsable) {
3205 deps.push_back(decl_par);
3206 }
3207 }
3208 }
3209 } cpb(env.envi(), m);
3210 top_down(cpb, f.e());
3211 if (cpb.isPar) {
3212 fnsToMakePar.insert({&f, {false, cpb.deps}});
3213 }
3214 } else {
3215 fnsToMakePar.insert({fi_par, {true, std::vector<FunctionI*>()}});
3216 }
3217 }
3218 }
3219 }
3220
3221 // Repeatedly remove functions whose dependencies cannot be made par
3222 bool didRemove;
3223 do {
3224 didRemove = false;
3225 std::vector<FunctionI*> toRemove;
3226 for (auto& p : fnsToMakePar) {
3227 for (auto* dep : p.second.second) {
3228 if (fnsToMakePar.find(dep) == fnsToMakePar.end()) {
3229 toRemove.push_back(p.first);
3230 }
3231 }
3232 }
3233 if (!toRemove.empty()) {
3234 didRemove = true;
3235 for (auto* p : toRemove) {
3236 fnsToMakePar.erase(p);
3237 }
3238 }
3239 } while (didRemove);
3240
3241 // Create par versions of remaining functions
3242 if (!fnsToMakePar.empty()) {
3243 // First step: copy and register functions
3244 std::vector<FunctionI*> parFunctions;
3245 CopyMap parCopyMap;
3246 // Step 1a: enter all global declarations into copy map
3247 class EnterGlobalDecls : public EVisitor {
3248 public:
3249 CopyMap& cm;
3250 EnterGlobalDecls(CopyMap& cm0) : cm(cm0) {}
3251 void vId(Id& ident) {
3252 if (ident.decl() != nullptr && ident.decl()->toplevel()) {
3253 cm.insert(ident.decl(), ident.decl());
3254 }
3255 }
3256 } _egd(parCopyMap);
3257 for (auto& p : fnsToMakePar) {
3258 if (!p.second.first) {
3259 for (unsigned int i = 0; i < p.first->paramCount(); i++) {
3260 top_down(_egd, p.first->param(i));
3261 }
3262 if (p.first->capturedAnnotationsVar() != nullptr) {
3263 top_down(_egd, p.first->capturedAnnotationsVar());
3264 }
3265 for (ExpressionSetIter i = p.first->ann().begin(); i != p.first->ann().end(); ++i) {
3266 top_down(_egd, *i);
3267 }
3268 top_down(_egd, p.first->e());
3269 }
3270 }
3271
3272 // Step 1b: copy functions
3273 for (auto& p : fnsToMakePar) {
3274 if (!p.second.first) {
3275 GCLock lock;
3276 auto* cp = copy(env.envi(), parCopyMap, p.first)->cast<FunctionI>();
3277 for (int i = 0; i < cp->paramCount(); i++) {
3278 VarDecl* v = cp->param(i);
3279 Type vt = v->ti()->type();
3280 vt.ti(Type::TI_PAR);
3281 v->ti()->type(vt);
3282 v->type(vt);
3283 }
3284 Type cpt(cp->ti()->type());
3285 cpt.ti(Type::TI_PAR);
3286 cp->ti()->type(cpt);
3287 bool didRegister = m->registerFn(env.envi(), cp, true, false);
3288 if (didRegister) {
3289 m->addItem(cp);
3290 parFunctions.push_back(cp);
3291 }
3292 }
3293 }
3294
3295 // Second step: make function bodies par
3296 // (needs to happen in a separate second step so that
3297 // matchFn will find the correct par function from first step)
3298 class MakeFnPar : public EVisitor {
3299 public:
3300 EnvI& env;
3301 Model* m;
3302 MakeFnPar(EnvI& env0, Model* m0) : env(env0), m(m0) {}
3303 static bool enter(Expression* e) {
3304 Type t(e->type());
3305 t.ti(Type::TI_PAR);
3306 t.cv(false);
3307 e->type(t);
3308 return true;
3309 }
3310 void vCall(Call& c) {
3311 FunctionI* decl = m->matchFn(env, &c, false);
3312 c.decl(decl);
3313 }
3314 void vBinOp(BinOp& bo) {
3315 if (bo.decl() != nullptr) {
3316 std::vector<Type> ta(2);
3317 ta[0] = bo.lhs()->type();
3318 ta[1] = bo.rhs()->type();
3319 FunctionI* decl = m->matchFn(env, bo.opToString(), ta, false);
3320 bo.decl(decl);
3321 }
3322 }
3323 void vUnOp(UnOp& uo) {
3324 if (uo.decl() != nullptr) {
3325 std::vector<Type> ta(1);
3326 ta[0] = uo.e()->type();
3327 FunctionI* decl = m->matchFn(env, uo.opToString(), ta, false);
3328 uo.decl(decl);
3329 }
3330 }
3331 } _mfp(env.envi(), m);
3332
3333 for (auto* p : parFunctions) {
3334 bottom_up(_mfp, p->e());
3335 }
3336 }
3337
3338 try {
3339 m->checkFnOverloading(env.envi());
3340 } catch (TypeError& e) {
3341 typeErrors.push_back(e);
3342 }
3343
3344 for (auto& decl : ts.decls) {
3345 if (decl->toplevel() && decl->type().isPar() && !decl->type().isAnn() && decl->e() == nullptr) {
3346 if (decl->type().isOpt() && decl->type().dim() == 0) {
3347 decl->e(constants().absent);
3348 decl->addAnnotation(constants().ann.mzn_was_undefined);
3349 } else if (!ignoreUndefinedParameters) {
3350 std::ostringstream ss;
3351 ss << " symbol error: variable `" << decl->id()->str()
3352 << "' must be defined (did you forget to specify a data file?)";
3353 typeErrors.emplace_back(env.envi(), decl->loc(), ss.str());
3354 }
3355 }
3356 if (decl->ti()->isEnum()) {
3357 decl->ti()->setIsEnum(false);
3358 Type vdt = decl->ti()->type();
3359 vdt.enumId(0);
3360 decl->ti()->type(vdt);
3361 }
3362 }
3363
3364 for (auto vd_k : env.envi().checkVars) {
3365 try {
3366 VarDecl* vd;
3367 try {
3368 vd = ts.get(env.envi(), vd_k()->cast<VarDecl>()->id()->str(),
3369 vd_k()->cast<VarDecl>()->loc());
3370 } catch (TypeError&) {
3371 if (vd_k()->cast<VarDecl>()->type().isvar()) {
3372 continue; // var can be undefined
3373 }
3374 throw;
3375 }
3376 vd->ann().add(constants().ann.mzn_check_var);
3377 if (vd->type().enumId() != 0) {
3378 GCLock lock;
3379 std::vector<unsigned int> enumIds({vd->type().enumId()});
3380 if (vd->type().dim() > 0) {
3381 enumIds = env.envi().getArrayEnum(vd->type().enumId());
3382 }
3383 std::vector<Expression*> enumIds_a(enumIds.size());
3384 for (unsigned int i = 0; i < enumIds.size(); i++) {
3385 if (enumIds[i] != 0) {
3386 enumIds_a[i] = env.envi().getEnum(enumIds[i])->e()->id();
3387 } else {
3388 enumIds_a[i] = new SetLit(Location().introduce(), std::vector<Expression*>());
3389 }
3390 }
3391 auto* enumIds_al = new ArrayLit(Location().introduce(), enumIds_a);
3392 enumIds_al->type(Type::parsetint(1));
3393 std::vector<Expression*> args({enumIds_al});
3394 Call* checkEnum =
3395 new Call(Location().introduce(), constants().ann.mzn_check_enum_var, args);
3396 checkEnum->type(Type::ann());
3397 checkEnum->decl(env.envi().model->matchFn(env.envi(), checkEnum, false));
3398 vd->ann().add(checkEnum);
3399 }
3400 Type vdktype = vd_k()->type();
3401 vdktype.ti(Type::TI_VAR);
3402 if (!vd_k()->type().isSubtypeOf(vd->type(), false)) {
3403 std::ostringstream ss;
3404 ss << "Solution checker requires `" << vd->id()->str() << "' to be of type `"
3405 << vdktype.toString(env.envi()) << "'";
3406 typeErrors.emplace_back(env.envi(), vd->loc(), ss.str());
3407 }
3408 } catch (TypeError& e) {
3409 typeErrors.emplace_back(env.envi(), e.loc(),
3410 e.msg() + " (required by solution checker model)");
3411 }
3412 }
3413}
3414
3415void typecheck(Env& env, Model* m, AssignI* ai) {
3416 std::vector<TypeError> typeErrors;
3417 Typer<true> ty(env.envi(), m, typeErrors, false);
3418 BottomUpIterator<Typer<true>> bottomUpTyper(ty);
3419 bottomUpTyper.run(ai->e());
3420 if (!typeErrors.empty()) {
3421 throw typeErrors[0];
3422 }
3423 if (!env.envi().isSubtype(ai->e()->type(), ai->decl()->ti()->type(), true)) {
3424 std::ostringstream ss;
3425 ss << "assignment value for `" << ai->decl()->id()->str()
3426 << "' has invalid type-inst: expected `" << ai->decl()->ti()->type().toString(env.envi())
3427 << "', actual `" << ai->e()->type().toString(env.envi()) << "'";
3428 throw TypeError(env.envi(), ai->e()->loc(), ss.str());
3429 }
3430}
3431
3432void output_var_desc_json(Env& env, VarDecl* vd, std::ostream& os, bool extra = false) {
3433 os << " \"" << *vd->id() << "\" : {";
3434 os << "\"type\" : ";
3435 switch (vd->type().bt()) {
3436 case Type::BT_INT:
3437 os << "\"int\"";
3438 break;
3439 case Type::BT_BOOL:
3440 os << "\"bool\"";
3441 break;
3442 case Type::BT_FLOAT:
3443 os << "\"float\"";
3444 break;
3445 case Type::BT_STRING:
3446 os << "\"string\"";
3447 break;
3448 case Type::BT_ANN:
3449 os << "\"ann\"";
3450 break;
3451 default:
3452 os << "\"?\"";
3453 break;
3454 }
3455 if (vd->type().ot() == Type::OT_OPTIONAL) {
3456 os << ", \"optional\" : true";
3457 }
3458 if (vd->type().st() == Type::ST_SET) {
3459 os << ", \"set\" : true";
3460 }
3461 if (vd->type().dim() > 0) {
3462 os << ", \"dim\" : " << vd->type().dim();
3463
3464 if (extra) {
3465 os << ", \"dims\" : [";
3466 bool had_dim = false;
3467 ASTExprVec<TypeInst> ranges = vd->ti()->ranges();
3468 for (auto& range : ranges) {
3469 if (range->type().enumId() > 0) {
3470 os << (had_dim ? "," : "") << "\""
3471 << *env.envi().getEnum(range->type().enumId())->e()->id() << "\"";
3472 } else {
3473 os << (had_dim ? "," : "") << "\"int\"";
3474 }
3475 had_dim = true;
3476 }
3477 os << "]";
3478
3479 if (vd->type().enumId() > 0) {
3480 const std::vector<unsigned int>& enumIds = env.envi().getArrayEnum(vd->type().enumId());
3481 if (enumIds.back() > 0) {
3482 os << ", \"enum_type\" : \"" << *env.envi().getEnum(enumIds.back())->e()->id() << "\"";
3483 }
3484 }
3485 }
3486
3487 } else {
3488 if (extra) {
3489 if (vd->type().enumId() > 0) {
3490 os << ", \"enum_type\" : \"" << *env.envi().getEnum(vd->type().enumId())->e()->id() << "\"";
3491 }
3492 }
3493 }
3494 os << "}";
3495}
3496
3497void output_model_variable_types(Env& env, Model* m, std::ostream& os,
3498 const std::vector<std::string>& skipDirs) {
3499 class VInfVisitor : public ItemVisitor {
3500 public:
3501 Env& env;
3502 const std::vector<std::string>& skipDirs;
3503 bool hadVar;
3504 bool hadEnum;
3505 std::ostringstream ossVars;
3506 std::ostringstream ossEnums;
3507 VInfVisitor(Env& env0, const std::vector<std::string>& skipDirs0)
3508 : env(env0), skipDirs(skipDirs0), hadVar(false), hadEnum(false) {}
3509 bool enter(Item* i) {
3510 if (auto* ii = i->dynamicCast<IncludeI>()) {
3511 std::string prefix =
3512 ii->m()->filepath().substr(0, ii->m()->filepath().size() - ii->f().size());
3513 for (const auto& skip_dir : skipDirs) {
3514 if (prefix.substr(0, skip_dir.size()) == skip_dir) {
3515 return false;
3516 }
3517 }
3518 }
3519 return true;
3520 }
3521 void vVarDeclI(VarDeclI* vdi) {
3522 if (!vdi->e()->type().isAnn() && !vdi->e()->ti()->isEnum()) {
3523 if (hadVar) {
3524 ossVars << ",\n";
3525 }
3526 output_var_desc_json(env, vdi->e(), ossVars, true);
3527 hadVar = true;
3528 } else if (vdi->e()->type().st() == Type::ST_SET && vdi->e()->type().enumId() != 0 &&
3529 !vdi->e()->type().isAnn()) {
3530 if (hadEnum) {
3531 ossEnums << ", ";
3532 }
3533 ossEnums << "\"" << *env.envi().getEnum(vdi->e()->type().enumId())->e()->id() << "\"";
3534 hadEnum = true;
3535 }
3536 }
3537 } _vinf(env, skipDirs);
3538 iter_items(_vinf, m);
3539 os << "{\"var_types\": {";
3540 os << "\n \"vars\": {\n" << _vinf.ossVars.str() << "\n },";
3541 os << "\n \"enums\": [" << _vinf.ossEnums.str() << "]\n";
3542 os << "}}\n";
3543}
3544
3545void output_model_interface(Env& env, Model* m, std::ostream& os,
3546 const std::vector<std::string>& skipDirs) {
3547 class IfcVisitor : public ItemVisitor {
3548 public:
3549 Env& env;
3550 const std::vector<std::string>& skipDirs;
3551 bool hadInput;
3552 bool hadOutput;
3553 bool hadIncludedFiles;
3554 bool hadAddToOutput = false;
3555 std::ostringstream ossInput;
3556 std::ostringstream ossOutput;
3557 std::ostringstream ossIncludedFiles;
3558 std::string method;
3559 bool outputItem;
3560 IfcVisitor(Env& env0, const std::vector<std::string>& skipDirs0)
3561 : env(env0),
3562 skipDirs(skipDirs0),
3563 hadInput(false),
3564 hadOutput(false),
3565 hadIncludedFiles(false),
3566 method("sat"),
3567 outputItem(false) {}
3568 bool enter(Item* i) {
3569 if (auto* ii = i->dynamicCast<IncludeI>()) {
3570 std::string prefix =
3571 ii->m()->filepath().substr(0, ii->m()->filepath().size() - ii->f().size());
3572 for (const auto& skip_dir : skipDirs) {
3573 if (prefix.substr(0, skip_dir.size()) == skip_dir) {
3574 return false;
3575 }
3576 }
3577 if (hadIncludedFiles) {
3578 ossIncludedFiles << ",\n";
3579 }
3580 ossIncludedFiles << " \"" << Printer::escapeStringLit(ii->m()->filepath()) << "\"";
3581 hadIncludedFiles = true;
3582 }
3583 return true;
3584 }
3585 void vVarDeclI(VarDeclI* vdi) {
3586 VarDecl* vd = vdi->e();
3587 if (vd->type().isPar() && !vd->type().isAnn() &&
3588 (vd->e() == nullptr || (vd->e() == constants().absent &&
3589 vd->ann().contains(constants().ann.mzn_was_undefined)))) {
3590 if (hadInput) {
3591 ossInput << ",\n";
3592 }
3593 output_var_desc_json(env, vd, ossInput);
3594 hadInput = true;
3595 } else {
3596 bool process_var = false;
3597 if (vd->ann().contains(constants().ann.add_to_output)) {
3598 if (!hadAddToOutput) {
3599 ossOutput.str("");
3600 hadOutput = false;
3601 }
3602 hadAddToOutput = true;
3603 process_var = true;
3604 } else if (!hadAddToOutput) {
3605 process_var =
3606 vd->type().isvar() &&
3607 (vd->e() == nullptr || vd->ann().contains(constants().ann.rhs_from_assignment));
3608 }
3609 if (process_var) {
3610 if (hadOutput) {
3611 ossOutput << ",\n";
3612 }
3613 output_var_desc_json(env, vd, ossOutput);
3614 hadOutput = true;
3615 }
3616 }
3617 }
3618 void vSolveI(SolveI* si) {
3619 switch (si->st()) {
3620 case SolveI::ST_MIN:
3621 method = "min";
3622 break;
3623 case SolveI::ST_MAX:
3624 method = "max";
3625 break;
3626 case SolveI::ST_SAT:
3627 method = "sat";
3628 break;
3629 }
3630 }
3631 void vOutputI(OutputI* oi) { outputItem = true; }
3632 } _ifc(env, skipDirs);
3633 iter_items(_ifc, m);
3634 os << "{\n \"input\" : {\n"
3635 << _ifc.ossInput.str() << "\n },\n \"output\" : {\n"
3636 << _ifc.ossOutput.str() << "\n }";
3637 os << ",\n \"method\": \"";
3638 os << _ifc.method;
3639 os << "\"";
3640 os << ",\n \"has_output_item\": " << (_ifc.outputItem ? "true" : "false");
3641 os << ",\n \"included_files\": [\n" << _ifc.ossIncludedFiles.str() << "\n ]";
3642 os << "\n}\n";
3643}
3644
3645std::string create_enum_to_string_name(Id* ident, const std::string& prefix) {
3646 std::ostringstream ss;
3647 if (ident->str().c_str()[0] == '\'') {
3648 ss << "'" << prefix << ident->str().substr(1);
3649 } else {
3650 ss << prefix << *ident;
3651 }
3652 return ss.str();
3653}
3654
3655} // namespace MiniZinc