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/flatten_internal.hh>
14#include <minizinc/model.hh>
15#include <minizinc/prettyprinter.hh>
16
17#undef MZN_DEBUG_FUNCTION_REGISTRY
18
19namespace MiniZinc {
20
21Model::FnEntry::FnEntry(FunctionI* fi0) : t(fi0->params().size()), fi(fi0), isPolymorphic(false) {
22 for (unsigned int i = 0; i < fi->params().size(); i++) {
23 t[i] = fi->params()[i]->type();
24 isPolymorphic |= (t[i].bt() == Type::BT_TOP);
25 }
26}
27
28bool Model::FnEntry::operator<(const Model::FnEntry& f) const {
29 assert(!compare(*this, f) || !compare(f, *this));
30 return compare(*this, f);
31}
32
33bool Model::FnEntry::compare(const Model::FnEntry& e1, const Model::FnEntry& e2) {
34 if (e1.t.size() < e2.t.size()) {
35 return true;
36 }
37 if (e1.t.size() == e2.t.size()) {
38 for (unsigned int i = 0; i < e1.t.size(); i++) {
39 if (e1.t[i] != e2.t[i]) {
40 if (e1.t[i].isSubtypeOf(e2.t[i], true)) {
41 return true;
42 }
43 if (e2.t[i].isSubtypeOf(e1.t[i], true)) {
44 return false;
45 }
46 switch (e1.t[i].cmp(e2.t[i])) {
47 case -1:
48 return true;
49 case 1:
50 return false;
51 default:
52 assert(false);
53 }
54 }
55 }
56 }
57 return false;
58}
59
60Model::Model() : _parent(nullptr), _solveItem(nullptr), _outputItem(nullptr) {}
61
62Model::~Model() {
63 for (auto* i : _items) {
64 if (auto* ii = i->dynamicCast<IncludeI>()) {
65 if (ii->own()) {
66 delete ii->m();
67 ii->m(nullptr);
68 }
69 }
70 }
71}
72
73VarDeclIteratorContainer Model::vardecls() { return VarDeclIteratorContainer(this); }
74ConstraintIteratorContainer Model::constraints() { return ConstraintIteratorContainer(this); }
75FunctionIteratorContainer Model::functions() { return FunctionIteratorContainer(this); }
76
77VarDeclIterator VarDeclIteratorContainer::begin() { return VarDeclIterator(_m, _m->begin()); }
78VarDeclIterator VarDeclIteratorContainer::end() { return VarDeclIterator(_m, _m->end()); }
79ConstraintIterator ConstraintIteratorContainer::begin() {
80 return ConstraintIterator(_m, _m->begin());
81}
82ConstraintIterator ConstraintIteratorContainer::end() { return ConstraintIterator(_m, _m->end()); }
83FunctionIterator FunctionIteratorContainer::begin() { return FunctionIterator(_m, _m->begin()); }
84FunctionIterator FunctionIteratorContainer::end() { return FunctionIterator(_m, _m->end()); }
85
86SolveI* Model::solveItem() { return _solveItem; }
87
88OutputI* Model::outputItem() { return _outputItem; }
89
90void Model::addItem(Item* i) {
91 _items.push_back(i);
92 if (i->isa<SolveI>()) {
93 Model* m = this;
94 while (m->_parent != nullptr) {
95 m = m->_parent;
96 }
97 m->_solveItem = i->cast<SolveI>();
98 } else if (i->isa<OutputI>()) {
99 Model* m = this;
100 while (m->_parent != nullptr) {
101 m = m->_parent;
102 }
103 m->_outputItem = i->cast<OutputI>();
104 }
105}
106
107void Model::setOutputItem(OutputI* oi) {
108 Model* m = this;
109 while (m->_parent != nullptr) {
110 m = m->_parent;
111 }
112 m->_outputItem = oi;
113}
114
115namespace {
116/// Return lowest possible base type given other type-inst restrictions
117Type::BaseType lowest_bt(const Type& t) {
118 if (t.st() == Type::ST_SET && t.ti() == Type::TI_VAR) {
119 return Type::BT_INT;
120 }
121 return Type::BT_BOOL;
122}
123/// Return highest possible base type given other type-inst restrictions
124Type::BaseType highest_bt(const Type& t) {
125 if (t.st() == Type::ST_SET && t.ti() == Type::TI_VAR) {
126 return Type::BT_INT;
127 }
128 if (t.ti() == Type::TI_VAR || t.st() == Type::ST_SET) {
129 return Type::BT_FLOAT;
130 }
131 return Type::BT_ANN;
132}
133} // namespace
134
135void Model::addPolymorphicInstances(Model::FnEntry& fe, std::vector<FnEntry>& entries) {
136 entries.push_back(fe);
137 if (fe.isPolymorphic) {
138 FnEntry cur = fe;
139 std::vector<std::vector<Type*> > type_ids;
140
141 // First step: initialise all type variables to bool
142 // and collect them in the stack vector
143 for (unsigned int i = 0; i < cur.t.size(); i++) {
144 if (cur.t[i].bt() == Type::BT_TOP) {
145 std::vector<Type*> t;
146 for (unsigned int j = i; j < cur.t.size(); j++) {
147 assert(cur.fi->params()[i]->ti()->domain() &&
148 cur.fi->params()[i]->ti()->domain()->isa<TIId>());
149 if ((cur.fi->params()[j]->ti()->domain() != nullptr) &&
150 cur.fi->params()[j]->ti()->domain()->isa<TIId>()) {
151 TIId* id0 = cur.fi->params()[i]->ti()->domain()->cast<TIId>();
152 TIId* id1 = cur.fi->params()[j]->ti()->domain()->cast<TIId>();
153 if (id0->v() == id1->v()) {
154 // Found parameter with same type variable
155 // Initialise to lowest concrete base type (bool)
156 cur.t[j].bt(lowest_bt(cur.t[j]));
157 t.push_back(&cur.t[j]);
158 }
159 }
160 }
161 type_ids.push_back(t);
162 }
163 }
164
165 std::vector<int> stack;
166 for (unsigned int i = 0; i < type_ids.size(); i++) {
167 stack.push_back(i);
168 }
169 int final_id = static_cast<int>(type_ids.size()) - 1;
170
171 while (!stack.empty()) {
172 if (stack.back() == final_id) {
173 // If this instance isn't in entries yet, add it
174 bool alreadyDefined = false;
175 for (auto& entry : entries) {
176 if (entry.t == cur.t) {
177 alreadyDefined = true;
178 break;
179 }
180 }
181 if (!alreadyDefined) {
182 entries.push_back(cur);
183 }
184 }
185
186 Type& back_t = *type_ids[stack.back()][0];
187 if (back_t.bt() == highest_bt(back_t) && back_t.st() == Type::ST_SET) {
188 // last type, remove this item
189 stack.pop_back();
190 } else {
191 if (back_t.bt() == highest_bt(back_t)) {
192 // Create set type for current item
193 for (auto& i : type_ids[stack.back()]) {
194 i->st(Type::ST_SET);
195 i->bt(lowest_bt(*i));
196 }
197 } else {
198 // Increment type of current item
199 auto nextType = static_cast<Type::BaseType>(back_t.bt() + 1);
200 for (auto& i : type_ids[stack.back()]) {
201 i->bt(nextType);
202 }
203 }
204 // Reset types of all further items and push them
205 for (unsigned int i = stack.back() + 1; i < type_ids.size(); i++) {
206 for (auto& j : type_ids[i]) {
207 j->bt(lowest_bt(*j));
208 }
209 stack.push_back(i);
210 }
211 }
212 }
213 }
214}
215
216bool Model::registerFn(EnvI& env, FunctionI* fi, bool keepSorted, bool throwIfDuplicate) {
217 Model* m = this;
218 while (m->_parent != nullptr) {
219 m = m->_parent;
220 }
221 auto i_id = m->_fnmap.find(fi->id());
222 if (i_id == m->_fnmap.end()) {
223 // new element
224 std::vector<FnEntry> v;
225 FnEntry fe(fi);
226 addPolymorphicInstances(fe, v);
227 m->_fnmap.insert(std::pair<ASTString, std::vector<FnEntry> >(fi->id(), v));
228 } else {
229 // add to list of existing elements
230 std::vector<FnEntry>& v = i_id->second;
231 for (auto& i : v) {
232 if (i.fi == fi) {
233 return true;
234 }
235 if (i.fi->params().size() == fi->params().size()) {
236 bool alleq = true;
237 for (unsigned int j = 0; j < fi->params().size(); j++) {
238 Type t1 = i.fi->params()[j]->type();
239 Type t2 = fi->params()[j]->type();
240 t1.enumId(0);
241 t2.enumId(0);
242 if (t1 != t2) {
243 alleq = false;
244 break;
245 }
246 }
247 if (alleq) {
248 if ((i.fi->e() != nullptr) && (fi->e() != nullptr) && !i.isPolymorphic) {
249 if (throwIfDuplicate) {
250 throw TypeError(
251 env, fi->loc(),
252 "function with the same type already defined in " + i.fi->loc().toString());
253 }
254 return false;
255 }
256 if ((fi->e() != nullptr) || i.isPolymorphic) {
257 if (Call* deprecated = i.fi->ann().getCall(constants().ann.mzn_deprecated)) {
258 fi->ann().add(deprecated);
259 }
260 i = fi;
261 } else if (Call* deprecated = fi->ann().getCall(constants().ann.mzn_deprecated)) {
262 i.fi->ann().add(deprecated);
263 }
264 return true;
265 }
266 }
267 }
268 FnEntry fe(fi);
269 addPolymorphicInstances(fe, v);
270 if (keepSorted) {
271 std::sort(i_id->second.begin(), i_id->second.end());
272 }
273 }
274 if (fi->id() == "mzn_reverse_map_var") {
275 if (fi->params().size() != 1 || fi->ti()->type() != Type::varbool()) {
276 throw TypeError(env, fi->loc(),
277 "functions called `mzn_reverse_map_var` must have a single argument and "
278 "return type var bool");
279 }
280 Type t = fi->params()[0]->type();
281 _revmapmap.insert(std::pair<int, FunctionI*>(t.toInt(), fi));
282 }
283 return true;
284}
285
286FunctionI* Model::matchFn(EnvI& env, const ASTString& id, const std::vector<Type>& t,
287 bool strictEnums) {
288 if (id == constants().varRedef->id()) {
289 return constants().varRedef;
290 }
291 Model* m = this;
292 while (m->_parent != nullptr) {
293 m = m->_parent;
294 }
295 auto i_id = m->_fnmap.find(id);
296 if (i_id == m->_fnmap.end()) {
297 return nullptr;
298 }
299 std::vector<FnEntry>& v = i_id->second;
300 for (auto& i : v) {
301 std::vector<Type>& fi_t = i.t;
302#ifdef MZN_DEBUG_FUNCTION_REGISTRY
303 std::cerr << "try " << *i.fi;
304#endif
305 if (fi_t.size() == t.size()) {
306 bool match = true;
307 for (unsigned int j = 0; j < t.size(); j++) {
308 if (!env.isSubtype(t[j], fi_t[j], strictEnums)) {
309#ifdef MZN_DEBUG_FUNCTION_REGISTRY
310 std::cerr << t[j].toString(env) << " does not match " << fi_t[j].toString(env) << "\n";
311#endif
312 match = false;
313 break;
314 }
315 }
316 if (match) {
317 return i.fi;
318 }
319 }
320 }
321 return nullptr;
322}
323
324void Model::mergeStdLib(EnvI& env, Model* m) const {
325 for (const auto& it : _fnmap) {
326 for (const auto& cit : it.second) {
327 if (cit.fi->fromStdLib()) {
328 (void)m->registerFn(env, cit.fi);
329 }
330 }
331 }
332 m->sortFn();
333}
334
335void Model::sortFn() {
336 Model* m = this;
337 while (m->_parent != nullptr) {
338 m = m->_parent;
339 }
340 for (auto& it : m->_fnmap) {
341 // Sort all functions by type
342 std::sort(it.second.begin(), it.second.end());
343 }
344}
345
346void Model::fixFnMap() {
347 Model* m = this;
348 while (m->_parent != nullptr) {
349 m = m->_parent;
350 }
351 for (auto& it : m->_fnmap) {
352 for (auto& i : it.second) {
353 for (unsigned int j = 0; j < i.t.size(); j++) {
354 if (i.t[j].isunknown()) {
355 i.t[j] = i.fi->params()[j]->type();
356 }
357 }
358 }
359 }
360}
361
362void Model::checkFnOverloading(EnvI& env) {
363 Model* m = this;
364 while (m->_parent != nullptr) {
365 m = m->_parent;
366 }
367 for (auto& it : m->_fnmap) {
368 std::vector<FnEntry>& fs = it.second;
369 for (unsigned int i = 0; i < fs.size() - 1; i++) {
370 FunctionI* cur = fs[i].fi;
371 for (unsigned int j = i + 1; j < fs.size(); j++) {
372 FunctionI* cmp = fs[j].fi;
373 if (cur == cmp || cur->params().size() != cmp->params().size()) {
374 break;
375 }
376 bool allEqual = true;
377 for (unsigned int i = 0; i < cur->params().size(); i++) {
378 Type t1 = cur->params()[i]->type();
379 Type t2 = cmp->params()[i]->type();
380 t1.enumId(0);
381 t2.enumId(0);
382 if (t1 != t2) {
383 allEqual = false;
384 break;
385 }
386 }
387 if (allEqual) {
388 throw TypeError(env, cur->loc(),
389 "unsupported type of overloading. \nFunction/predicate with equivalent "
390 "signature defined in " +
391 cmp->loc().toString());
392 }
393 }
394 }
395 }
396}
397
398namespace {
399int match_idx(std::vector<FunctionI*>& matched, Expression*& botarg, EnvI& env,
400 const std::vector<Model::FnEntry>& v, const std::vector<Expression*>& args,
401 bool strictEnums) {
402 botarg = nullptr;
403 for (unsigned int i = 0; i < v.size(); i++) {
404 const std::vector<Type>& fi_t = v[i].t;
405#ifdef MZN_DEBUG_FUNCTION_REGISTRY
406 std::cerr << "try " << *v[i].fi;
407#endif
408 if (fi_t.size() == args.size()) {
409 bool match = true;
410 for (unsigned int j = 0; j < args.size(); j++) {
411 if (!env.isSubtype(args[j]->type(), fi_t[j], strictEnums)) {
412#ifdef MZN_DEBUG_FUNCTION_REGISTRY
413 std::cerr << args[j]->type().toString(env) << " does not match " << fi_t[j].toString(env)
414 << "\n";
415#endif
416 match = false;
417 break;
418 }
419 if (args[j]->type().isbot() && fi_t[j].bt() != Type::BT_TOP) {
420 botarg = args[j];
421 }
422 }
423 if (match) {
424 matched.push_back(v[i].fi);
425 if (botarg == nullptr) {
426 return static_cast<int>(i);
427 }
428 }
429 }
430 }
431 return -1;
432}
433} // namespace
434
435FunctionI* Model::matchFn(EnvI& env, const ASTString& id, const std::vector<Expression*>& args,
436 bool strictEnums) const {
437 if (id == constants().varRedef->id()) {
438 return constants().varRedef;
439 }
440 const Model* m = this;
441 while (m->_parent != nullptr) {
442 m = m->_parent;
443 }
444 auto it = m->_fnmap.find(id);
445 if (it == m->_fnmap.end()) {
446 return nullptr;
447 }
448 const std::vector<FnEntry>& v = it->second;
449 std::vector<FunctionI*> matched;
450 Expression* botarg;
451 (void)match_idx(matched, botarg, env, v, args, strictEnums);
452 if (matched.empty()) {
453 return nullptr;
454 }
455 if (matched.size() == 1) {
456 return matched[0];
457 }
458 Type t = matched[0]->ti()->type();
459 t.ti(Type::TI_PAR);
460 for (unsigned int i = 1; i < matched.size(); i++) {
461 if (!env.isSubtype(t, matched[i]->ti()->type(), strictEnums)) {
462 throw TypeError(env, botarg->loc(), "ambiguous overloading on return type of function");
463 }
464 }
465 return matched[0];
466}
467
468FunctionI* Model::matchFn(EnvI& env, Call* c, bool strictEnums, bool throwIfNotFound) const {
469 if (c->id() == constants().varRedef->id()) {
470 return constants().varRedef;
471 }
472 const Model* m = this;
473 while (m->_parent != nullptr) {
474 m = m->_parent;
475 }
476 auto it = m->_fnmap.find(c->id());
477 if (it == m->_fnmap.end()) {
478 if (throwIfNotFound) {
479 std::ostringstream oss;
480 oss << "no function or predicate with name `";
481 oss << c->id() << "' found";
482
483 ASTString mostSimilar;
484 int minEdits = 3;
485 for (const auto& decls : m->_fnmap) {
486 if (std::abs(static_cast<int>(c->id().size()) - static_cast<int>(decls.first.size())) <=
487 3) {
488 int edits = c->id().levenshteinDistance(decls.first);
489 if (edits < minEdits && edits < std::min(c->id().size(), decls.first.size())) {
490 minEdits = edits;
491 mostSimilar = decls.first;
492 }
493 }
494 }
495 if (mostSimilar.size() > 0) {
496 oss << ", did you mean `" << mostSimilar << "'?";
497 }
498 throw TypeError(env, c->loc(), oss.str());
499 }
500 return nullptr;
501 }
502 const std::vector<FnEntry>& v = it->second;
503 std::vector<FunctionI*> matched;
504 Expression* botarg = nullptr;
505 for (const auto& i : v) {
506 const std::vector<Type>& fi_t = i.t;
507#ifdef MZN_DEBUG_FUNCTION_REGISTRY
508 std::cerr << "try " << *i.fi;
509#endif
510 if (fi_t.size() == c->argCount()) {
511 bool match = true;
512 for (unsigned int j = 0; j < c->argCount(); j++) {
513 if (!env.isSubtype(c->arg(j)->type(), fi_t[j], strictEnums)) {
514#ifdef MZN_DEBUG_FUNCTION_REGISTRY
515 std::cerr << c->arg(j)->type().toString(env) << " does not match "
516 << fi_t[j].toString(env) << "\n";
517 std::cerr << "Wrong argument is " << *c->arg(j);
518#endif
519 match = false;
520 break;
521 }
522 if (c->arg(j)->type().isbot() && fi_t[j].bt() != Type::BT_TOP) {
523 botarg = c->arg(j);
524 }
525 }
526 if (match) {
527 if (botarg != nullptr) {
528 matched.push_back(i.fi);
529 } else {
530 return i.fi;
531 }
532 }
533 }
534 }
535 if (matched.empty()) {
536 if (throwIfNotFound) {
537 std::ostringstream oss;
538 oss << "no function or predicate with this signature found: `";
539 oss << c->id() << "(";
540 for (unsigned int i = 0; i < c->argCount(); i++) {
541 oss << c->arg(i)->type().toString(env);
542 if (i < c->argCount() - 1) {
543 oss << ",";
544 }
545 }
546 oss << ")'\n";
547 oss << "Cannot use the following functions or predicates with the same identifier:\n";
548 Printer pp(oss, 0, false, &env);
549 for (const auto& i : v) {
550 const std::vector<Type>& fi_t = i.t;
551 Expression* body = i.fi->e();
552 i.fi->e(nullptr);
553 pp.print(i.fi);
554 i.fi->e(body);
555 if (fi_t.size() == c->argCount()) {
556 for (unsigned int j = 0; j < c->argCount(); j++) {
557 if (!env.isSubtype(c->arg(j)->type(), fi_t[j], strictEnums)) {
558 oss << " (argument " << (j + 1) << " expects type " << fi_t[j].toString(env);
559 oss << ", but type " << c->arg(j)->type().toString(env) << " given)\n";
560 }
561 if (c->arg(j)->type().isbot() && fi_t[j].bt() != Type::BT_TOP) {
562 botarg = c->arg(j);
563 }
564 }
565 } else {
566 oss << " (requires " << i.fi->params().size() << " argument"
567 << (i.fi->params().size() == 1 ? "" : "s") << ", but " << c->argCount()
568 << " given)\n";
569 }
570 }
571 throw TypeError(env, c->loc(), oss.str());
572 }
573 return nullptr;
574 }
575 if (matched.size() == 1) {
576 return matched[0];
577 }
578 Type t = matched[0]->ti()->type();
579 t.ti(Type::TI_PAR);
580 for (unsigned int i = 1; i < matched.size(); i++) {
581 if (!env.isSubtype(t, matched[i]->ti()->type(), strictEnums)) {
582 throw TypeError(env, botarg->loc(), "ambiguous overloading on return type of function");
583 }
584 }
585 return matched[0];
586}
587
588namespace {
589int first_overloaded(EnvI& env, const std::vector<Model::FnEntry>& v_f, int i_f) {
590 int first_i_f = i_f;
591 for (; (first_i_f--) != 0;) {
592 // find first instance overloaded on subtypes
593 if (v_f[first_i_f].t.size() != v_f[i_f].t.size()) {
594 break;
595 }
596 bool allSubtypes = true;
597 for (unsigned int i = 0; i < v_f[first_i_f].t.size(); i++) {
598 if (!env.isSubtype(v_f[first_i_f].t[i], v_f[i_f].t[i], false)) {
599 allSubtypes = false;
600 break;
601 }
602 }
603 if (!allSubtypes) {
604 break;
605 }
606 }
607 return first_i_f + 1;
608}
609} // namespace
610
611bool Model::sameOverloading(EnvI& env, const std::vector<Expression*>& args, FunctionI* f,
612 FunctionI* g) const {
613 const Model* m = this;
614 while (m->_parent != nullptr) {
615 m = m->_parent;
616 }
617 auto it_f = m->_fnmap.find(f->id());
618 auto it_g = m->_fnmap.find(g->id());
619 assert(it_f != m->_fnmap.end());
620 assert(it_g != m->_fnmap.end());
621 const std::vector<FnEntry>& v_f = it_f->second;
622 const std::vector<FnEntry>& v_g = it_g->second;
623
624 std::vector<FunctionI*> dummyMatched;
625 Expression* dummyBotarg;
626 int i_f = match_idx(dummyMatched, dummyBotarg, env, v_f, args, true);
627 if (i_f == -1) {
628 return false;
629 }
630 int i_g = match_idx(dummyMatched, dummyBotarg, env, v_g, args, true);
631 if (i_g == -1) {
632 return false;
633 }
634 assert(i_f < v_f.size());
635 assert(i_g < v_g.size());
636 unsigned int first_i_f = first_overloaded(env, v_f, i_f);
637 unsigned int first_i_g = first_overloaded(env, v_g, i_g);
638 if (i_f - first_i_f != i_g - first_i_g) {
639 // not the same number of overloaded versions
640 return false;
641 }
642 for (; first_i_f <= i_f; first_i_f++, first_i_g++) {
643 if (!(v_f[first_i_f].t == v_g[first_i_g].t)) {
644 // one of the overloaded versions does not agree in the types
645 return false;
646 }
647 }
648 return true;
649}
650
651FunctionI* Model::matchRevMap(EnvI& env, const Type& t0) const {
652 const Model* m = this;
653 while (m->_parent != nullptr) {
654 m = m->_parent;
655 }
656 Type t = t0;
657 t.enumId(0);
658 auto it = _revmapmap.find(t.toInt());
659 if (it != _revmapmap.end()) {
660 return it->second;
661 }
662 return nullptr;
663}
664
665Item*& Model::operator[](unsigned int i) {
666 assert(i < _items.size());
667 return _items[i];
668}
669const Item* Model::operator[](unsigned int i) const {
670 assert(i < _items.size());
671 return _items[i];
672}
673unsigned int Model::size() const { return static_cast<unsigned int>(_items.size()); }
674
675std::vector<Item*>::iterator Model::begin() { return _items.begin(); }
676
677std::vector<Item*>::const_iterator Model::begin() const { return _items.begin(); }
678
679std::vector<Item*>::iterator Model::end() { return _items.end(); }
680
681std::vector<Item*>::const_iterator Model::end() const { return _items.end(); }
682
683void Model::compact() {
684 struct {
685 bool operator()(const Item* i) { return i->removed(); }
686 } isremoved;
687 _items.erase(remove_if(_items.begin(), _items.end(), isremoved), _items.end());
688}
689
690} // namespace MiniZinc