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