diff options
-rw-r--r-- | 030container.cc | 180 | ||||
-rw-r--r-- | 031address.cc | 3 | ||||
-rw-r--r-- | 033exclusive_container.cc | 139 | ||||
-rw-r--r-- | 034call.cc | 4 | ||||
-rw-r--r-- | 058shape_shifting_container.cc | 132 |
5 files changed, 440 insertions, 18 deletions
diff --git a/030container.cc b/030container.cc index 60b38830..07e9718e 100644 --- a/030container.cc +++ b/030container.cc @@ -647,8 +647,7 @@ void check_invalid_types(type_tree* type, const string& block, const string& nam check_invalid_types(type->right, block, name); } -//:: Construct types out of their constituent fields. Doesn't currently do -//:: type-checking but *does* match sizes. +//:: Construct types out of their constituent fields. :(scenario merge) container foo [ @@ -668,6 +667,7 @@ MERGE, put(Recipe_ordinal, "merge", MERGE); :(before "End Primitive Recipe Checks") case MERGE: { + // type-checking in a separate transform below break; } :(before "End Primitive Recipe Implementations") @@ -678,3 +678,179 @@ case MERGE: { products.at(0).push_back(ingredients.at(i).at(j)); break; } + +//: type-check 'merge' to avoid interpreting numbers as addresses + +:(scenario merge_check) +% Hide_errors = true; +recipe main [ + 1:point <- merge 3, 4 +] +$error: 0 + +:(scenario merge_check_missing_element) +% Hide_errors = true; +recipe main [ + 1:point <- merge 3 +] ++error: main: too few ingredients in '1:point <- merge 3' + +:(scenario merge_check_extra_element) +% Hide_errors = true; +recipe main [ + 1:point <- merge 3, 4, 5 +] ++error: main: too many ingredients in '1:point <- merge 3, 4, 5' + +//: We want to avoid causing memory corruption, but other than that we want to +//: be flexible in how we construct containers of containers. It should be +//: equally easy to define a container out of primitives or intermediate +//: container fields. + +:(scenario merge_check_recursive_containers) +% Hide_errors = true; +recipe main [ + 1:point <- merge 3, 4 + 1:point-number <- merge 1:point, 5 +] +$error: 0 + +:(scenario merge_check_recursive_containers_2) +% Hide_errors = true; +recipe main [ + 1:point <- merge 3, 4 + 2:point-number <- merge 1:point +] ++error: main: too few ingredients in '2:point-number <- merge 1:point' + +:(scenario merge_check_recursive_containers_3) +% Hide_errors = true; +recipe main [ + 1:point-number <- merge 3, 4, 5 +] +$error: 0 + +:(scenario merge_check_recursive_containers_4) +% Hide_errors = true; +recipe main [ + 1:point-number <- merge 3, 4 +] ++error: main: too few ingredients in '1:point-number <- merge 3, 4' + +//: Since a container can be merged in several ways, we need to be able to +//: backtrack through different possibilities. Later we'll allow creating +//: exclusive containers which contain just one of rather than all of their +//: elements. That will also require backtracking capabilities. Here's the +//: state we need to maintain for backtracking: + +:(before "End Types") +struct merge_check_point { + reagent container; + long long int container_element_index; + merge_check_point(const reagent& c, long long int i) :container(c), container_element_index(i) {} +}; + +struct merge_check_state { + stack<merge_check_point> data; +}; + +:(before "End Checks") +Transform.push_back(check_merge_calls); +:(code) +void check_merge_calls(const recipe_ordinal r) { + const recipe& caller = get(Recipe, r); + trace(9991, "transform") << "--- type-check merge instructions in recipe " << caller.name << end(); + for (long long int i = 0; i < SIZE(caller.steps); ++i) { + const instruction& inst = caller.steps.at(i); + if (inst.name != "merge") continue; + if (SIZE(inst.products) != 1) { + raise_error << maybe(caller.name) << "'merge' should yield a single product in '" << inst.to_string() << "'\n" << end(); + continue; + } + reagent product = inst.products.at(0); + // Update product While Type-checking Merge + type_ordinal product_type = product.type->value; + if (product_type == 0 || !contains_key(Type, product_type)) { + raise_error << maybe(caller.name) << "'merge' should yield a container in '" << inst.to_string() << "'\n" << end(); + continue; + } + const type_info& info = get(Type, product_type); + if (info.kind != CONTAINER && info.kind != EXCLUSIVE_CONTAINER) { + raise_error << maybe(caller.name) << "'merge' should yield a container in '" << inst.to_string() << "'\n" << end(); + continue; + } + check_merge_call(inst.ingredients, product, caller, inst); + } +} + +void check_merge_call(const vector<reagent>& ingredients, const reagent& product, const recipe& caller, const instruction& inst) { + long long int ingredient_index = 0; + merge_check_state state; + state.data.push(merge_check_point(product, 0)); + while (true) { + assert(!state.data.empty()); + trace(9999, "transform") << ingredient_index << " vs " << SIZE(ingredients) << end(); + if (ingredient_index >= SIZE(ingredients)) { + raise_error << maybe(caller.name) << "too few ingredients in '" << inst.to_string() << "'\n" << end(); + return; + } + reagent& container = state.data.top().container; + type_info& container_info = get(Type, container.type->value); + switch (container_info.kind) { + case CONTAINER: { + reagent expected_ingredient = element_type(container, state.data.top().container_element_index); + trace(9999, "transform") << "checking container " << debug_string(container) << " || " << debug_string(expected_ingredient) << " vs ingredient " << ingredient_index << end(); + // if the current element is the ingredient we expect, move on to the next element/ingredient + if (types_coercible(expected_ingredient, ingredients.at(ingredient_index))) { + ++ingredient_index; + ++state.data.top().container_element_index; + while (state.data.top().container_element_index >= SIZE(get(Type, state.data.top().container.type->value).elements)) { + state.data.pop(); + if (state.data.empty()) { + if (ingredient_index < SIZE(ingredients)) + raise_error << maybe(caller.name) << "too many ingredients in '" << inst.to_string() << "'\n" << end(); + return; + } + ++state.data.top().container_element_index; + } + } + // if not, maybe it's a field of the current element + else { + // no change to ingredient_index + state.data.push(merge_check_point(expected_ingredient, 0)); + } + break; + } + // End valid_merge Cases + 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(); + return; + } + ++ingredient_index; + do { + state.data.pop(); + if (state.data.empty()) { + if (ingredient_index < SIZE(ingredients)) + raise_error << maybe(caller.name) << "too many ingredients in '" << inst.to_string() << "'\n" << end(); + return; + } + ++state.data.top().container_element_index; + } while (state.data.top().container_element_index >= SIZE(get(Type, state.data.top().container.type->value).elements)); + } + } + } + // never gets here + assert(false); +} + +:(scenario merge_check_product) +% Hide_errors = true; +recipe main [ + 1:number <- merge 3 +] ++error: main: 'merge' should yield a container in '1:number <- merge 3' + +:(before "End Includes") +#include <stack> +using std::stack; diff --git a/031address.cc b/031address.cc index 3fe289dd..5284dea1 100644 --- a/031address.cc +++ b/031address.cc @@ -84,6 +84,9 @@ recipe foo [ :(after "bool is_mu_number(reagent r)") if (!canonize_type(r)) return false; +:(after "Update product While Type-checking Merge") +if (!canonize_type(product)) continue; + :(code) bool canonize_type(reagent& r) { while (has_property(r, "lookup")) { diff --git a/033exclusive_container.cc b/033exclusive_container.cc index 46dd2e64..1d63f593 100644 --- a/033exclusive_container.cc +++ b/033exclusive_container.cc @@ -145,6 +145,7 @@ const reagent variant_type(const reagent& canonized_base, long long int tag) { assert(info.kind == EXCLUSIVE_CONTAINER); reagent element; element.type = new type_tree(*info.elements.at(tag)); + element.properties.at(0).second = new string_tree(*info.element_type_names.at(tag)); // End variant_type Special-cases return element; } @@ -195,6 +196,144 @@ recipe main [ +mem: storing 1 in location 4 +mem: storing 34 in location 5 +//: type-checking for 'merge' on exclusive containers + +:(scenario merge_handles_exclusive_container) +% Hide_errors = true; +exclusive-container foo [ + x:number + y:bar +] +container bar [ + z:number +] +recipe main [ + 1:foo <- merge 0/x, 34 +] ++mem: storing 0 in location 1 ++mem: storing 34 in location 2 +$error: 0 + +:(scenario merge_requires_literal_tag_for_exclusive_container) +% Hide_errors = true; +exclusive-container foo [ + x:number + y:bar +] +container bar [ + z:number +] +recipe main [ + local-scope + 1:number <- copy 0 + 2:foo <- merge 1:number, 34 +] ++error: main: ingredient 0 of 'merge' should be a literal, for the tag of exclusive-container foo + +:(before "End valid_merge Cases") +case EXCLUSIVE_CONTAINER: { + assert(state.data.top().container_element_index == 0); + trace(9999, "transform") << "checking exclusive container " << debug_string(container) << " vs ingredient " << ingredient_index << end(); + if (!is_literal(ingredients.at(ingredient_index))) { + raise_error << maybe(caller.name) << "ingredient " << ingredient_index << " of 'merge' should be a literal, for the tag of exclusive-container " << container_info.name << '\n' << end(); + return; + } + reagent ingredient = ingredients.at(ingredient_index); // unnecessary copy just to keep this function from modifying caller + populate_value(ingredient); + if (ingredient.value >= SIZE(container_info.elements)) { + raise_error << maybe(caller.name) << "invalid tag at " << ingredient_index << " for " << container_info.name << " in '" << inst.to_string() << '\n' << end(); + return; + } + reagent variant = variant_type(container, ingredient.value); + trace(9999, "transform") << "tag: " << ingredient.value << end(); + // replace union with its variant + state.data.pop(); + state.data.push(merge_check_point(variant, 0)); + ++ingredient_index; + break; +} + +:(scenario merge_check_container_containing_exclusive_container) +% Hide_errors = true; +container foo [ + x:number + y:bar +] +exclusive-container bar [ + x:number + y:number +] +recipe main [ + 1:foo <- merge 23, 1/y, 34 +] ++mem: storing 23 in location 1 ++mem: storing 1 in location 2 ++mem: storing 34 in location 3 +$error: 0 + +:(scenario merge_check_container_containing_exclusive_container_2) +% Hide_errors = true; +container foo [ + x:number + y:bar +] +exclusive-container bar [ + x:number + y:number +] +recipe main [ + 1:foo <- merge 23, 1/y, 34, 35 +] ++error: main: too many ingredients in '1:foo <- merge 23, 1/y, 34, 35' + +:(scenario merge_check_exclusive_container_containing_container) +% Hide_errors = true; +exclusive-container foo [ + x:number + y:bar +] +container bar [ + x:number + y:number +] +recipe main [ + 1:foo <- merge 1/y, 23, 34 +] ++mem: storing 1 in location 1 ++mem: storing 23 in location 2 ++mem: storing 34 in location 3 +$error: 0 + +:(scenario merge_check_exclusive_container_containing_container_2) +% Hide_errors = true; +exclusive-container foo [ + x:number + y:bar +] +container bar [ + x:number + y:number +] +recipe main [ + 1:foo <- merge 0/x, 23 +] +$error: 0 + +:(scenario merge_check_exclusive_container_containing_container_3) +% Hide_errors = true; +exclusive-container foo [ + x:number + y:bar +] +container bar [ + x:number + y:number +] +recipe main [ + 1:foo <- merge 1/y, 23 +] ++error: main: too few ingredients in '1:foo <- merge 1/y, 23' + //: Since the different variants of an exclusive-container might have //: different sizes, relax the size mismatch check for 'merge' instructions. :(before "End size_mismatch(x) Cases") diff --git a/034call.cc b/034call.cc index ff004dc1..f9ba5def 100644 --- a/034call.cc +++ b/034call.cc @@ -165,7 +165,3 @@ while (current_step_index() >= SIZE(Current_routine->steps())) { // todo: fail if no products returned ++current_step_index(); } - -:(before "End Includes") -#include <stack> -using std::stack; diff --git a/058shape_shifting_container.cc b/058shape_shifting_container.cc index 2fa8f497..e4822cc0 100644 --- a/058shape_shifting_container.cc +++ b/058shape_shifting_container.cc @@ -112,22 +112,27 @@ if (t.elements.at(i)->value >= START_TYPE_INGREDIENTS) { :(code) // shape-shifting version of size_of long long int size_of_type_ingredient(const type_tree* element_template, const type_tree* rest_of_use) { + type_tree* element_type = type_ingredient(element_template, rest_of_use); + if (!element_type) return 0; + long long int result = size_of(element_type); + delete element_type; + return result; +} + +type_tree* type_ingredient(const type_tree* element_template, const type_tree* rest_of_use) { long long int type_ingredient_index = element_template->value - START_TYPE_INGREDIENTS; const type_tree* curr = rest_of_use; - if (!curr) return 0; + if (!curr) return NULL; while (type_ingredient_index > 0) { --type_ingredient_index; curr = curr->right; - if (!curr) return 0; + if (!curr) return NULL; } assert(curr); assert(!curr->left); // unimplemented - if (!contains_key(Type, curr->value)) return 0; + if (!contains_key(Type, curr->value)) return NULL; trace(9999, "type") << "type deduced to be " << get(Type, curr->value).name << "$" << end(); - type_tree tmp(curr->value); - if (curr->right) - tmp.right = new type_tree(*curr->right); - return size_of(&tmp); + return new type_tree(*curr); } :(scenario get_on_shape_shifting_container) @@ -169,7 +174,7 @@ container foo:_t [ y:number ] recipe main [ - 1:foo:address:point <- merge 34, 48 # unsafe + 1:foo:address:point <- merge 34/unsafe, 48 2:address:point <- get 1:foo:address:point, x:offset ] +mem: storing 34 in location 2 @@ -178,7 +183,7 @@ recipe main [ if (contains_type_ingredient(element)) { if (!canonized_base.type->right) raise_error << "illegal type '" << debug_string(canonized_base.type) << "' seems to be missing a type ingredient or three\n" << end(); - replace_type_ingredient(element.type, canonized_base.type->right); + replace_type_ingredient(element.type, element.properties.at(0).second, canonized_base.type->right, canonized_base.properties.at(0).second->right); } :(code) @@ -192,7 +197,7 @@ bool contains_type_ingredient(const type_tree* type) { return contains_type_ingredient(type->left) || contains_type_ingredient(type->right); } -void replace_type_ingredient(type_tree* element_type, const type_tree* callsite_type) { +void replace_type_ingredient(type_tree* element_type, string_tree* element_type_name, const type_tree* callsite_type, const string_tree* callsite_type_name) { if (!callsite_type) return; // error but it's already been raised above if (!element_type) return; if (element_type->value >= START_TYPE_INGREDIENTS) { @@ -200,14 +205,21 @@ void replace_type_ingredient(type_tree* element_type, const type_tree* callsite_ raise_error << "illegal type '" << debug_string(callsite_type) << "' seems to be missing a type ingredient or three\n" << end(); return; } - const type_tree* replacement = nth_type(callsite_type, element_type->value-START_TYPE_INGREDIENTS); + const long long int type_ingredient_index = element_type->value-START_TYPE_INGREDIENTS; + const type_tree* replacement = nth_type(callsite_type, type_ingredient_index); 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 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 + element_type_name->right = replacement_name->right ? new string_tree(*replacement_name->right) : NULL; } - replace_type_ingredient(element_type->right, callsite_type); + replace_type_ingredient(element_type->right, element_type_name->right, callsite_type, callsite_type_name); } const type_tree* nth_type(const type_tree* base, long long int n) { @@ -216,6 +228,12 @@ const type_tree* nth_type(const type_tree* base, long long int n) { 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; + return nth_type_name(base->right, n-1); +} + bool has_nth_type(const type_tree* base, long long int n) { assert(n >= 0); if (base == NULL) return false; @@ -272,3 +290,93 @@ recipe main [ 2:number <- get 1:bar, 1:offset ] +mem: storing 17 in location 2 + +//: 'merge' on shape-shifting containers + +:(scenario merge_check_shape_shifting_container_containing_exclusive_container) +% Hide_errors = true; +container foo:_elem [ + x:number + y:_elem +] +exclusive-container bar [ + x:number + y:number +] +recipe main [ + 1:foo:bar <- merge 23, 1/y, 34 +] ++mem: storing 23 in location 1 ++mem: storing 1 in location 2 ++mem: storing 34 in location 3 +$error: 0 + +:(scenario merge_check_shape_shifting_container_containing_exclusive_container_2) +% Hide_errors = true; +container foo:_elem [ + x:number + y:_elem +] +exclusive-container bar [ + x:number + y:number +] +recipe main [ + 1:foo:bar <- merge 23, 1/y, 34, 35 +] ++error: main: too many ingredients in '1:foo:bar <- merge 23, 1/y, 34, 35' + +:(scenario merge_check_shape_shifting_exclusive_container_containing_container) +% Hide_errors = true; +exclusive-container foo:_elem [ + x:number + y:_elem +] +container bar [ + x:number + y:number +] +recipe main [ + 1:foo:bar <- merge 1/y, 23, 34 +] ++mem: storing 1 in location 1 ++mem: storing 23 in location 2 ++mem: storing 34 in location 3 +$error: 0 + +:(scenario merge_check_shape_shifting_exclusive_container_containing_container_2) +% Hide_errors = true; +exclusive-container foo:_elem [ + x:number + y:_elem +] +container bar [ + x:number + y:number +] +recipe main [ + 1:foo:bar <- merge 0/x, 23 +] +$error: 0 + +:(scenario merge_check_shape_shifting_exclusive_container_containing_container_3) +% Hide_errors = true; +exclusive-container foo:_elem [ + x:number + y:_elem +] +container bar [ + x:number + y:number +] +recipe main [ + 1:foo:bar <- merge 1/y, 23 +] ++error: main: too few ingredients in '1:foo:bar <- merge 1/y, 23' + +:(before "End variant_type Special-cases") +if (contains_type_ingredient(element)) { + if (!canonized_base.type->right) + raise_error << "illegal type '" << debug_string(canonized_base.type) << "' seems to be missing a type ingredient or three\n" << end(); + replace_type_ingredient(element.type, element.properties.at(0).second, canonized_base.type->right, canonized_base.properties.at(0).second->right); +} |