1 int payload_size(reagent/*copy*/ x) {
  2   x.properties.push_back(pair<string, string_tree*>("lookup", NULL));
  3   lookup_memory_core(x, /*check_for_null*/false);
  4   return size_of(x);
  5 }
  6 
  7 :(before "End type_tree Definition")
  8 struct address_element_info {
  9   // Where inside a container type (after flattening nested containers!) the
 10   // address lies
 11   int offset;
 12 
 13   // All the information we need to compute sizes of items inside an address
 14   // inside a container. 'payload_type' doesn't need to be a full-scale
 15   // reagent because an address inside a container can never be an array, and
 16   // because arrays are the only type that need to know their location to
 17   // compute their size.
 18   const type_tree* payload_type;
 19 
 20   address_element_info(int o, const type_tree* p);
 21   address_element_info(const address_element_info& other);
 22   ~address_element_info();
 23   address_element_info& operator=(const address_element_info& other);
 24 };
 25 :(code)
 26 address_element_info::address_element_info(int o, const type_tree* p) {
 27   offset = o;
 28   payload_type = p;
 29 }
 30 address_element_info::address_element_info(const address_element_info& other) {
 31   offset = other.offset;
 32   payload_type = copy(other.payload_type);
 33 }
 34 address_element_info::~address_element_info() {
 35   if (payload_type) {
 36     delete payload_type;
 37     payload_type = NULL;
 38   }
 39 }
 40 address_element_info& address_element_info::operator=(const address_element_info& other) {
 41   offset = other.offset;
 42   if (payload_type) delete payload_type;
 43   payload_type = copy(other.payload_type);
 44   return *this;
 45 }
 46 
 47 :(before "End type_tree Definition")
 48 // For exclusive containers we might sometimes have an address at some offset
 49 // if some other offset has a specific tag. This struct encapsulates such
 50 // guards.
 51 struct tag_condition_info {
 52   int offset;
 53   int tag;
 54   tag_condition_info(int o, int t) :offset(o), tag(t) {}
 55 };
 56 
 57 :(before "End container_metadata Fields")
 58 // a list of facts of the form:
 59 //
 60 //  IF offset o1 has tag t2 AND offset o2 has tag t2 AND .., THEN
 61 //    for all address_element_infos:
 62 //      there is an address at 'offset' pointing to a payload of type payload_type
 63 map<set<tag_condition_info>, set<address_element_info> > address;
 64 :(code)
 65 bool operator<(const set<tag_condition_info>& a, const set<tag_condition_info>& b) {
 66   if (a.size() != b.size()) return a.size() < b.size();
 67   for (set<tag_condition_info>::const_iterator pa = a.begin(), pb = b.begin();  pa != a.end();  ++pa, ++pb) {
 68     if (pa->offset != pb->offset) return pa->offset < pb->offset;
 69     if (pa->tag != pb->tag) return pa->tag < pb->tag;
 70   }
 71   return false;  // equal
 72 }
 73 bool operator<(const tag_condition_info& a, const tag_condition_info& b) {
 74   if (a.offset != b.offset) return a.offset < b.offset;
 75   if (a.tag != b.tag) return a.tag < b.tag;
 76   return false;  // equal
 77 }
 78 bool operator<(const set<address_element_info>& a, const set<address_element_info>& b) {
 79   if (a.size() != b.size()) return a.size() < b.size();
 80   for (set<address_element_info>::const_iterator pa = a.begin(), pb = b.begin();  pa != a.end();  ++pa, ++pb) {
 81     if (pa->offset != pb->offset) return pa->offset < pb->offset;
 82   }
 83   return false;  // equal
 84 }
 85 bool operator<(const address_element_info& a, const address_element_info& b) {
 86   if (a.offset != b.offset) return a.offset < b.offset;
 87   return false;  // equal
 88 }
 89 
 90 //: populate metadata.address in a separate transform, because it requires
 91 //: already knowing the sizes of all types
 92 
 93 :(after "Transform.push_back(compute_container_sizes)")
 94 Transform.push_back(compute_container_address_offsets);  // idempotent
 95 :(code)
 96 void compute_container_address_offsets(const recipe_ordinal r) {
 97   recipe& caller = get(Recipe, r);
 98   trace(9992, "transform") << "--- compute address offsets for " << caller.name << end();
 99   for (int i = 0;  i < SIZE(caller.steps);  ++i) {
100     instruction& inst = caller.steps.at(i);
101     trace(9993, "transform") << "- compute address offsets for " << to_string(inst) << end();
102     for (int i = 0;  i < SIZE(inst.ingredients);  ++i)
103       compute_container_address_offsets(inst.ingredients.at(i), " in '"+inst.original_string+"'");
104     for (int i = 0;  i < SIZE(inst.products);  ++i)
105       compute_container_address_offsets(inst.products.at(i), " in '"+inst.original_string+"'");
106   }
107 }
108 
109 void compute_container_address_offsets(reagent& r, const string& location_for_error_messages) {
110   if (is_literal(r) || is_dummy(r)) return;
111   compute_container_address_offsets(r.type, location_for_error_messages);
112   if (contains_key(Container_metadata, r.type))
113     r.metadata = get(Container_metadata, r.type);
114 }
115 
116 // the recursive structure of this function needs to exactly match
117 // compute_container_sizes
118 void compute_container_address_offsets(const type_tree* type, const string& location_for_error_messages) {
119   if (!type) return;
120   if (!type->atom) {
121     if (!type->left->atom) {
122       raise << "invalid type " << to_string(type) << location_for_error_messages << '\n' << end();
123       return;
124     }
125     if (type->left->name == "address")
126       compute_container_address_offsets(payload_type(type), location_for_error_messages);
127     else if (type->left->name == "array")
128       compute_container_address_offsets(array_element(type), location_for_error_messages);
129     // End compute_container_address_offsets Non-atom Special-cases
130   }
131   const type_tree* base_type = type;
132   // Update base_type in compute_container_address_offsets
133   if (!contains_key(Type, base_type->value)) return;  // error raised elsewhere
134   type_info& info = get(Type, base_type->value);
135   if (info.kind == CONTAINER) {
136     compute_container_address_offsets(info, type, location_for_error_messages);
137   }
138   if (info.kind == EXCLUSIVE_CONTAINER) {
139     compute_exclusive_container_address_offsets(info, type, location_for_error_messages);
140   }
141 }
142 
143 void compute_container_address_offsets(const type_info& container_info, const type_tree* full_type, const string& location_for_error_messages) {
144   container_metadata& metadata = get(Container_metadata, full_type);
145   if (!metadata.address.empty()) return;
146   trace(9994, "transform") << "compute address offsets for container " << container_info.name << end();
147   append_addresses(0, full_type, metadata.address, set<tag_condition_info>(), location_for_error_messages);
148 }
149 
150 void compute_exclusive_container_address_offsets(const type_info& exclusive_container_info, const type_tree* full_type, const string& location_for_error_messages) {
151   container_metadata& metadata = get(Container_metadata, full_type);
152   trace(9994, "transform") << "compute address offsets for exclusive container " << exclusive_container_info.name << end();
153   for (int tag = 0;  tag < SIZE(exclusive_container_info.elements);  ++tag) {
154     set<tag_condition_info> key;
155     key.insert(tag_condition_info(/*tag is at offset*/0, tag));
156     append_addresses(/*skip tag offset*/1, variant_type(full_type, tag).type, metadata.address, key, location_for_error_messages);
157   }
158 }
159 
160 void append_addresses(int base_offset, const type_tree* type, map<set<tag_condition_info>, set<address_element_info> >& out, const set<tag_condition_info>& key, const string& location_for_error_messages) {
161   if (is_mu_address(type)) {
162     get_or_insert(out, key).insert(address_element_info(base_offset, new type_tree(*payload_type(type))));
163     return;
164   }
165   const type_tree* base_type = type;
166   // Update base_type in append_container_address_offsets
167   const type_info& info = get(Type, base_type->value);
168   if (info.kind == CONTAINER) {
169     for (int curr_index = 0, curr_offset = base_offset;  curr_index < SIZE(info.elements);  ++curr_index) {
170       trace(9993, "transform") << "checking container " << base_type->name << ", element " << curr_index << end();
171       reagent/*copy*/ element = element_type(type, curr_index);  // not base_type
172       // Compute Container Address Offset(element)
173       if (is_mu_address(element)) {
174         trace(9993, "transform") << "address at offset " << curr_offset << end();
175         get_or_insert(out, key).insert(address_element_info(curr_offset, new type_tree(*payload_type(element.type))));
176         ++curr_offset;
177       }
178       else if (is_mu_array(element)) {
179         curr_offset += /*array length*/1;
180         const type_tree* array_element_type = array_element(element.type);
181         int array_element_size = size_of(array_element_type);
182         for (int i = 0; i < static_array_length(element.type); ++i) {
183           append_addresses(curr_offset, array_element_type, out, key, location_for_error_messages);
184           curr_offset += array_element_size;
185         }
186       }
187       else if (is_mu_container(element)) {
188         append_addresses(curr_offset, element.type, out, key, location_for_error_messages);
189         curr_offset += size_of(element);
190       }
191       else if (is_mu_exclusive_container(element)) {
192         const type_tree* element_base_type = element.type;
193         // Update element_base_type For Exclusive Container in append_addresses
194         const type_info& element_info = get(Type, element_base_type->value);
195         for (int tag = 0;  tag < SIZE(element_info.elements);  ++tag) {
196           set<tag_condition_info> new_key = key;
197           new_key.insert(tag_condition_info(curr_offset, tag));
198           if (!contains_key(out, new_key))
199             append_addresses(curr_offset+/*skip tag*/1, variant_type(element.type, tag).type, out, new_key, location_for_error_messages);
200         }
201         curr_offset += size_of(element);
202       }
203       else {
204         // non-address primitive
205         ++curr_offset;
206       }
207     }
208   }
209   else if (info.kind == EXCLUSIVE_CONTAINER) {
210     for (int tag = 0;  tag < SIZE(info.elements);  ++tag) {
211       set<tag_condition_info> new_key = key;
212       new_key.insert(tag_condition_info(base_offset, tag));
213       if (!contains_key(out, new_key))
214         append_addresses(base_offset+/*skip tag*/1, variant_type(type, tag).type, out, new_key, location_for_error_messages);
215     }
216   }
217 }
218 
219 //: for the following unit tests we'll do the work of the transform by hand
220 
221 :(before "End Unit Tests")
222 void test_container_address_offsets_empty() {
223   int old_size = SIZE(Container_metadata);
224   // define a container with no addresses
225   reagent r("x:point");
226   compute_container_sizes(r, "");  // need to first pre-populate the metadata
227   // scan
228   compute_container_address_offsets(r, "");
229   // global metadata contains just the entry for foo
230   // no entries for non-container types or other junk
231   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
232   // the reagent we scanned knows it has no addresses
233   CHECK(r.metadata.address.empty());
234   // the global table contains an identical entry
235   CHECK(contains_key(Container_metadata, r.type));
236   CHECK(get(Container_metadata, r.type).address.empty());
237   // compute_container_address_offsets creates no new entries
238   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
239 }
240 
241 void test_container_address_offsets() {
242   int old_size = SIZE(Container_metadata);
243   // define a container with an address at offset 0 that we have the size for
244   run("container foo [\n"
245       "  x:address:num\n"
246       "]\n");
247   reagent r("x:foo");
248   compute_container_sizes(r, "");  // need to first pre-populate the metadata
249   // scan
250   compute_container_address_offsets(r, "");
251   // global metadata contains just the entry for foo
252   // no entries for non-container types or other junk
253   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
254   // the reagent we scanned knows it has an address at offset 0
255   CHECK_EQ(SIZE(r.metadata.address), 1);
256   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
257   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());  // unconditional for containers
258   CHECK_EQ(SIZE(address_offsets), 1);
259   CHECK_EQ(address_offsets.begin()->offset, 0);
260   CHECK(address_offsets.begin()->payload_type->atom);
261   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
262   // the global table contains an identical entry
263   CHECK(contains_key(Container_metadata, r.type));
264   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
265   CHECK_EQ(SIZE(address_offsets2), 1);
266   CHECK_EQ(address_offsets2.begin()->offset, 0);
267   CHECK(address_offsets2.begin()->payload_type->atom);
268   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
269   // compute_container_address_offsets creates no new entries
270   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
271 }
272 
273 void test_container_address_offsets_2() {
274   int old_size = SIZE(Container_metadata);
275   // define a container with an address at offset 1 that we have the size for
276   run("container foo [\n"
277       "  x:num\n"
278       "  y:address:num\n"
279       "]\n");
280   reagent r("x:foo");
281   compute_container_sizes(r, "");  // need to first pre-populate the metadata
282   // global metadata contains just the entry for foo
283   // no entries for non-container types or other junk
284   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
285   // scan
286   compute_container_address_offsets(r, "");
287   // compute_container_address_offsets creates no new entries
288   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
289   // the reagent we scanned knows it has an address at offset 1
290   CHECK_EQ(SIZE(r.metadata.address), 1);
291   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
292   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());
293   CHECK_EQ(SIZE(address_offsets), 1);
294   CHECK_EQ(address_offsets.begin()->offset, 1);  //
295   CHECK(address_offsets.begin()->payload_type->atom);
296   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
297   // the global table contains an identical entry
298   CHECK(contains_key(Container_metadata, r.type));
299   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
300   CHECK_EQ(SIZE(address_offsets2), 1);
301   CHECK_EQ(address_offsets2.begin()->offset, 1);  //
302   CHECK(address_offsets2.begin()->payload_type->atom);
303   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
304 }
305 
306 void test_container_address_offsets_nested() {
307   int old_size = SIZE(Container_metadata);
308   // define a container with a nested container containing an address
309   run("container foo [\n"
310       "  x:address:num\n"
311       "  y:num\n"
312       "]\n"
313       "container bar [\n"
314       "  p:point\n"
315       "  f:foo\n"  // nested container containing address
316       "]\n");
317   reagent r("x:bar");
318   compute_container_sizes(r, "");  // need to first pre-populate the metadata
319   // global metadata contains entries for bar and included types: point and foo
320   // no entries for non-container types or other junk
321   CHECK_EQ(SIZE(Container_metadata)-old_size, 3);
322   // scan
323   compute_container_address_offsets(r, "");
324   // the reagent we scanned knows it has an address at offset 2
325   CHECK_EQ(SIZE(r.metadata.address), 1);
326   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
327   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());
328   CHECK_EQ(SIZE(address_offsets), 1);
329   CHECK_EQ(address_offsets.begin()->offset, 2);  //
330   CHECK(address_offsets.begin()->payload_type->atom);
331   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
332   // the global table also knows its address offset
333   CHECK(contains_key(Container_metadata, r.type));
334   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
335   CHECK_EQ(SIZE(address_offsets2), 1);
336   CHECK_EQ(address_offsets2.begin()->offset, 2);  //
337   CHECK(address_offsets2.begin()->payload_type->atom);
338   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
339   // compute_container_address_offsets creates no new entries
340   CHECK_EQ(SIZE(Container_metadata)-old_size, 3);
341 }
342 
343 void test_container_address_offsets_from_address() {
344   int old_size = SIZE(Container_metadata);
345   // define a container with an address at offset 0
346   run("container foo [\n"
347       "  x:address:num\n"
348       "]\n");
349   reagent r("x:address:foo");
350   compute_container_sizes(r, "");  // need to first pre-populate the metadata
351   // global metadata contains just the entry for foo
352   // no entries for non-container types or other junk
353   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
354   // scan an address to the container
355   compute_container_address_offsets(r, "");
356   // compute_container_address_offsets creates no new entries
357   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
358   // scanning precomputed metadata for the container
359   reagent container("x:foo");
360   CHECK(contains_key(Container_metadata, container.type));
361   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
362   CHECK_EQ(SIZE(address_offsets2), 1);
363   CHECK_EQ(address_offsets2.begin()->offset, 0);
364   CHECK(address_offsets2.begin()->payload_type->atom);
365   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
366 }
367 
368 void test_container_address_offsets_from_array() {
369   int old_size = SIZE(Container_metadata);
370   // define a container with an address at offset 0
371   run("container foo [\n"
372       "  x:address:num\n"
373       "]\n");
374   reagent r("x:array:foo");
375   compute_container_sizes(r, "");  // need to first pre-populate the metadata
376   // global metadata contains just the entry for foo
377   // no entries for non-container types or other junk
378   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
379   // scan an array of the container
380   compute_container_address_offsets(r, "");
381   // compute_container_address_offsets creates no new entries
382   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
383   // scanning precomputed metadata for the container
384   reagent container("x:foo");
385   CHECK(contains_key(Container_metadata, container.type));
386   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
387   CHECK_EQ(SIZE(address_offsets2), 1);
388   CHECK_EQ(address_offsets2.begin()->offset, 0);
389   CHECK(address_offsets2.begin()->payload_type->atom);
390   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
391 }
392 
393 void test_container_address_offsets_from_address_to_array() {
394   int old_size = SIZE(Container_metadata);
395   // define a container with an address at offset 0
396   run("container foo [\n"
397       "  x:address:num\n"
398       "]\n");
399   reagent r("x:address:array:foo");
400   compute_container_sizes(r, "");  // need to first pre-populate the metadata
401   // global metadata contains just the entry for foo
402   // no entries for non-container types or other junk
403   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
404   // scan an address to an array of the container
405   compute_container_address_offsets(r, "");
406   // compute_container_address_offsets creates no new entries
407   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
408   // scanning precomputed metadata for the container
409   reagent container("x:foo");
410   CHECK(contains_key(Container_metadata, container.type));
411   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
412   CHECK_EQ(SIZE(address_offsets2), 1);
413   CHECK_EQ(address_offsets2.begin()->offset, 0);
414   CHECK(address_offsets2.begin()->payload_type->atom);
415   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
416 }
417 
418 void test_container_address_offsets_from_static_array() {
419   int old_size = SIZE(Container_metadata);
420   // define a container with an address at offset 0
421   run("container foo [\n"
422       "  x:address:num\n"
423       "]\n");
424   reagent r("x:array:foo:10");
425   compute_container_sizes(r, "");  // need to first pre-populate the metadata
426   // global metadata contains just the entry for foo
427   // no entries for non-container types or other junk
428   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
429   // scan a static array of the container
430   compute_container_address_offsets(r, "");
431   // compute_container_address_offsets creates no new entries
432   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
433   // scanning precomputed metadata for the container
434   reagent container("x:foo");
435   CHECK(contains_key(Container_metadata, container.type));
436   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
437   CHECK_EQ(SIZE(address_offsets2), 1);
438   CHECK_EQ(address_offsets2.begin()->offset, 0);
439   CHECK(address_offsets2.begin()->payload_type->atom);
440   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
441 }
442 
443 void test_container_address_offsets_from_address_to_static_array() {
444   int old_size = SIZE(Container_metadata);
445   // define a container with an address at offset 0
446   run("container foo [\n"
447       "  x:address:num\n"
448       "]\n");
449   reagent r("x:address:array:foo:10");
450   compute_container_sizes(r, "");  // need to first pre-populate the metadata
451   // global metadata contains just the entry for foo
452   // no entries for non-container types or other junk
453   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
454   // scan an address to a static array of the container
455   compute_container_address_offsets(r, "");
456   // compute_container_address_offsets creates no new entries
457   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
458   // scanning precomputed metadata for the container
459   reagent container("x:foo");
460   CHECK(contains_key(Container_metadata, container.type));
461   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
462   CHECK_EQ(SIZE(address_offsets2), 1);
463   CHECK_EQ(address_offsets2.begin()->offset, 0);
464   CHECK(address_offsets2.begin()->payload_type->atom);
465   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
466 }
467 
468 void test_container_address_offsets_from_repeated_address_and_array_types() {
469   int old_size = SIZE(Container_metadata);
470   // define a container with an address at offset 0
471   run("container foo [\n"
472       "  x:address:num\n"
473       "]\n");
474   // scan a deep nest of 'address' and 'array' types modifying a container
475   reagent r("x:address:array:address:address:array:foo:10");
476   compute_container_sizes(r, "");  // need to first pre-populate the metadata
477   // global metadata contains just the entry for foo
478   // no entries for non-container types or other junk
479   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
480   compute_container_address_offsets(r, "");
481   // compute_container_address_offsets creates no new entries
482   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
483   // scanning precomputed metadata for the container
484   reagent container("x:foo");
485   CHECK(contains_key(Container_metadata, container.type));
486   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
487   CHECK_EQ(SIZE(address_offsets2), 1);
488   CHECK_EQ(address_offsets2.begin()->offset, 0);
489   CHECK(address_offsets2.begin()->payload_type->atom);
490   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
491 }
492 
493 :(code)
494 bool all_match(const vector<double>& data, const set<tag_condition_info>& conditions) {
495   for (set<tag_condition_info>::const_iterator p = conditions.begin();  p != conditions.end();  ++p) {
496     if (data.at(p->offset) != p->tag)
497       return false;
498   }
499   return true;
500 }
501 
502 bool is_mu_container(const reagent& r) {
503   return is_mu_container(r.type);
504 }
505 bool is_mu_container(const type_tree* type) {
506   if (!type) return false;
507   // End is_mu_container(type) Special-cases
508   if (type->value == 0) return false;
509   if (!contains_key(Type, type->value)) return false;  // error raised elsewhere
510   type_info& info = get(Type, type->value);
511   return info.kind == CONTAINER;
512 }
513 
514 bool is_mu_exclusive_container(const reagent& r) {
515   return is_mu_exclusive_container(r.type);
516 }
517 bool is_mu_exclusive_container(const type_tree* type) {
518   if (!type) return false;
519   // End is_mu_exclusive_container(type) Special-cases
520   if (type->value == 0) return false;
521   if (!contains_key(Type, type->value)) return false;  // error raised elsewhere
522   type_info& info = get(Type, type->value);
523   return info.kind == EXCLUSIVE_CONTAINER;
524 }