Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix 'wrap' being considered a variable inside 'with' #665

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:
In definition 'main':
Unbound variable 'y1'.
In compiled inet 'main':
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'.
Loading