1
2
3 :(scenario shape_shifting_recipe)
4 def main [
5 10:point <- merge 14, 15
6 11:point <- foo 10:point
7 ]
8
9 def foo a:num -> result:num [
10 local-scope
11 load-ingredients
12 result <- copy 34
13 ]
14
15 def foo a:_t -> result:_t [
16 local-scope
17 load-ingredients
18 result <- copy a
19 ]
20 +mem: storing 14 in location 11
21 +mem: storing 15 in location 12
22
23
24
25
26
27
28
29
30
31
32 :(before "End Transform Checks")
33 r.transformed_until = t;
34 if (Transform.at(t) != static_cast<transform_fn>(expand_type_abbreviations) && any_type_ingredient_in_header(p->first)) continue;
35
36 :(after "Running One Instruction")
37 if (Current_routine->calls.front().running_step_index == 0
38 ¦ && any_type_ingredient_in_header(Current_routine->calls.front().running_recipe)) {
39
40 raise << "ran into unspecialized shape-shifting recipe " << current_recipe_name() << '\n' << end();
41
42 }
43
44
45
46 :(before "End Matching Types For Literal(to)")
47 if (contains_type_ingredient_name(to)) return false;
48
49 :(after "Static Dispatch Phase 3")
50 candidates = strictly_matching_shape_shifting_variants(inst, variants);
51 if (!candidates.empty()) {
52 recipe_ordinal exemplar = best_shape_shifting_variant(inst, candidates);
53 trace(9992, "transform") << "found variant to specialize: " << exemplar << ' ' << get(Recipe, exemplar).name << end();
54 recipe_ordinal new_recipe_ordinal = new_variant(exemplar, inst, caller_recipe);
55 if (new_recipe_ordinal == 0) goto skip_shape_shifting_variants;
56 variants.push_back(new_recipe_ordinal);
57 recipe& variant = get(Recipe, new_recipe_ordinal);
58
59 if (!variant.steps.empty()) {
60 ¦ trace(9992, "transform") << "transforming new specialization: " << variant.name << end();
61 ¦ for (int t = 0; t < SIZE(Transform); ++t) {
62 ¦ ¦
63 ¦ ¦ if (Transform.at(t) == static_cast<transform_fn>(insert_fragments))
64 ¦ ¦ ¦ continue;
65 ¦ ¦ (*Transform.at(t))(new_recipe_ordinal);
66 ¦ }
67 }
68 variant.transformed_until = SIZE(Transform)-1;
69 trace(9992, "transform") << "new specialization: " << variant.name << end();
70 return variant.name;
71 }
72 skip_shape_shifting_variants:;
73
74
75
76
77 :(before "End Instruction Operation Checks")
78 if (contains_key(Recipe, inst.operation) && inst.operation >= MAX_PRIMITIVE_RECIPES
79 ¦ && any_type_ingredient_in_header(inst.operation)) {
80 raise << maybe(caller.name) << "instruction '" << inst.name << "' has no valid specialization\n" << end();
81 return;
82 }
83
84 :(replace{} "Match Literal Zero Against Address")
85 if (is_literal(from) && is_mu_address(to))
86 return from.name == "0" && !contains_type_ingredient_name(to);
87
88 :(code)
89
90 vector<recipe_ordinal> strictly_matching_shape_shifting_variants(const instruction& inst, vector<recipe_ordinal>& variants) {
91 vector<recipe_ordinal> result;
92 for (int i = 0; i < SIZE(variants); ++i) {
93 ¦ if (variants.at(i) == -1) continue;
94 ¦ if (!any_type_ingredient_in_header(variants.at(i))) continue;
95 ¦ if (!all_concrete_header_reagents_strictly_match(inst, get(Recipe, variants.at(i)))) continue;
96 ¦ result.push_back(variants.at(i));
97 }
98 return result;
99 }
100
101 bool all_concrete_header_reagents_strictly_match(const instruction& inst, const recipe& variant) {
102 for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) {
103 ¦ if (!concrete_type_names_strictly_match(variant.ingredients.at(i), inst.ingredients.at(i))) {
104 ¦ ¦ trace(9993, "transform") << "concrete-type match failed: ingredient " << i << end();
105 ¦ ¦ return false;
106 ¦ }
107 }
108 for (int i = 0; i < min(SIZE(inst.products), SIZE(variant.ingredients)); ++i) {
109 ¦ if (is_dummy(inst.products.at(i))) continue;
110 ¦ if (!concrete_type_names_strictly_match(variant.products.at(i), inst.products.at(i))) {
111 ¦ ¦ trace(9993, "transform") << "concrete-type match failed: product " << i << end();
112 ¦ ¦ return false;
113 ¦ }
114 }
115 return true;
116 }
117
118
119 recipe_ordinal best_shape_shifting_variant(const instruction& inst, vector<recipe_ordinal>& candidates) {
120 assert(!candidates.empty());
121
122 int max_score = -1;
123 for (int i = 0; i < SIZE(candidates); ++i) {
124 ¦ int score = number_of_concrete_type_names(candidates.at(i));
125 ¦ assert(score > -1);
126 ¦ if (score > max_score) max_score = score;
127 }
128
129 int min_score2 = 999;
130 int best_index = 0;
131 for (int i = 0; i < SIZE(candidates); ++i) {
132 ¦ int score1 = number_of_concrete_type_names(candidates.at(i));
133 ¦ assert(score1 <= max_score);
134 ¦ if (score1 != max_score) continue;
135 ¦ const recipe& candidate = get(Recipe, candidates.at(i));
136 ¦ int score2 = (SIZE(candidate.products)-SIZE(inst.products))
137 ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ + (SIZE(inst.ingredients)-SIZE(candidate.ingredients));
138 ¦ assert(score2 < 999);
139 ¦ if (score2 < min_score2) {
140 ¦ ¦ min_score2 = score2;
141 ¦ ¦ best_index = i;
142 ¦ }
143 }
144 return candidates.at(best_index);
145 }
146
147 bool any_type_ingredient_in_header(recipe_ordinal variant) {
148 const recipe& caller = get(Recipe, variant);
149 for (int i = 0; i < SIZE(caller.ingredients); ++i) {
150 ¦ if (contains_type_ingredient_name(caller.ingredients.at(i)))
151 ¦ ¦ return true;
152 }
153 for (int i = 0; i < SIZE(caller.products); ++i) {
154 ¦ if (contains_type_ingredient_name(caller.products.at(i)))
155 ¦ ¦ return true;
156 }
157 return false;
158 }
159
160 bool concrete_type_names_strictly_match(reagent to, reagent from) {
161 canonize_type(to);
162 canonize_type(from);
163 return concrete_type_names_strictly_match(to.type, from.type, from);
164 }
165
166 bool concrete_type_names_strictly_match(const type_tree* to, const type_tree* from, const reagent& rhs_reagent) {
167 if (!to) return !from;
168 if (!from) return !to;
169 if (to->atom && is_type_ingredient_name(to->name)) return true;
170 if (!to->atom && to->right == NULL && to->left != NULL && to->left->atom && is_type_ingredient_name(to->left->name)) return true;
171 if (from->atom && is_mu_address(to))
172 ¦ return from->name == "literal" && rhs_reagent.name == "0";
173 if (!from->atom && !to->atom)
174 ¦ return concrete_type_names_strictly_match(to->left, from->left, rhs_reagent)
175 ¦ ¦ ¦ && concrete_type_names_strictly_match(to->right, from->right, rhs_reagent);
176 if (from->atom != to->atom) return false;
177
178 if (from->name == "literal")
179 ¦ return Literal_type_names.find(to->name) != Literal_type_names.end();
180 if (to->name == "literal")
181 ¦ return Literal_type_names.find(from->name) != Literal_type_names.end();
182 return to->name == from->name;
183 }
184
185 bool contains_type_ingredient_name(const reagent& x) {
186 return contains_type_ingredient_name(x.type);
187 }
188
189 bool contains_type_ingredient_name(const type_tree* type) {
190 if (!type) return false;
191 if (is_type_ingredient_name(type->name)) return true;
192 return contains_type_ingredient_name(type->left) || contains_type_ingredient_name(type->right);
193 }
194
195 int number_of_concrete_type_names(recipe_ordinal r) {
196 const recipe& caller = get(Recipe, r);
197 int result = 0;
198 for (int i = 0; i < SIZE(caller.ingredients); ++i)
199 ¦ result += number_of_concrete_type_names(caller.ingredients.at(i));
200 for (int i = 0; i < SIZE(caller.products); ++i)
201 ¦ result += number_of_concrete_type_names(caller.products.at(i));
202 return result;
203 }
204
205 int number_of_concrete_type_names(const reagent& r) {
206 return number_of_concrete_type_names(r.type);
207 }
208
209 int number_of_concrete_type_names(const type_tree* type) {
210 if (!type) return 0;
211 if (type->atom)
212 ¦ return is_type_ingredient_name(type->name) ? 0 : 1;
213 return number_of_concrete_type_names(type->left)
214 ¦ ¦ ¦+ number_of_concrete_type_names(type->right);
215 }
216
217 recipe_ordinal new_variant(recipe_ordinal exemplar, const instruction& inst, const recipe& caller_recipe) {
218 string new_name = next_unused_recipe_name(inst.name);
219 assert(!contains_key(Recipe_ordinal, new_name));
220 recipe_ordinal new_recipe_ordinal = put(Recipe_ordinal, new_name, Next_recipe_ordinal++);
221
222 assert(contains_key(Recipe, exemplar));
223 assert(!contains_key(Recipe, new_recipe_ordinal));
224 recipe new_recipe = get(Recipe, exemplar);
225 new_recipe.name = new_name;
226 new_recipe.is_autogenerated = true;
227 trace(9993, "transform") << "switching " << inst.name << " to specialized " << header_label(new_recipe) << end();
228
229
230
231
232
233
234 insert_fragments(new_recipe);
235
236
237 expand_type_abbreviations(new_recipe);
238 compute_type_names(new_recipe);
239
240 {
241 ¦ map<string, const type_tree*> mappings;
242 ¦ bool error = false;
243 ¦ compute_type_ingredient_mappings(get(Recipe, exemplar), inst, mappings, caller_recipe, &error);
244 ¦ if (!error) error = (SIZE(mappings) != type_ingredient_count_in_header(exemplar));
245 ¦ if (!error) replace_type_ingredients(new_recipe, mappings);
246 ¦ for (map<string, const type_tree*>::iterator p = mappings.begin(); p != mappings.end(); ++p)
247 ¦ ¦ delete p->second;
248 ¦ if (error) return 0;
249 }
250 ensure_all_concrete_types(new_recipe, get(Recipe, exemplar));
251 put(Recipe, new_recipe_ordinal, new_recipe);
252 return new_recipe_ordinal;
253 }
254
255 void compute_type_names(recipe& variant) {
256 trace(9993, "transform") << "-- compute type names: " << variant.name << end();
257 map<string, type_tree*> type_names;
258 for (int i = 0; i < SIZE(variant.ingredients); ++i)
259 ¦ save_or_deduce_type_name(variant.ingredients.at(i), type_names, variant, "");
260 for (int i = 0; i < SIZE(variant.products); ++i)
261 ¦ save_or_deduce_type_name(variant.products.at(i), type_names, variant, "");
262 for (int i = 0; i < SIZE(variant.steps); ++i) {
263 ¦ instruction& inst = variant.steps.at(i);
264 ¦ trace(9993, "transform") << " instruction: " << to_string(inst) << end();
265 ¦ for (int in = 0; in < SIZE(inst.ingredients); ++in)
266 ¦ ¦ save_or_deduce_type_name(inst.ingredients.at(in), type_names, variant, " in '" + inst.original_string + "'");
267 ¦ for (int out = 0; out < SIZE(inst.products); ++out)
268 ¦ ¦ save_or_deduce_type_name(inst.products.at(out), type_names, variant, " in '" + inst.original_string + "'");
269 }
270 }
271
272 void save_or_deduce_type_name(reagent& x, map<string, type_tree*>& type, const recipe& variant, const string& context) {
273 trace(9994, "transform") << " checking " << to_string(x) << ": " << names_to_string(x.type) << end();
274 if (!x.type && contains_key(type, x.name)) {
275 ¦ x.type = new type_tree(*get(type, x.name));
276 ¦ trace(9994, "transform") << " deducing type to " << names_to_string(x.type) << end();
277 ¦ return;
278 }
279 if (!x.type) {
280 ¦ raise << maybe(variant.original_name) << "unknown type for '" << x.original_string << "'" << context << " (check the name for typos)\n" << end();
281 ¦ return;
282 }
283 if (contains_key(type, x.name)) return;
284 if (x.type->name == "offset" || x.type->name == "variant") return;
285 put(type, x.name, x.type);
286 trace(9993, "transform") << "type of '" << x.name << "' is " << names_to_string(x.type) << end();
287 }
288
289 void compute_type_ingredient_mappings(const recipe& exemplar, const instruction& inst, map<string, const type_tree*>& mappings, const recipe& caller_recipe, bool* error) {
290 int limit = min(SIZE(inst.ingredients), SIZE(exemplar.ingredients));
291 for (int i = 0; i < limit; ++i) {
292 ¦ const reagent& exemplar_reagent = exemplar.ingredients.at(i);
293 ¦ reagent ingredient = inst.ingredients.at(i);
294 ¦ canonize_type(ingredient);
295 ¦ if (is_mu_address(exemplar_reagent) && ingredient.name == "0") continue;
296 ¦ accumulate_type_ingredients(exemplar_reagent, ingredient, mappings, exemplar, inst, caller_recipe, error);
297 }
298 limit = min(SIZE(inst.products), SIZE(exemplar.products));
299 for (int i = 0; i < limit; ++i) {
300 ¦ const reagent& exemplar_reagent = exemplar.products.at(i);
301 ¦ reagent product = inst.products.at(i);
302 ¦ if (is_dummy(product)) continue;
303 ¦ canonize_type(product);
304 ¦ accumulate_type_ingredients(exemplar_reagent, product, mappings, exemplar, inst, caller_recipe, error);
305 }
306 }
307
308 void accumulate_type_ingredients(const reagent& exemplar_reagent, reagent& refinement, map<string, const type_tree*>& mappings, const recipe& exemplar, const instruction& call_instruction, const recipe& caller_recipe, bool* error) {
309 assert(refinement.type);
310 accumulate_type_ingredients(exemplar_reagent.type, refinement.type, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error);
311 }
312
313 void accumulate_type_ingredients(const type_tree* exemplar_type, const type_tree* refinement_type, map<string, const type_tree*>& mappings, const recipe& exemplar, const reagent& exemplar_reagent, const instruction& call_instruction, const recipe& caller_recipe, bool* error) {
314 if (!exemplar_type) return;
315 if (!refinement_type) {
316 ¦
317 ¦
318 ¦ raise << maybe(exemplar.name) << "missing type ingredient for " << exemplar_reagent.original_string << '\n' << end();
319 ¦ raise << " (called from '" << to_original_string(call_instruction) << "')\n" << end();
320 ¦ return;
321 }
322 if (!exemplar_type->atom && exemplar_type->right == NULL && !refinement_type->atom && refinement_type->right != NULL) {
323 ¦ exemplar_type = exemplar_type->left;
324 ¦ assert_for_now(exemplar_type->atom);
325 }
326 if (exemplar_type->atom) {
327 ¦ if (is_type_ingredient_name(exemplar_type->name)) {
328 ¦ ¦ const type_tree* curr_refinement_type = NULL;
329 ¦ ¦ if (exemplar_type->atom)
330 ¦ ¦ ¦ curr_refinement_type = new type_tree(*refinement_type);
331 ¦ ¦ else {
332 ¦ ¦ ¦ assert(!refinement_type->atom);
333 ¦ ¦ ¦ curr_refinement_type = new type_tree(*refinement_type->left);
334 ¦ ¦ }
335 ¦ ¦ if (!contains_key(mappings, exemplar_type->name)) {
336 ¦ ¦ ¦ trace(9993, "transform") << "adding mapping from " << exemplar_type->name << " to " << to_string(curr_refinement_type) << end();
337 ¦ ¦ ¦ put(mappings, exemplar_type->name, new type_tree(*curr_refinement_type));
338 ¦ ¦ }
339 ¦ ¦ else {
340 ¦ ¦ ¦ if (!deeply_equal_type_names(get(mappings, exemplar_type->name), curr_refinement_type)) {
341 ¦ ¦ ¦ ¦ raise << maybe(caller_recipe.name) << "no call found for '" << to_original_string(call_instruction) << "'\n" << end();
342 ¦ ¦ ¦ ¦ *error = true;
343 ¦ ¦ ¦ ¦ delete curr_refinement_type;
344 ¦ ¦ ¦ ¦ return;
345 ¦ ¦ ¦ }
346 ¦ ¦ ¦ if (get(mappings, exemplar_type->name)->name == "literal") {
347 ¦ ¦ ¦ ¦ delete get(mappings, exemplar_type->name);
348 ¦ ¦ ¦ ¦ put(mappings, exemplar_type->name, new type_tree(*curr_refinement_type));
349 ¦ ¦ ¦ }
350 ¦ ¦ }
351 ¦ ¦ delete curr_refinement_type;
352 ¦ }
353 }
354 else {
355 ¦ accumulate_type_ingredients(exemplar_type->left, refinement_type->left, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error);
356 ¦ accumulate_type_ingredients(exemplar_type->right, refinement_type->right, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error);
357 }
358 }
359
360 void replace_type_ingredients(recipe& new_recipe, const map<string, const type_tree*>& mappings) {
361
362 if (mappings.empty()) return;
363 trace(9993, "transform") << "replacing in recipe header ingredients" << end();
364 for (int i = 0; i < SIZE(new_recipe.ingredients); ++i)
365 ¦ replace_type_ingredients(new_recipe.ingredients.at(i), mappings, new_recipe);
366 trace(9993, "transform") << "replacing in recipe header products" << end();
367 for (int i = 0; i < SIZE(new_recipe.products); ++i)
368 ¦ replace_type_ingredients(new_recipe.products.at(i), mappings, new_recipe);
369
370 for (int i = 0; i < SIZE(new_recipe.steps); ++i) {
371 ¦ instruction& inst = new_recipe.steps.at(i);
372 ¦ trace(9993, "transform") << "replacing in instruction '" << to_string(inst) << "'" << end();
373 ¦ for (int j = 0; j < SIZE(inst.ingredients); ++j)
374 ¦ ¦ replace_type_ingredients(inst.ingredients.at(j), mappings, new_recipe);
375 ¦ for (int j = 0; j < SIZE(inst.products); ++j)
376 ¦ ¦ replace_type_ingredients(inst.products.at(j), mappings, new_recipe);
377 ¦
378 ¦ if (inst.name == "new" && inst.ingredients.at(0).type->name != "literal-string") {
379 ¦ ¦ type_tree* type = parse_type_tree(inst.ingredients.at(0).name);
380 ¦ ¦ replace_type_ingredients(type, mappings);
381 ¦ ¦ inst.ingredients.at(0).name = inspect(type);
382 ¦ ¦ delete type;
383 ¦ }
384 }
385 }
386
387 void replace_type_ingredients(reagent& x, const map<string, const type_tree*>& mappings, const recipe& caller) {
388 string before = to_string(x);
389 trace(9993, "transform") << "replacing in ingredient " << x.original_string << end();
390 if (!x.type) {
391 ¦ raise << "specializing " << caller.original_name << ": missing type for '" << x.original_string << "'\n" << end();
392 ¦ return;
393 }
394 replace_type_ingredients(x.type, mappings);
395 }
396
397 void replace_type_ingredients(type_tree* type, const map<string, const type_tree*>& mappings) {
398 if (!type) return;
399 if (!type->atom) {
400 ¦ if (type->right == NULL && type->left != NULL && type->left->atom && contains_key(mappings, type->left->name) && !get(mappings, type->left->name)->atom && get(mappings, type->left->name)->right != NULL) {
401 ¦ ¦ *type = *get(mappings, type->left->name);
402 ¦ ¦ return;
403 ¦ }
404 ¦ replace_type_ingredients(type->left, mappings);
405 ¦ replace_type_ingredients(type->right, mappings);
406 ¦ return;
407 }
408 if (contains_key(Type_ordinal, type->name))
409 ¦ type->value = get(Type_ordinal, type->name);
410 if (!contains_key(mappings, type->name))
411 ¦ return;
412 const type_tree* replacement = get(mappings, type->name);
413 trace(9993, "transform") << type->name << " => " << names_to_string(replacement) << end();
414 if (replacement->atom) {
415 ¦ if (!contains_key(Type_ordinal, replacement->name)) {
416 ¦ ¦
417 ¦ ¦ return;
418 ¦ }
419 ¦ type->name = (replacement->name == "literal") ? "number" : replacement->name;
420 ¦ type->value = get(Type_ordinal, type->name);
421 }
422 else {
423 ¦ *type = *replacement;
424 }
425 }
426
427 int type_ingredient_count_in_header(recipe_ordinal variant) {
428 const recipe& caller = get(Recipe, variant);
429 set<string> type_ingredients;
430 for (int i = 0; i < SIZE(caller.ingredients); ++i)
431 ¦ accumulate_type_ingredients(caller.ingredients.at(i).type, type_ingredients);
432 for (int i = 0; i < SIZE(caller.products); ++i)
433 ¦ accumulate_type_ingredients(caller.products.at(i).type, type_ingredients);
434 return SIZE(type_ingredients);
435 }
436
437 void accumulate_type_ingredients(const type_tree* type, set<string>& out) {
438 if (!type) return;
439 if (is_type_ingredient_name(type->name)) out.insert(type->name);
440 accumulate_type_ingredients(type->left, out);
441 accumulate_type_ingredients(type->right, out);
442 }
443
444 type_tree* parse_type_tree(const string& s) {
445 string_tree* s2 = parse_string_tree(s);
446 type_tree* result = new_type_tree(s2);
447 delete s2;
448 return result;
449 }
450
451 string inspect(const type_tree* x) {
452 ostringstream out;
453 dump_inspect(x, out);
454 return out.str();
455 }
456
457 void dump_inspect(const type_tree* x, ostream& out) {
458 if (!x->left && !x->right) {
459 ¦ out << x->name;
460 ¦ return;
461 }
462 out << '(';
463 for (const type_tree* curr = x; curr; curr = curr->right) {
464 ¦ if (curr != x) out << ' ';
465 ¦ if (curr->left)
466 ¦ ¦ dump_inspect(curr->left, out);
467 ¦ else
468 ¦ ¦ out << curr->name;
469 }
470 out << ')';
471 }
472
473 void ensure_all_concrete_types( recipe& new_recipe, const recipe& exemplar) {
474 trace(9993, "transform") << "-- ensure all concrete types in recipe " << new_recipe.name << end();
475 for (int i = 0; i < SIZE(new_recipe.ingredients); ++i)
476 ¦ ensure_all_concrete_types(new_recipe.ingredients.at(i), exemplar);
477 for (int i = 0; i < SIZE(new_recipe.products); ++i)
478 ¦ ensure_all_concrete_types(new_recipe.products.at(i), exemplar);
479 for (int i = 0; i < SIZE(new_recipe.steps); ++i) {
480 ¦ instruction& inst = new_recipe.steps.at(i);
481 ¦ for (int j = 0; j < SIZE(inst.ingredients); ++j)
482 ¦ ¦ ensure_all_concrete_types(inst.ingredients.at(j), exemplar);
483 ¦ for (int j = 0; j < SIZE(inst.products); ++j)
484 ¦ ¦ ensure_all_concrete_types(inst.products.at(j), exemplar);
485 }
486 }
487
488 void ensure_all_concrete_types( reagent& x, const recipe& exemplar) {
489 if (!x.type || contains_type_ingredient_name(x.type)) {
490 ¦ raise << maybe(exemplar.name) << "failed to map a type to " << x.original_string << '\n' << end();
491 ¦ if (!x.type) x.type = new type_tree("", 0);
492 ¦ return;
493 }
494 if (x.type->value == -1) {
495 ¦ raise << maybe(exemplar.name) << "failed to map a type to the unknown " << x.original_string << '\n' << end();
496 ¦ return;
497 }
498 }
499
500 :(scenario shape_shifting_recipe_2)
501 def main [
502 10:point <- merge 14, 15
503 11:point <- foo 10:point
504 ]
505
506 def foo a:_t, b:_t -> result:num [
507 local-scope
508 load-ingredients
509 result <- copy 34
510 ]
511
512 def foo a:_t -> result:_t [
513 local-scope
514 load-ingredients
515 result <- copy a
516 ]
517 +mem: storing 14 in location 11
518 +mem: storing 15 in location 12
519
520 :(scenario shape_shifting_recipe_nonroot)
521 def main [
522 10:foo:point <- merge 14, 15, 16
523 20:point/raw <- bar 10:foo:point
524 ]
525
526 def bar a:foo:_t -> result:_t [
527 local-scope
528 load-ingredients
529 result <- get a, x:offset
530 ]
531 container foo:_t [
532 x:_t
533 y:num
534 ]
535 +mem: storing 14 in location 20
536 +mem: storing 15 in location 21
537
538 :(scenario shape_shifting_recipe_nested)
539 container c:_a:_b [
540 a:_a
541 b:_b
542 ]
543 def main [
544 s:text <- new [abc]
545 {x: (c (address array character) number)} <- merge s, 34
546 foo x
547 ]
548 def foo x:c:_bar:_baz [
549 local-scope
550 load-ingredients
551 ]
552
553
554 :(scenario shape_shifting_recipe_type_deduction_ignores_offsets)
555 def main [
556 10:foo:point <- merge 14, 15, 16
557 20:point/raw <- bar 10:foo:point
558 ]
559 def bar a:foo:_t -> result:_t [
560 local-scope
561 load-ingredients
562 x:num <- copy 1
563 result <- get a, x:offset
564 ]
565 container foo:_t [
566 x:_t
567 y:num
568 ]
569 +mem: storing 14 in location 20
570 +mem: storing 15 in location 21
571
572 :(scenario shape_shifting_recipe_empty)
573 def main [
574 foo 1
575 ]
576
577 def foo a:_t [
578 ]
579
580
581 :(scenario shape_shifting_recipe_handles_shape_shifting_new_ingredient)
582 def main [
583 1:&:foo:point <- bar 3
584 11:foo:point <- copy *1:&:foo:point
585 ]
586 container foo:_t [
587 x:_t
588 y:num
589 ]
590 def bar x:num -> result:&:foo:_t [
591 local-scope
592 load-ingredients
593
594 result <- new {(foo _t) : type}
595 ]
596 +mem: storing 0 in location 11
597 +mem: storing 0 in location 12
598 +mem: storing 0 in location 13
599
600 :(scenario shape_shifting_recipe_handles_shape_shifting_new_ingredient_2)
601 def main [
602 1:&:foo:point <- bar 3
603 11:foo:point <- copy *1:&:foo:point
604 ]
605 def bar x:num -> result:&:foo:_t [
606 local-scope
607 load-ingredients
608
609 result <- new {(foo _t) : type}
610 ]
611
612 container foo:_t [
613 x:_t
614 y:num
615 ]
616 +mem: storing 0 in location 11
617 +mem: storing 0 in location 12
618 +mem: storing 0 in location 13
619
620 :(scenario shape_shifting_recipe_called_with_dummy)
621 def main [
622 _ <- bar 34
623 ]
624 def bar x:_t -> result:&:_t [
625 local-scope
626 load-ingredients
627 result <- copy 0
628 ]
629 $error: 0
630
631 :(code)
632
633 void test_shape_shifting_new_ingredient_does_not_pollute_global_namespace() {
634
635 transform("def barz x:_elem [\n"
636 ¦ ¦ ¦ ¦ ¦ " local-scope\n"
637 ¦ ¦ ¦ ¦ ¦ " load-ingredients\n"
638 ¦ ¦ ¦ ¦ ¦ " y:&:num <- new _elem:type\n"
639 ¦ ¦ ¦ ¦ ¦ "]\n"
640 ¦ ¦ ¦ ¦ ¦ "def fooz [\n"
641 ¦ ¦ ¦ ¦ ¦ " local-scope\n"
642 ¦ ¦ ¦ ¦ ¦ " barz 34\n"
643 ¦ ¦ ¦ ¦ ¦ "]\n");
644
645
646 run("container foo:_elem [\n"
647 ¦ ¦ " x:_elem\n"
648 ¦ ¦ " y:num\n"
649 ¦ ¦ "]\n");
650
651 reagent callsite("x:foo:point");
652 reagent element = element_type(callsite.type, 0);
653 CHECK_EQ(element.name, "x");
654 CHECK_EQ(element.type->name, "point");
655 CHECK(!element.type->right);
656 }
657
658
659 :(scenario shape_shifting_recipe_supports_compound_types)
660 def main [
661 1:&:point <- new point:type
662 *1:&:point <- put *1:&:point, y:offset, 34
663 3:&:point <- bar 1:&:point
664 4:point <- copy *3:&:point
665 ]
666 def bar a:_t -> result:_t [
667 local-scope
668 load-ingredients
669 result <- copy a
670 ]
671 +mem: storing 34 in location 5
672
673
674 :(scenario shape_shifting_recipe_supports_compound_types_2)
675 container foo:_t [
676 value:_t
677 ]
678 def bar x:&:foo:_t -> result:_t [
679 local-scope
680 load-ingredients
681 result <- get *x, value:offset
682 ]
683 def main [
684 1:&:foo:&:point <- new {(foo address point): type}
685 2:&:point <- bar 1:&:foo:&:point
686 ]
687
688
689 :(scenario shape_shifting_recipe_error)
690 % Hide_errors = true;
691 def main [
692 a:num <- copy 3
693 b:&:num <- foo a
694 ]
695 def foo a:_t -> b:_t [
696 load-ingredients
697 b <- copy a
698 ]
699 +error: main: no call found for 'b:&:num <- foo a'
700
701 :(scenario specialize_inside_recipe_without_header)
702 def main [
703 foo 3
704 ]
705 def foo [
706 local-scope
707 x:num <- next-ingredient
708 1:num/raw <- bar x
709 ]
710 def bar x:_elem -> y:_elem [
711 local-scope
712 load-ingredients
713 y <- add x, 1
714 ]
715 +mem: storing 4 in location 1
716
717 :(scenario specialize_with_literal)
718 def main [
719 local-scope
720
721 1:num/raw <- foo 3
722 ]
723 def foo x:_elem -> y:_elem [
724 local-scope
725 load-ingredients
726 y <- add x, 1
727 ]
728 +mem: storing 4 in location 1
729
730 :(scenario specialize_with_literal_2)
731 def main [
732 local-scope
733
734 1:char/raw <- foo 3
735 ]
736 def foo x:_elem -> y:_elem [
737 local-scope
738 load-ingredients
739 y <- add x, 1
740 ]
741 +mem: storing 4 in location 1
742
743 :(scenario specialize_with_literal_3)
744 def main [
745 local-scope
746
747 1:&:char/raw <- foo 0
748 ]
749 def foo x:&:_elem -> y:&:_elem [
750 local-scope
751 load-ingredients
752 y <- copy x
753 ]
754 +mem: storing 0 in location 1
755 $error: 0
756
757 :(scenario specialize_with_literal_4)
758 % Hide_errors = true;
759 def main [
760 local-scope
761
762 foo 0
763 ]
764 def foo x:&:_elem -> y:&:_elem [
765 local-scope
766 load-ingredients
767 y <- copy x
768 ]
769 +error: main: instruction 'foo' has no valid specialization
770
771 :(scenario specialize_with_literal_5)
772 def main [
773 foo 3, 4
774 ]
775 def foo x:_elem, y:_elem [
776 local-scope
777 load-ingredients
778 1:num/raw <- add x, y
779 ]
780 +mem: storing 7 in location 1
781
782 :(scenario multiple_shape_shifting_variants)
783
784 def main [
785 e1:d1:num <- merge 3
786 e2:d2:num <- merge 4, 5
787 1:num/raw <- foo e1
788 2:num/raw <- foo e2
789 ]
790
791 def foo a:d1:_elem -> b:num [
792 local-scope
793 load-ingredients
794 return 34
795 ]
796 def foo a:d2:_elem -> b:num [
797 local-scope
798 load-ingredients
799 return 35
800 ]
801
802 container d1:_elem [
803 x:_elem
804 ]
805 container d2:_elem [
806 x:num
807 y:_elem
808 ]
809 +mem: storing 34 in location 1
810 +mem: storing 35 in location 2
811
812 :(scenario multiple_shape_shifting_variants_2)
813
814 def main [
815 e1:d1:num <- merge 3
816 e2:&:d2:num <- new {(d2 number): type}
817 1:num/raw <- foo e1
818 2:num/raw <- foo *e2
819 ]
820 def foo a:d1:_elem -> b:num [
821 local-scope
822 load-ingredients
823 return 34
824 ]
825 def foo a:d2:_elem -> b:num [
826 local-scope
827 load-ingredients
828 return 35
829 ]
830 container d1:_elem [
831 x:_elem
832 ]
833 container d2:_elem [
834 x:num
835 y:_elem
836 ]
837 +mem: storing 34 in location 1
838 +mem: storing 35 in location 2
839
840 :(scenario missing_type_in_shape_shifting_recipe)
841 % Hide_errors = true;
842 def main [
843 a:d1:num <- merge 3
844 foo a
845 ]
846 def foo a:d1:_elem -> b:num [
847 local-scope
848 load-ingredients
849 copy e
850 return 34
851 ]
852 container d1:_elem [
853 x:_elem
854 ]
855 +error: foo: unknown type for 'e' in 'copy e' (check the name for typos)
856 +error: specializing foo: missing type for 'e'
857
858
859 :(scenario missing_type_in_shape_shifting_recipe_2)
860 % Hide_errors = true;
861 def main [
862 a:d1:num <- merge 3
863 foo a
864 ]
865 def foo a:d1:_elem -> b:num [
866 local-scope
867 load-ingredients
868 get e, x:offset
869 return 34
870 ]
871 container d1:_elem [
872 x:_elem
873 ]
874 +error: foo: unknown type for 'e' in 'get e, x:offset' (check the name for typos)
875 +error: specializing foo: missing type for 'e'
876
877
878 :(scenarios transform)
879 :(scenario specialize_recursive_shape_shifting_recipe)
880 def main [
881 1:num <- copy 34
882 2:num <- foo 1:num
883 ]
884 def foo x:_elem -> y:num [
885 local-scope
886 load-ingredients
887 {
888 ¦ break
889 ¦ y:num <- foo x
890 }
891 return y
892 ]
893 +transform: new specialization: foo_2
894
895
896 :(scenarios run)
897 :(scenario specialize_most_similar_variant)
898 def main [
899 1:&:num <- new number:type
900 2:num <- foo 1:&:num
901 ]
902 def foo x:_elem -> y:num [
903 local-scope
904 load-ingredients
905 return 34
906 ]
907 def foo x:&:_elem -> y:num [
908 local-scope
909 load-ingredients
910 return 35
911 ]
912 +mem: storing 35 in location 2
913
914 :(scenario specialize_most_similar_variant_2)
915
916 def main [
917 1:num <- copy 23
918 2:&:@:num <- copy 0
919 3:num <- foo 2:&:@:num, 1:num
920 ]
921
922 def foo dummy:&:@:num, x:num -> y:num, dummy:&:@:num [
923 local-scope
924 load-ingredients
925 return 34
926 ]
927
928 def foo dummy:&:@:num, x:_elem -> y:num, dummy:&:@:num [
929 local-scope
930 load-ingredients
931 return 35
932 ]
933
934 +mem: storing 34 in location 3
935
936 :(scenario specialize_most_similar_variant_3)
937 def main [
938 1:text <- new [abc]
939 foo 1:text
940 ]
941 def foo x:text [
942 2:num <- copy 34
943 ]
944 def foo x:&:_elem [
945 2:num <- copy 35
946 ]
947
948 +mem: storing 34 in location 2
949
950 :(scenario specialize_literal_as_number)
951 def main [
952 1:num <- foo 23
953 ]
954 def foo x:_elem -> y:num [
955 local-scope
956 load-ingredients
957 return 34
958 ]
959 def foo x:char -> y:num [
960 local-scope
961 load-ingredients
962 return 35
963 ]
964 +mem: storing 34 in location 1
965
966 :(scenario specialize_literal_as_number_2)
967
968 def main [
969 1:num <- foo 0
970 ]
971
972 def foo x:num -> y:num [
973 local-scope
974 load-ingredients
975 return 34
976 ]
977
978 def foo x:&:_elem -> y:num [
979 local-scope
980 load-ingredients
981 return 35
982 ]
983
984 +mem: storing 34 in location 1
985
986 :(scenario specialize_literal_as_address)
987 def main [
988 1:num <- foo 0
989 ]
990
991 def foo x:&:num -> y:num [
992 local-scope
993 load-ingredients
994 return 34
995 ]
996
997 def foo x:&:_elem -> y:num [
998 local-scope
999 load-ingredients
1000 return 35
1001 ]
1002
1003 +mem: storing 34 in location 1
1004
1005 :(scenario missing_type_during_specialization)
1006 % Hide_errors = true;
1007
1008 def foo a:_elem [
1009 ]
1010
1011 container foo2 [
1012 z:num
1013 ]
1014 def main [
1015 local-scope
1016 x:foo2 <- merge 34
1017 y:num <- get x, z:offse
1018
1019 z:num <- copy 34
1020
1021 foo z
1022 ]
1023
1024
1025 :(scenario missing_type_during_specialization2)
1026 % Hide_errors = true;
1027
1028 def foo a:_elem [
1029 ]
1030
1031 container foo2 [
1032 z:num
1033 ]
1034 def main [
1035 local-scope
1036 x:foo2 <- merge 34
1037 y:num <- get x, z:offse
1038
1039 z:&:num <- copy 34
1040
1041 foo *z
1042 ]
1043
1044
1045 :(scenario tangle_shape_shifting_recipe)
1046
1047 def foo a:_elem [
1048 local-scope
1049 load-ingredients
1050 <label1>
1051 ]
1052
1053 after <label1> [
1054 b:_elem <- copy a
1055 ]
1056
1057 def main [
1058 local-scope
1059 foo 34
1060 ]
1061 $error: 0
1062
1063 :(scenario tangle_shape_shifting_recipe_with_type_abbreviation)
1064
1065 def foo a:_elem [
1066 local-scope
1067 load-ingredients
1068 <label1>
1069 ]
1070
1071 after <label1> [
1072 b:bool <- copy 0
1073 ]
1074
1075 def main [
1076 local-scope
1077 foo 34
1078 ]
1079 $error: 0
1080
1081 :(scenario shape_shifting_recipe_coexists_with_primitive)
1082
1083 def add a:&:foo:_elem [
1084 assert 0, [should not get here]
1085 ]
1086 def main [
1087
1088 add 0, 0
1089 ]
1090 $error: 0