about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-05-24 19:14:01 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-05-24 19:14:01 -0700
commite38b7a998955dac52d53ac20eead3854974efdf2 (patch)
treeaf72b95cf86a56a46ab8bc31788663612a92d70d
parentc43b455d24c9a9418b05245b4917b608793701a9 (diff)
downloadmu-e38b7a998955dac52d53ac20eead3854974efdf2.tar.gz
3003
Bugfix: overriding a primitive recipe with a generic variant that takes
an address of something shouldn't mask the primitive when you call it
with literal 0.
-rw-r--r--054static_dispatch.cc4
-rw-r--r--056shape_shifting_recipe.cc30
2 files changed, 32 insertions, 2 deletions
diff --git a/054static_dispatch.cc b/054static_dispatch.cc
index fa0c6c74..106c757a 100644
--- a/054static_dispatch.cc
+++ b/054static_dispatch.cc
@@ -277,7 +277,7 @@ bool types_strictly_match_except_literal_zero_against_address(const reagent& to,
   // 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 from.name == "0" && !contains_type_ingredient_name(to);
   return types_strictly_match(to, from);
 }
 
@@ -317,7 +317,7 @@ bool types_strictly_match_except_literal_against_address_or_boolean(const reagen
       && 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 from.name == "0" && !contains_type_ingredient_name(to);
   return types_strictly_match(to, from);
 }
 
diff --git a/056shape_shifting_recipe.cc b/056shape_shifting_recipe.cc
index bd804c6f..70a464e0 100644
--- a/056shape_shifting_recipe.cc
+++ b/056shape_shifting_recipe.cc
@@ -246,6 +246,7 @@ recipe_ordinal new_variant(recipe_ordinal exemplar, const instruction& inst, con
     map<string, const type_tree*> mappings;
     bool error = false;
     compute_type_ingredient_mappings(get(Recipe, exemplar), inst, mappings, caller_recipe, &error);
+    if (!error) error = (SIZE(mappings) != type_ingredient_count_in_header(exemplar));
     if (!error) replace_type_ingredients(new_recipe, mappings);
     for (map<string, const type_tree*>::iterator p = mappings.begin(); p != mappings.end(); ++p)
       delete p->second;
@@ -446,6 +447,23 @@ void replace_type_ingredients(type_tree* type, const map<string, const type_tree
   }
 }
 
+int type_ingredient_count_in_header(recipe_ordinal variant) {
+  const recipe& caller = get(Recipe, variant);
+  set<string> type_ingredients;
+  for (int i = 0; i < SIZE(caller.ingredients); ++i)
+    accumulate_type_ingredients(caller.ingredients.at(i).type, type_ingredients);
+  for (int i = 0; i < SIZE(caller.products); ++i)
+    accumulate_type_ingredients(caller.products.at(i).type, type_ingredients);
+  return SIZE(type_ingredients);
+}
+
+void accumulate_type_ingredients(const type_tree* type, set<string>& out) {
+  if (!type) return;
+  if (is_type_ingredient_name(type->name)) out.insert(type->name);
+  accumulate_type_ingredients(type->left, out);
+  accumulate_type_ingredients(type->right, out);
+}
+
 type_tree* parse_type_tree(const string& s) {
   istringstream in(s);
   in >> std::noskipws;
@@ -1072,3 +1090,15 @@ def main [
   foo 34
 ]
 $error: 0
+
+:(scenario shape_shifting_recipe_coexists_with_primitive)
+# recipe overloading a primitive with a generic type
+def add a:address:foo:_elem [
+  assert 0, [should not get here]
+]
+
+def main [
+  # call primitive add with literal 0
+  add 0, 0
+]
+$error: 0