about summary refs log tree commit diff stats
path: root/046check_type_by_name.cc
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-06-25 13:36:27 -0700
committerKartik Agaram <vc@akkartik.com>2018-06-25 13:36:27 -0700
commit2caaa7f18f391995feae550c59fa175af86b6817 (patch)
treeffe03071ec5be2033c904e1a4e55d6973c5c8d2b /046check_type_by_name.cc
parentb0631dec20089bb142be2ccf28438ebe09489ce5 (diff)
downloadmu-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.cc78
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