about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-05-18 18:40:58 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-05-18 18:40:58 -0700
commit6152ef13a865eb1795fd5547f4d307a6f5690558 (patch)
tree6fb203d22a8875366a5f836a3279f00d5011a895
parent56109c6d03a4442b6dc1e1968710a0ec7f159963 (diff)
downloadmu-6152ef13a865eb1795fd5547f4d307a6f5690558.tar.gz
1399 - better 'unknown type' warnings
Implement warnings for types without definitions without constraining
where type definitions must appear.

We also eliminate the anti-pattern where a change in layer 10 had its
test in layer 11 (commit 1383).
-rw-r--r--010vm.cc6
-rw-r--r--011load.cc1
-rw-r--r--012transform.cc2
-rw-r--r--020run.cc8
-rw-r--r--030container.cc97
5 files changed, 100 insertions, 14 deletions
diff --git a/010vm.cc b/010vm.cc
index 3399a539..a5069c45 100644
--- a/010vm.cc
+++ b/010vm.cc
@@ -184,8 +184,10 @@ reagent::reagent(string s) :value(0), initialized(false) {
   name = properties.at(0).first;
   for (long long int i = 0; i < SIZE(properties.at(0).second); ++i) {
     string type = properties.at(0).second.at(i);
-    if (Type_number.find(type) == Type_number.end())
-      raise << "unknown type: " << type << '\n';
+    if (Type_number.find(type) == Type_number.end()) {
+//?       cerr << type << " is " << Next_type_number << '\n'; //? 1
+      Type_number[type] = Next_type_number++;
+    }
     types.push_back(Type_number[type]);
   }
   if (name == "_" && types.empty()) {
diff --git a/011load.cc b/011load.cc
index 662089e2..e75bb364 100644
--- a/011load.cc
+++ b/011load.cc
@@ -46,6 +46,7 @@ vector<recipe_number> load(istream& in) {
       raise << "unknown top-level command: " << command << '\n';
     }
   }
+  // End Load Sanity Checks
   return result;
 }
 
