From 3432a32af8c17efdabe20d4249f8da49608de179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 16 Feb 2021 22:33:26 +0100 Subject: simple and dumb unused variable report --- src/compiler.rs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index 94ae2aa..3d95e2f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -127,6 +127,7 @@ struct Variable { upvalue: bool, captured: bool, mutable: bool, + read: bool, } enum LoopOp { @@ -203,6 +204,14 @@ impl Frame { None } + fn mark_read(&mut self, var: &Variable) { + (if var.upvalue { + &mut self.upvalues + } else { + &mut self.stack + })[var.slot].read = true; + } + fn add_upvalue(&mut self, variable: Variable) -> Variable { let new_variable = Variable { outer_upvalue: variable.upvalue, @@ -243,9 +252,16 @@ macro_rules! push_frame { // Return value stored as a variable $compiler.define_variable("", Type::Unknown, &mut $block).unwrap(); + $code - $compiler.frames.pop().unwrap(); + let frame = $compiler.frames.pop().unwrap(); + for var in frame.stack.iter() { + if !(var.read || var.upvalue) { + error!($compiler, format!("Unused variable {}", var.name)); + } + $compiler.panic = false; + } // The 0th slot is the return value, which is passed out // from functions, and should not be popped. 0 @@ -814,6 +830,7 @@ impl Compiler { // Variables if let Some(var) = self.find_variable(&name) { + self.frame_mut().mark_read(&var); if var.upvalue { add_op(self, block, Op::ReadUpvalue(var.slot)); } else { @@ -867,6 +884,7 @@ impl Compiler { active: false, upvalue: false, mutable: true, + read: false, }); Ok(slot) } @@ -892,6 +910,7 @@ impl Compiler { active: false, upvalue: false, mutable: false, + read: false, }); Ok(slot) } @@ -1387,12 +1406,14 @@ impl Compiler { captured: false, upvalue: false, mutable: true, + read: false, }); let mut block = Block::new(name, file, 0); while self.peek() != Token::EOF { self.statement(&mut block); - expect!(self, Token::Newline | Token::EOF, "Expect newline or EOF after expression."); + expect!(self, Token::Newline | Token::EOF, + "Expect newline or EOF after expression."); } add_op(self, &mut block, Op::Constant(self.nil_value())); add_op(self, &mut block, Op::Return); @@ -1410,6 +1431,13 @@ impl Compiler { } } + for var in self.frames.pop().unwrap().stack.iter().skip(1) { + if !(var.read || var.upvalue) { + error!(self, format!("Unused variable {}", var.name)); + } + self.panic = false; + } + self.blocks.insert(0, Rc::new(RefCell::new(block))); if self.errors.is_empty() { -- cgit v1.2.1 From f30ddabe055525783c8513fd24010e314ff7d4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 16 Feb 2021 23:02:19 +0100 Subject: simplify variables --- src/compiler.rs | 119 ++++++++++++++++++++++++++------------------------------ 1 file changed, 55 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index 3d95e2f..bd2638e 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -119,6 +119,7 @@ struct Variable { typ: Type, scope: usize, slot: usize, + line: usize, outer_slot: usize, outer_upvalue: bool, @@ -130,6 +131,27 @@ struct Variable { read: bool, } +impl Variable { + fn new(name: &str, mutable: bool, typ: Type) -> Self { + Self { + name: String::from(name), + typ, + scope: 0, + slot: 0, + line: 0, + + outer_slot: 0, + outer_upvalue: false, + + active: false, + upvalue: false, + captured: false, + mutable, + read: false, + } + } +} + enum LoopOp { Continue, Break, @@ -251,14 +273,16 @@ macro_rules! push_frame { $compiler.frames.push(Frame::new()); // Return value stored as a variable - $compiler.define_variable("", Type::Unknown, &mut $block).unwrap(); + let var = Variable::new("", true, Type::Unknown); + $compiler.define(var).unwrap(); $code let frame = $compiler.frames.pop().unwrap(); - for var in frame.stack.iter() { + // 0-th slot is the function itself. + for var in frame.stack.iter().skip(1) { if !(var.read || var.upvalue) { - error!($compiler, format!("Unused variable {}", var.name)); + error!($compiler, format!("Unused variable '{}'", var.name)); } $compiler.panic = false; } @@ -278,13 +302,21 @@ macro_rules! push_scope { $compiler.frame_mut().scope -= 1; - for var in $compiler.frame().stack[ss..$compiler.stack().len()].iter().rev() { + let mut errors = Vec::new(); + for var in $compiler.frame().stack.iter().skip(ss).rev() { + if !(var.read || var.upvalue) { + errors.push(format!("Unused variable '{}'", var.name)) + } if var.captured { add_op($compiler, $block, Op::PopUpvalue); } else { add_op($compiler, $block, Op::Pop); } } + + for err in errors.iter() { + error!($compiler, err); + } $compiler.stack_mut().truncate(ss); }; } @@ -749,7 +781,8 @@ impl Compiler { expect!(self, Token::Colon, "Expected ':' after parameter name."); if let Ok(typ) = self.parse_type() { args.push(typ.clone()); - if let Ok(slot) = self.define_variable(&name, typ, &mut function_block) { + let var = Variable::new(&name, true, typ); + if let Ok(slot) = self.define(var) { self.stack_mut()[slot].active = true; } } else { @@ -863,60 +896,26 @@ impl Compiler { parse_branch!(self, block, self.call(block)); } - fn define_variable(&mut self, name: &str, typ: Type, _block: &mut Block) -> Result { - if let Some(var) = self.find_variable(&name) { + fn define(&mut self, mut var: Variable) -> Result { + if let Some(var) = self.find_variable(&var.name) { if var.scope == self.frame().scope { - error!(self, format!("Multiple definitions of {} in this block.", name)); + error!(self, format!("Multiple definitions of '{}' in this block.", + var.name)); return Err(()); } } let slot = self.stack().len(); - let scope = self.frame().scope; - self.stack_mut().push(Variable { - name: String::from(name), - captured: false, - outer_upvalue: false, - outer_slot: 0, - slot, - typ, - scope, - active: false, - upvalue: false, - mutable: true, - read: false, - }); - Ok(slot) - } - - fn define_constant(&mut self, name: &str, typ: Type, _block: &mut Block) -> Result { - if let Some(var) = self.find_variable(&name) { - if var.scope == self.frame().scope { - error!(self, format!("Multiple definitions of {} in this block.", name)); - return Err(()); - } - } - - let slot = self.stack().len(); - let scope = self.frame().scope; - self.stack_mut().push(Variable { - name: String::from(name), - captured: false, - outer_upvalue: false, - outer_slot: 0, - slot, - typ, - scope, - active: false, - upvalue: false, - mutable: false, - read: false, - }); + var.slot = slot; + var.scope = self.frame().scope; + var.line = self.line(); + self.stack_mut().push(var); Ok(slot) } fn definition_statement(&mut self, name: &str, typ: Type, block: &mut Block) { - let slot = self.define_variable(name, typ.clone(), block); + let mut var = Variable::new(name, true, typ.clone()); + let slot = self.define(var); self.expression(block); let constant = self.add_constant(Value::Ty(typ)); add_op(self, block, Op::Define(constant)); @@ -944,7 +943,8 @@ impl Compiler { } } - let slot = self.define_constant(name, typ.clone(), block); + let var = Variable::new(name, false, typ); + let slot = self.define(var); self.expression(block); if let Ok(slot) = slot { @@ -1395,19 +1395,8 @@ impl Compiler { .enumerate() .map(|(i, (s, f))| (s, (i, f))) .collect(); - self.stack_mut().push(Variable { - name: String::from("/main/"), - typ: Type::Void, - outer_upvalue: false, - outer_slot: 0, - slot: 0, - scope: 0, - active: false, - captured: false, - upvalue: false, - mutable: true, - read: false, - }); + let mut main = Variable::new("/main/", false, Type::Void); + self.define(main); let mut block = Block::new(name, file, 0); while self.peek() != Token::EOF { @@ -1433,7 +1422,9 @@ impl Compiler { for var in self.frames.pop().unwrap().stack.iter().skip(1) { if !(var.read || var.upvalue) { - error!(self, format!("Unused variable {}", var.name)); + let e = ErrorKind::SyntaxError(var.line, Token::Identifier(var.name.clone())); + let m = format!("Unused value '{}'.", var.name); + self.error_on_line(e, var.line, Some(m)); } self.panic = false; } -- cgit v1.2.1 From 56991fc914623e4b82ec6d9ffd1bbad4fd4446bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 16 Feb 2021 23:03:38 +0100 Subject: fix compilation warnings --- src/compiler.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index bd2638e..c449ace 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -914,7 +914,7 @@ impl Compiler { } fn definition_statement(&mut self, name: &str, typ: Type, block: &mut Block) { - let mut var = Variable::new(name, true, typ.clone()); + let var = Variable::new(name, true, typ.clone()); let slot = self.define(var); self.expression(block); let constant = self.add_constant(Value::Ty(typ)); @@ -1395,8 +1395,8 @@ impl Compiler { .enumerate() .map(|(i, (s, f))| (s, (i, f))) .collect(); - let mut main = Variable::new("/main/", false, Type::Void); - self.define(main); + let main = Variable::new("/main/", false, Type::Void); + let _ = self.define(main); let mut block = Block::new(name, file, 0); while self.peek() != Token::EOF { -- cgit v1.2.1 From a668f98f725f27a9e9cdc40b75cc0f99b8f998a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 16 Feb 2021 23:44:57 +0100 Subject: find unused upvalues --- src/compiler.rs | 58 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index c449ace..6bb749c 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -113,7 +113,7 @@ nextable_enum!(Prec { Factor, }); -#[derive(Clone)] +#[derive(Clone, Debug)] struct Variable { name: String, typ: Type, @@ -226,14 +226,6 @@ impl Frame { None } - fn mark_read(&mut self, var: &Variable) { - (if var.upvalue { - &mut self.upvalues - } else { - &mut self.stack - })[var.slot].read = true; - } - fn add_upvalue(&mut self, variable: Variable) -> Variable { let new_variable = Variable { outer_upvalue: variable.upvalue, @@ -282,7 +274,15 @@ macro_rules! push_frame { // 0-th slot is the function itself. for var in frame.stack.iter().skip(1) { if !(var.read || var.upvalue) { - error!($compiler, format!("Unused variable '{}'", var.name)); + let e = ErrorKind::SyntaxError( + var.line, + Token::Identifier(var.name.clone() + )); + $compiler.error_on_line( + e, + var.line, + Some(format!("Usage of undefined value: '{}'.", var.name)) + ); } $compiler.panic = false; } @@ -305,7 +305,15 @@ macro_rules! push_scope { let mut errors = Vec::new(); for var in $compiler.frame().stack.iter().skip(ss).rev() { if !(var.read || var.upvalue) { - errors.push(format!("Unused variable '{}'", var.name)) + let e = ErrorKind::SyntaxError( + var.line, + Token::Identifier(var.name.clone() + )); + errors.push(( + e, + var.line, + format!("Usage of undefined value: '{}'.", var.name),) + ); } if var.captured { add_op($compiler, $block, Op::PopUpvalue); @@ -314,8 +322,9 @@ macro_rules! push_scope { } } - for err in errors.iter() { - error!($compiler, err); + for (e, l, m) in errors.iter() { + $compiler.error_on_line(e.clone(), *l, Some(m.clone())); + $compiler.panic = false; } $compiler.stack_mut().truncate(ss); }; @@ -385,6 +394,26 @@ impl Compiler { &mut self.frames[last] } + /// Marks a variable as read, also marks upvalues. + fn mark_read(&mut self, frame_id: usize, var: &Variable) { + // Early out + if var.read { + return; + } + + + if if let Some(up) = self.frames[frame_id].upvalues.get(var.slot) { + up.name == var.name + } else { false } { + let mut inner_var = self.frames[frame_id].upvalues[var.slot].clone(); + inner_var.slot = inner_var.outer_slot; + self.mark_read(frame_id - 1, &inner_var); + self.frames[frame_id].upvalues[var.slot].read = true; + } else { + self.frames[frame_id].stack[var.slot].read = true; + } + } + fn stack(&self) -> &[Variable] { &self.frame().stack.as_ref() } @@ -863,7 +892,7 @@ impl Compiler { // Variables if let Some(var) = self.find_variable(&name) { - self.frame_mut().mark_read(&var); + self.mark_read(self.frames.len() - 1, &var); if var.upvalue { add_op(self, block, Op::ReadUpvalue(var.slot)); } else { @@ -1221,6 +1250,7 @@ impl Compiler { _ => unreachable!(), }; if let Some(var) = self.find_variable(&name) { + self.mark_read(self.frames.len() - 1, &var); if var.upvalue { add_op(self, block, Op::ReadUpvalue(var.slot)); } else { -- cgit v1.2.1 From 742c16dccef17a57494fe5846818cfc3324bbd45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 16 Feb 2021 23:45:45 +0100 Subject: add tests for unusued variables --- src/lib.rs | 14 ++++++++++++++ src/vm.rs | 9 ++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 77176c4..7ef13f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -856,6 +856,20 @@ mod tests { assert_errs!(run_string("a :: B()\n", true, Vec::new()), [ErrorKind::SyntaxError(_, _)]); } + #[test] + fn unused_variable() { + assert_errs!(run_string("a := 1", true, Vec::new()), [ErrorKind::SyntaxError(1, _)]); + } + + #[test] + fn unused_upvalue() { + assert_errs!(run_string("a := 1\nf :: fn { a = 2 }\nf()", true, Vec::new()), [ErrorKind::SyntaxError(1, _)]); + } + + #[test] + fn unused_function() { + assert_errs!(run_string("a := 1\nf := fn { a }\n", true, Vec::new()), [ErrorKind::SyntaxError(2, _)]); + } macro_rules! test_multiple { ($mod:ident, $( $fn:ident : $prog:literal ),+ $( , )? ) => { diff --git a/src/vm.rs b/src/vm.rs index f5c1cf0..70cb5e4 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -831,15 +831,18 @@ mod tests { test_string!(uncallable_type, " f := fn i: int { i() - }", + } + f", [ErrorKind::TypeError(_, _)]); test_string!(wrong_params, " - f : fn -> int = fn a: int -> int {}", + f : fn -> int = fn a: int -> int {} + f", [ErrorKind::TypeError(_, _), ErrorKind::TypeError(_, _)]); test_string!(wrong_ret, " - f : fn -> int = fn {}", + f : fn -> int = fn {} + f", [ErrorKind::TypeError(_, _)]); } } -- cgit v1.2.1 From 6bb72c7bbe2bbb1b627809d4b52f44a77bdb4b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 16 Feb 2021 23:46:03 +0100 Subject: fix tests with unused variables --- src/lib.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 7ef13f9..52b0d9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -848,7 +848,7 @@ mod tests { #[test] fn assign_to_constant_upvalue() { - assert_errs!(run_string("a :: 2\nq :: fn { a = 2 }\n", true, Vec::new()), [ErrorKind::SyntaxError(_, _)]); + assert_errs!(run_string("a :: 2\nq :: fn { a = 2 }\nq()\na", true, Vec::new()), [ErrorKind::SyntaxError(_, _)]); } #[test] @@ -1043,7 +1043,8 @@ a() <=> 4 blob, simple: "blob A {}", instantiate: "blob A {} - a := A()", + a := A() + a", field: "blob A { a: int }", field_assign: "blob A { a: int } a := A() @@ -1065,6 +1066,7 @@ a() <=> 4 blob_infer: " blob A { } a : A = A() +a ", ); @@ -1072,8 +1074,8 @@ a : A = A() add: "(1, 2, 3, 4) + (4, 3, 2, 1) <=> (5, 5, 5, 5)", sub: "(1, -2, 3, -4) - (4, 3, -2, -1) <=> (-3, 1, 1, -5)", mul: "(0, 1, 2) * (2, 3, 4) <=> (0, 3, 8)", - types: "a: (int, float, int) = (1, 1., 1)", - more_types: "a: (str, bool, int) = (\"abc\", true, 1)", + types: "a: (int, float, int) = (1, 1., 1)\na", + more_types: "a: (str, bool, int) = (\"abc\", true, 1)\na", ); test_file!(scoping, "progs/tests/scoping.sy"); @@ -1178,6 +1180,7 @@ a <=> 1 b := 2 { a <=> 1 + b <=> 2 }", ); @@ -1188,6 +1191,7 @@ a := 0 b := 99999 a += 1 a <=> 1 +b <=> 99999 ", simple_sub: " @@ -1195,6 +1199,7 @@ a := 0 b := 99999 a -= 1 a <=> -1 +b <=> 99999 ", strange: " @@ -1214,6 +1219,7 @@ a <=> -1 declaration_order, blob_simple: " a := A() +a blob A { a: int @@ -1226,6 +1232,11 @@ b := B() c := C() b2 := B() +a +b +c +b2 + blob A { c: C } @@ -1237,6 +1248,7 @@ blob B { } blob A { } a : A = A() +a ", @@ -1299,4 +1311,5 @@ q <=> 3 ", ); + } -- cgit v1.2.1 From 07de510d83f62e5fa10f9b801fe4f0ed943f9469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Wed, 17 Feb 2021 21:01:00 +0100 Subject: remove unused field in block --- src/compiler.rs | 6 +++--- src/lib.rs | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index 94ae2aa..6607c7f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -719,10 +719,10 @@ impl Compiler { let mut args = Vec::new(); let mut return_type = Type::Void; - let mut function_block = Block::new(&name, &self.current_file, self.line()); + let mut function_block = Block::new(&name, &self.current_file); let block_id = self.blocks.len(); - let temp_block = Block::new(&name, &self.current_file, self.line()); + let temp_block = Block::new(&name, &self.current_file); self.blocks.push(Rc::new(RefCell::new(temp_block))); let _ret = push_frame!(self, function_block, { @@ -1389,7 +1389,7 @@ impl Compiler { mutable: true, }); - let mut block = Block::new(name, file, 0); + let mut block = Block::new(name, file); while self.peek() != Token::EOF { self.statement(&mut block); expect!(self, Token::Newline | Token::EOF, "Expect newline or EOF after expression."); diff --git a/src/lib.rs b/src/lib.rs index 77176c4..c940a88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -635,11 +635,10 @@ pub struct Block { ops: Vec, last_line_offset: usize, line_offsets: HashMap, - line: usize, } impl Block { - fn new(name: &str, file: &Path, line: usize) -> Self { + fn new(name: &str, file: &Path) -> Self { Self { ty: Type::Void, upvalues: Vec::new(), @@ -648,7 +647,6 @@ impl Block { ops: Vec::new(), last_line_offset: 0, line_offsets: HashMap::new(), - line, } } -- cgit v1.2.1 From b205748bde51c551468a8dc89123f85b67c660dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Wed, 17 Feb 2021 21:01:12 +0100 Subject: solve edge case for constants --- src/compiler.rs | 13 ++++++++++--- src/lib.rs | 24 +++++++++++++++++++++--- src/vm.rs | 19 +++++++++++++------ 3 files changed, 44 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index 6607c7f..c0dacec 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -914,12 +914,19 @@ impl Compiler { // Remove the function, since it's a constant and we already // added it. block.ops.pop().unwrap(); - if let Entry::Occupied(entry) = self.unknown.entry(String::from(name)) { + let slot = if let Entry::Occupied(entry) = self.unknown.entry(String::from(name)) { let (_, (slot, _)) = entry.remove_entry(); self.constants[slot] = self.constants.pop().unwrap(); - add_op(self, block, Op::Link(slot)); + slot } else { - add_op(self, block, Op::Link(self.constants.len() - 1)); + self.constants.len() - 1 + }; + add_op(self, block, Op::Link(slot)); + if let Value::Function(_, block) = &self.constants[slot] { + let needs_linking = block.borrow().upvalues.len() != 0; + block.borrow_mut().constant = needs_linking; + } else { + unreachable!(); } return; } diff --git a/src/lib.rs b/src/lib.rs index c940a88..1bdcc00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,7 +151,7 @@ impl From<&Type> for Value { Type::String => Value::String(Rc::new("".to_string())), Type::Function(_, _) => Value::Function( Vec::new(), - Rc::new(RefCell::new(Block::empty_with_type(ty)))), + Rc::new(RefCell::new(Block::stubbed_block(ty)))), } } } @@ -635,6 +635,8 @@ pub struct Block { ops: Vec, last_line_offset: usize, line_offsets: HashMap, + linked: bool, + constant: bool, } impl Block { @@ -647,12 +649,14 @@ impl Block { ops: Vec::new(), last_line_offset: 0, line_offsets: HashMap::new(), + linked: false, + constant: false, } } // Used to create empty functions. - fn empty_with_type(ty: &Type) -> Self { - let mut block = Block::new("/empty/", Path::new(""), 0); + fn stubbed_block(ty: &Type) -> Self { + let mut block = Block::new("/empty/", Path::new("")); block.ty = ty.clone(); block } @@ -854,6 +858,20 @@ mod tests { assert_errs!(run_string("a :: B()\n", true, Vec::new()), [ErrorKind::SyntaxError(_, _)]); } + #[test] + fn call_before_link() { + let prog = " +a := 1 +f() +c := 5 + +f :: fn { + c <=> 5 +} + "; + assert_errs!(run_string(prog, true, Vec::new()), [ErrorKind::InvalidProgram, ErrorKind::RuntimeTypeError(_, _)]); + } + macro_rules! test_multiple { ($mod:ident, $( $fn:ident : $prog:literal ),+ $( , )? ) => { diff --git a/src/vm.rs b/src/vm.rs index f5c1cf0..20125be 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -557,6 +557,13 @@ impl VM { Value::Function(_, block) => { self.push(Value::Function(Vec::new(), block.clone())); + if block.borrow().constant && !block.borrow().linked { + error!(self, + ErrorKind::InvalidProgram, + format!("Calling function '{}' before all captured variables are declared.", + block.borrow().name)); + } + let mut types = Vec::new(); for (slot, is_up, ty) in block.borrow().upvalues.iter() { if *is_up { @@ -667,10 +674,10 @@ impl VM { } Op::Link(slot) => { - println!("{:?}", self.constants); - println!("{:?} - {}", self.constant(slot), slot); match self.constant(slot).clone() { - Value::Function(_, _) => {} + Value::Function(_, block) => { + block.borrow_mut().linked = true; + } value => { error!(self, ErrorKind::TypeError(op, vec![Type::from(&value)]), @@ -733,7 +740,7 @@ impl VM { } _ => { error!(self, - ErrorKind::TypeError(op, vec![Type::from(&self.stack[new_base])]), + ErrorKind::InvalidProgram, format!("Tried to call non-function {:?}", self.stack[new_base])); } } @@ -773,7 +780,7 @@ impl VM { }); if self.print_blocks { - println!("\n [[{}]]\n", "TYPECHECK".purple()); + println!("\n [[{} - {}]]\n", "TYPECHECKING".purple(), self.frame().block.borrow().name); self.frame().block.borrow().debug_print(); } @@ -832,7 +839,7 @@ mod tests { f := fn i: int { i() }", - [ErrorKind::TypeError(_, _)]); + [ErrorKind::InvalidProgram]); test_string!(wrong_params, " f : fn -> int = fn a: int -> int {}", -- cgit v1.2.1 From 090dd8c52e4ae60742fe8bad7b74e18bb808ba0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Wed, 17 Feb 2021 21:15:54 +0100 Subject: use enums instead of 2 bools --- src/compiler.rs | 3 +-- src/lib.rs | 29 +++++++++++++++++++++++++---- src/vm.rs | 4 ++-- 3 files changed, 28 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index c0dacec..c70640d 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -923,8 +923,7 @@ impl Compiler { }; add_op(self, block, Op::Link(slot)); if let Value::Function(_, block) = &self.constants[slot] { - let needs_linking = block.borrow().upvalues.len() != 0; - block.borrow_mut().constant = needs_linking; + block.borrow_mut().mark_constant(); } else { unreachable!(); } diff --git a/src/lib.rs b/src/lib.rs index 1bdcc00..42a74a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -625,18 +625,24 @@ mod op { } } +#[derive(Debug)] +enum BlockLinkState { + Linked, + Unlinked, + Nothing, +} + #[derive(Debug)] pub struct Block { pub ty: Type, upvalues: Vec<(usize, bool, Type)>, + linking: BlockLinkState, pub name: String, pub file: PathBuf, ops: Vec, last_line_offset: usize, line_offsets: HashMap, - linked: bool, - constant: bool, } impl Block { @@ -644,16 +650,31 @@ impl Block { Self { ty: Type::Void, upvalues: Vec::new(), + linking: BlockLinkState::Nothing, + name: String::from(name), file: file.to_owned(), ops: Vec::new(), last_line_offset: 0, line_offsets: HashMap::new(), - linked: false, - constant: false, } } + fn mark_constant(&mut self) { + if self.upvalues.is_empty() { + return; + } + self.linking = BlockLinkState::Unlinked; + } + + fn link(&mut self) { + self.linking = BlockLinkState::Linked; + } + + fn needs_linking(&self) -> bool { + matches!(self.linking, BlockLinkState::Unlinked) + } + // Used to create empty functions. fn stubbed_block(ty: &Type) -> Self { let mut block = Block::new("/empty/", Path::new("")); diff --git a/src/vm.rs b/src/vm.rs index 20125be..5e7810d 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -557,7 +557,7 @@ impl VM { Value::Function(_, block) => { self.push(Value::Function(Vec::new(), block.clone())); - if block.borrow().constant && !block.borrow().linked { + if block.borrow().needs_linking() { error!(self, ErrorKind::InvalidProgram, format!("Calling function '{}' before all captured variables are declared.", @@ -676,7 +676,7 @@ impl VM { Op::Link(slot) => { match self.constant(slot).clone() { Value::Function(_, block) => { - block.borrow_mut().linked = true; + block.borrow_mut().link(); } value => { error!(self, -- cgit v1.2.1 From 31d68bc738c2973c6f018e38a0fb89f6e233f444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Thu, 18 Feb 2021 19:55:03 +0100 Subject: fix grammer in commment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gustav Sörnäs --- src/compiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index 7b9fd9a..a3066b0 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -394,7 +394,7 @@ impl Compiler { &mut self.frames[last] } - /// Marks a variable as read, also marks upvalues. + /// Marks a variable as read. Also marks upvalues. fn mark_read(&mut self, frame_id: usize, var: &Variable) { // Early out if var.read { -- cgit v1.2.1 From f024e88de53c24fd5e5e2fb2d66947dc93262af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Fri, 19 Feb 2021 17:44:39 +0100 Subject: fix incorrect error message --- src/compiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/compiler.rs b/src/compiler.rs index a3066b0..cd4ffda 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -281,7 +281,7 @@ macro_rules! push_frame { $compiler.error_on_line( e, var.line, - Some(format!("Usage of undefined value: '{}'.", var.name)) + Some(format!("Unused value '{}'.", var.name)) ); } $compiler.panic = false; -- cgit v1.2.1 From 9c8089bc3ec2a8e810682e8d1a1cab30096a157b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Fri, 19 Feb 2021 18:18:25 +0100 Subject: fix expected errors in tests --- src/lib.rs | 2 +- src/vm.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index e8b6c54..82e3ba6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -908,7 +908,7 @@ f :: fn { } a "; - assert_errs!(run_string(prog, true, Vec::new()), [ErrorKind::InvalidProgram, ErrorKind::RuntimeTypeError(_, _)]); + assert_errs!(run_string(prog, true, Vec::new()), [ErrorKind::InvalidProgram, ErrorKind::TypeError(_, _)]); } #[test] diff --git a/src/vm.rs b/src/vm.rs index a637e90..2d45c30 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -836,7 +836,7 @@ mod tests { i() } f", - [ErrorKind::ValueError(Op::Call(0), _)]); + [ErrorKind::InvalidProgram]); test_string!(invalid_assign, "a := 1\na = 0.1\na", [ErrorKind::TypeMismatch(Type::Int, Type::Float)]); -- cgit v1.2.1 From 930401a3ee2e45e449b1927cabee32c2c7bb705e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Fri, 19 Feb 2021 18:21:33 +0100 Subject: remove unused import --- src/vm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/vm.rs b/src/vm.rs index 2d45c30..f5e5c58 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -829,7 +829,7 @@ impl VM { mod tests { mod typing { use crate::error::ErrorKind; - use crate::{test_string, Op, Type}; + use crate::{test_string, Type}; test_string!(uncallable_type, " f := fn i: int { -- cgit v1.2.1