From 220185f83544720967c6172075e1645e104bed7c Mon Sep 17 00:00:00 2001 From: Christoph Burgdorf Date: Mon, 14 Dec 2020 19:10:30 +0100 Subject: [PATCH] Ensure integer type constructor reject every expression that isn't a numeric literal Related to #145 --- compiler/tests/compile_errors.rs | 3 ++- .../type_constructor_from_variable.fe | 4 ++++ newsfragments/163.feature.md | 9 +++++++++ semantics/src/errors.rs | 9 +++++++++ semantics/src/traversal/expressions.rs | 18 ++++++++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 compiler/tests/fixtures/compile_errors/type_constructor_from_variable.fe create mode 100644 newsfragments/163.feature.md diff --git a/compiler/tests/compile_errors.rs b/compiler/tests/compile_errors.rs index ef4d266839..1d5d3ad739 100644 --- a/compiler/tests/compile_errors.rs +++ b/compiler/tests/compile_errors.rs @@ -24,7 +24,8 @@ use std::fs; case("return_addition_with_mixed_types.fe", "TypeError"), case("return_lt_mixed_types.fe", "TypeError"), case("indexed_event.fe", "MoreThanThreeIndexedParams"), - case("unary_minus_on_bool.fe", "TypeError") + case("unary_minus_on_bool.fe", "TypeError"), + case("type_constructor_from_variable.fe", "NumericLiteralExpected") )] fn test_compile_errors(fixture_file: &str, expected_error: &str) { let src = fs::read_to_string(format!("tests/fixtures/compile_errors/{}", fixture_file)) diff --git a/compiler/tests/fixtures/compile_errors/type_constructor_from_variable.fe b/compiler/tests/fixtures/compile_errors/type_constructor_from_variable.fe new file mode 100644 index 0000000000..8da8772578 --- /dev/null +++ b/compiler/tests/fixtures/compile_errors/type_constructor_from_variable.fe @@ -0,0 +1,4 @@ +contract Foo: + + pub def bar(val: u8) -> u16: + return u16(val) diff --git a/newsfragments/163.feature.md b/newsfragments/163.feature.md new file mode 100644 index 0000000000..43bf13c246 --- /dev/null +++ b/newsfragments/163.feature.md @@ -0,0 +1,9 @@ +Ensure integer type constructor reject all expressions that aren't a numeric literal. +For instance, previously the compiler would not reject the following code even though it could not be guaranteed that `val` would fit into an `u16`. + +``` +pub def bar(val: u8) -> u16: + return u16(val) +``` + +Now such code is rejected and integer type constructor do only work with numeric literals such as `1` or `-3`. \ No newline at end of file diff --git a/semantics/src/errors.rs b/semantics/src/errors.rs index 27b92e1938..22b32ff054 100644 --- a/semantics/src/errors.rs +++ b/semantics/src/errors.rs @@ -15,6 +15,7 @@ pub enum ErrorKind { TypeError, CannotMove, NotCallable, + NumericLiteralExpected, MoreThanThreeIndexedParams, } @@ -99,6 +100,14 @@ impl SemanticError { } } + /// Create a new error with kind `NumericLiteralExpected` + pub fn numeric_literal_expected() -> Self { + SemanticError { + kind: ErrorKind::NumericLiteralExpected, + context: vec![], + } + } + /// Create a new error with kind `MoreThanThreeIndexedParams` pub fn more_than_three_indexed_params() -> Self { SemanticError { diff --git a/semantics/src/traversal/expressions.rs b/semantics/src/traversal/expressions.rs index 5e46a30b31..389c23f801 100644 --- a/semantics/src/traversal/expressions.rs +++ b/semantics/src/traversal/expressions.rs @@ -352,6 +352,9 @@ fn expr_call( return Err(SemanticError::type_error()); } + // Ensure something like u8(x) fails, only literals allowed e.g u8(1) + validate_is_numeric_literal(&args.node[0].node)?; + context .borrow_mut() .add_call(exp, CallType::TypeConstructor); @@ -364,6 +367,21 @@ fn expr_call( unreachable!() } +fn validate_is_numeric_literal(call_arg: &fe::CallArg) -> Result<(), SemanticError> { + let is_numeric_literal = + if let fe::CallArg::Arg(fe::Expr::UnaryOperation { operand, op: _ }) = call_arg { + matches!((*operand).node, fe::Expr::Num(_)) + } else { + matches!(call_arg, fe::CallArg::Arg(fe::Expr::Num(_))) + }; + + if is_numeric_literal { + Ok(()) + } else { + Err(SemanticError::numeric_literal_expected()) + } +} + fn expr_call_self( scope: Shared, _context: Shared,