From 6fb552ea51ae104c18e4ba48479911487170b995 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 26 Nov 2016 15:27:37 +0100 Subject: [PATCH] fix: Don't panic when compiling partially compiled constructors Ideally partially applied constructors should be compiled into an automatically generated lambda but until then we should at least not panic when encountering one. Fixes #202 --- tests/vm.rs | 50 ++++++++++++++++++++++++++++++++++++---------- vm/src/compiler.rs | 17 +++++++++------- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/tests/vm.rs b/tests/vm.rs index 846c2867c5..4aff498329 100644 --- a/tests/vm.rs +++ b/tests/vm.rs @@ -717,6 +717,15 @@ pure 123 value.0); } +#[test] +fn dont_panic_on_partially_applied_constructor() { + let _ = ::env_logger::init(); + let vm = make_vm(); + + let result = Compiler::new().run_expr::>(&vm, "test", "Some"); + assert!(result.is_err()); +} + #[test] fn stacktrace() { use gluon::vm::stack::StacktraceFrame; @@ -740,17 +749,36 @@ g 10 let f = stacktrace.frames[1].as_ref().unwrap().name.clone(); let end = stacktrace.frames[6].as_ref().unwrap().name.clone(); assert_eq!(stacktrace.frames, - vec![ - // Removed due to being a tail call - // Some(StacktraceFrame { name: f.clone(), line: 9 }), - Some(StacktraceFrame { name: g.clone(), line: 7.into() }), - Some(StacktraceFrame { name: f.clone(), line: 6.into() }), - Some(StacktraceFrame { name: g.clone(), line: 7.into() }), - Some(StacktraceFrame { name: f.clone(), line: 6.into() }), - Some(StacktraceFrame { name: g.clone(), line: 7.into() }), - Some(StacktraceFrame { name: f.clone(), line: 4.into() }), - Some(StacktraceFrame { name: end.clone(), line: 1.into() }), - ]); + vec![// Removed due to being a tail call + // Some(StacktraceFrame { name: f.clone(), line: 9 }), + Some(StacktraceFrame { + name: g.clone(), + line: 7.into(), + }), + Some(StacktraceFrame { + name: f.clone(), + line: 6.into(), + }), + Some(StacktraceFrame { + name: g.clone(), + line: 7.into(), + }), + Some(StacktraceFrame { + name: f.clone(), + line: 6.into(), + }), + Some(StacktraceFrame { + name: g.clone(), + line: 7.into(), + }), + Some(StacktraceFrame { + name: f.clone(), + line: 4.into(), + }), + Some(StacktraceFrame { + name: end.clone(), + line: 1.into(), + })]); } Err(err) => panic!("Unexpected error `{}`", err), Ok(_) => panic!("Expected an error"), diff --git a/vm/src/compiler.rs b/vm/src/compiler.rs index a5e014466d..f69a71e544 100644 --- a/vm/src/compiler.rs +++ b/vm/src/compiler.rs @@ -13,7 +13,7 @@ use vm::GlobalVmState; use source_map::SourceMap; use self::Variable::*; -use Result; +use {Error, Result}; pub type CExpr = SpannedExpr; @@ -440,7 +440,7 @@ impl<'a> Compiler<'a> { Ok(function) } - fn load_identifier(&self, id: &Symbol, function: &mut FunctionEnvs) { + fn load_identifier(&self, id: &Symbol, function: &mut FunctionEnvs) -> Result<()> { match self.find(id, function) .unwrap_or_else(|| panic!("Undefined variable {}", self.symbols.string(&id))) { Stack(index) => function.emit(Push(index)), @@ -453,8 +453,11 @@ impl<'a> Compiler<'a> { args: 0, }) } - Constructor(..) => panic!("Constructor {:?} is not fully applied", id), + Constructor(..) => { + return Err(Error::Message(format!("Constructor `{}` is not fully applied", id))) + } } + Ok(()) } fn compile(&mut self, @@ -504,7 +507,7 @@ impl<'a> Compiler<'a> { Literal::Char(c) => function.emit(PushInt(c as isize)), } } - Expr::Ident(ref id) => self.load_identifier(&id.name, function), + Expr::Ident(ref id) => self.load_identifier(&id.name, function)?, Expr::IfElse(ref pred, ref if_true, ref if_false) => { self.compile(&**pred, function, false)?; let jump_index = function.function.instructions.len(); @@ -570,7 +573,7 @@ impl<'a> Compiler<'a> { "#Float<" => FloatLT, "#Float==" => FloatEQ, _ => { - self.load_identifier(&op.name, function); + self.load_identifier(&op.name, function)?; Call(2) } }; @@ -689,7 +692,7 @@ impl<'a> Compiler<'a> { // Create a catch all to prevent us from running into undefined behaviour if !catch_all { let error_fn = self.symbols.symbol("#error"); - self.load_identifier(&error_fn, function); + self.load_identifier(&error_fn, function)?; function.emit_string(self.intern("Non-exhaustive pattern")?); function.emit(Call(1)); // The stack has been increased by 1 here but it should not affect compiling the @@ -759,7 +762,7 @@ impl<'a> Compiler<'a> { for field in fields { match field.1 { Some(ref field_expr) => self.compile(field_expr, function, false)?, - None => self.load_identifier(&field.0, function), + None => self.load_identifier(&field.0, function)?, } } let index =