about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--010vm.cc2
-rw-r--r--011load.cc4
-rw-r--r--043space.cc15
-rw-r--r--045closure_name.cc6
-rw-r--r--046check_type_by_name.cc78
-rw-r--r--057immutable.cc24
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;