//:: Container definitions can contain 'type ingredients'
//: pre-requisite: extend our notion of containers to not necessarily be
//: atomic types
:(after "Update GET base_type in Check")
base_type = get_base_type(base_type);
:(after "Update GET base_type in Run")
base_type = get_base_type(base_type);
:(after "Update PUT base_type in Check")
base_type = get_base_type(base_type);
:(after "Update PUT base_type in Run")
base_type = get_base_type(base_type);
:(after "Update MAYBE_CONVERT base_type in Check")
base_type = get_base_type(base_type);
:(after "Update base_type in element_type")
base_type = get_base_type(base_type);
:(after "Update base_type in skip_addresses")
base_type = get_base_type(base_type);
:(replace{} "const type_tree* get_base_type(const type_tree* t)")
const type_tree* get_base_type(const type_tree* t) {
const type_tree* result = t->atom ? t : t->left;
if (!result->atom)
raise << "invalid type " << to_string(t) << '\n' << end();
return result;
}
:(code)
void test_ill_formed_container() {
Hide_errors = true;
run(
"def main [\n"
" {1: ((foo) num)} <- copy 0\n"
"]\n"
);
// no crash
}
//: update size_of to handle non-atom container types
void test_size_of_shape_shifting_container() {
run(
"container foo:_t [\n"
" x:_t\n"
" y:num\n"
"]\n"
"def main [\n"
" 1:foo:num <- merge 12, 13\n"
" 3:foo:point <- merge 14, 15, 16\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"mem: storing 12 in location 1\n"
"mem: storing 13 in location 2\n"
"mem: storing 14 in location 3\n"
"mem: storing 15 in location 4\n"
"mem: storing 16 in location 5\n"
);
}
void test_size_of_shape_shifting_container_2() {
run(
// multiple type ingredients
"container foo:_a:_b [\n"
" x:_a\n"
" y:_b\n"
"]\n"
"def main [\n"
" 1:foo:num:bool <- merge 34, true\n"
"]\n"
);
CHECK_TRACE_COUNT("error", 0);
}
void test_size_of_shape_shifting_container_3() {
run(
"container foo:_a:_b [\n"
" x:_a\n"
" y:_b\n"
"]\n"
"def main [\n"
" 1:text <- new [abc]\n"
// compound types for type ingredients
" {3: (foo number (address array character))} <- merge 34/x, 1:text/y\n"
"]\n"
);
CHECK_TRACE_COUNT("error", 0);
}
void test_size_of_shape_shifting_container_4() {
run(
"container foo:_a:_b [\n"
" x:_a\n"
" y:_b\n"
"]\n"
"container bar:_a:_b [\n"
// dilated element
" {data: (foo _a (address _b))}\n"
"]\n"
"def main [\n"
" 1:text <- new [abc]\n"
" 3:bar:num:@:char <- merge 34/x, 1:text/y\n"
"]\n"
);
CHECK_TRACE_COUNT("error", 0);
}
void test_shape_shifting_container_extend() {
run(
"container foo:_a [\n"
" x:_a\n"
"]\n"
"container foo:_a [\n"
" y:_a\n"
"]\n"
);
CHECK_TRACE_COUNT("error", 0);
}
void test_shape_shifting_container_extend_error() {
Hide_errors = true;
run(
"container foo:_a [\n"
" x:_a\n"
"]\n"
"container foo:_b [\n"
" y:_b\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"error: headers of container 'foo' must use identical type ingredients\n"
);
}
void test_type_ingredient_must_start_with_underscore() {
Hide_errors = true;
run(
"container foo:t [\n"
" x:num\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"error: foo: type ingredient 't' must begin with an underscore\n"
);
}
:(before "End Globals")
// We'll use large type ordinals to mean "the following type of the variable".
// For example, if we have a generic type called foo:_elem, the type
// ingredient _elem in foo's type_info will have value START_TYPE_INGREDIENTS,
// and we'll handle it by looking in the current reagent for the next type
// that appears after foo.
extern const int START_TYPE_INGREDIENTS = 2000;
:(before "End Commandline Parsing") // after loading .mu files
assert(Next_type_ordinal < START_TYPE_INGREDIENTS);
:(before "End type_info Fields")
map<string, type_ordinal> type_ingredient_names;