about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-01-12 12:53:04 -0800
committerKartik K. Agaram <vc@akkartik.com>2016-01-12 12:53:04 -0800
commit7ac8feeff10abfd76f712f8f32406dd30ba1c73e (patch)
tree020352ac442c96bc402d71ef3c21bd82a82ca52a
parentbdb6161bf0cf7b8658845783d526204ad4bee638 (diff)
downloadmu-7ac8feeff10abfd76f712f8f32406dd30ba1c73e.tar.gz
2557 - type-check most ingredients ahead of time
-rw-r--r--020run.cc1
-rw-r--r--035call_ingredient.cc3
-rw-r--r--052tangle.cc6
-rw-r--r--056recipe_header.cc85
-rw-r--r--057static_dispatch.cc39
5 files changed, 86 insertions, 48 deletions
diff --git a/020run.cc b/020run.cc
index a3948dd1..587e9f88 100644
--- a/020run.cc
+++ b/020run.cc
@@ -325,6 +325,7 @@ void run(string form) {
   vector<recipe_ordinal> tmp = load(form);
   transform_all();
   if (tmp.empty()) return;
+  if (trace_count("error") > 0) return;
   run(tmp.front());
 }
 
diff --git a/035call_ingredient.cc b/035call_ingredient.cc
index 7036fa06..f079f0c5 100644
--- a/035call_ingredient.cc
+++ b/035call_ingredient.cc
@@ -77,9 +77,8 @@ case NEXT_INGREDIENT: {
     products.resize(2);
     // pad the first product with sufficient zeros to match its type
     long long int size = size_of(current_instruction().products.at(0));
-    for (long long int i = 0; i < size; ++i) {
+    for (long long int i = 0; i < size; ++i)
       products.at(0).push_back(0);
-    }
     products.at(1).push_back(0);
   }
   break;
diff --git a/052tangle.cc b/052tangle.cc
index efbbbead..3a55f451 100644
--- a/052tangle.cc
+++ b/052tangle.cc
@@ -182,12 +182,6 @@ before +label1 [
   2:number <- copy 0
 ]
 +error: can't tangle before label +label1
