diff options
author | Kartik Agaram <vc@akkartik.com> | 2018-06-25 13:36:27 -0700 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2018-06-25 13:36:27 -0700 |
commit | 2caaa7f18f391995feae550c59fa175af86b6817 (patch) | |
tree | ffe03071ec5be2033c904e1a4e55d6973c5c8d2b | |
parent | b0631dec20089bb142be2ccf28438ebe09489ce5 (diff) | |
download | mu-2caaa7f18f391995feae550c59fa175af86b6817.tar.gz |
4272 - type-check variables in non-local spaces
So far we only checked if a single recipe used a variable with multiple types in any single space. Now we also ensure that the types deduced for a variable in a space are identical across recipes.
-rw-r--r-- | 010vm.cc | 2 | ||||
-rw-r--r-- | 011load.cc | 4 | ||||
-rw-r--r-- | 043space.cc | 15 | ||||
-rw-r--r-- | 045closure_name.cc | 6 | ||||
-rw-r--r-- | 046check_type_by_name.cc | 78 | ||||
-rw-r--r-- | 057immutable.cc | 24 |
6 files changed, 87 insertions, 42 deletions
diff --git a/010vm.cc b/010vm.cc index 6860ffa4..9cec18d2 100644 --- a/010vm.cc +++ b/010vm.cc @@ -17,6 +17,7 @@ typedef int recipe_ordinal; // Recipes are lists of instructions. To perform or 'run' a recipe, the // computer runs its instructions. struct recipe { + recipe_ordinal ordinal; string name; vector<instruction> steps; // End recipe Fields @@ -261,6 +262,7 @@ void restore_non_recipe_snapshots() { :(code) recipe::recipe() { + ordinal = -1; // End recipe Constructor } diff --git a/011load.cc b/011load.cc index 8eec19a0..666f1c6e 100644 --- a/011load.cc +++ b/011load.cc @@ -65,7 +65,9 @@ int slurp_recipe(istream& in) { raise << "empty result.name\n" << end(); trace(9991, "parse") << "--- defining " << result.name << end(); if (!contains_key(Recipe_ordinal, result.name)) - put(Recipe_ordinal, result.name, Next_recipe_ordinal++); + put(Recipe_ordinal, result.name, Next_recipe_ordinal); + result.ordinal = get(Recipe_ordinal, result.name); + ++Next_recipe_ordinal; if (Recipe.find(get(Recipe_ordinal, result.name)) != Recipe.end()) { trace(9991, "parse") << "already exists" << end(); if (should_check_for_redefine(result.name)) diff --git a/043space.cc b/043space.cc index f9125daf..d348f736 100644 --- a/043space.cc +++ b/043space.cc @@ -292,18 +292,3 @@ bool contains_non_special_name(const recipe_ordinal r) { } return false; } - -// reagent comparison -- only between reagents in a single recipe -bool operator==(const reagent& a, const reagent& b) { - if (a.name != b.name) return false; - if (property(a, "space") != property(b, "space")) return false; - return true; -} - -bool operator<(const reagent& a, const reagent& b) { - int aspace = 0, bspace = 0; - if (has_property(a, "space")) aspace = to_integer(property(a, "space")->value); - if (has_property(b, "space")) bspace = to_integer(property(b, "space")->value); - if (aspace != bspace) return aspace < bspace; - return a.name < b.name; -} diff --git a/045closure_name.cc b/045closure_name.cc index d1c4859a..9fbda150 100644 --- a/045closure_name.cc +++ b/045closure_name.cc @@ -33,9 +33,11 @@ def increment-counter [ //: surrounding space of each recipe. :(before "End Globals") -map<recipe_ordinal, recipe_ordinal> Surrounding_space; +map<recipe_ordinal, recipe_ordinal> Surrounding_space; // internal to transform; no need to snapshot +:(before "End Reset") +Surrounding_space.clear(); -:(before "Transform.push_back(transform_names)") +:(before "Begin Type Modifying Transforms") Transform.push_back(collect_surrounding_spaces); // idempotent :(code) diff --git a/046check_type_by_name.cc b/046check_type_by_name.cc index e44f87d5..b960799a 100644 --- a/046check_type_by_name.cc +++ b/046check_type_by_name.cc @@ -14,36 +14,53 @@ def main [ ] +error: main: 'x' used with multiple types -:(after "Transform.push_back(expand_type_abbreviations)") +//: we need surrounding-space info for type-checking variables in other spaces +:(after "Transform.push_back(collect_surrounding_spaces)") Transform.push_back(check_or_set_types_by_name); // idempotent +// Keep the name->type mapping for all recipes around for the entire +// transformation phase. +:(before "End Globals") +map<recipe_ordinal, set<reagent, name_lt> > Types_by_space; // internal to transform; no need to snapshot +:(before "End Reset") +Types_by_space.clear(); +:(before "End transform_all") +Types_by_space.clear(); +:(before "End Types") +struct name_lt { + bool operator()(const reagent& a, const reagent& b) const { return a.name < b.name; } +}; + :(code) void check_or_set_types_by_name(const recipe_ordinal r) { - trace(9991, "transform") << "--- deduce types for recipe " << get(Recipe, r).name << end(); recipe& caller = get(Recipe, r); - set<reagent> known; + trace(9991, "transform") << "--- deduce types for recipe " << caller.name << end(); for (int i = 0; i < SIZE(caller.steps); ++i) { instruction& inst = caller.steps.at(i); - for (int in = 0; in < SIZE(inst.ingredients); ++in) { - deduce_missing_type(known, inst.ingredients.at(in), caller); - check_type(known, inst.ingredients.at(in), caller); - } - for (int out = 0; out < SIZE(inst.products); ++out) { - deduce_missing_type(known, inst.products.at(out), caller); - check_type(known, inst.products.at(out), caller); - } + for (int in = 0; in < SIZE(inst.ingredients); ++in) + check_or_set_type(inst.ingredients.at(in), caller); + for (int out = 0; out < SIZE(inst.products); ++out) + check_or_set_type(inst.products.at(out), caller); } } -void deduce_missing_type(set<reagent>& known, reagent& x, const recipe& caller) { +void check_or_set_type(reagent& curr, const recipe& caller) { + if (is_literal(curr)) return; + if (is_integer(curr.name)) return; // no type-checking for raw locations + set<reagent, name_lt>& known_types = Types_by_space[owning_recipe(curr, caller.ordinal)]; + deduce_missing_type(known_types, curr, caller); + check_type(known_types, curr, caller); +} + +void deduce_missing_type(set<reagent, name_lt>& known_types, reagent& x, const recipe& caller) { // Deduce Missing Type(x, caller) if (x.type) return; if (is_jump_target(x.name)) { x.type = new type_tree("label"); return; } - if (known.find(x) == known.end()) return; - const reagent& exemplar = *known.find(x); + if (known_types.find(x) == known_types.end()) return; + const reagent& exemplar = *known_types.find(x); x.type = new type_tree(*exemplar.type); trace(9992, "transform") << x.name << " <= " << names_to_string(x.type) << end(); // spaces are special; their type includes their /names property @@ -56,20 +73,19 @@ void deduce_missing_type(set<reagent>& known, reagent& x, const recipe& caller) } } -void check_type(set<reagent>& known, const reagent& x, const recipe& caller) { +void check_type(set<reagent, name_lt>& known_types, const reagent& x, const recipe& caller) { if (is_literal(x)) return; - if (is_integer(x.name)) return; // if you use raw locations you're probably doing something unsafe if (!x.type) return; // might get filled in by other logic later if (is_jump_target(x.name)) { if (!x.type->atom || x.type->name != "label") raise << maybe(caller.name) << "non-label '" << x.name << "' must begin with a letter\n" << end(); return; } - if (known.find(x) == known.end()) { + if (known_types.find(x) == known_types.end()) { trace(9992, "transform") << x.name << " => " << names_to_string(x.type) << end(); - known.insert(x); + known_types.insert(x); } - if (!types_strictly_match(known.find(x)->type, x.type)) { + if (!types_strictly_match(known_types.find(x)->type, x.type)) { raise << maybe(caller.name) << "'" << x.name << "' used with multiple types\n" << end(); return; } @@ -85,6 +101,14 @@ void check_type(set<reagent>& known, const reagent& x, const recipe& caller) { } } +recipe_ordinal owning_recipe(const reagent& x, recipe_ordinal r) { + for (int s = space_index(x); s > 0; --s) { + if (!contains_key(Surrounding_space, r)) break; // error raised elsewhere + r = Surrounding_space[r]; + } + return r; +} + :(scenario transform_fills_in_missing_types) def main [ x:num <- copy 11 @@ -167,3 +191,19 @@ def main [ ] +error: illegal name '*' # no crash + +:(scenario transform_checks_types_in_surrounding_spaces) +% Hide_errors = true; +# 'x' is a bool in foo's space +def foo [ + local-scope + x:bool <- copy false + return default-space/names:foo +] +# try to read 'x' as a num in foo's space +def main [ + local-scope + 0:space/names:foo <- foo + x:num/space:1 <- copy 34 +] +error: foo: 'x' used with multiple types diff --git a/057immutable.cc b/057immutable.cc index 658f301b..d1f1df71 100644 --- a/057immutable.cc +++ b/057immutable.cc @@ -350,7 +350,7 @@ void check_immutable_ingredients(const recipe_ordinal r) { const reagent& current_ingredient = caller.ingredients.at(i); if (is_present_in_products(caller, current_ingredient.name)) continue; // not expected to be immutable // End Immutable Ingredients Special-cases - set<reagent> immutable_vars; + set<reagent, name_and_space_lt> immutable_vars; immutable_vars.insert(current_ingredient); for (int i = 0; i < SIZE(caller.steps); ++i) { const instruction& inst = caller.steps.at(i); @@ -361,7 +361,7 @@ void check_immutable_ingredients(const recipe_ordinal r) { } } -void update_aliases(const instruction& inst, set<reagent>& current_ingredient_and_aliases) { +void update_aliases(const instruction& inst, set<reagent, name_and_space_lt>& current_ingredient_and_aliases) { set<int> current_ingredient_indices = ingredient_indices(inst, current_ingredient_and_aliases); if (!contains_key(Recipe, inst.operation)) { // primitive recipe @@ -393,7 +393,7 @@ void update_aliases(const instruction& inst, set<reagent>& current_ingredient_an } set<int> scan_contained_in_product_indices(const instruction& inst, set<int>& ingredient_indices) { - set<reagent> selected_ingredients; + set<reagent, name_and_space_lt> selected_ingredients; const recipe& callee = get(Recipe, inst.operation); for (set<int>::iterator p = ingredient_indices.begin(); p != ingredient_indices.end(); ++p) { if (*p >= SIZE(callee.ingredients)) continue; // optional immutable ingredient @@ -435,6 +435,20 @@ bool is_mu_exclusive_container(const type_tree* type) { return info.kind == EXCLUSIVE_CONTAINER; } +:(before "End Types") +// reagent comparison -- only in the context of a single recipe +struct name_and_space_lt { + bool operator()(const reagent& a, const reagent& b) const; +}; +:(code) +bool name_and_space_lt::operator()(const reagent& a, const reagent& b) const { + int aspace = 0, bspace = 0; + if (has_property(a, "space")) aspace = to_integer(property(a, "space")->value); + if (has_property(b, "space")) bspace = to_integer(property(b, "space")->value); + if (aspace != bspace) return aspace < bspace; + return a.name < b.name; +} + :(scenarios transform) :(scenario immutability_infects_contained_in_variables) % Hide_errors = true; @@ -461,7 +475,7 @@ def test-next x:&:test-list -> y:&:test-list/contained-in:x [ +error: foo: cannot modify 'p2' in instruction '*p2 <- put *p2, value:offset, 34' because that would modify p which is an ingredient of recipe foo but not also a product :(code) -void check_immutable_ingredient_in_instruction(const instruction& inst, const set<reagent>& current_ingredient_and_aliases, const string& original_ingredient_name, const recipe& caller) { +void check_immutable_ingredient_in_instruction(const instruction& inst, const set<reagent, name_and_space_lt>& current_ingredient_and_aliases, const string& original_ingredient_name, const recipe& caller) { // first check if the instruction is directly modifying something it shouldn't for (int i = 0; i < SIZE(inst.products); ++i) { if (has_property(inst.products.at(i), "lookup") @@ -527,7 +541,7 @@ bool is_present_in_products(const recipe& callee, const string& ingredient_name) return false; } -set<int> ingredient_indices(const instruction& inst, const set<reagent>& ingredient_names) { +set<int> ingredient_indices(const instruction& inst, const set<reagent, name_and_space_lt>& ingredient_names) { set<int> result; for (int i = 0; i < SIZE(inst.ingredients); ++i) { if (is_literal(inst.ingredients.at(i))) continue; |