diff --git a/012transform.cc b/012transform.cc
index 731156a9..7cd4d698 100644
--- a/012transform.cc
+++ b/012transform.cc
@@ -15,7 +15,7 @@ vector<transform_fn> Transform;
 
 :(code)
 void transform_all() {
-//?   cout << "AAA transform_all\n"; //? 1
+//?   cerr << "AAA transform_all\n"; //? 2
   for (long long int t = 0; t < SIZE(Transform); ++t) {
     for (map<recipe_number, recipe>::iterator p = Recipe.begin(); p != Recipe.end(); ++p) {
       recipe& r = p->second;
diff --git a/020run.cc b/020run.cc
index f0acfb86..af66e859 100644
--- a/020run.cc
+++ b/020run.cc
@@ -239,11 +239,3 @@ recipe main [
   _ <- copy 0:literal
 ]
 +run: instruction main/0
-
-:(scenario run_warns_on_unknown_types)
-% Hide_warnings = true;
-recipe main [
-  # integer is not a type
-  1:integer <- copy 0:literal
-]
-+warn: unknown type: integer
diff --git a/030container.cc b/030container.cc
index 9c940e36..6bd39e5b 100644
--- a/030container.cc
+++ b/030container.cc
@@ -82,6 +82,8 @@ if (t.kind == container) {
   // size of a container is the sum of the sizes of its elements
   long long int result = 0;
   for (long long int i = 0; i < SIZE(t.elements); ++i) {
+    // todo: strengthen assertion to disallow mutual type recursion
+    assert(types.at(0) != t.elements.at(i).at(0));
     result += size_of(t.elements.at(i));
   }
   return result;
@@ -237,7 +239,6 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
   }
   trace("parse") << "type number: " << Type_number[name];
   skip_bracket(in, "'container' must begin with '['");
-  assert(Type.find(Type_number[name]) == Type.end());
   type_info& t = Type[Type_number[name]];
   recently_added_types.push_back(Type_number[name]);
   t.name = name;
@@ -252,9 +253,10 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
     vector<type_number> types;
     while (!inner.eof()) {
       string type_name = slurp_until(inner, ':');
-      if (Type_number.find(type_name) == Type_number.end()
-          || Type_number[type_name] == 0)
+      if (Type_number.find(type_name) == Type_number.end()) {
+//?         cerr << type_name << " is " << Next_type_number << '\n'; //? 1
         Type_number[type_name] = Next_type_number++;
+      }
       types.push_back(Type_number[type_name]);
       trace("parse") << "  type: " << types.back();
     }
@@ -276,6 +278,21 @@ for (long long int i = 0; i < SIZE(recently_added_types); ++i) {
   Type.erase(recently_added_types.at(i));
 }
 recently_added_types.clear();
+// delete recent type references
+// can't rely on recently_added_types to cleanup Type_number, because of deliberately misbehaving tests with references to undefined types
+map<string, type_number>::iterator p = Type_number.begin();
+while(p != Type_number.end()) {
+  // save current item
+  string name = p->first;
+  type_number t = p->second;
+  // increment iterator
+  ++p;
+  // now delete current item if necessary
+  if (t >= 1000) {
+//?     cerr << "AAA " << name << " " << t << '\n'; //? 1
+    Type_number.erase(name);
+  }
+}
 //: lastly, ensure scenarios are consistent by always starting them at the
 //: same type number.
 Next_type_number = 1000;
@@ -284,6 +301,80 @@ assert(Next_type_number < 1000);
 :(before "End Setup")
 Next_type_number = 1000;
 
+//:: Allow container definitions anywhere in the codebase, but warn if you
+//:: can't find a definition.
+
+:(scenarios run)
+:(scenario run_warns_on_unknown_types)
+% Hide_warnings = true;
+#? % Trace_stream->dump_layer = "run";
+recipe main [
+  # integer is not a type
+  1:integer <- copy 0:literal
+]
++warn: unknown type: integer
+
+:(scenario run_allows_type_definition_after_use)
+% Hide_warnings = true;
+recipe main [
+  1:bar <- copy 0:literal
+]
+
+container bar [
+  x:number
+]
+-warn: unknown type: bar
+
+:(after "int main")
+  Transform.push_back(check_invalid_types);
+
+:(code)
+void check_invalid_types(const recipe_number r) {
+  for (long long int index = 0; index < SIZE(Recipe[r].steps); ++index) {
+    const instruction& inst = Recipe[r].steps.at(index);
+    for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
+      check_invalid_types(inst.ingredients.at(i));
+    }
+    for (long long int i = 0; i < SIZE(inst.products); ++i) {
+      check_invalid_types(inst.products.at(i));
+    }
+  }
+}
+
+void check_invalid_types(const reagent& r) {
+  for (long long int i = 0; i < SIZE(r.types); ++i) {
+    if (r.types.at(i) == 0) continue;
+    if (Type.find(r.types.at(i)) == Type.end())
+      raise << "unknown type: " << r.properties.at(0).second.at(i) << '\n';
+  }
+}
+
+:(scenario container_unknown_field)
+% Hide_warnings = true;
+container foo [
+  x:number
+  y:bar
+]
++warn: unknown type for field y in foo
+
+:(before "End Load Sanity Checks")
+check_container_field_types();
+
+:(code)
+void check_container_field_types() {
+  for (map<type_number, type_info>::iterator p = Type.begin(); p != Type.end(); ++p) {
+    const type_info& info = p->second;
+//?     cerr << "checking " << p->first << '\n'; //? 1
+    for (long long int i = 0; i < SIZE(info.elements); ++i) {
+      for (long long int j = 0; j < SIZE(info.elements.at(i)); ++j) {
+        if (info.elements.at(i).at(j) == 0) continue;
+        if (Type.find(info.elements.at(i).at(j)) == Type.end())
+          raise << "unknown type for field " << info.element_names.at(i) << " in " << info.name << '\n';
+      }
+    }
+  }
+}
+
 //:: helpers
 
 :(code)