From cd46ed737fa4bde5232b771bbb3aeef0a7e4ad05 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 17 Aug 2020 14:25:03 -0400 Subject: [PATCH] Add more test cases fix discovered issues - Parse negative exponent reals - Produces errors on certain float literals, see rust-lang/rust#31407 - Switch order of checking for type references in init exprs - Report an error when using type references as initializer values in var and const decls - Don't use empty exprs when reporting the wrong number of init values - Report when a forward type is not resolved - Report errors on zero sized ranges in normal arrays - Report errros on non-index type range specifiers in arrays - Don't print out code unit --- src/compiler/frontend/parser/mod.rs | 4 + src/compiler/frontend/parser/stmt.rs | 2 +- src/compiler/frontend/scanner.rs | 28 +++ .../frontend/validator/expr_resolve.rs | 22 +- src/compiler/frontend/validator/mod.rs | 227 +++++++++++++++++- .../frontend/validator/stmt_resolve.rs | 30 ++- .../frontend/validator/type_resolve.rs | 43 ++-- src/lib.rs | 3 - 8 files changed, 324 insertions(+), 35 deletions(-) diff --git a/src/compiler/frontend/parser/mod.rs b/src/compiler/frontend/parser/mod.rs index 8d5c4be9..bf0076a6 100644 --- a/src/compiler/frontend/parser/mod.rs +++ b/src/compiler/frontend/parser/mod.rs @@ -1507,6 +1507,10 @@ type enumeration : enum (a, b, c, d, e, f) assert_eq!(parser.parse(), true); let mut parser = make_test_parser("const a := 1 or 1"); assert_eq!(parser.parse(), true); + let mut parser = make_test_parser("const a := 1 & 1"); + assert_eq!(parser.parse(), true); + let mut parser = make_test_parser("const a := 1 | 1"); + assert_eq!(parser.parse(), true); let mut parser = make_test_parser("const a := 1 xor 1"); assert_eq!(parser.parse(), true); let mut parser = make_test_parser("const a := 1 in 1"); diff --git a/src/compiler/frontend/parser/stmt.rs b/src/compiler/frontend/parser/stmt.rs index 2584bf3f..8cefc2b9 100644 --- a/src/compiler/frontend/parser/stmt.rs +++ b/src/compiler/frontend/parser/stmt.rs @@ -259,7 +259,7 @@ impl<'s> Parser<'s> { .types() .type_from_ref(&ident.type_spec) }) { - // Resolve forwards (otherwise `is_resolved` would be false) + // Resolve forwards (otherwise `is_resolved` would be true) // We known that the old ident is valid (from above condtion) let old_ident = old_ident.unwrap(); diff --git a/src/compiler/frontend/scanner.rs b/src/compiler/frontend/scanner.rs index 052c7b44..3ddd6ebb 100644 --- a/src/compiler/frontend/scanner.rs +++ b/src/compiler/frontend/scanner.rs @@ -473,6 +473,11 @@ impl<'s> Scanner<'s> { // Nom the 'e' self.next_char(); + if self.peek == '-' { + // Consume negative exponent sign + self.next_char(); + } + // Parse the exponent digits while matches!(self.peek, '0'..='9') { self.next_char(); @@ -1166,6 +1171,12 @@ mod test { // Should still produce a token assert_eq!(scanner.tokens[0].token_type, TokenType::NatLiteral(0)); + // Out of range (= overflow) + let mut scanner = Scanner::new("18446744073709551616#0000"); + assert!(!scanner.scan_tokens()); + // Should still produce a token + assert_eq!(scanner.tokens[0].token_type, TokenType::NatLiteral(0)); + // Invalid digit let mut scanner = Scanner::new("10#999a999"); assert!(!scanner.scan_tokens()); @@ -1199,6 +1210,13 @@ mod test { TokenType::RealLiteral(100.00e100) ); + let mut scanner = Scanner::new("100.00e-100"); + assert!(scanner.scan_tokens()); + assert_eq!( + scanner.tokens[0].token_type, + TokenType::RealLiteral(100.00e-100) + ); + let mut scanner = Scanner::new("1e100"); assert!(scanner.scan_tokens()); assert_eq!(scanner.tokens[0].token_type, TokenType::RealLiteral(1e100)); @@ -1209,6 +1227,16 @@ mod test { // Should still produce a value assert_eq!(scanner.tokens[0].token_type, TokenType::RealLiteral(0f64)); + let mut scanner = Scanner::new("1e-"); + assert!(!scanner.scan_tokens()); + // Should still produce a value + assert_eq!(scanner.tokens[0].token_type, TokenType::RealLiteral(0f64)); + + let mut scanner = Scanner::new("1e--2"); + assert!(!scanner.scan_tokens()); + // Should still produce a value + assert_eq!(scanner.tokens[0].token_type, TokenType::RealLiteral(0f64)); + // Too big let mut scanner = Scanner::new("1e600"); assert!(!scanner.scan_tokens()); diff --git a/src/compiler/frontend/validator/expr_resolve.rs b/src/compiler/frontend/validator/expr_resolve.rs index 4eea80e7..e455ec8d 100644 --- a/src/compiler/frontend/validator/expr_resolve.rs +++ b/src/compiler/frontend/validator/expr_resolve.rs @@ -23,15 +23,15 @@ impl Validator { expr.set_span(span); } - if !matches!(expr, Expr::Empty) && !expr.is_compile_eval() { + if super::is_type_reference(expr) { self.reporter.report_error( expr.get_span(), - format_args!("Expression is not a compile-time expression"), + format_args!("Reference does not refer to a variable or constant"), ); - } else if super::is_type_reference(expr) { + } else if !matches!(expr, Expr::Empty) && !expr.is_compile_eval() { self.reporter.report_error( expr.get_span(), - format_args!("Reference does not refer to a variable or constant"), + format_args!("Expression is not a compile-time expression"), ); } } @@ -323,13 +323,13 @@ impl Validator { *is_compile_eval = false; match op { - TokenType::Not => self.reporter.report_error(loc, format_args!("Operand of 'not' must be an integer (int or nat) or a boolean")), - TokenType::Plus => self.reporter.report_error(loc, format_args!("Operand of prefix '+' must be a scalar (int, real, or nat)")), - TokenType::Minus => self.reporter.report_error(loc, format_args!("Operand of unary negation must be a scalar (int, real, or nat)")), - TokenType::Caret => self.reporter.report_error(loc, format_args!("Operand of pointer dereference must be a pointer")), - TokenType::Pound => self.reporter.report_error(loc, format_args!("Operand of nat cheat must be a literal, or a reference to a variable or constant")), - _ => unreachable!() - } + TokenType::Not => self.reporter.report_error(loc, format_args!("Operand of 'not' must be an integer (int or nat) or a boolean")), + TokenType::Plus => self.reporter.report_error(loc, format_args!("Operand of prefix '+' must be a scalar (int, real, or nat)")), + TokenType::Minus => self.reporter.report_error(loc, format_args!("Operand of unary negation must be a scalar (int, real, or nat)")), + TokenType::Caret => self.reporter.report_error(loc, format_args!("Operand of pointer dereference must be a pointer")), + TokenType::Pound => self.reporter.report_error(loc, format_args!("Operand of nat cheat must be a literal, or a reference to a variable or constant")), + _ => unreachable!() + } // Produce no value return None; diff --git a/src/compiler/frontend/validator/mod.rs b/src/compiler/frontend/validator/mod.rs index 8f08f9ba..dfd57601 100644 --- a/src/compiler/frontend/validator/mod.rs +++ b/src/compiler/frontend/validator/mod.rs @@ -519,6 +519,14 @@ mod test { false, run_validator("var c : string(2) := 'ce'\nvar a : char := c") ); + assert_eq!( + false, + run_validator("var c : char(2) := 'ce'\nvar a : char\na := c") + ); + assert_eq!( + false, + run_validator("var c : string(2) := 'ce'\nvar a : char\na := c") + ); // Compatibility with char into char(n) and string(n) assert_eq!( @@ -545,6 +553,9 @@ mod test { assert_eq!(true, run_validator("var a : char(6) := 'abcd'")); assert_eq!(true, run_validator("var a : char(6) := 'abcdaa'")); assert_eq!(false, run_validator("var a : char(6) := 'abcdaaa'")); + assert_eq!(true, run_validator("var a : char(6)\na := 'abcd'")); + assert_eq!(true, run_validator("var a : char(6)\na := 'abcdaa'")); + assert_eq!(false, run_validator("var a : char(6)\na := 'abcdaaa'")); // (In)compatibility between string(n) of same or different size assert_eq!( @@ -559,6 +570,18 @@ mod test { false, run_validator("var s : string(7) := 'abcdaaa'\nvar a : string(6) := s") ); + assert_eq!( + true, + run_validator("var s : string(4) := 'abcd' \nvar a : string(6)\na := s") + ); + assert_eq!( + true, + run_validator("var s : string(6) := 'abcdaa' \nvar a : string(6)\na := s") + ); + assert_eq!( + false, + run_validator("var s : string(7) := 'abcdaaa'\nvar a : string(6)\na := s") + ); // Compatibility between real and number types assert_eq!( @@ -577,6 +600,8 @@ mod test { // Incompatibility between real and integers assert_eq!(false, run_validator("var i : int := 1.0")); assert_eq!(false, run_validator("var i : nat := 1.0")); + assert_eq!(false, run_validator("var i : int\ni := 1.0")); + assert_eq!(false, run_validator("var i : nat\ni := 1.0")); // Incompatibility between numbers and strings assert_eq!(false, run_validator("var i : int := \"text\"")); @@ -588,11 +613,23 @@ mod test { assert_eq!(false, run_validator("var i : int := 't'")); assert_eq!(false, run_validator("var i : nat := 't'")); assert_eq!(false, run_validator("var i : real := 't'")); + assert_eq!(false, run_validator("var i : int \ni := \"text\"")); + assert_eq!(false, run_validator("var i : nat \ni := \"text\"")); + assert_eq!(false, run_validator("var i : real\ni := \"text\"")); + assert_eq!(false, run_validator("var i : int \ni := 'text'")); + assert_eq!(false, run_validator("var i : nat \ni := 'text'")); + assert_eq!(false, run_validator("var i : real\ni := 'text'")); + assert_eq!(false, run_validator("var i : int \ni := 't'")); + assert_eq!(false, run_validator("var i : nat \ni := 't'")); + assert_eq!(false, run_validator("var i : real\ni := 't'")); // Incompatibility between numbers and booleans assert_eq!(false, run_validator("var i : int := false")); assert_eq!(false, run_validator("var i : nat := false")); assert_eq!(false, run_validator("var i : real := false")); + assert_eq!(false, run_validator("var i : int \ni := false")); + assert_eq!(false, run_validator("var i : nat \ni := false")); + assert_eq!(false, run_validator("var i : real\ni := false")); // Compatibility between booleans assert_eq!(true, run_validator("var i : boolean := false")); @@ -603,6 +640,11 @@ mod test { assert_eq!(false, run_validator("var i : boolean := \"h\"")); assert_eq!(false, run_validator("var i : boolean := 'ha'")); assert_eq!(false, run_validator("var i : boolean := 'h'")); + assert_eq!(false, run_validator("var i : boolean\ni := 1")); + assert_eq!(false, run_validator("var i : boolean\ni := 1.0")); + assert_eq!(false, run_validator("var i : boolean\ni := \"h\"")); + assert_eq!(false, run_validator("var i : boolean\ni := 'ha'")); + assert_eq!(false, run_validator("var i : boolean\ni := 'h'")); // Compatibility with ranges assert_eq!(true, run_validator("var i : false .. true := true")); @@ -622,6 +664,7 @@ mod test { ); assert_eq!(true, run_validator("type e0 : enum(a, b, c, d)\ntype s0 : set of e0\ntype s1 : set of e0\nvar a : s0\nvar b : s1 := a")); + // Incompatibilty with sets of incompatible ranges assert_eq!( false, run_validator( @@ -682,6 +725,18 @@ mod test { false, run_validator("type e0 : enum(a, b, c)\ntype e1 : enum(a, b, c)\nvar a : e0 := e1.a") ); + assert_eq!( + false, + run_validator( + "type e0 : enum(a, b, c)\ntype e1 : enum(a, b, c)\nvar a : e0\nvar b : e1\nb := a" + ) + ); + assert_eq!( + false, + run_validator( + "type e0 : enum(a, b, c)\ntype e1 : enum(a, b, c)\nvar a : e0\na := e1.a" + ) + ); // Compatibility between enum and fields of same root type declaration assert_eq!( @@ -714,6 +769,10 @@ mod test { false, run_validator("type e0 : enum(a, b, c)\ntype e1 : enum(a, b, c)\nvar a : e1 := e0.a") ); + + // Type refs are never assignable or used as an assignment value + assert_eq!(false, run_validator("type a : int\na := 1")); + assert_eq!(false, run_validator("type a : int\nvar b : int := a")); } #[test] @@ -807,6 +866,16 @@ mod test { "type s : set of 1 .. 3\ntype t : set of boolean\nvar a : s\nvar c : t\nc += a" ) ); + + // Also check for invalid use of type refs in binary exprs + assert_eq!( + false, + run_validator("type a : int\nvar b : int\nb := a + b") + ); + assert_eq!( + false, + run_validator("type a : int\nvar b : int\nb := b + a") + ); } #[test] @@ -1351,7 +1420,23 @@ mod test { )) ); assert_eq!(true, run_validator(&format!("type s0 : set of 1 .. 3\ntype s1 : set of 1 .. 3\nvar a : s0\nvar b : s1\nvar c : boolean := a {} b", compare_op))); - // Missing: enum (direct field & behind const) & objectclass compares + // Missing: objectclass compares + + // Comparison operands are applicable (and foldable) to enum fields behind constants + assert_eq!( + true, + run_validator(&format!( + "type e0 : enum(a, b)\nconst a : e0 := e0.b\nvar c : boolean := a {} e0.a", + compare_op + )) + ); + assert_eq!( + true, + run_validator(&format!( + "type e0 : enum(a, b)\nconst a : e0 := e0.b\nvar c : boolean := e0.a {} a", + compare_op + )) + ); // Comparison operands must be the same type (class) // bool is whether to always reject @@ -1900,6 +1985,10 @@ mod test { true, run_validator("var a : function a() : int\nvar b : nat := #a") ); + assert_eq!( + true, + run_validator("var a : proc a(a : int, b, c : nat)\nvar b : nat := #a") + ); assert_eq!( true, run_validator("type e0 : enum (a)\nvar a : nat := #e0.a") @@ -1934,12 +2023,31 @@ mod test { false, run_validator("type e0 : enum (a, b, c)\nvar a := e0.d") ); + + // Reference is not a compound type + assert_eq!(false, run_validator("var a : int\na.b")); + assert_eq!(false, run_validator("var a : array 1 .. 2 of int\na.b")); + assert_eq!(false, run_validator("var a : ^int\na->b")); + + // Reference is behind a pointer (gives a special error message) + assert_eq!(false, run_validator("var a : ^int\na.b")); + + // TODO: Handle cases for union & Record fields + // Class, Module, and Monitor qualified exported types are checked in test_type_resolution } #[test] fn test_range_size_checking() { // Ranges in Turing are inclusive on both bounds assert_eq!(true, run_validator("var a : 1 .. 16")); + assert_eq!( + true, + run_validator( + "const s : int := 0 + const e : int := 10 + var a : s .. e" + ) + ); // 1 sized ranges are valid assert_eq!(true, run_validator("var a : 'a' .. 'a'")); @@ -1954,12 +2062,34 @@ mod test { true, run_validator("type e : enum(a, b)\nconst c : e := e.a\nvar a : c .. c") ); + assert_eq!( + true, + run_validator( + "const s : int := 0 + const e : int := 0 + var a : s .. e" + ) + ); // End range overflows constitute a valid range (but emits a warning) assert_eq!(true, run_validator("var a : -8000 .. 16#8000000000000000")); assert_eq!(true, run_validator("var a : -1 .. 16#ffffffffffffffff")); assert_eq!(true, run_validator("var a : 0 .. 16#ffffffffffffffff")); assert_eq!(true, run_validator("var a : 1 .. 16#ffffffffffffffff")); + assert_eq!(true, run_validator("var a : 0-2+2 .. 16#8000000000000000")); + assert_eq!(true, run_validator("var a : 1-2+2 .. 16#8000000000000000")); + assert_eq!( + true, + run_validator("const s : int := 1\nvar a : s .. 16#ffffffffffffffff") + ); + assert_eq!( + true, + run_validator("const s : int := 0\nvar a : s .. 16#fffffffffffffffe") + ); + assert_eq!( + true, + run_validator("const s : int := 0\nvar a : s .. 16#ffffffffffffffff") + ); assert_eq!( true, run_validator("var a : -16#7fffffffffffffff - 1 .. 16#ffffffffffffffff") @@ -1992,8 +2122,23 @@ mod test { run_validator("type e : enum(a, b)\nvar a : flexible array e.b .. e.a of int") ); assert_eq!(true, run_validator("type e : enum(a, b)\nconst c : e := e.b\nconst d : e := e.a\nvar a : flexible array c .. d of int")); + assert_eq!( + true, + run_validator( + "const s : int := 1 + const e : int := 0 + var a : flexible array s .. e of int" + ) + ); // 0 sized ranges aren't valid anywhere else + assert_eq!(false, run_validator("var a : array 1 .. 0 of int")); + assert_eq!( + false, + run_validator("var a : array 16#80000000 .. 16#7fffffff of int") + ); + assert_eq!(false, run_validator("var a : array true .. false of int")); + assert_eq!(false, run_validator("var a : array 'D' .. 'C' of int")); assert_eq!(false, run_validator("var a : 16#80000000 .. 16#7fffffff")); assert_eq!(false, run_validator("var a : true .. false")); assert_eq!(false, run_validator("var a : 'D' .. 'C'")); @@ -2013,6 +2158,14 @@ mod test { "type e : enum(a, b)\nconst c : e := e.b\nconst d : e := e.a\nvar a : c .. d" ) ); + assert_eq!( + false, + run_validator( + "const s : int := 1 + const e : int := 0 + var a : array s .. e of int" + ) + ); // 0 sized ranges can't hide behind aliases assert_eq!( @@ -2039,6 +2192,10 @@ mod test { false, run_validator("type a : 'D' .. 'C' \ntype b : set of a") ); + assert_eq!( + false, + run_validator("type a : -1+2 .. 1-1 \ntype b : set of a") + ); // Negative size ranges are invalid assert_eq!(false, run_validator("var a : 16#80000000 .. 16#7ffffffe")); @@ -2057,14 +2214,22 @@ mod test { "type e : enum(a, b, c)\nconst c : e := e.c\nconst d : e := e.a\nvar a : c .. d" ) ); + assert_eq!(false, run_validator("var a : -1+3 .. 0")); - // Rnage bounds can't be reals, or any other types + // Range bounds can't be reals, or any other types assert_eq!(false, run_validator("type e : 0.0 .. 0.0")); + assert_eq!(false, run_validator("type e : 0.0 + 0.0 .. 0.0 + 0.0")); + + // Range bounds must be compile-time expressions + assert_eq!(false, run_validator("var a : int\ntype e : set of a..0")); + assert_eq!(false, run_validator("var a : int\ntype e : set of 0..a")); + assert_eq!(false, run_validator("var a : int\ntype e : set of a..a")); // Still ba safe when handling empty expressions assert_eq!(false, run_validator("type e set of(0..")); assert_eq!(false, run_validator("type e..")); assert_eq!(false, run_validator("type e:..0*0")); + assert_eq!(false, run_validator("var a: array of int")); } #[test] @@ -2416,6 +2581,20 @@ const d := a + b + c % 4*4 + 1 + 1 + 1 TypeRef::Primitive(PrimitiveType::Char) ); + let (success, unit) = + make_validator("var depend := 65530\nconst a : string(1 + depend + 1) := 'a'"); + assert_eq!(false, success); + assert_eq!( + unit.root_block() + .borrow() + .scope + .get_ident("a") + .as_ref() + .unwrap() + .type_spec, + TypeRef::Primitive(PrimitiveType::String_) + ); + // Constant propogation should allow enum fields to be hidden behind constant vars assert_eq!( true, @@ -2722,6 +2901,11 @@ const d := a + b + c % 4*4 + 1 + 1 + 1 true, run_validator("type a : int\ntype b : a\nvar c : int := 1\nvar d : b := c") ); + assert_eq!(true, run_validator("type a : boolean\ntype b : set of a")); + assert_eq!(true, run_validator("type a : char\ntype b : set of a")); + assert_eq!(false, run_validator("type a : real\ntype b : set of a")); + assert_eq!(false, run_validator("type a : string\ntype b : set of a")); + // TODO: include cases of record and union // Aliases of resolved forward types are equivalent to their base types assert_eq!( @@ -2747,6 +2931,9 @@ const d := a + b + c % 4*4 + 1 + 1 + 1 run_validator("type a : forward\ntype k : set of a\ntype a : int") ); + // Forward refs must be resolved in the current unit + assert_eq!(false, run_validator("type a : forward")); + // Range bounds types do not match assert_eq!(false, run_validator("type a : true .. 'c'")); assert_eq!(false, run_validator("type a : 1 .. 'c'")); @@ -2889,6 +3076,11 @@ const d := a + b + c % 4*4 + 1 + 1 + 1 run_validator("var c := 5\nvar a : array 1 .. * of int := init(1, c, 3)") ); + assert_eq!( + false, + run_validator("type c : int\nvar a : array 1 .. * of int := init(1, c, 3)") + ); + // Should be as many elements as specified by the array ranges assert_eq!( true, @@ -2905,6 +3097,18 @@ const d := a + b + c % 4*4 + 1 + 1 + 1 run_validator("var a : array 1 .. 3 of int := init(1, 2)") ); + // Should not panic + assert_eq!( + false, + run_validator("var a : array 1 .. 3 of int := init(1, 2, 3, or)") + ); + + // Reported by parser + assert_eq!( + false, + run_validator("var a : array 1 .. 3 of int := init()") + ); + // Should also apply to other types assert_eq!( true, @@ -2977,6 +3181,25 @@ const d := a + b + c % 4*4 + 1 + 1 + 1 ); } + #[test] + fn test_resolve_var_decl() { + assert_eq!(true, run_validator("var a : 1 .. 3 := 2")); + assert_eq!(true, run_validator("const a : 1 .. 3 := 2")); + + // Init-sized type specs are not allowed in const/var decls + assert_eq!(false, run_validator("var a : 1 .. *")); + + assert_eq!(false, run_validator("const a : 1 .. *")); + } + + #[test] + fn test_resolve_type_decl() { + assert_eq!(true, run_validator("type a : 1 .. 3")); + + // Should report redecl errors + assert_eq!(false, run_validator("var a : int\ntype a : 1 .. 3")); + } + #[test] fn test_resolve_block_stmt() { // Local scope identifiers don't leak out of scope boundaries diff --git a/src/compiler/frontend/validator/stmt_resolve.rs b/src/compiler/frontend/validator/stmt_resolve.rs index cf62fe74..bc1fd375 100644 --- a/src/compiler/frontend/validator/stmt_resolve.rs +++ b/src/compiler/frontend/validator/stmt_resolve.rs @@ -47,6 +47,14 @@ impl Validator { } is_compile_eval = expr.is_compile_eval(); + + if super::is_type_reference(expr) { + self.reporter.report_error( + expr.get_span(), + format_args!("A type reference cannot be used as an initializer value"), + ); + *value = None; + } } // Resolve the identifier type spec or sized char sequence type spec, if possible @@ -229,13 +237,23 @@ impl Validator { if !has_fields_remaining && next_init.is_some() { // Too many init fields + let (next_expr, _) = next_init.unwrap(); + + let report_at = if !matches!(next_expr, Expr::Empty) { + next_expr.get_span() + } else { + // If empty, at init + // No other close location to report at + init + }; + self.reporter.report_error( - next_init.unwrap().0.get_span(), + report_at, format_args!("Too many initializer values"), ); } else if has_fields_remaining && next_init.is_none() { // Too few init - let report_at = if !exprs.is_empty() { + let report_at = if !matches!(exprs.last(), Some(Expr::Empty)) { exprs.last().unwrap().get_span() } else { // If empty, at init @@ -320,6 +338,14 @@ impl Validator { // Resolve the associated type (do not allow forward references) ident.type_spec = self.resolve_type(ident.type_spec, ResolveContext::CompileTime(false)); + } else if let Some(Type::Forward { is_resolved: false }) = + self.type_table.type_from_ref(&ident.type_spec) + { + // Not resolved in the current unit + self.reporter.report_error( + &ident.token.location, + format_args!("'{}' is not resolved in the current unit", ident.name), + ); } // Declare the identifier and check for redeclaration errors diff --git a/src/compiler/frontend/validator/type_resolve.rs b/src/compiler/frontend/validator/type_resolve.rs index ac3d5e76..6fb624ef 100644 --- a/src/compiler/frontend/validator/type_resolve.rs +++ b/src/compiler/frontend/validator/type_resolve.rs @@ -244,13 +244,9 @@ impl Validator { // Not required to be compile-time, unless we are in a compile-time context *range = self.resolve_type(*range, resolving_context); - if matches!(resolving_context, ResolveContext::CompileTime(_)) - && !is_flexible - && !is_init_sized - { + if !is_flexible && !is_init_sized { // If the following holds true // - The index type is a range, - // - We are in a compile-time context, // - This is an explict sized array (i.e. not `flexible` nor `init` sized) // Check if it is a not a zero sized range if let Some(Type::Range { @@ -258,9 +254,11 @@ impl Validator { }) = self.type_table.type_from_ref(&range) { // Not being `flexible` nor `init`-sized guarrantees that end is a `Some` - if *size == Some(0) { + let end = end.as_ref().unwrap(); + + if end.is_compile_eval() && *size == Some(0) { // Zero sized ranges aren't allowed in compile-time array types - let range_span = start.get_span().span_to(end.as_ref().unwrap().get_span()); + let range_span = start.get_span().span_to(end.get_span()); self.reporter.report_error( &range_span, format_args!("Range bounds creates a zero-sized range"), @@ -587,9 +585,22 @@ impl Validator { *index = TypeRef::TypeError; } } - } else if types::is_primitive(&index) && types::is_index_type(&index, &self.type_table) { - // Is a primitive, either a 'char' or 'boolean' - // Keep as is + } else if types::is_primitive(&index) { + if !types::is_index_type(&index, &self.type_table) { + // Is a primitive, but neither a 'char' nor a 'boolean' + *index = TypeRef::TypeError; + + // Report the error based on the reference location + // If not a reference, already reported by the parser + if let Some(Type::Reference { expr }) = + self.type_table.type_from_ref(&old_index_ref) + { + self.reporter.report_error( + expr.get_span(), + format_args!("Set index is not a range, char, boolean, or enumerated type"), + ); + } + } } else { // Ensure that the range is really a type error // Don't need to report, as it is covered by a previous error @@ -729,17 +740,17 @@ pub(super) fn get_range_size( // Overflow is not ok end_value.overflowing_sub(adjusted_start) } else { - // Is zero, keep the same size - (end_value, false) + // Is zero, range size is just end value plus 1 + end_value.overflowing_add(1) }; if overflow { - if start_value.is_negative() { - // Start value pushed range into overflow - Err(RangeSizeError::Overflow) - } else { + if start_value.is_positive() { // Start value pushed range into the negatives Err(RangeSizeError::NegativeSize) + } else { + // Start value pushed range into overflow + Err(RangeSizeError::Overflow) } } else { #[cfg(target_pointer_width = "32")] diff --git a/src/lib.rs b/src/lib.rs index dab30999..96100082 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,9 +52,6 @@ pub fn resolve_unit(mut code_unit: CodeUnit) { return; } - // Code Unit - println!("Parsed Code Unit:\n{:#?}\n", code_unit); - // Generate IR for the given unit /*let ir_builder = compiler::ir::IrBuilder::new(code_unit); let ir = ir_builder.generate_ir().unwrap();