about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-10-06 22:15:45 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-10-06 22:15:45 -0700
commit5f98a10cc78829a03c9fa5a137392e7d5e9030ac (patch)
treeb88536e28f6d507c4b68b337423c0b6a4e28306c
parent75aa3a98e2b9311d65df91523ec754d5a2770456 (diff)
downloadmu-5f98a10cc78829a03c9fa5a137392e7d5e9030ac.tar.gz
2258 - separate warnings from errors
At the lowest level I'm reluctantly starting to see the need for errors
that stop the program in its tracks. Only way to avoid memory corruption
and security issues. But beyond that core I still want to be as lenient
as possible at higher levels of abstraction.
-rw-r--r--003trace.cc19
-rw-r--r--003trace.test.cc8
-rw-r--r--011load.cc22
-rw-r--r--013literal_string.cc4
-rw-r--r--020run.cc12
-rw-r--r--021check_instruction.cc20
-rw-r--r--022arithmetic.cc50
-rw-r--r--023boolean.cc8
-rw-r--r--024jump.cc16
-rw-r--r--025compare.cc18
-rw-r--r--029tools.cc42
-rw-r--r--030container.cc86
-rw-r--r--031address.cc18
-rw-r--r--032array.cc62
-rw-r--r--033exclusive_container.cc10
-rw-r--r--034call.cc14
-rw-r--r--035call_ingredient.cc16
-rw-r--r--036call_reply.cc26
-rw-r--r--037recipe.cc4
-rw-r--r--038scheduler.cc30
-rw-r--r--039wait.cc8
-rw-r--r--040brace.cc20
-rw-r--r--041jump_target.cc18
-rw-r--r--042name.cc55
-rw-r--r--043new.cc16
-rw-r--r--044space.cc14
-rw-r--r--045space_surround.cc2
-rw-r--r--046closure_name.cc24
-rw-r--r--047global.cc19
-rw-r--r--048check_type_by_name.cc22
-rw-r--r--050scenario.cc96
-rw-r--r--052tangle.cc14
-rw-r--r--053continuation.cc6
-rw-r--r--064random.cc6
-rw-r--r--070display.cc50
-rw-r--r--072scenario_screen.cc28
-rw-r--r--075scenario_console.cc4
-rw-r--r--081run_interactive.cc66
-rw-r--r--082persist.cc14
39 files changed, 507 insertions, 460 deletions
diff --git a/003trace.cc b/003trace.cc
index e37b9f12..455ced85 100644
--- a/003trace.cc
+++ b/003trace.cc
@@ -81,8 +81,10 @@
 // End Tracing  // hack to ensure most code in this layer comes before anything else
 
 :(before "End Tracing")
+bool Hide_errors = false;
 bool Hide_warnings = false;
 :(before "End Setup")
+Hide_errors = false;
 Hide_warnings = false;
 
 :(before "End Types")
@@ -132,7 +134,9 @@ struct trace_stream {
     string curr_contents = curr_stream->str();
     if (curr_contents.empty()) return;
     past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents));  // preserve indent in contents
-    if (!Hide_warnings && curr_label == "warn")
+    if (!Hide_errors && curr_label == "error")
+      cerr << curr_label << ": " << curr_contents << '\n';
+    else if (!Hide_warnings && curr_label == "warn")
       cerr << curr_label << ": " << curr_contents << '\n';
     delete curr_stream;
     curr_stream = NULL;
@@ -160,9 +164,10 @@ trace_stream* Trace_stream = NULL;
 
 // Top-level helper. IMPORTANT: can't nest.
 #define trace(...)  !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(__VA_ARGS__)
-// Warnings should go straight to cerr by default since calls to trace() have
+// Errors and warnings should go straight to cerr by default since calls to trace() have
 // some unfriendly constraints (they delay printing, they can't nest)
 #define raise  ((!Trace_stream || !Hide_warnings) ? (tb_shutdown(),cerr) /*do print*/ : Trace_stream->stream("warn"))
+#define raise_error  ((!Trace_stream || !Hide_errors) ? (tb_shutdown(),cerr) /*do print*/ : Trace_stream->stream("error"))
 
 :(before "End Types")
 struct end {};
@@ -263,12 +268,12 @@ int trace_count(string label, string line) {
   return result;
 }
 
