fix other mandelbrot variants
[mu.git] / archive / 1.vm / 072recipe.cc
bloba3bb00bfaf47117c1fed77d4a0431e60773df57a
1 //: So far we've been calling a fixed recipe in each instruction, but we'd
2 //: also like to make the recipe a variable, pass recipes to "higher-order"
3 //: recipes, return recipes from recipes and so on.
5 void test_call_literal_recipe() {
6 run(
7 "def main [\n"
8 " 1:num <- call f, 34\n"
9 "]\n"
10 "def f x:num -> y:num [\n"
11 " local-scope\n"
12 " load-ingredients\n"
13 " y <- copy x\n"
14 "]\n"
16 CHECK_TRACE_CONTENTS(
17 "mem: storing 34 in location 1\n"
21 :(before "End Mu Types Initialization")
22 put(Type_ordinal, "recipe-literal", 0);
23 // 'recipe' variables can store recipe-literal
24 type_ordinal recipe = put(Type_ordinal, "recipe", Next_type_ordinal++);
25 get_or_insert(Type, recipe).name = "recipe";
27 :(after "Deduce Missing Type(x, caller)")
28 if (!x.type)
29 try_initialize_recipe_literal(x, caller);
30 :(before "Type Check in Type-ingredient-aware check_or_set_types_by_name")
31 if (!x.type)
32 try_initialize_recipe_literal(x, variant);
33 :(code)
34 void try_initialize_recipe_literal(reagent& x, const recipe& caller) {
35 if (x.type) return;
36 if (!contains_key(Recipe_ordinal, x.name)) return;
37 if (contains_reagent_with_non_recipe_literal_type(caller, x.name)) return;
38 x.type = new type_tree("recipe-literal");
39 x.set_value(get(Recipe_ordinal, x.name));
41 bool contains_reagent_with_non_recipe_literal_type(const recipe& caller, const string& name) {
42 for (int i = 0; i < SIZE(caller.steps); ++i) {
43 const instruction& inst = caller.steps.at(i);
44 for (int i = 0; i < SIZE(inst.ingredients); ++i)
45 if (is_matching_non_recipe_literal(inst.ingredients.at(i), name)) return true;
46 for (int i = 0; i < SIZE(inst.products); ++i)
47 if (is_matching_non_recipe_literal(inst.products.at(i), name)) return true;
49 return false;
51 bool is_matching_non_recipe_literal(const reagent& x, const string& name) {
52 if (x.name != name) return false;
53 if (!x.type) return false;
54 return !x.type->atom || x.type->name != "recipe-literal";
57 //: It's confusing to use variable names that are also recipe names. Always
58 //: assume variable types override recipe literals.
59 void test_error_on_recipe_literal_used_as_a_variable() {
60 Hide_errors = true;
61 run(
62 "def main [\n"
63 " local-scope\n"
64 " a:bool <- equal break 0\n"
65 " break:bool <- copy 0\n"
66 "]\n"
68 CHECK_TRACE_CONTENTS(
69 "error: main: missing type for 'break' in 'a:bool <- equal break, 0'\n"
73 :(before "End Primitive Recipe Declarations")
74 CALL,
75 :(before "End Primitive Recipe Numbers")
76 put(Recipe_ordinal, "call", CALL);
77 :(before "End Primitive Recipe Checks")
78 case CALL: {
79 if (inst.ingredients.empty()) {
80 raise << maybe(get(Recipe, r).name) << "'call' requires at least one ingredient (the recipe to call)\n" << end();
81 break;
83 if (!is_mu_recipe(inst.ingredients.at(0))) {
84 raise << maybe(get(Recipe, r).name) << "first ingredient of 'call' should be a recipe, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
85 break;
87 break;
89 :(before "End Primitive Recipe Implementations")
90 case CALL: {
91 // Begin Call
92 trace(Callstack_depth+1, "trace") << "indirect 'call': incrementing callstack depth to " << Callstack_depth << end();
93 ++Callstack_depth;
94 assert(Callstack_depth < Max_depth);
95 if (!ingredients.at(0).at(0)) {
96 raise << maybe(current_recipe_name()) << "tried to call empty recipe in '" << to_string(current_instruction()) << "'" << end();
97 break;
99 const call& caller_frame = current_call();
100 instruction/*copy*/ call_instruction = to_instruction(caller_frame);
101 call_instruction.operation = ingredients.at(0).at(0);
102 call_instruction.ingredients.erase(call_instruction.ingredients.begin());
103 Current_routine->calls.push_front(call(ingredients.at(0).at(0)));
104 ingredients.erase(ingredients.begin()); // drop the callee
105 finish_call_housekeeping(call_instruction, ingredients);
106 // not done with caller
107 write_products = false;
108 fall_through_to_next_instruction = false;
109 break;
112 :(code)
113 void test_call_variable() {
114 run(
115 "def main [\n"
116 " {1: (recipe number -> number)} <- copy f\n"
117 " 2:num <- call {1: (recipe number -> number)}, 34\n"
118 "]\n"
119 "def f x:num -> y:num [\n"
120 " local-scope\n"
121 " load-ingredients\n"
122 " y <- copy x\n"
123 "]\n"
125 CHECK_TRACE_CONTENTS(
126 "mem: storing 34 in location 2\n"
130 void test_call_literal_recipe_repeatedly() {
131 run(
132 "def main [\n"
133 " 1:num <- call f, 34\n"
134 " 1:num <- call f, 35\n"
135 "]\n"
136 "def f x:num -> y:num [\n"
137 " local-scope\n"
138 " load-ingredients\n"
139 " y <- copy x\n"
140 "]\n"
142 CHECK_TRACE_CONTENTS(
143 "mem: storing 34 in location 1\n"
144 "mem: storing 35 in location 1\n"
148 void test_call_shape_shifting_recipe() {
149 run(
150 "def main [\n"
151 " 1:num <- call f, 34\n"
152 "]\n"
153 "def f x:_elem -> y:_elem [\n"
154 " local-scope\n"
155 " load-ingredients\n"
156 " y <- copy x\n"
157 "]\n"
159 CHECK_TRACE_CONTENTS(
160 "mem: storing 34 in location 1\n"
164 void test_call_shape_shifting_recipe_inside_shape_shifting_recipe() {
165 run(
166 "def main [\n"
167 " 1:num <- f 34\n"
168 "]\n"
169 "def f x:_elem -> y:_elem [\n"
170 " local-scope\n"
171 " load-ingredients\n"
172 " y <- call g x\n"
173 "]\n"
174 "def g x:_elem -> y:_elem [\n"
175 " local-scope\n"
176 " load-ingredients\n"
177 " y <- copy x\n"
178 "]\n"
180 CHECK_TRACE_CONTENTS(
181 "mem: storing 34 in location 1\n"
185 void test_call_shape_shifting_recipe_repeatedly_inside_shape_shifting_recipe() {
186 run(
187 "def main [\n"
188 " 1:num <- f 34\n"
189 "]\n"
190 "def f x:_elem -> y:_elem [\n"
191 " local-scope\n"
192 " load-ingredients\n"
193 " y <- call g x\n"
194 " y <- call g x\n"
195 "]\n"
196 "def g x:_elem -> y:_elem [\n"
197 " local-scope\n"
198 " load-ingredients\n"
199 " y <- copy x\n"
200 "]\n"
202 CHECK_TRACE_CONTENTS(
203 "mem: storing 34 in location 1\n"
207 //:: check types for 'call' instructions
209 void test_call_check_literal_recipe() {
210 Hide_errors = true;
211 run(
212 "def main [\n"
213 " 1:num <- call f, 34\n"
214 "]\n"
215 "def f x:point -> y:point [\n"
216 " local-scope\n"
217 " load-ingredients\n"
218 " y <- copy x\n"
219 "]\n"
221 CHECK_TRACE_CONTENTS(
222 "error: main: ingredient 0 has the wrong type at '1:num <- call f, 34'\n"
223 "error: main: product 0 has the wrong type at '1:num <- call f, 34'\n"
227 void test_call_check_variable_recipe() {
228 Hide_errors = true;
229 run(
230 "def main [\n"
231 " {1: (recipe point -> point)} <- copy f\n"
232 " 2:num <- call {1: (recipe point -> point)}, 34\n"
233 "]\n"
234 "def f x:point -> y:point [\n"
235 " local-scope\n"
236 " load-ingredients\n"
237 " y <- copy x\n"
238 "]\n"
240 CHECK_TRACE_CONTENTS(
241 "error: main: ingredient 0 has the wrong type at '2:num <- call {1: (recipe point -> point)}, 34'\n"
242 "error: main: product 0 has the wrong type at '2:num <- call {1: (recipe point -> point)}, 34'\n"
246 :(before "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases")
247 if (inst.name == "call" && !inst.ingredients.empty() && is_recipe_literal(inst.ingredients.at(0))) {
248 resolve_indirect_ambiguous_call(r, index, inst, caller_recipe);
249 return;
251 :(code)
252 bool is_recipe_literal(const reagent& x) {
253 return x.type && x.type->atom && x.type->name == "recipe-literal";
255 void resolve_indirect_ambiguous_call(const recipe_ordinal r, int index, instruction& inst, const recipe& caller_recipe) {
256 instruction inst2;
257 inst2.name = inst.ingredients.at(0).name;
258 for (int i = /*skip recipe*/1; i < SIZE(inst.ingredients); ++i)
259 inst2.ingredients.push_back(inst.ingredients.at(i));
260 for (int i = 0; i < SIZE(inst.products); ++i)
261 inst2.products.push_back(inst.products.at(i));
262 resolve_ambiguous_call(r, index, inst2, caller_recipe);
263 inst.ingredients.at(0).name = inst2.name;
264 inst.ingredients.at(0).set_value(get(Recipe_ordinal, inst2.name));
267 :(after "Transform.push_back(check_instruction)")
268 Transform.push_back(check_indirect_calls_against_header); // idempotent
269 :(code)
270 void check_indirect_calls_against_header(const recipe_ordinal r) {
271 trace(101, "transform") << "--- type-check 'call' instructions inside recipe " << get(Recipe, r).name << end();
272 const recipe& caller = get(Recipe, r);
273 for (int i = 0; i < SIZE(caller.steps); ++i) {
274 const instruction& inst = caller.steps.at(i);
275 if (!is_indirect_call(inst.operation)) continue;
276 if (inst.ingredients.empty()) continue; // error raised above
277 const reagent& callee = inst.ingredients.at(0);
278 if (!is_mu_recipe(callee)) continue; // error raised above
279 const recipe callee_header = is_literal(callee) ? get(Recipe, callee.value) : from_reagent(inst.ingredients.at(0));
280 if (!callee_header.has_header) continue;
281 if (is_indirect_call_with_ingredients(inst.operation)) {
282 for (long int i = /*skip callee*/1; i < min(SIZE(inst.ingredients), SIZE(callee_header.ingredients)+/*skip callee*/1); ++i) {
283 if (!types_coercible(callee_header.ingredients.at(i-/*skip callee*/1), inst.ingredients.at(i)))
284 raise << maybe(caller.name) << "ingredient " << i-/*skip callee*/1 << " has the wrong type at '" << to_original_string(inst) << "'\n" << end();
287 if (is_indirect_call_with_products(inst.operation)) {
288 for (long int i = 0; i < min(SIZE(inst.products), SIZE(callee_header.products)); ++i) {
289 if (is_dummy(inst.products.at(i))) continue;
290 if (!types_coercible(callee_header.products.at(i), inst.products.at(i)))
291 raise << maybe(caller.name) << "product " << i << " has the wrong type at '" << to_original_string(inst) << "'\n" << end();
297 bool is_indirect_call(const recipe_ordinal r) {
298 return is_indirect_call_with_ingredients(r) || is_indirect_call_with_products(r);
301 bool is_indirect_call_with_ingredients(const recipe_ordinal r) {
302 if (r == CALL) return true;
303 // End is_indirect_call_with_ingredients Special-cases
304 return false;
306 bool is_indirect_call_with_products(const recipe_ordinal r) {
307 if (r == CALL) return true;
308 // End is_indirect_call_with_products Special-cases
309 return false;
312 recipe from_reagent(const reagent& r) {
313 assert(r.type);
314 recipe result_header; // will contain only ingredients and products, nothing else
315 result_header.has_header = true;
316 // Begin Reagent->Recipe(r, recipe_header)
317 if (r.type->atom) {
318 assert(r.type->name == "recipe");
319 return result_header;
321 const type_tree* root_type = r.type->atom ? r.type : r.type->left;
322 assert(root_type->atom);
323 assert(root_type->name == "recipe");
324 const type_tree* curr = r.type->right;
325 for (/*nada*/; curr && !curr->atom; curr = curr->right) {
326 if (curr->left->atom && curr->left->name == "->") {
327 curr = curr->right; // skip delimiter
328 goto read_products;
330 result_header.ingredients.push_back(next_recipe_reagent(curr->left));
332 if (curr) {
333 assert(curr->atom);
334 result_header.ingredients.push_back(next_recipe_reagent(curr));
335 return result_header; // no products
337 read_products:
338 for (/*nada*/; curr && !curr->atom; curr = curr->right)
339 result_header.products.push_back(next_recipe_reagent(curr->left));
340 if (curr) {
341 assert(curr->atom);
342 result_header.products.push_back(next_recipe_reagent(curr));
344 return result_header;
347 :(before "End Unit Tests")
348 void test_from_reagent_atomic() {
349 reagent a("{f: recipe}");
350 recipe r_header = from_reagent(a);
351 CHECK(r_header.ingredients.empty());
352 CHECK(r_header.products.empty());
354 void test_from_reagent_non_atomic() {
355 reagent a("{f: (recipe number -> number)}");
356 recipe r_header = from_reagent(a);
357 CHECK_EQ(SIZE(r_header.ingredients), 1);
358 CHECK_EQ(SIZE(r_header.products), 1);
360 void test_from_reagent_reads_ingredient_at_end() {
361 reagent a("{f: (recipe number number)}");
362 recipe r_header = from_reagent(a);
363 CHECK_EQ(SIZE(r_header.ingredients), 2);
364 CHECK(r_header.products.empty());
366 void test_from_reagent_reads_sole_ingredient_at_end() {
367 reagent a("{f: (recipe number)}");
368 recipe r_header = from_reagent(a);
369 CHECK_EQ(SIZE(r_header.ingredients), 1);
370 CHECK(r_header.products.empty());
373 :(code)
374 reagent next_recipe_reagent(const type_tree* curr) {
375 if (!curr->left) return reagent("recipe:"+curr->name);
376 return reagent(new type_tree(*curr));
379 bool is_mu_recipe(const reagent& r) {
380 if (!r.type) return false;
381 if (r.type->atom) {
382 // End is_mu_recipe Atom Cases(r)
383 return r.type->name == "recipe-literal";
385 return r.type->left->atom && r.type->left->name == "recipe";
388 void test_copy_typecheck_recipe_variable() {
389 Hide_errors = true;
390 run(
391 "def main [\n"
392 " 3:num <- copy 34\n"
393 " {1: (recipe number -> number)} <- copy f\n" // store literal in a matching variable
394 " {2: (recipe boolean -> boolean)} <- copy {1: (recipe number -> number)}\n" // mismatch between recipe variables
395 "]\n"
396 "def f x:num -> y:num [\n"
397 " local-scope\n"
398 " load-ingredients\n"
399 " y <- copy x\n"
400 "]\n"
402 CHECK_TRACE_CONTENTS(
403 "error: main: can't copy '{1: (recipe number -> number)}' to '{2: (recipe boolean -> boolean)}'; types don't match\n"
407 void test_copy_typecheck_recipe_variable_2() {
408 Hide_errors = true;
409 run(
410 "def main [\n"
411 " {1: (recipe number -> number)} <- copy f\n" // mismatch with a recipe literal
412 "]\n"
413 "def f x:bool -> y:bool [\n"
414 " local-scope\n"
415 " load-ingredients\n"
416 " y <- copy x\n"
417 "]\n"
419 CHECK_TRACE_CONTENTS(
420 "error: main: can't copy 'f' to '{1: (recipe number -> number)}'; types don't match\n"
424 :(before "End Matching Types For Literal(to)")
425 if (is_mu_recipe(to)) {
426 if (!contains_key(Recipe, from.value)) {
427 raise << "trying to store recipe " << from.name << " into " << to_string(to) << " but there's no such recipe\n" << end();
428 return false;
430 const recipe& rrhs = get(Recipe, from.value);
431 const recipe& rlhs = from_reagent(to);
432 for (long int i = 0; i < min(SIZE(rlhs.ingredients), SIZE(rrhs.ingredients)); ++i) {
433 if (!types_match(rlhs.ingredients.at(i), rrhs.ingredients.at(i)))
434 return false;
436 for (long int i = 0; i < min(SIZE(rlhs.products), SIZE(rrhs.products)); ++i) {
437 if (!types_match(rlhs.products.at(i), rrhs.products.at(i)))
438 return false;
440 return true;
443 :(code)
444 void test_call_variable_compound_ingredient() {
445 run(
446 "def main [\n"
447 " {1: (recipe (address number) -> number)} <- copy f\n"
448 " 2:&:num <- copy null\n"
449 " 3:num <- call {1: (recipe (address number) -> number)}, 2:&:num\n"
450 "]\n"
451 "def f x:&:num -> y:num [\n"
452 " local-scope\n"
453 " load-ingredients\n"
454 " y <- deaddress x\n"
455 "]\n"
457 CHECK_TRACE_COUNT("error", 0);
460 //: make sure we don't accidentally break on a recipe literal
461 void test_jump_forbidden_on_recipe_literals() {
462 Hide_errors = true;
463 run(
464 "def foo [\n"
465 " local-scope\n"
466 "]\n"
467 "def main [\n"
468 " local-scope\n"
469 " {\n"
470 " break-if foo\n"
471 " }\n"
472 "]\n"
474 // error should be as if foo is not a recipe
475 CHECK_TRACE_CONTENTS(
476 "error: main: missing type for 'foo' in 'break-if foo'\n"
480 :(before "End JUMP_IF Checks")
481 check_for_recipe_literals(inst, get(Recipe, r));
482 :(before "End JUMP_UNLESS Checks")
483 check_for_recipe_literals(inst, get(Recipe, r));
484 :(code)
485 void check_for_recipe_literals(const instruction& inst, const recipe& caller) {
486 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
487 if (is_mu_recipe(inst.ingredients.at(i))) {
488 raise << maybe(caller.name) << "missing type for '" << inst.ingredients.at(i).original_string << "' in '" << to_original_string(inst) << "'\n" << end();
489 if (is_present_in_ingredients(caller, inst.ingredients.at(i).name))
490 raise << " did you forget 'load-ingredients'?\n" << end();
495 void test_load_ingredients_missing_error_3() {
496 Hide_errors = true;
497 run(
498 "def foo {f: (recipe num -> num)} [\n"
499 " local-scope\n"
500 " b:num <- call f, 1\n"
501 "]\n"
503 CHECK_TRACE_CONTENTS(
504 "error: foo: missing type for 'f' in 'b:num <- call f, 1'\n"
505 "error: did you forget 'load-ingredients'?\n"
509 :(before "End Mu Types Initialization")
510 put(Type_abbreviations, "function", new_type_tree("recipe"));
511 put(Type_abbreviations, "fn", new_type_tree("recipe"));
513 //: copying functions to variables
515 :(code)
516 void test_copy_recipe_to_variable() {
517 run(
518 "def main [\n"
519 " {1: (fn number -> number)} <- copy f\n"
520 " 2:num <- call {1: (function number -> number)}, 34\n"
521 "]\n"
522 "def f x:num -> y:num [\n"
523 " local-scope\n"
524 " load-ingredients\n"
525 " y <- copy x\n"
526 "]\n"
528 CHECK_TRACE_CONTENTS(
529 "mem: storing 34 in location 2\n"
533 void test_copy_overloaded_recipe_to_variable() {
534 run(
535 "def main [\n"
536 " local-scope\n"
537 " {x: (fn num -> num)} <- copy f\n"
538 " 1:num/raw <- call x, 34\n"
539 "]\n"
540 // variant f
541 "def f x:bool -> y:bool [\n"
542 " local-scope\n"
543 " load-ingredients\n"
544 " y <- copy x\n"
545 "]\n"
546 // variant f_2
547 "def f x:num -> y:num [\n"
548 " local-scope\n"
549 " load-ingredients\n"
550 " y <- copy x\n"
551 "]\n"
553 // x contains f_2
554 CHECK_TRACE_CONTENTS(
555 "mem: storing 34 in location 1\n"
559 :(before "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases")
560 if (inst.name == "copy") {
561 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
562 if (!is_recipe_literal(inst.ingredients.at(i))) continue;
563 if (non_ghost_size(get_or_insert(Recipe_variants, inst.ingredients.at(i).name)) < 1) continue;
564 // potentially overloaded recipe
565 string new_name = resolve_ambiguous_call(inst.ingredients.at(i).name, inst.products.at(i), r, index, caller_recipe);
566 if (new_name == "") continue;
567 inst.ingredients.at(i).name = new_name;
568 inst.ingredients.at(i).value = get(Recipe_ordinal, new_name);
570 return;
572 :(code)
573 string resolve_ambiguous_call(const string& recipe_name, const reagent& call_types, const recipe_ordinal r, int index, const recipe& caller_recipe) {
574 instruction inst;
575 inst.name = recipe_name;
576 if (!is_mu_recipe(call_types)) return ""; // error raised elsewhere
577 if (is_recipe_literal(call_types)) return ""; // error raised elsewhere
578 construct_fake_call(call_types, inst);
579 resolve_ambiguous_call(r, index, inst, caller_recipe);
580 return inst.name;
582 void construct_fake_call(const reagent& recipe_var, instruction& out) {
583 assert(recipe_var.type->left->name == "recipe");
584 type_tree* stem = NULL;
585 for (stem = recipe_var.type->right; stem && stem->left->name != "->"; stem = stem->right)
586 out.ingredients.push_back(copy(stem->left));
587 if (stem == NULL) return;
588 for (/*skip '->'*/stem = stem->right; stem; stem = stem->right)
589 out.products.push_back(copy(stem->left));
592 void test_copy_shape_shifting_recipe_to_variable() {
593 run(
594 "def main [\n"
595 " local-scope\n"
596 " {x: (fn num -> num)} <- copy f\n"
597 " 1:num/raw <- call x, 34\n"
598 "]\n"
599 "def f x:_elem -> y:_elem [\n"
600 " local-scope\n"
601 " load-inputs\n"
602 " y <- copy x\n"
603 "]\n"
605 CHECK_TRACE_CONTENTS(
606 "mem: storing 34 in location 1\n"
610 //: passing function literals to (higher-order) functions
612 void test_pass_overloaded_recipe_literal_to_ingredient() {
613 run(
614 // like test_copy_overloaded_recipe_to_variable, except we bind 'x' in
615 // the course of a 'call' rather than 'copy'
616 "def main [\n"
617 " 1:num <- g f\n"
618 "]\n"
619 "def g {x: (fn num -> num)} -> result:num [\n"
620 " local-scope\n"
621 " load-ingredients\n"
622 " result <- call x, 34\n"
623 "]\n"
624 // variant f
625 "def f x:bool -> y:bool [\n"
626 " local-scope\n"
627 " load-ingredients\n"
628 " y <- copy x\n"
629 "]\n"
630 // variant f_2
631 "def f x:num -> y:num [\n"
632 " local-scope\n"
633 " load-ingredients\n"
634 " y <- copy x\n"
635 "]\n"
637 // x contains f_2
638 CHECK_TRACE_CONTENTS(
639 "mem: storing 34 in location 1\n"
643 :(after "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases")
644 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
645 if (!is_mu_recipe(inst.ingredients.at(i))) continue;
646 if (non_ghost_size(get_or_insert(Recipe_variants, inst.ingredients.at(i).name)) < 1) continue;
647 if (get(Recipe_ordinal, inst.name) < MAX_PRIMITIVE_RECIPES) continue;
648 if (non_ghost_size(get_or_insert(Recipe_variants, inst.name)) > 1) {
649 raise << maybe(caller_recipe.name) << "sorry, we're not yet smart enough to simultaneously guess which overloads you want for '" << inst.name << "' and '" << inst.ingredients.at(i).name << "'\n" << end();
650 return;
652 const recipe& callee = get(Recipe, get(Recipe_ordinal, inst.name));
653 if (!callee.has_header) {
654 raise << maybe(caller_recipe.name) << "sorry, we're not yet smart enough to guess which variant of '" << inst.ingredients.at(i).name << "' you want, when the caller '" << inst.name << "' doesn't have a header\n" << end();
655 return;
657 string new_name = resolve_ambiguous_call(inst.ingredients.at(i).name, callee.ingredients.at(i), r, index, caller_recipe);
658 if (new_name != "") {
659 inst.ingredients.at(i).name = new_name;
660 inst.ingredients.at(i).value = get(Recipe_ordinal, new_name);
664 :(code)
665 void test_return_overloaded_recipe_literal_to_caller() {
666 run(
667 "def main [\n"
668 " local-scope\n"
669 " {x: (fn num -> num)} <- g\n"
670 " 1:num/raw <- call x, 34\n"
671 "]\n"
672 "def g -> {x: (fn num -> num)} [\n"
673 " local-scope\n"
674 " return f\n"
675 "]\n"
676 // variant f
677 "def f x:bool -> y:bool [\n"
678 " local-scope\n"
679 " load-ingredients\n"
680 " y <- copy x\n"
681 "]\n"
682 // variant f_2
683 "def f x:num -> y:num [\n"
684 " local-scope\n"
685 " load-ingredients\n"
686 " y <- copy x\n"
687 "]\n"
689 // x contains f_2
690 CHECK_TRACE_CONTENTS(
691 "mem: storing 34 in location 1\n"
695 :(before "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases")
696 if (inst.name == "return" || inst.name == "reply") {
697 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
698 if (!is_recipe_literal(inst.ingredients.at(i))) continue;
699 if (non_ghost_size(get_or_insert(Recipe_variants, inst.ingredients.at(i).name)) < 1) continue;
700 // potentially overloaded recipe
701 if (!caller_recipe.has_header) {
702 raise << maybe(caller_recipe.name) << "sorry, we're not yet smart enough to guess which variant of '" << inst.ingredients.at(i).name << "' you want, without a recipe header\n" << end();
703 return;
705 string new_name = resolve_ambiguous_call(inst.ingredients.at(i).name, caller_recipe.products.at(i), r, index, caller_recipe);
706 if (new_name == "") continue;
707 inst.ingredients.at(i).name = new_name;
708 inst.ingredients.at(i).value = get(Recipe_ordinal, new_name);
710 return;