2670 - improvements to generics

Eliminated a few holes, gained more clarity on the shape of others.

Maybe I was sleep-deprived, but this was really hard until I wrote a few
unit tests directly on replace_type_ingredient.

Still one flaw remaining: the type-checker isn't smart enough to handle
'merge' for all the new cases. Tests pass since we don't use those
features outside C++ tests yet.
This commit is contained in:
Kartik K. Agaram 2016-02-18 01:19:36 -08:00
parent 00472b898d
commit 07ca569193
2 changed files with 146 additions and 11 deletions

View File

@ -808,6 +808,8 @@ void check_merge_call(const vector<reagent>& ingredients, const reagent& product
default: {
if (!types_coercible(container, ingredients.at(ingredient_index))) {
raise_error << maybe(caller.name) << "incorrect type of ingredient " << ingredient_index << " in '" << inst.to_string() << "'\n" << end();
cerr << " expected " << debug_string(container) << '\n';
cerr << " got " << debug_string(ingredients.at(ingredient_index)) << '\n';
return;
}
++ingredient_index;

View File

@ -1,9 +1,7 @@
//:: Container definitions can contain type parameters.
//:: Container definitions can contain 'type ingredients'
//:
//: Extremely hacky initial implementation. We still don't support the full
//: complexity of type trees inside container definitions. So for example you
//: can't have a container element with this type:
//: (map (array address character) (list number))
//: Incomplete implementation; see the pending test below. There may be
//: others.
:(scenario size_of_shape_shifting_container)
container foo:_t [
@ -20,6 +18,41 @@ recipe main [
+mem: storing 15 in location 4
+mem: storing 16 in location 5
:(scenario size_of_shape_shifting_container_2)
# multiple type ingredients
container foo:_a:_b [
x:_a
y:_b
]
recipe main [
1:foo:number:boolean <- merge 34, 1/true
]
:(scenario size_of_shape_shifting_container_3)
container foo:_a:_b [
x:_a
y:_b
]
recipe main [
1:address:shared:array:character <- new [abc]
# compound types for type ingredients
{2: (foo number (address shared array character))} <- merge 34/x, 1:address:shared:array:character/y
]
:(scenario size_of_shape_shifting_container_4)
container foo:_a:_b [
x:_a
y:_b
]
container bar:_a:_b [
# dilated element
{data: (foo _a (address shared _b))}
]
recipe main [
1:address:shared:array:character <- new [abc]
2:bar:number:array:character <- merge 34/x, 1:address:shared:array:character/y
]
:(before "End Globals")
// We'll use large type ordinals to mean "the following type of the variable".
const int START_TYPE_INGREDIENTS = 2000;
@ -129,8 +162,8 @@ type_tree* type_ingredient(const type_tree* element_template, const type_tree* r
if (!curr) return NULL;
}
assert(curr);
assert(!curr->left); // unimplemented
if (!contains_key(Type, curr->value)) return NULL;
if (curr->left) curr = curr->left;
assert(curr->value > 0);
trace(9999, "type") << "type deduced to be " << get(Type, curr->value).name << "$" << end();
return new type_tree(*curr);
}
@ -210,27 +243,127 @@ void replace_type_ingredient(type_tree* element_type, string_tree* element_type_
element_type->value = replacement->value;
assert(!element_type->left); // since value is set
element_type->left = replacement->left ? new type_tree(*replacement->left) : NULL;
assert(!element_type->right); // unsupported
if (element_type->right) delete element_type->right;
element_type->right = replacement->right ? new type_tree(*replacement->right) : NULL;
const string_tree* replacement_name = nth_type_name(callsite_type_name, type_ingredient_index);
element_type_name->value = replacement_name->value;
assert(!element_type_name->left); // since value is set
element_type_name->left = replacement_name->left ? new string_tree(*replacement_name->left) : NULL;
assert(!element_type_name->right); // unsupported
if (element_type_name->right) delete element_type_name->right;
element_type_name->right = replacement_name->right ? new string_tree(*replacement_name->right) : NULL;
}
replace_type_ingredient(element_type->right, element_type_name->right, callsite_type, callsite_type_name);
}
void test_replace_type_ingredient_entire() {
run("container foo:_elem [\n"
" x:_elem\n"
" y:number\n"
"]\n");
reagent callsite("x:foo:point");
reagent element = element_type(callsite, 0);
CHECK_EQ(element.name, "x");
CHECK_EQ(element.properties.at(0).second->value, "point");
CHECK(!element.properties.at(0).second->right);
}
void test_replace_type_ingredient_tail() {
run("container foo:_elem [\n"
" x:_elem\n"
"]\n"
"container bar:_elem [\n"
" x:foo:_elem\n"
"]\n");
reagent callsite("x:bar:point");
reagent element = element_type(callsite, 0);
CHECK_EQ(element.name, "x");
CHECK_EQ(element.properties.at(0).second->value, "foo");
CHECK_EQ(element.properties.at(0).second->right->value, "point");
CHECK(!element.properties.at(0).second->right->right);
}
void test_replace_type_ingredient_head_tail_multiple() {
run("container foo:_elem [\n"
" x:_elem\n"
"]\n"
"container bar:_elem [\n"
" x:foo:_elem\n"
"]\n");
reagent callsite("x:bar:address:shared:array:character");
reagent element = element_type(callsite, 0);
CHECK_EQ(element.name, "x");
CHECK_EQ(element.properties.at(0).second->value, "foo");
CHECK_EQ(element.properties.at(0).second->right->value, "address");
CHECK_EQ(element.properties.at(0).second->right->right->value, "shared");
CHECK_EQ(element.properties.at(0).second->right->right->right->value, "array");
CHECK_EQ(element.properties.at(0).second->right->right->right->right->value, "character");
CHECK(!element.properties.at(0).second->right->right->right->right->right);
}
//// generic containers can't yet have type ingredients at non-root position
//// of an element definition
void pending_test_replace_type_ingredient_head_middle() { // not supported yet
run("container foo:_elem [\n"
" x:_elem\n"
"]\n"
"container bar:_elem [\n"
" x:foo:_elem:number\n"
"]\n");
reagent callsite("x:bar:address");
reagent element = element_type(callsite, 0);
CHECK_EQ(element.name, "x");
CHECK_EQ(element.properties.at(0).second->value, "foo");
CHECK_EQ(element.properties.at(0).second->right->value, "address");
CHECK_EQ(element.properties.at(0).second->right->right->value, "number");
CHECK(!element.properties.at(0).second->right->right->right);
}
void test_replace_last_type_ingredient_with_multiple() {
run("container foo:_a:_b [\n"
" x:_a\n"
" y:_b\n"
"]\n");
reagent callsite("{f: (foo number (address shared array character))}");
reagent element = element_type(callsite, 1);
CHECK_EQ(element.name, "y");
CHECK_EQ(element.properties.at(0).second->value, "address");
CHECK_EQ(element.properties.at(0).second->right->value, "shared");
CHECK_EQ(element.properties.at(0).second->right->right->value, "array");
CHECK_EQ(element.properties.at(0).second->right->right->right->value, "character");
CHECK(!element.properties.at(0).second->right->right->right->right);
}
void test_replace_middle_type_ingredient_with_multiple() {
run("container foo:_a:_b:_c [\n"
" x:_a\n"
" y:_b\n"
" z:_c\n"
"]\n");
reagent callsite("{f: (foo number (address shared array character) boolean)}");
reagent element = element_type(callsite, 1);
CHECK_EQ(element.name, "y");
CHECK_EQ(element.properties.at(0).second->value, "address");
CHECK_EQ(element.properties.at(0).second->right->value, "shared");
CHECK_EQ(element.properties.at(0).second->right->right->value, "array");
CHECK_EQ(element.properties.at(0).second->right->right->right->value, "character");
CHECK(!element.properties.at(0).second->right->right->right->right);
}
const type_tree* nth_type(const type_tree* base, long long int n) {
assert(n >= 0);
if (n == 0) return base;
if (n == 0) {
if (base && base->left) return base->left;
return base;
}
return nth_type(base->right, n-1);
}
const string_tree* nth_type_name(const string_tree* base, long long int n) {
assert(n >= 0);
if (n == 0) return base;
if (n == 0) {
if (base && base->left) return base->left;
return base;
}
return nth_type_name(base->right, n-1);
}