aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdvard Thörnros <edvard.thornros@gmail.com>2021-02-18 19:51:30 +0100
committerEdvard Thörnros <edvard.thornros@gmail.com>2021-02-18 19:51:30 +0100
commit75b2027c7b545b9827607f17ea7444f67622999d (patch)
treed6cc2e077c96ce0cfb683c0e3e0c2ab3fe4b997c
parent090dd8c52e4ae60742fe8bad7b74e18bb808ba0d (diff)
parent6bb72c7bbe2bbb1b627809d4b52f44a77bdb4b33 (diff)
downloadsylt-75b2027c7b545b9827607f17ea7444f67622999d.tar.gz
Merge branch 'unusued-variables' into fix-constant-bug
-rw-r--r--src/compiler.rs171
-rw-r--r--src/lib.rs36
-rw-r--r--src/vm.rs9
3 files changed, 148 insertions, 68 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index c70640d..7b9fd9a 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -113,12 +113,13 @@ nextable_enum!(Prec {
Factor,
});
-#[derive(Clone)]
+#[derive(Clone, Debug)]
struct Variable {
name: String,
typ: Type,
scope: usize,
slot: usize,
+ line: usize,
outer_slot: usize,
outer_upvalue: bool,
@@ -127,6 +128,28 @@ struct Variable {
upvalue: bool,
captured: bool,
mutable: bool,
+ 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 {
@@ -242,10 +265,27 @@ 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
- $compiler.frames.pop().unwrap();
+ let frame = $compiler.frames.pop().unwrap();
+ // 0-th slot is the function itself.
+ for var in frame.stack.iter().skip(1) {
+ if !(var.read || var.upvalue) {
+ 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;
+ }
// The 0th slot is the return value, which is passed out
// from functions, and should not be popped.
0
@@ -262,13 +302,30 @@ 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) {
+ 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);
} else {
add_op($compiler, $block, Op::Pop);
}
}
+
+ 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);
};
}
@@ -337,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()
}
@@ -733,7 +810,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 {
@@ -814,6 +892,7 @@ impl Compiler {
// Variables
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 {
@@ -846,58 +925,26 @@ impl Compiler {
parse_branch!(self, block, self.call(block));
}
- fn define_variable(&mut self, name: &str, typ: Type, _block: &mut Block) -> Result<usize, ()> {
- 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: true,
- });
- Ok(slot)
- }
-
- fn define_constant(&mut self, name: &str, typ: Type, _block: &mut Block) -> Result<usize, ()> {
- if let Some(var) = self.find_variable(&name) {
+ fn define(&mut self, mut var: Variable) -> Result<usize, ()> {
+ 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: 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 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));
@@ -931,7 +978,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 {
@@ -1208,6 +1256,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 {
@@ -1382,23 +1431,14 @@ 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,
- });
+ let main = Variable::new("/main/", false, Type::Void);
+ let _ = self.define(main);
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.");
+ 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);
@@ -1416,6 +1456,15 @@ impl Compiler {
}
}
+ for var in self.frames.pop().unwrap().stack.iter().skip(1) {
+ if !(var.read || var.upvalue) {
+ 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;
+ }
+
self.blocks.insert(0, Rc::new(RefCell::new(block)));
if self.errors.is_empty() {
diff --git a/src/lib.rs b/src/lib.rs
index 42a74a0..40bae2b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -871,7 +871,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]
@@ -889,10 +889,25 @@ c := 5
f :: fn {
c <=> 5
}
+a
";
assert_errs!(run_string(prog, true, Vec::new()), [ErrorKind::InvalidProgram, ErrorKind::RuntimeTypeError(_, _)]);
}
+ #[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 ),+ $( , )? ) => {
@@ -1066,7 +1081,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()
@@ -1088,6 +1104,7 @@ a() <=> 4
blob_infer: "
blob A { }
a : A = A()
+a
",
);
@@ -1095,8 +1112,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");
@@ -1201,6 +1218,7 @@ a <=> 1
b := 2
{
a <=> 1
+ b <=> 2
}",
);
@@ -1211,6 +1229,7 @@ a := 0
b := 99999
a += 1
a <=> 1
+b <=> 99999
",
simple_sub: "
@@ -1218,6 +1237,7 @@ a := 0
b := 99999
a -= 1
a <=> -1
+b <=> 99999
",
strange: "
@@ -1237,6 +1257,7 @@ a <=> -1
declaration_order,
blob_simple: "
a := A()
+a
blob A {
a: int
@@ -1249,6 +1270,11 @@ b := B()
c := C()
b2 := B()
+a
+b
+c
+b2
+
blob A {
c: C
}
@@ -1260,6 +1286,7 @@ blob B { }
blob A { }
a : A = A()
+a
",
@@ -1322,4 +1349,5 @@ q <=> 3
",
);
+
}
diff --git a/src/vm.rs b/src/vm.rs
index 5e7810d..8d8ec23 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -838,15 +838,18 @@ mod tests {
test_string!(uncallable_type, "
f := fn i: int {
i()
- }",
+ }
+ f",
[ErrorKind::InvalidProgram]);
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(_, _)]);
}
}