1 //:: Container definitions can contain 'type ingredients'
  2 
  3 //: pre-requisite: extend our notion of containers to not necessarily be
  4 //: atomic types
  5 :(after "Update GET base_type in Check")
  6 base_type = get_base_type(base_type);
  7 :(after "Update GET base_type in Run")
  8 base_type = get_base_type(base_type);
  9 :(after "Update PUT base_type in Check")
 10 base_type = get_base_type(base_type);
 11 :(after "Update PUT base_type in Run")
 12 base_type = get_base_type(base_type);
 13 :(after "Update MAYBE_CONVERT base_type in Check")
 14 base_type = get_base_type(base_type);
 15 :(after "Update base_type in size_of(type)")
 16 base_type = get_base_type(base_type);
 17 :(after "Update base_type in element_type")
 18 base_type = get_base_type(base_type);
 19 //? :(after "Update base_type in compute_container_address_offsets")
 20 //? base_type = get_base_type(base_type);
 21 //? :(after "Update base_type in append_container_address_offsets")
 22 //? base_type = get_base_type(base_type);
 23 //? :(after "Update element_base_type For Exclusive Container in append_addresses")
 24 //? element_base_type = get_base_type(element_base_type);
 25 :(after "Update base_type in skip_addresses")
 26 base_type = get_base_type(base_type);
 27 :(replace{} "const type_tree* get_base_type(const type_tree* t)")
 28 const type_tree* get_base_type(const type_tree* t) {
 29   const type_tree* result = t->atom ? t : t->left;
 30   if (!result->atom)
 31     raise << "invalid type " << to_string(t) << '\n' << end();
 32   return result;
 33 }
 34 
 35 :(scenario ill_formed_container)
 36 % Hide_errors = true;
 37 def main [
 38   {1: ((foo) num)} <- copy 0
 39 ]
 40 # no crash
 41 
 42 :(scenario size_of_shape_shifting_container)
 43 container foo:_t [
 44   x:_t
 45   y:num
 46 ]
 47 def main [
 48   1:foo:num <- merge 12, 13
 49   3:foo:point <- merge 14, 15, 16
 50 ]
 51 +mem: storing 12 in location 1
 52 +mem: storing 13 in location 2
 53 +mem: storing 14 in location 3
 54 +mem: storing 15 in location 4
 55 +mem: storing 16 in location 5
 56 
 57 :(scenario size_of_shape_shifting_container_2)
 58 # multiple type ingredients
 59 container foo:_a:_b [
 60   x:_a
 61   y:_b
 62 ]
 63 def main [
 64   1:foo:num:bool <- merge 34, 1/true
 65 ]
 66 $error: 0
 67 
 68 :(scenario size_of_shape_shifting_container_3)
 69 container foo:_a:_b [
 70   x:_a
 71   y:_b
 72 ]
 73 def main [
 74   1:text <- new [abc]
 75   # compound types for type ingredients
 76   {2: (foo number (address array character))} <- merge 34/x, 1:text/y
 77 ]
 78 $error: 0
 79 
 80 :(scenario size_of_shape_shifting_container_4)
 81 container foo:_a:_b [
 82   x:_a
 83   y:_b
 84 ]
 85 container bar:_a:_b [
 86   # dilated element
 87   {data: (foo _a (address _b))}
 88 ]
 89 def main [
 90   1:text <- new [abc]
 91   2:bar:num:@:char <- merge 34/x, 1:text/y
 92 ]
 93 $error: 0
 94 
 95 :(scenario shape_shifting_container_extend)
 96 container foo:_a [
 97   x:_a
 98 ]
 99 container foo:_a [
100   y:_a
101 ]
102 $error: 0
103 
104 :(scenario shape_shifting_container_extend_error)
105 % Hide_errors = true;
106 container foo:_a [
107   x:_a
108 ]
109 container foo:_b [
110   y:_b
111 ]
112 +error: headers of container 'foo' must use identical type ingredients
113 
114 :(scenario type_ingredient_must_start_with_underscore)
115 % Hide_errors = true;
116 container foo:t [
117   x:num
118 ]
119 +error: foo: type ingredient 't' must begin with an underscore
120 
121 :(before "End Globals")
122 // We'll use large type ordinals to mean "the following type of the variable".
123 // For example, if we have a generic type called foo:_elem, the type
124 // ingredient _elem in foo's type_info will have value START_TYPE_INGREDIENTS,
125 // and we'll handle it by looking in the current reagent for the next type
126 // that appears after foo.
127 extern const int START_TYPE_INGREDIENTS = 2000;
128 :(before "End Commandline Parsing")  // after loading .mu files
129 assert(Next_type_ordinal < START_TYPE_INGREDIENTS);
130 
131 :(before "End type_info Fields")
132 map<string, type_ordinal> type_ingredient_names;
133 
134 //: Suppress unknown type checks in shape-shifting containers.
135 
136 :(before "Check Container Field Types(info)")
137 if (!info.type_ingredient_names.empty()) continue;
138 
139 :(before "End container Name Refinements")
140 if (name.find(':') != string::npos) {
141   trace("parse") << "container has type ingredients; parsing" << end();
142   if (!read_type_ingredients(name, command)) {
143     // error; skip rest of the container definition and continue
144     slurp_balanced_bracket(in);
145     return;
146   }
147 }
148 
149 :(code)
150 bool read_type_ingredients(string& name, const string& command) {
151   string save_name = name;
152   istringstream in(save_name);
153   name = slurp_until(in, ':');
154   map<string, type_ordinal> type_ingredient_names;
155   if (!slurp_type_ingredients(in, type_ingredient_names, name)) {
156     return false;
157   }
158   if (contains_key(Type_ordinal, name)
159       && contains_key(Type, get(Type_ordinal, name))) {
160     const type_info& previous_info = get(Type, get(Type_ordinal, name));
161     // we've already seen this container; make sure type ingredients match
162     if (!type_ingredients_match(type_ingredient_names, previous_info.type_ingredient_names)) {
163       raise << "headers of " << command << " '" << name << "' must use identical type ingredients\n" << end();
164       return false;
165     }
166     return true;
167   }
168   // we haven't seen this container before
169   if (!contains_key(Type_ordinal, name) || get(Type_ordinal, name) == 0)
170     put(Type_ordinal, name, Next_type_ordinal++);
171   type_info& info = get_or_insert(Type, get(Type_ordinal, name));
172   info.type_ingredient_names.swap(type_ingredient_names);
173   return true;
174 }
175 
176 bool slurp_type_ingredients(istream& in, map<string, type_ordinal>& out, const string& container_name) {
177   int next_type_ordinal = START_TYPE_INGREDIENTS;
178   while (has_data(in)) {
179     string curr = slurp_until(in, ':');
180     if (curr.empty()) {
181       raise << container_name << ": empty type ingredients not permitted\n" << end();
182       return false;
183     }
184     if (!starts_with(curr, "_")) {
185       raise << container_name << ": type ingredient '" << curr << "' must begin with an underscore\n" << end();
186       return false;
187     }
188     if (out.find(curr) != out.end()) {
189       raise << container_name << ": can't repeat type ingredient name'" << curr << "' in a single container definition\n" << end();
190       return false;
191     }
192     put(out, curr, next_type_ordinal++);
193   }
194   return true;
195 }
196 
197 bool type_ingredients_match(const map<string, type_ordinal>& a, const map<string, type_ordinal>& b) {
198   if (SIZE(a) != SIZE(b)) return false;
199   for (map<string, type_ordinal>::const_iterator p = a.begin();  p != a.end();  ++p) {
200     if (!contains_key(b, p->first)) return false;
201     if (p->second != get(b, p->first)) return false;
202   }
203   return true;
204 }
205 
206 :(before "End insert_container Special-cases")
207 // check for use of type ingredients
208 else if (is_type_ingredient_name(type->name)) {
209   type->value = get(info.type_ingredient_names, type->name);
210 }
211 :(code)
212 bool is_type_ingredient_name(const string& type) {
213   return starts_with(type, "_");
214 }
215 
216 :(before "End Container Type Checks")
217 if (type->value >= START_TYPE_INGREDIENTS
218     && (type->value - START_TYPE_INGREDIENTS) < SIZE(get(Type, type->value).type_ingredient_names))
219   return;
220 
221 :(scenario size_of_shape_shifting_exclusive_container)
222 exclusive-container foo:_t [
223   x:_t
224   y:num
225 ]
226 def main [
227   1:foo:num <- merge 0/x, 34
228   3:foo:point <- merge 0/x, 15, 16
229   6:foo:point <- merge 1/y, 23
230 ]
231 +run: {1: ("foo" "number")} <- merge {0: "literal", "x": ()}, {34: "literal"}
232 +mem: storing 0 in location 1
233 +mem: storing 34 in location 2
234 +run: {3: ("foo" "point")} <- merge {0: "literal", "x": ()}, {15: "literal"}, {16: "literal"}
235 +mem: storing 0 in location 3
236 +mem: storing 15 in location 4
237 +mem: storing 16 in location 5
238 +run: {6: ("foo" "point")} <- merge {1: "literal", "y": ()}, {23: "literal"}
239 +mem: storing 1 in location 6
240 +mem: storing 23 in location 7
241 +run: return
242 # no other stores
243 % CHECK_EQ(trace_count_prefix("mem", "storing"), 7);
244 
245 :(before "End variant_type Special-cases")
246 if (contains_type_ingredient(element))
247   replace_type_ingredients(element.type, type->right, info, " while computing variant type of exclusive-container");
248 
249 :(scenario get_on_shape_shifting_container)
250 container foo:_t [
251   x:_t
252   y:num
253 ]
254 def main [
255   1:foo:point <- merge 14, 15, 16
256   2:num <- get 1:foo:point, y:offset
257 ]
258 +mem: storing 16 in location 2
259 
260 :(scenario get_on_shape_shifting_container_2)
261 container foo:_t [
262   x:_t
263   y:num
264 ]
265 def main [
266   1:foo:point <- merge 14, 15, 16
267   2:point <- get 1:foo:point, x:offset
268 ]
269 +mem: storing 14 in location 2
270 +mem: storing 15 in location 3
271 
272 :(scenario get_on_shape_shifting_container_3)
273 container foo:_t [
274   x:_t
275   y:num
276 ]
277 def main [
278   1:foo:&:point <- merge 34/unsafe, 48
279   3:&:point <- get 1:foo:&:point, x:offset
280 ]
281 +mem: storing 34 in location 3
282 
283 :(scenario get_on_shape_shifting_container_inside_container)
284 container foo:_t [
285   x:_t
286   y:num
287 ]
288 container bar [
289   x:foo:point
290   y:num
291 ]
292 def main [
293   1:bar <- merge 14, 15, 16, 17
294   2:num <- get 1:bar, 1:offset
295 ]
296 +mem: storing 17 in location 2
297 
298 :(scenario get_on_complex_shape_shifting_container)
299 container foo:_a:_b [
300   x:_a
301   y:_b
302 ]
303 def main [
304   1:text <- new [abc]
305   {2: (foo number (address array character))} <- merge 34/x, 1:text/y
306   3:text <- get {2: (foo number (address array character))}, y:offset
307   4:bool <- equal 1:text, 3:text
308 ]
309 +mem: storing 1 in location 4
310 
311 :(before "End element_type Special-cases")
312 replace_type_ingredients(element, type, info, " while computing element type of container");
313 :(before "Compute Container Size(element, full_type)")
314 replace_type_ingredients(element, full_type, container_info, location_for_error_messages);
315 :(before "Compute Exclusive Container Size(element, full_type)")
316 replace_type_ingredients(element, full_type, exclusive_container_info, location_for_error_messages);
317 //? :(before "Compute Container Address Offset(element)")
318 //? replace_type_ingredients(element, type, info, location_for_error_messages);
319 //? if (contains_type_ingredient(element)) return;  // error raised elsewhere
320 
321 :(after "Compute size_of Container")
322 assert(!contains_type_ingredient(type));
323 :(after "Compute size_of Exclusive Container")
324 assert(!contains_type_ingredient(type));
325 
326 :(code)
327 bool contains_type_ingredient(const reagent& x) {
328   return contains_type_ingredient(x.type);
329 }
330 
331 bool contains_type_ingredient(const type_tree* type) {
332   if (!type) return false;
333   if (type->atom) return type->value >= START_TYPE_INGREDIENTS;
334   return contains_type_ingredient(type->left) || contains_type_ingredient(type->right);
335 }
336 
337 void replace_type_ingredients(reagent& element, const type_tree* caller_type, const type_info& info, const string& location_for_error_messages) {
338   if (contains_type_ingredient(element)) {
339     if (!caller_type->right)
340       raise << "illegal type " << names_to_string(caller_type) << " seems to be missing a type ingredient or three" << location_for_error_messages << '\n' << end();
341     replace_type_ingredients(element.type, caller_type->right, info, location_for_error_messages);
342   }
343 }
344 
345 // replace all type_ingredients in element_type with corresponding elements of callsite_type
346 void replace_type_ingredients(type_tree* element_type, const type_tree* callsite_type, const type_info& container_info, const string& location_for_error_messages) {
347   if (!callsite_type) return;  // error but it's already been raised above
348   if (!element_type) return;
349   if (!element_type->atom) {
350     if (element_type->right == NULL && is_type_ingredient(element_type->left)) {
351       int type_ingredient_index = to_type_ingredient_index(element_type->left);
352       if (corresponding(callsite_type, type_ingredient_index, is_final_type_ingredient(type_ingredient_index, container_info))->right) {
353         // replacing type ingredient at end of list, and replacement is a non-degenerate compound type -- (a b) but not (a)
354         replace_type_ingredient_at(type_ingredient_index, element_type, callsite_type, container_info, location_for_error_messages);
355         return;
356       }
357     }
358     replace_type_ingredients(element_type->left, callsite_type, container_info, location_for_error_messages);
359     replace_type_ingredients(element_type->right, callsite_type, container_info, location_for_error_messages);
360     return;
361   }
362   if (is_type_ingredient(element_type))
363     replace_type_ingredient_at(to_type_ingredient_index(element_type), element_type, callsite_type, container_info, location_for_error_messages);
364 }
365 
366 const type_tree* corresponding(const type_tree* type, int index, bool final) {
367   for (const type_tree* curr = type;  curr;  curr = curr->right, --index) {
368     assert_for_now(!curr->atom);
369     if (index == 0)
370       return final ? curr : curr->left;
371   }
372   assert_for_now(false);
373 }
374 
375 bool is_type_ingredient(const type_tree* type) {
376   return type->atom && type->value >= START_TYPE_INGREDIENTS;
377 }
378 
379 int to_type_ingredient_index(const type_tree* type) {
380   assert(type->atom);
381   return type->value-START_TYPE_INGREDIENTS;
382 }
383 
384 void replace_type_ingredient_at(const int type_ingredient_index, type_tree* element_type, const type_tree* callsite_type, const type_info& container_info, const string& location_for_error_messages) {
385   if (!has_nth_type(callsite_type, type_ingredient_index)) {
386     raise << "illegal type " << names_to_string(callsite_type) << " seems to be missing a type ingredient or three" << location_for_error_messages << '\n' << end();
387     return;
388   }
389   *element_type = *nth_type_ingredient(callsite_type, type_ingredient_index, container_info);
390 }
391 
392 const type_tree* nth_type_ingredient(const type_tree* callsite_type, int type_ingredient_index, const type_info& container_info) {
393   bool final = is_final_type_ingredient(type_ingredient_index, container_info);
394   const type_tree* curr = callsite_type;
395   for (int i = 0;  i < type_ingredient_index;  ++i) {
396     assert(curr);
397     assert(!curr->atom);
398 //?     cerr << "type ingredient " << i << " is " << to_string(curr->left) << '\n';
399     curr = curr->right;
400   }
401   assert(curr);
402   if (curr->atom) return curr;
403   if (!final) return curr->left;
404   if (!curr->right) return curr->left;
405   return curr;
406 }
407 
408 bool is_final_type_ingredient(int type_ingredient_index, const type_info& container_info) {
409   for (map<string, type_ordinal>::const_iterator p = container_info.type_ingredient_names.begin();
410        p != container_info.type_ingredient_names.end();
411        ++p) {
412     if (p->second > START_TYPE_INGREDIENTS+type_ingredient_index) return false;
413   }
414   return true;
415 }
416 
417 :(before "End Unit Tests")
418 void test_replace_type_ingredients_entire() {
419   run("container foo:_elem [\n"
420       "  x:_elem\n"
421       "  y:num\n"
422       "]\n");
423   reagent callsite("x:foo:point");
424   reagent element = element_type(callsite.type, 0);
425   CHECK_EQ(to_string(element), "{x: \"point\"}");
426 }
427 
428 void test_replace_type_ingredients_tail() {
429   run("container foo:_elem [\n"
430       "  x:_elem\n"
431       "]\n"
432       "container bar:_elem [\n"
433       "  x:foo:_elem\n"
434       "]\n");
435   reagent callsite("x:bar:point");
436   reagent element = element_type(callsite.type, 0);
437   CHECK_EQ(to_string(element), "{x: (\"foo\" \"point\")}");
438 }
439 
440 void test_replace_type_ingredients_head_tail_multiple() {
441   run("container foo:_elem [\n"
442       "  x:_elem\n"
443       "]\n"
444       "container bar:_elem [\n"
445       "  x:foo:_elem\n"
446       "]\n");
447   reagent callsite("x:bar:address:array:character");
448   reagent element = element_type(callsite.type, 0);
449   CHECK_EQ(to_string(element), "{x: (\"foo\" \"address\" \"array\" \"character\")}");
450 }
451 
452 void test_replace_type_ingredients_head_middle() {
453   run("container foo:_elem [\n"
454       "  x:_elem\n"
455       "]\n"
456       "container bar:_elem [\n"
457       "  x:foo:_elem:num\n"
458       "]\n");
459   reagent callsite("x:bar:address");
460   reagent element = element_type(callsite.type, 0);
461   CHECK_EQ(to_string(element), "{x: (\"foo\" \"address\" \"number\")}");
462 }
463 
464 void test_replace_last_type_ingredient_with_multiple() {
465   run("container foo:_a:_b [\n"
466       "  x:_a\n"
467       "  y:_b\n"
468       "]\n");
469   reagent callsite("{f: (foo number (address array character))}");
470   reagent element1 = element_type(callsite.type, 0);
471   CHECK_EQ(to_string(element1), "{x: \"number\"}");
472   reagent element2 = element_type(callsite.type, 1);
473   CHECK_EQ(to_string(element2), "{y: (\"address\" \"array\" \"character\")}");
474 }
475 
476 void test_replace_last_type_ingredient_inside_compound() {
477   run("container foo:_a:_b [\n"
478       "  {x: (bar _a (address _b))}\n"
479       "]\n");
480   reagent callsite("f:foo:number:array:character");
481   reagent element = element_type(callsite.type, 0);
482   CHECK_EQ(names_to_string_without_quotes(element.type), "(bar number (address array character))");
483 }
484 
485 void test_replace_middle_type_ingredient_with_multiple() {
486   run("container foo:_a:_b:_c [\n"
487       "  x:_a\n"
488       "  y:_b\n"
489       "  z:_c\n"
490       "]\n");
491   reagent callsite("{f: (foo number (address array character) boolean)}");
492   reagent element1 = element_type(callsite.type, 0);
493   CHECK_EQ(to_string(element1), "{x: \"number\"}");
494   reagent element2 = element_type(callsite.type, 1);
495   CHECK_EQ(to_string(element2), "{y: (\"address\" \"array\" \"character\")}");
496   reagent element3 = element_type(callsite.type, 2);
497   CHECK_EQ(to_string(element3), "{z: \"boolean\"}");
498 }
499 
500 void test_replace_middle_type_ingredient_with_multiple2() {
501   run("container foo:_key:_value [\n"
502       "  key:_key\n"
503       "  value:_value\n"
504       "]\n");
505   reagent callsite("{f: (foo (address array character) number)}");
506   reagent element = element_type(callsite.type, 0);
507   CHECK_EQ(to_string(element), "{key: (\"address\" \"array\" \"character\")}");
508 }
509 
510 void test_replace_middle_type_ingredient_with_multiple3() {
511   run("container foo_table:_key:_value [\n"
512       "  data:&:@:foo_table_row:_key:_value\n"
513       "]\n"
514       "\n"
515       "container foo_table_row:_key:_value [\n"
516       "  key:_key\n"
517       "  value:_value\n"
518       "]\n");
519   reagent callsite("{f: (foo_table (address array character) number)}");
520   reagent element = element_type(callsite.type, 0);
521   CHECK_EQ(to_string(element), "{data: (\"address\" \"array\" \"foo_table_row\" (\"address\" \"array\" \"character\") \"number\")}");
522 }
523 
524 :(code)
525 bool has_nth_type(const type_tree* base, int n) {
526   assert(n >= 0);
527   if (!base) return false;
528   if (n == 0) return true;
529   return has_nth_type(base->right, n-1);
530 }
531 
532 :(scenario get_on_shape_shifting_container_error)
533 % Hide_errors = true;
534 container foo:_t [
535   x:_t
536   y:num
537 ]
538 def main [
539   10:foo:point <- merge 14, 15, 16
540   1:num <- get 10:foo, 1:offset
541 ]
542 +error: illegal type "foo" seems to be missing a type ingredient or three in '1:num <- get 10:foo, 1:offset'
543 
544 //:: fix up previous layers
545 
546 //: We have two transforms in previous layers -- for computing sizes and
547 //: offsets containing addresses for containers and exclusive containers --
548 //: that we need to teach about type ingredients.
549 
550 :(before "End compute_container_sizes Non-atom Special-cases")
551 const type_tree* root = get_base_type(type);
552 if (contains_key(Type, root->value)) {
553   type_info& info = get(Type, root->value);
554   if (info.kind == CONTAINER) {
555     compute_container_sizes(info, type, pending_metadata, location_for_error_messages);
556     return;
557   }
558   if (info.kind == EXCLUSIVE_CONTAINER) {
559     compute_exclusive_container_sizes(info, type, pending_metadata, location_for_error_messages);
560     return;
561   }
562 }  // otherwise error raised elsewhere
563 
564 :(before "End Unit Tests")
565 void test_container_sizes_shape_shifting_container() {
566   run("container foo:_t [\n"
567       "  x:num\n"
568       "  y:_t\n"
569       "]\n");
570   reagent r("x:foo:point");
571   compute_container_sizes(r, "");
572   CHECK_EQ(r.metadata.size, 3);
573 }
574 
575 void test_container_sizes_shape_shifting_exclusive_container() {
576   run("exclusive-container foo:_t [\n"
577       "  x:num\n"
578       "  y:_t\n"
579       "]\n");
580   reagent r("x:foo:point");
581   compute_container_sizes(r, "");
582   CHECK_EQ(r.metadata.size, 3);
583   reagent r2("x:foo:num");
584   compute_container_sizes(r2, "");
585   CHECK_EQ(r2.metadata.size, 2);
586 }
587 
588 void test_container_sizes_compound_type_ingredient() {
589   run("container foo:_t [\n"
590       "  x:num\n"
591       "  y:_t\n"
592       "]\n");
593   reagent r("x:foo:&:point");
594   compute_container_sizes(r, "");
595   CHECK_EQ(r.metadata.size, 2);
596   // scan also pre-computes metadata for type ingredient
597   reagent point("x:point");
598   CHECK(contains_key(Container_metadata, point.type));
599   CHECK_EQ(get(Container_metadata, point.type).size, 2);
600 }
601 
602 void test_container_sizes_recursive_shape_shifting_container() {
603   run("container foo:_t [\n"
604       "  x:num\n"
605       "  y:&:foo:_t\n"
606       "]\n");
607   reagent r2("x:foo:num");
608   compute_container_sizes(r2, "");
609   CHECK_EQ(r2.metadata.size, 2);
610 }
611 
612 :(scenario typos_in_container_definitions)
613 % Hide_errors = true;
614 container foo:_t [
615   x:adress:_t  # typo
616 ]
617 def main [
618   local-scope
619   x:address:foo:num <- new {(foo num): type}
620 ]
621 # no crash
622 
623 :(scenario typos_in_recipes)
624 % Hide_errors = true;
625 def foo [
626   local-scope
627   x:adress:array:number <- copy 0  # typo
628 ]
629 # shouldn't crash
630 
631 //:: 'merge' on shape-shifting containers
632 
633 :(scenario merge_check_shape_shifting_container_containing_exclusive_container)
634 container foo:_elem [
635   x:num
636   y:_elem
637 ]
638 exclusive-container bar [
639   x:num
640   y:num
641 ]
642 def main [
643   1:foo:bar <- merge 23, 1/y, 34
644 ]
645 +mem: storing 23 in location 1
646 +mem: storing 1 in location 2
647 +mem: storing 34 in location 3
648 $error: 0
649 
650 :(scenario merge_check_shape_shifting_container_containing_exclusive_container_2)
651 % Hide_errors = true;
652 container foo:_elem [
653   x:num
654   y:_elem
655 ]
656 exclusive-container bar [
657   x:num
658   y:num
659 ]
660 def main [
661   1:foo:bar <- merge 23, 1/y, 34, 35
662 ]
663 +error: main: too many ingredients in '1:foo:bar <- merge 23, 1/y, 34, 35'
664 
665 :(scenario merge_check_shape_shifting_exclusive_container_containing_container)
666 exclusive-container foo:_elem [
667   x:num
668   y:_elem
669 ]
670 container bar [
671   x:num
672   y:num
673 ]
674 def main [
675   1:foo:bar <- merge 1/y, 23, 34
676 ]
677 +mem: storing 1 in location 1
678 +mem: storing 23 in location 2
679 +mem: storing 34 in location 3
680 $error: 0
681 
682 :(scenario merge_check_shape_shifting_exclusive_container_containing_container_2)
683 exclusive-container foo:_elem [
684   x:num
685   y:_elem
686 ]
687 container bar [
688   x:num
689   y:num
690 ]
691 def main [
692   1:foo:bar <- merge 0/x, 23
693 ]
694 $error: 0
695 
696 :(scenario merge_check_shape_shifting_exclusive_container_containing_container_3)
697 % Hide_errors = true;
698 exclusive-container foo:_elem [
699   x:num
700   y:_elem
701 ]
702 container bar [
703   x:num
704   y:num
705 ]
706 def main [
707   1:foo:bar <- merge 1/y, 23
708 ]
709 +error: main: too few ingredients in '1:foo:bar <- merge 1/y, 23'