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