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);  ++i) {
 59   ¦ if (!contains_key(Recipe, variants.at(i))) continue;
 60   ¦ const recipe& candidate = get(Recipe, variants.at(i));
 61   ¦ if (!all_reagents_match(rr, candidate)) continue;
 62   ¦ return candidate.name;
 63   }
 64   return "";
 65 }
 66 
 67 bool all_reagents_match(const recipe& r1, const recipe& r2) {
 68   if (SIZE(r1.ingredients) != SIZE(r2.ingredients)) return false;
 69   if (SIZE(r1.products) != SIZE(r2.products)) return false;
 70   for (int i = 0;  i < SIZE(r1.ingredients);  ++i) {
 71   ¦ expand_type_abbreviations(r1.ingredients.at(i).type);
 72   ¦ expand_type_abbreviations(r2.ingredients.at(i).type);
 73   ¦ if (!deeply_equal_type_names(r1.ingredients.at(i), r2.ingredients.at(i)))
 74   ¦ ¦ return false;
 75   }
 76   for (int i = 0;  i < SIZE(r1.products);  ++i) {
 77   ¦ expand_type_abbreviations(r1.products.at(i).type);
 78   ¦ expand_type_abbreviations(r2.products.at(i).type);
 79   ¦ if (!deeply_equal_type_names(r1.products.at(i), r2.products.at(i)))
 80   ¦ ¦ return false;
 81   }
 82   return true;
 83 }
 84 
 85 :(before "End Globals")
 86 set<string> Literal_type_names;
 87 :(before "End One-time Setup")
 88 Literal_type_names.insert("number");
 89 Literal_type_names.insert("character");
 90 :(code)
 91 bool deeply_equal_type_names(const reagent& a, const reagent& b) {
 92   return deeply_equal_type_names(a.type, b.type);
 93 }
 94 bool deeply_equal_type_names(const type_tree* a, const type_tree* b) {
 95   if (!a) return !b;
 96   if (!b) return !a;
 97   if (a->atom != b->atom) return false;
 98   if (a->atom) {
 99   ¦ if (a->name == "literal" && b->name == "literal")
100   ¦ ¦ return true;
101   ¦ if (a->name == "literal")
102   ¦ ¦ return Literal_type_names.find(b->name) != Literal_type_names.end();
103   ¦ if (b->name == "literal")
104   ¦ ¦ return Literal_type_names.find(a->name) != Literal_type_names.end();
105   ¦ return a->name == b->name;
106   }
107   return deeply_equal_type_names(a->left, bTEST_PATH_CONFIG)

test_flake8:
	@echo "Running flake8..."
	flake8 $(TEST_PATHS_MAIN) $(TEST_PATH_CONFIG)

test_doctest:
	@echo "Running doctests..."
	@for FILE in $(shell grep -IHm 1 doctest -r ranger | grep $(FILTER) | cut -d: -f1); do \
		echo "Testing $$FILE..."; \
		RANGER_DOCTEST=1 PYTHONPATH=".:"$$PYTHONPATH ${PYTHON} $$FILE; \
	done

test_pytest:
	@echo "Running py.test tests..."
	py.test tests

test: test_pylint test_flake8 test_doctest test_pytest
	@echo "Finished testing: All tests passed!"

man:
	pod2man --stderr --center='ranger manual' --date='$(NAME)-$(VERSION)' \
		--release=$(shell date +%x) doc/ranger.pod doc/ranger.1
	pod2man --stderr --center='rifle manual' --date='$(NAME_RIFLE)-$(VERSION_RIFLE)' \
		--release=$(shell date +%x) doc/rifle.pod doc/rifle.1

manhtml:
	pod2html doc/ranger.pod --outfile=doc/ranger.1.html

cleandoc:
	test -d $(DOCDIR) && rm -- $(DOCDIR)/*.html || true

snapshot:
	git archive --prefix='$(NAME)-$(VERSION)/' --format=tar HEAD | gzip > $(SNAPSHOT_NAME)

dist: snapshot

todo:
	@grep --color -Ion '\(TODO\|XXX\).*' -r ranger

.PHONY: clean cleandoc compile default dist doc help install man manhtml \
	options snapshot test test_pylint test_flake8 test_doctest test_pytest todo pypi_sdist
'blank' types in this transform in a later layer, for shape-shifting recipes) 143 :(after "Transform.push_back(transform_names)") 144 Transform.push_back(resolve_ambiguous_calls); // idempotent 145 146 //: In a later layer we'll introduce recursion in resolve_ambiguous_calls, by 147 //: having it generate code for shape-shifting recipes and then transform such 148 //: code. This data structure will help error messages be more useful. 149 //: 150 //: We're punning the 'call' data structure just because it has slots for 151 //: calling recipe and calling instruction. 152 :(before "End Globals") 153 list<call> Resolve_stack; 154 155 :(code) 156 void resolve_ambiguous_calls(const recipe_ordinal r) { 157 recipe& caller_recipe = get(Recipe, r); 158 trace(9991, "transform") << "--- resolve ambiguous calls for recipe " << caller_recipe.name << end(); 159 for (int index = 0; index < SIZE(caller_recipe.steps); ++index) { 160 ¦ instruction& inst = caller_recipe.steps.at(index); 161 ¦ if (inst.is_label) continue; 162 ¦ if (non_ghost_size(get_or_insert(Recipe_variants, inst.name)) == 0) continue; 163 ¦ trace(9992, "transform") << "instruction " << inst.original_string << end(); 164 ¦ Resolve_stack.push_front(call(r)); 165 ¦ Resolve_stack.front().running_step_index = index; 166 ¦ string new_name = best_variant(inst, caller_recipe); 167 ¦ if (!new_name.empty()) 168 ¦ ¦ inst.name = new_name; 169 ¦ assert(Resolve_stack.front().running_recipe == r); 170 ¦ assert(Resolve_stack.front().running_step_index == index); 171 ¦ Resolve_stack.pop_front(); 172 } 173 } 174 175 string best_variant(instruction& inst, const recipe& caller_recipe) { 176 vector<recipe_ordinal>& variants = get(Recipe_variants, inst.name); 177 vector<recipe_ordinal> candidates; 178 179 // Static Dispatch Phase 1 180 candidates = strictly_matching_variants(inst, variants); 181 if (!candidates.empty()) return best_variant(inst, candidates).name; 182 183 // Static Dispatch Phase 2 184 candidates = strictly_matching_variants_except_literal_against_address_or_boolean(inst, variants); 185 if (!candidates.empty()) return best_variant(inst, candidates).name; 186 187 // Static Dispatch Phase 3 188 //: (shape-shifting recipes in a later layer) 189 // End Static Dispatch Phase 3 190 191 // Static Dispatch Phase 4 192 candidates = matching_variants(inst, variants); 193 if (!candidates.empty()) return best_variant(inst, candidates).name; 194 195 // error messages 196 if (get(Recipe_ordinal, inst.name) >= MAX_PRIMITIVE_RECIPES) { // we currently don't check types for primitive variants 197 ¦ if (SIZE(variants) == 1) { 198 ¦ ¦ raise << maybe(caller_recipe.name) << "instruction '" << inst.original_string << "' does not match '" << header_label(get(Recipe, variants.at(0))) << "'\n" << end(); 199 ¦ ¦ raise << " instruction expands to '" << to_string(inst) << "'\n" << end(); 200 ¦ } 201 ¦ else { 202 ¦ ¦ raise << maybe(caller_recipe.name) << "failed to find a matching call for '" << inst.original_string << "'\n" << end(); 203 ¦ ¦ raise << " which expands to:\n" << end(); 204 ¦ ¦ raise << " " << to_string(inst) << '\n' << end(); 205 ¦ ¦ raise << " available variants are:\n" << end(); 206 ¦ ¦ for (int i = 0; i < SIZE(variants); ++i) { 207 ¦ ¦ ¦ const recipe& curr = get(Recipe, variants.at(i)); 208 ¦ ¦ ¦ raise << " " << header_label(curr) << '\n' << end(); 209 ¦ ¦ } 210 ¦ } 211 ¦ for (list<call>::iterator p = /*skip*/++Resolve_stack.begin(); p != Resolve_stack.end(); ++p) { 212 ¦ ¦ const recipe& specializer_recipe = get(Recipe, p->running_recipe); 213 ¦ ¦ const instruction& specializer_inst = specializer_recipe.steps.at(p->running_step_index); 214 ¦ ¦ if (specializer_recipe.name != "interactive") 215 ¦ ¦ ¦ raise << " (from '" << to_original_string(specializer_inst) << "' in " << specializer_recipe.name << ")\n" << end(); 216 ¦ ¦ else 217 ¦ ¦ ¦ raise << " (from '" << to_original_string(specializer_inst) << "')\n" << end(); 218 ¦ ¦ // One special-case to help with the rewrite_stash transform. (cross-layer) 219 ¦ ¦ if (specializer_inst.products.at(0).name.find("stash_") == 0) { 220 ¦ ¦ ¦ instruction stash_inst; 221 ¦ ¦ ¦ if (next_stash(*p, &stash_inst)) { 222 ¦ ¦ ¦ ¦ if (specializer_recipe.name != "interactive") 223 ¦ ¦ ¦ ¦ ¦ raise << " (part of '" << stash_inst.original_string << "' in " << specializer_recipe.name << ")\n" << end(); 224 ¦ ¦ ¦ ¦ else 225 ¦ ¦ ¦ ¦ ¦ raise << " (part of '" << stash_inst.original_string << "')\n" << end(); 226 ¦ ¦ ¦ } 227 ¦ ¦ } 228 ¦ } 229 } 230 return ""; 231 } 232 233 // phase 1 234 vector<recipe_ordinal> strictly_matching_variants(const instruction& inst, vector<recipe_ordinal>& variants) { 235 vector<recipe_ordinal> result; 236 for (int i = 0; i < SIZE(variants); ++i) { 237 ¦ if (variants.at(i) == -1) continue; 238 ¦ trace(9992, "transform") << "checking variant (strict) " << i << ": " << header_label(variants.at(i)) << end(); 239 ¦ if (all_header_reagents_strictly_match(inst, get(Recipe, variants.at(i)))) 240 ¦ ¦ result.push_back(variants.at(i)); 241 } 242 return result; 243 } 244 245 bool all_header_reagents_strictly_match(const instruction& inst, const recipe& variant) { 246 for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) { 247 ¦ if (!types_strictly_match(variant.ingredients.at(i), inst.ingredients.at(i))) { 248 ¦ ¦ trace(9993, "transform") << "strict match failed: ingredient " << i << end(); 249 ¦ ¦ return false; 250 ¦ } 251 } 252 for (int i = 0; i < min(SIZE(inst.products), SIZE(variant.products)); ++i) { 253 ¦ if (is_dummy(inst.products.at(i))) continue; 254 ¦ if (!types_strictly_match(variant.products.at(i), inst.products.at(i))) { 255 ¦ ¦ trace(9993, "transform") << "strict match failed: product " << i << end(); 256 ¦ ¦ return false; 257 ¦ } 258 } 259 return true; 260 } 261 262 // phase 2 263 vector<recipe_ordinal> strictly_matching_variants_except_literal_against_address_or_boolean(const instruction& inst, vector<recipe_ordinal>& variants) { 264 vector<recipe_ordinal> result; 265 for (int i = 0; i < SIZE(variants); ++i) { 266 ¦ if (variants.at(i) == -1) continue; 267 ¦ trace(9992, "transform") << "checking variant (strict except literal-against-boolean) " << i << ": " << header_label(variants.at(i)) << end(); 268 ¦ if (all_header_reagents_strictly_match_except_literal_against_address_or_boolean(inst, get(Recipe, variants.at(i)))) 269 ¦ ¦ result.push_back(variants.at(i)); 270 } 271 return result; 272 } 273 274 bool all_header_reagents_strictly_match_except_literal_against_address_or_boolean(const instruction& inst, const recipe& variant) { 275 for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) { 276 ¦ if (!types_strictly_match_except_literal_against_address_or_boolean(variant.ingredients.at(i), inst.ingredients.at(i))) { 277 ¦ ¦ trace(9993, "transform") << "match failed: ingredient " << i << end(); 278 ¦ ¦ return false; 279 ¦ } 280 } 281 for (int i = 0; i < min(SIZE(variant.products), SIZE(inst.products)); ++i) { 282 ¦ if (is_dummy(inst.products.at(i))) continue; 283 ¦ if (!types_strictly_match_except_literal_against_address_or_boolean(variant.products.at(i), inst.products.at(i))) { 284 ¦ ¦ trace(9993, "transform") << "match failed: product " << i << end(); 285 ¦ ¦ return false; 286 ¦ } 287 } 288 return true; 289 } 290 291 bool types_strictly_match_except_literal_against_address_or_boolean(const reagent& to, const reagent& from) { 292 if (is_literal(from) && is_mu_boolean(to)) 293 ¦ return from.name == "0" || from.name == "1"; 294 // Match Literal Zero Against Address { 295 if (is_literal(from) && is_mu_address(to)) 296 ¦ return from.name == "0"; 297 // } 298 return types_strictly_match(to, from); 299 } 300 301 // phase 4 302 vector<recipe_ordinal> matching_variants(const instruction& inst, vector<recipe_ordinal>& variants) { 303 vector<recipe_ordinal> result; 304 for (int i = 0; i < SIZE(variants); ++i) { 305 ¦ if (variants.at(i) == -1) continue; 306 ¦ trace(9992, "transform") << "checking variant " << i << ": " << header_label(variants.at(i)) << end(); 307 ¦ if (all_header_reagents_match(inst, get(Recipe, variants.at(i)))) 308 ¦ ¦ result.push_back(variants.at(i)); 309 } 310 return result; 311 } 312 313 bool all_header_reagents_match(const instruction& inst, const recipe& variant) { 314 for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) { 315 ¦ if (!types_match(variant.ingredients.at(i), inst.ingredients.at(i))) { 316 ¦ ¦ trace(9993, "transform") << "match failed: ingredient " << i << end(); 317 ¦ ¦ return false; 318 ¦ } 319 } 320 for (int i = 0; i < min(SIZE(variant.products), SIZE(inst.products)); ++i) { 321 ¦ if (is_dummy(inst.products.at(i))) continue; 322 ¦ if (!types_match(variant.products.at(i), inst.products.at(i))) { 323 ¦ ¦ trace(9993, "transform") << "match failed: product " << i << end(); 324 ¦ ¦ return false; 325 ¦ } 326 } 327 return true; 328 } 329 330 // tie-breaker for each phase 331 const recipe& best_variant(const instruction& inst, vector<recipe_ordinal>& candidates) { 332 assert(!candidates.empty()); 333 int min_score = 999; 334 int min_index = 0; 335 for (int i = 0; i < SIZE(candidates); ++i) { 336 ¦ const recipe& candidate = get(Recipe, candidates.at(i)); 337 ¦ // prefer functions without extra or missing ingredients or products 338 ¦ int score = abs(SIZE(candidate.products)-SIZE(inst.products)) 339 ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ + abs(SIZE(candidate.ingredients)-SIZE(inst.ingredients)); 340 ¦ // prefer functions with non-address ingredients or products 341 ¦ for (int j = 0; j < SIZE(candidate.ingredients); ++j) { 342 ¦ ¦ if (is_mu_address(candidate.ingredients.at(j))) 343 ¦ ¦ ¦ ++score; 344 ¦ } 345 ¦ for (int j = 0; j < SIZE(candidate.products); ++j) { 346 ¦ ¦ if (is_mu_address(candidate.products.at(j))) 347 ¦ ¦ ¦ ++score; 348 ¦ } 349 ¦ assert(score < 999); 350 ¦ if (score < min_score) { 351 ¦ ¦ min_score = score; 352 ¦ ¦ min_index = i; 353 ¦ } 354 } 355 return get(Recipe, candidates.at(min_index)); 356 } 357 358 int non_ghost_size(vector<recipe_ordinal>& variants) { 359 int result = 0; 360 for (int i = 0; i < SIZE(variants); ++i) 361 ¦ if (variants.at(i) != -1) ++result; 362 return result; 363 } 364 365 bool next_stash(const call& c, instruction* stash_inst) { 366 const recipe& specializer_recipe = get(Recipe, c.running_recipe); 367 int index = c.running_step_index; 368 for (++index; index < SIZE(specializer_recipe.steps); ++index) { 369 ¦ const instruction& inst = specializer_recipe.steps.at(index); 370 ¦ if (inst.name == "stash") { 371 ¦ ¦ *stash_inst = inst; 372 ¦ ¦ return true; 373 ¦ } 374 } 375 return false; 376 } 377 378 :(scenario static_dispatch_disabled_in_recipe_without_variants) 379 def main [ 380 1:num <- test 3 381 ] 382 def test [ 383 2:num <- next-ingredient # ensure no header 384 return 34 385 ] 386 +mem: storing 34 in location 1 387 388 :(scenario static_dispatch_disabled_on_headerless_definition) 389 % Hide_errors = true; 390 def test a:num -> z:num [ 391 z <- copy 1 392 ] 393 def test [ 394 return 34 395 ] 396 +error: redefining recipe test 397 398 :(scenario static_dispatch_disabled_on_headerless_definition_2) 399 % Hide_errors = true; 400 def test [ 401 return 34 402 ] 403 def test a:num -> z:num [ 404 z <- copy 1 405 ] 406 +error: redefining recipe test 407 408 :(scenario static_dispatch_on_primitive_names) 409 def main [ 410 1:num <- copy 34 411 2:num <- copy 34 412 3:bool <- equal 1:num, 2:num 413 4:bool <- copy 0/false 414 5:bool <- copy 0/false 415 6:bool <- equal 4:bool, 5:bool 416 ] 417 # temporarily hardcode number equality to always fail 418 def equal x:num, y:num -> z:bool [ 419 local-scope 420 load-ingredients 421 z <- copy 0/false 422 ] 423 # comparing numbers used overload 424 +mem: storing 0 in location 3 425 # comparing booleans continues to use primitive 426 +mem: storing 1 in location 6 427 428 :(scenario static_dispatch_works_with_dummy_results_for_containers) 429 def main [ 430 _ <- test 3, 4 431 ] 432 def test a:num -> z:point [ 433 local-scope 434 load-ingredients 435 z <- merge a, 0 436 ] 437 def test a:num, b:num -> z:point [ 438 local-scope 439 load-ingredients 440 z <- merge a, b 441 ] 442 $error: 0 443 444 :(scenario static_dispatch_works_with_compound_type_containing_container_defined_after_first_use) 445 def main [ 446 x:&:foo <- new foo:type 447 test x 448 ] 449 container foo [ 450 x:num 451 ] 452 def test a:&:foo -> z:num [ 453 local-scope 454 load-ingredients 455 z:num <- get *a, x:offset 456 ] 457 $error: 0 458 459 :(scenario static_dispatch_works_with_compound_type_containing_container_defined_after_second_use) 460 def main [ 461 x:&:foo <- new foo:type 462 test x 463 ] 464 def test a:&:foo -> z:num [ 465 local-scope 466 load-ingredients 467 z:num <- get *a, x:offset 468 ] 469 container foo [ 470 x:num 471 ] 472 $error: 0 473 474 :(scenario static_dispatch_prefers_literals_to_be_numbers_rather_than_addresses) 475 def main [ 476 1:num <- foo 0 477 ] 478 def foo x:&:num -> y:num [ 479 return 34 480 ] 481 def foo x:num -> y:num [ 482 return 35 483 ] 484 +mem: storing 35 in location 1 485 486 :(scenario static_dispatch_prefers_literals_to_be_numbers_rather_than_addresses_2) 487 def main [ 488 1:num <- foo 0 0 489 ] 490 # Both variants need to bind 0 to address in first ingredient. 491 # We still want to prefer the variant with a number rather than address for 492 # _subsequent_ ingredients. 493 def foo x:&:num y:&:num -> z:num [ # put the bad match before the good one 494 return 34 495 ] 496 def foo x:&:num y:num -> z:num [ 497 return 35 498 ] 499 +mem: storing 35 in location 1 500 501 :(scenario static_dispatch_on_non_literal_character_ignores_variant_with_numbers) 502 % Hide_errors = true; 503 def main [ 504 local-scope 505 x:char <- copy 10/newline 506 1:num/raw <- foo x 507 ] 508 def foo x:num -> y:num [ 509 load-ingredients 510 return 34 511 ] 512 +error: main: ingredient 0 has the wrong type at '1:num/raw <- foo x' 513 -mem: storing 34 in location 1 514 515 :(scenario static_dispatch_dispatches_literal_to_boolean_before_character) 516 def main [ 517 1:num/raw <- foo 0 # valid literal for boolean 518 ] 519 def foo x:char -> y:num [ 520 local-scope 521 load-ingredients 522 return 34 523 ] 524 def foo x:bool -> y:num [ 525 local-scope 526 load-ingredients 527 return 35 528 ] 529 # boolean variant is preferred 530 +mem: storing 35 in location 1 531 532 :(scenario static_dispatch_dispatches_literal_to_character_when_out_of_boolean_range) 533 def main [ 534 1:num/raw <- foo 97 # not a valid literal for boolean 535 ] 536 def foo x:char -> y:num [ 537 local-scope 538 load-ingredients 539 return 34 540 ] 541 def foo x:bool -> y:num [ 542 local-scope 543 load-ingredients 544 return 35 545 ] 546 # character variant is preferred 547 +mem: storing 34 in location 1 548 549 :(scenario static_dispatch_dispatches_literal_to_number_if_at_all_possible) 550 def main [ 551 1:num/raw <- foo 97 552 ] 553 def foo x:char -> y:num [ 554 local-scope 555 load-ingredients 556 return 34 557 ] 558 def foo x:num -> y:num [ 559 local-scope 560 load-ingredients 561 return 35 562 ] 563 # number variant is preferred 564 +mem: storing 35 in location 1 565 566 :(code) 567 string header_label(const recipe_ordinal r) { 568 return header_label(get(Recipe, r)); 569 } 570 string header_label(const recipe& caller) { 571 ostringstream out; 572 out << "recipe " << caller.name; 573 for (int i = 0; i < SIZE(caller.ingredients); ++i) 574 ¦ out << ' ' << to_string(caller.ingredients.at(i)); 575 if (!caller.products.empty()) out << " ->"; 576 for (int i = 0; i < SIZE(caller.products); ++i) 577 ¦ out << ' ' << to_string(caller.products.at(i)); 578 return out.str(); 579 } 580 581 :(scenario reload_variant_retains_other_variants) 582 def main [ 583 1:num <- copy 34 584 2:num <- foo 1:num 585 ] 586 def foo x:num -> y:num [ 587 local-scope 588 load-ingredients 589 return 34 590 ] 591 def foo x:&:num -> y:num [ 592 local-scope 593 load-ingredients 594 return 35 595 ] 596 def! foo x:&:num -> y:num [ 597 local-scope 598 load-ingredients 599 return 36 600 ] 601 +mem: storing 34 in location 2 602 $error: 0 603 604 :(scenario dispatch_errors_come_after_unknown_name_errors) 605 % Hide_errors = true; 606 def main [ 607 y:num <- foo x 608 ] 609 def foo a:num -> b:num [ 610 local-scope 611 load-ingredients 612 return 34 613 ] 614 def foo a:bool -> b:num [ 615 local-scope 616 load-ingredients 617 return 35 618 ] 619 +error: main: missing type for 'x' in 'y:num <- foo x' 620 +error: main: failed to find a matching call for 'y:num <- foo x' 621 622 :(scenario override_methods_with_type_abbreviations) 623 def main [ 624 local-scope 625 s:text <- new [abc] 626 1:num/raw <- foo s 627 ] 628 def foo a:address:array:character -> result:number [ 629 return 34 630 ] 631 # identical to previous variant once you take type abbreviations into account 632 def! foo a:text -> result:num [ 633 return 35 634 ] 635 +mem: storing 35 in location 1 636 637 :(scenario ignore_static_dispatch_in_type_errors_without_overloading) 638 % Hide_errors = true; 639 def main [ 640 local-scope 641 x:&:num <- copy 0 642 foo x 643 ] 644 def foo x:&:char [ 645 local-scope 646 load-ingredients 647 ] 648 +error: main: instruction 'foo x' does not match 'recipe foo {x: ("address" "character")}' 649 +error: instruction expands to 'foo {x: ("address" "number")}' 650 651 :(scenario show_available_variants_in_dispatch_errors) 652 % Hide_errors = true; 653 def main [ 654 local-scope 655 x:&:num <- copy 0 656 foo x 657 ] 658 def foo x:&:char [ 659 local-scope 660 load-ingredients 661 ] 662 def foo x:&:bool [ 663 local-scope 664 load-ingredients 665 ] 666 +error: main: failed to find a matching call for 'foo x' 667 +error: which expands to: 668 +error: foo {x: ("address" "number")} 669 +error: available variants are: 670 +error: recipe foo {x: ("address" "character")} 671 +error: recipe foo_2 {x: ("address" "boolean")} 672 673 :(before "End Includes") 674 using std::abs;