From e595c14fae0b8de310625859c55cb33c1c622471 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 6 Jul 2020 20:38:10 +0200 Subject: [PATCH] wip --- compiler/can/src/expr.rs | 7 +- compiler/can/src/num.rs | 41 ++++-- compiler/can/src/pattern.rs | 2 +- compiler/problem/src/can.rs | 5 +- compiler/reporting/src/error/canonicalize.rs | 71 +++++----- compiler/reporting/tests/test_reporting.rs | 141 +++++++++++++++++++ 6 files changed, 213 insertions(+), 54 deletions(-) diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index ad62f52ee5..8bd58dbdbb 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -178,12 +178,13 @@ pub fn canonicalize_expr<'a>( let (expr, output) = match expr { ast::Expr::Num(string) => { - let answer = num_expr_from_result(var_store, finish_parsing_int(*string), env); + let answer = num_expr_from_result(var_store, finish_parsing_int(*string), region, env); (answer, Output::default()) } ast::Expr::Float(string) => { - let answer = float_expr_from_result(var_store, finish_parsing_float(string), env); + let answer = + float_expr_from_result(var_store, finish_parsing_float(string), region, env); (answer, Output::default()) } @@ -630,7 +631,7 @@ pub fn canonicalize_expr<'a>( result = result.map(i64::neg); } - let answer = int_expr_from_result(var_store, result, env); + let answer = int_expr_from_result(var_store, result, region, env); (answer, Output::default()) } diff --git a/compiler/can/src/num.rs b/compiler/can/src/num.rs index 7fa1528d13..585b1bcc64 100644 --- a/compiler/can/src/num.rs +++ b/compiler/can/src/num.rs @@ -3,22 +3,30 @@ use crate::expr::Expr; use roc_parse::ast::Base; use roc_problem::can::Problem; use roc_problem::can::RuntimeError::*; +use roc_region::all::Region; use roc_types::subs::VarStore; use std::i64; +use std::num::{ParseFloatError, ParseIntError}; + +// TODO distinguish number parsing failures +// +// We're waiting for rust here, see https://github.com/rust-lang/rust/issues/22639 +// There is a nightly API for exposing the parse error. #[inline(always)] pub fn num_expr_from_result( var_store: &mut VarStore, - result: Result, + result: Result, + region: Region, env: &mut Env, ) -> Expr { match result { Ok(int) => Expr::Num(var_store.fresh(), int), - Err(raw) => { + Err((raw, _error)) => { // (Num *) compiles to Int if it doesn't // get specialized to something else first, // so use int's overflow bounds here. - let runtime_error = IntOutsideRange(raw.into()); + let runtime_error = IntOutsideRange(raw.into(), region); env.problem(Problem::RuntimeError(runtime_error.clone())); @@ -30,14 +38,15 @@ pub fn num_expr_from_result( #[inline(always)] pub fn int_expr_from_result( var_store: &mut VarStore, - result: Result, + result: Result, + region: Region, env: &mut Env, ) -> Expr { // Int stores a variable to generate better error messages match result { Ok(int) => Expr::Int(var_store.fresh(), int), - Err(raw) => { - let runtime_error = IntOutsideRange(raw.into()); + Err((raw, _error)) => { + let runtime_error = IntOutsideRange(raw.into(), region); env.problem(Problem::RuntimeError(runtime_error.clone())); @@ -49,14 +58,15 @@ pub fn int_expr_from_result( #[inline(always)] pub fn float_expr_from_result( var_store: &mut VarStore, - result: Result, + result: Result, + region: Region, env: &mut Env, ) -> Expr { // Float stores a variable to generate better error messages match result { Ok(float) => Expr::Float(var_store.fresh(), float), - Err(raw) => { - let runtime_error = FloatOutsideRange(raw.into()); + Err((raw, _error)) => { + let runtime_error = FloatOutsideRange(raw.into(), region); env.problem(Problem::RuntimeError(runtime_error.clone())); @@ -66,13 +76,13 @@ pub fn float_expr_from_result( } #[inline(always)] -pub fn finish_parsing_int(raw: &str) -> Result { +pub fn finish_parsing_int(raw: &str) -> Result { // Ignore underscores. - raw.replace("_", "").parse::().map_err(|_| raw) + raw.replace("_", "").parse::().map_err(|e| (raw, e)) } #[inline(always)] -pub fn finish_parsing_base(raw: &str, base: Base) -> Result { +pub fn finish_parsing_base(raw: &str, base: Base) -> Result { let radix = match base { Base::Hex => 16, Base::Octal => 8, @@ -80,14 +90,15 @@ pub fn finish_parsing_base(raw: &str, base: Base) -> Result { }; // Ignore underscores. - i64::from_str_radix(raw.replace("_", "").as_str(), radix).map_err(|_| raw) + i64::from_str_radix(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e)) } #[inline(always)] -pub fn finish_parsing_float(raw: &str) -> Result { +pub fn finish_parsing_float(raw: &str) -> Result { // Ignore underscores. match raw.replace("_", "").parse::() { Ok(float) if float.is_finite() => Ok(float), - _ => Err(raw), + Ok(_float) => panic!("TODO handle infinite float literal"), + Err(e) => Err((raw, e)), } } diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index 97877b208b..0ceaca9503 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -96,7 +96,7 @@ pub fn canonicalize_pattern<'a>( use roc_parse::ast::Pattern::*; use PatternType::*; - let can_pattern = match pattern { + let can_pattern = match dbg!(pattern) { Identifier(name) => match scope.introduce( (*name).into(), &env.exposed_ident_ids, diff --git a/compiler/problem/src/can.rs b/compiler/problem/src/can.rs index 2ace5df59b..6351cb9dfb 100644 --- a/compiler/problem/src/can.rs +++ b/compiler/problem/src/can.rs @@ -72,7 +72,6 @@ pub enum RuntimeError { UnsupportedPattern(Region), // Example: when 1 is 1.X -> 32 MalformedPattern(MalformedPatternProblem, Region), - UnrecognizedFunctionName(Located), LookupNotInScope(Located, MutSet>), ValueNotExposed { module_name: InlinableString, @@ -87,8 +86,8 @@ pub enum RuntimeError { InvalidPrecedence(PrecedenceProblem, Region), MalformedIdentifier(Box, Region), MalformedClosure(Region), - FloatOutsideRange(Box), - IntOutsideRange(Box), + FloatOutsideRange(Box, Region), + IntOutsideRange(Box, Region), InvalidHex(std::num::ParseIntError, Box), InvalidOctal(std::num::ParseIntError, Box), InvalidBinary(std::num::ParseIntError, Box), diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index 90f0d1cc0f..db57d76264 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -372,39 +372,46 @@ fn pretty_runtime_error<'b>( hint, ]) } - - other => { - // // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments! - // UnsupportedPattern(Region), - // UnrecognizedFunctionName(Located), - // SymbolNotExposed { - // module_name: InlinableString, - // ident: InlinableString, - // region: Region, - // }, - // ModuleNotImported { - // module_name: InlinableString, - // ident: InlinableString, - // region: Region, - // }, - // InvalidPrecedence(PrecedenceProblem, Region), - // MalformedIdentifier(Box, Region), - // MalformedClosure(Region), - // FloatOutsideRange(Box), - // IntOutsideRange(Box), - // InvalidHex(std::num::ParseIntError, Box), - // InvalidOctal(std::num::ParseIntError, Box), - // InvalidBinary(std::num::ParseIntError, Box), - // QualifiedPatternIdent(InlinableString), - // CircularDef( - // Vec>, - // Vec<(Region /* pattern */, Region /* expr */)>, - // ), - // - // /// When the author specifies a type annotation but no implementation - // NoImplementation, - todo!("TODO implement run time error reporting for {:?}", other) + RuntimeError::UnsupportedPattern(_) => { + todo!("unsupported patterns are currently not parsed!") } + RuntimeError::ValueNotExposed { .. } => todo!("value not exposed"), + RuntimeError::ModuleNotImported { .. } => todo!("module not imported"), + RuntimeError::InvalidPrecedence(_, _) => { + // do nothing, reported with PrecedenceProblem + unreachable!() + } + RuntimeError::MalformedIdentifier(_, _) => { + todo!("malformed identifier, currently gives a parse error and thus is unreachable") + } + RuntimeError::MalformedClosure(_) => todo!(""), + RuntimeError::FloatOutsideRange(_raw_str, _region) => todo!(""), + RuntimeError::IntOutsideRange(raw_str, region) => { + let big_or_small = if raw_str.starts_with('-') { + "small" + } else { + "big" + }; + + let hint = alloc + .hint() + .append(alloc.reflow("Learn more about number literals at TODO")); + + alloc.stack(vec![ + alloc.concat(vec![ + alloc.reflow("This integer literal is too "), + alloc.text(big_or_small), + alloc.reflow(":"), + ]), + alloc.region(region), + alloc.reflow("Roc uses signed 64-bit integers, allowing values between −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807."), + hint, + ]) + } + RuntimeError::InvalidHex(_, _) => todo!("invalid hex, unreachable"), + RuntimeError::InvalidOctal(_, _) => todo!("invalid octal, unreachable"), + RuntimeError::InvalidBinary(_, _) => todo!("invalid binary, unreachable"), + RuntimeError::NoImplementation => todo!("no implementation, unreachable"), } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 63fc5c0731..030bf2b6bc 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -3108,4 +3108,145 @@ mod test_reporting { ), ) } + + #[test] + fn integer_out_of_range() { + report_problem_as( + indoc!( + r#" + x = 9_223_372_036_854_775_807_000 + + y = -9_223_372_036_854_775_807_000 + + x + y + "# + ), + indoc!( + r#" + -- SYNTAX PROBLEM -------------------------------------------------------------- + + This integer literal is too small: + + 3 ┆ y = -9_223_372_036_854_775_807_000 + ┆ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Roc uses signed 64-bit integers, allowing values between + −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807. + + Hint: Learn more about number literals at TODO + + -- SYNTAX PROBLEM -------------------------------------------------------------- + + This integer literal is too big: + + 1 ┆ x = 9_223_372_036_854_775_807_000 + ┆ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Roc uses signed 64-bit integers, allowing values between + −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807. + + Hint: Learn more about number literals at TODO + "# + ), + ) + } + + #[test] + fn integer_malformed() { + // the generated messages here are incorrect. Waiting for a rust nightly feature to land, + // see https://github.com/rust-lang/rust/issues/22639 + // this test is here to spot regressions in error reporting + report_problem_as( + indoc!( + r#" + dec = 100A + + hex = 0xZZZ + + oct = 0o9 + + bin = 0b2 + + dec + hex + oct + bin + "# + ), + indoc!( + r#" + -- SYNTAX PROBLEM -------------------------------------------------------------- + + This integer literal is too big: + + 3 ┆ hex = 0xZZZ + ┆ ^^^^^ + + Roc uses signed 64-bit integers, allowing values between + −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807. + + Hint: Learn more about number literals at TODO + + -- SYNTAX PROBLEM -------------------------------------------------------------- + + This integer literal is too big: + + 5 ┆ oct = 0o9 + ┆ ^^^ + + Roc uses signed 64-bit integers, allowing values between + −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807. + + Hint: Learn more about number literals at TODO + + -- SYNTAX PROBLEM -------------------------------------------------------------- + + This integer literal is too big: + + 7 ┆ bin = 0b2 + ┆ ^^^ + + Roc uses signed 64-bit integers, allowing values between + −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807. + + Hint: Learn more about number literals at TODO + + -- SYNTAX PROBLEM -------------------------------------------------------------- + + This integer literal is too big: + + 1 ┆ dec = 100A + ┆ ^^^^ + + Roc uses signed 64-bit integers, allowing values between + −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807. + + Hint: Learn more about number literals at TODO + "# + ), + ) + } + + #[test] + fn precedence_conflict() { + report_problem_as( + indoc!( + r#" + foo@bar + "# + ), + indoc!( + r#" + -- SYNTAX PROBLEM -------------------------------------------------------------- + + This integer literal is too big: + + 1 ┆ dec = 100A + ┆ ^^^^ + + Roc uses signed 64-bit integers, allowing values between + −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807. + + Hint: Learn more about number literals at TODO + "# + ), + ) + } }