-+mem: storing 0 in location 1
-+mem: storing 0 in location 4
-# label1
--mem: storing 0 in location 2
-# nothing else
-$mem: 2
 
 :(scenario tangle_keeps_labels_separate)
 recipe main [
diff --git a/056recipe_header.cc b/056recipe_header.cc
index dae4cd22..a438054c 100644
--- a/056recipe_header.cc
+++ b/056recipe_header.cc
@@ -138,16 +138,97 @@ if (result.has_header) {
 :(before "End Rewrite Instruction(curr, recipe result)")
 if (curr.name == "load-ingredients") {
   curr.clear();
-  recipe_ordinal op = get(Recipe_ordinal, "next-ingredient");
+  recipe_ordinal op = get(Recipe_ordinal, "next-ingredient-without-typechecking");
   for (long long int i = 0; i < SIZE(result.ingredients); ++i) {
     curr.operation = op;
-    curr.name = "next-ingredient";
+    curr.name = "next-ingredient-without-typechecking";
     curr.products.push_back(result.ingredients.at(i));
     result.steps.push_back(curr);
     curr.clear();
   }
 }
 
+//: internal version of next-ingredient; don't call this directly
+:(before "End Primitive Recipe Declarations")
+NEXT_INGREDIENT_WITHOUT_TYPECHECKING,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "next-ingredient-without-typechecking", NEXT_INGREDIENT_WITHOUT_TYPECHECKING);
+:(before "End Primitive Recipe Checks")
+case NEXT_INGREDIENT_WITHOUT_TYPECHECKING: {
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case NEXT_INGREDIENT_WITHOUT_TYPECHECKING: {
+  assert(!Current_routine->calls.empty());
+  if (current_call().next_ingredient_to_process < SIZE(current_call().ingredient_atoms)) {
+    products.push_back(
+        current_call().ingredient_atoms.at(current_call().next_ingredient_to_process));
+    assert(SIZE(products) == 1);  products.resize(2);  // push a new vector
+    products.at(1).push_back(1);
+    ++current_call().next_ingredient_to_process;
+  }
+  else {
+    raise_error << maybe(current_recipe_name()) << "no ingredient to save in " << current_instruction().products.at(0).original_string << '\n' << end();
+    products.resize(2);
+    products.at(0).push_back(0);
+    products.at(1).push_back(0);
+  }
+  break;
+}
+
+//:: Check all calls against headers.
+
+:(scenario show_clear_error_on_bad_call)
+% Hide_errors = true;
+recipe main [
+  1:number <- foo 34
+]
+recipe foo x:boolean -> y:number [
+  local-scope
+  load-ingredients
+  reply 35
+]
++error: main: ingredient 0 has the wrong type at '1:number <- foo 34'
+
+:(scenario show_clear_error_on_bad_call_2)
+% Hide_errors = true;
+recipe main [
+  1:boolean <- foo 34
+]
+recipe foo x:number -> y:number [
+  local-scope
+  load-ingredients
+  reply x
+]
++error: main: product 0 has the wrong type at '1:boolean <- foo 34'
+
+:(after "Transform.push_back(check_instruction)")
+Transform.push_back(check_calls_against_header);  // idempotent
+:(code)
+void check_calls_against_header(const recipe_ordinal r) {
+  trace(9991, "transform") << "--- type-check calls inside recipe " << get(Recipe, r).name << end();
+  const recipe& caller = get(Recipe, r);
+  for (long long int i = 0; i < SIZE(caller.steps); ++i) {
+    const instruction& inst = caller.steps.at(i);
+    if (inst.operation < MAX_PRIMITIVE_RECIPES) continue;
+    const recipe& callee = get(Recipe, inst.operation);
+    if (!callee.has_header) continue;
+    for (long int i = 0; i < smaller(SIZE(inst.ingredients), SIZE(callee.ingredients)); ++i) {
+      if (!types_coercible(callee.ingredients.at(i), inst.ingredients.at(i)))
+        raise_error << maybe(caller.name) << "ingredient " << i << " has the wrong type at '" << inst.to_string() << "'\n" << end();
+    }
+    for (long int i = 0; i < smaller(SIZE(inst.products), SIZE(callee.products)); ++i) {
+      if (is_dummy(inst.products.at(i))) continue;
+      if (!types_coercible(callee.products.at(i), inst.products.at(i)))
+        raise_error << maybe(caller.name) << "product " << i << " has the wrong type at '" << inst.to_string() << "'\n" << end();
+    }
+  }
+}
+
+inline long long int smaller(long long int a, long long int b) {
+  return a < b ? a : b;
+}
+
 //:: Check types going in and out of all recipes with headers.
 
 :(scenarios transform)
diff --git a/057static_dispatch.cc b/057static_dispatch.cc
index ed656f0c..2b48af10 100644
--- a/057static_dispatch.cc
+++ b/057static_dispatch.cc
@@ -359,7 +359,7 @@ recipe foo x:number -> y:number [
   load-ingredients
   reply 34
 ]
-+error: foo: wrong type for ingredient x:number
++error: main: ingredient 0 has the wrong type at '1:number/raw <- foo x'
 -mem: storing 34 in location 1
 
 :(scenario static_dispatch_dispatches_literal_to_boolean_before_character)
@@ -413,43 +413,6 @@ recipe foo x:number -> y:number [
 # number variant is preferred
 +mem: storing 35 in location 1
 
-//: after we make all attempts to dispatch, any unhandled cases will end up at
-//: some wrong variant and trigger an error while trying to load-ingredients
-
-:(scenario static_dispatch_shows_clear_error_on_missing_variant)
-% Hide_errors = true;
-recipe main [
-  1:number <- foo 34
-]
-recipe foo x:boolean -> y:number [
-  local-scope
-  load-ingredients
-  reply 35
-]
-+error: foo: wrong type for ingredient x:boolean
-+error:   (we're inside recipe foo x:boolean -> y:number)
-+error:   (we're trying to call '1:number <- foo 34' inside recipe main)
-
-:(before "End next-ingredient Type Mismatch Error")
-raise_error << "   (we're inside " << header_label(current_call().running_recipe) << ")\n" << end();
-raise_error << "   (we're trying to call '" << to_instruction(*++Current_routine->calls.begin()).to_string() << "' inside " << header_label((++Current_routine->calls.begin())->running_recipe) << ")\n" << end();
-
-:(scenario static_dispatch_shows_clear_error_on_missing_variant_2)
-% Hide_errors = true;
-recipe main [
-  1:boolean <- foo 34
-]
-recipe foo x:number -> y:number [
-  local-scope
-  load-ingredients
-  reply x
-]
-+error: foo: reply ingredient x can't be saved in 1:boolean
-+error:   (we just returned from recipe foo x:number -> y:number)
-
-:(before "End reply Type Mismatch Error")
-raise_error << "   (we just returned from " << header_label(caller_instruction.operation) << ")\n" << end();
-
 :(code)
 string header_label(recipe_ordinal r) {
   const recipe& caller = get(Recipe, r);