1 //: Transform to maintain multiple variants of a recipe depending on the
  2 //: number and types of the ingredients and products. Allows us to use nice
  3 //: names like 'print' or 'length' in many mutually extensible ways.
  4 
  5 :(scenario static_dispatch)
  6 def main [
  7   7:num/raw <- test 3
  8 ]
  9 def test a:num -> z:num [
 10   z <- copy 1
 11 ]
 12 def test a:num, b:num -> z:num [
 13   z <- copy 2
 14 ]
 15 +mem: storing 1 in location 7
 16 
 17 //: When loading recipes, accumulate variants if headers don't collide, and
 18 //: flag an error if headers collide.
 19 
 20 :(before "End Globals")
 21 map<string, vector<recipe_ordinal> > Recipe_variants;
 22 :(before "End One-time Setup")
 23 put(Recipe_variants, "main", vector<recipe_ordinal>());  // since we manually added main to Recipe_ordinal
 24 
 25 :(before "End Globals")
 26 map<string, vector<recipe_ordinal> > Recipe_variants_snapshot;
 27 :(before "End save_snapshots")
 28 Recipe_variants_snapshot = Recipe_variants;
 29 :(before "End restore_snapshots")
 30 Recipe_variants = Recipe_variants_snapshot;
 31 
 32 :(before "End Load Recipe Header(result)")
 33 // there can only ever be one variant for main
 34 if (result.name != "main" && contains_key(Recipe_ordinal, result.name)) {
 35   const recipe_ordinal r = get(Recipe_ordinal, result.name);
 36   if (!contains_key(Recipe, r) || get(Recipe, r).has_header) {
 37   ¦ string new_name = matching_variant_name(result);
 38   ¦ if (new_name.empty()) {
 39   ¦ ¦ // variant doesn't already exist
 40   ¦ ¦ new_name = next_unused_recipe_name(result.name);
 41   ¦ ¦ put(Recipe_ordinal, new_name, Next_recipe_ordinal++);
 42   ¦ ¦ get_or_insert(Recipe_variants, result.name).push_back(get(Recipe_ordinal, new_name));
 43   ¦ }
 44   ¦ trace(9999, "load") << "switching " << result.name << " to " << new_name << end();
 45   ¦ result.name = new_name;
 46   ¦ result.is_autogenerated = true;
 47   }
 48 }
 49 else {
 50   // save first variant
 51   put(Recipe_ordinal, result.name, Next_recipe_ordinal++);
 52   get_or_insert(Recipe_variants, result.name).push_back(get(Recipe_ordinal, result.name));
 53 }
 54 
 55 :(code)
 56 string matching_variant_name(const recipe& rr) {
 57   const vector<recipe_ordinal>& variants = get_or_insert(Recipe_variants, rr.name);
 58   for (int i = 0;  i < SIZE(variants);  ++ititle>Mu - screen.mu</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="none">
<meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #aaaaaa; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #aaaaaa; background-color: #080808; }
a { color:#eeeeee; text-decoration: none; }
a:hover { text-decoration: underline; }
* { font-size: 12pt; font-size: 1em; }
.muRecipe { color: #ff8700; }
.Special { color: #c00000; }
.Comment { color: #9090ff; }
.Comment a { color:#0000ee; text-decoration:underline; }
.Constant { color: #00a0a0; }
.LineNr { color: #444444; }
-->
</style>

<script type='text/javascript'>
<!--

/* function to open any folds containing a jumped-to line before jumping to it */
function JumpToLine()
{
  var lineNum;
  lineNum = window.location.hash;
  lineNum = lineNum.substr(1); /* strip off '#' */

  if (lineNum.indexOf('L') == -1) {
    lineNum = 'L'+lineNum;
  }
  lineElem = document.getElementById(lineNum);
  /* Always jump to new location even if the line was hidden inside a fold, or
   * we corrected the raw number to a line ID.
   */
  if (lineElem) {
    lineElem.scrollIntoView(true);
  }
  return true;
}
if ('onhashchange' in window) {
  window.onhashchange = JumpToLine;
}

-->
</script>
</head>
<body onload='JumpToLine();'>
<pre id='vimCodeElement'>
<span id="L1" class="LineNr"> 1 </span><span class="Comment"># example program: managing the display using 'screen' objects</span>
<span id="L2" class="LineNr"> 2 </span>
<span id="L3" class="LineNr"> 3 </span><span class="Comment"># The zero screen below means 'use the real screen'. Tests can also use fake</span>
<span id="L4" class="LineNr"> 4 </span><span class="Comment"># screens.</span>
<span id="L5" class="LineNr"> 5 </span><span class="muRecipe">def</span> <a href='screen.mu.html#L5'>main</a> [
<span id="L6" class="LineNr"> 6 </span>  open-console
<span id="L7" class="LineNr"> 7 </span>  <a href='081print.mu.html#L46'>clear-screen</a> <span class="Constant">0/screen</span>  <span class="Comment"># non-scrolling app</span>
<span id="L8" class="LineNr"> 8 </span>  10:char <span class="Special">&lt;-</span> copy <span class="Constant">97/a</span>
<span id="L9" class="LineNr"> 9 </span>  print <span class="Constant">0/screen</span>, 10:char/a, <span class="Constant">1/red</span>, <span class="Constant">2/green</span>
<span id="L10" class="LineNr">10 </span>  1:num/<span class="Special">raw</span>, 2:num/<span class="Special">raw</span> <span class="Special">&lt;-</span> <a href='081print.mu.html#L577'>cursor-position</a> <span class="Constant">0/screen</span>
<span id="L11" class="LineNr">11 </span>  <a href='084console.mu.html#L88'>wait-for-event</a> <span class="Constant">0/console</span>
<span id="L12" class="LineNr">12 </span>  <a href='081print.mu.html#L46'>clear-screen</a> <span class="Constant">0/screen</span>
<span id="L13" class="LineNr">13 </span>  <a href='081print.mu.html#L591'>move-cursor</a> <span class="Constant">0/screen</span>, <span class="Constant">0/row</span>, <span class="Constant">4/column</span>
<span id="L14" class="LineNr">14 </span>  10:char <span class="Special">&lt;-</span> copy <span class="Constant">98/b</span>
<span id="L15" class="LineNr">15 </span>  print <span class="Constant">0/screen</span>, 10:char
<span id="L16" class="LineNr">16 </span>  <a href='084console.mu.html#L88'>wait-for-event</a> <span class="Constant">0/console</span>
<span id="L17" class="LineNr">17 </span>  <a href='081print.mu.html#L591'>move-cursor</a> <span class="Constant">0/screen</span>, <span class="Constant">0/row</span>, <span class="Constant">0/column</span>
<span id="L18" class="LineNr">18 </span>  <a href='081print.mu.html#L524'>clear-line</a> <span class="Constant">0/screen</span>
<span id="L19" class="LineNr">19 </span>  <a href='084console.mu.html#L88'>wait-for-event</a> <span class="Constant">0/console</span>
<span id="L20" class="LineNr">20 </span>  <a href='081print.mu.html#L645'>cursor-down</a> <span class="Constant">0/screen</span>
<span id="L21" class="LineNr">21 </span>  <a href='084console.mu.html#L88'>wait-for-event</a> <span class="Constant">0/console</span>
<span id="L22" class="LineNr">22 </span>  <a href='081print.mu.html#L706'>cursor-right</a> <span class="Constant">0/screen</span>
<span id="L23" class="LineNr">23 </span>  <a href='084console.mu.html#L88'>wait-for-event</a> <span class="Constant">0/console</span>
<span id="L24" class="LineNr">24 </span>  <a href='081print.mu.html#L726'>cursor-left</a> <span class="Constant">0/screen</span>
<span id="L25" class="LineNr">25 </span>  <a href='084console.mu.html#L88'>wait-for-event</a> <span class="Constant">0/console</span>
<span id="L26" class="LineNr">26 </span>  <a href='081print.mu.html#L688'>cursor-up</a> <span class="Constant">0/screen</span>
<span id="L27" class="LineNr">27 </span>  <a href='084console.mu.html#L88'>wait-for-event</a> <span class="Constant">0/console</span>
<span id="L28" class="LineNr">28 </span>  close-console
<span id="L29" class="LineNr">29 </span>]
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->
span> 221 ¦ ¦ // One special-case to help with the rewrite_stash transform. (cross-layer) 222 ¦ ¦ if (specializer_inst.products.at(0).name.find("stash_") == 0) { 223 ¦ ¦ ¦ instruction stash_inst; 224 ¦ ¦ ¦ if (next_stash(*p, &stash_inst)) { 225 ¦ ¦ ¦ ¦ if (specializer_recipe.name != "interactive") 226 ¦ ¦ ¦ ¦ ¦ raise << " (part of '" << to_original_string(stash_inst) << "' in " << specializer_recipe.name << ")\n" << end(); 227 ¦ ¦ ¦ ¦ else 228 ¦ ¦ ¦ ¦ ¦ raise << " (part of '" << to_original_string(stash_inst) << "')\n" << end(); 229 ¦ ¦ ¦ } 230 ¦ ¦ } 231 ¦ } 232 } 233 return ""; 234 } 235 236 // phase 1 237 vector<recipe_ordinal> strictly_matching_variants(const instruction& inst, vector<recipe_ordinal>& variants) { 238 vector<recipe_ordinal> result; 239 for (int i = 0; i < SIZE(variants); ++i) { 240 ¦ if (variants.at(i) == -1) continue; 241 ¦ trace(9992, "transform") << "checking variant (strict) " << i << ": " << header_label(variants.at(i)) << end(); 242 ¦ if (all_header_reagents_strictly_match(inst, get(Recipe, variants.at(i)))) 243 ¦ ¦ result.push_back(variants.at(i)); 244 } 245 return result; 246 } 247 248 bool all_header_reagents_strictly_match(const instruction& inst, const recipe& variant) { 249 for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) { 250 ¦ if (!types_strictly_match(variant.ingredients.at(i), inst.ingredients.at(i))) { 251 ¦ ¦ trace(9993, "transform") << "strict match failed: ingredient " << i << end(); 252 ¦ ¦ return false; 253 ¦ } 254 } 255 for (int i = 0; i < min(SIZE(inst.products), SIZE(variant.products)); ++i) { 256 ¦ if (is_dummy(inst.products.at(i))) continue; 257 ¦ if (!types_strictly_match(variant.products.at(i), inst.products.at(i))) { 258 ¦ ¦ trace(9993, "transform") << "strict match failed: product " << i << end(); 259 ¦ ¦ return false; 260 ¦ } 261 } 262 return true; 263 } 264 265 // phase 2 266 vector<recipe_ordinal> strictly_matching_variants_except_literal_against_address_or_boolean(const instruction& inst, vector<recipe_ordinal>& variants) { 267 vector<recipe_ordinal> result; 268 for (int i = 0; i < SIZE(variants); ++i) { 269 ¦ if (variants.at(i) == -1) continue; 270 ¦ trace(9992, "transform") << "checking variant (strict except literal-against-boolean) " << i << ": " << header_label(variants.at(i)) << end(); 271 ¦ if (all_header_reagents_strictly_match_except_literal_against_address_or_boolean(inst, get(Recipe, variants.at(i)))) 272 ¦ ¦ result.push_back(variants.at(i)); 273 } 274 return result; 275 } 276 277 bool all_header_reagents_strictly_match_except_literal_against_address_or_boolean(const instruction& inst, const recipe& variant) { 278 for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) { 279 ¦ if (!types_strictly_match_except_literal_against_address_or_boolean(variant.ingredients.at(i), inst.ingredients.at(i))) { 280 ¦ ¦ trace(9993, "transform") << "match failed: ingredient " << i << end(); 281 ¦ ¦ return false; 282 ¦ } 283 } 284 for (int i = 0; i < min(SIZE(variant.products), SIZE(inst.products)); ++i) { 285 ¦ if (is_dummy(inst.products.at(i))) continue; 286 ¦ if (!types_strictly_match_except_literal_against_address_or_boolean(variant.products.at(i), inst.products.at(i))) { 287 ¦ ¦ trace(9993, "transform") << "match failed: product " << i << end(); 288 ¦ ¦ return false; 289 ¦ } 290 } 291 return true; 292 } 293 294 bool types_strictly_match_except_literal_against_address_or_boolean(const reagent& to, const reagent& from) { 295 if (is_literal(from) && is_mu_boolean(to)) 296 ¦ return from.name == "0" || from.name == "1"; 297 // Match Literal Zero Against Address { 298 if (is_literal(from) && is_mu_address(to)) 299 ¦ return from.name == "0"; 300 // } 301 return types_strictly_match(to, from); 302 } 303 304 // phase 4 305 vector<recipe_ordinal> matching_variants(const instruction& inst, vector<recipe_ordinal>& variants) { 306 vector<recipe_ordinal> result; 307 for (int i = 0; i < SIZE(variants); ++i) { 308 ¦ if (variants.at(i) == -1) continue; 309 ¦ trace(9992, "transform") << "checking variant " << i << ": " << header_label(variants.at(i)) << end(); 310 ¦ if (all_header_reagents_match(inst, get(Recipe, variants.at(i)))) 311 ¦ ¦ result.push_back(variants.at(i)); 312 } 313 return result; 314 } 315 316 bool all_header_reagents_match(const instruction& inst, const recipe& variant) { 317 for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) { 318 ¦ if (!types_match(variant.ingredients.at(i), inst.ingredients.at(i))) { 319 ¦ ¦ trace(9993, "transform") << "match failed: ingredient " << i << end(); 320 ¦ ¦ return false; 321 ¦ } 322 } 323 for (int i = 0; i < min(SIZE(variant.products), SIZE(inst.products)); ++i) { 324 ¦ if (is_dummy(inst.products.at(i))) continue; 325 ¦ if (!types_match(variant.products.at(i), inst.products.at(i))) { 326 ¦ ¦ trace(9993, "transform") << "match failed: product " << i << end(); 327 ¦ ¦ return false; 328 ¦ } 329 } 330 return true; 331 } 332 333 // tie-breaker for each phase 334 const recipe& best_variant(const instruction& inst, vector<recipe_ordinal>& candidates) { 335 assert(!candidates.empty()); 336 int min_score = 999; 337 int min_index = 0; 338 for (int i = 0; i < SIZE(candidates); ++i) { 339 ¦ const recipe& candidate = get(Recipe, candidates.at(i)); 340 ¦ // prefer functions without extra or missing ingredients or products 341 ¦ int score = abs(SIZE(candidate.products)-SIZE(inst.products)) 342 ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ + abs(SIZE(candidate.ingredients)-SIZE(inst.ingredients)); 343 ¦ // prefer functions with non-address ingredients or products 344 ¦ for (int j = 0; j < SIZE(candidate.ingredients); ++j) { 345 ¦ ¦ if (is_mu_address(candidate.ingredients.at(j))) 346 ¦ ¦ ¦ ++score; 347 ¦ } 348 ¦ for (int j = 0; j < SIZE(candidate.products); ++j) { 349 ¦ ¦ if (is_mu_address(candidate.products.at(j))) 350 ¦ ¦ ¦ ++score; 351 ¦ } 352 ¦ assert(score < 999); 353 ¦ if (score < min_score) { 354 ¦ ¦ min_score = score; 355 ¦ ¦ min_index = i; 356 ¦ } 357 } 358 return get(Recipe, candidates.at(min_index)); 359 } 360 361 int non_ghost_size(vector<recipe_ordinal>& variants) { 362 int result = 0; 363 for (int i = 0; i < SIZE(variants); ++i) 364 ¦ if (variants.at(i) != -1) ++result; 365 return result; 366 } 367 368 bool next_stash(const call& c, instruction* stash_inst) { 369 const recipe& specializer_recipe = get(Recipe, c.running_recipe); 370 int index = c.running_step_index; 371 for (++index; index < SIZE(specializer_recipe.steps); ++index) { 372 ¦ const instruction& inst = specializer_recipe.steps.at(index); 373 ¦ if (inst.name == "stash") { 374 ¦ ¦ *stash_inst = inst; 375 ¦ ¦ return true; 376 ¦ } 377 } 378 return false; 379 } 380 381 :(scenario static_dispatch_disabled_in_recipe_without_variants) 382 def main [ 383 1:num <- test 3 384 ] 385 def test [ 386 2:num <- next-ingredient # ensure no header 387 return 34 388 ] 389 +mem: storing 34 in location 1 390 391 :(scenario static_dispatch_disabled_on_headerless_definition) 392 % Hide_errors = true; 393 def test a:num -> z:num [ 394 z <- copy 1 395 ] 396 def test [ 397 return 34 398 ] 399 +error: redefining recipe test 400 401 :(scenario static_dispatch_disabled_on_headerless_definition_2) 402 % Hide_errors = true; 403 def test [ 404 return 34 405 ] 406 def test a:num -> z:num [ 407 z <- copy 1 408 ] 409 +error: redefining recipe test 410 411 :(scenario static_dispatch_on_primitive_names) 412 def main [ 413 1:num <- copy 34 414 2:num <- copy 34 415 3:bool <- equal 1:num, 2:num 416 4:bool <- copy 0/false 417 5:bool <- copy 0/false 418 6:bool <- equal 4:bool, 5:bool 419 ] 420 # temporarily hardcode number equality to always fail 421 def equal x:num, y:num -> z:bool [ 422 local-scope 423 load-ingredients 424 z <- copy 0/false 425 ] 426 # comparing numbers used overload 427 +mem: storing 0 in location 3 428 # comparing booleans continues to use primitive 429 +mem: storing 1 in location 6 430 431 :(scenario static_dispatch_works_with_dummy_results_for_containers) 432 def main [ 433 _ <- test 3, 4 434 ] 435 def test a:num -> z:point [ 436 local-scope 437 load-ingredients 438 z <- merge a, 0 439 ] 440 def test a:num, b:num -> z:point [ 441 local-scope 442 load-ingredients 443 z <- merge a, b 444 ] 445 $error: 0 446 447 :(scenario static_dispatch_works_with_compound_type_containing_container_defined_after_first_use) 448 def main [ 449 x:&:foo <- new foo:type 450 test x 451 ] 452 container foo [ 453 x:num 454 ] 455 def test a:&:foo -> z:num [ 456 local-scope 457 load-ingredients 458 z:num <- get *a, x:offset 459 ] 460 $error: 0 461 462 :(scenario static_dispatch_works_with_compound_type_containing_container_defined_after_second_use) 463 def main [ 464 x:&:foo <- new foo:type 465 test x 466 ] 467 def test a:&:foo -> z:num [ 468 local-scope 469 load-ingredients 470 z:num <- get *a, x:offset 471 ] 472 container foo [ 473 x:num 474 ] 475 $error: 0 476 477 :(scenario static_dispatch_prefers_literals_to_be_numbers_rather_than_addresses) 478 def main [ 479 1:num <- foo 0 480 ] 481 def foo x:&:num -> y:num [ 482 return 34 483 ] 484 def foo x:num -> y:num [ 485 return 35 486 ] 487 +mem: storing 35 in location 1 488 489 :(scenario static_dispatch_prefers_literals_to_be_numbers_rather_than_addresses_2) 490 def main [ 491 1:num <- foo 0 0 492 ] 493 # Both variants need to bind 0 to address in first ingredient. 494 # We still want to prefer the variant with a number rather than address for 495 # _subsequent_ ingredients. 496 def foo x:&:num y:&:num -> z:num [ # put the bad match before the good one 497 return 34 498 ] 499 def foo x:&:num y:num -> z:num [ 500 return 35 501 ] 502 +mem: storing 35 in location 1 503 504 :(scenario static_dispatch_on_non_literal_character_ignores_variant_with_numbers) 505 % Hide_errors = true; 506 def main [ 507 local-scope 508 x:char <- copy 10/newline 509 1:num/raw <- foo x 510 ] 511 def foo x:num -> y:num [ 512 load-ingredients 513 return 34 514 ] 515 +error: main: ingredient 0 has the wrong type at '1:num/raw <- foo x' 516 -mem: storing 34 in location 1 517 518 :(scenario static_dispatch_dispatches_literal_to_boolean_before_character) 519 def main [ 520 1:num/raw <- foo 0 # valid literal for boolean 521 ] 522 def foo x:char -> y:num [ 523 local-scope 524 load-ingredients 525 return 34 526 ] 527 def foo x:bool -> y:num [ 528 local-scope 529 load-ingredients 530 return 35 531 ] 532 # boolean variant is preferred 533 +mem: storing 35 in location 1 534 535 :(scenario static_dispatch_dispatches_literal_to_character_when_out_of_boolean_range) 536 def main [ 537 1:num/raw <- foo 97 # not a valid literal for boolean 538 ] 539 def foo x:char -> y:num [ 540 local-scope 541 load-ingredients 542 return 34 543 ] 544 def foo x:bool -> y:num [ 545 local-scope 546 load-ingredients 547 return 35 548 ] 549 # character variant is preferred 550 +mem: storing 34 in location 1 551 552 :(scenario static_dispatch_dispatches_literal_to_number_if_at_all_possible) 553 def main [ 554 1:num/raw <- foo 97 555 ] 556 def foo x:char -> y:num [ 557 local-scope 558 load-ingredients 559 return 34 560 ] 561 def foo x:num -> y:num [ 562 local-scope 563 load-ingredients 564 return 35 565 ] 566 # number variant is preferred 567 +mem: storing 35 in location 1 568 569 :(replace{} "string header_label(const recipe_ordinal r)") 570 string header_label(const recipe_ordinal r) { 571 return header_label(get(Recipe, r)); 572 } 573 :(code) 574 string header_label(const recipe& caller) { 575 ostringstream out; 576 out << "recipe " << caller.name; 577 for (int i = 0; i < SIZE(caller.ingredients); ++i) 578 ¦ out << ' ' << to_string(caller.ingredients.at(i)); 579 if (!caller.products.empty()) out << " ->"; 580 for (int i = 0; i < SIZE(caller.products); ++i) 581 ¦ out << ' ' << to_string(caller.products.at(i)); 582 return out.str(); 583 } 584 585 string original_header_label(const recipe& caller) { 586 ostringstream out; 587 out << "recipe " << caller.original_name; 588 for (int i = 0; i < SIZE(caller.ingredients); ++i) 589 ¦ out << ' ' << caller.ingredients.at(i).original_string; 590 if (!caller.products.empty()) out << " ->"; 591 for (int i = 0; i < SIZE(caller.products); ++i) 592 ¦ out << ' ' << caller.products.at(i).original_string; 593 return out.str(); 594 } 595 596 :(scenario reload_variant_retains_other_variants) 597 def main [ 598 1:num <- copy 34 599 2:num <- foo 1:num 600 ] 601 def foo x:num -> y:num [ 602 local-scope 603 load-ingredients 604 return 34 605 ] 606 def foo x:&:num -> y:num [ 607 local-scope 608 load-ingredients 609 return 35 610 ] 611 def! foo x:&:num -> y:num [ 612 local-scope 613 load-ingredients 614 return 36 615 ] 616 +mem: storing 34 in location 2 617 $error: 0 618 619 :(scenario dispatch_errors_come_after_unknown_name_errors) 620 % Hide_errors = true; 621 def main [ 622 y:num <- foo x 623 ] 624 def foo a:num -> b:num [ 625 local-scope 626 load-ingredients 627 return 34 628 ] 629 def foo a:bool -> b:num [ 630 local-scope 631 load-ingredients 632 return 35 633 ] 634 +error: main: missing type for 'x' in 'y:num <- foo x' 635 +error: main: failed to find a matching call for 'y:num <- foo x' 636 637 :(scenario override_methods_with_type_abbreviations) 638 def main [ 639 local-scope 640 s:text <- new [abc] 641 1:num/raw <- foo s 642 ] 643 def foo a:address:array:character -> result:number [ 644 return 34 645 ] 646 # identical to previous variant once you take type abbreviations into account 647 def! foo a:text -> result:num [ 648 return 35 649 ] 650 +mem: storing 35 in location 1 651 652 :(scenario ignore_static_dispatch_in_type_errors_without_overloading) 653 % Hide_errors = true; 654 def main [ 655 local-scope 656 x:&:num <- copy 0 657 foo x 658 ] 659 def foo x:&:char [ 660 local-scope 661 load-ingredients 662 ] 663 +error: main: types don't match in call for 'foo x' 664 +error: which tries to call 'recipe foo x:&:char' 665 666 :(scenario show_available_variants_in_dispatch_errors) 667 % Hide_errors = true; 668 def main [ 669 local-scope 670 x:&:num <- copy 0 671 foo x 672 ] 673 def foo x:&:char [ 674 local-scope 675 load-ingredients 676 ] 677 def foo x:&:bool [ 678 local-scope 679 load-ingredients 680 ] 681 +error: main: failed to find a matching call for 'foo x' 682 +error: available variants are: 683 +error: recipe foo x:&:char 684 +error: recipe foo x:&:bool 685 686 :(before "End Includes") 687 using std::abs;