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 /046check_type_by_name.cc | |
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.
Diffstat (limited to '046check_type_by_name.cc')
-rw-r--r-- | 046check_type_by_name.cc | 78 |
1 files changed, 59 insertions, 19 deletions
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 |