-#define CHECK_TRACE_WARNS()  CHECK(trace_count("warn") > 0)
-#define CHECK_TRACE_DOESNT_WARN() \
-  if (trace_count("warn") > 0) { \
+#define CHECK_TRACE_CONTAINS_ERROR()  CHECK(trace_count("error") > 0)
+#define CHECK_TRACE_DOESNT_CONTAIN_ERROR() \
+  if (trace_count("error") > 0) { \
     ++Num_failures; \
-    cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected warnings\n"; \
-    DUMP("warn"); \
+    cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
+    DUMP("error"); \
     Passed = false; \
     return; \
   }
diff --git a/003trace.test.cc b/003trace.test.cc
index a16457a1..f34cbf88 100644
--- a/003trace.test.cc
+++ b/003trace.test.cc
@@ -59,11 +59,11 @@ void test_trace_count_ignores_trailing_whitespace() {
 // pending: DUMP tests
 // pending: readable_contents() adds newline if necessary.
 // pending: raise also prints to stderr.
-// pending: raise doesn't print to stderr if Hide_warnings is set.
-// pending: raise doesn't have to be saved if Hide_warnings is set, just printed.
+// pending: raise doesn't print to stderr if Hide_errors is set.
+// pending: raise doesn't have to be saved if Hide_errors is set, just printed.
 // pending: raise prints to stderr if Trace_stream is NULL.
-// pending: raise prints to stderr if Trace_stream is NULL even if Hide_warnings is set.
-// pending: raise << ... die() doesn't die if Hide_warnings is set.
+// pending: raise prints to stderr if Trace_stream is NULL even if Hide_errors is set.
+// pending: raise << ... die() doesn't die if Hide_errors is set.
 
 
 
diff --git a/011load.cc b/011load.cc
index a084ba6e..4b1b1929 100644
--- a/011load.cc
+++ b/011load.cc
@@ -34,7 +34,7 @@ vector<recipe_ordinal> load(istream& in) {
     }
     // End Command Handlers
     else {
-      raise << "unknown top-level command: " << command << '\n' << end();
+      raise_error << "unknown top-level command: " << command << '\n' << end();
     }
   }
   return result;
@@ -43,7 +43,7 @@ vector<recipe_ordinal> load(istream& in) {
 long long int slurp_recipe(istream& in) {
   string recipe_name = next_word(in);
   if (recipe_name.empty())
-    raise << "empty recipe name\n" << end();
+    raise_error << "empty recipe name\n" << end();
   if (Recipe_ordinal.find(recipe_name) == Recipe_ordinal.end()) {
     Recipe_ordinal[recipe_name] = Next_recipe_ordinal++;
   }
@@ -63,7 +63,7 @@ recipe slurp_body(istream& in) {
   recipe result;
   skip_whitespace(in);
   if (in.get() != '[')
-    raise << "recipe body must begin with '['\n" << end();
+    raise_error << "recipe body must begin with '['\n" << end();
   skip_whitespace_and_comments(in);
   instruction curr;
   while (next_instruction(in, &curr)) {
@@ -77,17 +77,17 @@ bool next_instruction(istream& in, instruction* curr) {
   in >> std::noskipws;
   curr->clear();
   if (in.eof()) {
-    raise << "0: unbalanced '[' for recipe\n" << end();
+    raise_error << "0: unbalanced '[' for recipe\n" << end();
     return false;
   }
   skip_whitespace(in);
   if (in.eof()) {
-    raise << "1: unbalanced '[' for recipe\n" << end();
+    raise_error << "1: unbalanced '[' for recipe\n" << end();
     return false;
   }
   skip_whitespace_and_comments(in);
   if (in.eof()) {
-    raise << "2: unbalanced '[' for recipe\n" << end();
+    raise_error << "2: unbalanced '[' for recipe\n" << end();
     return false;
   }
 
@@ -95,7 +95,7 @@ bool next_instruction(istream& in, instruction* curr) {
   while (in.peek() != '\n' && !in.eof()) {
     skip_whitespace(in);
     if (in.eof()) {
-      raise << "3: unbalanced '[' for recipe\n" << end();
+      raise_error << "3: unbalanced '[' for recipe\n" << end();
       return false;
     }
     string word = next_word(in);
@@ -112,7 +112,7 @@ bool next_instruction(istream& in, instruction* curr) {
     curr->label = words.at(0);
     trace("parse") << "label: " << curr->label << end();
     if (in.eof()) {
-      raise << "7: unbalanced '[' for recipe\n" << end();
+      raise_error << "7: unbalanced '[' for recipe\n" << end();
       return false;
     }
     return true;
@@ -128,7 +128,7 @@ bool next_instruction(istream& in, instruction* curr) {
   }
 
   if (p == words.end()) {
-    raise << "instruction prematurely ended with '<-'\n" << end();
+    raise_error << "instruction prematurely ended with '<-'\n" << end();
     return false;
   }
   curr->name = *p;
@@ -136,7 +136,7 @@ bool next_instruction(istream& in, instruction* curr) {
     Recipe_ordinal[*p] = Next_recipe_ordinal++;
   }
   if (Recipe_ordinal[*p] == 0) {
-    raise << "Recipe " << *p << " has number 0, which is reserved for IDLE.\n" << end();
+    raise_error << "Recipe " << *p << " has number 0, which is reserved for IDLE.\n" << end();
     return false;
   }
   curr->operation = Recipe_ordinal[*p];  ++p;
@@ -154,7 +154,7 @@ bool next_instruction(istream& in, instruction* curr) {
     trace("parse") << "  product: " << p->to_string() << end();
   }
   if (in.eof()) {
-    raise << "9: unbalanced '[' for recipe\n" << end();
+    raise_error << "9: unbalanced '[' for recipe\n" << end();
     return false;
   }
   return true;
diff --git a/013literal_string.cc b/013literal_string.cc
index 7424a31d..98cc85e7 100644
--- a/013literal_string.cc
+++ b/013literal_string.cc
@@ -73,7 +73,7 @@ void slurp_quoted_comment_oblivious(istream& in, ostringstream& out) {
     if (brace_depth == 0) break;
   }
   if (in.eof() && brace_depth > 0) {
-    raise << "unbalanced '['\n" << end();
+    raise_error << "unbalanced '['\n" << end();
     out.clear();
   }
 }
@@ -100,7 +100,7 @@ void slurp_quoted_comment_aware(istream& in, ostringstream& out) {
     out << c;
     if (c == ']') return;
   }
-  raise << "unbalanced '['\n" << end();
+  raise_error << "unbalanced '['\n" << end();
   out.clear();
 }
 
diff --git a/020run.cc b/020run.cc
index 329bcdb0..c08049a0 100644
--- a/020run.cc
+++ b/020run.cc
@@ -64,7 +64,7 @@ void run_current_routine()
     if (current_instruction().is_label) { ++current_step_index(); continue; }
     trace(Initial_callstack_depth+Callstack_depth, "run") << current_instruction().to_string() << end();
     if (Memory[0] != 0) {
-      raise << "something wrote to location 0; this should never happen\n" << end();
+      raise_error << "something wrote to location 0; this should never happen\n" << end();
       Memory[0] = 0;
     }
     // Read all ingredients from memory.
@@ -92,7 +92,7 @@ void run_current_routine()
       }
     }
     if (SIZE(products) < SIZE(current_instruction().products)) {
-      raise << SIZE(products) << " vs " << SIZE(current_instruction().products) << ": failed to write to all products! " << current_instruction().to_string() << '\n' << end();
+      raise_error << SIZE(products) << " vs " << SIZE(current_instruction().products) << ": failed to write to all products! " << current_instruction().to_string() << '\n' << end();
     }
     else {
       for (long long int i = 0; i < SIZE(current_instruction().products); ++i) {
@@ -210,7 +210,7 @@ void load_permanently(string filename) {
   ifstream fin(filename.c_str());
   fin.peek();
   if (!fin) {
-    raise << "no such file " << filename << '\n' << end();
+    raise_error << "no such file " << filename << '\n' << end();
     return;
   }
   fin >> std::noskipws;
@@ -267,7 +267,7 @@ void write_memory(reagent x, vector<double> data) {
   if (is_literal(x)) return;
   long long int base = x.value;
   if (size_mismatch(x, data)) {
-    raise << maybe(current_recipe_name()) << "size mismatch in storing to " << x.original_string << " (" << size_of(x.types) << " vs " << SIZE(data) << ") at '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "size mismatch in storing to " << x.original_string << " (" << size_of(x.types) << " vs " << SIZE(data) << ") at '" << current_instruction().to_string() << "'\n" << end();
     return;
   }
   for (long long int offset = 0; offset < SIZE(data); ++offset) {
@@ -304,7 +304,7 @@ bool is_literal(const reagent& r) {
   return SIZE(r.types) == 1 && r.types.at(0) == 0;
 }
 
-// hook to suppress inserting recipe name into warnings
+// hook to suppress inserting recipe name into errors and warnings (for later layers)
 string maybe(string s) {
   return s + ": ";
 }
@@ -334,7 +334,7 @@ recipe main [
 +run: _ <- copy 0
 
 :(scenario write_to_0_disallowed)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   0:number <- copy 34
 ]
diff --git a/021check_instruction.cc b/021check_instruction.cc
index bf238020..fcd861da 100644
--- a/021check_instruction.cc
+++ b/021check_instruction.cc
@@ -21,12 +21,12 @@ void check_instruction(const recipe_ordinal r) {
       // Primitive Recipe Checks
       case COPY: {
         if (SIZE(inst.products) != SIZE(inst.ingredients)) {
-          raise << "ingredients and products should match in '" << inst.to_string() << "'\n" << end();
+          raise_error << "ingredients and products should match in '" << inst.to_string() << "'\n" << end();
           break;
         }
         for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
           if (!types_match(inst.products.at(i), inst.ingredients.at(i))) {
-            raise << maybe(Recipe[r].name) << "can't copy " << inst.ingredients.at(i).original_string << " to " << inst.products.at(i).original_string << "; types don't match\n" << end();
+            raise_error << maybe(Recipe[r].name) << "can't copy " << inst.ingredients.at(i).original_string << " to " << inst.products.at(i).original_string << "; types don't match\n" << end();
             goto finish_checking_instruction;
           }
         }
@@ -43,32 +43,32 @@ void check_instruction(const recipe_ordinal r) {
 }
 
 :(scenario copy_checks_reagent_count)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number <- copy 34, 35
 ]
-+warn: ingredients and products should match in '1:number <- copy 34, 35'
++error: ingredients and products should match in '1:number <- copy 34, 35'
 
 :(scenario write_scalar_to_array_disallowed)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:array:number <- copy 34
 ]
-+warn: main: can't copy 34 to 1:array:number; types don't match
++error: main: can't copy 34 to 1:array:number; types don't match
 
 :(scenario write_scalar_to_array_disallowed_2)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number, 2:array:number <- copy 34, 35
 ]
-+warn: main: can't copy 35 to 2:array:number; types don't match
++error: main: can't copy 35 to 2:array:number; types don't match
 
 :(scenario write_scalar_to_address_disallowed)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:address:number <- copy 34
 ]
-+warn: main: can't copy 34 to 1:address:number; types don't match
++error: main: can't copy 34 to 1:address:number; types don't match
 
 :(code)
 bool types_match(reagent lhs, reagent rhs) {
diff --git a/022arithmetic.cc b/022arithmetic.cc
index fbf3029a..16fab86c 100644
--- a/022arithmetic.cc
+++ b/022arithmetic.cc
@@ -9,16 +9,16 @@ case ADD: {
   // primary goal of these checks is to forbid address arithmetic
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_number(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'add' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'add' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
   if (SIZE(inst.products) > 1) {
-    raise << maybe(Recipe[r].name) << "'add' yields exactly one product in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'add' yields exactly one product in '" << inst.to_string() << "'\n" << end();
     break;
   }
   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
-    raise << maybe(Recipe[r].name) << "'add' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'add' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -55,18 +55,18 @@ recipe main [
 +mem: storing 12 in location 1
 
 :(scenario add_checks_type)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number <- add 2:boolean, 1
 ]
-+warn: main: 'add' requires number ingredients, but got 2:boolean
++error: main: 'add' requires number ingredients, but got 2:boolean
 
 :(scenario add_checks_return_type)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:address:number <- add 2, 2
 ]
-+warn: main: 'add' should yield a number, but got 1:address:number
++error: main: 'add' should yield a number, but got 1:address:number
 
 :(before "End Primitive Recipe Declarations")
 SUBTRACT,
@@ -75,22 +75,22 @@ Recipe_ordinal["subtract"] = SUBTRACT;
 :(before "End Primitive Recipe Checks")
 case SUBTRACT: {
   if (inst.ingredients.empty()) {
-    raise << maybe(Recipe[r].name) << "'subtract' has no ingredients\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'subtract' has no ingredients\n" << end();
     break;
   }
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (is_raw(inst.ingredients.at(i))) continue;  // permit address offset computations in tests
     if (!is_mu_number(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'subtract' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'subtract' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
   if (SIZE(inst.products) > 1) {
-    raise << maybe(Recipe[r].name) << "'subtract' yields exactly one product in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'subtract' yields exactly one product in '" << inst.to_string() << "'\n" << end();
     break;
   }
   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
-    raise << maybe(Recipe[r].name) << "'subtract' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'subtract' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -133,16 +133,16 @@ Recipe_ordinal["multiply"] = MULTIPLY;
 case MULTIPLY: {
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_number(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'add' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'multiply' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
   if (SIZE(inst.products) > 1) {
-    raise << maybe(Recipe[r].name) << "'multiply' yields exactly one product in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'multiply' yields exactly one product in '" << inst.to_string() << "'\n" << end();
     break;
   }
   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
-    raise << maybe(Recipe[r].name) << "'multiply' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'multiply' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -185,21 +185,21 @@ Recipe_ordinal["divide"] = DIVIDE;
 :(before "End Primitive Recipe Checks")
 case DIVIDE: {
   if (inst.ingredients.empty()) {
-    raise << maybe(Recipe[r].name) << "'divide' has no ingredients\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'divide' has no ingredients\n" << end();
     break;
   }
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_number(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'divide' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'divide' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
   if (SIZE(inst.products) > 1) {
-    raise << maybe(Recipe[r].name) << "'divide' yields exactly one product in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'divide' yields exactly one product in '" << inst.to_string() << "'\n" << end();
     break;
   }
   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
-    raise << maybe(Recipe[r].name) << "'divide' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'divide' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -243,20 +243,20 @@ Recipe_ordinal["divide-with-remainder"] = DIVIDE_WITH_REMAINDER;
 :(before "End Primitive Recipe Checks")
 case DIVIDE_WITH_REMAINDER: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(current_recipe_name()) << "'divide-with-remainder' requires exactly two ingredients, but got '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "'divide-with-remainder' requires exactly two ingredients, but got '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(1))) {
-    raise << maybe(current_recipe_name()) << "'divide-with-remainder' requires number ingredients, but got '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "'divide-with-remainder' requires number ingredients, but got '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   if (SIZE(inst.products) > 2) {
-    raise << maybe(Recipe[r].name) << "'divide-with-remainder' yields two products in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'divide-with-remainder' yields two products in '" << inst.to_string() << "'\n" << end();
     break;
   }
   for (long long int i = 0; i < SIZE(inst.products); ++i) {
     if (!is_dummy(inst.products.at(i)) && !is_mu_number(inst.products.at(i))) {
-      raise << maybe(Recipe[r].name) << "'divide-with-remainder' should yield a number, but got " << inst.products.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'divide-with-remainder' should yield a number, but got " << inst.products.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
@@ -268,7 +268,7 @@ case DIVIDE_WITH_REMAINDER: {
   long long int a = static_cast<long long int>(ingredients.at(0).at(0));
   long long int b = static_cast<long long int>(ingredients.at(1).at(0));
   if (b == 0) {
-    raise << maybe(current_recipe_name()) << "divide by zero in '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "divide by zero in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   long long int quotient = a / b;
@@ -308,12 +308,12 @@ recipe main [
 +mem: storing inf in location 1
 
 :(scenario divide_by_zero_2)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number <- divide-with-remainder 4, 0
 ]
 # integer division can't return floating-point infinity
-+warn: main: divide by zero in '1:number <- divide-with-remainder 4, 0'
++error: main: divide by zero in '1:number <- divide-with-remainder 4, 0'
 
 :(code)
 inline bool scalar(const vector<long long int>& x) {
diff --git a/023boolean.cc b/023boolean.cc
index 24752e72..49683455 100644
--- a/023boolean.cc
+++ b/023boolean.cc
@@ -8,7 +8,7 @@ Recipe_ordinal["and"] = AND;
 case AND: {
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_scalar(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'and' requires boolean ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'and' requires boolean ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
@@ -58,7 +58,7 @@ Recipe_ordinal["or"] = OR;
 case OR: {
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_scalar(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'and' requires boolean ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'and' requires boolean ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
@@ -107,12 +107,12 @@ Recipe_ordinal["not"] = NOT;
 :(before "End Primitive Recipe Checks")
 case NOT: {
   if (SIZE(inst.products) > SIZE(inst.ingredients)) {
-    raise << maybe(Recipe[r].name) << "'not' cannot have fewer ingredients than products in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'not' cannot have fewer ingredients than products in '" << inst.to_string() << "'\n" << end();
     break;
   }
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_scalar(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'not' requires boolean ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'not' requires boolean ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
diff --git a/024jump.cc b/024jump.cc
index b249535e..9e5c0838 100644
--- a/024jump.cc
+++ b/024jump.cc
@@ -16,11 +16,11 @@ Recipe_ordinal["jump"] = JUMP;
 :(before "End Primitive Recipe Checks")
 case JUMP: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'jump' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'jump' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'jump' should be a label or offset, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'jump' should be a label or offset, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -55,15 +55,15 @@ Recipe_ordinal["jump-if"] = JUMP_IF;
 :(before "End Primitive Recipe Checks")
 case JUMP_IF: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'jump-if' requires exactly two ingredients, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'jump-if' requires exactly two ingredients, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "'jump-if' requires a boolean for its first ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'jump-if' requires a boolean for its first ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "'jump-if' requires a label or offset for its second ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'jump-if' requires a label or offset for its second ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -107,15 +107,15 @@ Recipe_ordinal["jump-unless"] = JUMP_UNLESS;
 :(before "End Primitive Recipe Checks")
 case JUMP_UNLESS: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'jump-unless' requires exactly two ingredients, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'jump-unless' requires exactly two ingredients, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "'jump-unless' requires a boolean for its first ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'jump-unless' requires a boolean for its first ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "'jump-unless' requires a label or offset for its second ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'jump-unless' requires a label or offset for its second ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
diff --git a/025compare.cc b/025compare.cc
index 74f1e4f1..f7acb64c 100644
--- a/025compare.cc
+++ b/025compare.cc
@@ -7,7 +7,7 @@ Recipe_ordinal["equal"] = EQUAL;
 :(before "End Primitive Recipe Checks")
 case EQUAL: {
   if (SIZE(inst.ingredients) <= 1) {
-    raise << maybe(Recipe[r].name) << "'equal' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'equal' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
     break;
   }
   break;
@@ -66,12 +66,12 @@ Recipe_ordinal["greater-than"] = GREATER_THAN;
 :(before "End Primitive Recipe Checks")
 case GREATER_THAN: {
   if (SIZE(inst.ingredients) <= 1) {
-    raise << maybe(Recipe[r].name) << "'greater-than' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'greater-than' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
     break;
   }
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_number(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'greater-than' can only compare numbers; got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'greater-than' can only compare numbers; got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
@@ -125,12 +125,12 @@ Recipe_ordinal["lesser-than"] = LESSER_THAN;
 :(before "End Primitive Recipe Checks")
 case LESSER_THAN: {
   if (SIZE(inst.ingredients) <= 1) {
-    raise << maybe(Recipe[r].name) << "'lesser-than' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'lesser-than' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
     break;
   }
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_number(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'lesser-than' can only compare numbers; got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'lesser-than' can only compare numbers; got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
@@ -184,12 +184,12 @@ Recipe_ordinal["greater-or-equal"] = GREATER_OR_EQUAL;
 :(before "End Primitive Recipe Checks")
 case GREATER_OR_EQUAL: {
   if (SIZE(inst.ingredients) <= 1) {
-    raise << maybe(Recipe[r].name) << "'greater-or-equal' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'greater-or-equal' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
     break;
   }
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_number(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'greater-or-equal' can only compare numbers; got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'greater-or-equal' can only compare numbers; got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
@@ -251,12 +251,12 @@ Recipe_ordinal["lesser-or-equal"] = LESSER_OR_EQUAL;
 :(before "End Primitive Recipe Checks")
 case LESSER_OR_EQUAL: {
   if (SIZE(inst.ingredients) <= 1) {
-    raise << maybe(Recipe[r].name) << "'lesser-or-equal' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'lesser-or-equal' needs at least two ingredients to compare in '" << inst.to_string() << "'\n" << end();
     break;
   }
   for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
     if (!is_mu_number(inst.ingredients.at(i))) {
-      raise << maybe(Recipe[r].name) << "'lesser-or-equal' can only compare numbers; got " << inst.ingredients.at(i).original_string << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "'lesser-or-equal' can only compare numbers; got " << inst.ingredients.at(i).original_string << '\n' << end();
       goto finish_checking_instruction;
     }
   }
diff --git a/029tools.cc b/029tools.cc
index 17dea6ef..d17c9b81 100644
--- a/029tools.cc
+++ b/029tools.cc
@@ -13,15 +13,15 @@ Recipe_ordinal["trace"] = TRACE;
 :(before "End Primitive Recipe Checks")
 case TRACE: {
   if (SIZE(inst.ingredients) < 3) {
-    raise << maybe(Recipe[r].name) << "'trace' takes three or more ingredients rather than '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'trace' takes three or more ingredients rather than '" << inst.to_string() << "'\n" << end();
     break;
   }
   if (!is_mu_number(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'trace' should be a number (depth), but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'trace' should be a number (depth), but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   if (!is_literal_string(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "second ingredient of 'trace' should be a literal string (label), but got " << inst.ingredients.at(1).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "second ingredient of 'trace' should be a literal string (label), but got " << inst.ingredients.at(1).original_string << '\n' << end();
     break;
   }
   break;
@@ -90,30 +90,30 @@ string print_mu(const reagent& r, const vector<double>& data) {
 }
 
 :(before "End Primitive Recipe Declarations")
-HIDE_WARNINGS,
+HIDE_ERRORS,
 :(before "End Primitive Recipe Numbers")
-Recipe_ordinal["hide-warnings"] = HIDE_WARNINGS;
+Recipe_ordinal["hide-errors"] = HIDE_ERRORS;
 :(before "End Primitive Recipe Checks")
-case HIDE_WARNINGS: {
+case HIDE_ERRORS: {
   break;
 }
 :(before "End Primitive Recipe Implementations")
-case HIDE_WARNINGS: {
-  Hide_warnings = true;
+case HIDE_ERRORS: {
+  Hide_errors = true;
   break;
 }
 
 :(before "End Primitive Recipe Declarations")
-SHOW_WARNINGS,
+SHOW_ERRORS,
 :(before "End Primitive Recipe Numbers")
-Recipe_ordinal["show-warnings"] = SHOW_WARNINGS;
+Recipe_ordinal["show-errors"] = SHOW_ERRORS;
 :(before "End Primitive Recipe Checks")
-case SHOW_WARNINGS: {
+case SHOW_ERRORS: {
   break;
 }
 :(before "End Primitive Recipe Implementations")
-case SHOW_WARNINGS: {
-  Hide_warnings = false;
+case SHOW_ERRORS: {
+  Hide_errors = false;
   break;
 }
 
@@ -170,11 +170,11 @@ case _CLEAR_TRACE: {
 //: assert: perform sanity checks at runtime
 
 :(scenario assert)
-% Hide_warnings = true;  // '%' lines insert arbitrary C code into tests before calling 'run' with the lines below. Must be immediately after :(scenario) line.
+% Hide_errors = true;  // '%' lines insert arbitrary C code into tests before calling 'run' with the lines below. Must be immediately after :(scenario) line.
 recipe main [
   assert 0, [this is an assert in mu]
 ]
-+warn: this is an assert in mu
++error: this is an assert in mu
 
 :(before "End Primitive Recipe Declarations")
 ASSERT,
@@ -183,15 +183,15 @@ Recipe_ordinal["assert"] = ASSERT;
 :(before "End Primitive Recipe Checks")
 case ASSERT: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'assert' takes exactly two ingredients rather than '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'assert' takes exactly two ingredients rather than '" << inst.to_string() << "'\n" << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "'assert' requires a boolean for its first ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'assert' requires a boolean for its first ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   if (!is_literal_string(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "'assert' requires a literal string for its second ingredient, but got " << inst.ingredients.at(1).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'assert' requires a literal string for its second ingredient, but got " << inst.ingredients.at(1).original_string << '\n' << end();
     break;
   }
   break;
@@ -199,7 +199,7 @@ case ASSERT: {
 :(before "End Primitive Recipe Implementations")
 case ASSERT: {
   if (!ingredients.at(0).at(0)) {
-    raise << current_instruction().ingredients.at(1).name << '\n' << end();
+    raise_error << current_instruction().ingredients.at(1).name << '\n' << end();
   }
   break;
 }
@@ -255,8 +255,8 @@ _SYSTEM,
 Recipe_ordinal["$system"] = _SYSTEM;
 :(before "End Primitive Recipe Checks")
 case _SYSTEM: {
-  if (inst.ingredients.empty()) {
-    raise << maybe(Recipe[r].name) << "'$system' requires exactly one ingredient, but got none\n" << end();
+  if (SIZE(inst.ingredients) != 1) {
+    raise_error << maybe(Recipe[r].name) << "'$system' requires exactly one ingredient, but got none\n" << end();
     break;
   }
   break;
diff --git a/030container.cc b/030container.cc
index 0cecdec7..a0b214a0 100644
--- a/030container.cc
+++ b/030container.cc
@@ -16,7 +16,7 @@ Type[point].elements.push_back(i);
 
 //: Tests in this layer often explicitly setup memory before reading it as a
 //: container. Don't do this in general. I'm tagging exceptions with /raw to
-//: avoid warnings.
+//: avoid errors.
 :(scenario copy_multiple_locations)
 recipe main [
   1:number <- copy 34
@@ -28,11 +28,11 @@ recipe main [
 
 //: trying to copy to a differently-typed destination will fail
 :(scenario copy_checks_size)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   2:point <- copy 1:number
 ]
-+warn: main: can't copy 1:number to 2:point; types don't match
++error: main: can't copy 1:number to 2:point; types don't match
 
 :(before "End Mu Types Initialization")
 // A more complex container, containing another container as one of its
@@ -92,7 +92,7 @@ if (t.kind == container) {
   for (long long int i = 0; i < SIZE(t.elements); ++i) {
     // todo: strengthen assertion to disallow mutual type recursion
     if (types.at(0) == t.elements.at(i).at(0)) {
-      raise << "container " << t.name << " can't include itself as a member\n" << end();
+      raise_error << "container " << t.name << " can't include itself as a member\n" << end();
       return 0;
     }
     // End size_of(type) Container Cases
@@ -126,26 +126,26 @@ Recipe_ordinal["get"] = GET;
 :(before "End Primitive Recipe Checks")
 case GET: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'get' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'get' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
     break;
   }
   reagent base = inst.ingredients.at(0);
   // Update GET base in Check
   if (base.types.empty() || Type[base.types.at(0)].kind != container) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'get' should be a container, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'get' should be a container, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   type_ordinal base_type = base.types.at(0);
   reagent offset = inst.ingredients.at(1);
   if (!is_literal(offset) || !is_mu_scalar(offset)) {
-    raise << maybe(Recipe[r].name) << "second ingredient of 'get' should have type 'offset', but got " << inst.ingredients.at(1).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "second ingredient of 'get' should have type 'offset', but got " << inst.ingredients.at(1).original_string << '\n' << end();
     break;
   }
   long long int offset_value = 0;
   if (is_integer(offset.name)) {  // later layers permit non-integer offsets
     offset_value = to_integer(offset.name);
     if (offset_value < 0 || offset_value >= SIZE(Type[base_type].elements)) {
-      raise << maybe(Recipe[r].name) << "invalid offset " << offset_value << " for " << Type[base_type].name << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "invalid offset " << offset_value << " for " << Type[base_type].name << '\n' << end();
       break;
     }
   }
@@ -157,7 +157,7 @@ case GET: {
   reagent element;
   element.types = Type[base_type].elements.at(offset_value);
   if (!types_match(product, element)) {
-    raise << maybe(Recipe[r].name) << "'get' " << offset.original_string << " (" << offset_value << ") on " << Type[base_type].name << " can't be saved in " << product.original_string << "; type should be " << dump_types(element) << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'get' " << offset.original_string << " (" << offset_value << ") on " << Type[base_type].name << " can't be saved in " << product.original_string << "; type should be " << dump_types(element) << '\n' << end();
     break;
   }
   break;
@@ -168,7 +168,7 @@ case GET: {
   // Update GET base in Run
   long long int base_address = base.value;
   if (base_address == 0) {
-    raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   type_ordinal base_type = base.types.at(0);
@@ -219,34 +219,34 @@ recipe main [
 +mem: storing 13 in location 15
 
 :(scenario get_out_of_bounds)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   12:number <- copy 34
   13:number <- copy 35
   14:number <- copy 36
   get 12:point-number/raw, 2:offset  # point-number occupies 3 locations but has only 2 fields; out of bounds
 ]
-+warn: main: invalid offset 2 for point-number
++error: main: invalid offset 2 for point-number
 
 :(scenario get_out_of_bounds_2)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   12:number <- copy 34
   13:number <- copy 35
   14:number <- copy 36
   get 12:point-number/raw, -1:offset
 ]
-+warn: main: invalid offset -1 for point-number
++error: main: invalid offset -1 for point-number
 
 :(scenario get_product_type_mismatch)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   12:number <- copy 34
   13:number <- copy 35
   14:number <- copy 36
   15:address:number <- get 12:point-number/raw, 1:offset
 ]
-+warn: main: 'get' 1:offset (1) on point-number can't be saved in 15:address:number; type should be number
++error: main: 'get' 1:offset (1) on point-number can't be saved in 15:address:number; type should be number
 
 :(before "End Primitive Recipe Declarations")
 GET_ADDRESS,
@@ -255,26 +255,26 @@ Recipe_ordinal["get-address"] = GET_ADDRESS;
 :(before "End Primitive Recipe Checks")
 case GET_ADDRESS: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'get-address' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'get-address' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
     break;
   }
   reagent base = inst.ingredients.at(0);
   // Update GET_ADDRESS base in Check
   if (base.types.empty() || Type[base.types.at(0)].kind != container) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'get-address' should be a container, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'get-address' should be a container, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   type_ordinal base_type = base.types.at(0);
   reagent offset = inst.ingredients.at(1);
   if (!is_literal(offset) || !is_mu_scalar(offset)) {
-    raise << maybe(Recipe[r].name) << "second ingredient of 'get' should have type 'offset', but got " << inst.ingredients.at(1).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "second ingredient of 'get' should have type 'offset', but got " << inst.ingredients.at(1).original_string << '\n' << end();
     break;
   }
   long long int offset_value = 0;
   if (is_integer(offset.name)) {  // later layers permit non-integer offsets
     offset_value = to_integer(offset.name);
     if (offset_value < 0 || offset_value >= SIZE(Type[base_type].elements)) {
-      raise << maybe(Recipe[r].name) << "invalid offset " << offset_value << " for " << Type[base_type].name << '\n' << end();
+      raise_error << maybe(Recipe[r].name) << "invalid offset " << offset_value << " for " << Type[base_type].name << '\n' << end();
       break;
     }
   }
@@ -287,7 +287,7 @@ case GET_ADDRESS: {
   element.types = Type[base_type].elements.at(offset_value);
   element.types.insert(element.types.begin(), Type_ordinal["address"]);
   if (!types_match(product, element)) {
-    raise << maybe(Recipe[r].name) << "'get-address' " << offset.original_string << " (" << offset_value << ") on " << Type[base_type].name << " can't be saved in " << product.original_string << "; type should be " << dump_types(element) << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'get-address' " << offset.original_string << " (" << offset_value << ") on " << Type[base_type].name << " can't be saved in " << product.original_string << "; type should be " << dump_types(element) << '\n' << end();
     break;
   }
   break;
@@ -298,7 +298,7 @@ case GET_ADDRESS: {
   // Update GET_ADDRESS base in Run
   long long int base_address = base.value;
   if (base_address == 0) {
-    raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   type_ordinal base_type = base.types.at(0);
@@ -316,34 +316,34 @@ case GET_ADDRESS: {
 }
 
 :(scenario get_address_out_of_bounds)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   12:number <- copy 34
   13:number <- copy 35
   14:number <- copy 36
   get-address 12:point-number/raw, 2:offset  # point-number occupies 3 locations but has only 2 fields; out of bounds
 ]
-+warn: main: invalid offset 2 for point-number
++error: main: invalid offset 2 for point-number
 
 :(scenario get_address_out_of_bounds_2)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   12:number <- copy 34
   13:number <- copy 35
   14:number <- copy 36
   get-address 12:point-number/raw, -1:offset
 ]
-+warn: main: invalid offset -1 for point-number
++error: main: invalid offset -1 for point-number
 
 :(scenario get_address_product_type_mismatch)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   12:number <- copy 34
   13:number <- copy 35
   14:number <- copy 36
   15:number <- get-address 12:point-number/raw, 1:offset
 ]
-+warn: main: 'get-address' 1:offset (1) on point-number can't be saved in 15:number; type should be address:number
++error: main: 'get-address' 1:offset (1) on point-number can't be saved in 15:number; type should be address:number
 
 //:: Allow containers to be defined in mu code.
 
@@ -427,7 +427,7 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
 void skip_bracket(istream& in, string message) {
   skip_whitespace_and_comments(in);
   if (in.get() != '[')
-    raise << message << '\n' << end();
+    raise_error << message << '\n' << end();
 }
 
 :(scenarios run)
@@ -482,19 +482,19 @@ assert(Next_type_ordinal < 1000);
 :(before "End Setup")
 Next_type_ordinal = 1000;
 
-//:: Allow container definitions anywhere in the codebase, but warn if you
-//:: can't find a definition.
+//:: Allow container definitions anywhere in the codebase, but complain if you
+//:: can't find a definition at the end.
 
-:(scenario run_warns_on_unknown_types)
-% Hide_warnings = true;
+:(scenario run_complains_on_unknown_types)
+% Hide_errors = true;
 recipe main [
   # integer is not a type
   1:integer <- copy 0
 ]
-+warn: unknown type: integer
++error: unknown type: integer
 
 :(scenario run_allows_type_definition_after_use)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:bar <- copy 0/raw
 ]
@@ -502,8 +502,8 @@ recipe main [
 container bar [
   x:number
 ]
--warn: unknown type: bar
-$warn: 0
+-error: unknown type: bar
+$error: 0
 
 :(after "int main")
   Transform.push_back(check_invalid_types);
@@ -525,17 +525,17 @@ 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' << end();
+      raise_error << "unknown type: " << r.properties.at(0).second.at(i) << '\n' << end();
   }
 }
 
 :(scenario container_unknown_field)
-% Hide_warnings = true;
+% Hide_errors = true;
 container foo [
   x:number
   y:bar
 ]
-+warn: unknown type for field y in foo
++error: unknown type for field y in foo
 
 :(scenario read_container_with_bracket_in_comment)
 container foo [
@@ -561,7 +561,7 @@ void check_container_field_types() {
         if (info.elements.at(i).at(j) == 0) continue;
         if (Type.find(info.elements.at(i).at(j)) != Type.end()) continue;
         // End Container Type Checks
-        raise << "unknown type for field " << info.element_names.at(i) << " in " << info.name << '\n' << end();
+        raise_error << "unknown type for field " << info.element_names.at(i) << " in " << info.name << '\n' << end();
       }
     }
   }
@@ -630,11 +630,11 @@ map<string, type_ordinal> ingredient_names;
 if (element.find(':') == string::npos) {
   // no type; we're defining a generic variable
   if (next_word(in) != "<-") {
-    raise << "Element " << element << " of container " << name << " doesn't provide a type.\n" << end();
+    raise_error << "Element " << element << " of container " << name << " doesn't provide a type.\n" << end();
     break;
   }
   if (next_word(in) != "next-type") {
-    raise << "Type " << element << " of container " << name << " must be defined using 'next-type'\n" << end();
+    raise_error << "Type " << element << " of container " << name << " must be defined using 'next-type'\n" << end();
     break;
   }
   type_ordinal next_type_ordinal = SIZE(info.ingredient_names);
diff --git a/031address.cc b/031address.cc
index 66172fbe..fd7c61e0 100644
--- a/031address.cc
+++ b/031address.cc
@@ -25,19 +25,19 @@ recipe main [
 :(before "long long int base = x.value" following "void write_memory(reagent x, vector<double> data)")
 x = canonize(x);
 if (x.value == 0) {
-  raise << "can't write to location 0 in '" << current_instruction().to_string() << "'\n" << end();
+  raise_error << "can't write to location 0 in '" << current_instruction().to_string() << "'\n" << end();
   return;
 }
 
 //: writes to address 0 always loudly fail
-:(scenario store_to_0_warns)
-% Hide_warnings = true;
+:(scenario store_to_0_fails)
+% Hide_errors = true;
 recipe main [
   1:address:number <- copy 0
   1:address:number/lookup <- copy 34
 ]
 -mem: storing 34 in location 0
-+warn: can't write to location 0 in '1:address:number/lookup <- copy 34'
++error: can't write to location 0 in '1:address:number/lookup <- copy 34'
 
 :(code)
 reagent canonize(reagent x) {
@@ -52,12 +52,12 @@ reagent lookup_memory(reagent x) {
   static const type_ordinal ADDRESS = Type_ordinal["address"];
   reagent result;
   if (x.types.empty() || x.types.at(0) != ADDRESS) {
-    raise << maybe(current_recipe_name()) << "tried to /lookup " << x.original_string << " but it isn't an address\n" << end();
+    raise_error << maybe(current_recipe_name()) << "tried to /lookup " << x.original_string << " but it isn't an address\n" << end();
     return result;
   }
   // compute value
   if (x.value == 0) {
-    raise << maybe(current_recipe_name()) << "tried to /lookup 0\n" << end();
+    raise_error << maybe(current_recipe_name()) << "tried to /lookup 0\n" << end();
     return result;
   }
   result.set_value(Memory[x.value]);
@@ -97,11 +97,11 @@ reagent lookup_memory(reagent x) {
 bool canonize_type(reagent& r) {
   while (has_property(r, "lookup")) {
     if (r.types.empty()) {
-      raise << "can't lookup non-address: " << r.original_string << '\n' << end();
+      raise_error << "can't lookup non-address: " << r.original_string << '\n' << end();
       return false;
     }
     if (r.types.at(0) != Type_ordinal["address"]) {
-      raise << "can't lookup non-address: " << r.original_string << '\n' << end();
+      raise_error << "can't lookup non-address: " << r.original_string << '\n' << end();
       return false;
     }
     r.types.erase(r.types.begin());
@@ -180,7 +180,7 @@ recipe main [
     properties.push_back(pair<string, vector<string> >("lookup", vector<string>()));
   }
   if (name.empty())
-    raise << "illegal name " << original_string << '\n' << end();
+    raise_error << "illegal name " << original_string << '\n' << end();
 }
 
 //:: helpers for debugging
diff --git a/032array.cc b/032array.cc
index f8df003f..1a1b2d17 100644
--- a/032array.cc
+++ b/032array.cc
@@ -20,26 +20,26 @@ Recipe_ordinal["create-array"] = CREATE_ARRAY;
 :(before "End Primitive Recipe Checks")
 case CREATE_ARRAY: {
   if (inst.products.empty()) {
-    raise << maybe(Recipe[r].name) << "'create-array' needs one product and no ingredients but got '" << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'create-array' needs one product and no ingredients but got '" << inst.to_string() << '\n' << end();
     break;
   }
   reagent product = inst.products.at(0);
   canonize_type(product);
   if (!is_mu_array(product)) {
-    raise << maybe(Recipe[r].name) << "'create-array' cannot create non-array " << product.original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'create-array' cannot create non-array " << product.original_string << '\n' << end();
     break;
   }
   if (SIZE(product.types) == 1) {
-    raise << maybe(Recipe[r].name) << "create array of what? " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "create array of what? " << inst.to_string() << '\n' << end();
     break;
   }
   // 'create-array' will need to check properties rather than types
   if (SIZE(product.properties.at(0).second) <= 2) {
-    raise << maybe(Recipe[r].name) << "create array of what size? " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "create array of what size? " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_integer(product.properties.at(0).second.at(2))) {
-    raise << maybe(Recipe[r].name) << "'create-array' product should specify size of array after its element type, but got " << product.properties.at(0).second.at(2) << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'create-array' product should specify size of array after its element type, but got " << product.properties.at(0).second.at(2) << '\n' << end();
     break;
   }
   break;
@@ -108,7 +108,7 @@ if (x.types.at(0) == Type_ordinal["array"]) return false;
 :(before "End size_of(reagent) Cases")
 if (r.types.at(0) == Type_ordinal["array"]) {
   if (SIZE(r.types) == 1) {
-    raise << maybe(current_recipe_name()) << "'" << r.original_string << "' is an array of what?\n" << end();
+    raise_error << maybe(current_recipe_name()) << "'" << r.original_string << "' is an array of what?\n" << end();
     return 1;
   }
   // skip the 'array' type to get at the element type
@@ -145,13 +145,13 @@ Recipe_ordinal["index"] = INDEX;
 :(before "End Primitive Recipe Checks")
 case INDEX: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'index' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'index' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
     break;
   }
   reagent base = inst.ingredients.at(0);
   canonize_type(base);
   if (!is_mu_array(base)) {
-    raise << maybe(Recipe[r].name) << "'index' on a non-array " << base.original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'index' on a non-array " << base.original_string << '\n' << end();
     break;
   }
   if (inst.products.empty()) break;
@@ -160,7 +160,7 @@ case INDEX: {
   reagent element;
   element.types = array_element(base.types);
   if (!types_match(product, element)) {
-    raise << maybe(Recipe[r].name) << "'index' on " << base.original_string << " can't be saved in " << product.original_string << "; type should be " << dump_types(element) << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'index' on " << base.original_string << " can't be saved in " << product.original_string << "; type should be " << dump_types(element) << '\n' << end();
     break;
   }
   break;
@@ -170,14 +170,14 @@ case INDEX: {
   reagent base = canonize(current_instruction().ingredients.at(0));
   long long int base_address = base.value;
   if (base_address == 0) {
-    raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   reagent offset = canonize(current_instruction().ingredients.at(1));
   vector<double> offset_val(read_memory(offset));
   vector<type_ordinal> element_type = array_element(base.types);
   if (offset_val.at(0) < 0 || offset_val.at(0) >= Memory[base_address]) {
-    raise << maybe(current_recipe_name()) << "invalid index " << no_scientific(offset_val.at(0)) << '\n' << end();
+    raise_error << maybe(current_recipe_name()) << "invalid index " << no_scientific(offset_val.at(0)) << '\n' << end();
     break;
   }
   long long int src = base_address + 1 + offset_val.at(0)*size_of(element_type);
@@ -207,7 +207,7 @@ recipe main [
 +mem: storing 15 in location 6
 
 :(scenario index_out_of_bounds)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:array:number:3 <- create-array
   2:number <- copy 14
@@ -219,10 +219,10 @@ recipe main [
   8:address:array:point <- copy 1
   index *8:address:array:point, 4  # less than size of array in locations, but larger than its length in elements
 ]
-+warn: main: invalid index 4
++error: main: invalid index 4
 
 :(scenario index_out_of_bounds_2)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:array:point:3 <- create-array
   2:number <- copy 14
@@ -234,10 +234,10 @@ recipe main [
   8:address:array:point <- copy 1/raw
   index *8:address:array:point, -1
 ]
-+warn: main: invalid index -1
++error: main: invalid index -1
 
 :(scenario index_product_type_mismatch)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:array:point:3 <- create-array
   2:number <- copy 14
@@ -249,7 +249,7 @@ recipe main [
   8:address:array:point <- copy 1/raw
   9:number <- index *8:address:array:point, 0
 ]
-+warn: main: 'index' on *8:address:array:point can't be saved in 9:number; type should be point
++error: main: 'index' on *8:address:array:point can't be saved in 9:number; type should be point
 
 //:: To write to elements of containers, you need their address.
 
@@ -270,13 +270,13 @@ Recipe_ordinal["index-address"] = INDEX_ADDRESS;
 :(before "End Primitive Recipe Checks")
 case INDEX_ADDRESS: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'index-address' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'index-address' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
     break;
   }
   reagent base = inst.ingredients.at(0);
   canonize_type(base);
   if (!is_mu_array(base)) {
-    raise << current_recipe_name () << "'index-address' on a non-array " << base.original_string << '\n' << end();
+    raise_error << current_recipe_name () << "'index-address' on a non-array " << base.original_string << '\n' << end();
     break;
   }
   if (inst.products.empty()) break;
@@ -286,7 +286,7 @@ case INDEX_ADDRESS: {
   element.types = array_element(base.types);
   element.types.insert(element.types.begin(), Type_ordinal["address"]);
   if (!types_match(product, element)) {
-    raise << maybe(Recipe[r].name) << "'index' on " << base.original_string << " can't be saved in " << product.original_string << "; type should be " << dump_types(element) << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'index' on " << base.original_string << " can't be saved in " << product.original_string << "; type should be " << dump_types(element) << '\n' << end();
     break;
   }
   break;
@@ -296,14 +296,14 @@ case INDEX_ADDRESS: {
   reagent base = canonize(current_instruction().ingredients.at(0));
   long long int base_address = base.value;
   if (base_address == 0) {
-    raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   reagent offset = canonize(current_instruction().ingredients.at(1));
   vector<double> offset_val(read_memory(offset));
   vector<type_ordinal> element_type = array_element(base.types);
   if (offset_val.at(0) < 0 || offset_val.at(0) >= Memory[base_address]) {
-    raise << maybe(current_recipe_name()) << "invalid index " << no_scientific(offset_val.at(0)) << '\n' << end();
+    raise_error << maybe(current_recipe_name()) << "invalid index " << no_scientific(offset_val.at(0)) << '\n' << end();
     break;
   }
   long long int result = base_address + 1 + offset_val.at(0)*size_of(element_type);
@@ -313,7 +313,7 @@ case INDEX_ADDRESS: {
 }
 
 :(scenario index_address_out_of_bounds)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:array:point:3 <- create-array
   2:number <- copy 14
@@ -325,10 +325,10 @@ recipe main [
   8:address:array:point <- copy 1  # unsafe
   index-address *8:address:array:point, 4  # less than size of array in locations, but larger than its length in elements
 ]
-+warn: main: invalid index 4
++error: main: invalid index 4
 
 :(scenario index_address_out_of_bounds_2)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:array:point:3 <- create-array
   2:number <- copy 14
@@ -340,10 +340,10 @@ recipe main [
   8:address:array:point <- copy 1/raw
   index-address *8:address:array:point, -1
 ]
-+warn: main: invalid index -1
++error: main: invalid index -1
 
 :(scenario index_address_product_type_mismatch)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:array:point:3 <- create-array
   2:number <- copy 14
@@ -355,7 +355,7 @@ recipe main [
   8:address:array:point <- copy 1/raw
   9:address:number <- index-address *8:address:array:point, 0
 ]
-+warn: main: 'index' on *8:address:array:point can't be saved in 9:address:number; type should be address:point
++error: main: 'index' on *8:address:array:point can't be saved in 9:address:number; type should be address:point
 
 //:: compute the length of an array
 
@@ -376,13 +376,13 @@ Recipe_ordinal["length"] = LENGTH;
 :(before "End Primitive Recipe Checks")
 case LENGTH: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'length' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'length' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
     break;
   }
   reagent x = inst.ingredients.at(0);
   canonize_type(x);
   if (!is_mu_array(x)) {
-    raise << "tried to calculate length of non-array " << x.original_string << '\n' << end();
+    raise_error << "tried to calculate length of non-array " << x.original_string << '\n' << end();
     break;
   }
   break;
@@ -391,7 +391,7 @@ case LENGTH: {
 case LENGTH: {
   reagent x = canonize(current_instruction().ingredients.at(0));
   if (x.value == 0) {
-    raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   products.resize(1);
diff --git a/033exclusive_container.cc b/033exclusive_container.cc
index d4973c7c..388a4f4f 100644
--- a/033exclusive_container.cc
+++ b/033exclusive_container.cc
@@ -23,7 +23,7 @@ Type[tmp].element_names.push_back("p");
 
 //: Tests in this layer often explicitly setup memory before reading it as an
 //: array. Don't do this in general. I'm tagging exceptions with /raw to
-//: avoid warnings.
+//: avoid errors.
 :(scenario copy_exclusive_container)
 # Copying exclusive containers copies all their contents and an extra location for the tag.
 recipe main [
@@ -84,17 +84,17 @@ Recipe_ordinal["maybe-convert"] = MAYBE_CONVERT;
 :(before "End Primitive Recipe Checks")
 case MAYBE_CONVERT: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'maybe-convert' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'maybe-convert' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end();
     break;
   }
   reagent base = inst.ingredients.at(0);
   canonize_type(base);
   if (base.types.empty() || Type[base.types.at(0)].kind != exclusive_container) {
-    raise << current_recipe_name () << "first ingredient of 'maybe-convert' should be an exclusive-container, but got " << base.original_string << '\n' << end();
+    raise_error << current_recipe_name () << "first ingredient of 'maybe-convert' should be an exclusive-container, but got " << base.original_string << '\n' << end();
     break;
   }
   if (!is_literal(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "second ingredient of 'maybe-convert' should have type 'variant', but got " << inst.ingredients.at(1).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "second ingredient of 'maybe-convert' should have type 'variant', but got " << inst.ingredients.at(1).original_string << '\n' << end();
     break;
   }
   break;
@@ -104,7 +104,7 @@ case MAYBE_CONVERT: {
   reagent base = canonize(current_instruction().ingredients.at(0));
   long long int base_address = base.value;
   if (base_address == 0) {
-    raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   long long int tag = current_instruction().ingredients.at(1).value;
diff --git a/034call.cc b/034call.cc
index 27a26e90..ebfb9534 100644
--- a/034call.cc
+++ b/034call.cc
@@ -80,7 +80,7 @@ inline const instruction& current_instruction() {
 :(after "Defined Recipe Checks")
 // not a primitive; check that it's present in the book of recipes
 if (Recipe.find(inst.operation) == Recipe.end()) {
-  raise << maybe(Recipe[r].name) << "undefined operation in '" << inst.to_string() << "'\n" << end();
+  raise_error << maybe(Recipe[r].name) << "undefined operation in '" << inst.to_string() << "'\n" << end();
   break;
 }
 :(replace{} "default:" following "End Primitive Recipe Implementations")
@@ -98,19 +98,19 @@ default: {
   continue;  // not done with caller; don't increment current_step_index()
 }
 
-:(scenario calling_undefined_recipe_warns)
-% Hide_warnings = true;
+:(scenario calling_undefined_recipe_fails)
+% Hide_errors = true;
 recipe main [
   foo
 ]
-+warn: main: undefined operation in 'foo '
++error: main: undefined operation in 'foo '
 
 :(scenario calling_undefined_recipe_handles_missing_result)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   x:number <- foo
 ]
-+warn: main: undefined operation in 'x:number <- foo '
++error: main: undefined operation in 'x:number <- foo '
 
 //:: finally, we need to fix the termination conditions for the run loop
 
@@ -133,7 +133,7 @@ while (current_step_index() >= SIZE(Current_routine->steps())) {
   Current_routine->calls.pop_front();
   if (Current_routine->calls.empty()) return;
   // Complete Call Fallthrough
-  // todo: no products returned warning
+  // todo: fail if no products returned
   ++current_step_index();
 }
 
diff --git a/035call_ingredient.cc b/035call_ingredient.cc
index fd8e7508..a56510d6 100644
--- a/035call_ingredient.cc
+++ b/035call_ingredient.cc
@@ -38,7 +38,7 @@ Recipe_ordinal["next-ingredient"] = NEXT_INGREDIENT;
 :(before "End Primitive Recipe Checks")
 case NEXT_INGREDIENT: {
   if (!inst.ingredients.empty()) {
-    raise << maybe(Recipe[r].name) << "'next-ingredient' didn't expect any ingredients in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'next-ingredient' didn't expect any ingredients in '" << inst.to_string() << "'\n" << end();
     break;
   }
   break;
@@ -55,7 +55,7 @@ case NEXT_INGREDIENT: {
   }
   else {
     if (SIZE(current_instruction().products) < 2)
-      raise << maybe(current_recipe_name()) << "no ingredient to save in " << current_instruction().products.at(0).original_string << '\n' << end();
+      raise_error << maybe(current_recipe_name()) << "no ingredient to save in " << current_instruction().products.at(0).original_string << '\n' << end();
     if (current_instruction().products.empty()) break;
     products.resize(2);
     // pad the first product with sufficient zeros to match its type
@@ -68,15 +68,15 @@ case NEXT_INGREDIENT: {
   break;
 }
 
-:(scenario next_ingredient_warn_on_missing)
-% Hide_warnings = true;
+:(scenario next_ingredient_fail_on_missing)
+% Hide_errors = true;
 recipe main [
   f
 ]
 recipe f [
   11:number <- next-ingredient
 ]
-+warn: f: no ingredient to save in 11:number
++error: f: no ingredient to save in 11:number
 
 :(scenario rewind_ingredients)
 recipe main [
@@ -125,11 +125,11 @@ Recipe_ordinal["ingredient"] = INGREDIENT;
 :(before "End Primitive Recipe Checks")
 case INGREDIENT: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'ingredient' expects exactly one ingredient, but got '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'ingredient' expects exactly one ingredient, but got '" << inst.to_string() << "'\n" << end();
     break;
   }
-  if (!is_literal(inst.ingredients.at(0)) && !is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "'ingredient' expects a literal ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+  if (!is_literal(inst.ingredients.at(0)) && !is_mu_number(inst.ingredients.at(0))) {
+    raise_error << maybe(Recipe[r].name) << "'ingredient' expects a literal ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
diff --git a/036call_reply.cc b/036call_reply.cc
index 14fb0c5c..ccf18a2f 100644
--- a/036call_reply.cc
+++ b/036call_reply.cc
@@ -32,13 +32,13 @@ case REPLY: {
   const instruction& caller_instruction = current_instruction();
   // check types with the caller
   if (SIZE(caller_instruction.products) > SIZE(ingredients)) {
-    raise << "too few values replied from " << callee << '\n' << end();
+    raise_error << "too few values replied from " << callee << '\n' << end();
     break;
   }
   for (long long int i = 0; i < SIZE(caller_instruction.products); ++i) {
     if (has_property(caller_instruction.products.at(i), "skiptypecheck")) continue;  // todo: drop this once we have generic containers
     if (!types_match(caller_instruction.products.at(i), reply_inst.ingredients.at(i))) {
-      raise << maybe(callee) << "reply ingredient " << reply_inst.ingredients.at(i).original_string << " can't be saved in " << caller_instruction.products.at(i).original_string << '\n' << end();
+      raise_error << maybe(callee) << "reply ingredient " << reply_inst.ingredients.at(i).original_string << " can't be saved in " << caller_instruction.products.at(i).original_string << '\n' << end();
       goto finish_reply;
     }
   }
@@ -51,14 +51,14 @@ case REPLY: {
     if (has_property(reply_inst.ingredients.at(i), "same-as-ingredient")) {
       vector<string> tmp = property(reply_inst.ingredients.at(i), "same-as-ingredient");
       if (SIZE(tmp) != 1) {
-        raise << maybe(current_recipe_name()) << "'same-as-ingredient' metadata should take exactly one value in " << reply_inst.to_string() << '\n' << end();
+        raise_error << maybe(current_recipe_name()) << "'same-as-ingredient' metadata should take exactly one value in " << reply_inst.to_string() << '\n' << end();
         goto finish_reply;
       }
       long long int ingredient_index = to_integer(tmp.at(0));
       if (ingredient_index >= SIZE(caller_instruction.ingredients))
-        raise << maybe(current_recipe_name()) << "'same-as-ingredient' metadata overflows ingredients in: " << caller_instruction.to_string() << '\n' << end();
+        raise_error << maybe(current_recipe_name()) << "'same-as-ingredient' metadata overflows ingredients in: " << caller_instruction.to_string() << '\n' << end();
       if (!is_dummy(caller_instruction.products.at(i)) && caller_instruction.products.at(i).value != caller_instruction.ingredients.at(ingredient_index).value)
-        raise << maybe(current_recipe_name()) << "'same-as-ingredient' product from call to " << callee << " must be " << caller_instruction.ingredients.at(ingredient_index).original_string << " rather than " << caller_instruction.products.at(i).original_string << '\n' << end();
+        raise_error << maybe(current_recipe_name()) << "'same-as-ingredient' product from call to " << callee << " must be " << caller_instruction.ingredients.at(ingredient_index).original_string << " rather than " << caller_instruction.products.at(i).original_string << '\n' << end();
     }
   }
   // End Reply
@@ -81,7 +81,7 @@ recipe f [
 +mem: storing 35 in location 4
 
 :(scenario reply_type_mismatch)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   3:number <- f 2
 ]
@@ -91,7 +91,7 @@ recipe f [
   14:point <- copy 12:point/raw
   reply 14:point
 ]
-+warn: f: reply ingredient 14:point can't be saved in 3:number
++error: f: reply ingredient 14:point can't be saved in 3:number
 
 //: In mu we'd like to assume that any instruction doesn't modify its
 //: ingredients unless they're also products. The /same-as-ingredient inside
@@ -99,7 +99,7 @@ recipe f [
 //: 'ingredient-products' (sometimes called in-out parameters in other languages).
 
 :(scenario reply_same_as_ingredient)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number <- copy 0
   2:number <- test1 1:number  # call with different ingredient and product
@@ -108,10 +108,10 @@ recipe test1 [
   10:number <- next-ingredient
   reply 10:number/same-as-ingredient:0
 ]
-+warn: main: 'same-as-ingredient' product from call to test1 must be 1:number rather than 2:number
++error: main: 'same-as-ingredient' product from call to test1 must be 1:number rather than 2:number
 
 :(scenario reply_same_as_ingredient_dummy)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number <- copy 0
   _ <- test1 1:number  # call with different ingredient and product
@@ -120,7 +120,7 @@ recipe test1 [
   10:address:number <- next-ingredient
   reply 10:address:number/same-as-ingredient:0
 ]
-$warn: 0
+$error: 0
 
 :(code)
 string to_string(const vector<double>& in) {
@@ -182,7 +182,7 @@ if (curr.name == "reply-if") {
     curr.ingredients.swap(results);
   }
   else {
-    raise << "'reply-if' never yields any products\n" << end();
+    raise_error << "'reply-if' never yields any products\n" << end();
   }
 }
 // rewrite `reply-unless a, b, c, ...` to
@@ -205,6 +205,6 @@ if (curr.name == "reply-unless") {
     curr.ingredients.swap(results);
   }
   else {
-    raise << "'reply-unless' never yields any products\n" << end();
+    raise_error << "'reply-unless' never yields any products\n" << end();
   }
 }
diff --git a/037recipe.cc b/037recipe.cc
index c3f258da..f7dd62d7 100644
--- a/037recipe.cc
+++ b/037recipe.cc
@@ -43,11 +43,11 @@ Recipe_ordinal["call"] = CALL;
 :(before "End Primitive Recipe Checks")
 case CALL: {
   if (inst.ingredients.empty()) {
-    raise << maybe(Recipe[r].name) << "'call' requires at least one ingredient (the recipe to call)\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'call' requires at least one ingredient (the recipe to call)\n" << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'call' should be a recipe, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'call' should be a recipe, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
diff --git a/038scheduler.cc b/038scheduler.cc
index 8f2706ff..ac16db22 100644
--- a/038scheduler.cc
+++ b/038scheduler.cc
@@ -147,11 +147,11 @@ Recipe_ordinal["start-running"] = START_RUNNING;
 :(before "End Primitive Recipe Checks")
 case START_RUNNING: {
   if (inst.ingredients.empty()) {
-    raise << maybe(Recipe[r].name) << "'start-running' requires at least one ingredient: the recipe to start running\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'start-running' requires at least one ingredient: the recipe to start running\n" << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'start-running' should be a recipe, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'start-running' should be a recipe, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -258,7 +258,7 @@ recipe f1 [
 //:: Errors in a routine cause it to terminate.
 
 :(scenario scheduler_terminates_routines_after_errors)
-% Hide_warnings = true;
+% Hide_errors = true;
 % Scheduling_interval = 2;
 recipe f1 [
   start-running f2:recipe
@@ -271,11 +271,11 @@ recipe f2 [
   4:number <- divide-with-remainder 4, 0
 ]
 # f2 should stop after first divide by 0
-+warn: f2: divide by zero in '3:number <- divide-with-remainder 4, 0'
--warn: f2: divide by zero in '4:number <- divide-with-remainder 4, 0'
++error: f2: divide by zero in '3:number <- divide-with-remainder 4, 0'
+-error: f2: divide by zero in '4:number <- divide-with-remainder 4, 0'
 
 :(after "operator<<(ostream& os, unused end)")
-  if (Trace_stream && Trace_stream->curr_label == "warn" && Current_routine) {
+  if (Trace_stream && Trace_stream->curr_label == "error" && Current_routine) {
     Current_routine->state = COMPLETED;
   }
 
@@ -333,11 +333,11 @@ Recipe_ordinal["routine-state"] = ROUTINE_STATE;
 :(before "End Primitive Recipe Checks")
 case ROUTINE_STATE: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'routine-state' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'routine-state' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'routine-state' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'routine-state' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -366,11 +366,11 @@ Recipe_ordinal["restart"] = RESTART;
 :(before "End Primitive Recipe Checks")
 case RESTART: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'restart' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'restart' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'restart' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'restart' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -394,11 +394,11 @@ Recipe_ordinal["stop"] = STOP;
 :(before "End Primitive Recipe Checks")
 case STOP: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'stop' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'stop' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'stop' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'stop' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -476,15 +476,15 @@ Recipe_ordinal["limit-time"] = LIMIT_TIME;
 :(before "End Primitive Recipe Checks")
 case LIMIT_TIME: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'limit-time' requires exactly two ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'limit-time' requires exactly two ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'limit-time' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'limit-time' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "second ingredient of 'limit-time' should be a number (of instructions to run for), but got " << inst.ingredients.at(1).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "second ingredient of 'limit-time' should be a number (of instructions to run for), but got " << inst.ingredients.at(1).original_string << '\n' << end();
     break;
   }
   break;
diff --git a/039wait.cc b/039wait.cc
index b21f92ca..314ab1cd 100644
--- a/039wait.cc
+++ b/039wait.cc
@@ -95,11 +95,11 @@ Recipe_ordinal["wait-for-routine"] = WAIT_FOR_ROUTINE;
 :(before "End Primitive Recipe Checks")
 case WAIT_FOR_ROUTINE: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'wait-for-routine' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'wait-for-routine' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
-  if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'wait-for-routine' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'wait-for-routine' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -107,7 +107,7 @@ case WAIT_FOR_ROUTINE: {
 :(before "End Primitive Recipe Implementations")
 case WAIT_FOR_ROUTINE: {
   if (ingredients.at(0).at(0) == Current_routine->id) {
-    raise << maybe(current_recipe_name()) << "routine can't wait for itself! " << current_instruction().to_string() << '\n' << end();
+    raise_error << maybe(current_recipe_name()) << "routine can't wait for itself! " << current_instruction().to_string() << '\n' << end();
     break;
   }
   Current_routine->state = WAITING;
diff --git a/040brace.cc b/040brace.cc
index d5e89a6c..ba8e9fd6 100644
--- a/040brace.cc
+++ b/040brace.cc
@@ -76,7 +76,7 @@ void transform_braces(const recipe_ordinal r) {
     // check for errors
     if (inst.name.find("-if") != string::npos || inst.name.find("-unless") != string::npos) {
       if (inst.ingredients.empty()) {
-        raise << inst.name << " expects 1 or 2 ingredients, but got none\n" << end();
+        raise_error << inst.name << " expects 1 or 2 ingredients, but got none\n" << end();
         continue;
       }
     }
@@ -107,7 +107,7 @@ void transform_braces(const recipe_ordinal r) {
     target.types.push_back(Type_ordinal["offset"]);
     target.set_value(0);
     if (open_braces.empty())
-      raise << inst.name << " needs a '{' before\n" << end();
+      raise_error << inst.name << " needs a '{' before\n" << end();
     else if (inst.name.find("loop") != string::npos)
       target.set_value(open_braces.top()-index);
     else  // break instruction
@@ -132,7 +132,7 @@ long long int matching_brace(long long int index, const list<pair<int, long long
     stacksize += (p->first ? 1 : -1);
     if (stacksize == 0) return p->second;
   }
-  raise << maybe(Recipe[r].name) << "unbalanced '{'\n" << end();
+  raise_error << maybe(Recipe[r].name) << "unbalanced '{'\n" << end();
   return SIZE(Recipe[r].steps);  // exit current routine
 }
 
@@ -279,7 +279,7 @@ recipe main [
 +after-brace: jump 2:offset
 
 :(scenario break_label)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number <- copy 0
   {
@@ -361,18 +361,18 @@ recipe test-factorial [
 ]
 +mem: location 2 is 120
 
-:(scenario break_outside_braces_warns)
-% Hide_warnings = true;
+:(scenario break_outside_braces_fails)
+% Hide_errors = true;
 recipe main [
   break
 ]
-+warn: break needs a '{' before
++error: break needs a '{' before
 
-:(scenario break_conditional_without_ingredient_warns)
-% Hide_warnings = true;
+:(scenario break_conditional_without_ingredient_fails)
+% Hide_errors = true;
 recipe main [
   {
     break-if
   }
 ]
-+warn: break-if expects 1 or 2 ingredients, but got none
++error: break-if expects 1 or 2 ingredients, but got none
diff --git a/041jump_target.cc b/041jump_target.cc
index fad1e3c7..7207c799 100644
--- a/041jump_target.cc
+++ b/041jump_target.cc
@@ -31,7 +31,7 @@ void transform_labels(const recipe_ordinal r) {
         offset[inst.label] = i;
       }
       else {
-        raise << maybe(Recipe[r].name) << "duplicate label '" << inst.label << "'" << end();
+        raise_error << maybe(Recipe[r].name) << "duplicate label '" << inst.label << "'" << end();
         // have all jumps skip some random but noticeable and deterministic amount of code
         offset[inst.label] = 9999;
       }
@@ -60,19 +60,19 @@ void transform_labels(const recipe_ordinal r) {
 :(code)
 void replace_offset(reagent& x, /*const*/ map<string, long long int>& offset, const long long int current_offset, const recipe_ordinal r) {
   if (!is_literal(x)) {
-    raise << maybe(Recipe[r].name) << "jump target must be offset or label but is " << x.original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "jump target must be offset or label but is " << x.original_string << '\n' << end();
     x.set_value(0);  // no jump by default
     return;
   }
   assert(!x.initialized);
   if (is_integer(x.name)) return;  // non-labels will be handled like other number operands
   if (!is_jump_target(x.name)) {
-    raise << maybe(Recipe[r].name) << "can't jump to label " << x.name << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "can't jump to label " << x.name << '\n' << end();
     x.set_value(0);  // no jump by default
     return;
   }
   if (offset.find(x.name) == offset.end()) {
-    raise << maybe(Recipe[r].name) << "can't find label " << x.name << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "can't find label " << x.name << '\n' << end();
     x.set_value(0);  // no jump by default
     return;
   }
@@ -133,18 +133,18 @@ recipe main [
 +mem: storing 0 in location 5
 -mem: storing 0 in location 4
 
-:(scenario recipe_warns_on_duplicate_jump_target)
-% Hide_warnings = true;
+:(scenario recipe_fails_on_duplicate_jump_target)
+% Hide_errors = true;
 recipe main [
   +label
   1:number <- copy 0
   +label
   2:number <- copy 0
 ]
-+warn: main: duplicate label '+label'
++error: main: duplicate label '+label'
 
 :(scenario jump_ignores_nontarget_label)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   # first a few lines of padding to exercise the offset computation
   1:number <- copy 0
@@ -155,4 +155,4 @@ recipe main [
   $target
   5:number <- copy 0
 ]
-+warn: main: can't jump to label $target
++error: main: can't jump to label $target
diff --git a/042name.cc b/042name.cc
index fbc83abd..99fda76c 100644
--- a/042name.cc
+++ b/042name.cc
@@ -10,12 +10,13 @@ recipe main [
 +mem: storing 0 in location 1
 
 :(scenarios transform)
-:(scenario transform_names_warns)
-% Hide_warnings = true;
+:(scenario transform_names_fails_on_use_before_define)
+% Hide_errors = true;
 recipe main [
   x:number <- copy y:number
 ]
-+warn: main: use before set: y
++error: main: use before set: y
+# todo: detect conditional defines
 
 :(after "int main")
   Transform.push_back(transform_names);
@@ -44,7 +45,7 @@ void transform_names(const recipe_ordinal r) {
       if (is_named_location(inst.ingredients.at(in))) names_used = true;
       if (disqualified(inst.ingredients.at(in), inst, Recipe[r].name)) continue;
       if (!already_transformed(inst.ingredients.at(in), names)) {
-        raise << maybe(Recipe[r].name) << "use before set: " << inst.ingredients.at(in).name << '\n' << end();
+        raise_error << maybe(Recipe[r].name) << "use before set: " << inst.ingredients.at(in).name << '\n' << end();
       }
       inst.ingredients.at(in).set_value(lookup_name(inst.ingredients.at(in), r));
     }
@@ -61,12 +62,12 @@ void transform_names(const recipe_ordinal r) {
     }
   }
   if (names_used && numeric_locations_used)
-    raise << maybe(Recipe[r].name) << "mixing variable names and numeric addresses\n" << end();
+    raise_error << maybe(Recipe[r].name) << "mixing variable names and numeric addresses\n" << end();
 }
 
 bool disqualified(/*mutable*/ reagent& x, const instruction& inst, const string& recipe_name) {
   if (x.types.empty()) {
-    raise << maybe(recipe_name) << "missing type for " << x.original_string << " in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(recipe_name) << "missing type for " << x.original_string << " in '" << inst.to_string() << "'\n" << end();
     return true;
   }
   if (is_raw(x)) return true;
@@ -89,7 +90,7 @@ type_ordinal skip_addresses(const vector<type_ordinal>& types, const string& rec
   for (long long int i = 0; i < SIZE(types); ++i) {
     if (types.at(i) != Type_ordinal["address"]) return types.at(i);
   }
-  raise << maybe(recipe_name) << "expected a container" << '\n' << end();
+  raise_error << maybe(recipe_name) << "expected a container" << '\n' << end();
   return -1;
 }
 
@@ -98,7 +99,7 @@ int find_element_name(const type_ordinal t, const string& name, const string& re
   for (long long int i = 0; i < SIZE(container.element_names); ++i) {
     if (container.element_names.at(i) == name) return i;
   }
-  raise << maybe(recipe_name) << "unknown element " << name << " in container " << Type[t].name << '\n' << end();
+  raise_error << maybe(recipe_name) << "unknown element " << name << " in container " << Type[t].name << '\n' << end();
   return -1;
 }
 
@@ -134,44 +135,44 @@ recipe main [
 //: an escape hatch to suppress name conversion that we'll use later
 :(scenarios run)
 :(scenario transform_names_passes_raw)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   x:number/raw <- copy 0
 ]
 -name: assign x 1
-+warn: can't write to location 0 in 'x:number/raw <- copy 0'
++error: can't write to location 0 in 'x:number/raw <- copy 0'
 
 :(scenarios transform)
-:(scenario transform_names_warns_when_mixing_names_and_numeric_locations)
-% Hide_warnings = true;
+:(scenario transform_names_fails_when_mixing_names_and_numeric_locations)
+% Hide_errors = true;
 recipe main [
   x:number <- copy 1:number
 ]
-+warn: main: mixing variable names and numeric addresses
++error: main: mixing variable names and numeric addresses
 
-:(scenario transform_names_warns_when_mixing_names_and_numeric_locations_2)
-% Hide_warnings = true;
+:(scenario transform_names_fails_when_mixing_names_and_numeric_locations_2)
+% Hide_errors = true;
 recipe main [
   x:number <- copy 1
   1:number <- copy x:number
 ]
-+warn: main: mixing variable names and numeric addresses
++error: main: mixing variable names and numeric addresses
 
-:(scenario transform_names_does_not_warn_when_mixing_names_and_raw_locations)
-% Hide_warnings = true;
+:(scenario transform_names_does_not_fail_when_mixing_names_and_raw_locations)
+% Hide_errors = true;
 recipe main [
   x:number <- copy 1:number/raw
 ]
--warn: main: mixing variable names and numeric addresses
-$warn: 0
+-error: main: mixing variable names and numeric addresses
+$error: 0
 
-:(scenario transform_names_does_not_warn_when_mixing_names_and_literals)
-% Hide_warnings = true;
+:(scenario transform_names_does_not_fail_when_mixing_names_and_literals)
+% Hide_errors = true;
 recipe main [
   x:number <- copy 1
 ]
--warn: main: mixing variable names and numeric addresses
-$warn: 0
+-error: main: mixing variable names and numeric addresses
+$error: 0
 
 //:: Support element names for containers in 'get' and 'get-address'.
 
@@ -193,11 +194,11 @@ recipe main [
 if (inst.operation == Recipe_ordinal["get"]
     || inst.operation == Recipe_ordinal["get-address"]) {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "exactly 2 ingredients expected in '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "exactly 2 ingredients expected in '" << inst.to_string() << "'\n" << end();
     break;
   }
   if (!is_literal(inst.ingredients.at(1)))
-    raise << maybe(Recipe[r].name) << "expected ingredient 1 of " << (inst.operation == Recipe_ordinal["get"] ? "'get'" : "'get-address'") << " to have type 'offset'; got " << inst.ingredients.at(1).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "expected ingredient 1 of " << (inst.operation == Recipe_ordinal["get"] ? "'get'" : "'get-address'") << " to have type 'offset'; got " << inst.ingredients.at(1).original_string << '\n' << end();
   if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) {
     // since first non-address in base type must be a container, we don't have to canonize
     type_ordinal base_type = skip_addresses(inst.ingredients.at(0).types, Recipe[r].name);
@@ -233,7 +234,7 @@ recipe main [
 // convert variant names of exclusive containers
 if (inst.operation == Recipe_ordinal["maybe-convert"]) {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "exactly 2 ingredients expected in '" << current_instruction().to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "exactly 2 ingredients expected in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   assert(is_literal(inst.ingredients.at(1)));
diff --git a/043new.cc b/043new.cc
index 6bd733d1..f821fad3 100644
--- a/043new.cc
+++ b/043new.cc
@@ -34,13 +34,13 @@ if (inst.operation == Recipe_ordinal["new"]) {
   // End NEW Transform Special-cases
   // first arg must be of type 'type'
   if (inst.ingredients.empty())
-    raise << maybe(Recipe[r].name) << "'new' expects one or two ingredients\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'new' expects one or two ingredients\n" << end();
   if (inst.ingredients.at(0).properties.empty()
       || inst.ingredients.at(0).properties.at(0).second.empty()
       || inst.ingredients.at(0).properties.at(0).second.at(0) != "type")
-    raise << maybe(Recipe[r].name) << "first ingredient of 'new' should be a type, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'new' should be a type, but got " << inst.ingredients.at(0).original_string << '\n' << end();
   if (Type_ordinal.find(inst.ingredients.at(0).name) == Type_ordinal.end())
-    raise << maybe(Recipe[r].name) << "unknown type " << inst.ingredients.at(0).name << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "unknown type " << inst.ingredients.at(0).name << '\n' << end();
   inst.ingredients.at(0).set_value(Type_ordinal[inst.ingredients.at(0).name]);
   trace(Primitive_recipe_depth, "new") << inst.ingredients.at(0).name << " -> " << inst.ingredients.at(0).name << end();
   end_new_transform:;
@@ -56,12 +56,12 @@ Recipe_ordinal["new"] = NEW;
 :(before "End Primitive Recipe Checks")
 case NEW: {
   if (inst.ingredients.empty() || SIZE(inst.ingredients) > 2) {
-    raise << maybe(Recipe[r].name) << "'new' requires one or two ingredients, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'new' requires one or two ingredients, but got " << inst.to_string() << '\n' << end();
     break;
   }
   reagent type = inst.ingredients.at(0);
   if (!is_mu_scalar(type) && !is_literal(type)) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'new' should be a type, but got " << type.original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'new' should be a type, but got " << type.original_string << '\n' << end();
     break;
   }
   break;
@@ -221,13 +221,13 @@ Recipe_ordinal["abandon"] = ABANDON;
 :(before "End Primitive Recipe Checks")
 case ABANDON: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'abandon' requires one ingredient, but got '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'abandon' requires one ingredient, but got '" << inst.to_string() << "'\n" << end();
     break;
   }
   reagent types = inst.ingredients.at(0);
   canonize_type(types);
   if (types.types.empty() || types.types.at(0) != Type_ordinal["address"]) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'abandon' should be an address, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'abandon' should be an address, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -260,7 +260,7 @@ if (Free_list[size]) {
   Free_list[size] = Memory[result];
   for (long long int curr = result+1; curr < result+size; ++curr) {
     if (Memory[curr] != 0) {
-      raise << maybe(current_recipe_name()) << "memory in free list was not zeroed out: " << curr << '/' << result << "; somebody wrote to us after free!!!\n" << end();
+      raise_error << maybe(current_recipe_name()) << "memory in free list was not zeroed out: " << curr << '/' << result << "; somebody wrote to us after free!!!\n" << end();
       break;  // always fatal
     }
   }
diff --git a/044space.cc b/044space.cc
index 59b292a5..55e3cc23 100644
--- a/044space.cc
+++ b/044space.cc
@@ -52,7 +52,7 @@ reagent absolutize(reagent x) {
   if (is_raw(x) || is_dummy(x)) return x;
   if (x.name == "default-space") return x;
   if (!x.initialized) {
-    raise << current_instruction().to_string() << ": reagent not initialized: " << x.original_string << '\n' << end();
+    raise_error << current_instruction().to_string() << ": reagent not initialized: " << x.original_string << '\n' << end();
     return x;
   }
   reagent r = x;
@@ -133,12 +133,12 @@ if (curr.name == "new-default-space") {
     vector<double> result;
     result.push_back(Name[Recipe_ordinal[current_recipe_name()]][""]);
     if (result.back() == 0)
-      raise << "no space allocated for default-space in recipe " << current_recipe_name() << "; are you using names\n" << end();
+      raise_error << "no space allocated for default-space in recipe " << current_recipe_name() << "; are you using names\n" << end();
     return result;
   }
 :(after "void write_memory(reagent x, vector<double> data)")
   if (x.name == "number-of-locals") {
-    raise << maybe(current_recipe_name()) << "can't write to special name 'number-of-locals'\n" << end();
+    raise_error << maybe(current_recipe_name()) << "can't write to special name 'number-of-locals'\n" << end();
     return;
   }
 
@@ -185,11 +185,11 @@ void try_reclaim_locals() {
 void rewrite_default_space_instruction(instruction& curr) {
   curr.operation = Recipe_ordinal["new"];
   if (!curr.ingredients.empty())
-    raise << "new-default-space can't take any ingredients\n" << end();
+    raise_error << "new-default-space can't take any ingredients\n" << end();
   curr.ingredients.push_back(reagent("location:type"));
   curr.ingredients.push_back(reagent("number-of-locals:literal"));
   if (!curr.products.empty())
-    raise << "new-default-space can't take any results\n" << end();
+    raise_error << "new-default-space can't take any results\n" << end();
   curr.products.push_back(reagent("default-space:address:array:location"));
 }
 
@@ -204,7 +204,7 @@ long long int address(long long int offset, long long int base) {
   if (base == 0) return offset;  // raw
   if (offset >= static_cast<long long int>(Memory[base])) {
     // todo: test
-    raise << "location " << offset << " is out of bounds " << no_scientific(Memory[base]) << " at " << base << '\n' << end();
+    raise_error << "location " << offset << " is out of bounds " << no_scientific(Memory[base]) << " at " << base << '\n' << end();
   }
   return base+1 + offset;
 }
@@ -212,7 +212,7 @@ long long int address(long long int offset, long long int base) {
 :(after "void write_memory(reagent x, vector<double> data)")
   if (x.name == "default-space") {
     if (!scalar(data))
-      raise << maybe(current_recipe_name()) << "'default-space' should be of type address:array:location, but tried to write " << to_string(data) << '\n' << end();
+      raise_error << maybe(current_recipe_name()) << "'default-space' should be of type address:array:location, but tried to write " << to_string(data) << '\n' << end();
     Current_routine->calls.front().default_space = data.at(0);
     return;
   }
diff --git a/045space_surround.cc b/045space_surround.cc
index 94a717a0..a29ad67c 100644
--- a/045space_surround.cc
+++ b/045space_surround.cc
@@ -42,7 +42,7 @@ long long int space_index(const reagent& x) {
   for (long long int i = /*skip name:type*/1; i < SIZE(x.properties); ++i) {
     if (x.properties.at(i).first == "space") {
       if (SIZE(x.properties.at(i).second) != 1)
-        raise << maybe(current_recipe_name()) << "/space metadata should take exactly one value in " << x.original_string << '\n' << end();
+        raise_error << maybe(current_recipe_name()) << "/space metadata should take exactly one value in " << x.original_string << '\n' << end();
       return to_integer(x.properties.at(i).second.at(0));
     }
   }
diff --git a/046closure_name.cc b/046closure_name.cc
index eef4753f..82ba5db3 100644
--- a/046closure_name.cc
+++ b/046closure_name.cc
@@ -50,19 +50,19 @@ void collect_surrounding_spaces(const recipe_ordinal r) {
           || inst.products.at(j).types.at(0) != Type_ordinal["address"]
           || inst.products.at(j).types.at(1) != Type_ordinal["array"]
           || inst.products.at(j).types.at(2) != Type_ordinal["location"]) {
-        raise << "slot 0 should always have type address:array:location, but is " << inst.products.at(j).to_string() << '\n' << end();
+        raise_error << "slot 0 should always have type address:array:location, but is " << inst.products.at(j).to_string() << '\n' << end();
         continue;
       }
       vector<string> s = property(inst.products.at(j), "names");
       if (s.empty()) {
-        raise << "slot 0 requires a /names property in recipe " << Recipe[r].name << end();
+        raise_error << "slot 0 requires a /names property in recipe " << Recipe[r].name << end();
         continue;
       }
-      if (SIZE(s) > 1) raise << "slot 0 should have a single value in /names, but got " << inst.products.at(j).to_string() << '\n' << end();
+      if (SIZE(s) > 1) raise_error << "slot 0 should have a single value in /names, but got " << inst.products.at(j).to_string() << '\n' << end();
       string surrounding_recipe_name = s.at(0);
       if (Surrounding_space.find(r) != Surrounding_space.end()
           && Surrounding_space[r] != Recipe_ordinal[surrounding_recipe_name]) {
-        raise << "recipe " << Recipe[r].name << " can have only one 'surrounding' recipe but has " << Recipe[Surrounding_space[r]].name << " and " << surrounding_recipe_name << '\n' << end();
+        raise_error << "recipe " << Recipe[r].name << " can have only one 'surrounding' recipe but has " << Recipe[Surrounding_space[r]].name << " and " << surrounding_recipe_name << '\n' << end();
         continue;
       }
       trace("name") << "recipe " << Recipe[r].name << " is surrounded by " << surrounding_recipe_name << end();
@@ -77,11 +77,11 @@ void collect_surrounding_spaces(const recipe_ordinal r) {
 :(replace{} "long long int lookup_name(const reagent& r, const recipe_ordinal default_recipe)")
 long long int lookup_name(const reagent& x, const recipe_ordinal default_recipe) {
   if (!has_property(x, "space")) {
-    if (Name[default_recipe].empty()) raise << "name not found: " << x.name << '\n' << end();
+    if (Name[default_recipe].empty()) raise_error << "name not found: " << x.name << '\n' << end();
     return Name[default_recipe][x.name];
   }
   vector<string> p = property(x, "space");
-  if (SIZE(p) != 1) raise << "/space property should have exactly one (non-negative integer) value\n" << end();
+  if (SIZE(p) != 1) raise_error << "/space property should have exactly one (non-negative integer) value\n" << end();
   long long int n = to_integer(p.at(0));
   assert(n >= 0);
   recipe_ordinal surrounding_recipe = lookup_surrounding_recipe(default_recipe, n);
@@ -95,11 +95,11 @@ long long int lookup_name(const reagent& x, const recipe_ordinal default_recipe)
 long long int lookup_name(const reagent& x, const recipe_ordinal r, set<recipe_ordinal>& done, vector<recipe_ordinal>& path) {
   if (!Name[r].empty()) return Name[r][x.name];
   if (done.find(r) != done.end()) {
-    raise << "can't compute address of " << x.to_string() << " because " << end();
+    raise_error << "can't compute address of " << x.to_string() << " because " << end();
     for (long long int i = 1; i < SIZE(path); ++i) {
-      raise << path.at(i-1) << " requires computing names of " << path.at(i) << '\n' << end();
+      raise_error << path.at(i-1) << " requires computing names of " << path.at(i) << '\n' << end();
     }
-    raise << path.at(SIZE(path)-1) << " requires computing names of " << r << "..ad infinitum\n" << end();
+    raise_error << path.at(SIZE(path)-1) << " requires computing names of " << r << "..ad infinitum\n" << end();
     return 0;
   }
   done.insert(r);
@@ -112,20 +112,20 @@ long long int lookup_name(const reagent& x, const recipe_ordinal r, set<recipe_o
 recipe_ordinal lookup_surrounding_recipe(const recipe_ordinal r, long long int n) {
   if (n == 0) return r;
   if (Surrounding_space.find(r) == Surrounding_space.end()) {
-    raise << "don't know surrounding recipe of " << Recipe[r].name << '\n' << end();
+    raise_error << "don't know surrounding recipe of " << Recipe[r].name << '\n' << end();
     return 0;
   }
   assert(Surrounding_space[r]);
   return lookup_surrounding_recipe(Surrounding_space[r], n-1);
 }
 
-//: weaken use-before-set warnings just a tad
+//: weaken use-before-set detection just a tad
 :(replace{} "bool already_transformed(const reagent& r, const map<string, long long int>& names)")
 bool already_transformed(const reagent& r, const map<string, long long int>& names) {
   if (has_property(r, "space")) {
     vector<string> p = property(r, "space");
     if (SIZE(p) != 1) {
-      raise << "/space property should have exactly one (non-negative integer) value in " << r.original_string << '\n' << end();
+      raise_error << "/space property should have exactly one (non-negative integer) value in " << r.original_string << '\n' << end();
       return false;
     }
     if (p.at(0) != "0") return true;
diff --git a/047global.cc b/047global.cc
index 00e8ff15..d0a3d47e 100644
--- a/047global.cc
+++ b/047global.cc
@@ -31,10 +31,15 @@ long long int global_space;
 global_space = 0;
 :(after "void write_memory(reagent x, vector<double> data)")
   if (x.name == "global-space") {
-    if (!scalar(data))
-      raise << maybe(current_recipe_name()) << "'global-space' should be of type address:array:location, but tried to write " << to_string(data) << '\n' << end();
+    if (!scalar(data)
+        || SIZE(x.types) != 3
+        || x.types.at(0) != Type_ordinal["address"]
+        || x.types.at(1) != Type_ordinal["array"]
+        || x.types.at(2) != Type_ordinal["location"]) {
+      raise_error << maybe(current_recipe_name()) << "'global-space' should be of type address:array:location, but tried to write " << to_string(data) << '\n' << end();
+    }
     if (Current_routine->global_space)
-      raise << "routine already has a global-space; you can't over-write your globals" << end();
+      raise_error << "routine already has a global-space; you can't over-write your globals" << end();
     Current_routine->global_space = data.at(0);
     return;
   }
@@ -43,7 +48,7 @@ global_space = 0;
 :(after "long long int space_base(const reagent& x)")
   if (is_global(x)) {
     if (!Current_routine->global_space)
-      raise << "routine has no global space\n" << end();
+      raise_error << "routine has no global space\n" << end();
     return Current_routine->global_space;
   }
 
@@ -51,14 +56,14 @@ global_space = 0;
 //: don't want to make them too comfortable to use.
 
 :(scenario global_space_with_names)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   global-space:address:array:location <- new location:type, 10
   x:number <- copy 23
   1:number/space:global <- copy 24
 ]
-# don't warn that we're mixing numeric addresses and names
-$warn: 0
+# don't complain about mixing numeric addresses and names
+$error: 0
 
 :(after "bool is_numeric_location(const reagent& x)")
   if (is_global(x)) return false;
diff --git a/048check_type_by_name.cc b/048check_type_by_name.cc
index cd113631..e96c54f2 100644
--- a/048check_type_by_name.cc
+++ b/048check_type_by_name.cc
@@ -6,13 +6,13 @@
 //: every single time. You can't use the same name with multiple types in a
 //: single recipe.
 
-:(scenario transform_warns_on_reusing_name_with_different_type)
-% Hide_warnings = true;
+:(scenario transform_fails_on_reusing_name_with_different_type)
+% Hide_errors = true;
 recipe main [
   x:number <- copy 1
   x:boolean <- copy 1
 ]
-+warn: main: x used with multiple types
++error: main: x used with multiple types
 
 :(after "int main")
   Transform.push_back(check_types_by_name);
@@ -38,11 +38,11 @@ void check_metadata(map<string, vector<type_ordinal> >& metadata, const reagent&
   if (is_raw(x)) return;
   // if you use raw locations you're probably doing something unsafe
   if (is_integer(x.name)) return;
-  if (x.types.empty()) return;  // will throw a more precise warning elsewhere
+  if (x.types.empty()) return;  // will throw a more precise error elsewhere
   if (metadata.find(x.name) == metadata.end())
     metadata[x.name] = x.types;
   if (metadata[x.name] != x.types)
-    raise << maybe(Recipe[r].name) << x.name << " used with multiple types\n" << end();
+    raise_error << maybe(Recipe[r].name) << x.name << " used with multiple types\n" << end();
 }
 
 :(scenario transform_fills_in_missing_types)
@@ -74,18 +74,18 @@ recipe main [
 ]
 +mem: storing 2 in location 1
 
-:(scenario transform_warns_on_missing_types_in_first_mention)
-% Hide_warnings = true;
+:(scenario transform_fails_on_missing_types_in_first_mention)
+% Hide_errors = true;
 recipe main [
   x <- copy 1
   x:number <- copy 2
 ]
-+warn: main: missing type for x in 'x <- copy 1'
++error: main: missing type for x in 'x <- copy 1'
 
-:(scenario typo_in_address_type_warns)
-% Hide_warnings = true;
+:(scenario typo_in_address_type_fails)
+% Hide_errors = true;
 recipe main [
   y:address:charcter <- new character:type
   *y <- copy 67
 ]
-+warn: unknown type: charcter
++error: unknown type: charcter
diff --git a/050scenario.cc b/050scenario.cc
index 5f92fec8..6dbd9f56 100644
--- a/050scenario.cc
+++ b/050scenario.cc
@@ -75,7 +75,7 @@ scenario parse_scenario(istream& in) {
   scenario result;
   result.name = next_word(in);
   if (Scenario_names.find(result.name) != Scenario_names.end())
-    raise << "duplicate scenario name: " << result.name << '\n' << end();
+    raise_error << "duplicate scenario name: " << result.name << '\n' << end();
   Scenario_names.insert(result.name);
   skip_whitespace_and_comments(in);
   assert(in.peek() == '[');
@@ -224,7 +224,7 @@ recipe main [
 +mem: storing 13 in location 1
 +mem: storing 13 in location 2
 
-//: 'memory-should-contain' raises warnings if specific locations aren't as expected
+//: 'memory-should-contain' raises errors if specific locations aren't as expected
 //: Also includes some special support for checking strings.
 
 :(before "End Globals")
@@ -234,14 +234,14 @@ Scenario_testing_scenario = false;
 
 :(scenario memory_check)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   memory-should-contain [
     1 <- 13
   ]
 ]
 +run: checking location 1
-+warn: expected location 1 to contain 13 but saw 0
++error: expected location 1 to contain 13 but saw 0
 
 :(before "End Primitive Recipe Declarations")
 MEMORY_SHOULD_CONTAIN,
@@ -277,16 +277,16 @@ void check_memory(const string& s) {
     skip_whitespace_and_comments(in);
     double value = 0;  in >> value;
     if (locations_checked.find(address) != locations_checked.end())
-      raise << "duplicate expectation for location " << address << '\n' << end();
+      raise_error << "duplicate expectation for location " << address << '\n' << end();
     trace(Primitive_recipe_depth, "run") << "checking location " << address << end();
     if (Memory[address] != value) {
       if (Current_scenario && !Scenario_testing_scenario) {
         // genuine test in a mu file
-        raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain " << no_scientific(value) << " but saw " << no_scientific(Memory[address]) << '\n' << end();
+        raise_error << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain " << no_scientific(value) << " but saw " << no_scientific(Memory[address]) << '\n' << end();
       }
       else {
         // just testing scenario support
-        raise << "expected location " << address << " to contain " << no_scientific(value) << " but saw " << no_scientific(Memory[address]) << '\n' << end();
+        raise_error << "expected location " << address << " to contain " << no_scientific(value) << " but saw " << no_scientific(Memory[address]) << '\n' << end();
       }
       if (!Scenario_testing_scenario) {
         Passed = false;
@@ -314,16 +314,16 @@ void check_type(const string& lhs, istream& in) {
     check_string(address, literal);
     return;
   }
-  raise << "don't know how to check memory for " << lhs << '\n' << end();
+  raise_error << "don't know how to check memory for " << lhs << '\n' << end();
 }
 
 void check_string(long long int address, const string& literal) {
   trace(Primitive_recipe_depth, "run") << "checking string length at " << address << end();
   if (Memory[address] != SIZE(literal)) {
     if (Current_scenario && !Scenario_testing_scenario)
-      raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << no_scientific(Memory[address]) << '\n' << end();
+      raise_error << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << no_scientific(Memory[address]) << '\n' << end();
     else
-      raise << "expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << no_scientific(Memory[address]) << '\n' << end();
+      raise_error << "expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << no_scientific(Memory[address]) << '\n' << end();
     if (!Scenario_testing_scenario) {
       Passed = false;
       ++Num_failures;
@@ -336,11 +336,11 @@ void check_string(long long int address, const string& literal) {
     if (Memory[address+i] != literal.at(i)) {
       if (Current_scenario && !Scenario_testing_scenario) {
         // genuine test in a mu file
-        raise << "\nF - " << Current_scenario->name << ": expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << no_scientific(Memory[address+i]) << '\n' << end();
+        raise_error << "\nF - " << Current_scenario->name << ": expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << no_scientific(Memory[address+i]) << '\n' << end();
       }
       else {
         // just testing scenario support
-        raise << "expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << no_scientific(Memory[address+i]) << '\n' << end();
+        raise_error << "expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << no_scientific(Memory[address+i]) << '\n' << end();
       }
       if (!Scenario_testing_scenario) {
         Passed = false;
@@ -353,18 +353,18 @@ void check_string(long long int address, const string& literal) {
 
 :(scenario memory_check_multiple)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   memory-should-contain [
     1 <- 0
     1 <- 0
   ]
 ]
-+warn: duplicate expectation for location 1
++error: duplicate expectation for location 1
 
 :(scenario memory_check_string_length)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number <- copy 3
   2:number <- copy 97  # 'a'
@@ -374,7 +374,7 @@ recipe main [
     1:string <- [ab]
   ]
 ]
-+warn: expected location 1 to contain length 2 of string [ab] but saw 3
++error: expected location 1 to contain length 2 of string [ab] but saw 3
 
 :(scenario memory_check_string)
 recipe main [
@@ -397,16 +397,16 @@ recipe main [
 // that the lines are present *and* in the specified sequence. (There can be
 // other lines in between.)
 
-:(scenario trace_check_warns_on_failure)
+:(scenario trace_check_fails)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   trace-should-contain [
     a: b
     a: d
   ]
 ]
-+warn: missing [b] in trace with label a
++error: missing [b] in trace with label a
 
 :(before "End Primitive Recipe Declarations")
 TRACE_SHOULD_CONTAIN,
@@ -424,7 +424,7 @@ case TRACE_SHOULD_CONTAIN: {
 }
 
 :(code)
-// simplified version of check_trace_contents() that emits warnings rather
+// simplified version of check_trace_contents() that emits errors rather
 // than just printing to stderr
 bool check_trace(const string& expected) {
   Trace_stream->newline();
@@ -441,8 +441,8 @@ bool check_trace(const string& expected) {
     }
   }
 
-  raise << "missing [" << expected_lines.at(curr_expected_line).contents << "] "
-        << "in trace with label " << expected_lines.at(curr_expected_line).label << '\n' << end();
+  raise_error << "missing [" << expected_lines.at(curr_expected_line).contents << "] "
+              << "in trace with label " << expected_lines.at(curr_expected_line).label << '\n' << end();
   Passed = false;
   return false;
 }
@@ -459,9 +459,9 @@ vector<trace_line> parse_trace(const string& expected) {
   return result;
 }
 
-:(scenario trace_check_warns_on_failure_in_later_line)
+:(scenario trace_check_fails_in_nonfirst_line)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   run [
     trace 1, [a], [b]
@@ -471,11 +471,11 @@ recipe main [
     a: d
   ]
 ]
-+warn: missing [d] in trace with label a
++error: missing [d] in trace with label a
 
 :(scenario trace_check_passes_silently)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   run [
     trace 1, [a], [b]
@@ -484,16 +484,16 @@ recipe main [
     a: b
   ]
 ]
--warn: missing [b] in trace with label a
-$warn: 0
+-error: missing [b] in trace with label a
+$error: 0
 
 //: 'trace-should-not-contain' is like the '-' lines in our scenarios so far
 //: Each trace line is separately checked for absense. Order is *not*
 //: important, so you can't say things like "B should not exist after A."
 
-:(scenario trace_negative_check_warns_on_failure)
+:(scenario trace_negative_check_fails)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   run [
     trace 1, [a], [b]
@@ -502,7 +502,7 @@ recipe main [
     a: b
   ]
 ]
-+warn: unexpected [b] in trace with label a
++error: unexpected [b] in trace with label a
 
 :(before "End Primitive Recipe Declarations")
 TRACE_SHOULD_NOT_CONTAIN,
@@ -520,14 +520,14 @@ case TRACE_SHOULD_NOT_CONTAIN: {
 }
 
 :(code)
-// simplified version of check_trace_contents() that emits warnings rather
+// simplified version of check_trace_contents() that emits errors rather
 // than just printing to stderr
 bool check_trace_missing(const string& in) {
   Trace_stream->newline();
   vector<trace_line> lines = parse_trace(in);
   for (long long int i = 0; i < SIZE(lines); ++i) {
     if (trace_count(lines.at(i).label, lines.at(i).contents) != 0) {
-      raise << "unexpected [" << lines.at(i).contents << "] in trace with label " << lines.at(i).label << '\n' << end();
+      raise_error << "unexpected [" << lines.at(i).contents << "] in trace with label " << lines.at(i).label << '\n' << end();
       Passed = false;
       return false;
     }
@@ -537,18 +537,18 @@ bool check_trace_missing(const string& in) {
 
 :(scenario trace_negative_check_passes_silently)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   trace-should-not-contain [
     a: b
   ]
 ]
--warn: unexpected [b] in trace with label a
-$warn: 0
+-error: unexpected [b] in trace with label a
+$error: 0
 
-:(scenario trace_negative_check_warns_on_any_unexpected_line)
+:(scenario trace_negative_check_fails_on_any_unexpected_line)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   run [
     trace 1, [a], [d]
@@ -558,7 +558,7 @@ recipe main [
     a: d
   ]
 ]
-+warn: unexpected [d] in trace with label a
++error: unexpected [d] in trace with label a
 
 :(scenario trace_count_check)
 recipe main [
@@ -575,15 +575,15 @@ Recipe_ordinal["check-trace-count-for-label"] = CHECK_TRACE_COUNT_FOR_LABEL;
 :(before "End Primitive Recipe Checks")
 case CHECK_TRACE_COUNT_FOR_LABEL: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'check-trace-for-label' requires exactly two ingredients, but got '" << inst.to_string() << "'\n" << end();
+    raise_error << maybe(Recipe[r].name) << "'check-trace-for-label' requires exactly two ingredients, but got '" << inst.to_string() << "'\n" << end();
     break;
   }
-  if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'check-trace-for-label' should be a number (count), but got " << inst.ingredients.at(0).original_string << '\n' << end();
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'check-trace-for-label' should be a number (count), but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   if (!is_literal_string(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "second ingredient of 'check-trace-for-label' should be a literal string (label), but got " << inst.ingredients.at(1).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "second ingredient of 'check-trace-for-label' should be a literal string (label), but got " << inst.ingredients.at(1).original_string << '\n' << end();
     break;
   }
   break;
@@ -597,13 +597,13 @@ case CHECK_TRACE_COUNT_FOR_LABEL: {
   if (count != expected_count) {
     if (Current_scenario && !Scenario_testing_scenario) {
       // genuine test in a mu file
-      raise << "\nF - " << Current_scenario->name << ": " << maybe(current_recipe_name()) << "expected " << expected_count << " lines in trace with label " << label << " in trace: ";
+      raise_error << "\nF - " << Current_scenario->name << ": " << maybe(current_recipe_name()) << "expected " << expected_count << " lines in trace with label " << label << " in trace: ";
       DUMP(label);
-      raise;
+      raise_error;
     }
     else {
       // just testing scenario support
-      raise << maybe(current_recipe_name()) << "expected " << expected_count << " lines in trace with label " << label << " in trace\n" << end();
+      raise_error << maybe(current_recipe_name()) << "expected " << expected_count << " lines in trace with label " << label << " in trace\n" << end();
     }
     if (!Scenario_testing_scenario) {
       Passed = false;
@@ -615,14 +615,14 @@ case CHECK_TRACE_COUNT_FOR_LABEL: {
 
 :(scenario trace_count_check_2)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   run [
     trace 1, [a], [foo]
   ]
   check-trace-count-for-label 2, [a]
 ]
-+warn: main: expected 2 lines in trace with label a in trace
++error: main: expected 2 lines in trace with label a in trace
 
 //: Minor detail: ignore 'system' calls in scenarios, since anything we do
 //: with them is by definition impossible to test through mu.
diff --git a/052tangle.cc b/052tangle.cc
index 84b96d7f..08c5f790 100644
--- a/052tangle.cc
+++ b/052tangle.cc
@@ -39,7 +39,7 @@ else if (command == "before") {
   if (is_waypoint(label))
     Before_fragments[label].steps.insert(Before_fragments[label].steps.end(), tmp.steps.begin(), tmp.steps.end());
   else
-    raise << "can't tangle before label " << label << '\n' << end();
+    raise_error << "can't tangle before label " << label << '\n' << end();
 }
 else if (command == "after") {
   string label = next_word(in);
@@ -47,7 +47,7 @@ else if (command == "after") {
   if (is_waypoint(label))
     After_fragments[label].steps.insert(After_fragments[label].steps.begin(), tmp.steps.begin(), tmp.steps.end());
   else
-    raise << "can't tangle after label " << label << '\n' << end();
+    raise_error << "can't tangle after label " << label << '\n' << end();
 }
 
 //: after all recipes are loaded, insert fragments at appropriate labels.
@@ -130,7 +130,7 @@ bool is_waypoint(string label) {
   return *label.begin() == '<' && *label.rbegin() == '>';
 }
 
-//: warn about unapplied fragments
+//: complain about unapplied fragments
 :(before "End Globals")
 bool Transform_check_insert_fragments_Ran = false;
 :(before "End One-time Setup")
@@ -141,11 +141,11 @@ void check_insert_fragments(unused recipe_ordinal) {
   Transform_check_insert_fragments_Ran = true;
   for (map<string, recipe>::iterator p = Before_fragments.begin(); p != Before_fragments.end(); ++p) {
     if (Fragments_used.find(p->first) == Fragments_used.end())
-      raise << "could not locate insert before " << p->first << '\n' << end();
+      raise_error << "could not locate insert before " << p->first << '\n' << end();
   }
   for (map<string, recipe>::iterator p = After_fragments.begin(); p != After_fragments.end(); ++p) {
     if (Fragments_used.find(p->first) == Fragments_used.end())
-      raise << "could not locate insert after " << p->first << '\n' << end();
+      raise_error << "could not locate insert after " << p->first << '\n' << end();
   }
 }
 
@@ -170,7 +170,7 @@ after <label1> [
 $mem: 4
 
 :(scenario tangle_ignores_jump_target)
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   1:number <- copy 0
   +label1
@@ -179,7 +179,7 @@ recipe main [
 before +label1 [
   2:number <- copy 0
 ]
-+warn: can't tangle before label +label1
++error: can't tangle before label +label1
 +mem: storing 0 in location 1
 +mem: storing 0 in location 4
 # label1
diff --git a/053continuation.cc b/053continuation.cc
index 18174cc1..c9a6a5cb 100644
--- a/053continuation.cc
+++ b/053continuation.cc
@@ -45,7 +45,7 @@ Recipe_ordinal["continue-from"] = CONTINUE_FROM;
 :(before "End Primitive Recipe Checks")
 case CONTINUE_FROM: {
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'continue-from' should be a continuation id generated by 'current-continuation', but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'continue-from' should be a continuation id generated by 'current-continuation', but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -214,7 +214,7 @@ case REPLY_DELIMITED_CONTINUATION: {
   call_stack::iterator find_reset(call_stack& c);  // manual prototype containing '::'
   call_stack::iterator reset = find_reset(Current_routine->calls);
   if (reset == Current_routine->calls.end()) {
-    raise << maybe(current_recipe_name()) << "couldn't find a 'reset' call to jump out to\n" << end();
+    raise_error << maybe(current_recipe_name()) << "couldn't find a 'reset' call to jump out to\n" << end();
     break;
   }
   Delimited_continuation[Next_delimited_continuation_id] = call_stack(Current_routine->calls.begin(), reset);
@@ -244,7 +244,7 @@ call_stack::iterator find_reset(call_stack& c) {
     // copy multiple calls on to current call stack
     assert(scalar(ingredients.at(0)));
     if (Delimited_continuation.find(ingredients.at(0).at(0)) == Delimited_continuation.end()) {
-      raise << maybe(current_recipe_name()) << "no such delimited continuation " << current_instruction().ingredients.at(0).original_string << '\n' << end();
+      raise_error << maybe(current_recipe_name()) << "no such delimited continuation " << current_instruction().ingredients.at(0).original_string << '\n' << end();
     }
     const call_stack& new_calls = Delimited_continuation[ingredients.at(0).at(0)];
     for (call_stack::const_reverse_iterator p = new_calls.rbegin(); p != new_calls.rend(); ++p)
diff --git a/064random.cc b/064random.cc
index de304714..12e1b848 100644
--- a/064random.cc
+++ b/064random.cc
@@ -36,11 +36,11 @@ Recipe_ordinal["round"] = ROUND;
 :(before "End Primitive Recipe Checks")
 case ROUND: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'round' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'round' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
-  if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'round' should be a number, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'round' should be a number, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
diff --git a/070display.cc b/070display.cc
index 8859a909..49668a99 100644
--- a/070display.cc
+++ b/070display.cc
@@ -22,9 +22,9 @@ case OPEN_CONSOLE: {
   long long int height = tb_height();
   if (width > 222 || height > 222) tb_shutdown();
   if (width > 222)
-    raise << "sorry, mu doesn't support windows wider than 222 characters. Please resize your window.\n" << end();
+    raise_error << "sorry, mu doesn't support windows wider than 222 characters. Please resize your window.\n" << end();
   if (height > 222)
-    raise << "sorry, mu doesn't support windows taller than 222 characters. Please resize your window.\n" << end();
+    raise_error << "sorry, mu doesn't support windows taller than 222 characters. Please resize your window.\n" << end();
   break;
 }
 
@@ -99,6 +99,26 @@ PRINT_CHARACTER_TO_DISPLAY,
 Recipe_ordinal["print-character-to-display"] = PRINT_CHARACTER_TO_DISPLAY;
 :(before "End Primitive Recipe Checks")
 case PRINT_CHARACTER_TO_DISPLAY: {
+  if (inst.ingredients.empty()) {
+    raise_error << maybe(Recipe[r].name) << "'print-character-to-display' requires at least one ingredient, but got " << inst.to_string() << '\n' << end();
+    break;
+  }
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'print-character-to-display' should be a character, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    break;
+  }
+  if (SIZE(inst.ingredients) > 1) {
+    if (!is_mu_number(inst.ingredients.at(1))) {
+      raise_error << maybe(Recipe[r].name) << "second ingredient of 'print-character-to-display' should be a foreground color number, but got " << inst.ingredients.at(1).original_string << '\n' << end();
+      break;
+    }
+  }
+  if (SIZE(inst.ingredients) > 2) {
+    if (!is_mu_number(inst.ingredients.at(2))) {
+      raise_error << maybe(Recipe[r].name) << "third ingredient of 'print-character-to-display' should be a background color number, but got " << inst.ingredients.at(2).original_string << '\n' << end();
+      break;
+    }
+  }
   break;
 }
 :(before "End Primitive Recipe Implementations")
@@ -106,29 +126,13 @@ case PRINT_CHARACTER_TO_DISPLAY: {
   int h=tb_height(), w=tb_width();
   long long int height = (h >= 0) ? h : 0;
   long long int width = (w >= 0) ? w : 0;
-  if (ingredients.empty()) {
-    raise << maybe(current_recipe_name()) << "'print-character-to-display' requires at least one ingredient, but got " << current_instruction().to_string() << '\n' << end();
-    break;
-  }
-  if (!scalar(ingredients.at(0))) {
-    raise << maybe(current_recipe_name()) << "first ingredient of 'print-character-to-display' should be a character, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end();
-    break;
-  }
   long long int c = ingredients.at(0).at(0);
   int color = TB_BLACK;
   if (SIZE(ingredients) > 1) {
-    if (!scalar(ingredients.at(1))) {
-      raise << maybe(current_recipe_name()) << "second ingredient of 'print-character-to-display' should be a foreground color number, but got " << current_instruction().ingredients.at(1).original_string << '\n' << end();
-      break;
-    }
     color = ingredients.at(1).at(0);
   }
   int bg_color = TB_BLACK;
   if (SIZE(ingredients) > 2) {
-    if (!scalar(ingredients.at(2))) {
-      raise << maybe(current_recipe_name()) << "third ingredient of 'print-character-to-display' should be a background color number, but got " << current_instruction().ingredients.at(2).original_string << '\n' << end();
-      break;
-    }
     bg_color = ingredients.at(2).at(0);
     if (bg_color == 0) bg_color = TB_BLACK;
   }
@@ -182,15 +186,15 @@ Recipe_ordinal["move-cursor-on-display"] = MOVE_CURSOR_ON_DISPLAY;
 :(before "End Primitive Recipe Checks")
 case MOVE_CURSOR_ON_DISPLAY: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'move-cursor-on-display' requires two ingredients, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'move-cursor-on-display' requires two ingredients, but got " << inst.to_string() << '\n' << end();
     break;
   }
-  if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'move-cursor-on-display' should be a row number, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'move-cursor-on-display' should be a row number, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
-  if (!is_mu_scalar(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "second ingredient of 'move-cursor-on-display' should be a column number, but got " << inst.ingredients.at(1).original_string << '\n' << end();
+  if (!is_mu_number(inst.ingredients.at(1))) {
+    raise_error << maybe(Recipe[r].name) << "second ingredient of 'move-cursor-on-display' should be a column number, but got " << inst.ingredients.at(1).original_string << '\n' << end();
     break;
   }
   break;
diff --git a/072scenario_screen.cc b/072scenario_screen.cc
index 76900238..4ecbbd4d 100644
--- a/072scenario_screen.cc
+++ b/072scenario_screen.cc
@@ -68,7 +68,7 @@ scenario screen-in-scenario-color [
 
 :(scenario screen_in_scenario_error)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 scenario screen-in-scenario-error [
   assume-screen 5/width, 3/height
   run [
@@ -81,11 +81,11 @@ scenario screen-in-scenario-error [
     .     .
   ]
 ]
-+warn: expected screen location (0, 0) to contain 98 ('b') instead of 97 ('a')
++error: expected screen location (0, 0) to contain 98 ('b') instead of 97 ('a')
 
 :(scenario screen_in_scenario_color_error)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 # screen-should-contain can check unicode characters in the fake screen
 scenario screen-in-scenario-color [
   assume-screen 5/width, 3/height
@@ -99,21 +99,21 @@ scenario screen-in-scenario-color [
     .     .
   ]
 ]
-+warn: expected screen location (0, 0) to be in color 2 instead of 1
++error: expected screen location (0, 0) to be in color 2 instead of 1
 
 //: allow naming just for 'screen'
 :(before "End is_special_name Cases")
 if (s == "screen") return true;
 
 :(scenarios run)
-:(scenario convert_names_does_not_warn_when_mixing_special_names_and_numeric_locations)
+:(scenario convert_names_does_not_fail_when_mixing_special_names_and_numeric_locations)
 % Scenario_testing_scenario = true;
-% Hide_warnings = true;
+% Hide_errors = true;
 recipe main [
   screen:number <- copy 1:number
 ]
--warn: mixing variable names and numeric addresses in main
-$warn: 0
+-error: mixing variable names and numeric addresses in main
+$error: 0
 :(scenarios run_mu_scenario)
 
 :(before "End Globals")
@@ -227,11 +227,11 @@ void check_screen(const string& expected_contents, const int color) {
         // contents match but color is off
         if (Current_scenario && !Scenario_testing_scenario) {
           // genuine test in a mu file
-          raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ", address " << addr << ", value " << no_scientific(Memory[addr]) << ") to be in color " << color << " instead of " << no_scientific(Memory[addr+cell_color_offset]) << "\n" << end();
+          raise_error << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ", address " << addr << ", value " << no_scientific(Memory[addr]) << ") to be in color " << color << " instead of " << no_scientific(Memory[addr+cell_color_offset]) << "\n" << end();
         }
         else {
           // just testing check_screen
-          raise << "expected screen location (" << row << ", " << column << ") to be in color " << color << " instead of " << no_scientific(Memory[addr+cell_color_offset]) << '\n' << end();
+          raise_error << "expected screen location (" << row << ", " << column << ") to be in color " << color << " instead of " << no_scientific(Memory[addr+cell_color_offset]) << '\n' << end();
         }
         if (!Scenario_testing_scenario) {
           Passed = false;
@@ -241,7 +241,7 @@ void check_screen(const string& expected_contents, const int color) {
       }
 
       // really a mismatch
-      // can't print multi-byte unicode characters in warnings just yet. not very useful for debugging anyway.
+      // can't print multi-byte unicode characters in errors just yet. not very useful for debugging anyway.
       char expected_pretty[10] = {0};
       if (curr < 256 && !iscntrl(curr)) {
         // " ('<curr>')"
@@ -257,12 +257,12 @@ void check_screen(const string& expected_contents, const int color) {
       if (color != -1) color_phrase << " in color " << color;
       if (Current_scenario && !Scenario_testing_scenario) {
         // genuine test in a mu file
-        raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << color_phrase.str() << " instead of " << no_scientific(Memory[addr]) << actual_pretty << '\n' << end();
+        raise_error << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << color_phrase.str() << " instead of " << no_scientific(Memory[addr]) << actual_pretty << '\n' << end();
         dump_screen();
       }
       else {
         // just testing check_screen
-        raise << "expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << color_phrase.str() << " instead of " << no_scientific(Memory[addr]) << actual_pretty << '\n' << end();
+        raise_error << "expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << color_phrase.str() << " instead of " << no_scientific(Memory[addr]) << actual_pretty << '\n' << end();
       }
       if (!Scenario_testing_scenario) {
         Passed = false;
@@ -281,7 +281,7 @@ raw_string_stream::raw_string_stream(const string& backing) :index(0), max(SIZE(
 bool raw_string_stream::at_end() const {
   if (index >= max) return true;
   if (tb_utf8_char_length(buf[index]) > max-index) {
-    raise << "unicode string seems corrupted at index "<< index << " character " << static_cast<int>(buf[index]) << '\n' << end();
+    raise_error << "unicode string seems corrupted at index "<< index << " character " << static_cast<int>(buf[index]) << '\n' << end();
     return true;
   }
   return false;
diff --git a/075scenario_console.cc b/075scenario_console.cc
index 44bed831..ab6aba94 100644
--- a/075scenario_console.cc
+++ b/075scenario_console.cc
@@ -74,7 +74,7 @@ case ASSUME_CONSOLE: {
       else if (Key.find(key) != Key.end())
         Memory[Current_routine->alloc+1] = Key[key];
       else
-        raise << "assume-console: can't press " << key << '\n' << end();
+        raise_error << "assume-console: can't press " << key << '\n' << end();
       if (Memory[Current_routine->alloc+1] < 256)
         // these keys are in ascii
         Memory[Current_routine->alloc] = /*tag for 'text' variant of 'event' exclusive-container*/0;
@@ -235,7 +235,7 @@ case REPLACE_IN_CONSOLE: {
 case REPLACE_IN_CONSOLE: {
   assert(scalar(ingredients.at(0)));
   if (!Memory[CONSOLE]) {
-    raise << "console not initialized\n" << end();
+    raise_error << "console not initialized\n" << end();
     break;
   }
   long long int console_data = Memory[Memory[CONSOLE]+1];
diff --git a/081run_interactive.cc b/081run_interactive.cc
index 224998ec..7eb0cc8c 100644
--- a/081run_interactive.cc
+++ b/081run_interactive.cc
@@ -17,9 +17,9 @@ recipe main [
 # result is null
 +mem: storing 0 in location 1
 
-//: run code in 'interactive mode', i.e. with warnings off and return:
+//: run code in 'interactive mode', i.e. with errors+warnings off and return:
 //:   stringified output in case we want to print it to screen
-//:   any warnings encountered
+//:   any errors+warnings encountered
 //:   simulated screen any prints went to
 //:   any 'app' layer traces generated
 :(before "End Primitive Recipe Declarations")
@@ -29,11 +29,11 @@ Recipe_ordinal["run-interactive"] = RUN_INTERACTIVE;
 :(before "End Primitive Recipe Checks")
 case RUN_INTERACTIVE: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'run-interactive' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'run-interactive' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'run-interactive' should be a string, but got " << inst.ingredients.at(0).to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'run-interactive' should be a string, but got " << inst.ingredients.at(0).to_string() << '\n' << end();
     break;
   }
   break;
@@ -44,7 +44,7 @@ case RUN_INTERACTIVE: {
   if (!new_code_pushed_to_stack) {
     products.resize(5);
     products.at(0).push_back(0);
-    products.at(1).push_back(trace_contents("warn"));
+    products.at(1).push_back(trace_error_warning_contents());
     products.at(2).push_back(0);
     products.at(3).push_back(trace_contents("app"));
     products.at(4).push_back(1);  // completed
@@ -94,7 +94,7 @@ bool run_interactive(long long int address) {
        "]\n");
   transform_all();
   Current_routine = save_current_routine;
-  if (trace_count("warn") > 0) return false;
+  if (trace_count("error") > 0) return false;
   // now call 'sandbox' which will run 'interactive' in a separate routine,
   // and wait for it
   Current_routine->calls.push_front(call(Recipe_ordinal["sandbox"]));
@@ -104,17 +104,20 @@ bool run_interactive(long long int address) {
 void run_code_begin() {
   // stuff to undo later, in run_code_end()
   Hide_warnings = true;
+  Hide_errors = true;
   Disable_redefine_warnings = true;
   Save_trace_stream = Trace_stream;
   Save_trace_file = Trace_file;
   Trace_file = "";
   Trace_stream = new trace_stream;
+  Trace_stream->collect_layers.insert("error");
   Trace_stream->collect_layers.insert("warn");
   Trace_stream->collect_layers.insert("app");
 }
 
 void run_code_end() {
   Hide_warnings = false;
+  Hide_errors = false;
   Disable_redefine_warnings = false;
   delete Trace_stream;
   Trace_stream = Save_trace_stream;
@@ -136,7 +139,7 @@ load(string(
   "sandbox-state:number <- routine-state r/routine_id\n" +
   "completed?:boolean <- equal sandbox-state, 1/completed\n" +
   "output:address:array:character <- $most-recent-products\n" +
-  "warnings:address:array:character <- save-trace [warn]\n" +
+  "warnings:address:array:character <- save-errors-warnings\n" +
   "stashes:address:array:character <- save-trace [app]\n" +
   "$cleanup-run-interactive\n" +
   "reply output, warnings, screen, stashes, completed?\n" +
@@ -144,7 +147,7 @@ load(string(
 transform_all();
 recently_added_recipes.clear();
 
-//: adjust warnings in the sandbox
+//: adjust errors/warnings in the sandbox
 :(after "string maybe(string s)")
   if (s == "interactive") return "";
 
@@ -210,6 +213,21 @@ case _MOST_RECENT_PRODUCTS: {
 }
 
 :(before "End Primitive Recipe Declarations")
+SAVE_ERRORS_WARNINGS,
+:(before "End Primitive Recipe Numbers")
+Recipe_ordinal["save-errors-warnings"] = SAVE_ERRORS_WARNINGS;
+:(before "End Primitive Recipe Checks")
+case SAVE_ERRORS_WARNINGS: {
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case SAVE_ERRORS_WARNINGS: {
+  products.resize(1);
+  products.at(0).push_back(trace_error_warning_contents());
+  break;
+}
+
+:(before "End Primitive Recipe Declarations")
 SAVE_TRACE,
 :(before "End Primitive Recipe Numbers")
 Recipe_ordinal["save-trace"] = SAVE_TRACE;
@@ -263,15 +281,15 @@ recipe main [
 +mem: storing 97 in location 11
 +mem: storing 98 in location 12
 
-:(scenario "run_interactive_returns_warnings")
+:(scenario "run_interactive_returns_errors")
 recipe main [
-  # run a command that generates a warning
+  # run a command that generates an error
   1:address:array:character <- new [x:number <- copy 34
 get x:number, foo:offset]
   2:address:array:character, 3:address:array:character <- run-interactive 1:address:array:character
   10:array:character <- copy 3:address:array:character/lookup
 ]
-# warning should be "unknown element foo in container number"
+# error should be "unknown element foo in container number"
 +mem: storing 117 in location 11
 +mem: storing 110 in location 12
 +mem: storing 107 in location 13
@@ -295,7 +313,7 @@ void track_most_recent_products(const instruction& instruction, const vector<vec
       if (is_mu_string(instruction.products.at(i))) {
         if (!scalar(products.at(i))) {
           tb_shutdown();
-          cerr << read_mu_string(trace_contents("warn")) << '\n';
+          cerr << read_mu_string(trace_error_warning_contents()) << '\n';
           cerr << SIZE(products.at(i)) << ": ";
           for (long long int j = 0; j < SIZE(products.at(i)); ++j)
             cerr << no_scientific(products.at(i).at(j)) << ' ';
@@ -336,6 +354,20 @@ long long int stringified_value_of_location(long long int address) {
   return new_mu_string(out.str());
 }
 
+long long int trace_error_warning_contents() {
+  if (!Trace_stream) return 0;
+  ostringstream out;
+  for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
+    if (p->label != "warn" && p->label != "error") continue;
+    out << p->contents;
+    if (*--p->contents.end() != '\n') out << '\n';
+  }
+  string result = out.str();
+  if (result.empty()) return 0;
+  truncate(result);
+  return new_mu_string(result);
+}
+
 long long int trace_contents(const string& layer) {
   if (!Trace_stream) return 0;
   if (trace_count(layer) <= 0) return 0;
@@ -361,7 +393,7 @@ void truncate(string& x) {
 }
 
 //: simpler version of run-interactive: doesn't do any running, just loads
-//: recipes and reports warnings.
+//: recipes and reports errors+warnings.
 
 :(before "End Primitive Recipe Declarations")
 RELOAD,
@@ -370,11 +402,11 @@ Recipe_ordinal["reload"] = RELOAD;
 :(before "End Primitive Recipe Checks")
 case RELOAD: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'reload' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'reload' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (!is_mu_scalar(inst.ingredients.at(0))) {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'reload' should be a literal string, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'reload' should be a literal string, but got " << inst.ingredients.at(0).original_string << '\n' << end();
     break;
   }
   break;
@@ -398,12 +430,12 @@ case RELOAD: {
   Trace_stream->newline();  // flush trace
   Current_routine = save_current_routine;
   products.resize(1);
-  products.at(0).push_back(trace_contents("warn"));
+  products.at(0).push_back(trace_error_warning_contents());
   run_code_end();  // wait until we're done with the trace contents
   break;
 }
 
-:(scenario reload_continues_past_warning)
+:(scenario reload_continues_past_error)
 recipe main [
   local-scope
   x:address:array:character <- new [recipe foo [
diff --git a/082persist.cc b/082persist.cc
index 0a0064d3..a43a4395 100644
--- a/082persist.cc
+++ b/082persist.cc
@@ -9,7 +9,7 @@ Recipe_ordinal["restore"] = RESTORE;
 :(before "End Primitive Recipe Checks")
 case RESTORE: {
   if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(Recipe[r].name) << "'restore' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'restore' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
     break;
   }
   string filename;
@@ -20,7 +20,7 @@ case RESTORE: {
     ;
   }
   else {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'restore' should be a string, but got " << inst.ingredients.at(0).to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'restore' should be a string, but got " << inst.ingredients.at(0).to_string() << '\n' << end();
     break;
   }
   break;
@@ -73,7 +73,7 @@ Recipe_ordinal["save"] = SAVE;
 :(before "End Primitive Recipe Checks")
 case SAVE: {
   if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(Recipe[r].name) << "'save' requires exactly two ingredients, but got " << inst.to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "'save' requires exactly two ingredients, but got " << inst.to_string() << '\n' << end();
     break;
   }
   if (is_literal_string(inst.ingredients.at(0))) {
@@ -83,11 +83,11 @@ case SAVE: {
     ;
   }
   else {
-    raise << maybe(Recipe[r].name) << "first ingredient of 'save' should be a string, but got " << inst.ingredients.at(0).to_string() << '\n' << end();
+    raise_error << maybe(Recipe[r].name) << "first ingredient of 'save' should be a string, but got " << inst.ingredients.at(0).to_string() << '\n' << end();
     break;
   }
-  if (!is_mu_scalar(inst.ingredients.at(1))) {
-    raise << maybe(Recipe[r].name) << "second ingredient of 'save' should be an address:array:character, but got " << inst.ingredients.at(1).to_string() << '\n' << end();
+  if (!is_mu_string(inst.ingredients.at(1))) {
+    raise_error << maybe(Recipe[r].name) << "second ingredient of 'save' should be an address:array:character, but got " << inst.ingredients.at(1).to_string() << '\n' << end();
     break;
   }
   break;
@@ -111,7 +111,7 @@ case SAVE: {
   // explicitly say '--all' for git 1.9
   int status = system("cd lesson; git add --all .; git diff HEAD --exit-code >/dev/null || git commit -a -m . >/dev/null");
   if (status != 0)
-    raise << "error in commit: contents " << contents << '\n' << end();
+    raise_error << "error in commit: contents " << contents << '\n' << end();
   break;
 }