about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-05-05 21:47:59 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-05-05 21:59:50 -0700
commite76a27f3edfa0703a204f3ab30ce01b58877f6af (patch)
treeb9ff84216ac81d78b6d14dbbf7211981cb30e153
parent5d5116e3343b5f6efa289e35e1c29b761db12068 (diff)
downloadmu-e76a27f3edfa0703a204f3ab30ce01b58877f6af.tar.gz
2929 - fix a bug in static dispatch
Thanks Caleb for finding this. We'd been using sandboxes for so long, I
hadn't tried a null/0 screen/console in a while and somewhere down the
road Mu stopped matching 0 against concrete addresses.
-rw-r--r--021check_instruction.cc9
-rw-r--r--056static_dispatch.cc74
-rw-r--r--058shape_shifting_recipe.cc24
3 files changed, 84 insertions, 23 deletions
diff --git a/021check_instruction.cc b/021check_instruction.cc
index 138aa733..599fa6c9 100644
--- a/021check_instruction.cc
+++ b/021check_instruction.cc
@@ -123,15 +123,6 @@ bool types_match(const reagent& to, const reagent& from) {
   return types_strictly_match(to, from);
 }
 
-bool types_strictly_match_except_literal_against_boolean(const reagent& to, const reagent& from) {
-  // 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_literal(from)
-      && to.type && to.type->value == get(Type_ordinal, "boolean"))
-    return boolean_matches_literal(to, from);
-  return types_strictly_match(to, from);
-}
-
 bool boolean_matches_literal(const reagent& to, const reagent& from) {
   if (!is_literal(from)) return false;
   if (!to.type) return false;
diff --git a/056static_dispatch.cc b/056static_dispatch.cc
index 353b4b50..5271fe13 100644
--- a/056static_dispatch.cc
+++ b/056static_dispatch.cc
@@ -175,14 +175,18 @@ string best_variant(instruction& inst, const recipe& caller_recipe) {
   if (!candidates.empty()) return best_variant(inst, candidates).name;
 
   // Static Dispatch Phase 2
-  //: (shape-shifting recipes in a later layer)
-  // End Static Dispatch Phase 2
+  candidates = strictly_matching_variants_except_literal_zero_against_address(inst, variants);
+  if (!candidates.empty()) return best_variant(inst, candidates).name;
 
   // Static Dispatch Phase 3
-  candidates = strictly_matching_variants_except_literal_against_boolean(inst, variants);
-  if (!candidates.empty()) return best_variant(inst, candidates).name;
+  //: (shape-shifting recipes in a later layer)
+  // End Static Dispatch Phase 3
 
   // Static Dispatch Phase 4
+  candidates = strictly_matching_variants_except_literal_against_address_or_boolean(inst, variants);
+  if (!candidates.empty()) return best_variant(inst, candidates).name;
+
+  // Static Dispatch Phase 5
   candidates = matching_variants(inst, variants);
   if (!candidates.empty()) return best_variant(inst, candidates).name;
 
@@ -240,28 +244,65 @@ bool all_header_reagents_strictly_match(const instruction& inst, const recipe& v
   return true;
 }
 
-// phase 3
-vector<recipe_ordinal> strictly_matching_variants_except_literal_against_boolean(const instruction& inst, vector<recipe_ordinal>& variants) {
+// phase 2
+vector<recipe_ordinal> strictly_matching_variants_except_literal_zero_against_address(const instruction& inst, vector<recipe_ordinal>& variants) {
+  vector<recipe_ordinal> result;
+  for (int i = 0; i < SIZE(variants); ++i) {
+    if (variants.at(i) == -1) continue;
+    trace(9992, "transform") << "checking variant (strict) " << i << ": " << header_label(variants.at(i)) << end();
+    if (all_header_reagents_strictly_match_except_literal_zero_against_address(inst, get(Recipe, variants.at(i))))
+      result.push_back(variants.at(i));
+  }
+  return result;
+}
+
+bool all_header_reagents_strictly_match_except_literal_zero_against_address(const instruction& inst, const recipe& variant) {
+  for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) {
+    if (!types_strictly_match_except_literal_zero_against_address(variant.ingredients.at(i), inst.ingredients.at(i))) {
+      trace(9993, "transform") << "strict match failed: ingredient " << i << end();
+      return false;
+    }
+  }
+  for (int i = 0; i < min(SIZE(inst.products), SIZE(variant.products)); ++i) {
+    if (is_dummy(inst.products.at(i))) continue;
+    if (!types_strictly_match(variant.products.at(i), inst.products.at(i))) {
+      trace(9993, "transform") << "strict match failed: product " << i << end();
+      return false;
+    }
+  }
+  return true;
+}
+
+bool types_strictly_match_except_literal_zero_against_address(const reagent& to, const reagent& from) {
+  // 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_literal(from) && is_mu_address(to))
+    return from.name == "0";
+  return types_strictly_match(to, from);
+}
+
+// phase 4
+vector<recipe_ordinal> strictly_matching_variants_except_literal_against_address_or_boolean(const instruction& inst, vector<recipe_ordinal>& variants) {
   vector<recipe_ordinal> result;
   for (int i = 0; i < SIZE(variants); ++i) {
     if (variants.at(i) == -1) continue;
     trace(9992, "transform") << "checking variant (strict except literals-against-booleans) " << i << ": " << header_label(variants.at(i)) << end();
-    if (all_header_reagents_strictly_match_except_literal_against_boolean(inst, get(Recipe, variants.at(i))))
+    if (all_header_reagents_strictly_match_except_literal_against_address_or_boolean(inst, get(Recipe, variants.at(i))))
       result.push_back(variants.at(i));
   }
   return result;
 }
 
-bool all_header_reagents_strictly_match_except_literal_against_boolean(const instruction& inst, const recipe& variant) {
+bool all_header_reagents_strictly_match_except_literal_against_address_or_boolean(const instruction& inst, const recipe& variant) {
   for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) {
-    if (!types_strictly_match_except_literal_against_boolean(variant.ingredients.at(i), inst.ingredients.at(i))) {
+    if (!types_strictly_match_except_literal_against_address_or_boolean(variant.ingredients.at(i), inst.ingredients.at(i))) {
       trace(9993, "transform") << "strict match failed: ingredient " << i << end();
       return false;
     }
   }
   for (int i = 0; i < min(SIZE(variant.products), SIZE(inst.products)); ++i) {
     if (is_dummy(inst.products.at(i))) continue;
-    if (!types_strictly_match_except_literal_against_boolean(variant.products.at(i), inst.products.at(i))) {
+    if (!types_strictly_match_except_literal_against_address_or_boolean(variant.products.at(i), inst.products.at(i))) {
       trace(9993, "transform") << "strict match failed: product " << i << end();
       return false;
     }
@@ -269,7 +310,18 @@ bool all_header_reagents_strictly_match_except_literal_against_boolean(const ins
   return true;
 }
 
-// phase 4
+bool types_strictly_match_except_literal_against_address_or_boolean(const reagent& to, const reagent& from) {
+  // 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_literal(from)
+      && to.type && to.type->value == get(Type_ordinal, "boolean"))
+    return boolean_matches_literal(to, from);
+  if (is_literal(from) && is_mu_address(to))
+    return from.name == "0";
+  return types_strictly_match(to, from);
+}
+
+// phase 5
 vector<recipe_ordinal> matching_variants(const instruction& inst, vector<recipe_ordinal>& variants) {
   vector<recipe_ordinal> result;
   for (int i = 0; i < SIZE(variants); ++i) {
diff --git a/058shape_shifting_recipe.cc b/058shape_shifting_recipe.cc
index 433147cd..5f87e6da 100644
--- a/058shape_shifting_recipe.cc
+++ b/058shape_shifting_recipe.cc
@@ -48,7 +48,7 @@ string original_name;
 :(before "End Load Recipe Name")
 result.original_name = result.name;
 
-:(after "Static Dispatch Phase 2")
+:(after "Static Dispatch Phase 3")
 candidates = strictly_matching_shape_shifting_variants(inst, variants);
 if (!candidates.empty()) {
   recipe_ordinal exemplar = best_shape_shifting_variant(inst, candidates);
@@ -778,8 +778,7 @@ def foo x:address:_elem -> y:address:_elem [
   load-ingredients
   y <- copy x
 ]
-+error: foo: failed to map a type to x
-+error: foo: failed to map a type to y
++error: main: instruction foo has no valid specialization
 
 :(scenario specialize_with_literal_5)
 def main [
@@ -996,6 +995,25 @@ def foo x:address:_elem -> y:number [
 # prefer the concrete variant, ignore concrete types in scoring the shape-shifting variant
 +mem: storing 34 in location 1
 
+:(scenario specialize_literal_as_address)
+def main [
+  1:number <- foo 0
+]
+# variant with concrete address type
+def foo x:address:number -> y:number [
+  local-scope
+  load-ingredients
+  return 34
+]
+# shape-shifting variant
+def foo x:address:_elem -> y:number [
+  local-scope
+  load-ingredients
+  return 35
+]
+# prefer the concrete variant, ignore concrete types in scoring the shape-shifting variant
++mem: storing 34 in location 1
+
 :(scenario missing_type_during_specialization)
 % Hide_errors = true;
 # define a shape-shifting recipe