From d00030128e465c8351412b1aefb4260681d8b497 Mon Sep 17 00:00:00 2001 From: jhahn Date: Wed, 9 Nov 2022 23:47:07 +0000 Subject: feat(tvix/eval): detect division by zero This detects if the second argument of a division is a zero (either as integer or as float). If so, an error message is displayed. This fixes b/219. Change-Id: I50203d14a71482bc757832a2c8dee08eb7d35c49 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7258 Tested-by: BuildkiteCI Reviewed-by: flokli --- tvix/eval/src/errors.rs | 6 ++++++ .../tests/nix_tests/eval-fail-division-by-zero-float.nix | 1 + .../tests/nix_tests/eval-fail-division-by-zero-int.nix | 1 + tvix/eval/src/vm.rs | 15 ++++++++++++++- 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tvix/eval/src/tests/nix_tests/eval-fail-division-by-zero-float.nix create mode 100644 tvix/eval/src/tests/nix_tests/eval-fail-division-by-zero-int.nix diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs index 8f782cb86151..ba9d6cb98ba6 100644 --- a/tvix/eval/src/errors.rs +++ b/tvix/eval/src/errors.rs @@ -20,6 +20,8 @@ pub enum ErrorKind { Abort(String), AssertionFailed, + DivisionByZero, + DuplicateAttrsKey { key: String, }, @@ -215,6 +217,8 @@ impl Display for Error { ErrorKind::Abort(msg) => write!(f, "evaluation aborted: {}", msg), ErrorKind::AssertionFailed => write!(f, "assertion failed"), + ErrorKind::DivisionByZero => write!(f, "division by zero"), + ErrorKind::DuplicateAttrsKey { key } => { write!(f, "attribute key '{}' already defined", key) } @@ -656,6 +660,7 @@ impl Error { | ErrorKind::TailEmptyList | ErrorKind::TypeError { .. } | ErrorKind::Incomparable { .. } + | ErrorKind::DivisionByZero | ErrorKind::DynamicKeyInScope(_) | ErrorKind::UnknownStaticVariable | ErrorKind::UnknownDynamicVariable(_) @@ -717,6 +722,7 @@ impl Error { ErrorKind::FromJsonError { .. } => "E030", ErrorKind::UnexpectedArgument { .. } => "E031", ErrorKind::RelativePathResolution(_) => "E032", + ErrorKind::DivisionByZero => "E033", // Special error code that is not part of the normal // ordering. diff --git a/tvix/eval/src/tests/nix_tests/eval-fail-division-by-zero-float.nix b/tvix/eval/src/tests/nix_tests/eval-fail-division-by-zero-float.nix new file mode 100644 index 000000000000..82dd6873218e --- /dev/null +++ b/tvix/eval/src/tests/nix_tests/eval-fail-division-by-zero-float.nix @@ -0,0 +1 @@ +1.0 / 0.0 diff --git a/tvix/eval/src/tests/nix_tests/eval-fail-division-by-zero-int.nix b/tvix/eval/src/tests/nix_tests/eval-fail-division-by-zero-int.nix new file mode 100644 index 000000000000..72dca4d5e478 --- /dev/null +++ b/tvix/eval/src/tests/nix_tests/eval-fail-division-by-zero-int.nix @@ -0,0 +1 @@ +1 / 0 diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index 8c45ee7363e1..127d9bfa513c 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -412,7 +412,20 @@ impl<'o> VM<'o> { OpCode::OpSub => arithmetic_op!(self, -), OpCode::OpMul => arithmetic_op!(self, *), - OpCode::OpDiv => arithmetic_op!(self, /), + OpCode::OpDiv => { + let b = self.peek(0); + + match b { + Value::Integer(0) => return Err(self.error(ErrorKind::DivisionByZero)), + Value::Float(b) => { + if *b == (0.0 as f64) { + return Err(self.error(ErrorKind::DivisionByZero)); + } + arithmetic_op!(self, /) + } + _ => arithmetic_op!(self, /), + }; + } OpCode::OpInvert => { let v = fallible!(self, self.pop().as_bool()); -- cgit 1.4.1