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/compiler.rs') 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/compiler.rs') 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/compiler.rs') 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/compiler.rs') 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 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 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/compiler.rs') 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."); -- 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 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src/compiler.rs') 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; } -- 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 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/compiler.rs') 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!(); } -- 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/compiler.rs') 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/compiler.rs') 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