about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-11-29 11:43:25 -0800
committerKartik K. Agaram <vc@akkartik.com>2015-11-29 11:51:02 -0800
commit691b529e53a608c0837599f2562897e01fda1888 (patch)
tree7116fa6cedb9c7febba7528777b577c9ca23438a
parentafb467ea0270c59f9fdcad9fe5303d0ed526177c (diff)
downloadmu-691b529e53a608c0837599f2562897e01fda1888.tar.gz
2607 - resolve some edge cases in static dispatch
-rw-r--r--021check_instruction.cc25
-rw-r--r--035call_ingredient.cc1
-rw-r--r--048check_type_by_name.cc2
-rw-r--r--057static_dispatch.cc76
-rw-r--r--999spaces.cc13
5 files changed, 101 insertions, 16 deletions
diff --git a/021check_instruction.cc b/021check_instruction.cc
index 79022e2a..400ef96d 100644
--- a/021check_instruction.cc
+++ b/021check_instruction.cc
@@ -82,7 +82,7 @@ recipe main [
 $error: 0
 
 :(code)
-// types_match with some special-cases
+// types_match with some leniency
 bool types_coercible(const reagent& lhs, const reagent& rhs) {
   if (types_match(lhs, rhs)) return true;
   if (is_mu_address(rhs) && is_mu_number(lhs)) return true;
@@ -99,34 +99,41 @@ bool types_match(const reagent& lhs, const reagent& rhs) {
     // End Matching Types For Literal(lhs)
     // allow writing 0 to any address
     if (is_mu_address(lhs)) return rhs.name == "0";
+    if (!lhs.type) return false;
+    if (lhs.type->value == get(Type_ordinal, "boolean"))
+      return boolean_matches_literal(lhs, rhs);
     return size_of(lhs) == 1;  // literals are always scalars
   }
   return types_strictly_match(lhs, rhs);
 }
 
+bool boolean_matches_literal(const reagent& lhs, const reagent& rhs) {
+  if (!is_literal(rhs)) return false;
+  if (!lhs.type) return false;
+  if (lhs.type->value != get(Type_ordinal, "boolean")) return false;
+  return rhs.name == "0" || rhs.name == "1";
+}
+
 // copy arguments because later layers will want to make changes to them
 // without perturbing the caller
 bool types_strictly_match(reagent lhs, reagent rhs) {
+  if (is_literal(rhs) && lhs.type->value == get(Type_ordinal, "number")) return true;
   // to sidestep type-checking, use /unsafe in the source.
   // this will be highlighted in red inside vim. just for setting up some tests.
   if (is_unsafe(rhs)) return true;
   // '_' never raises type error
   if (is_dummy(lhs)) return true;
   if (!lhs.type) return !rhs.type;
-  return types_match(lhs.type, rhs.type);
+  return types_strictly_match(lhs.type, rhs.type);
 }
 
 // two types match if the second begins like the first
 // (trees perform the same check recursively on each subtree)
-bool types_match(type_tree* lhs, type_tree* rhs) {
+bool types_strictly_match(type_tree* lhs, type_tree* rhs) {
   if (!lhs) return true;
-  if (!rhs || rhs->value == 0) {
-    if (lhs->value == get(Type_ordinal, "array")) return false;
-    if (lhs->value == get(Type_ordinal, "address")) return false;
-    return size_of(rhs) == size_of(lhs);
-  }
+  if (!rhs) return lhs->value == 0;
   if (lhs->value != rhs->value) return false;
-  return types_match(lhs->left, rhs->left) && types_match(lhs->right, rhs->right);
+  return types_strictly_match(lhs->left, rhs->left) && types_strictly_match(lhs->right, rhs->right);
 }
 
 bool is_raw(const reagent& r) {
diff --git a/035call_ingredient.cc b/035call_ingredient.cc
index 89d6507d..b949230d 100644
--- a/035call_ingredient.cc
+++ b/035call_ingredient.cc
@@ -62,6 +62,7 @@ case NEXT_INGREDIENT: {
     else if (!types_match(product,
                           current_call().ingredients.at(current_call().next_ingredient_to_process))) {
       raise_error << maybe(current_recipe_name()) << "wrong type for ingredient " << product.original_string << '\n' << end();
+      // End next-ingredient Type Mismatch Error
     }
     products.push_back(
         current_call().ingredient_atoms.at(current_call().next_ingredient_to_process));
diff --git a/048check_type_by_name.cc b/048check_type_by_name.cc
index 1d4e7dc1..ea4bcba2 100644
--- a/048check_type_by_name.cc
+++ b/048check_type_by_name.cc
@@ -57,7 +57,7 @@ void check_type(map<string, type_tree*>& type, map<string, string_tree*>& type_n
   if (!contains_key(type_name, x.name)) {
     type_name[x.name] = x.properties.at(0).second;
   }
-  if (!types_match(type[x.name], x.type))
+  if (!types_strictly_match(type[x.name], x.type))
     raise_error << maybe(get(Recipe, r).name) << x.name << " used with multiple types\n" << end();
 }
 
diff --git a/057static_dispatch.cc b/057static_dispatch.cc
index a333838c..6613012a 100644
--- a/057static_dispatch.cc
+++ b/057static_dispatch.cc
@@ -140,10 +140,13 @@ void resolve_ambiguous_calls(recipe_ordinal r) {
 void replace_best_variant(instruction& inst, const recipe& caller_recipe) {
   trace(9992, "transform") << "instruction " << inst.name << end();
   vector<recipe_ordinal>& variants = get(Recipe_variants, inst.name);
+//?   trace(9992, "transform") << "checking base: " << get(Recipe_ordinal, inst.name) << end();
   long long int best_score = variant_score(inst, get(Recipe_ordinal, inst.name));
+  trace(9992, "transform") << "score for base: " << best_score << end();
   for (long long int i = 0; i < SIZE(variants); ++i) {
+//?     trace(9992, "transform") << "checking variant " << i << ": " << variants.at(i) << end();
     long long int current_score = variant_score(inst, variants.at(i));
-    trace(9992, "transform") << "checking variant " << i << ": " << current_score << end();
+    trace(9992, "transform") << "score for variant " << i << ": " << current_score << end();
     if (current_score > best_score) {
       inst.name = get(Recipe, variants.at(i)).name;
       best_score = current_score;
@@ -153,7 +156,7 @@ void replace_best_variant(instruction& inst, const recipe& caller_recipe) {
 }
 
 long long int variant_score(const instruction& inst, recipe_ordinal variant) {
-  long long int result = 100;
+  long long int result = 1000;
   if (variant == -1) return -1;  // ghost from a previous test
 //?   cerr << "variant score: " << inst.to_string() << '\n';
   if (!contains_key(Recipe, variant)) {
@@ -177,11 +180,16 @@ long long int variant_score(const instruction& inst, recipe_ordinal variant) {
       trace(9993, "transform") << "strict match: ingredient " << i << end();
 //?       cerr << "strict match: ingredient " << i << '\n';
     }
+    else if (boolean_matches_literal(header_ingredients.at(i), inst.ingredients.at(i))) {
+      // slight penalty for coercing literal to boolean (prefer direct conversion to number if possible)
+      trace(9993, "transform") << "boolean matches literal: ingredient " << i << end();
+      result--;
+    }
     else {
-      // slight penalty for modifying type
+      // slightly larger penalty for modifying type in other ways
       trace(9993, "transform") << "non-strict match: ingredient " << i << end();
 //?       cerr << "non-strict match: ingredient " << i << '\n';
-      result--;
+      result-=10;
     }
   }
 //?   cerr << "=== done checking ingredients\n";
@@ -201,11 +209,16 @@ long long int variant_score(const instruction& inst, recipe_ordinal variant) {
       trace(9993, "transform") << "strict match: product " << i << end();
 //?       cerr << "strict match: product " << i << '\n';
     }
+    else if (boolean_matches_literal(header_products.at(i), inst.products.at(i))) {
+      // slight penalty for coercing literal to boolean (prefer direct conversion to number if possible)
+      trace(9993, "transform") << "boolean matches literal: product " << i << end();
+      result--;
+    }
     else {
-      // slight penalty for modifying type
+      // slightly larger penalty for modifying type in other ways
       trace(9993, "transform") << "non-strict match: product " << i << end();
 //?       cerr << "non-strict match: product " << i << '\n';
-      result--;
+      result-=10;
     }
   }
   // the greater the number of unused ingredients/products, the lower the score
@@ -279,3 +292,54 @@ recipe foo x:number -> y:number [
 ]
 +error: foo: wrong type for ingredient x:number
 -mem: storing 34 in location 1
+
+:(scenario static_dispatch_dispatches_literal_to_boolean_before_character)
+recipe main [
+  1:number/raw <- foo 0  # valid literal for boolean
+]
+recipe foo x:character -> y:number [
+  local-scope
+  load-ingredients
+  reply 34
+]
+recipe foo x:boolean -> y:number [
+  local-scope
+  load-ingredients
+  reply 35
+]
+# boolean variant is preferred
++mem: storing 35 in location 1
+
+:(scenario static_dispatch_dispatches_literal_to_character_when_out_of_boolean_range)
+recipe main [
+  1:number/raw <- foo 97  # not a valid literal for boolean
+]
+recipe foo x:character -> y:number [
+  local-scope
+  load-ingredients
+  reply 34
+]
+recipe foo x:boolean -> y:number [
+  local-scope
+  load-ingredients
+  reply 35
+]
+# character variant is preferred
++mem: storing 34 in location 1
+
+:(scenario static_dispatch_dispatches_literal_to_number_if_at_all_possible)
+recipe main [
+  1:number/raw <- foo 97
+]
+recipe foo x:character -> y:number [
+  local-scope
+  load-ingredients
+  reply 34
+]
+recipe foo x:number -> y:number [
+  local-scope
+  load-ingredients
+  reply 35
+]
+# number variant is preferred
++mem: storing 35 in location 1
diff --git a/999spaces.cc b/999spaces.cc
index 2ab83f87..8c0b2a60 100644
--- a/999spaces.cc
+++ b/999spaces.cc
@@ -69,3 +69,16 @@ assert(Max_callstack_depth == 9989);
 //:     56 check reply instructions against header
 //:   end checks
 //: end transforms
+
+//:: Summary of type-checking in different phases
+//: when dispatching instructions we accept first recipe that:
+//:   strictly matches all types
+//:   maps literal 0 or literal 1 to boolean for some ingredients
+//:   performs some other acceptable type conversion
+//:     literal 0 -> address
+//:     literal -> character
+//: when checking instructions we ensure that types match, and that literals map to some scalar
+//:   (address can only map to literal 0)
+//:   (boolean can only map to literal 0 or literal 1)
+//:     (but conditionals can take any scalar)
+//: at runtime we perform no checks