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