Skip to content

Commit

Permalink
Fix 'wrap' being considered a variable inside 'with'
Browse files Browse the repository at this point in the history
  • Loading branch information
developedby committed Aug 7, 2024
1 parent dc14ed2 commit bfc9309
Show file tree
Hide file tree
Showing 27 changed files with 73 additions and 34 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project does not currently adhere to a particular versioning scheme.
- Expand references to functions generated by the `float_combinators` pass inside the main function. ([#642][gh-642])
- Expand references inside constructors in the main function. ([#643][gh-643])
- Fix readback when hvm net has `a{n}` or `x{n}` vars. ([#659][gh-659])
- Fix `wrap` inside `with` being deferred applied like a variable. ([#664][gh-664])

### Added

Expand All @@ -23,6 +24,7 @@ and this project does not currently adhere to a particular versioning scheme.
- Add error message when input file is not found. ([#513][gh-513])
- Add `List/filter` and `String/{equals, filter}` builtins.
- Add IO functions for loading dynamically linked libraries (`IO/DyLib/open`, `IO/DyLib/call`, `IO/DyLib/close`). ([#621][gh-621])
- Add builtin functions `Tree/to_list`, `Tree/reverse`, `DiffList/concat` and `DiffList/wrap`. ([#662][gh-662])

### Changed

Expand Down Expand Up @@ -420,4 +422,6 @@ and this project does not currently adhere to a particular versioning scheme.
[gh-648]: https://github.com/HigherOrderCO/Bend/issues/648
[gh-657]: https://github.com/HigherOrderCO/Bend/issues/657
[gh-659]: https://github.com/HigherOrderCO/Bend/pull/659
[gh-662]: https://github.com/HigherOrderCO/Bend/pull/662
[gh-664]: https://github.com/HigherOrderCO/Bend/issues/664
[Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.36...HEAD
14 changes: 9 additions & 5 deletions src/fun/check/unbound_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ impl Ctx<'_> {

impl Term {
/// Checks that all variables are bound.
/// Precondition: References have been resolved, implicit binds have been solved.
///
/// Precondition: References have been resolved, implicit binds
/// have been solved.
///
/// `check_unbound_vars` should be called once before this, since
/// the original names will be lost after.
pub fn check_unbound_vars<'a>(
&'a mut self,
scope: &mut HashMap<&'a Name, u64>,
Expand Down Expand Up @@ -127,17 +131,17 @@ impl std::fmt::Display for UnboundVarErr {
if var == desugar_bend::RECURSIVE_KW {
write!(
f,
"Unbound variable '{}'.\n Note: '{}' is only a keyword inside the 'when' arm of a 'bend'.",
"Unbound name '{}'.\n Note: '{}' is only a keyword inside the 'when' arm of a 'bend'.",
var,
desugar_bend::RECURSIVE_KW
)
} else if let Some((pre, suf)) = var.rsplit_once('-') {
write!(
f,
"Unbound variable '{var}'. If you wanted to subtract '{pre}' from '{suf}', you must separate it with spaces ('{pre} - {suf}') since '-' is a valid name character."
"Unbound name '{var}'. If you wanted to subtract '{pre}' from '{suf}', you must separate it with spaces ('{pre} - {suf}') since '-' is a valid name character."
)
} else {
write!(f, "Unbound variable '{var}'.")
write!(f, "Unbound name '{var}'.")
}
}
UnboundVarErr::Global { var, declared, used } => match (declared, used) {
Expand Down
5 changes: 5 additions & 0 deletions src/fun/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,11 @@ impl<'a> TermParser<'a> {
let typ = self.parse_name()?;
self.consume("{")?;
let bod = self.parse_term()?;
let bod = Term::Use {
nam: Some(Name::new("wrap")),
val: Box::new(Term::Var { nam: Name::new(format!("{typ}/wrap")) }),
nxt: Box::new(bod),
};
self.consume("}")?;
return Ok(Term::With { typ: Name::new(typ), bod: Box::new(bod) });
}
Expand Down
2 changes: 2 additions & 0 deletions src/fun/transform/desugar_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ impl Book {
///
/// // Transforms to:
/// (λx x λx x λx x)
///
/// Should only be used after `make_var_names_unique`.
/// ```
pub fn desugar_use(&mut self) {
for def in self.defs.values_mut() {
Expand Down
8 changes: 1 addition & 7 deletions src/fun/transform/desugar_with_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,7 @@ impl Term {
maybe_grow(|| {
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) };
*self = std::mem::take(bod);
}

if let Term::Ask { pat, val, nxt } = self {
Expand Down
6 changes: 3 additions & 3 deletions src/fun/transform/fix_match_terms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Ctx<'_> {
///
/// Example:
/// For the program
/// ```hvm
/// ```bend
/// data MyList = (Cons h t) | Nil
/// match x {
/// Cons: (A x.h x.t)
Expand All @@ -35,9 +35,9 @@ impl Ctx<'_> {
/// * The bind `%matched-2` will be generated and stored in the switch term.
/// * If either was missing one of the match cases (a number or a constructor), we'd get an error.
/// * If either included one of the cases more than once (including wildcard patterns), we'd get a warning.
/// ```hvm
/// ```bend
/// 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 }
/// }
/// ```
Expand Down
9 changes: 6 additions & 3 deletions src/fun/transform/unique_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::collections::HashMap;
impl Book {
/// Makes all variables in each definition have a new unique name.
/// Skips unbound variables.
///
/// Precondition: Definition references have been resolved.
pub fn make_var_names_unique(&mut self) {
for def in self.defs.values_mut() {
Expand All @@ -32,7 +33,7 @@ pub struct UniqueNameGenerator {
}

impl UniqueNameGenerator {
// Recursively assign an id to each variable in the term, then convert each id into a unique name.
/// Recursively assign an id to each variable in the term, then convert each id into a unique name.
pub fn unique_names_in_term(&mut self, term: &mut Term) {
// Note: we can't use the children iterators here because we mutate the binds,
// which are shared across multiple children.
Expand Down Expand Up @@ -189,14 +190,16 @@ impl UniqueNameGenerator {
}
}

fn use_var(&self, nam: &Name) -> Name {
fn use_var(&mut self, nam: &Name) -> Name {
if let Some(vars) = self.name_map.get(nam) {
let var_id = *vars.last().unwrap();
Name::from(var_id)
} else {
// Skip unbound variables.
// With this, we can use this function before checking for unbound vars.
nam.clone()
let nam = Name::from(self.name_count);
self.name_count += 1;
nam
}
}
}
5 changes: 5 additions & 0 deletions src/imp/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,11 @@ impl<'a> PyParser<'a> {

self.consume_indent_exactly(*indent)?;
let (bod, nxt_indent) = self.parse_statement(indent)?;
let bod = Stmt::Use {
nam: Name::new("wrap"),
val: Box::new(Expr::Var { nam: Name::new(format!("{typ}/wrap")) }),
nxt: Box::new(bod),
};
indent.exit_level();

if nxt_indent == *indent {
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,13 @@ pub fn desugar_book(

ctx.desugar_bend()?;
ctx.desugar_fold()?;
ctx.desugar_with_blocks()?;

ctx.check_unbound_vars()?;

ctx.book.make_var_names_unique();
ctx.book.desugar_use();
ctx.desugar_with_blocks()?;

// Auto match linearization
ctx.book.make_var_names_unique();
ctx.book.desugar_use();
Expand Down
2 changes: 1 addition & 1 deletion tests/golden_tests/compile_file/ask_outside_do.bend
Original file line number Diff line number Diff line change
@@ -1 +1 @@
main = ask x = (Result/Ok x); x
main = ask x = (Result/Ok 0); x
2 changes: 1 addition & 1 deletion tests/golden_tests/compile_file_o_all/cyclic_dup.bend
Original file line number Diff line number Diff line change
@@ -1 +1 @@
main = let {x1 x2} = y1; let {y1 y2} = x1; (x2 y2)
main = let {$x1 x2} = $y1; let {$y1 y2} = $x1; (x2 y2)
2 changes: 2 additions & 0 deletions tests/golden_tests/desugar_file/bind_syntax.bend
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Result/bind (Result/Ok val) f = ((undefer f) val)
Result/bind err _ = err

Result/wrap x = (Result/Ok x)

safe_div a b = switch b {
0: (Result/Err "Div by 0")
_: (Result/Ok (/ a b))
Expand Down
2 changes: 2 additions & 0 deletions tests/golden_tests/run_file/do_block_mixed.bend
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Result/bind r nxt = match r {
Result/Err: r
}

Result/wrap x = (Result/Ok x)

main = with Result {
let x = 1
let y = (Result/Ok x)
Expand Down
2 changes: 2 additions & 0 deletions tests/golden_tests/run_file/recursive_bind.bend
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Result/bind (Result/Ok val) f = ((undefer f) val)
Result/bind err _ = err

Result/wrap x = (Result/Ok x)

Bar x = (Result/Err 0)

Foo x y = with Result {
Expand Down
3 changes: 3 additions & 0 deletions tests/golden_tests/run_file/strict_monad_fn.bend
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ Result/bind = @val @nxt match val {
Result/Ok: ((undefer nxt) val.val)
Result/Err: (Result/Err val.val)
}

Result/wrap x = (Result/Ok x)

Result/foo x y =
with Result {
ask a = (Result/Ok x)
Expand Down
2 changes: 1 addition & 1 deletion tests/snapshots/cli__warn_and_err.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ input_file: tests/golden_tests/cli/warn_and_err.bend

Errors:
In definition 'Main':
Unbound variable 'a'.
Unbound name 'a'.
2 changes: 1 addition & 1 deletion tests/snapshots/compile_file__ask_outside_do.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/ask_outside_do.bend
---
Errors:
In definition 'main':
Monadic bind operation 'x <- ...' used outside of a `do` block.
Monadic bind operation 'a <- ...' used outside of a `do` block.
2 changes: 1 addition & 1 deletion tests/snapshots/compile_file__unbound_var.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/unbound_var.bend
---
Errors:
In definition 'main':
Unbound variable 'a'.
Unbound name 'a'.
2 changes: 1 addition & 1 deletion tests/snapshots/compile_file__unbound_var_scope.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/unbound_var_scope.bend
---
Errors:
In definition 'main':
Unbound variable 'b'.
Unbound name 'b'.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/unbound_with_tup_pattern.bend
---
Errors:
In definition 'Foo':
Unbound variable 'a'.
Unbound name 'a'.
2 changes: 1 addition & 1 deletion tests/snapshots/compile_file__warn_and_err.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ input_file: tests/golden_tests/compile_file/warn_and_err.bend

Errors:
In definition 'Main':
Unbound variable 'a'.
Unbound name 'a'.
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ input_file: tests/golden_tests/compile_file_o_all/bad_parens_making_erased_let.b
---
Errors:
In definition 'main':
Unbound variable 'two'.
Unbound variable 'qua'.
Unbound name 'two'.
Unbound name 'qua'.
4 changes: 2 additions & 2 deletions tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file_o_all/cyclic_dup.bend
---
Errors:
[1mIn definition '[4mmain[0m[1m':[0m
Unbound variable 'y1'.
[1mIn compiled inet '[4mmain[0m[1m':[0m
Found term that compiles into an inet with a vicious cycle
2 changes: 1 addition & 1 deletion tests/snapshots/desugar_file__ask_branch.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ input_file: tests/golden_tests/desugar_file/ask_branch.bend

(IO/bind) = λa λb (a IO/bind__C2 b)

(main) = (IO/bind (Bool/T λa switch a { 0: (IO/wrap 0); _: λ* (IO/wrap 0); }) λb (b λc λd (c d) IO/wrap))
(main) = (IO/bind (Bool/T λa switch a { 0: (IO/wrap 0); _: λ* (IO/wrap 0); }) λb (b λc (IO/wrap c)))

(IO/Done) = λa λb λc (c IO/Done/tag a b)

Expand Down
2 changes: 2 additions & 0 deletions tests/snapshots/desugar_file__bind_syntax.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ input_file: tests/golden_tests/desugar_file/bind_syntax.bend

(Result/bind) = λa λb (a Result/bind__C2 b)

(Result/wrap) = λa (Result/Ok a)

(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) = λ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)
Expand Down
10 changes: 9 additions & 1 deletion tests/snapshots/parse_file__imp_program.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ input_file: tests/golden_tests/parse_file/imp_program.bend

(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); }; }

(IO/MAGIC) = (13683217, 16719857)

(IO/wrap) = λ%arg0 use x = %arg0; (IO/Done IO/MAGIC x)

(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) = (Point/Point 1 2)
Expand Down Expand Up @@ -36,7 +40,7 @@ input_file: tests/golden_tests/parse_file/imp_program.bend

(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) = with IO { ask x = IO.read; x }
(main) = with IO { use wrap = IO/wrap; ask x = IO.read; x }

(List/Nil) = λ%x (%x List/Nil/tag)

Expand All @@ -46,6 +50,8 @@ input_file: tests/golden_tests/parse_file/imp_program.bend

(Map/Leaf) = λ%x (%x Map/Leaf/tag)

(IO/Done) = λmagic λexpr λ%x (%x IO/Done/tag magic expr)

(Point/Point) = λx λy λ%x (%x Point/Point/tag x y)

(Bool/True) = λ%x (%x Bool/True/tag)
Expand All @@ -60,6 +66,8 @@ input_file: tests/golden_tests/parse_file/imp_program.bend

(Map/Leaf/tag) = 1

(IO/Done/tag) = 0

(Point/Point/tag) = 0

(Bool/True/tag) = 0
Expand Down
2 changes: 1 addition & 1 deletion tests/snapshots/run_file__unbound_wrap.bend.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ input_file: tests/golden_tests/run_file/unbound_wrap.bend
---
Errors:
In definition 'main':
Reference to undefined function 'Maybe/wrap'
Unbound name 'Maybe/wrap'.

0 comments on commit bfc9309

Please sign in to comment.