From 917ec9c44c5e1a23a7d53a15a944fa333084beca Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 24 Dec 2020 01:00:37 +0100 Subject: [PATCH 1/6] make List.first/List.last work --- cli/tests/repl_eval.rs | 10 ++++- compiler/mono/src/ir.rs | 42 +++++++++++++++----- compiler/problem/src/can.rs | 3 ++ compiler/reporting/src/error/canonicalize.rs | 6 +++ 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/cli/tests/repl_eval.rs b/cli/tests/repl_eval.rs index b8829ab8c1..0d06c2aa15 100644 --- a/cli/tests/repl_eval.rs +++ b/cli/tests/repl_eval.rs @@ -292,13 +292,16 @@ mod repl_eval { expect_success("List.sum [ 1.1, 2.2, 3.3 ]", "6.6 : F64"); } - // TODO add test cases for empty lists once error messages in the repl are correct #[test] fn list_first() { expect_success( "List.first [ 12, 9, 6, 3 ]", "Ok 12 : Result (Num *) [ ListWasEmpty ]*", ); + expect_success( + "List.first []", + "Err (ListWasEmpty) : Result * [ ListWasEmpty ]*", + ); } #[test] @@ -307,6 +310,11 @@ mod repl_eval { "List.last [ 12, 9, 6, 3 ]", "Ok 3 : Result (Num *) [ ListWasEmpty ]*", ); + + expect_success( + "List.last []", + "Err (ListWasEmpty) : Result * [ ListWasEmpty ]*", + ); } #[test] diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index dc08c98176..882574fc6e 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2585,13 +2585,23 @@ pub fn with_hole<'a>( let mut field_symbols_temp = Vec::with_capacity_in(args.len(), env.arena); - for (var, arg) in args.drain(..) { + for (var, mut arg) in args.drain(..) { // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field - let layout = layout_cache - .from_var(env.arena, var, env.subs) - .unwrap_or_else(|err| { - panic!("TODO turn fn_var into a RuntimeError {:?}", err) - }); + let layout = match layout_cache.from_var(env.arena, var, env.subs) { + Ok(cached) => cached, + Err(LayoutProblem::UnresolvedTypeVar(_)) => { + // this argument has type `forall a. a`, which is isomorphic to + // the empty type (Void, Never, the empty tag union `[]`) + use roc_can::expr::Expr; + use roc_problem::can::RuntimeError; + arg.value = Expr::RuntimeError(RuntimeError::VoidValue); + Layout::Struct(&[]) + } + Err(LayoutProblem::Erroneous) => { + // something went very wrong + panic!("TODO turn fn_var into a RuntimeError") + } + }; let alignment = layout.alignment_bytes(8); @@ -3575,9 +3585,23 @@ pub fn with_hole<'a>( let arg_symbols = arg_symbols.into_bump_slice(); // layout of the return type - let layout = layout_cache - .from_var(env.arena, ret_var, env.subs) - .unwrap_or_else(|err| todo!("TODO turn fn_var into a RuntimeError {:?}", err)); + let layout = match layout_cache.from_var(env.arena, ret_var, env.subs) { + Ok(cached) => cached, + Err(LayoutProblem::UnresolvedTypeVar(_)) => { + return Stmt::RuntimeError(env.arena.alloc(format!( + "UnresolvedTypeVar {} line {}", + file!(), + line!() + ))); + } + Err(LayoutProblem::Erroneous) => { + return Stmt::RuntimeError(env.arena.alloc(format!( + "Erroneous {} line {}", + file!(), + line!() + ))); + } + }; let result = Stmt::Let(assigned, Expr::RunLowLevel(op, arg_symbols), layout, hole); diff --git a/compiler/problem/src/can.rs b/compiler/problem/src/can.rs index 59c1b431cd..6cf91131fc 100644 --- a/compiler/problem/src/can.rs +++ b/compiler/problem/src/can.rs @@ -147,6 +147,9 @@ pub enum RuntimeError { /// When the author specifies a type annotation but no implementation NoImplementation, + /// cases where the `[]` value (or equivalently, `forall a. a`) pops up + VoidValue, + ExposedButNotDefined(Symbol), } diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index c216473b98..e13c0d1c42 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -338,6 +338,12 @@ fn pretty_runtime_error<'b>( runtime_error: RuntimeError, ) -> RocDocBuilder<'b> { match runtime_error { + RuntimeError::VoidValue => { + // is used to communicate to the compiler that + // a branch is unreachable; this should never reach a user + unreachable!("") + } + RuntimeError::Shadowing { original_region, shadow, From 2bcaf3921f60d552a64615ac5480cd0080d8f005 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 25 Dec 2020 20:13:42 +0100 Subject: [PATCH 2/6] fix #333: bug with symbol in if condition --- compiler/gen/tests/gen_primitives.rs | 16 ++++++++++++++++ compiler/mono/src/ir.rs | 11 +++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler/gen/tests/gen_primitives.rs b/compiler/gen/tests/gen_primitives.rs index 4c2ace6a05..56c39caef0 100644 --- a/compiler/gen/tests/gen_primitives.rs +++ b/compiler/gen/tests/gen_primitives.rs @@ -1732,4 +1732,20 @@ mod gen_primitives { |_| 0 ); } + + #[test] + fn hof_conditional() { + // exposed issue with the if condition being just a symbol + assert_evals_to!( + indoc!( + r#" + passTrue = \f -> f True + + passTrue (\trueVal -> if trueVal then False else True) + "# + ), + 0, + u8 + ); + } } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 882574fc6e..14e92e8653 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -3678,7 +3678,7 @@ pub fn from_can<'a>( let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache); for (loc_cond, loc_then) in branches.into_iter().rev() { - let branching_symbol = env.unique_symbol(); + let branching_symbol = possible_reuse_symbol(env, procs, &loc_cond.value); let then = from_can(env, branch_var, loc_then.value, procs, layout_cache); stmt = Stmt::Cond { @@ -3691,15 +3691,14 @@ pub fn from_can<'a>( ret_layout: ret_layout.clone(), }; - // add condition - stmt = with_hole( + stmt = assign_to_symbol( env, - loc_cond.value, - cond_var, procs, layout_cache, + cond_var, + loc_cond, branching_symbol, - env.arena.alloc(stmt), + stmt, ); } From 51cf54e3678bd9bcc6bf2ccea76c3ee901174c63 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 25 Dec 2020 21:26:29 +0100 Subject: [PATCH 3/6] raise exception on Num.abs overflow --- compiler/gen/src/llvm/build.rs | 144 ++++++++++++++++++++++++--------- compiler/gen/tests/gen_num.rs | 20 +++++ 2 files changed, 128 insertions(+), 36 deletions(-) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index f2bd4cb3c4..2097a8e195 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -2902,7 +2902,7 @@ fn run_low_level<'a, 'ctx, 'env>( match arg_builtin { Int128 | Int64 | Int32 | Int16 | Int8 => { - build_int_unary_op(env, arg.into_int_value(), arg_layout, op) + build_int_unary_op(env, arg.into_int_value(), arg_builtin, op) } Float128 | Float64 | Float32 | Float16 => { build_float_unary_op(env, arg.into_float_value(), op) @@ -3492,51 +3492,52 @@ fn build_float_binop<'a, 'ctx, 'env>( fn build_int_unary_op<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, arg: IntValue<'ctx>, - arg_layout: &Layout<'a>, + arg_layout: &Builtin<'a>, op: LowLevel, ) -> BasicValueEnum<'ctx> { use roc_module::low_level::LowLevel::*; let bd = env.builder; + let ctx = env.context; match op { NumNeg => bd.build_int_neg(arg, "negate_int").into(), NumAbs => { - // This is how libc's abs() is implemented - it uses no branching! - // - // abs = \arg -> - // shifted = arg >>> 63 - // - // (xor arg shifted) - shifted + use Builtin::*; + // integer abs overflows when applied to the minimum value of a signed type + match arg_layout { + Int128 => { + let a = i128::MIN as u64; + let b = (i128::MIN >> 64) as u64; - let ctx = env.context; - let shifted_name = "abs_shift_right"; - let shifted_alloca = { - let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1; - let shift_val = ctx.i64_type().const_int(bits_to_shift, false); - let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name); - let alloca = bd.build_alloca( - basic_type_from_layout(env.arena, ctx, arg_layout, env.ptr_bytes), - "#int_abs_help", - ); - - // shifted = arg >>> 63 - bd.build_store(alloca, shifted); - - alloca - }; - - let xored_arg = bd.build_xor( - arg, - bd.build_load(shifted_alloca, shifted_name).into_int_value(), - "xor_arg_shifted", - ); - - BasicValueEnum::IntValue(bd.build_int_sub( - xored_arg, - bd.build_load(shifted_alloca, shifted_name).into_int_value(), - "sub_xored_shifted", - )) + int_abs_raise_on_overflow( + env, + arg, + ctx.i128_type().const_int_arbitrary_precision(&[b, a]), + ) + } + Int64 => int_abs_raise_on_overflow( + env, + arg, + ctx.i64_type().const_int(i64::MIN as u64, false), + ), + Int32 => int_abs_raise_on_overflow( + env, + arg, + ctx.i32_type().const_int(i32::MIN as u64, false), + ), + Int16 => int_abs_raise_on_overflow( + env, + arg, + ctx.i16_type().const_int(i16::MIN as u64, false), + ), + Int8 => int_abs_raise_on_overflow( + env, + arg, + ctx.i8_type().const_int(i8::MIN as u64, false), + ), + _ => unreachable!("not an integer type"), + } } NumToFloat => { // This is an Int, so we need to convert it. @@ -3553,6 +3554,77 @@ fn build_int_unary_op<'a, 'ctx, 'env>( } } +fn int_abs_raise_on_overflow<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + arg: IntValue<'ctx>, + min_val: IntValue<'ctx>, +) -> BasicValueEnum<'ctx> { + let builder = env.builder; + let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val"); + + let block = env.builder.get_insert_block().expect("to be in a function"); + let parent = block.get_parent().expect("to be in a function"); + let then_block = env.context.append_basic_block(parent, "then"); + let else_block = env.context.append_basic_block(parent, "else"); + + env.builder + .build_conditional_branch(condition, then_block, else_block); + + builder.position_at_end(then_block); + + throw_exception( + env, + "integer absolute overflowed because its argument is the minimum value", + ); + + builder.position_at_end(else_block); + + int_abs_with_overflow(env, arg, Layout::Builtin(Builtin::Int64)) +} + +fn int_abs_with_overflow<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + arg: IntValue<'ctx>, + arg_layout: Layout<'a>, +) -> BasicValueEnum<'ctx> { + // This is how libc's abs() is implemented - it uses no branching! + // + // abs = \arg -> + // shifted = arg >>> 63 + // + // (xor arg shifted) - shifted + + let bd = env.builder; + let ctx = env.context; + let shifted_name = "abs_shift_right"; + let shifted_alloca = { + let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1; + let shift_val = ctx.i64_type().const_int(bits_to_shift, false); + let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name); + let alloca = bd.build_alloca( + basic_type_from_layout(env.arena, ctx, &arg_layout, env.ptr_bytes), + "#int_abs_help", + ); + + // shifted = arg >>> 63 + bd.build_store(alloca, shifted); + + alloca + }; + + let xored_arg = bd.build_xor( + arg, + bd.build_load(shifted_alloca, shifted_name).into_int_value(), + "xor_arg_shifted", + ); + + BasicValueEnum::IntValue(bd.build_int_sub( + xored_arg, + bd.build_load(shifted_alloca, shifted_name).into_int_value(), + "sub_xored_shifted", + )) +} + fn build_float_unary_op<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, arg: FloatValue<'ctx>, diff --git a/compiler/gen/tests/gen_num.rs b/compiler/gen/tests/gen_num.rs index d8aa2e2e04..4150d9b4e7 100644 --- a/compiler/gen/tests/gen_num.rs +++ b/compiler/gen/tests/gen_num.rs @@ -40,6 +40,8 @@ mod gen_num { fn f64_abs() { assert_evals_to!("Num.abs -4.7", 4.7, f64); assert_evals_to!("Num.abs 5.8", 5.8, f64); + //assert_evals_to!("Num.abs Num.maxFloat", f64::MAX, f64); + //assert_evals_to!("Num.abs Num.minFloat", -f64::MIN, f64); } #[test] @@ -52,6 +54,24 @@ mod gen_num { assert_evals_to!("Num.abs 1", 1, i64); assert_evals_to!("Num.abs 9_000_000_000_000", 9_000_000_000_000, i64); assert_evals_to!("Num.abs -9_000_000_000_000", 9_000_000_000_000, i64); + assert_evals_to!("Num.abs Num.maxInt", i64::MAX, i64); + assert_evals_to!("Num.abs (Num.minInt + 1)", -(i64::MIN + 1), i64); + } + + #[test] + #[should_panic( + expected = r#"Roc failed with message: "integer absolute overflowed because its argument is the minimum value"# + )] + fn abs_min_int_overflow() { + assert_evals_to!( + indoc!( + r#" + Num.abs Num.minInt + "# + ), + 0, + i64 + ); } #[test] From f0ce8006d279c19c36c5400d70d0b7b3dc8e2f13 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 26 Dec 2020 00:48:40 +0100 Subject: [PATCH 4/6] factor out basic_type_from_builtin --- compiler/gen/src/llvm/convert.rs | 48 +++++++++++++++++++------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/compiler/gen/src/llvm/convert.rs b/compiler/gen/src/llvm/convert.rs index 97d23fd622..37e062ee63 100644 --- a/compiler/gen/src/llvm/convert.rs +++ b/compiler/gen/src/llvm/convert.rs @@ -4,7 +4,7 @@ use inkwell::context::Context; use inkwell::types::BasicTypeEnum::{self, *}; use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType}; use inkwell::AddressSpace; -use roc_mono::layout::Layout; +use roc_mono::layout::{Builtin, Layout}; /// TODO could this be added to Inkwell itself as a method on BasicValueEnum? pub fn get_ptr_type<'ctx>( @@ -103,8 +103,7 @@ pub fn basic_type_from_layout<'ctx>( layout: &Layout<'_>, ptr_bytes: u32, ) -> BasicTypeEnum<'ctx> { - use roc_mono::layout::Builtin::*; - use roc_mono::layout::Layout::*; + use Layout::*; match layout { FunctionPointer(args, ret_layout) => { @@ -147,22 +146,33 @@ pub fn basic_type_from_layout<'ctx>( .as_basic_type_enum() } - Builtin(builtin) => match builtin { - Int128 => context.i128_type().as_basic_type_enum(), - Int64 => context.i64_type().as_basic_type_enum(), - Int32 => context.i32_type().as_basic_type_enum(), - Int16 => context.i16_type().as_basic_type_enum(), - Int8 => context.i8_type().as_basic_type_enum(), - Int1 => context.bool_type().as_basic_type_enum(), - Float128 => context.f128_type().as_basic_type_enum(), - Float64 => context.f64_type().as_basic_type_enum(), - Float32 => context.f32_type().as_basic_type_enum(), - Float16 => context.f16_type().as_basic_type_enum(), - Dict(_, _) | EmptyDict => panic!("TODO layout_to_basic_type for Builtin::Dict"), - Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"), - List(_, _) | Str | EmptyStr => collection(context, ptr_bytes).into(), - EmptyList => BasicTypeEnum::StructType(collection(context, ptr_bytes)), - }, + Builtin(builtin) => basic_type_from_builtin(arena, context, builtin, ptr_bytes), + } +} + +pub fn basic_type_from_builtin<'ctx>( + _arena: &Bump, + context: &'ctx Context, + builtin: &Builtin<'_>, + ptr_bytes: u32, +) -> BasicTypeEnum<'ctx> { + use Builtin::*; + + match builtin { + Int128 => context.i128_type().as_basic_type_enum(), + Int64 => context.i64_type().as_basic_type_enum(), + Int32 => context.i32_type().as_basic_type_enum(), + Int16 => context.i16_type().as_basic_type_enum(), + Int8 => context.i8_type().as_basic_type_enum(), + Int1 => context.bool_type().as_basic_type_enum(), + Float128 => context.f128_type().as_basic_type_enum(), + Float64 => context.f64_type().as_basic_type_enum(), + Float32 => context.f32_type().as_basic_type_enum(), + Float16 => context.f16_type().as_basic_type_enum(), + Dict(_, _) | EmptyDict => panic!("TODO layout_to_basic_type for Builtin::Dict"), + Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"), + List(_, _) | Str | EmptyStr => collection(context, ptr_bytes).into(), + EmptyList => BasicTypeEnum::StructType(collection(context, ptr_bytes)), } } From 59d6cdba0465c452c637db26cf5bf57070a7125f Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 26 Dec 2020 00:50:09 +0100 Subject: [PATCH 5/6] make Num.neg raise on overflow --- compiler/gen/src/llvm/build.rs | 113 +++++++++++++++++++++------------ compiler/gen/tests/gen_num.rs | 18 ++++++ 2 files changed, 89 insertions(+), 42 deletions(-) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 2097a8e195..34c8673460 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -9,7 +9,8 @@ use crate::llvm::build_str::{ }; use crate::llvm::compare::{build_eq, build_neq}; use crate::llvm::convert::{ - basic_type_from_layout, block_of_memory, collection, get_fn_type, get_ptr_type, ptr_int, + basic_type_from_builtin, basic_type_from_layout, block_of_memory, collection, get_fn_type, + get_ptr_type, ptr_int, }; use crate::llvm::refcounting::{ decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison, @@ -3489,6 +3490,33 @@ fn build_float_binop<'a, 'ctx, 'env>( } } +fn int_type_signed_min<'ctx>(int_type: IntType<'ctx>) -> IntValue<'ctx> { + let width = int_type.get_bit_width(); + + debug_assert!(width <= 128); + let shift = 128 - width as usize; + + if shift < 64 { + let min = i128::MIN >> shift; + let a = min as u64; + let b = (min >> 64) as u64; + + int_type.const_int_arbitrary_precision(&[b, a]) + } else { + int_type.const_int((i128::MIN >> shift) as u64, false) + } +} + +fn builtin_to_int_type<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + builtin: &Builtin<'a>, +) -> IntType<'ctx> { + let result = basic_type_from_builtin(env.arena, env.context, builtin, env.ptr_bytes); + debug_assert!(result.is_int_type()); + + result.into_int_type() +} + fn build_int_unary_op<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, arg: IntValue<'ctx>, @@ -3498,46 +3526,15 @@ fn build_int_unary_op<'a, 'ctx, 'env>( use roc_module::low_level::LowLevel::*; let bd = env.builder; - let ctx = env.context; match op { - NumNeg => bd.build_int_neg(arg, "negate_int").into(), - NumAbs => { - use Builtin::*; + NumNeg => { // integer abs overflows when applied to the minimum value of a signed type - match arg_layout { - Int128 => { - let a = i128::MIN as u64; - let b = (i128::MIN >> 64) as u64; - - int_abs_raise_on_overflow( - env, - arg, - ctx.i128_type().const_int_arbitrary_precision(&[b, a]), - ) - } - Int64 => int_abs_raise_on_overflow( - env, - arg, - ctx.i64_type().const_int(i64::MIN as u64, false), - ), - Int32 => int_abs_raise_on_overflow( - env, - arg, - ctx.i32_type().const_int(i32::MIN as u64, false), - ), - Int16 => int_abs_raise_on_overflow( - env, - arg, - ctx.i16_type().const_int(i16::MIN as u64, false), - ), - Int8 => int_abs_raise_on_overflow( - env, - arg, - ctx.i8_type().const_int(i8::MIN as u64, false), - ), - _ => unreachable!("not an integer type"), - } + int_neg_raise_on_overflow(env, arg, arg_layout) + } + NumAbs => { + // integer abs overflows when applied to the minimum value of a signed type + int_abs_raise_on_overflow(env, arg, arg_layout) } NumToFloat => { // This is an Int, so we need to convert it. @@ -3554,12 +3551,44 @@ fn build_int_unary_op<'a, 'ctx, 'env>( } } +fn int_neg_raise_on_overflow<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + arg: IntValue<'ctx>, + builtin: &Builtin<'a>, +) -> BasicValueEnum<'ctx> { + let builder = env.builder; + + let min_val = int_type_signed_min(builtin_to_int_type(env, builtin)); + let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val"); + + let block = env.builder.get_insert_block().expect("to be in a function"); + let parent = block.get_parent().expect("to be in a function"); + let then_block = env.context.append_basic_block(parent, "then"); + let else_block = env.context.append_basic_block(parent, "else"); + + env.builder + .build_conditional_branch(condition, then_block, else_block); + + builder.position_at_end(then_block); + + throw_exception( + env, + "integer negation overflowed because its argument is the minimum value", + ); + + builder.position_at_end(else_block); + + builder.build_int_neg(arg, "negate_int").into() +} + fn int_abs_raise_on_overflow<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, arg: IntValue<'ctx>, - min_val: IntValue<'ctx>, + builtin: &Builtin<'a>, ) -> BasicValueEnum<'ctx> { let builder = env.builder; + + let min_val = int_type_signed_min(builtin_to_int_type(env, builtin)); let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val"); let block = env.builder.get_insert_block().expect("to be in a function"); @@ -3579,13 +3608,13 @@ fn int_abs_raise_on_overflow<'a, 'ctx, 'env>( builder.position_at_end(else_block); - int_abs_with_overflow(env, arg, Layout::Builtin(Builtin::Int64)) + int_abs_with_overflow(env, arg, builtin) } fn int_abs_with_overflow<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, arg: IntValue<'ctx>, - arg_layout: Layout<'a>, + arg_layout: &Builtin<'a>, ) -> BasicValueEnum<'ctx> { // This is how libc's abs() is implemented - it uses no branching! // @@ -3602,7 +3631,7 @@ fn int_abs_with_overflow<'a, 'ctx, 'env>( let shift_val = ctx.i64_type().const_int(bits_to_shift, false); let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name); let alloca = bd.build_alloca( - basic_type_from_layout(env.arena, ctx, &arg_layout, env.ptr_bytes), + basic_type_from_builtin(env.arena, ctx, arg_layout, env.ptr_bytes), "#int_abs_help", ); diff --git a/compiler/gen/tests/gen_num.rs b/compiler/gen/tests/gen_num.rs index 4150d9b4e7..1fa2726576 100644 --- a/compiler/gen/tests/gen_num.rs +++ b/compiler/gen/tests/gen_num.rs @@ -553,6 +553,24 @@ mod gen_num { #[test] fn int_negate() { assert_evals_to!("Num.neg 123", -123, i64); + assert_evals_to!("Num.neg Num.maxInt", -i64::MAX, i64); + assert_evals_to!("Num.neg (Num.minInt + 1)", i64::MAX, i64); + } + + #[test] + #[should_panic( + expected = r#"Roc failed with message: "integer negation overflowed because its argument is the minimum value"# + )] + fn neg_min_int_overflow() { + assert_evals_to!( + indoc!( + r#" + Num.neg Num.minInt + "# + ), + 0, + i64 + ); } #[test] From 5418dd8e7662a1ea9c5b220cb4984e77412a58b2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 26 Dec 2020 00:52:37 +0100 Subject: [PATCH 6/6] clippy --- compiler/gen/src/llvm/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index eb8cd1f6b3..a14c7fae26 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -3575,7 +3575,7 @@ fn build_float_binop<'a, 'ctx, 'env>( } } -fn int_type_signed_min<'ctx>(int_type: IntType<'ctx>) -> IntValue<'ctx> { +fn int_type_signed_min(int_type: IntType) -> IntValue { let width = int_type.get_bit_width(); debug_assert!(width <= 128);