about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-04-27 22:28:55 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-04-27 22:28:55 -0700
commit5b22547bb352f6e5a98ddd8ff42f37861a8e16ea (patch)
treecf531cce10d5f570f55dd54a46361dad882a14f6
parentb1ddb414006352b2d3af3652263a536acbd2702e (diff)
downloadmu-5b22547bb352f6e5a98ddd8ff42f37861a8e16ea.tar.gz
2880
Turns out we don't need a primitive to return an empty value of
arbitrary type. Just create it on the heap using 'new'.

But this uncovered *yet* another bug, sigh. When I specialize generic
functions I was running all transforms on the generated functions after
specialization completed. But there's one transform that includes code
from elsewhere. If such code included type-ingredients -- kaboom. Now
fixed and there's a test, so I've got that going for me which is nice.
-rw-r--r--056static_dispatch.cc3
-rw-r--r--058shape_shifting_recipe.cc30
-rw-r--r--072channel.mu34
-rw-r--r--channel.mu3
-rw-r--r--chessboard.mu11
5 files changed, 59 insertions, 22 deletions
diff --git a/056static_dispatch.cc b/056static_dispatch.cc
index ef99ae43..353b4b50 100644
--- a/056static_dispatch.cc
+++ b/056static_dispatch.cc
@@ -174,7 +174,8 @@ string best_variant(instruction& inst, const recipe& caller_recipe) {
   candidates = strictly_matching_variants(inst, variants);
   if (!candidates.empty()) return best_variant(inst, candidates).name;
 
-  // Static Dispatch Phase 2 (shape-shifting recipes in a later layer)
+  // Static Dispatch Phase 2
+  //: (shape-shifting recipes in a later layer)
   // End Static Dispatch Phase 2
 
   // Static Dispatch Phase 3
diff --git a/058shape_shifting_recipe.cc b/058shape_shifting_recipe.cc
index 6067fe7e..0384dd57 100644
--- a/058shape_shifting_recipe.cc
+++ b/058shape_shifting_recipe.cc
@@ -61,6 +61,8 @@ if (!candidates.empty()) {
   if (!variant.steps.empty()) {
     trace(9992, "transform") << "transforming new specialization: " << variant.name << end();
     for (int t = 0; t < SIZE(Transform); ++t) {
+      // one exception: skip tangle, which would have already occurred inside new_variant above
+      if (Transform.at(t) == insert_fragments) continue;
       (*Transform.at(t))(new_recipe_ordinal);
     }
   }
@@ -230,8 +232,14 @@ recipe_ordinal new_variant(recipe_ordinal exemplar, const instruction& inst, con
   recipe& new_recipe = get(Recipe, new_recipe_ordinal);
   new_recipe.name = new_name;
   trace(9993, "transform") << "switching " << inst.name << " to specialized " << header_label(new_recipe_ordinal) << end();
-  // Since the exemplar never ran any transforms, we have to redo some of the
-  // work of the check_types_by_name transform while supporting type-ingredients.
+
+  // Replace type ingredients with concrete types in new_recipe.
+  //
+  // preprocessing: micro-manage a couple of transforms
+  // a) perform tangle *before* replacing type ingredients, just in case
+  // inserted code involves type ingredients
+  insert_fragments(new_recipe_ordinal);
+  // b) do the work of check_types_by_name while supporting type-ingredients
   compute_type_names(new_recipe);
   // that gives enough information to replace type-ingredients with concrete types
   {
@@ -1027,3 +1035,21 @@ def main [
   foo *z
 ]
 # shouldn't crash
+
+:(scenario tangle_shape_shifting_recipe)
+# shape-shifting recipe
+def foo a:_elem [
+  local-scope
+  load-ingredients
+  <label1>
+]
+# tangle some code that refers to the type ingredient
+after <label1> [
+  b:_elem <- copy a
+]
+# trigger specialization
+def main [
+  local-scope
+  foo 34
+]
+$exit: 0
diff --git a/072channel.mu b/072channel.mu
index 993581ab..d391aeeb 100644
--- a/072channel.mu
+++ b/072channel.mu
@@ -12,10 +12,11 @@ scenario channel [
   run [
     1:address:source:number, 2:address:sink:number <- new-channel 3/capacity
     2:address:sink:number <- write 2:address:sink:number, 34
-    3:number, 1:address:source:number <- read 1:address:source:number
+    3:number, 4:boolean, 1:address:source:number <- read 1:address:source:number
   ]
   memory-should-contain [
     3 <- 34
+    4 <- 0  # read was successful
   ]
 ]
 
@@ -74,7 +75,6 @@ def write out:address:sink:_elem, val:_elem -> out:address:sink:_elem [
   free:number <- get *chan, first-free:offset
   *circular-buffer <- put-index *circular-buffer, free, val
   # mark its slot as filled
-  # todo: clear the slot itself
   free <- add free, 1
   {
     # wrap free around to 0 if necessary
@@ -87,9 +87,10 @@ def write out:address:sink:_elem, val:_elem -> out:address:sink:_elem [
   *chan <- put *chan, first-free:offset, free
 ]
 
-def read in:address:source:_elem -> result:_elem, in:address:source:_elem [
+def read in:address:source:_elem -> result:_elem, fail?:boolean, in:address:source:_elem [
   local-scope
   load-ingredients
+  fail? <- copy 0/false  # default status
   chan:address:channel:_elem <- get *in, chan:offset
   {
     # block if chan is empty
@@ -103,8 +104,10 @@ def read in:address:source:_elem -> result:_elem, in:address:source:_elem [
   full:number <- get *chan, first-full:offset
   circular-buffer:address:array:_elem <- get *chan, data:offset
   result <- index *circular-buffer, full
+  # clear the slot
+  empty:address:_elem <- new _elem:type
+  *circular-buffer <- put-index *circular-buffer, full, *empty
   # mark its slot as empty
-  # todo: clear the slot itself
   full <- add full, 1
   {
     # wrap full around to 0 if necessary
@@ -124,7 +127,7 @@ def clear in:address:source:_elem -> in:address:source:_elem [
   {
     empty?:boolean <- channel-empty? chan
     break-if empty?
-    _, in <- read in
+    _, _, in <- read in
   }
 ]
 
@@ -159,7 +162,7 @@ scenario channel-read-increments-full [
   run [
     1:address:source:number, 2:address:sink:number <- new-channel 3/capacity
     2:address:sink:number <- write 2:address:sink:number, 34
-    _, 1:address:source:number <- read 1:address:source:number
+    _, _, 1:address:source:number <- read 1:address:source:number
     3:address:channel:number <- get *1:address:source:number, chan:offset
     4:number <- get *3:address:channel:number, first-full:offset
     5:number <- get *3:address:channel:number, first-free:offset
@@ -177,7 +180,7 @@ scenario channel-wrap [
     3:address:channel:number <- get *1:address:source:number, chan:offset
     # write and read a value
     2:address:sink:number <- write 2:address:sink:number, 34
-    _, 1:address:source:number <- read 1:address:source:number
+    _, _, 1:address:source:number <- read 1:address:source:number
     # first-free will now be 1
     4:number <- get *3:address:channel:number, first-free:offset
     5:number <- get *3:address:channel:number, first-free:offset
@@ -185,7 +188,7 @@ scenario channel-wrap [
     2:address:sink:number <- write 2:address:sink:number, 34
     6:number <- get *3:address:channel:number, first-free:offset
     # read second value, verify that first-full wraps
-    _, 1:address:source:number <- read 1:address:source:number
+    _, _, 1:address:source:number <- read 1:address:source:number
     7:number <- get *3:address:channel:number, first-full:offset
   ]
   memory-should-contain [
@@ -242,7 +245,7 @@ scenario channel-read-not-full [
     1:address:source:number, 2:address:sink:number <- new-channel 1/capacity
     3:address:channel:number <- get *1:address:source:number, chan:offset
     2:address:sink:number <- write 2:address:sink:number, 34
-    _, 1:address:source:number <- read 1:address:source:number
+    _, _, 1:address:source:number <- read 1:address:source:number
     4:boolean <- channel-empty? 3:address:channel:number
     5:boolean <- channel-full? 3:address:channel:number
   ]
@@ -288,7 +291,11 @@ after <channel-write-initial> [
 
 after <channel-read-empty> [
   closed?:boolean <- get *chan, closed?:offset
-  return-if closed?, 0/hack  # only scalar _elems supported in channels; need to support construction of arbitrary empty containers
+  {
+    break-unless closed?
+    empty-result:address:_elem <- new _elem:type
+    return *empty-result, 1/true
+  }
 ]
 
 ## helpers
@@ -335,12 +342,14 @@ def buffer-lines in:address:source:character, buffered-out:address:sink:characte
   local-scope
   load-ingredients
   # repeat forever
+  eof?:boolean <- copy 0/false
   {
     line:address:buffer <- new-buffer 30
     # read characters from 'in' until newline, copy into line
     {
       +next-character
-      c:character, in <- read in
+      c:character, eof?:boolean, in <- read in
+      break-if eof?
       # drop a character on backspace
       {
         # special-case: if it's a backspace
@@ -361,9 +370,6 @@ def buffer-lines in:address:source:character, buffered-out:address:sink:characte
       line <- append line, c
       line-done?:boolean <- equal c, 10/newline
       break-if line-done?
-      # stop buffering on eof (currently only generated by fake console)
-      eof?:boolean <- equal c, 0/eof
-      break-if eof?
       loop
     }
     # copy line into 'buffered-out'
diff --git a/channel.mu b/channel.mu
index d74cbcad..e7415754 100644
--- a/channel.mu
+++ b/channel.mu
@@ -24,7 +24,8 @@ def consumer source:address:source:character -> source:address:source:character
   load-ingredients
   {
     # read an integer from the channel
-    n:character, source <- read source
+    n:character, eof?:boolean, source <- read source
+    break-if eof?
     # other threads might get between these prints
     $print [consume: ], n:character, [ 
 ]
diff --git a/chessboard.mu b/chessboard.mu
index 7ead26cb..57e40b19 100644
--- a/chessboard.mu
+++ b/chessboard.mu
@@ -260,7 +260,8 @@ def read-move stdin:address:source:character, screen:address:screen -> result:ad
 def read-file stdin:address:source:character, screen:address:screen -> file:number, quit:boolean, error:boolean, stdin:address:source:character, screen:address:screen [
   local-scope
   load-ingredients
-  c:character, stdin <- read stdin
+  c:character, eof?:boolean, stdin <- read stdin
+  return-if eof?, 0/dummy, 1/quit, 0/error
   {
     q-pressed?:boolean <- equal c, 81/Q
     break-unless q-pressed?
@@ -302,11 +303,12 @@ def read-file stdin:address:source:character, screen:address:screen -> file:numb
   return file, 0/quit, 0/error
 ]
 
-# valid values: 0-7, -1 (quit), -2 (error)
+# valid values for rank: 0-7
 def read-rank stdin:address:source:character, screen:address:screen -> rank:number, quit?:boolean, error?:boolean, stdin:address:source:character, screen:address:screen [
   local-scope
   load-ingredients
-  c:character, stdin <- read stdin
+  c:character, eof?:boolean, stdin <- read stdin
+  return-if eof?, 0/dummy, 1/quit, 0/error
   {
     q-pressed?:boolean <- equal c, 8/Q
     break-unless q-pressed?
@@ -347,7 +349,8 @@ def read-rank stdin:address:source:character, screen:address:screen -> rank:numb
 def expect-from-channel stdin:address:source:character, expected:character, screen:address:screen -> result:boolean, stdin:address:source:character, screen:address:screen [
   local-scope
   load-ingredients
-  c:character, stdin <- read stdin
+  c:character, eof?:boolean, stdin <- read stdin
+  return-if eof? 1/true
   {
     match?:boolean <- equal c, expected
     break-if match?