From 0b468c2802286279d543b5aa79cd10038099d864 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Mon, 15 Jul 2024 01:17:57 +0200 Subject: [PATCH 01/29] Add type syntax --- Cargo.lock | 2 +- cspell.json | 1 + src/fun/builtins.bend | 297 +++++----- src/fun/builtins.rs | 4 +- src/fun/load_book.rs | 4 +- src/fun/mod.rs | 52 +- src/fun/parser.rs | 686 +++++++++++++----------- src/fun/transform/definition_merge.rs | 6 +- src/fun/transform/desugar_bend.rs | 11 +- src/fun/transform/desugar_fold.rs | 60 ++- src/fun/transform/desugar_match_defs.rs | 12 +- src/fun/transform/encode_adts.rs | 20 +- src/fun/transform/expand_main.rs | 2 +- src/fun/transform/fix_match_defs.rs | 2 +- src/fun/transform/fix_match_terms.rs | 12 +- src/fun/transform/float_combinators.rs | 26 +- src/fun/transform/lift_local_defs.rs | 18 +- src/imp/mod.rs | 41 +- src/imp/order_kwargs.rs | 2 +- src/imp/parser.rs | 553 +++++++++++++------ src/imp/to_fun.rs | 10 +- src/main.rs | 2 +- 22 files changed, 1066 insertions(+), 757 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 890183d65..8556640f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,7 @@ dependencies = [ [[package]] name = "bend-lang" -version = "0.2.36" +version = "0.2.37-alpha.1" dependencies = [ "TSPL", "clap", diff --git a/cspell.json b/cspell.json index 4aa80d973..e60e050e8 100644 --- a/cspell.json +++ b/cspell.json @@ -32,6 +32,7 @@ "elif", "elifs", "foldl", + "forall", "hasher", "hexdigit", "hvm's", diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index cca33de52..eba0622be 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -1,33 +1,39 @@ -type String = (Nil) | (Cons head ~tail) +type String: + Nil + Cons { head: u24, ~tail: String } -type List = (Nil) | (Cons head ~tail) +type List(T): + Nil + Cons { head: T, ~tail: List(T) } -# List/length(xs: List(T)) -> (u24, List(T)) # Returns the length of a list and the list itself. -List/length xs = fold xs with len=0, acc=DiffList/new { - List/Nil: (len, (DiffList/to_list acc)) - List/Cons: (xs.tail (+ len 1) (DiffList/append acc xs.head)) -} +def List/length(xs: List(T)) -> (u24, List(T)): + fold xs with len=0, acc=DiffList/new: + case List/Nil: + return (len, DiffList/to_list(acc)) + case List/Cons: + return xs.tail(len + 1, DiffList/append(acc, xs.head)) -# List/reverse(xs: List(T)) -> List(T) # Reverses a list. -List/reverse xs = fold xs with acc=[] { - List/Nil: acc - List/Cons: (xs.tail (List/Cons xs.head acc)) -} +def List/reverse(xs: List(T)) -> List(T): + fold xs with acc=[]: + case List/Nil: + return acc + case List/Cons: + return xs.tail(List/Cons(xs.head, acc)) -# List/flatten(xs: List(List(T))) -> List(T) # Flattens a list of lists. +List/flatten (xs: (List (List T))) : (List T) List/flatten (List/Cons x xs) = (List/concat x (List/flatten xs)) List/flatten (List/Nil) = (List/Nil) -# List/concat(xs: List(T), ys: List(T)) -> List(T) # Concatenates two lists. +List/concat(xs: (List T)) (ys: (List T)) : (List T) List/concat (List/Cons x xs) ys = (List/Cons x (List/concat xs ys)) List/concat (List/Nil) ys = ys -# List/split_once(xs: List(T), val: T) -> (Result(List(T), List(T))) # Splits a list into two lists at the first occurrence of a value. +List/split_once (xs: (List T)) (val: T) -> (Result(List(T), List(T))) List/split_once xs val = (List/split_once.go xs val @x x) List/split_once.go List/Nil val acc = (Result/Err (acc List/Nil)) List/split_once.go (List/Cons x xs) val acc = @@ -37,8 +43,8 @@ List/split_once xs val = (List/split_once.go xs val @x x) (List/split_once.go xs val @y (acc (List/Cons x y))) } -# List/filter(xs: List(T), pred: T -> Bool) -> List(T) # Filters a list based on a predicate function. +List/filter (xs: (List T)) (pred: T -> Bool) : (List T) List/filter (List/Nil) _ = List/Nil List/filter (List/Cons x xs) pred = if (pred x) { @@ -47,8 +53,8 @@ List/filter (List/Cons x xs) pred = (List/filter xs pred) } -# String/equals(s1: String, s2: String) -> u24 # Checks if two strings are equal. +String/equals (s1: String) (s2: String) -> u24 String/equals (String/Nil) (String/Nil) = 1 String/equals (String/Cons x xs) (String/Cons y ys) = if (== x y) { @@ -58,8 +64,8 @@ String/equals (String/Cons x xs) (String/Cons y ys) = } String/equals * * = 0 -# String/split(s: String, delimiter: u24) -> List(String) # Splits a list into two lists at the first occurrence of a value. +String/split (s: String) (delimiter: u24) : (List String) String/split s delim = (String/split.go s delim (List/Cons String/Nil List/Nil)) String/split.go (String/Nil) _ acc = (List/reverse acc) String/split.go (String/Cons c cs) delim acc = @@ -72,44 +78,47 @@ String/split s delim = (String/split.go s delim (List/Cons String/Nil List/Nil)) } } -# DiffList/new() -> (List(T) -> List(T)) # Create a new difference list -DiffList/new = λx x +def DiffList/new() -> (List(T) -> List(T)): + return lambda x: x -# DiffList/wrap(head: T) -> (List(T) -> List(T) # Creates a new difference list with just the given value. -def DiffList/wrap(head): +def DiffList/wrap(head: T) -> (List(T) -> List(T)): return lambda tail: List/Cons(head, tail) -# DiffList/append(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)) # Append a value to the end of the difference list -DiffList/append diff val = λx (diff (List/Cons val x)) +def DiffList/append(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)): + return lambda x: diff(List/Cons(val, x)) -# DiffList/append(left: List(T) -> List(T), right: List(T) -> List(T)) -> (List(T) -> List(T)) # Concatenates two difference lists. -def DiffList/concat(left, right): +def DiffList/concat( + left: List(T) -> List(T), + right: List(T) -> List(T) +) -> (List(T) -> List(T)): return lambda x: left(right(x)) -# DiffList/cons(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)) # Append a value to the beginning of the difference list -DiffList/cons diff val = λx (List/Cons val (diff x)) +def DiffList/cons(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)): + return lambda x: List/Cons(val, diff(x)) -# DiffList/to_list(diff: List(T) -> List(T)) -> (List(T)) # Convert a difference list to a list -DiffList/to_list diff = (diff List/Nil) +def DiffList/to_list(diff: List(T) -> List(T)) -> (List(T)): + return diff(List/Nil) -type Nat = (Succ ~pred) | (Zero) +type Nat = (Succ ~(pred: Nat)) | (Zero) -type Result = (Ok val) | (Err val) +type (Result o e) = (Ok (val: o)) | (Err (val: e)) -Result/unwrap res = match res { - Result/Ok: res.val; - Result/Err: res.val; -} +def Result/unwrap(res: Result(T, E)) -> Any: + match res: + case Result/Ok: + return res.val + case Result/Err: + return res.val -type Tree: - Node { ~left, ~right } - Leaf { value } +type Tree(T): + Node { ~left: Tree(T), ~right: Tree(T) } + Leaf { value: T } # Returns a List converted from a Tree. def Tree/to_list(tree): @@ -130,11 +139,11 @@ def Tree/reverse(tree): # MAP Impl -type Map = (Node value ~left ~right) | (Leaf) +type Map T = (Node (value: T) ~(left: (Map T)) ~(right: (Map T))) | (Leaf) -Map/empty = Map/Leaf +Map/empty : (Map T) = Map/Leaf -Map/get map key = +Map/get (map: (Map T)) (key: u24) : ((Map T), T) = match map { Map/Leaf: (*, map) Map/Node: @@ -151,7 +160,7 @@ Map/get map key = } } -Map/set map key value = +Map/set (map: (Map T)) (key: u24) (value: T) : (Map T) = match map { Map/Node: switch _ = (== 0 key) { @@ -171,6 +180,7 @@ Map/set map key value = } } +Map/map (map: (Map T)) (key: u24) (f: T -> T) : (Map T) Map/map (Map/Leaf) key f = Map/Leaf Map/map (Map/Node value left right) key f = switch _ = (== 0 key) { @@ -186,22 +196,22 @@ Map/map (Map/Node value left right) key f = # IO Impl -type IO: - Done { magic, expr } - Call { magic, func, argm, cont } +type IO(T): + Done { magic: (u24, u24), expr: T } + Call { magic: (u24, u24), func: String, argm: Any, cont: T -> IO(T) } -type IOError: +type IOError(T): Type Name - Inner { value } + Inner { value: T } -def IO/MAGIC: +def IO/MAGIC() -> (u24, u24): return (0xD0CA11, 0xFF1FF1) -def IO/wrap(x): +def IO/wrap(x: T) -> IO(T): return IO/Done(IO/MAGIC, x) -def IO/bind(a, b): +def IO/bind(a: IO(A), b: A -> IO(B)) -> IO(B): match a: case IO/Done: b = undefer(b) @@ -209,7 +219,7 @@ def IO/bind(a, b): case IO/Call: return IO/Call(IO/MAGIC, a.func, a.argm, lambda x: IO/bind(a.cont(x), b)) -def call(func, argm): +def IO/call(func: String, argm: Any) -> IO(Result(Any, Any)): return IO/Call(IO/MAGIC, func, argm, lambda x: IO/Done(IO/MAGIC, x)) IO/done_on_err (IO/Call magic func argm cont) = (IO/Call magic func argm @res match res { @@ -220,16 +230,17 @@ IO/done_on_err done = done ## Time and sleep # Returns a monotonically increasing nanosecond timestamp as an u48 encoded as a pair of u24s. -IO/get_time = with IO { - ask res = (IO/Call IO/MAGIC "GET_TIME" * @x (IO/Done IO/MAGIC x)) - (wrap (Result/unwrap res)) - } +def IO/get_time() -> IO((u24, u24)): + with IO: + res <- IO/call("GET_TIME", *) + return wrap(Result/unwrap(res)) # Sleeps for the given number of nanoseconds, given by an u48 encoded as a pair of u24s. -IO/nanosleep hi_lo = (IO/Call IO/MAGIC "SLEEP" hi_lo @x (IO/Done IO/MAGIC x)) +def IO/nanosleep(hi_lo: (u24, u24)) -> IO(None): + return IO/call("SLEEP", hi_lo) # Sleeps for a given amount of seconds as a float. -def IO/sleep(seconds): +def IO/sleep(seconds: f24) -> IO(None): nanos = seconds * 1_000_000_000.0 lo = to_u24(nanos % 0x1_000_000.0) hi = to_u24(nanos / 0x1_000_000.0) @@ -240,54 +251,52 @@ def IO/sleep(seconds): ## File IO ### File IO primitives -# IO/FS/open(path: String, mode: u24) -> (IO u24) -IO/FS/open path mode = (IO/Call IO/MAGIC "OPEN" (path, mode) IO/wrap) +def IO/FS/open(path: String, mode: String) -> IO(u24): + return IO/call("OPEN", (path, mode)) -# IO/FS/close(file: u24) -> (IO None) -IO/FS/close file = (IO/Call IO/MAGIC "CLOSE" file IO/wrap) +def IO/FS/close(file: u24) -> IO(None): + return IO/call("CLOSE", file) -# IO/FS/read(file: u24, num_bytes: u24) -> (IO (List u24)) -IO/FS/read file num_bytes = (IO/Call IO/MAGIC "READ" (file, num_bytes) IO/wrap) +def IO/FS/read(file: u24, num_bytes: u24) -> IO(List(u24)): + return IO/call("READ", (file, num_bytes)) -# IO/FS/write(file: u24, bytes: (List u24)) -> (IO None) -IO/FS/write file bytes = (IO/Call IO/MAGIC "WRITE" (file, bytes) IO/wrap) +def IO/FS/write(file: u24, bytes: List(u24)) -> IO(None): + return IO/call("WRITE", (file, bytes)) -# IO/FS/seek(file: u24, offset: i24, mode: u24) -> (IO None) -IO/FS/seek file offset mode = (IO/Call IO/MAGIC "SEEK" (file, (offset, mode)) IO/wrap) +def IO/FS/seek(file: u24, offset: i24, mode: u24) -> IO(None): + return IO/call("SEEK", (file, (offset, mode))) -# IO/FS/flush(file: u24) -> (IO None) -IO/FS/flush file = (IO/Call IO/MAGIC "FLUSH" file IO/wrap) +def IO/FS/flush(file: u24) -> IO(None): + return IO/call("FLUSH", file) ### Always available files -IO/FS/STDIN = 0 -IO/FS/STDOUT = 1 -IO/FS/STDERR = 2 +IO/FS/STDIN : u24 = 0 +IO/FS/STDOUT : u24 = 1 +IO/FS/STDERR : u24 = 2 ### Seek modes # Seek from start of file. -IO/FS/SEEK_SET = +0 +IO/FS/SEEK_SET : i24 = +0 # Seek from current position. -IO/FS/SEEK_CUR = +1 +IO/FS/SEEK_CUR : i24 = +1 # Seek from end of file. -IO/FS/SEEK_END = +2 +IO/FS/SEEK_END : i24 = +2 ### File utilities -# IO/FS/read_file(path: String) -> (IO (List u24)) # Reads an entire file, returning a list of bytes. -def IO/FS/read_file(path): +def IO/FS/read_file(path: String) -> IO(List(u24)): with IO: fd <- IO/done_on_err(IO/FS/open(path, "r")) bytes <- IO/FS/read_to_end(fd) * <- IO/done_on_err(IO/FS/close(fd)) return wrap(bytes) -# IO/FS/read_to_end(fd: u24) -> (IO (List u24)) # Reads the remaining contents of a file, returning a list of read bytes. -def IO/FS/read_to_end(fd): +def IO/FS/read_to_end(fd: u24) -> IO(List(u24)): return IO/FS/read_to_end.read_chunks(fd, []) -def IO/FS/read_to_end.read_chunks(fd, chunks): +def IO/FS/read_to_end.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u24)): with IO: # Read file in 1MB chunks chunk <- IO/done_on_err(IO/FS/read(fd, 1048576)) @@ -297,12 +306,11 @@ def IO/FS/read_to_end.read_chunks(fd, chunks): case List/Cons: return IO/FS/read_to_end.read_chunks(fd, List/Cons(chunk, chunks)) -# IO/FS/read_line(fd: u24) -> (IO (List u24)) # Reads a single line from a file, returning a list of bytes. -def IO/FS/read_line(fd): +def IO/FS/read_line(fd: u24) -> IO(List(u24)): return IO/FS/read_line.read_chunks(fd, []) -def IO/FS/read_line.read_chunks(fd, chunks): +def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u24)): with IO: # Read line in 1kB chunks chunk <- IO/done_on_err(IO/FS/read(fd, 1024)) @@ -328,9 +336,8 @@ def IO/FS/read_line.read_chunks(fd, chunks): chunks = List/Cons(line, chunks) return IO/FS/read_line.read_chunks(fd, chunks) -# IO/FS/write_file(path: String, bytes: (List u24)) -> (IO None) # Writes a list of bytes to a file given by a path. -def IO/FS/write_file(path, bytes): +def IO/FS/write_file(path: String, bytes: List(u24)) -> IO(None): with IO: f <- IO/FS/open(path, "w") match f: @@ -343,18 +350,19 @@ def IO/FS/write_file(path, bytes): ### Standard input and output utilities -# IO/print(text: String) -> (IO *) # Prints a string to stdout, encoding it with utf-8. -IO/print text = with IO { - ask res = (IO/FS/write IO/FS/STDOUT (String/encode_utf8 text)) - (wrap (Result/unwrap res)) - } +def IO/print(text: String) -> IO(None): + with IO: + res <- IO/FS/write(IO/FS/STDOUT, String/encode_utf8(text)) + return wrap(Result/unwrap(res)) # IO/input() -> IO String # Read characters from stdin until a newline is found. # Returns the read input decoded as utf-8. -IO/input = (IO/input.go DiffList/new) -def IO/input.go(acc): +def IO/input() -> IO(String): + return IO/input.go(DiffList/new) + +def IO/input.go(acc: List(u24) -> List(u24)) -> IO(String): # TODO: This is slow and inefficient, should be done in hvm using fgets. with IO: byte <- IO/done_on_err(IO/FS/read(IO/FS/STDIN, 1)) @@ -373,21 +381,19 @@ def IO/input.go(acc): ### Dynamically linked libraries -# IO/DyLib/open(path: String, lazy: u24) -> u24 # 'path' is the path to the library file. # 'lazy' is a boolean encoded as a u24 that determines if all functions are loaded lazily or upfront. # Returns an unique id to the library object encoded as a u24 -def IO/DyLib/open(path, lazy): - return IO/Call(IO/MAGIC, "DL_OPEN", (path, lazy), IO/wrap) +def IO/DyLib/open(path: String, lazy: u24) -> IO(u24): + return IO/call("DL_OPEN", (path, lazy)) -# IO/DyLib/call(dl: u24, fn: String, args: Any) -> Any # Calls a function of a previously opened library. # 'dl' is the id of the library object. # 'fn' is the name of the function in the library. # 'args' are the arguments to the function. The expected values depend on the called function. # The returned value is determined by the called function. -def IO/DyLib/call(dl, fn, args): - return IO/Call(IO/MAGIC, "DL_CALL", (dl, fn, args), IO/wrap) +def IO/DyLib/call(dl: u24, fn: String, args: Any) -> IO(Any): + return IO/call("DL_CALL", (dl, (fn, args))) # IO/DyLib/close(dl: u24) -> None # Closes a previously open library. @@ -404,48 +410,34 @@ def IO/DyLib/close(dl): # We can build a defered call directly or by by using defer and defer_arg # The example above can be written as: # (defer_arg (defer_arg (defer_arg (defer @arg1 @arg2 @arg3 (f arg1 arg2 arg3)) arg1) arg2) arg3) -defer val = @x (x val) +defer val = + @x (x val) # defer_arg(defered: A -> B -> C, arg: B) -> (A -> C) -defer_arg defered arg = @x (defered x arg) +defer_arg defered arg = + @x (defered x arg) # undefer(defered: (A -> A) -> B) -> B -undefer defered = (defered @x x) - - -# Native number casts - -# to_f24(x: native number) -> f24 -# Casts any native number to an f24. -hvm to_f24: - ($([f24] ret) ret) - -# to_u24(x: native number) -> u24 -# Casts any native number to a u24. -hvm to_u24: - ($([u24] ret) ret) +undefer defered = + (defered @x x) -# to_i24(x: native number) -> i24 -# Casts any native number to an i24. -hvm to_i24: - ($([i24] ret) ret) # String Encoding and Decoding -Utf8/REPLACEMENT_CHARACTER = '\u{FFFD}' +Utf8/REPLACEMENT_CHARACTER : u24 = '\u{FFFD}' # String/decode_utf8(List u24) -> String # Decodes a sequence of bytes to a String using utf-8 encoding. -String/decode_utf8 bytes = +String/decode_utf8 (bytes: (List u24)) : String = let (got, rest) = (Utf8/decode_character bytes) match rest { List/Nil: (String/Cons got String/Nil) List/Cons: (String/Cons got (String/decode_utf8 rest)) } -# Utf8/decode_character(List u24) -> (u24, List u24) # Decodes one utf-8 character from the start of a sequence of bytes. # Returns the decoded character and the remaining bytes. +Utf8/decode_character (bytes: (List u24)) : (u24, (List u24)) Utf8/decode_character [] = (0, []) Utf8/decode_character [a] = if (<= a 0x7F) { (a, []) } else { (Utf8/REPLACEMENT_CHARACTER, []) } Utf8/decode_character [a, b] = @@ -506,8 +498,8 @@ Utf8/decode_character (List/Cons a (List/Cons b (List/Cons c (List/Cons d rest)) } } -# String/encode_utf8(String) -> (List u24) # Encodes a string to a sequence of bytes using utf-8 encoding. +String/encode_utf8 (str: String) : (List u24) String/encode_utf8 (String/Nil) = (List/Nil) String/encode_utf8 (String/Cons x xs) = use Utf8/rune1max = 0b01111111 @@ -541,13 +533,13 @@ String/encode_utf8 (String/Cons x xs) = } } -# String/decode_ascii(List u24) -> String # Decodes a sequence of bytes to a String using ascii encoding. +String/decode_ascii (bytes: (List u24)) : String String/decode_ascii (List/Cons x xs) = (String/Cons x (String/decode_ascii xs)) String/decode_ascii (List/Nil) = (String/Nil) -# String/encode_ascii(String) -> (List u24) # Encodes a string to a sequence of bytes using ascii encoding. +String/encode_ascii (str: String) : (List u24) String/encode_ascii (String/Cons x xs) = (List/Cons x (String/encode_ascii xs)) String/encode_ascii (String/Nil) = (List/Nil) @@ -555,93 +547,90 @@ String/encode_ascii (String/Nil) = (List/Nil) # Math/PI() -> f24 # The Pi (π) constant. -def Math/PI(): +def Math/PI() -> f24: return 3.1415926535 -def Math/E(): +def Math/E() -> f24: return 2.718281828 # Math/log(x: f24, base: f24) -> f24 # Computes the logarithm of `x` with the specified `base`. -hvm Math/log: +hvm Math/log -> (f24 -> f24 -> f24): (x ($([|] $(x ret)) ret)) # Math/atan2(x: f24, y: f24) -> f24 # Has the same behaviour as `atan2f` in the C math lib. # Computes the arctangent of the quotient of its two arguments. -hvm Math/atan2: +hvm Math/atan2 -> (f24 -> f24 -> f24): ($([&] $(y ret)) (y ret)) # Math/sin(a: f24) -> f24 # Computes the sine of the given angle in radians. -hvm Math/sin: +hvm Math/sin -> (f24 -> f24): ($([<<0x0] a) a) # Math/cos(a: f24) -> f24 # Computes the cosine of the given angle in radians. -hvm Math/cos: +hvm Math/cos -> (f24 -> f24): (a b) & @Math/PI ~ $([:/2.0] $([-] $(a $([<<0x0] b)))) # Math/tan(a: f24) -> f24 # Computes the tangent of the given angle in radians. -hvm Math/tan: +hvm Math/tan -> (f24 -> f24): ($([>>0x0] a) a) -# Math/cot(a: f24) -> f24 # Computes the cotangent of the given angle in radians. -Math/cot a = (/ 1.0 (Math/tan a)) +Math/cot (a: f24) : f24 = + (/ 1.0 (Math/tan a)) -# Math/sec(a: f24) -> f24 # Computes the secant of the given angle in radians. -Math/sec a = (/ 1.0 (Math/cos a)) +Math/sec (a: f24) : f24 = + (/ 1.0 (Math/cos a)) -# Math/csc(a: f24) -> f24 # Computes the cosecant of the given angle in radians. -Math/csc a = (/ 1.0 (Math/sin a)) +Math/csc (a: f24) : f24 = + (/ 1.0 (Math/sin a)) -# Math/atan(a: f24) -> f24 # Computes the arctangent of the given angle. -Math/atan a = (Math/atan2 a 1.0) +Math/atan (a: f24) : f24 = + (Math/atan2 a 1.0) -# Math/asin(a: f24) -> f24 # Computes the arcsine of the given angle. -Math/asin a = (Math/atan2 a (Math/sqrt (- 1.0 (* a a)))) +Math/asin (a: f24) : f24 = + (Math/atan2 a (Math/sqrt (- 1.0 (* a a)))) -# Math/acos(a: f24) -> f24 # Computes the arccosine of the given angle. -Math/acos a = (Math/atan2 (Math/sqrt (- 1.0 (* a a))) a) +Math/acos (a: f24) : f24 = + (Math/atan2 (Math/sqrt (- 1.0 (* a a))) a) -# Math/radians(a: f24) -> f24 # Converts degrees to radians. -Math/radians a = (* a (/ Math/PI 180.0)) +Math/radians (a: f24) : f24 = + (* a (/ Math/PI 180.0)) -# Math/sqrt(x: f24) -> f24 # Computes the square root of the given number. -Math/sqrt n = (** n 0.5) +Math/sqrt (n: f24) : f24 = + (** n 0.5) -# Math/ceil(n: f24) -> f24 # Round float up to the nearest integer. -def Math/ceil(n): +def Math/ceil(n: f24) -> f24: i_n = to_f24(to_i24(n)) if n <= i_n: return i_n else: return i_n + 1.0 -# Math/floor(n: f24) -> f24 # Round float down to the nearest integer. -def Math/floor(n): +def Math/floor(n: f24) -> f24: i_n = to_f24(to_i24(n)) if n < i_n: return i_n - 1.0 else: return i_n -# Math/round(n: f24) -> f24 # Round float to the nearest integer. -def Math/round(n): +def Math/round(n: f24) -> f24: i_n = to_f24(to_i24(n)) if n - i_n < 0.5: return Math/floor(n) diff --git a/src/fun/builtins.rs b/src/fun/builtins.rs index d9489ffef..3496e0319 100644 --- a/src/fun/builtins.rs +++ b/src/fun/builtins.rs @@ -1,5 +1,5 @@ use super::{ - parser::{ParseBook, TermParser}, + parser::{FunParser, ParseBook}, Book, Name, Num, Pattern, Term, }; use crate::maybe_grow; @@ -47,7 +47,7 @@ pub const BUILTIN_TYPES: &[&str] = &[LIST, STRING, NAT, TREE, MAP, IO]; impl ParseBook { pub fn builtins() -> Self { - let book = TermParser::new(BUILTINS).parse_book(Self::default(), true); + let book = FunParser::new(Name::new(""), BUILTINS, true).parse_book(Self::default()); book.unwrap_or_else(|e| panic!("Error parsing builtin file, this should not happen:\n{e}")) } } diff --git a/src/fun/load_book.rs b/src/fun/load_book.rs index a88c45097..fd7b4ea76 100644 --- a/src/fun/load_book.rs +++ b/src/fun/load_book.rs @@ -1,5 +1,5 @@ use super::{ - parser::{ParseBook, TermParser}, + parser::{FunParser, ParseBook}, Book, Name, Source, SourceKind, }; use crate::{ @@ -41,7 +41,7 @@ pub fn load_to_book( pub fn do_parse_book(code: &str, origin: &Path, mut book: ParseBook) -> Result { book.source = Name::new(origin.to_string_lossy()); - TermParser::new(code).parse_book(book, false).map_err(|err| { + FunParser::new(book.source.clone(), code, false).parse_book(book).map_err(|err| { let mut diagnostics = Diagnostics::default(); let span = TextSpan::from_byte_span(code, err.span.0..err.span.1); let source = diff --git a/src/fun/mod.rs b/src/fun/mod.rs index b88b5c246..e3d739610 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -6,7 +6,11 @@ use crate::{ use indexmap::{IndexMap, IndexSet}; use interner::global::{GlobalPool, GlobalString}; use itertools::Itertools; -use std::{borrow::Cow, hash::Hash, ops::Deref}; +use std::{ + borrow::Cow, + hash::Hash, + ops::{Deref, Range}, +}; pub mod builtins; pub mod check; @@ -64,6 +68,8 @@ pub type Constructors = IndexMap; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Definition { pub name: Name, + pub typ: Type, + pub check: bool, pub rules: Vec, pub source: Source, } @@ -93,10 +99,25 @@ pub enum SourceKind { #[derive(Debug, Clone)] pub struct HvmDefinition { pub name: Name, + pub typ: Type, pub body: hvm::ast::Net, pub source: Source, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Type { + Any, + Hole, + Var(Name), + All(Name, Box), + Ctr(Name, Vec), + Arr(Box, Box), + Tup(Vec), + U24, + F24, + I24, +} + /// A pattern matching rule of a definition. #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] pub struct Rule { @@ -280,15 +301,25 @@ pub enum Tag { /// A user defined datatype #[derive(Debug, Clone)] pub struct Adt { - pub ctrs: IndexMap>, + pub name: Name, + pub vars: Vec, + pub ctrs: IndexMap, pub source: Source, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] +pub struct AdtCtr { + pub name: Name, + pub typ: Type, + pub fields: Vec, +} + +#[derive(Debug, Clone)] pub struct CtrField { pub nam: Name, pub rec: bool, } + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Name(GlobalString); @@ -1027,14 +1058,10 @@ impl Rule { } impl Definition { - pub fn new(name: Name, rules: Vec, source: Source) -> Self { - Self { name, rules, source } - } - - pub fn new_gen(name: Name, rules: Vec, source: Source) -> Self { + pub fn new_gen(name: Name, rules: Vec, source: Source, check: bool) -> Self { let kind = if source.is_builtin() { SourceKind::Builtin } else { SourceKind::Generated }; let source = Source { kind, ..source }; - Self { name, rules, source } + Self { name, typ: Type::Hole, check, rules, source } } pub fn is_builtin(&self) -> bool { @@ -1134,6 +1161,13 @@ impl Source { pub fn is_local(&self) -> bool { matches!(self.kind, SourceKind::User) } + + pub fn from_file_span(file: &Name, txt: &str, span: Range, builtin: bool) -> Self { + let span = Some(TextSpan::from_byte_span(txt, span)); + let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; + let file = Some(file.to_string()); + Source { file, span, kind } + } } impl Default for Source { diff --git a/src/fun/parser.rs b/src/fun/parser.rs index fadea7430..df997105d 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -1,22 +1,18 @@ -use std::ops::Range; - use crate::{ - diagnostics::{TextLocation, TextSpan}, fun::{ - display::DisplayFn, Adt, Adts, Constructors, CtrField, FanKind, HvmDefinition, HvmDefinitions, MatchRule, - Name, Num, Op, Pattern, Rule, Source, Tag, Term, STRINGS, + display::DisplayFn, Adt, AdtCtr, Adts, Constructors, CtrField, FanKind, HvmDefinition, HvmDefinitions, + MatchRule, Name, Num, Op, Pattern, Rule, Source, SourceKind, Tag, Term, Type, STRINGS, }, - imp::{parser::PyParser, Enum, RepeatedNames, Variant}, + imp::parser::ImpParser, imports::{Import, ImportCtx, ImportType}, maybe_grow, }; use highlight_error::highlight_error; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; +use std::ops::Range; use TSPL::{ParseError, Parser}; -use super::SourceKind; - type FunDefinition = super::Definition; type ImpDefinition = crate::imp::Definition; @@ -60,246 +56,320 @@ impl ParseBook { } } -// Bend grammar description: -// ::= ( | )* -// ::= "type" "=" ( | "(" ()* ")" )+ -// ::= ("(" * ")" | *) "=" -// ::= "(" * ")" | | | "(" ("," )+ ")" -// ::= -// | | | | | | | | | | -// | | | | | | | | | -// ::= ? ("λ"|"@") -// ::= ? ("λ"|"@") "$" -// ::= "(" ")" -// ::= "(" ("," )+ ")" -// ::= ? "(" ()+ ")" -// ::= "(" ")" -// ::= "use" "=" ";"? -// ::= "let" "=" ";"? -// ::= "with" "{" "}" -// ::= "ask" "=" ";" | -// ::= "let" "(" ("," )+ ")" "=" ";"? -// ::= "let" ? "{" (","? )+ "}" "=" ";"? -// ::= "[" ( ","?)* "]" -// ::= "\"" (escape sequence | [^"])* "\"" -// ::= "'" (escape sequence | [^']) "'" -// ::= "match" ? "{" + "}" -// ::= "fold" ? "{" + "}" -// ::= ( "=" ) | -// ::= "with" ( ("=" )? ","?)+ -// ::= "|"? ":" ";"? -// ::= "switch" ? "{" + "}" -// ::= "|"? (|"_") ":" ";"? -// ::= "bend" ( ","?)+ "{" "when" ":" "else" ":" "}" -// ::= -// ::= "$" -// ::= | "*" -// ::= "*" -// ::= "#" -// ::= [_\-./a-zA-Z0-9]+ -// ::= ([0-9]+ | "0x"[0-9a-fA-F]+ | "0b"[01]+) -// ::= ( "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<<" | ">>" | "<" | ">" | "&" | "|" | "^" | "**" ) - pub type ParseResult = std::result::Result; -pub struct TermParser<'i> { +pub struct FunParser<'i> { + file: Name, input: &'i str, index: usize, + builtin: bool, } -impl<'a> TermParser<'a> { - pub fn new(input: &'a str) -> Self { - Self { input, index: 0 } +impl<'a> FunParser<'a> { + pub fn new(file: Name, input: &'a str, builtin: bool) -> Self { + Self { file, input, index: 0, builtin } } /* AST parsing functions */ - pub fn parse_book(&mut self, default_book: ParseBook, builtin: bool) -> ParseResult { + pub fn parse_book(&mut self, default_book: ParseBook) -> ParseResult { let mut book = default_book; let mut indent = self.advance_newlines()?; - let mut last_rule = None; while !self.is_eof() { - let ini_idx = *self.index(); - // Record type definition - if self.try_parse_keyword("object") { - let mut prs = PyParser { input: self.input, index: *self.index() }; - let (obj, nxt_indent) = prs.parse_object(indent)?; + if self.starts_with_keyword("object") { + let ini_idx = *self.index(); + let mut prs = ImpParser { + file: self.file.clone(), + input: self.input, + index: *self.index(), + builtin: self.builtin, + }; + let (adt, nxt_indent) = prs.parse_object(indent)?; self.index = prs.index; let end_idx = *self.index(); - self.add_object(obj, &mut book, ini_idx..end_idx, builtin)?; + self.add_type_def(adt, &mut book, ini_idx..end_idx)?; indent = nxt_indent; - last_rule = None; continue; } // Imp function definition - if self.try_parse_keyword("def") { - let mut prs = PyParser { input: self.input, index: *self.index() }; - let (def, nxt_indent) = prs.parse_def(indent)?; + if self.starts_with_keyword("def") { + let ini_idx = *self.index(); + let mut prs = + ImpParser { file: self.file.clone(), input: self.input, index: ini_idx, builtin: self.builtin }; + let (def, nxt_indent) = prs.parse_function_def(indent)?; self.index = prs.index; let end_idx = *self.index(); - self.add_imp_def(def, &mut book, ini_idx..end_idx, builtin)?; + self.add_imp_def(def, &mut book, ini_idx..end_idx)?; indent = nxt_indent; - last_rule = None; continue; } // Fun/Imp type definition - if self.try_parse_keyword("type") { - self.skip_trivia(); - let rewind_index = self.index; - - let _ = self.labelled(|p| p.parse_top_level_name(), "datatype name")?; + if self.starts_with_keyword("type") { + fn starts_with_imp_type(p: &mut FunParser) -> ParseResult<()> { + p.parse_keyword("type")?; + p.skip_trivia_inline()?; + p.parse_top_level_name()?; + p.skip_trivia_inline()?; + if p.starts_with(":") || p.starts_with("(") { + Ok(()) + } else { + Err(ParseError::new((0, 0), "")) + } + } - // Imp type definition - if self.starts_with(":") { - let mut prs = PyParser { input: self.input, index: rewind_index }; - let (r#enum, nxt_indent) = prs.parse_type(indent)?; + let ini_idx = *self.index(); + let is_imp = starts_with_imp_type(self).is_ok(); + self.index = ini_idx; + if is_imp { + // Imp type definition + let mut prs = ImpParser { + file: self.file.clone(), + input: self.input, + index: *self.index(), + builtin: self.builtin, + }; + let (adt, nxt_indent) = prs.parse_type_def(indent)?; self.index = prs.index; let end_idx = *self.index(); - self.add_imp_type(r#enum, &mut book, ini_idx..end_idx, builtin)?; + self.add_type_def(adt, &mut book, ini_idx..end_idx)?; indent = nxt_indent; - last_rule = None; continue; - // Fun type definition } else { - self.index = rewind_index; - let (nam, ctrs) = self.parse_datatype()?; + // Fun type definition + let adt = self.parse_type_def()?; let end_idx = *self.index(); - - let span = Some(TextSpan::from_byte_span(self.input(), ini_idx..end_idx)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = Some(book.source.to_string()); - let source = Source { file, span, kind }; - - let adt = Adt { ctrs, source }; - self.add_fun_type(&mut book, nam, adt, ini_idx..end_idx)?; + self.add_type_def(adt, &mut book, ini_idx..end_idx)?; indent = self.advance_newlines()?; - last_rule = None; continue; } } // HVM native function definition - if self.try_parse_keyword("hvm") { - let def = self.parse_hvm(builtin)?; + if self.starts_with_keyword("hvm") { + let ini_idx = self.index; + let mut prs = + ImpParser { file: self.file.clone(), input: self.input, index: self.index, builtin: self.builtin }; + let (def, nxt_indent) = prs.parse_hvm()?; + *self.index() = prs.index; let end_idx = *self.index(); self.add_hvm(def, &mut book, ini_idx..end_idx)?; - indent = self.advance_newlines()?; - last_rule = None; + indent = nxt_indent; continue; } // Import declaration - if self.try_parse_keyword("from") { - self.skip_trivia(); + if self.starts_with_keyword("from") { let import = self.parse_from_import()?; book.import_ctx.add_import(import); indent = self.advance_newlines()?; - last_rule = None; continue; } - if self.try_parse_keyword("import") { - self.skip_trivia(); + if self.starts_with_keyword("import") { let imports = self.parse_import()?; for imp in imports { book.import_ctx.add_import(imp); } indent = self.advance_newlines()?; - last_rule = None; continue; } // Fun function definition let ini_idx = *self.index(); - let (name, rule) = self.parse_rule()?; + let def = self.parse_fun_def()?; let end_idx = *self.index(); - if let Some(def) = book.imp_defs.get(&name) { - let msg = Self::redefinition_of_function_msg(def.source.is_builtin(), &name); - return self.with_ctx(Err(msg), ini_idx..end_idx); - } - - self.add_fun_def(&name, rule, builtin, &last_rule, &mut book, ini_idx..end_idx)?; + self.add_fun_def(def, &mut book, ini_idx..end_idx)?; indent = self.advance_newlines()?; - last_rule = Some(name); } Ok(book) } - fn parse_datatype(&mut self) -> ParseResult<(Name, IndexMap>)> { - // type name = ctr (| ctr)* + fn parse_type_def(&mut self) -> ParseResult { + // type (name var1 ... varN) = ctr (| ctr)* + let ini_idx = self.index; + self.parse_keyword("type")?; self.skip_trivia(); - let name = self.labelled(|p| p.parse_top_level_name(), "datatype name")?; - self.consume("=")?; - let mut ctrs = vec![self.parse_datatype_ctr(&name)?]; + + let name; + let vars; + if self.try_consume("(") { + // parens around name and vars + self.skip_trivia(); + name = self.parse_top_level_name()?; + vars = self.list_like(|p| p.parse_var_name(), "", ")", "", false, 0)?; + self.consume("=")?; + } else { + // no parens + name = self.parse_top_level_name()?; + vars = self.list_like(|p| p.parse_var_name(), "", "=", "", false, 0)?; + } + + let mut ctrs = vec![self.parse_type_ctr(&name, &vars)?]; while self.try_consume("|") { - ctrs.push(self.parse_datatype_ctr(&name)?); + ctrs.push(self.parse_type_ctr(&name, &vars)?); } - let ctrs = ctrs.into_iter().collect(); - Ok((name, ctrs)) + let ctrs = ctrs.into_iter().map(|ctr| (ctr.name.clone(), ctr)).collect::>(); + + let end_idx = *self.index(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let adt = Adt { name, vars, ctrs, source }; + Ok(adt) } - fn parse_datatype_ctr(&mut self, typ_name: &Name) -> ParseResult<(Name, Vec)> { - // (name ('~'? field)*) + fn parse_type_ctr(&mut self, typ_name: &Name, typ_vars: &[Name]) -> ParseResult { + // '(' name (( '~'? field) | ('~'? '('field (':' type)? ')') )* ')' // name + self.skip_trivia(); + let ini_idx = *self.index(); if self.try_consume("(") { + // name and optionally fields + self.skip_trivia(); let ctr_name = self.parse_top_level_name()?; let ctr_name = Name::new(format!("{typ_name}/{ctr_name}")); - fn parse_field(p: &mut TermParser) -> ParseResult { - let rec = p.try_consume("~"); - p.skip_trivia(); - let nam = p.labelled(|p| p.parse_bend_name(), "datatype constructor field")?; - Ok(CtrField { nam, rec }) - } + let fields = self.list_like(|p| p.parse_type_ctr_field(), "", ")", "", false, 0)?; + let (fields, field_types): (Vec<_>, Vec<_>) = fields.into_iter().unzip(); + let end_idx = *self.index(); + self.check_repeated_ctr_fields(&fields, &ctr_name, ini_idx..end_idx)?; - let fields = self.list_like(parse_field, "", ")", "", false, 0)?; - if let Some(field) = fields.find_repeated_names().into_iter().next() { - let msg = format!("Found a repeated field '{field}' in constructor {ctr_name}."); - return self.expected_message(&msg)?; - } - Ok((ctr_name, fields)) + let typ = Type::Ctr(typ_name.clone(), typ_vars.iter().cloned().map(Type::Var).collect()); + let typ = field_types.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(acc), Box::new(typ))); + let typ = typ_vars.iter().cloned().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); + let ctr = AdtCtr { name: ctr_name, typ, fields }; + Ok(ctr) } else { - // name + // just name let name = self.labelled(|p| p.parse_top_level_name(), "datatype constructor name")?; let name = Name::new(format!("{typ_name}/{name}")); - Ok((name, vec![])) + let typ = Type::Ctr(typ_name.clone(), typ_vars.iter().cloned().map(Type::Var).collect()); + let typ = typ_vars.iter().cloned().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); + let ctr = AdtCtr { name, typ, fields: vec![] }; + Ok(ctr) } } - fn parse_hvm(&mut self, builtin: bool) -> ParseResult { - self.skip_trivia_inline()?; - let name = self.parse_bend_name()?; - self.skip_trivia_inline()?; - self.consume_exactly(":")?; - self.consume_new_line()?; - // TODO: This will have the wrong index + fn parse_type_ctr_field(&mut self) -> ParseResult<(CtrField, Type)> { + let rec = self.try_consume("~"); + + let nam; + let typ; + if self.try_consume("(") { + nam = self.parse_var_name()?; + if self.try_consume(":") { + typ = self.parse_type_term(&mut Default::default())?; + } else { + typ = Type::Any; + } + self.consume(")")?; + } else { + nam = self.parse_var_name()?; + typ = Type::Any; + } + Ok((CtrField { nam, rec }, typ)) + } + + fn parse_fun_def(&mut self) -> ParseResult { let ini_idx = *self.index(); - let mut p = hvm::ast::CoreParser::new(&self.input[*self.index()..]); - let body = p.parse_net()?; - *self.index() = ini_idx + *p.index(); - let end_idx = *self.index(); - let span = Some(TextSpan::from_byte_span(self.input(), ini_idx..end_idx)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = None; // should we pass the book's source here? - let source = Source { file, span, kind }; + // Try to parse signature + if let Ok((name, args, typ)) = self.parse_def_sig() { + if self.try_consume("=") { + // Single rule with signature + let body = self.parse_term()?; + let pats = args.into_iter().map(|nam| Pattern::Var(Some(nam))).collect(); + let rules = vec![Rule { pats, body }]; + let end_idx = *self.index(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let def = FunDefinition { name, typ, check: true, rules, source }; + Ok(def) + } else { + // Multiple rules with signature + let mut rules = vec![]; + let (_, rule) = self.parse_rule()?; + rules.push(rule); + while self.starts_with_rule(&name) { + let (_, rule) = self.parse_rule()?; + rules.push(rule); + } + let end_idx = *self.index(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let def = FunDefinition { name, typ, check: true, rules, source }; + Ok(def) + } + } else { + // Was not a signature, backtrack and read the name from the first rule + self.index = ini_idx; + let mut rules = vec![]; + let (name, rule) = self.parse_rule()?; + rules.push(rule); + while self.starts_with_rule(&name) { + let (_, rule) = self.parse_rule()?; + rules.push(rule); + } + let end_idx = *self.index(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let def = FunDefinition { name, typ: Type::Any, check: true, rules, source }; + Ok(def) + } + } + + /// Parses a function definition signature. + /// Returns the name, name of the arguments and the type of the function. + fn parse_def_sig(&mut self) -> ParseResult<(Name, Vec, Type)> { + // '(' name ((arg | '(' arg (':' type)? ')'))* ')' ':' type + // name ((arg | '(' arg (':' type)? ')'))* ':' type + let mut type_vars = IndexSet::new(); + let (name, args, typ) = if self.try_consume("(") { + let name = self.parse_top_level_name()?; + let args = self.list_like(|p| p.parse_def_sig_arg(&mut type_vars), "", ")", "", false, 0)?; + self.consume(":")?; + let typ = self.parse_type_term(&mut type_vars)?; + (name, args, typ) + } else { + let name = self.parse_top_level_name()?; + let args = self.list_like(|p| p.parse_def_sig_arg(&mut type_vars), "", ":", "", false, 0)?; + let typ = self.parse_type_term(&mut type_vars)?; + (name, args, typ) + }; + let (args, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip(); + let typ = arg_types.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(acc), Box::new(typ))); + let typ = type_vars.into_iter().rfold(typ, |acc, typ| Type::All(typ, Box::new(acc))); + Ok((name, args, typ)) + } - let def = HvmDefinition { name: name.clone(), body, source }; - Ok(def) + fn parse_def_sig_arg(&mut self, type_vars: &mut IndexSet) -> ParseResult<(Name, Type)> { + // name + // '(' name ')' + // '(' name ':' type ')' + if self.try_consume("(") { + let name = self.parse_var_name()?; + let typ = if self.try_consume(":") { self.parse_type_term(type_vars)? } else { Type::Any }; + self.consume(")")?; + Ok((name, typ)) + } else { + let name = self.parse_var_name()?; + Ok((name, Type::Any)) + } } fn parse_from_import(&mut self) -> ParseResult { // from path import package // from path import (a, b) // from path import * + self.parse_keyword("from")?; + self.skip_trivia_inline()?; + let path = self.parse_restricted_name("Path")?; + self.skip_trivia_inline()?; + self.consume("import")?; + self.skip_trivia_inline()?; let relative = path.starts_with("./") | path.starts_with("../"); @@ -347,6 +417,9 @@ impl<'a> TermParser<'a> { let pats = self.list_like(|p| p.parse_pattern(false), "", ")", "", false, 0)?; Ok((name, pats)) } else { + // Rule without parens + // Here we use a different label for the error because this is + // the last alternative case for top-level definitions. let name = self.labelled(|p| p.parse_top_level_name(), "top-level definition")?; let mut pats = vec![]; self.skip_trivia(); @@ -369,6 +442,24 @@ impl<'a> TermParser<'a> { Ok((name, rule)) } + fn starts_with_rule(&mut self, expected_name: &Name) -> bool { + let ini_idx = *self.index(); + let res = self.parse_rule_lhs(); + self.index = ini_idx; + if let Ok((name, _)) = res { + if &name == expected_name { + // Found rule with the expected name + true + } else { + // Found rule with a different name + false + } + } else { + // Not a rule + false + } + } + fn parse_pattern(&mut self, simple: bool) -> ParseResult { maybe_grow(|| { let (tag, unexpected_tag) = self.parse_tag()?; @@ -438,7 +529,7 @@ impl<'a> TermParser<'a> { unexpected_tag(self)?; self.advance_one(); self.skip_trivia(); - let name = self.parse_bend_name()?; + let name = self.parse_var_name()?; return Ok(Pattern::Chn(name)); } @@ -488,11 +579,9 @@ impl<'a> TermParser<'a> { // jk, actually a tuple if self.starts_with(",") && opr == Op::MUL { - let mut els = vec![Term::Era]; - while self.try_consume(",") { - els.push(self.parse_term()?); - } - self.consume(")")?; + self.consume_exactly(",")?; + let tail = self.list_like(|p| p.parse_term(), "", ")", ",", true, 1)?; + let els = [Term::Era].into_iter().chain(tail).collect(); return Ok(Term::Fan { fan: FanKind::Tup, tag: tag.unwrap_or(Tag::Static), els }); } @@ -570,7 +659,7 @@ impl<'a> TermParser<'a> { self.advance_one(); unexpected_tag(self)?; self.skip_trivia(); - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; return Ok(Term::Link { nam }); } @@ -621,7 +710,7 @@ impl<'a> TermParser<'a> { if self.try_parse_keyword("use") { unexpected_tag(self)?; self.skip_trivia(); - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; self.consume("=")?; let val = self.parse_term()?; self.try_consume(";"); @@ -654,41 +743,9 @@ impl<'a> TermParser<'a> { // Def if self.try_parse_keyword("def") { self.skip_trivia(); - let (cur_name, rule) = self.parse_rule()?; - let mut rules = vec![rule]; - // the current index to backtrack in case of fail to parse the next rule. - let mut nxt_term = *self.index(); - loop { - self.skip_trivia(); - // save the start position of the rule that can be a next def term. - let nxt_def = *self.index(); - match self.parse_rule() { - Ok((name, rule)) => { - if name == "def" { - // parse the nxt def term. - self.index = nxt_def; - let span = Some(TextSpan::from_byte_span(self.input(), nxt_def..*self.index())); - let source = Source { span, file: None, kind: SourceKind::User }; - let def = FunDefinition::new(name, rules, source); - return Ok(Term::Def { def, nxt: Box::new(self.parse_term()?) }); - } - if name == cur_name { - rules.push(rule); - // save the current position. - nxt_term = *self.index(); - } else { - let cur = *self.index(); - let msg = format!("Expected a rule with name '{cur_name}'."); - return self.with_ctx(Err(msg), nxt_def..cur); - } - } - // if failed it is a term. - Err(_) => break self.index = nxt_term, - } - } + let mut def = self.parse_fun_def()?; + def.source.kind = SourceKind::Generated; let nxt = self.parse_term()?; - let span = Some(TextSpan::from_byte_span(self.input(), nxt_term..*self.index())); - let def = FunDefinition::new(cur_name, rules, Source { span, file: None, kind: SourceKind::User }); return Ok(Term::Def { def, nxt: Box::new(nxt) }); } @@ -798,7 +855,7 @@ impl<'a> TermParser<'a> { unexpected_tag(self)?; let args = self.list_like( |p| { - let bind = p.parse_bend_name()?; + let bind = p.parse_var_name()?; let init = if p.try_consume("=") { p.parse_term()? } else { Term::Var { nam: bind.clone() } }; Ok((bind, init)) }, @@ -835,7 +892,7 @@ impl<'a> TermParser<'a> { self.skip_trivia(); let typ = self.parse_top_level_name()?; self.skip_trivia(); - let var = self.parse_bend_name()?; + let var = self.parse_var_name()?; self.try_consume(";"); let bod = self.parse_term()?; return Ok(Term::Open { typ, var, bod: Box::new(bod) }); @@ -843,7 +900,7 @@ impl<'a> TermParser<'a> { // Var unexpected_tag(self)?; - let nam = self.labelled(|p| p.parse_bend_name(), "term")?; + let nam = self.labelled(|p| p.parse_var_name(), "term")?; Ok(Term::Var { nam }) }) } @@ -854,7 +911,7 @@ impl<'a> TermParser<'a> { if p.try_consume_exactly("*") { Ok(None) } else { - let nam = p.parse_bend_name()?; + let nam = p.parse_var_name()?; Ok(Some(nam)) } }, @@ -908,7 +965,7 @@ impl<'a> TermParser<'a> { /// A named arg with non-optional name. fn parse_named_arg(&mut self) -> ParseResult<(Option, Term)> { - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; self.skip_trivia(); if self.starts_with("=") { self.advance_one(); @@ -940,59 +997,84 @@ impl<'a> TermParser<'a> { Ok((nam, vec![], bod)) } - fn add_fun_def( - &mut self, - name: &Name, - rule: Rule, - builtin: bool, - last_rule: &Option, - book: &mut ParseBook, - span: Range, - ) -> ParseResult<()> { - match (book.fun_defs.get_mut(name), last_rule) { - // Continuing with a new rule to the current definition - (Some(def), Some(last_rule)) if last_rule == name => { - def.rules.push(rule); - if let Some(s) = &mut def.source.span { - s.end = TextLocation::from_byte_loc(self.input(), span.end); + fn parse_type_term(&mut self, vars: &mut IndexSet) -> ParseResult { + let mut left = self.parse_type_atom(vars)?; + self.skip_trivia(); + while self.try_consume_exactly("->") { + let right = self.parse_type_term(vars)?; + left = Type::Arr(Box::new(left), Box::new(right)); + } + Ok(left) + } + + /// Parses a type without an ending arrow. + /// Either an atom, a tuple, a ctr or a parenthesized type. + fn parse_type_atom(&mut self, vars: &mut IndexSet) -> ParseResult { + self.skip_trivia(); + if self.try_parse_keyword("Any") { + Ok(Type::Any) + } else if self.try_parse_keyword("U24") { + Ok(Type::U24) + } else if self.try_parse_keyword("I24") { + Ok(Type::I24) + } else if self.try_parse_keyword("F24") { + Ok(Type::F24) + } else if self.try_consume_exactly("(") { + // Tuple, constructor or parenthesized expression + let ini_idx = *self.index(); + let head = self.parse_type_term(vars)?; + self.skip_trivia(); + if self.try_consume_exactly(")") { + // Parens + Ok(head) + } else if self.try_consume_exactly(",") { + // Tuple + let mut types = vec![head]; + loop { + types.push(self.parse_type_term(vars)?); + self.skip_trivia(); + if !self.try_consume_exactly(",") { + break; + } } + self.consume(")")?; + Ok(Type::Tup(types)) + } else { + // Constructor + let Type::Var(nam) = head else { + let end_idx = *self.index(); + // TODO: This is not a good error message + return self.expected_spanned("type constructor", ini_idx..end_idx); + }; + let mut args = vec![]; + // We know there's at least one argument, otherwise it would go in the parens case. + while !self.try_consume(")") { + args.push(self.parse_type_term(vars)?); + self.skip_trivia(); + } + Ok(Type::Ctr(nam, args)) } - // Trying to add a new rule to a previous definition, coming from a different rule. - (Some(def), Some(_)) => { - let msg = Self::redefinition_of_function_msg(def.is_builtin(), name); - return self.with_ctx(Err(msg), span); - } - // Trying to add a new rule to a previous definition, coming from another kind of top-level. - (Some(def), None) => { - let msg = Self::redefinition_of_function_msg(def.is_builtin(), name); - return self.with_ctx(Err(msg), span); - } - // Adding the first rule of a new definition - (None, _) => { - self.check_top_level_redefinition(name, book, span.clone())?; - let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); - let file = Some(book.source.to_string()); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let source = Source { file, span, kind }; - book.fun_defs.insert(name.clone(), FunDefinition::new(name.clone(), vec![rule], source)); - } + } else { + // Variable + // TODO: This will show "expected Name" instead of "expected type" + let nam = self.parse_var_name()?; + Ok(Type::Var(nam)) } + } + + fn add_fun_def(&mut self, def: FunDefinition, book: &mut ParseBook, span: Range) -> ParseResult<()> { + self.check_top_level_redefinition(&def.name, book, span)?; + book.fun_defs.insert(def.name.clone(), def); Ok(()) } fn add_imp_def( &mut self, - mut def: crate::imp::Definition, + def: crate::imp::Definition, book: &mut ParseBook, span: Range, - builtin: bool, ) -> ParseResult<()> { - self.check_top_level_redefinition(&def.name, book, span.clone())?; - let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = Some(book.source.to_string()); - let source = Source { file, span, kind }; - def.source = source; + self.check_top_level_redefinition(&def.name, book, span)?; book.imp_defs.insert(def.name.clone(), def); Ok(()) } @@ -1003,78 +1085,22 @@ impl<'a> TermParser<'a> { Ok(()) } - fn add_imp_type( - &mut self, - enum_: Enum, - book: &mut ParseBook, - range: Range, - builtin: bool, - ) -> ParseResult<()> { - self.check_type_redefinition(&enum_.name, book, range.clone())?; - - let span = Some(TextSpan::from_byte_span(self.input(), range.start..range.end)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = Some(book.source.to_string()); - let source = Source { file, span, kind }; - - let mut adt = Adt { ctrs: Default::default(), source }; - for variant in enum_.variants { - self.check_top_level_redefinition(&enum_.name, book, range.clone())?; - book.ctrs.insert(variant.name.clone(), enum_.name.clone()); - adt.ctrs.insert(variant.name, variant.fields); - } - book.adts.insert(enum_.name.clone(), adt); - Ok(()) - } - - fn add_fun_type( - &mut self, - book: &mut ParseBook, - nam: Name, - adt: Adt, - span: Range, - ) -> ParseResult<()> { - if book.adts.contains_key(&nam) { - let msg = TermParser::redefinition_of_type_msg(&nam); - return self.with_ctx(Err(msg), span); - } else { - for ctr in adt.ctrs.keys() { - if let Some(builtin) = book.contains_builtin_def(ctr) { - let msg = TermParser::redefinition_of_function_msg(builtin, ctr); - return self.expected_and("function", &msg); - } - match book.ctrs.entry(ctr.clone()) { - indexmap::map::Entry::Vacant(e) => _ = e.insert(nam.clone()), - indexmap::map::Entry::Occupied(e) => { - let msg = TermParser::redefinition_of_constructor_msg(e.key()); - return self.with_ctx(Err(msg), span); - } + fn add_type_def(&mut self, adt: Adt, book: &mut ParseBook, span: Range) -> ParseResult<()> { + self.check_type_redefinition(&adt.name, book, span.clone())?; + for ctr in adt.ctrs.keys() { + if let Some(builtin) = book.contains_builtin_def(ctr) { + let msg = FunParser::redefinition_of_function_msg(builtin, ctr); + return self.with_ctx(Err(msg), span); + } + match book.ctrs.entry(ctr.clone()) { + indexmap::map::Entry::Vacant(e) => _ = e.insert(adt.name.clone()), + indexmap::map::Entry::Occupied(e) => { + let msg = FunParser::redefinition_of_constructor_msg(e.key()); + return self.with_ctx(Err(msg), span); } } - book.adts.insert(nam.clone(), adt); } - Ok(()) - } - - fn add_object( - &mut self, - obj: Variant, - book: &mut ParseBook, - span: Range, - builtin: bool, - ) -> ParseResult<()> { - self.check_type_redefinition(&obj.name, book, span.clone())?; - self.check_top_level_redefinition(&obj.name, book, span.clone())?; - - let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = Some(book.source.to_string()); - let source = Source { file, span, kind }; - - let mut adt = Adt { ctrs: Default::default(), source }; - book.ctrs.insert(obj.name.clone(), obj.name.clone()); - adt.ctrs.insert(obj.name.clone(), obj.fields); - book.adts.insert(obj.name, adt); + book.adts.insert(adt.name.clone(), adt); Ok(()) } @@ -1113,7 +1139,7 @@ impl<'a> TermParser<'a> { } } -impl<'a> Parser<'a> for TermParser<'a> { +impl<'a> Parser<'a> for FunParser<'a> { fn input(&mut self) -> &'a str { self.input } @@ -1224,7 +1250,7 @@ impl Indent { } } -impl<'a> ParserCommons<'a> for TermParser<'a> {} +impl<'a> ParserCommons<'a> for FunParser<'a> {} pub trait ParserCommons<'a>: Parser<'a> { /// Generates an error message that does not print expected terms. @@ -1247,9 +1273,9 @@ pub trait ParserCommons<'a>: Parser<'a> { fn parse_restricted_name(&mut self, kind: &str) -> ParseResult { let ini_idx = *self.index(); - let name = self.take_while(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/'); + let name = self.take_while(is_name_char); if name.is_empty() { - self.expected("name")? + self.expected(&format!("{kind} name"))? } let name = Name::new(name.to_owned()); let end_idx = *self.index(); @@ -1268,7 +1294,7 @@ pub trait ParserCommons<'a>: Parser<'a> { self.parse_restricted_name("Top-level") } - fn parse_bend_name(&mut self) -> ParseResult { + fn parse_var_name(&mut self) -> ParseResult { self.parse_restricted_name("Variable") } @@ -1389,6 +1415,7 @@ pub trait ParserCommons<'a>: Parser<'a> { self.with_ctx(Err(msg), span) } + /// Same as `expected_spanned` but adds an information message before the expected message. fn expected_spanned_and(&mut self, exp: &str, msg: &str, span: Range) -> ParseResult { let is_eof = self.is_eof(); let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) }); @@ -1429,12 +1456,7 @@ pub trait ParserCommons<'a>: Parser<'a> { } fn try_parse_keyword(&mut self, keyword: &str) -> bool { - if !self.starts_with(keyword) { - return false; - } - let input = &self.input()[*self.index() + keyword.len()..]; - let next_is_name = input.chars().next().map_or(false, is_name_char); - if !next_is_name { + if self.starts_with_keyword(keyword) { self.consume_exactly(keyword).unwrap(); true } else { @@ -1455,6 +1477,16 @@ pub trait ParserCommons<'a>: Parser<'a> { } } + fn starts_with_keyword(&mut self, keyword: &str) -> bool { + if self.starts_with(keyword) { + let input = &self.input()[*self.index() + keyword.len()..]; + let next_is_name = input.chars().next().map_or(false, is_name_char); + !next_is_name + } else { + false + } + } + /// Parses a list-like structure like "[x1, x2, x3,]". /// Since a list is always well terminated, we consume newlines. /// @@ -1466,7 +1498,7 @@ pub trait ParserCommons<'a>: Parser<'a> { /// `min_els` determines how many elements must be parsed at minimum. fn list_like( &mut self, - parser: impl Fn(&mut Self) -> ParseResult, + mut parser: impl FnMut(&mut Self) -> ParseResult, start: &str, end: &str, sep: &str, @@ -1704,6 +1736,22 @@ pub trait ParserCommons<'a>: Parser<'a> { Ok(result) } + fn check_repeated_ctr_fields( + &mut self, + fields: &[CtrField], + ctr_name: &Name, + span: Range, + ) -> ParseResult<()> { + for i in 0..fields.len() { + let field = &fields[i]; + if fields.iter().skip(i + 1).any(|a: &CtrField| a.nam == field.nam) { + let msg = format!("Found a repeated field '{}' in constructor {}.", field.nam, ctr_name); + return self.with_ctx(Err(msg), span); + } + } + Ok(()) + } + fn redefinition_of_function_msg(builtin: bool, function_name: &str) -> String { if builtin { format!("Redefinition of builtin (function) '{function_name}'.") diff --git a/src/fun/transform/definition_merge.rs b/src/fun/transform/definition_merge.rs index 54e9b8ef5..a35a34454 100644 --- a/src/fun/transform/definition_merge.rs +++ b/src/fun/transform/definition_merge.rs @@ -42,10 +42,12 @@ impl Book { // it's based on. // This could be done by having SourceKind::Generated contain a Vec or Vec. let any_def_name = equal_defs.iter().next().unwrap(); // we know we can unwrap since equal_defs.len() > 1 - let source = self.defs[any_def_name].source.clone(); // Add the merged def - let new_def = Definition::new_gen(new_name.clone(), vec![Rule { pats: vec![], body: term }], source); + let source = self.defs[any_def_name].source.clone(); + let rules = vec![Rule { pats: vec![], body: term }]; + // Note: This will erase types, so type checking needs to come before this. + let new_def = Definition::new_gen(new_name.clone(), rules, source, false); self.defs.insert(new_name.clone(), new_def); // Remove the old ones and write the map of old names to new ones. for name in equal_defs { diff --git a/src/fun/transform/desugar_bend.rs b/src/fun/transform/desugar_bend.rs index 3d0753c1f..e153a0af0 100644 --- a/src/fun/transform/desugar_bend.rs +++ b/src/fun/transform/desugar_bend.rs @@ -15,7 +15,9 @@ impl Ctx<'_> { for def in self.book.defs.values_mut() { let mut fresh = 0; for rule in def.rules.iter_mut() { - if let Err(err) = rule.body.desugar_bend(&def.name, &mut fresh, &mut new_defs, &def.source) { + if let Err(err) = + rule.body.desugar_bend(&def.name, &mut fresh, &mut new_defs, def.source.clone(), def.check) + { self.info.add_function_error(err, def.name.clone(), def.source.clone()); break; } @@ -34,12 +36,13 @@ impl Term { def_name: &Name, fresh: &mut usize, new_defs: &mut IndexMap, - source: &Source, + source: Source, + check: bool, ) -> Result<(), String> { maybe_grow(|| { // Recursively encode bends in the children for child in self.children_mut() { - child.desugar_bend(def_name, fresh, new_defs, source)?; + child.desugar_bend(def_name, fresh, new_defs, source.clone(), check)?; } // Convert a bend into a new recursive function and call it. @@ -87,7 +90,7 @@ impl Term { let body = Term::rfold_lams(body, free_vars.iter().cloned().map(Some)); // Make a definition from the new function - let def = Definition::new(new_nam.clone(), vec![Rule { pats: vec![], body }], source.clone()); + let def = Definition::new_gen(new_nam.clone(), vec![Rule { pats: vec![], body }], source, check); new_defs.insert(new_nam.clone(), def); // Call the new function in the original term. diff --git a/src/fun/transform/desugar_fold.rs b/src/fun/transform/desugar_fold.rs index 492cc9ed1..63af7ac73 100644 --- a/src/fun/transform/desugar_fold.rs +++ b/src/fun/transform/desugar_fold.rs @@ -34,14 +34,17 @@ impl Ctx<'_> { for def in self.book.defs.values_mut() { let mut fresh = 0; for rule in def.rules.iter_mut() { - let res = rule.body.desugar_fold( - &def.name, - &mut fresh, - &mut new_defs, - &self.book.ctrs, - &self.book.adts, - &def.source, - ); + let mut ctx = DesugarFoldCtx { + def_name: &def.name, + fresh: &mut fresh, + new_defs: &mut new_defs, + ctrs: &self.book.ctrs, + adts: &self.book.adts, + source: def.source.clone(), + check: def.check, + }; + + let res = rule.body.desugar_fold(&mut ctx); if let Err(e) = res { self.info.add_function_error(e, def.name.clone(), def.source.clone()); } @@ -54,19 +57,21 @@ impl Ctx<'_> { } } +struct DesugarFoldCtx<'a> { + pub def_name: &'a Name, + pub fresh: &'a mut usize, + pub new_defs: &'a mut Vec, + pub ctrs: &'a Constructors, + pub adts: &'a Adts, + pub source: Source, + pub check: bool, +} + impl Term { - pub fn desugar_fold( - &mut self, - def_name: &Name, - fresh: &mut usize, - new_defs: &mut Vec, - ctrs: &Constructors, - adts: &Adts, - source: &Source, - ) -> Result<(), String> { + fn desugar_fold(&mut self, ctx: &mut DesugarFoldCtx<'_>) -> Result<(), String> { maybe_grow(|| { for child in self.children_mut() { - child.desugar_fold(def_name, fresh, new_defs, ctrs, adts, source)?; + child.desugar_fold(ctx)?; } if let Term::Fold { .. } = self { @@ -90,19 +95,19 @@ impl Term { } let free_vars = free_vars.into_iter().collect::>(); - let new_nam = Name::new(format!("{}__fold{}", def_name, fresh)); - *fresh += 1; + let new_nam = Name::new(format!("{}__fold{}", ctx.def_name, ctx.fresh)); + *ctx.fresh += 1; // Substitute the implicit recursive calls to call the new function let ctr = arms[0].0.as_ref().unwrap(); - let adt_nam = ctrs.get(ctr).unwrap(); - let ctrs = &adts.get(adt_nam).unwrap().ctrs; + let adt_nam = ctx.ctrs.get(ctr).unwrap(); + let ctrs = &ctx.adts.get(adt_nam).unwrap().ctrs; for arm in arms.iter_mut() { let ctr = arm.0.as_ref().unwrap(); let recursive = arm .1 .iter() - .zip(ctrs.get(ctr).unwrap()) + .zip(&ctrs.get(ctr).unwrap().fields) .filter_map(|(var, field)| if field.rec { Some(var.as_ref().unwrap().clone()) } else { None }) .collect::>(); arm.2.call_recursive(&new_nam, &recursive, &free_vars); @@ -121,8 +126,13 @@ impl Term { let body = Term::rfold_lams(body, free_vars.iter().map(|nam| Some(nam.clone()))); let body = Term::lam(Pattern::Var(Some(x_nam)), body); - let def = Definition::new(new_nam.clone(), vec![Rule { pats: vec![], body }], source.clone()); - new_defs.push(def); + let def = Definition::new_gen( + new_nam.clone(), + vec![Rule { pats: vec![], body }], + ctx.source.clone(), + ctx.check, + ); + ctx.new_defs.push(def); // Call the new function let call = Term::call(Term::Ref { nam: new_nam.clone() }, [std::mem::take(arg.as_mut())]); diff --git a/src/fun/transform/desugar_match_defs.rs b/src/fun/transform/desugar_match_defs.rs index da7939d0b..22fd1f385 100644 --- a/src/fun/transform/desugar_match_defs.rs +++ b/src/fun/transform/desugar_match_defs.rs @@ -460,8 +460,8 @@ fn switch_rule( let old_args = args.split_off(1); let mut new_arms = vec![]; - for (ctr, fields) in &adts[&adt_name].ctrs { - let new_args = fields.iter().map(|f| Name::new(format!("{}.{}", arg, f.nam))); + for (ctr_nam, ctr) in &adts[&adt_name].ctrs { + let new_args = ctr.fields.iter().map(|f| Name::new(format!("{}.{}", arg, f.nam))); let args = new_args.clone().chain(old_args.clone()).collect(); let mut new_rules = vec![]; @@ -472,7 +472,7 @@ fn switch_rule( // (Ctr pat0_0 ... pat0_m) pat1 ... patN: body // becomes // pat0_0 ... pat0_m pat1 ... patN: body - Pattern::Ctr(found_ctr, new_pats) if ctr == found_ctr => { + Pattern::Ctr(found_ctr, new_pats) if ctr_nam == found_ctr => { let pats = new_pats.iter().cloned().chain(old_pats).collect(); let body = rule.body.clone(); let rule = Rule { pats, body }; @@ -488,7 +488,7 @@ fn switch_rule( let pats = new_pats.chain(old_pats.clone()).collect(); let mut body = rule.body.clone(); let reconstructed_var = - Term::call(Term::Ref { nam: ctr.clone() }, new_args.clone().map(|nam| Term::Var { nam })); + Term::call(Term::Ref { nam: ctr_nam.clone() }, new_args.clone().map(|nam| Term::Var { nam })); if let Some(var) = var { body = Term::Use { nam: Some(var.clone()), val: Box::new(reconstructed_var), nxt: Box::new(body) }; @@ -501,11 +501,11 @@ fn switch_rule( } if new_rules.is_empty() { - return Err(DesugarMatchDefErr::AdtNotExhaustive { adt: adt_name, ctr: ctr.clone() }); + return Err(DesugarMatchDefErr::AdtNotExhaustive { adt: adt_name, ctr: ctr_nam.clone() }); } let body = simplify_rule_match(args, new_rules, with.clone(), ctrs, adts)?; - new_arms.push((Some(ctr.clone()), new_args.map(Some).collect(), body)); + new_arms.push((Some(ctr_nam.clone()), new_args.map(Some).collect(), body)); } // Linearize previously matched vars and current args. diff --git a/src/fun/transform/encode_adts.rs b/src/fun/transform/encode_adts.rs index 9ef6c62cd..25b2e84d7 100644 --- a/src/fun/transform/encode_adts.rs +++ b/src/fun/transform/encode_adts.rs @@ -1,5 +1,5 @@ use crate::{ - fun::{Book, Definition, Name, Num, Pattern, Rule, Source, Term}, + fun::{Book, Definition, Name, Num, Pattern, Rule, Source, Term, Type}, AdtEncoding, }; @@ -10,14 +10,14 @@ impl Book { let mut tags = vec![]; for (adt_name, adt) in self.adts.iter() { - for (ctr_idx, (ctr_name, fields)) in adt.ctrs.iter().enumerate() { + for (ctr_idx, (ctr_name, ctr)) in adt.ctrs.iter().enumerate() { let ctrs: Vec<_> = adt.ctrs.keys().cloned().collect(); let body = match adt_encoding { - AdtEncoding::Scott => encode_ctr_scott(fields.iter().map(|f| &f.nam), ctrs, ctr_name), + AdtEncoding::Scott => encode_ctr_scott(ctr.fields.iter().map(|f| &f.nam), ctrs, ctr_name), AdtEncoding::NumScott => { let tag = make_tag(adt_name == ctr_name, ctr_name); - let body = encode_ctr_num_scott(fields.iter().map(|f| &f.nam), &tag); + let body = encode_ctr_num_scott(ctr.fields.iter().map(|f| &f.nam), &tag); let tag_def = make_tag_def(ctr_idx, &tag, adt.source.clone()); tags.push((tag, tag_def)); body @@ -25,7 +25,13 @@ impl Book { }; let rules = vec![Rule { pats: vec![], body }]; - let def = Definition::new(ctr_name.clone(), rules, adt.source.clone()); + let def = Definition { + name: ctr_name.clone(), + typ: ctr.typ.clone(), + check: true, + rules, + source: adt.source.clone(), + }; defs.push((ctr_name.clone(), def)); } } @@ -66,6 +72,6 @@ fn encode_ctr_num_scott<'a>(ctr_args: impl DoubleEndedIterator } fn make_tag_def(ctr_idx: usize, tag: &Name, source: Source) -> Definition { - let tag_rule = vec![Rule { pats: vec![], body: Term::Num { val: Num::U24(ctr_idx as u32) } }]; - Definition::new_gen(tag.clone(), tag_rule, source) + let rules = vec![Rule { pats: vec![], body: Term::Num { val: Num::U24(ctr_idx as u32) } }]; + Definition { name: tag.clone(), typ: Type::U24, check: true, rules, source } } diff --git a/src/fun/transform/expand_main.rs b/src/fun/transform/expand_main.rs index c2126eb52..4586c26e7 100644 --- a/src/fun/transform/expand_main.rs +++ b/src/fun/transform/expand_main.rs @@ -65,7 +65,7 @@ impl Term { // If the argument is a 0-ary constructor, we don't need to expand it. if let Term::Ref { nam } = arg { if let Some(adt_nam) = book.ctrs.get(nam) { - if book.adts.get(adt_nam).unwrap().ctrs.get(nam).unwrap().is_empty() { + if book.adts.get(adt_nam).unwrap().ctrs.get(nam).unwrap().fields.is_empty() { continue; } } diff --git a/src/fun/transform/fix_match_defs.rs b/src/fun/transform/fix_match_defs.rs index c7a6e1b1f..c1c53ce8b 100644 --- a/src/fun/transform/fix_match_defs.rs +++ b/src/fun/transform/fix_match_defs.rs @@ -82,7 +82,7 @@ impl Pattern { fn check_good_ctr(&self, ctrs: &Constructors, adts: &Adts, errs: &mut Vec) { if let Pattern::Ctr(nam, args) = self { if let Some(adt) = ctrs.get(nam) { - let expected_arity = adts[adt].ctrs[nam].len(); + let expected_arity = adts[adt].ctrs[nam].fields.len(); let found_arity = args.len(); if expected_arity != found_arity { errs.push(format!( diff --git a/src/fun/transform/fix_match_terms.rs b/src/fun/transform/fix_match_terms.rs index 754cc88f8..fb4001e85 100644 --- a/src/fun/transform/fix_match_terms.rs +++ b/src/fun/transform/fix_match_terms.rs @@ -159,15 +159,15 @@ impl Term { // Build the match arms, with all constructors let mut new_rules = vec![]; - for (ctr, fields) in adt_ctrs.iter() { - let fields = fields.iter().map(|f| Some(match_field(&bnd, &f.nam))).collect::>(); - let body = if let Some(Some(body)) = bodies.remove(ctr) { + for (ctr_nam, ctr) in adt_ctrs.iter() { + let fields = ctr.fields.iter().map(|f| Some(match_field(&bnd, &f.nam))).collect::>(); + let body = if let Some(Some(body)) = bodies.remove(ctr_nam) { body } else { - errs.push(FixMatchErr::NonExhaustiveMatch { typ: adt_nam.clone(), missing: ctr.clone() }); + errs.push(FixMatchErr::NonExhaustiveMatch { typ: adt_nam.clone(), missing: ctr_nam.clone() }); Term::Err }; - new_rules.push((Some(ctr.clone()), fields, body)); + new_rules.push((Some(ctr_nam.clone()), fields, body)); } *arms = new_rules; return; @@ -248,7 +248,7 @@ fn fixed_match_arms<'a>( if let Some(var) = &rules[rule_idx].0 { new_body = Term::Use { nam: Some(var.clone()), - val: Box::new(rebuild_ctr(bnd, ctr, &adts[adt_nam].ctrs[&**ctr])), + val: Box::new(rebuild_ctr(bnd, ctr, &adts[adt_nam].ctrs[&**ctr].fields)), nxt: Box::new(new_body), }; } diff --git a/src/fun/transform/float_combinators.rs b/src/fun/transform/float_combinators.rs index f7870cc5e..2476adf35 100644 --- a/src/fun/transform/float_combinators.rs +++ b/src/fun/transform/float_combinators.rs @@ -45,10 +45,11 @@ impl Book { } let source = def.source.clone(); + let check = def.check; let body = &mut def.rule_mut().body; ctx.reset(); ctx.def_size = body.size(); - body.float_combinators(&mut ctx, def_name, source.clone()); + body.float_combinators(&mut ctx, def_name, source, check); } self.defs.extend(ctx.combinators.into_iter().map(|(nam, (_, def))| (nam, def))); @@ -84,11 +85,17 @@ impl<'b> FloatCombinatorsCtx<'b> { } impl Term { - fn float_combinators(&mut self, ctx: &mut FloatCombinatorsCtx, def_name: &Name, source: Source) { + fn float_combinators( + &mut self, + ctx: &mut FloatCombinatorsCtx, + def_name: &Name, + source: Source, + check: bool, + ) { maybe_grow(|| { // Recursively float the grandchildren terms. for child in self.float_children_mut() { - child.float_combinators(ctx, def_name, source.clone()); + child.float_combinators(ctx, def_name, source.clone(), check); } let mut size = self.size(); @@ -104,14 +111,21 @@ impl Term { if child.is_combinator() && child_size > 0 && (!child_is_safe || extract_for_size) { ctx.def_size -= child_size; size -= child_size; - child.float(ctx, def_name, source.clone(), child_is_safe); + child.float(ctx, def_name, source.clone(), check, child_is_safe); } } }) } /// Inserts a new definition for the given term in the combinators map. - fn float(&mut self, ctx: &mut FloatCombinatorsCtx, def_name: &Name, source: Source, is_safe: bool) { + fn float( + &mut self, + ctx: &mut FloatCombinatorsCtx, + def_name: &Name, + source: Source, + check: bool, + is_safe: bool, + ) { let comb_name = Name::new(format!("{}{}{}", def_name, NAME_SEP, ctx.name_gen)); ctx.name_gen += 1; @@ -119,7 +133,7 @@ impl Term { let extracted_term = std::mem::replace(self, comb_ref); let rules = vec![Rule { body: extracted_term, pats: Vec::new() }]; - let rule = Definition::new_gen(comb_name.clone(), rules, source); + let rule = Definition::new_gen(comb_name.clone(), rules, source, check); ctx.combinators.insert(comb_name, (is_safe, rule)); } } diff --git a/src/fun/transform/lift_local_defs.rs b/src/fun/transform/lift_local_defs.rs index 4cff00dd5..4682deb38 100644 --- a/src/fun/transform/lift_local_defs.rs +++ b/src/fun/transform/lift_local_defs.rs @@ -13,7 +13,7 @@ impl Book { for (name, def) in self.defs.iter_mut() { let mut gen = 0; for rule in def.rules.iter_mut() { - rule.body.lift_local_defs(name, &mut defs, &mut gen); + rule.body.lift_local_defs(name, def.check, &mut defs, &mut gen); } } self.defs.extend(defs); @@ -27,14 +27,20 @@ impl Rule { } impl Term { - pub fn lift_local_defs(&mut self, parent: &Name, defs: &mut IndexMap, gen: &mut usize) { + pub fn lift_local_defs( + &mut self, + parent: &Name, + check: bool, + defs: &mut IndexMap, + gen: &mut usize, + ) { maybe_grow(|| match self { Term::Def { def, nxt } => { let local_name = Name::new(format!("{}__local_{}_{}", parent, gen, def.name)); for rule in def.rules.iter_mut() { - rule.body.lift_local_defs(&local_name, defs, gen); + rule.body.lift_local_defs(&local_name, check, defs, gen); } - nxt.lift_local_defs(parent, defs, gen); + nxt.lift_local_defs(parent, check, defs, gen); *gen += 1; let inner_defs = @@ -46,12 +52,12 @@ impl Term { apply_closure(&mut rules, &fvs); - let new_def = Definition::new_gen(local_name.clone(), rules, source); + let new_def = Definition::new_gen(local_name.clone(), rules, source, check); defs.insert(local_name.clone(), new_def); } _ => { for child in self.children_mut() { - child.lift_local_defs(parent, defs, gen); + child.lift_local_defs(parent, check, defs, gen); } } }) diff --git a/src/imp/mod.rs b/src/imp/mod.rs index 41f020610..0976f3903 100644 --- a/src/imp/mod.rs +++ b/src/imp/mod.rs @@ -3,8 +3,7 @@ mod order_kwargs; pub mod parser; pub mod to_fun; -use crate::fun::{CtrField, Name, Num, Op, Source}; -use indexmap::{IndexMap, IndexSet}; +use crate::fun::{Name, Num, Op, Source, Type}; use interner::global::GlobalString; #[derive(Clone, Debug)] @@ -201,29 +200,17 @@ pub enum Stmt { Err, } -// {name} "{" {field}* "}" -#[derive(Clone, Debug)] -pub struct Variant { - pub name: Name, - pub fields: Vec, -} - // "def" {name} "(" {params} ")" ":" {body} #[derive(Clone, Debug)] pub struct Definition { pub name: Name, - pub params: Vec, + pub typ: Type, + pub check: bool, + pub args: Vec, pub body: Stmt, pub source: Source, } -// "type" {name} ":" {variant}* -#[derive(Clone, Debug)] -pub struct Enum { - pub name: Name, - pub variants: Vec, -} - impl InPlaceOp { pub fn to_lang_op(self) -> Op { match self { @@ -238,23 +225,3 @@ impl InPlaceOp { } } } - -pub trait RepeatedNames { - fn find_repeated_names(&self) -> IndexSet; -} - -impl RepeatedNames for Vec { - fn find_repeated_names(&self) -> IndexSet { - let mut count = IndexMap::new(); - for field in self.iter() { - *count.entry(field.nam.clone()).or_insert(0) += 1; - } - count.into_iter().filter_map(|(name, count)| if count > 1 { Some(name) } else { None }).collect() - } -} - -impl RepeatedNames for Variant { - fn find_repeated_names(&self) -> IndexSet { - self.fields.find_repeated_names() - } -} diff --git a/src/imp/order_kwargs.rs b/src/imp/order_kwargs.rs index 1482841b2..82a626b1f 100644 --- a/src/imp/order_kwargs.rs +++ b/src/imp/order_kwargs.rs @@ -215,7 +215,7 @@ fn get_args_def_or_ctr(name: &Name, book: &ParseBook, use_map: &IndexMap { +pub struct ImpParser<'i> { + pub file: Name, pub input: &'i str, pub index: usize, + pub builtin: bool, } -impl<'a> PyParser<'a> { - pub fn new(input: &'a str) -> Self { - Self { input, index: 0 } +impl<'a> ImpParser<'a> { + pub fn new(file: Name, input: &'a str, builtin: bool) -> Self { + Self { file, input, index: 0, builtin } } -} -impl<'a> ParserCommons<'a> for PyParser<'a> {} + pub fn parse_function_def(&mut self, indent: Indent) -> ParseResult<(Definition, Indent)> { + // def name(arg1: type1, arg2: type2, ...) -> type: + // body + if indent != Indent::Val(0) { + let msg = "Indentation error. Functions defined with 'def' must be at the start of the line."; + let idx = *self.index(); + return self.with_ctx(Err(msg), idx..idx + 1); + } + // TODO: checked vs unchecked functions + let (mut def, nxt_indent) = self.parse_def_aux(indent, true)?; + def.source.kind = if self.builtin { SourceKind::Builtin } else { SourceKind::User }; + Ok((def, nxt_indent)) + } -impl<'a> Parser<'a> for PyParser<'a> { - fn input(&mut self) -> &'a str { - self.input + pub fn parse_type_def(&mut self, mut indent: Indent) -> ParseResult<(Adt, Indent)> { + if indent != Indent::Val(0) { + let msg = "Indentation error. Types defined with 'type' must be at the start of the line."; + let idx = *self.index(); + return self.with_ctx(Err(msg), idx..idx + 1); + } + let ini_idx = *self.index(); + + self.parse_keyword("type")?; + self.skip_trivia_inline()?; + + let type_name = self.parse_top_level_name()?; + self.skip_trivia_inline()?; + + let type_vars = if self.try_consume_exactly("(") { + self.list_like(|p| p.parse_var_name(), "", ")", ",", true, 0)? + } else { + vec![] + }; + self.skip_trivia_inline()?; + + self.consume_exactly(":")?; + self.consume_new_line()?; + indent.enter_level(); + self.consume_indent_exactly(indent)?; + + let mut ctrs = Vec::new(); + let mut nxt_indent = indent; + while nxt_indent == indent { + ctrs.push(self.parse_type_def_variant(&type_name, &type_vars)?); + if !self.is_eof() { + self.consume_new_line()?; + } + nxt_indent = self.consume_indent_at_most(indent)?; + } + indent.exit_level(); + + let ctrs = ctrs.into_iter().map(|ctr| (ctr.name.clone(), ctr)).collect(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..self.index, self.builtin); + let adt = Adt { name: type_name, vars: type_vars, ctrs, source }; + + Ok((adt, nxt_indent)) } - fn index(&mut self) -> &mut usize { - &mut self.index + pub fn parse_object(&mut self, indent: Indent) -> ParseResult<(Adt, Indent)> { + // object Pair(a, b) { fst: a, snd: b } + if indent != Indent::Val(0) { + let msg = "Indentation error. Types defined with 'object' must be at the start of the line."; + let idx = *self.index(); + return self.with_ctx(Err(msg), idx..idx + 1); + } + let ini_idx = *self.index(); + + self.parse_keyword("object")?; + + let type_vars = self.list_like(|p| p.parse_var_name(), "(", ")", ",", true, 0)?; + self.skip_trivia_inline()?; + + let name = self.parse_top_level_name()?; + self.skip_trivia_inline()?; + + let (fields, field_types): (Vec<_>, Vec<_>) = if self.starts_with("{") { + self.list_like(|p| p.parse_variant_field(&mut Default::default()), "{", "}", ",", true, 0)? + } else { + vec![] + } + .into_iter() + .unzip(); + + let end_idx = *self.index(); + self.check_repeated_ctr_fields(&fields, &name, ini_idx..end_idx)?; + + if !self.is_eof() { + self.consume_new_line()?; + } + let nxt_indent = self.advance_newlines()?; + + let typ = Type::Ctr(name.clone(), type_vars.iter().cloned().map(Type::Var).collect()); + let typ = field_types.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))); + let typ = type_vars.iter().cloned().rfold(typ, |acc, typ| Type::All(typ, Box::new(acc))); + let ctr = AdtCtr { name: name.clone(), typ, fields }; + + let ctrs = [(name.clone(), ctr)].into_iter().collect(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let adt = Adt { name, vars: type_vars, ctrs, source }; + Ok((adt, nxt_indent)) } - /// Generates an error message for parsing failures, including the highlighted context. - /// - /// Override to have our own error message. - fn expected(&mut self, exp: &str) -> ParseResult { + pub fn parse_hvm(&mut self) -> ParseResult<(HvmDefinition, Indent)> { let ini_idx = *self.index(); - let end_idx = *self.index() + 1; - self.expected_spanned(exp, ini_idx..end_idx) + + self.parse_keyword("hvm")?; + self.skip_trivia_inline()?; + + let name = self.parse_var_name()?; + self.skip_trivia_inline()?; + + let mut type_vars = IndexSet::new(); + let typ = self.parse_return_type(&mut type_vars)?; + let typ = type_vars.into_iter().rfold(typ, |acc, typ| Type::All(typ, Box::new(acc))); + self.skip_trivia_inline()?; + + self.consume_exactly(":")?; + self.consume_new_line()?; + + // TODO: This will have the wrong index + let net_idx = *self.index(); + let mut p = hvm::ast::CoreParser::new(&self.input[net_idx..]); + let body = p.parse_net()?; + *self.index() = net_idx + *p.index(); + + let source = Source::from_file_span(&self.file, self.input, ini_idx..self.index, self.builtin); + let def = HvmDefinition { name: name.clone(), typ, body, source }; + let nxt_indent = self.advance_newlines()?; + Ok((def, nxt_indent)) } - /// Consumes an instance of the given string, erroring if it is not found. - /// - /// Override to have our own error message. - fn consume(&mut self, text: &str) -> ParseResult<()> { - self.skip_trivia(); - if self.input().get(*self.index()..).unwrap_or_default().starts_with(text) { - *self.index() += text.len(); - Ok(()) + fn parse_type_def_variant(&mut self, type_name: &Name, type_vars: &[Name]) -> ParseResult { + let ini_idx = *self.index(); + let name = self.parse_top_level_name()?; + let name = Name::new(format!("{type_name}/{name}")); + self.skip_trivia_inline()?; + + let fields = if self.try_consume_exactly("{") { + self.list_like(|p| p.parse_variant_field(&mut Default::default()), "", "}", ",", true, 0)? } else { - self.expected(format!("'{text}'").as_str()) - } + vec![] + }; + let (fields, field_types): (Vec<_>, Vec<_>) = fields.into_iter().unzip(); + let end_idx = *self.index(); + self.check_repeated_ctr_fields(&fields, &name, ini_idx..end_idx)?; + + // Type is forall type vars, arrows of the fields, returns the type of the constructor + let typ = Type::Ctr(type_name.clone(), type_vars.iter().cloned().map(Type::Var).collect()); + let typ = field_types.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))); + let typ = type_vars.iter().cloned().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); + + Ok(AdtCtr { name, typ, fields }) } - fn skip_trivia(&mut self) { - while let Some(c) = self.peek_one() { - if c.is_ascii_whitespace() { - self.advance_one(); - continue; - } - if c == '#' { - while let Some(c) = self.peek_one() { - if c != '\n' { - self.advance_one(); - } else { - break; - } - } - self.advance_one(); // Skip the newline character as well - continue; - } - break; - } + fn parse_variant_field(&mut self, vars: &mut IndexSet) -> ParseResult<(CtrField, Type)> { + let rec = self.try_consume_exactly("~"); + self.skip_trivia_inline()?; + + let nam = self.parse_var_name()?; + self.skip_trivia_inline()?; + + let typ = if self.try_consume_exactly(":") { self.parse_type_expr(vars)? } else { Type::Any }; + + Ok((CtrField { nam, rec }, typ)) } -} -impl<'a> PyParser<'a> { - /// ? - /// fn parse_primary_expr(&mut self, inline: bool) -> ParseResult { if inline { self.skip_trivia_inline()?; @@ -86,12 +194,12 @@ impl<'a> PyParser<'a> { self.skip_trivia(); } if self.try_parse_keyword("lambda") | self.try_consume_exactly("λ") { - fn parse_lam_var(p: &mut PyParser) -> ParseResult<(Name, bool)> { + fn parse_lam_var(p: &mut ImpParser) -> ParseResult<(Name, bool)> { if p.starts_with("$") { p.advance_one(); - Ok((p.parse_bend_name()?, true)) + Ok((p.parse_var_name()?, true)) } else { - Ok((p.parse_bend_name()?, false)) + Ok((p.parse_var_name()?, false)) } } let names = self.list_like(|p| parse_lam_var(p), "", ":", ",", false, 1)?; @@ -126,7 +234,7 @@ impl<'a> PyParser<'a> { } else if self.starts_with("$") { // Unscoped var self.advance_one(); - Ok(Expr::Chn { nam: self.parse_bend_name()? }) + Ok(Expr::Chn { nam: self.parse_var_name()? }) } else if self.starts_with("*") { // Era self.advance_one(); @@ -137,7 +245,7 @@ impl<'a> PyParser<'a> { Ok(Expr::Num { val: self.parse_number()? }) } else { // Var - let nam = self.labelled(|p| p.parse_bend_name(), "expression")?; + let nam = self.labelled(|p| p.parse_var_name(), "expression")?; Ok(Expr::Var { nam }) } } else { @@ -270,7 +378,7 @@ impl<'a> PyParser<'a> { fn data_kwarg(&mut self) -> ParseResult<(Name, Expr)> { self.skip_trivia(); - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; self.consume(":")?; let expr = self.parse_expr(false, false)?; Ok((nam, expr)) @@ -297,7 +405,7 @@ impl<'a> PyParser<'a> { if self.try_parse_keyword("for") { // Comprehension self.skip_trivia(); - let bind = self.parse_bend_name()?; + let bind = self.parse_var_name()?; self.skip_trivia(); self.parse_keyword("in")?; let iter = self.parse_expr(false, false)?; @@ -435,25 +543,25 @@ impl<'a> PyParser<'a> { /// Parses a statement and returns the indentation of the next statement. fn parse_statement(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { maybe_grow(|| { - if self.try_parse_keyword("return") { + if self.starts_with_keyword("return") { self.parse_return() - } else if self.try_parse_keyword("def") { + } else if self.starts_with_keyword("def") { self.parse_local_def(indent) - } else if self.try_parse_keyword("if") { + } else if self.starts_with_keyword("if") { self.parse_if(indent) - } else if self.try_parse_keyword("match") { + } else if self.starts_with_keyword("match") { self.parse_match(indent) - } else if self.try_parse_keyword("switch") { + } else if self.starts_with_keyword("switch") { self.parse_switch(indent) - } else if self.try_parse_keyword("fold") { + } else if self.starts_with_keyword("fold") { self.parse_fold(indent) - } else if self.try_parse_keyword("bend") { + } else if self.starts_with_keyword("bend") { self.parse_bend(indent) - } else if self.try_parse_keyword("with") { + } else if self.starts_with_keyword("with") { self.parse_with(indent) - } else if self.try_parse_keyword("open") { + } else if self.starts_with_keyword("open") { self.parse_open(indent) - } else if self.try_parse_keyword("use") { + } else if self.starts_with_keyword("use") { self.parse_use(indent) } else { self.parse_assign(indent) @@ -559,18 +667,25 @@ impl<'a> PyParser<'a> { } fn parse_return(&mut self) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("return")?; + let term = self.parse_expr(true, true)?; self.skip_trivia_inline()?; + self.try_consume_exactly(";"); if !self.is_eof() { self.consume_new_line()?; } let indent = self.advance_newlines()?; + Ok((Stmt::Return { term: Box::new(term) }, indent)) } fn parse_if(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { - let cond = self.parse_expr(true, false)?; + self.parse_keyword("if")?; + self.skip_trivia_inline()?; + + let cond = self.parse_expr(true, true)?; self.skip_trivia_inline()?; self.consume_exactly(":")?; indent.enter_level(); @@ -633,8 +748,12 @@ impl<'a> PyParser<'a> { } fn parse_match(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("match")?; + self.skip_trivia_inline()?; + let (bnd, arg) = self.parse_match_arg()?; self.skip_trivia_inline()?; + let (with_bnd, with_arg) = self.parse_with_clause()?; self.consume_new_line()?; indent.enter_level(); @@ -687,7 +806,7 @@ impl<'a> PyParser<'a> { } fn parse_with_arg(&mut self) -> ParseResult<(Option, Expr)> { - let bind = self.parse_bend_name()?; + let bind = self.parse_var_name()?; self.skip_trivia_inline()?; if self.try_consume("=") { let arg = self.parse_expr(false, false)?; @@ -703,7 +822,7 @@ impl<'a> PyParser<'a> { let pat = if self.try_consume_exactly("_") { None } else { - let nam = self.labelled(|p| p.parse_bend_name(), "name or '_'")?; + let nam = self.labelled(|p| p.parse_var_name(), "name or '_'")?; Some(nam) }; self.skip_trivia_inline()?; @@ -720,8 +839,12 @@ impl<'a> PyParser<'a> { } fn parse_switch(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("switch")?; + self.skip_trivia_inline()?; + let (bnd, arg) = self.parse_match_arg()?; self.skip_trivia_inline()?; + let (with_bnd, with_arg) = self.parse_with_clause()?; indent.enter_level(); @@ -797,9 +920,13 @@ impl<'a> PyParser<'a> { /// /// ... fn parse_fold(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("fold")?; + self.skip_trivia_inline()?; + // Actually identical to match, except the return let (bind, arg) = self.parse_match_arg()?; self.skip_trivia_inline()?; + let (with_bnd, with_arg) = self.parse_with_clause()?; self.consume_new_line()?; indent.enter_level(); @@ -830,6 +957,9 @@ impl<'a> PyParser<'a> { /// "else" ":" /// fn parse_bend(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("bend")?; + self.skip_trivia_inline()?; + let args = self.list_like(|p| p.parse_match_arg(), "", ":", ",", true, 1)?; let (bind, init) = args.into_iter().unzip(); self.consume_new_line()?; @@ -837,8 +967,11 @@ impl<'a> PyParser<'a> { self.consume_indent_exactly(*indent).or(self.expected_spanned("'when'", self.index..self.index + 1))?; self.parse_keyword("when")?; - let cond = self.parse_expr(true, false)?; self.skip_trivia_inline()?; + + let cond = self.parse_expr(true, true)?; + self.skip_trivia_inline()?; + self.consume_exactly(":")?; self.consume_new_line()?; indent.enter_level(); @@ -891,9 +1024,12 @@ impl<'a> PyParser<'a> { /// /// ? fn parse_with(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("with")?; self.skip_trivia_inline()?; - let typ = self.parse_bend_name()?; + + let typ = self.parse_var_name()?; self.skip_trivia_inline()?; + self.consume_exactly(":")?; self.consume_new_line()?; indent.enter_level(); @@ -957,7 +1093,7 @@ impl<'a> PyParser<'a> { } else if self.starts_with("$") { self.advance_one(); self.skip_trivia_inline()?; - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; Ok(AssignPattern::Chn(nam)) } else if self.starts_with("(") { self.advance_one(); @@ -965,159 +1101,190 @@ impl<'a> PyParser<'a> { self.consume(")")?; Ok(assign) } else { - Ok(AssignPattern::Var(self.parse_bend_name()?)) + Ok(AssignPattern::Var(self.parse_var_name()?)) } } /// "open" {typ} ":" {var} ";"? {nxt} fn parse_open(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("open")?; self.skip_trivia_inline()?; - let typ = self.labelled(|p| p.parse_bend_name(), "type name")?; + + let typ = self.labelled(|p| p.parse_var_name(), "type name")?; self.skip_trivia_inline()?; + self.consume_exactly(":")?; self.skip_trivia_inline()?; - let var = self.labelled(|p| p.parse_bend_name(), "variable name")?; + + let var = self.labelled(|p| p.parse_var_name(), "variable name")?; self.skip_trivia_inline()?; + self.try_consume_exactly(";"); self.consume_new_line()?; self.consume_indent_exactly(*indent)?; + let (nxt, nxt_indent) = self.parse_statement(indent)?; + let stmt = Stmt::Open { typ, var, nxt: Box::new(nxt) }; Ok((stmt, nxt_indent)) } fn parse_use(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("use")?; self.skip_trivia_inline()?; - let nam = self.parse_bend_name()?; + + let nam = self.parse_var_name()?; self.skip_trivia_inline()?; + self.consume_exactly("=")?; self.skip_trivia_inline()?; + let bod = self.parse_expr(true, true)?; self.skip_trivia_inline()?; + self.try_consume_exactly(";"); self.consume_new_line()?; self.consume_indent_exactly(*indent)?; + let (nxt, nxt_indent) = self.parse_statement(indent)?; + let stmt = Stmt::Use { nam, val: Box::new(bod), nxt: Box::new(nxt) }; Ok((stmt, nxt_indent)) } fn parse_local_def(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { - let (def, mut nxt_indent) = self.parse_def_aux(*indent)?; + // TODO: checked vs unchecked functions + let (mut def, mut nxt_indent) = self.parse_def_aux(*indent, true)?; + def.source.kind = if self.builtin { SourceKind::Builtin } else { SourceKind::Generated }; let (nxt, nxt_indent) = self.parse_statement(&mut nxt_indent)?; let stmt = Stmt::LocalDef { def: Box::new(def), nxt: Box::new(nxt) }; Ok((stmt, nxt_indent)) } - pub fn parse_def(&mut self, indent: Indent) -> ParseResult<(Definition, Indent)> { - if indent != Indent::Val(0) { - let msg = "Indentation error. Functions defined with 'def' must be at the start of the line."; - let idx = *self.index(); - return self.with_ctx(Err(msg), idx..idx + 1); - } - self.parse_def_aux(indent) + /// Parses a type expression, returning the type and the type variables. + fn parse_type_expr(&mut self, vars: &mut IndexSet) -> ParseResult { + // TODO: We should probably not have it all be inline or not inline. + // For example, in tuple types or constructors, we could have line breaks. + maybe_grow(|| { + self.skip_trivia_inline()?; + let ini_idx = *self.index(); + let lft = if self.try_parse_keyword("Any") { + Type::Any + } else if self.try_parse_keyword("U24") { + Type::U24 + } else if self.try_parse_keyword("I24") { + Type::I24 + } else if self.try_parse_keyword("F24") { + Type::F24 + } else if self.try_consume_exactly("(") { + // Tuple or parenthesized expression + self.skip_trivia(); + let head = self.parse_type_expr(vars)?; + self.skip_trivia(); + if self.try_consume_exactly(")") { + // Parens + head + } else if self.starts_with(",") { + // Tuple + let mut types = vec![head]; + loop { + self.consume_exactly(",")?; + types.push(self.parse_type_expr(vars)?); + if self.try_consume_exactly(")") { + break; + } + if !self.starts_with(",") { + return self.expected("',' or ')'"); + } + } + Type::Tup(types) + } else { + let end_idx = *self.index(); + return self.expected_spanned("tuple type or parenthesized type", ini_idx..end_idx); + } + } else { + // Variable or Constructor + // TODO: This will show "expected Name" instead of "expected type" + let name = self.parse_var_name()?; + self.skip_trivia_inline()?; + if self.try_consume_exactly("(") { + // Constructor with arguments + // name "(" (type ("," type)* ","?)? ")" + + let args = self.list_like(|p| p.parse_type_expr(vars), "", ")", ",", true, 0)?; + Type::Ctr(name, args) + } else { + // Variable + vars.insert(name.clone()); + Type::Var(name) + } + }; + + // Handle arrow types + self.skip_trivia_inline()?; + if self.try_consume_exactly("->") { + let rgt = self.parse_type_expr(vars)?; + Ok(Type::Arr(Box::new(lft), Box::new(rgt))) + } else { + Ok(lft) + } + }) } - fn parse_def_aux(&mut self, mut indent: Indent) -> ParseResult<(Definition, Indent)> { + fn parse_def_aux(&mut self, mut indent: Indent, check: bool) -> ParseResult<(Definition, Indent)> { + let ini_idx = *self.index(); + self.parse_keyword("def")?; self.skip_trivia_inline()?; + let name = self.parse_top_level_name()?; self.skip_trivia_inline()?; - let params = if self.starts_with("(") { - self.list_like(|p| p.parse_bend_name(), "(", ")", ",", true, 0)? + + let mut type_vars = IndexSet::new(); + let args = if self.try_consume_exactly("(") { + self.list_like(|p| p.parse_def_arg(&mut type_vars), "", ")", ",", true, 0)? } else { vec![] }; self.skip_trivia_inline()?; - self.consume_exactly(":")?; - self.consume_new_line()?; - indent.enter_level(); + let (args, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - self.consume_indent_exactly(indent)?; - let (body, nxt_indent) = self.parse_statement(&mut indent)?; - indent.exit_level(); + let ret_type = self.parse_return_type(&mut type_vars)?; + self.skip_trivia_inline()?; - // Temporary source, should be overwritten later - let source = Source { file: None, span: None, kind: SourceKind::Generated }; - let def = Definition { name, params, body, source }; - Ok((def, nxt_indent)) - } + let typ = arg_types.into_iter().rfold(ret_type, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))); + let typ = type_vars.into_iter().rfold(typ, |acc, typ| Type::All(typ, Box::new(acc))); - pub fn parse_type(&mut self, mut indent: Indent) -> ParseResult<(Enum, Indent)> { - if indent != Indent::Val(0) { - let msg = "Indentation error. Types defined with 'type' must be at the start of the line."; - let idx = *self.index(); - return self.with_ctx(Err(msg), idx..idx + 1); - } - - self.skip_trivia_inline()?; - let typ_name = self.parse_top_level_name()?; - self.skip_trivia_inline()?; self.consume_exactly(":")?; self.consume_new_line()?; indent.enter_level(); - self.consume_indent_exactly(indent)?; - let mut variants = Vec::new(); - let mut nxt_indent = indent; - while nxt_indent == indent { - variants.push(self.parse_enum_variant(&typ_name)?); - if !self.is_eof() { - self.consume_new_line()?; - } - nxt_indent = self.consume_indent_at_most(indent)?; - } + + let (body, nxt_indent) = self.parse_statement(&mut indent)?; indent.exit_level(); - let enum_ = Enum { name: typ_name, variants }; - Ok((enum_, nxt_indent)) + // Note: The source kind gets replaced later (generated if a local def, user otherwise) + let source = Source::from_file_span(&self.file, self.input, ini_idx..self.index, self.builtin); + let def = Definition { name, args, typ, check, body, source }; + Ok((def, nxt_indent)) } - pub fn parse_enum_variant(&mut self, typ_name: &Name) -> ParseResult { - let ctr_name = self.parse_top_level_name()?; - let ctr_name = Name::new(format!("{typ_name}/{ctr_name}")); - let mut fields = Vec::new(); + fn parse_def_arg(&mut self, vars: &mut IndexSet) -> ParseResult<(Name, Type)> { + let name = self.parse_var_name()?; self.skip_trivia_inline()?; - if self.starts_with("{") { - fields = self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)?; - } - if let Some(field) = fields.find_repeated_names().into_iter().next() { - let msg = format!("Found a repeated field '{field}' in constructor {ctr_name}."); - return self.expected_message(&msg); + if self.try_consume_exactly(":") { + let typ = self.parse_type_expr(vars)?; + Ok((name, typ)) + } else { + Ok((name, Type::Any)) } - Ok(Variant { name: ctr_name, fields }) } - pub fn parse_object(&mut self, indent: Indent) -> ParseResult<(Variant, Indent)> { - if indent != Indent::Val(0) { - let msg = "Indentation error. Types defined with 'object' must be at the start of the line."; - let idx = *self.index(); - return self.with_ctx(Err(msg), idx..idx + 1); - } - - self.skip_trivia_inline()?; - let name = self.parse_top_level_name()?; - self.skip_trivia_inline()?; - let fields = if self.starts_with("{") { - self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)? + fn parse_return_type(&mut self, vars: &mut IndexSet) -> ParseResult { + if self.try_consume_exactly("->") { + self.parse_type_expr(vars) } else { - vec![] - }; - if let Some(field) = fields.find_repeated_names().into_iter().next() { - let msg = format!("Found a repeated field '{field}' in object {name}."); - return self.expected_message(&msg); - } - if !self.is_eof() { - self.consume_new_line()?; + Ok(Type::Any) } - let nxt_indent = self.advance_newlines()?; - Ok((Variant { name, fields }, nxt_indent)) - } - - fn parse_variant_field(&mut self) -> ParseResult { - let rec = self.try_consume_exactly("~"); - self.skip_trivia(); - let nam = self.parse_bend_name()?; - Ok(CtrField { nam, rec }) } fn expected_indent(&mut self, expected: Indent, got: Indent) -> ParseResult { @@ -1146,6 +1313,61 @@ impl<'a> PyParser<'a> { } } +impl<'a> ParserCommons<'a> for ImpParser<'a> {} + +impl<'a> Parser<'a> for ImpParser<'a> { + fn input(&mut self) -> &'a str { + self.input + } + + fn index(&mut self) -> &mut usize { + &mut self.index + } + + /// Generates an error message for parsing failures, including the highlighted context. + /// + /// Override to have our own error message. + fn expected(&mut self, exp: &str) -> ParseResult { + let ini_idx = *self.index(); + let end_idx = *self.index() + 1; + self.expected_spanned(exp, ini_idx..end_idx) + } + + /// Consumes an instance of the given string, erroring if it is not found. + /// + /// Override to have our own error message. + fn consume(&mut self, text: &str) -> ParseResult<()> { + self.skip_trivia(); + if self.input().get(*self.index()..).unwrap_or_default().starts_with(text) { + *self.index() += text.len(); + Ok(()) + } else { + self.expected(format!("'{text}'").as_str()) + } + } + + fn skip_trivia(&mut self) { + while let Some(c) = self.peek_one() { + if c.is_ascii_whitespace() { + self.advance_one(); + continue; + } + if c == '#' { + while let Some(c) = self.peek_one() { + if c != '\n' { + self.advance_one(); + } else { + break; + } + } + self.advance_one(); // Skip the newline character as well + continue; + } + break; + } + } +} + impl Op { fn precedence(&self) -> usize { match self { @@ -1170,6 +1392,7 @@ impl Op { Op::LOG => todo!(), } } + fn max_precedence() -> usize { 8 } diff --git a/src/imp/to_fun.rs b/src/imp/to_fun.rs index 114427e79..bbbc3204d 100644 --- a/src/imp/to_fun.rs +++ b/src/imp/to_fun.rs @@ -50,9 +50,15 @@ impl Definition { }; let rule = - fun::Rule { pats: self.params.into_iter().map(|param| fun::Pattern::Var(Some(param))).collect(), body }; + fun::Rule { pats: self.args.into_iter().map(|param| fun::Pattern::Var(Some(param))).collect(), body }; - let def = fun::Definition::new(self.name, vec![rule], self.source); + let def = fun::Definition { + name: self.name, + typ: self.typ, + check: self.check, + rules: vec![rule], + source: self.source, + }; Ok(def) } } diff --git a/src/main.rs b/src/main.rs index 02f5b2743..94162c819 100644 --- a/src/main.rs +++ b/src/main.rs @@ -105,7 +105,7 @@ struct RunArgs { #[arg(help = "Path to the input file")] path: PathBuf, - #[arg(value_parser = |arg: &str| bend::fun::parser::TermParser::new(arg).parse_term())] + #[arg(value_parser = |arg: &str| bend::fun::parser::FunParser::new(Name::new(""), arg, false).parse_term())] arguments: Option>, } From 1333bfbc8398d6cc38804046d3b9d1de3b26f4f0 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Mon, 15 Jul 2024 18:26:28 +0200 Subject: [PATCH 02/29] Fix resolution of type vars to type constructors --- src/fun/display.rs | 25 ++++++++- src/fun/mod.rs | 1 + src/fun/parser.rs | 68 ++++++++++++---------- src/fun/transform/mod.rs | 1 + src/fun/transform/resolve_type_ctrs.rs | 78 ++++++++++++++++++++++++++ src/imp/parser.rs | 62 +++++++++----------- src/lib.rs | 1 + 7 files changed, 170 insertions(+), 66 deletions(-) create mode 100644 src/fun/transform/resolve_type_ctrs.rs diff --git a/src/fun/display.rs b/src/fun/display.rs index ad54a761e..f0d93100e 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -1,4 +1,4 @@ -use super::{Book, Definition, FanKind, Name, Num, Op, Pattern, Rule, Tag, Term}; +use super::{Book, Definition, FanKind, Name, Num, Op, Pattern, Rule, Tag, Term, Type}; use crate::maybe_grow; use std::{fmt, ops::Deref, sync::atomic::AtomicU64}; @@ -222,6 +222,7 @@ impl Rule { impl fmt::Display for Definition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { namegen_reset(); + writeln!(f, "{}: {}", self.name, self.typ)?; write!(f, "{}", DisplayJoin(|| self.rules.iter().map(|x| x.display(&self.name)), "\n")) } } @@ -292,6 +293,28 @@ impl Tag { } } +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Type::Hole => write!(f, "_"), + Type::Var(nam) => write!(f, "{nam}"), + Type::All(nam, bod) => write!(f, "∀{nam} {bod}"), + Type::Arr(lft, rgt) => write!(f, "({lft} -> {rgt})"), + Type::Ctr(nam, args) => if args.is_empty() { + write!(f, "{nam}") + } else { + write!(f, "({nam} {})", DisplayJoin(|| args.iter(), ", ")) + }, + Type::U24 => write!(f, "u24"), + Type::I24 => write!(f, "i24"), + Type::F24 => write!(f, "f24"), + Type::Any => write!(f, "Any"), + Type::None => write!(f, "None"), + Type::Tup(els) => write!(f, "({})", DisplayJoin(|| els.iter(), ", ")), + } + } +} + fn var_as_str(nam: &Option) -> &str { nam.as_ref().map_or("*", Name::deref) } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index e3d739610..b843f1c86 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -116,6 +116,7 @@ pub enum Type { U24, F24, I24, + None, } /// A pattern matching rule of a definition. diff --git a/src/fun/parser.rs b/src/fun/parser.rs index df997105d..c0e43a962 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -8,7 +8,7 @@ use crate::{ maybe_grow, }; use highlight_error::highlight_error; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexMap; use itertools::Itertools; use std::ops::Range; use TSPL::{ParseError, Parser}; @@ -221,7 +221,7 @@ impl<'a> FunParser<'a> { Ok(adt) } - fn parse_type_ctr(&mut self, typ_name: &Name, typ_vars: &[Name]) -> ParseResult { + fn parse_type_ctr(&mut self, type_name: &Name, type_vars: &[Name]) -> ParseResult { // '(' name (( '~'? field) | ('~'? '('field (':' type)? ')') )* ')' // name self.skip_trivia(); @@ -231,24 +231,21 @@ impl<'a> FunParser<'a> { self.skip_trivia(); let ctr_name = self.parse_top_level_name()?; - let ctr_name = Name::new(format!("{typ_name}/{ctr_name}")); + let ctr_name = Name::new(format!("{type_name}/{ctr_name}")); let fields = self.list_like(|p| p.parse_type_ctr_field(), "", ")", "", false, 0)?; let (fields, field_types): (Vec<_>, Vec<_>) = fields.into_iter().unzip(); let end_idx = *self.index(); self.check_repeated_ctr_fields(&fields, &ctr_name, ini_idx..end_idx)?; - let typ = Type::Ctr(typ_name.clone(), typ_vars.iter().cloned().map(Type::Var).collect()); - let typ = field_types.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(acc), Box::new(typ))); - let typ = typ_vars.iter().cloned().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); + let typ = make_ctr_type(type_name.clone(), &field_types, type_vars); let ctr = AdtCtr { name: ctr_name, typ, fields }; Ok(ctr) } else { // just name let name = self.labelled(|p| p.parse_top_level_name(), "datatype constructor name")?; - let name = Name::new(format!("{typ_name}/{name}")); - let typ = Type::Ctr(typ_name.clone(), typ_vars.iter().cloned().map(Type::Var).collect()); - let typ = typ_vars.iter().cloned().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); + let name = Name::new(format!("{type_name}/{name}")); + let typ = make_ctr_type(type_name.clone(), &[], type_vars); let ctr = AdtCtr { name, typ, fields: vec![] }; Ok(ctr) } @@ -262,7 +259,7 @@ impl<'a> FunParser<'a> { if self.try_consume("(") { nam = self.parse_var_name()?; if self.try_consume(":") { - typ = self.parse_type_term(&mut Default::default())?; + typ = self.parse_type_term()?; } else { typ = Type::Any; } @@ -324,32 +321,30 @@ impl<'a> FunParser<'a> { fn parse_def_sig(&mut self) -> ParseResult<(Name, Vec, Type)> { // '(' name ((arg | '(' arg (':' type)? ')'))* ')' ':' type // name ((arg | '(' arg (':' type)? ')'))* ':' type - let mut type_vars = IndexSet::new(); let (name, args, typ) = if self.try_consume("(") { let name = self.parse_top_level_name()?; - let args = self.list_like(|p| p.parse_def_sig_arg(&mut type_vars), "", ")", "", false, 0)?; + let args = self.list_like(|p| p.parse_def_sig_arg(), "", ")", "", false, 0)?; self.consume(":")?; - let typ = self.parse_type_term(&mut type_vars)?; + let typ = self.parse_type_term()?; (name, args, typ) } else { let name = self.parse_top_level_name()?; - let args = self.list_like(|p| p.parse_def_sig_arg(&mut type_vars), "", ":", "", false, 0)?; - let typ = self.parse_type_term(&mut type_vars)?; + let args = self.list_like(|p| p.parse_def_sig_arg(), "", ":", "", false, 0)?; + let typ = self.parse_type_term()?; (name, args, typ) }; let (args, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - let typ = arg_types.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(acc), Box::new(typ))); - let typ = type_vars.into_iter().rfold(typ, |acc, typ| Type::All(typ, Box::new(acc))); + let typ = make_fn_type(&arg_types, typ); Ok((name, args, typ)) } - fn parse_def_sig_arg(&mut self, type_vars: &mut IndexSet) -> ParseResult<(Name, Type)> { + fn parse_def_sig_arg(&mut self) -> ParseResult<(Name, Type)> { // name // '(' name ')' // '(' name ':' type ')' if self.try_consume("(") { let name = self.parse_var_name()?; - let typ = if self.try_consume(":") { self.parse_type_term(type_vars)? } else { Type::Any }; + let typ = if self.try_consume(":") { self.parse_type_term()? } else { Type::Any }; self.consume(")")?; Ok((name, typ)) } else { @@ -997,11 +992,11 @@ impl<'a> FunParser<'a> { Ok((nam, vec![], bod)) } - fn parse_type_term(&mut self, vars: &mut IndexSet) -> ParseResult { - let mut left = self.parse_type_atom(vars)?; + fn parse_type_term(&mut self) -> ParseResult { + let mut left = self.parse_type_atom()?; self.skip_trivia(); while self.try_consume_exactly("->") { - let right = self.parse_type_term(vars)?; + let right = self.parse_type_term()?; left = Type::Arr(Box::new(left), Box::new(right)); } Ok(left) @@ -1009,20 +1004,22 @@ impl<'a> FunParser<'a> { /// Parses a type without an ending arrow. /// Either an atom, a tuple, a ctr or a parenthesized type. - fn parse_type_atom(&mut self, vars: &mut IndexSet) -> ParseResult { + fn parse_type_atom(&mut self) -> ParseResult { self.skip_trivia(); if self.try_parse_keyword("Any") { Ok(Type::Any) - } else if self.try_parse_keyword("U24") { + } else if self.try_parse_keyword("None") { + Ok(Type::None) + } else if self.try_parse_keyword("u24") { Ok(Type::U24) - } else if self.try_parse_keyword("I24") { + } else if self.try_parse_keyword("i24") { Ok(Type::I24) - } else if self.try_parse_keyword("F24") { + } else if self.try_parse_keyword("f24") { Ok(Type::F24) } else if self.try_consume_exactly("(") { // Tuple, constructor or parenthesized expression let ini_idx = *self.index(); - let head = self.parse_type_term(vars)?; + let head = self.parse_type_term()?; self.skip_trivia(); if self.try_consume_exactly(")") { // Parens @@ -1031,7 +1028,7 @@ impl<'a> FunParser<'a> { // Tuple let mut types = vec![head]; loop { - types.push(self.parse_type_term(vars)?); + types.push(self.parse_type_term()?); self.skip_trivia(); if !self.try_consume_exactly(",") { break; @@ -1049,7 +1046,7 @@ impl<'a> FunParser<'a> { let mut args = vec![]; // We know there's at least one argument, otherwise it would go in the parens case. while !self.try_consume(")") { - args.push(self.parse_type_term(vars)?); + args.push(self.parse_type_term()?); self.skip_trivia(); } Ok(Type::Ctr(nam, args)) @@ -1226,6 +1223,19 @@ pub fn is_num_char(c: char) -> bool { "0123456789+-".contains(c) } +pub fn make_fn_type(args: &[Type], ret: Type) -> Type { + let typ = ret; + let typ = args.iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ.clone()), Box::new(acc))); + typ +} + +pub fn make_ctr_type(type_name: Name, fields: &[Type], vars: &[Name]) -> Type { + let typ = Type::Ctr(type_name, vars.iter().cloned().map(Type::Var).collect()); + let typ = fields.iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ.clone()), Box::new(acc))); + let typ = vars.iter().rfold(typ, |acc, typ| Type::All(typ.clone(), Box::new(acc))); + typ +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Indent { Val(isize), diff --git a/src/fun/transform/mod.rs b/src/fun/transform/mod.rs index dc702c100..d2c4968a3 100644 --- a/src/fun/transform/mod.rs +++ b/src/fun/transform/mod.rs @@ -18,6 +18,7 @@ pub mod lift_local_defs; pub mod linearize_matches; pub mod linearize_vars; pub mod resolve_refs; +pub mod resolve_type_ctrs; pub mod resugar_list; pub mod resugar_string; pub mod unique_names; diff --git a/src/fun/transform/resolve_type_ctrs.rs b/src/fun/transform/resolve_type_ctrs.rs new file mode 100644 index 000000000..12b0598ec --- /dev/null +++ b/src/fun/transform/resolve_type_ctrs.rs @@ -0,0 +1,78 @@ +use indexmap::IndexSet; + +use crate::{ + diagnostics::Diagnostics, + fun::{Adts, Ctx, Name, Type}, + maybe_grow, +}; + +impl Ctx<'_> { + /// Resolves type constructors in the book and adds for alls for the free type vars. + pub fn resolve_type_ctrs(&mut self) -> Result<(), Diagnostics> { + self.info.start_pass(); + + for def in self.book.defs.values_mut() { + let mut free_vars = Default::default(); + match def.typ.resolve_type_ctrs(&mut vec![], &mut free_vars, &self.book.adts) { + Ok(_) => { + let typ = std::mem::replace(&mut def.typ, Type::Hole); + def.typ = free_vars.into_iter().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); + } + Err(e) => self.info.add_rule_error(e, def.name.clone()), + } + } + + self.info.fatal(()) + } +} + +impl Type { + /// Resolves type constructors in the type. + pub fn resolve_type_ctrs( + &mut self, + scope: &mut Vec, + free_vars: &mut IndexSet, + adts: &Adts, + ) -> Result<(), String> { + maybe_grow(|| { + match self { + Type::Var(nam) => { + // If the variable actually refers to a type, we change the type to a constructor. + if adts.get(nam).is_some() { + // Only change if the name is not being shadowed by a forall. + if !scope.contains(nam) { + *self = Type::Ctr(nam.clone(), vec![]); + } + } else if !scope.contains(nam) && !free_vars.contains(nam) { + // A new free variable, add it to the free var set to be added as a for all later. + free_vars.insert(nam.clone()); + } + } + Type::Ctr(name, args) => { + if !adts.contains_key(name) { + return Err(format!("Found unknown type constructor '{name}'.")); + } + for arg in args { + arg.resolve_type_ctrs(scope, free_vars, adts)?; + } + } + Type::All(nam, body) => { + scope.push(nam.clone()); + body.resolve_type_ctrs(scope, free_vars, adts)?; + scope.pop(); + } + Type::Arr(lft, rgt) => { + lft.resolve_type_ctrs(scope, free_vars, adts)?; + rgt.resolve_type_ctrs(scope, free_vars, adts)?; + } + Type::Tup(els) => { + for el in els { + el.resolve_type_ctrs(scope, free_vars, adts)?; + } + } + Type::Any | Type::Hole | Type::None | Type::U24 | Type::I24 | Type::F24 => {} + } + Ok(()) + }) + } +} diff --git a/src/imp/parser.rs b/src/imp/parser.rs index 3ba677ec6..95065f8be 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -1,12 +1,11 @@ use crate::{ fun::{ - parser::{is_num_char, Indent, ParseResult, ParserCommons}, + parser::{is_num_char, make_ctr_type, make_fn_type, Indent, ParseResult, ParserCommons}, Adt, AdtCtr, CtrField, HvmDefinition, Name, Num, Op, Source, SourceKind, Type, STRINGS, }, imp::{AssignPattern, Definition, Expr, InPlaceOp, MatchArm, Stmt}, maybe_grow, }; -use indexmap::IndexSet; use TSPL::Parser; pub struct ImpParser<'i> { @@ -97,7 +96,7 @@ impl<'a> ImpParser<'a> { self.skip_trivia_inline()?; let (fields, field_types): (Vec<_>, Vec<_>) = if self.starts_with("{") { - self.list_like(|p| p.parse_variant_field(&mut Default::default()), "{", "}", ",", true, 0)? + self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)? } else { vec![] } @@ -112,9 +111,7 @@ impl<'a> ImpParser<'a> { } let nxt_indent = self.advance_newlines()?; - let typ = Type::Ctr(name.clone(), type_vars.iter().cloned().map(Type::Var).collect()); - let typ = field_types.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))); - let typ = type_vars.iter().cloned().rfold(typ, |acc, typ| Type::All(typ, Box::new(acc))); + let typ = make_ctr_type(name.clone(), &field_types, &type_vars); let ctr = AdtCtr { name: name.clone(), typ, fields }; let ctrs = [(name.clone(), ctr)].into_iter().collect(); @@ -132,9 +129,8 @@ impl<'a> ImpParser<'a> { let name = self.parse_var_name()?; self.skip_trivia_inline()?; - let mut type_vars = IndexSet::new(); - let typ = self.parse_return_type(&mut type_vars)?; - let typ = type_vars.into_iter().rfold(typ, |acc, typ| Type::All(typ, Box::new(acc))); + let typ = self.parse_return_type()?; + let typ = make_fn_type(&[], typ); self.skip_trivia_inline()?; self.consume_exactly(":")?; @@ -159,7 +155,7 @@ impl<'a> ImpParser<'a> { self.skip_trivia_inline()?; let fields = if self.try_consume_exactly("{") { - self.list_like(|p| p.parse_variant_field(&mut Default::default()), "", "}", ",", true, 0)? + self.list_like(|p| p.parse_variant_field(), "", "}", ",", true, 0)? } else { vec![] }; @@ -167,22 +163,18 @@ impl<'a> ImpParser<'a> { let end_idx = *self.index(); self.check_repeated_ctr_fields(&fields, &name, ini_idx..end_idx)?; - // Type is forall type vars, arrows of the fields, returns the type of the constructor - let typ = Type::Ctr(type_name.clone(), type_vars.iter().cloned().map(Type::Var).collect()); - let typ = field_types.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))); - let typ = type_vars.iter().cloned().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); - + let typ = make_ctr_type(type_name.clone(), &field_types, type_vars); Ok(AdtCtr { name, typ, fields }) } - fn parse_variant_field(&mut self, vars: &mut IndexSet) -> ParseResult<(CtrField, Type)> { + fn parse_variant_field(&mut self) -> ParseResult<(CtrField, Type)> { let rec = self.try_consume_exactly("~"); self.skip_trivia_inline()?; let nam = self.parse_var_name()?; self.skip_trivia_inline()?; - let typ = if self.try_consume_exactly(":") { self.parse_type_expr(vars)? } else { Type::Any }; + let typ = if self.try_consume_exactly(":") { self.parse_type_expr()? } else { Type::Any }; Ok((CtrField { nam, rec }, typ)) } @@ -1162,7 +1154,7 @@ impl<'a> ImpParser<'a> { } /// Parses a type expression, returning the type and the type variables. - fn parse_type_expr(&mut self, vars: &mut IndexSet) -> ParseResult { + fn parse_type_expr(&mut self) -> ParseResult { // TODO: We should probably not have it all be inline or not inline. // For example, in tuple types or constructors, we could have line breaks. maybe_grow(|| { @@ -1170,16 +1162,18 @@ impl<'a> ImpParser<'a> { let ini_idx = *self.index(); let lft = if self.try_parse_keyword("Any") { Type::Any - } else if self.try_parse_keyword("U24") { + } else if self.try_parse_keyword("None") { + Type::None + } else if self.try_parse_keyword("u24") { Type::U24 - } else if self.try_parse_keyword("I24") { + } else if self.try_parse_keyword("i24") { Type::I24 - } else if self.try_parse_keyword("F24") { + } else if self.try_parse_keyword("f24") { Type::F24 } else if self.try_consume_exactly("(") { // Tuple or parenthesized expression self.skip_trivia(); - let head = self.parse_type_expr(vars)?; + let head = self.parse_type_expr()?; self.skip_trivia(); if self.try_consume_exactly(")") { // Parens @@ -1189,7 +1183,7 @@ impl<'a> ImpParser<'a> { let mut types = vec![head]; loop { self.consume_exactly(",")?; - types.push(self.parse_type_expr(vars)?); + types.push(self.parse_type_expr()?); if self.try_consume_exactly(")") { break; } @@ -1211,11 +1205,10 @@ impl<'a> ImpParser<'a> { // Constructor with arguments // name "(" (type ("," type)* ","?)? ")" - let args = self.list_like(|p| p.parse_type_expr(vars), "", ")", ",", true, 0)?; + let args = self.list_like(|p| p.parse_type_expr(), "", ")", ",", true, 0)?; Type::Ctr(name, args) } else { // Variable - vars.insert(name.clone()); Type::Var(name) } }; @@ -1223,7 +1216,7 @@ impl<'a> ImpParser<'a> { // Handle arrow types self.skip_trivia_inline()?; if self.try_consume_exactly("->") { - let rgt = self.parse_type_expr(vars)?; + let rgt = self.parse_type_expr()?; Ok(Type::Arr(Box::new(lft), Box::new(rgt))) } else { Ok(lft) @@ -1239,21 +1232,17 @@ impl<'a> ImpParser<'a> { let name = self.parse_top_level_name()?; self.skip_trivia_inline()?; - let mut type_vars = IndexSet::new(); let args = if self.try_consume_exactly("(") { - self.list_like(|p| p.parse_def_arg(&mut type_vars), "", ")", ",", true, 0)? + self.list_like(|p| p.parse_def_arg(), "", ")", ",", true, 0)? } else { vec![] }; self.skip_trivia_inline()?; let (args, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - let ret_type = self.parse_return_type(&mut type_vars)?; + let ret_type = self.parse_return_type()?; self.skip_trivia_inline()?; - let typ = arg_types.into_iter().rfold(ret_type, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))); - let typ = type_vars.into_iter().rfold(typ, |acc, typ| Type::All(typ, Box::new(acc))); - self.consume_exactly(":")?; self.consume_new_line()?; indent.enter_level(); @@ -1264,24 +1253,25 @@ impl<'a> ImpParser<'a> { // Note: The source kind gets replaced later (generated if a local def, user otherwise) let source = Source::from_file_span(&self.file, self.input, ini_idx..self.index, self.builtin); + let typ = make_fn_type(&arg_types, ret_type); let def = Definition { name, args, typ, check, body, source }; Ok((def, nxt_indent)) } - fn parse_def_arg(&mut self, vars: &mut IndexSet) -> ParseResult<(Name, Type)> { + fn parse_def_arg(&mut self) -> ParseResult<(Name, Type)> { let name = self.parse_var_name()?; self.skip_trivia_inline()?; if self.try_consume_exactly(":") { - let typ = self.parse_type_expr(vars)?; + let typ = self.parse_type_expr()?; Ok((name, typ)) } else { Ok((name, Type::Any)) } } - fn parse_return_type(&mut self, vars: &mut IndexSet) -> ParseResult { + fn parse_return_type(&mut self) -> ParseResult { if self.try_consume_exactly("->") { - self.parse_type_expr(vars) + self.parse_type_expr() } else { Ok(Type::Any) } diff --git a/src/lib.rs b/src/lib.rs index 209d32bf2..996f49050 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,6 +104,7 @@ pub fn desugar_book( ctx.book.encode_builtins(); ctx.resolve_refs()?; + ctx.resolve_type_ctrs()?; ctx.desugar_match_defs()?; From b16438976526ead4d490cfad4c404c8e2861397c Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Fri, 19 Jul 2024 23:43:09 +0200 Subject: [PATCH 03/29] Make types work with imports, fix nested fatal passes --- src/diagnostics.rs | 20 +- src/fun/check/unbound_refs.rs | 1 - src/fun/check/unbound_vars.rs | 2 - src/fun/display.rs | 12 +- src/fun/load_book.rs | 4 - src/fun/mod.rs | 62 +++++++ src/fun/parser.rs | 4 +- src/fun/term_to_net.rs | 2 - src/fun/transform/apply_args.rs | 2 - src/fun/transform/definition_pruning.rs | 8 +- src/fun/transform/desugar_bend.rs | 1 - src/fun/transform/desugar_fold.rs | 2 - src/fun/transform/desugar_match_defs.rs | 4 +- src/fun/transform/desugar_open.rs | 2 - src/fun/transform/desugar_with_blocks.rs | 2 - src/fun/transform/fix_match_defs.rs | 2 - src/fun/transform/fix_match_terms.rs | 2 - src/fun/transform/resolve_refs.rs | 2 - src/fun/transform/resolve_type_ctrs.rs | 4 +- src/hvm/check_net_size.rs | 3 - src/hvm/mutual_recursion.rs | 2 - src/imp/parser.rs | 9 +- src/imports/book.rs | 147 +++++++++------ src/imports/packages.rs | 4 +- src/lib.rs | 1 - tests/golden_tests.rs | 175 +++++++++--------- .../cli__desugar_bool_scott.bend.snap | 3 + .../cli__desugar_float_combinators.bend.snap | 5 + .../cli__desugar_linearize_matches.bend.snap | 1 + ...i__desugar_linearize_matches_alt.bend.snap | 1 + tests/snapshots/cli__desugar_merge.bend.snap | 2 + tests/snapshots/cli__desugar_prune.bend.snap | 1 + .../desugar_file__bind_syntax.bend.snap | 18 ++ .../desugar_file__combinators.bend.snap | 23 ++- .../desugar_file__deref_loop.bend.snap | 9 + .../desugar_file__dup_linearization.bend.snap | 1 + .../desugar_file__local_def_shadow.bend.snap | 5 + .../desugar_file__main_aux.bend.snap | 4 + .../desugar_file__mapper_syntax.bend.snap | 32 ++++ .../desugar_file__switch_with_use.bend.snap | 1 + .../desugar_file__tree_syntax.bend.snap | 17 ++ .../snapshots/desugar_file__use_id.bend.snap | 1 + .../desugar_file__use_shadow.bend.snap | 1 + .../desugar_file__used_once_names.bend.snap | 2 + ...ncode_pattern_match__adt_tup_era.bend.snap | 7 + .../encode_pattern_match__and3.bend.snap | 10 + .../encode_pattern_match__bool.bend.snap | 16 ++ .../encode_pattern_match__bool_tup.bend.snap | 10 + .../encode_pattern_match__box.bend.snap | 5 + .../encode_pattern_match__common.bend.snap | 51 +++++ .../encode_pattern_match__concat.bend.snap | 10 + ...encode_pattern_match__concat_def.bend.snap | 10 + .../encode_pattern_match__def_tups.bend.snap | 4 + ..._pattern_match__definition_merge.bend.snap | 14 ++ .../encode_pattern_match__expr.bend.snap | 39 ++++ ...e_pattern_match__flatten_era_pat.bend.snap | 8 + .../encode_pattern_match__full_map.bend.snap | 20 ++ ...code_pattern_match__is_some_some.bend.snap | 10 + ...e_pattern_match__list_merge_sort.bend.snap | 26 +++ ..._list_str_encoding_undeclared_fn.bend.snap | 6 + ...list_str_encoding_undeclared_map.bend.snap | 2 + ...match__match_adt_unscoped_in_arm.bend.snap | 8 + ...match__match_adt_unscoped_lambda.bend.snap | 8 + ...rn_match__match_adt_unscoped_var.bend.snap | 12 ++ ..._match__match_auto_linearization.bend.snap | 11 ++ ...encode_pattern_match__match_bind.bend.snap | 4 + ..._match__match_num_adt_tup_parser.bend.snap | 16 ++ ...de_pattern_match__match_num_pred.bend.snap | 10 + ...code_pattern_match__match_syntax.bend.snap | 8 + ...e_pattern_match__merge_recursive.bend.snap | 8 + ...ncode_pattern_match__no_patterns.bend.snap | 6 + ...tern_match__non_matching_fst_arg.bend.snap | 8 + .../encode_pattern_match__ntup_sum.bend.snap | 4 + ...rn_match__pattern_match_encoding.bend.snap | 19 ++ ...tern_match__switch_in_switch_arg.bend.snap | 2 + .../encode_pattern_match__var_only.bend.snap | 10 + .../encode_pattern_match__weekday.bend.snap | 23 +++ tests/snapshots/parse_file__fun_def.bend.snap | 3 +- .../parse_file__fun_def_name.bend.snap | 6 +- tests/snapshots/parse_file__imp_map.bend.snap | 12 +- .../parse_file__imp_program.bend.snap | 50 ++++- .../parse_file__multi_line_comment.bend.snap | 16 +- .../parse_file__scape_chars.bend.snap | 5 + .../simplify_matches__adt_tup_era.bend.snap | 4 + .../simplify_matches__already_flat.bend.snap | 24 +++ .../simplify_matches__bits_dec.bend.snap | 7 + ...plify_matches__complex_with_case.bend.snap | 6 + ...plify_matches__double_unwrap_box.bend.snap | 4 + ...ify_matches__double_unwrap_maybe.bend.snap | 6 + ...y_matches__flatten_with_terminal.bend.snap | 6 + ...mplify_matches__irrefutable_case.bend.snap | 9 +- ...ify_matches__linearize_match_all.bend.snap | 14 ++ .../simplify_matches__match_str.bend.snap | 2 + .../simplify_matches__nested.bend.snap | 11 ++ .../simplify_matches__nested2.bend.snap | 5 + .../simplify_matches__nested_0ary.bend.snap | 5 + ...lify_matches__redundant_with_era.bend.snap | 2 + 97 files changed, 972 insertions(+), 236 deletions(-) diff --git a/src/diagnostics.rs b/src/diagnostics.rs index c8264ae37..088a81d8d 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -11,7 +11,6 @@ pub const ERR_INDENT_SIZE: usize = 2; #[derive(Debug, Clone, Default)] pub struct Diagnostics { - err_counter: usize, pub diagnostics: BTreeMap>, pub config: DiagnosticsConfig, } @@ -71,21 +70,18 @@ pub enum WarningType { impl Diagnostics { pub fn new(config: DiagnosticsConfig) -> Self { - Self { err_counter: 0, diagnostics: Default::default(), config } + Self { diagnostics: Default::default(), config } } pub fn add_parsing_error(&mut self, err: impl std::fmt::Display, source: Source) { - self.err_counter += 1; self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Parsing, source); } pub fn add_book_error(&mut self, err: impl std::fmt::Display) { - self.err_counter += 1; self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Book, Default::default()); } pub fn add_function_error(&mut self, err: impl std::fmt::Display, name: Name, source: Source) { - self.err_counter += 1; self.add_diagnostic( err, Severity::Error, @@ -95,7 +91,6 @@ impl Diagnostics { } pub fn add_inet_error(&mut self, err: impl std::fmt::Display, def_name: String) { - self.err_counter += 1; self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Inet(def_name), Default::default()); } @@ -107,9 +102,6 @@ impl Diagnostics { source: Source, ) { let severity = self.config.warning_severity(warn_type); - if severity == Severity::Error { - self.err_counter += 1; - } self.add_diagnostic( warn, severity, @@ -120,9 +112,6 @@ impl Diagnostics { pub fn add_book_warning(&mut self, warn: impl std::fmt::Display, warn_type: WarningType) { let severity = self.config.warning_severity(warn_type); - if severity == Severity::Error { - self.err_counter += 1; - } self.add_diagnostic(warn, severity, DiagnosticOrigin::Book, Default::default()); } @@ -173,16 +162,11 @@ impl Diagnostics { self.has_severity(Severity::Error) } - /// Resets the internal counter - pub fn start_pass(&mut self) { - self.err_counter = 0; - } - /// Checks if any error was emitted since the start of the pass, /// Returning all the current information as a `Err(Info)`, replacing `&mut self` with an empty one. /// Otherwise, returns the given arg as an `Ok(T)`. pub fn fatal(&mut self, t: T) -> Result { - if self.err_counter == 0 { + if !self.has_errors() { Ok(t) } else { Err(std::mem::take(self)) diff --git a/src/fun/check/unbound_refs.rs b/src/fun/check/unbound_refs.rs index 1aa4cd5e5..f78fee9de 100644 --- a/src/fun/check/unbound_refs.rs +++ b/src/fun/check/unbound_refs.rs @@ -7,7 +7,6 @@ use std::collections::HashSet; impl Ctx<'_> { pub fn check_unbound_refs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); for def in self.book.defs.values() { let mut unbounds = HashSet::new(); for rule in def.rules.iter() { diff --git a/src/fun/check/unbound_vars.rs b/src/fun/check/unbound_vars.rs index 61b6a36e1..6fa572f74 100644 --- a/src/fun/check/unbound_vars.rs +++ b/src/fun/check/unbound_vars.rs @@ -14,8 +14,6 @@ pub enum UnboundVarErr { impl Ctx<'_> { /// Checks that there are no unbound variables in all definitions. pub fn check_unbound_vars(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for (def_name, def) in self.book.defs.iter_mut() { let mut errs = Vec::new(); for rule in &mut def.rules { diff --git a/src/fun/display.rs b/src/fun/display.rs index f0d93100e..f531f070b 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -300,11 +300,13 @@ impl fmt::Display for Type { Type::Var(nam) => write!(f, "{nam}"), Type::All(nam, bod) => write!(f, "∀{nam} {bod}"), Type::Arr(lft, rgt) => write!(f, "({lft} -> {rgt})"), - Type::Ctr(nam, args) => if args.is_empty() { - write!(f, "{nam}") - } else { - write!(f, "({nam} {})", DisplayJoin(|| args.iter(), ", ")) - }, + Type::Ctr(nam, args) => { + if args.is_empty() { + write!(f, "{nam}") + } else { + write!(f, "({nam} {})", DisplayJoin(|| args.iter(), " ")) + } + } Type::U24 => write!(f, "u24"), Type::I24 => write!(f, "i24"), Type::F24 => write!(f, "f24"), diff --git a/src/fun/load_book.rs b/src/fun/load_book.rs index fd7b4ea76..1f084c591 100644 --- a/src/fun/load_book.rs +++ b/src/fun/load_book.rs @@ -50,7 +50,3 @@ pub fn do_parse_book(code: &str, origin: &Path, mut book: ParseBook) -> Result

Result { - do_parse_book(code, origin, ParseBook::builtins())?.to_fun() -} diff --git a/src/fun/mod.rs b/src/fun/mod.rs index b843f1c86..06bc6d267 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -836,6 +836,26 @@ impl Term { } } + /// Substitutes the occurrences of a type constructor in the term with the given name. + pub fn subst_type_ctrs(&mut self, from: &Name, to: &Name) { + maybe_grow(|| { + match self { + Term::Def { def, nxt: _ } => { + def.typ.subst_ctr(from, to); + } + Term::With { typ, bod: _ } => { + if typ == from { + *typ = to.clone(); + } + } + _ => (), + } + for child in self.children_mut() { + child.subst_type_ctrs(from, to); + } + }); + } + /// Substitute the occurrence of an unscoped variable with the given term. pub fn subst_unscoped(&mut self, from: &Name, to: &Term) { maybe_grow(|| { @@ -1092,6 +1112,48 @@ impl Definition { } } +impl Type { + /// Substitutes the occurrences of a type constructor with the given name. + /// Substitutes both `Var` and `Ctr` types since `Var` could be referring to + /// an unresolved type constructor. + pub fn subst_ctr(&mut self, from: &Name, to: &Name) { + maybe_grow(|| { + match self { + Type::Var(nam) => { + if nam == from { + *nam = to.clone(); + } + } + Type::Ctr(nam, _) => { + if nam == from { + *nam = to.clone(); + } + } + Type::All(nam, _) if nam == from => { + // This forall is shadowing the subst variable, so stop here. + return; + } + _ => (), + }; + for child in self.children_mut() { + child.subst_ctr(from, to); + } + }) + } + + pub fn children_mut(&mut self) -> impl Iterator { + multi_iterator!(ChildrenIter { Zero, One, Two, Vec }); + match self { + Type::Any | Type::None | Type::Hole | Type::I24 | Type::F24 | Type::U24 | Type::Var(_) => { + ChildrenIter::Zero([]) + } + Type::Arr(lft, rgt) => ChildrenIter::Two([lft.as_mut(), rgt.as_mut()]), + Type::Tup(els) | Type::Ctr(_, els) => ChildrenIter::Vec(els), + Type::All(_, body) => ChildrenIter::One([body.as_mut()]), + } + } +} + impl Name { pub fn new<'a, V: Into>>(value: V) -> Name { Name(STRINGS.get(value)) diff --git a/src/fun/parser.rs b/src/fun/parser.rs index c0e43a962..443f40afa 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -37,7 +37,7 @@ pub struct ParseBook { /// Imported packages to be loaded in the program pub import_ctx: ImportCtx, - /// Source of the book + /// File path that the book was loaded from. pub source: Name, } @@ -384,6 +384,8 @@ impl<'a> FunParser<'a> { fn parse_import(&mut self) -> ParseResult> { // import path // import (path/a, path/b) + self.parse_keyword("import")?; + self.skip_trivia_inline()?; let new_import = |import: Name, alias: Option, relative: bool| -> Import { let (path, import) = match import.rsplit_once('/') { diff --git a/src/fun/term_to_net.rs b/src/fun/term_to_net.rs index 4a07bc816..e26699da5 100644 --- a/src/fun/term_to_net.rs +++ b/src/fun/term_to_net.rs @@ -16,8 +16,6 @@ use std::{ pub struct ViciousCycleErr; pub fn book_to_hvm(book: &Book, diags: &mut Diagnostics) -> Result<(hvm::ast::Book, Labels), Diagnostics> { - diags.start_pass(); - let mut hvm_book = hvm::ast::Book { defs: Default::default() }; let mut labels = Labels::default(); diff --git a/src/fun/transform/apply_args.rs b/src/fun/transform/apply_args.rs index f1bf8b9a3..0def4bf17 100644 --- a/src/fun/transform/apply_args.rs +++ b/src/fun/transform/apply_args.rs @@ -15,8 +15,6 @@ impl Ctx<'_> { /// main = (λx1 λx2 λx3 (MainBody x1 x2 x3) arg1 arg2 arg3) /// ``` pub fn apply_args(&mut self, args: Option>) -> Result<(), Diagnostics> { - self.info.start_pass(); - if let Some(entrypoint) = &self.book.entrypoint { let main_def = &mut self.book.defs[entrypoint]; diff --git a/src/fun/transform/definition_pruning.rs b/src/fun/transform/definition_pruning.rs index 8f7b9b586..4063b84e1 100644 --- a/src/fun/transform/definition_pruning.rs +++ b/src/fun/transform/definition_pruning.rs @@ -30,7 +30,9 @@ impl Ctx<'_> { if let Some(main) = &self.book.entrypoint { let def = self.book.defs.get(main).unwrap(); used.insert(main.clone(), Used::Main); - self.book.find_used_definitions_from_term(&def.rule().body, Used::Main, &mut used); + for rule in def.rules.iter() { + self.book.find_used_definitions_from_term(&rule.body, Used::Main, &mut used); + } } // Get the functions that are accessible from non-builtins. @@ -41,7 +43,9 @@ impl Ctx<'_> { } else { used.insert(def.name.clone(), Used::NonBuiltin); } - self.book.find_used_definitions_from_term(&def.rule().body, Used::NonBuiltin, &mut used); + for rule in def.rules.iter() { + self.book.find_used_definitions_from_term(&rule.body, Used::NonBuiltin, &mut used); + } } } for def in self.book.hvm_defs.values() { diff --git a/src/fun/transform/desugar_bend.rs b/src/fun/transform/desugar_bend.rs index e153a0af0..3b34156a4 100644 --- a/src/fun/transform/desugar_bend.rs +++ b/src/fun/transform/desugar_bend.rs @@ -10,7 +10,6 @@ const NEW_FN_SEP: &str = "__bend"; impl Ctx<'_> { pub fn desugar_bend(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); let mut new_defs = IndexMap::new(); for def in self.book.defs.values_mut() { let mut fresh = 0; diff --git a/src/fun/transform/desugar_fold.rs b/src/fun/transform/desugar_fold.rs index 63af7ac73..fe5886f4c 100644 --- a/src/fun/transform/desugar_fold.rs +++ b/src/fun/transform/desugar_fold.rs @@ -28,8 +28,6 @@ impl Ctx<'_> { /// } /// ``` pub fn desugar_fold(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - let mut new_defs = vec![]; for def in self.book.defs.values_mut() { let mut fresh = 0; diff --git a/src/fun/transform/desugar_match_defs.rs b/src/fun/transform/desugar_match_defs.rs index 22fd1f385..39bbb0c9c 100644 --- a/src/fun/transform/desugar_match_defs.rs +++ b/src/fun/transform/desugar_match_defs.rs @@ -15,8 +15,6 @@ pub enum DesugarMatchDefErr { impl Ctx<'_> { /// Converts equational-style pattern matching function definitions into trees of match terms. pub fn desugar_match_defs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for (def_name, def) in self.book.defs.iter_mut() { let errs = def.desugar_match_def(&self.book.ctrs, &self.book.adts); for err in errs { @@ -566,7 +564,7 @@ impl Pattern { match self { Pattern::Var(_) | Pattern::Chn(_) => Type::Any, Pattern::Ctr(ctr_nam, _) => { - let adt_nam = ctrs.get(ctr_nam).expect("Unknown constructor '{ctr_nam}'"); + let adt_nam = ctrs.get(ctr_nam).unwrap_or_else(|| panic!("Unknown constructor '{ctr_nam}'")); Type::Adt(adt_nam.clone()) } Pattern::Fan(is_tup, tag, args) => Type::Fan(*is_tup, tag.clone(), args.len()), diff --git a/src/fun/transform/desugar_open.rs b/src/fun/transform/desugar_open.rs index 0c662f162..e402c28fd 100644 --- a/src/fun/transform/desugar_open.rs +++ b/src/fun/transform/desugar_open.rs @@ -6,8 +6,6 @@ use crate::{ impl Ctx<'_> { pub fn desugar_open(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for def in self.book.defs.values_mut() { for rule in def.rules.iter_mut() { if let Err(err) = rule.body.desugar_open(&self.book.adts) { diff --git a/src/fun/transform/desugar_with_blocks.rs b/src/fun/transform/desugar_with_blocks.rs index e163781f5..ca27ab4c9 100644 --- a/src/fun/transform/desugar_with_blocks.rs +++ b/src/fun/transform/desugar_with_blocks.rs @@ -8,8 +8,6 @@ use std::collections::HashSet; impl Ctx<'_> { /// Converts `ask` terms inside `with` blocks into calls to a monadic bind operation. pub fn desugar_with_blocks(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - let def_names = self.book.defs.keys().cloned().collect::>(); for def in self.book.defs.values_mut() { diff --git a/src/fun/transform/fix_match_defs.rs b/src/fun/transform/fix_match_defs.rs index c1c53ce8b..fc6f188e6 100644 --- a/src/fun/transform/fix_match_defs.rs +++ b/src/fun/transform/fix_match_defs.rs @@ -8,8 +8,6 @@ impl Ctx<'_> { /// /// Does not check exhaustiveness of rules and type mismatches. (Inter-ctr/type proprieties) pub fn fix_match_defs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for def in self.book.defs.values_mut() { let mut errs = vec![]; diff --git a/src/fun/transform/fix_match_terms.rs b/src/fun/transform/fix_match_terms.rs index fb4001e85..ee6b239e1 100644 --- a/src/fun/transform/fix_match_terms.rs +++ b/src/fun/transform/fix_match_terms.rs @@ -42,8 +42,6 @@ impl Ctx<'_> { /// } /// ``` pub fn fix_match_terms(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for def in self.book.defs.values_mut() { for rule in def.rules.iter_mut() { let errs = rule.body.fix_match_terms(&self.book.ctrs, &self.book.adts); diff --git a/src/fun/transform/resolve_refs.rs b/src/fun/transform/resolve_refs.rs index 0f9e29879..19cd35bd9 100644 --- a/src/fun/transform/resolve_refs.rs +++ b/src/fun/transform/resolve_refs.rs @@ -18,8 +18,6 @@ impl Ctx<'_> { /// /// Postcondition: Refs are encoded as refs, with the correct def id. pub fn resolve_refs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - let def_names = self.book.defs.keys().cloned().chain(self.book.hvm_defs.keys().cloned()).collect::>(); for (def_name, def) in &mut self.book.defs { diff --git a/src/fun/transform/resolve_type_ctrs.rs b/src/fun/transform/resolve_type_ctrs.rs index 12b0598ec..4d990029b 100644 --- a/src/fun/transform/resolve_type_ctrs.rs +++ b/src/fun/transform/resolve_type_ctrs.rs @@ -9,8 +9,6 @@ use crate::{ impl Ctx<'_> { /// Resolves type constructors in the book and adds for alls for the free type vars. pub fn resolve_type_ctrs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for def in self.book.defs.values_mut() { let mut free_vars = Default::default(); match def.typ.resolve_type_ctrs(&mut vec![], &mut free_vars, &self.book.adts) { @@ -18,7 +16,7 @@ impl Ctx<'_> { let typ = std::mem::replace(&mut def.typ, Type::Hole); def.typ = free_vars.into_iter().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); } - Err(e) => self.info.add_rule_error(e, def.name.clone()), + Err(e) => self.info.add_function_error(e, def.name.clone(), def.source.clone()), } } diff --git a/src/hvm/check_net_size.rs b/src/hvm/check_net_size.rs index 82a2abedf..152666f1d 100644 --- a/src/hvm/check_net_size.rs +++ b/src/hvm/check_net_size.rs @@ -14,9 +14,6 @@ pub fn check_net_sizes( CompilerTarget::Cuda => (MAX_NET_SIZE_CUDA, "Cuda"), _ => (MAX_NET_SIZE_C, "C"), }; - - diagnostics.start_pass(); - for (name, net) in &book.defs { let nodes = count_nodes(net); if nodes > net_size_bound { diff --git a/src/hvm/mutual_recursion.rs b/src/hvm/mutual_recursion.rs index cf00c1b53..53c112eab 100644 --- a/src/hvm/mutual_recursion.rs +++ b/src/hvm/mutual_recursion.rs @@ -16,8 +16,6 @@ type RefSet = IndexSet; pub struct Graph(IndexMap); pub fn check_cycles(book: &Book, diagnostics: &mut Diagnostics) -> Result<(), Diagnostics> { - diagnostics.start_pass(); - let graph = Graph::from(book); let cycles = graph.cycles(); diff --git a/src/imp/parser.rs b/src/imp/parser.rs index 95065f8be..a0aeacdd1 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -88,13 +88,18 @@ impl<'a> ImpParser<'a> { let ini_idx = *self.index(); self.parse_keyword("object")?; - - let type_vars = self.list_like(|p| p.parse_var_name(), "(", ")", ",", true, 0)?; self.skip_trivia_inline()?; let name = self.parse_top_level_name()?; self.skip_trivia_inline()?; + let type_vars = if self.starts_with("(") { + self.list_like(|p| p.parse_var_name(), "(", ")", ",", true, 0)? + } else { + vec![] + }; + self.skip_trivia_inline()?; + let (fields, field_types): (Vec<_>, Vec<_>) = if self.starts_with("{") { self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)? } else { diff --git a/src/imports/book.rs b/src/imports/book.rs index c6ea1676e..b61386030 100644 --- a/src/imports/book.rs +++ b/src/imports/book.rs @@ -2,8 +2,9 @@ use super::{BindMap, ImportsMap, PackageLoader}; use crate::{ diagnostics::{Diagnostics, DiagnosticsConfig}, fun::{parser::ParseBook, Adt, Book, Definition, HvmDefinition, Name, Rule, Source, SourceKind, Term}, - imp::{self, Expr, Stmt}, + imp::{self, Expr, MatchArm, Stmt}, imports::packages::Packages, + maybe_grow, }; use indexmap::{map::Entry, IndexMap}; use itertools::Itertools; @@ -29,11 +30,16 @@ impl ParseBook { ) -> Result { let diag = &mut Diagnostics::new(diag_config); let pkgs = &mut Packages::new(self); + // Load all the imports recursively, saving them in `pkgs`. + // `book` is the root book with the entry point. let mut book = pkgs.load_imports(&mut loader, diag)?; + // Apply the imports to the book book.apply_imports(None, diag, pkgs)?; + diag.fatal(())?; eprint!("{}", diag); + // Convert the parse-level AST into the internal functional representation. let mut book = book.to_fun()?; // Process terms that contains constructors names and can't be updated by `desugar_use`. @@ -63,8 +69,6 @@ impl ParseBook { diag: &mut Diagnostics, pkgs: &mut Packages, ) -> Result<(), Diagnostics> { - diag.start_pass(); - let sources = self.import_ctx.sources().into_iter().cloned().collect_vec(); for src in sources { @@ -101,8 +105,7 @@ impl ParseBook { self.add_imported_hvm_def(def, diag); } } - - diag.fatal(()) + Ok(()) } /// Applies a chain of `use bind = src` to every local definition. @@ -151,15 +154,17 @@ impl ParseBook { for (_, def) in self.local_defs_mut() { def.apply_binds(true, &local_imports); - def.apply_types(&adt_imports); + def.apply_type_binds(&local_imports); } } /// Applying the necessary naming transformations to the book ADTs, - /// adding `use ctr = ctr_src` chains to every local definition. + /// adding `use ctr = ctr_src` chains to every local definition and + /// substituting the name of type ctrs for the canonical ones. fn apply_adts(&mut self, src: &Name, main_imports: &ImportsMap) { let adts = std::mem::take(&mut self.adts); let mut new_adts = IndexMap::new(); + let mut adts_map = vec![]; let mut ctrs_map = IndexMap::new(); let mut new_ctrs = IndexMap::new(); @@ -168,6 +173,7 @@ impl ParseBook { for (mut name, mut adt) in adts { if adt.source.is_local() { adt.source.kind = SourceKind::Imported; + let old_name = name.clone(); name = Name::new(format!("{}/{}", src, name)); let mangle_name = !main_imports.contains_source(&name); @@ -191,15 +197,28 @@ impl ParseBook { if mangle_adt_name { name = Name::new(format!("__{}", name)); } + + adts_map.push((old_name, name.clone())); } new_adts.insert(name.clone(), adt); } - // Applies the binds for the new constructor names for every definition. - // As ADTs names are not used in the syntax, we don't bind their new names. + // Apply the binds for the type constructors in the constructor types + for (_, adt) in &mut new_adts { + for (_, ctr) in &mut adt.ctrs { + for (from, to) in &adts_map { + ctr.typ.subst_ctr(from, to); + } + } + } + for (_, def) in self.local_defs_mut() { + // Applies the binds for the new constructor names for every definition. def.apply_binds(true, &ctrs_map); + + // Apply the binds for the type constructors in the def types and in the `def` terms. + def.apply_type_binds(&ctrs_map); } self.adts = new_adts; @@ -238,6 +257,7 @@ impl ParseBook { } fn add_imported_adt(&mut self, nam: Name, adt: Adt, diag: &mut Diagnostics) { + eprintln!("Adding imported adt {}", nam); if self.adts.get(&nam).is_some() { let err = format!("The imported datatype '{nam}' conflicts with the datatype '{nam}'."); diag.add_book_error(err); @@ -308,11 +328,13 @@ trait Def { *def_name = new_name; } - /// Should apply the binds to the definition, - /// and if there are possible constructor names on it, rename rule patterns. + /// Applies the binds for definition names by placing `use` terms. + /// + /// If we know that the bind map doesn't contain any constructor names, + /// we skip renaming rule patterns. fn apply_binds(&mut self, maybe_constructor: bool, binds: &BindMap); - fn apply_types(&mut self, types: &BindMap); + fn apply_type_binds(&mut self, binds: &BindMap); fn source(&self) -> &Source; fn source_mut(&mut self) -> &mut Source; @@ -340,20 +362,13 @@ impl Def for Definition { } } - fn apply_types(&mut self, types: &BindMap) { - fn rename_with_type(bod: &mut Term, types: &BindMap) { - if let Term::With { typ, .. } = bod { - if let Some(alias) = types.get(typ).cloned() { - *typ = alias; - } - } - for child in bod.children_mut() { - rename_with_type(child, types); + fn apply_type_binds(&mut self, binds: &BindMap) { + for (from, to) in binds.iter().rev() { + self.typ.subst_ctr(from, to); + for rule in &mut self.rules { + rule.body.subst_type_ctrs(from, to); } } - for rule in self.rules.iter_mut() { - rename_with_type(&mut rule.body, types); - } } fn source(&self) -> &Source { @@ -375,84 +390,90 @@ impl Def for imp::Definition { self.body = bod.fold_uses(binds.iter().rev()); } - fn apply_types(&mut self, types: &BindMap) { - fn rename_with_type(bod: &mut Stmt, types: &BindMap) { - match bod { - Stmt::With { typ, bod, nxt } => { - if let Some(alias) = types.get(typ).cloned() { - *typ = alias - } - rename_with_type(bod, types); - if let Some(nxt) = nxt { - rename_with_type(nxt, types); - } - } + fn apply_type_binds(&mut self, binds: &BindMap) { + fn subst_type_ctrs_stmt(stmt: &mut Stmt, from: &Name, to: &Name) { + maybe_grow(|| match stmt { Stmt::Assign { nxt, .. } => { if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::InPlace { nxt, .. } => { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } Stmt::If { then, otherwise, nxt, .. } => { - rename_with_type(then, types); - rename_with_type(otherwise, types); + subst_type_ctrs_stmt(then, from, to); + subst_type_ctrs_stmt(otherwise, from, to); if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Match { arms, nxt, .. } => { - for arm in arms { - rename_with_type(&mut arm.rgt, types); + for MatchArm { lft: _, rgt } in arms { + subst_type_ctrs_stmt(rgt, from, to); } if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Switch { arms, nxt, .. } => { for arm in arms { - rename_with_type(arm, types); + subst_type_ctrs_stmt(arm, from, to); } if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Bend { step, base, nxt, .. } => { - rename_with_type(step, types); - rename_with_type(base, types); + subst_type_ctrs_stmt(step, from, to); + subst_type_ctrs_stmt(base, from, to); if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Fold { arms, nxt, .. } => { - for arm in arms { - rename_with_type(&mut arm.rgt, types); + for MatchArm { lft: _, rgt } in arms { + subst_type_ctrs_stmt(rgt, from, to); } if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); + } + } + Stmt::With { typ, bod, nxt } => { + if typ == from { + *typ = to.clone(); + } + subst_type_ctrs_stmt(bod, from, to); + if let Some(nxt) = nxt { + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Ask { nxt, .. } => { if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Return { .. } => {} - Stmt::Open { nxt, .. } => { - rename_with_type(nxt, types); + Stmt::Open { typ, nxt, .. } => { + if typ == from { + *typ = to.clone(); + } + subst_type_ctrs_stmt(nxt, from, to); } Stmt::Use { nxt, .. } => { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } Stmt::LocalDef { def, nxt } => { - def.apply_types(types); - rename_with_type(nxt, types); + def.apply_type_binds(&[(from.clone(), to.clone())].into_iter().collect()); + subst_type_ctrs_stmt(nxt, from, to); } Stmt::Err => {} - } + }) + } + for (from, to) in binds.iter().rev() { + self.typ.subst_ctr(from, to); + subst_type_ctrs_stmt(&mut self.body, from, to); } - rename_with_type(&mut self.body, types); } fn source(&self) -> &Source { @@ -472,7 +493,11 @@ impl Def for HvmDefinition { /// Do nothing, can not apply binds to a HvmDefinition. fn apply_binds(&mut self, _maybe_constructor: bool, _binds: &BindMap) {} - fn apply_types(&mut self, _types: &BindMap) {} + fn apply_type_binds(&mut self, binds: &BindMap) { + for (from, to) in binds.iter().rev() { + self.typ.subst_ctr(from, to); + } + } fn source(&self) -> &Source { &self.source diff --git a/src/imports/packages.rs b/src/imports/packages.rs index b77085d56..668f5ca92 100644 --- a/src/imports/packages.rs +++ b/src/imports/packages.rs @@ -11,6 +11,7 @@ pub struct Packages { /// Map from source name to parsed book. pub books: IndexMap>, /// Already loaded ADTs information to be used when applying ADT binds. + /// Source path -> ADT names -> constructor names. pub loaded_adts: IndexMap>>, /// Queue of books indexes that still needs to load its imports. load_queue: VecDeque, @@ -32,10 +33,7 @@ impl Packages { loader: &mut impl PackageLoader, diag: &mut Diagnostics, ) -> Result { - diag.start_pass(); - self.load_imports_go(0, None, loader)?; - while let Some(idx) = self.load_queue.pop_front() { let parent_dir = { let book = self.books[idx].borrow(); diff --git a/src/lib.rs b/src/lib.rs index 996f49050..597f5a467 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,7 +60,6 @@ pub fn compile_book( } if opts.inline { - diagnostics.start_pass(); if let Err(e) = inline_hvm_book(&mut hvm_book) { diagnostics.add_book_error(format!("During inlining:\n{:ERR_INDENT_SIZE$}{}", "", e)); } diff --git a/tests/golden_tests.rs b/tests/golden_tests.rs index 618e55270..0c26182b9 100644 --- a/tests/golden_tests.rs +++ b/tests/golden_tests.rs @@ -1,7 +1,20 @@ +//! This module runs snapshot tests for compiling and running Bend programs. +//! +//! The result of each test is saved as a snapshot and used as golden output +//! for future tests. This allows us to test regressions in compilation and +//! have a history of how certain programs compiled and ran. +//! +//! These tests use `cargo-insta`. To run the tests, run `cargo insta test`. +//! If there are any changes to the snapshots, they'll be highlighted by the +//! CLI tool. Then, run `cargo insta review` to review these changes. + use bend::{ compile_book, desugar_book, diagnostics::{Diagnostics, DiagnosticsConfig, Severity}, - fun::{load_book::do_parse_book_default, net_to_term::net_to_term, term_to_net::Labels, Book, Ctx, Name}, + fun::{ + load_book::do_parse_book, net_to_term::net_to_term, parser::ParseBook, term_to_net::Labels, Book, Ctx, + Name, + }, hvm::hvm_book_show_pretty, imports::DefaultLoader, load_to_book, @@ -28,6 +41,10 @@ const TESTS_PATH: &str = "/tests/golden_tests/"; type RunFn = dyn Fn(&str, &Path) -> Result; +pub fn parse_book_single_file(code: &str, origin: &Path) -> Result { + do_parse_book(code, origin, ParseBook::builtins())?.to_fun() +} + fn run_single_golden_test(path: &Path, run: &[&RunFn]) -> Result<(), String> { println!("{}", path.display()); let code = std::fs::read_to_string(path).map_err(|e| e.to_string())?; @@ -92,10 +109,11 @@ fn run_golden_test_dir_multiple(test_name: &str, run: &[&RunFn]) { and what to save as a snapshot. */ +/// Compiles a file with regular compilation options. #[test] fn compile_file() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let compile_opts = CompileOpts::default(); let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Allow, ..Default::default() }; @@ -104,10 +122,11 @@ fn compile_file() { }) } +/// Compiles a file with `-Oall` option. #[test] fn compile_file_o_all() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig { recursion_cycle: Severity::Warning, @@ -120,10 +139,11 @@ fn compile_file_o_all() { }) } +/// Compiles a file with `-Ono-all` option. #[test] fn compile_file_o_no_all() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let compile_opts = CompileOpts::default().set_no_all(); let diagnostics_cfg = DiagnosticsConfig::default(); let res = compile_book(&mut book, compile_opts, diagnostics_cfg, None)?; @@ -131,11 +151,12 @@ fn compile_file_o_no_all() { }) } +/// Runs a file, but with linear readback enabled. #[test] fn linear_readback() { run_golden_test_dir(function_name!(), &|code, path| { let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; + let book = parse_book_single_file(code, path)?; let compile_opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig::default(); let (term, _, diags) = run_book( @@ -152,13 +173,15 @@ fn linear_readback() { }); } +/// Runs a file with regular compilation options, but rejecting all warnings. +/// Runs once for each ADT encoding. #[test] fn run_file() { run_golden_test_dir_multiple( function_name!(), &[(&|code, path| { let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; + let book = parse_book_single_file(code, path)?; let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Allow, ..DiagnosticsConfig::new(Severity::Error, true) @@ -178,6 +201,7 @@ fn run_file() { ) } +/// Runs bend programs, all sharing a common lib to test the import system. #[test] fn import_system() { run_golden_test_dir_multiple( @@ -202,27 +226,7 @@ fn import_system() { ) } -#[test] -#[ignore = "while lazy execution is not implemented for hvm32"] -fn run_lazy() { - run_golden_test_dir(function_name!(), &|_code, _path| { - todo!() - /* let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; - let compile_opts = CompileOpts::default_lazy(); - let diagnostics_cfg = DiagnosticsConfig { - recursion_cycle: Severity::Allow, - unused_definition: Severity::Allow, - ..DiagnosticsConfig::new(Severity::Error, true) - }; - let run_opts = RunOpts::lazy(); - - let (term, _, diags) = run_book(book, run_opts, compile_opts, diagnostics_cfg, None)?; - let res = format!("{diags}{term}"); - Ok(res) */ - }) -} - +/// Reads back an HVM net. #[test] fn readback_hvm() { run_golden_test_dir(function_name!(), &|code, _| { @@ -236,6 +240,7 @@ fn readback_hvm() { }) } +/// Runs compilation up to fixing, simplifying and linearizing matches. #[test] fn simplify_matches() { run_golden_test_dir(function_name!(), &|code, path| { @@ -243,16 +248,16 @@ fn simplify_matches() { irrefutable_match: Severity::Allow, ..DiagnosticsConfig::new(Severity::Error, true) }; - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let mut ctx = Ctx::new(&mut book, diagnostics_cfg); ctx.check_shared_names(); - ctx.set_entrypoint(); ctx.book.encode_adts(AdtEncoding::NumScott); ctx.fix_match_defs()?; ctx.desugar_open()?; ctx.book.encode_builtins(); ctx.resolve_refs()?; + ctx.resolve_type_ctrs()?; ctx.desugar_match_defs()?; ctx.fix_match_terms()?; ctx.desugar_bend()?; @@ -273,36 +278,22 @@ fn simplify_matches() { }) } -#[test] -fn parse_file() { - run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; - let mut ctx = Ctx::new(&mut book, Default::default()); - ctx.set_entrypoint(); - ctx.book.encode_adts(AdtEncoding::NumScott); - ctx.book.encode_builtins(); - ctx.resolve_refs().expect("Resolve refs"); - ctx.desugar_match_defs().expect("Desugar match defs"); - ctx.prune(false); - Ok(book.to_string()) - }) -} - +/// Runs compilation up to encoding `match` terms as lambdas. #[test] fn encode_pattern_match() { run_golden_test_dir(function_name!(), &|code, path| { let mut result = String::new(); for adt_encoding in [AdtEncoding::Scott, AdtEncoding::NumScott] { let diagnostics_cfg = DiagnosticsConfig::default(); - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let mut ctx = Ctx::new(&mut book, diagnostics_cfg); ctx.check_shared_names(); - ctx.set_entrypoint(); ctx.book.encode_adts(adt_encoding); ctx.fix_match_defs()?; ctx.desugar_open()?; ctx.book.encode_builtins(); ctx.resolve_refs()?; + ctx.resolve_type_ctrs()?; ctx.desugar_match_defs()?; ctx.fix_match_terms()?; ctx.desugar_bend()?; @@ -327,6 +318,22 @@ fn encode_pattern_match() { }) } +/// Parses a file, but does not desugar or compile it. +#[test] +fn parse_file() { + run_golden_test_dir(function_name!(), &|code, path| { + let mut book = parse_book_single_file(code, path)?; + let mut ctx = Ctx::new(&mut book, Default::default()); + ctx.set_entrypoint(); + ctx.book.encode_adts(AdtEncoding::NumScott); + ctx.book.encode_builtins(); + ctx.resolve_refs().expect("Resolve refs"); + ctx.prune(false); + Ok(book.to_string()) + }) +} + +/// Runs compilation up to the last term-level pass (`bend desugar` command). #[test] fn desugar_file() { run_golden_test_dir(function_name!(), &|code, path| { @@ -335,12 +342,13 @@ fn desugar_file() { unused_definition: Severity::Allow, ..DiagnosticsConfig::new(Severity::Error, true) }; - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; desugar_book(&mut book, compile_opts, diagnostics_cfg, None)?; Ok(book.to_string()) }) } +/// Runs a file that is expected to hang. #[test] #[ignore = "bug - the subprocess created by run_book leaks"] fn hangs() { @@ -348,7 +356,7 @@ fn hangs() { run_golden_test_dir(function_name!(), &move |code, path| { let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; + let book = parse_book_single_file(code, path)?; let compile_opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig::new(Severity::Allow, false); @@ -367,10 +375,11 @@ fn hangs() { }) } +/// Compiles a file with a custom entrypoint. #[test] fn compile_entrypoint() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; book.entrypoint = Some(Name::new("foo")); let diagnostics_cfg = DiagnosticsConfig { ..DiagnosticsConfig::new(Severity::Error, true) }; let res = compile_book(&mut book, CompileOpts::default(), diagnostics_cfg, None)?; @@ -378,12 +387,13 @@ fn compile_entrypoint() { }) } +/// Runs a file with a custom entrypoint. #[test] #[ignore = "while execution with different entrypoints is not implemented for hvm32"] fn run_entrypoint() { run_golden_test_dir(function_name!(), &|code, path| { let _guard = RUN_MUTEX.lock().unwrap(); - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; book.entrypoint = Some(Name::new("foo")); let compile_opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig { ..DiagnosticsConfig::new(Severity::Error, true) }; @@ -394,6 +404,7 @@ fn run_entrypoint() { }) } +/// Runs a Bend CLI command. #[test] fn cli() { run_golden_test_dir(function_name!(), &|_code, path| { @@ -414,49 +425,38 @@ fn cli() { }) } +/// Compiles a file to check for mutual recursion. #[test] fn mutual_recursion() { run_golden_test_dir(function_name!(), &|code, path| { let diagnostics_cfg = DiagnosticsConfig { recursion_cycle: Severity::Error, ..DiagnosticsConfig::new(Severity::Allow, true) }; - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let opts = CompileOpts { merge: true, ..CompileOpts::default() }; let res = compile_book(&mut book, opts, diagnostics_cfg, None)?; Ok(format!("{}{}", res.diagnostics, hvm_book_show_pretty(&res.hvm_book))) }) } +/// Runs a file that uses IO. #[test] fn io() { - run_golden_test_dir_multiple( + run_golden_test_dir( function_name!(), - &[ - /* (&|code, path| { - let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; - let compile_opts = CompileOpts::default_lazy(); - let diagnostics_cfg = DiagnosticsConfig::default_lazy(); - let Output { status, stdout, stderr } = - run_book(book, None, RunOpts::lazy(), compile_opts, diagnostics_cfg, None)?; - let stderr = String::from_utf8_lossy(&stderr); - let status = if !status.success() { format!("\n{status}") } else { String::new() }; - let stdout = String::from_utf8_lossy(&stdout); - Ok(format!("Lazy mode:\n{}{}{}", stderr, status, stdout)) - }), */ - (&|code, path| { - let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; - let compile_opts = CompileOpts::default(); - let diagnostics_cfg = DiagnosticsConfig::default(); - let (term, _, diags) = - run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); - let res = format!("{diags}{term}"); - Ok(format!("Strict mode:\n{res}")) - }), - ], + (&|code, path| { + let _guard = RUN_MUTEX.lock().unwrap(); + let book = parse_book_single_file(code, path)?; + let compile_opts = CompileOpts::default(); + let diagnostics_cfg = DiagnosticsConfig::default(); + let (term, _, diags) = + run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); + let res = format!("{diags}{term}"); + Ok(format!("Strict mode:\n{res}")) + }), ) } +/// Runs all examples in the examples folder. #[test] fn examples() -> Result<(), Diagnostics> { let examples_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples"); @@ -472,16 +472,12 @@ fn examples() -> Result<(), Diagnostics> { eprintln!("Testing {}", path.display()); let code = std::fs::read_to_string(path).map_err(|e| e.to_string())?; - let res = match do_parse_book_default(&code, path) { - Ok(book) => { - let compile_opts = CompileOpts::default(); - let diagnostics_cfg = DiagnosticsConfig::default(); - let (term, _, diags) = - run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); - format!("{diags}{term}") - } - Err(e) => e.to_string(), - }; + let book = parse_book_single_file(&code, path).unwrap(); + let compile_opts = CompileOpts::default(); + let diagnostics_cfg = DiagnosticsConfig::default(); + let (term, _, diags) = + run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); + let res = format!("{diags}{term}"); let mut settings = insta::Settings::clone_current(); settings.set_prepend_module_to_snapshot(false); @@ -496,10 +492,11 @@ fn examples() -> Result<(), Diagnostics> { Ok(()) } +/// Test that the Scott encoding correctly triggers unused definition warnings. #[test] fn scott_triggers_unused() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let opts = CompileOpts::default(); let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Error, ..DiagnosticsConfig::default() }; @@ -509,10 +506,12 @@ fn scott_triggers_unused() { } // TODO: also run the long string file to test the readback +/// Compiles a file that is very large and takes a long time to compile. +/// Only outputs if compilation worked without errors. #[test] fn compile_long() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig { recursion_cycle: Severity::Warning, diff --git a/tests/snapshots/cli__desugar_bool_scott.bend.snap b/tests/snapshots/cli__desugar_bool_scott.bend.snap index 713942c37..6f124b516 100644 --- a/tests/snapshots/cli__desugar_bool_scott.bend.snap +++ b/tests/snapshots/cli__desugar_bool_scott.bend.snap @@ -2,8 +2,11 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_bool_scott.bend --- +main: Any (main) = λa λ* a +Boolean/True: Boolean (Boolean/True) = λa λ* a +Boolean/False: Boolean (Boolean/False) = λ* λa a diff --git a/tests/snapshots/cli__desugar_float_combinators.bend.snap b/tests/snapshots/cli__desugar_float_combinators.bend.snap index 5f3bf79b8..fe69996f5 100644 --- a/tests/snapshots/cli__desugar_float_combinators.bend.snap +++ b/tests/snapshots/cli__desugar_float_combinators.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_float_combinators.bend --- +Z: Any (Z) = λ* λa a +S: Any (S) = λa λb let {c d} = b; λe (c (a d e)) +get: Any (get) = λa (a get__C0 0) +main: Any (main) = (get (S (S Z))) +get__C0: _ (get__C0) = λa (+ a 1) diff --git a/tests/snapshots/cli__desugar_linearize_matches.bend.snap b/tests/snapshots/cli__desugar_linearize_matches.bend.snap index b55ac394b..f34c8c6ec 100644 --- a/tests/snapshots/cli__desugar_linearize_matches.bend.snap +++ b/tests/snapshots/cli__desugar_linearize_matches.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_linearize_matches.bend --- +main: Any (main) = λa λb λc (switch a { 0: λd λ* d; _: λ* λ* λe e; } b c) diff --git a/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap b/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap index c84d47536..28d5ed79c 100644 --- a/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap +++ b/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_linearize_matches_alt.bend --- +main: Any (main) = λa switch a { 0: λb b; _: λ* λc c; } diff --git a/tests/snapshots/cli__desugar_merge.bend.snap b/tests/snapshots/cli__desugar_merge.bend.snap index 3df1b698b..e767fb41a 100644 --- a/tests/snapshots/cli__desugar_merge.bend.snap +++ b/tests/snapshots/cli__desugar_merge.bend.snap @@ -7,6 +7,8 @@ input_file: tests/golden_tests/cli/desugar_merge.bend In definition 'Z': Definition is unused. +F__M_Z: _ (F__M_Z) = λ* λa a +main: Any (main) = λ* λa a diff --git a/tests/snapshots/cli__desugar_prune.bend.snap b/tests/snapshots/cli__desugar_prune.bend.snap index c136f21bb..081f3d47a 100644 --- a/tests/snapshots/cli__desugar_prune.bend.snap +++ b/tests/snapshots/cli__desugar_prune.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_prune.bend --- +main: Any (main) = * diff --git a/tests/snapshots/desugar_file__bind_syntax.bend.snap b/tests/snapshots/desugar_file__bind_syntax.bend.snap index eb850cbe9..b986fd9d3 100644 --- a/tests/snapshots/desugar_file__bind_syntax.bend.snap +++ b/tests/snapshots/desugar_file__bind_syntax.bend.snap @@ -2,38 +2,56 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/bind_syntax.bend --- +undefer: Any (undefer) = λa (a λb b) +Result/bind: Any (Result/bind) = λa λb (a Result/bind__C2 b) +safe_div: Any (safe_div) = λa λb (switch b { 0: λ* (Result/Err (String/Cons 68 (String/Cons 105 (String/Cons 118 (String/Cons 32 (String/Cons 98 (String/Cons 121 (String/Cons 32 (String/Cons 48 String/Nil))))))))); _: safe_div__C0; } a) +safe_rem: Any (safe_rem) = λa λb (switch b { 0: λ* (Result/Err (String/Cons 77 (String/Cons 111 (String/Cons 100 (String/Cons 32 (String/Cons 98 (String/Cons 121 (String/Cons 32 (String/Cons 48 String/Nil))))))))); _: safe_rem__C0; } a) +Main: Any (Main) = (Result/bind (safe_div 3 2) λa (a λb (Result/bind (safe_rem b 0) λc (c λd d)))) +String/Nil: String (String/Nil) = λa (a String/Nil/tag) +String/Cons: (u24 -> (String -> String)) (String/Cons) = λa λb λc (c String/Cons/tag a b) +Result/Ok: ∀o ∀e (o -> (Result o e)) (Result/Ok) = λa λb (b Result/Ok/tag a) +Result/Err: ∀o ∀e (e -> (Result o e)) (Result/Err) = λa λb (b Result/Err/tag a) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 +Result/Ok/tag: u24 (Result/Ok/tag) = 0 +Result/Err/tag: u24 (Result/Err/tag) = 1 +Result/bind__C0: _ (Result/bind__C0) = λa λb (undefer b a) +Result/bind__C1: _ (Result/bind__C1) = λ* λa λ* (Result/Err a) +Result/bind__C2: _ (Result/bind__C2) = λa switch a { 0: Result/bind__C0; _: Result/bind__C1; } +safe_div__C0: _ (safe_div__C0) = λa λb (Result/Ok (/ b (+ a 1))) +safe_rem__C0: _ (safe_rem__C0) = λa λb (Result/Ok (% b (+ a 1))) diff --git a/tests/snapshots/desugar_file__combinators.bend.snap b/tests/snapshots/desugar_file__combinators.bend.snap index 58ce05b16..6504077af 100644 --- a/tests/snapshots/desugar_file__combinators.bend.snap +++ b/tests/snapshots/desugar_file__combinators.bend.snap @@ -2,44 +2,65 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/combinators.bend --- +foo: Any (foo) = λa λ* λ* (foo a) +bar: Any (bar) = λa λb (a bar b) +List/ignore: Any (List/ignore) = λa λ* (a List/ignore__C1) +baz: Any (baz) = {0 1 2 3 λa a foo} +qux: Any (qux) = {0 qux} +clax: Any (clax) = (λa a clax__C0) +tup: Any (tup) = (tup, 1, 0) +list: Any (list) = (List/Cons 0 list__C0) +A: Any (A) = λa (A__C0 a) +B: Any (B) = λa (B__C0 a) -(Main) = (List/Cons 0 (List/Cons list List/Nil)) +Main: Any +(Main) = (List/Cons 0 list__C0) +List/Nil: ∀t (List t) (List/Nil) = λa (a List/Nil/tag) +List/Cons: ∀t (u24 -> ((List t) -> (List t))) (List/Cons) = λa λb λc (c List/Cons/tag a b) +List/Nil/tag: u24 (List/Nil/tag) = 0 +List/Cons/tag: u24 (List/Cons/tag) = 1 +A__C0: _ (A__C0) = let {a b} = A; λc (a b c) +B__C0: _ (B__C0) = let (a, b) = B; λc (a b c) +List/ignore__C0: _ (List/ignore__C0) = λ* λ* λa (List/ignore a List/ignore) +List/ignore__C1: _ (List/ignore__C1) = λa switch a { 0: 0; _: List/ignore__C0; } +clax__C0: _ (clax__C0) = λ* λ* λ* λa (clax a) +list__C0: _ (list__C0) = (List/Cons list List/Nil) diff --git a/tests/snapshots/desugar_file__deref_loop.bend.snap b/tests/snapshots/desugar_file__deref_loop.bend.snap index 4cedbf623..18562cfb6 100644 --- a/tests/snapshots/desugar_file__deref_loop.bend.snap +++ b/tests/snapshots/desugar_file__deref_loop.bend.snap @@ -2,20 +2,29 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/deref_loop.bend --- +foo: Any (foo) = λa (a foo__C1) +bar: Any (bar) = (foo 1) +main: Any (main) = (foo 0) +nat/succ: (Any -> nat) (nat/succ) = λa λb (b nat/succ/tag a) +nat/zero: nat (nat/zero) = λa (a nat/zero/tag) +nat/succ/tag: u24 (nat/succ/tag) = 0 +nat/zero/tag: u24 (nat/zero/tag) = 1 +foo__C0: _ (foo__C0) = λ* (bar 0) +foo__C1: _ (foo__C1) = λa switch a { 0: λb b; _: foo__C0; } diff --git a/tests/snapshots/desugar_file__dup_linearization.bend.snap b/tests/snapshots/desugar_file__dup_linearization.bend.snap index 7aaacbb2b..26f3ad315 100644 --- a/tests/snapshots/desugar_file__dup_linearization.bend.snap +++ b/tests/snapshots/desugar_file__dup_linearization.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/dup_linearization.bend --- +main: Any (main) = let {a b c d e} = *; (a, e, d, c, b) diff --git a/tests/snapshots/desugar_file__local_def_shadow.bend.snap b/tests/snapshots/desugar_file__local_def_shadow.bend.snap index 551da5ee9..534dd51d9 100644 --- a/tests/snapshots/desugar_file__local_def_shadow.bend.snap +++ b/tests/snapshots/desugar_file__local_def_shadow.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/local_def_shadow.bend --- +main: Any (main) = 1 +main__local_0_A__local_0_B: _ (main__local_0_A__local_0_B) = 0 +main__local_1_A__local_1_B: _ (main__local_1_A__local_1_B) = 1 +main__local_1_A: _ (main__local_1_A) = main__local_1_A__local_1_B +main__local_0_A: _ (main__local_0_A) = main__local_0_A__local_0_B diff --git a/tests/snapshots/desugar_file__main_aux.bend.snap b/tests/snapshots/desugar_file__main_aux.bend.snap index 4de950351..b52a4bdc4 100644 --- a/tests/snapshots/desugar_file__main_aux.bend.snap +++ b/tests/snapshots/desugar_file__main_aux.bend.snap @@ -2,10 +2,14 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/main_aux.bend --- +main: Any (main) = (main__local_0_aux 89 2) +main__local_0_aux__local_0_aux__local_0_aux: _ (main__local_0_aux__local_0_aux__local_0_aux) = λa λb (+ b a) +main__local_0_aux__local_0_aux: _ (main__local_0_aux__local_0_aux) = λa λb (main__local_0_aux__local_0_aux__local_0_aux a b) +main__local_0_aux: _ (main__local_0_aux) = λa λb (main__local_0_aux__local_0_aux a b) diff --git a/tests/snapshots/desugar_file__mapper_syntax.bend.snap b/tests/snapshots/desugar_file__mapper_syntax.bend.snap index 4423c04ff..401f2fd61 100644 --- a/tests/snapshots/desugar_file__mapper_syntax.bend.snap +++ b/tests/snapshots/desugar_file__mapper_syntax.bend.snap @@ -2,66 +2,98 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/mapper_syntax.bend --- +Map/empty: ∀T (Map T) (Map/empty) = Map/Leaf +Map/get: ∀T ((Map T) -> (u24 -> ((Map T), T))) (Map/get) = λa λb (a Map/get__C5 b) +Map/set: ∀T ((Map T) -> (u24 -> (T -> (Map T)))) (Map/set) = λa λb λc (a Map/set__C10 b c) +Map/map: ∀T ((Map T) -> (u24 -> ((T -> T) -> (Map T)))) (Map/map) = λa λb λc (a Map/map__C5 b c) +main: Any (main) = let (c, d) = (Map/get (Map/map (Map/map (Map/set (Map/set Map/empty 0 3) 1 4) 1 λa (+ a 1)) 1 λb (* b 2)) 1); let (e, *) = (Map/get d 0); ((λf (+ f 1) 1), c, e) +Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) (Map/Node) = λa λb λc λd (d Map/Node/tag a b c) +Map/Leaf: ∀T (Map T) (Map/Leaf) = λa (a Map/Leaf/tag) +Map/Node/tag: u24 (Map/Node/tag) = 0 +Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 +Map/get__C0: _ (Map/get__C0) = λa λb λc λd let (e, f) = (Map/get c (/ a 2)); (e, (Map/Node b f d)) +Map/get__C1: _ (Map/get__C1) = λ* λa λb λc λd let (e, f) = (Map/get d (/ a 2)); (e, (Map/Node b c f)) +Map/get__C2: _ (Map/get__C2) = λa let {b c} = a; λd λe λf (switch (% b 2) { 0: Map/get__C0; _: Map/get__C1; } c d e f) +Map/get__C3: _ (Map/get__C3) = λ* λ* λa let {b c} = a; λd λe (b, (Map/Node c d e)) +Map/get__C4: _ (Map/get__C4) = λa λb λc λd let {e f} = d; (switch (== 0 e) { 0: Map/get__C2; _: Map/get__C3; } f a b c) +Map/get__C5: _ (Map/get__C5) = λa switch a { 0: Map/get__C4; _: λ* λ* (*, Map/Leaf); } +Map/map__C0: _ (Map/map__C0) = λa λb λc λd λe (Map/Node c (Map/map d (/ a 2) b) e) +Map/map__C1: _ (Map/map__C1) = λ* λa λb λc λd λe (Map/Node c d (Map/map e (/ a 2) b)) +Map/map__C2: _ (Map/map__C2) = λa let {b c} = a; λd λe λf λg (switch (% b 2) { 0: Map/map__C0; _: Map/map__C1; } c d e f g) +Map/map__C3: _ (Map/map__C3) = λ* λ* λa λb λc λd (Map/Node (a b) c d) +Map/map__C4: _ (Map/map__C4) = λa λb λc λd let {e f} = d; λg (switch (== 0 e) { 0: Map/map__C2; _: Map/map__C3; } f g a b c) +Map/map__C5: _ (Map/map__C5) = λa switch a { 0: Map/map__C4; _: λ* λ* λ* Map/Leaf; } +Map/set__C0: _ (Map/set__C0) = λa λb λc λd λe (Map/Node c (Map/set d (/ a 2) b) e) +Map/set__C1: _ (Map/set__C1) = λ* λa λb λc λd λe (Map/Node c d (Map/set e (/ a 2) b)) +Map/set__C10: _ (Map/set__C10) = λa switch a { 0: Map/set__C8; _: Map/set__C9; } +Map/set__C2: _ (Map/set__C2) = λa let {b c} = a; λd λe λf λg (switch (% b 2) { 0: Map/set__C0; _: Map/set__C1; } c d e f g) +Map/set__C3: _ (Map/set__C3) = λ* λ* λa λ* λb λc (Map/Node a b c) +Map/set__C4: _ (Map/set__C4) = λa λb (Map/Node * (Map/set Map/Leaf (/ a 2) b) Map/Leaf) +Map/set__C5: _ (Map/set__C5) = λ* λa λb (Map/Node * Map/Leaf (Map/set Map/Leaf (/ a 2) b)) +Map/set__C6: _ (Map/set__C6) = λa let {b c} = a; λd (switch (% b 2) { 0: Map/set__C4; _: Map/set__C5; } c d) +Map/set__C7: _ (Map/set__C7) = λ* λ* λa (Map/Node a Map/Leaf Map/Leaf) +Map/set__C8: _ (Map/set__C8) = λa λb λc λd let {e f} = d; λg (switch (== 0 e) { 0: Map/set__C2; _: Map/set__C3; } f g a b c) +Map/set__C9: _ (Map/set__C9) = λ* λa let {b c} = a; λd (switch (== 0 b) { 0: Map/set__C6; _: Map/set__C7; } c d) diff --git a/tests/snapshots/desugar_file__switch_with_use.bend.snap b/tests/snapshots/desugar_file__switch_with_use.bend.snap index c565c0ade..808f5456c 100644 --- a/tests/snapshots/desugar_file__switch_with_use.bend.snap +++ b/tests/snapshots/desugar_file__switch_with_use.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/switch_with_use.bend --- +main: Any (main) = λa λb λc λ* λ* (switch c { 0: λd d; _: λe λf (e f); } (a b)) diff --git a/tests/snapshots/desugar_file__tree_syntax.bend.snap b/tests/snapshots/desugar_file__tree_syntax.bend.snap index 12d0870e6..a1abe4fe7 100644 --- a/tests/snapshots/desugar_file__tree_syntax.bend.snap +++ b/tests/snapshots/desugar_file__tree_syntax.bend.snap @@ -2,36 +2,53 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/tree_syntax.bend --- +fun0: Any (fun0) = (Tree/Node (Tree/Leaf 0) (Tree/Node (Tree/Leaf 1) (Tree/Node (Tree/Leaf 2) (Tree/Leaf 3)))) +fun1: Any (fun1) = (Tree/Leaf (Tree/Node (Tree/Leaf *) (Tree/Leaf *))) +fun2: Any (fun2) = (Tree/Leaf (Tree/Leaf (Tree/Leaf *))) +fun3: Any (fun3) = (Tree/Leaf 1) +fun4: Any (fun4) = λa switch a { 0: (Tree/Leaf 0); _: fun4__C0; } +main: Any (main) = * +imp0: Any (imp0) = (Tree/Node (Tree/Leaf 0) (Tree/Node (Tree/Leaf 1) (Tree/Node (Tree/Leaf 2) (Tree/Leaf 3)))) +imp1: Any (imp1) = (Tree/Leaf (Tree/Node (Tree/Leaf *) (Tree/Leaf *))) +imp2: Any (imp2) = (Tree/Leaf (Tree/Leaf (Tree/Leaf *))) +imp3: Any (imp3) = (Tree/Leaf 1) +imp4: (Any -> Any) (imp4) = λa switch a { 0: (Tree/Leaf 0); _: imp4__C0; } +Tree/Node: ∀T ((Tree T) -> ((Tree T) -> (Tree T))) (Tree/Node) = λa λb λc (c Tree/Node/tag a b) +Tree/Leaf: ∀T (T -> (Tree T)) (Tree/Leaf) = λa λb (b Tree/Leaf/tag a) +Tree/Node/tag: u24 (Tree/Node/tag) = 0 +Tree/Leaf/tag: u24 (Tree/Leaf/tag) = 1 +fun4__C0: _ (fun4__C0) = λa let {b c} = a; (Tree/Node (fun4 b) (fun4 c)) +imp4__C0: _ (imp4__C0) = λa let {b c} = a; (Tree/Node (imp4 b) (imp4 c)) diff --git a/tests/snapshots/desugar_file__use_id.bend.snap b/tests/snapshots/desugar_file__use_id.bend.snap index 903fd3977..4c292199c 100644 --- a/tests/snapshots/desugar_file__use_id.bend.snap +++ b/tests/snapshots/desugar_file__use_id.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/use_id.bend --- +Main: Any (Main) = (λa a 2 3 (λb b 2 3) (λc c 2 3 (λd d 2 3))) diff --git a/tests/snapshots/desugar_file__use_shadow.bend.snap b/tests/snapshots/desugar_file__use_shadow.bend.snap index c69f8530c..c36b51ce1 100644 --- a/tests/snapshots/desugar_file__use_shadow.bend.snap +++ b/tests/snapshots/desugar_file__use_shadow.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/use_shadow.bend --- +main: Any (main) = λa let {b c} = a; λd (b c d) diff --git a/tests/snapshots/desugar_file__used_once_names.bend.snap b/tests/snapshots/desugar_file__used_once_names.bend.snap index 813ec54aa..da4b14214 100644 --- a/tests/snapshots/desugar_file__used_once_names.bend.snap +++ b/tests/snapshots/desugar_file__used_once_names.bend.snap @@ -2,6 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/used_once_names.bend --- +foo: Any (foo) = λa λb λc let {d e} = c; (a b (d e)) +main: Any (main) = (foo 2 3 λa a) diff --git a/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap b/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap index 4ccd0b476..9921743e1 100644 --- a/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap +++ b/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap @@ -3,17 +3,24 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/adt_tup_era.bend --- Scott +Foo: Any (Foo) = λa (a λb λc (b λd λ* λ* d c)) +Main: Any (Main) = (Foo (Tuple/Pair 1 5)) +Tuple/Pair: (Any -> (Any -> Tuple)) (Tuple/Pair) = λa λb λc (c a b) NumScott +Foo: Any (Foo) = λa (a λb switch b { 0: λc λd (c λe switch e { 0: λf λ* λ* f; _: *; } d); _: *; }) +Main: Any (Main) = (Foo (Tuple/Pair 1 5)) +Tuple/Pair: (Any -> (Any -> Tuple)) (Tuple/Pair) = λa λb λc (c Tuple/Pair/tag a b) +Tuple/Pair/tag: u24 (Tuple/Pair/tag) = 0 diff --git a/tests/snapshots/encode_pattern_match__and3.bend.snap b/tests/snapshots/encode_pattern_match__and3.bend.snap index 14ca7da2c..1429c58e2 100644 --- a/tests/snapshots/encode_pattern_match__and3.bend.snap +++ b/tests/snapshots/encode_pattern_match__and3.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/and3.bend --- Scott +And: Any (And) = λa let (b, c, d) = a; (b λe λf (e λg (g Bool/T Bool/F) λ* Bool/F f) λ* λ* Bool/F c d) +main: Any (main) = (And (Bool/F, Bool/T, Bool/F)) +Bool/T: Bool (Bool/T) = λa λ* a +Bool/F: Bool (Bool/F) = λ* λb b NumScott +And: Any (And) = λa let (b, c, d) = a; (b λe switch e { 0: λf λg (f λh switch h { 0: λi (i λj switch j { 0: Bool/T; _: λ* Bool/F; }); _: λ* λ* Bool/F; } g); _: λ* λ* λ* Bool/F; } c d) +main: Any (main) = (And (Bool/F, Bool/T, Bool/F)) +Bool/T: Bool (Bool/T) = λa (a Bool/T/tag) +Bool/F: Bool (Bool/F) = λa (a Bool/F/tag) +Bool/T/tag: u24 (Bool/T/tag) = 0 +Bool/F/tag: u24 (Bool/F/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__bool.bend.snap b/tests/snapshots/encode_pattern_match__bool.bend.snap index 57db838ae..4fd8925b7 100644 --- a/tests/snapshots/encode_pattern_match__bool.bend.snap +++ b/tests/snapshots/encode_pattern_match__bool.bend.snap @@ -3,35 +3,51 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/bool.bend --- Scott +not: Any (not) = λa (a bool/false bool/true) +and: Any (and) = λa (a λb (b bool/true bool/false) λd (d bool/false bool/false)) +and2: Any (and2) = λa (a λb b λd let * = d; bool/false) +and3: Any (and3) = λa (a λb (b bool/true bool/false) λd let * = d; bool/false) +and4: Any (and4) = λa (a λb (b bool/true bool/false) λd let * = d; bool/false) +bool/true: bool (bool/true) = λa λ* a +bool/false: bool (bool/false) = λ* λb b NumScott +not: Any (not) = λa (a λb switch b { 0: bool/false; _: λ* bool/true; }) +and: Any (and) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf (f λh switch h { 0: bool/false; _: λ* bool/false; }); }) +and2: Any (and2) = λa (a λb switch b { 0: λc c; _: λ* λe let * = e; bool/false; }) +and3: Any (and3) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf let * = f; bool/false; }) +and4: Any (and4) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf let * = f; bool/false; }) +bool/true: bool (bool/true) = λa (a bool/true/tag) +bool/false: bool (bool/false) = λa (a bool/false/tag) +bool/true/tag: u24 (bool/true/tag) = 0 +bool/false/tag: u24 (bool/false/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__bool_tup.bend.snap b/tests/snapshots/encode_pattern_match__bool_tup.bend.snap index c8b2e15ba..545e9154d 100644 --- a/tests/snapshots/encode_pattern_match__bool_tup.bend.snap +++ b/tests/snapshots/encode_pattern_match__bool_tup.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/bool_tup.bend --- Scott +foo: Any (foo) = λa let (b, c) = a; (b λd d λ* Bool/F c) +main: Any (main) = (foo (Bool/F, Bool/T)) +Bool/T: Bool (Bool/T) = λa λ* a +Bool/F: Bool (Bool/F) = λ* λb b NumScott +foo: Any (foo) = λa let (b, c) = a; (b λd switch d { 0: λe e; _: λ* λ* Bool/F; } c) +main: Any (main) = (foo (Bool/F, Bool/T)) +Bool/T: Bool (Bool/T) = λa (a Bool/T/tag) +Bool/F: Bool (Bool/F) = λa (a Bool/F/tag) +Bool/T/tag: u24 (Bool/T/tag) = 0 +Bool/F/tag: u24 (Bool/F/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__box.bend.snap b/tests/snapshots/encode_pattern_match__box.bend.snap index 02f4208b3..45c8813a9 100644 --- a/tests/snapshots/encode_pattern_match__box.bend.snap +++ b/tests/snapshots/encode_pattern_match__box.bend.snap @@ -3,13 +3,18 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/box.bend --- Scott +unbox: Any (unbox) = λa (a λb b) +box/new: (Any -> box) (box/new) = λa λb (b a) NumScott +unbox: Any (unbox) = λa (a λb switch b { 0: λc c; _: *; }) +box/new: (Any -> box) (box/new) = λa λb (b box/new/tag a) +box/new/tag: u24 (box/new/tag) = 0 diff --git a/tests/snapshots/encode_pattern_match__common.bend.snap b/tests/snapshots/encode_pattern_match__common.bend.snap index 802b979f2..7c0bd147b 100644 --- a/tests/snapshots/encode_pattern_match__common.bend.snap +++ b/tests/snapshots/encode_pattern_match__common.bend.snap @@ -3,105 +3,156 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/common.bend --- Scott +Box/Filled: (Any -> Box) (Box/Filled) = λa λb λ* (b a) +Box/Empty: Box (Box/Empty) = λ* λb b +Option/Some: (Any -> Option) (Option/Some) = λa λb λ* (b a) +Option/None: Option (Option/None) = λ* λb b +Result_/Ok: (Any -> Result_) (Result_/Ok) = λa λb λ* (b a) +Result_/Err: (Any -> Result_) (Result_/Err) = λa λ* λc (c a) +List_/Cons: (Any -> (Any -> List_)) (List_/Cons) = λa λb λc λ* (c a b) +List_/Nil: List_ (List_/Nil) = λ* λb b +Bool/True: Bool (Bool/True) = λa λ* a +Bool/False: Bool (Bool/False) = λ* λb b +Light/Red: Light (Light/Red) = λa λ* λ* a +Light/Yellow: Light (Light/Yellow) = λ* λb λ* b +Light/Green: Light (Light/Green) = λ* λ* λc c +Direction/North: Direction (Direction/North) = λa λ* λ* λ* a +Direction/South: Direction (Direction/South) = λ* λb λ* λ* b +Direction/East: Direction (Direction/East) = λ* λ* λc λ* c +Direction/West: Direction (Direction/West) = λ* λ* λ* λd d NumScott +Box/Filled: (Any -> Box) (Box/Filled) = λa λb (b Box/Filled/tag a) +Box/Empty: Box (Box/Empty) = λa (a Box/Empty/tag) +Option/Some: (Any -> Option) (Option/Some) = λa λb (b Option/Some/tag a) +Option/None: Option (Option/None) = λa (a Option/None/tag) +Result_/Ok: (Any -> Result_) (Result_/Ok) = λa λb (b Result_/Ok/tag a) +Result_/Err: (Any -> Result_) (Result_/Err) = λa λb (b Result_/Err/tag a) +List_/Cons: (Any -> (Any -> List_)) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) +List_/Nil: List_ (List_/Nil) = λa (a List_/Nil/tag) +Bool/True: Bool (Bool/True) = λa (a Bool/True/tag) +Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) +Light/Red: Light (Light/Red) = λa (a Light/Red/tag) +Light/Yellow: Light (Light/Yellow) = λa (a Light/Yellow/tag) +Light/Green: Light (Light/Green) = λa (a Light/Green/tag) +Direction/North: Direction (Direction/North) = λa (a Direction/North/tag) +Direction/South: Direction (Direction/South) = λa (a Direction/South/tag) +Direction/East: Direction (Direction/East) = λa (a Direction/East/tag) +Direction/West: Direction (Direction/West) = λa (a Direction/West/tag) +Box/Filled/tag: u24 (Box/Filled/tag) = 0 +Box/Empty/tag: u24 (Box/Empty/tag) = 1 +Option/Some/tag: u24 (Option/Some/tag) = 0 +Option/None/tag: u24 (Option/None/tag) = 1 +Result_/Ok/tag: u24 (Result_/Ok/tag) = 0 +Result_/Err/tag: u24 (Result_/Err/tag) = 1 +List_/Cons/tag: u24 (List_/Cons/tag) = 0 +List_/Nil/tag: u24 (List_/Nil/tag) = 1 +Bool/True/tag: u24 (Bool/True/tag) = 0 +Bool/False/tag: u24 (Bool/False/tag) = 1 +Light/Red/tag: u24 (Light/Red/tag) = 0 +Light/Yellow/tag: u24 (Light/Yellow/tag) = 1 +Light/Green/tag: u24 (Light/Green/tag) = 2 +Direction/North/tag: u24 (Direction/North/tag) = 0 +Direction/South/tag: u24 (Direction/South/tag) = 1 +Direction/East/tag: u24 (Direction/East/tag) = 2 +Direction/West/tag: u24 (Direction/West/tag) = 3 diff --git a/tests/snapshots/encode_pattern_match__concat.bend.snap b/tests/snapshots/encode_pattern_match__concat.bend.snap index 6d634e7e2..bb4a87216 100644 --- a/tests/snapshots/encode_pattern_match__concat.bend.snap +++ b/tests/snapshots/encode_pattern_match__concat.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/concat.bend --- Scott +String/concat: Any (String/concat) = λ* λb b +main: Any (main) = (String/concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) +String/Nil: String (String/Nil) = λa λ* a +String/Cons: (u24 -> (String -> String)) (String/Cons) = λa λb λ* λd (d a b) NumScott +String/concat: Any (String/concat) = λ* λb b +main: Any (main) = (String/concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) +String/Nil: String (String/Nil) = λa (a String/Nil/tag) +String/Cons: (u24 -> (String -> String)) (String/Cons) = λa λb λc (c String/Cons/tag a b) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__concat_def.bend.snap b/tests/snapshots/encode_pattern_match__concat_def.bend.snap index ac6c42c13..64d25a4b4 100644 --- a/tests/snapshots/encode_pattern_match__concat_def.bend.snap +++ b/tests/snapshots/encode_pattern_match__concat_def.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/concat_def.bend --- Scott +concat: Any (concat) = λa (a λb b λd λe λf (String/Cons d (concat e f))) +main: Any (main) = (concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) +String/Nil: String (String/Nil) = λa λ* a +String/Cons: (u24 -> (String -> String)) (String/Cons) = λa λb λ* λd (d a b) NumScott +concat: Any (concat) = λa (a λb switch b { 0: λc c; _: λ* λe λf λg (String/Cons e (concat f g)); }) +main: Any (main) = (concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) +String/Nil: String (String/Nil) = λa (a String/Nil/tag) +String/Cons: (u24 -> (String -> String)) (String/Cons) = λa λb λc (c String/Cons/tag a b) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__def_tups.bend.snap b/tests/snapshots/encode_pattern_match__def_tups.bend.snap index 92edc1f2d..2309f23e7 100644 --- a/tests/snapshots/encode_pattern_match__def_tups.bend.snap +++ b/tests/snapshots/encode_pattern_match__def_tups.bend.snap @@ -3,11 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/def_tups.bend --- Scott +go: Any (go) = λa let (b, c) = a; let (d, e) = c; let (f, g) = e; let (h, i) = g; (+ (+ (+ (+ i h) f) d) b) +main: Any (main) = (go (1, (2, (3, (4, 5))))) NumScott +go: Any (go) = λa let (b, c) = a; let (d, e) = c; let (f, g) = e; let (h, i) = g; (+ (+ (+ (+ i h) f) d) b) +main: Any (main) = (go (1, (2, (3, (4, 5))))) diff --git a/tests/snapshots/encode_pattern_match__definition_merge.bend.snap b/tests/snapshots/encode_pattern_match__definition_merge.bend.snap index b673bacce..a4281a0d0 100644 --- a/tests/snapshots/encode_pattern_match__definition_merge.bend.snap +++ b/tests/snapshots/encode_pattern_match__definition_merge.bend.snap @@ -3,31 +3,45 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/definition_merge.bend --- Scott +Foo: Any (Foo) = λa (a λb (b λc (c λf (f 1 1) λg (g 2 2)) λh (h λk (k 1 1) λl (l 2 2))) λm (m λn (n λq (q 3 3) λr (r 3 3)) λs (s λv (v 3 3) λw (w 3 3)))) +Either/Left: (Any -> Either) (Either/Left) = λa λb λ* (b a) +Either/Right: (Any -> Either) (Either/Right) = λa λ* λc (c a) +Bool/True: Bool (Bool/True) = λa λ* a +Bool/False: Bool (Bool/False) = λ* λb b NumScott +Foo: Any (Foo) = λa (a λb switch b { 0: λc (c λd switch d { 0: λe (e λh switch h { 0: λi (i λj switch j { 0: 1; _: λ* 1; }); _: λ* λk (k λl switch l { 0: 2; _: λ* 2; }); }); _: λ* λm (m λp switch p { 0: λq (q λr switch r { 0: 1; _: λ* 1; }); _: λ* λs (s λt switch t { 0: 2; _: λ* 2; }); }); }); _: λ* λu (u λv switch v { 0: λw (w λz switch z { 0: λab (ab λbb switch bb { 0: 3; _: λ* 3; }); _: λ* λcb (cb λdb switch db { 0: 3; _: λ* 3; }); }); _: λ* λeb (eb λhb switch hb { 0: λib (ib λjb switch jb { 0: 3; _: λ* 3; }); _: λ* λkb (kb λlb switch lb { 0: 3; _: λ* 3; }); }); }); }) +Either/Left: (Any -> Either) (Either/Left) = λa λb (b Either/Left/tag a) +Either/Right: (Any -> Either) (Either/Right) = λa λb (b Either/Right/tag a) +Bool/True: Bool (Bool/True) = λa (a Bool/True/tag) +Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) +Either/Left/tag: u24 (Either/Left/tag) = 0 +Either/Right/tag: u24 (Either/Right/tag) = 1 +Bool/True/tag: u24 (Bool/True/tag) = 0 +Bool/False/tag: u24 (Bool/False/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__expr.bend.snap b/tests/snapshots/encode_pattern_match__expr.bend.snap index 69afdf370..09b09e99a 100644 --- a/tests/snapshots/encode_pattern_match__expr.bend.snap +++ b/tests/snapshots/encode_pattern_match__expr.bend.snap @@ -3,81 +3,120 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/expr.bend --- Scott +Expr/Var: (Any -> Expr) (Expr/Var) = λa λb λ* λ* λ* λ* λ* λ* λ* λ* (b a) +Expr/Num: (Any -> Expr) (Expr/Num) = λa λ* λc λ* λ* λ* λ* λ* λ* λ* (c a) +Expr/App: (Any -> (Any -> Expr)) (Expr/App) = λa λb λ* λ* λe λ* λ* λ* λ* λ* λ* (e a b) +Expr/Fun: (Any -> (Any -> Expr)) (Expr/Fun) = λa λb λ* λ* λ* λf λ* λ* λ* λ* λ* (f a b) +Expr/If: (Any -> (Any -> (Any -> Expr))) (Expr/If) = λa λb λc λ* λ* λ* λ* λh λ* λ* λ* λ* (h a b c) +Expr/Let: (Any -> (Any -> (Any -> Expr))) (Expr/Let) = λa λb λc λ* λ* λ* λ* λ* λi λ* λ* λ* (i a b c) +Expr/Dup: (Any -> (Any -> (Any -> (Any -> Expr)))) (Expr/Dup) = λa λb λc λd λ* λ* λ* λ* λ* λ* λk λ* λ* (k a b c d) +Expr/Tup: (Any -> (Any -> Expr)) (Expr/Tup) = λa λb λ* λ* λ* λ* λ* λ* λ* λj λ* (j a b) +Expr/Op2: (Any -> (Any -> (Any -> Expr))) (Expr/Op2) = λa λb λc λ* λ* λ* λ* λ* λ* λ* λ* λl (l a b c) +Op/Add: Op (Op/Add) = λa λ* λ* λ* a +Op/Sub: Op (Op/Sub) = λ* λb λ* λ* b +Op/Mul: Op (Op/Mul) = λ* λ* λc λ* c +Op/Div: Op (Op/Div) = λ* λ* λ* λd d NumScott +Expr/Var: (Any -> Expr) (Expr/Var) = λa λb (b Expr/Var/tag a) +Expr/Num: (Any -> Expr) (Expr/Num) = λa λb (b Expr/Num/tag a) +Expr/App: (Any -> (Any -> Expr)) (Expr/App) = λa λb λc (c Expr/App/tag a b) +Expr/Fun: (Any -> (Any -> Expr)) (Expr/Fun) = λa λb λc (c Expr/Fun/tag a b) +Expr/If: (Any -> (Any -> (Any -> Expr))) (Expr/If) = λa λb λc λd (d Expr/If/tag a b c) +Expr/Let: (Any -> (Any -> (Any -> Expr))) (Expr/Let) = λa λb λc λd (d Expr/Let/tag a b c) +Expr/Dup: (Any -> (Any -> (Any -> (Any -> Expr)))) (Expr/Dup) = λa λb λc λd λe (e Expr/Dup/tag a b c d) +Expr/Tup: (Any -> (Any -> Expr)) (Expr/Tup) = λa λb λc (c Expr/Tup/tag a b) +Expr/Op2: (Any -> (Any -> (Any -> Expr))) (Expr/Op2) = λa λb λc λd (d Expr/Op2/tag a b c) +Op/Add: Op (Op/Add) = λa (a Op/Add/tag) +Op/Sub: Op (Op/Sub) = λa (a Op/Sub/tag) +Op/Mul: Op (Op/Mul) = λa (a Op/Mul/tag) +Op/Div: Op (Op/Div) = λa (a Op/Div/tag) +Expr/Var/tag: u24 (Expr/Var/tag) = 0 +Expr/Num/tag: u24 (Expr/Num/tag) = 1 +Expr/App/tag: u24 (Expr/App/tag) = 2 +Expr/Fun/tag: u24 (Expr/Fun/tag) = 3 +Expr/If/tag: u24 (Expr/If/tag) = 4 +Expr/Let/tag: u24 (Expr/Let/tag) = 5 +Expr/Dup/tag: u24 (Expr/Dup/tag) = 6 +Expr/Tup/tag: u24 (Expr/Tup/tag) = 7 +Expr/Op2/tag: u24 (Expr/Op2/tag) = 8 +Op/Add/tag: u24 (Op/Add/tag) = 0 +Op/Sub/tag: u24 (Op/Sub/tag) = 1 +Op/Mul/tag: u24 (Op/Mul/tag) = 2 +Op/Div/tag: u24 (Op/Div/tag) = 3 diff --git a/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap b/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap index eada49386..e0297e441 100644 --- a/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap +++ b/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/flatten_era_pat.bend --- Scott +Fn1: Any (Fn1) = λa λ* let (*, d) = a; let (e, *) = d; e +Fn2: Any (Fn2) = λa let (*, c) = a; let (*, e) = c; let (f, *) = e; f +Fn3: Any (Fn3) = λa let (b, c) = a; (switch b { 0: λ* λe let * = e; 0; _: λg λ* λi let * = i; (+ g 1); } c) +main: Any (main) = (Fn2 ((1, 2), (3, (4, (5, 6)))) 0) NumScott +Fn1: Any (Fn1) = λa λ* let (*, d) = a; let (e, *) = d; e +Fn2: Any (Fn2) = λa let (*, c) = a; let (*, e) = c; let (f, *) = e; f +Fn3: Any (Fn3) = λa let (b, c) = a; (switch b { 0: λ* λe let * = e; 0; _: λg λ* λi let * = i; (+ g 1); } c) +main: Any (main) = (Fn2 ((1, 2), (3, (4, (5, 6)))) 0) diff --git a/tests/snapshots/encode_pattern_match__full_map.bend.snap b/tests/snapshots/encode_pattern_match__full_map.bend.snap index aac39459c..ac0cb38e7 100644 --- a/tests/snapshots/encode_pattern_match__full_map.bend.snap +++ b/tests/snapshots/encode_pattern_match__full_map.bend.snap @@ -3,43 +3,63 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/full_map.bend --- Scott +Map/get: ∀T ((Map T) -> (u24 -> ((Map T), T))) (Map/get) = λa (a λb let {b b_2 b_3 b_4} = b; λc let {c c_2 c_3} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3 e_4} = e; switch (== 0 e) { 0: switch (% e_2 2) { 0: let (f, g) = (Map/get c (/ e_3 2)); (f, (Map/Node b g d)); _: λ* let (i, j) = (Map/get d_2 (/ e_4 2)); (i, (Map/Node b_2 c_2 j)); }; _: λ* (b_3, (Map/Node b_4 c_3 d_3)); } λ* (*, Map/Leaf)) +prng: (Any -> Any) (prng) = λa let {a a_2} = a; let {b b_2} = (^ a (<< a_2 13)); let {c c_2} = (^ b (>> b_2 17)); (^ c (<< c_2 5)) +fullMap: Any (fullMap) = (fullMap__bend0 14) +test: (Any -> Any) (test) = λa (test__bend0 0 a) +main: Any (main) = (test fullMap) +Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) (Map/Node) = λa λb λc λd λ* (d a b c) +Map/Leaf: ∀T (Map T) (Map/Leaf) = λ* λb b +fullMap__bend0: _ (fullMap__bend0) = λa let {a a_2 a_3} = a; switch (> a 0) { 0: Map/Leaf; _: λ* (Map/Node 1 (fullMap__bend0 (- a_2 1)) (fullMap__bend0 (- a_3 1))); } +test__bend0: _ (test__bend0) = λa let {a a_2 a_3} = a; switch (< a 1000) { 0: λ* 0; _: λ* λd let (e, f) = (Map/get d (% (prng a_2) 4096)); (+ e (test__bend0 (+ a_3 1) f)); } NumScott +Map/get: ∀T ((Map T) -> (u24 -> ((Map T), T))) (Map/get) = λa (a λb switch b { 0: λc let {c c_2 c_3 c_4} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3} = e; λf let {f f_2 f_3 f_4} = f; switch (== 0 f) { 0: switch (% f_2 2) { 0: let (g, h) = (Map/get d (/ f_3 2)); (g, (Map/Node c h e)); _: λ* let (j, k) = (Map/get e_2 (/ f_4 2)); (j, (Map/Node c_2 d_2 k)); }; _: λ* (c_3, (Map/Node c_4 d_3 e_3)); }; _: λ* λ* (*, Map/Leaf); }) +prng: (Any -> Any) (prng) = λa let {a a_2} = a; let {b b_2} = (^ a (<< a_2 13)); let {c c_2} = (^ b (>> b_2 17)); (^ c (<< c_2 5)) +fullMap: Any (fullMap) = (fullMap__bend0 14) +test: (Any -> Any) (test) = λa (test__bend0 0 a) +main: Any (main) = (test fullMap) +Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) (Map/Node) = λa λb λc λd (d Map/Node/tag a b c) +Map/Leaf: ∀T (Map T) (Map/Leaf) = λa (a Map/Leaf/tag) +Map/Node/tag: u24 (Map/Node/tag) = 0 +Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 +fullMap__bend0: _ (fullMap__bend0) = λa let {a a_2 a_3} = a; switch (> a 0) { 0: Map/Leaf; _: λ* (Map/Node 1 (fullMap__bend0 (- a_2 1)) (fullMap__bend0 (- a_3 1))); } +test__bend0: _ (test__bend0) = λa let {a a_2 a_3} = a; switch (< a 1000) { 0: λ* 0; _: λ* λd let (e, f) = (Map/get d (% (prng a_2) 4096)); (+ e (test__bend0 (+ a_3 1) f)); } diff --git a/tests/snapshots/encode_pattern_match__is_some_some.bend.snap b/tests/snapshots/encode_pattern_match__is_some_some.bend.snap index 45d789362..426432482 100644 --- a/tests/snapshots/encode_pattern_match__is_some_some.bend.snap +++ b/tests/snapshots/encode_pattern_match__is_some_some.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/is_some_some.bend --- Scott +some_some: Any (some_some) = λa (a λb (b λ* 1 0) 0) +main: Any (main) = (some_some (Option/Some 1)) +Option/Some: (Any -> Option) (Option/Some) = λa λb λ* (b a) +Option/None: Option (Option/None) = λ* λb b NumScott +some_some: Any (some_some) = λa (a λb switch b { 0: λc (c λd switch d { 0: λ* 1; _: λ* 0; }); _: λ* 0; }) +main: Any (main) = (some_some (Option/Some 1)) +Option/Some: (Any -> Option) (Option/Some) = λa λb (b Option/Some/tag a) +Option/None: Option (Option/None) = λa (a Option/None/tag) +Option/Some/tag: u24 (Option/Some/tag) = 0 +Option/None/tag: u24 (Option/None/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap b/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap index 6be31d664..dce461210 100644 --- a/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap @@ -3,55 +3,81 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_merge_sort.bend --- Scott +If: Any (If) = λa (a λb λc let * = c; b λf λg let * = f; g) +Pure: Any (Pure) = λa (List_/Cons a List_/Nil) +Map: Any (Map) = λa (a λb λc λd let {e e_2} = d; (List_/Cons (e b) (Map c e_2)) λf let * = f; List_/Nil) +MergeSort: Any (MergeSort) = λa λb (Unpack a (Map b Pure)) +Unpack: Any (Unpack) = λa λb (b λc λd λe (d λf λg λh let {h h_2} = h; λi (Unpack h (MergePair h_2 (List_/Cons i (List_/Cons f g)))) λ* λk k e c) λ* List_/Nil a) +MergePair: Any (MergePair) = λa λb (b λc λd λe (d λf λg λh let {h h_2} = h; λi (List_/Cons (Merge h i f) (MergePair h_2 g)) λ* λk (List_/Cons k List_/Nil) e c) λ* List_/Nil a) +Merge: Any (Merge) = λa λb (b λc λd λe λf (f λh let {h h_2 h_3} = h; λi let {i i_2} = i; λj let {j j_2 j_3} = j; λk let {k k_2 k_3} = k; λl let {l l_2} = l; (If (j k h) (List_/Cons k_2 (Merge j_2 l (List_/Cons h_2 i))) (List_/Cons h_3 (Merge j_3 (List_/Cons k_3 l_2) i_2))) λ* λp λq (List_/Cons p q) e c d) λ* λs s a) +Bool/True: Bool (Bool/True) = λa λ* a +Bool/False: Bool (Bool/False) = λ* λb b +List_/Cons: (Any -> (Any -> List_)) (List_/Cons) = λa λb λc λ* (c a b) +List_/Nil: List_ (List_/Nil) = λ* λb b NumScott +If: Any (If) = λa (a λb switch b { 0: λc λd let * = d; c; _: λ* λg λh let * = g; h; }) +Pure: Any (Pure) = λa (List_/Cons a List_/Nil) +Map: Any (Map) = λa (a λb switch b { 0: λc λd λe let {f f_2} = e; (List_/Cons (f c) (Map d f_2)); _: λ* λg let * = g; List_/Nil; }) +MergeSort: Any (MergeSort) = λa λb (Unpack a (Map b Pure)) +Unpack: Any (Unpack) = λa λb (b λc switch c { 0: λd λe λf (e λg switch g { 0: λh λi λj let {j j_2} = j; λk (Unpack j (MergePair j_2 (List_/Cons k (List_/Cons h i)))); _: λ* λ* λm m; } f d); _: λ* λ* List_/Nil; } a) +MergePair: Any (MergePair) = λa λb (b λc switch c { 0: λd λe λf (e λg switch g { 0: λh λi λj let {j j_2} = j; λk (List_/Cons (Merge j k h) (MergePair j_2 i)); _: λ* λ* λm (List_/Cons m List_/Nil); } f d); _: λ* λ* List_/Nil; } a) +Merge: Any (Merge) = λa λb (b λc switch c { 0: λd λe λf λg (g λi switch i { 0: λj let {j j_2 j_3} = j; λk let {k k_2} = k; λl let {l l_2 l_3} = l; λm let {m m_2 m_3} = m; λn let {n n_2} = n; (If (l m j) (List_/Cons m_2 (Merge l_2 n (List_/Cons j_2 k))) (List_/Cons j_3 (Merge l_3 (List_/Cons m_3 n_2) k_2))); _: λ* λ* λr λs (List_/Cons r s); } f d e); _: λ* λ* λu u; } a) +Bool/True: Bool (Bool/True) = λa (a Bool/True/tag) +Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) +List_/Cons: (Any -> (Any -> List_)) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) +List_/Nil: List_ (List_/Nil) = λa (a List_/Nil/tag) +Bool/True/tag: u24 (Bool/True/tag) = 0 +Bool/False/tag: u24 (Bool/False/tag) = 1 +List_/Cons/tag: u24 (List_/Cons/tag) = 0 +List_/Nil/tag: u24 (List_/Nil/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap index 9dd3fd6d3..c7fd87bb5 100644 --- a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap @@ -3,15 +3,21 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_str_encoding_undeclared_fn.bend --- Scott +main: Any (main) = * +Foo: Any (Foo) = λa (a 0 λ* λ* 1) +Bar: Any (Bar) = λa (a 1 λ* λ* 0) NumScott +main: Any (main) = * +Foo: Any (Foo) = λa (a λb switch b { 0: 0; _: λ* λ* λ* 1; }) +Bar: Any (Bar) = λa (a λb switch b { 0: 1; _: λ* λ* λ* 0; }) diff --git a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap index edf0b7a65..7b996fb19 100644 --- a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap @@ -3,7 +3,9 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_str_encoding_undeclared_map.bend --- Scott +main: Any (main) = λa λb ((a 2 λ* λ* 1), (b 2 λ* λ* 1)) NumScott +main: Any (main) = λa λb ((a λc switch c { 0: 2; _: λ* λ* λ* 1; }), (b λg switch g { 0: 2; _: λ* λ* λ* 1; })) diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap index bb3caa307..8950a8a6c 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_in_arm.bend --- Scott +main: Any (main) = λ* λ$x $x +bool/T: bool (bool/T) = λa λ* a +bool/F: bool (bool/F) = λ* λb b NumScott +main: Any (main) = λ* λ$x $x +bool/T: bool (bool/T) = λa (a bool/T/tag) +bool/F: bool (bool/F) = λa (a bool/F/tag) +bool/T/tag: u24 (bool/T/tag) = 0 +bool/F/tag: u24 (bool/F/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap index 0d83206e0..38b6880ad 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_lambda.bend --- Scott +main: Any (main) = (Maybe/Some 1 λ$x * λa a $x) +Maybe/None: Maybe (Maybe/None) = λa λ* a +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λ* λc (c a) NumScott +main: Any (main) = (Maybe/Some 1 λa switch a { 0: λ$x *; _: λ* λb b; } $x) +Maybe/None: Maybe (Maybe/None) = λa (a Maybe/None/tag) +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λb (b Maybe/Some/tag a) +Maybe/None/tag: u24 (Maybe/None/tag) = 0 +Maybe/Some/tag: u24 (Maybe/Some/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap index 7ac3cdabd..53f17bb50 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap @@ -3,27 +3,39 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_var.bend --- Scott +Foo: Any (Foo) = λ$x (Maybe/Some 1 $x λa a) +Bar: Any (Bar) = (Maybe/Some 1 $x λa a λ$x *) +main: Any (main) = * +Maybe/None: Maybe (Maybe/None) = λa λ* a +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λ* λc (c a) NumScott +Foo: Any (Foo) = λ$x (Maybe/Some 1 λa switch a { 0: $x; _: λ* λb b; }) +Bar: Any (Bar) = (Maybe/Some 1 λa switch a { 0: $x; _: λ* λb b; } λ$x *) +main: Any (main) = * +Maybe/None: Maybe (Maybe/None) = λa (a Maybe/None/tag) +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λb (b Maybe/Some/tag a) +Maybe/None/tag: u24 (Maybe/None/tag) = 0 +Maybe/Some/tag: u24 (Maybe/Some/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap b/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap index a39592681..3b8b4313e 100644 --- a/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap @@ -3,25 +3,36 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_auto_linearization.bend --- Scott +switch_linearization: Any (switch_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; switch a_2 { 0: let {e e_2} = 2; let {f g} = e; (b_2 e_2 f g (a_3, b_3) c d); _: λh let {i i_2} = 2; let {j k} = i; (h b_4 i_2 j k (a_4, b_5) c_2 d_2); } +match_linearization: Any (match_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; (a_2 λe let {f f_2} = 2; let {g h} = f; (e b_2 f_2 g h (a_3, b_3) c d) let {i i_2} = 2; let {j k} = i; (b_4 i_2 j k (a_4, b_5) c_2 d_2)) +switch_shadowed_field: Any (switch_shadowed_field) = λa switch a { 0: λb b; _: λc λ* c; } +match_shadowed_field: Any (match_shadowed_field) = λa (a λb λc (List/Cons b c) λd λe λ* λ* (List/Cons d e)) +List/Cons: ∀t (u24 -> ((List t) -> (List t))) (List/Cons) = λa λb λ* λd (d a b) NumScott +switch_linearization: Any (switch_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; switch a_2 { 0: let {e e_2} = 2; let {f g} = e; (b_2 e_2 f g (a_3, b_3) c d); _: λh let {i i_2} = 2; let {j k} = i; (h b_4 i_2 j k (a_4, b_5) c_2 d_2); } +match_linearization: Any (match_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; (a_2 λe switch e { 0: λf let {g g_2} = 2; let {h i} = g; (f b_2 g_2 h i (a_3, b_3) c d); _: λ* let {j j_2} = 2; let {k l} = j; (b_4 j_2 k l (a_4, b_5) c_2 d_2); }) +switch_shadowed_field: Any (switch_shadowed_field) = λa switch a { 0: λb b; _: λc λ* c; } +match_shadowed_field: Any (match_shadowed_field) = λa (a λb switch b { 0: λc λd (List/Cons c d); _: λ* λe λf λ* λ* (List/Cons e f); }) +List/Cons: ∀t (u24 -> ((List t) -> (List t))) (List/Cons) = λa λb λc (c List/Cons/tag a b) +List/Cons/tag: u24 (List/Cons/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_bind.bend.snap b/tests/snapshots/encode_pattern_match__match_bind.bend.snap index 0b0da60b0..33a29753f 100644 --- a/tests/snapshots/encode_pattern_match__match_bind.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_bind.bend.snap @@ -3,11 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_bind.bend --- Scott +cheese: Any (cheese) = switch (+ 2 3) { 0: 653323; _: λa let {a a_2} = a; (+ (+ a 1) a_2); } +main: Any (main) = cheese NumScott +cheese: Any (cheese) = switch (+ 2 3) { 0: 653323; _: λa let {a a_2} = a; (+ (+ a 1) a_2); } +main: Any (main) = cheese diff --git a/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap b/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap index e70f1f0d6..695c3f5b9 100644 --- a/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap @@ -3,35 +3,51 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_num_adt_tup_parser.bend --- Scott +Parse: Any (Parse) = λa λb (b λc (Result_/Err (String/Nil, c)) λd λe λf (switch (- d 10) { 0: λg λh (Result_/Ok (0, h, g)); _: λi λj λk (switch (- i 29) { 0: λl λm (Result_/Ok (40, m, l)); _: λn λo λp (switch n { 0: λq λr (Result_/Ok (41, r, q)); _: λs λt λu (Result_/Err ((String/Cons (+ s 42) u), t)); } o p); } j k); } f e) a) +main: Any (main) = (Parse * (String/Cons 40 (String/Cons 43 String/Nil)) λc let (d, e, f) = c; (d, (Parse f e)) λg (Result_/Err g)) +String/Nil: String (String/Nil) = λa λ* a +String/Cons: (u24 -> (String -> String)) (String/Cons) = λa λb λ* λd (d a b) +Result_/Ok: (Any -> Result_) (Result_/Ok) = λa λb λ* (b a) +Result_/Err: (Any -> Result_) (Result_/Err) = λa λ* λc (c a) NumScott +Parse: Any (Parse) = λa λb (b λc switch c { 0: λd (Result_/Err (String/Nil, d)); _: λ* λe λf λg (switch (- e 10) { 0: λh λi (Result_/Ok (0, i, h)); _: λj λk λl (switch (- j 29) { 0: λm λn (Result_/Ok (40, n, m)); _: λo λp λq (switch o { 0: λr λs (Result_/Ok (41, s, r)); _: λt λu λv (Result_/Err ((String/Cons (+ t 42) v), u)); } p q); } k l); } g f); } a) +main: Any (main) = (Parse * (String/Cons 40 (String/Cons 43 String/Nil)) λc switch c { 0: λd let (e, f, g) = d; (e, (Parse g f)); _: λ* λh (Result_/Err h); }) +String/Nil: String (String/Nil) = λa (a String/Nil/tag) +String/Cons: (u24 -> (String -> String)) (String/Cons) = λa λb λc (c String/Cons/tag a b) +Result_/Ok: (Any -> Result_) (Result_/Ok) = λa λb (b Result_/Ok/tag a) +Result_/Err: (Any -> Result_) (Result_/Err) = λa λb (b Result_/Err/tag a) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 +Result_/Ok/tag: u24 (Result_/Ok/tag) = 0 +Result_/Err/tag: u24 (Result_/Err/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap b/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap index a3125d796..00e56553e 100644 --- a/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_num_pred.bend --- Scott +pred: Any (pred) = λa switch a { 0: 0; _: λb b; } +pred2: Any (pred2) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc c; }; } +pred3: Any (pred3) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc switch c { 0: 0; _: λd d; }; }; } +zero: Any (zero) = λa switch a { 0: 1; _: λb switch b { 0: 0; _: λ* 0; }; } +main: Any (main) = * NumScott +pred: Any (pred) = λa switch a { 0: 0; _: λb b; } +pred2: Any (pred2) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc c; }; } +pred3: Any (pred3) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc switch c { 0: 0; _: λd d; }; }; } +zero: Any (zero) = λa switch a { 0: 1; _: λb switch b { 0: 0; _: λ* 0; }; } +main: Any (main) = * diff --git a/tests/snapshots/encode_pattern_match__match_syntax.bend.snap b/tests/snapshots/encode_pattern_match__match_syntax.bend.snap index 2a92b75c1..72d2806b5 100644 --- a/tests/snapshots/encode_pattern_match__match_syntax.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_syntax.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_syntax.bend --- Scott +head: Any (head) = λa (a λb λ* b List_/Nil) +List_/Cons: (Any -> (Any -> List_)) (List_/Cons) = λa λb λc λ* (c a b) +List_/Nil: List_ (List_/Nil) = λ* λb b NumScott +head: Any (head) = λa (a λb switch b { 0: λc λ* c; _: λ* List_/Nil; }) +List_/Cons: (Any -> (Any -> List_)) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) +List_/Nil: List_ (List_/Nil) = λa (a List_/Nil/tag) +List_/Cons/tag: u24 (List_/Cons/tag) = 0 +List_/Nil/tag: u24 (List_/Nil/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap b/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap index c56283456..f0c6d5dab 100644 --- a/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap +++ b/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/merge_recursive.bend --- Scott +foo_1: Any (foo_1) = λa (a foo_2) +foo_2: Any (foo_2) = λa λb (a, b) +bar_1: Any (bar_1) = λa (a bar_2) +bar_2: Any (bar_2) = λa λb (a, b) NumScott +foo_1: Any (foo_1) = λa (a foo_2) +foo_2: Any (foo_2) = λa λb (a, b) +bar_1: Any (bar_1) = λa (a bar_2) +bar_2: Any (bar_2) = λa λb (a, b) diff --git a/tests/snapshots/encode_pattern_match__no_patterns.bend.snap b/tests/snapshots/encode_pattern_match__no_patterns.bend.snap index 4b1948738..c586025e2 100644 --- a/tests/snapshots/encode_pattern_match__no_patterns.bend.snap +++ b/tests/snapshots/encode_pattern_match__no_patterns.bend.snap @@ -3,15 +3,21 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/no_patterns.bend --- Scott +Id: Any (Id) = λa a +Id2: Any (Id2) = λa a +Pair: Any (Pair) = λa λb (a, b) NumScott +Id: Any (Id) = λa a +Id2: Any (Id2) = λa a +Pair: Any (Pair) = λa λb (a, b) diff --git a/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap b/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap index b7ecfc64a..1272d01f6 100644 --- a/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap +++ b/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/non_matching_fst_arg.bend --- Scott +Foo: Any (Foo) = λa λb (b λc let {c c_2} = c; (Foo c c_2) λd d a) +bool/true: bool (bool/true) = λa λ* a +bool/false: bool (bool/false) = λ* λb b NumScott +Foo: Any (Foo) = λa λb (b λc switch c { 0: λd let {d d_2} = d; (Foo d d_2); _: λ* λe e; } a) +bool/true: bool (bool/true) = λa (a bool/true/tag) +bool/false: bool (bool/false) = λa (a bool/false/tag) +bool/true/tag: u24 (bool/true/tag) = 0 +bool/false/tag: u24 (bool/false/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap b/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap index 91b91f7eb..fc7d9ba84 100644 --- a/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap +++ b/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap @@ -3,11 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/ntup_sum.bend --- Scott +ntupSum: Any (ntupSum) = λa let (b, c, d, e, f) = a; (+ b (+ c (+ d (+ e f)))) +main: Any (main) = (ntupSum (1, 3, 3, 2, 1)) NumScott +ntupSum: Any (ntupSum) = λa let (b, c, d, e, f) = a; (+ b (+ c (+ d (+ e f)))) +main: Any (main) = (ntupSum (1, 3, 3, 2, 1)) diff --git a/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap b/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap index 3a81eeb10..308a19c2b 100644 --- a/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap +++ b/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap @@ -3,41 +3,60 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/pattern_match_encoding.bend --- Scott +Foo: Any (Foo) = λa (a λ* 100 λ* 200 λ* 200 λ* λ* 200 λ* λ* 200) +main: Any (main) = (Foo MyType/A 2) +MyType/A: (Any -> MyType) (MyType/A) = λa λb λ* λ* λ* λ* (b a) +MyType/B: (Any -> MyType) (MyType/B) = λa λ* λc λ* λ* λ* (c a) +MyType/C: (Any -> MyType) (MyType/C) = λa λ* λ* λd λ* λ* (d a) +MyType/D: (Any -> (Any -> MyType)) (MyType/D) = λa λb λ* λ* λ* λf λ* (f a b) +MyType/E: (Any -> (Any -> MyType)) (MyType/E) = λa λb λ* λ* λ* λ* λg (g a b) NumScott +Foo: Any (Foo) = λa (a λb switch b { 0: λ* 100; _: λd switch d { 0: λ* 200; _: λf switch f { 0: λ* 200; _: λh switch h { 0: λ* λ* 200; _: λ* λ* λ* 200; }; }; }; }) +main: Any (main) = (Foo MyType/A 2) +MyType/A: (Any -> MyType) (MyType/A) = λa λb (b MyType/A/tag a) +MyType/B: (Any -> MyType) (MyType/B) = λa λb (b MyType/B/tag a) +MyType/C: (Any -> MyType) (MyType/C) = λa λb (b MyType/C/tag a) +MyType/D: (Any -> (Any -> MyType)) (MyType/D) = λa λb λc (c MyType/D/tag a b) +MyType/E: (Any -> (Any -> MyType)) (MyType/E) = λa λb λc (c MyType/E/tag a b) +MyType/A/tag: u24 (MyType/A/tag) = 0 +MyType/B/tag: u24 (MyType/B/tag) = 1 +MyType/C/tag: u24 (MyType/C/tag) = 2 +MyType/D/tag: u24 (MyType/D/tag) = 3 +MyType/E/tag: u24 (MyType/E/tag) = 4 diff --git a/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap b/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap index 0cfe4b49a..dbdb41915 100644 --- a/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap +++ b/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap @@ -3,7 +3,9 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/switch_in_switch_arg.bend --- Scott +main: Any (main) = λa switch switch a { 0: 0; _: λb b; } { 0: 0; _: λc (+ c 1); } NumScott +main: Any (main) = λa switch switch a { 0: 0; _: λb b; } { 0: 0; _: λc (+ c 1); } diff --git a/tests/snapshots/encode_pattern_match__var_only.bend.snap b/tests/snapshots/encode_pattern_match__var_only.bend.snap index 65c06bc22..da91c652f 100644 --- a/tests/snapshots/encode_pattern_match__var_only.bend.snap +++ b/tests/snapshots/encode_pattern_match__var_only.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/var_only.bend --- Scott +Foo: Any (Foo) = λa λ* λc (c a) +main: Any (main) = λ* Foo +Bool/False: Bool (Bool/False) = λa λ* a +Bool/True: Bool (Bool/True) = λ* λb b NumScott +Foo: Any (Foo) = λa λ* λc (c a) +main: Any (main) = λ* Foo +Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) +Bool/True: Bool (Bool/True) = λa (a Bool/True/tag) +Bool/False/tag: u24 (Bool/False/tag) = 0 +Bool/True/tag: u24 (Bool/True/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__weekday.bend.snap b/tests/snapshots/encode_pattern_match__weekday.bend.snap index 298584978..d8b97546d 100644 --- a/tests/snapshots/encode_pattern_match__weekday.bend.snap +++ b/tests/snapshots/encode_pattern_match__weekday.bend.snap @@ -3,49 +3,72 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/weekday.bend --- Scott +main: Any (main) = (λa a Weekday/Saturday) +Weekday/Monday: Weekday (Weekday/Monday) = λa λ* λ* λ* λ* λ* λ* a +Weekday/Tuesday: Weekday (Weekday/Tuesday) = λ* λb λ* λ* λ* λ* λ* b +Weekday/Wednesday: Weekday (Weekday/Wednesday) = λ* λ* λc λ* λ* λ* λ* c +Weekday/Thursday: Weekday (Weekday/Thursday) = λ* λ* λ* λd λ* λ* λ* d +Weekday/Friday: Weekday (Weekday/Friday) = λ* λ* λ* λ* λe λ* λ* e +Weekday/Saturday: Weekday (Weekday/Saturday) = λ* λ* λ* λ* λ* λf λ* f +Weekday/Sunday: Weekday (Weekday/Sunday) = λ* λ* λ* λ* λ* λ* λg g NumScott +main: Any (main) = (λa a Weekday/Saturday) +Weekday/Monday: Weekday (Weekday/Monday) = λa (a Weekday/Monday/tag) +Weekday/Tuesday: Weekday (Weekday/Tuesday) = λa (a Weekday/Tuesday/tag) +Weekday/Wednesday: Weekday (Weekday/Wednesday) = λa (a Weekday/Wednesday/tag) +Weekday/Thursday: Weekday (Weekday/Thursday) = λa (a Weekday/Thursday/tag) +Weekday/Friday: Weekday (Weekday/Friday) = λa (a Weekday/Friday/tag) +Weekday/Saturday: Weekday (Weekday/Saturday) = λa (a Weekday/Saturday/tag) +Weekday/Sunday: Weekday (Weekday/Sunday) = λa (a Weekday/Sunday/tag) +Weekday/Monday/tag: u24 (Weekday/Monday/tag) = 0 +Weekday/Tuesday/tag: u24 (Weekday/Tuesday/tag) = 1 +Weekday/Wednesday/tag: u24 (Weekday/Wednesday/tag) = 2 +Weekday/Thursday/tag: u24 (Weekday/Thursday/tag) = 3 +Weekday/Friday/tag: u24 (Weekday/Friday/tag) = 4 +Weekday/Saturday/tag: u24 (Weekday/Saturday/tag) = 5 +Weekday/Sunday/tag: u24 (Weekday/Sunday/tag) = 6 diff --git a/tests/snapshots/parse_file__fun_def.bend.snap b/tests/snapshots/parse_file__fun_def.bend.snap index 98c4381f2..9ec226324 100644 --- a/tests/snapshots/parse_file__fun_def.bend.snap +++ b/tests/snapshots/parse_file__fun_def.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/fun_def.bend --- -(main) = let base = 0; def (aux) = λ%arg0 match %arg0 = %arg0 { List/Nil: base; List/Cons %arg0.head %arg0.tail: use tail = %arg0.tail; use head = %arg0.head; (+ head (aux tail)); }(aux (List/Cons 1 (List/Cons 2 (List/Cons 3 List/Nil)))) +main: Any +(main) = let base = 0; def (aux (List/Nil)) = base(aux (List/Cons head tail)) = (+ head (aux tail))(aux (List/Cons 1 (List/Cons 2 (List/Cons 3 List/Nil)))) diff --git a/tests/snapshots/parse_file__fun_def_name.bend.snap b/tests/snapshots/parse_file__fun_def_name.bend.snap index e2a6af2e6..5ba6c0e51 100644 --- a/tests/snapshots/parse_file__fun_def_name.bend.snap +++ b/tests/snapshots/parse_file__fun_def_name.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/fun_def_name.bend --- Errors: -In tests/golden_tests/parse_file/fun_def_name.bend : -Expected a rule with name 'aux'. - 4 | aux2 (List/Cons head tail) = (+ head (aux tail)) +In tests/golden_tests/parse_file/fun_def_name.bend : +Redefinition of builtin (constructor) 'List/Cons'. + 4 | aux2 (List/Cons head tail) = (+ head (aux tail)) diff --git a/tests/snapshots/parse_file__imp_map.bend.snap b/tests/snapshots/parse_file__imp_map.bend.snap index 3f04e2682..a8f88badd 100644 --- a/tests/snapshots/parse_file__imp_map.bend.snap +++ b/tests/snapshots/parse_file__imp_map.bend.snap @@ -2,18 +2,26 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/imp_map.bend --- +Map/empty: (Map T) (Map/empty) = Map/Leaf -(Map/get) = λ%arg0 λ%arg1 use key = %arg1; use map = %arg0; match map = map { Map/Leaf: (*, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } +Map/get: ((Map T) -> (u24 -> ((Map T), T))) +(Map/get map key) = match map = map { Map/Leaf: (*, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } -(Map/set) = λ%arg0 λ%arg1 λ%arg2 use value = %arg2; use key = %arg1; use map = %arg0; match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +Map/set: ((Map T) -> (u24 -> (T -> (Map T)))) +(Map/set map key value) = match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +main: Any (main) = let x = (Map/set (Map/set Map/empty 2 1) 3 2); let (map/get%1, x) = (Map/get x 2); let y = (id map/get%1); let z = 4; let x = (Map/set x z 4); let (map/get%0, x) = (Map/get x z); (+ y map/get%0) +Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) (Map/Node) = λvalue λleft λright λ%x (%x Map/Node/tag value left right) +Map/Leaf: ∀T (Map T) (Map/Leaf) = λ%x (%x Map/Leaf/tag) +Map/Node/tag: u24 (Map/Node/tag) = 0 +Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 diff --git a/tests/snapshots/parse_file__imp_program.bend.snap b/tests/snapshots/parse_file__imp_program.bend.snap index da62166e1..d852e10ee 100644 --- a/tests/snapshots/parse_file__imp_program.bend.snap +++ b/tests/snapshots/parse_file__imp_program.bend.snap @@ -2,66 +2,98 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/imp_program.bend --- +Map/empty: (Map T) (Map/empty) = Map/Leaf -(Map/get) = λ%arg0 λ%arg1 use key = %arg1; use map = %arg0; match map = map { Map/Leaf: (*, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } +Map/get: ((Map T) -> (u24 -> ((Map T), T))) +(Map/get map key) = match map = map { Map/Leaf: (*, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } -(Map/set) = λ%arg0 λ%arg1 λ%arg2 use value = %arg2; use key = %arg1; use map = %arg0; match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +Map/set: ((Map T) -> (u24 -> (T -> (Map T)))) +(Map/set map key value) = match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +symbols: Any (symbols) = let x = (Map/set (Map/set Map/empty 49 5) 2 3); let x = (Map/set x 49 2); let x = (Map/set x 2 3); let (map/get%0, x) = (Map/get x 49); (+ map/get%0 8293490) +mk_point: Any (mk_point) = (Point/Point 1 2) -(identity) = λ%arg0 use x = %arg0; x +identity: (Any -> Any) +(identity x) = x -(inc) = λ%arg0 use n = %arg0; let n = (+ n 1); n +inc: (Any -> Any) +(inc n) = let n = (+ n 1); n -(inc_list) = λ%arg0 use list = %arg0; fold %iter = list { List/Nil: List/Nil; List/Cons: let x = %iter.head; (List/Cons (+ x 1) %iter.tail); } +inc_list: (Any -> Any) +(inc_list list) = fold %iter = list { List/Nil: List/Nil; List/Cons: let x = %iter.head; (List/Cons (+ x 1) %iter.tail); } +lam: Any (lam) = λx λy x -(do_match) = λ%arg0 use b = %arg0; match b = b { Bool/True: 1; Bool/False: 0; } +do_match: (Any -> Any) +(do_match b) = match b = b { Bool/True: 1; Bool/False: 0; } +true: Any (true) = Bool/True -(fib) = λ%arg0 use n = %arg0; switch %pred = (< n 2) { 0: (+ (fib (- n 1)) (fib (- n 2))); _ %pred-1: n; } +fib: (Any -> Any) +(fib n) = switch %pred = (< n 2) { 0: (+ (fib (- n 1)) (fib (- n 2))); _ %pred-1: n; } -(swt) = λ%arg0 use n = %arg0; switch n = n { 0: 42; _ n-1: 1; } +swt: (Any -> Any) +(swt n) = switch n = n { 0: 42; _ n-1: 1; } -(fld) = λ%arg0 use list = %arg0; fold list = list { List/Cons: 1; List/Nil: 2; } +fld: (Any -> Any) +(fld list) = fold list = list { List/Cons: 1; List/Nil: 2; } +bnd: Any (bnd) = bend x = 0, { when (< x 10): (List/Cons x (fork (+ x 1))); else: List/Nil } +era: Any (era) = let * = (+ 2 3); let the_expr_killer = *; (the_expr_killer 9) +sup: Any (sup) = let x = {(List/Cons 1 (List/Cons 2 List/Nil)) (List/Cons 3 (List/Cons 4 (List/Cons 5 (List/Cons 6 List/Nil))))}; x +main: Any (main) = with IO { ask x = IO.read; x } +List/Nil: ∀t (List t) (List/Nil) = λ%x (%x List/Nil/tag) +List/Cons: ∀t (u24 -> ((List t) -> (List t))) (List/Cons) = λhead λtail λ%x (%x List/Cons/tag head tail) +Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) (Map/Node) = λvalue λleft λright λ%x (%x Map/Node/tag value left right) +Map/Leaf: ∀T (Map T) (Map/Leaf) = λ%x (%x Map/Leaf/tag) +Point/Point: (Any -> (Any -> Point)) (Point/Point) = λx λy λ%x (%x Point/Point/tag x y) +Bool/True: Bool (Bool/True) = λ%x (%x Bool/True/tag) +Bool/False: Bool (Bool/False) = λ%x (%x Bool/False/tag) +List/Nil/tag: u24 (List/Nil/tag) = 0 +List/Cons/tag: u24 (List/Cons/tag) = 1 +Map/Node/tag: u24 (Map/Node/tag) = 0 +Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 +Point/Point/tag: u24 (Point/Point/tag) = 0 +Bool/True/tag: u24 (Bool/True/tag) = 0 +Bool/False/tag: u24 (Bool/False/tag) = 1 diff --git a/tests/snapshots/parse_file__multi_line_comment.bend.snap b/tests/snapshots/parse_file__multi_line_comment.bend.snap index 0782b826f..4864282af 100644 --- a/tests/snapshots/parse_file__multi_line_comment.bend.snap +++ b/tests/snapshots/parse_file__multi_line_comment.bend.snap @@ -2,22 +2,32 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/multi_line_comment.bend --- -(X) = λ%arg0 λ%arg1 λ%arg2 use x = %arg2; x +X: Any +(X x x x) = x -(String/is_empty) = λ%arg0 use s = %arg0; match s = s { String/Nil: 1; String/Cons: 0; } +String/is_empty: Any +(String/is_empty s) = match s = s { String/Nil: 1; String/Cons: 0; } +main: Any (main) = 0 -(String/not_empty) = λ%arg0 use s = %arg0; match s = s { String/Nil: 0; String/Cons: 1; } +String/not_empty: (Any -> Any) +(String/not_empty s) = match s = s { String/Nil: 0; String/Cons: 1; } +Foo/Foo: (Any -> Foo) (Foo/Foo) = λfoo λ%x (%x Foo/Foo/tag foo) +Bar: (Any -> Bar) (Bar) = λbar λ%x (%x Bar/tag bar) +V/V: V (V/V) = λ%x (%x V/V/tag) +Foo/Foo/tag: u24 (Foo/Foo/tag) = 0 +Bar/tag: u24 (Bar/tag) = 0 +V/V/tag: u24 (V/V/tag) = 0 diff --git a/tests/snapshots/parse_file__scape_chars.bend.snap b/tests/snapshots/parse_file__scape_chars.bend.snap index f626e7b78..0f2acd25d 100644 --- a/tests/snapshots/parse_file__scape_chars.bend.snap +++ b/tests/snapshots/parse_file__scape_chars.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/scape_chars.bend --- +main: Any (main) = (String/Cons 92 (String/Cons 32 (String/Cons 10 (String/Cons 32 (String/Cons 9 (String/Cons 32 (String/Cons 34 String/Nil))))))) +String/Nil: String (String/Nil) = λ%x (%x String/Nil/tag) +String/Cons: (u24 -> (String -> String)) (String/Cons) = λhead λtail λ%x (%x String/Cons/tag head tail) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 diff --git a/tests/snapshots/simplify_matches__adt_tup_era.bend.snap b/tests/snapshots/simplify_matches__adt_tup_era.bend.snap index 33931e508..032138686 100644 --- a/tests/snapshots/simplify_matches__adt_tup_era.bend.snap +++ b/tests/snapshots/simplify_matches__adt_tup_era.bend.snap @@ -2,10 +2,14 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/adt_tup_era.bend --- +Foo: Any (Foo) = λa match a { Tuple/Pair b c: (match b { Tuple/Pair d e: λf d; } c); } +Main: Any (Main) = (Foo (Tuple/Pair 1 5)) +Tuple/Pair: (Any -> (Any -> Tuple)) (Tuple/Pair) = λa λb λc (c Tuple/Pair/tag a b) +Tuple/Pair/tag: u24 (Tuple/Pair/tag) = 0 diff --git a/tests/snapshots/simplify_matches__already_flat.bend.snap b/tests/snapshots/simplify_matches__already_flat.bend.snap index 1e11decca..c10f149f0 100644 --- a/tests/snapshots/simplify_matches__already_flat.bend.snap +++ b/tests/snapshots/simplify_matches__already_flat.bend.snap @@ -2,50 +2,74 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/already_flat.bend --- +Rule1: Any (Rule1) = λa a +Rule2: Any (Rule2) = λa λb b +Rule3: Any (Rule3) = λa λb λc λd (a b c d) +Rule4: Any (Rule4) = λa match a { Foo/CtrA: λb b; Foo/CtrB c: c; } +Rule5: Any (Rule5) = λa match a { Bar/CtrA1 b: λc let d = c; (b d); Bar/CtrA2 e f: λg let h = g; (e f h); Bar/CtrA3 i: λj let k = j; (match k { Baz/CtrB0: λl (Bar/CtrA3 l); Baz/CtrB1 m: λn (Bar/CtrA3 n m); Baz/CtrB2 o: λp (Bar/CtrA3 p (Baz/CtrB2 o)); Baz/CtrB3 q: λr (r q); } i); } +Rule6: Any (Rule6) = λa a +Foo/CtrA: Foo (Foo/CtrA) = λa (a Foo/CtrA/tag) +Foo/CtrB: (Any -> Foo) (Foo/CtrB) = λa λb (b Foo/CtrB/tag a) +Bar/CtrA1: (Any -> Bar) (Bar/CtrA1) = λa λb (b Bar/CtrA1/tag a) +Bar/CtrA2: (Any -> (Any -> Bar)) (Bar/CtrA2) = λa λb λc (c Bar/CtrA2/tag a b) +Bar/CtrA3: (Any -> Bar) (Bar/CtrA3) = λa λb (b Bar/CtrA3/tag a) +Baz/CtrB0: Baz (Baz/CtrB0) = λa (a Baz/CtrB0/tag) +Baz/CtrB1: (Any -> Baz) (Baz/CtrB1) = λa λb (b Baz/CtrB1/tag a) +Baz/CtrB2: (Any -> Baz) (Baz/CtrB2) = λa λb (b Baz/CtrB2/tag a) +Baz/CtrB3: (Any -> Baz) (Baz/CtrB3) = λa λb (b Baz/CtrB3/tag a) +Foo/CtrA/tag: u24 (Foo/CtrA/tag) = 0 +Foo/CtrB/tag: u24 (Foo/CtrB/tag) = 1 +Bar/CtrA1/tag: u24 (Bar/CtrA1/tag) = 0 +Bar/CtrA2/tag: u24 (Bar/CtrA2/tag) = 1 +Bar/CtrA3/tag: u24 (Bar/CtrA3/tag) = 2 +Baz/CtrB0/tag: u24 (Baz/CtrB0/tag) = 0 +Baz/CtrB1/tag: u24 (Baz/CtrB1/tag) = 1 +Baz/CtrB2/tag: u24 (Baz/CtrB2/tag) = 2 +Baz/CtrB3/tag: u24 (Baz/CtrB3/tag) = 3 diff --git a/tests/snapshots/simplify_matches__bits_dec.bend.snap b/tests/snapshots/simplify_matches__bits_dec.bend.snap index d8d452d25..456c46e20 100644 --- a/tests/snapshots/simplify_matches__bits_dec.bend.snap +++ b/tests/snapshots/simplify_matches__bits_dec.bend.snap @@ -2,16 +2,23 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/bits_dec.bend --- +Data.Bits.dec: Any (Data.Bits.dec) = λa match a { Data/Bits/e: Data/Bits/e; Data/Bits/o b: match b { Data/Bits/e: Data/Bits/e; Data/Bits/o c: (Data/Bits/i (Data.Bits.dec c)); Data/Bits/i d: (Data/Bits/i (Data.Bits.dec d)); }; Data/Bits/i e: match e { Data/Bits/e: (Data/Bits/o Data/Bits/e); Data/Bits/o f: (Data/Bits/o f); Data/Bits/i g: (Data/Bits/o g); }; } +Data/Bits/e: Data/Bits (Data/Bits/e) = λa (a Data/Bits/e/tag) +Data/Bits/o: (Any -> Data/Bits) (Data/Bits/o) = λa λb (b Data/Bits/o/tag a) +Data/Bits/i: (Any -> Data/Bits) (Data/Bits/i) = λa λb (b Data/Bits/i/tag a) +Data/Bits/e/tag: u24 (Data/Bits/e/tag) = 0 +Data/Bits/o/tag: u24 (Data/Bits/o/tag) = 1 +Data/Bits/i/tag: u24 (Data/Bits/i/tag) = 2 diff --git a/tests/snapshots/simplify_matches__complex_with_case.bend.snap b/tests/snapshots/simplify_matches__complex_with_case.bend.snap index c01798f0c..437bf26fd 100644 --- a/tests/snapshots/simplify_matches__complex_with_case.bend.snap +++ b/tests/snapshots/simplify_matches__complex_with_case.bend.snap @@ -2,14 +2,20 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/complex_with_case.bend --- +map: Any (map) = λa λb (match b { Tree_/Node c d e f: λg (Tree_/Node (map g c) (map g d) (map g e) (map g f)); Tree_/Leaf h: λi (Tree_/Leaf (i h)); } a) +main: Any (main) = map +Tree_/Node: (Any -> (Any -> (Any -> (Any -> Tree_)))) (Tree_/Node) = λa λb λc λd λe (e Tree_/Node/tag a b c d) +Tree_/Leaf: (Any -> Tree_) (Tree_/Leaf) = λa λb (b Tree_/Leaf/tag a) +Tree_/Node/tag: u24 (Tree_/Node/tag) = 0 +Tree_/Leaf/tag: u24 (Tree_/Leaf/tag) = 1 diff --git a/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap b/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap index bba06a8f8..4de8eb697 100644 --- a/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap +++ b/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap @@ -2,10 +2,14 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/double_unwrap_box.bend --- +DoubleUnbox: Any (DoubleUnbox) = λa match a { Boxed/Box b: match b { Boxed/Box c: λd let e = d; let f = e; c; }; } +Main: Any (Main) = (DoubleUnbox (Boxed/Box (Boxed/Box 0)) 5) +Boxed/Box: (Any -> Boxed) (Boxed/Box) = λa λb (b Boxed/Box/tag a) +Boxed/Box/tag: u24 (Boxed/Box/tag) = 0 diff --git a/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap b/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap index 47244b401..0ddd57299 100644 --- a/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap +++ b/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap @@ -2,14 +2,20 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/double_unwrap_maybe.bend --- +DoubleUnwrap: Any (DoubleUnwrap) = λa match a { Maybe/Some b: match b { Maybe/Some c: λd let e = d; let f = e; c; Maybe/None: λg let h = g; let i = h; i; }; Maybe/None: λj let k = j; k; } +Main: Any (Main) = (DoubleUnwrap (Maybe/Some Maybe/None) 5) +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λb (b Maybe/Some/tag a) +Maybe/None: Maybe (Maybe/None) = λa (a Maybe/None/tag) +Maybe/Some/tag: u24 (Maybe/Some/tag) = 0 +Maybe/None/tag: u24 (Maybe/None/tag) = 1 diff --git a/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap b/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap index f5be98805..6721ec50c 100644 --- a/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap +++ b/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap @@ -2,14 +2,20 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/flatten_with_terminal.bend --- +Foo: Any (Foo) = λa switch a { 0: λb let c = b; match c { A_t/A d: match d { B_t/B: B_t/B; }; }; _ e: λf let g = f; *; } +main: Any (main) = (Foo 2 (A_t/A B_t/B)) +A_t/A: (Any -> A_t) (A_t/A) = λa λb (b A_t/A/tag a) +B_t/B: B_t (B_t/B) = λa (a B_t/B/tag) +A_t/A/tag: u24 (A_t/A/tag) = 0 +B_t/B/tag: u24 (B_t/B/tag) = 0 diff --git a/tests/snapshots/simplify_matches__irrefutable_case.bend.snap b/tests/snapshots/simplify_matches__irrefutable_case.bend.snap index 4691e6a79..589552ab4 100644 --- a/tests/snapshots/simplify_matches__irrefutable_case.bend.snap +++ b/tests/snapshots/simplify_matches__irrefutable_case.bend.snap @@ -1,17 +1,24 @@ --- source: tests/golden_tests.rs -input_file: tests/golden_tests/simplify_matches/redundant_cases.bend +input_file: tests/golden_tests/simplify_matches/irrefutable_case.bend --- +l: Any (l) = 1001 +v1: Any (v1) = l +v2: Any (v2) = l +v3: Any (v3) = 2002 +v4: Any (v4) = 3003 +v5: Any (v5) = (λa a 5005) +main: Any (main) = (v1, v2, v3, v4, v5) diff --git a/tests/snapshots/simplify_matches__linearize_match_all.bend.snap b/tests/snapshots/simplify_matches__linearize_match_all.bend.snap index 5235f9c7d..a9373b568 100644 --- a/tests/snapshots/simplify_matches__linearize_match_all.bend.snap +++ b/tests/snapshots/simplify_matches__linearize_match_all.bend.snap @@ -2,30 +2,44 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/linearize_match_all.bend --- +A: Any (A) = λa switch a { 0: λb λc (b c); _ d: λe λf (d e f); } +B: Any (B) = λa λb λc (switch c { 0: λd λe (d e); _ f: λg λh (g h f); } a b) +C: Any (C) = λa λb λc switch c { 0: (a b); _ d: (a b d); } +D: Any (D) = λa switch a { 0: λb λc c; _ d: λe λf (d f); } +E: Any (E) = λa match a { ConsList/Cons b c: λd let e = d; (match e { ConsList/Cons f g: λh λi (h i f g); ConsList/Nil: λj λk (ConsList/Cons j k ConsList/Nil); } b c); ConsList/Nil: λl let m = l; (ConsList/Nil m); } +A2: Any (A2) = λa match a { ConsList/Cons b c: λd λe (b c d e); ConsList/Nil: λf λg (f g); } +B2: Any (B2) = λa λb λc (match c { ConsList/Cons d e: λf λg (f g d e); ConsList/Nil: λh λi (h i); } a b) +C2: Any (C2) = λa λb λc match c { ConsList/Cons d e: (a b d e); ConsList/Nil: (a b); } +D2: Any (D2) = λa match a { ConsList/Cons b c: λd λe (b c d e); ConsList/Nil: λf λg (f g); } +main: Any (main) = * +ConsList/Cons: (Any -> (Any -> ConsList)) (ConsList/Cons) = λa λb λc (c ConsList/Cons/tag a b) +ConsList/Nil: ConsList (ConsList/Nil) = λa (a ConsList/Nil/tag) +ConsList/Cons/tag: u24 (ConsList/Cons/tag) = 0 +ConsList/Nil/tag: u24 (ConsList/Nil/tag) = 1 diff --git a/tests/snapshots/simplify_matches__match_str.bend.snap b/tests/snapshots/simplify_matches__match_str.bend.snap index e34400d53..f28e2aa7d 100644 --- a/tests/snapshots/simplify_matches__match_str.bend.snap +++ b/tests/snapshots/simplify_matches__match_str.bend.snap @@ -2,6 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/match_str.bend --- +is_as: Any (is_as) = λa match a { String/Nil: 1; String/Cons b c: (switch (- b 65) { 0: λd match d { String/Nil: 0; String/Cons e f: (switch (- e 115) { 0: λg match g { String/Nil: 2; String/Cons h i: 0; }; _ j: λk 0; } f); }; _ l: λm (switch (- l 31) { 0: λn match n { String/Nil: 0; String/Cons o p: (switch (- o 115) { 0: λq match q { String/Nil: 2; String/Cons r s: 0; }; _ t: λu 0; } p); }; _ v: λw 0; } m); } c); } +main: Any (main) = * diff --git a/tests/snapshots/simplify_matches__nested.bend.snap b/tests/snapshots/simplify_matches__nested.bend.snap index 05ada9d73..bc947825f 100644 --- a/tests/snapshots/simplify_matches__nested.bend.snap +++ b/tests/snapshots/simplify_matches__nested.bend.snap @@ -2,24 +2,35 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested.bend --- +Rule: Any (Rule) = λa match a { Foo/CtrA b c: (match c { Bar/CtrB1 d: λe (e d); Bar/CtrB2 f g: λh (match f { Baz/CtrC: λi λj (i j); } h g); } b); Foo/CtrB k: k; } +Foo/CtrA: (Any -> (Any -> Foo)) (Foo/CtrA) = λa λb λc (c Foo/CtrA/tag a b) +Foo/CtrB: (Any -> Foo) (Foo/CtrB) = λa λb (b Foo/CtrB/tag a) +Bar/CtrB1: (Any -> Bar) (Bar/CtrB1) = λa λb (b Bar/CtrB1/tag a) +Bar/CtrB2: (Any -> (Any -> Bar)) (Bar/CtrB2) = λa λb λc (c Bar/CtrB2/tag a b) +Baz/CtrC: Baz (Baz/CtrC) = λa (a Baz/CtrC/tag) +Foo/CtrA/tag: u24 (Foo/CtrA/tag) = 0 +Foo/CtrB/tag: u24 (Foo/CtrB/tag) = 1 +Bar/CtrB1/tag: u24 (Bar/CtrB1/tag) = 0 +Bar/CtrB2/tag: u24 (Bar/CtrB2/tag) = 1 +Baz/CtrC/tag: u24 (Baz/CtrC/tag) = 0 diff --git a/tests/snapshots/simplify_matches__nested2.bend.snap b/tests/snapshots/simplify_matches__nested2.bend.snap index d4d731234..5c02078fe 100644 --- a/tests/snapshots/simplify_matches__nested2.bend.snap +++ b/tests/snapshots/simplify_matches__nested2.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested2.bend --- +Foo: Any (Foo) = λa λb (match b { List/Nil: λc (c List/Nil); List/Cons d e: λf (match e { List/Nil: λg λh (g (List/Cons h List/Nil)); List/Cons i j: λk λl (k l i j); } f d); } a) +List/Nil: ∀t (List t) (List/Nil) = λa (a List/Nil/tag) +List/Cons: ∀t (u24 -> ((List t) -> (List t))) (List/Cons) = λa λb λc (c List/Cons/tag a b) +List/Nil/tag: u24 (List/Nil/tag) = 0 +List/Cons/tag: u24 (List/Cons/tag) = 1 diff --git a/tests/snapshots/simplify_matches__nested_0ary.bend.snap b/tests/snapshots/simplify_matches__nested_0ary.bend.snap index 2f188c932..a57909179 100644 --- a/tests/snapshots/simplify_matches__nested_0ary.bend.snap +++ b/tests/snapshots/simplify_matches__nested_0ary.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested_0ary.bend --- +Unpack: Any (Unpack) = λa λb (match b { list/Cons c d: λe (match d { list/Cons f g: λh λi (h (list/Cons i (list/Cons f g))); list/Nil: λj λk k; } e c); list/Nil: λl list/Nil; } a) +list/Cons: (Any -> (Any -> list)) (list/Cons) = λa λb λc (c list/Cons/tag a b) +list/Nil: list (list/Nil) = λa (a list/Nil/tag) +list/Cons/tag: u24 (list/Cons/tag) = 0 +list/Nil/tag: u24 (list/Nil/tag) = 1 diff --git a/tests/snapshots/simplify_matches__redundant_with_era.bend.snap b/tests/snapshots/simplify_matches__redundant_with_era.bend.snap index b7c6e710b..4d24e84af 100644 --- a/tests/snapshots/simplify_matches__redundant_with_era.bend.snap +++ b/tests/snapshots/simplify_matches__redundant_with_era.bend.snap @@ -2,6 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/redundant_with_era.bend --- +Fn2: Any (Fn2) = λa switch a { 0: λb let c = b; let (d, e) = c; let (f, g) = e; g; _ h: λi let j = i; let (k, l) = j; let (m, n) = l; n; } +main: Any (main) = * From f7006428ef096f48cc5e8868d99d319d9a2c7545 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Tue, 6 Aug 2024 11:12:54 +0200 Subject: [PATCH 04/29] Removed unused atan2 and log adt nodes --- src/fun/display.rs | 2 -- src/fun/mod.rs | 4 ---- src/fun/net_to_term.rs | 4 ++-- src/fun/term_to_net.rs | 6 ++---- src/imp/parser.rs | 2 -- 5 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/fun/display.rs b/src/fun/display.rs index f531f070b..aa11b8489 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -274,8 +274,6 @@ impl fmt::Display for Op { Op::POW => write!(f, "**"), Op::SHR => write!(f, ">>"), Op::SHL => write!(f, "<<"), - Op::LOG => todo!(), - Op::ATN => todo!(), Op::LE => write!(f, "<="), Op::GE => write!(f, ">="), } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 06bc6d267..5d9817879 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -259,10 +259,6 @@ pub enum Op { XOR, SHL, SHR, - /// atan(a, b) - ATN, - /// log_a(b) - LOG, // a^b POW, /// Less than or equal diff --git a/src/fun/net_to_term.rs b/src/fun/net_to_term.rs index 764b4d333..5ac6aebd9 100644 --- a/src/fun/net_to_term.rs +++ b/src/fun/net_to_term.rs @@ -302,14 +302,14 @@ impl Reader<'_> { hvm::hvm::OP_GT => Op::GT, hvm::hvm::OP_AND => { if typ == hvm::hvm::TY_F24 { - Op::ATN + todo!("Implement readback of atan2") } else { Op::AND } } hvm::hvm::OP_OR => { if typ == hvm::hvm::TY_F24 { - Op::LOG + todo!("Implement readback of log") } else { Op::OR } diff --git a/src/fun/term_to_net.rs b/src/fun/term_to_net.rs index e26699da5..7f80ef13f 100644 --- a/src/fun/term_to_net.rs +++ b/src/fun/term_to_net.rs @@ -191,8 +191,8 @@ impl<'t, 'l> EncodeTermState<'t, 'l> { } // Partially apply with snd, flip (fst, Term::Num { val }) => { - if [Op::POW, Op::ATN, Op::LOG].contains(opr) { - // POW, ATN and LOG share tags with AND, OR and XOR, so don't flip or results will be wrong + if let Op::POW = opr { + // POW shares tags with AND, so don't flip or results will be wrong let opr_val = hvm::ast::Numb(hvm::hvm::Numb::new_sym(opr.to_native_tag()).0); let oper = Place::Tree(LoanedMut::new(Tree::Num { val: opr_val })); let node1 = self.new_opr(); @@ -471,8 +471,6 @@ impl Op { Op::SHL => hvm::hvm::OP_SHL, Op::SHR => hvm::hvm::OP_SHR, - Op::ATN => hvm::hvm::OP_AND, - Op::LOG => hvm::hvm::OP_OR, Op::POW => hvm::hvm::OP_XOR, Op::LE => hvm::hvm::OP_GT, diff --git a/src/imp/parser.rs b/src/imp/parser.rs index a0aeacdd1..1df8bf179 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -1383,8 +1383,6 @@ impl Op { Op::DIV => 7, Op::REM => 7, Op::POW => 8, - Op::ATN => todo!(), - Op::LOG => todo!(), } } From 8a0613456958a0a2b900f9a718138f7005875b26 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 8 Aug 2024 20:12:59 +0200 Subject: [PATCH 05/29] Add type checking by compiling to kindc --- cspell.json | 1 + src/fun/builtins.bend | 113 +- src/fun/mod.rs | 1 + src/fun/parser.rs | 7 +- src/fun/transform/expand_main.rs | 7 +- src/fun/transform/mod.rs | 1 + src/fun/transform/resolve_type_ctrs.rs | 2 +- src/fun/transform/type_check.rs | 1288 +++++++++++++++++ src/imp/parser.rs | 13 +- src/lib.rs | 24 +- src/main.rs | 4 + tests/snapshots/cli__compile_all.bend.snap | 4 +- .../cli__compile_pre_reduce.bend.snap | 2 +- .../cli__compile_wrong_opt.bend.snap | 2 +- 14 files changed, 1413 insertions(+), 56 deletions(-) create mode 100644 src/fun/transform/type_check.rs diff --git a/cspell.json b/cspell.json index e60e050e8..e19d45aba 100644 --- a/cspell.json +++ b/cspell.json @@ -49,6 +49,7 @@ "ints", "itertools", "ITRS", + "kindc", "kwarg", "kwargs", "lcons", diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index eba0622be..01ac10060 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -1,10 +1,10 @@ type String: Nil - Cons { head: u24, ~tail: String } + Cons { head: u24, ~tail: String } type List(T): Nil - Cons { head: T, ~tail: List(T) } + Cons { head: T, ~tail: List(T) } # Returns the length of a list and the list itself. def List/length(xs: List(T)) -> (u24, List(T)): @@ -33,15 +33,23 @@ List/concat (List/Cons x xs) ys = (List/Cons x (List/concat xs ys)) List/concat (List/Nil) ys = ys # Splits a list into two lists at the first occurrence of a value. -List/split_once (xs: (List T)) (val: T) -> (Result(List(T), List(T))) -List/split_once xs val = (List/split_once.go xs val @x x) - List/split_once.go List/Nil val acc = (Result/Err (acc List/Nil)) - List/split_once.go (List/Cons x xs) val acc = - if (== val x) { - (Result/Ok ((acc List/Nil), xs)) - } else { - (List/split_once.go xs val @y (acc (List/Cons x y))) - } +# Returns the original list if the value is not found +def List/split_once(xs: List(T), val: T) -> (Result((List(T), List(T)), List(T))): + return List/split_once.go(xs, val, DiffList/new) + +def List/split_once.go( + xs: List(T), + val: T, + acc: List(T) -> List(T) +) -> Result((List(T), List(T)), List(T)): + match xs: + case List/Nil: + return Result/Err(DiffList/to_list(acc)) + case List/Cons: + if val == xs.head: + return Result/Ok((DiffList/to_list(acc), xs.tail)) + else: + return List/split_once.go(xs.tail, val, DiffList/append(acc, xs.head)) # Filters a list based on a predicate function. List/filter (xs: (List T)) (pred: T -> Bool) : (List T) @@ -54,7 +62,7 @@ List/filter (List/Cons x xs) pred = } # Checks if two strings are equal. -String/equals (s1: String) (s2: String) -> u24 +String/equals (s1: String) (s2: String) : u24 String/equals (String/Nil) (String/Nil) = 1 String/equals (String/Cons x xs) (String/Cons y ys) = if (== x y) { @@ -143,7 +151,7 @@ type Map T = (Node (value: T) ~(left: (Map T)) ~(right: (Map T))) | (Leaf) Map/empty : (Map T) = Map/Leaf -Map/get (map: (Map T)) (key: u24) : ((Map T), T) = +Map/get (map: (Map T)) (key: u24) : (T, (Map T)) = match map { Map/Leaf: (*, map) Map/Node: @@ -198,7 +206,7 @@ Map/map (Map/Node value left right) key f = type IO(T): Done { magic: (u24, u24), expr: T } - Call { magic: (u24, u24), func: String, argm: Any, cont: T -> IO(T) } + Call { magic: (u24, u24), func: String, argm: Any, cont: Any -> IO(T) } type IOError(T): Type @@ -211,13 +219,13 @@ def IO/MAGIC() -> (u24, u24): def IO/wrap(x: T) -> IO(T): return IO/Done(IO/MAGIC, x) -def IO/bind(a: IO(A), b: A -> IO(B)) -> IO(B): +def IO/bind(a: IO(A), b: ((A -> IO(B)) -> (A -> IO(B))) -> (A -> IO(B))) -> IO(B): match a: case IO/Done: b = undefer(b) return b(a.expr) case IO/Call: - return IO/Call(IO/MAGIC, a.func, a.argm, lambda x: IO/bind(a.cont(x), b)) + return IO/Call(a.magic, a.func, a.argm, lambda x: IO/bind(a.cont(x), b)) def IO/call(func: String, argm: Any) -> IO(Result(Any, Any)): return IO/Call(IO/MAGIC, func, argm, lambda x: IO/Done(IO/MAGIC, x)) @@ -242,8 +250,8 @@ def IO/nanosleep(hi_lo: (u24, u24)) -> IO(None): # Sleeps for a given amount of seconds as a float. def IO/sleep(seconds: f24) -> IO(None): nanos = seconds * 1_000_000_000.0 - lo = to_u24(nanos % 0x1_000_000.0) - hi = to_u24(nanos / 0x1_000_000.0) + lo = f24_to_u24(nanos % 0x1_000_000.0) + hi = f24_to_u24(nanos / 0x1_000_000.0) with IO: res <- IO/nanosleep((hi, lo)) return wrap(Result/unwrap(res)) @@ -319,7 +327,7 @@ def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u24 case Result/Ok: (line, rest) = res.val (length, *) = List/length(rest) - * <- IO/FS/seek(fd, to_i24(length) * -1, IO/FS/SEEK_CUR) + * <- IO/FS/seek(fd, u24_to_i24(length) * -1, IO/FS/SEEK_CUR) chunks = List/Cons(line, chunks) bytes = List/flatten(chunks) return wrap(bytes) @@ -381,45 +389,74 @@ def IO/input.go(acc: List(u24) -> List(u24)) -> IO(String): ### Dynamically linked libraries +# Returns an unique id to the library object encoded as a u24 # 'path' is the path to the library file. # 'lazy' is a boolean encoded as a u24 that determines if all functions are loaded lazily or upfront. -# Returns an unique id to the library object encoded as a u24 def IO/DyLib/open(path: String, lazy: u24) -> IO(u24): return IO/call("DL_OPEN", (path, lazy)) # Calls a function of a previously opened library. +# The returned value is determined by the called function. # 'dl' is the id of the library object. # 'fn' is the name of the function in the library. # 'args' are the arguments to the function. The expected values depend on the called function. -# The returned value is determined by the called function. def IO/DyLib/call(dl: u24, fn: String, args: Any) -> IO(Any): return IO/call("DL_CALL", (dl, (fn, args))) -# IO/DyLib/close(dl: u24) -> None # Closes a previously open library. -# 'dl' is the id of the library object. # Returns nothing. -def IO/DyLib/close(dl): +# 'dl' is the id of the library object. +def IO/DyLib/close(dl: u24) -> IO(None): return IO/Call(IO/MAGIC, "DL_CLOSE", (dl), IO/wrap) # Lazy thunks -# defer(val: T) -> ((T -> T) -> T) # We can defer the evaluation of a function by wrapping it in a thunk # Ex: @x (x @arg1 @arg2 @arg3 (f arg1 arg2 arg3) arg1 arg2 arg3) -# This is only evaluated when we call it with 'undefer' (undefer my_thunk) +# This is only evaluated when we call it with ' ' (undefer my_thunk) # We can build a defered call directly or by by using defer and defer_arg # The example above can be written as: # (defer_arg (defer_arg (defer_arg (defer @arg1 @arg2 @arg3 (f arg1 arg2 arg3)) arg1) arg2) arg3) -defer val = - @x (x val) +def defer(val: T) -> (T -> T) -> T: + return lambda x: x(val) + +def defer_arg(defered: ((A -> B) -> (A -> B)) -> (A -> B), arg: B) -> ((A -> A) -> A): + return lambda x: defered(x, arg) + +def undefer(defered: ((A -> A) -> A)) -> A: + return defered(lambda x: x) + + +# Native number casts + +# f24_to_u24(x: f24) -> u24 +# Casts a f24 number to a u24. +hvm f24_to_u24 -> (f24 -> u24): + ($([u24] ret) ret) + +# i24_to_u24(x: i24) -> u24 +# Casts an i24 number to a u24. +hvm i24_to_u24 -> (i24 -> u24): + ($([u24] ret) ret) + +# u24_to_i24(x: u24) -> i24 +# Casts a u24 number to an i24. +hvm u24_to_i24 -> (u24 -> i24): + ($([i24] ret) ret) + +# f24_to_i24(x: f24) -> i24 +# Casts a f24 number to an i24. +hvm f24_to_i24 -> (f24 -> i24): + ($([i24] ret) ret) -# defer_arg(defered: A -> B -> C, arg: B) -> (A -> C) -defer_arg defered arg = - @x (defered x arg) +# u24_to_f24(x: u24) -> f24 +# Casts a u24 number to a f24. +hvm u24_to_f24 -> (u24 -> f24): + ($([f24] ret) ret) -# undefer(defered: (A -> A) -> B) -> B -undefer defered = - (defered @x x) +# i24_to_f24(x: i24) -> f24 +# Casts an i24 number to a f24. +hvm i24_to_f24 -> (i24 -> f24): + ($([f24] ret) ret) # String Encoding and Decoding @@ -615,7 +652,7 @@ Math/sqrt (n: f24) : f24 = # Round float up to the nearest integer. def Math/ceil(n: f24) -> f24: - i_n = to_f24(to_i24(n)) + i_n = i24_to_f24(f24_to_i24(n)) if n <= i_n: return i_n else: @@ -623,7 +660,7 @@ def Math/ceil(n: f24) -> f24: # Round float down to the nearest integer. def Math/floor(n: f24) -> f24: - i_n = to_f24(to_i24(n)) + i_n = i24_to_f24(f24_to_i24(n)) if n < i_n: return i_n - 1.0 else: @@ -631,8 +668,8 @@ def Math/floor(n: f24) -> f24: # Round float to the nearest integer. def Math/round(n: f24) -> f24: - i_n = to_f24(to_i24(n)) - if n - i_n < 0.5: + i_n = i24_to_f24(f24_to_i24(n)) + if (n - i_n) < 0.5: return Math/floor(n) else: return Math/ceil(n) diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 5d9817879..d861c5b9d 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -315,6 +315,7 @@ pub struct AdtCtr { pub struct CtrField { pub nam: Name, pub rec: bool, + pub typ: Type, } #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 443f40afa..7ff2ae20d 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -234,7 +234,7 @@ impl<'a> FunParser<'a> { let ctr_name = Name::new(format!("{type_name}/{ctr_name}")); let fields = self.list_like(|p| p.parse_type_ctr_field(), "", ")", "", false, 0)?; - let (fields, field_types): (Vec<_>, Vec<_>) = fields.into_iter().unzip(); + let field_types = fields.iter().map(|f| f.typ.clone()).collect::>(); let end_idx = *self.index(); self.check_repeated_ctr_fields(&fields, &ctr_name, ini_idx..end_idx)?; @@ -251,7 +251,7 @@ impl<'a> FunParser<'a> { } } - fn parse_type_ctr_field(&mut self) -> ParseResult<(CtrField, Type)> { + fn parse_type_ctr_field(&mut self) -> ParseResult { let rec = self.try_consume("~"); let nam; @@ -268,7 +268,7 @@ impl<'a> FunParser<'a> { nam = self.parse_var_name()?; typ = Type::Any; } - Ok((CtrField { nam, rec }, typ)) + Ok(CtrField { nam, typ, rec }) } fn parse_fun_def(&mut self) -> ParseResult { @@ -440,6 +440,7 @@ impl<'a> FunParser<'a> { } fn starts_with_rule(&mut self, expected_name: &Name) -> bool { + self.skip_trivia(); let ini_idx = *self.index(); let res = self.parse_rule_lhs(); self.index = ini_idx; diff --git a/src/fun/transform/expand_main.rs b/src/fun/transform/expand_main.rs index 4586c26e7..70bc09ec1 100644 --- a/src/fun/transform/expand_main.rs +++ b/src/fun/transform/expand_main.rs @@ -40,13 +40,16 @@ impl Term { Term::Ref { nam } => { if seen.contains(nam) { // Don't expand recursive references - } else { + } else if let Some(def) = book.defs.get(nam) { + // Regular function, expand seen.push(nam.clone()); - let mut body = book.defs.get(nam).unwrap().rule().body.clone(); + let mut body = def.rule().body.clone(); body.rename_unscoped(globals_count, &mut HashMap::new()); *self = body; self.expand_ref_return(book, seen, globals_count); seen.pop().unwrap(); + } else { + // Not a regular function, don't expand } } Term::Fan { els, .. } | Term::List { els } => { diff --git a/src/fun/transform/mod.rs b/src/fun/transform/mod.rs index d2c4968a3..612590293 100644 --- a/src/fun/transform/mod.rs +++ b/src/fun/transform/mod.rs @@ -21,4 +21,5 @@ pub mod resolve_refs; pub mod resolve_type_ctrs; pub mod resugar_list; pub mod resugar_string; +pub mod type_check; pub mod unique_names; diff --git a/src/fun/transform/resolve_type_ctrs.rs b/src/fun/transform/resolve_type_ctrs.rs index 4d990029b..5cf6d25fd 100644 --- a/src/fun/transform/resolve_type_ctrs.rs +++ b/src/fun/transform/resolve_type_ctrs.rs @@ -7,7 +7,7 @@ use crate::{ }; impl Ctx<'_> { - /// Resolves type constructors in the book and adds for alls for the free type vars. + /// Resolves type constructors in the book and adds for-alls for the free type vars. pub fn resolve_type_ctrs(&mut self) -> Result<(), Diagnostics> { for def in self.book.defs.values_mut() { let mut free_vars = Default::default(); diff --git a/src/fun/transform/type_check.rs b/src/fun/transform/type_check.rs new file mode 100644 index 000000000..7872b6a18 --- /dev/null +++ b/src/fun/transform/type_check.rs @@ -0,0 +1,1288 @@ +use crate::{ + diagnostics::Diagnostics, + fun::{ + Adt, AdtCtr, Book, CtrField, Ctx, Definition, FanKind, Name, Num, Op, Pattern, Source, Tag, Term, Type, + }, + maybe_grow, +}; +use core::fmt; +use std::{collections::BTreeMap, fmt::Write, process::Output}; +use TSPL::{new_parser, Parser}; + +impl Ctx<'_> { + /// Type check by compiling to kind-core + pub fn type_check(&mut self) -> Result<(), Diagnostics> { + // Compile to kind-core + let mut counter = 1; // Reserve 0 for unknown sources + let mut kind_book = KindCBook::new(); + + for def in self.book.defs.values() { + if let Some(adt_nam) = self.book.ctrs.get(&def.name) { + // Constructors get a special compilation using self types + let adt = self.book.adts.get(adt_nam).unwrap(); + ctr_to_kindc(&def.name, adt, &mut counter, &mut kind_book); + } else if !def.check { + // Unchecked functions probably contain unsupported features like unscoped variables. + // We can't check their real values, so just use an unchecked dummy. + unchecked_def_to_kindc(&def.name, &def.typ, &mut counter, &mut kind_book); + } else { + // Normal functions + def_to_kindc(def, &mut counter, self.book, &mut kind_book); + } + } + + // HVM native defs must be unchecked and compiled to a dummy value + for def in self.book.hvm_defs.values() { + unchecked_def_to_kindc(&def.name, &def.typ, &mut counter, &mut kind_book); + } + + for adt in self.book.adts.values() { + adt_to_kindc(adt, &mut counter, &mut kind_book); + } + + // Build the kindc program + let mut out = String::new(); + for (_, (nam, term)) in kind_book.iter() { + writeln!(out, "{nam} = {term};").unwrap(); + } + + // Write the output to a temporary file + if let Err(e) = std::fs::write(".out.kindc", out) { + self.info.add_book_error(format!("Error writing type checking output file: {e}")); + return self.info.fatal(()); + } + + // Call kind-core on the file + let output = std::process::Command::new("kindc") + .arg("check-all") + .arg(".out.kindc") + .output() + .map_err(|e| format!("While running the type checker: {e}"))?; + + // Remove temporary file + if let Err(e) = std::fs::remove_file(".out.kindc") { + self.info.add_book_error(format!("Error removing type checking output file: {e}")); + } + + self.read_kindc_output(output, &kind_book)?; + + self.info.fatal(()) + } + + /// Create adts and constructors for native values (numbers, tuples, etc.) + pub fn make_native_defs(&mut self) { + // Tuple adts + // TODO: Make this more flexible, maybe generating only exactly the used tuple types. + for i in 2..16 { + let ctr_nam = Name::new(format!("kindc__tup{}/new", i)); + let vars = (0..i).map(|i| Name::new(format!("t{}", i))).collect::>(); + let ctr_type = (0..i).rfold(Type::Tup(vars.iter().cloned().map(Type::Var).collect()), |typ, i| { + Type::All(Name::new(format!("t{}", i)), Box::new(typ)) + }); + let fields = (0..i) + .map(|i| CtrField { + nam: Name::new(format!("x{}", i)), + rec: false, + typ: Type::Var(Name::new(format!("t{}", i))), + }) + .collect(); + let ctr = AdtCtr { name: ctr_nam.clone(), typ: ctr_type, fields }; + + let adt_name = Name::new(format!("kindc__tup{}", i)); + let adt = Adt { + name: adt_name.clone(), + vars, + ctrs: [(ctr_nam.clone(), ctr)].into(), + source: Source::Generated, + }; + self.book.ctrs.insert(ctr_nam.clone(), adt_name.clone()); + self.book.adts.insert(adt_name.clone(), adt.clone()); + } + // None adt + let adt_name = Name::new("kindc__none"); + let ctr_nam = Name::new("kindc__none/new"); + self.book.ctrs.insert(ctr_nam.clone(), adt_name.clone()); + self.book.adts.insert( + adt_name.clone(), + Adt { + name: adt_name.clone(), + vars: vec![], + ctrs: [( + ctr_nam.clone(), + AdtCtr { name: ctr_nam.clone(), typ: Type::Ctr(adt_name.clone(), vec![]), fields: vec![] }, + )] + .into(), + source: Source::Generated, + }, + ); + } + + /// Read, parse and report errors from the kindc output. + fn read_kindc_output(&mut self, output: Output, kind_book: &KindCBook) -> Result<(), Diagnostics> { + if !output.status.success() { + let out = String::from_utf8_lossy(&output.stdout); + let err = String::from_utf8_lossy(&output.stderr); + self.info.add_book_error(format!("Unexpected error from the type checker:\n{err}\n{out}")); + return self.info.fatal(()); + } + + // Parse the output + if !output.stderr.is_empty() { + let err = String::from_utf8_lossy(&output.stderr); + eprintln!("unexpected error from the type checker:\n{err}"); + } else if !output.stdout.is_empty() { + let err = String::from_utf8_lossy(&output.stdout); + eprintln!("raw type error:\n{err}"); + let mut p = KindCParser::new(&err); + match p.parse_result() { + Ok(errs) => { + for err in errs { + match err { + KindCErr::ExpectedFound { expected, found, val, src } => { + let msg = format!( + "Type checking error:\nExpected: {}\nFound: {}\nValue: {}\nSource: {}", + expected.to_bend_type(), + found.to_bend_type(), + val, + name_from_src(src, kind_book), + ); + self.info.add_book_error(msg); + } + KindCErr::ExpectedFun { found, val, src } => { + // TODO: Improve this message, it should mention that it's applying to a non-function. + // Either too many args or applying to a non-function. + let var_name = format!( + "Type checking error:\nExpected: function\nFound: {}\nValue: {}\nSource: {}", + found.to_bend_type(), + val, + name_from_src(src, kind_book), + ); + let msg = var_name; + self.info.add_book_error(msg); + } + KindCErr::ExpectedSelf { found, val, src } => { + let msg = format!( + "Type checking error:\nExpected: a self-type\nFound: {}\nValue: {}\nSource: {}", + found.to_bend_type(), + val, + name_from_src(src, kind_book), + ); + self.info.add_book_error(msg); + } + KindCErr::UndefinedRef { nam, src } => { + let msg = format!( + "Type checking error:\nUndefined reference.\nValue: {}\nSource: {}", + nam, + name_from_src(src, kind_book), + ); + self.info.add_book_error(msg); + } + KindCErr::CantInferLam { val, src } => { + let msg = format!( + "Type checking error:\nCan't infer type of lambda.\nValue: {}\nSource: {}", + val, + name_from_src(src, kind_book), + ); + self.info.add_book_error(msg); + } + KindCErr::CantInferHol { val, src } => { + let msg = format!( + "Type checking error:\nCan't infer type of hole.\nValue: {}\nSource: {}", + val, + name_from_src(src, kind_book), + ); + self.info.add_book_error(msg); + } + KindCErr::CantInferMet { val, src } => { + let msg = format!( + "Type checking error:\nCan't infer type of meta-variable.\nValue: {}\nSource: {}", + val, + name_from_src(src, kind_book), + ); + self.info.add_book_error(msg); + } + KindCErr::CantInferVar { val, src } => { + let msg = format!( + "Type checking error:\nCan't infer type of variable.\nValue: {}\nSource: {}", + val, + name_from_src(src, kind_book), + ); + self.info.add_book_error(msg); + } + } + } + } + Err(p_err) => { + eprintln!("Parse error: {p_err}"); + let msg = format!("Unexpected output from the type checker:\n{err}"); + self.info.add_book_error(msg); + } + } + } + + self.info.fatal(()) + } +} + +type KindCBook = BTreeMap; + +pub enum KindCTerm { + All { + nam: String, + typ: Box, + bod: Box, + }, + Lam { + nam: String, + bod: Box, + }, + App { + fun: Box, + arg: Box, + }, + Ann { + chk: bool, + bod: Box, + typ: Box, + }, + Slf { + nam: String, + typ: Box, + bod: Box, + }, + Ins { + bod: Box, + }, + Ref { + nam: String, + }, + Let { + nam: String, + bod: Box, + nxt: Box, + }, + Use { + nam: String, + bod: Box, + nxt: Box, + }, + Set, + Any, + U48, + I48, + F48, + UNum { + val: u32, + }, + INum { + val: i32, + }, + FNum { + val: f32, + }, + Op2 { + op: Oper, + fst: Box, + snd: Box, + }, + Swi { + bind: String, + arg: Box, + zero: Box, + succ: Box, + motive: Box, + }, + Hol { + nam: String, + ctx: Vec, + }, + Met { + uid: u64, + }, + Src { + src: u64, + bod: Box, + }, + Txt { + val: String, + }, + Nat { + val: u32, + }, +} + +use KindCTerm as KT; + +pub enum Oper { + ADD, + SUB, + MUL, + DIV, + MOD, + EQ, + NE, + LT, + GT, + LTE, + GTE, + AND, + OR, + XOR, + LSH, + RSH, +} + +fn def_to_kindc(def: &Definition, counter: &mut u64, bend_book: &Book, kindc_book: &mut KindCBook) { + let src = fresh_num(counter); + let bod = def.rule().body.to_kindc(counter, bend_book, src); + let type_vars = def.typ.leading_type_vars(); + let bod = bod.rfold_lams(type_vars); + let typ = def.typ.to_kindc(counter, src); + let chk = def.check; + let bod = KT::Ann { chk, bod: Box::new(bod), typ: Box::new(typ) }; + kindc_book.insert(src, (def.name.to_string(), bod)); +} + +fn ctr_to_kindc(ctr_nam: &Name, adt: &Adt, counter: &mut u64, book: &mut KindCBook) { + // λ for each type variable + // λ for each field + // self instantiation + // λ for predicate + // λ for each constructor + // (ctr fields) : ∀(type vars: _) ∀(field: field type) (Type vars) + // TODO: Use the actual encoding and not always scott encoding + let src = fresh_num(counter); + let ctr = adt.ctrs.get(ctr_nam).unwrap(); + + let term = KT::Ref { nam: ctr_nam.to_string() }; + let term = term.fold_vars(ctr.fields.iter().map(|field| field.nam.to_string())); + let term = term.rfold_lams(adt.ctrs.keys().map(|ctr| ctr.to_string())); + let term = term.rfold_lams(["P".to_string()]); + let term = KT::Ins { bod: Box::new(term) }; + let term = term.rfold_lams(ctr.fields.iter().map(|field| field.nam.to_string())); + let term = term.rfold_lams(adt.vars.iter().map(|var| var.to_string())); + + let typ = KT::Ref { nam: adt.name.to_string() }; + let typ = typ.fold_vars(adt.vars.iter().map(|var| var.to_string())); + let typ = ctr.fields.iter().rfold(typ, |typ, field| KT::All { + nam: field.nam.to_string(), + typ: Box::new(field.typ.to_kindc(counter, src)), + bod: Box::new(typ), + }); + let typ = adt.vars.iter().rfold(typ, |typ, var| KT::All { + nam: var.to_string(), + typ: Box::new(KT::Set), + bod: Box::new(typ), + }); + let term = KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) }; + book.insert(src, (ctr_nam.to_string(), term)); +} + +fn adt_to_kindc(adt: &Adt, counter: &mut u64, book: &mut KindCBook) { + // λ type vars + // $(self: (Type vars)) + // ∀(P: ∀(x: (Type vars)) *) + // ∀(constructor: ∀(fields: field types) (P (constructor vars fields))) + // (P self): ∀(type vars: _) * + let src = fresh_num(counter); + let term = KT::Ref { nam: "P".to_string() }; + let term = term.fold_vars(["self".to_string()]); + let term = adt.ctrs.iter().rfold(term, |term, (ctr_nam, ctr)| { + let typ = KT::Ref { nam: ctr_nam.to_string() }; + let typ = typ.fold_vars(adt.vars.iter().map(|var| var.to_string())); + let typ = typ.fold_vars(ctr.fields.iter().map(|field| field.nam.to_string())); + let typ = KT::App { fun: Box::new(KT::Ref { nam: "P".to_string() }), arg: Box::new(typ) }; + let typ = ctr.fields.iter().rfold(typ, |typ, field| KT::All { + nam: field.nam.to_string(), + typ: Box::new(field.typ.to_kindc(counter, src)), + bod: Box::new(typ), + }); + KT::All { nam: ctr_nam.to_string(), typ: Box::new(typ), bod: Box::new(term) } + }); + let term = KT::All { + nam: "P".to_string(), + typ: Box::new(KT::All { + nam: "x".to_string(), + typ: Box::new( + KT::Ref { nam: adt.name.to_string() }.fold_vars(adt.vars.iter().map(|var| var.to_string())), + ), + bod: Box::new(KT::Set), + }), + bod: Box::new(term), + }; + let term = KT::Slf { + nam: "self".to_string(), + typ: Box::new( + KT::Ref { nam: adt.name.to_string() }.fold_vars(adt.vars.iter().map(|var| var.to_string())), + ), + bod: Box::new(term), + }; + let term = term.rfold_lams(adt.vars.iter().map(|var| var.to_string())); + + let typ = adt.vars.iter().rfold(KT::Set, |typ, var| KT::All { + nam: var.to_string(), + typ: Box::new(KT::Set), + bod: Box::new(typ), + }); + + let term = KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) }; + book.insert(src, (adt.name.to_string(), term)); +} + +fn unchecked_def_to_kindc(def_name: &Name, typ: &Type, counter: &mut u64, book: &mut KindCBook) { + let src = fresh_num(counter); + let term = KT::Met { uid: fresh_num(counter) }; + let typ = typ.to_kindc(counter, src); + let term = KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) }; + book.insert(src, (def_name.to_string(), term)); +} + +impl Term { + pub fn to_kindc(&self, counter: &mut u64, book: &Book, src: u64) -> KT { + maybe_grow(|| { + let term = match self { + Term::Lam { tag, pat, bod } => { + if tag != &Tag::Static { + todo!(); + } + match pat.as_ref() { + Pattern::Var(nam) => { + let term = KT::Lam { nam: bind_to_kind(nam), bod: Box::new(bod.to_kindc(counter, book, src)) }; + // TODO: We don't need to annotate every single lambda, only the top ones that don't already have one. + let typ = KT::All { + nam: "arr__".to_string(), + typ: Box::new(KT::Met { uid: fresh_num(counter) }), + bod: Box::new(KT::Met { uid: fresh_num(counter) }), + }; + KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) } + } + _ => todo!(), + } + } + Term::Var { nam } => KT::Ref { nam: nam.to_string() }, + Term::Let { pat, val, nxt } => match pat.as_ref() { + Pattern::Var(nam) => KT::Let { + nam: bind_to_kind(nam), + bod: Box::new(val.to_kindc(counter, book, src)), + nxt: Box::new(nxt.to_kindc(counter, book, src)), + }, + Pattern::Fan(FanKind::Tup, Tag::Static, els) => { + assert!(els.len() < 16); + // First flatten the let to have a single pattern. + let mut nxt = nxt.as_ref().clone(); + let mut binds = vec![]; + for (i, el) in els.iter().enumerate().rev() { + if let Pattern::Var(nam) = el { + binds.push(nam.clone()); + } else { + let new_var = Name::new(format!("let_tup__{i}")); + nxt = Term::Let { + pat: Box::new(el.clone()), + val: Box::new(Term::Var { nam: new_var.clone() }), + nxt: Box::new(nxt), + }; + binds.push(Some(new_var)); + } + } + binds.reverse(); + + // Then convert native tuple let terms into matches on adt tuples. + let term = Term::Mat { + bnd: None, + arg: val.clone(), + with_bnd: vec![], + with_arg: vec![], + arms: vec![(Some(Name::new(format!("kindc__tup{}/new", els.len()))), binds, nxt)], + }; + term.to_kindc(counter, book, src) + } + Pattern::Chn(_) => unreachable!(), + _ => todo!(), + }, + Term::Use { nam, val, nxt } => KT::Use { + nam: bind_to_kind(nam), + bod: Box::new(val.to_kindc(counter, book, src)), + nxt: Box::new(nxt.to_kindc(counter, book, src)), + }, + Term::App { tag, fun, arg } => { + if tag != &Tag::Static { + todo!(); + } + KT::App { + fun: Box::new(fun.to_kindc(counter, book, src)), + arg: Box::new(arg.to_kindc(counter, book, src)), + } + } + Term::Num { val } => match val { + Num::U24(val) => KT::UNum { val: *val }, + Num::I24(val) => KT::INum { val: *val }, + Num::F24(val) => KT::FNum { val: *val }, + }, + Term::Oper { opr, fst, snd } => KT::Op2 { + op: opr.to_kindc(), + fst: Box::new(fst.to_kindc(counter, book, src)), + snd: Box::new(snd.to_kindc(counter, book, src)), + }, + Term::Mat { bnd: _, arg, with_bnd: _, with_arg: _, arms } => { + // Note: 'with' arguments should be gone by now + // Match becomes: + // λ type vars + // λ P + // λ case__ctrs + // λ __matched + // (~__matched P case__ctrs) : + // ∀(type vars: _) + // ∀(P: ∀(x: (Type vars)) *) + // ∀(case__ctrs: ∀(fields: field types) (P (ctr vars fields))) + // ∀(__motive: (Type vars)) + // (P __motive) + // Then apply the matched value: + // let arg__ = arg; + // (__match _vars _P arms arg__) + let ctr_nam = arms[0].0.as_ref().unwrap(); + let adt_nam = book.ctrs.get(ctr_nam).unwrap(); + let adt = book.adts.get(adt_nam).unwrap(); + let on_ctr_nams = adt.ctrs.keys().map(|ctr_nam| format!("case__{ctr_nam}")); + let type_vars = adt.vars.iter().map(|var| var.to_string()); + + // Match function + let term = KT::Ref { nam: "matched__".to_string() }; + let term = KT::Ins { bod: Box::new(term) }; + let term = term.fold_vars(["P".to_string()]); + let term = term.fold_vars(on_ctr_nams.clone()); + let term = term.rfold_lams(["matched__".to_string()]); + let term = term.rfold_lams(on_ctr_nams); + let term = term.rfold_lams(["P".to_string()]); + let term = term.rfold_lams(type_vars.clone()); + + // Type of the match function + let typ = KT::Ref { nam: "P".to_string() }; + let typ = typ.fold_vars(["motive__".to_string()]); + let motive_type = KT::Ref { nam: adt.name.to_string() }; + let motive_type = motive_type.fold_vars(type_vars.clone()); + let typ = KT::All { nam: "motive__".to_string(), typ: Box::new(motive_type), bod: Box::new(typ) }; + let typ = adt.ctrs.keys().rfold(typ, |typ, ctr_nam| { + let ctr = adt.ctrs.get(ctr_nam).unwrap(); + let forall_type = KT::Ref { nam: ctr_nam.to_string() }; + let forall_type = forall_type.fold_vars(type_vars.clone()); + let forall_type = forall_type.fold_vars(ctr.fields.iter().map(|field| field.nam.to_string())); + let forall_type = + KT::App { fun: Box::new(KT::Ref { nam: "P".to_string() }), arg: Box::new(forall_type) }; + let forall_type = ctr.fields.iter().rfold(forall_type, |typ, field| KT::All { + nam: field.nam.to_string(), + typ: Box::new(field.typ.to_kindc(counter, src)), + bod: Box::new(typ), + }); + KT::All { nam: format!("case__{ctr_nam}"), typ: Box::new(forall_type), bod: Box::new(typ) } + }); + let p_type = KT::All { + nam: "x".to_string(), + typ: Box::new(KT::Ref { nam: adt_nam.to_string() }.fold_vars(type_vars.clone())), + bod: Box::new(KT::Set), + }; + let typ = KT::All { nam: "P".to_string(), typ: Box::new(p_type), bod: Box::new(typ) }; + let typ = type_vars.clone().rfold(typ, |typ, var| KT::All { + nam: var.to_string(), + typ: Box::new(KT::Met { uid: fresh_num(counter) }), + bod: Box::new(typ), + }); + + // Apply the arms to the match function + let term = KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) }; + let term = type_vars.fold(term, |term, _| KT::App { + fun: Box::new(term), + arg: Box::new(KT::Met { uid: fresh_num(counter) }), + }); + let term = KT::App { fun: Box::new(term), arg: Box::new(KT::Met { uid: fresh_num(counter) }) }; + let term = arms.iter().fold(term, |term, arm| { + let arg = arm.2.to_kindc(counter, book, src); + let arg = + arm.1.iter().rfold(arg, |acc, nam| KT::Lam { nam: bind_to_kind(nam), bod: Box::new(acc) }); + KT::App { fun: Box::new(term), arg: Box::new(arg) } + }); + + // Apply the argument to the match term + let term = KT::App { fun: Box::new(term), arg: Box::new(KT::Ref { nam: "arg__".to_string() }) }; + KT::Let { + nam: "arg__".to_string(), + bod: Box::new(arg.to_kindc(counter, book, src)), + nxt: Box::new(term), + } + } + Term::Swt { bnd: _, arg, with_bnd: _, with_arg: _, pred, arms } => { + let bind = format!("swi__{}", fresh_num(counter)); + KT::Swi { + bind: bind.clone(), + arg: Box::new(arg.to_kindc(counter, book, src)), + zero: Box::new(arms[0].to_kindc(counter, book, src)), + succ: Box::new(KT::Use { + nam: pred.as_ref().unwrap().to_string(), + bod: Box::new(KT::Ref { nam: format!("{}-1", bind) }), + nxt: Box::new(arms[1].to_kindc(counter, book, src)), + }), + motive: Box::new(KT::Met { uid: fresh_num(counter) }), + } + } + Term::Ref { nam } => { + // For function calls, we need to add a meta variable for each + // parametric type variable of the function. + let typ = if let Some(def) = book.defs.get(nam) { + &def.typ + } else if let Some(def) = book.hvm_defs.get(nam) { + &def.typ + } else { + unreachable!() + }; + typ.leading_type_vars().into_iter().fold(KT::Ref { nam: nam.to_string() }, |acc, _nam| KT::App { + fun: Box::new(acc), + arg: Box::new(KT::Met { uid: fresh_num(counter) }), + }) + } + + Term::Fan { fan: FanKind::Tup, tag: Tag::Static, els } => { + // Native tuples are converted to a tuple adt constructor. + assert!(els.len() < 16); + let term = Term::Ref { nam: Name::new(format!("kindc__tup{}/new", els.len())) }; + let term = els.iter().fold(term, |term, el| Term::App { + tag: Tag::Static, + fun: Box::new(term), + arg: Box::new(el.clone()), + }); + term.to_kindc(counter, book, src) + } + Term::Fan { fan: _, tag: _, els: _ } => todo!(), + Term::Era => KT::Ref { nam: "kindc__none/new".to_string() }, + + Term::Link { nam: _ } => unreachable!(), + Term::With { .. } => unreachable!(), + Term::Str { .. } => unreachable!(), + Term::Nat { .. } => unreachable!(), + Term::List { .. } => unreachable!(), + Term::Fold { .. } => unreachable!(), + Term::Bend { .. } => unreachable!(), + Term::Open { .. } => unreachable!(), + Term::Def { .. } => unreachable!(), + Term::Ask { .. } => unreachable!(), + Term::Err => unreachable!(), + }; + KT::Src { src, bod: Box::new(term) } + }) + } +} + +impl Type { + fn to_kindc(&self, counter: &mut u64, src: u64) -> KT { + let kt = maybe_grow(|| match self { + Type::Hole => KT::Met { uid: fresh_num(counter) }, + Type::Var(nam) => KT::Ref { nam: nam.to_string() }, + Type::All(nam, bod) => { + KT::All { nam: nam.to_string(), typ: Box::new(KT::Set), bod: Box::new(bod.to_kindc(counter, src)) } + } + Type::Arr(lft, rgt) => KT::All { + nam: "arr__".to_string(), + typ: Box::new(lft.to_kindc(counter, src)), + bod: Box::new(rgt.to_kindc(counter, src)), + }, + Type::Ctr(nam, args) => args.iter().fold(KT::Ref { nam: nam.to_string() }, |acc, arg| KT::App { + fun: Box::new(acc), + arg: Box::new(arg.to_kindc(counter, src)), + }), + Type::U24 => KT::U48, + // TODO: Implement i24 and f24 in kindc + Type::I24 => KT::I48, + Type::F24 => KT::F48, + Type::Any => KT::Any, + Type::Tup(els) => els.iter().fold(KT::Ref { nam: format!("kindc__tup{}", els.len()) }, |typ, el| { + KT::App { fun: Box::new(typ), arg: Box::new(el.to_kindc(counter, src)) } + }), + Type::None => KT::Ref { nam: "kindc__none".to_string() }, + }); + KT::Src { src, bod: Box::new(kt) } + } + + fn leading_type_vars(&self) -> Vec { + fn go(typ: &Type, acc: &mut Vec) { + maybe_grow(|| { + if let Type::All(nam, bod) = typ { + acc.push(nam.to_string()); + go(bod, acc); + } else { + // Not a leading type variable, stop + } + }) + } + let mut vars = vec![]; + go(self, &mut vars); + vars + } +} + +impl Op { + pub fn to_kindc(&self) -> Oper { + match self { + Op::ADD => Oper::ADD, + Op::SUB => Oper::SUB, + Op::MUL => Oper::MUL, + Op::DIV => Oper::DIV, + Op::REM => Oper::MOD, + Op::EQ => Oper::EQ, + Op::NEQ => Oper::NE, + Op::LT => Oper::LT, + Op::GT => Oper::GT, + Op::LE => Oper::LTE, + Op::GE => Oper::GTE, + Op::AND => Oper::AND, + Op::OR => Oper::OR, + Op::XOR => Oper::XOR, + Op::SHL => Oper::LSH, + Op::SHR => Oper::RSH, + // TODO: Implement pow in kindc + Op::POW => Oper::MUL, + } + } +} + +impl KT { + pub fn to_bend_type(&self) -> Type { + maybe_grow(|| match self { + // Forall types -> type variable, erase the All + // Forall value of specific type -> function argument, convert into an arrow + KT::All { nam: _, typ, bod } => match typ.as_ref() { + KT::Set => bod.to_bend_type(), + _ => Type::Arr(Box::new(typ.to_bend_type()), Box::new(bod.to_bend_type())), + }, + KT::App { .. } => self.app_to_bend_type(vec![]), + // TODO: This does not convert nullary constructors correctly, + // but since we only use this for printing, it's not currently a problem. + // Nullary constructors become variables instead. + KT::Ref { nam } => Type::Var(Name::new(nam)), + KT::Any => Type::Any, + KT::U48 => Type::U24, + KT::I48 => Type::I24, + KT::F48 => Type::F24, + KT::Slf { .. } => todo!(), + KT::Ins { .. } => todo!(), + KT::Let { .. } => todo!(), + KT::Use { .. } => todo!(), + KT::Set => todo!(), + // TODO: Should use an actual unknown type here + KT::Met { .. } => Type::Var(Name::new("_")), + KT::Src { .. } => todo!(), + KT::Ann { .. } => todo!(), + KT::UNum { .. } => unreachable!(), + KT::INum { .. } => unreachable!(), + KT::FNum { .. } => unreachable!(), + KT::Op2 { .. } => unreachable!(), + KT::Swi { .. } => unreachable!(), + KT::Hol { .. } => unreachable!(), + KT::Txt { .. } => unreachable!(), + KT::Lam { .. } => unreachable!(), + KT::Nat { .. } => unreachable!(), + }) + } + + pub fn app_to_bend_type(&self, mut args: Vec) -> Type { + maybe_grow(|| match self { + KT::App { fun, arg } => { + args.push(arg.to_bend_type()); + fun.app_to_bend_type(args) + } + KT::Ref { nam } => { + args.reverse(); + Type::Ctr(Name::new(nam), args) + } + _ => unreachable!(), + }) + } + + pub fn fold_vars(self, vars: impl IntoIterator) -> Self { + vars + .into_iter() + .fold(self, |acc, var| KT::App { fun: Box::new(acc), arg: Box::new(KT::Ref { nam: var }) }) + } + + pub fn rfold_lams(self, lams: I) -> Self + where + I: IntoIterator, + I::IntoIter: DoubleEndedIterator, + { + lams.into_iter().rfold(self, |acc, lam| KT::Lam { nam: lam, bod: Box::new(acc) }) + } +} + +fn bind_to_kind(nam: &Option) -> String { + match nam { + Some(nam) => nam.to_string(), + None => "era__".to_string(), + } +} + +fn fresh_num(val: &mut u64) -> u64 { + let fresh = *val; + *val += 1; + fresh +} + +fn name_from_src(src: u64, kind_book: &KindCBook) -> &str { + if let Some(name) = kind_book.get(&src) { + name.0.as_str() + } else { + "unknown" + } +} +/* Stringify to KindC */ + +impl fmt::Display for KT { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + maybe_grow(|| match self { + KT::All { nam, typ, bod } => write!(f, "∀({nam}: {typ}) {bod}"), + KT::Lam { nam, bod } => write!(f, "λ{nam} {bod}"), + KT::App { fun, arg } => write!(f, "({fun} {arg})"), + KT::Ann { chk: false, bod, typ } => write!(f, "{{{bod}: {typ}}}"), + KT::Ann { chk: true, bod, typ } => write!(f, "{{{bod}:: {typ}}}"), + KT::Slf { nam, typ, bod } => write!(f, "$({nam}: {typ}) {bod}"), + KT::Ins { bod } => write!(f, "~{bod}"), + KT::Ref { nam } => write!(f, "{nam}"), + KT::Let { nam, bod, nxt } => write!(f, "let {nam} = {bod}; {nxt}"), + KT::Use { nam, bod, nxt } => write!(f, "use {nam} = {bod}; {nxt}"), + KT::Set => write!(f, "*"), + KT::Any => write!(f, "Any"), + KT::U48 => write!(f, "U48"), + KT::I48 => write!(f, "I48"), + KT::F48 => write!(f, "F48"), + KT::UNum { val } => write!(f, "{val}"), + KT::INum { val } => { + let sign = if *val >= 0 { "+" } else { "-" }; + write!(f, "{}{}", sign, val.abs()) + } + KT::FNum { val } => { + write!(f, "{val:.?}") + } + KT::Op2 { op, fst, snd } => write!(f, "({op} {fst} {snd})"), + KT::Swi { bind, arg, zero, succ, motive } => { + write!(f, "switch {bind} = {arg} {{ 0: {zero} _: {succ} }}: {motive}") + } + KT::Hol { nam, ctx } => { + write!(f, "?{nam}")?; + if !ctx.is_empty() { + write!(f, " [")?; + for (i, term) in ctx.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{term}")?; + } + write!(f, "]")?; + } + Ok(()) + } + KT::Met { uid } => write!(f, "_{uid}"), + KT::Src { src, bod } => write!(f, "!{src} {bod}"), + KT::Txt { val } => write!(f, "\"{val}\""), + KT::Nat { val } => write!(f, "#{val}"), + }) + } +} + +impl fmt::Display for Oper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Oper::ADD => write!(f, "+"), + Oper::SUB => write!(f, "-"), + Oper::MUL => write!(f, "*"), + Oper::DIV => write!(f, "/"), + Oper::MOD => write!(f, "%"), + Oper::EQ => write!(f, "=="), + Oper::NE => write!(f, "!="), + Oper::LT => write!(f, "<"), + Oper::GT => write!(f, ">"), + Oper::LTE => write!(f, "<="), + Oper::GTE => write!(f, ">="), + Oper::AND => write!(f, "&"), + Oper::OR => write!(f, "|"), + Oper::XOR => write!(f, "^"), + Oper::LSH => write!(f, "<<"), + Oper::RSH => write!(f, ">>"), + } + } +} + +/* Parse from KindC */ +enum KindCErr { + ExpectedFound { expected: KT, found: KT, val: KT, src: u64 }, + ExpectedFun { found: KT, val: KT, src: u64 }, + ExpectedSelf { found: KT, val: KT, src: u64 }, + UndefinedRef { nam: String, src: u64 }, + CantInferLam { val: KT, src: u64 }, + CantInferHol { val: KT, src: u64 }, + CantInferMet { val: KT, src: u64 }, + CantInferVar { val: KT, src: u64 }, +} + +new_parser!(KindCParser); + +impl KindCParser<'_> { + fn parse_result(&mut self) -> Result, String> { + let mut errs = vec![]; + self.skip_trivia(); + while !self.is_eof() { + errs.push(self.parse_err()?); + self.skip_trivia(); + } + Ok(errs) + } + + fn parse_err(&mut self) -> Result { + self.consume("#error{")?; + self.skip_trivia(); + if self.starts_with("?function") { + self.consume("?function")?; + let found = self.parse_term()?; + let val = self.parse_term()?; + let src = self.parse_u64()?; + self.consume("}")?; + Ok(KindCErr::ExpectedFun { found, val, src }) + } else if self.starts_with("?self-type") { + self.consume("?self-type")?; + let found = self.parse_term()?; + let val = self.parse_term()?; + let src = self.parse_u64()?; + self.consume("}")?; + Ok(KindCErr::ExpectedSelf { found, val, src }) + } else if self.starts_with("?undefined_reference") { + self.consume("?undefined_reference")?; + self.consume("?unknown_type")?; + let nam = self.parse_name()?; + let src = self.parse_u64()?; + self.consume("}")?; + Ok(KindCErr::UndefinedRef { nam, src }) + } else if self.starts_with("?type_annotation") { + self.consume("?type_annotation")?; + if self.starts_with("?untyped_lambda") { + self.consume("?untyped_lambda")?; + let val = self.parse_term()?; + let src = self.parse_u64()?; + self.consume("}")?; + Ok(KindCErr::CantInferLam { val, src }) + } else if self.starts_with("?untyped_hole") { + self.consume("?untyped_hole")?; + let val = self.parse_term()?; + let src = self.parse_u64()?; + self.consume("}")?; + Ok(KindCErr::CantInferHol { val, src }) + } else if self.starts_with("?untyped_meta") { + self.consume("?untyped_meta")?; + let val = self.parse_term()?; + let src = self.parse_u64()?; + self.consume("}")?; + Ok(KindCErr::CantInferMet { val, src }) + } else if self.starts_with("?untyped_variable") { + self.consume("?untyped_variable")?; + let val = self.parse_term()?; + let src = self.parse_u64()?; + self.consume("}")?; + Ok(KindCErr::CantInferVar { val, src }) + } else { + unreachable!() + } + } else { + let expected = self.parse_term()?; + let found = self.parse_term()?; + let val = self.parse_term()?; + let src = self.parse_u64()?; + self.consume("}")?; + Ok(KindCErr::ExpectedFound { expected, found, val, src }) + } + } + + fn parse_term(&mut self) -> Result { + maybe_grow(|| { + self.skip_trivia(); + if self.starts_with("∀") { + self.parse_all() + } else if self.starts_with("λ") { + self.parse_lam() + } else if self.starts_with("(") { + self.advance_one(); + self.skip_trivia(); + if let Some(c) = self.peek_one() { + if c == '_' { + self.parse_met() + } else if "+-*/%=!<>&|^".contains(c) { + self.parse_op2() + } else { + self.parse_app() + } + } else { + self.parse_app() + } + } else if self.starts_with("{") { + self.parse_ann() + } else if self.starts_with("$(") { + self.parse_slf() + } else if self.starts_with("~") { + self.parse_ins() + } else if self.starts_with("let") { + self.parse_let() + } else if self.starts_with("use") { + self.parse_use() + } else if self.starts_with("*") { + self.parse_set() + } else if self.starts_with("Any") { + self.parse_any() + } else if self.starts_with("U48") { + self.parse_u48() + } else if self.starts_with("I48") { + self.parse_i48() + } else if self.starts_with("F48") { + self.parse_f48() + } else if self.starts_with("#") { + self.parse_nat() + } else if self.starts_with("switch") { + self.parse_swi() + } else if self.starts_with("\"") { + self.parse_txt() + } else if self.starts_with("!") { + self.parse_src() + } else if self.starts_with("?") { + self.parse_hol() + } else if let Some(c) = self.peek_one() { + if "0123456789-+".contains(c) { + self.parse_num() + } else { + self.parse_ref() + } + } else { + self.parse_ref() + } + }) + } + + fn parse_all(&mut self) -> Result { + self.consume("∀")?; + self.consume("(")?; + let nam = self.parse_name()?; + self.consume(":")?; + let typ = Box::new(self.parse_term()?); + self.consume(")")?; + let bod = Box::new(self.parse_term()?); + Ok(KT::All { nam, typ, bod }) + } + + fn parse_lam(&mut self) -> Result { + self.consume("λ")?; + let nam = self.parse_name()?; + let bod = Box::new(self.parse_term()?); + Ok(KT::Lam { nam, bod }) + } + + fn parse_app(&mut self) -> Result { + // Parens already consumed + let fun = Box::new(self.parse_term()?); + let arg = Box::new(self.parse_term()?); + self.consume(")")?; + Ok(KT::App { fun, arg }) + } + + fn parse_op2(&mut self) -> Result { + // Parens already consumed + let op = self.parse_oper()?; + let fst = Box::new(self.parse_term()?); + let snd = Box::new(self.parse_term()?); + self.consume(")")?; + Ok(KT::Op2 { op, fst, snd }) + } + + fn parse_met(&mut self) -> Result { + // Parens already consumed + self.consume("_")?; + self.skip_trivia(); + while !self.starts_with(")") { + // TODO: Should we do something with the spine? + self.parse_term()?; + self.skip_trivia(); + } + self.consume(")")?; + // TODO: No UID is returned, should I add something here? + Ok(KT::Met { uid: 0 }) + } + + fn parse_ann(&mut self) -> Result { + self.consume("{")?; + let bod = Box::new(self.parse_term()?); + self.consume(":")?; + let chk = if self.starts_with(":") { + self.advance_one(); + false + } else { + true + }; + let typ = Box::new(self.parse_term()?); + self.consume("}")?; + Ok(KT::Ann { chk, bod, typ }) + } + + fn parse_slf(&mut self) -> Result { + self.consume("$(")?; + let nam = self.parse_name()?; + self.consume(":")?; + let typ = Box::new(self.parse_term()?); + self.consume(")")?; + let bod = Box::new(self.parse_term()?); + Ok(KT::Slf { nam, typ, bod }) + } + + fn parse_ins(&mut self) -> Result { + self.consume("~")?; + let bod = Box::new(self.parse_term()?); + Ok(KT::Ins { bod }) + } + + fn parse_let(&mut self) -> Result { + self.consume("let")?; + let nam = self.parse_name()?; + self.consume("=")?; + let bod = Box::new(self.parse_term()?); + self.consume(";")?; + let nxt = Box::new(self.parse_term()?); + Ok(KT::Let { nam, bod, nxt }) + } + + fn parse_use(&mut self) -> Result { + self.consume("use")?; + let nam = self.parse_name()?; + self.consume("=")?; + let bod = Box::new(self.parse_term()?); + self.consume(";")?; + let nxt = Box::new(self.parse_term()?); + Ok(KT::Use { nam, bod, nxt }) + } + + fn parse_set(&mut self) -> Result { + self.consume("*")?; + Ok(KT::Set) + } + + fn parse_any(&mut self) -> Result { + self.consume("Any")?; + Ok(KT::Any) + } + + fn parse_u48(&mut self) -> Result { + self.consume("U48")?; + Ok(KT::U48) + } + + fn parse_i48(&mut self) -> Result { + self.consume("I48")?; + Ok(KT::I48) + } + + fn parse_f48(&mut self) -> Result { + self.consume("F48")?; + Ok(KT::F48) + } + + fn parse_num(&mut self) -> Result { + let sgn = if let Some('+') = self.peek_one() { + self.advance_one(); + Some(1) + } else if let Some('-') = self.peek_one() { + self.advance_one(); + Some(-1) + } else { + None + }; + + let int = self.parse_u64()?; + + let frac = if let Some('.') = self.peek_one() { + self.advance_one(); + let ini_idx = *self.index(); + let frac = self.parse_u64()?; + let end_idx = *self.index(); + let frac = frac as f32 / 10f32.powi((end_idx - ini_idx) as i32); + Some(frac) + } else { + None + }; + + if let Some(frac) = frac { + let sgn = sgn.unwrap_or(1); + Ok(KT::FNum { val: sgn as f32 * (int as f32 + frac) }) + } else if let Some(sgn) = sgn { + let int = sgn * int as i32; + Ok(KT::INum { val: int }) + } else { + Ok(KT::UNum { val: int as u32 }) + } + } + + fn parse_swi(&mut self) -> Result { + self.consume("switch")?; + let bind = self.parse_name()?; + self.consume("=")?; + let arg = Box::new(self.parse_term()?); + self.consume("{")?; + self.consume("0:")?; + let zero = Box::new(self.parse_term()?); + self.consume("_:")?; + let succ = Box::new(self.parse_term()?); + self.consume("}")?; + self.consume(":")?; + let motive = Box::new(self.parse_term()?); + Ok(KT::Swi { bind, arg, zero, succ, motive }) + } + + fn parse_txt(&mut self) -> Result { + let val = self.parse_quoted_string()?; + Ok(KT::Txt { val }) + } + + fn parse_nat(&mut self) -> Result { + self.consume("#")?; + let val = self.parse_u64()? as u32; + Ok(KT::Nat { val }) + } + + fn parse_hol(&mut self) -> Result { + self.consume("?")?; + let nam = self.parse_name()?; + Ok(KT::Hol { nam, ctx: vec![] }) + } + + fn parse_src(&mut self) -> Result { + self.consume("!")?; + let src = self.parse_u64()?; + let bod = Box::new(self.parse_term()?); + Ok(KT::Src { src, bod }) + } + + fn parse_ref(&mut self) -> Result { + let nam = self.parse_name()?; + Ok(KT::Ref { nam }) + } + + fn parse_oper(&mut self) -> Result { + let op = self.take_while(|c| "+-*/%=!<>&|^".contains(c)); + match op { + "+" => Ok(Oper::ADD), + "-" => Ok(Oper::SUB), + "*" => Ok(Oper::MUL), + "/" => Ok(Oper::DIV), + "%" => Ok(Oper::MOD), + "==" => Ok(Oper::EQ), + "!=" => Ok(Oper::NE), + "<" => Ok(Oper::LT), + ">" => Ok(Oper::GT), + "<=" => Ok(Oper::LTE), + ">=" => Ok(Oper::GTE), + "&" => Ok(Oper::AND), + "|" => Ok(Oper::OR), + "^" => Ok(Oper::XOR), + "<<" => Ok(Oper::LSH), + ">>" => Ok(Oper::RSH), + _ => Err(format!("Invalid operator: {}", op)), + } + } +} diff --git a/src/imp/parser.rs b/src/imp/parser.rs index 1df8bf179..fe476c929 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -100,13 +100,12 @@ impl<'a> ImpParser<'a> { }; self.skip_trivia_inline()?; - let (fields, field_types): (Vec<_>, Vec<_>) = if self.starts_with("{") { + let fields = if self.starts_with("{") { self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)? } else { vec![] - } - .into_iter() - .unzip(); + }; + let field_types = fields.iter().map(|f| f.typ.clone()).collect::>(); let end_idx = *self.index(); self.check_repeated_ctr_fields(&fields, &name, ini_idx..end_idx)?; @@ -164,7 +163,7 @@ impl<'a> ImpParser<'a> { } else { vec![] }; - let (fields, field_types): (Vec<_>, Vec<_>) = fields.into_iter().unzip(); + let field_types = fields.iter().map(|f| f.typ.clone()).collect::>(); let end_idx = *self.index(); self.check_repeated_ctr_fields(&fields, &name, ini_idx..end_idx)?; @@ -172,7 +171,7 @@ impl<'a> ImpParser<'a> { Ok(AdtCtr { name, typ, fields }) } - fn parse_variant_field(&mut self) -> ParseResult<(CtrField, Type)> { + fn parse_variant_field(&mut self) -> ParseResult { let rec = self.try_consume_exactly("~"); self.skip_trivia_inline()?; @@ -181,7 +180,7 @@ impl<'a> ImpParser<'a> { let typ = if self.try_consume_exactly(":") { self.parse_type_expr()? } else { Type::Any }; - Ok((CtrField { nam, rec }, typ)) + Ok(CtrField { nam, typ, rec }) } fn parse_primary_expr(&mut self, inline: bool) -> ParseResult { diff --git a/src/lib.rs b/src/lib.rs index 597f5a467..8d21d6f88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,7 +103,6 @@ pub fn desugar_book( ctx.book.encode_builtins(); ctx.resolve_refs()?; - ctx.resolve_type_ctrs()?; ctx.desugar_match_defs()?; @@ -128,6 +127,10 @@ pub fn desugar_book( // Manual match linearization ctx.book.linearize_match_with(); + if opts.type_check { + type_check_book(&mut ctx)?; + } + ctx.book.encode_matches(opts.adt_encoding); // sanity check @@ -165,6 +168,17 @@ pub fn desugar_book( } } +pub fn type_check_book(ctx: &mut Ctx) -> Result<(), Diagnostics> { + let old_book = std::mem::replace(ctx.book, ctx.book.clone()); + ctx.make_native_defs(); + ctx.book.encode_adts(AdtEncoding::Scott); + ctx.resolve_type_ctrs()?; + let res = ctx.type_check(); + *ctx.book = old_book; + res?; + Ok(()) +} + pub fn run_book( mut book: Book, run_opts: RunOpts, @@ -362,6 +376,9 @@ pub struct CompileOpts { /// Enables [hvm::check_net_size]. pub check_net_size: bool, + /// Enables [type_check_book]. + pub type_check: bool, + /// Determines the encoding of constructors and matches. pub adt_encoding: AdtEncoding, } @@ -376,8 +393,9 @@ impl CompileOpts { prune: true, float_combinators: true, merge: true, - inline: true, linearize_matches: OptLevel::Enabled, + type_check: true, + inline: self.inline, check_net_size: self.check_net_size, adt_encoding: self.adt_encoding, } @@ -394,6 +412,7 @@ impl CompileOpts { float_combinators: false, merge: false, inline: false, + type_check: self.type_check, check_net_size: self.check_net_size, adt_encoding: self.adt_encoding, } @@ -426,6 +445,7 @@ impl Default for CompileOpts { merge: false, inline: false, check_net_size: true, + type_check: true, adt_encoding: AdtEncoding::NumScott, } } diff --git a/src/main.rs b/src/main.rs index 94162c819..a49bddb1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -188,6 +188,8 @@ pub enum OptArgs { NoCheckNetSize, AdtScott, AdtNumScott, + TypeCheck, + NoTypeCheck, } fn compile_opts_from_cli(args: &Vec, compiler_target: CompilerTarget) -> CompileOpts { @@ -210,6 +212,8 @@ fn compile_opts_from_cli(args: &Vec, compiler_target: CompilerTarget) - NoInline => opts.inline = false, CheckNetSize => opts.check_net_size = true, NoCheckNetSize => opts.check_net_size = false, + TypeCheck => opts.type_check = true, + NoTypeCheck => opts.type_check = false, LinearizeMatches => opts.linearize_matches = OptLevel::Enabled, LinearizeMatchesAlt => opts.linearize_matches = OptLevel::Alt, diff --git a/tests/snapshots/cli__compile_all.bend.snap b/tests/snapshots/cli__compile_all.bend.snap index 980463d45..4053e69c4 100644 --- a/tests/snapshots/cli__compile_all.bend.snap +++ b/tests/snapshots/cli__compile_all.bend.snap @@ -8,7 +8,9 @@ input_file: tests/golden_tests/cli/compile_all.bend @Pair.get__C1 = (?((@Pair.get__C0 *) a) a) -@Pair/Pair = (a (b ((0 (a (b c))) c))) +@Pair/Pair = (a (b ((@Pair/Pair/tag (a (b c))) c))) + +@Pair/Pair/tag = 0 @main = d & @Pair.get ~ (($([+] $(a b)) (a b)) (c d)) diff --git a/tests/snapshots/cli__compile_pre_reduce.bend.snap b/tests/snapshots/cli__compile_pre_reduce.bend.snap index 35e3536c3..b5b6cf12e 100644 --- a/tests/snapshots/cli__compile_pre_reduce.bend.snap +++ b/tests/snapshots/cli__compile_pre_reduce.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/compile_pre_reduce.bend --- error: invalid value 'pre-reduce' for '-O ' - [possible values: all, no-all, eta, no-eta, prune, no-prune, linearize-matches, linearize-matches-alt, no-linearize-matches, float-combinators, no-float-combinators, merge, no-merge, inline, no-inline, check-net-size, no-check-net-size, adt-scott, adt-num-scott] + [possible values: all, no-all, eta, no-eta, prune, no-prune, linearize-matches, linearize-matches-alt, no-linearize-matches, float-combinators, no-float-combinators, merge, no-merge, inline, no-inline, check-net-size, no-check-net-size, adt-scott, adt-num-scott, type-check, no-type-check] For more information, try '--help'. diff --git a/tests/snapshots/cli__compile_wrong_opt.bend.snap b/tests/snapshots/cli__compile_wrong_opt.bend.snap index 947d8daf7..58742bb05 100644 --- a/tests/snapshots/cli__compile_wrong_opt.bend.snap +++ b/tests/snapshots/cli__compile_wrong_opt.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/compile_wrong_opt.bend --- error: invalid value 'foo' for '-O ' - [possible values: all, no-all, eta, no-eta, prune, no-prune, linearize-matches, linearize-matches-alt, no-linearize-matches, float-combinators, no-float-combinators, merge, no-merge, inline, no-inline, check-net-size, no-check-net-size, adt-scott, adt-num-scott] + [possible values: all, no-all, eta, no-eta, prune, no-prune, linearize-matches, linearize-matches-alt, no-linearize-matches, float-combinators, no-float-combinators, merge, no-merge, inline, no-inline, check-net-size, no-check-net-size, adt-scott, adt-num-scott, type-check, no-type-check] tip: a similar value exists: 'float-combinators' From dd6d8f8037b56d54830ed4f7d04478942475c1ac Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Wed, 21 Aug 2024 15:40:45 +0200 Subject: [PATCH 06/29] Update builtins for typechecker --- src/fun/builtins.bend | 155 ++++++++++++++++++++++++++++++------------ 1 file changed, 110 insertions(+), 45 deletions(-) diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index 01ac10060..b7f328ae5 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -124,6 +124,23 @@ def Result/unwrap(res: Result(T, E)) -> Any: case Result/Err: return res.val +# Returns the second result if the first one is `Ok`. +# Otherwise, returns the `Err` of the first result. +def Result/and(fst: Result(T, E), snd: Result(T, E)) -> Result(T, E): + match fst: + case Result/Ok: + return snd + case Result/Err: + return fst + +# Maps the error value of a result. +def Result/map_err(res: Result(T, E), f: E -> F) -> Result(T, F): + match res: + case Result/Ok: + return Result/Ok(res.val) + case Result/Err: + return Result/Err(f(res.val)) + type Tree(T): Node { ~left: Tree(T), ~right: Tree(T) } Leaf { value: T } @@ -213,6 +230,15 @@ type IOError(T): Name Inner { value: T } +def IOError/unwrap_inner(err: IOError(T)) -> T: + match err: + case IOError/Type: + return unreachable() + case IOError/Name: + return unreachable() + case IOError/Inner: + return err.value + def IO/MAGIC() -> (u24, u24): return (0xD0CA11, 0xFF1FF1) @@ -222,22 +248,49 @@ def IO/wrap(x: T) -> IO(T): def IO/bind(a: IO(A), b: ((A -> IO(B)) -> (A -> IO(B))) -> (A -> IO(B))) -> IO(B): match a: case IO/Done: - b = undefer(b) - return b(a.expr) + return undefer(b, a.expr) case IO/Call: return IO/Call(a.magic, a.func, a.argm, lambda x: IO/bind(a.cont(x), b)) -def IO/call(func: String, argm: Any) -> IO(Result(Any, Any)): +# Calls an IO by its name with the given arguments. +# +# The arguments are untyped and not checked for type correctness. +# If type safety is desired, this function should be wrapped with +# another that checks the types of the arguments and of the return. +# +# Always returns a `Result` where the error is an `IOError`, a type +# that either contains an internal error of the IO function, like an +# `errno` in the case of FS functions, or a general Bend IO error, +# like a type error if the arguments are invalid or a name error if +# the called IO is not found. +def IO/call(func: String, argm: Any) -> IO(Result(Any, IOError(Any))): return IO/Call(IO/MAGIC, func, argm, lambda x: IO/Done(IO/MAGIC, x)) -IO/done_on_err (IO/Call magic func argm cont) = (IO/Call magic func argm @res match res { - Result/Ok: (cont res.val) - Result/Err: (IO/Done IO/MAGIC (Result/Err res.val)) -}) +# If the IO result is an error, returns it without calling the continuation. +# Otherwise, calls the continuation with the unwrapped result. +IO/done_on_err (io: (IO (Result A B))) : (IO (Result A B)) +IO/done_on_err (IO/Call magic func argm cont) = + let cont = @res match res { + Result/Ok: (cont res.val) + Result/Err: (IO/Done IO/MAGIC (Result/Err res.val)) + } + (IO/Call magic func argm cont) IO/done_on_err done = done +# Maps the result of an IO. +def IO/map(io: IO(A), f: A -> B) -> IO(B): + return IO/bind(io, lambda x: x(lambda f, y: IO/Done(IO/MAGIC, f(y)), f)) + +# Unwraps the `IOError` of the result of an IO, returning the `Inner` variant. +# +# Should only be called if the other `IOError` variants are unreachable. +def IO/unwrap_inner(io: IO(Result(A, IOError(B)))) -> IO(Result(A, B)): + return IO/map(io, lambda x: Result/map_err(x, IOError/unwrap_inner)) + ## Time and sleep -# Returns a monotonically increasing nanosecond timestamp as an u48 encoded as a pair of u24s. + + # Returns a monotonically increasing nanosecond timestamp as an u48 +# encoded as a pair of u24s. def IO/get_time() -> IO((u24, u24)): with IO: res <- IO/call("GET_TIME", *) @@ -245,37 +298,37 @@ def IO/get_time() -> IO((u24, u24)): # Sleeps for the given number of nanoseconds, given by an u48 encoded as a pair of u24s. def IO/nanosleep(hi_lo: (u24, u24)) -> IO(None): - return IO/call("SLEEP", hi_lo) + with IO: + res <- IO/call("SLEEP", hi_lo) + return wrap(Result/unwrap(res)) # Sleeps for a given amount of seconds as a float. def IO/sleep(seconds: f24) -> IO(None): nanos = seconds * 1_000_000_000.0 lo = f24_to_u24(nanos % 0x1_000_000.0) hi = f24_to_u24(nanos / 0x1_000_000.0) - with IO: - res <- IO/nanosleep((hi, lo)) - return wrap(Result/unwrap(res)) + return IO/nanosleep((hi, lo)) ## File IO ### File IO primitives -def IO/FS/open(path: String, mode: String) -> IO(u24): - return IO/call("OPEN", (path, mode)) +def IO/FS/open(path: String, mode: String) -> IO(Result(u24, u24)): + return IO/unwrap_inner(IO/call("OPEN", (path, mode))) -def IO/FS/close(file: u24) -> IO(None): - return IO/call("CLOSE", file) +def IO/FS/close(file: u24) -> IO(Result(None, u24)): + return IO/unwrap_inner(IO/call("CLOSE", file)) -def IO/FS/read(file: u24, num_bytes: u24) -> IO(List(u24)): - return IO/call("READ", (file, num_bytes)) +def IO/FS/read(file: u24, num_bytes: u24) -> IO(Result(List(u24), u24)): + return IO/unwrap_inner(IO/call("READ", (file, num_bytes))) -def IO/FS/write(file: u24, bytes: List(u24)) -> IO(None): - return IO/call("WRITE", (file, bytes)) +def IO/FS/write(file: u24, bytes: List(u24)) -> IO(Result(None, u24)): + return IO/unwrap_inner(IO/call("WRITE", (file, bytes))) -def IO/FS/seek(file: u24, offset: i24, mode: u24) -> IO(None): - return IO/call("SEEK", (file, (offset, mode))) +def IO/FS/seek(file: u24, offset: i24, mode: u24) -> IO(Result(None, u24)): + return IO/unwrap_inner(IO/call("SEEK", (file, (offset, mode)))) -def IO/FS/flush(file: u24) -> IO(None): - return IO/call("FLUSH", file) +def IO/FS/flush(file: u24) -> IO(Result(None, u24)): + return IO/unwrap_inner(IO/call("FLUSH", file)) ### Always available files IO/FS/STDIN : u24 = 0 @@ -293,18 +346,18 @@ IO/FS/SEEK_END : i24 = +2 ### File utilities # Reads an entire file, returning a list of bytes. -def IO/FS/read_file(path: String) -> IO(List(u24)): +def IO/FS/read_file(path: String) -> IO(Result(List(u24), u24)): with IO: fd <- IO/done_on_err(IO/FS/open(path, "r")) - bytes <- IO/FS/read_to_end(fd) + bytes <- IO/done_on_err(IO/FS/read_to_end(fd)) * <- IO/done_on_err(IO/FS/close(fd)) return wrap(bytes) # Reads the remaining contents of a file, returning a list of read bytes. -def IO/FS/read_to_end(fd: u24) -> IO(List(u24)): +def IO/FS/read_to_end(fd: u24) -> IO(Result(List(u24), u24)): return IO/FS/read_to_end.read_chunks(fd, []) -def IO/FS/read_to_end.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u24)): +def IO/FS/read_to_end.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(List(u24), u24)): with IO: # Read file in 1MB chunks chunk <- IO/done_on_err(IO/FS/read(fd, 1048576)) @@ -315,24 +368,27 @@ def IO/FS/read_to_end.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u return IO/FS/read_to_end.read_chunks(fd, List/Cons(chunk, chunks)) # Reads a single line from a file, returning a list of bytes. -def IO/FS/read_line(fd: u24) -> IO(List(u24)): +def IO/FS/read_line(fd: u24) -> IO(Result(List(u24), u24)): return IO/FS/read_line.read_chunks(fd, []) -def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u24)): +def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(List(u24), u24)): with IO: # Read line in 1kB chunks chunk <- IO/done_on_err(IO/FS/read(fd, 1024)) match res = List/split_once(chunk, '\n'): # Found a newline, backtrack and join chunks case Result/Ok: + # Found line end, backtrack on the file to the start of the next line + # and return the found line. (line, rest) = res.val (length, *) = List/length(rest) - * <- IO/FS/seek(fd, u24_to_i24(length) * -1, IO/FS/SEEK_CUR) + * <- IO/done_on_err(IO/FS/seek(fd, u24_to_i24(length) * -1, IO/FS/SEEK_CUR)) chunks = List/Cons(line, chunks) bytes = List/flatten(chunks) - return wrap(bytes) + return wrap(Result/Ok(bytes)) # Newline not found case Result/Err: + # No line end found, continue reading. line = res.val (length, line) = List/length(line) # If length is 0, the end of the file was reached, return as if it was a newline @@ -345,7 +401,7 @@ def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u24 return IO/FS/read_line.read_chunks(fd, chunks) # Writes a list of bytes to a file given by a path. -def IO/FS/write_file(path: String, bytes: List(u24)) -> IO(None): +def IO/FS/write_file(path: String, bytes: List(u24)) -> IO(Result(None, u24)): with IO: f <- IO/FS/open(path, "w") match f: @@ -367,10 +423,10 @@ def IO/print(text: String) -> IO(None): # IO/input() -> IO String # Read characters from stdin until a newline is found. # Returns the read input decoded as utf-8. -def IO/input() -> IO(String): +def IO/input() -> IO(Result(String, u24)): return IO/input.go(DiffList/new) -def IO/input.go(acc: List(u24) -> List(u24)) -> IO(String): +def IO/input.go(acc: List(u24) -> List(u24)) -> IO(Result(String, u24)): # TODO: This is slow and inefficient, should be done in hvm using fgets. with IO: byte <- IO/done_on_err(IO/FS/read(IO/FS/STDIN, 1)) @@ -382,7 +438,7 @@ def IO/input.go(acc: List(u24) -> List(u24)) -> IO(String): if byte.head == '\n': bytes = DiffList/to_list(acc) text = String/decode_utf8(bytes) - return wrap(text) + return wrap(Result/Ok(text)) else: acc = DiffList/append(acc, byte.head) return IO/input.go(acc) @@ -392,8 +448,8 @@ def IO/input.go(acc: List(u24) -> List(u24)) -> IO(String): # Returns an unique id to the library object encoded as a u24 # 'path' is the path to the library file. # 'lazy' is a boolean encoded as a u24 that determines if all functions are loaded lazily or upfront. -def IO/DyLib/open(path: String, lazy: u24) -> IO(u24): - return IO/call("DL_OPEN", (path, lazy)) +def IO/DyLib/open(path: String, lazy: u24) -> IO(Result(u24, String)): + return IO/unwrap_inner(IO/call("DL_OPEN", (path, lazy))) # Calls a function of a previously opened library. # The returned value is determined by the called function. @@ -401,20 +457,24 @@ def IO/DyLib/open(path: String, lazy: u24) -> IO(u24): # 'fn' is the name of the function in the library. # 'args' are the arguments to the function. The expected values depend on the called function. def IO/DyLib/call(dl: u24, fn: String, args: Any) -> IO(Any): - return IO/call("DL_CALL", (dl, (fn, args))) + return IO/unwrap_inner(IO/call("DL_CALL", (dl, (fn, args)))) # Closes a previously open library. # Returns nothing. # 'dl' is the id of the library object. def IO/DyLib/close(dl: u24) -> IO(None): - return IO/Call(IO/MAGIC, "DL_CLOSE", (dl), IO/wrap) + return IO/unwrap_inner(IO/call("DL_CLOSE", dl)) # Lazy thunks -# We can defer the evaluation of a function by wrapping it in a thunk + +# We can defer the evaluation of a function by wrapping it in a thunk. # Ex: @x (x @arg1 @arg2 @arg3 (f arg1 arg2 arg3) arg1 arg2 arg3) -# This is only evaluated when we call it with ' ' (undefer my_thunk) -# We can build a defered call directly or by by using defer and defer_arg +# +# This is only evaluated when we call it with `(undefer my_thunk)`. +# We can build a defered call directly or by by using `defer` and `defer_arg`. +# # The example above can be written as: +# # (defer_arg (defer_arg (defer_arg (defer @arg1 @arg2 @arg3 (f arg1 arg2 arg3)) arg1) arg2) arg3) def defer(val: T) -> (T -> T) -> T: return lambda x: x(val) @@ -422,9 +482,14 @@ def defer(val: T) -> (T -> T) -> T: def defer_arg(defered: ((A -> B) -> (A -> B)) -> (A -> B), arg: B) -> ((A -> A) -> A): return lambda x: defered(x, arg) -def undefer(defered: ((A -> A) -> A)) -> A: +def undefer(defered: (A -> A) -> A) -> A: return defered(lambda x: x) +# A function that can be used in unreachable code. +# +# Is not type safe and if used in code that is actually reachable, will corrupt the program. +def unreachable() -> Any: + return * # Native number casts @@ -463,8 +528,8 @@ hvm i24_to_f24 -> (i24 -> f24): Utf8/REPLACEMENT_CHARACTER : u24 = '\u{FFFD}' -# String/decode_utf8(List u24) -> String # Decodes a sequence of bytes to a String using utf-8 encoding. +# Invalid utf-8 sequences are replaced with Utf8/REPLACEMENT_CHARACTER. String/decode_utf8 (bytes: (List u24)) : String = let (got, rest) = (Utf8/decode_character bytes) match rest { From 43ce0d9faee0dfd997779edeafa304aefbd2889b Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Wed, 21 Aug 2024 20:27:27 +0200 Subject: [PATCH 07/29] Implement a basic hindley-milner type system for bend --- src/fun/check/mod.rs | 1 + src/fun/check/type_check.rs | 532 ++++++++++ src/fun/display.rs | 1 - src/fun/mod.rs | 8 +- src/fun/parser.rs | 1 - src/fun/transform/fix_match_terms.rs | 2 +- src/fun/transform/mod.rs | 1 - src/fun/transform/resolve_type_ctrs.rs | 53 +- src/fun/transform/type_check.rs | 1288 ------------------------ src/lib.rs | 4 +- 10 files changed, 556 insertions(+), 1335 deletions(-) create mode 100644 src/fun/check/type_check.rs delete mode 100644 src/fun/transform/type_check.rs diff --git a/src/fun/check/mod.rs b/src/fun/check/mod.rs index e54bdbd3f..9690e250b 100644 --- a/src/fun/check/mod.rs +++ b/src/fun/check/mod.rs @@ -1,4 +1,5 @@ pub mod set_entrypoint; pub mod shared_names; +pub mod type_check; pub mod unbound_refs; pub mod unbound_vars; diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs new file mode 100644 index 000000000..710cfcb44 --- /dev/null +++ b/src/fun/check/type_check.rs @@ -0,0 +1,532 @@ +//! Hindley-Milner-like type system. +//! +//! Based on https://github.com/developedby/algorithm-w-rs +//! and https://github.com/mgrabmueller/AlgorithmW. +use crate::{ + fun::{Adt, Book, Constructors, Ctx, MatchRule, Name, Pattern, Tag, Term, Type}, + maybe_grow, +}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; + +impl Ctx<'_> { + pub fn type_check(&mut self) -> Result<(), String> { + eprintln!("book:\n{}", self.book); + eprintln!("ctrs:\n{:?}", self.book.adts); + let types = infer_book(self.book)?; + let types = refresh_vars(types); + + for (nam, typ) in types.0 { + eprintln!("{nam}: {typ}"); + } + Ok(()) + } +} + +#[derive(Default)] +pub struct ProgramTypes(BTreeMap); + +/// A type scheme, aka a polymorphic type. +#[derive(Clone)] +struct Scheme(Vec, Type); + +/// A finite mapping from type variables to types. +#[derive(Clone, Default, Debug)] +struct Subst(BTreeMap); + +/// A mapping from term variables to type schemes. +#[derive(Clone, Default)] +struct TypeEnv(BTreeMap); + +/// Variable generator for type variables. +#[derive(Default)] +struct VarGen(usize); + +/// Topologically ordered set of mutually recursive groups of functions. +struct RecGroups(Vec>); + +/* Implementations */ + +impl Type { + fn free_type_vars(&self) -> BTreeSet { + maybe_grow(|| match self { + Type::Any => todo!(), + Type::Hole => todo!(), + Type::Var(x) => BTreeSet::from([x.clone()]), + Type::Arr(t1, t2) => t1.free_type_vars().union(&t2.free_type_vars()).cloned().collect(), + Type::Ctr(_, ts) => ts.iter().flat_map(|t| t.free_type_vars()).collect(), + Type::Tup(_) => todo!(), + Type::U24 => todo!(), + Type::F24 => todo!(), + Type::I24 => todo!(), + Type::None => todo!(), + }) + } + + fn subst(&self, subst: &Subst) -> Type { + maybe_grow(|| match self { + Type::Var(nam) => match subst.0.get(nam) { + Some(new) => new.clone(), + None => self.clone(), + }, + Type::Arr(t1, t2) => Type::Arr(Box::new(t1.subst(subst)), Box::new(t2.subst(subst))), + Type::Ctr(name, ts) => Type::Ctr(name.clone(), ts.iter().map(|t| t.subst(subst)).collect()), + Type::Any => todo!(), + Type::Hole => todo!(), + Type::Tup(_) => todo!(), + Type::U24 => todo!(), + Type::F24 => todo!(), + Type::I24 => todo!(), + Type::None => todo!(), + }) + } + + /// Converts a monomorphic type into a type scheme by abstracting + /// over the type variables free in `t`, but not free in the type + /// environment. + fn generalize(&self, env: &TypeEnv) -> Scheme { + let vars_env = env.free_type_vars(); + let vars_t = self.free_type_vars(); + let vars = vars_t.difference(&vars_env).cloned().collect(); + Scheme(vars, self.clone()) + } +} + +impl Scheme { + fn free_type_vars(&self) -> BTreeSet { + let vars = self.1.free_type_vars(); + let bound_vars = self.0.iter().cloned().collect(); + vars.difference(&bound_vars).cloned().collect() + } + + fn subst(&self, subst: &Subst) -> Scheme { + let mut subst = subst.clone(); + for x in self.0.iter() { + subst.0.remove(x); + } + let t = self.1.subst(&subst); + Scheme(self.0.clone(), t) + } + + /// Converts a type scheme into a monomorphic type by assigning + /// fresh type variables to each variable bound by the scheme. + fn instantiate(&self, var_gen: &mut VarGen) -> Type { + let new_vars = self.0.iter().map(|_| var_gen.fresh()); + let subst = Subst(self.0.iter().cloned().zip(new_vars).collect()); + self.1.subst(&subst) + } +} + +impl Subst { + /// Compose two substitutions. + /// + /// Applies the first substitution to the second, and then inserts the result into the first. + fn compose(&self, other: &Subst) -> Subst { + let other = other.0.iter().map(|(x, t)| (x.clone(), t.subst(self))); + let subst = self.0.iter().map(|(x, t)| (x.clone(), t.clone())).chain(other).collect(); + Subst(subst) + } +} + +impl TypeEnv { + fn free_type_vars(&self) -> BTreeSet { + let mut vars = BTreeSet::new(); + for scheme in self.0.values() { + let scheme_vars = scheme.free_type_vars(); + vars = vars.union(&scheme_vars).cloned().collect(); + } + vars + } + + fn subst(&self, subst: &Subst) -> TypeEnv { + let env = self.0.iter().map(|(x, scheme)| (x.clone(), scheme.subst(subst))).collect(); + TypeEnv(env) + } + + fn append(&self, name: &Name, scheme: Scheme) -> TypeEnv { + let mut env = self.0.clone(); + env.insert(name.clone(), scheme); + TypeEnv(env) + } +} + +impl VarGen { + fn fresh(&mut self) -> Type { + let x = format!("a{}", self.0); + self.0 += 1; + Type::Var(Name::new(x)) + } +} + +impl RecGroups { + fn from_book(book: &Book) -> RecGroups { + type DependencyGraph<'a> = HashMap<&'a Name, HashSet<&'a Name>>; + + fn collect_dependencies<'a>( + term: &'a Term, + ctrs: &'a Constructors, + scope: &mut Vec, + deps: &mut HashSet<&'a Name>, + ) { + if let Term::Ref { nam } = term { + if ctrs.contains_key(nam) { + // Don't infer types for constructors + } else { + deps.insert(nam); + } + } + for (child, binds) in term.children_with_binds() { + scope.extend(binds.clone().flatten().cloned()); + collect_dependencies(child, ctrs, scope, deps); + scope.truncate(scope.len() - binds.flatten().count()); + } + } + + fn strong_connect<'a>( + v: &'a Name, + deps: &DependencyGraph<'a>, + index: &mut usize, + index_map: &mut HashMap<&'a Name, usize>, + low_link: &mut HashMap<&'a Name, usize>, + stack: &mut Vec<&'a Name>, + components: &mut Vec>, + ) { + maybe_grow(|| { + index_map.insert(v, *index); + low_link.insert(v, *index); + *index += 1; + stack.push(v); + + if let Some(neighbors) = deps.get(v) { + for w in neighbors { + if !index_map.contains_key(w) { + // Successor w has not yet been visited, recurse on it. + strong_connect(w, deps, index, index_map, low_link, stack, components); + low_link.insert(v, low_link[v].min(low_link[w])); + } else if stack.contains(w) { + // Successor w is in stack S and hence in the current SCC. + low_link.insert(v, low_link[v].min(index_map[w])); + } else { + // If w is not on stack, then (v, w) is an edge pointing + // to an SCC already found and must be ignored. + } + } + } + + // If v is a root node, pop the stack and generate an SCC. + if low_link[v] == index_map[v] { + let mut component = BTreeSet::new(); + while let Some(w) = stack.pop() { + component.insert(w.clone()); + if w == v { + break; + } + } + components.push(component); + } + }) + } + + // Build the dependency graph + let mut deps = DependencyGraph::default(); + for (name, def) in &book.defs { + if book.ctrs.contains_key(name) { + // Don't infer types for constructors + continue; + } + let mut fn_deps = Default::default(); + collect_dependencies(&def.rule().body, &book.ctrs, &mut vec![], &mut fn_deps); + deps.insert(name, fn_deps); + } + + // Run Tarjan's algorithm + let mut index = 0; + let mut stack = Vec::new(); + let mut index_map = HashMap::new(); + let mut low_link = HashMap::new(); + let mut components = Vec::new(); + for name in deps.keys() { + if !index_map.contains_key(name) { + strong_connect(name, &deps, &mut index, &mut index_map, &mut low_link, &mut stack, &mut components); + } + } + let components = components.into_iter().map(|x| x.into_iter().collect()).collect(); + RecGroups(components) + } +} + +fn infer_book(book: &Book) -> Result { + let rec_groups = RecGroups::from_book(book); + let mut env = TypeEnv::default(); + let mut types = BTreeMap::new(); + // Add the constructors to the environment. + for adt in book.adts.values() { + for ctr in adt.ctrs.values() { + let scheme = ctr.typ.generalize(&TypeEnv::default()); + env.0.insert(ctr.name.clone(), scheme); + types.insert(ctr.name.clone(), ctr.typ.clone()); + } + } + // Infer the types of regular functions. + let fn_ts = infer_fns(env, book, rec_groups.0.into_iter(), &mut VarGen::default())?; + types.extend(fn_ts); + Ok(ProgramTypes(types)) +} + +fn infer_fns( + mut env: TypeEnv, + book: &Book, + mut groups: impl Iterator>, + var_gen: &mut VarGen, +) -> Result, String> { + maybe_grow(|| { + if let Some(group) = groups.next() { + // Generate fresh type variables for each function in the group. + let tvs = group.iter().map(|_| var_gen.fresh()).collect::>(); + for (name, tv) in group.iter().zip(tvs.iter()) { + env.0.insert(name.clone(), Scheme(vec![], tv.clone())); + } + + // Infer the types of the functions in the group. + let mut ss = vec![]; + let mut ts = vec![]; + for name in &group { + let def = book.defs.get(name).unwrap(); + let (s, t) = infer(&env, book, &def.rule().body, var_gen)?; + ss.push(s); + ts.push(t); + } + + // Unify the inferred body with the corresponding type variable. + let mut group_s = ss.into_iter().fold(Subst::default(), |s, s2| s.compose(&s2)); + for (bod_t, tv) in ts.into_iter().zip(tvs.iter()) { + let s = unify(&tv.subst(&group_s), &bod_t)?; + group_s = s.compose(&group_s); + } + + // Generalize the function types + let mut env = env.subst(&group_s); + let final_ts = tvs.into_iter().map(|tv| tv.subst(&group_s)).collect::>(); + for (name, t) in group.iter().zip(final_ts.iter()) { + env.0.insert(name.clone(), t.generalize(&env)); + } + + let rest_ts = infer_fns(env, book, groups, var_gen)?; + let mut program_types = group.into_iter().zip(final_ts).collect::>(); + program_types.extend(rest_ts); + Ok(program_types) + } else { + Ok(vec![]) + } + }) +} + +/// Infer the type of a term in the given environment. +/// +/// The type environment must contain bindings for all the free variables of the term. +/// +/// The returned substitution records the type constraints imposed on type variables by the term. +/// The returned type is the type of the term. +fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Result<(Subst, Type), String> { + maybe_grow(|| match term { + Term::Var { nam } | Term::Ref { nam } => match env.0.get(nam) { + Some(scheme) => { + let t = scheme.instantiate(var_gen); + Ok((Subst::default(), t)) + } + None => unreachable!("unbound name '{}'", nam), + }, + Term::Lam { tag: Tag::Static, pat, bod } => match pat.as_ref() { + Pattern::Var(nam) => { + let tv = var_gen.fresh(); + let env = if let Some(nam) = nam { env.append(nam, Scheme(vec![], tv.clone())) } else { env.clone() }; + let (s, bod_t) = infer(&env, book, bod, var_gen)?; + let var_t = tv.subst(&s); + Ok((s, Type::Arr(Box::new(var_t), Box::new(bod_t)))) + } + _ => unreachable!(), + }, + Term::App { tag: Tag::Static, fun, arg } => { + let (s1, fun_t) = infer(env, book, fun, var_gen)?; + let (s2, arg_t) = infer(&env.subst(&s1), book, arg, var_gen)?; + let app_t = var_gen.fresh(); + let s3 = unify(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())))?; + Ok((s3.compose(&s2).compose(&s1), app_t.subst(&s3))) + } + Term::Let { pat, val, nxt } => match pat.as_ref() { + Pattern::Var(nam) => { + let (s1, val_t) = infer(env, book, val, var_gen)?; + let nxt_env = + if let Some(nam) = nam { env.append(nam, val_t.generalize(&env.subst(&s1))) } else { env.clone() }; + let (s2, nxt_t) = infer(&nxt_env, book, nxt, var_gen)?; + Ok((s2.compose(&s1), nxt_t)) + } + _ => todo!(), + }, + Term::Mat { bnd: _, arg, with_bnd: _, with_arg: _, arms } => { + // Infer type of the scrutinee + let (s1, t1) = infer(env, book, arg, var_gen)?; + + // Instantiate the expected type of the scrutinee + let adt_name = book.ctrs.get(arms[0].0.as_ref().unwrap()).unwrap(); + let adt = &book.adts[adt_name]; + let (s2, arg_t) = instantiate_adt(adt, var_gen)?; + + // Unify the inferred type with the expected type + let s3 = unify(&t1.subst(&s2), &arg_t)?; + + // For each case, infer the types and unify them all. + // Unify the inferred type of the destructured fields with the + // expected from what we inferred from the scrutinee. + let s = s3.compose(&s2).compose(&s1); + let env = env.subst(&s); + infer_match_cases(&env, book, adt, arms, &s, var_gen) + } + Term::Lam { tag: _, .. } => todo!(), + Term::App { tag: _, .. } => todo!(), + Term::Link { .. } => todo!(), + Term::Use { .. } => todo!(), + Term::Fan { .. } => todo!(), + Term::Num { .. } => todo!(), + Term::Oper { .. } => todo!(), + Term::Swt { .. } => todo!(), + Term::With { .. } + | Term::Ask { .. } + | Term::Nat { .. } + | Term::Str { .. } + | Term::List { .. } + | Term::Fold { .. } + | Term::Bend { .. } + | Term::Open { .. } + | Term::Def { .. } + | Term::Era + | Term::Err => unreachable!(), + }) +} + +fn instantiate_adt(adt: &Adt, var_gen: &mut VarGen) -> Result<(Subst, Type), String> { + let tvs = adt.vars.iter().map(|_| var_gen.fresh()); + let s = Subst(adt.vars.iter().zip(tvs).map(|(x, t)| (x.clone(), t)).collect()); + let t = Type::Ctr(adt.name.clone(), adt.vars.iter().cloned().map(Type::Var).collect()); + let t = t.subst(&s); + Ok((s, t)) +} + +fn infer_match_cases( + env: &TypeEnv, + book: &Book, + adt: &Adt, + arms: &[MatchRule], + s: &Subst, + var_gen: &mut VarGen, +) -> Result<(Subst, Type), String> { + maybe_grow(|| { + if let Some(((ctr_nam, vars, bod), rest)) = arms.split_first() { + let ctr = &adt.ctrs[ctr_nam.as_ref().unwrap()]; + // One fresh var per field, we later unify with the expected type. + let tvs = vars.iter().map(|_| var_gen.fresh()).collect::>(); + + // Add the fields to the environment. + let mut case_env = env.clone(); + for (var, tv) in vars.iter().zip(tvs.iter()) { + case_env.0.insert(var.as_ref().unwrap().clone(), Scheme(vec![], tv.clone())); + } + + // Infer the body and unify the inferred field types with the expected. + let (s1, t1) = infer(&case_env, book, bod, var_gen)?; + let inf_ts = tvs.into_iter().map(|tv| tv.subst(&s1)).collect::>(); + let exp_ts = ctr.fields.iter().map(|f| f.typ.subst(s)).collect::>(); + let s2 = unify_fields(inf_ts.iter().zip(exp_ts.iter()))?; + + // Recurse and unify with the other arms. + let (s_rest, t_rest) = infer_match_cases(env, book, adt, rest, s, var_gen)?; + let final_s = unify(&t1.subst(&s_rest.compose(&s2)), &t_rest)?; + + Ok((final_s.compose(&s_rest).compose(&s2).compose(&s1).compose(s), t_rest.subst(&final_s))) + } else { + let t = var_gen.fresh().subst(s); + Ok((s.clone(), t)) + } + }) +} + +fn unify_fields<'a>(ts: impl Iterator) -> Result { + let ss = ts.map(|(inf, exp)| unify(inf, exp)).collect::, _>>()?; + let mut s = Subst::default(); + for s2 in ss.into_iter().rev() { + s = s.compose(&s2); + } + Ok(s) +} + +fn unify(t1: &Type, t2: &Type) -> Result { + maybe_grow(|| match (t1, t2) { + (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { + let s1 = unify(l1, l2)?; + let s2 = unify(&r1.subst(&s1), &r2.subst(&s1))?; + Ok(s2.compose(&s1)) + } + (t, Type::Var(x)) | (Type::Var(x), t) => { + // Try to bind variable `x` to `t` + if let Type::Var(y) = t { + if y == x { + // Don't bind a variable to itself + return Ok(Subst::default()); + } + } + // Occurs check + if t.free_type_vars().contains(x) { + return Err(format!( + "Cannot unify variable '{x}' with type '{t}' because it occurs as a free variable" + )); + } + Ok(Subst(BTreeMap::from([(x.clone(), t.clone())]))) + } + (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { + let mut s = Subst::default(); + for (t1, t2) in ts1.iter().zip(ts2.iter()) { + s = s.compose(&unify(t1, t2)?); + } + Ok(s) + } + _ => Err(format!("Types do not unify: '{t1}' and '{t2}'")), + }) +} + +fn refresh_vars(types: ProgramTypes) -> ProgramTypes { + let mut new_types = BTreeMap::new(); + for (name, mut typ) in types.0 { + refresh_vars_go(&mut typ, &mut BTreeMap::new(), &mut VarGen::default()); + new_types.insert(name, typ); + } + ProgramTypes(new_types) +} + +fn refresh_vars_go(typ: &mut Type, map: &mut BTreeMap, var_gen: &mut VarGen) { + maybe_grow(|| match typ { + Type::Var(x) => { + if let Some(y) = map.get(x) { + *typ = y.clone(); + } else { + let y = var_gen.fresh(); + map.insert(x.clone(), y.clone()); + *typ = y; + } + } + Type::Arr(t1, t2) => { + refresh_vars_go(t1, map, var_gen); + refresh_vars_go(t2, map, var_gen); + } + Type::Ctr(_, ts) => { + for t in ts { + refresh_vars_go(t, map, var_gen); + } + } + Type::Any => todo!(), + Type::Hole => todo!(), + Type::Tup(_) => todo!(), + Type::U24 => todo!(), + Type::F24 => todo!(), + Type::I24 => todo!(), + Type::None => todo!(), + }) +} diff --git a/src/fun/display.rs b/src/fun/display.rs index aa11b8489..5aae90d1b 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -296,7 +296,6 @@ impl fmt::Display for Type { match self { Type::Hole => write!(f, "_"), Type::Var(nam) => write!(f, "{nam}"), - Type::All(nam, bod) => write!(f, "∀{nam} {bod}"), Type::Arr(lft, rgt) => write!(f, "({lft} -> {rgt})"), Type::Ctr(nam, args) => { if args.is_empty() { diff --git a/src/fun/mod.rs b/src/fun/mod.rs index d861c5b9d..603962546 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -109,7 +109,6 @@ pub enum Type { Any, Hole, Var(Name), - All(Name, Box), Ctr(Name, Vec), Arr(Box, Box), Tup(Vec), @@ -1126,10 +1125,6 @@ impl Type { *nam = to.clone(); } } - Type::All(nam, _) if nam == from => { - // This forall is shadowing the subst variable, so stop here. - return; - } _ => (), }; for child in self.children_mut() { @@ -1139,14 +1134,13 @@ impl Type { } pub fn children_mut(&mut self) -> impl Iterator { - multi_iterator!(ChildrenIter { Zero, One, Two, Vec }); + multi_iterator!(ChildrenIter { Zero, Two, Vec }); match self { Type::Any | Type::None | Type::Hole | Type::I24 | Type::F24 | Type::U24 | Type::Var(_) => { ChildrenIter::Zero([]) } Type::Arr(lft, rgt) => ChildrenIter::Two([lft.as_mut(), rgt.as_mut()]), Type::Tup(els) | Type::Ctr(_, els) => ChildrenIter::Vec(els), - Type::All(_, body) => ChildrenIter::One([body.as_mut()]), } } } diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 7ff2ae20d..0c9db003f 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -1235,7 +1235,6 @@ pub fn make_fn_type(args: &[Type], ret: Type) -> Type { pub fn make_ctr_type(type_name: Name, fields: &[Type], vars: &[Name]) -> Type { let typ = Type::Ctr(type_name, vars.iter().cloned().map(Type::Var).collect()); let typ = fields.iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ.clone()), Box::new(acc))); - let typ = vars.iter().rfold(typ, |acc, typ| Type::All(typ.clone(), Box::new(acc))); typ } diff --git a/src/fun/transform/fix_match_terms.rs b/src/fun/transform/fix_match_terms.rs index ee6b239e1..6467ddb38 100644 --- a/src/fun/transform/fix_match_terms.rs +++ b/src/fun/transform/fix_match_terms.rs @@ -37,7 +37,7 @@ impl Ctx<'_> { /// * If either included one of the cases more than once (including wildcard patterns), we'd get a warning. /// ```hvm /// match x { - /// Cons: (A x.h x.t) + /// Cons x.h x.t: (A x.h x.t) /// Nil: let %matched = (Foo y); switch %matched { 0: B; 1: C; _: D } /// } /// ``` diff --git a/src/fun/transform/mod.rs b/src/fun/transform/mod.rs index 612590293..d2c4968a3 100644 --- a/src/fun/transform/mod.rs +++ b/src/fun/transform/mod.rs @@ -21,5 +21,4 @@ pub mod resolve_refs; pub mod resolve_type_ctrs; pub mod resugar_list; pub mod resugar_string; -pub mod type_check; pub mod unique_names; diff --git a/src/fun/transform/resolve_type_ctrs.rs b/src/fun/transform/resolve_type_ctrs.rs index 5cf6d25fd..814d4ccea 100644 --- a/src/fun/transform/resolve_type_ctrs.rs +++ b/src/fun/transform/resolve_type_ctrs.rs @@ -1,22 +1,22 @@ -use indexmap::IndexSet; - use crate::{ diagnostics::Diagnostics, - fun::{Adts, Ctx, Name, Type}, + fun::{Adts, Ctx, Type}, maybe_grow, }; impl Ctx<'_> { - /// Resolves type constructors in the book and adds for-alls for the free type vars. + /// Resolves type constructors in the book. pub fn resolve_type_ctrs(&mut self) -> Result<(), Diagnostics> { for def in self.book.defs.values_mut() { - let mut free_vars = Default::default(); - match def.typ.resolve_type_ctrs(&mut vec![], &mut free_vars, &self.book.adts) { - Ok(_) => { - let typ = std::mem::replace(&mut def.typ, Type::Hole); - def.typ = free_vars.into_iter().rfold(typ, |acc, nam| Type::All(nam, Box::new(acc))); - } - Err(e) => self.info.add_function_error(e, def.name.clone(), def.source.clone()), + let res = def.typ.resolve_type_ctrs(&self.book.adts); + self.info.take_rule_err(res, def.name.clone()); + } + + let adts = self.book.adts.clone(); + for adt in self.book.adts.values_mut() { + for ctr in adt.ctrs.values_mut() { + let res = ctr.typ.resolve_type_ctrs(&adts); + self.info.take_rule_err(res, ctr.name.clone()); } } @@ -26,24 +26,14 @@ impl Ctx<'_> { impl Type { /// Resolves type constructors in the type. - pub fn resolve_type_ctrs( - &mut self, - scope: &mut Vec, - free_vars: &mut IndexSet, - adts: &Adts, - ) -> Result<(), String> { + pub fn resolve_type_ctrs(&mut self, adts: &Adts) -> Result<(), String> { maybe_grow(|| { match self { Type::Var(nam) => { // If the variable actually refers to a type, we change the type to a constructor. - if adts.get(nam).is_some() { - // Only change if the name is not being shadowed by a forall. - if !scope.contains(nam) { - *self = Type::Ctr(nam.clone(), vec![]); - } - } else if !scope.contains(nam) && !free_vars.contains(nam) { - // A new free variable, add it to the free var set to be added as a for all later. - free_vars.insert(nam.clone()); + if adts.contains_key(nam) { + eprintln!("found adt: {nam}"); + *self = Type::Ctr(nam.clone(), vec![]); } } Type::Ctr(name, args) => { @@ -51,21 +41,16 @@ impl Type { return Err(format!("Found unknown type constructor '{name}'.")); } for arg in args { - arg.resolve_type_ctrs(scope, free_vars, adts)?; + arg.resolve_type_ctrs(adts)?; } } - Type::All(nam, body) => { - scope.push(nam.clone()); - body.resolve_type_ctrs(scope, free_vars, adts)?; - scope.pop(); - } Type::Arr(lft, rgt) => { - lft.resolve_type_ctrs(scope, free_vars, adts)?; - rgt.resolve_type_ctrs(scope, free_vars, adts)?; + lft.resolve_type_ctrs(adts)?; + rgt.resolve_type_ctrs(adts)?; } Type::Tup(els) => { for el in els { - el.resolve_type_ctrs(scope, free_vars, adts)?; + el.resolve_type_ctrs(adts)?; } } Type::Any | Type::Hole | Type::None | Type::U24 | Type::I24 | Type::F24 => {} diff --git a/src/fun/transform/type_check.rs b/src/fun/transform/type_check.rs deleted file mode 100644 index 7872b6a18..000000000 --- a/src/fun/transform/type_check.rs +++ /dev/null @@ -1,1288 +0,0 @@ -use crate::{ - diagnostics::Diagnostics, - fun::{ - Adt, AdtCtr, Book, CtrField, Ctx, Definition, FanKind, Name, Num, Op, Pattern, Source, Tag, Term, Type, - }, - maybe_grow, -}; -use core::fmt; -use std::{collections::BTreeMap, fmt::Write, process::Output}; -use TSPL::{new_parser, Parser}; - -impl Ctx<'_> { - /// Type check by compiling to kind-core - pub fn type_check(&mut self) -> Result<(), Diagnostics> { - // Compile to kind-core - let mut counter = 1; // Reserve 0 for unknown sources - let mut kind_book = KindCBook::new(); - - for def in self.book.defs.values() { - if let Some(adt_nam) = self.book.ctrs.get(&def.name) { - // Constructors get a special compilation using self types - let adt = self.book.adts.get(adt_nam).unwrap(); - ctr_to_kindc(&def.name, adt, &mut counter, &mut kind_book); - } else if !def.check { - // Unchecked functions probably contain unsupported features like unscoped variables. - // We can't check their real values, so just use an unchecked dummy. - unchecked_def_to_kindc(&def.name, &def.typ, &mut counter, &mut kind_book); - } else { - // Normal functions - def_to_kindc(def, &mut counter, self.book, &mut kind_book); - } - } - - // HVM native defs must be unchecked and compiled to a dummy value - for def in self.book.hvm_defs.values() { - unchecked_def_to_kindc(&def.name, &def.typ, &mut counter, &mut kind_book); - } - - for adt in self.book.adts.values() { - adt_to_kindc(adt, &mut counter, &mut kind_book); - } - - // Build the kindc program - let mut out = String::new(); - for (_, (nam, term)) in kind_book.iter() { - writeln!(out, "{nam} = {term};").unwrap(); - } - - // Write the output to a temporary file - if let Err(e) = std::fs::write(".out.kindc", out) { - self.info.add_book_error(format!("Error writing type checking output file: {e}")); - return self.info.fatal(()); - } - - // Call kind-core on the file - let output = std::process::Command::new("kindc") - .arg("check-all") - .arg(".out.kindc") - .output() - .map_err(|e| format!("While running the type checker: {e}"))?; - - // Remove temporary file - if let Err(e) = std::fs::remove_file(".out.kindc") { - self.info.add_book_error(format!("Error removing type checking output file: {e}")); - } - - self.read_kindc_output(output, &kind_book)?; - - self.info.fatal(()) - } - - /// Create adts and constructors for native values (numbers, tuples, etc.) - pub fn make_native_defs(&mut self) { - // Tuple adts - // TODO: Make this more flexible, maybe generating only exactly the used tuple types. - for i in 2..16 { - let ctr_nam = Name::new(format!("kindc__tup{}/new", i)); - let vars = (0..i).map(|i| Name::new(format!("t{}", i))).collect::>(); - let ctr_type = (0..i).rfold(Type::Tup(vars.iter().cloned().map(Type::Var).collect()), |typ, i| { - Type::All(Name::new(format!("t{}", i)), Box::new(typ)) - }); - let fields = (0..i) - .map(|i| CtrField { - nam: Name::new(format!("x{}", i)), - rec: false, - typ: Type::Var(Name::new(format!("t{}", i))), - }) - .collect(); - let ctr = AdtCtr { name: ctr_nam.clone(), typ: ctr_type, fields }; - - let adt_name = Name::new(format!("kindc__tup{}", i)); - let adt = Adt { - name: adt_name.clone(), - vars, - ctrs: [(ctr_nam.clone(), ctr)].into(), - source: Source::Generated, - }; - self.book.ctrs.insert(ctr_nam.clone(), adt_name.clone()); - self.book.adts.insert(adt_name.clone(), adt.clone()); - } - // None adt - let adt_name = Name::new("kindc__none"); - let ctr_nam = Name::new("kindc__none/new"); - self.book.ctrs.insert(ctr_nam.clone(), adt_name.clone()); - self.book.adts.insert( - adt_name.clone(), - Adt { - name: adt_name.clone(), - vars: vec![], - ctrs: [( - ctr_nam.clone(), - AdtCtr { name: ctr_nam.clone(), typ: Type::Ctr(adt_name.clone(), vec![]), fields: vec![] }, - )] - .into(), - source: Source::Generated, - }, - ); - } - - /// Read, parse and report errors from the kindc output. - fn read_kindc_output(&mut self, output: Output, kind_book: &KindCBook) -> Result<(), Diagnostics> { - if !output.status.success() { - let out = String::from_utf8_lossy(&output.stdout); - let err = String::from_utf8_lossy(&output.stderr); - self.info.add_book_error(format!("Unexpected error from the type checker:\n{err}\n{out}")); - return self.info.fatal(()); - } - - // Parse the output - if !output.stderr.is_empty() { - let err = String::from_utf8_lossy(&output.stderr); - eprintln!("unexpected error from the type checker:\n{err}"); - } else if !output.stdout.is_empty() { - let err = String::from_utf8_lossy(&output.stdout); - eprintln!("raw type error:\n{err}"); - let mut p = KindCParser::new(&err); - match p.parse_result() { - Ok(errs) => { - for err in errs { - match err { - KindCErr::ExpectedFound { expected, found, val, src } => { - let msg = format!( - "Type checking error:\nExpected: {}\nFound: {}\nValue: {}\nSource: {}", - expected.to_bend_type(), - found.to_bend_type(), - val, - name_from_src(src, kind_book), - ); - self.info.add_book_error(msg); - } - KindCErr::ExpectedFun { found, val, src } => { - // TODO: Improve this message, it should mention that it's applying to a non-function. - // Either too many args or applying to a non-function. - let var_name = format!( - "Type checking error:\nExpected: function\nFound: {}\nValue: {}\nSource: {}", - found.to_bend_type(), - val, - name_from_src(src, kind_book), - ); - let msg = var_name; - self.info.add_book_error(msg); - } - KindCErr::ExpectedSelf { found, val, src } => { - let msg = format!( - "Type checking error:\nExpected: a self-type\nFound: {}\nValue: {}\nSource: {}", - found.to_bend_type(), - val, - name_from_src(src, kind_book), - ); - self.info.add_book_error(msg); - } - KindCErr::UndefinedRef { nam, src } => { - let msg = format!( - "Type checking error:\nUndefined reference.\nValue: {}\nSource: {}", - nam, - name_from_src(src, kind_book), - ); - self.info.add_book_error(msg); - } - KindCErr::CantInferLam { val, src } => { - let msg = format!( - "Type checking error:\nCan't infer type of lambda.\nValue: {}\nSource: {}", - val, - name_from_src(src, kind_book), - ); - self.info.add_book_error(msg); - } - KindCErr::CantInferHol { val, src } => { - let msg = format!( - "Type checking error:\nCan't infer type of hole.\nValue: {}\nSource: {}", - val, - name_from_src(src, kind_book), - ); - self.info.add_book_error(msg); - } - KindCErr::CantInferMet { val, src } => { - let msg = format!( - "Type checking error:\nCan't infer type of meta-variable.\nValue: {}\nSource: {}", - val, - name_from_src(src, kind_book), - ); - self.info.add_book_error(msg); - } - KindCErr::CantInferVar { val, src } => { - let msg = format!( - "Type checking error:\nCan't infer type of variable.\nValue: {}\nSource: {}", - val, - name_from_src(src, kind_book), - ); - self.info.add_book_error(msg); - } - } - } - } - Err(p_err) => { - eprintln!("Parse error: {p_err}"); - let msg = format!("Unexpected output from the type checker:\n{err}"); - self.info.add_book_error(msg); - } - } - } - - self.info.fatal(()) - } -} - -type KindCBook = BTreeMap; - -pub enum KindCTerm { - All { - nam: String, - typ: Box, - bod: Box, - }, - Lam { - nam: String, - bod: Box, - }, - App { - fun: Box, - arg: Box, - }, - Ann { - chk: bool, - bod: Box, - typ: Box, - }, - Slf { - nam: String, - typ: Box, - bod: Box, - }, - Ins { - bod: Box, - }, - Ref { - nam: String, - }, - Let { - nam: String, - bod: Box, - nxt: Box, - }, - Use { - nam: String, - bod: Box, - nxt: Box, - }, - Set, - Any, - U48, - I48, - F48, - UNum { - val: u32, - }, - INum { - val: i32, - }, - FNum { - val: f32, - }, - Op2 { - op: Oper, - fst: Box, - snd: Box, - }, - Swi { - bind: String, - arg: Box, - zero: Box, - succ: Box, - motive: Box, - }, - Hol { - nam: String, - ctx: Vec, - }, - Met { - uid: u64, - }, - Src { - src: u64, - bod: Box, - }, - Txt { - val: String, - }, - Nat { - val: u32, - }, -} - -use KindCTerm as KT; - -pub enum Oper { - ADD, - SUB, - MUL, - DIV, - MOD, - EQ, - NE, - LT, - GT, - LTE, - GTE, - AND, - OR, - XOR, - LSH, - RSH, -} - -fn def_to_kindc(def: &Definition, counter: &mut u64, bend_book: &Book, kindc_book: &mut KindCBook) { - let src = fresh_num(counter); - let bod = def.rule().body.to_kindc(counter, bend_book, src); - let type_vars = def.typ.leading_type_vars(); - let bod = bod.rfold_lams(type_vars); - let typ = def.typ.to_kindc(counter, src); - let chk = def.check; - let bod = KT::Ann { chk, bod: Box::new(bod), typ: Box::new(typ) }; - kindc_book.insert(src, (def.name.to_string(), bod)); -} - -fn ctr_to_kindc(ctr_nam: &Name, adt: &Adt, counter: &mut u64, book: &mut KindCBook) { - // λ for each type variable - // λ for each field - // self instantiation - // λ for predicate - // λ for each constructor - // (ctr fields) : ∀(type vars: _) ∀(field: field type) (Type vars) - // TODO: Use the actual encoding and not always scott encoding - let src = fresh_num(counter); - let ctr = adt.ctrs.get(ctr_nam).unwrap(); - - let term = KT::Ref { nam: ctr_nam.to_string() }; - let term = term.fold_vars(ctr.fields.iter().map(|field| field.nam.to_string())); - let term = term.rfold_lams(adt.ctrs.keys().map(|ctr| ctr.to_string())); - let term = term.rfold_lams(["P".to_string()]); - let term = KT::Ins { bod: Box::new(term) }; - let term = term.rfold_lams(ctr.fields.iter().map(|field| field.nam.to_string())); - let term = term.rfold_lams(adt.vars.iter().map(|var| var.to_string())); - - let typ = KT::Ref { nam: adt.name.to_string() }; - let typ = typ.fold_vars(adt.vars.iter().map(|var| var.to_string())); - let typ = ctr.fields.iter().rfold(typ, |typ, field| KT::All { - nam: field.nam.to_string(), - typ: Box::new(field.typ.to_kindc(counter, src)), - bod: Box::new(typ), - }); - let typ = adt.vars.iter().rfold(typ, |typ, var| KT::All { - nam: var.to_string(), - typ: Box::new(KT::Set), - bod: Box::new(typ), - }); - let term = KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) }; - book.insert(src, (ctr_nam.to_string(), term)); -} - -fn adt_to_kindc(adt: &Adt, counter: &mut u64, book: &mut KindCBook) { - // λ type vars - // $(self: (Type vars)) - // ∀(P: ∀(x: (Type vars)) *) - // ∀(constructor: ∀(fields: field types) (P (constructor vars fields))) - // (P self): ∀(type vars: _) * - let src = fresh_num(counter); - let term = KT::Ref { nam: "P".to_string() }; - let term = term.fold_vars(["self".to_string()]); - let term = adt.ctrs.iter().rfold(term, |term, (ctr_nam, ctr)| { - let typ = KT::Ref { nam: ctr_nam.to_string() }; - let typ = typ.fold_vars(adt.vars.iter().map(|var| var.to_string())); - let typ = typ.fold_vars(ctr.fields.iter().map(|field| field.nam.to_string())); - let typ = KT::App { fun: Box::new(KT::Ref { nam: "P".to_string() }), arg: Box::new(typ) }; - let typ = ctr.fields.iter().rfold(typ, |typ, field| KT::All { - nam: field.nam.to_string(), - typ: Box::new(field.typ.to_kindc(counter, src)), - bod: Box::new(typ), - }); - KT::All { nam: ctr_nam.to_string(), typ: Box::new(typ), bod: Box::new(term) } - }); - let term = KT::All { - nam: "P".to_string(), - typ: Box::new(KT::All { - nam: "x".to_string(), - typ: Box::new( - KT::Ref { nam: adt.name.to_string() }.fold_vars(adt.vars.iter().map(|var| var.to_string())), - ), - bod: Box::new(KT::Set), - }), - bod: Box::new(term), - }; - let term = KT::Slf { - nam: "self".to_string(), - typ: Box::new( - KT::Ref { nam: adt.name.to_string() }.fold_vars(adt.vars.iter().map(|var| var.to_string())), - ), - bod: Box::new(term), - }; - let term = term.rfold_lams(adt.vars.iter().map(|var| var.to_string())); - - let typ = adt.vars.iter().rfold(KT::Set, |typ, var| KT::All { - nam: var.to_string(), - typ: Box::new(KT::Set), - bod: Box::new(typ), - }); - - let term = KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) }; - book.insert(src, (adt.name.to_string(), term)); -} - -fn unchecked_def_to_kindc(def_name: &Name, typ: &Type, counter: &mut u64, book: &mut KindCBook) { - let src = fresh_num(counter); - let term = KT::Met { uid: fresh_num(counter) }; - let typ = typ.to_kindc(counter, src); - let term = KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) }; - book.insert(src, (def_name.to_string(), term)); -} - -impl Term { - pub fn to_kindc(&self, counter: &mut u64, book: &Book, src: u64) -> KT { - maybe_grow(|| { - let term = match self { - Term::Lam { tag, pat, bod } => { - if tag != &Tag::Static { - todo!(); - } - match pat.as_ref() { - Pattern::Var(nam) => { - let term = KT::Lam { nam: bind_to_kind(nam), bod: Box::new(bod.to_kindc(counter, book, src)) }; - // TODO: We don't need to annotate every single lambda, only the top ones that don't already have one. - let typ = KT::All { - nam: "arr__".to_string(), - typ: Box::new(KT::Met { uid: fresh_num(counter) }), - bod: Box::new(KT::Met { uid: fresh_num(counter) }), - }; - KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) } - } - _ => todo!(), - } - } - Term::Var { nam } => KT::Ref { nam: nam.to_string() }, - Term::Let { pat, val, nxt } => match pat.as_ref() { - Pattern::Var(nam) => KT::Let { - nam: bind_to_kind(nam), - bod: Box::new(val.to_kindc(counter, book, src)), - nxt: Box::new(nxt.to_kindc(counter, book, src)), - }, - Pattern::Fan(FanKind::Tup, Tag::Static, els) => { - assert!(els.len() < 16); - // First flatten the let to have a single pattern. - let mut nxt = nxt.as_ref().clone(); - let mut binds = vec![]; - for (i, el) in els.iter().enumerate().rev() { - if let Pattern::Var(nam) = el { - binds.push(nam.clone()); - } else { - let new_var = Name::new(format!("let_tup__{i}")); - nxt = Term::Let { - pat: Box::new(el.clone()), - val: Box::new(Term::Var { nam: new_var.clone() }), - nxt: Box::new(nxt), - }; - binds.push(Some(new_var)); - } - } - binds.reverse(); - - // Then convert native tuple let terms into matches on adt tuples. - let term = Term::Mat { - bnd: None, - arg: val.clone(), - with_bnd: vec![], - with_arg: vec![], - arms: vec![(Some(Name::new(format!("kindc__tup{}/new", els.len()))), binds, nxt)], - }; - term.to_kindc(counter, book, src) - } - Pattern::Chn(_) => unreachable!(), - _ => todo!(), - }, - Term::Use { nam, val, nxt } => KT::Use { - nam: bind_to_kind(nam), - bod: Box::new(val.to_kindc(counter, book, src)), - nxt: Box::new(nxt.to_kindc(counter, book, src)), - }, - Term::App { tag, fun, arg } => { - if tag != &Tag::Static { - todo!(); - } - KT::App { - fun: Box::new(fun.to_kindc(counter, book, src)), - arg: Box::new(arg.to_kindc(counter, book, src)), - } - } - Term::Num { val } => match val { - Num::U24(val) => KT::UNum { val: *val }, - Num::I24(val) => KT::INum { val: *val }, - Num::F24(val) => KT::FNum { val: *val }, - }, - Term::Oper { opr, fst, snd } => KT::Op2 { - op: opr.to_kindc(), - fst: Box::new(fst.to_kindc(counter, book, src)), - snd: Box::new(snd.to_kindc(counter, book, src)), - }, - Term::Mat { bnd: _, arg, with_bnd: _, with_arg: _, arms } => { - // Note: 'with' arguments should be gone by now - // Match becomes: - // λ type vars - // λ P - // λ case__ctrs - // λ __matched - // (~__matched P case__ctrs) : - // ∀(type vars: _) - // ∀(P: ∀(x: (Type vars)) *) - // ∀(case__ctrs: ∀(fields: field types) (P (ctr vars fields))) - // ∀(__motive: (Type vars)) - // (P __motive) - // Then apply the matched value: - // let arg__ = arg; - // (__match _vars _P arms arg__) - let ctr_nam = arms[0].0.as_ref().unwrap(); - let adt_nam = book.ctrs.get(ctr_nam).unwrap(); - let adt = book.adts.get(adt_nam).unwrap(); - let on_ctr_nams = adt.ctrs.keys().map(|ctr_nam| format!("case__{ctr_nam}")); - let type_vars = adt.vars.iter().map(|var| var.to_string()); - - // Match function - let term = KT::Ref { nam: "matched__".to_string() }; - let term = KT::Ins { bod: Box::new(term) }; - let term = term.fold_vars(["P".to_string()]); - let term = term.fold_vars(on_ctr_nams.clone()); - let term = term.rfold_lams(["matched__".to_string()]); - let term = term.rfold_lams(on_ctr_nams); - let term = term.rfold_lams(["P".to_string()]); - let term = term.rfold_lams(type_vars.clone()); - - // Type of the match function - let typ = KT::Ref { nam: "P".to_string() }; - let typ = typ.fold_vars(["motive__".to_string()]); - let motive_type = KT::Ref { nam: adt.name.to_string() }; - let motive_type = motive_type.fold_vars(type_vars.clone()); - let typ = KT::All { nam: "motive__".to_string(), typ: Box::new(motive_type), bod: Box::new(typ) }; - let typ = adt.ctrs.keys().rfold(typ, |typ, ctr_nam| { - let ctr = adt.ctrs.get(ctr_nam).unwrap(); - let forall_type = KT::Ref { nam: ctr_nam.to_string() }; - let forall_type = forall_type.fold_vars(type_vars.clone()); - let forall_type = forall_type.fold_vars(ctr.fields.iter().map(|field| field.nam.to_string())); - let forall_type = - KT::App { fun: Box::new(KT::Ref { nam: "P".to_string() }), arg: Box::new(forall_type) }; - let forall_type = ctr.fields.iter().rfold(forall_type, |typ, field| KT::All { - nam: field.nam.to_string(), - typ: Box::new(field.typ.to_kindc(counter, src)), - bod: Box::new(typ), - }); - KT::All { nam: format!("case__{ctr_nam}"), typ: Box::new(forall_type), bod: Box::new(typ) } - }); - let p_type = KT::All { - nam: "x".to_string(), - typ: Box::new(KT::Ref { nam: adt_nam.to_string() }.fold_vars(type_vars.clone())), - bod: Box::new(KT::Set), - }; - let typ = KT::All { nam: "P".to_string(), typ: Box::new(p_type), bod: Box::new(typ) }; - let typ = type_vars.clone().rfold(typ, |typ, var| KT::All { - nam: var.to_string(), - typ: Box::new(KT::Met { uid: fresh_num(counter) }), - bod: Box::new(typ), - }); - - // Apply the arms to the match function - let term = KT::Ann { chk: false, bod: Box::new(term), typ: Box::new(typ) }; - let term = type_vars.fold(term, |term, _| KT::App { - fun: Box::new(term), - arg: Box::new(KT::Met { uid: fresh_num(counter) }), - }); - let term = KT::App { fun: Box::new(term), arg: Box::new(KT::Met { uid: fresh_num(counter) }) }; - let term = arms.iter().fold(term, |term, arm| { - let arg = arm.2.to_kindc(counter, book, src); - let arg = - arm.1.iter().rfold(arg, |acc, nam| KT::Lam { nam: bind_to_kind(nam), bod: Box::new(acc) }); - KT::App { fun: Box::new(term), arg: Box::new(arg) } - }); - - // Apply the argument to the match term - let term = KT::App { fun: Box::new(term), arg: Box::new(KT::Ref { nam: "arg__".to_string() }) }; - KT::Let { - nam: "arg__".to_string(), - bod: Box::new(arg.to_kindc(counter, book, src)), - nxt: Box::new(term), - } - } - Term::Swt { bnd: _, arg, with_bnd: _, with_arg: _, pred, arms } => { - let bind = format!("swi__{}", fresh_num(counter)); - KT::Swi { - bind: bind.clone(), - arg: Box::new(arg.to_kindc(counter, book, src)), - zero: Box::new(arms[0].to_kindc(counter, book, src)), - succ: Box::new(KT::Use { - nam: pred.as_ref().unwrap().to_string(), - bod: Box::new(KT::Ref { nam: format!("{}-1", bind) }), - nxt: Box::new(arms[1].to_kindc(counter, book, src)), - }), - motive: Box::new(KT::Met { uid: fresh_num(counter) }), - } - } - Term::Ref { nam } => { - // For function calls, we need to add a meta variable for each - // parametric type variable of the function. - let typ = if let Some(def) = book.defs.get(nam) { - &def.typ - } else if let Some(def) = book.hvm_defs.get(nam) { - &def.typ - } else { - unreachable!() - }; - typ.leading_type_vars().into_iter().fold(KT::Ref { nam: nam.to_string() }, |acc, _nam| KT::App { - fun: Box::new(acc), - arg: Box::new(KT::Met { uid: fresh_num(counter) }), - }) - } - - Term::Fan { fan: FanKind::Tup, tag: Tag::Static, els } => { - // Native tuples are converted to a tuple adt constructor. - assert!(els.len() < 16); - let term = Term::Ref { nam: Name::new(format!("kindc__tup{}/new", els.len())) }; - let term = els.iter().fold(term, |term, el| Term::App { - tag: Tag::Static, - fun: Box::new(term), - arg: Box::new(el.clone()), - }); - term.to_kindc(counter, book, src) - } - Term::Fan { fan: _, tag: _, els: _ } => todo!(), - Term::Era => KT::Ref { nam: "kindc__none/new".to_string() }, - - Term::Link { nam: _ } => unreachable!(), - Term::With { .. } => unreachable!(), - Term::Str { .. } => unreachable!(), - Term::Nat { .. } => unreachable!(), - Term::List { .. } => unreachable!(), - Term::Fold { .. } => unreachable!(), - Term::Bend { .. } => unreachable!(), - Term::Open { .. } => unreachable!(), - Term::Def { .. } => unreachable!(), - Term::Ask { .. } => unreachable!(), - Term::Err => unreachable!(), - }; - KT::Src { src, bod: Box::new(term) } - }) - } -} - -impl Type { - fn to_kindc(&self, counter: &mut u64, src: u64) -> KT { - let kt = maybe_grow(|| match self { - Type::Hole => KT::Met { uid: fresh_num(counter) }, - Type::Var(nam) => KT::Ref { nam: nam.to_string() }, - Type::All(nam, bod) => { - KT::All { nam: nam.to_string(), typ: Box::new(KT::Set), bod: Box::new(bod.to_kindc(counter, src)) } - } - Type::Arr(lft, rgt) => KT::All { - nam: "arr__".to_string(), - typ: Box::new(lft.to_kindc(counter, src)), - bod: Box::new(rgt.to_kindc(counter, src)), - }, - Type::Ctr(nam, args) => args.iter().fold(KT::Ref { nam: nam.to_string() }, |acc, arg| KT::App { - fun: Box::new(acc), - arg: Box::new(arg.to_kindc(counter, src)), - }), - Type::U24 => KT::U48, - // TODO: Implement i24 and f24 in kindc - Type::I24 => KT::I48, - Type::F24 => KT::F48, - Type::Any => KT::Any, - Type::Tup(els) => els.iter().fold(KT::Ref { nam: format!("kindc__tup{}", els.len()) }, |typ, el| { - KT::App { fun: Box::new(typ), arg: Box::new(el.to_kindc(counter, src)) } - }), - Type::None => KT::Ref { nam: "kindc__none".to_string() }, - }); - KT::Src { src, bod: Box::new(kt) } - } - - fn leading_type_vars(&self) -> Vec { - fn go(typ: &Type, acc: &mut Vec) { - maybe_grow(|| { - if let Type::All(nam, bod) = typ { - acc.push(nam.to_string()); - go(bod, acc); - } else { - // Not a leading type variable, stop - } - }) - } - let mut vars = vec![]; - go(self, &mut vars); - vars - } -} - -impl Op { - pub fn to_kindc(&self) -> Oper { - match self { - Op::ADD => Oper::ADD, - Op::SUB => Oper::SUB, - Op::MUL => Oper::MUL, - Op::DIV => Oper::DIV, - Op::REM => Oper::MOD, - Op::EQ => Oper::EQ, - Op::NEQ => Oper::NE, - Op::LT => Oper::LT, - Op::GT => Oper::GT, - Op::LE => Oper::LTE, - Op::GE => Oper::GTE, - Op::AND => Oper::AND, - Op::OR => Oper::OR, - Op::XOR => Oper::XOR, - Op::SHL => Oper::LSH, - Op::SHR => Oper::RSH, - // TODO: Implement pow in kindc - Op::POW => Oper::MUL, - } - } -} - -impl KT { - pub fn to_bend_type(&self) -> Type { - maybe_grow(|| match self { - // Forall types -> type variable, erase the All - // Forall value of specific type -> function argument, convert into an arrow - KT::All { nam: _, typ, bod } => match typ.as_ref() { - KT::Set => bod.to_bend_type(), - _ => Type::Arr(Box::new(typ.to_bend_type()), Box::new(bod.to_bend_type())), - }, - KT::App { .. } => self.app_to_bend_type(vec![]), - // TODO: This does not convert nullary constructors correctly, - // but since we only use this for printing, it's not currently a problem. - // Nullary constructors become variables instead. - KT::Ref { nam } => Type::Var(Name::new(nam)), - KT::Any => Type::Any, - KT::U48 => Type::U24, - KT::I48 => Type::I24, - KT::F48 => Type::F24, - KT::Slf { .. } => todo!(), - KT::Ins { .. } => todo!(), - KT::Let { .. } => todo!(), - KT::Use { .. } => todo!(), - KT::Set => todo!(), - // TODO: Should use an actual unknown type here - KT::Met { .. } => Type::Var(Name::new("_")), - KT::Src { .. } => todo!(), - KT::Ann { .. } => todo!(), - KT::UNum { .. } => unreachable!(), - KT::INum { .. } => unreachable!(), - KT::FNum { .. } => unreachable!(), - KT::Op2 { .. } => unreachable!(), - KT::Swi { .. } => unreachable!(), - KT::Hol { .. } => unreachable!(), - KT::Txt { .. } => unreachable!(), - KT::Lam { .. } => unreachable!(), - KT::Nat { .. } => unreachable!(), - }) - } - - pub fn app_to_bend_type(&self, mut args: Vec) -> Type { - maybe_grow(|| match self { - KT::App { fun, arg } => { - args.push(arg.to_bend_type()); - fun.app_to_bend_type(args) - } - KT::Ref { nam } => { - args.reverse(); - Type::Ctr(Name::new(nam), args) - } - _ => unreachable!(), - }) - } - - pub fn fold_vars(self, vars: impl IntoIterator) -> Self { - vars - .into_iter() - .fold(self, |acc, var| KT::App { fun: Box::new(acc), arg: Box::new(KT::Ref { nam: var }) }) - } - - pub fn rfold_lams(self, lams: I) -> Self - where - I: IntoIterator, - I::IntoIter: DoubleEndedIterator, - { - lams.into_iter().rfold(self, |acc, lam| KT::Lam { nam: lam, bod: Box::new(acc) }) - } -} - -fn bind_to_kind(nam: &Option) -> String { - match nam { - Some(nam) => nam.to_string(), - None => "era__".to_string(), - } -} - -fn fresh_num(val: &mut u64) -> u64 { - let fresh = *val; - *val += 1; - fresh -} - -fn name_from_src(src: u64, kind_book: &KindCBook) -> &str { - if let Some(name) = kind_book.get(&src) { - name.0.as_str() - } else { - "unknown" - } -} -/* Stringify to KindC */ - -impl fmt::Display for KT { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - maybe_grow(|| match self { - KT::All { nam, typ, bod } => write!(f, "∀({nam}: {typ}) {bod}"), - KT::Lam { nam, bod } => write!(f, "λ{nam} {bod}"), - KT::App { fun, arg } => write!(f, "({fun} {arg})"), - KT::Ann { chk: false, bod, typ } => write!(f, "{{{bod}: {typ}}}"), - KT::Ann { chk: true, bod, typ } => write!(f, "{{{bod}:: {typ}}}"), - KT::Slf { nam, typ, bod } => write!(f, "$({nam}: {typ}) {bod}"), - KT::Ins { bod } => write!(f, "~{bod}"), - KT::Ref { nam } => write!(f, "{nam}"), - KT::Let { nam, bod, nxt } => write!(f, "let {nam} = {bod}; {nxt}"), - KT::Use { nam, bod, nxt } => write!(f, "use {nam} = {bod}; {nxt}"), - KT::Set => write!(f, "*"), - KT::Any => write!(f, "Any"), - KT::U48 => write!(f, "U48"), - KT::I48 => write!(f, "I48"), - KT::F48 => write!(f, "F48"), - KT::UNum { val } => write!(f, "{val}"), - KT::INum { val } => { - let sign = if *val >= 0 { "+" } else { "-" }; - write!(f, "{}{}", sign, val.abs()) - } - KT::FNum { val } => { - write!(f, "{val:.?}") - } - KT::Op2 { op, fst, snd } => write!(f, "({op} {fst} {snd})"), - KT::Swi { bind, arg, zero, succ, motive } => { - write!(f, "switch {bind} = {arg} {{ 0: {zero} _: {succ} }}: {motive}") - } - KT::Hol { nam, ctx } => { - write!(f, "?{nam}")?; - if !ctx.is_empty() { - write!(f, " [")?; - for (i, term) in ctx.iter().enumerate() { - if i != 0 { - write!(f, ", ")?; - } - write!(f, "{term}")?; - } - write!(f, "]")?; - } - Ok(()) - } - KT::Met { uid } => write!(f, "_{uid}"), - KT::Src { src, bod } => write!(f, "!{src} {bod}"), - KT::Txt { val } => write!(f, "\"{val}\""), - KT::Nat { val } => write!(f, "#{val}"), - }) - } -} - -impl fmt::Display for Oper { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Oper::ADD => write!(f, "+"), - Oper::SUB => write!(f, "-"), - Oper::MUL => write!(f, "*"), - Oper::DIV => write!(f, "/"), - Oper::MOD => write!(f, "%"), - Oper::EQ => write!(f, "=="), - Oper::NE => write!(f, "!="), - Oper::LT => write!(f, "<"), - Oper::GT => write!(f, ">"), - Oper::LTE => write!(f, "<="), - Oper::GTE => write!(f, ">="), - Oper::AND => write!(f, "&"), - Oper::OR => write!(f, "|"), - Oper::XOR => write!(f, "^"), - Oper::LSH => write!(f, "<<"), - Oper::RSH => write!(f, ">>"), - } - } -} - -/* Parse from KindC */ -enum KindCErr { - ExpectedFound { expected: KT, found: KT, val: KT, src: u64 }, - ExpectedFun { found: KT, val: KT, src: u64 }, - ExpectedSelf { found: KT, val: KT, src: u64 }, - UndefinedRef { nam: String, src: u64 }, - CantInferLam { val: KT, src: u64 }, - CantInferHol { val: KT, src: u64 }, - CantInferMet { val: KT, src: u64 }, - CantInferVar { val: KT, src: u64 }, -} - -new_parser!(KindCParser); - -impl KindCParser<'_> { - fn parse_result(&mut self) -> Result, String> { - let mut errs = vec![]; - self.skip_trivia(); - while !self.is_eof() { - errs.push(self.parse_err()?); - self.skip_trivia(); - } - Ok(errs) - } - - fn parse_err(&mut self) -> Result { - self.consume("#error{")?; - self.skip_trivia(); - if self.starts_with("?function") { - self.consume("?function")?; - let found = self.parse_term()?; - let val = self.parse_term()?; - let src = self.parse_u64()?; - self.consume("}")?; - Ok(KindCErr::ExpectedFun { found, val, src }) - } else if self.starts_with("?self-type") { - self.consume("?self-type")?; - let found = self.parse_term()?; - let val = self.parse_term()?; - let src = self.parse_u64()?; - self.consume("}")?; - Ok(KindCErr::ExpectedSelf { found, val, src }) - } else if self.starts_with("?undefined_reference") { - self.consume("?undefined_reference")?; - self.consume("?unknown_type")?; - let nam = self.parse_name()?; - let src = self.parse_u64()?; - self.consume("}")?; - Ok(KindCErr::UndefinedRef { nam, src }) - } else if self.starts_with("?type_annotation") { - self.consume("?type_annotation")?; - if self.starts_with("?untyped_lambda") { - self.consume("?untyped_lambda")?; - let val = self.parse_term()?; - let src = self.parse_u64()?; - self.consume("}")?; - Ok(KindCErr::CantInferLam { val, src }) - } else if self.starts_with("?untyped_hole") { - self.consume("?untyped_hole")?; - let val = self.parse_term()?; - let src = self.parse_u64()?; - self.consume("}")?; - Ok(KindCErr::CantInferHol { val, src }) - } else if self.starts_with("?untyped_meta") { - self.consume("?untyped_meta")?; - let val = self.parse_term()?; - let src = self.parse_u64()?; - self.consume("}")?; - Ok(KindCErr::CantInferMet { val, src }) - } else if self.starts_with("?untyped_variable") { - self.consume("?untyped_variable")?; - let val = self.parse_term()?; - let src = self.parse_u64()?; - self.consume("}")?; - Ok(KindCErr::CantInferVar { val, src }) - } else { - unreachable!() - } - } else { - let expected = self.parse_term()?; - let found = self.parse_term()?; - let val = self.parse_term()?; - let src = self.parse_u64()?; - self.consume("}")?; - Ok(KindCErr::ExpectedFound { expected, found, val, src }) - } - } - - fn parse_term(&mut self) -> Result { - maybe_grow(|| { - self.skip_trivia(); - if self.starts_with("∀") { - self.parse_all() - } else if self.starts_with("λ") { - self.parse_lam() - } else if self.starts_with("(") { - self.advance_one(); - self.skip_trivia(); - if let Some(c) = self.peek_one() { - if c == '_' { - self.parse_met() - } else if "+-*/%=!<>&|^".contains(c) { - self.parse_op2() - } else { - self.parse_app() - } - } else { - self.parse_app() - } - } else if self.starts_with("{") { - self.parse_ann() - } else if self.starts_with("$(") { - self.parse_slf() - } else if self.starts_with("~") { - self.parse_ins() - } else if self.starts_with("let") { - self.parse_let() - } else if self.starts_with("use") { - self.parse_use() - } else if self.starts_with("*") { - self.parse_set() - } else if self.starts_with("Any") { - self.parse_any() - } else if self.starts_with("U48") { - self.parse_u48() - } else if self.starts_with("I48") { - self.parse_i48() - } else if self.starts_with("F48") { - self.parse_f48() - } else if self.starts_with("#") { - self.parse_nat() - } else if self.starts_with("switch") { - self.parse_swi() - } else if self.starts_with("\"") { - self.parse_txt() - } else if self.starts_with("!") { - self.parse_src() - } else if self.starts_with("?") { - self.parse_hol() - } else if let Some(c) = self.peek_one() { - if "0123456789-+".contains(c) { - self.parse_num() - } else { - self.parse_ref() - } - } else { - self.parse_ref() - } - }) - } - - fn parse_all(&mut self) -> Result { - self.consume("∀")?; - self.consume("(")?; - let nam = self.parse_name()?; - self.consume(":")?; - let typ = Box::new(self.parse_term()?); - self.consume(")")?; - let bod = Box::new(self.parse_term()?); - Ok(KT::All { nam, typ, bod }) - } - - fn parse_lam(&mut self) -> Result { - self.consume("λ")?; - let nam = self.parse_name()?; - let bod = Box::new(self.parse_term()?); - Ok(KT::Lam { nam, bod }) - } - - fn parse_app(&mut self) -> Result { - // Parens already consumed - let fun = Box::new(self.parse_term()?); - let arg = Box::new(self.parse_term()?); - self.consume(")")?; - Ok(KT::App { fun, arg }) - } - - fn parse_op2(&mut self) -> Result { - // Parens already consumed - let op = self.parse_oper()?; - let fst = Box::new(self.parse_term()?); - let snd = Box::new(self.parse_term()?); - self.consume(")")?; - Ok(KT::Op2 { op, fst, snd }) - } - - fn parse_met(&mut self) -> Result { - // Parens already consumed - self.consume("_")?; - self.skip_trivia(); - while !self.starts_with(")") { - // TODO: Should we do something with the spine? - self.parse_term()?; - self.skip_trivia(); - } - self.consume(")")?; - // TODO: No UID is returned, should I add something here? - Ok(KT::Met { uid: 0 }) - } - - fn parse_ann(&mut self) -> Result { - self.consume("{")?; - let bod = Box::new(self.parse_term()?); - self.consume(":")?; - let chk = if self.starts_with(":") { - self.advance_one(); - false - } else { - true - }; - let typ = Box::new(self.parse_term()?); - self.consume("}")?; - Ok(KT::Ann { chk, bod, typ }) - } - - fn parse_slf(&mut self) -> Result { - self.consume("$(")?; - let nam = self.parse_name()?; - self.consume(":")?; - let typ = Box::new(self.parse_term()?); - self.consume(")")?; - let bod = Box::new(self.parse_term()?); - Ok(KT::Slf { nam, typ, bod }) - } - - fn parse_ins(&mut self) -> Result { - self.consume("~")?; - let bod = Box::new(self.parse_term()?); - Ok(KT::Ins { bod }) - } - - fn parse_let(&mut self) -> Result { - self.consume("let")?; - let nam = self.parse_name()?; - self.consume("=")?; - let bod = Box::new(self.parse_term()?); - self.consume(";")?; - let nxt = Box::new(self.parse_term()?); - Ok(KT::Let { nam, bod, nxt }) - } - - fn parse_use(&mut self) -> Result { - self.consume("use")?; - let nam = self.parse_name()?; - self.consume("=")?; - let bod = Box::new(self.parse_term()?); - self.consume(";")?; - let nxt = Box::new(self.parse_term()?); - Ok(KT::Use { nam, bod, nxt }) - } - - fn parse_set(&mut self) -> Result { - self.consume("*")?; - Ok(KT::Set) - } - - fn parse_any(&mut self) -> Result { - self.consume("Any")?; - Ok(KT::Any) - } - - fn parse_u48(&mut self) -> Result { - self.consume("U48")?; - Ok(KT::U48) - } - - fn parse_i48(&mut self) -> Result { - self.consume("I48")?; - Ok(KT::I48) - } - - fn parse_f48(&mut self) -> Result { - self.consume("F48")?; - Ok(KT::F48) - } - - fn parse_num(&mut self) -> Result { - let sgn = if let Some('+') = self.peek_one() { - self.advance_one(); - Some(1) - } else if let Some('-') = self.peek_one() { - self.advance_one(); - Some(-1) - } else { - None - }; - - let int = self.parse_u64()?; - - let frac = if let Some('.') = self.peek_one() { - self.advance_one(); - let ini_idx = *self.index(); - let frac = self.parse_u64()?; - let end_idx = *self.index(); - let frac = frac as f32 / 10f32.powi((end_idx - ini_idx) as i32); - Some(frac) - } else { - None - }; - - if let Some(frac) = frac { - let sgn = sgn.unwrap_or(1); - Ok(KT::FNum { val: sgn as f32 * (int as f32 + frac) }) - } else if let Some(sgn) = sgn { - let int = sgn * int as i32; - Ok(KT::INum { val: int }) - } else { - Ok(KT::UNum { val: int as u32 }) - } - } - - fn parse_swi(&mut self) -> Result { - self.consume("switch")?; - let bind = self.parse_name()?; - self.consume("=")?; - let arg = Box::new(self.parse_term()?); - self.consume("{")?; - self.consume("0:")?; - let zero = Box::new(self.parse_term()?); - self.consume("_:")?; - let succ = Box::new(self.parse_term()?); - self.consume("}")?; - self.consume(":")?; - let motive = Box::new(self.parse_term()?); - Ok(KT::Swi { bind, arg, zero, succ, motive }) - } - - fn parse_txt(&mut self) -> Result { - let val = self.parse_quoted_string()?; - Ok(KT::Txt { val }) - } - - fn parse_nat(&mut self) -> Result { - self.consume("#")?; - let val = self.parse_u64()? as u32; - Ok(KT::Nat { val }) - } - - fn parse_hol(&mut self) -> Result { - self.consume("?")?; - let nam = self.parse_name()?; - Ok(KT::Hol { nam, ctx: vec![] }) - } - - fn parse_src(&mut self) -> Result { - self.consume("!")?; - let src = self.parse_u64()?; - let bod = Box::new(self.parse_term()?); - Ok(KT::Src { src, bod }) - } - - fn parse_ref(&mut self) -> Result { - let nam = self.parse_name()?; - Ok(KT::Ref { nam }) - } - - fn parse_oper(&mut self) -> Result { - let op = self.take_while(|c| "+-*/%=!<>&|^".contains(c)); - match op { - "+" => Ok(Oper::ADD), - "-" => Ok(Oper::SUB), - "*" => Ok(Oper::MUL), - "/" => Ok(Oper::DIV), - "%" => Ok(Oper::MOD), - "==" => Ok(Oper::EQ), - "!=" => Ok(Oper::NE), - "<" => Ok(Oper::LT), - ">" => Ok(Oper::GT), - "<=" => Ok(Oper::LTE), - ">=" => Ok(Oper::GTE), - "&" => Ok(Oper::AND), - "|" => Ok(Oper::OR), - "^" => Ok(Oper::XOR), - "<<" => Ok(Oper::LSH), - ">>" => Ok(Oper::RSH), - _ => Err(format!("Invalid operator: {}", op)), - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 8d21d6f88..7236fc95e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,6 +119,7 @@ pub fn desugar_book( // Auto match linearization ctx.book.make_var_names_unique(); ctx.book.desugar_use(); + match opts.linearize_matches { OptLevel::Disabled => (), OptLevel::Alt => ctx.book.linearize_match_binds(), @@ -170,8 +171,7 @@ pub fn desugar_book( pub fn type_check_book(ctx: &mut Ctx) -> Result<(), Diagnostics> { let old_book = std::mem::replace(ctx.book, ctx.book.clone()); - ctx.make_native_defs(); - ctx.book.encode_adts(AdtEncoding::Scott); + //ctx.make_native_defs(); ctx.resolve_type_ctrs()?; let res = ctx.type_check(); *ctx.book = old_book; From 4d8739f88f93b9feb32f7c5060e23f86240112e2 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Fri, 23 Aug 2024 20:25:52 +0200 Subject: [PATCH 08/29] Implement type inference for the remaining types + checking --- src/fun/check/type_check.rs | 456 ++++++++++++++++++------- src/fun/display.rs | 6 +- src/fun/mod.rs | 17 +- src/fun/transform/resolve_type_ctrs.rs | 9 +- 4 files changed, 357 insertions(+), 131 deletions(-) diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index 710cfcb44..e7228c7a0 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -1,29 +1,26 @@ -//! Hindley-Milner-like type system. +//! Gradual Hindley-Milner-like type system. //! //! Based on https://github.com/developedby/algorithm-w-rs //! and https://github.com/mgrabmueller/AlgorithmW. use crate::{ - fun::{Adt, Book, Constructors, Ctx, MatchRule, Name, Pattern, Tag, Term, Type}, + fun::{num_to_name, Adt, Book, Ctx, FanKind, MatchRule, Name, Num, Pattern, Tag, Term, Type}, maybe_grow, }; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; impl Ctx<'_> { pub fn type_check(&mut self) -> Result<(), String> { - eprintln!("book:\n{}", self.book); - eprintln!("ctrs:\n{:?}", self.book.adts); - let types = infer_book(self.book)?; - let types = refresh_vars(types); + let mut types = infer_book(self.book)?; + refresh_vars_book(&mut types); - for (nam, typ) in types.0 { + for (nam, typ) in types { eprintln!("{nam}: {typ}"); } Ok(()) } } -#[derive(Default)] -pub struct ProgramTypes(BTreeMap); +pub type ProgramTypes = BTreeMap; /// A type scheme, aka a polymorphic type. #[derive(Clone)] @@ -49,16 +46,11 @@ struct RecGroups(Vec>); impl Type { fn free_type_vars(&self) -> BTreeSet { maybe_grow(|| match self { - Type::Any => todo!(), - Type::Hole => todo!(), Type::Var(x) => BTreeSet::from([x.clone()]), + Type::Ctr(_, ts) | Type::Tup(ts) => ts.iter().flat_map(|t| t.free_type_vars()).collect(), Type::Arr(t1, t2) => t1.free_type_vars().union(&t2.free_type_vars()).cloned().collect(), - Type::Ctr(_, ts) => ts.iter().flat_map(|t| t.free_type_vars()).collect(), - Type::Tup(_) => todo!(), - Type::U24 => todo!(), - Type::F24 => todo!(), - Type::I24 => todo!(), - Type::None => todo!(), + Type::Number(t) | Type::Integer(t) => t.free_type_vars(), + Type::U24 | Type::F24 | Type::I24 | Type::None | Type::Any | Type::Hole => BTreeSet::new(), }) } @@ -68,15 +60,12 @@ impl Type { Some(new) => new.clone(), None => self.clone(), }, - Type::Arr(t1, t2) => Type::Arr(Box::new(t1.subst(subst)), Box::new(t2.subst(subst))), Type::Ctr(name, ts) => Type::Ctr(name.clone(), ts.iter().map(|t| t.subst(subst)).collect()), - Type::Any => todo!(), - Type::Hole => todo!(), - Type::Tup(_) => todo!(), - Type::U24 => todo!(), - Type::F24 => todo!(), - Type::I24 => todo!(), - Type::None => todo!(), + Type::Arr(t1, t2) => Type::Arr(Box::new(t1.subst(subst)), Box::new(t2.subst(subst))), + Type::Tup(els) => Type::Tup(els.iter().map(|t| t.subst(subst)).collect()), + Type::Number(t) => Type::Number(Box::new(t.subst(subst))), + Type::Integer(t) => Type::Integer(Box::new(t.subst(subst))), + t @ (Type::U24 | Type::F24 | Type::I24 | Type::None | Type::Any | Type::Hole) => t.clone(), }) } @@ -151,7 +140,7 @@ impl TypeEnv { impl VarGen { fn fresh(&mut self) -> Type { - let x = format!("a{}", self.0); + let x = num_to_name(self.0 as u64); self.0 += 1; Type::Var(Name::new(x)) } @@ -163,20 +152,20 @@ impl RecGroups { fn collect_dependencies<'a>( term: &'a Term, - ctrs: &'a Constructors, + book: &'a Book, scope: &mut Vec, deps: &mut HashSet<&'a Name>, ) { if let Term::Ref { nam } = term { - if ctrs.contains_key(nam) { - // Don't infer types for constructors + if book.ctrs.contains_key(nam) || book.hvm_defs.contains_key(nam) || !book.defs[nam].check { + // Don't infer types for constructors or unchecked functions } else { deps.insert(nam); } } for (child, binds) in term.children_with_binds() { scope.extend(binds.clone().flatten().cloned()); - collect_dependencies(child, ctrs, scope, deps); + collect_dependencies(child, book, scope, deps); scope.truncate(scope.len() - binds.flatten().count()); } } @@ -234,7 +223,7 @@ impl RecGroups { continue; } let mut fn_deps = Default::default(); - collect_dependencies(&def.rule().body, &book.ctrs, &mut vec![], &mut fn_deps); + collect_dependencies(&def.rule().body, book, &mut vec![], &mut fn_deps); deps.insert(name, fn_deps); } @@ -255,7 +244,7 @@ impl RecGroups { } fn infer_book(book: &Book) -> Result { - let rec_groups = RecGroups::from_book(book); + let groups = RecGroups::from_book(book); let mut env = TypeEnv::default(); let mut types = BTreeMap::new(); // Add the constructors to the environment. @@ -266,58 +255,78 @@ fn infer_book(book: &Book) -> Result { types.insert(ctr.name.clone(), ctr.typ.clone()); } } + // Add the types of unchecked functions to the environment. + for def in book.defs.values() { + if !def.check { + let scheme = def.typ.generalize(&TypeEnv::default()); + env.0.insert(def.name.clone(), scheme); + types.insert(def.name.clone(), def.typ.clone()); + } + } + // Add the types of hvm functions to the environment. + for def in book.hvm_defs.values() { + let scheme = def.typ.generalize(&TypeEnv::default()); + env.0.insert(def.name.clone(), scheme); + types.insert(def.name.clone(), def.typ.clone()); + } + // Infer the types of regular functions. - let fn_ts = infer_fns(env, book, rec_groups.0.into_iter(), &mut VarGen::default())?; - types.extend(fn_ts); - Ok(ProgramTypes(types)) + for group in &groups.0 { + if let Err(e) = infer_group(&mut env, book, group, &mut types) { + eprintln!("Error while inferring types for group '{group:?}'"); + return Err(e); + } + } + Ok(types) } -fn infer_fns( - mut env: TypeEnv, +fn infer_group( + env: &mut TypeEnv, book: &Book, - mut groups: impl Iterator>, - var_gen: &mut VarGen, -) -> Result, String> { - maybe_grow(|| { - if let Some(group) = groups.next() { - // Generate fresh type variables for each function in the group. - let tvs = group.iter().map(|_| var_gen.fresh()).collect::>(); - for (name, tv) in group.iter().zip(tvs.iter()) { - env.0.insert(name.clone(), Scheme(vec![], tv.clone())); - } + group: &[Name], + types: &mut ProgramTypes, +) -> Result<(), String> { + let var_gen = &mut VarGen::default(); + + // Generate fresh type variables for each function in the group. + let tvs = group.iter().map(|_| var_gen.fresh()).collect::>(); + for (name, tv) in group.iter().zip(tvs.iter()) { + env.0.insert(name.clone(), Scheme(vec![], tv.clone())); + } - // Infer the types of the functions in the group. - let mut ss = vec![]; - let mut ts = vec![]; - for name in &group { - let def = book.defs.get(name).unwrap(); - let (s, t) = infer(&env, book, &def.rule().body, var_gen)?; - ss.push(s); - ts.push(t); - } + // Infer the types of the functions in the group. + let mut ss = vec![]; + let mut inf_ts = vec![]; + let mut exp_ts = vec![]; + for name in group { + let def = book.defs.get(name).unwrap(); + let (s, t) = infer(env, book, &def.rule().body, var_gen)?; + ss.push(s); + inf_ts.push(t); + exp_ts.push(&def.typ); + } - // Unify the inferred body with the corresponding type variable. - let mut group_s = ss.into_iter().fold(Subst::default(), |s, s2| s.compose(&s2)); - for (bod_t, tv) in ts.into_iter().zip(tvs.iter()) { - let s = unify(&tv.subst(&group_s), &bod_t)?; - group_s = s.compose(&group_s); - } + // Unify the inferred body with the corresponding type variable. + let mut s = ss.iter().fold(Subst::default(), |s, s2| s.compose(s2)); + let mut ts = vec![]; + for (bod_t, tv) in inf_ts.into_iter().zip(tvs.iter()) { + let (t, s2) = unify(&tv.subst(&s), &bod_t)?; + ts.push(t); + s = s.compose(&s2); + } + let ts = ts.into_iter().map(|t| t.subst(&s)).collect::>(); - // Generalize the function types - let mut env = env.subst(&group_s); - let final_ts = tvs.into_iter().map(|tv| tv.subst(&group_s)).collect::>(); - for (name, t) in group.iter().zip(final_ts.iter()) { - env.0.insert(name.clone(), t.generalize(&env)); - } + // Specialize against the expected type + for ((name, exp_t), inf_t) in group.iter().zip(exp_ts.iter()).zip(ts.iter()) { + types.insert(name.clone(), specialize(inf_t, exp_t)?); + } - let rest_ts = infer_fns(env, book, groups, var_gen)?; - let mut program_types = group.into_iter().zip(final_ts).collect::>(); - program_types.extend(rest_ts); - Ok(program_types) - } else { - Ok(vec![]) - } - }) + // Generalize the function types + let mut env = env.subst(&s); + for name in group { + env.0.insert(name.clone(), types[name].generalize(&env)); + } + Ok(()) } /// Infer the type of a term in the given environment. @@ -349,7 +358,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let (s1, fun_t) = infer(env, book, fun, var_gen)?; let (s2, arg_t) = infer(&env.subst(&s1), book, arg, var_gen)?; let app_t = var_gen.fresh(); - let s3 = unify(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())))?; + let (_, s3) = unify(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())))?; Ok((s3.compose(&s2).compose(&s1), app_t.subst(&s3))) } Term::Let { pat, val, nxt } => match pat.as_ref() { @@ -360,8 +369,64 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let (s2, nxt_t) = infer(&nxt_env, book, nxt, var_gen)?; Ok((s2.compose(&s1), nxt_t)) } + Pattern::Fan(FanKind::Tup, Tag::Static, els) => { + // Tuple elimination behaves like pattern matching. + // Variables from tuple patterns don't get generalized. + let (s1, t1) = infer(env, book, val, var_gen)?; + + let exp_tvs = els.iter().map(|_| var_gen.fresh()).collect::>(); + let tup_t = Type::Tup(exp_tvs.clone()); + let (_, s2) = unify(&t1.subst(&s1), &tup_t)?; + let s = s2.compose(&s1); + let mut env = env.subst(&s); + + let mut vars = vec![]; + let mut inf_tvs = vec![]; + for el in els { + if let Pattern::Var(nam) = el { + vars.push(nam); + inf_tvs.push(var_gen.fresh()); + } else { + unreachable!("Nested patterns should've been removed in earlier pass"); + } + } + // + for (var, tv) in vars.iter().zip(inf_tvs.iter()) { + if let Some(var) = var { + env.0.insert(var.clone(), Scheme(vec![], tv.subst(&s2))); + } + } + + let (s3, nxt_t) = infer(&env, book, nxt, var_gen)?; + let inf_ts = inf_tvs.into_iter().map(|tv| tv.subst(&s3)).collect::>(); + let exp_ts = exp_tvs.into_iter().map(|tv| tv.subst(&s)).collect::>(); + let s4 = unify_fields(inf_ts.iter().zip(exp_ts.iter()))?; + Ok((s4.compose(&s3).compose(&s), nxt_t.subst(&s4))) + } + Pattern::Fan(FanKind::Dup, Tag::Auto, els) => { + // We pretend that sups don't exist and dups don't collide. + // All variables must have the same type as the body of the dup. + let (s1, t1) = infer(env, book, val, var_gen)?; + let mut tvs = vec![]; + let mut env = env.subst(&s1); + for el in els { + if let Pattern::Var(nam) = el { + let tv = var_gen.fresh(); + tvs.push(tv.clone()); + if let Some(nam) = nam { + env.0.insert(nam.clone(), Scheme(vec![], tv.clone())); + } + } else { + unreachable!("Nested patterns should've been removed in earlier pass"); + } + } + let (s2, t2) = infer(&env, book, nxt, var_gen)?; + let s = unify_fields(tvs.iter().map(|tv| (&t1, tv)))?; + Ok((s.compose(&s2).compose(&s1), t2.subst(&s))) + } _ => todo!(), }, + Term::Mat { bnd: _, arg, with_bnd: _, with_arg: _, arms } => { // Infer type of the scrutinee let (s1, t1) = infer(env, book, arg, var_gen)?; @@ -372,7 +437,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let (s2, arg_t) = instantiate_adt(adt, var_gen)?; // Unify the inferred type with the expected type - let s3 = unify(&t1.subst(&s2), &arg_t)?; + let (_, s3) = unify(&t1.subst(&s2), &arg_t)?; // For each case, infer the types and unify them all. // Unify the inferred type of the destructured fields with the @@ -381,15 +446,77 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let env = env.subst(&s); infer_match_cases(&env, book, adt, arms, &s, var_gen) } - Term::Lam { tag: _, .. } => todo!(), - Term::App { tag: _, .. } => todo!(), - Term::Link { .. } => todo!(), - Term::Use { .. } => todo!(), - Term::Fan { .. } => todo!(), - Term::Num { .. } => todo!(), - Term::Oper { .. } => todo!(), - Term::Swt { .. } => todo!(), - Term::With { .. } + + Term::Num { val } => { + let t = match val { + Num::U24(_) => Type::U24, + Num::I24(_) => Type::I24, + Num::F24(_) => Type::F24, + }; + Ok((Subst::default(), t)) + } + Term::Oper { opr, fst, snd } => { + let (s1, t1) = infer(env, book, fst, var_gen)?; + let (s2, t2) = infer(&env.subst(&s1), book, snd, var_gen)?; + let (t2, s3) = unify(&t2.subst(&s1), &t1.subst(&s2))?; + let s_args = s3.compose(&s2).compose(&s1); + let t_args = t2.subst(&s_args); + // Check numeric type matches the operation + let tv = var_gen.fresh(); + let (t_opr, s_opr) = match opr { + // Any numeric type + crate::fun::Op::ADD + | crate::fun::Op::SUB + | crate::fun::Op::MUL + | crate::fun::Op::DIV + | crate::fun::Op::EQ + | crate::fun::Op::NEQ + | crate::fun::Op::LT + | crate::fun::Op::GT + | crate::fun::Op::GE + | crate::fun::Op::LE => unify(&t_args, &Type::Number(Box::new(tv.clone())))?, + // Integers + crate::fun::Op::REM + | crate::fun::Op::AND + | crate::fun::Op::OR + | crate::fun::Op::XOR + | crate::fun::Op::SHL + | crate::fun::Op::SHR => unify(&t_args, &Type::Integer(Box::new(tv.clone())))?, + // Floating + crate::fun::Op::POW => unify(&t_args, &Type::F24)?, + }; + Ok((s_opr.compose(&s_args), t_opr.subst(&s_opr))) + } + Term::Swt { bnd: _, arg, with_bnd: _, with_arg: _, pred, arms } => { + debug_assert!(arms.len() == 2); + let (s1, t1) = infer(env, book, arg, var_gen)?; + let (_, s2) = unify(&t1, &Type::U24)?; + let s_arg = s2.compose(&s1); + let mut env = env.subst(&s_arg); + + let (s_zero, t_zero) = infer(&env, book, &arms[0], var_gen)?; + if let Some(pred) = pred { + env.0.insert(pred.clone(), Scheme(vec![], Type::U24)); + } + let (s_succ, t_succ) = infer(&env, book, &arms[1], var_gen)?; + let s_arms = s_succ.compose(&s_zero); + let (t_swt, s_swt) = unify(&t_zero.subst(&s_arms), &t_succ.subst(&s_arms))?; + + Ok((s_swt.compose(&s_arms).compose(&s_arg), t_swt.subst(&s_swt))) + } + + Term::Fan { fan: FanKind::Tup, tag: Tag::Static, els } => { + let res = els.iter().map(|el| infer(env, book, el, var_gen)).collect::, _>>()?; + let (ss, ts): (Vec, Vec) = res.into_iter().unzip(); + let t = Type::Tup(ts); + Ok((ss.into_iter().fold(Subst::default(), |acc, s| acc.compose(&s)), t)) + } + Term::Era => Ok((Subst::default(), Type::None)), + Term::Fan { .. } | Term::Lam { tag: _, .. } | Term::App { tag: _, .. } | Term::Link { .. } => { + unreachable!("'{term}' while type checking. Should never occur in checked functions") + } + Term::Use { .. } + | Term::With { .. } | Term::Ask { .. } | Term::Nat { .. } | Term::Str { .. } @@ -398,8 +525,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul | Term::Bend { .. } | Term::Open { .. } | Term::Def { .. } - | Term::Era - | Term::Err => unreachable!(), + | Term::Err => unreachable!("'{term}' while type checking. Should have been removed in earlier pass"), }) } @@ -439,9 +565,9 @@ fn infer_match_cases( // Recurse and unify with the other arms. let (s_rest, t_rest) = infer_match_cases(env, book, adt, rest, s, var_gen)?; - let final_s = unify(&t1.subst(&s_rest.compose(&s2)), &t_rest)?; + let (t_final, s_final) = unify(&t1.subst(&s_rest.compose(&s2)), &t_rest)?; - Ok((final_s.compose(&s_rest).compose(&s2).compose(&s1).compose(s), t_rest.subst(&final_s))) + Ok((s_final.compose(&s_rest).compose(&s2).compose(&s1).compose(s), t_final.subst(&s_final))) } else { let t = var_gen.fresh().subst(s); Ok((s.clone(), t)) @@ -452,25 +578,25 @@ fn infer_match_cases( fn unify_fields<'a>(ts: impl Iterator) -> Result { let ss = ts.map(|(inf, exp)| unify(inf, exp)).collect::, _>>()?; let mut s = Subst::default(); - for s2 in ss.into_iter().rev() { + for (_, s2) in ss.into_iter().rev() { s = s.compose(&s2); } Ok(s) } -fn unify(t1: &Type, t2: &Type) -> Result { +fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { maybe_grow(|| match (t1, t2) { (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { - let s1 = unify(l1, l2)?; - let s2 = unify(&r1.subst(&s1), &r2.subst(&s1))?; - Ok(s2.compose(&s1)) + let (t1, s1) = unify(l1, l2)?; + let (t2, s2) = unify(&r1.subst(&s1), &r2.subst(&s1))?; + Ok((Type::Arr(Box::new(t1), Box::new(t2)), s2.compose(&s1))) } (t, Type::Var(x)) | (Type::Var(x), t) => { // Try to bind variable `x` to `t` if let Type::Var(y) = t { if y == x { // Don't bind a variable to itself - return Ok(Subst::default()); + return Ok((t.clone(), Subst::default())); } } // Occurs check @@ -479,31 +605,126 @@ fn unify(t1: &Type, t2: &Type) -> Result { "Cannot unify variable '{x}' with type '{t}' because it occurs as a free variable" )); } - Ok(Subst(BTreeMap::from([(x.clone(), t.clone())]))) + Ok((t.clone(), Subst(BTreeMap::from([(x.clone(), t.clone())])))) } (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { let mut s = Subst::default(); + let mut ts = vec![]; for (t1, t2) in ts1.iter().zip(ts2.iter()) { - s = s.compose(&unify(t1, t2)?); + let (t, s2) = unify(t1, t2)?; + ts.push(t); + s = s.compose(&s2); + } + Ok((Type::Ctr(name1.clone(), ts), s)) + } + (Type::Tup(els1), Type::Tup(els2)) if els1.len() == els2.len() => { + let mut s = Subst::default(); + let mut ts = vec![]; + for (t1, t2) in els1.iter().zip(els2.iter()) { + let (t, s2) = unify(t1, t2)?; + ts.push(t); + s = s.compose(&s2); + } + Ok((Type::Tup(ts), s)) + } + t @ ((Type::U24, Type::U24) + | (Type::F24, Type::F24) + | (Type::I24, Type::I24) + | (Type::None, Type::None)) => Ok((t.0.clone(), Subst::default())), + (Type::Number(t1), Type::Number(t2)) => { + let (t, s) = unify(t1, t2)?; + Ok((Type::Number(Box::new(t)), s)) + } + (Type::Number(tn), Type::Integer(ti)) | (Type::Integer(ti), Type::Number(tn)) => { + let (t, s) = unify(ti, tn)?; + Ok((Type::Integer(Box::new(t)), s)) + } + (Type::Integer(t1), Type::Integer(t2)) => { + let (t, s) = unify(t1, t2)?; + Ok((Type::Integer(Box::new(t)), s)) + } + (Type::Number(t1) | Type::Integer(t1), t2 @ (Type::U24 | Type::I24 | Type::F24)) + | (t2 @ (Type::U24 | Type::I24 | Type::F24), Type::Number(t1) | Type::Integer(t1)) => { + let (t, s) = unify(t1, t2)?; + Ok((t, s)) + } + + (Type::Hole, t) | (t, Type::Hole) => Ok((t.clone(), Subst::default())), + + (Type::Any, t) | (t, Type::Any) => { + let mut s = Subst::default(); + // Recurse to assign variables to Any as well + for child in t.children() { + let (_, s2) = unify(&Type::Any, child)?; + s = s2.compose(&s); } - Ok(s) + Ok((Type::Any, s)) } + _ => Err(format!("Types do not unify: '{t1}' and '{t2}'")), }) } -fn refresh_vars(types: ProgramTypes) -> ProgramTypes { - let mut new_types = BTreeMap::new(); - for (name, mut typ) in types.0 { - refresh_vars_go(&mut typ, &mut BTreeMap::new(), &mut VarGen::default()); - new_types.insert(name, typ); +/// Specializes a type against another type. +/// +/// Errors if the first type is not a superset of the second type. +fn specialize(gen: &Type, spe: &Type) -> Result { + fn merge_specialization(inf: &Type, exp: &Type, s: &mut Subst) -> Result { + maybe_grow(|| match (inf, exp) { + (t, Type::Hole) => Ok(t.clone()), + (inf, Type::Var(x)) => { + if let Some(exp) = s.0.get(x) { + if inf == exp { + Ok(inf.clone()) + } else { + Err(format!("Cannot specialize type '{inf}' with type '{exp}'")) + } + } else { + s.0.insert(x.clone(), inf.clone()); + Ok(inf.clone()) + } + } + (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { + let l = merge_specialization(l1, l2, s)?; + let r = merge_specialization(r1, r2, s)?; + Ok(Type::Arr(Box::new(l), Box::new(r))) + } + (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { + let mut ts = vec![]; + for (t1, t2) in ts1.iter().zip(ts2.iter()) { + let t = merge_specialization(t1, t2, s)?; + ts.push(t); + } + Ok(Type::Ctr(name1.clone(), ts)) + } + (Type::Number(t1), Type::Number(t2)) => Ok(Type::Number(Box::new(merge_specialization(t1, t2, s)?))), + (Type::Integer(t1), Type::Integer(t2)) => Ok(Type::Integer(Box::new(merge_specialization(t1, t2, s)?))), + (Type::Any, Type::Any) + | (Type::U24, Type::U24) + | (Type::F24, Type::F24) + | (Type::I24, Type::I24) + | (Type::None, Type::None) => Ok(inf.clone()), + (_inf, Type::Any) => Ok(Type::Any), + (Type::Any, exp) => Ok(exp.clone()), + _ => Err(format!("Cannot specialize type '{inf}' with type '{exp}'")), + }) + } + + let (t, s) = unify(gen, spe)?; + // Merge the inferred specialization with the expected type. + // This is done to cast to/from `Any` types. + merge_specialization(&t.subst(&s), spe, &mut Subst::default()) +} + +fn refresh_vars_book(types: &mut ProgramTypes) { + for typ in types.values_mut() { + refresh_vars_type(typ, &mut BTreeMap::new(), &mut VarGen::default()); } - ProgramTypes(new_types) } -fn refresh_vars_go(typ: &mut Type, map: &mut BTreeMap, var_gen: &mut VarGen) { - maybe_grow(|| match typ { - Type::Var(x) => { +fn refresh_vars_type(typ: &mut Type, map: &mut BTreeMap, var_gen: &mut VarGen) { + maybe_grow(|| { + if let Type::Var(x) = typ { if let Some(y) = map.get(x) { *typ = y.clone(); } else { @@ -512,21 +733,8 @@ fn refresh_vars_go(typ: &mut Type, map: &mut BTreeMap, var_gen: &mut *typ = y; } } - Type::Arr(t1, t2) => { - refresh_vars_go(t1, map, var_gen); - refresh_vars_go(t2, map, var_gen); - } - Type::Ctr(_, ts) => { - for t in ts { - refresh_vars_go(t, map, var_gen); - } + for child in typ.children_mut() { + refresh_vars_type(child, map, var_gen); } - Type::Any => todo!(), - Type::Hole => todo!(), - Type::Tup(_) => todo!(), - Type::U24 => todo!(), - Type::F24 => todo!(), - Type::I24 => todo!(), - Type::None => todo!(), }) } diff --git a/src/fun/display.rs b/src/fun/display.rs index 5aae90d1b..f44958a82 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -293,7 +293,7 @@ impl Tag { impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { + maybe_grow(|| match self { Type::Hole => write!(f, "_"), Type::Var(nam) => write!(f, "{nam}"), Type::Arr(lft, rgt) => write!(f, "({lft} -> {rgt})"), @@ -304,13 +304,15 @@ impl fmt::Display for Type { write!(f, "({nam} {})", DisplayJoin(|| args.iter(), " ")) } } + Type::Number(t) => write!(f, "(Number {t})"), + Type::Integer(t) => write!(f, "(Integer {t})"), Type::U24 => write!(f, "u24"), Type::I24 => write!(f, "i24"), Type::F24 => write!(f, "f24"), Type::Any => write!(f, "Any"), Type::None => write!(f, "None"), Type::Tup(els) => write!(f, "({})", DisplayJoin(|| els.iter(), ", ")), - } + }) } } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 603962546..3f119f994 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -116,6 +116,8 @@ pub enum Type { F24, I24, None, + Number(Box), + Integer(Box), } /// A pattern matching rule of a definition. @@ -1133,12 +1135,25 @@ impl Type { }) } + pub fn children(&self) -> impl Iterator { + multi_iterator!(ChildrenIter { Zero, One, Two, Vec }); + match self { + Type::Any | Type::None | Type::Hole | Type::I24 | Type::F24 | Type::U24 | Type::Var(_) => { + ChildrenIter::Zero([]) + } + Type::Number(t) | Type::Integer(t) => ChildrenIter::One([t.as_ref()]), + Type::Arr(lft, rgt) => ChildrenIter::Two([lft.as_ref(), rgt.as_ref()]), + Type::Tup(els) | Type::Ctr(_, els) => ChildrenIter::Vec(els), + } + } + pub fn children_mut(&mut self) -> impl Iterator { - multi_iterator!(ChildrenIter { Zero, Two, Vec }); + multi_iterator!(ChildrenIter { Zero, One, Two, Vec }); match self { Type::Any | Type::None | Type::Hole | Type::I24 | Type::F24 | Type::U24 | Type::Var(_) => { ChildrenIter::Zero([]) } + Type::Number(t) | Type::Integer(t) => ChildrenIter::One([t.as_mut()]), Type::Arr(lft, rgt) => ChildrenIter::Two([lft.as_mut(), rgt.as_mut()]), Type::Tup(els) | Type::Ctr(_, els) => ChildrenIter::Vec(els), } diff --git a/src/fun/transform/resolve_type_ctrs.rs b/src/fun/transform/resolve_type_ctrs.rs index 814d4ccea..bde9c712d 100644 --- a/src/fun/transform/resolve_type_ctrs.rs +++ b/src/fun/transform/resolve_type_ctrs.rs @@ -44,15 +44,16 @@ impl Type { arg.resolve_type_ctrs(adts)?; } } - Type::Arr(lft, rgt) => { - lft.resolve_type_ctrs(adts)?; - rgt.resolve_type_ctrs(adts)?; - } Type::Tup(els) => { for el in els { el.resolve_type_ctrs(adts)?; } } + Type::Arr(lft, rgt) => { + lft.resolve_type_ctrs(adts)?; + rgt.resolve_type_ctrs(adts)?; + } + Type::Number(t) | Type::Integer(t) => t.resolve_type_ctrs(adts)?, Type::Any | Type::Hole | Type::None | Type::U24 | Type::I24 | Type::F24 => {} } Ok(()) From 05f57297304b863cd8837fd2002742b926e72f36 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Mon, 26 Aug 2024 21:03:40 +0200 Subject: [PATCH 09/29] Add hole type syntax, fix infer for dup, tup elim and match --- src/fun/builtins.bend | 12 +- src/fun/check/type_check.rs | 337 ++++++++++++++++++++---------------- src/fun/parser.rs | 2 + src/imp/parser.rs | 2 + 4 files changed, 194 insertions(+), 159 deletions(-) diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index b7f328ae5..670055abe 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -223,7 +223,7 @@ Map/map (Map/Node value left right) key f = type IO(T): Done { magic: (u24, u24), expr: T } - Call { magic: (u24, u24), func: String, argm: Any, cont: Any -> IO(T) } + Call { magic: (u24, u24), func: String, argm: Any, cont: Result(Any, IOError(Any)) -> IO(T) } type IOError(T): Type @@ -245,7 +245,7 @@ def IO/MAGIC() -> (u24, u24): def IO/wrap(x: T) -> IO(T): return IO/Done(IO/MAGIC, x) -def IO/bind(a: IO(A), b: ((A -> IO(B)) -> (A -> IO(B))) -> (A -> IO(B))) -> IO(B): +def IO/bind(a: IO(A), b: ((id -> id) -> A -> IO(B))) -> IO(B): match a: case IO/Done: return undefer(b, a.expr) @@ -279,7 +279,9 @@ IO/done_on_err done = done # Maps the result of an IO. def IO/map(io: IO(A), f: A -> B) -> IO(B): - return IO/bind(io, lambda x: x(lambda f, y: IO/Done(IO/MAGIC, f(y)), f)) + with IO: + a <- io + return wrap(f(a)) # Unwraps the `IOError` of the result of an IO, returning the `Inner` variant. # @@ -479,10 +481,10 @@ def IO/DyLib/close(dl: u24) -> IO(None): def defer(val: T) -> (T -> T) -> T: return lambda x: x(val) -def defer_arg(defered: ((A -> B) -> (A -> B)) -> (A -> B), arg: B) -> ((A -> A) -> A): +def defer_arg(defered: (Id -> Id) -> A -> B, arg: B) -> ((Id -> Id) -> A): return lambda x: defered(x, arg) -def undefer(defered: (A -> A) -> A) -> A: +def undefer(defered: (Id -> Id) -> T) -> T: return defered(lambda x: x) # A function that can be used in unreachable code. diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index e7228c7a0..e65febaa4 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -3,15 +3,14 @@ //! Based on https://github.com/developedby/algorithm-w-rs //! and https://github.com/mgrabmueller/AlgorithmW. use crate::{ - fun::{num_to_name, Adt, Book, Ctx, FanKind, MatchRule, Name, Num, Pattern, Tag, Term, Type}, + fun::{num_to_name, Adt, Book, Ctx, FanKind, MatchRule, Name, Num, Op, Pattern, Tag, Term, Type}, maybe_grow, }; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet}; impl Ctx<'_> { pub fn type_check(&mut self) -> Result<(), String> { - let mut types = infer_book(self.book)?; - refresh_vars_book(&mut types); + let types = infer_book(self.book)?; for (nam, typ) in types { eprintln!("{nam}: {typ}"); @@ -78,6 +77,23 @@ impl Type { let vars = vars_t.difference(&vars_env).cloned().collect(); Scheme(vars, self.clone()) } + + fn refresh_vars(&mut self, s: &mut Subst, var_gen: &mut VarGen) { + maybe_grow(|| { + if let Type::Var(x) = self { + if let Some(y) = s.0.get(x) { + *self = y.clone(); + } else { + let y = var_gen.fresh(); + s.0.insert(x.clone(), y.clone()); + *self = y; + } + } + for child in self.children_mut() { + child.refresh_vars(s, var_gen); + } + }) + } } impl Scheme { @@ -103,6 +119,17 @@ impl Scheme { let subst = Subst(self.0.iter().cloned().zip(new_vars).collect()); self.1.subst(&subst) } + + fn refresh_vars(&mut self) { + let mut s = Subst::default(); + let mut var_gen = VarGen::default(); + for x in self.0.iter_mut() { + let y = var_gen.fresh_name(); + s.0.insert(x.clone(), Type::Var(y.clone())); + *x = y; + } + self.1.refresh_vars(&mut s, &mut var_gen); + } } impl Subst { @@ -131,30 +158,35 @@ impl TypeEnv { TypeEnv(env) } - fn append(&self, name: &Name, scheme: Scheme) -> TypeEnv { - let mut env = self.0.clone(); - env.insert(name.clone(), scheme); - TypeEnv(env) + fn insert(&mut self, name: Name, scheme: Scheme) { + eprintln!("inserting {name}: {} to env", scheme.1); + self.0.insert(name, scheme); } } impl VarGen { fn fresh(&mut self) -> Type { + let x = self.fresh_name(); + eprintln!("fresh var: {x}"); + Type::Var(x) + } + + fn fresh_name(&mut self) -> Name { let x = num_to_name(self.0 as u64); self.0 += 1; - Type::Var(Name::new(x)) + Name::new(x) } } impl RecGroups { fn from_book(book: &Book) -> RecGroups { - type DependencyGraph<'a> = HashMap<&'a Name, HashSet<&'a Name>>; + type DependencyGraph<'a> = BTreeMap<&'a Name, BTreeSet<&'a Name>>; fn collect_dependencies<'a>( term: &'a Term, book: &'a Book, scope: &mut Vec, - deps: &mut HashSet<&'a Name>, + deps: &mut BTreeSet<&'a Name>, ) { if let Term::Ref { nam } = term { if book.ctrs.contains_key(nam) || book.hvm_defs.contains_key(nam) || !book.defs[nam].check { @@ -174,8 +206,8 @@ impl RecGroups { v: &'a Name, deps: &DependencyGraph<'a>, index: &mut usize, - index_map: &mut HashMap<&'a Name, usize>, - low_link: &mut HashMap<&'a Name, usize>, + index_map: &mut BTreeMap<&'a Name, usize>, + low_link: &mut BTreeMap<&'a Name, usize>, stack: &mut Vec<&'a Name>, components: &mut Vec>, ) { @@ -230,8 +262,8 @@ impl RecGroups { // Run Tarjan's algorithm let mut index = 0; let mut stack = Vec::new(); - let mut index_map = HashMap::new(); - let mut low_link = HashMap::new(); + let mut index_map = BTreeMap::new(); + let mut low_link = BTreeMap::new(); let mut components = Vec::new(); for name in deps.keys() { if !index_map.contains_key(name) { @@ -243,6 +275,7 @@ impl RecGroups { } } +/* Inference, unification and type checking */ fn infer_book(book: &Book) -> Result { let groups = RecGroups::from_book(book); let mut env = TypeEnv::default(); @@ -251,7 +284,7 @@ fn infer_book(book: &Book) -> Result { for adt in book.adts.values() { for ctr in adt.ctrs.values() { let scheme = ctr.typ.generalize(&TypeEnv::default()); - env.0.insert(ctr.name.clone(), scheme); + env.insert(ctr.name.clone(), scheme); types.insert(ctr.name.clone(), ctr.typ.clone()); } } @@ -259,14 +292,14 @@ fn infer_book(book: &Book) -> Result { for def in book.defs.values() { if !def.check { let scheme = def.typ.generalize(&TypeEnv::default()); - env.0.insert(def.name.clone(), scheme); + env.insert(def.name.clone(), scheme); types.insert(def.name.clone(), def.typ.clone()); } } // Add the types of hvm functions to the environment. for def in book.hvm_defs.values() { let scheme = def.typ.generalize(&TypeEnv::default()); - env.0.insert(def.name.clone(), scheme); + env.insert(def.name.clone(), scheme); types.insert(def.name.clone(), def.typ.clone()); } @@ -287,11 +320,11 @@ fn infer_group( types: &mut ProgramTypes, ) -> Result<(), String> { let var_gen = &mut VarGen::default(); - + eprintln!(); // Generate fresh type variables for each function in the group. let tvs = group.iter().map(|_| var_gen.fresh()).collect::>(); for (name, tv) in group.iter().zip(tvs.iter()) { - env.0.insert(name.clone(), Scheme(vec![], tv.clone())); + env.insert(name.clone(), Scheme(vec![], tv.clone())); } // Infer the types of the functions in the group. @@ -299,8 +332,10 @@ fn infer_group( let mut inf_ts = vec![]; let mut exp_ts = vec![]; for name in group { - let def = book.defs.get(name).unwrap(); + eprintln!("inferring fn: {name} = {}", book.defs[name].rule().body); + let def = &book.defs[name]; let (s, t) = infer(env, book, &def.rule().body, var_gen)?; + eprintln!("inferred fn {name} : {t}"); ss.push(s); inf_ts.push(t); exp_ts.push(&def.typ); @@ -309,8 +344,8 @@ fn infer_group( // Unify the inferred body with the corresponding type variable. let mut s = ss.iter().fold(Subst::default(), |s, s2| s.compose(s2)); let mut ts = vec![]; - for (bod_t, tv) in inf_ts.into_iter().zip(tvs.iter()) { - let (t, s2) = unify(&tv.subst(&s), &bod_t)?; + for ((bod_t, tv), nam) in inf_ts.into_iter().zip(tvs.iter()).zip(group.iter()) { + let (t, s2) = unify(&tv.subst(&s), &bod_t, &book.defs[nam].rule().body)?; ts.push(t); s = s.compose(&s2); } @@ -318,13 +353,15 @@ fn infer_group( // Specialize against the expected type for ((name, exp_t), inf_t) in group.iter().zip(exp_ts.iter()).zip(ts.iter()) { - types.insert(name.clone(), specialize(inf_t, exp_t)?); + types.insert(name.clone(), specialize(inf_t, exp_t, &book.defs[name].rule().body)?); } // Generalize the function types - let mut env = env.subst(&s); + *env = env.subst(&s); for name in group { - env.0.insert(name.clone(), types[name].generalize(&env)); + let mut scheme = types[name].generalize(&TypeEnv::default()); + scheme.refresh_vars(); + env.insert(name.clone(), scheme); } Ok(()) } @@ -336,18 +373,19 @@ fn infer_group( /// The returned substitution records the type constraints imposed on type variables by the term. /// The returned type is the type of the term. fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Result<(Subst, Type), String> { - maybe_grow(|| match term { + eprintln!("infer: {term}"); + let res = maybe_grow(|| match term { Term::Var { nam } | Term::Ref { nam } => match env.0.get(nam) { - Some(scheme) => { - let t = scheme.instantiate(var_gen); - Ok((Subst::default(), t)) - } + Some(scheme) => Ok::<_, String>((Subst::default(), scheme.instantiate(var_gen))), None => unreachable!("unbound name '{}'", nam), }, Term::Lam { tag: Tag::Static, pat, bod } => match pat.as_ref() { Pattern::Var(nam) => { let tv = var_gen.fresh(); - let env = if let Some(nam) = nam { env.append(nam, Scheme(vec![], tv.clone())) } else { env.clone() }; + let mut env = env.clone(); + if let Some(nam) = nam { + env.insert(nam.clone(), Scheme(vec![], tv.clone())); + } let (s, bod_t) = infer(&env, book, bod, var_gen)?; let var_t = tv.subst(&s); Ok((s, Type::Arr(Box::new(var_t), Box::new(bod_t)))) @@ -358,71 +396,66 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let (s1, fun_t) = infer(env, book, fun, var_gen)?; let (s2, arg_t) = infer(&env.subst(&s1), book, arg, var_gen)?; let app_t = var_gen.fresh(); - let (_, s3) = unify(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())))?; + let (_, s3) = unify(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())), term)?; Ok((s3.compose(&s2).compose(&s1), app_t.subst(&s3))) } Term::Let { pat, val, nxt } => match pat.as_ref() { Pattern::Var(nam) => { let (s1, val_t) = infer(env, book, val, var_gen)?; - let nxt_env = - if let Some(nam) = nam { env.append(nam, val_t.generalize(&env.subst(&s1))) } else { env.clone() }; - let (s2, nxt_t) = infer(&nxt_env, book, nxt, var_gen)?; + let mut env = env.clone(); + if let Some(nam) = nam { + env.insert(nam.clone(), val_t.generalize(&env.subst(&s1))); + } + let (s2, nxt_t) = infer(&env.subst(&s1), book, nxt, var_gen)?; Ok((s2.compose(&s1), nxt_t)) } Pattern::Fan(FanKind::Tup, Tag::Static, els) => { // Tuple elimination behaves like pattern matching. // Variables from tuple patterns don't get generalized. - let (s1, t1) = infer(env, book, val, var_gen)?; - - let exp_tvs = els.iter().map(|_| var_gen.fresh()).collect::>(); - let tup_t = Type::Tup(exp_tvs.clone()); - let (_, s2) = unify(&t1.subst(&s1), &tup_t)?; - let s = s2.compose(&s1); - let mut env = env.subst(&s); + let (s1, val_t) = infer(env, book, val, var_gen)?; - let mut vars = vec![]; - let mut inf_tvs = vec![]; + let mut env = env.clone(); + let mut tvs = vec![]; for el in els { if let Pattern::Var(nam) = el { - vars.push(nam); - inf_tvs.push(var_gen.fresh()); + let tv = var_gen.fresh(); + tvs.push(tv.clone()); + if let Some(nam) = nam { + env.insert(nam.clone(), Scheme(vec![], tv)); + } } else { unreachable!("Nested patterns should've been removed in earlier pass"); } } - // - for (var, tv) in vars.iter().zip(inf_tvs.iter()) { - if let Some(var) = var { - env.0.insert(var.clone(), Scheme(vec![], tv.subst(&s2))); - } - } - - let (s3, nxt_t) = infer(&env, book, nxt, var_gen)?; - let inf_ts = inf_tvs.into_iter().map(|tv| tv.subst(&s3)).collect::>(); - let exp_ts = exp_tvs.into_iter().map(|tv| tv.subst(&s)).collect::>(); - let s4 = unify_fields(inf_ts.iter().zip(exp_ts.iter()))?; - Ok((s4.compose(&s3).compose(&s), nxt_t.subst(&s4))) + let (s2, nxt_t) = infer(&env.subst(&s1), book, nxt, var_gen)?; + let tvs = tvs.into_iter().map(|tv| tv.subst(&s2)).collect::>(); + let (_, s3) = unify(&val_t, &Type::Tup(tvs), val)?; + Ok((s3.compose(&s2).compose(&s1), nxt_t)) } Pattern::Fan(FanKind::Dup, Tag::Auto, els) => { // We pretend that sups don't exist and dups don't collide. // All variables must have the same type as the body of the dup. - let (s1, t1) = infer(env, book, val, var_gen)?; + let (s1, mut val_t) = infer(env, book, val, var_gen)?; let mut tvs = vec![]; - let mut env = env.subst(&s1); + let mut env = env.clone(); for el in els { if let Pattern::Var(nam) = el { let tv = var_gen.fresh(); tvs.push(tv.clone()); if let Some(nam) = nam { - env.0.insert(nam.clone(), Scheme(vec![], tv.clone())); + env.insert(nam.clone(), Scheme(vec![], tv.clone())); } } else { unreachable!("Nested patterns should've been removed in earlier pass"); } } - let (s2, t2) = infer(&env, book, nxt, var_gen)?; - let s = unify_fields(tvs.iter().map(|tv| (&t1, tv)))?; - Ok((s.compose(&s2).compose(&s1), t2.subst(&s))) + let (mut s2, nxt_t) = infer(&env.subst(&s1), book, nxt, var_gen)?; + for tv in tvs { + let (val_t_, s) = unify(&val_t, &tv.subst(&s2), val)?; + val_t = val_t_; + s2 = s2.compose(&s); + } + Ok((s2.compose(&s1), nxt_t)) } _ => todo!(), }, @@ -434,17 +467,16 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul // Instantiate the expected type of the scrutinee let adt_name = book.ctrs.get(arms[0].0.as_ref().unwrap()).unwrap(); let adt = &book.adts[adt_name]; - let (s2, arg_t) = instantiate_adt(adt, var_gen)?; - - // Unify the inferred type with the expected type - let (_, s3) = unify(&t1.subst(&s2), &arg_t)?; + let (adt_s, adt_t) = instantiate_adt(adt, var_gen)?; // For each case, infer the types and unify them all. // Unify the inferred type of the destructured fields with the // expected from what we inferred from the scrutinee. - let s = s3.compose(&s2).compose(&s1); - let env = env.subst(&s); - infer_match_cases(&env, book, adt, arms, &s, var_gen) + let (s2, nxt_t) = infer_match_cases(&env.subst(&s1), book, adt, arms, &adt_s, var_gen)?; + + // Unify the inferred type with the expected type + let (_, s3) = unify(&t1, &adt_t.subst(&s2), arg)?; + Ok((s3.compose(&s2).compose(&s1), nxt_t)) } Term::Num { val } => { @@ -458,49 +490,39 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul Term::Oper { opr, fst, snd } => { let (s1, t1) = infer(env, book, fst, var_gen)?; let (s2, t2) = infer(&env.subst(&s1), book, snd, var_gen)?; - let (t2, s3) = unify(&t2.subst(&s1), &t1.subst(&s2))?; + let (t2, s3) = unify(&t2.subst(&s1), &t1.subst(&s2), term)?; let s_args = s3.compose(&s2).compose(&s1); let t_args = t2.subst(&s_args); // Check numeric type matches the operation let tv = var_gen.fresh(); let (t_opr, s_opr) = match opr { // Any numeric type - crate::fun::Op::ADD - | crate::fun::Op::SUB - | crate::fun::Op::MUL - | crate::fun::Op::DIV - | crate::fun::Op::EQ - | crate::fun::Op::NEQ - | crate::fun::Op::LT - | crate::fun::Op::GT - | crate::fun::Op::GE - | crate::fun::Op::LE => unify(&t_args, &Type::Number(Box::new(tv.clone())))?, + Op::ADD | Op::SUB | Op::MUL | Op::DIV | Op::EQ | Op::NEQ | Op::LT | Op::GT | Op::GE | Op::LE => { + unify(&t_args, &Type::Number(Box::new(tv.clone())), term)? + } // Integers - crate::fun::Op::REM - | crate::fun::Op::AND - | crate::fun::Op::OR - | crate::fun::Op::XOR - | crate::fun::Op::SHL - | crate::fun::Op::SHR => unify(&t_args, &Type::Integer(Box::new(tv.clone())))?, + Op::REM | Op::AND | Op::OR | Op::XOR | Op::SHL | Op::SHR => { + unify(&t_args, &Type::Integer(Box::new(tv.clone())), term)? + } // Floating - crate::fun::Op::POW => unify(&t_args, &Type::F24)?, + Op::POW => unify(&t_args, &Type::F24, term)?, }; Ok((s_opr.compose(&s_args), t_opr.subst(&s_opr))) } Term::Swt { bnd: _, arg, with_bnd: _, with_arg: _, pred, arms } => { debug_assert!(arms.len() == 2); let (s1, t1) = infer(env, book, arg, var_gen)?; - let (_, s2) = unify(&t1, &Type::U24)?; + let (_, s2) = unify(&t1, &Type::U24, arg)?; let s_arg = s2.compose(&s1); let mut env = env.subst(&s_arg); let (s_zero, t_zero) = infer(&env, book, &arms[0], var_gen)?; if let Some(pred) = pred { - env.0.insert(pred.clone(), Scheme(vec![], Type::U24)); + env.insert(pred.clone(), Scheme(vec![], Type::U24)); } let (s_succ, t_succ) = infer(&env, book, &arms[1], var_gen)?; let s_arms = s_succ.compose(&s_zero); - let (t_swt, s_swt) = unify(&t_zero.subst(&s_arms), &t_succ.subst(&s_arms))?; + let (t_swt, s_swt) = unify(&t_zero.subst(&s_arms), &t_succ.subst(&s_arms), term)?; Ok((s_swt.compose(&s_arms).compose(&s_arg), t_swt.subst(&s_swt))) } @@ -509,7 +531,8 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let res = els.iter().map(|el| infer(env, book, el, var_gen)).collect::, _>>()?; let (ss, ts): (Vec, Vec) = res.into_iter().unzip(); let t = Type::Tup(ts); - Ok((ss.into_iter().fold(Subst::default(), |acc, s| acc.compose(&s)), t)) + let s = ss.into_iter().fold(Subst::default(), |acc, s| acc.compose(&s)); + Ok((s, t)) } Term::Era => Ok((Subst::default(), Type::None)), Term::Fan { .. } | Term::Lam { tag: _, .. } | Term::App { tag: _, .. } | Term::Link { .. } => { @@ -526,9 +549,14 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul | Term::Open { .. } | Term::Def { .. } | Term::Err => unreachable!("'{term}' while type checking. Should have been removed in earlier pass"), - }) + })?; + eprintln!("inferred: {term} : {}\n{}", res.1, res.0); + Ok(res) } +/// Instantiates the type constructor of an ADT, also returning the +/// ADT var to instantiated var substitution, to be used when +/// instantiating the types of the fields of the eliminated constructors. fn instantiate_adt(adt: &Adt, var_gen: &mut VarGen) -> Result<(Subst, Type), String> { let tvs = adt.vars.iter().map(|_| var_gen.fresh()); let s = Subst(adt.vars.iter().zip(tvs).map(|(x, t)| (x.clone(), t)).collect()); @@ -542,7 +570,7 @@ fn infer_match_cases( book: &Book, adt: &Adt, arms: &[MatchRule], - s: &Subst, + adt_s: &Subst, var_gen: &mut VarGen, ) -> Result<(Subst, Type), String> { maybe_grow(|| { @@ -554,29 +582,30 @@ fn infer_match_cases( // Add the fields to the environment. let mut case_env = env.clone(); for (var, tv) in vars.iter().zip(tvs.iter()) { - case_env.0.insert(var.as_ref().unwrap().clone(), Scheme(vec![], tv.clone())); + if let Some(var) = var { + case_env.insert(var.clone(), Scheme(vec![], tv.clone())); + } } // Infer the body and unify the inferred field types with the expected. let (s1, t1) = infer(&case_env, book, bod, var_gen)?; let inf_ts = tvs.into_iter().map(|tv| tv.subst(&s1)).collect::>(); - let exp_ts = ctr.fields.iter().map(|f| f.typ.subst(s)).collect::>(); - let s2 = unify_fields(inf_ts.iter().zip(exp_ts.iter()))?; + let exp_ts = ctr.fields.iter().map(|f| f.typ.subst(adt_s)).collect::>(); + let s2 = unify_fields(inf_ts.iter().zip(exp_ts.iter()), bod)?; // Recurse and unify with the other arms. - let (s_rest, t_rest) = infer_match_cases(env, book, adt, rest, s, var_gen)?; - let (t_final, s_final) = unify(&t1.subst(&s_rest.compose(&s2)), &t_rest)?; + let (s_rest, t_rest) = infer_match_cases(env, book, adt, rest, adt_s, var_gen)?; + let (t_final, s_final) = unify(&t1, &t_rest, bod)?; - Ok((s_final.compose(&s_rest).compose(&s2).compose(&s1).compose(s), t_final.subst(&s_final))) + Ok((s_final.compose(&s_rest).compose(&s2).compose(&s1), t_final)) } else { - let t = var_gen.fresh().subst(s); - Ok((s.clone(), t)) + Ok((Subst::default(), var_gen.fresh())) } }) } -fn unify_fields<'a>(ts: impl Iterator) -> Result { - let ss = ts.map(|(inf, exp)| unify(inf, exp)).collect::, _>>()?; +fn unify_fields<'a>(ts: impl Iterator, ctx: &Term) -> Result { + let ss = ts.map(|(inf, exp)| unify(inf, exp, ctx)).collect::, _>>()?; let mut s = Subst::default(); for (_, s2) in ss.into_iter().rev() { s = s.compose(&s2); @@ -584,13 +613,10 @@ fn unify_fields<'a>(ts: impl Iterator) -> Result Result<(Type, Subst), String> { +fn unify(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { + eprintln!("\t{t1} ~ {t2}"); maybe_grow(|| match (t1, t2) { - (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { - let (t1, s1) = unify(l1, l2)?; - let (t2, s2) = unify(&r1.subst(&s1), &r2.subst(&s1))?; - Ok((Type::Arr(Box::new(t1), Box::new(t2)), s2.compose(&s1))) - } + (t, Type::Hole) | (Type::Hole, t) => Ok((t.clone(), Subst::default())), (t, Type::Var(x)) | (Type::Var(x), t) => { // Try to bind variable `x` to `t` if let Type::Var(y) = t { @@ -602,16 +628,21 @@ fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { // Occurs check if t.free_type_vars().contains(x) { return Err(format!( - "Cannot unify variable '{x}' with type '{t}' because it occurs as a free variable" + "In {ctx}: Cannot unify variable '{x}' with type '{t}' because it occurs as a free variable" )); } Ok((t.clone(), Subst(BTreeMap::from([(x.clone(), t.clone())])))) } + (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { + let (t1, s1) = unify(l1, l2, ctx)?; + let (t2, s2) = unify(&r1.subst(&s1), &r2.subst(&s1), ctx)?; + Ok((Type::Arr(Box::new(t1), Box::new(t2)), s2.compose(&s1))) + } (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { let mut s = Subst::default(); let mut ts = vec![]; for (t1, t2) in ts1.iter().zip(ts2.iter()) { - let (t, s2) = unify(t1, t2)?; + let (t, s2) = unify(t1, t2, ctx)?; ts.push(t); s = s.compose(&s2); } @@ -621,7 +652,7 @@ fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { let mut s = Subst::default(); let mut ts = vec![]; for (t1, t2) in els1.iter().zip(els2.iter()) { - let (t, s2) = unify(t1, t2)?; + let (t, s2) = unify(t1, t2, ctx)?; ts.push(t); s = s.compose(&s2); } @@ -632,44 +663,42 @@ fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { | (Type::I24, Type::I24) | (Type::None, Type::None)) => Ok((t.0.clone(), Subst::default())), (Type::Number(t1), Type::Number(t2)) => { - let (t, s) = unify(t1, t2)?; + let (t, s) = unify(t1, t2, ctx)?; Ok((Type::Number(Box::new(t)), s)) } (Type::Number(tn), Type::Integer(ti)) | (Type::Integer(ti), Type::Number(tn)) => { - let (t, s) = unify(ti, tn)?; + let (t, s) = unify(ti, tn, ctx)?; Ok((Type::Integer(Box::new(t)), s)) } (Type::Integer(t1), Type::Integer(t2)) => { - let (t, s) = unify(t1, t2)?; + let (t, s) = unify(t1, t2, ctx)?; Ok((Type::Integer(Box::new(t)), s)) } (Type::Number(t1) | Type::Integer(t1), t2 @ (Type::U24 | Type::I24 | Type::F24)) | (t2 @ (Type::U24 | Type::I24 | Type::F24), Type::Number(t1) | Type::Integer(t1)) => { - let (t, s) = unify(t1, t2)?; + let (t, s) = unify(t1, t2, ctx)?; Ok((t, s)) } - (Type::Hole, t) | (t, Type::Hole) => Ok((t.clone(), Subst::default())), - (Type::Any, t) | (t, Type::Any) => { let mut s = Subst::default(); - // Recurse to assign variables to Any as well + // Recurse to assign variables to `Any` as well for child in t.children() { - let (_, s2) = unify(&Type::Any, child)?; + let (_, s2) = unify(&Type::Any, child, ctx)?; s = s2.compose(&s); } Ok((Type::Any, s)) } - _ => Err(format!("Types do not unify: '{t1}' and '{t2}'")), + _ => Err(format!("In {ctx}: Types do not unify: '{t1}' and '{t2}'")), }) } /// Specializes a type against another type. /// /// Errors if the first type is not a superset of the second type. -fn specialize(gen: &Type, spe: &Type) -> Result { - fn merge_specialization(inf: &Type, exp: &Type, s: &mut Subst) -> Result { +fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { + fn merge_specialization(inf: &Type, exp: &Type, s: &mut Subst, ctx: &Term) -> Result { maybe_grow(|| match (inf, exp) { (t, Type::Hole) => Ok(t.clone()), (inf, Type::Var(x)) => { @@ -677,7 +706,7 @@ fn specialize(gen: &Type, spe: &Type) -> Result { if inf == exp { Ok(inf.clone()) } else { - Err(format!("Cannot specialize type '{inf}' with type '{exp}'")) + Err(format!("Cannot substitute type '{inf}' with type '{exp}'")) } } else { s.0.insert(x.clone(), inf.clone()); @@ -685,20 +714,32 @@ fn specialize(gen: &Type, spe: &Type) -> Result { } } (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { - let l = merge_specialization(l1, l2, s)?; - let r = merge_specialization(r1, r2, s)?; + let l = merge_specialization(l1, l2, s, ctx)?; + let r = merge_specialization(r1, r2, s, ctx)?; Ok(Type::Arr(Box::new(l), Box::new(r))) } (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { let mut ts = vec![]; for (t1, t2) in ts1.iter().zip(ts2.iter()) { - let t = merge_specialization(t1, t2, s)?; + let t = merge_specialization(t1, t2, s, ctx)?; ts.push(t); } Ok(Type::Ctr(name1.clone(), ts)) } - (Type::Number(t1), Type::Number(t2)) => Ok(Type::Number(Box::new(merge_specialization(t1, t2, s)?))), - (Type::Integer(t1), Type::Integer(t2)) => Ok(Type::Integer(Box::new(merge_specialization(t1, t2, s)?))), + (Type::Tup(ts1), Type::Tup(ts2)) if ts1.len() == ts2.len() => { + let mut ts = vec![]; + for (t1, t2) in ts1.iter().zip(ts2.iter()) { + let t = merge_specialization(t1, t2, s, ctx)?; + ts.push(t); + } + Ok(Type::Tup(ts)) + } + (Type::Number(t1), Type::Number(t2)) => { + Ok(Type::Number(Box::new(merge_specialization(t1, t2, s, ctx)?))) + } + (Type::Integer(t1), Type::Integer(t2)) => { + Ok(Type::Integer(Box::new(merge_specialization(t1, t2, s, ctx)?))) + } (Type::Any, Type::Any) | (Type::U24, Type::U24) | (Type::F24, Type::F24) @@ -710,31 +751,19 @@ fn specialize(gen: &Type, spe: &Type) -> Result { }) } - let (t, s) = unify(gen, spe)?; + let (t, s) = unify(gen, spe, ctx)?; // Merge the inferred specialization with the expected type. // This is done to cast to/from `Any` types. - merge_specialization(&t.subst(&s), spe, &mut Subst::default()) + eprintln!("specialize: {t} ~ {spe}"); + merge_specialization(&t.subst(&s), spe, &mut Subst::default(), ctx) } -fn refresh_vars_book(types: &mut ProgramTypes) { - for typ in types.values_mut() { - refresh_vars_type(typ, &mut BTreeMap::new(), &mut VarGen::default()); - } -} - -fn refresh_vars_type(typ: &mut Type, map: &mut BTreeMap, var_gen: &mut VarGen) { - maybe_grow(|| { - if let Type::Var(x) = typ { - if let Some(y) = map.get(x) { - *typ = y.clone(); - } else { - let y = var_gen.fresh(); - map.insert(x.clone(), y.clone()); - *typ = y; - } +impl std::fmt::Display for Subst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "Subst {{")?; + for (x, y) in &self.0 { + writeln!(f, " {x} => {y},")?; } - for child in typ.children_mut() { - refresh_vars_type(child, map, var_gen); - } - }) + write!(f, "}}") + } } diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 0c9db003f..95227322e 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -1013,6 +1013,8 @@ impl<'a> FunParser<'a> { Ok(Type::Any) } else if self.try_parse_keyword("None") { Ok(Type::None) + } else if self.try_parse_keyword("_") { + Ok(Type::Hole) } else if self.try_parse_keyword("u24") { Ok(Type::U24) } else if self.try_parse_keyword("i24") { diff --git a/src/imp/parser.rs b/src/imp/parser.rs index fe476c929..8a2ac7bed 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -1168,6 +1168,8 @@ impl<'a> ImpParser<'a> { Type::Any } else if self.try_parse_keyword("None") { Type::None + } else if self.try_parse_keyword("_") { + Type::Hole } else if self.try_parse_keyword("u24") { Type::U24 } else if self.try_parse_keyword("i24") { From cc8e1c215137b8174996830f9fb1fd14d7db6919 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Tue, 27 Aug 2024 21:37:48 +0200 Subject: [PATCH 10/29] Fix typing for builting functions --- src/fun/builtins.bend | 186 +++++++++++++------------ src/fun/check/type_check.rs | 43 +++--- src/fun/transform/resolve_type_ctrs.rs | 1 - 3 files changed, 118 insertions(+), 112 deletions(-) diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index 670055abe..3a845323e 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -74,17 +74,22 @@ String/equals * * = 0 # Splits a list into two lists at the first occurrence of a value. String/split (s: String) (delimiter: u24) : (List String) -String/split s delim = (String/split.go s delim (List/Cons String/Nil List/Nil)) - String/split.go (String/Nil) _ acc = (List/reverse acc) - String/split.go (String/Cons c cs) delim acc = - if (== c delim) { - (String/split.go cs delim (List/Cons String/Nil acc)) - } else { - match acc { - List/Cons: (String/split.go cs delim (List/Cons (String/Cons c acc.head) acc.tail)) - List/Nil: [] - } +String/split s delim = (String/split.go s delim [""]) + +String/split.go (cs: String) (delim: u24) (acc: (List String)) : (List String) +String/split.go (String/Nil) _ acc = (List/reverse acc) +String/split.go (String/Cons c cs) delim acc = + if (== c delim) { + # Start a new split string + (String/split.go cs delim (List/Cons String/Nil acc)) + } else { + match acc { + # Add the current character to the current split string + List/Cons: (String/split.go cs delim (List/Cons (String/Cons c acc.head) acc.tail)) + # Should be unreachable + List/Nil: [] } + } # Create a new difference list def DiffList/new() -> (List(T) -> List(T)): @@ -126,7 +131,7 @@ def Result/unwrap(res: Result(T, E)) -> Any: # Returns the second result if the first one is `Ok`. # Otherwise, returns the `Err` of the first result. -def Result/and(fst: Result(T, E), snd: Result(T, E)) -> Result(T, E): +def Result/and(fst: Result(A, E), snd: Result(B, E)) -> Result(B, E): match fst: case Result/Ok: return snd @@ -146,7 +151,7 @@ type Tree(T): Leaf { value: T } # Returns a List converted from a Tree. -def Tree/to_list(tree): +def Tree/to_list(tree: Tree(T)) -> List(T): fold tree: case Tree/Leaf: list = DiffList/wrap(tree.value) @@ -155,7 +160,7 @@ def Tree/to_list(tree): return DiffList/to_list(list) # Reverses a tree swapping right and left leaves. -def Tree/reverse(tree): +def Tree/reverse(tree: Tree(T)) -> Tree(T): fold tree: case Tree/Leaf: return !tree.value @@ -245,7 +250,7 @@ def IO/MAGIC() -> (u24, u24): def IO/wrap(x: T) -> IO(T): return IO/Done(IO/MAGIC, x) -def IO/bind(a: IO(A), b: ((id -> id) -> A -> IO(B))) -> IO(B): +def IO/bind(a: IO(A), b: ((Id -> Id) -> A -> IO(B))) -> IO(B): match a: case IO/Done: return undefer(b, a.expr) @@ -266,17 +271,6 @@ def IO/bind(a: IO(A), b: ((id -> id) -> A -> IO(B))) -> IO(B): def IO/call(func: String, argm: Any) -> IO(Result(Any, IOError(Any))): return IO/Call(IO/MAGIC, func, argm, lambda x: IO/Done(IO/MAGIC, x)) -# If the IO result is an error, returns it without calling the continuation. -# Otherwise, calls the continuation with the unwrapped result. -IO/done_on_err (io: (IO (Result A B))) : (IO (Result A B)) -IO/done_on_err (IO/Call magic func argm cont) = - let cont = @res match res { - Result/Ok: (cont res.val) - Result/Err: (IO/Done IO/MAGIC (Result/Err res.val)) - } - (IO/Call magic func argm cont) -IO/done_on_err done = done - # Maps the result of an IO. def IO/map(io: IO(A), f: A -> B) -> IO(B): with IO: @@ -326,7 +320,7 @@ def IO/FS/read(file: u24, num_bytes: u24) -> IO(Result(List(u24), u24)): def IO/FS/write(file: u24, bytes: List(u24)) -> IO(Result(None, u24)): return IO/unwrap_inner(IO/call("WRITE", (file, bytes))) -def IO/FS/seek(file: u24, offset: i24, mode: u24) -> IO(Result(None, u24)): +def IO/FS/seek(file: u24, offset: i24, mode: i24) -> IO(Result(None, u24)): return IO/unwrap_inner(IO/call("SEEK", (file, (offset, mode)))) def IO/FS/flush(file: u24) -> IO(Result(None, u24)): @@ -350,10 +344,15 @@ IO/FS/SEEK_END : i24 = +2 # Reads an entire file, returning a list of bytes. def IO/FS/read_file(path: String) -> IO(Result(List(u24), u24)): with IO: - fd <- IO/done_on_err(IO/FS/open(path, "r")) - bytes <- IO/done_on_err(IO/FS/read_to_end(fd)) - * <- IO/done_on_err(IO/FS/close(fd)) - return wrap(bytes) + res_fd <- IO/FS/open(path, "r") + match res_fd: + case Result/Ok: + fd = res_fd.val + res1 <- IO/FS/read_to_end(fd) + res2 <- IO/FS/close(fd) + return wrap(Result/and(res2, res1)) + case Result/Err: + return wrap(Result/Err(res_fd.val)) # Reads the remaining contents of a file, returning a list of read bytes. def IO/FS/read_to_end(fd: u24) -> IO(Result(List(u24), u24)): @@ -362,12 +361,17 @@ def IO/FS/read_to_end(fd: u24) -> IO(Result(List(u24), u24)): def IO/FS/read_to_end.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(List(u24), u24)): with IO: # Read file in 1MB chunks - chunk <- IO/done_on_err(IO/FS/read(fd, 1048576)) - match chunk: - case List/Nil: - return wrap(List/flatten(chunks)) - case List/Cons: - return IO/FS/read_to_end.read_chunks(fd, List/Cons(chunk, chunks)) + res_chunk <- IO/FS/read(fd, 1048576) + match res_chunk: + case Result/Ok: + chunk = res_chunk.val + match chunk: + case List/Nil: + return wrap(Result/Ok(List/flatten(chunks))) + case List/Cons: + return IO/FS/read_to_end.read_chunks(fd, List/Cons(chunk, chunks)) + case Result/Err: + return wrap(Result/Err(res_chunk.val)) # Reads a single line from a file, returning a list of bytes. def IO/FS/read_line(fd: u24) -> IO(Result(List(u24), u24)): @@ -376,43 +380,52 @@ def IO/FS/read_line(fd: u24) -> IO(Result(List(u24), u24)): def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(List(u24), u24)): with IO: # Read line in 1kB chunks - chunk <- IO/done_on_err(IO/FS/read(fd, 1024)) - match res = List/split_once(chunk, '\n'): - # Found a newline, backtrack and join chunks + res_chunk <- IO/FS/read(fd, 1024) + match res_chunk: case Result/Ok: - # Found line end, backtrack on the file to the start of the next line - # and return the found line. - (line, rest) = res.val - (length, *) = List/length(rest) - * <- IO/done_on_err(IO/FS/seek(fd, u24_to_i24(length) * -1, IO/FS/SEEK_CUR)) - chunks = List/Cons(line, chunks) - bytes = List/flatten(chunks) - return wrap(Result/Ok(bytes)) - # Newline not found + chunk = res_chunk.val + match res = List/split_once(chunk, '\n'): + # Found a newline, backtrack and join chunks + case Result/Ok: + # Found line end, backtrack on the file to the start of the next line + # and return the found line. + (line, rest) = res.val + (length, *) = List/length(rest) + res_seek <- IO/FS/seek(fd, u24_to_i24(length) * -1, IO/FS/SEEK_CUR) + match res_seek: + case Result/Ok: + chunks = List/Cons(line, chunks) + bytes = List/flatten(chunks) + return wrap(Result/Ok(bytes)) + case Result/Err: + return wrap(Result/Err(res_seek.val)) + # Newline not found + case Result/Err: + line = res.val + (length, line) = List/length(line) + # If length is 0, the end of the file was reached, return as if it was a newline + if length == 0: + bytes = List/flatten(chunks) + return wrap(bytes) + # Otherwise, the line is still ongoing, read more chunks + else: + chunks = List/Cons(line, chunks) + return IO/FS/read_line.read_chunks(fd, chunks) case Result/Err: - # No line end found, continue reading. - line = res.val - (length, line) = List/length(line) - # If length is 0, the end of the file was reached, return as if it was a newline - if length == 0: - bytes = List/flatten(chunks) - return wrap(bytes) - # Otherwise, the line is still ongoing, read more chunks - else: - chunks = List/Cons(line, chunks) - return IO/FS/read_line.read_chunks(fd, chunks) + return wrap(Result/Err(res_chunk.val)) # Writes a list of bytes to a file given by a path. def IO/FS/write_file(path: String, bytes: List(u24)) -> IO(Result(None, u24)): with IO: - f <- IO/FS/open(path, "w") - match f: - case Result/Err: - return wrap(Result/Err(f.val)) + res_f <- IO/FS/open(path, "w") + match res_f: case Result/Ok: - * <- IO/done_on_err(IO/FS/write(f.val, bytes)) - * <- IO/done_on_err(IO/FS/close(f.val)) - return wrap(Result/Ok(bytes)) + f = res_f.val + res1 <- IO/FS/write(f, bytes) + res2 <- IO/FS/close(f) + return wrap(Result/and(res2, res1)) + case Result/Err: + return wrap(Result/Err(res_f.val)) ### Standard input and output utilities @@ -431,19 +444,24 @@ def IO/input() -> IO(Result(String, u24)): def IO/input.go(acc: List(u24) -> List(u24)) -> IO(Result(String, u24)): # TODO: This is slow and inefficient, should be done in hvm using fgets. with IO: - byte <- IO/done_on_err(IO/FS/read(IO/FS/STDIN, 1)) - match byte: - case List/Nil: - # Nothing read, try again (to simulate blocking a read) - return IO/input.go(acc) - case List/Cons: - if byte.head == '\n': - bytes = DiffList/to_list(acc) - text = String/decode_utf8(bytes) - return wrap(Result/Ok(text)) - else: - acc = DiffList/append(acc, byte.head) - return IO/input.go(acc) + res_byte <- IO/FS/read(IO/FS/STDIN, 1) + match res_byte: + case Result/Ok: + byte = res_byte.val + match byte: + case List/Nil: + # Nothing read, try again (to simulate blocking a read) + return IO/input.go(acc) + case List/Cons: + if byte.head == '\n': + bytes = DiffList/to_list(acc) + text = String/decode_utf8(bytes) + return wrap(Result/Ok(text)) + else: + acc = DiffList/append(acc, byte.head) + return IO/input.go(acc) + case Result/Err: + return wrap(Result/Err(res_byte.val)) ### Dynamically linked libraries @@ -458,13 +476,13 @@ def IO/DyLib/open(path: String, lazy: u24) -> IO(Result(u24, String)): # 'dl' is the id of the library object. # 'fn' is the name of the function in the library. # 'args' are the arguments to the function. The expected values depend on the called function. -def IO/DyLib/call(dl: u24, fn: String, args: Any) -> IO(Any): +def IO/DyLib/call(dl: u24, fn: String, args: Any) -> IO(Result(Any, String)): return IO/unwrap_inner(IO/call("DL_CALL", (dl, (fn, args)))) # Closes a previously open library. # Returns nothing. # 'dl' is the id of the library object. -def IO/DyLib/close(dl: u24) -> IO(None): +def IO/DyLib/close(dl: u24) -> IO(Result(None, String)): return IO/unwrap_inner(IO/call("DL_CLOSE", dl)) # Lazy thunks @@ -495,32 +513,26 @@ def unreachable() -> Any: # Native number casts -# f24_to_u24(x: f24) -> u24 # Casts a f24 number to a u24. hvm f24_to_u24 -> (f24 -> u24): ($([u24] ret) ret) -# i24_to_u24(x: i24) -> u24 # Casts an i24 number to a u24. hvm i24_to_u24 -> (i24 -> u24): ($([u24] ret) ret) -# u24_to_i24(x: u24) -> i24 # Casts a u24 number to an i24. hvm u24_to_i24 -> (u24 -> i24): ($([i24] ret) ret) -# f24_to_i24(x: f24) -> i24 # Casts a f24 number to an i24. hvm f24_to_i24 -> (f24 -> i24): ($([i24] ret) ret) -# u24_to_f24(x: u24) -> f24 # Casts a u24 number to a f24. hvm u24_to_f24 -> (u24 -> f24): ($([f24] ret) ret) -# i24_to_f24(x: i24) -> f24 # Casts an i24 number to a f24. hvm i24_to_f24 -> (i24 -> f24): ($([f24] ret) ret) diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index e65febaa4..5a8dd1603 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -159,7 +159,6 @@ impl TypeEnv { } fn insert(&mut self, name: Name, scheme: Scheme) { - eprintln!("inserting {name}: {} to env", scheme.1); self.0.insert(name, scheme); } } @@ -167,7 +166,6 @@ impl TypeEnv { impl VarGen { fn fresh(&mut self) -> Type { let x = self.fresh_name(); - eprintln!("fresh var: {x}"); Type::Var(x) } @@ -305,10 +303,7 @@ fn infer_book(book: &Book) -> Result { // Infer the types of regular functions. for group in &groups.0 { - if let Err(e) = infer_group(&mut env, book, group, &mut types) { - eprintln!("Error while inferring types for group '{group:?}'"); - return Err(e); - } + infer_group(&mut env, book, group, &mut types)?; } Ok(types) } @@ -320,7 +315,6 @@ fn infer_group( types: &mut ProgramTypes, ) -> Result<(), String> { let var_gen = &mut VarGen::default(); - eprintln!(); // Generate fresh type variables for each function in the group. let tvs = group.iter().map(|_| var_gen.fresh()).collect::>(); for (name, tv) in group.iter().zip(tvs.iter()) { @@ -332,10 +326,9 @@ fn infer_group( let mut inf_ts = vec![]; let mut exp_ts = vec![]; for name in group { - eprintln!("inferring fn: {name} = {}", book.defs[name].rule().body); let def = &book.defs[name]; let (s, t) = infer(env, book, &def.rule().body, var_gen)?; - eprintln!("inferred fn {name} : {t}"); + let t = t.subst(&s); ss.push(s); inf_ts.push(t); exp_ts.push(&def.typ); @@ -373,7 +366,6 @@ fn infer_group( /// The returned substitution records the type constraints imposed on type variables by the term. /// The returned type is the type of the term. fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Result<(Subst, Type), String> { - eprintln!("infer: {term}"); let res = maybe_grow(|| match term { Term::Var { nam } | Term::Ref { nam } => match env.0.get(nam) { Some(scheme) => Ok::<_, String>((Subst::default(), scheme.instantiate(var_gen))), @@ -497,9 +489,13 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let tv = var_gen.fresh(); let (t_opr, s_opr) = match opr { // Any numeric type - Op::ADD | Op::SUB | Op::MUL | Op::DIV | Op::EQ | Op::NEQ | Op::LT | Op::GT | Op::GE | Op::LE => { + Op::ADD | Op::SUB | Op::MUL | Op::DIV => { unify(&t_args, &Type::Number(Box::new(tv.clone())), term)? } + Op::EQ | Op::NEQ | Op::LT | Op::GT | Op::GE | Op::LE => { + let (_, s) = unify(&t_args, &Type::Number(Box::new(tv.clone())), term)?; + (Type::U24, s) + } // Integers Op::REM | Op::AND | Op::OR | Op::XOR | Op::SHL | Op::SHR => { unify(&t_args, &Type::Integer(Box::new(tv.clone())), term)? @@ -550,7 +546,6 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul | Term::Def { .. } | Term::Err => unreachable!("'{term}' while type checking. Should have been removed in earlier pass"), })?; - eprintln!("inferred: {term} : {}\n{}", res.1, res.0); Ok(res) } @@ -614,7 +609,6 @@ fn unify_fields<'a>(ts: impl Iterator, ctx: &Term) } fn unify(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { - eprintln!("\t{t1} ~ {t2}"); maybe_grow(|| match (t1, t2) { (t, Type::Hole) | (Type::Hole, t) => Ok((t.clone(), Subst::default())), (t, Type::Var(x)) | (Type::Var(x), t) => { @@ -700,13 +694,17 @@ fn unify(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { fn merge_specialization(inf: &Type, exp: &Type, s: &mut Subst, ctx: &Term) -> Result { maybe_grow(|| match (inf, exp) { + // These rules have to come before (t, Type::Hole) => Ok(t.clone()), + (_inf, Type::Any) => Ok(Type::Any), + (Type::Any, exp) => Ok(exp.clone()), + (inf, Type::Var(x)) => { if let Some(exp) = s.0.get(x) { if inf == exp { Ok(inf.clone()) } else { - Err(format!("Cannot substitute type '{inf}' with type '{exp}'")) + Err(format!("In {ctx}: Cannot substitute type '{inf}' with type '{exp}'")) } } else { s.0.insert(x.clone(), inf.clone()); @@ -740,22 +738,19 @@ fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { (Type::Integer(t1), Type::Integer(t2)) => { Ok(Type::Integer(Box::new(merge_specialization(t1, t2, s, ctx)?))) } - (Type::Any, Type::Any) - | (Type::U24, Type::U24) - | (Type::F24, Type::F24) - | (Type::I24, Type::I24) - | (Type::None, Type::None) => Ok(inf.clone()), - (_inf, Type::Any) => Ok(Type::Any), - (Type::Any, exp) => Ok(exp.clone()), - _ => Err(format!("Cannot specialize type '{inf}' with type '{exp}'")), + (Type::U24, Type::U24) | (Type::F24, Type::F24) | (Type::I24, Type::I24) | (Type::None, Type::None) => { + Ok(inf.clone()) + } + _ => Err(format!("In {ctx}: Cannot specialize type '{inf}' with type '{exp}'")), }) } let (t, s) = unify(gen, spe, ctx)?; // Merge the inferred specialization with the expected type. // This is done to cast to/from `Any` types. - eprintln!("specialize: {t} ~ {spe}"); - merge_specialization(&t.subst(&s), spe, &mut Subst::default(), ctx) + let mut merge_s = Subst::default(); + let t = merge_specialization(&t.subst(&s), spe, &mut merge_s, ctx)?; + Ok(t.subst(&merge_s)) } impl std::fmt::Display for Subst { diff --git a/src/fun/transform/resolve_type_ctrs.rs b/src/fun/transform/resolve_type_ctrs.rs index bde9c712d..a8ac534d9 100644 --- a/src/fun/transform/resolve_type_ctrs.rs +++ b/src/fun/transform/resolve_type_ctrs.rs @@ -32,7 +32,6 @@ impl Type { Type::Var(nam) => { // If the variable actually refers to a type, we change the type to a constructor. if adts.contains_key(nam) { - eprintln!("found adt: {nam}"); *self = Type::Ctr(nam.clone(), vec![]); } } From 74d200fb92845663314e6149e9e54fcef80118a8 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Wed, 28 Aug 2024 17:39:12 +0200 Subject: [PATCH 11/29] Check that checked functions don't have untyped terms --- src/fun/builtins.bend | 2 +- src/fun/check/check_untyped.rs | 81 ++++++++++++++++++++++++++++++++++ src/fun/check/mod.rs | 1 + src/fun/check/type_check.rs | 49 ++++---------------- src/fun/display.rs | 15 ++++++- src/lib.rs | 7 +-- 6 files changed, 108 insertions(+), 47 deletions(-) create mode 100644 src/fun/check/check_untyped.rs diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index 3a845323e..8e3baacb1 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -106,7 +106,7 @@ def DiffList/append(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)): # Concatenates two difference lists. def DiffList/concat( left: List(T) -> List(T), - right: List(T) -> List(T) + right: List(T) -> List(T) ) -> (List(T) -> List(T)): return lambda x: left(right(x)) diff --git a/src/fun/check/check_untyped.rs b/src/fun/check/check_untyped.rs new file mode 100644 index 000000000..91348ba95 --- /dev/null +++ b/src/fun/check/check_untyped.rs @@ -0,0 +1,81 @@ +use crate::{ + diagnostics::Diagnostics, + fun::{Ctx, FanKind, Pattern, Tag, Term}, + maybe_grow, +}; + +impl Ctx<'_> { + /// Checks that terms that cannot be typed are only used inside untyped functions. + pub fn check_untyped_terms(&mut self) -> Result<(), Diagnostics> { + for def in self.book.defs.values() { + if def.check { + for rule in def.rules.iter() { + if let Err(e) = rule.body.check_untyped_terms() { + self.info.add_rule_error(e, def.name.clone()); + } + } + } + } + self.info.fatal(()) + } +} + +impl Term { + fn check_untyped_terms(&self) -> Result<(), String> { + maybe_grow(|| { + match self { + Term::Lam { tag: Tag::Static, pat, .. } => pat.check_untyped_patterns()?, + Term::Lam { tag: _, .. } => { + return Err("Tagged lambda in type-checked function".to_string()); + } + Term::Link { nam } => { + return Err(format!("Unscoped variable '${nam}' in type-checked function")); + } + Term::App { tag: Tag::Static, .. } => {} + Term::App { tag: _, .. } => { + return Err("Tagged application in type-checked function".to_string()); + } + Term::Fan { fan: FanKind::Dup, .. } => { + return Err("Superposition term in type-checked function".to_string()); + } + Term::Fan { fan: FanKind::Tup, tag: Tag::Static, .. } => {} + Term::Fan { fan: FanKind::Tup, tag: _, .. } => { + return Err("Tagged tuple in type-checked function".to_string()); + } + Term::Let { pat, .. } => { + pat.check_untyped_patterns()?; + } + _ => {} + } + for child in self.children() { + child.check_untyped_terms()?; + } + Ok(()) + }) + } +} + +impl Pattern { + fn check_untyped_patterns(&self) -> Result<(), String> { + maybe_grow(|| { + match self { + Pattern::Chn(x) => { + return Err(format!("Unscoped variable bind '${x}' in type-checked function")); + } + Pattern::Fan(FanKind::Dup, Tag::Auto, _) => {} + Pattern::Fan(FanKind::Dup, _, _) => { + return Err("Tagged duplication in type-checked function".to_string()); + } + Pattern::Fan(FanKind::Tup, Tag::Static, _) => {} + Pattern::Fan(FanKind::Tup, _, _) => { + return Err("Tagged tuple elimination in type-checked function".to_string()); + } + _ => {} + } + for pat in self.children() { + pat.check_untyped_patterns()?; + } + Ok(()) + }) + } +} diff --git a/src/fun/check/mod.rs b/src/fun/check/mod.rs index 9690e250b..21a8e2c8f 100644 --- a/src/fun/check/mod.rs +++ b/src/fun/check/mod.rs @@ -1,3 +1,4 @@ +pub mod check_untyped; pub mod set_entrypoint; pub mod shared_names; pub mod type_check; diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index 5a8dd1603..22ee1d041 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -26,7 +26,7 @@ pub type ProgramTypes = BTreeMap; struct Scheme(Vec, Type); /// A finite mapping from type variables to types. -#[derive(Clone, Default, Debug)] +#[derive(Clone, Default)] struct Subst(BTreeMap); /// A mapping from term variables to type schemes. @@ -77,23 +77,6 @@ impl Type { let vars = vars_t.difference(&vars_env).cloned().collect(); Scheme(vars, self.clone()) } - - fn refresh_vars(&mut self, s: &mut Subst, var_gen: &mut VarGen) { - maybe_grow(|| { - if let Type::Var(x) = self { - if let Some(y) = s.0.get(x) { - *self = y.clone(); - } else { - let y = var_gen.fresh(); - s.0.insert(x.clone(), y.clone()); - *self = y; - } - } - for child in self.children_mut() { - child.refresh_vars(s, var_gen); - } - }) - } } impl Scheme { @@ -119,17 +102,6 @@ impl Scheme { let subst = Subst(self.0.iter().cloned().zip(new_vars).collect()); self.1.subst(&subst) } - - fn refresh_vars(&mut self) { - let mut s = Subst::default(); - let mut var_gen = VarGen::default(); - for x in self.0.iter_mut() { - let y = var_gen.fresh_name(); - s.0.insert(x.clone(), Type::Var(y.clone())); - *x = y; - } - self.1.refresh_vars(&mut s, &mut var_gen); - } } impl Subst { @@ -344,18 +316,15 @@ fn infer_group( } let ts = ts.into_iter().map(|t| t.subst(&s)).collect::>(); - // Specialize against the expected type + // Specialize against the expected type, then generalize and store. for ((name, exp_t), inf_t) in group.iter().zip(exp_ts.iter()).zip(ts.iter()) { - types.insert(name.clone(), specialize(inf_t, exp_t, &book.defs[name].rule().body)?); - } - - // Generalize the function types - *env = env.subst(&s); - for name in group { - let mut scheme = types[name].generalize(&TypeEnv::default()); - scheme.refresh_vars(); + let t = specialize(inf_t, exp_t, &book.defs[name].rule().body)?; + let scheme = t.generalize(&TypeEnv::default()); + let t = scheme.instantiate(&mut VarGen::default()); env.insert(name.clone(), scheme); + types.insert(name.clone(), t); } + Ok(()) } @@ -382,7 +351,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let var_t = tv.subst(&s); Ok((s, Type::Arr(Box::new(var_t), Box::new(bod_t)))) } - _ => unreachable!(), + _ => unreachable!("{}", term), }, Term::App { tag: Tag::Static, fun, arg } => { let (s1, fun_t) = infer(env, book, fun, var_gen)?; @@ -698,7 +667,6 @@ fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { (t, Type::Hole) => Ok(t.clone()), (_inf, Type::Any) => Ok(Type::Any), (Type::Any, exp) => Ok(exp.clone()), - (inf, Type::Var(x)) => { if let Some(exp) = s.0.get(x) { if inf == exp { @@ -711,6 +679,7 @@ fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { Ok(inf.clone()) } } + (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { let l = merge_specialization(l1, l2, s, ctx)?; let r = merge_specialization(r1, r2, s, ctx)?; diff --git a/src/fun/display.rs b/src/fun/display.rs index f44958a82..b51cc1d45 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -296,7 +296,7 @@ impl fmt::Display for Type { maybe_grow(|| match self { Type::Hole => write!(f, "_"), Type::Var(nam) => write!(f, "{nam}"), - Type::Arr(lft, rgt) => write!(f, "({lft} -> {rgt})"), + Type::Arr(lft, rgt) => write!(f, "({} -> {})", lft, rgt.display_arrow()), Type::Ctr(nam, args) => { if args.is_empty() { write!(f, "{nam}") @@ -316,6 +316,19 @@ impl fmt::Display for Type { } } +impl Type { + pub fn display_arrow(&self) -> impl fmt::Display + '_ { + maybe_grow(|| { + DisplayFn(move |f| match self { + Type::Arr(lft, rgt) => { + write!(f, "{} -> {}", lft, rgt.display_arrow()) + } + _ => write!(f, "{}", self), + }) + }) + } +} + fn var_as_str(nam: &Option) -> &str { nam.as_ref().map_or("*", Name::deref) } diff --git a/src/lib.rs b/src/lib.rs index 7236fc95e..88705ccbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -170,12 +170,9 @@ pub fn desugar_book( } pub fn type_check_book(ctx: &mut Ctx) -> Result<(), Diagnostics> { - let old_book = std::mem::replace(ctx.book, ctx.book.clone()); - //ctx.make_native_defs(); + ctx.check_untyped_terms()?; ctx.resolve_type_ctrs()?; - let res = ctx.type_check(); - *ctx.book = old_book; - res?; + ctx.type_check()?; Ok(()) } From 1ae3289d229cbd4e7e7578d8e636c74126657234 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 29 Aug 2024 14:55:20 +0200 Subject: [PATCH 12/29] Improve type errs, add unchecked fn syntax --- src/fun/check/type_check.rs | 124 ++++++++++++++++++++---------------- src/fun/display.rs | 2 +- src/fun/parser.rs | 13 ++-- src/imp/parser.rs | 8 ++- 4 files changed, 81 insertions(+), 66 deletions(-) diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index 22ee1d041..1320738ee 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -3,14 +3,15 @@ //! Based on https://github.com/developedby/algorithm-w-rs //! and https://github.com/mgrabmueller/AlgorithmW. use crate::{ + diagnostics::Diagnostics, fun::{num_to_name, Adt, Book, Ctx, FanKind, MatchRule, Name, Num, Op, Pattern, Tag, Term, Type}, maybe_grow, }; use std::collections::{BTreeMap, BTreeSet}; impl Ctx<'_> { - pub fn type_check(&mut self) -> Result<(), String> { - let types = infer_book(self.book)?; + pub fn type_check(&mut self) -> Result<(), Diagnostics> { + let types = infer_book(self.book, &mut self.info)?; for (nam, typ) in types { eprintln!("{nam}: {typ}"); @@ -220,8 +221,8 @@ impl RecGroups { // Build the dependency graph let mut deps = DependencyGraph::default(); for (name, def) in &book.defs { - if book.ctrs.contains_key(name) { - // Don't infer types for constructors + if book.ctrs.contains_key(name) || !def.check { + // Don't infer types for constructors or unchecked functions continue; } let mut fn_deps = Default::default(); @@ -246,7 +247,7 @@ impl RecGroups { } /* Inference, unification and type checking */ -fn infer_book(book: &Book) -> Result { +fn infer_book(book: &Book, diags: &mut Diagnostics) -> Result { let groups = RecGroups::from_book(book); let mut env = TypeEnv::default(); let mut types = BTreeMap::new(); @@ -275,7 +276,7 @@ fn infer_book(book: &Book) -> Result { // Infer the types of regular functions. for group in &groups.0 { - infer_group(&mut env, book, group, &mut types)?; + infer_group(&mut env, book, group, &mut types, diags)?; } Ok(types) } @@ -285,7 +286,8 @@ fn infer_group( book: &Book, group: &[Name], types: &mut ProgramTypes, -) -> Result<(), String> { + diags: &mut Diagnostics, +) -> Result<(), Diagnostics> { let var_gen = &mut VarGen::default(); // Generate fresh type variables for each function in the group. let tvs = group.iter().map(|_| var_gen.fresh()).collect::>(); @@ -299,7 +301,10 @@ fn infer_group( let mut exp_ts = vec![]; for name in group { let def = &book.defs[name]; - let (s, t) = infer(env, book, &def.rule().body, var_gen)?; + let (s, t) = infer(env, book, &def.rule().body, var_gen).map_err(|e| { + diags.add_rule_error(e, name.clone()); + std::mem::take(diags) + })?; let t = t.subst(&s); ss.push(s); inf_ts.push(t); @@ -310,7 +315,7 @@ fn infer_group( let mut s = ss.iter().fold(Subst::default(), |s, s2| s.compose(s2)); let mut ts = vec![]; for ((bod_t, tv), nam) in inf_ts.into_iter().zip(tvs.iter()).zip(group.iter()) { - let (t, s2) = unify(&tv.subst(&s), &bod_t, &book.defs[nam].rule().body)?; + let (t, s2) = unify_term(&tv.subst(&s), &bod_t, &book.defs[nam].rule().body)?; ts.push(t); s = s.compose(&s2); } @@ -318,14 +323,18 @@ fn infer_group( // Specialize against the expected type, then generalize and store. for ((name, exp_t), inf_t) in group.iter().zip(exp_ts.iter()).zip(ts.iter()) { - let t = specialize(inf_t, exp_t, &book.defs[name].rule().body)?; + let t = specialize(inf_t, exp_t).map_err(|e| { + diags.add_rule_error(e, name.clone()); + std::mem::take(diags) + })?; let scheme = t.generalize(&TypeEnv::default()); let t = scheme.instantiate(&mut VarGen::default()); env.insert(name.clone(), scheme); + eprintln!("{name}: {t}"); types.insert(name.clone(), t); } - Ok(()) + diags.fatal(()) } /// Infer the type of a term in the given environment. @@ -357,7 +366,8 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let (s1, fun_t) = infer(env, book, fun, var_gen)?; let (s2, arg_t) = infer(&env.subst(&s1), book, arg, var_gen)?; let app_t = var_gen.fresh(); - let (_, s3) = unify(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())), term)?; + let (_, s3) = + unify_term(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())), fun)?; Ok((s3.compose(&s2).compose(&s1), app_t.subst(&s3))) } Term::Let { pat, val, nxt } => match pat.as_ref() { @@ -390,7 +400,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul } let (s2, nxt_t) = infer(&env.subst(&s1), book, nxt, var_gen)?; let tvs = tvs.into_iter().map(|tv| tv.subst(&s2)).collect::>(); - let (_, s3) = unify(&val_t, &Type::Tup(tvs), val)?; + let (_, s3) = unify_term(&val_t, &Type::Tup(tvs), val)?; Ok((s3.compose(&s2).compose(&s1), nxt_t)) } Pattern::Fan(FanKind::Dup, Tag::Auto, els) => { @@ -412,7 +422,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul } let (mut s2, nxt_t) = infer(&env.subst(&s1), book, nxt, var_gen)?; for tv in tvs { - let (val_t_, s) = unify(&val_t, &tv.subst(&s2), val)?; + let (val_t_, s) = unify_term(&val_t, &tv.subst(&s2), val)?; val_t = val_t_; s2 = s2.compose(&s); } @@ -436,7 +446,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let (s2, nxt_t) = infer_match_cases(&env.subst(&s1), book, adt, arms, &adt_s, var_gen)?; // Unify the inferred type with the expected type - let (_, s3) = unify(&t1, &adt_t.subst(&s2), arg)?; + let (_, s3) = unify_term(&t1, &adt_t.subst(&s2), arg)?; Ok((s3.compose(&s2).compose(&s1), nxt_t)) } @@ -451,33 +461,33 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul Term::Oper { opr, fst, snd } => { let (s1, t1) = infer(env, book, fst, var_gen)?; let (s2, t2) = infer(&env.subst(&s1), book, snd, var_gen)?; - let (t2, s3) = unify(&t2.subst(&s1), &t1.subst(&s2), term)?; + let (t2, s3) = unify_term(&t2.subst(&s1), &t1.subst(&s2), term)?; let s_args = s3.compose(&s2).compose(&s1); let t_args = t2.subst(&s_args); // Check numeric type matches the operation let tv = var_gen.fresh(); let (t_opr, s_opr) = match opr { // Any numeric type - Op::ADD | Op::SUB | Op::MUL | Op::DIV => { - unify(&t_args, &Type::Number(Box::new(tv.clone())), term)? + Op::ADD | Op::SUB | Op::MUL | Op::DIV => { + unify_term(&t_args, &Type::Number(Box::new(tv.clone())), term)? } Op::EQ | Op::NEQ | Op::LT | Op::GT | Op::GE | Op::LE => { - let (_, s) = unify(&t_args, &Type::Number(Box::new(tv.clone())), term)?; + let (_, s) = unify_term(&t_args, &Type::Number(Box::new(tv.clone())), term)?; (Type::U24, s) } // Integers Op::REM | Op::AND | Op::OR | Op::XOR | Op::SHL | Op::SHR => { - unify(&t_args, &Type::Integer(Box::new(tv.clone())), term)? + unify_term(&t_args, &Type::Integer(Box::new(tv.clone())), term)? } // Floating - Op::POW => unify(&t_args, &Type::F24, term)?, + Op::POW => unify_term(&t_args, &Type::F24, term)?, }; Ok((s_opr.compose(&s_args), t_opr.subst(&s_opr))) } Term::Swt { bnd: _, arg, with_bnd: _, with_arg: _, pred, arms } => { debug_assert!(arms.len() == 2); let (s1, t1) = infer(env, book, arg, var_gen)?; - let (_, s2) = unify(&t1, &Type::U24, arg)?; + let (_, s2) = unify_term(&t1, &Type::U24, arg)?; let s_arg = s2.compose(&s1); let mut env = env.subst(&s_arg); @@ -487,7 +497,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul } let (s_succ, t_succ) = infer(&env, book, &arms[1], var_gen)?; let s_arms = s_succ.compose(&s_zero); - let (t_swt, s_swt) = unify(&t_zero.subst(&s_arms), &t_succ.subst(&s_arms), term)?; + let (t_swt, s_swt) = unify_term(&t_zero.subst(&s_arms), &t_succ.subst(&s_arms), term)?; Ok((s_swt.compose(&s_arms).compose(&s_arg), t_swt.subst(&s_swt))) } @@ -559,7 +569,7 @@ fn infer_match_cases( // Recurse and unify with the other arms. let (s_rest, t_rest) = infer_match_cases(env, book, adt, rest, adt_s, var_gen)?; - let (t_final, s_final) = unify(&t1, &t_rest, bod)?; + let (t_final, s_final) = unify_term(&t1, &t_rest, bod)?; Ok((s_final.compose(&s_rest).compose(&s2).compose(&s1), t_final)) } else { @@ -569,7 +579,7 @@ fn infer_match_cases( } fn unify_fields<'a>(ts: impl Iterator, ctx: &Term) -> Result { - let ss = ts.map(|(inf, exp)| unify(inf, exp, ctx)).collect::, _>>()?; + let ss = ts.map(|(inf, exp)| unify_term(inf, exp, ctx)).collect::, _>>()?; let mut s = Subst::default(); for (_, s2) in ss.into_iter().rev() { s = s.compose(&s2); @@ -577,7 +587,14 @@ fn unify_fields<'a>(ts: impl Iterator, ctx: &Term) Ok(s) } -fn unify(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { +fn unify_term(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { + match unify(t1, t2) { + Ok((t, s)) => Ok((t, s)), + Err(msg) => Err(format!("In {ctx}: Can't unify '{t1}' and '{t2}'.{msg}")), + } +} + +fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { maybe_grow(|| match (t1, t2) { (t, Type::Hole) | (Type::Hole, t) => Ok((t.clone(), Subst::default())), (t, Type::Var(x)) | (Type::Var(x), t) => { @@ -590,22 +607,20 @@ fn unify(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { } // Occurs check if t.free_type_vars().contains(x) { - return Err(format!( - "In {ctx}: Cannot unify variable '{x}' with type '{t}' because it occurs as a free variable" - )); + return Err(format!(" Variable '{x}' occurs in '{t}'")); } Ok((t.clone(), Subst(BTreeMap::from([(x.clone(), t.clone())])))) } (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { - let (t1, s1) = unify(l1, l2, ctx)?; - let (t2, s2) = unify(&r1.subst(&s1), &r2.subst(&s1), ctx)?; + let (t1, s1) = unify(l1, l2)?; + let (t2, s2) = unify(&r1.subst(&s1), &r2.subst(&s1))?; Ok((Type::Arr(Box::new(t1), Box::new(t2)), s2.compose(&s1))) } (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { let mut s = Subst::default(); let mut ts = vec![]; for (t1, t2) in ts1.iter().zip(ts2.iter()) { - let (t, s2) = unify(t1, t2, ctx)?; + let (t, s2) = unify(t1, t2)?; ts.push(t); s = s.compose(&s2); } @@ -615,7 +630,7 @@ fn unify(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { let mut s = Subst::default(); let mut ts = vec![]; for (t1, t2) in els1.iter().zip(els2.iter()) { - let (t, s2) = unify(t1, t2, ctx)?; + let (t, s2) = unify(t1, t2)?; ts.push(t); s = s.compose(&s2); } @@ -626,20 +641,20 @@ fn unify(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { | (Type::I24, Type::I24) | (Type::None, Type::None)) => Ok((t.0.clone(), Subst::default())), (Type::Number(t1), Type::Number(t2)) => { - let (t, s) = unify(t1, t2, ctx)?; + let (t, s) = unify(t1, t2)?; Ok((Type::Number(Box::new(t)), s)) } (Type::Number(tn), Type::Integer(ti)) | (Type::Integer(ti), Type::Number(tn)) => { - let (t, s) = unify(ti, tn, ctx)?; + let (t, s) = unify(ti, tn)?; Ok((Type::Integer(Box::new(t)), s)) } (Type::Integer(t1), Type::Integer(t2)) => { - let (t, s) = unify(t1, t2, ctx)?; + let (t, s) = unify(t1, t2)?; Ok((Type::Integer(Box::new(t)), s)) } (Type::Number(t1) | Type::Integer(t1), t2 @ (Type::U24 | Type::I24 | Type::F24)) | (t2 @ (Type::U24 | Type::I24 | Type::F24), Type::Number(t1) | Type::Integer(t1)) => { - let (t, s) = unify(t1, t2, ctx)?; + let (t, s) = unify(t1, t2)?; Ok((t, s)) } @@ -647,21 +662,21 @@ fn unify(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { let mut s = Subst::default(); // Recurse to assign variables to `Any` as well for child in t.children() { - let (_, s2) = unify(&Type::Any, child, ctx)?; + let (_, s2) = unify(&Type::Any, child)?; s = s2.compose(&s); } Ok((Type::Any, s)) } - _ => Err(format!("In {ctx}: Types do not unify: '{t1}' and '{t2}'")), + _ => Err(String::new()), }) } -/// Specializes a type against another type. +/// Specializes the inferred type against the type annotation. /// /// Errors if the first type is not a superset of the second type. -fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { - fn merge_specialization(inf: &Type, exp: &Type, s: &mut Subst, ctx: &Term) -> Result { +fn specialize(inf: &Type, ann: &Type) -> Result { + fn merge_specialization(inf: &Type, exp: &Type, s: &mut Subst) -> Result { maybe_grow(|| match (inf, exp) { // These rules have to come before (t, Type::Hole) => Ok(t.clone()), @@ -672,7 +687,7 @@ fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { if inf == exp { Ok(inf.clone()) } else { - Err(format!("In {ctx}: Cannot substitute type '{inf}' with type '{exp}'")) + Err(format!(" Cannot substitute type '{inf}' with type '{exp}'")) } } else { s.0.insert(x.clone(), inf.clone()); @@ -681,14 +696,14 @@ fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { } (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { - let l = merge_specialization(l1, l2, s, ctx)?; - let r = merge_specialization(r1, r2, s, ctx)?; + let l = merge_specialization(l1, l2, s)?; + let r = merge_specialization(r1, r2, s)?; Ok(Type::Arr(Box::new(l), Box::new(r))) } (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { let mut ts = vec![]; for (t1, t2) in ts1.iter().zip(ts2.iter()) { - let t = merge_specialization(t1, t2, s, ctx)?; + let t = merge_specialization(t1, t2, s)?; ts.push(t); } Ok(Type::Ctr(name1.clone(), ts)) @@ -696,29 +711,26 @@ fn specialize(gen: &Type, spe: &Type, ctx: &Term) -> Result { (Type::Tup(ts1), Type::Tup(ts2)) if ts1.len() == ts2.len() => { let mut ts = vec![]; for (t1, t2) in ts1.iter().zip(ts2.iter()) { - let t = merge_specialization(t1, t2, s, ctx)?; + let t = merge_specialization(t1, t2, s)?; ts.push(t); } Ok(Type::Tup(ts)) } - (Type::Number(t1), Type::Number(t2)) => { - Ok(Type::Number(Box::new(merge_specialization(t1, t2, s, ctx)?))) - } - (Type::Integer(t1), Type::Integer(t2)) => { - Ok(Type::Integer(Box::new(merge_specialization(t1, t2, s, ctx)?))) - } + (Type::Number(t1), Type::Number(t2)) => Ok(Type::Number(Box::new(merge_specialization(t1, t2, s)?))), + (Type::Integer(t1), Type::Integer(t2)) => Ok(Type::Integer(Box::new(merge_specialization(t1, t2, s)?))), (Type::U24, Type::U24) | (Type::F24, Type::F24) | (Type::I24, Type::I24) | (Type::None, Type::None) => { Ok(inf.clone()) } - _ => Err(format!("In {ctx}: Cannot specialize type '{inf}' with type '{exp}'")), + _ => Err(format!(" Cannot specialize type '{inf}' with type '{exp}'")), }) } - let (t, s) = unify(gen, spe, ctx)?; + let (t, s) = unify(inf, ann) + .map_err(|e| format!("Type Error: Expected function type '{ann}' but found '{inf}'.{e}"))?; // Merge the inferred specialization with the expected type. // This is done to cast to/from `Any` types. let mut merge_s = Subst::default(); - let t = merge_specialization(&t.subst(&s), spe, &mut merge_s, ctx)?; + let t = merge_specialization(&t.subst(&s), ann, &mut merge_s)?; Ok(t.subst(&merge_s)) } diff --git a/src/fun/display.rs b/src/fun/display.rs index b51cc1d45..36ee63d88 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -222,7 +222,7 @@ impl Rule { impl fmt::Display for Definition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { namegen_reset(); - writeln!(f, "{}: {}", self.name, self.typ)?; + writeln!(f, "{}{}: {}", if !self.check { "unchecked " } else { "" }, self.name, self.typ)?; write!(f, "{}", DisplayJoin(|| self.rules.iter().map(|x| x.display(&self.name)), "\n")) } } diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 95227322e..7215ef4bb 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -275,7 +275,7 @@ impl<'a> FunParser<'a> { let ini_idx = *self.index(); // Try to parse signature - if let Ok((name, args, typ)) = self.parse_def_sig() { + if let Ok((name, args, check, typ)) = self.parse_def_sig() { if self.try_consume("=") { // Single rule with signature let body = self.parse_term()?; @@ -283,7 +283,7 @@ impl<'a> FunParser<'a> { let rules = vec![Rule { pats, body }]; let end_idx = *self.index(); let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); - let def = FunDefinition { name, typ, check: true, rules, source }; + let def = FunDefinition { name, typ, check, rules, source }; Ok(def) } else { // Multiple rules with signature @@ -296,12 +296,12 @@ impl<'a> FunParser<'a> { } let end_idx = *self.index(); let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); - let def = FunDefinition { name, typ, check: true, rules, source }; + let def = FunDefinition { name, typ, check, rules, source }; Ok(def) } } else { // Was not a signature, backtrack and read the name from the first rule - self.index = ini_idx; + self.index = ini_idx; let mut rules = vec![]; let (name, rule) = self.parse_rule()?; rules.push(rule); @@ -318,9 +318,10 @@ impl<'a> FunParser<'a> { /// Parses a function definition signature. /// Returns the name, name of the arguments and the type of the function. - fn parse_def_sig(&mut self) -> ParseResult<(Name, Vec, Type)> { + fn parse_def_sig(&mut self) -> ParseResult<(Name, Vec, bool, Type)> { // '(' name ((arg | '(' arg (':' type)? ')'))* ')' ':' type // name ((arg | '(' arg (':' type)? ')'))* ':' type + let check = !self.try_parse_keyword("unchecked"); let (name, args, typ) = if self.try_consume("(") { let name = self.parse_top_level_name()?; let args = self.list_like(|p| p.parse_def_sig_arg(), "", ")", "", false, 0)?; @@ -335,7 +336,7 @@ impl<'a> FunParser<'a> { }; let (args, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip(); let typ = make_fn_type(&arg_types, typ); - Ok((name, args, typ)) + Ok((name, args, check, typ)) } fn parse_def_sig_arg(&mut self) -> ParseResult<(Name, Type)> { diff --git a/src/imp/parser.rs b/src/imp/parser.rs index 8a2ac7bed..3e08ec26a 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -29,7 +29,7 @@ impl<'a> ImpParser<'a> { return self.with_ctx(Err(msg), idx..idx + 1); } // TODO: checked vs unchecked functions - let (mut def, nxt_indent) = self.parse_def_aux(indent, true)?; + let (mut def, nxt_indent) = self.parse_def_aux(indent)?; def.source.kind = if self.builtin { SourceKind::Builtin } else { SourceKind::User }; Ok((def, nxt_indent)) } @@ -1150,7 +1150,7 @@ impl<'a> ImpParser<'a> { fn parse_local_def(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { // TODO: checked vs unchecked functions - let (mut def, mut nxt_indent) = self.parse_def_aux(*indent, true)?; + let (mut def, mut nxt_indent) = self.parse_def_aux(*indent)?; def.source.kind = if self.builtin { SourceKind::Builtin } else { SourceKind::Generated }; let (nxt, nxt_indent) = self.parse_statement(&mut nxt_indent)?; let stmt = Stmt::LocalDef { def: Box::new(def), nxt: Box::new(nxt) }; @@ -1230,11 +1230,13 @@ impl<'a> ImpParser<'a> { }) } - fn parse_def_aux(&mut self, mut indent: Indent, check: bool) -> ParseResult<(Definition, Indent)> { + fn parse_def_aux(&mut self, mut indent: Indent) -> ParseResult<(Definition, Indent)> { let ini_idx = *self.index(); self.parse_keyword("def")?; self.skip_trivia_inline()?; + let check = !self.try_parse_keyword("unchecked"); + let name = self.parse_top_level_name()?; self.skip_trivia_inline()?; From 4754445cb673bf750e1f90d3685bbdfacdb2193c Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 29 Aug 2024 19:06:24 +0200 Subject: [PATCH 13/29] Fix parsing of unchecked fun functions --- src/fun/check/type_check.rs | 5 +---- src/fun/parser.rs | 6 ++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index 1320738ee..9d79d168b 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -330,7 +330,6 @@ fn infer_group( let scheme = t.generalize(&TypeEnv::default()); let t = scheme.instantiate(&mut VarGen::default()); env.insert(name.clone(), scheme); - eprintln!("{name}: {t}"); types.insert(name.clone(), t); } @@ -366,8 +365,7 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul let (s1, fun_t) = infer(env, book, fun, var_gen)?; let (s2, arg_t) = infer(&env.subst(&s1), book, arg, var_gen)?; let app_t = var_gen.fresh(); - let (_, s3) = - unify_term(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())), fun)?; + let (_, s3) = unify_term(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())), fun)?; Ok((s3.compose(&s2).compose(&s1), app_t.subst(&s3))) } Term::Let { pat, val, nxt } => match pat.as_ref() { @@ -560,7 +558,6 @@ fn infer_match_cases( case_env.insert(var.clone(), Scheme(vec![], tv.clone())); } } - // Infer the body and unify the inferred field types with the expected. let (s1, t1) = infer(&case_env, book, bod, var_gen)?; let inf_ts = tvs.into_iter().map(|tv| tv.subst(&s1)).collect::>(); diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 7215ef4bb..679dd3e76 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -301,7 +301,9 @@ impl<'a> FunParser<'a> { } } else { // Was not a signature, backtrack and read the name from the first rule - self.index = ini_idx; + self.index = ini_idx; + let check = !self.try_parse_keyword("unchecked"); + self.skip_trivia(); let mut rules = vec![]; let (name, rule) = self.parse_rule()?; rules.push(rule); @@ -311,7 +313,7 @@ impl<'a> FunParser<'a> { } let end_idx = *self.index(); let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); - let def = FunDefinition { name, typ: Type::Any, check: true, rules, source }; + let def = FunDefinition { name, typ: Type::Any, check, rules, source }; Ok(def) } } From e8687f48829553bcfb63f6a8142ce4f930cbc452 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 29 Aug 2024 22:46:17 +0200 Subject: [PATCH 14/29] Add infer for switches with more than 2 arms, fix specialization --- src/fun/check/type_check.rs | 66 +++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index 9d79d168b..66a152688 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -483,21 +483,35 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul Ok((s_opr.compose(&s_args), t_opr.subst(&s_opr))) } Term::Swt { bnd: _, arg, with_bnd: _, with_arg: _, pred, arms } => { - debug_assert!(arms.len() == 2); let (s1, t1) = infer(env, book, arg, var_gen)?; let (_, s2) = unify_term(&t1, &Type::U24, arg)?; let s_arg = s2.compose(&s1); let mut env = env.subst(&s_arg); - let (s_zero, t_zero) = infer(&env, book, &arms[0], var_gen)?; + let mut ss_nums = vec![]; + let mut ts_nums = vec![]; + for arm in arms.iter().rev().skip(1) { + let (s, t) = infer(&env, book, arm, var_gen)?; + ss_nums.push(s); + ts_nums.push(t); + } if let Some(pred) = pred { env.insert(pred.clone(), Scheme(vec![], Type::U24)); } let (s_succ, t_succ) = infer(&env, book, &arms[1], var_gen)?; - let s_arms = s_succ.compose(&s_zero); - let (t_swt, s_swt) = unify_term(&t_zero.subst(&s_arms), &t_succ.subst(&s_arms), term)?; - Ok((s_swt.compose(&s_arms).compose(&s_arg), t_swt.subst(&s_swt))) + let s_arms = ss_nums.into_iter().fold(s_succ, |acc, s| acc.compose(&s)); + let mut t_swt = t_succ; + let mut s_swt = Subst::default(); + for t_num in ts_nums { + let (t, s) = unify_term(&t_swt, &t_num, term)?; + t_swt = t; + s_swt = s.compose(&s_swt); + } + + let s = s_swt.compose(&s_arms).compose(&s_arg); + let t = t_swt.subst(&s); + Ok((s, t)) } Term::Fan { fan: FanKind::Tup, tag: Tag::Static, els } => { @@ -670,6 +684,10 @@ fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { } /// Specializes the inferred type against the type annotation. +/// This way, the annotation can be less general than the inferred type. +/// +/// It also forces inferred 'Any' to the annotated, inferred types to +/// annotated 'Any' and fills 'Hole' with the inferred type. /// /// Errors if the first type is not a superset of the second type. fn specialize(inf: &Type, ann: &Type) -> Result { @@ -677,18 +695,21 @@ fn specialize(inf: &Type, ann: &Type) -> Result { maybe_grow(|| match (inf, exp) { // These rules have to come before (t, Type::Hole) => Ok(t.clone()), + (Type::Hole, _) => unreachable!("Hole should never appear in the inferred type"), + (_inf, Type::Any) => Ok(Type::Any), (Type::Any, exp) => Ok(exp.clone()), - (inf, Type::Var(x)) => { - if let Some(exp) = s.0.get(x) { - if inf == exp { - Ok(inf.clone()) + + (Type::Var(x), new) => { + if let Some(old) = s.0.get(x) { + if old == new { + Ok(new.clone()) } else { - Err(format!(" Cannot substitute type '{inf}' with type '{exp}'")) + Err(format!(" Inferred type variable '{x}' must be both '{old}' and '{new}'")) } } else { - s.0.insert(x.clone(), inf.clone()); - Ok(inf.clone()) + s.0.insert(x.clone(), new.clone()); + Ok(new.clone()) } } @@ -718,17 +739,28 @@ fn specialize(inf: &Type, ann: &Type) -> Result { (Type::U24, Type::U24) | (Type::F24, Type::F24) | (Type::I24, Type::I24) | (Type::None, Type::None) => { Ok(inf.clone()) } - _ => Err(format!(" Cannot specialize type '{inf}' with type '{exp}'")), + _ => Err(String::new()), }) } - let (t, s) = unify(inf, ann) + // Refresh the variable names to avoid conflicts when unifying + // Names of type vars in the annotation have nothing to do with names in the inferred type. + let var_gen = &mut VarGen::default(); + let inf2 = inf.generalize(&TypeEnv::default()).instantiate(var_gen); + let ann2 = ann.generalize(&TypeEnv::default()).instantiate(var_gen); + + let (t, s) = unify(&inf2, &ann2) .map_err(|e| format!("Type Error: Expected function type '{ann}' but found '{inf}'.{e}"))?; + let t = t.subst(&s); + // Merge the inferred specialization with the expected type. - // This is done to cast to/from `Any` types. + // This is done to cast to/from `Any` and `_` types. let mut merge_s = Subst::default(); - let t = merge_specialization(&t.subst(&s), ann, &mut merge_s)?; - Ok(t.subst(&merge_s)) + let t2 = merge_specialization(&t, ann, &mut merge_s).map_err(|e| { + format!("Type Error: Annotated type '{ann}' is not a subtype of inferred type '{inf2}'.{e}") + })?; + + Ok(t2.subst(&merge_s)) } impl std::fmt::Display for Subst { From 5394399f890386267db2f3ecf8d6637cad0d9a9e Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 29 Aug 2024 22:59:49 +0200 Subject: [PATCH 15/29] Add typing to examples --- examples/bubble_sort.bend | 14 ++-- examples/callcc.bend | 8 ++- examples/example_fun.bend | 113 +++++++++++++++++++++++++----- examples/fib.bend | 6 +- examples/fusing_not.bend | 17 +++-- examples/gen_tree.bend | 8 +-- examples/hello_world.bend | 2 +- examples/insertion_sort.bend | 12 ++-- examples/list.bend | 130 ++++++++++++++++------------------- examples/parallel_and.bend | 9 +-- examples/parallel_sum.bend | 10 +-- examples/queue.bend | 2 +- examples/quick_sort.bend | 31 +++++---- examples/radix_sort.bend | 123 ++++++++++++++++----------------- src/fun/builtins.bend | 29 ++++---- 15 files changed, 289 insertions(+), 225 deletions(-) diff --git a/examples/bubble_sort.bend b/examples/bubble_sort.bend index 4a0cf6f36..d3c05ade2 100644 --- a/examples/bubble_sort.bend +++ b/examples/bubble_sort.bend @@ -1,28 +1,26 @@ # Sorts a list -def sort(xs): +def sort(xs: List(u24)) -> List(u24): match xs: case List/Nil: return List/Nil case List/Cons: return insert(xs.head, sort(xs.tail)) -# Insert : U60 -> List -> List -def insert(v, xs): +def insert(v: u24, xs: List(u24)) -> List(u24): match xs: case List/Nil: return List/Cons(v, List/Nil) case List/Cons: return swap_gt(v, xs.head, xs.tail) -# SwapGT : U60 -> U60 -> U60 -> List -> List -def swap_gt(v, x, xs): +def swap_gt(v: u24, x: u24, xs: List(u24)) -> List(u24): if x > v: return List/Cons(v, List/Cons(x, xs)) else: return List/Cons(x, insert(v, xs)) # Generates a list of 'n' random u24 numbers using xorshift -def rnd(n): +def rnd(n: u24) -> List(u24): bend n, state=1: when n != 0: state = state ^ (state << 13) @@ -33,13 +31,13 @@ def rnd(n): return List/Nil # Sums a list of u24 numbers -def sum(xs): +def sum(xs: List(u24)) -> u24: fold xs: case List/Nil: return 0 case List/Cons: return xs.head + xs.tail -def main: +def main() -> u24: n = 100 return sum(sort(rnd(n))) diff --git a/examples/callcc.bend b/examples/callcc.bend index 57c484316..c21026062 100644 --- a/examples/callcc.bend +++ b/examples/callcc.bend @@ -1,16 +1,18 @@ # This program is an example that shows how scopeless lambdas can be used. -Seq a b = a +Seq (a: A) (b: B) : A = a # Create a program capable of using `callcc` -CC.lang = λprogram +unchecked CC.lang : ((((a -> b) -> b) -> c) -> c) -> d = +λprogram let callcc = λcallback (λ$garbage($hole) (callback λ$hole(0))); let result = (program callcc); let garbage = $garbage; # Discard `$garbage`, which is the value returned by `callback` (Seq result garbage) -main = (CC.lang λcallcc +main: u24 = (CC.lang λcallcc # This code calls `callcc`, then calls `k` to fill the hole with `42`. # This means that the call to callcc returns `42`, and the program returns `52` (+ 10 (callcc λk(+ (k 42) 1729))) ) + \ No newline at end of file diff --git a/examples/example_fun.bend b/examples/example_fun.bend index 3357091a0..0ac50c696 100644 --- a/examples/example_fun.bend +++ b/examples/example_fun.bend @@ -1,23 +1,38 @@ # Example of some things you can do with the 'fun' syntax -# Write definitions like this +# Define functions like this: +# By default they accept and return any type (Def1) = ((λa a) (λb b)) -# You can call a definition by just referencing its name -# It will be substituted in place of the reference +# You can call a definition by just referencing its name. +# It will be substituted in place of the reference. (Def2) = ((λa a) Def1) -# Definitions and variables can have names in upper and lower case and contain numbers -# Names defined in a more inner position shadow names in an outer position +# Definitions and variables can have names in upper and lower case and +# contain numbers, '.', '-', '_' and '/'. +# Names defined in a more inner position shadow names in an outer position. (def3) = ((λDef1 Def1) (λx λx x)) -# The language is affine, but if you use a variable more than once the compiler inserts duplications for you -# Of course you can always do them manually -(def4) = λz let {z1 z2} = z; (z1 ((λx (x x x x x)) z2)) +# You can annotate a function with the expected types and Bend will check them. +# The parentheses on the left side of the definition are optional. +const (a: A) (b: B) : A = a -# You can use machine numbers and some native numeric operations -# Numeric operations can only reduce numbers, doing (+ (λx x) 1) will not do anything -(nums) = λx1 λx2 (* (+ x1 1) (/ (- x2 2) 1)) +# There are three types of native numbers available: u24, i24 and f24. +# u24: Unsigned 24-bit integers +# i24: Signed 24-bit integers +# f24: Floating point numbers with 24-bit precision +(unsigneds (x1: u24) (x2: u24)) : u24 = (* (+ x1 1) (/ (- x2 2) 1)) + +# '+' or '-' are mandatory for i24. +(signeds (x1: i24) (x2: i24)) : i24 = (* (+ x1 +1) (/ (- x2 -2) +1)) + +# The '.' is mandatory for f24. +(floats (x1: f24) (x2: f24)) : f24 = (* (+ x1 1.0) (/ (- x2 -2.0) +1.0)) + +# Numeric operations are only meaningful on native numbers. +# You can force the compiler to do it anyway by using untyped values. +id = λx x # 'id' wasn't given a type so it's inferred as 'Any'. +bad_nums : Any = (+ id 1) # You can use numbers on the native match expression # The `+` arm binds the `scrutinee`-1 variable to the value of the number -1 @@ -34,7 +49,7 @@ type Bool = True | False # You can have pattern matching on definitions # Use `*` to ignore a pattern (Option.unwrap_or (Option/Some val) *) = val -(Option.unwrap_or Option/None or) = or +(Option.unwrap_or Option/None or) = or (Bool.or Bool/True *) = Bool/True (Bool.or * Bool/True) = Bool/True @@ -45,17 +60,16 @@ type Bool = True | False match bool { Bool/True: Bool/False Bool/False: Bool/True - } + } # Data types can store values -type Boxed = (Box val) +type Boxed T = (Box (val: T)) -# Types with only one constructor can be destructured using `let` or a single matching definition +# Types with only one constructor can be destructured using `open`, +# a single matching definition or a 'match'. (Box.map (Boxed/Box val) f) = (Boxed/Box (f val)) -(Box.unbox) = λbox match box { - Boxed/Box: box.val -} +(Box.unbox (box: (Boxed T))): T = open Boxed box; box.val # Use tuples to store two values together without needing to create a new data type (Tuple.new fst snd) = @@ -69,6 +83,67 @@ type Boxed = (Box val) let (fst, snd) = pair snd +# You can give type annotations to type definitions as well. +# The recursive fields should be annotated with a '~'. +type ListList T = + (Cons (head: (List T)) ~(tail: (ListList T))) | + Nil + +# Bend functions usually center around recursion +sum (List/Nil) = 0 +sum (List/Cons x xs) = (+ x (sum xs)) + +sum_list (ListList/Nil) = List/Nil +sum_list (ListList/Cons x xs) = (List/Cons (sum x) (sum_list xs)) + +# To create local recursive functions that consume a recursive type, you can use 'fold'. +sum_list2 ll = fold ll { + # The fold is implicitly called for fields marked with '~' in their definition. + # In this case, 'll.tail' is replaced with a recursive call to the fold. + ListList/Cons: (List/Cons (sum ll.head) ll.tail) + ListList/Nil: List/Nil +} + +# To do the opposite and create a recursive structure, you can use 'bend'. +# 'bend' can be seen as a tree-like generalization of a while loop. +new_list = bend x = 0 { + when (< x 10): + # 'fork' calls the bend recursively with the provided values. + (List/Cons x (fork (+ x 1))) + else: + List/Nil +} + +# To make programs that are more parallelizable, you generally want to +# avoid lists and use tree-like structures with multiple recursion instead. +sum_nums from to = + # + if (< from to) { + (+ + (sum_nums from (/ 2 (+ from to))) + (sum_nums (+ 1 (/ 2 (+ from to))) to)) + } else { + from + } + +# Internally, each variable is only used once. +# If you use a variable is used more than once, the compiler will insert duplications for you. +# You can also write them manually with 'let {a b} = ...', +# but then you have to mark the function as unchecked. +unchecked (def4) = λz let {z1 z2} = z; (z1 ((λx (x x x x x)) z2)) + +# Duplicating a term that duplicates a variable is not allowed and will break the program. +map f (List/Cons x xs) = (List/Cons (f x) (map f xs)) # 'f' is duplicated here +map f (List/Nil) = List/Nil + +# 'map' duplicated the lambda which duplicates 'a'. +# Although this is well defined, it does not produce a sound lambda-calculus result. +VeryBad (a: u24) (xs: (List u24)) : (List u24) = +(map + λ x (+ (* a x) a) # 'a' is duplicated here + [1, 2, 3, 4, 5]) + + # All files must have a main definition to run. # You can execute a program in Bend with "bend run " # Other options are "check" to just see if the file is well formed @@ -83,4 +158,4 @@ type Boxed = (Box val) let map = (Box.map box Option.unwrap_or) let unboxed = ((Box.unbox map) snd) - (nums 3 unboxed) + (unsigneds 3 unboxed) diff --git a/examples/fib.bend b/examples/fib.bend index 070c42090..6e37c19be 100644 --- a/examples/fib.bend +++ b/examples/fib.bend @@ -3,7 +3,7 @@ # Calculates fibonacci numbers recursively. # Although branching recursion is usually a good idea to parallelize, # it makes this code run in exponential time. -def fib_recursive(n): +def fib_recursive(n: u24) -> u24: switch n: case 0: return 0 @@ -14,14 +14,14 @@ def fib_recursive(n): # Calculates fibonacci numbers iteratively (tail-recursively). # This function is inherently sequential, but runs in linear time. -def fib_iterative(n): +def fib_iterative(n: u24) -> u24: bend a=0, b=1, n: when n != 0: return fork(b, a + b, n - 1) else: return a -def main(): +def main() -> u24: # With the iterative version, we can calculate large fibonacci numbers # While with the recursive version, we will quickly run out of memory. # Note that for numbers larger than 36 the result will overflow the space of the 24-bit integer. diff --git a/examples/fusing_not.bend b/examples/fusing_not.bend index b6fd4909b..50b535021 100644 --- a/examples/fusing_not.bend +++ b/examples/fusing_not.bend @@ -1,16 +1,19 @@ # Example of a boolean 'not' function that fuses inifinitely through composition.. -true = λt λf t -false = λt λf f +true : a -> b -> a = λt λf t +false : a -> b -> b = λt λf f -not = λboolean (boolean false true) -fusing_not = λboolean λt λf (boolean f t) +not : ((a -> b -> b) -> (c -> d -> c) -> e) -> e = + λboolean (boolean false true) + +fusing_not : (b -> a -> c) -> a -> b -> c = + λboolean λt λf (boolean f t) # Creates a Church numeral out of a native number -to_church n = switch n { +to_church (n: u24) : (a -> a) -> a -> a = switch n { 0: λf λx x _: λf λx (f (to_church n-1 f x)) } -main = +main: _ = # Self-composes `not` 2^23-1 times and prints the result. - ((to_church 0x7FFFFF) fusing_not) # try replacing 'fusing_not' by 'not'. Will it still work? + ((to_church 0x7FFFF) fusing_not) # try replacing 'fusing_not' by 'not'. Will it still work? diff --git a/examples/gen_tree.bend b/examples/gen_tree.bend index f433a5b97..ea2e72ad5 100644 --- a/examples/gen_tree.bend +++ b/examples/gen_tree.bend @@ -1,16 +1,16 @@ # Generates a tree with numbers in the nodes using 'bend' -type MyTree: - Node { val, ~left, ~right } +type MyTree(t): + Node { val: t, ~left: MyTree(t), ~right: MyTree(t) } Leaf -def tree_gen(n, x): +def tree_gen(n: u24, x: u24) -> MyTree(u24): bend n, x: when n != 0: return MyTree/Node(x, fork(n - 1, x*2+1), fork(n - 1, x*2+2)) else: return MyTree/Leaf -def main: +def main -> MyTree(u24): depth = 4 first_val = 1 return tree_gen(depth, first_val) diff --git a/examples/hello_world.bend b/examples/hello_world.bend index 81ad13e50..60f8d13a5 100644 --- a/examples/hello_world.bend +++ b/examples/hello_world.bend @@ -1,4 +1,4 @@ -def main(): +def main() -> IO(u24): with IO: * <- IO/print("Hello, world!\n") return wrap(0) \ No newline at end of file diff --git a/examples/insertion_sort.bend b/examples/insertion_sort.bend index 1119eeca8..667f52178 100644 --- a/examples/insertion_sort.bend +++ b/examples/insertion_sort.bend @@ -1,4 +1,4 @@ -def insertion_sort(xs): +def insertion_sort(xs: List(u24)) -> List(u24): match xs: case List/Nil: return List/Nil @@ -6,32 +6,32 @@ def insertion_sort(xs): return insertion_sort.insert(xs.head, insertion_sort(xs.tail)) # Inserts a value into a partially sorted list -def insertion_sort.insert(v, xs): +def insertion_sort.insert(v: u24, xs: List(u24)) -> List(u24): match xs: case List/Nil: return List/Cons(v, List/Nil) case List/Cons: return insert_aux(v > xs.head, v, xs.head, xs.tail) -def insert_aux(n, v, x, xs): +def insert_aux(n: u24, v: u24, x: u24, xs: List(u24)) -> List(u24): if n == 0: return List/Cons(v, List/Cons(x, xs)) else: return List/Cons(x, insertion_sort.insert(v, xs)) # Generates a list of random numbers -def rnd(n): +def rnd(n: u24) -> List(u24): if n == 0: return List/Nil else: return List/Cons(random(10000 - n), rnd(n - 1)) # Generates a pseudo-random number (not very good) -def random(n): +def random(n: u24) -> u24: if n == 0: return 0 else: return (random(n - 1) * 16 + 101387) % 429453 -def main(): +def main() -> List(u24): return insertion_sort(rnd(10)) diff --git a/examples/list.bend b/examples/list.bend index c3c0c487f..7ce66da29 100644 --- a/examples/list.bend +++ b/examples/list.bend @@ -12,173 +12,159 @@ ########################################### # List clear: -# List l -> list l # clears all elements from list l. This is equivalent to initializing an empty list. -clear = @l [] +clear : (List t) -> (List t) = @l [] # List concat: -# List l -> List l # combines two lists (l1, l2) from left to right. -concat = @l1 @l2 +concat : (List t) -> (List t) -> (List t) = + @l1 @l2 match l1 { List/Cons: (List/Cons l1.head (concat l1.tail l2)) List/Nil: l2 } # List add_front: -# List l -> List l -# adds a non-List element e to the front of list l. -add_front = @l @e +# adds an element e to the front of list l. +add_front : (List t) -> t -> (List t) = + @l @e match l { List/Cons: (List/Cons e l) List/Nil: (List/Cons e List/Nil) } # List append (add_back): -# List l -> List l -# adds a non-list element e to the back of list l. -append = @l @e - (concat l (List/Cons e List/Nil)) +# adds an element e to the back of list l. +append : (List t) -> t -> (List t) = + @l @e (concat l (List/Cons e List/Nil)) # list sum: -# List l -> uint # returns the sum of all items in the list. -sum = @l +sum : (List u24) -> u24 = @l match l { List/Cons: (+ l.head (sum l.tail)) List/Nil: 0 } # List reverse: -# List l -> List l # reverses the order of elements in list l. -reverse.aux = @acc @l +reverse.aux : (List t) -> (List t) -> (List t) = + @acc @l match l { List/Nil: acc List/Cons: (reverse.aux (List/Cons l.head acc) l.tail) } -reverse = @l - (reverse.aux [] l) +reverse : (List t) -> (List t) = + @l (reverse.aux [] l) # List length: -# List l -> uint # returns the number of elements in list l. -len = @l +len : (List t) -> u24 = + @l match l { List/Nil: 0 List/Cons: (+ 1 (len l.tail)) } # List count: -# List l -> uint -> uint -# returns the number of instances of Some s in list l. -count.aux = @acc @l @s +# returns the number of instances of some number n in list l. +count.aux : u24 -> (List u24) -> u24 -> u24 = + @acc @l @n match l { List/Nil: acc - List/Cons: use acc = switch (== l.head s) { - 0: acc; - _: (+ acc 1); - } - (count.aux acc l.tail s) + List/Cons: + let acc = if (== l.head n) { (+ acc 1) } else { acc } + (count.aux acc l.tail n) } -count = @l @s - (count.aux 0 l s) +count = @l @n + (count.aux 0 l n) # List index: -# List l -> Some s # returns the value of a specific list index i, or * if the index doesn't exist. -index = @l @i +index : (List t) -> u24 -> (Result t None) = + @l @i match l { List/Cons: switch i { - 0: l.head + 0: (Result/Ok l.head) _: (index l.tail (i-1)) } - List/Nil: * + List/Nil: (Result/Err *) } # List head: -# List l -> Some s # returns the first item in the list, or [] if the list is empty. -head = @l +head : (List t) -> (Result t None) = + @l match l { - List/Cons: l.head - List/Nil: [] + List/Cons: (Result/Ok l.head) + List/Nil : (Result/Err *) } # List tail: -# List l -> List -# returns the list except for the first item in it, or [] if the list is empty. -def tail(l): - match l: - case List/Cons: - return l.tail - case List/Nil: - return [] +# returns the list whithout the first item, or [] if the list is empty. +tail : (List t) -> (List t) = + @l + match l { + List/Cons: l.tail + List/Nil: [] + } # List equals: -# List xs, List ys, function cmp -> 1|0 # Compares the elements in two lists and returns 1 if they're equal, and 0 otherwise. # The function cmp compares two values and returns 1 if they're equal, and 0 otherwise. -equals xs ys cmp = match xs { - List/Cons: match ys { - List/Cons: if (cmp xs.head ys.head) { - (equals xs.tail ys.tail cmp) - } else { - 0 - } - List/Nil: 0 - } - List/Nil: match ys { - List/Cons: 0 - List/Nil: 1 - } -} +equals (xs: (List a)) (ys: (List b)) (cmp: a -> b -> u24) : u24 +equals (List/Cons x xs) (List/Cons y ys) cmp = if (cmp x y) { (equals xs ys cmp) } else { 0 } +equals (List/Cons x xs) List/Nil cmp = 0 +equals List/Nil (List/Cons y ys) cmp = 0 +equals List/Nil List/Nil cmp = 1 # List pop_front: -# List l -> List l # removes and discards the first item of list l. # The new list is returned, or [] if the list is empty. -pop_front = @l +pop_front : (List t) -> (List t) = + @l match l { List/Cons: l.tail List/Nil: [] } # List pop_back: -# List l -> List l # removes and discards the the last item of list l. +pop_back : (List t) -> (List t) pop_back (List/Nil) = List/Nil pop_back (List/Cons x List/Nil) = List/Nil pop_back (List/Cons head tail) = (List/Cons head (pop_back tail)) # List remove: -# List l -> Some s -> List l # removes the first occurrence of element e from list l. -remove = @l @s +remove : (List u24) -> u24 -> (List u24) = + @l @s match l { List/Cons: - switch (== l.head s) { - 0: (List/Cons l.head (remove l.tail s)) - _: l.tail + if (== l.head s) { + l.tail + } else { + (List/Cons l.head (remove l.tail s)) } List/Nil: List/Nil } # List split: -# list l -> uint i -> (List l, list l) # splits list l into two lists (l1, l2) at index i. # the second list takes the element at index i during the split. -split = @l @i (split.aux [] l i) +split : (List t) -> u24 -> ((List t), (List t)) = + @l @i (split.aux [] l i) -split.aux = @acc @l @i +split.aux : (List t) -> (List t) -> u24 -> ((List t), (List t)) = + @acc @l @i match l { List/Cons: switch i { 0: (acc, l) _: (split.aux (append acc l.head) l.tail i-1) } - List/Nil: * + List/Nil: (acc, []) } diff --git a/examples/parallel_and.bend b/examples/parallel_and.bend index 07fd6830d..bd41552da 100644 --- a/examples/parallel_and.bend +++ b/examples/parallel_and.bend @@ -3,26 +3,27 @@ type Bool: True False -def and(a, b): +def and(a: Bool, b: Bool) -> Bool: match a: case Bool/True: return b case Bool/False: return Bool/False -def all(tree): +# Trees of tuples are not typeable +def all(tree: Tree(Bool)) -> Bool: fold tree: case Tree/Node: return and(tree.left, tree.right) case Tree/Leaf: return tree.value -def gen(n): +def gen(n: u24) -> Tree(Bool): switch n: case 0: return Tree/Leaf(Bool/True) case _: return Tree/Node { left: gen(n-1), right: gen(n-1) } -def main(): +def main() -> Bool: return all(gen(8)) diff --git a/examples/parallel_sum.bend b/examples/parallel_sum.bend index 321d24fc9..b6fb578f1 100644 --- a/examples/parallel_sum.bend +++ b/examples/parallel_sum.bend @@ -2,12 +2,12 @@ # Creates a tree with numbers and then sums all values in parallel # a binary tree -type MyTree: - Node { val, ~left, ~right } +type MyTree(t): + Node { val: t, ~left: MyTree(t), ~right: MyTree(t) } Leaf # sums all values in a tree -def sum(tree): +def sum(tree: MyTree(u24)) -> u24: fold tree: case MyTree/Node: return tree.val + tree.left + tree.right @@ -15,7 +15,7 @@ def sum(tree): return 0 # generates a binary tree of given depth -def gen(depth): +def gen(depth: u24) -> MyTree(u24): bend height=0, val = 1: when height < depth: tree = MyTree/Node { val: val, left: fork(height+1, 2*val), right: fork(height+1, 2*val+1) } @@ -24,5 +24,5 @@ def gen(depth): return tree # returns the sum of [1 .. 2^16), truncated to 24 bits -def main: +def main() -> u24: return sum(gen(16)) \ No newline at end of file diff --git a/examples/queue.bend b/examples/queue.bend index 346651174..e31278f9c 100644 --- a/examples/queue.bend +++ b/examples/queue.bend @@ -8,7 +8,7 @@ Qnew = λx x Qadd = λx λq λk (q λc (c x k)) # Qrem : Queue a -> Pair a (Queue a) -Qrem = λq (q $k λx λxs λp(p x λ$k xs)) +unchecked Qrem = λq (q $k λx λxs λp(p x λ$k xs)) # Output: [1, 2, 3] main = diff --git a/examples/quick_sort.bend b/examples/quick_sort.bend index 84f0296f2..9fc39d92e 100644 --- a/examples/quick_sort.bend +++ b/examples/quick_sort.bend @@ -1,22 +1,26 @@ -type MyTree = Leaf | (Node lft val rgt) +type MyTree t = Leaf | (Node ~(lft: (MyTree t)) (val: t) ~(rgt: (MyTree t))) # Parallel QuickSort -(Sort List/Nil) = MyTree/Leaf +(Sort) : (List u24) -> (MyTree u24) +(Sort List/Nil) = MyTree/Leaf (Sort (List/Cons head tail)) = - ((Part head tail) λmin λmax - let lft = (Sort min) - let rgt = (Sort max) - (MyTree/Node lft head rgt)) + let (min, max) = (Part head tail) + let lft = (Sort min) + let rgt = (Sort max) + (MyTree/Node lft head rgt) - # Partitions a list in two halves, less-than-p and greater-than-p - (Part p List/Nil) = λt (t List/Nil List/Nil) - (Part p (List/Cons head tail)) = (Push (> head p) head (Part p tail)) +# Partitions a list in two halves, less-than-p and greater-than-p +(Part) : u24 -> (List u24) -> ((List u24), (List u24)) +(Part p List/Nil) = (List/Nil, List/Nil) +(Part p (List/Cons head tail)) = (Push (> head p) head (Part p tail)) - # Pushes a value to the first or second list of a pair - (Push 0 x pair) = (pair λmin λmax λp (p (List/Cons x min) max)) - (Push _ x pair) = (pair λmin λmax λp (p min (List/Cons x max))) +# Pushes a value to the first or second list of a pair +(Push) : u24 -> u24 -> ((List u24), (List u24)) -> ((List u24), (List u24)) +(Push 0 x (min, max)) = ((List/Cons x min), max) +(Push _ x (min, max)) = (min, (List/Cons x max)) # Generates a random list with xorshift +(Rnd) : u24 -> (u24) -> (List u24) (Rnd 0 state) = List/Nil (Rnd n state) = let state = (^ state (<< state 13)) @@ -25,11 +29,12 @@ type MyTree = Leaf | (Node lft val rgt) (List/Cons state (Rnd (- n 1) state)) # Sums all elements in a concatenation tree +(Sum) : (MyTree u24) -> u24 (Sum MyTree/Leaf) = 0 (Sum (MyTree/Node lft val rgt)) = (+ val (+ (Sum lft) (Sum rgt))) # Sorts and sums n random numbers -(Main) = +(Main) : u24 = (Sum (Sort (Rnd 0x100 1))) # Use an argument from cli diff --git a/examples/radix_sort.bend b/examples/radix_sort.bend index f92525c2f..8ea735c5f 100644 --- a/examples/radix_sort.bend +++ b/examples/radix_sort.bend @@ -1,100 +1,95 @@ -type Map_: +type MyMap: Free Used - Both {~a, ~b} + Both { ~a: MyMap, ~b: MyMap } -type Arr: +type Arr(t): Null - Leaf {~a} - Node {~a, ~b} + Leaf { a: t } + Node { ~a: Arr(t), ~b: Arr(t) } -def swap(s, a, b): +def swap(s: u24, a: MyMap, b: MyMap) -> MyMap: switch s: case 0: - return Map_/Both{ a: a, b: b } + return MyMap/Both{ a: a, b: b } case _: - return Map_/Both{ a: b, b: a } + return MyMap/Both{ a: b, b: a } -# Sort : Arr -> Arr -def sort(t): +def sort(t: Arr(u24)) -> Arr(u24): return to_arr(0, to_map(t)) -# ToMap : Arr -> Map -def to_map(t): +def to_map(t: Arr(u24)) -> MyMap: match t: case Arr/Null: - return Map_/Free + return MyMap/Free case Arr/Leaf: return radix(t.a) case Arr/Node: return merge(to_map(t.a), to_map(t.b)) -# ToArr : U60 -> Map -> Arr -def to_arr(x, m): +def to_arr(x: u24, m: MyMap) -> Arr(u24): match m: - case Map_/Free: + case MyMap/Free: return Arr/Null - case Map_/Used: + case MyMap/Used: return Arr/Leaf{ a: x } - case Map_/Both: + case MyMap/Both: return Arr/Node{ a: to_arr(x * 2, m.a), b: to_arr(x * 2 + 1, m.b) } -# Merge : Map -> Map -> Map -def merge(a, b): +def merge(a: MyMap, b: MyMap) -> MyMap: match a: - case Map_/Free: + case MyMap/Free: return b - case Map_/Used: - return Map_/Used - case Map_/Both: + case MyMap/Used: + return MyMap/Used + case MyMap/Both: match b: - case Map_/Free: + case MyMap/Free: return a - case Map_/Used: - return Map_/Used - case Map_/Both: - return Map_/Both{ a: merge(a.a, b.a), b: merge(a.b, b.b) } + case MyMap/Used: + return MyMap/Used + case MyMap/Both: + return MyMap/Both{ a: merge(a.a, b.a), b: merge(a.b, b.b) } -# Radix : U60 -> Map -def radix(n): - r = Map_/Used - r = swap(n & 1, r, Map_/Free) - r = swap(n & 2, r, Map_/Free) - r = swap(n & 4, r, Map_/Free) - r = swap(n & 8, r, Map_/Free) - r = swap(n & 16, r, Map_/Free) - r = swap(n & 32, r, Map_/Free) - r = swap(n & 64, r, Map_/Free) - r = swap(n & 128, r, Map_/Free) - r = swap(n & 256, r, Map_/Free) - r = swap(n & 512, r, Map_/Free) +def radix(n: u24) -> MyMap: + r = MyMap/Used + r = swap(n & 1, r, MyMap/Free) + r = swap(n & 2, r, MyMap/Free) + r = swap(n & 4, r, MyMap/Free) + r = swap(n & 8, r, MyMap/Free) + r = swap(n & 16, r, MyMap/Free) + r = swap(n & 32, r, MyMap/Free) + r = swap(n & 64, r, MyMap/Free) + r = swap(n & 128, r, MyMap/Free) + r = swap(n & 256, r, MyMap/Free) + r = swap(n & 512, r, MyMap/Free) return radix2(n, r) # At the moment, we need to manually break very large functions into smaller ones # if we want to run this program on the GPU. # In a future version of Bend, we will be able to do this automatically. -def radix2(n, r): - r = swap(n & 1024, r, Map_/Free) - r = swap(n & 2048, r, Map_/Free) - r = swap(n & 4096, r, Map_/Free) - r = swap(n & 8192, r, Map_/Free) - r = swap(n & 16384, r, Map_/Free) - r = swap(n & 32768, r, Map_/Free) - r = swap(n & 65536, r, Map_/Free) - r = swap(n & 131072, r, Map_/Free) - r = swap(n & 262144, r, Map_/Free) - r = swap(n & 524288, r, Map_/Free) +def radix2(n: u24, r: MyMap) -> MyMap: + r = swap(n & 1024, r, MyMap/Free) + r = swap(n & 2048, r, MyMap/Free) + r = swap(n & 4096, r, MyMap/Free) + r = swap(n & 8192, r, MyMap/Free) + r = swap(n & 16384, r, MyMap/Free) + r = swap(n & 32768, r, MyMap/Free) + r = swap(n & 65536, r, MyMap/Free) + r = swap(n & 131072, r, MyMap/Free) + r = swap(n & 262144, r, MyMap/Free) + r = swap(n & 524288, r, MyMap/Free) return radix3(n, r) -def radix3(n, r): - r = swap(n & 1048576, r, Map_/Free) - r = swap(n & 2097152, r, Map_/Free) - r = swap(n & 4194304, r, Map_/Free) - r = swap(n & 8388608, r, Map_/Free) +def radix3(n: u24, r: MyMap) -> MyMap: + r = swap(n & 1048576, r, MyMap/Free) + r = swap(n & 2097152, r, MyMap/Free) + r = swap(n & 4194304, r, MyMap/Free) + r = swap(n & 8388608, r, MyMap/Free) return r -def reverse(t): +def reverse(t: Arr(u24)) -> Arr(u24): match t: case Arr/Null: return Arr/Null @@ -103,8 +98,7 @@ def reverse(t): case Arr/Node: return Arr/Node{ a: reverse(t.b), b: reverse(t.a) } -# Sum : Arr -> U60 -def sum(t): +def sum(t: Arr(u24)) -> u24: match t: case Arr/Null: return 0 @@ -113,11 +107,10 @@ def sum(t): case Arr/Node: return sum(t.a) + sum(t.b) -# Gen : U60 -> Arr -def gen(n): +def gen(n: u24) -> Arr(u24): return gen_go(n, 0) -def gen_go(n, x): +def gen_go(n: u24, x: u24) -> Arr(u24): switch n: case 0: return Arr/Leaf{ a: x } @@ -126,4 +119,4 @@ def gen_go(n, x): b = x * 2 + 1 return Arr/Node{ a: gen_go(n-1, a), b: gen_go(n-1, b) } -Main = (sum (sort(reverse(gen 4)))) +Main: u24 = (sum (sort(reverse(gen 4)))) diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index 8e3baacb1..e549f460b 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -32,27 +32,30 @@ List/concat(xs: (List T)) (ys: (List T)) : (List T) List/concat (List/Cons x xs) ys = (List/Cons x (List/concat xs ys)) List/concat (List/Nil) ys = ys -# Splits a list into two lists at the first occurrence of a value. +# Splits a list into two lists at the first value that passes a condition. # Returns the original list if the value is not found -def List/split_once(xs: List(T), val: T) -> (Result((List(T), List(T)), List(T))): - return List/split_once.go(xs, val, DiffList/new) +def List/split_once( + xs: List(T), + cond: T -> u24 +) -> (Result((List(T), List(T)), List(T))): + return List/split_once.go(xs, cond, DiffList/new) def List/split_once.go( xs: List(T), - val: T, + cond: T -> u24, acc: List(T) -> List(T) ) -> Result((List(T), List(T)), List(T)): match xs: case List/Nil: return Result/Err(DiffList/to_list(acc)) case List/Cons: - if val == xs.head: + if cond(xs.head): return Result/Ok((DiffList/to_list(acc), xs.tail)) else: - return List/split_once.go(xs.tail, val, DiffList/append(acc, xs.head)) + return List/split_once.go(xs.tail, cond, DiffList/append(acc, xs.head)) # Filters a list based on a predicate function. -List/filter (xs: (List T)) (pred: T -> Bool) : (List T) +List/filter (xs: (List T)) (pred: T -> u24) : (List T) List/filter (List/Nil) _ = List/Nil List/filter (List/Cons x xs) pred = if (pred x) { @@ -175,7 +178,7 @@ Map/empty : (Map T) = Map/Leaf Map/get (map: (Map T)) (key: u24) : (T, (Map T)) = match map { - Map/Leaf: (*, map) + Map/Leaf: (unreachable, map) Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { @@ -203,8 +206,8 @@ Map/set (map: (Map T)) (key: u24) (value: T) : (Map T) = Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { - 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf) - _: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)) + 0: (Map/Node unreachable (Map/set Map/Leaf (/ key 2) value) Map/Leaf) + _: (Map/Node unreachable Map/Leaf (Map/set Map/Leaf (/ key 2) value)) } _: (Map/Node value Map/Leaf Map/Leaf) } @@ -384,11 +387,9 @@ def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(L match res_chunk: case Result/Ok: chunk = res_chunk.val - match res = List/split_once(chunk, '\n'): + match res = List/split_once(chunk, lambda x: x == '\n'): # Found a newline, backtrack and join chunks case Result/Ok: - # Found line end, backtrack on the file to the start of the next line - # and return the found line. (line, rest) = res.val (length, *) = List/length(rest) res_seek <- IO/FS/seek(fd, u24_to_i24(length) * -1, IO/FS/SEEK_CUR) @@ -499,7 +500,7 @@ def IO/DyLib/close(dl: u24) -> IO(Result(None, String)): def defer(val: T) -> (T -> T) -> T: return lambda x: x(val) -def defer_arg(defered: (Id -> Id) -> A -> B, arg: B) -> ((Id -> Id) -> A): +def defer_arg(defered: (Id -> Id) -> A -> B, arg: A) -> ((Id -> Id) -> B): return lambda x: defered(x, arg) def undefer(defered: (Id -> Id) -> T) -> T: From ad3556aee842909e777732d919770a8c616098a6 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Mon, 2 Sep 2024 13:12:41 +0200 Subject: [PATCH 16/29] Fix after rebase, optimize type inference --- src/fun/check/check_untyped.rs | 2 +- src/fun/check/type_check.rs | 245 +++++++++++++++++---------------- src/fun/mod.rs | 9 ++ src/fun/parser.rs | 40 ++++-- src/imp/parser.rs | 38 +++-- src/lib.rs | 2 +- tests/golden_tests.rs | 25 ++-- 7 files changed, 206 insertions(+), 155 deletions(-) diff --git a/src/fun/check/check_untyped.rs b/src/fun/check/check_untyped.rs index 91348ba95..9f74e80a2 100644 --- a/src/fun/check/check_untyped.rs +++ b/src/fun/check/check_untyped.rs @@ -11,7 +11,7 @@ impl Ctx<'_> { if def.check { for rule in def.rules.iter() { if let Err(e) = rule.body.check_untyped_terms() { - self.info.add_rule_error(e, def.name.clone()); + self.info.add_function_error(e, def.name.clone(), def.source.clone()); } } } diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index 66a152688..11a7c786d 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -7,20 +7,21 @@ use crate::{ fun::{num_to_name, Adt, Book, Ctx, FanKind, MatchRule, Name, Num, Op, Pattern, Tag, Term, Type}, maybe_grow, }; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; impl Ctx<'_> { pub fn type_check(&mut self) -> Result<(), Diagnostics> { let types = infer_book(self.book, &mut self.info)?; - for (nam, typ) in types { - eprintln!("{nam}: {typ}"); + for def in self.book.defs.values_mut() { + def.typ = types[&def.name].instantiate(&mut VarGen::default()); } + Ok(()) } } -pub type ProgramTypes = BTreeMap; +type ProgramTypes = HashMap; /// A type scheme, aka a polymorphic type. #[derive(Clone)] @@ -109,10 +110,10 @@ impl Subst { /// Compose two substitutions. /// /// Applies the first substitution to the second, and then inserts the result into the first. - fn compose(&self, other: &Subst) -> Subst { - let other = other.0.iter().map(|(x, t)| (x.clone(), t.subst(self))); - let subst = self.0.iter().map(|(x, t)| (x.clone(), t.clone())).chain(other).collect(); - Subst(subst) + fn compose(mut self, other: Subst) -> Subst { + let other = other.0.into_iter().map(|(x, t)| (x, t.subst(&self))).collect::>(); + self.0.extend(other); + self } } @@ -134,6 +135,28 @@ impl TypeEnv { fn insert(&mut self, name: Name, scheme: Scheme) { self.0.insert(name, scheme); } + + fn add_binds<'a>( + &mut self, + bnd: impl IntoIterator, Scheme)>, + ) -> Vec<(Name, Option)> { + let mut old_bnd = vec![]; + for (name, scheme) in bnd { + if let Some(name) = name { + let old = self.0.insert(name.clone(), scheme); + old_bnd.push((name.clone(), old)); + } + } + old_bnd + } + + fn pop_binds(&mut self, old_bnd: Vec<(Name, Option)>) { + for (name, scheme) in old_bnd { + if let Some(scheme) = scheme { + self.0.insert(name, scheme); + } + } + } } impl VarGen { @@ -250,28 +273,28 @@ impl RecGroups { fn infer_book(book: &Book, diags: &mut Diagnostics) -> Result { let groups = RecGroups::from_book(book); let mut env = TypeEnv::default(); - let mut types = BTreeMap::new(); + // Note: We store the inferred and generalized types in a separate + // environment, to avoid unnecessary cloning (since no immutable data). + let mut types = ProgramTypes::default(); + // Add the constructors to the environment. for adt in book.adts.values() { for ctr in adt.ctrs.values() { let scheme = ctr.typ.generalize(&TypeEnv::default()); - env.insert(ctr.name.clone(), scheme); - types.insert(ctr.name.clone(), ctr.typ.clone()); + types.insert(ctr.name.clone(), scheme); } } // Add the types of unchecked functions to the environment. for def in book.defs.values() { if !def.check { let scheme = def.typ.generalize(&TypeEnv::default()); - env.insert(def.name.clone(), scheme); - types.insert(def.name.clone(), def.typ.clone()); + types.insert(def.name.clone(), scheme); } } // Add the types of hvm functions to the environment. for def in book.hvm_defs.values() { let scheme = def.typ.generalize(&TypeEnv::default()); - env.insert(def.name.clone(), scheme); - types.insert(def.name.clone(), def.typ.clone()); + types.insert(def.name.clone(), scheme); } // Infer the types of regular functions. @@ -301,8 +324,8 @@ fn infer_group( let mut exp_ts = vec![]; for name in group { let def = &book.defs[name]; - let (s, t) = infer(env, book, &def.rule().body, var_gen).map_err(|e| { - diags.add_rule_error(e, name.clone()); + let (s, t) = infer(env, book, types, &def.rule().body, var_gen).map_err(|e| { + diags.add_function_error(e, name.clone(), def.source.clone()); std::mem::take(diags) })?; let t = t.subst(&s); @@ -311,26 +334,30 @@ fn infer_group( exp_ts.push(&def.typ); } + // Remove the type variables of the group from the environment. + // This avoids cloning of already generalized types. + for name in group.iter() { + env.0.remove(name); + } + // Unify the inferred body with the corresponding type variable. - let mut s = ss.iter().fold(Subst::default(), |s, s2| s.compose(s2)); + let mut s = ss.into_iter().fold(Subst::default(), |acc, s| acc.compose(s)); let mut ts = vec![]; for ((bod_t, tv), nam) in inf_ts.into_iter().zip(tvs.iter()).zip(group.iter()) { let (t, s2) = unify_term(&tv.subst(&s), &bod_t, &book.defs[nam].rule().body)?; ts.push(t); - s = s.compose(&s2); + s = s.compose(s2); } let ts = ts.into_iter().map(|t| t.subst(&s)).collect::>(); // Specialize against the expected type, then generalize and store. for ((name, exp_t), inf_t) in group.iter().zip(exp_ts.iter()).zip(ts.iter()) { let t = specialize(inf_t, exp_t).map_err(|e| { - diags.add_rule_error(e, name.clone()); + diags.add_function_error(e, name.clone(), book.defs[name].source.clone()); std::mem::take(diags) })?; let scheme = t.generalize(&TypeEnv::default()); - let t = scheme.instantiate(&mut VarGen::default()); - env.insert(name.clone(), scheme); - types.insert(name.clone(), t); + types.insert(name.clone(), scheme); } diags.fatal(()) @@ -342,96 +369,86 @@ fn infer_group( /// /// The returned substitution records the type constraints imposed on type variables by the term. /// The returned type is the type of the term. -fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Result<(Subst, Type), String> { +fn infer( + env: &mut TypeEnv, + book: &Book, + types: &ProgramTypes, + term: &Term, + var_gen: &mut VarGen, +) -> Result<(Subst, Type), String> { let res = maybe_grow(|| match term { - Term::Var { nam } | Term::Ref { nam } => match env.0.get(nam) { - Some(scheme) => Ok::<_, String>((Subst::default(), scheme.instantiate(var_gen))), - None => unreachable!("unbound name '{}'", nam), - }, + Term::Var { nam } | Term::Ref { nam } => { + if let Some(scheme) = env.0.get(nam) { + Ok::<_, String>((Subst::default(), scheme.instantiate(var_gen))) + } else if let Some(scheme) = types.get(nam) { + Ok((Subst::default(), scheme.instantiate(var_gen))) + } else { + unreachable!("unbound name '{}'", nam) + } + } Term::Lam { tag: Tag::Static, pat, bod } => match pat.as_ref() { Pattern::Var(nam) => { let tv = var_gen.fresh(); - let mut env = env.clone(); - if let Some(nam) = nam { - env.insert(nam.clone(), Scheme(vec![], tv.clone())); - } - let (s, bod_t) = infer(&env, book, bod, var_gen)?; + let old_bnd = env.add_binds([(nam, Scheme(vec![], tv.clone()))]); + let (s, bod_t) = infer(env, book, types, bod, var_gen)?; + env.pop_binds(old_bnd); let var_t = tv.subst(&s); Ok((s, Type::Arr(Box::new(var_t), Box::new(bod_t)))) } _ => unreachable!("{}", term), }, Term::App { tag: Tag::Static, fun, arg } => { - let (s1, fun_t) = infer(env, book, fun, var_gen)?; - let (s2, arg_t) = infer(&env.subst(&s1), book, arg, var_gen)?; + let (s1, fun_t) = infer(env, book, types, fun, var_gen)?; + let (s2, arg_t) = infer(&mut env.subst(&s1), book, types, arg, var_gen)?; let app_t = var_gen.fresh(); let (_, s3) = unify_term(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())), fun)?; - Ok((s3.compose(&s2).compose(&s1), app_t.subst(&s3))) + let t = app_t.subst(&s3); + Ok((s3.compose(s2).compose(s1), t)) } Term::Let { pat, val, nxt } => match pat.as_ref() { Pattern::Var(nam) => { - let (s1, val_t) = infer(env, book, val, var_gen)?; - let mut env = env.clone(); - if let Some(nam) = nam { - env.insert(nam.clone(), val_t.generalize(&env.subst(&s1))); - } - let (s2, nxt_t) = infer(&env.subst(&s1), book, nxt, var_gen)?; - Ok((s2.compose(&s1), nxt_t)) + let (s1, val_t) = infer(env, book, types, val, var_gen)?; + let old_bnd = env.add_binds([(nam, val_t.generalize(&env.subst(&s1)))]); + let (s2, nxt_t) = infer(&mut env.subst(&s1), book, types, nxt, var_gen)?; + env.pop_binds(old_bnd); + Ok((s2.compose(s1), nxt_t)) } - Pattern::Fan(FanKind::Tup, Tag::Static, els) => { + Pattern::Fan(FanKind::Tup, Tag::Static, _) => { // Tuple elimination behaves like pattern matching. // Variables from tuple patterns don't get generalized. - let (s1, val_t) = infer(env, book, val, var_gen)?; - - let mut env = env.clone(); - let mut tvs = vec![]; - for el in els { - if let Pattern::Var(nam) = el { - let tv = var_gen.fresh(); - tvs.push(tv.clone()); - if let Some(nam) = nam { - env.insert(nam.clone(), Scheme(vec![], tv)); - } - } else { - unreachable!("Nested patterns should've been removed in earlier pass"); - } - } - let (s2, nxt_t) = infer(&env.subst(&s1), book, nxt, var_gen)?; + debug_assert!(!(pat.has_unscoped() || pat.has_nested())); + let (s1, val_t) = infer(env, book, types, val, var_gen)?; + + let tvs = pat.binds().map(|_| var_gen.fresh()).collect::>(); + let old_bnd = env.add_binds(pat.binds().zip(tvs.iter().map(|tv| Scheme(vec![], tv.clone())))); + let (s2, nxt_t) = infer(&mut env.subst(&s1), book, types, nxt, var_gen)?; + env.pop_binds(old_bnd); let tvs = tvs.into_iter().map(|tv| tv.subst(&s2)).collect::>(); let (_, s3) = unify_term(&val_t, &Type::Tup(tvs), val)?; - Ok((s3.compose(&s2).compose(&s1), nxt_t)) + Ok((s3.compose(s2).compose(s1), nxt_t)) } - Pattern::Fan(FanKind::Dup, Tag::Auto, els) => { + Pattern::Fan(FanKind::Dup, Tag::Auto, _) => { // We pretend that sups don't exist and dups don't collide. // All variables must have the same type as the body of the dup. - let (s1, mut val_t) = infer(env, book, val, var_gen)?; - let mut tvs = vec![]; - let mut env = env.clone(); - for el in els { - if let Pattern::Var(nam) = el { - let tv = var_gen.fresh(); - tvs.push(tv.clone()); - if let Some(nam) = nam { - env.insert(nam.clone(), Scheme(vec![], tv.clone())); - } - } else { - unreachable!("Nested patterns should've been removed in earlier pass"); - } - } - let (mut s2, nxt_t) = infer(&env.subst(&s1), book, nxt, var_gen)?; + debug_assert!(!(pat.has_unscoped() || pat.has_nested())); + let (s1, mut val_t) = infer(env, book, types, val, var_gen)?; + let tvs = pat.binds().map(|_| var_gen.fresh()).collect::>(); + let old_bnd = env.add_binds(pat.binds().zip(tvs.iter().map(|tv| Scheme(vec![], tv.clone())))); + let (mut s2, nxt_t) = infer(&mut env.subst(&s1), book, types, nxt, var_gen)?; + env.pop_binds(old_bnd); for tv in tvs { let (val_t_, s) = unify_term(&val_t, &tv.subst(&s2), val)?; val_t = val_t_; - s2 = s2.compose(&s); + s2 = s2.compose(s); } - Ok((s2.compose(&s1), nxt_t)) + Ok((s2.compose(s1), nxt_t)) } - _ => todo!(), + _ => unreachable!(), }, Term::Mat { bnd: _, arg, with_bnd: _, with_arg: _, arms } => { // Infer type of the scrutinee - let (s1, t1) = infer(env, book, arg, var_gen)?; + let (s1, t1) = infer(env, book, types, arg, var_gen)?; // Instantiate the expected type of the scrutinee let adt_name = book.ctrs.get(arms[0].0.as_ref().unwrap()).unwrap(); @@ -441,11 +458,11 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul // For each case, infer the types and unify them all. // Unify the inferred type of the destructured fields with the // expected from what we inferred from the scrutinee. - let (s2, nxt_t) = infer_match_cases(&env.subst(&s1), book, adt, arms, &adt_s, var_gen)?; + let (s2, nxt_t) = infer_match_cases(env.subst(&s1), book, types, adt, arms, &adt_s, var_gen)?; // Unify the inferred type with the expected type let (_, s3) = unify_term(&t1, &adt_t.subst(&s2), arg)?; - Ok((s3.compose(&s2).compose(&s1), nxt_t)) + Ok((s3.compose(s2).compose(s1), nxt_t)) } Term::Num { val } => { @@ -457,10 +474,10 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul Ok((Subst::default(), t)) } Term::Oper { opr, fst, snd } => { - let (s1, t1) = infer(env, book, fst, var_gen)?; - let (s2, t2) = infer(&env.subst(&s1), book, snd, var_gen)?; + let (s1, t1) = infer(env, book, types, fst, var_gen)?; + let (s2, t2) = infer(&mut env.subst(&s1), book, types, snd, var_gen)?; let (t2, s3) = unify_term(&t2.subst(&s1), &t1.subst(&s2), term)?; - let s_args = s3.compose(&s2).compose(&s1); + let s_args = s3.compose(s2).compose(s1); let t_args = t2.subst(&s_args); // Check numeric type matches the operation let tv = var_gen.fresh(); @@ -480,45 +497,46 @@ fn infer(env: &TypeEnv, book: &Book, term: &Term, var_gen: &mut VarGen) -> Resul // Floating Op::POW => unify_term(&t_args, &Type::F24, term)?, }; - Ok((s_opr.compose(&s_args), t_opr.subst(&s_opr))) + let t = t_opr.subst(&s_opr); + Ok((s_opr.compose(s_args), t)) } Term::Swt { bnd: _, arg, with_bnd: _, with_arg: _, pred, arms } => { - let (s1, t1) = infer(env, book, arg, var_gen)?; + let (s1, t1) = infer(env, book, types, arg, var_gen)?; let (_, s2) = unify_term(&t1, &Type::U24, arg)?; - let s_arg = s2.compose(&s1); + let s_arg = s2.compose(s1); let mut env = env.subst(&s_arg); let mut ss_nums = vec![]; let mut ts_nums = vec![]; for arm in arms.iter().rev().skip(1) { - let (s, t) = infer(&env, book, arm, var_gen)?; + let (s, t) = infer(&mut env, book, types, arm, var_gen)?; + env = env.subst(&s); ss_nums.push(s); ts_nums.push(t); } - if let Some(pred) = pred { - env.insert(pred.clone(), Scheme(vec![], Type::U24)); - } - let (s_succ, t_succ) = infer(&env, book, &arms[1], var_gen)?; + let old_bnd = env.add_binds([(pred, Scheme(vec![], Type::U24))]); + let (s_succ, t_succ) = infer(&mut env, book, types, &arms[1], var_gen)?; + env.pop_binds(old_bnd); - let s_arms = ss_nums.into_iter().fold(s_succ, |acc, s| acc.compose(&s)); + let s_arms = ss_nums.into_iter().fold(s_succ, |acc, s| acc.compose(s)); let mut t_swt = t_succ; let mut s_swt = Subst::default(); for t_num in ts_nums { let (t, s) = unify_term(&t_swt, &t_num, term)?; t_swt = t; - s_swt = s.compose(&s_swt); + s_swt = s.compose(s_swt); } - let s = s_swt.compose(&s_arms).compose(&s_arg); + let s = s_swt.compose(s_arms).compose(s_arg); let t = t_swt.subst(&s); Ok((s, t)) } Term::Fan { fan: FanKind::Tup, tag: Tag::Static, els } => { - let res = els.iter().map(|el| infer(env, book, el, var_gen)).collect::, _>>()?; + let res = els.iter().map(|el| infer(env, book, types, el, var_gen)).collect::, _>>()?; let (ss, ts): (Vec, Vec) = res.into_iter().unzip(); let t = Type::Tup(ts); - let s = ss.into_iter().fold(Subst::default(), |acc, s| acc.compose(&s)); + let s = ss.into_iter().fold(Subst::default(), |acc, s| acc.compose(s)); Ok((s, t)) } Term::Era => Ok((Subst::default(), Type::None)), @@ -552,8 +570,9 @@ fn instantiate_adt(adt: &Adt, var_gen: &mut VarGen) -> Result<(Subst, Type), Str } fn infer_match_cases( - env: &TypeEnv, + mut env: TypeEnv, book: &Book, + types: &ProgramTypes, adt: &Adt, arms: &[MatchRule], adt_s: &Subst, @@ -565,24 +584,20 @@ fn infer_match_cases( // One fresh var per field, we later unify with the expected type. let tvs = vars.iter().map(|_| var_gen.fresh()).collect::>(); - // Add the fields to the environment. - let mut case_env = env.clone(); - for (var, tv) in vars.iter().zip(tvs.iter()) { - if let Some(var) = var { - case_env.insert(var.clone(), Scheme(vec![], tv.clone())); - } - } // Infer the body and unify the inferred field types with the expected. - let (s1, t1) = infer(&case_env, book, bod, var_gen)?; + let old_bnd = env.add_binds(vars.iter().zip(tvs.iter().map(|tv| Scheme(vec![], tv.clone())))); + let (s1, t1) = infer(&mut env, book, types, bod, var_gen)?; + env.pop_binds(old_bnd); let inf_ts = tvs.into_iter().map(|tv| tv.subst(&s1)).collect::>(); let exp_ts = ctr.fields.iter().map(|f| f.typ.subst(adt_s)).collect::>(); let s2 = unify_fields(inf_ts.iter().zip(exp_ts.iter()), bod)?; // Recurse and unify with the other arms. - let (s_rest, t_rest) = infer_match_cases(env, book, adt, rest, adt_s, var_gen)?; + let s = s2.compose(s1); + let (s_rest, t_rest) = infer_match_cases(env.subst(&s), book, types, adt, rest, adt_s, var_gen)?; let (t_final, s_final) = unify_term(&t1, &t_rest, bod)?; - Ok((s_final.compose(&s_rest).compose(&s2).compose(&s1), t_final)) + Ok((s_final.compose(s_rest).compose(s), t_final)) } else { Ok((Subst::default(), var_gen.fresh())) } @@ -593,7 +608,7 @@ fn unify_fields<'a>(ts: impl Iterator, ctx: &Term) let ss = ts.map(|(inf, exp)| unify_term(inf, exp, ctx)).collect::, _>>()?; let mut s = Subst::default(); for (_, s2) in ss.into_iter().rev() { - s = s.compose(&s2); + s = s.compose(s2); } Ok(s) } @@ -625,7 +640,7 @@ fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { let (t1, s1) = unify(l1, l2)?; let (t2, s2) = unify(&r1.subst(&s1), &r2.subst(&s1))?; - Ok((Type::Arr(Box::new(t1), Box::new(t2)), s2.compose(&s1))) + Ok((Type::Arr(Box::new(t1), Box::new(t2)), s2.compose(s1))) } (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { let mut s = Subst::default(); @@ -633,7 +648,7 @@ fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { for (t1, t2) in ts1.iter().zip(ts2.iter()) { let (t, s2) = unify(t1, t2)?; ts.push(t); - s = s.compose(&s2); + s = s.compose(s2); } Ok((Type::Ctr(name1.clone(), ts), s)) } @@ -643,7 +658,7 @@ fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { for (t1, t2) in els1.iter().zip(els2.iter()) { let (t, s2) = unify(t1, t2)?; ts.push(t); - s = s.compose(&s2); + s = s.compose(s2); } Ok((Type::Tup(ts), s)) } @@ -674,7 +689,7 @@ fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { // Recurse to assign variables to `Any` as well for child in t.children() { let (_, s2) = unify(&Type::Any, child)?; - s = s2.compose(&s); + s = s2.compose(s); } Ok((Type::Any, s)) } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 3f119f994..a4ad06e61 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -1068,6 +1068,15 @@ impl Pattern { Pattern::Ctr(_, x) | Pattern::Fan(_, _, x) | Pattern::Lst(x) => x.iter().any(|x| x.has_unscoped()), } } + + pub fn has_nested(&self) -> bool { + for child in self.children() { + if matches!(child, Pattern::Ctr(_, _) | Pattern::Fan(_, _, _) | Pattern::Lst(_)) { + return true; + } + } + false + } } impl Rule { diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 679dd3e76..1bd4d5724 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -200,13 +200,15 @@ impl<'a> FunParser<'a> { if self.try_consume("(") { // parens around name and vars self.skip_trivia(); - name = self.parse_top_level_name()?; - vars = self.list_like(|p| p.parse_var_name(), "", ")", "", false, 0)?; + name = self.parse_restricted_name("Datatype")?; + vars = self + .labelled(|p| p.list_like(|p| p.parse_var_name(), "", ")", "", false, 0), "Type variable or ')'")?; self.consume("=")?; } else { // no parens - name = self.parse_top_level_name()?; - vars = self.list_like(|p| p.parse_var_name(), "", "=", "", false, 0)?; + name = self.parse_restricted_name("Datatype")?; + vars = self + .labelled(|p| p.list_like(|p| p.parse_var_name(), "", "=", "", false, 0), "Type variable or '='")?; } let mut ctrs = vec![self.parse_type_ctr(&name, &vars)?]; @@ -243,7 +245,7 @@ impl<'a> FunParser<'a> { Ok(ctr) } else { // just name - let name = self.labelled(|p| p.parse_top_level_name(), "datatype constructor name")?; + let name = self.parse_restricted_name("Datatype constructor")?; let name = Name::new(format!("{type_name}/{name}")); let typ = make_ctr_type(type_name.clone(), &[], type_vars); let ctr = AdtCtr { name, typ, fields: vec![] }; @@ -302,8 +304,8 @@ impl<'a> FunParser<'a> { } else { // Was not a signature, backtrack and read the name from the first rule self.index = ini_idx; - let check = !self.try_parse_keyword("unchecked"); - self.skip_trivia(); + // No signature, don't check by default + let check = self.parse_checked(false); let mut rules = vec![]; let (name, rule) = self.parse_rule()?; rules.push(rule); @@ -323,7 +325,8 @@ impl<'a> FunParser<'a> { fn parse_def_sig(&mut self) -> ParseResult<(Name, Vec, bool, Type)> { // '(' name ((arg | '(' arg (':' type)? ')'))* ')' ':' type // name ((arg | '(' arg (':' type)? ')'))* ':' type - let check = !self.try_parse_keyword("unchecked"); + // Signature, check by default + let check = self.parse_checked(true); let (name, args, typ) = if self.try_consume("(") { let name = self.parse_top_level_name()?; let args = self.list_like(|p| p.parse_def_sig_arg(), "", ")", "", false, 0)?; @@ -337,7 +340,7 @@ impl<'a> FunParser<'a> { (name, args, typ) }; let (args, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - let typ = make_fn_type(&arg_types, typ); + let typ = make_fn_type(arg_types, typ); Ok((name, args, check, typ)) } @@ -356,6 +359,16 @@ impl<'a> FunParser<'a> { } } + fn parse_checked(&mut self, default: bool) -> bool { + if self.try_parse_keyword("checked") { + true + } else if self.try_parse_keyword("unchecked") { + false + } else { + default + } + } + fn parse_from_import(&mut self) -> ParseResult { // from path import package // from path import (a, b) @@ -413,7 +426,7 @@ impl<'a> FunParser<'a> { fn parse_rule_lhs(&mut self) -> ParseResult<(Name, Vec)> { if self.try_consume_exactly("(") { self.skip_trivia(); - let name = self.labelled(|p| p.parse_top_level_name(), "function name")?; + let name = self.parse_restricted_name("Function")?; let pats = self.list_like(|p| p.parse_pattern(false), "", ")", "", false, 0)?; Ok((name, pats)) } else { @@ -432,6 +445,7 @@ impl<'a> FunParser<'a> { } fn parse_rule(&mut self) -> ParseResult<(Name, Rule)> { + self.skip_trivia(); let (name, pats) = self.parse_rule_lhs()?; self.consume("=")?; @@ -443,8 +457,8 @@ impl<'a> FunParser<'a> { } fn starts_with_rule(&mut self, expected_name: &Name) -> bool { - self.skip_trivia(); let ini_idx = *self.index(); + self.skip_trivia(); let res = self.parse_rule_lhs(); self.index = ini_idx; if let Ok((name, _)) = res { @@ -1231,9 +1245,9 @@ pub fn is_num_char(c: char) -> bool { "0123456789+-".contains(c) } -pub fn make_fn_type(args: &[Type], ret: Type) -> Type { +pub fn make_fn_type(args: Vec, ret: Type) -> Type { let typ = ret; - let typ = args.iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ.clone()), Box::new(acc))); + let typ = args.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))); typ } diff --git a/src/imp/parser.rs b/src/imp/parser.rs index 3e08ec26a..ece2eea30 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -45,7 +45,7 @@ impl<'a> ImpParser<'a> { self.parse_keyword("type")?; self.skip_trivia_inline()?; - let type_name = self.parse_top_level_name()?; + let type_name = self.parse_restricted_name("datatype")?; self.skip_trivia_inline()?; let type_vars = if self.try_consume_exactly("(") { @@ -133,8 +133,8 @@ impl<'a> ImpParser<'a> { let name = self.parse_var_name()?; self.skip_trivia_inline()?; - let typ = self.parse_return_type()?; - let typ = make_fn_type(&[], typ); + let typ = self.parse_return_type()?.unwrap_or(Type::Any); + let typ = make_fn_type(vec![], typ); self.skip_trivia_inline()?; self.consume_exactly(":")?; @@ -1235,7 +1235,13 @@ impl<'a> ImpParser<'a> { self.parse_keyword("def")?; self.skip_trivia_inline()?; - let check = !self.try_parse_keyword("unchecked"); + let check = if self.try_parse_keyword("unchecked") { + (false, true) + } else if self.try_parse_keyword("checked") { + (true, false) + } else { + (false, false) + }; let name = self.parse_top_level_name()?; self.skip_trivia_inline()?; @@ -1259,29 +1265,39 @@ impl<'a> ImpParser<'a> { let (body, nxt_indent) = self.parse_statement(&mut indent)?; indent.exit_level(); + // If any annotation, check by default, otherwise unchecked by default + let check = if check.0 { + true + } else if check.1 { + false + } else { + ret_type.is_some() || arg_types.iter().any(|t| t.is_some()) + }; + let arg_types = arg_types.into_iter().map(|t| t.unwrap_or(Type::Any)).collect::>(); + let typ = make_fn_type(arg_types, ret_type.unwrap_or(Type::Any)); + // Note: The source kind gets replaced later (generated if a local def, user otherwise) let source = Source::from_file_span(&self.file, self.input, ini_idx..self.index, self.builtin); - let typ = make_fn_type(&arg_types, ret_type); let def = Definition { name, args, typ, check, body, source }; Ok((def, nxt_indent)) } - fn parse_def_arg(&mut self) -> ParseResult<(Name, Type)> { + fn parse_def_arg(&mut self) -> ParseResult<(Name, Option)> { let name = self.parse_var_name()?; self.skip_trivia_inline()?; if self.try_consume_exactly(":") { let typ = self.parse_type_expr()?; - Ok((name, typ)) + Ok((name, Some(typ))) } else { - Ok((name, Type::Any)) + Ok((name, None)) } } - fn parse_return_type(&mut self) -> ParseResult { + fn parse_return_type(&mut self) -> ParseResult> { if self.try_consume_exactly("->") { - self.parse_type_expr() + Ok(Some(self.parse_type_expr()?)) } else { - Ok(Type::Any) + Ok(None) } } diff --git a/src/lib.rs b/src/lib.rs index 88705ccbd..3a5d25386 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -392,7 +392,7 @@ impl CompileOpts { merge: true, linearize_matches: OptLevel::Enabled, type_check: true, - inline: self.inline, + inline: true, check_net_size: self.check_net_size, adt_encoding: self.adt_encoding, } diff --git a/tests/golden_tests.rs b/tests/golden_tests.rs index 0c26182b9..f781b2a6f 100644 --- a/tests/golden_tests.rs +++ b/tests/golden_tests.rs @@ -41,7 +41,7 @@ const TESTS_PATH: &str = "/tests/golden_tests/"; type RunFn = dyn Fn(&str, &Path) -> Result; -pub fn parse_book_single_file(code: &str, origin: &Path) -> Result { +pub fn parse_book_single_file(code: &str, origin: &Path) -> Result { do_parse_book(code, origin, ParseBook::builtins())?.to_fun() } @@ -441,19 +441,16 @@ fn mutual_recursion() { /// Runs a file that uses IO. #[test] fn io() { - run_golden_test_dir( - function_name!(), - (&|code, path| { - let _guard = RUN_MUTEX.lock().unwrap(); - let book = parse_book_single_file(code, path)?; - let compile_opts = CompileOpts::default(); - let diagnostics_cfg = DiagnosticsConfig::default(); - let (term, _, diags) = - run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); - let res = format!("{diags}{term}"); - Ok(format!("Strict mode:\n{res}")) - }), - ) + run_golden_test_dir(function_name!(), &|code, path| { + let _guard = RUN_MUTEX.lock().unwrap(); + let book = parse_book_single_file(code, path)?; + let compile_opts = CompileOpts::default(); + let diagnostics_cfg = DiagnosticsConfig::default(); + let (term, _, diags) = + run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); + let res = format!("{diags}{term}"); + Ok(format!("Strict mode:\n{res}")) + }) } /// Runs all examples in the examples folder. From 5027fc3578a4cb8af06fbe0b686b535de64d5313 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Mon, 2 Sep 2024 14:12:50 +0200 Subject: [PATCH 17/29] Optimize unbound var check --- src/fun/check/unbound_vars.rs | 41 ++++++++++------------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/src/fun/check/unbound_vars.rs b/src/fun/check/unbound_vars.rs index 6fa572f74..6d0bc555a 100644 --- a/src/fun/check/unbound_vars.rs +++ b/src/fun/check/unbound_vars.rs @@ -3,7 +3,7 @@ use crate::{ fun::{transform::desugar_bend, Ctx, Name, Pattern, Term}, maybe_grow, }; -use std::collections::{hash_map::Entry, HashMap}; +use std::collections::HashMap; #[derive(Debug, Clone)] pub enum UnboundVarErr { @@ -17,11 +17,8 @@ impl Ctx<'_> { for (def_name, def) in self.book.defs.iter_mut() { let mut errs = Vec::new(); for rule in &mut def.rules { - let mut scope = HashMap::new(); - for pat in &rule.pats { - pat.binds().for_each(|nam| push_scope(nam.as_ref(), &mut scope)); - } - + // Note: Using a Vec instead of a Map is a deliberate optimization. + let mut scope = rule.pats.iter().flat_map(|pat| pat.binds()).map(|x| x.as_ref()).collect::>(); rule.body.check_unbound_vars(&mut scope, &mut errs); } @@ -38,11 +35,7 @@ impl Term { /// Checks that all variables are bound. /// Precondition: References have been resolved, implicit binds have been solved. - pub fn check_unbound_vars<'a>( - &'a mut self, - scope: &mut HashMap<&'a Name, u64>, - errs: &mut Vec, - ) { + pub fn check_unbound_vars<'a>(&'a mut self, scope: &mut Vec>, errs: &mut Vec) { let mut globals = HashMap::new(); check_uses(self, scope, &mut globals, errs); @@ -57,13 +50,13 @@ impl Term { /// Globals has how many times a global var name was declared and used. pub fn check_uses<'a>( term: &'a mut Term, - scope: &mut HashMap<&'a Name, u64>, + scope: &mut Vec>, globals: &mut HashMap, errs: &mut Vec, ) { maybe_grow(move || match term { Term::Var { nam } => { - if !scope.contains_key(nam) { + if !scope_contains(nam, scope) { errs.push(UnboundVarErr::Local(nam.clone())); *term = Term::Err; } @@ -78,11 +71,11 @@ pub fn check_uses<'a>( } for (child, binds) in term.children_mut_with_binds() { for bind in binds.clone() { - push_scope(bind.as_ref(), scope); + scope.push(bind.as_ref()); } check_uses(child, scope, globals, errs); - for bind in binds { - pop_scope(bind.as_ref(), scope); + for _ in binds { + scope.pop(); } } } @@ -102,20 +95,8 @@ pub fn check_global_binds(pat: &Pattern, globals: &mut HashMap(nam: Option<&'a Name>, scope: &mut HashMap<&'a Name, u64>) { - if let Some(nam) = nam { - *scope.entry(nam).or_default() += 1; - } -} - -fn pop_scope<'a>(nam: Option<&'a Name>, scope: &mut HashMap<&'a Name, u64>) { - if let Some(nam) = nam { - let Entry::Occupied(n_declarations) = scope.entry(nam).and_modify(|e| *e -= 1) else { unreachable!() }; - - if *n_declarations.get() == 0 { - n_declarations.remove(); - } - } +fn scope_contains(nam: &Name, scope: &[Option<&Name>]) -> bool { + scope.iter().rev().any(|scope_nam| scope_nam == nam) } impl std::fmt::Display for UnboundVarErr { From 433a5f6781b12511af2ab0df797744ce486969a1 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Tue, 3 Sep 2024 20:12:56 +0200 Subject: [PATCH 18/29] Small improvements --- src/fun/builtins.rs | 3 ++- src/fun/check/type_check.rs | 12 ++++-------- src/fun/check/unbound_vars.rs | 6 +++++- src/fun/parser.rs | 24 +++++++++--------------- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/fun/builtins.rs b/src/fun/builtins.rs index 3496e0319..10d650874 100644 --- a/src/fun/builtins.rs +++ b/src/fun/builtins.rs @@ -47,7 +47,8 @@ pub const BUILTIN_TYPES: &[&str] = &[LIST, STRING, NAT, TREE, MAP, IO]; impl ParseBook { pub fn builtins() -> Self { - let book = FunParser::new(Name::new(""), BUILTINS, true).parse_book(Self::default()); + let book = + FunParser::new(Name::new("/src/fun/builtins.bend"), BUILTINS, true).parse_book(Self::default()); book.unwrap_or_else(|e| panic!("Error parsing builtin file, this should not happen:\n{e}")) } } diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index 11a7c786d..bb0890396 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -280,21 +280,18 @@ fn infer_book(book: &Book, diags: &mut Diagnostics) -> Result(&'a mut self, scope: &mut Vec>, errs: &mut Vec) { + pub fn check_unbound_vars<'a>( + &'a mut self, + scope: &mut Vec>, + errs: &mut Vec, + ) { let mut globals = HashMap::new(); check_uses(self, scope, &mut globals, errs); diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 1bd4d5724..d24048498 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -460,6 +460,10 @@ impl<'a> FunParser<'a> { let ini_idx = *self.index(); self.skip_trivia(); let res = self.parse_rule_lhs(); + if !self.try_consume("=") { + self.index = ini_idx; + return false; + } self.index = ini_idx; if let Ok((name, _)) = res { if &name == expected_name { @@ -1246,9 +1250,7 @@ pub fn is_num_char(c: char) -> bool { } pub fn make_fn_type(args: Vec, ret: Type) -> Type { - let typ = ret; - let typ = args.into_iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))); - typ + args.into_iter().rfold(ret, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))) } pub fn make_ctr_type(type_name: Name, fields: &[Type], vars: &[Name]) -> Type { @@ -1284,17 +1286,6 @@ impl Indent { impl<'a> ParserCommons<'a> for FunParser<'a> {} pub trait ParserCommons<'a>: Parser<'a> { - /// Generates an error message that does not print expected terms. - fn expected_message(&mut self, msg: &str) -> ParseResult { - let ini_idx = *self.index(); - let end_idx = *self.index() + 1; - - let is_eof = self.is_eof(); - let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) }); - let msg = format!("\x1b[1m- information:\x1b[0m {}\n\x1b[1m- location:\x1b[0m{}", msg, detected); - self.with_ctx(Err(msg), ini_idx..end_idx) - } - fn labelled(&mut self, parser: impl Fn(&mut Self) -> ParseResult, label: &str) -> ParseResult { match parser(self) { Ok(val) => Ok(val), @@ -1457,10 +1448,13 @@ pub trait ParserCommons<'a>: Parser<'a> { self.with_ctx(Err(msg), span) } + /// If the parser result is an error, adds code location information to the error message. fn with_ctx(&mut self, res: Result, span: Range) -> ParseResult { res.map_err(|msg| { let ctx = highlight_error(span.start, span.end, self.input()); - let msg = format!("{msg}\n{ctx}"); + let is_eof = self.is_eof(); + let eof_msg = if is_eof { " end of input" } else { "" }; + let msg = format!("{msg}\nLocation:{eof_msg}\n{ctx}"); ParseError::new((span.start, span.end), msg) }) } From 0b8246117756ae3aec8d9d604adc56e4f0547b9c Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Tue, 3 Sep 2024 20:13:20 +0200 Subject: [PATCH 19/29] Update snapshots for types --- examples/example_fun.bend | 11 +-- examples/queue.bend | 8 +-- src/fun/builtins.bend | 12 +++- .../redefinition_with_type_between.bend | 2 +- tests/golden_tests/run_file/num_cast.bend | 72 ++++++++----------- .../run_file/superposed_is_even.bend | 20 +++--- tests/snapshots/cli__compile_all.bend.snap | 4 +- .../compile_file__just_data.bend.snap | 2 +- .../compile_file__just_paren.bend.snap | 2 +- .../compile_file__missing_adt_eq.bend.snap | 2 +- .../compile_file__missing_ctrs.bend.snap | 2 +- .../desugar_file__ask_branch.bend.snap | 22 +++++- .../desugar_file__bind_syntax.bend.snap | 30 ++++---- .../desugar_file__combinators.bend.snap | 40 +++++------ .../desugar_file__deref_loop.bend.snap | 10 +-- .../desugar_file__dup_linearization.bend.snap | 2 +- .../desugar_file__local_def_shadow.bend.snap | 10 +-- .../desugar_file__main_aux.bend.snap | 8 +-- .../desugar_file__mapper_syntax.bend.snap | 23 +++--- .../desugar_file__switch_with_use.bend.snap | 2 +- .../desugar_file__tree_syntax.bend.snap | 30 ++++---- .../snapshots/desugar_file__use_id.bend.snap | 2 +- .../desugar_file__use_shadow.bend.snap | 2 +- .../desugar_file__used_once_names.bend.snap | 4 +- ...ncode_pattern_match__adt_tup_era.bend.snap | 12 ++-- .../encode_pattern_match__and3.bend.snap | 8 +-- .../encode_pattern_match__bool.bend.snap | 20 +++--- .../encode_pattern_match__bool_tup.bend.snap | 8 +-- .../encode_pattern_match__box.bend.snap | 4 +- .../encode_pattern_match__common.bend.snap | 4 +- .../encode_pattern_match__concat.bend.snap | 12 ++-- ...encode_pattern_match__concat_def.bend.snap | 12 ++-- .../encode_pattern_match__def_tups.bend.snap | 8 +-- ..._pattern_match__definition_merge.bend.snap | 4 +- .../encode_pattern_match__expr.bend.snap | 28 ++++---- ...e_pattern_match__flatten_era_pat.bend.snap | 16 ++--- .../encode_pattern_match__full_map.bend.snap | 46 ++++++------ ...code_pattern_match__is_some_some.bend.snap | 8 +-- ...e_pattern_match__list_merge_sort.bend.snap | 32 ++++----- ..._list_str_encoding_undeclared_fn.bend.snap | 12 ++-- ...list_str_encoding_undeclared_map.bend.snap | 4 +- ...match__match_adt_unscoped_in_arm.bend.snap | 4 +- ...match__match_adt_unscoped_lambda.bend.snap | 4 +- ...rn_match__match_adt_unscoped_var.bend.snap | 12 ++-- ..._match__match_auto_linearization.bend.snap | 20 +++--- ...encode_pattern_match__match_bind.bend.snap | 8 +-- ..._match__match_num_adt_tup_parser.bend.snap | 12 ++-- ...de_pattern_match__match_num_pred.bend.snap | 20 +++--- ...code_pattern_match__match_syntax.bend.snap | 8 +-- ...e_pattern_match__merge_recursive.bend.snap | 16 ++--- ...ncode_pattern_match__no_patterns.bend.snap | 12 ++-- ...tern_match__non_matching_fst_arg.bend.snap | 4 +- .../encode_pattern_match__ntup_sum.bend.snap | 8 +-- ...rn_match__pattern_match_encoding.bend.snap | 16 ++--- ...tern_match__switch_in_switch_arg.bend.snap | 4 +- .../encode_pattern_match__var_only.bend.snap | 8 +-- .../encode_pattern_match__weekday.bend.snap | 4 +- tests/snapshots/examples__list.bend.snap | 2 +- tests/snapshots/parse_file__era.bend.snap | 1 + .../parse_file__fun_def_name.bend.snap | 2 +- tests/snapshots/parse_file__imp_map.bend.snap | 17 +++-- .../parse_file__imp_program.bend.snap | 51 ++++++------- .../parse_file__multi_line_comment.bend.snap | 8 +-- tests/snapshots/run_file__num_cast.bend.snap | 4 +- .../run_file__superposed_is_even.bend.snap | 4 +- 65 files changed, 416 insertions(+), 393 deletions(-) diff --git a/examples/example_fun.bend b/examples/example_fun.bend index 0ac50c696..8d701be0c 100644 --- a/examples/example_fun.bend +++ b/examples/example_fun.bend @@ -117,7 +117,6 @@ new_list = bend x = 0 { # To make programs that are more parallelizable, you generally want to # avoid lists and use tree-like structures with multiple recursion instead. sum_nums from to = - # if (< from to) { (+ (sum_nums from (/ 2 (+ from to))) @@ -126,10 +125,12 @@ sum_nums from to = from } -# Internally, each variable is only used once. -# If you use a variable is used more than once, the compiler will insert duplications for you. -# You can also write them manually with 'let {a b} = ...', -# but then you have to mark the function as unchecked. +# Internally, each variable is only used once. If you use a variable +# is used more than once, the compiler will insert duplications for you. +# +# You can also write them manually with 'let {a b} = ...', but then +# your function needs to be unchecked, either by not annotating it +# with types or by marking it as unchecked. unchecked (def4) = λz let {z1 z2} = z; (z1 ((λx (x x x x x)) z2)) # Duplicating a term that duplicates a variable is not allowed and will break the program. diff --git a/examples/queue.bend b/examples/queue.bend index e31278f9c..b43281b5b 100644 --- a/examples/queue.bend +++ b/examples/queue.bend @@ -2,16 +2,16 @@ # Implements constant-time queues with just lambdas # Qnew : Queue a -Qnew = λx x +Qnew: _ = λx x # Qadd : a -> Queue a -> Queue a -Qadd = λx λq λk (q λc (c x k)) +Qadd: _ = λx λq λk (q λc (c x k)) # Qrem : Queue a -> Pair a (Queue a) -unchecked Qrem = λq (q $k λx λxs λp(p x λ$k xs)) +Qrem = λq (q $k λx λxs λp(p x λ$k xs)) # Output: [1, 2, 3] -main = +main: (List u24) = let q = Qnew let q = (Qadd 1 q) let q = (Qadd 2 q) diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index e549f460b..feb22e6db 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -284,11 +284,17 @@ def IO/map(io: IO(A), f: A -> B) -> IO(B): # # Should only be called if the other `IOError` variants are unreachable. def IO/unwrap_inner(io: IO(Result(A, IOError(B)))) -> IO(Result(A, B)): - return IO/map(io, lambda x: Result/map_err(x, IOError/unwrap_inner)) + with IO: + res <- io + match res: + case Result/Ok: + return wrap(Result/Ok(res.val)) + case Result/Err: + return wrap(Result/Err(IOError/unwrap_inner(res.val))) ## Time and sleep - # Returns a monotonically increasing nanosecond timestamp as an u48 +# Returns a monotonically increasing nanosecond timestamp as an u48 # encoded as a pair of u24s. def IO/get_time() -> IO((u24, u24)): with IO: @@ -407,7 +413,7 @@ def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(L # If length is 0, the end of the file was reached, return as if it was a newline if length == 0: bytes = List/flatten(chunks) - return wrap(bytes) + return wrap(Result/Ok(bytes)) # Otherwise, the line is still ongoing, read more chunks else: chunks = List/Cons(line, chunks) diff --git a/tests/golden_tests/parse_file/redefinition_with_type_between.bend b/tests/golden_tests/parse_file/redefinition_with_type_between.bend index 8dc6f0c42..682e19143 100644 --- a/tests/golden_tests/parse_file/redefinition_with_type_between.bend +++ b/tests/golden_tests/parse_file/redefinition_with_type_between.bend @@ -1,3 +1,3 @@ A = 0 -type B = B +type MyType = MyType A = 1 diff --git a/tests/golden_tests/run_file/num_cast.bend b/tests/golden_tests/run_file/num_cast.bend index cd93d43dd..ccf163407 100644 --- a/tests/golden_tests/run_file/num_cast.bend +++ b/tests/golden_tests/run_file/num_cast.bend @@ -1,48 +1,34 @@ -main = +main: _ = use inf = (** 9.0 (** 9.0 9.0)) ( - (to_u24 0), - (to_u24 1), - (to_u24 123456), - (to_u24 -1), - (to_u24 +1), - (to_u24 +0), - (to_u24 +400), - (to_u24 1.0), - (to_u24 1.5), - (to_u24 -3.0), - (to_u24 -3.5), - (to_u24 inf), # inf - (to_u24 (* -1.0 inf)), # -inf - (to_u24 (/ inf inf)), # nan (inf/inf) + (i24_to_u24 -1), + (i24_to_u24 +1), + (i24_to_u24 +0), + (i24_to_u24 +400), + (f24_to_u24 1.0), + (f24_to_u24 1.5), + (f24_to_u24 -3.0), + (f24_to_u24 -3.5), + (f24_to_u24 inf), # inf + (f24_to_u24 (* -1.0 inf)), # -inf + (f24_to_u24 (/ inf inf)), # nan (inf/inf) 99999, - (to_i24 0), - (to_i24 1), - (to_i24 123456), - (to_i24 -1), - (to_i24 +1), - (to_i24 +0), - (to_i24 +400), - (to_i24 1.0), - (to_i24 1.5), - (to_i24 -3.0), - (to_i24 -3.5), - (to_i24 inf), # inf - (to_i24 (* -1.0 inf)), # -inf - (to_i24 (/ inf inf)), # nan (inf/inf) + (u24_to_i24 0), + (u24_to_i24 1), + (u24_to_i24 123456), + (f24_to_i24 1.0), + (f24_to_i24 1.5), + (f24_to_i24 -3.0), + (f24_to_i24 -3.5), + (f24_to_i24 inf), # inf + (f24_to_i24 (* -1.0 inf)), # -inf + (f24_to_i24 (/ inf inf)), # nan (inf/inf) 99999, - (to_f24 0), - (to_f24 1), - (to_f24 123456), - (to_f24 -1), - (to_f24 +1), - (to_f24 +0), - (to_f24 +400), - (to_f24 1.0), - (to_f24 1.5), - (to_f24 -3.0), - (to_f24 -3.5), - (to_f24 inf), # inf - (to_f24 (* -1.0 inf)), # -inf - (to_f24 (/ inf inf)) # nan (inf/inf) + (u24_to_f24 0), + (u24_to_f24 1), + (u24_to_f24 123456), + (i24_to_f24 -1), + (i24_to_f24 +1), + (i24_to_f24 +0), + (i24_to_f24 +400) ) \ No newline at end of file diff --git a/tests/golden_tests/run_file/superposed_is_even.bend b/tests/golden_tests/run_file/superposed_is_even.bend index 2f2a4c2d4..6fb60e0b0 100644 --- a/tests/golden_tests/run_file/superposed_is_even.bend +++ b/tests/golden_tests/run_file/superposed_is_even.bend @@ -1,18 +1,18 @@ -type N = (S pred) | Z -type B = T | F +type MyNat = (S pred) | Z +type MyBool = T | F -(Not B/T) = B/F -(Not B/F) = B/T +(Not MyBool/T) = MyBool/F +(Not MyBool/F) = MyBool/T (IsEven n) = match n { - N/S: (Not (IsEven n.pred)) - N/Z: B/T + MyNat/S: (Not (IsEven n.pred)) + MyNat/Z: MyBool/T } -N0 = N/Z -N1 = (N/S N0) -N2 = (N/S N1) -N3 = (N/S N2) +N0 = MyNat/Z +N1 = (MyNat/S N0) +N2 = (MyNat/S N1) +N3 = (MyNat/S N2) Main = (IsEven {{N0 N1} {N2 N3}}) diff --git a/tests/snapshots/cli__compile_all.bend.snap b/tests/snapshots/cli__compile_all.bend.snap index 4053e69c4..980463d45 100644 --- a/tests/snapshots/cli__compile_all.bend.snap +++ b/tests/snapshots/cli__compile_all.bend.snap @@ -8,9 +8,7 @@ input_file: tests/golden_tests/cli/compile_all.bend @Pair.get__C1 = (?((@Pair.get__C0 *) a) a) -@Pair/Pair = (a (b ((@Pair/Pair/tag (a (b c))) c))) - -@Pair/Pair/tag = 0 +@Pair/Pair = (a (b ((0 (a (b c))) c))) @main = d & @Pair.get ~ (($([+] $(a b)) (a b)) (c d)) diff --git a/tests/snapshots/compile_file__just_data.bend.snap b/tests/snapshots/compile_file__just_data.bend.snap index e8c8a198d..937857d9c 100644 --- a/tests/snapshots/compile_file__just_data.bend.snap +++ b/tests/snapshots/compile_file__just_data.bend.snap @@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/just_data.bend --- Errors: In tests/golden_tests/compile_file/just_data.bend : -- expected: datatype name +- expected: Datatype name - detected: end of input  1 | type  diff --git a/tests/snapshots/compile_file__just_paren.bend.snap b/tests/snapshots/compile_file__just_paren.bend.snap index 473cdde5a..1a36d8623 100644 --- a/tests/snapshots/compile_file__just_paren.bend.snap +++ b/tests/snapshots/compile_file__just_paren.bend.snap @@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/just_paren.bend --- Errors: In tests/golden_tests/compile_file/just_paren.bend : -- expected: function name +- expected: Function name - detected: end of input  2 | (  diff --git a/tests/snapshots/compile_file__missing_adt_eq.bend.snap b/tests/snapshots/compile_file__missing_adt_eq.bend.snap index d73423579..508bc39ad 100644 --- a/tests/snapshots/compile_file__missing_adt_eq.bend.snap +++ b/tests/snapshots/compile_file__missing_adt_eq.bend.snap @@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/missing_adt_eq.bend --- Errors: In tests/golden_tests/compile_file/missing_adt_eq.bend : -- expected: '=' +- expected: Type variable or '=' - detected: end of input  1 | type Adt  diff --git a/tests/snapshots/compile_file__missing_ctrs.bend.snap b/tests/snapshots/compile_file__missing_ctrs.bend.snap index 820876baa..1ecd93d7d 100644 --- a/tests/snapshots/compile_file__missing_ctrs.bend.snap +++ b/tests/snapshots/compile_file__missing_ctrs.bend.snap @@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/missing_ctrs.bend --- Errors: In tests/golden_tests/compile_file/missing_ctrs.bend : -- expected: datatype constructor name +- expected: Datatype constructor name - detected: end of input  1 | type Adt =  diff --git a/tests/snapshots/desugar_file__ask_branch.bend.snap b/tests/snapshots/desugar_file__ask_branch.bend.snap index c6a5750a3..dc7177059 100644 --- a/tests/snapshots/desugar_file__ask_branch.bend.snap +++ b/tests/snapshots/desugar_file__ask_branch.bend.snap @@ -2,34 +2,50 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/ask_branch.bend --- -(undefer) = λa (a λb b) - +IO/MAGIC: (u24, u24) (IO/MAGIC) = (13683217, 16719857) +IO/wrap: (a -> (IO a)) (IO/wrap) = λa (IO/Done IO/MAGIC a) +IO/bind: ((IO a) -> ((c -> c) -> a -> (IO b)) -> (IO b)) (IO/bind) = λa λb (a IO/bind__C2 b) +undefer: (((a -> a) -> b) -> b) +(undefer) = λa (a λb b) + +unchecked main: Any (main) = (IO/bind (Bool/T λa switch a { 0: (IO/wrap 0); _: λ* (IO/wrap 0); }) λb (b λc λd (c d) IO/wrap)) +IO/Done: ((u24, u24) -> a -> (IO a)) (IO/Done) = λa λb λc (c IO/Done/tag a b) +IO/Call: ((u24, u24) -> String -> Any -> ((Result Any (IOError Any)) -> (IO a)) -> (IO a)) (IO/Call) = λa λb λc λd λe (e IO/Call/tag a b c d) +Bool/T: Bool (Bool/T) = λa (a Bool/T/tag) +Bool/F: Bool (Bool/F) = λa (a Bool/F/tag) +IO/Done/tag: u24 (IO/Done/tag) = 0 +IO/Call/tag: u24 (IO/Call/tag) = 1 +Bool/T/tag: u24 (Bool/T/tag) = 0 +Bool/F/tag: u24 (Bool/F/tag) = 1 +IO/bind__C0: _ (IO/bind__C0) = λ* λa λb (undefer b a) -(IO/bind__C1) = λ* λ* λa λb λc λd (IO/Call IO/MAGIC a b λe (IO/bind (c e) d)) +IO/bind__C1: _ +(IO/bind__C1) = λ* λa λb λc λd λe (IO/Call a b c λf (IO/bind (d f) e)) +IO/bind__C2: _ (IO/bind__C2) = λa switch a { 0: IO/bind__C0; _: IO/bind__C1; } diff --git a/tests/snapshots/desugar_file__bind_syntax.bend.snap b/tests/snapshots/desugar_file__bind_syntax.bend.snap index b986fd9d3..55b6fd2b7 100644 --- a/tests/snapshots/desugar_file__bind_syntax.bend.snap +++ b/tests/snapshots/desugar_file__bind_syntax.bend.snap @@ -2,31 +2,31 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/bind_syntax.bend --- -undefer: Any -(undefer) = λa (a λb b) - -Result/bind: Any +unchecked Result/bind: Any (Result/bind) = λa λb (a Result/bind__C2 b) -safe_div: Any +unchecked safe_div: Any (safe_div) = λa λb (switch b { 0: λ* (Result/Err (String/Cons 68 (String/Cons 105 (String/Cons 118 (String/Cons 32 (String/Cons 98 (String/Cons 121 (String/Cons 32 (String/Cons 48 String/Nil))))))))); _: safe_div__C0; } a) -safe_rem: Any +unchecked safe_rem: Any (safe_rem) = λa λb (switch b { 0: λ* (Result/Err (String/Cons 77 (String/Cons 111 (String/Cons 100 (String/Cons 32 (String/Cons 98 (String/Cons 121 (String/Cons 32 (String/Cons 48 String/Nil))))))))); _: safe_rem__C0; } a) -Main: Any +unchecked Main: Any (Main) = (Result/bind (safe_div 3 2) λa (a λb (Result/bind (safe_rem b 0) λc (c λd d)))) +undefer: (((a -> a) -> b) -> b) +(undefer) = λa (a λb b) + String/Nil: String (String/Nil) = λa (a String/Nil/tag) -String/Cons: (u24 -> (String -> String)) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λc (c String/Cons/tag a b) -Result/Ok: ∀o ∀e (o -> (Result o e)) +Result/Ok: (b -> (Result b a)) (Result/Ok) = λa λb (b Result/Ok/tag a) -Result/Err: ∀o ∀e (e -> (Result o e)) +Result/Err: (a -> (Result b a)) (Result/Err) = λa λb (b Result/Err/tag a) String/Nil/tag: u24 @@ -41,17 +41,17 @@ Result/Ok/tag: u24 Result/Err/tag: u24 (Result/Err/tag) = 1 -Result/bind__C0: _ +unchecked Result/bind__C0: _ (Result/bind__C0) = λa λb (undefer b a) -Result/bind__C1: _ +unchecked Result/bind__C1: _ (Result/bind__C1) = λ* λa λ* (Result/Err a) -Result/bind__C2: _ +unchecked Result/bind__C2: _ (Result/bind__C2) = λa switch a { 0: Result/bind__C0; _: Result/bind__C1; } -safe_div__C0: _ +unchecked safe_div__C0: _ (safe_div__C0) = λa λb (Result/Ok (/ b (+ a 1))) -safe_rem__C0: _ +unchecked safe_rem__C0: _ (safe_rem__C0) = λa λb (Result/Ok (% b (+ a 1))) diff --git a/tests/snapshots/desugar_file__combinators.bend.snap b/tests/snapshots/desugar_file__combinators.bend.snap index 6504077af..e261f6ac9 100644 --- a/tests/snapshots/desugar_file__combinators.bend.snap +++ b/tests/snapshots/desugar_file__combinators.bend.snap @@ -2,43 +2,43 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/combinators.bend --- -foo: Any +unchecked foo: Any (foo) = λa λ* λ* (foo a) -bar: Any +unchecked bar: Any (bar) = λa λb (a bar b) -List/ignore: Any +unchecked List/ignore: Any (List/ignore) = λa λ* (a List/ignore__C1) -baz: Any +unchecked baz: Any (baz) = {0 1 2 3 λa a foo} -qux: Any +unchecked qux: Any (qux) = {0 qux} -clax: Any +unchecked clax: Any (clax) = (λa a clax__C0) -tup: Any +unchecked tup: Any (tup) = (tup, 1, 0) -list: Any +unchecked list: Any (list) = (List/Cons 0 list__C0) -A: Any +unchecked A: Any (A) = λa (A__C0 a) -B: Any +unchecked B: Any (B) = λa (B__C0 a) -Main: Any -(Main) = (List/Cons 0 list__C0) +unchecked Main: Any +(Main) = (List/Cons 0 (List/Cons list List/Nil)) -List/Nil: ∀t (List t) +List/Nil: (List a) (List/Nil) = λa (a List/Nil/tag) -List/Cons: ∀t (u24 -> ((List t) -> (List t))) +List/Cons: (a -> (List a) -> (List a)) (List/Cons) = λa λb λc (c List/Cons/tag a b) List/Nil/tag: u24 @@ -47,20 +47,20 @@ List/Nil/tag: u24 List/Cons/tag: u24 (List/Cons/tag) = 1 -A__C0: _ +unchecked A__C0: _ (A__C0) = let {a b} = A; λc (a b c) -B__C0: _ +unchecked B__C0: _ (B__C0) = let (a, b) = B; λc (a b c) -List/ignore__C0: _ +unchecked List/ignore__C0: _ (List/ignore__C0) = λ* λ* λa (List/ignore a List/ignore) -List/ignore__C1: _ +unchecked List/ignore__C1: _ (List/ignore__C1) = λa switch a { 0: 0; _: List/ignore__C0; } -clax__C0: _ +unchecked clax__C0: _ (clax__C0) = λ* λ* λ* λa (clax a) -list__C0: _ +unchecked list__C0: _ (list__C0) = (List/Cons list List/Nil) diff --git a/tests/snapshots/desugar_file__deref_loop.bend.snap b/tests/snapshots/desugar_file__deref_loop.bend.snap index 18562cfb6..33630f986 100644 --- a/tests/snapshots/desugar_file__deref_loop.bend.snap +++ b/tests/snapshots/desugar_file__deref_loop.bend.snap @@ -2,13 +2,13 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/deref_loop.bend --- -foo: Any +unchecked foo: Any (foo) = λa (a foo__C1) -bar: Any +unchecked bar: Any (bar) = (foo 1) -main: Any +unchecked main: Any (main) = (foo 0) nat/succ: (Any -> nat) @@ -23,8 +23,8 @@ nat/succ/tag: u24 nat/zero/tag: u24 (nat/zero/tag) = 1 -foo__C0: _ +unchecked foo__C0: _ (foo__C0) = λ* (bar 0) -foo__C1: _ +unchecked foo__C1: _ (foo__C1) = λa switch a { 0: λb b; _: foo__C0; } diff --git a/tests/snapshots/desugar_file__dup_linearization.bend.snap b/tests/snapshots/desugar_file__dup_linearization.bend.snap index 26f3ad315..ac3a8441d 100644 --- a/tests/snapshots/desugar_file__dup_linearization.bend.snap +++ b/tests/snapshots/desugar_file__dup_linearization.bend.snap @@ -2,5 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/dup_linearization.bend --- -main: Any +unchecked main: Any (main) = let {a b c d e} = *; (a, e, d, c, b) diff --git a/tests/snapshots/desugar_file__local_def_shadow.bend.snap b/tests/snapshots/desugar_file__local_def_shadow.bend.snap index 534dd51d9..95bde9e9a 100644 --- a/tests/snapshots/desugar_file__local_def_shadow.bend.snap +++ b/tests/snapshots/desugar_file__local_def_shadow.bend.snap @@ -2,17 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/local_def_shadow.bend --- -main: Any +unchecked main: Any (main) = 1 -main__local_0_A__local_0_B: _ +unchecked main__local_0_A__local_0_B: _ (main__local_0_A__local_0_B) = 0 -main__local_1_A__local_1_B: _ +unchecked main__local_1_A__local_1_B: _ (main__local_1_A__local_1_B) = 1 -main__local_1_A: _ +unchecked main__local_1_A: _ (main__local_1_A) = main__local_1_A__local_1_B -main__local_0_A: _ +unchecked main__local_0_A: _ (main__local_0_A) = main__local_0_A__local_0_B diff --git a/tests/snapshots/desugar_file__main_aux.bend.snap b/tests/snapshots/desugar_file__main_aux.bend.snap index b52a4bdc4..cfa74850c 100644 --- a/tests/snapshots/desugar_file__main_aux.bend.snap +++ b/tests/snapshots/desugar_file__main_aux.bend.snap @@ -2,14 +2,14 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/main_aux.bend --- -main: Any +unchecked main: Any (main) = (main__local_0_aux 89 2) -main__local_0_aux__local_0_aux__local_0_aux: _ +unchecked main__local_0_aux__local_0_aux__local_0_aux: _ (main__local_0_aux__local_0_aux__local_0_aux) = λa λb (+ b a) -main__local_0_aux__local_0_aux: _ +unchecked main__local_0_aux__local_0_aux: _ (main__local_0_aux__local_0_aux) = λa λb (main__local_0_aux__local_0_aux__local_0_aux a b) -main__local_0_aux: _ +unchecked main__local_0_aux: _ (main__local_0_aux) = λa λb (main__local_0_aux__local_0_aux a b) diff --git a/tests/snapshots/desugar_file__mapper_syntax.bend.snap b/tests/snapshots/desugar_file__mapper_syntax.bend.snap index 401f2fd61..060499a74 100644 --- a/tests/snapshots/desugar_file__mapper_syntax.bend.snap +++ b/tests/snapshots/desugar_file__mapper_syntax.bend.snap @@ -2,25 +2,28 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/mapper_syntax.bend --- -Map/empty: ∀T (Map T) +Map/empty: (Map a) (Map/empty) = Map/Leaf -Map/get: ∀T ((Map T) -> (u24 -> ((Map T), T))) +Map/get: ((Map a) -> u24 -> (a, (Map a))) (Map/get) = λa λb (a Map/get__C5 b) -Map/set: ∀T ((Map T) -> (u24 -> (T -> (Map T)))) +Map/set: ((Map a) -> u24 -> a -> (Map a)) (Map/set) = λa λb λc (a Map/set__C10 b c) -Map/map: ∀T ((Map T) -> (u24 -> ((T -> T) -> (Map T)))) +Map/map: ((Map a) -> u24 -> (a -> a) -> (Map a)) (Map/map) = λa λb λc (a Map/map__C5 b c) -main: Any +unreachable: Any +(unreachable) = * + +unchecked main: Any (main) = let (c, d) = (Map/get (Map/map (Map/map (Map/set (Map/set Map/empty 0 3) 1 4) 1 λa (+ a 1)) 1 λb (* b 2)) 1); let (e, *) = (Map/get d 0); ((λf (+ f 1) 1), c, e) -Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) +Map/Node: (a -> (Map a) -> (Map a) -> (Map a)) (Map/Node) = λa λb λc λd (d Map/Node/tag a b c) -Map/Leaf: ∀T (Map T) +Map/Leaf: (Map a) (Map/Leaf) = λa (a Map/Leaf/tag) Map/Node/tag: u24 @@ -45,7 +48,7 @@ Map/get__C4: _ (Map/get__C4) = λa λb λc λd let {e f} = d; (switch (== 0 e) { 0: Map/get__C2; _: Map/get__C3; } f a b c) Map/get__C5: _ -(Map/get__C5) = λa switch a { 0: Map/get__C4; _: λ* λ* (*, Map/Leaf); } +(Map/get__C5) = λa switch a { 0: Map/get__C4; _: λ* λ* (unreachable, Map/Leaf); } Map/map__C0: _ (Map/map__C0) = λa λb λc λd λe (Map/Node c (Map/map d (/ a 2) b) e) @@ -81,10 +84,10 @@ Map/set__C3: _ (Map/set__C3) = λ* λ* λa λ* λb λc (Map/Node a b c) Map/set__C4: _ -(Map/set__C4) = λa λb (Map/Node * (Map/set Map/Leaf (/ a 2) b) Map/Leaf) +(Map/set__C4) = λa λb (Map/Node unreachable (Map/set Map/Leaf (/ a 2) b) Map/Leaf) Map/set__C5: _ -(Map/set__C5) = λ* λa λb (Map/Node * Map/Leaf (Map/set Map/Leaf (/ a 2) b)) +(Map/set__C5) = λ* λa λb (Map/Node unreachable Map/Leaf (Map/set Map/Leaf (/ a 2) b)) Map/set__C6: _ (Map/set__C6) = λa let {b c} = a; λd (switch (% b 2) { 0: Map/set__C4; _: Map/set__C5; } c d) diff --git a/tests/snapshots/desugar_file__switch_with_use.bend.snap b/tests/snapshots/desugar_file__switch_with_use.bend.snap index 808f5456c..2a7b24b60 100644 --- a/tests/snapshots/desugar_file__switch_with_use.bend.snap +++ b/tests/snapshots/desugar_file__switch_with_use.bend.snap @@ -2,5 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/switch_with_use.bend --- -main: Any +unchecked main: Any (main) = λa λb λc λ* λ* (switch c { 0: λd d; _: λe λf (e f); } (a b)) diff --git a/tests/snapshots/desugar_file__tree_syntax.bend.snap b/tests/snapshots/desugar_file__tree_syntax.bend.snap index a1abe4fe7..ac61c380b 100644 --- a/tests/snapshots/desugar_file__tree_syntax.bend.snap +++ b/tests/snapshots/desugar_file__tree_syntax.bend.snap @@ -2,43 +2,43 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/tree_syntax.bend --- -fun0: Any +unchecked fun0: Any (fun0) = (Tree/Node (Tree/Leaf 0) (Tree/Node (Tree/Leaf 1) (Tree/Node (Tree/Leaf 2) (Tree/Leaf 3)))) -fun1: Any +unchecked fun1: Any (fun1) = (Tree/Leaf (Tree/Node (Tree/Leaf *) (Tree/Leaf *))) -fun2: Any +unchecked fun2: Any (fun2) = (Tree/Leaf (Tree/Leaf (Tree/Leaf *))) -fun3: Any +unchecked fun3: Any (fun3) = (Tree/Leaf 1) -fun4: Any +unchecked fun4: Any (fun4) = λa switch a { 0: (Tree/Leaf 0); _: fun4__C0; } -main: Any +unchecked main: Any (main) = * -imp0: Any +unchecked imp0: Any (imp0) = (Tree/Node (Tree/Leaf 0) (Tree/Node (Tree/Leaf 1) (Tree/Node (Tree/Leaf 2) (Tree/Leaf 3)))) -imp1: Any +unchecked imp1: Any (imp1) = (Tree/Leaf (Tree/Node (Tree/Leaf *) (Tree/Leaf *))) -imp2: Any +unchecked imp2: Any (imp2) = (Tree/Leaf (Tree/Leaf (Tree/Leaf *))) -imp3: Any +unchecked imp3: Any (imp3) = (Tree/Leaf 1) -imp4: (Any -> Any) +unchecked imp4: (Any -> Any) (imp4) = λa switch a { 0: (Tree/Leaf 0); _: imp4__C0; } -Tree/Node: ∀T ((Tree T) -> ((Tree T) -> (Tree T))) +Tree/Node: ((Tree a) -> (Tree a) -> (Tree a)) (Tree/Node) = λa λb λc (c Tree/Node/tag a b) -Tree/Leaf: ∀T (T -> (Tree T)) +Tree/Leaf: (a -> (Tree a)) (Tree/Leaf) = λa λb (b Tree/Leaf/tag a) Tree/Node/tag: u24 @@ -47,8 +47,8 @@ Tree/Node/tag: u24 Tree/Leaf/tag: u24 (Tree/Leaf/tag) = 1 -fun4__C0: _ +unchecked fun4__C0: _ (fun4__C0) = λa let {b c} = a; (Tree/Node (fun4 b) (fun4 c)) -imp4__C0: _ +unchecked imp4__C0: _ (imp4__C0) = λa let {b c} = a; (Tree/Node (imp4 b) (imp4 c)) diff --git a/tests/snapshots/desugar_file__use_id.bend.snap b/tests/snapshots/desugar_file__use_id.bend.snap index 4c292199c..73bce83c7 100644 --- a/tests/snapshots/desugar_file__use_id.bend.snap +++ b/tests/snapshots/desugar_file__use_id.bend.snap @@ -2,5 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/use_id.bend --- -Main: Any +unchecked Main: Any (Main) = (λa a 2 3 (λb b 2 3) (λc c 2 3 (λd d 2 3))) diff --git a/tests/snapshots/desugar_file__use_shadow.bend.snap b/tests/snapshots/desugar_file__use_shadow.bend.snap index c36b51ce1..3bfce84dd 100644 --- a/tests/snapshots/desugar_file__use_shadow.bend.snap +++ b/tests/snapshots/desugar_file__use_shadow.bend.snap @@ -2,5 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/use_shadow.bend --- -main: Any +unchecked main: Any (main) = λa let {b c} = a; λd (b c d) diff --git a/tests/snapshots/desugar_file__used_once_names.bend.snap b/tests/snapshots/desugar_file__used_once_names.bend.snap index da4b14214..904a4ea19 100644 --- a/tests/snapshots/desugar_file__used_once_names.bend.snap +++ b/tests/snapshots/desugar_file__used_once_names.bend.snap @@ -2,8 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/used_once_names.bend --- -foo: Any +unchecked foo: Any (foo) = λa λb λc let {d e} = c; (a b (d e)) -main: Any +unchecked main: Any (main) = (foo 2 3 λa a) diff --git a/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap b/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap index 9921743e1..f1125de0e 100644 --- a/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap +++ b/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap @@ -3,23 +3,23 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/adt_tup_era.bend --- Scott -Foo: Any +unchecked Foo: Any (Foo) = λa (a λb λc (b λd λ* λ* d c)) -Main: Any +unchecked Main: Any (Main) = (Foo (Tuple/Pair 1 5)) -Tuple/Pair: (Any -> (Any -> Tuple)) +Tuple/Pair: (Any -> Any -> Tuple) (Tuple/Pair) = λa λb λc (c a b) NumScott -Foo: Any +unchecked Foo: Any (Foo) = λa (a λb switch b { 0: λc λd (c λe switch e { 0: λf λ* λ* f; _: *; } d); _: *; }) -Main: Any +unchecked Main: Any (Main) = (Foo (Tuple/Pair 1 5)) -Tuple/Pair: (Any -> (Any -> Tuple)) +Tuple/Pair: (Any -> Any -> Tuple) (Tuple/Pair) = λa λb λc (c Tuple/Pair/tag a b) Tuple/Pair/tag: u24 diff --git a/tests/snapshots/encode_pattern_match__and3.bend.snap b/tests/snapshots/encode_pattern_match__and3.bend.snap index 1429c58e2..372d1ec55 100644 --- a/tests/snapshots/encode_pattern_match__and3.bend.snap +++ b/tests/snapshots/encode_pattern_match__and3.bend.snap @@ -3,10 +3,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/and3.bend --- Scott -And: Any +unchecked And: Any (And) = λa let (b, c, d) = a; (b λe λf (e λg (g Bool/T Bool/F) λ* Bool/F f) λ* λ* Bool/F c d) -main: Any +unchecked main: Any (main) = (And (Bool/F, Bool/T, Bool/F)) Bool/T: Bool @@ -16,10 +16,10 @@ Bool/F: Bool (Bool/F) = λ* λb b NumScott -And: Any +unchecked And: Any (And) = λa let (b, c, d) = a; (b λe switch e { 0: λf λg (f λh switch h { 0: λi (i λj switch j { 0: Bool/T; _: λ* Bool/F; }); _: λ* λ* Bool/F; } g); _: λ* λ* λ* Bool/F; } c d) -main: Any +unchecked main: Any (main) = (And (Bool/F, Bool/T, Bool/F)) Bool/T: Bool diff --git a/tests/snapshots/encode_pattern_match__bool.bend.snap b/tests/snapshots/encode_pattern_match__bool.bend.snap index 4fd8925b7..fe60384ad 100644 --- a/tests/snapshots/encode_pattern_match__bool.bend.snap +++ b/tests/snapshots/encode_pattern_match__bool.bend.snap @@ -3,19 +3,19 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/bool.bend --- Scott -not: Any +unchecked not: Any (not) = λa (a bool/false bool/true) -and: Any +unchecked and: Any (and) = λa (a λb (b bool/true bool/false) λd (d bool/false bool/false)) -and2: Any +unchecked and2: Any (and2) = λa (a λb b λd let * = d; bool/false) -and3: Any +unchecked and3: Any (and3) = λa (a λb (b bool/true bool/false) λd let * = d; bool/false) -and4: Any +unchecked and4: Any (and4) = λa (a λb (b bool/true bool/false) λd let * = d; bool/false) bool/true: bool @@ -25,19 +25,19 @@ bool/false: bool (bool/false) = λ* λb b NumScott -not: Any +unchecked not: Any (not) = λa (a λb switch b { 0: bool/false; _: λ* bool/true; }) -and: Any +unchecked and: Any (and) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf (f λh switch h { 0: bool/false; _: λ* bool/false; }); }) -and2: Any +unchecked and2: Any (and2) = λa (a λb switch b { 0: λc c; _: λ* λe let * = e; bool/false; }) -and3: Any +unchecked and3: Any (and3) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf let * = f; bool/false; }) -and4: Any +unchecked and4: Any (and4) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf let * = f; bool/false; }) bool/true: bool diff --git a/tests/snapshots/encode_pattern_match__bool_tup.bend.snap b/tests/snapshots/encode_pattern_match__bool_tup.bend.snap index 545e9154d..ce956fc53 100644 --- a/tests/snapshots/encode_pattern_match__bool_tup.bend.snap +++ b/tests/snapshots/encode_pattern_match__bool_tup.bend.snap @@ -3,10 +3,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/bool_tup.bend --- Scott -foo: Any +unchecked foo: Any (foo) = λa let (b, c) = a; (b λd d λ* Bool/F c) -main: Any +unchecked main: Any (main) = (foo (Bool/F, Bool/T)) Bool/T: Bool @@ -16,10 +16,10 @@ Bool/F: Bool (Bool/F) = λ* λb b NumScott -foo: Any +unchecked foo: Any (foo) = λa let (b, c) = a; (b λd switch d { 0: λe e; _: λ* λ* Bool/F; } c) -main: Any +unchecked main: Any (main) = (foo (Bool/F, Bool/T)) Bool/T: Bool diff --git a/tests/snapshots/encode_pattern_match__box.bend.snap b/tests/snapshots/encode_pattern_match__box.bend.snap index 45c8813a9..214ba620e 100644 --- a/tests/snapshots/encode_pattern_match__box.bend.snap +++ b/tests/snapshots/encode_pattern_match__box.bend.snap @@ -3,14 +3,14 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/box.bend --- Scott -unbox: Any +unchecked unbox: Any (unbox) = λa (a λb b) box/new: (Any -> box) (box/new) = λa λb (b a) NumScott -unbox: Any +unchecked unbox: Any (unbox) = λa (a λb switch b { 0: λc c; _: *; }) box/new: (Any -> box) diff --git a/tests/snapshots/encode_pattern_match__common.bend.snap b/tests/snapshots/encode_pattern_match__common.bend.snap index 7c0bd147b..2b9fb1647 100644 --- a/tests/snapshots/encode_pattern_match__common.bend.snap +++ b/tests/snapshots/encode_pattern_match__common.bend.snap @@ -21,7 +21,7 @@ Result_/Ok: (Any -> Result_) Result_/Err: (Any -> Result_) (Result_/Err) = λa λ* λc (c a) -List_/Cons: (Any -> (Any -> List_)) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc λ* (c a b) List_/Nil: List_ @@ -73,7 +73,7 @@ Result_/Ok: (Any -> Result_) Result_/Err: (Any -> Result_) (Result_/Err) = λa λb (b Result_/Err/tag a) -List_/Cons: (Any -> (Any -> List_)) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) List_/Nil: List_ diff --git a/tests/snapshots/encode_pattern_match__concat.bend.snap b/tests/snapshots/encode_pattern_match__concat.bend.snap index bb4a87216..6e87c70fc 100644 --- a/tests/snapshots/encode_pattern_match__concat.bend.snap +++ b/tests/snapshots/encode_pattern_match__concat.bend.snap @@ -3,29 +3,29 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/concat.bend --- Scott -String/concat: Any +unchecked String/concat: Any (String/concat) = λ* λb b -main: Any +unchecked main: Any (main) = (String/concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) String/Nil: String (String/Nil) = λa λ* a -String/Cons: (u24 -> (String -> String)) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λ* λd (d a b) NumScott -String/concat: Any +unchecked String/concat: Any (String/concat) = λ* λb b -main: Any +unchecked main: Any (main) = (String/concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) String/Nil: String (String/Nil) = λa (a String/Nil/tag) -String/Cons: (u24 -> (String -> String)) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λc (c String/Cons/tag a b) String/Nil/tag: u24 diff --git a/tests/snapshots/encode_pattern_match__concat_def.bend.snap b/tests/snapshots/encode_pattern_match__concat_def.bend.snap index 64d25a4b4..8ae37a61c 100644 --- a/tests/snapshots/encode_pattern_match__concat_def.bend.snap +++ b/tests/snapshots/encode_pattern_match__concat_def.bend.snap @@ -3,29 +3,29 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/concat_def.bend --- Scott -concat: Any +unchecked concat: Any (concat) = λa (a λb b λd λe λf (String/Cons d (concat e f))) -main: Any +unchecked main: Any (main) = (concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) String/Nil: String (String/Nil) = λa λ* a -String/Cons: (u24 -> (String -> String)) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λ* λd (d a b) NumScott -concat: Any +unchecked concat: Any (concat) = λa (a λb switch b { 0: λc c; _: λ* λe λf λg (String/Cons e (concat f g)); }) -main: Any +unchecked main: Any (main) = (concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) String/Nil: String (String/Nil) = λa (a String/Nil/tag) -String/Cons: (u24 -> (String -> String)) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λc (c String/Cons/tag a b) String/Nil/tag: u24 diff --git a/tests/snapshots/encode_pattern_match__def_tups.bend.snap b/tests/snapshots/encode_pattern_match__def_tups.bend.snap index 2309f23e7..5f75be015 100644 --- a/tests/snapshots/encode_pattern_match__def_tups.bend.snap +++ b/tests/snapshots/encode_pattern_match__def_tups.bend.snap @@ -3,15 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/def_tups.bend --- Scott -go: Any +unchecked go: Any (go) = λa let (b, c) = a; let (d, e) = c; let (f, g) = e; let (h, i) = g; (+ (+ (+ (+ i h) f) d) b) -main: Any +unchecked main: Any (main) = (go (1, (2, (3, (4, 5))))) NumScott -go: Any +unchecked go: Any (go) = λa let (b, c) = a; let (d, e) = c; let (f, g) = e; let (h, i) = g; (+ (+ (+ (+ i h) f) d) b) -main: Any +unchecked main: Any (main) = (go (1, (2, (3, (4, 5))))) diff --git a/tests/snapshots/encode_pattern_match__definition_merge.bend.snap b/tests/snapshots/encode_pattern_match__definition_merge.bend.snap index a4281a0d0..48f8b8b29 100644 --- a/tests/snapshots/encode_pattern_match__definition_merge.bend.snap +++ b/tests/snapshots/encode_pattern_match__definition_merge.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/definition_merge.bend --- Scott -Foo: Any +unchecked Foo: Any (Foo) = λa (a λb (b λc (c λf (f 1 1) λg (g 2 2)) λh (h λk (k 1 1) λl (l 2 2))) λm (m λn (n λq (q 3 3) λr (r 3 3)) λs (s λv (v 3 3) λw (w 3 3)))) Either/Left: (Any -> Either) @@ -19,7 +19,7 @@ Bool/False: Bool (Bool/False) = λ* λb b NumScott -Foo: Any +unchecked Foo: Any (Foo) = λa (a λb switch b { 0: λc (c λd switch d { 0: λe (e λh switch h { 0: λi (i λj switch j { 0: 1; _: λ* 1; }); _: λ* λk (k λl switch l { 0: 2; _: λ* 2; }); }); _: λ* λm (m λp switch p { 0: λq (q λr switch r { 0: 1; _: λ* 1; }); _: λ* λs (s λt switch t { 0: 2; _: λ* 2; }); }); }); _: λ* λu (u λv switch v { 0: λw (w λz switch z { 0: λab (ab λbb switch bb { 0: 3; _: λ* 3; }); _: λ* λcb (cb λdb switch db { 0: 3; _: λ* 3; }); }); _: λ* λeb (eb λhb switch hb { 0: λib (ib λjb switch jb { 0: 3; _: λ* 3; }); _: λ* λkb (kb λlb switch lb { 0: 3; _: λ* 3; }); }); }); }) Either/Left: (Any -> Either) diff --git a/tests/snapshots/encode_pattern_match__expr.bend.snap b/tests/snapshots/encode_pattern_match__expr.bend.snap index 09b09e99a..4b4ce406b 100644 --- a/tests/snapshots/encode_pattern_match__expr.bend.snap +++ b/tests/snapshots/encode_pattern_match__expr.bend.snap @@ -9,25 +9,25 @@ Expr/Var: (Any -> Expr) Expr/Num: (Any -> Expr) (Expr/Num) = λa λ* λc λ* λ* λ* λ* λ* λ* λ* (c a) -Expr/App: (Any -> (Any -> Expr)) +Expr/App: (Any -> Any -> Expr) (Expr/App) = λa λb λ* λ* λe λ* λ* λ* λ* λ* λ* (e a b) -Expr/Fun: (Any -> (Any -> Expr)) +Expr/Fun: (Any -> Any -> Expr) (Expr/Fun) = λa λb λ* λ* λ* λf λ* λ* λ* λ* λ* (f a b) -Expr/If: (Any -> (Any -> (Any -> Expr))) +Expr/If: (Any -> Any -> Any -> Expr) (Expr/If) = λa λb λc λ* λ* λ* λ* λh λ* λ* λ* λ* (h a b c) -Expr/Let: (Any -> (Any -> (Any -> Expr))) +Expr/Let: (Any -> Any -> Any -> Expr) (Expr/Let) = λa λb λc λ* λ* λ* λ* λ* λi λ* λ* λ* (i a b c) -Expr/Dup: (Any -> (Any -> (Any -> (Any -> Expr)))) +Expr/Dup: (Any -> Any -> Any -> Any -> Expr) (Expr/Dup) = λa λb λc λd λ* λ* λ* λ* λ* λ* λk λ* λ* (k a b c d) -Expr/Tup: (Any -> (Any -> Expr)) +Expr/Tup: (Any -> Any -> Expr) (Expr/Tup) = λa λb λ* λ* λ* λ* λ* λ* λ* λj λ* (j a b) -Expr/Op2: (Any -> (Any -> (Any -> Expr))) +Expr/Op2: (Any -> Any -> Any -> Expr) (Expr/Op2) = λa λb λc λ* λ* λ* λ* λ* λ* λ* λ* λl (l a b c) Op/Add: Op @@ -49,25 +49,25 @@ Expr/Var: (Any -> Expr) Expr/Num: (Any -> Expr) (Expr/Num) = λa λb (b Expr/Num/tag a) -Expr/App: (Any -> (Any -> Expr)) +Expr/App: (Any -> Any -> Expr) (Expr/App) = λa λb λc (c Expr/App/tag a b) -Expr/Fun: (Any -> (Any -> Expr)) +Expr/Fun: (Any -> Any -> Expr) (Expr/Fun) = λa λb λc (c Expr/Fun/tag a b) -Expr/If: (Any -> (Any -> (Any -> Expr))) +Expr/If: (Any -> Any -> Any -> Expr) (Expr/If) = λa λb λc λd (d Expr/If/tag a b c) -Expr/Let: (Any -> (Any -> (Any -> Expr))) +Expr/Let: (Any -> Any -> Any -> Expr) (Expr/Let) = λa λb λc λd (d Expr/Let/tag a b c) -Expr/Dup: (Any -> (Any -> (Any -> (Any -> Expr)))) +Expr/Dup: (Any -> Any -> Any -> Any -> Expr) (Expr/Dup) = λa λb λc λd λe (e Expr/Dup/tag a b c d) -Expr/Tup: (Any -> (Any -> Expr)) +Expr/Tup: (Any -> Any -> Expr) (Expr/Tup) = λa λb λc (c Expr/Tup/tag a b) -Expr/Op2: (Any -> (Any -> (Any -> Expr))) +Expr/Op2: (Any -> Any -> Any -> Expr) (Expr/Op2) = λa λb λc λd (d Expr/Op2/tag a b c) Op/Add: Op diff --git a/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap b/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap index e0297e441..e80843335 100644 --- a/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap +++ b/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap @@ -3,27 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/flatten_era_pat.bend --- Scott -Fn1: Any +unchecked Fn1: Any (Fn1) = λa λ* let (*, d) = a; let (e, *) = d; e -Fn2: Any +unchecked Fn2: Any (Fn2) = λa let (*, c) = a; let (*, e) = c; let (f, *) = e; f -Fn3: Any +unchecked Fn3: Any (Fn3) = λa let (b, c) = a; (switch b { 0: λ* λe let * = e; 0; _: λg λ* λi let * = i; (+ g 1); } c) -main: Any +unchecked main: Any (main) = (Fn2 ((1, 2), (3, (4, (5, 6)))) 0) NumScott -Fn1: Any +unchecked Fn1: Any (Fn1) = λa λ* let (*, d) = a; let (e, *) = d; e -Fn2: Any +unchecked Fn2: Any (Fn2) = λa let (*, c) = a; let (*, e) = c; let (f, *) = e; f -Fn3: Any +unchecked Fn3: Any (Fn3) = λa let (b, c) = a; (switch b { 0: λ* λe let * = e; 0; _: λg λ* λi let * = i; (+ g 1); } c) -main: Any +unchecked main: Any (main) = (Fn2 ((1, 2), (3, (4, (5, 6)))) 0) diff --git a/tests/snapshots/encode_pattern_match__full_map.bend.snap b/tests/snapshots/encode_pattern_match__full_map.bend.snap index ac0cb38e7..ea9797b3b 100644 --- a/tests/snapshots/encode_pattern_match__full_map.bend.snap +++ b/tests/snapshots/encode_pattern_match__full_map.bend.snap @@ -3,53 +3,59 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/full_map.bend --- Scott -Map/get: ∀T ((Map T) -> (u24 -> ((Map T), T))) -(Map/get) = λa (a λb let {b b_2 b_3 b_4} = b; λc let {c c_2 c_3} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3 e_4} = e; switch (== 0 e) { 0: switch (% e_2 2) { 0: let (f, g) = (Map/get c (/ e_3 2)); (f, (Map/Node b g d)); _: λ* let (i, j) = (Map/get d_2 (/ e_4 2)); (i, (Map/Node b_2 c_2 j)); }; _: λ* (b_3, (Map/Node b_4 c_3 d_3)); } λ* (*, Map/Leaf)) +Map/get: ((Map T) -> u24 -> (T, (Map T))) +(Map/get) = λa (a λb let {b b_2 b_3 b_4} = b; λc let {c c_2 c_3} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3 e_4} = e; switch (== 0 e) { 0: switch (% e_2 2) { 0: let (f, g) = (Map/get c (/ e_3 2)); (f, (Map/Node b g d)); _: λ* let (i, j) = (Map/get d_2 (/ e_4 2)); (i, (Map/Node b_2 c_2 j)); }; _: λ* (b_3, (Map/Node b_4 c_3 d_3)); } λ* (unreachable, Map/Leaf)) -prng: (Any -> Any) +unreachable: Any +(unreachable) = * + +unchecked prng: (Any -> Any) (prng) = λa let {a a_2} = a; let {b b_2} = (^ a (<< a_2 13)); let {c c_2} = (^ b (>> b_2 17)); (^ c (<< c_2 5)) -fullMap: Any +unchecked fullMap: Any (fullMap) = (fullMap__bend0 14) -test: (Any -> Any) +unchecked test: (Any -> Any) (test) = λa (test__bend0 0 a) -main: Any +unchecked main: Any (main) = (test fullMap) -Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) +Map/Node: (T -> (Map T) -> (Map T) -> (Map T)) (Map/Node) = λa λb λc λd λ* (d a b c) -Map/Leaf: ∀T (Map T) +Map/Leaf: (Map T) (Map/Leaf) = λ* λb b -fullMap__bend0: _ +unchecked fullMap__bend0: _ (fullMap__bend0) = λa let {a a_2 a_3} = a; switch (> a 0) { 0: Map/Leaf; _: λ* (Map/Node 1 (fullMap__bend0 (- a_2 1)) (fullMap__bend0 (- a_3 1))); } -test__bend0: _ +unchecked test__bend0: _ (test__bend0) = λa let {a a_2 a_3} = a; switch (< a 1000) { 0: λ* 0; _: λ* λd let (e, f) = (Map/get d (% (prng a_2) 4096)); (+ e (test__bend0 (+ a_3 1) f)); } NumScott -Map/get: ∀T ((Map T) -> (u24 -> ((Map T), T))) -(Map/get) = λa (a λb switch b { 0: λc let {c c_2 c_3 c_4} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3} = e; λf let {f f_2 f_3 f_4} = f; switch (== 0 f) { 0: switch (% f_2 2) { 0: let (g, h) = (Map/get d (/ f_3 2)); (g, (Map/Node c h e)); _: λ* let (j, k) = (Map/get e_2 (/ f_4 2)); (j, (Map/Node c_2 d_2 k)); }; _: λ* (c_3, (Map/Node c_4 d_3 e_3)); }; _: λ* λ* (*, Map/Leaf); }) +Map/get: ((Map T) -> u24 -> (T, (Map T))) +(Map/get) = λa (a λb switch b { 0: λc let {c c_2 c_3 c_4} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3} = e; λf let {f f_2 f_3 f_4} = f; switch (== 0 f) { 0: switch (% f_2 2) { 0: let (g, h) = (Map/get d (/ f_3 2)); (g, (Map/Node c h e)); _: λ* let (j, k) = (Map/get e_2 (/ f_4 2)); (j, (Map/Node c_2 d_2 k)); }; _: λ* (c_3, (Map/Node c_4 d_3 e_3)); }; _: λ* λ* (unreachable, Map/Leaf); }) + +unreachable: Any +(unreachable) = * -prng: (Any -> Any) +unchecked prng: (Any -> Any) (prng) = λa let {a a_2} = a; let {b b_2} = (^ a (<< a_2 13)); let {c c_2} = (^ b (>> b_2 17)); (^ c (<< c_2 5)) -fullMap: Any +unchecked fullMap: Any (fullMap) = (fullMap__bend0 14) -test: (Any -> Any) +unchecked test: (Any -> Any) (test) = λa (test__bend0 0 a) -main: Any +unchecked main: Any (main) = (test fullMap) -Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) +Map/Node: (T -> (Map T) -> (Map T) -> (Map T)) (Map/Node) = λa λb λc λd (d Map/Node/tag a b c) -Map/Leaf: ∀T (Map T) +Map/Leaf: (Map T) (Map/Leaf) = λa (a Map/Leaf/tag) Map/Node/tag: u24 @@ -58,8 +64,8 @@ Map/Node/tag: u24 Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 -fullMap__bend0: _ +unchecked fullMap__bend0: _ (fullMap__bend0) = λa let {a a_2 a_3} = a; switch (> a 0) { 0: Map/Leaf; _: λ* (Map/Node 1 (fullMap__bend0 (- a_2 1)) (fullMap__bend0 (- a_3 1))); } -test__bend0: _ +unchecked test__bend0: _ (test__bend0) = λa let {a a_2 a_3} = a; switch (< a 1000) { 0: λ* 0; _: λ* λd let (e, f) = (Map/get d (% (prng a_2) 4096)); (+ e (test__bend0 (+ a_3 1) f)); } diff --git a/tests/snapshots/encode_pattern_match__is_some_some.bend.snap b/tests/snapshots/encode_pattern_match__is_some_some.bend.snap index 426432482..0a3d13e9c 100644 --- a/tests/snapshots/encode_pattern_match__is_some_some.bend.snap +++ b/tests/snapshots/encode_pattern_match__is_some_some.bend.snap @@ -3,10 +3,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/is_some_some.bend --- Scott -some_some: Any +unchecked some_some: Any (some_some) = λa (a λb (b λ* 1 0) 0) -main: Any +unchecked main: Any (main) = (some_some (Option/Some 1)) Option/Some: (Any -> Option) @@ -16,10 +16,10 @@ Option/None: Option (Option/None) = λ* λb b NumScott -some_some: Any +unchecked some_some: Any (some_some) = λa (a λb switch b { 0: λc (c λd switch d { 0: λ* 1; _: λ* 0; }); _: λ* 0; }) -main: Any +unchecked main: Any (main) = (some_some (Option/Some 1)) Option/Some: (Any -> Option) diff --git a/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap b/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap index dce461210..ab9648b40 100644 --- a/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap @@ -3,25 +3,25 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_merge_sort.bend --- Scott -If: Any +unchecked If: Any (If) = λa (a λb λc let * = c; b λf λg let * = f; g) -Pure: Any +unchecked Pure: Any (Pure) = λa (List_/Cons a List_/Nil) -Map: Any +unchecked Map: Any (Map) = λa (a λb λc λd let {e e_2} = d; (List_/Cons (e b) (Map c e_2)) λf let * = f; List_/Nil) -MergeSort: Any +unchecked MergeSort: Any (MergeSort) = λa λb (Unpack a (Map b Pure)) -Unpack: Any +unchecked Unpack: Any (Unpack) = λa λb (b λc λd λe (d λf λg λh let {h h_2} = h; λi (Unpack h (MergePair h_2 (List_/Cons i (List_/Cons f g)))) λ* λk k e c) λ* List_/Nil a) -MergePair: Any +unchecked MergePair: Any (MergePair) = λa λb (b λc λd λe (d λf λg λh let {h h_2} = h; λi (List_/Cons (Merge h i f) (MergePair h_2 g)) λ* λk (List_/Cons k List_/Nil) e c) λ* List_/Nil a) -Merge: Any +unchecked Merge: Any (Merge) = λa λb (b λc λd λe λf (f λh let {h h_2 h_3} = h; λi let {i i_2} = i; λj let {j j_2 j_3} = j; λk let {k k_2 k_3} = k; λl let {l l_2} = l; (If (j k h) (List_/Cons k_2 (Merge j_2 l (List_/Cons h_2 i))) (List_/Cons h_3 (Merge j_3 (List_/Cons k_3 l_2) i_2))) λ* λp λq (List_/Cons p q) e c d) λ* λs s a) Bool/True: Bool @@ -30,32 +30,32 @@ Bool/True: Bool Bool/False: Bool (Bool/False) = λ* λb b -List_/Cons: (Any -> (Any -> List_)) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc λ* (c a b) List_/Nil: List_ (List_/Nil) = λ* λb b NumScott -If: Any +unchecked If: Any (If) = λa (a λb switch b { 0: λc λd let * = d; c; _: λ* λg λh let * = g; h; }) -Pure: Any +unchecked Pure: Any (Pure) = λa (List_/Cons a List_/Nil) -Map: Any +unchecked Map: Any (Map) = λa (a λb switch b { 0: λc λd λe let {f f_2} = e; (List_/Cons (f c) (Map d f_2)); _: λ* λg let * = g; List_/Nil; }) -MergeSort: Any +unchecked MergeSort: Any (MergeSort) = λa λb (Unpack a (Map b Pure)) -Unpack: Any +unchecked Unpack: Any (Unpack) = λa λb (b λc switch c { 0: λd λe λf (e λg switch g { 0: λh λi λj let {j j_2} = j; λk (Unpack j (MergePair j_2 (List_/Cons k (List_/Cons h i)))); _: λ* λ* λm m; } f d); _: λ* λ* List_/Nil; } a) -MergePair: Any +unchecked MergePair: Any (MergePair) = λa λb (b λc switch c { 0: λd λe λf (e λg switch g { 0: λh λi λj let {j j_2} = j; λk (List_/Cons (Merge j k h) (MergePair j_2 i)); _: λ* λ* λm (List_/Cons m List_/Nil); } f d); _: λ* λ* List_/Nil; } a) -Merge: Any +unchecked Merge: Any (Merge) = λa λb (b λc switch c { 0: λd λe λf λg (g λi switch i { 0: λj let {j j_2 j_3} = j; λk let {k k_2} = k; λl let {l l_2 l_3} = l; λm let {m m_2 m_3} = m; λn let {n n_2} = n; (If (l m j) (List_/Cons m_2 (Merge l_2 n (List_/Cons j_2 k))) (List_/Cons j_3 (Merge l_3 (List_/Cons m_3 n_2) k_2))); _: λ* λ* λr λs (List_/Cons r s); } f d e); _: λ* λ* λu u; } a) Bool/True: Bool @@ -64,7 +64,7 @@ Bool/True: Bool Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) -List_/Cons: (Any -> (Any -> List_)) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) List_/Nil: List_ diff --git a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap index c7fd87bb5..dade87c53 100644 --- a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap @@ -3,21 +3,21 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_str_encoding_undeclared_fn.bend --- Scott -main: Any +unchecked main: Any (main) = * -Foo: Any +unchecked Foo: Any (Foo) = λa (a 0 λ* λ* 1) -Bar: Any +unchecked Bar: Any (Bar) = λa (a 1 λ* λ* 0) NumScott -main: Any +unchecked main: Any (main) = * -Foo: Any +unchecked Foo: Any (Foo) = λa (a λb switch b { 0: 0; _: λ* λ* λ* 1; }) -Bar: Any +unchecked Bar: Any (Bar) = λa (a λb switch b { 0: 1; _: λ* λ* λ* 0; }) diff --git a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap index 7b996fb19..69db62589 100644 --- a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap @@ -3,9 +3,9 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_str_encoding_undeclared_map.bend --- Scott -main: Any +unchecked main: Any (main) = λa λb ((a 2 λ* λ* 1), (b 2 λ* λ* 1)) NumScott -main: Any +unchecked main: Any (main) = λa λb ((a λc switch c { 0: 2; _: λ* λ* λ* 1; }), (b λg switch g { 0: 2; _: λ* λ* λ* 1; })) diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap index 8950a8a6c..2f78b566f 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_in_arm.bend --- Scott -main: Any +unchecked main: Any (main) = λ* λ$x $x bool/T: bool @@ -13,7 +13,7 @@ bool/F: bool (bool/F) = λ* λb b NumScott -main: Any +unchecked main: Any (main) = λ* λ$x $x bool/T: bool diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap index 38b6880ad..74e369db9 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_lambda.bend --- Scott -main: Any +unchecked main: Any (main) = (Maybe/Some 1 λ$x * λa a $x) Maybe/None: Maybe @@ -13,7 +13,7 @@ Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λ* λc (c a) NumScott -main: Any +unchecked main: Any (main) = (Maybe/Some 1 λa switch a { 0: λ$x *; _: λ* λb b; } $x) Maybe/None: Maybe diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap index 53f17bb50..b983ba126 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap @@ -3,13 +3,13 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_var.bend --- Scott -Foo: Any +unchecked Foo: Any (Foo) = λ$x (Maybe/Some 1 $x λa a) -Bar: Any +unchecked Bar: Any (Bar) = (Maybe/Some 1 $x λa a λ$x *) -main: Any +unchecked main: Any (main) = * Maybe/None: Maybe @@ -19,13 +19,13 @@ Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λ* λc (c a) NumScott -Foo: Any +unchecked Foo: Any (Foo) = λ$x (Maybe/Some 1 λa switch a { 0: $x; _: λ* λb b; }) -Bar: Any +unchecked Bar: Any (Bar) = (Maybe/Some 1 λa switch a { 0: $x; _: λ* λb b; } λ$x *) -main: Any +unchecked main: Any (main) = * Maybe/None: Maybe diff --git a/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap b/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap index 3b8b4313e..0f9c26225 100644 --- a/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap @@ -3,35 +3,35 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_auto_linearization.bend --- Scott -switch_linearization: Any +unchecked switch_linearization: Any (switch_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; switch a_2 { 0: let {e e_2} = 2; let {f g} = e; (b_2 e_2 f g (a_3, b_3) c d); _: λh let {i i_2} = 2; let {j k} = i; (h b_4 i_2 j k (a_4, b_5) c_2 d_2); } -match_linearization: Any +unchecked match_linearization: Any (match_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; (a_2 λe let {f f_2} = 2; let {g h} = f; (e b_2 f_2 g h (a_3, b_3) c d) let {i i_2} = 2; let {j k} = i; (b_4 i_2 j k (a_4, b_5) c_2 d_2)) -switch_shadowed_field: Any +unchecked switch_shadowed_field: Any (switch_shadowed_field) = λa switch a { 0: λb b; _: λc λ* c; } -match_shadowed_field: Any +unchecked match_shadowed_field: Any (match_shadowed_field) = λa (a λb λc (List/Cons b c) λd λe λ* λ* (List/Cons d e)) -List/Cons: ∀t (u24 -> ((List t) -> (List t))) +List/Cons: (T -> (List T) -> (List T)) (List/Cons) = λa λb λ* λd (d a b) NumScott -switch_linearization: Any +unchecked switch_linearization: Any (switch_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; switch a_2 { 0: let {e e_2} = 2; let {f g} = e; (b_2 e_2 f g (a_3, b_3) c d); _: λh let {i i_2} = 2; let {j k} = i; (h b_4 i_2 j k (a_4, b_5) c_2 d_2); } -match_linearization: Any +unchecked match_linearization: Any (match_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; (a_2 λe switch e { 0: λf let {g g_2} = 2; let {h i} = g; (f b_2 g_2 h i (a_3, b_3) c d); _: λ* let {j j_2} = 2; let {k l} = j; (b_4 j_2 k l (a_4, b_5) c_2 d_2); }) -switch_shadowed_field: Any +unchecked switch_shadowed_field: Any (switch_shadowed_field) = λa switch a { 0: λb b; _: λc λ* c; } -match_shadowed_field: Any +unchecked match_shadowed_field: Any (match_shadowed_field) = λa (a λb switch b { 0: λc λd (List/Cons c d); _: λ* λe λf λ* λ* (List/Cons e f); }) -List/Cons: ∀t (u24 -> ((List t) -> (List t))) +List/Cons: (T -> (List T) -> (List T)) (List/Cons) = λa λb λc (c List/Cons/tag a b) List/Cons/tag: u24 diff --git a/tests/snapshots/encode_pattern_match__match_bind.bend.snap b/tests/snapshots/encode_pattern_match__match_bind.bend.snap index 33a29753f..79a24af10 100644 --- a/tests/snapshots/encode_pattern_match__match_bind.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_bind.bend.snap @@ -3,15 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_bind.bend --- Scott -cheese: Any +unchecked cheese: Any (cheese) = switch (+ 2 3) { 0: 653323; _: λa let {a a_2} = a; (+ (+ a 1) a_2); } -main: Any +unchecked main: Any (main) = cheese NumScott -cheese: Any +unchecked cheese: Any (cheese) = switch (+ 2 3) { 0: 653323; _: λa let {a a_2} = a; (+ (+ a 1) a_2); } -main: Any +unchecked main: Any (main) = cheese diff --git a/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap b/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap index 695c3f5b9..6a7c7b57c 100644 --- a/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap @@ -3,16 +3,16 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_num_adt_tup_parser.bend --- Scott -Parse: Any +unchecked Parse: Any (Parse) = λa λb (b λc (Result_/Err (String/Nil, c)) λd λe λf (switch (- d 10) { 0: λg λh (Result_/Ok (0, h, g)); _: λi λj λk (switch (- i 29) { 0: λl λm (Result_/Ok (40, m, l)); _: λn λo λp (switch n { 0: λq λr (Result_/Ok (41, r, q)); _: λs λt λu (Result_/Err ((String/Cons (+ s 42) u), t)); } o p); } j k); } f e) a) -main: Any +unchecked main: Any (main) = (Parse * (String/Cons 40 (String/Cons 43 String/Nil)) λc let (d, e, f) = c; (d, (Parse f e)) λg (Result_/Err g)) String/Nil: String (String/Nil) = λa λ* a -String/Cons: (u24 -> (String -> String)) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λ* λd (d a b) Result_/Ok: (Any -> Result_) @@ -22,16 +22,16 @@ Result_/Err: (Any -> Result_) (Result_/Err) = λa λ* λc (c a) NumScott -Parse: Any +unchecked Parse: Any (Parse) = λa λb (b λc switch c { 0: λd (Result_/Err (String/Nil, d)); _: λ* λe λf λg (switch (- e 10) { 0: λh λi (Result_/Ok (0, i, h)); _: λj λk λl (switch (- j 29) { 0: λm λn (Result_/Ok (40, n, m)); _: λo λp λq (switch o { 0: λr λs (Result_/Ok (41, s, r)); _: λt λu λv (Result_/Err ((String/Cons (+ t 42) v), u)); } p q); } k l); } g f); } a) -main: Any +unchecked main: Any (main) = (Parse * (String/Cons 40 (String/Cons 43 String/Nil)) λc switch c { 0: λd let (e, f, g) = d; (e, (Parse g f)); _: λ* λh (Result_/Err h); }) String/Nil: String (String/Nil) = λa (a String/Nil/tag) -String/Cons: (u24 -> (String -> String)) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λc (c String/Cons/tag a b) Result_/Ok: (Any -> Result_) diff --git a/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap b/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap index 00e56553e..13036ccb2 100644 --- a/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap @@ -3,33 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_num_pred.bend --- Scott -pred: Any +unchecked pred: Any (pred) = λa switch a { 0: 0; _: λb b; } -pred2: Any +unchecked pred2: Any (pred2) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc c; }; } -pred3: Any +unchecked pred3: Any (pred3) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc switch c { 0: 0; _: λd d; }; }; } -zero: Any +unchecked zero: Any (zero) = λa switch a { 0: 1; _: λb switch b { 0: 0; _: λ* 0; }; } -main: Any +unchecked main: Any (main) = * NumScott -pred: Any +unchecked pred: Any (pred) = λa switch a { 0: 0; _: λb b; } -pred2: Any +unchecked pred2: Any (pred2) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc c; }; } -pred3: Any +unchecked pred3: Any (pred3) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc switch c { 0: 0; _: λd d; }; }; } -zero: Any +unchecked zero: Any (zero) = λa switch a { 0: 1; _: λb switch b { 0: 0; _: λ* 0; }; } -main: Any +unchecked main: Any (main) = * diff --git a/tests/snapshots/encode_pattern_match__match_syntax.bend.snap b/tests/snapshots/encode_pattern_match__match_syntax.bend.snap index 72d2806b5..be146e147 100644 --- a/tests/snapshots/encode_pattern_match__match_syntax.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_syntax.bend.snap @@ -3,20 +3,20 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_syntax.bend --- Scott -head: Any +unchecked head: Any (head) = λa (a λb λ* b List_/Nil) -List_/Cons: (Any -> (Any -> List_)) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc λ* (c a b) List_/Nil: List_ (List_/Nil) = λ* λb b NumScott -head: Any +unchecked head: Any (head) = λa (a λb switch b { 0: λc λ* c; _: λ* List_/Nil; }) -List_/Cons: (Any -> (Any -> List_)) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) List_/Nil: List_ diff --git a/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap b/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap index f0c6d5dab..035a4e1e8 100644 --- a/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap +++ b/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap @@ -3,27 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/merge_recursive.bend --- Scott -foo_1: Any +unchecked foo_1: Any (foo_1) = λa (a foo_2) -foo_2: Any +unchecked foo_2: Any (foo_2) = λa λb (a, b) -bar_1: Any +unchecked bar_1: Any (bar_1) = λa (a bar_2) -bar_2: Any +unchecked bar_2: Any (bar_2) = λa λb (a, b) NumScott -foo_1: Any +unchecked foo_1: Any (foo_1) = λa (a foo_2) -foo_2: Any +unchecked foo_2: Any (foo_2) = λa λb (a, b) -bar_1: Any +unchecked bar_1: Any (bar_1) = λa (a bar_2) -bar_2: Any +unchecked bar_2: Any (bar_2) = λa λb (a, b) diff --git a/tests/snapshots/encode_pattern_match__no_patterns.bend.snap b/tests/snapshots/encode_pattern_match__no_patterns.bend.snap index c586025e2..1d3dcb99e 100644 --- a/tests/snapshots/encode_pattern_match__no_patterns.bend.snap +++ b/tests/snapshots/encode_pattern_match__no_patterns.bend.snap @@ -3,21 +3,21 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/no_patterns.bend --- Scott -Id: Any +unchecked Id: Any (Id) = λa a -Id2: Any +unchecked Id2: Any (Id2) = λa a -Pair: Any +unchecked Pair: Any (Pair) = λa λb (a, b) NumScott -Id: Any +unchecked Id: Any (Id) = λa a -Id2: Any +unchecked Id2: Any (Id2) = λa a -Pair: Any +unchecked Pair: Any (Pair) = λa λb (a, b) diff --git a/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap b/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap index 1272d01f6..b438c6c19 100644 --- a/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap +++ b/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/non_matching_fst_arg.bend --- Scott -Foo: Any +unchecked Foo: Any (Foo) = λa λb (b λc let {c c_2} = c; (Foo c c_2) λd d a) bool/true: bool @@ -13,7 +13,7 @@ bool/false: bool (bool/false) = λ* λb b NumScott -Foo: Any +unchecked Foo: Any (Foo) = λa λb (b λc switch c { 0: λd let {d d_2} = d; (Foo d d_2); _: λ* λe e; } a) bool/true: bool diff --git a/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap b/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap index fc7d9ba84..07545f0b9 100644 --- a/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap +++ b/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap @@ -3,15 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/ntup_sum.bend --- Scott -ntupSum: Any +unchecked ntupSum: Any (ntupSum) = λa let (b, c, d, e, f) = a; (+ b (+ c (+ d (+ e f)))) -main: Any +unchecked main: Any (main) = (ntupSum (1, 3, 3, 2, 1)) NumScott -ntupSum: Any +unchecked ntupSum: Any (ntupSum) = λa let (b, c, d, e, f) = a; (+ b (+ c (+ d (+ e f)))) -main: Any +unchecked main: Any (main) = (ntupSum (1, 3, 3, 2, 1)) diff --git a/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap b/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap index 308a19c2b..a28748f6b 100644 --- a/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap +++ b/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap @@ -3,10 +3,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/pattern_match_encoding.bend --- Scott -Foo: Any +unchecked Foo: Any (Foo) = λa (a λ* 100 λ* 200 λ* 200 λ* λ* 200 λ* λ* 200) -main: Any +unchecked main: Any (main) = (Foo MyType/A 2) MyType/A: (Any -> MyType) @@ -18,17 +18,17 @@ MyType/B: (Any -> MyType) MyType/C: (Any -> MyType) (MyType/C) = λa λ* λ* λd λ* λ* (d a) -MyType/D: (Any -> (Any -> MyType)) +MyType/D: (Any -> Any -> MyType) (MyType/D) = λa λb λ* λ* λ* λf λ* (f a b) -MyType/E: (Any -> (Any -> MyType)) +MyType/E: (Any -> Any -> MyType) (MyType/E) = λa λb λ* λ* λ* λ* λg (g a b) NumScott -Foo: Any +unchecked Foo: Any (Foo) = λa (a λb switch b { 0: λ* 100; _: λd switch d { 0: λ* 200; _: λf switch f { 0: λ* 200; _: λh switch h { 0: λ* λ* 200; _: λ* λ* λ* 200; }; }; }; }) -main: Any +unchecked main: Any (main) = (Foo MyType/A 2) MyType/A: (Any -> MyType) @@ -40,10 +40,10 @@ MyType/B: (Any -> MyType) MyType/C: (Any -> MyType) (MyType/C) = λa λb (b MyType/C/tag a) -MyType/D: (Any -> (Any -> MyType)) +MyType/D: (Any -> Any -> MyType) (MyType/D) = λa λb λc (c MyType/D/tag a b) -MyType/E: (Any -> (Any -> MyType)) +MyType/E: (Any -> Any -> MyType) (MyType/E) = λa λb λc (c MyType/E/tag a b) MyType/A/tag: u24 diff --git a/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap b/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap index dbdb41915..f9b8f4dfb 100644 --- a/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap +++ b/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap @@ -3,9 +3,9 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/switch_in_switch_arg.bend --- Scott -main: Any +unchecked main: Any (main) = λa switch switch a { 0: 0; _: λb b; } { 0: 0; _: λc (+ c 1); } NumScott -main: Any +unchecked main: Any (main) = λa switch switch a { 0: 0; _: λb b; } { 0: 0; _: λc (+ c 1); } diff --git a/tests/snapshots/encode_pattern_match__var_only.bend.snap b/tests/snapshots/encode_pattern_match__var_only.bend.snap index da91c652f..8ef45eb22 100644 --- a/tests/snapshots/encode_pattern_match__var_only.bend.snap +++ b/tests/snapshots/encode_pattern_match__var_only.bend.snap @@ -3,10 +3,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/var_only.bend --- Scott -Foo: Any +unchecked Foo: Any (Foo) = λa λ* λc (c a) -main: Any +unchecked main: Any (main) = λ* Foo Bool/False: Bool @@ -16,10 +16,10 @@ Bool/True: Bool (Bool/True) = λ* λb b NumScott -Foo: Any +unchecked Foo: Any (Foo) = λa λ* λc (c a) -main: Any +unchecked main: Any (main) = λ* Foo Bool/False: Bool diff --git a/tests/snapshots/encode_pattern_match__weekday.bend.snap b/tests/snapshots/encode_pattern_match__weekday.bend.snap index d8b97546d..4ab9c2349 100644 --- a/tests/snapshots/encode_pattern_match__weekday.bend.snap +++ b/tests/snapshots/encode_pattern_match__weekday.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/weekday.bend --- Scott -main: Any +unchecked main: Any (main) = (λa a Weekday/Saturday) Weekday/Monday: Weekday @@ -28,7 +28,7 @@ Weekday/Sunday: Weekday (Weekday/Sunday) = λ* λ* λ* λ* λ* λ* λg g NumScott -main: Any +unchecked main: Any (main) = (λa a Weekday/Saturday) Weekday/Monday: Weekday diff --git a/tests/snapshots/examples__list.bend.snap b/tests/snapshots/examples__list.bend.snap index c22021d0a..ca010cac8 100644 --- a/tests/snapshots/examples__list.bend.snap +++ b/tests/snapshots/examples__list.bend.snap @@ -2,4 +2,4 @@ source: tests/golden_tests.rs input_file: examples/list.bend --- -5 +λa (a Result/Ok/tag 5) diff --git a/tests/snapshots/parse_file__era.bend.snap b/tests/snapshots/parse_file__era.bend.snap index 3f2e17e1a..7c3b1bb61 100644 --- a/tests/snapshots/parse_file__era.bend.snap +++ b/tests/snapshots/parse_file__era.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/era.bend --- +unchecked Main: Any (Main) = * diff --git a/tests/snapshots/parse_file__fun_def_name.bend.snap b/tests/snapshots/parse_file__fun_def_name.bend.snap index 5ba6c0e51..bb02fc3ae 100644 --- a/tests/snapshots/parse_file__fun_def_name.bend.snap +++ b/tests/snapshots/parse_file__fun_def_name.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/fun_def_name.bend --- Errors: -In tests/golden_tests/parse_file/fun_def_name.bend : +In tests/golden_tests/parse_file/fun_def_name.bend : Redefinition of builtin (constructor) 'List/Cons'.  4 | aux2 (List/Cons head tail) = (+ head (aux tail)) diff --git a/tests/snapshots/parse_file__imp_map.bend.snap b/tests/snapshots/parse_file__imp_map.bend.snap index a8f88badd..053384a3c 100644 --- a/tests/snapshots/parse_file__imp_map.bend.snap +++ b/tests/snapshots/parse_file__imp_map.bend.snap @@ -5,19 +5,22 @@ input_file: tests/golden_tests/parse_file/imp_map.bend Map/empty: (Map T) (Map/empty) = Map/Leaf -Map/get: ((Map T) -> (u24 -> ((Map T), T))) -(Map/get map key) = match map = map { Map/Leaf: (*, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } +Map/get: ((Map T) -> u24 -> (T, (Map T))) +(Map/get map key) = match map = map { Map/Leaf: (unreachable, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } -Map/set: ((Map T) -> (u24 -> (T -> (Map T)))) -(Map/set map key value) = match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +Map/set: ((Map T) -> u24 -> T -> (Map T)) +(Map/set map key value) = match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node unreachable (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node unreachable Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } -main: Any +unreachable: Any +(unreachable) = * + +unchecked main: Any (main) = let x = (Map/set (Map/set Map/empty 2 1) 3 2); let (map/get%1, x) = (Map/get x 2); let y = (id map/get%1); let z = 4; let x = (Map/set x z 4); let (map/get%0, x) = (Map/get x z); (+ y map/get%0) -Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) +Map/Node: (T -> (Map T) -> (Map T) -> (Map T)) (Map/Node) = λvalue λleft λright λ%x (%x Map/Node/tag value left right) -Map/Leaf: ∀T (Map T) +Map/Leaf: (Map T) (Map/Leaf) = λ%x (%x Map/Leaf/tag) Map/Node/tag: u24 diff --git a/tests/snapshots/parse_file__imp_program.bend.snap b/tests/snapshots/parse_file__imp_program.bend.snap index d852e10ee..88f5b969d 100644 --- a/tests/snapshots/parse_file__imp_program.bend.snap +++ b/tests/snapshots/parse_file__imp_program.bend.snap @@ -5,70 +5,73 @@ input_file: tests/golden_tests/parse_file/imp_program.bend Map/empty: (Map T) (Map/empty) = Map/Leaf -Map/get: ((Map T) -> (u24 -> ((Map T), T))) -(Map/get map key) = match map = map { Map/Leaf: (*, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } +Map/get: ((Map T) -> u24 -> (T, (Map T))) +(Map/get map key) = match map = map { Map/Leaf: (unreachable, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } -Map/set: ((Map T) -> (u24 -> (T -> (Map T)))) -(Map/set map key value) = match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +Map/set: ((Map T) -> u24 -> T -> (Map T)) +(Map/set map key value) = match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node unreachable (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node unreachable Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } -symbols: Any +unreachable: Any +(unreachable) = * + +unchecked symbols: Any (symbols) = let x = (Map/set (Map/set Map/empty 49 5) 2 3); let x = (Map/set x 49 2); let x = (Map/set x 2 3); let (map/get%0, x) = (Map/get x 49); (+ map/get%0 8293490) -mk_point: Any +unchecked mk_point: Any (mk_point) = (Point/Point 1 2) -identity: (Any -> Any) +unchecked identity: (Any -> Any) (identity x) = x -inc: (Any -> Any) +unchecked inc: (Any -> Any) (inc n) = let n = (+ n 1); n -inc_list: (Any -> Any) +unchecked inc_list: (Any -> Any) (inc_list list) = fold %iter = list { List/Nil: List/Nil; List/Cons: let x = %iter.head; (List/Cons (+ x 1) %iter.tail); } -lam: Any +unchecked lam: Any (lam) = λx λy x -do_match: (Any -> Any) +unchecked do_match: (Any -> Any) (do_match b) = match b = b { Bool/True: 1; Bool/False: 0; } -true: Any +unchecked true: Any (true) = Bool/True -fib: (Any -> Any) +unchecked fib: (Any -> Any) (fib n) = switch %pred = (< n 2) { 0: (+ (fib (- n 1)) (fib (- n 2))); _ %pred-1: n; } -swt: (Any -> Any) +unchecked swt: (Any -> Any) (swt n) = switch n = n { 0: 42; _ n-1: 1; } -fld: (Any -> Any) +unchecked fld: (Any -> Any) (fld list) = fold list = list { List/Cons: 1; List/Nil: 2; } -bnd: Any +unchecked bnd: Any (bnd) = bend x = 0, { when (< x 10): (List/Cons x (fork (+ x 1))); else: List/Nil } -era: Any +unchecked era: Any (era) = let * = (+ 2 3); let the_expr_killer = *; (the_expr_killer 9) -sup: Any +unchecked sup: Any (sup) = let x = {(List/Cons 1 (List/Cons 2 List/Nil)) (List/Cons 3 (List/Cons 4 (List/Cons 5 (List/Cons 6 List/Nil))))}; x -main: Any +unchecked main: Any (main) = with IO { ask x = IO.read; x } -List/Nil: ∀t (List t) +List/Nil: (List T) (List/Nil) = λ%x (%x List/Nil/tag) -List/Cons: ∀t (u24 -> ((List t) -> (List t))) +List/Cons: (T -> (List T) -> (List T)) (List/Cons) = λhead λtail λ%x (%x List/Cons/tag head tail) -Map/Node: ∀T (T -> ((Map T) -> ((Map T) -> (Map T)))) +Map/Node: (T -> (Map T) -> (Map T) -> (Map T)) (Map/Node) = λvalue λleft λright λ%x (%x Map/Node/tag value left right) -Map/Leaf: ∀T (Map T) +Map/Leaf: (Map T) (Map/Leaf) = λ%x (%x Map/Leaf/tag) -Point/Point: (Any -> (Any -> Point)) +Point/Point: (Any -> Any -> Point) (Point/Point) = λx λy λ%x (%x Point/Point/tag x y) Bool/True: Bool diff --git a/tests/snapshots/parse_file__multi_line_comment.bend.snap b/tests/snapshots/parse_file__multi_line_comment.bend.snap index 4864282af..9c672edde 100644 --- a/tests/snapshots/parse_file__multi_line_comment.bend.snap +++ b/tests/snapshots/parse_file__multi_line_comment.bend.snap @@ -2,16 +2,16 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/multi_line_comment.bend --- -X: Any +unchecked X: Any (X x x x) = x -String/is_empty: Any +unchecked String/is_empty: Any (String/is_empty s) = match s = s { String/Nil: 1; String/Cons: 0; } -main: Any +unchecked main: Any (main) = 0 -String/not_empty: (Any -> Any) +unchecked String/not_empty: (Any -> Any) (String/not_empty s) = match s = s { String/Nil: 0; String/Cons: 1; } Foo/Foo: (Any -> Foo) diff --git a/tests/snapshots/run_file__num_cast.bend.snap b/tests/snapshots/run_file__num_cast.bend.snap index 320e6cb24..d781ab5c4 100644 --- a/tests/snapshots/run_file__num_cast.bend.snap +++ b/tests/snapshots/run_file__num_cast.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/num_cast.bend --- NumScott: -(0, (1, (123456, (16777215, (1, (0, (400, (1, (1, (0, (0, (16777215, (0, (0, (99999, (+0, (+1, (+123456, (-1, (+1, (+0, (+400, (+1, (+1, (-3, (-3, (+8388607, (-8388608, (+0, (99999, (0.000, (1.000, (123456.000, (-1.000, (1.000, (0.000, (400.000, (1.000, (1.500, (-3.000, (-3.500, (inf, (-inf, NaN))))))))))))))))))))))))))))))))))))))))))) +(16777215, (1, (0, (400, (1, (1, (0, (0, (16777215, (0, (0, (99999, (+0, (+1, (+123456, (+1, (+1, (-3, (-3, (+8388607, (-8388608, (+0, (99999, (0.000, (1.000, (123456.000, (-1.000, (1.000, (0.000, 400.000))))))))))))))))))))))))))))) Scott: -(0, (1, (123456, (16777215, (1, (0, (400, (1, (1, (0, (0, (16777215, (0, (0, (99999, (+0, (+1, (+123456, (-1, (+1, (+0, (+400, (+1, (+1, (-3, (-3, (+8388607, (-8388608, (+0, (99999, (0.000, (1.000, (123456.000, (-1.000, (1.000, (0.000, (400.000, (1.000, (1.500, (-3.000, (-3.500, (inf, (-inf, NaN))))))))))))))))))))))))))))))))))))))))))) +(16777215, (1, (0, (400, (1, (1, (0, (0, (16777215, (0, (0, (99999, (+0, (+1, (+123456, (+1, (+1, (-3, (-3, (+8388607, (-8388608, (+0, (99999, (0.000, (1.000, (123456.000, (-1.000, (1.000, (0.000, 400.000))))))))))))))))))))))))))))) diff --git a/tests/snapshots/run_file__superposed_is_even.bend.snap b/tests/snapshots/run_file__superposed_is_even.bend.snap index 811a010a5..eafb5fcd1 100644 --- a/tests/snapshots/run_file__superposed_is_even.bend.snap +++ b/tests/snapshots/run_file__superposed_is_even.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/superposed_is_even.bend --- NumScott: -{{B/T B/F} {B/T B/F}} +{{MyBool/T MyBool/F} {MyBool/T MyBool/F}} Scott: -{{B/T B/F} {B/T B/F}} +{{MyBool/T MyBool/F} {MyBool/T MyBool/F}} From 66e454b655e52494c1652ecb33a4f841d8cbff64 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Wed, 4 Sep 2024 17:39:21 +0200 Subject: [PATCH 20/29] fix parsing of unchecked imp functions --- src/imp/parser.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/imp/parser.rs b/src/imp/parser.rs index ece2eea30..b8037656d 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -1242,6 +1242,7 @@ impl<'a> ImpParser<'a> { } else { (false, false) }; + self.skip_trivia_inline()?; let name = self.parse_top_level_name()?; self.skip_trivia_inline()?; From e6010e90564d63fa13295fe12b169bfdfe8df130 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 5 Sep 2024 15:52:05 +0200 Subject: [PATCH 21/29] Slightly improve err messages with location info --- src/fun/parser.rs | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/fun/parser.rs b/src/fun/parser.rs index d24048498..893ebc18d 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -948,16 +948,15 @@ impl<'a> FunParser<'a> { && !self.peek_many(2).is_some_and(|x| x.chars().nth(1).unwrap().is_ascii_digit()) { let msg = "Tagged terms not supported for hvm32.".to_string(); - return self.with_ctx(Err(msg), index..index + 1); + return self.err_msg_spanned(&msg, index..index + 1); } else { None }; let end_index = self.index; - let has_tag = tag.is_some(); - Ok((tag, move |slf: &mut Self| { - if has_tag { - let msg = "\x1b[1m- unexpected tag:\x1b[0m".to_string(); - slf.with_ctx(Err(msg), index..end_index) + Ok((tag.clone(), move |slf: &mut Self| { + if let Some(tag) = tag { + let msg = format!("Unexpected tag '{tag}'"); + slf.err_msg_spanned(&msg, index..end_index) } else { Ok(()) } @@ -1113,13 +1112,13 @@ impl<'a> FunParser<'a> { for ctr in adt.ctrs.keys() { if let Some(builtin) = book.contains_builtin_def(ctr) { let msg = FunParser::redefinition_of_function_msg(builtin, ctr); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } match book.ctrs.entry(ctr.clone()) { indexmap::map::Entry::Vacant(e) => _ = e.insert(adt.name.clone()), indexmap::map::Entry::Occupied(e) => { let msg = FunParser::redefinition_of_constructor_msg(e.key()); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } } } @@ -1135,15 +1134,15 @@ impl<'a> FunParser<'a> { ) -> ParseResult<()> { if let Some(builtin) = book.contains_builtin_def(name) { let msg = Self::redefinition_of_function_msg(builtin, name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } if book.ctrs.contains_key(name) { let msg = Self::redefinition_of_constructor_msg(name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } if book.hvm_defs.contains_key(name) { let msg = Self::redefinition_of_hvm_msg(false, name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } Ok(()) } @@ -1156,7 +1155,7 @@ impl<'a> FunParser<'a> { ) -> ParseResult<()> { if book.adts.contains_key(name) { let msg = Self::redefinition_of_type_msg(name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } Ok(()) } @@ -1303,10 +1302,10 @@ pub trait ParserCommons<'a>: Parser<'a> { let end_idx = *self.index(); if name.contains("__") { let msg = format!("{kind} names are not allowed to contain \"__\"."); - self.with_ctx(Err(msg), ini_idx..end_idx) + self.err_msg_spanned(&msg, ini_idx..end_idx) } else if name.starts_with("//") { let msg = format!("{kind} names are not allowed to start with \"//\"."); - self.with_ctx(Err(msg), ini_idx..end_idx) + self.err_msg_spanned(&msg, ini_idx..end_idx) } else { Ok(name) } @@ -1378,7 +1377,7 @@ pub trait ParserCommons<'a>: Parser<'a> { while let Some(c) = self.peek_one() { if c == '\t' { let idx = *self.index(); - return self.with_ctx(Err("Tabs are not accepted for indentation.".to_string()), idx..idx); + return self.err_msg_spanned("Tabs are not accepted for indentation.", idx..idx+1); } if " ".contains(c) { self.advance_one(); @@ -1449,12 +1448,18 @@ pub trait ParserCommons<'a>: Parser<'a> { } /// If the parser result is an error, adds code location information to the error message. + fn err_msg_spanned(&mut self, msg: &str, span: Range) -> ParseResult { + let is_eof = self.is_eof(); + let eof_msg = if is_eof { " end of input" } else { "" }; + let msg = format!("{msg}\nLocation:{eof_msg}"); + self.with_ctx(Err(msg), span) + } + + /// If the parser result is an error, adds highlighted code context to the message. fn with_ctx(&mut self, res: Result, span: Range) -> ParseResult { res.map_err(|msg| { let ctx = highlight_error(span.start, span.end, self.input()); - let is_eof = self.is_eof(); - let eof_msg = if is_eof { " end of input" } else { "" }; - let msg = format!("{msg}\nLocation:{eof_msg}\n{ctx}"); + let msg = format!("{msg}\n{ctx}"); ParseError::new((span.start, span.end), msg) }) } @@ -1731,7 +1736,7 @@ pub trait ParserCommons<'a>: Parser<'a> { fn num_range_err(&mut self, ini_idx: usize, typ: &str) -> ParseResult { let msg = format!("\x1b[1mNumber literal outside of range for {}.\x1b[0m", typ); let end_idx = *self.index(); - self.with_ctx(Err(msg), ini_idx..end_idx) + self.err_msg_spanned(&msg, ini_idx..end_idx) } /// Parses up to 4 base64 characters surrounded by "`". @@ -1771,7 +1776,7 @@ pub trait ParserCommons<'a>: Parser<'a> { let field = &fields[i]; if fields.iter().skip(i + 1).any(|a: &CtrField| a.nam == field.nam) { let msg = format!("Found a repeated field '{}' in constructor {}.", field.nam, ctr_name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } } Ok(()) From 6882d30d2c4e24b3661e6cfadc68ce558ac79c2e Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 5 Sep 2024 15:52:25 +0200 Subject: [PATCH 22/29] Update simplify_matches and encode_pattern_match tests for local defs --- tests/golden_tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/golden_tests.rs b/tests/golden_tests.rs index f781b2a6f..b47270d62 100644 --- a/tests/golden_tests.rs +++ b/tests/golden_tests.rs @@ -260,6 +260,7 @@ fn simplify_matches() { ctx.resolve_type_ctrs()?; ctx.desugar_match_defs()?; ctx.fix_match_terms()?; + ctx.book.lift_local_defs(); ctx.desugar_bend()?; ctx.desugar_fold()?; ctx.desugar_with_blocks()?; @@ -293,9 +294,9 @@ fn encode_pattern_match() { ctx.desugar_open()?; ctx.book.encode_builtins(); ctx.resolve_refs()?; - ctx.resolve_type_ctrs()?; ctx.desugar_match_defs()?; ctx.fix_match_terms()?; + ctx.book.lift_local_defs(); ctx.desugar_bend()?; ctx.desugar_fold()?; ctx.desugar_with_blocks()?; From c9642749b956271a4658b894539bf8b37f6863b6 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 5 Sep 2024 21:22:56 +0200 Subject: [PATCH 23/29] Update all snapshots for type system branch --- src/fun/transform/encode_adts.rs | 3 +- tests/golden_tests/io/load.bend | 9 ++- tests/golden_tests/io/load_fail.bend | 9 ++- tests/golden_tests/io/read_line_eof.bend | 21 +++++-- tests/golden_tests/io/store.bend | 5 +- tests/golden_tests/io/store_fail.bend | 5 +- tests/golden_tests/run_file/num_cast.bend | 56 +++++++++---------- .../cli__desugar_bool_scott.bend.snap | 2 +- .../cli__desugar_float_combinators.bend.snap | 10 ++-- .../cli__desugar_linearize_matches.bend.snap | 2 +- ...i__desugar_linearize_matches_alt.bend.snap | 2 +- tests/snapshots/cli__desugar_merge.bend.snap | 4 +- tests/snapshots/cli__desugar_prune.bend.snap | 2 +- ...ompile_file__error_data_def_name.bend.snap | 1 + .../compile_file__number_too_large.bend.snap | 1 + ..._file__top_level_name_slashslash.bend.snap | 1 + .../compile_file_o_all__adt_string.bend.snap | 1 + tests/snapshots/io__load.bend.snap | 2 +- tests/snapshots/io__load_fail.bend.snap | 2 +- tests/snapshots/io__store.bend.snap | 2 +- tests/snapshots/io__store_fail.bend.snap | 2 +- tests/snapshots/parse_file__fun_def.bend.snap | 2 +- .../parse_file__fun_def_name.bend.snap | 1 + ...parse_file__redefinition_builtin.bend.snap | 1 + ..._file__redefinition_ctr_with_fun.bend.snap | 1 + ...parse_file__redefinition_fun_imp.bend.snap | 1 + ...parse_file__redefinition_imp_fun.bend.snap | 1 + ...e__redefinition_type_with_object.bend.snap | 1 + ...e__redefinition_with_def_between.bend.snap | 1 + ...redefinition_with_object_between.bend.snap | 1 + ...__redefinition_with_type_between.bend.snap | 1 + .../parse_file__repeated_adt_name.bend.snap | 1 + ...rse_file__repeated_datatype_name.bend.snap | 7 +-- .../parse_file__scape_chars.bend.snap | 4 +- tests/snapshots/parse_file__tab.bend.snap | 3 +- .../parse_file__tuple_assign.bend.snap | 1 + .../parse_file__tuple_commas.bend.snap | 4 +- .../parse_file__tuple_need_parens.bend.snap | 19 +++++-- .../run_file__override_list_ctr.bend.snap | 1 + .../run_file__override_str_ctr.bend.snap | 1 + .../simplify_matches__adt_tup_era.bend.snap | 6 +- .../simplify_matches__already_flat.bend.snap | 14 ++--- .../simplify_matches__bits_dec.bend.snap | 2 +- ...plify_matches__complex_with_case.bend.snap | 6 +- ...plify_matches__double_unwrap_box.bend.snap | 4 +- ...ify_matches__double_unwrap_maybe.bend.snap | 4 +- ...y_matches__flatten_with_terminal.bend.snap | 4 +- ...mplify_matches__irrefutable_case.bend.snap | 14 ++--- ...ify_matches__linearize_match_all.bend.snap | 22 ++++---- .../simplify_matches__match_str.bend.snap | 4 +- .../simplify_matches__nested.bend.snap | 6 +- .../simplify_matches__nested2.bend.snap | 6 +- .../simplify_matches__nested_0ary.bend.snap | 4 +- ...lify_matches__redundant_with_era.bend.snap | 4 +- ...simplify_matches__wrong_fn_arity.bend.snap | 3 - 55 files changed, 171 insertions(+), 126 deletions(-) diff --git a/src/fun/transform/encode_adts.rs b/src/fun/transform/encode_adts.rs index 25b2e84d7..409f99aca 100644 --- a/src/fun/transform/encode_adts.rs +++ b/src/fun/transform/encode_adts.rs @@ -1,5 +1,5 @@ use crate::{ - fun::{Book, Definition, Name, Num, Pattern, Rule, Source, Term, Type}, + fun::{Book, Definition, Name, Num, Pattern, Rule, Source, SourceKind, Term, Type}, AdtEncoding, }; @@ -73,5 +73,6 @@ fn encode_ctr_num_scott<'a>(ctr_args: impl DoubleEndedIterator fn make_tag_def(ctr_idx: usize, tag: &Name, source: Source) -> Definition { let rules = vec![Rule { pats: vec![], body: Term::Num { val: Num::U24(ctr_idx as u32) } }]; + let source = Source { kind: SourceKind::Generated, ..source }; Definition { name: tag.clone(), typ: Type::U24, check: true, rules, source } } diff --git a/tests/golden_tests/io/load.bend b/tests/golden_tests/io/load.bend index efc8e016a..85d1cde6f 100644 --- a/tests/golden_tests/io/load.bend +++ b/tests/golden_tests/io/load.bend @@ -1,6 +1,11 @@ -(Main) = +(Main): (IO (Result String u24)) = use path = "tests/golden_tests/io/load.txt" with IO { ask file = (IO/FS/read_file path) - (String/decode_utf8 file) + match file { + Result/Err: + (wrap (Result/Err (file.val))) + Result/Ok: + (wrap (Result/Ok (String/decode_utf8 file.val))) + } } diff --git a/tests/golden_tests/io/load_fail.bend b/tests/golden_tests/io/load_fail.bend index d43fccdd8..c10856c17 100644 --- a/tests/golden_tests/io/load_fail.bend +++ b/tests/golden_tests/io/load_fail.bend @@ -1,6 +1,11 @@ -(Main) = +(Main) : (IO (Result String u24)) = use path = "tests/golden_tests/io/missing_dir/load_fail.txt" with IO { ask file = (IO/FS/read_file path) - (String/decode_utf8 file) + match file { + Result/Err: + (wrap (Result/Err (file.val))) + Result/Ok: + (wrap (Result/Ok (String/decode_utf8 file.val))) + } } diff --git a/tests/golden_tests/io/read_line_eof.bend b/tests/golden_tests/io/read_line_eof.bend index 877b63308..a42f5ca95 100644 --- a/tests/golden_tests/io/read_line_eof.bend +++ b/tests/golden_tests/io/read_line_eof.bend @@ -1,6 +1,17 @@ -def main: +def main() -> IO(String): with IO: - fd <- IO/done_on_err(IO/FS/open("tests/golden_tests/io/eof.txt", "r")) - bytes <- IO/FS/read_line(fd) - txt = String/decode_utf8(bytes) - return wrap(txt) + fd <- IO/FS/open("tests/golden_tests/io/eof.txt", "r") + match fd: + case Result/Err: + return wrap(append("Err: ", u24/to_string(fd.val))) + case Result/Ok: + bytes <- IO/FS/read_line(fd.val) + match bytes: + case Result/Err: + return wrap(append("Err: ", u24/to_string(bytes.val))) + case Result/Ok: + txt = String/decode_utf8(bytes.val) + return wrap(txt) + +append (String/Nil) str = str +append (String/Cons x xs) str = (String/Cons x (append xs str)) diff --git a/tests/golden_tests/io/store.bend b/tests/golden_tests/io/store.bend index ca55eb508..7d50f08af 100644 --- a/tests/golden_tests/io/store.bend +++ b/tests/golden_tests/io/store.bend @@ -1,6 +1,5 @@ -(Main) = +(Main) : (IO (Result None u24)) = use path = "tests/golden_tests/io/store.txt" with IO { - ask res = (IO/FS/write_file path (String/encode_utf8 "(Main) = 0")) - res + (IO/FS/write_file path (String/encode_utf8 "(Main) = 0")) } diff --git a/tests/golden_tests/io/store_fail.bend b/tests/golden_tests/io/store_fail.bend index ef5b3086e..4743e087f 100644 --- a/tests/golden_tests/io/store_fail.bend +++ b/tests/golden_tests/io/store_fail.bend @@ -1,6 +1,5 @@ -(Main) = +(Main) : (IO (Result None u24)) = use path = "tests/golden_tests/io/missing_dir/store_fail.txt" with IO { - ask res = (IO/FS/write_file path (String/encode_utf8 "(Main) = 0")) - res + (IO/FS/write_file path (String/encode_utf8 "(Main) = 0")) } diff --git a/tests/golden_tests/run_file/num_cast.bend b/tests/golden_tests/run_file/num_cast.bend index ccf163407..c86939127 100644 --- a/tests/golden_tests/run_file/num_cast.bend +++ b/tests/golden_tests/run_file/num_cast.bend @@ -1,34 +1,34 @@ main: _ = use inf = (** 9.0 (** 9.0 9.0)) ( - (i24_to_u24 -1), - (i24_to_u24 +1), - (i24_to_u24 +0), - (i24_to_u24 +400), - (f24_to_u24 1.0), - (f24_to_u24 1.5), - (f24_to_u24 -3.0), - (f24_to_u24 -3.5), - (f24_to_u24 inf), # inf - (f24_to_u24 (* -1.0 inf)), # -inf - (f24_to_u24 (/ inf inf)), # nan (inf/inf) + (i24/to_u24 -1), + (i24/to_u24 +1), + (i24/to_u24 +0), + (i24/to_u24 +400), + (f24/to_u24 1.0), + (f24/to_u24 1.5), + (f24/to_u24 -3.0), + (f24/to_u24 -3.5), + (f24/to_u24 inf), # inf + (f24/to_u24 (* -1.0 inf)), # -inf + (f24/to_u24 (/ inf inf)), # nan (inf/inf) 99999, - (u24_to_i24 0), - (u24_to_i24 1), - (u24_to_i24 123456), - (f24_to_i24 1.0), - (f24_to_i24 1.5), - (f24_to_i24 -3.0), - (f24_to_i24 -3.5), - (f24_to_i24 inf), # inf - (f24_to_i24 (* -1.0 inf)), # -inf - (f24_to_i24 (/ inf inf)), # nan (inf/inf) + (u24/to_i24 0), + (u24/to_i24 1), + (u24/to_i24 123456), + (f24/to_i24 1.0), + (f24/to_i24 1.5), + (f24/to_i24 -3.0), + (f24/to_i24 -3.5), + (f24/to_i24 inf), # inf + (f24/to_i24 (* -1.0 inf)), # -inf + (f24/to_i24 (/ inf inf)), # nan (inf/inf) 99999, - (u24_to_f24 0), - (u24_to_f24 1), - (u24_to_f24 123456), - (i24_to_f24 -1), - (i24_to_f24 +1), - (i24_to_f24 +0), - (i24_to_f24 +400) + (u24/to_f24 0), + (u24/to_f24 1), + (u24/to_f24 123456), + (i24/to_f24 -1), + (i24/to_f24 +1), + (i24/to_f24 +0), + (i24/to_f24 +400) ) \ No newline at end of file diff --git a/tests/snapshots/cli__desugar_bool_scott.bend.snap b/tests/snapshots/cli__desugar_bool_scott.bend.snap index 6f124b516..7052bb85c 100644 --- a/tests/snapshots/cli__desugar_bool_scott.bend.snap +++ b/tests/snapshots/cli__desugar_bool_scott.bend.snap @@ -2,7 +2,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_bool_scott.bend --- -main: Any +unchecked main: Any (main) = λa λ* a Boolean/True: Boolean diff --git a/tests/snapshots/cli__desugar_float_combinators.bend.snap b/tests/snapshots/cli__desugar_float_combinators.bend.snap index fe69996f5..e65464916 100644 --- a/tests/snapshots/cli__desugar_float_combinators.bend.snap +++ b/tests/snapshots/cli__desugar_float_combinators.bend.snap @@ -2,17 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_float_combinators.bend --- -Z: Any +unchecked Z: Any (Z) = λ* λa a -S: Any +unchecked S: Any (S) = λa λb let {c d} = b; λe (c (a d e)) -get: Any +unchecked get: Any (get) = λa (a get__C0 0) -main: Any +unchecked main: Any (main) = (get (S (S Z))) -get__C0: _ +unchecked get__C0: _ (get__C0) = λa (+ a 1) diff --git a/tests/snapshots/cli__desugar_linearize_matches.bend.snap b/tests/snapshots/cli__desugar_linearize_matches.bend.snap index f34c8c6ec..6ce94cb07 100644 --- a/tests/snapshots/cli__desugar_linearize_matches.bend.snap +++ b/tests/snapshots/cli__desugar_linearize_matches.bend.snap @@ -2,5 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_linearize_matches.bend --- -main: Any +unchecked main: Any (main) = λa λb λc (switch a { 0: λd λ* d; _: λ* λ* λe e; } b c) diff --git a/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap b/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap index 28d5ed79c..dc83c7fec 100644 --- a/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap +++ b/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap @@ -2,5 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_linearize_matches_alt.bend --- -main: Any +unchecked main: Any (main) = λa switch a { 0: λb b; _: λ* λc c; } diff --git a/tests/snapshots/cli__desugar_merge.bend.snap b/tests/snapshots/cli__desugar_merge.bend.snap index e767fb41a..784d18150 100644 --- a/tests/snapshots/cli__desugar_merge.bend.snap +++ b/tests/snapshots/cli__desugar_merge.bend.snap @@ -7,8 +7,8 @@ input_file: tests/golden_tests/cli/desugar_merge.bend In definition 'Z': Definition is unused. -F__M_Z: _ +unchecked F__M_Z: _ (F__M_Z) = λ* λa a -main: Any +unchecked main: Any (main) = λ* λa a diff --git a/tests/snapshots/cli__desugar_prune.bend.snap b/tests/snapshots/cli__desugar_prune.bend.snap index 081f3d47a..a228dd463 100644 --- a/tests/snapshots/cli__desugar_prune.bend.snap +++ b/tests/snapshots/cli__desugar_prune.bend.snap @@ -2,5 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_prune.bend --- -main: Any +unchecked main: Any (main) = * diff --git a/tests/snapshots/compile_file__error_data_def_name.bend.snap b/tests/snapshots/compile_file__error_data_def_name.bend.snap index cd3ddfcf8..b4c30720c 100644 --- a/tests/snapshots/compile_file__error_data_def_name.bend.snap +++ b/tests/snapshots/compile_file__error_data_def_name.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/compile_file/error_data_def_name.bend Errors: In tests/golden_tests/compile_file/error_data_def_name.bend : Redefinition of constructor 'A/A'. +Location:  2 | A/A = 0 diff --git a/tests/snapshots/compile_file__number_too_large.bend.snap b/tests/snapshots/compile_file__number_too_large.bend.snap index fefe7c825..04ec3558d 100644 --- a/tests/snapshots/compile_file__number_too_large.bend.snap +++ b/tests/snapshots/compile_file__number_too_large.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/compile_file/number_too_large.bend Errors: In tests/golden_tests/compile_file/number_too_large.bend : Number literal outside of range for U24. +Location: end of input  1 | main = 0x10000000 diff --git a/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap b/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap index 671f5a591..56239fdbb 100644 --- a/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap +++ b/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/compile_file/top_level_name_slashslash.bend Errors: In tests/golden_tests/compile_file/top_level_name_slashslash.bend : Top-level names are not allowed to start with "//". +Location:  4 | def //thisshouldfail(): diff --git a/tests/snapshots/compile_file_o_all__adt_string.bend.snap b/tests/snapshots/compile_file_o_all__adt_string.bend.snap index c491410d8..0738e2dc5 100644 --- a/tests/snapshots/compile_file_o_all__adt_string.bend.snap +++ b/tests/snapshots/compile_file_o_all__adt_string.bend.snap @@ -5,6 +5,7 @@ input_file: tests/golden_tests/compile_file_o_all/adt_string.bend Errors: In tests/golden_tests/compile_file_o_all/adt_string.bend : Redefinition of builtin (type) 'String'. +Location:  1 | type String = S  2 |   3 | main = String/S diff --git a/tests/snapshots/io__load.bend.snap b/tests/snapshots/io__load.bend.snap index 392c27c54..d875f76e9 100644 --- a/tests/snapshots/io__load.bend.snap +++ b/tests/snapshots/io__load.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/io/load.bend --- Strict mode: -"Contents\n" +λa (a IO/Done/tag IO/MAGIC λb (b Result/Ok/tag "Contents\n")) diff --git a/tests/snapshots/io__load_fail.bend.snap b/tests/snapshots/io__load_fail.bend.snap index 5106f7eaa..24919534b 100644 --- a/tests/snapshots/io__load_fail.bend.snap +++ b/tests/snapshots/io__load_fail.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/io/load_fail.bend --- Strict mode: -1 +λa (a IO/Done/tag IO/MAGIC λb (b Result/Err/tag +2)) diff --git a/tests/snapshots/io__store.bend.snap b/tests/snapshots/io__store.bend.snap index aeb5109a7..9b77cee4f 100644 --- a/tests/snapshots/io__store.bend.snap +++ b/tests/snapshots/io__store.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/io/store.bend --- Strict mode: -λa (a Result/Ok/tag [40, 77, 97, 105, 110, 41, 32, 61, 32, 48]) +λa (a IO/Done/tag IO/MAGIC λb (b Result/Ok/tag *)) diff --git a/tests/snapshots/io__store_fail.bend.snap b/tests/snapshots/io__store_fail.bend.snap index 80b262705..73e2559a7 100644 --- a/tests/snapshots/io__store_fail.bend.snap +++ b/tests/snapshots/io__store_fail.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/io/store_fail.bend --- Strict mode: -λa (a Result/Err/tag λb (b 2 +2)) +λa (a IO/Done/tag IO/MAGIC λb (b Result/Err/tag +2)) diff --git a/tests/snapshots/parse_file__fun_def.bend.snap b/tests/snapshots/parse_file__fun_def.bend.snap index 9ec226324..26748c7c4 100644 --- a/tests/snapshots/parse_file__fun_def.bend.snap +++ b/tests/snapshots/parse_file__fun_def.bend.snap @@ -2,5 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/fun_def.bend --- -main: Any +unchecked main: Any (main) = let base = 0; def (aux (List/Nil)) = base(aux (List/Cons head tail)) = (+ head (aux tail))(aux (List/Cons 1 (List/Cons 2 (List/Cons 3 List/Nil)))) diff --git a/tests/snapshots/parse_file__fun_def_name.bend.snap b/tests/snapshots/parse_file__fun_def_name.bend.snap index bb02fc3ae..50230aca9 100644 --- a/tests/snapshots/parse_file__fun_def_name.bend.snap +++ b/tests/snapshots/parse_file__fun_def_name.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/fun_def_name.bend Errors: In tests/golden_tests/parse_file/fun_def_name.bend : Redefinition of builtin (constructor) 'List/Cons'. +Location:  4 | aux2 (List/Cons head tail) = (+ head (aux tail)) diff --git a/tests/snapshots/parse_file__redefinition_builtin.bend.snap b/tests/snapshots/parse_file__redefinition_builtin.bend.snap index ee51c4004..2df6576de 100644 --- a/tests/snapshots/parse_file__redefinition_builtin.bend.snap +++ b/tests/snapshots/parse_file__redefinition_builtin.bend.snap @@ -5,6 +5,7 @@ input_file: tests/golden_tests/parse_file/redefinition_builtin.bend Errors: In tests/golden_tests/parse_file/redefinition_builtin.bend : Redefinition of builtin (function) 'Map/get'. +Location: end of input  1 | def Map/get(m):  2 |  return m  diff --git a/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap b/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap index 5c6e24669..4580960a1 100644 --- a/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap +++ b/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap @@ -5,6 +5,7 @@ input_file: tests/golden_tests/parse_file/redefinition_ctr_with_fun.bend Errors: In tests/golden_tests/parse_file/redefinition_ctr_with_fun.bend : Redefinition of builtin (constructor) 'String/Cons'. +Location: end of input  1 | def String/Cons(x):  2 |  return x  diff --git a/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap b/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap index b31d21b8a..5b9813ec5 100644 --- a/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap +++ b/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap @@ -5,5 +5,6 @@ input_file: tests/golden_tests/parse_file/redefinition_fun_imp.bend Errors: In tests/golden_tests/parse_file/redefinition_fun_imp.bend : Redefinition of function 'A'. +Location: end of input  3 | def A:  4 |  return 0 diff --git a/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap b/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap index da1ddb800..036a1c3e5 100644 --- a/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap +++ b/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/redefinition_imp_fun.bend Errors: In tests/golden_tests/parse_file/redefinition_imp_fun.bend : Redefinition of function 'A'. +Location: end of input  5 | (A) = 1 diff --git a/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap b/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap index 189f008db..5e17b4b34 100644 --- a/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap +++ b/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap @@ -5,5 +5,6 @@ input_file: tests/golden_tests/parse_file/redefinition_type_with_object.bend Errors: In tests/golden_tests/parse_file/redefinition_type_with_object.bend : Redefinition of builtin (type) 'IO'. +Location: end of input  1 | object IO { run }  diff --git a/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap index 807d09e32..3da28ff91 100644 --- a/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/redefinition_with_def_between.bend Errors: In tests/golden_tests/parse_file/redefinition_with_def_between.bend : Redefinition of function 'A'. +Location: end of input  4 | (A) = @x x diff --git a/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap index 005d6a4a2..2d75ae64a 100644 --- a/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/redefinition_with_object_between.bend Errors: In tests/golden_tests/parse_file/redefinition_with_object_between.bend : Redefinition of function 'A'. +Location: end of input  3 | A = 1 diff --git a/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap index 7280f6a08..4165c1645 100644 --- a/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/redefinition_with_type_between.bend Errors: In tests/golden_tests/parse_file/redefinition_with_type_between.bend : Redefinition of function 'A'. +Location:  3 | A = 1 diff --git a/tests/snapshots/parse_file__repeated_adt_name.bend.snap b/tests/snapshots/parse_file__repeated_adt_name.bend.snap index 434fc2a0c..5b68a2698 100644 --- a/tests/snapshots/parse_file__repeated_adt_name.bend.snap +++ b/tests/snapshots/parse_file__repeated_adt_name.bend.snap @@ -5,6 +5,7 @@ input_file: tests/golden_tests/parse_file/repeated_adt_name.bend Errors: In tests/golden_tests/parse_file/repeated_adt_name.bend : Redefinition of type 'Foo'. +Location:  2 | type Foo = B  3 |   4 | main = * diff --git a/tests/snapshots/parse_file__repeated_datatype_name.bend.snap b/tests/snapshots/parse_file__repeated_datatype_name.bend.snap index 8e23b5667..71e7d11ad 100644 --- a/tests/snapshots/parse_file__repeated_datatype_name.bend.snap +++ b/tests/snapshots/parse_file__repeated_datatype_name.bend.snap @@ -4,7 +4,6 @@ input_file: tests/golden_tests/parse_file/repeated_datatype_name.bend --- Errors: In tests/golden_tests/parse_file/repeated_datatype_name.bend : -- information: Found a repeated field 'Expr' in constructor Expr/Plus. -- location: - 3 | | (Plus Expr Expr) - +Found a repeated field 'Expr' in constructor Expr/Plus. +Location: + 3 | | (Plus Expr Expr) diff --git a/tests/snapshots/parse_file__scape_chars.bend.snap b/tests/snapshots/parse_file__scape_chars.bend.snap index 0f2acd25d..8d84f22fa 100644 --- a/tests/snapshots/parse_file__scape_chars.bend.snap +++ b/tests/snapshots/parse_file__scape_chars.bend.snap @@ -2,13 +2,13 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/scape_chars.bend --- -main: Any +unchecked main: Any (main) = (String/Cons 92 (String/Cons 32 (String/Cons 10 (String/Cons 32 (String/Cons 9 (String/Cons 32 (String/Cons 34 String/Nil))))))) String/Nil: String (String/Nil) = λ%x (%x String/Nil/tag) -String/Cons: (u24 -> (String -> String)) +String/Cons: (u24 -> String -> String) (String/Cons) = λhead λtail λ%x (%x String/Cons/tag head tail) String/Nil/tag: u24 diff --git a/tests/snapshots/parse_file__tab.bend.snap b/tests/snapshots/parse_file__tab.bend.snap index a41337cec..4eb2782e0 100644 --- a/tests/snapshots/parse_file__tab.bend.snap +++ b/tests/snapshots/parse_file__tab.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/tab.bend Errors: In tests/golden_tests/parse_file/tab.bend : Tabs are not accepted for indentation. - 2 |  x = 2 +Location: + 2 |  x = 2 diff --git a/tests/snapshots/parse_file__tuple_assign.bend.snap b/tests/snapshots/parse_file__tuple_assign.bend.snap index c87d16ff4..b2fc1aa27 100644 --- a/tests/snapshots/parse_file__tuple_assign.bend.snap +++ b/tests/snapshots/parse_file__tuple_assign.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/tuple_assign.bend --- +unchecked main: Any (main) = let (first, second) = (1, (2, 3)); second diff --git a/tests/snapshots/parse_file__tuple_commas.bend.snap b/tests/snapshots/parse_file__tuple_commas.bend.snap index c429d7111..dad4c062f 100644 --- a/tests/snapshots/parse_file__tuple_commas.bend.snap +++ b/tests/snapshots/parse_file__tuple_commas.bend.snap @@ -2,6 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/tuple_commas.bend --- +unchecked main: Any (main) = let tup = ((fst 1 λx x), 2); (tup, 3, (4, 5)) -(fst) = λ%arg0 λ%arg1 use y = %arg1; use x = %arg0; x +unchecked fst: (Any -> Any -> Any) +(fst x y) = x diff --git a/tests/snapshots/parse_file__tuple_need_parens.bend.snap b/tests/snapshots/parse_file__tuple_need_parens.bend.snap index 17e7cd8fc..9b8c1fcb4 100644 --- a/tests/snapshots/parse_file__tuple_need_parens.bend.snap +++ b/tests/snapshots/parse_file__tuple_need_parens.bend.snap @@ -2,8 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/tuple_need_parens.bend --- -Errors: -In tests/golden_tests/parse_file/tuple_need_parens.bend : -- expected: ':' -- detected: - 2 | if 1, 2: +unchecked main: Any +(main) = switch %pred = (1, 2) { 0: (String/Cons 104 (String/Cons 109 (String/Cons 109 (String/Cons 109 String/Nil)))); _ %pred-1: (String/Cons 110 (String/Cons 111 (String/Cons 116 String/Nil))); } + +String/Nil: String +(String/Nil) = λ%x (%x String/Nil/tag) + +String/Cons: (u24 -> String -> String) +(String/Cons) = λhead λtail λ%x (%x String/Cons/tag head tail) + +String/Nil/tag: u24 +(String/Nil/tag) = 0 + +String/Cons/tag: u24 +(String/Cons/tag) = 1 diff --git a/tests/snapshots/run_file__override_list_ctr.bend.snap b/tests/snapshots/run_file__override_list_ctr.bend.snap index 789f8c5ef..732e5bede 100644 --- a/tests/snapshots/run_file__override_list_ctr.bend.snap +++ b/tests/snapshots/run_file__override_list_ctr.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/run_file/override_list_ctr.bend Errors: In tests/golden_tests/run_file/override_list_ctr.bend : Redefinition of builtin (constructor) 'List/Nil'. +Location:  1 | List/Nil = * diff --git a/tests/snapshots/run_file__override_str_ctr.bend.snap b/tests/snapshots/run_file__override_str_ctr.bend.snap index 34d01367a..4c30deca1 100644 --- a/tests/snapshots/run_file__override_str_ctr.bend.snap +++ b/tests/snapshots/run_file__override_str_ctr.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/run_file/override_str_ctr.bend Errors: In tests/golden_tests/run_file/override_str_ctr.bend : Redefinition of builtin (constructor) 'String/Cons'. +Location:  1 | String/Cons = * diff --git a/tests/snapshots/simplify_matches__adt_tup_era.bend.snap b/tests/snapshots/simplify_matches__adt_tup_era.bend.snap index 032138686..323df4bdb 100644 --- a/tests/snapshots/simplify_matches__adt_tup_era.bend.snap +++ b/tests/snapshots/simplify_matches__adt_tup_era.bend.snap @@ -2,13 +2,13 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/adt_tup_era.bend --- -Foo: Any +unchecked Foo: Any (Foo) = λa match a { Tuple/Pair b c: (match b { Tuple/Pair d e: λf d; } c); } -Main: Any +unchecked Main: Any (Main) = (Foo (Tuple/Pair 1 5)) -Tuple/Pair: (Any -> (Any -> Tuple)) +Tuple/Pair: (Any -> Any -> Tuple) (Tuple/Pair) = λa λb λc (c Tuple/Pair/tag a b) Tuple/Pair/tag: u24 diff --git a/tests/snapshots/simplify_matches__already_flat.bend.snap b/tests/snapshots/simplify_matches__already_flat.bend.snap index c10f149f0..276be4380 100644 --- a/tests/snapshots/simplify_matches__already_flat.bend.snap +++ b/tests/snapshots/simplify_matches__already_flat.bend.snap @@ -2,22 +2,22 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/already_flat.bend --- -Rule1: Any +unchecked Rule1: Any (Rule1) = λa a -Rule2: Any +unchecked Rule2: Any (Rule2) = λa λb b -Rule3: Any +unchecked Rule3: Any (Rule3) = λa λb λc λd (a b c d) -Rule4: Any +unchecked Rule4: Any (Rule4) = λa match a { Foo/CtrA: λb b; Foo/CtrB c: c; } -Rule5: Any +unchecked Rule5: Any (Rule5) = λa match a { Bar/CtrA1 b: λc let d = c; (b d); Bar/CtrA2 e f: λg let h = g; (e f h); Bar/CtrA3 i: λj let k = j; (match k { Baz/CtrB0: λl (Bar/CtrA3 l); Baz/CtrB1 m: λn (Bar/CtrA3 n m); Baz/CtrB2 o: λp (Bar/CtrA3 p (Baz/CtrB2 o)); Baz/CtrB3 q: λr (r q); } i); } -Rule6: Any +unchecked Rule6: Any (Rule6) = λa a Foo/CtrA: Foo @@ -29,7 +29,7 @@ Foo/CtrB: (Any -> Foo) Bar/CtrA1: (Any -> Bar) (Bar/CtrA1) = λa λb (b Bar/CtrA1/tag a) -Bar/CtrA2: (Any -> (Any -> Bar)) +Bar/CtrA2: (Any -> Any -> Bar) (Bar/CtrA2) = λa λb λc (c Bar/CtrA2/tag a b) Bar/CtrA3: (Any -> Bar) diff --git a/tests/snapshots/simplify_matches__bits_dec.bend.snap b/tests/snapshots/simplify_matches__bits_dec.bend.snap index 456c46e20..12adb4c6b 100644 --- a/tests/snapshots/simplify_matches__bits_dec.bend.snap +++ b/tests/snapshots/simplify_matches__bits_dec.bend.snap @@ -2,7 +2,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/bits_dec.bend --- -Data.Bits.dec: Any +unchecked Data.Bits.dec: Any (Data.Bits.dec) = λa match a { Data/Bits/e: Data/Bits/e; Data/Bits/o b: match b { Data/Bits/e: Data/Bits/e; Data/Bits/o c: (Data/Bits/i (Data.Bits.dec c)); Data/Bits/i d: (Data/Bits/i (Data.Bits.dec d)); }; Data/Bits/i e: match e { Data/Bits/e: (Data/Bits/o Data/Bits/e); Data/Bits/o f: (Data/Bits/o f); Data/Bits/i g: (Data/Bits/o g); }; } Data/Bits/e: Data/Bits diff --git a/tests/snapshots/simplify_matches__complex_with_case.bend.snap b/tests/snapshots/simplify_matches__complex_with_case.bend.snap index 437bf26fd..e21173c2e 100644 --- a/tests/snapshots/simplify_matches__complex_with_case.bend.snap +++ b/tests/snapshots/simplify_matches__complex_with_case.bend.snap @@ -2,13 +2,13 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/complex_with_case.bend --- -map: Any +unchecked map: Any (map) = λa λb (match b { Tree_/Node c d e f: λg (Tree_/Node (map g c) (map g d) (map g e) (map g f)); Tree_/Leaf h: λi (Tree_/Leaf (i h)); } a) -main: Any +unchecked main: Any (main) = map -Tree_/Node: (Any -> (Any -> (Any -> (Any -> Tree_)))) +Tree_/Node: (Any -> Any -> Any -> Any -> Tree_) (Tree_/Node) = λa λb λc λd λe (e Tree_/Node/tag a b c d) Tree_/Leaf: (Any -> Tree_) diff --git a/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap b/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap index 4de8eb697..de945c1f6 100644 --- a/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap +++ b/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap @@ -2,10 +2,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/double_unwrap_box.bend --- -DoubleUnbox: Any +unchecked DoubleUnbox: Any (DoubleUnbox) = λa match a { Boxed/Box b: match b { Boxed/Box c: λd let e = d; let f = e; c; }; } -Main: Any +unchecked Main: Any (Main) = (DoubleUnbox (Boxed/Box (Boxed/Box 0)) 5) Boxed/Box: (Any -> Boxed) diff --git a/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap b/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap index 0ddd57299..12acb88c8 100644 --- a/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap +++ b/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap @@ -2,10 +2,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/double_unwrap_maybe.bend --- -DoubleUnwrap: Any +unchecked DoubleUnwrap: Any (DoubleUnwrap) = λa match a { Maybe/Some b: match b { Maybe/Some c: λd let e = d; let f = e; c; Maybe/None: λg let h = g; let i = h; i; }; Maybe/None: λj let k = j; k; } -Main: Any +unchecked Main: Any (Main) = (DoubleUnwrap (Maybe/Some Maybe/None) 5) Maybe/Some: (Any -> Maybe) diff --git a/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap b/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap index 6721ec50c..addcb36b1 100644 --- a/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap +++ b/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap @@ -2,10 +2,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/flatten_with_terminal.bend --- -Foo: Any +unchecked Foo: Any (Foo) = λa switch a { 0: λb let c = b; match c { A_t/A d: match d { B_t/B: B_t/B; }; }; _ e: λf let g = f; *; } -main: Any +unchecked main: Any (main) = (Foo 2 (A_t/A B_t/B)) A_t/A: (Any -> A_t) diff --git a/tests/snapshots/simplify_matches__irrefutable_case.bend.snap b/tests/snapshots/simplify_matches__irrefutable_case.bend.snap index 589552ab4..455013a7e 100644 --- a/tests/snapshots/simplify_matches__irrefutable_case.bend.snap +++ b/tests/snapshots/simplify_matches__irrefutable_case.bend.snap @@ -2,23 +2,23 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/irrefutable_case.bend --- -l: Any +unchecked l: Any (l) = 1001 -v1: Any +unchecked v1: Any (v1) = l -v2: Any +unchecked v2: Any (v2) = l -v3: Any +unchecked v3: Any (v3) = 2002 -v4: Any +unchecked v4: Any (v4) = 3003 -v5: Any +unchecked v5: Any (v5) = (λa a 5005) -main: Any +unchecked main: Any (main) = (v1, v2, v3, v4, v5) diff --git a/tests/snapshots/simplify_matches__linearize_match_all.bend.snap b/tests/snapshots/simplify_matches__linearize_match_all.bend.snap index a9373b568..5e74ffce1 100644 --- a/tests/snapshots/simplify_matches__linearize_match_all.bend.snap +++ b/tests/snapshots/simplify_matches__linearize_match_all.bend.snap @@ -2,37 +2,37 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/linearize_match_all.bend --- -A: Any +unchecked A: Any (A) = λa switch a { 0: λb λc (b c); _ d: λe λf (d e f); } -B: Any +unchecked B: Any (B) = λa λb λc (switch c { 0: λd λe (d e); _ f: λg λh (g h f); } a b) -C: Any +unchecked C: Any (C) = λa λb λc switch c { 0: (a b); _ d: (a b d); } -D: Any +unchecked D: Any (D) = λa switch a { 0: λb λc c; _ d: λe λf (d f); } -E: Any +unchecked E: Any (E) = λa match a { ConsList/Cons b c: λd let e = d; (match e { ConsList/Cons f g: λh λi (h i f g); ConsList/Nil: λj λk (ConsList/Cons j k ConsList/Nil); } b c); ConsList/Nil: λl let m = l; (ConsList/Nil m); } -A2: Any +unchecked A2: Any (A2) = λa match a { ConsList/Cons b c: λd λe (b c d e); ConsList/Nil: λf λg (f g); } -B2: Any +unchecked B2: Any (B2) = λa λb λc (match c { ConsList/Cons d e: λf λg (f g d e); ConsList/Nil: λh λi (h i); } a b) -C2: Any +unchecked C2: Any (C2) = λa λb λc match c { ConsList/Cons d e: (a b d e); ConsList/Nil: (a b); } -D2: Any +unchecked D2: Any (D2) = λa match a { ConsList/Cons b c: λd λe (b c d e); ConsList/Nil: λf λg (f g); } -main: Any +unchecked main: Any (main) = * -ConsList/Cons: (Any -> (Any -> ConsList)) +ConsList/Cons: (Any -> Any -> ConsList) (ConsList/Cons) = λa λb λc (c ConsList/Cons/tag a b) ConsList/Nil: ConsList diff --git a/tests/snapshots/simplify_matches__match_str.bend.snap b/tests/snapshots/simplify_matches__match_str.bend.snap index f28e2aa7d..0fd9ec75a 100644 --- a/tests/snapshots/simplify_matches__match_str.bend.snap +++ b/tests/snapshots/simplify_matches__match_str.bend.snap @@ -2,8 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/match_str.bend --- -is_as: Any +unchecked is_as: Any (is_as) = λa match a { String/Nil: 1; String/Cons b c: (switch (- b 65) { 0: λd match d { String/Nil: 0; String/Cons e f: (switch (- e 115) { 0: λg match g { String/Nil: 2; String/Cons h i: 0; }; _ j: λk 0; } f); }; _ l: λm (switch (- l 31) { 0: λn match n { String/Nil: 0; String/Cons o p: (switch (- o 115) { 0: λq match q { String/Nil: 2; String/Cons r s: 0; }; _ t: λu 0; } p); }; _ v: λw 0; } m); } c); } -main: Any +unchecked main: Any (main) = * diff --git a/tests/snapshots/simplify_matches__nested.bend.snap b/tests/snapshots/simplify_matches__nested.bend.snap index bc947825f..0f4e03554 100644 --- a/tests/snapshots/simplify_matches__nested.bend.snap +++ b/tests/snapshots/simplify_matches__nested.bend.snap @@ -2,10 +2,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested.bend --- -Rule: Any +unchecked Rule: Any (Rule) = λa match a { Foo/CtrA b c: (match c { Bar/CtrB1 d: λe (e d); Bar/CtrB2 f g: λh (match f { Baz/CtrC: λi λj (i j); } h g); } b); Foo/CtrB k: k; } -Foo/CtrA: (Any -> (Any -> Foo)) +Foo/CtrA: (Any -> Any -> Foo) (Foo/CtrA) = λa λb λc (c Foo/CtrA/tag a b) Foo/CtrB: (Any -> Foo) @@ -14,7 +14,7 @@ Foo/CtrB: (Any -> Foo) Bar/CtrB1: (Any -> Bar) (Bar/CtrB1) = λa λb (b Bar/CtrB1/tag a) -Bar/CtrB2: (Any -> (Any -> Bar)) +Bar/CtrB2: (Any -> Any -> Bar) (Bar/CtrB2) = λa λb λc (c Bar/CtrB2/tag a b) Baz/CtrC: Baz diff --git a/tests/snapshots/simplify_matches__nested2.bend.snap b/tests/snapshots/simplify_matches__nested2.bend.snap index 5c02078fe..1ec4282ea 100644 --- a/tests/snapshots/simplify_matches__nested2.bend.snap +++ b/tests/snapshots/simplify_matches__nested2.bend.snap @@ -2,13 +2,13 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested2.bend --- -Foo: Any +unchecked Foo: Any (Foo) = λa λb (match b { List/Nil: λc (c List/Nil); List/Cons d e: λf (match e { List/Nil: λg λh (g (List/Cons h List/Nil)); List/Cons i j: λk λl (k l i j); } f d); } a) -List/Nil: ∀t (List t) +List/Nil: (List T) (List/Nil) = λa (a List/Nil/tag) -List/Cons: ∀t (u24 -> ((List t) -> (List t))) +List/Cons: (T -> (List T) -> (List T)) (List/Cons) = λa λb λc (c List/Cons/tag a b) List/Nil/tag: u24 diff --git a/tests/snapshots/simplify_matches__nested_0ary.bend.snap b/tests/snapshots/simplify_matches__nested_0ary.bend.snap index a57909179..60efc52cc 100644 --- a/tests/snapshots/simplify_matches__nested_0ary.bend.snap +++ b/tests/snapshots/simplify_matches__nested_0ary.bend.snap @@ -2,10 +2,10 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested_0ary.bend --- -Unpack: Any +unchecked Unpack: Any (Unpack) = λa λb (match b { list/Cons c d: λe (match d { list/Cons f g: λh λi (h (list/Cons i (list/Cons f g))); list/Nil: λj λk k; } e c); list/Nil: λl list/Nil; } a) -list/Cons: (Any -> (Any -> list)) +list/Cons: (Any -> Any -> list) (list/Cons) = λa λb λc (c list/Cons/tag a b) list/Nil: list diff --git a/tests/snapshots/simplify_matches__redundant_with_era.bend.snap b/tests/snapshots/simplify_matches__redundant_with_era.bend.snap index 4d24e84af..75981be88 100644 --- a/tests/snapshots/simplify_matches__redundant_with_era.bend.snap +++ b/tests/snapshots/simplify_matches__redundant_with_era.bend.snap @@ -2,8 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/redundant_with_era.bend --- -Fn2: Any +unchecked Fn2: Any (Fn2) = λa switch a { 0: λb let c = b; let (d, e) = c; let (f, g) = e; g; _ h: λi let j = i; let (k, l) = j; let (m, n) = l; n; } -main: Any +unchecked main: Any (main) = * diff --git a/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap b/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap index d75933f7f..676abe503 100644 --- a/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap +++ b/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap @@ -6,6 +6,3 @@ input_file: tests/golden_tests/simplify_matches/wrong_fn_arity.bend In tests/golden_tests/simplify_matches/wrong_fn_arity.bend : In definition 'Foo': Incorrect pattern matching rule arity. Expected 3 args, found 0. - -Other diagnostics: -File has no 'main' definition. From 1fe7de918e059ae804ef12dabaf72f4155b423b6 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 5 Sep 2024 21:24:03 +0200 Subject: [PATCH 24/29] Fix renaming of imported types in with blocks --- src/fun/parser.rs | 2 +- src/fun/transform/desugar_with_blocks.rs | 5 ----- src/fun/transform/encode_adts.rs | 3 ++- src/imports/book.rs | 21 ++++++++++++--------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 893ebc18d..7e1c63aaa 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -1377,7 +1377,7 @@ pub trait ParserCommons<'a>: Parser<'a> { while let Some(c) = self.peek_one() { if c == '\t' { let idx = *self.index(); - return self.err_msg_spanned("Tabs are not accepted for indentation.", idx..idx+1); + return self.err_msg_spanned("Tabs are not accepted for indentation.", idx..idx + 1); } if " ".contains(c) { self.advance_one(); diff --git a/src/fun/transform/desugar_with_blocks.rs b/src/fun/transform/desugar_with_blocks.rs index ca27ab4c9..bcf5ff04f 100644 --- a/src/fun/transform/desugar_with_blocks.rs +++ b/src/fun/transform/desugar_with_blocks.rs @@ -32,11 +32,6 @@ impl Term { if let Term::With { typ, bod } = self { bod.desugar_with_blocks(Some(typ), def_names)?; let wrap_ref = Term::r#ref(&format!("{typ}/wrap")); - // let wrap_ref = if def_names.contains(&wrap_nam) { - // Term::r#ref(&wrap_nam) - // } else { - // return Err(format!("Could not find definition {wrap_nam} for type {typ}")); - // }; *self = Term::Use { nam: Some(Name::new("wrap")), val: Box::new(wrap_ref), nxt: std::mem::take(bod) }; } diff --git a/src/fun/transform/encode_adts.rs b/src/fun/transform/encode_adts.rs index 409f99aca..5dad66381 100644 --- a/src/fun/transform/encode_adts.rs +++ b/src/fun/transform/encode_adts.rs @@ -73,6 +73,7 @@ fn encode_ctr_num_scott<'a>(ctr_args: impl DoubleEndedIterator fn make_tag_def(ctr_idx: usize, tag: &Name, source: Source) -> Definition { let rules = vec![Rule { pats: vec![], body: Term::Num { val: Num::U24(ctr_idx as u32) } }]; - let source = Source { kind: SourceKind::Generated, ..source }; + let kind = if source.is_builtin() { SourceKind::Builtin } else { SourceKind::Generated }; + let source = Source { kind, ..source }; Definition { name: tag.clone(), typ: Type::U24, check: true, rules, source } } diff --git a/src/imports/book.rs b/src/imports/book.rs index b61386030..56a911078 100644 --- a/src/imports/book.rs +++ b/src/imports/book.rs @@ -1,7 +1,9 @@ use super::{BindMap, ImportsMap, PackageLoader}; use crate::{ diagnostics::{Diagnostics, DiagnosticsConfig}, - fun::{parser::ParseBook, Adt, Book, Definition, HvmDefinition, Name, Rule, Source, SourceKind, Term}, + fun::{ + parser::ParseBook, Adt, AdtCtr, Book, Definition, HvmDefinition, Name, Rule, Source, SourceKind, Term, + }, imp::{self, Expr, MatchArm, Stmt}, imports::packages::Packages, maybe_grow, @@ -115,7 +117,7 @@ impl ParseBook { // Can not be done outside the function because of the borrow checker. // Just serves to pass only the import map of the first call to `apply_imports_go`. let main_imports = main_imports.unwrap_or(&self.import_ctx.map); - + Debug let mut local_imports = BindMap::new(); let mut adt_imports = BindMap::new(); @@ -154,7 +156,7 @@ impl ParseBook { for (_, def) in self.local_defs_mut() { def.apply_binds(true, &local_imports); - def.apply_type_binds(&local_imports); + def.apply_type_binds(&adt_imports); } } @@ -179,8 +181,8 @@ impl ParseBook { let mangle_name = !main_imports.contains_source(&name); let mut mangle_adt_name = mangle_name; - for (ctr, f) in std::mem::take(&mut adt.ctrs) { - let mut ctr_name = Name::new(format!("{}/{}", src, ctr)); + for (old_nam, ctr) in std::mem::take(&mut adt.ctrs) { + let mut ctr_name = Name::new(format!("{}/{}", src, old_nam)); let mangle_ctr = mangle_name && !main_imports.contains_source(&ctr_name); @@ -189,9 +191,10 @@ impl ParseBook { ctr_name = Name::new(format!("__{}", ctr_name)); } + let ctr = AdtCtr { name: ctr_name.clone(), ..ctr }; new_ctrs.insert(ctr_name.clone(), name.clone()); - ctrs_map.insert(ctr, ctr_name.clone()); - adt.ctrs.insert(ctr_name, f); + ctrs_map.insert(old_nam, ctr_name.clone()); + adt.ctrs.insert(ctr_name, ctr); } if mangle_adt_name { @@ -213,12 +216,13 @@ impl ParseBook { } } + let adts_map = adts_map.into_iter().collect::>(); for (_, def) in self.local_defs_mut() { // Applies the binds for the new constructor names for every definition. def.apply_binds(true, &ctrs_map); // Apply the binds for the type constructors in the def types and in the `def` terms. - def.apply_type_binds(&ctrs_map); + def.apply_type_binds(&adts_map); } self.adts = new_adts; @@ -257,7 +261,6 @@ impl ParseBook { } fn add_imported_adt(&mut self, nam: Name, adt: Adt, diag: &mut Diagnostics) { - eprintln!("Adding imported adt {}", nam); if self.adts.get(&nam).is_some() { let err = format!("The imported datatype '{nam}' conflicts with the datatype '{nam}'."); diag.add_book_error(err); From dbba1dd2e6b4666f5548ceae639fd156b90f6c23 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 5 Sep 2024 21:24:15 +0200 Subject: [PATCH 25/29] Rename cast operations --- src/fun/builtins.bend | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index feb22e6db..542047d26 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -310,8 +310,8 @@ def IO/nanosleep(hi_lo: (u24, u24)) -> IO(None): # Sleeps for a given amount of seconds as a float. def IO/sleep(seconds: f24) -> IO(None): nanos = seconds * 1_000_000_000.0 - lo = f24_to_u24(nanos % 0x1_000_000.0) - hi = f24_to_u24(nanos / 0x1_000_000.0) + lo = f24/to_u24(nanos % 0x1_000_000.0) + hi = f24/to_u24(nanos / 0x1_000_000.0) return IO/nanosleep((hi, lo)) ## File IO @@ -398,7 +398,7 @@ def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(L case Result/Ok: (line, rest) = res.val (length, *) = List/length(rest) - res_seek <- IO/FS/seek(fd, u24_to_i24(length) * -1, IO/FS/SEEK_CUR) + res_seek <- IO/FS/seek(fd, u24/to_i24(length) * -1, IO/FS/SEEK_CUR) match res_seek: case Result/Ok: chunks = List/Cons(line, chunks) @@ -521,29 +521,39 @@ def unreachable() -> Any: # Native number casts # Casts a f24 number to a u24. -hvm f24_to_u24 -> (f24 -> u24): +hvm f24/to_u24 -> (f24 -> u24): ($([u24] ret) ret) # Casts an i24 number to a u24. -hvm i24_to_u24 -> (i24 -> u24): +hvm i24/to_u24 -> (i24 -> u24): ($([u24] ret) ret) # Casts a u24 number to an i24. -hvm u24_to_i24 -> (u24 -> i24): +hvm u24/to_i24 -> (u24 -> i24): ($([i24] ret) ret) # Casts a f24 number to an i24. -hvm f24_to_i24 -> (f24 -> i24): +hvm f24/to_i24 -> (f24 -> i24): ($([i24] ret) ret) # Casts a u24 number to a f24. -hvm u24_to_f24 -> (u24 -> f24): +hvm u24/to_f24 -> (u24 -> f24): ($([f24] ret) ret) # Casts an i24 number to a f24. -hvm i24_to_f24 -> (i24 -> f24): +hvm i24/to_f24 -> (i24 -> f24): ($([f24] ret) ret) +def u24/to_string(n: u24) -> String: + def go(n: u24) -> String -> String: + r = n % 10 + d = n / 10 + c = '0' + r + if d == 0: + return lambda t: String/Cons(c, t) + else: + return lambda t: go(d, String/Cons(c, t)) + return go(n, String/Nil) # String Encoding and Decoding @@ -738,7 +748,7 @@ Math/sqrt (n: f24) : f24 = # Round float up to the nearest integer. def Math/ceil(n: f24) -> f24: - i_n = i24_to_f24(f24_to_i24(n)) + i_n = i24/to_f24(f24/to_i24(n)) if n <= i_n: return i_n else: @@ -746,7 +756,7 @@ def Math/ceil(n: f24) -> f24: # Round float down to the nearest integer. def Math/floor(n: f24) -> f24: - i_n = i24_to_f24(f24_to_i24(n)) + i_n = i24/to_f24(f24/to_i24(n)) if n < i_n: return i_n - 1.0 else: @@ -754,7 +764,7 @@ def Math/floor(n: f24) -> f24: # Round float to the nearest integer. def Math/round(n: f24) -> f24: - i_n = i24_to_f24(f24_to_i24(n)) + i_n = i24/to_f24(f24/to_i24(n)) if (n - i_n) < 0.5: return Math/floor(n) else: From 617527b6087456d735f56c5f370cc2e3b778b979 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Thu, 5 Sep 2024 21:37:35 +0200 Subject: [PATCH 26/29] clean --- src/imports/book.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/imports/book.rs b/src/imports/book.rs index 56a911078..b491b85cc 100644 --- a/src/imports/book.rs +++ b/src/imports/book.rs @@ -117,7 +117,6 @@ impl ParseBook { // Can not be done outside the function because of the borrow checker. // Just serves to pass only the import map of the first call to `apply_imports_go`. let main_imports = main_imports.unwrap_or(&self.import_ctx.map); - Debug let mut local_imports = BindMap::new(); let mut adt_imports = BindMap::new(); From eaa2809a0c066b2ebc129dca89354435d1cebd17 Mon Sep 17 00:00:00 2001 From: imaqtkatt Date: Thu, 5 Sep 2024 20:15:28 -0300 Subject: [PATCH 27/29] WIP add documentation on type checking --- docs/syntax.md | 239 ++++++++++++++++++++++++++++++++++++++++++ docs/type-checking.md | 57 ++++++++++ 2 files changed, 296 insertions(+) create mode 100644 docs/type-checking.md diff --git a/docs/syntax.md b/docs/syntax.md index 389a422c0..4d3e9e06f 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -10,6 +10,10 @@ Click [here](#import-syntax) to see the import syntax. Click [here](#comments) to see the syntax for commenting code. +Click [here](#imp-type-syntax) to see the imperative type syntax. + +Click [here](#fun-type-syntax) to see the functional type syntax. + Both syntaxes can be mixed in the same file like the example below: ```python @@ -1338,3 +1342,238 @@ Multi-line commenting should also be used to document code. def second(x, y): return y ``` + +

+ +# Imp Type Syntax + +## Variable + +Any name represents a type variable. + +Used in generic or polymorphic type definitions. + +```python +# T is a type variable +type Option(T): + Some { value: T } + None + +# A is a type variable +def id(x: A) -> A: + return x +``` + +## Constructor + +`Ctr(...)` represents a constructor type. + +Used for defining custom data types or algebraic data types. +Can contain other types as parameters. + +```python +def head(list: List(T)) -> Option(T) + match list: + case List/Nil: + return Option/None + case List/Cons: + return Option/Some(list.head) +``` + +## Any + +`Any` represents any type. + +Can be used when the specific type is unknown or doesn't matter. + +```python +def main -> Any: + return lambda x: x +``` + +## None + +`None` represents an eraser or absence of a value. + +Often used to indicate that a function doesn't return anything. + +```python +def none -> None: + return * +``` + +## Hole + +`_` represents a hole type. + +Can be used to represent/infer any type in the current context. + +```python +def increment(x: _) -> _: + return x + 1 +``` + +## u24 + +`u24` represents an unsigned 24-bit integer. + +```python +def zero -> u24: + return 0 +``` + +## i24 + +`i24` represents a signed 24-bit integer. + +```python +def random_integer -> i24: + return -42 +``` + +## f24 + +`f24` represents a 24-bit floating-point number. + +```python +def PI -> f24: + return 3.14 +``` + +## Tuple + +`(_, _, ...)` represents a tuple type. + +Can contain two or more types separated by commas. + +```python +def make_tuple(fst: A, snd: B) -> (A, B): + return (fst, snd) +``` + +## Function + +`a -> b` represents a function type. + +`a` is the input type, and `b` is the output type. + +```python +def apply(f: A -> B, arg: A) -> B: + return f(arg) +``` + +
+ +# Fun Type Syntax + +## Variable + +Any name represents a type variable. + +Used in generic or polymorphic type definitions. + +```python +# T is a type variable +type (Option T) + = (Some T) + | None + +# A is a type variable +id : A -> A +id x = x +``` + +## Constructor + +`(Ctr ...)` represents a constructor type. + +Used for defining custom data types or algebraic data types. +Can contain other types as parameters. + +```python +head : (List T) -> (Option T) +head [] = Option/None +head (List/Cons head _) = (Option/Some head) +``` + +## Any + +`Any` represents any type. + +Can be used when the specific type is unknown or doesn't matter. + +```python +main : Any +main = @x x +``` + +## None + +`None` represents an eraser or absence of a value. + +Often used to indicate that a function doesn't return anything. + +```python +none : None +none = * +``` + +## Hole + +`_` represents a hole type. + +Can be used to represent/infer any type in the current context. + +```python +increment : _ -> _ +increment x = (+ x 1) +``` + +## u24 + +`u24` represents an unsigned 24-bit integer. + +```python +zero : u24 +zero = 0 +``` + +## i24 + +`i24` represents a signed 24-bit integer. + +```python +random_integer : i24 +random_integer = -24 +``` + +## f24 + +`f24` represents a 24-bit floating-point number. + +```python +PI : f24 +PI = 3.14 +``` + +## Tuple + +`(_, _, ...)` represents a tuple type. + +Can contain two or more types separated by commas. + +```python +make_tuple : A -> B -> (A, B) +make_tuple fst snd = (fst, snd) +``` + +## Function + +`a -> b` represents a function type. + +`a` is the input type, and `b` is the output type. + +```python +apply : (A -> B) -> A -> B +apply f arg = (f arg) +``` diff --git a/docs/type-checking.md b/docs/type-checking.md new file mode 100644 index 000000000..d9c3d6464 --- /dev/null +++ b/docs/type-checking.md @@ -0,0 +1,57 @@ +# Type Checking + +The Bend type checker is implemented based on a gradual type system with Hindley Milner inference. + +Programs can be optionally typed using the respective imp or fun type syntax. Type checking is +enabled by default, and can also be specified with the `-Otype-check` option. + +Bend has some builtin types such as `i24`, `u24`, `f24`, Integer and Number. Although Integer and +Number cannot be used directly. + +In example, the program below will not compile or run since some of the list elements do not have +type `i24`. + +```python +type Option(T): + Some { value: T } + None + +def head(list: List(T)) -> Option(T): + match list: + case List/Cons: + return Option/Some { value: list.head } + case List/Nil: + return Option/None + +def main -> Option(i24): + return head([1 ,"a"]) +``` + +In some cases we know that dynamically our program will not do something wrong. + +To disable type checking we can remove the type from some function or use the `-Ono-type-check` +option to not type check the entire program: + +```python +def main: + return head([1 ,"a"]) +``` + +## Limitations + +Currently, it is not possible to: + +- Define or check a superposed type or term. +- Check unscoped variables. +- ~~Check tagged terms.~~ + +In example, we know that it could be possible to call `add` with a superposed term and have `{3 4}` +as result. + +```python +def add(x: _, y: _) -> _: + return x + y + +def main -> _: + return add(1, {2, 3}) +``` From 538f9f0449308003b567cd18d9d4b96cee554a7c Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Mon, 9 Sep 2024 13:43:21 +0200 Subject: [PATCH 28/29] Improve type-check docs --- cspell.json | 5 +- docs/syntax.md | 56 +++++--- docs/type-checking.md | 135 ++++++++++++++---- src/fun/check/type_check.rs | 6 +- .../run_file/checked_scott_encoding.bend | 5 + ...run_file__checked_scott_encoding.bend.snap | 7 + 6 files changed, 166 insertions(+), 48 deletions(-) create mode 100644 tests/golden_tests/run_file/checked_scott_encoding.bend create mode 100644 tests/snapshots/run_file__checked_scott_encoding.bend.snap diff --git a/cspell.json b/cspell.json index e19d45aba..0ade9d2e0 100644 --- a/cspell.json +++ b/cspell.json @@ -32,9 +32,9 @@ "elif", "elifs", "foldl", - "forall", "hasher", "hexdigit", + "Hindley", "hvm's", "indexmap", "inet", @@ -49,7 +49,6 @@ "ints", "itertools", "ITRS", - "kindc", "kwarg", "kwargs", "lcons", @@ -59,6 +58,7 @@ "lnil", "lpthread", "mant", + "Milner", "miscompilation", "mult", "namegen", @@ -111,6 +111,7 @@ "succ", "supercombinator", "supercombinators", + "Tarjan's", "tlsv", "TSPL", "tunr", diff --git a/docs/syntax.md b/docs/syntax.md index 4d3e9e06f..25d5d03c8 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -48,12 +48,15 @@ main = Defines a top level function. ```python -def add(x, y): +def add(x: u24, y: u24) -> u24: result = x + y return result +def unchecked two() -> u24: + return 2 + def main: - return add(40, 2) + return add(40, two) ``` A function definition is composed by a name, a sequence of parameters and a body. @@ -63,6 +66,10 @@ A top-level name can be anything matching the regex `[A-Za-z0-9_.-/]+`, except i The last statement of each function must either be a `return` or a selection statement (`if`, `switch`, `match`, `fold`) where all branches `return`. +Each parameter of the function can receive a type annotation with `param_name: type` and the return value of the function can also be annotated with `def fn_name(args) -> return_type:`. + +We can force the type-checker to run or not on a specific function by adding `checked` or `unchecked` between `def` and the function name. + ### Type Defines an algebraic data type. @@ -72,14 +79,16 @@ type Option: Some { value } None -type Tree: - Node { value, ~left, ~right } +type Tree(T): + Node { value: T, ~left: Tree(T), ~right: Tree(T) } Leaf ``` Type names must be unique, and should have at least one constructor. -Each constructor is defined by a name followed by its fields. +For a generic or polymorphic type, all type variables used in the constructors must be declared first in the type definition with `type Name(type_var1, ...):` + +Each constructor is defined by a name followed by its fields. The fields can be annotated with types that will be checked when creating values of that type. The `~` notation indicates a recursive field. To use `fold` statements with a type its recursive fields must be correctly marked with `~`. @@ -93,9 +102,9 @@ Read [defining data types](./defining-data-types.md) to know more. Defines a type with a single constructor (like a struct, a record or a class). ```python -object Pair { fst, snd } +object Pair(A, B) { fst: A, snd: B } -object Function { name, args, body } +object Function(T) { name: String, args, body: T } object Vec { len, data } ``` @@ -1260,6 +1269,11 @@ hvm link_ports: (a (b *)) & (c a) ~ (d e) & (e b) ~ (d c) + +# Casts a `u24` to itself. +# We can give type annotations to HVM definitions. +hvm u24_to_u24 -> (u24 -> u24): + ($([u24] ret) ret) ``` It's also possible to define functions using HVM syntax. This can be @@ -1382,18 +1396,23 @@ def head(list: List(T)) -> Option(T) ## Any -`Any` represents any type. +`Any` represents the untyped type. -Can be used when the specific type is unknown or doesn't matter. +It accepts values of alls type and will forcefully cast any type to `Any`. + +Can be used for values that can't be statically typed, either because +they are unknown (like in raw IO calls), because they contain untypable +expressions (like unscoped variables), or because the expression cannot +be typed with the current type system (like the self application `lambda x: x(x)`). ```python def main -> Any: - return lambda x: x + return 24 ``` ## None -`None` represents an eraser or absence of a value. +`None` represents the eraser `*` or absence of a value. Often used to indicate that a function doesn't return anything. @@ -1406,7 +1425,7 @@ def none -> None: `_` represents a hole type. -Can be used to represent/infer any type in the current context. +This will let the type checker infer the most general type for an argument or return value. ```python def increment(x: _) -> _: @@ -1498,9 +1517,14 @@ head (List/Cons head _) = (Option/Some head) ## Any -`Any` represents any type. +`Any` represents the untyped type. + +It accepts values of alls type and will forcefully cast any type to `Any`. -Can be used when the specific type is unknown or doesn't matter. +Can be used for values that can't be statically typed, either because +they are unknown (like in raw IO calls), because they contain untypable +expressions (like unscoped variables), or because the expression cannot +be typed with the current type system (like the self application `λx (x x)`). ```python main : Any @@ -1509,7 +1533,7 @@ main = @x x ## None -`None` represents an eraser or absence of a value. +`None` represents the eraser `*` or absence of a value. Often used to indicate that a function doesn't return anything. @@ -1522,7 +1546,7 @@ none = * `_` represents a hole type. -Can be used to represent/infer any type in the current context. +This will let the type checker infer the most general type for an argument or return value. ```python increment : _ -> _ diff --git a/docs/type-checking.md b/docs/type-checking.md index d9c3d6464..37ed41e3a 100644 --- a/docs/type-checking.md +++ b/docs/type-checking.md @@ -1,57 +1,138 @@ # Type Checking -The Bend type checker is implemented based on a gradual type system with Hindley Milner inference. +Bend has a type checker with optional typing support based on a Hindley Milner type system. Programs can be optionally typed using the respective imp or fun type syntax. Type checking is -enabled by default, and can also be specified with the `-Otype-check` option. +enabled by default, but can be toggled with the `-Otype-check` and `-Ono-type-check` options. -Bend has some builtin types such as `i24`, `u24`, `f24`, Integer and Number. Although Integer and -Number cannot be used directly. +Every function can be annotated with a type for its arguments and return value. +The type checker will infer the type of the function and then compare if it's compatible with the annotated type. -In example, the program below will not compile or run since some of the list elements do not have -type `i24`. +```python +def add(x: u24, y: u24) -> u24: + return x + y + +# Arguments or return value without annotation are considered `Any`. +# They will be accepted by any function, regardless of being correct or not. +def push(list: List(T), value) -> List(T): + match list: + case List/Nil: + return List/Cons(value, List/Nil) + case List/Cons: + return List/Cons(list.head, push(list.tail, value)) + +# Error, List(T) must only receive values of type `T`. +def append_num(list: List(T), num: u24) -> List(T): + return List/Cons(num, list) + +# Error, Tree(T) can only store one type of value. +def my_tree() -> _: + return ![!1, !"a"] + +# Error, can't add a `u24` and a `f24`. +# Bend doesn't have implicit type conversions. +def add_float(x: u24, y: f24) -> f24: + return x + y +``` + +Bend comes with the following builtin types: + +* `u24`: Unsigned 24-bit integer. +* `i24`: Signed 24-bit integer. +* `f24`: Floating point number. +* `(T1, ..., Tn)`: Tuple with `n` elements of types `T1` to `Tn`. +* `Any`: Untyped value. +* `None`: Eraser `*`. +* `_`: A type that will be inferred by the type checker. + +The prelude library also defines some basic types that are used in Bend programs: + +* `String`: Text represented as a sequence of Unicode characters. +* `List(T)`: A list of values of type `T`. +* `Tree(T)`: A binary tree with values of type `T` at the leaves. +* `Map(T)`: A map from keys of type `u24` to values of type `T`. +* `IO(T)`: A monadic IO type that can be used to perform IO operations. +* `Result(O, E)`: Represents the result of an operation that can either succeed with an `O` or fail with an `E`. + + +Additionally, you can define your own algebraic data types. +In this case, all the type variables that occur in the constructors must be previously defined. ```python type Option(T): Some { value: T } None +``` + +All the constructors will be declared with the same type `TypeName(var2, var2, ...)`. -def head(list: List(T)) -> Option(T): +### Enabling and disabling type checking + +In some cases we know that dynamically our program will not do something wrong despite not being able to give it the proper type. + +We can disable type checking for a specific function by either removing the type annotations or by giving it the `unchecked` keyword: + +```python +# Error, type-checked functions can't contain an unscoped variable. +def channel(x: u24) -> (u24 -> u24, u24): + return (lambda $a: x, $a) + +# We can remove the annotations. It won't be type-checked, +# but its type will be `Any -> Any`. +def channel(x): + return (lambda $a: x, $a) + +# Instead, we can use the `unchecked` keyword. +# The annotated type will be considered the truth, regardless of being correct or not. +def unchecked channel(x: u24) -> (u24 -> u24, u24): + return (lambda $a: x, $a) +``` + +The opposite is also possible, we can enable type checking for an unannotated function by using the `checked` keyword before the name of the function in its declaration: + +```python +# Despite the inferred type being `List(T) -> List(T)`, the type checker will consider it as `Any -> Any` because it's not annotated. +def checked tail(list): match list: - case List/Cons: - return Option/Some { value: list.head } case List/Nil: - return Option/None - -def main -> Option(i24): - return head([1 ,"a"]) + return List/Nil + case List/Cons: + return list.tail + +# Error, can't infer the type of this function, despite having type `Any`. +# Not typeable by a Hindley-Milner type system. +checked (scott_concat a b) = (a + λh λt λcons λnil (cons h (scott_concat t b)) + b +) ``` -In some cases we know that dynamically our program will not do something wrong. +We can also disable type checking for the entire program by using the `-Ono-type-check` option. -To disable type checking we can remove the type from some function or use the `-Ono-type-check` -option to not type check the entire program: +Native HVM definitions are always unchecked. ```python -def main: - return head([1 ,"a"]) +# This function will be given the type `a -> a`. +hvm native_id -> (a -> a): + (x x) ``` -## Limitations +### Limitations -Currently, it is not possible to: +Currently, the following are not supported by the type checker: -- Define or check a superposed type or term. -- Check unscoped variables. -- ~~Check tagged terms.~~ +- Superpositions (`{a, b}`, the tuple type with duplication semantics, see [Dups and sups](https://github.com/HigherOrderCO/Bend/blob/main/docs/dups-and-sups.md)). +- Unscoped variables and variable binds (`$a`, `let $a = ...`, see [Scopeless lambdas](https://github.com/HigherOrderCO/Bend/blob/main/docs/using-scopeless-lambdas.md)). +- Expressions not typeable by a Hindley-Milner type system (e.g. self application `λx: x(x)`). -In example, we know that it could be possible to call `add` with a superposed term and have `{3 4}` -as result. +Additionally, the builtin types `Number` and `Integer` can't be used directly in type annotations. They are used internally by the type checker to handle numeric expressions. ```python +# The inferred type will be `Number(a) -> Number(a) -> Number(a)`. def add(x: _, y: _) -> _: return x + y -def main -> _: - return add(1, {2, 3}) +# The inferred type will be `Integer(a) -> Integer(a) -> Integer(a)`. +def shift(x: _, n: _) -> _: + return x << n ``` diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs index bb0890396..a06935c11 100644 --- a/src/fun/check/type_check.rs +++ b/src/fun/check/type_check.rs @@ -1,4 +1,4 @@ -//! Gradual Hindley-Milner-like type system. +//! Optional Hindley-Milner-like type system. //! //! Based on https://github.com/developedby/algorithm-w-rs //! and https://github.com/mgrabmueller/AlgorithmW. @@ -196,6 +196,7 @@ impl RecGroups { } } + /// Tarjan's algorithm for finding strongly connected components. fn strong_connect<'a>( v: &'a Name, deps: &DependencyGraph<'a>, @@ -253,7 +254,6 @@ impl RecGroups { deps.insert(name, fn_deps); } - // Run Tarjan's algorithm let mut index = 0; let mut stack = Vec::new(); let mut index_map = BTreeMap::new(); @@ -612,7 +612,7 @@ fn unify_fields<'a>(ts: impl Iterator, ctx: &Term) fn unify_term(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { match unify(t1, t2) { Ok((t, s)) => Ok((t, s)), - Err(msg) => Err(format!("In {ctx}: Can't unify '{t1}' and '{t2}'.{msg}")), + Err(msg) => Err(format!("In {ctx}:\n Can't unify '{t1}' and '{t2}'.{msg}")), } } diff --git a/tests/golden_tests/run_file/checked_scott_encoding.bend b/tests/golden_tests/run_file/checked_scott_encoding.bend new file mode 100644 index 000000000..aa7bc2d6f --- /dev/null +++ b/tests/golden_tests/run_file/checked_scott_encoding.bend @@ -0,0 +1,5 @@ +checked (scott_concat a b) = (a + λh λt λcons λnil (cons h (scott_concat t b)) + b +) +main = scott_concat \ No newline at end of file diff --git a/tests/snapshots/run_file__checked_scott_encoding.bend.snap b/tests/snapshots/run_file__checked_scott_encoding.bend.snap new file mode 100644 index 000000000..f8986b3ff --- /dev/null +++ b/tests/snapshots/run_file__checked_scott_encoding.bend.snap @@ -0,0 +1,7 @@ +--- +source: tests/golden_tests.rs +input_file: tests/golden_tests/run_file/checked_scott_encoding.bend +--- +Errors: +In λa λb (a λe λf λg λh (g e (scott_concat f b)) b): + Can't unify '(e -> c -> j)' and '(((d -> e -> (d -> j -> k) -> g -> k) -> c -> m) -> c -> m)'. Variable 'e' occurs in '((d -> e -> (d -> j -> k) -> g -> k) -> c -> m)' From 55f8d8783fe89614d93f71617d3d1751baa13689 Mon Sep 17 00:00:00 2001 From: Nicolas Abril Date: Mon, 9 Sep 2024 18:03:45 +0200 Subject: [PATCH 29/29] Update changelog for type checker PR --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4dbfc433..c865d7abc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project does not currently adhere to a particular versioning scheme. ### Added +- Add type system for Bend. ([#615][gh-615], [#679][gh-679], see [Type Checking](docs/type-checking.md)) - Add import system. ([#544][gh-544]) - Add multi line comment `#{ ... #}` syntax. ([#595][gh-595]) - Add error message when input file is not found. ([#513][gh-513])