diff --git a/cli/src/build.rs b/cli/src/build.rs index 4695598593..41dc30b0fb 100644 --- a/cli/src/build.rs +++ b/cli/src/build.rs @@ -29,6 +29,7 @@ pub fn build_file( ) -> Result { let compilation_start = SystemTime::now(); let arena = Bump::new(); + let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; // Step 1: compile the app and generate the .o file let subs_by_module = MutMap::default(); @@ -36,7 +37,7 @@ pub fn build_file( // Release builds use uniqueness optimizations let stdlib = match opt_level { OptLevel::Normal => roc_builtins::std::standard_stdlib(), - OptLevel::Optimize => roc_builtins::unique::uniq_stdlib(), + OptLevel::Optimize => roc_builtins::std::standard_stdlib(), }; let loaded = roc_load::file::load_and_monomorphize( &arena, @@ -44,6 +45,7 @@ pub fn build_file( stdlib, src_dir.as_path(), subs_by_module, + ptr_bytes, )?; let path_to_platform = loaded.platform_path.clone(); diff --git a/cli/src/repl/gen.rs b/cli/src/repl/gen.rs index a67dda0ddb..3a674e87ba 100644 --- a/cli/src/repl/gen.rs +++ b/cli/src/repl/gen.rs @@ -35,6 +35,8 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result Result MutMap { ), ); - // addWrap : Int, Int -> Int + // addWrap : Int range, Int range -> Int range add_type( Symbol::NUM_ADD_WRAP, - top_level_function(vec![int_type(), int_type()], Box::new(int_type())), + top_level_function( + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), + ), ); // sub or (-) : Num a, Num a -> Num a @@ -106,10 +109,13 @@ pub fn types() -> MutMap { ), ); - // subWrap : Int, Int -> Int + // subWrap : Int range, Int range -> Int range add_type( Symbol::NUM_SUB_WRAP, - top_level_function(vec![int_type(), int_type()], Box::new(int_type())), + top_level_function( + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), + ), ); // subChecked : Num a, Num a -> Result (Num a) [ Overflow ]* @@ -130,10 +136,13 @@ pub fn types() -> MutMap { ), ); - // mulWrap : Int, Int -> Int + // mulWrap : Int range, Int range -> Int range add_type( Symbol::NUM_MUL_WRAP, - top_level_function(vec![int_type(), int_type()], Box::new(int_type())), + top_level_function( + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), + ), ); // mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]* @@ -214,10 +223,13 @@ pub fn types() -> MutMap { ), ); - // toFloat : Num a -> Float + // toFloat : Num * -> Float * add_type( Symbol::NUM_TO_FLOAT, - top_level_function(vec![num_type(flex(TVAR1))], Box::new(float_type())), + top_level_function( + vec![num_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR2))), + ), ); // isNegative : Num a -> Bool @@ -250,11 +262,11 @@ pub fn types() -> MutMap { top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())), ); - // maxInt : Int - add_type(Symbol::NUM_MAX_INT, int_type()); + // maxInt : Int range + add_type(Symbol::NUM_MAX_INT, int_type(flex(TVAR1))); - // minInt : Int - add_type(Symbol::NUM_MIN_INT, int_type()); + // minInt : Int range + add_type(Symbol::NUM_MIN_INT, int_type(flex(TVAR1))); // div : Int, Int -> Result Int [ DivByZero ]* let div_by_zero = SolvedType::TagUnion( @@ -265,56 +277,59 @@ pub fn types() -> MutMap { add_type( Symbol::NUM_DIV_INT, top_level_function( - vec![int_type(), int_type()], - Box::new(result_type(int_type(), div_by_zero.clone())), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), ), ); - // bitwiseAnd : Int, Int -> Int + // bitwiseAnd : Int a, Int a -> Int a add_type( Symbol::NUM_BITWISE_AND, - top_level_function(vec![int_type(), int_type()], Box::new(int_type())), + top_level_function( + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), + ), ); - // rem : Int, Int -> Result Int [ DivByZero ]* + // rem : Int a, Int a -> Result (Int a) [ DivByZero ]* add_type( Symbol::NUM_REM, top_level_function( - vec![int_type(), int_type()], - Box::new(result_type(int_type(), div_by_zero.clone())), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), ), ); - // mod : Int, Int -> Result Int [ DivByZero ]* + // mod : Int a, Int a -> Result (Int a) [ DivByZero ]* add_type( Symbol::NUM_MOD_INT, top_level_function( - vec![int_type(), int_type()], - Box::new(result_type(int_type(), div_by_zero.clone())), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), ), ); // Float module - // div : Float, Float -> Float + // div : Float a, Float a -> Float a add_type( Symbol::NUM_DIV_FLOAT, top_level_function( - vec![float_type(), float_type()], - Box::new(result_type(float_type(), div_by_zero.clone())), + vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], + Box::new(result_type(float_type(flex(TVAR1)), div_by_zero.clone())), ), ); - // mod : Float, Float -> Result Int [ DivByZero ]* + // mod : Float a, Float a -> Result (Float a) [ DivByZero ]* add_type( Symbol::NUM_MOD_FLOAT, top_level_function( - vec![float_type(), float_type()], - Box::new(result_type(float_type(), div_by_zero)), + vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], + Box::new(result_type(float_type(flex(TVAR1)), div_by_zero)), ), ); - // sqrt : Float -> Float + // sqrt : Float a -> Float a let sqrt_of_negative = SolvedType::TagUnion( vec![(TagName::Global("SqrtOfNegative".into()), vec![])], Box::new(SolvedType::Wildcard), @@ -323,81 +338,114 @@ pub fn types() -> MutMap { add_type( Symbol::NUM_SQRT, top_level_function( - vec![float_type()], - Box::new(result_type(float_type(), sqrt_of_negative)), + vec![float_type(flex(TVAR1))], + Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)), ), ); - // round : Float -> Int + // round : Float a -> Int b add_type( Symbol::NUM_ROUND, - top_level_function(vec![float_type()], Box::new(int_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR2))), + ), ); - // sin : Float -> Float + // sin : Float a -> Float a add_type( Symbol::NUM_SIN, - top_level_function(vec![float_type()], Box::new(float_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), + ), ); - // cos : Float -> Float + // cos : Float a -> Float a add_type( Symbol::NUM_COS, - top_level_function(vec![float_type()], Box::new(float_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), + ), ); - // tan : Float -> Float + // tan : Float a -> Float a add_type( Symbol::NUM_TAN, - top_level_function(vec![float_type()], Box::new(float_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), + ), ); - // maxFloat : Float - add_type(Symbol::NUM_MAX_FLOAT, float_type()); + // maxFloat : Float a + add_type(Symbol::NUM_MAX_FLOAT, float_type(flex(TVAR1))); - // minFloat : Float - add_type(Symbol::NUM_MIN_FLOAT, float_type()); + // minFloat : Float a + add_type(Symbol::NUM_MIN_FLOAT, float_type(flex(TVAR1))); - // pow : Float, Float -> Float + // pow : Float a, Float a -> Float a add_type( Symbol::NUM_POW, - top_level_function(vec![float_type(), float_type()], Box::new(float_type())), + top_level_function( + vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), + ), ); - // ceiling : Float -> Int + // ceiling : Float a -> Int b add_type( Symbol::NUM_CEILING, - top_level_function(vec![float_type()], Box::new(int_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR2))), + ), ); - // powInt : Int, Int -> Int + // powInt : Int a, Int a -> Int a add_type( Symbol::NUM_POW_INT, - top_level_function(vec![int_type(), int_type()], Box::new(int_type())), + top_level_function( + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), + ), ); - // floor : Float -> Int + // floor : Float a -> Int b add_type( Symbol::NUM_FLOOR, - top_level_function(vec![float_type()], Box::new(int_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR2))), + ), ); - // atan : Float -> Float + // atan : Float a -> Float a add_type( Symbol::NUM_ATAN, - top_level_function(vec![float_type()], Box::new(float_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), + ), ); - // acos : Float -> Float + // acos : Float a -> Float a add_type( Symbol::NUM_ACOS, - top_level_function(vec![float_type()], Box::new(float_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), + ), ); - // asin : Float -> Float + // asin : Float a -> Float a add_type( Symbol::NUM_ASIN, - top_level_function(vec![float_type()], Box::new(float_type())), + top_level_function( + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), + ), ); // Bool module @@ -461,16 +509,16 @@ pub fn types() -> MutMap { top_level_function(vec![str_type(), str_type()], Box::new(bool_type())), ); - // countGraphemes : Str -> Int + // countGraphemes : Str -> Int a add_type( Symbol::STR_COUNT_GRAPHEMES, - top_level_function(vec![str_type()], Box::new(int_type())), + top_level_function(vec![str_type()], Box::new(int_type(flex(TVAR1)))), ); - // fromInt : Int -> Str + // fromInt : Int a -> Str add_type( Symbol::STR_FROM_INT, - top_level_function(vec![int_type()], Box::new(str_type())), + top_level_function(vec![int_type(flex(TVAR1))], Box::new(str_type())), ); // List module @@ -484,7 +532,7 @@ pub fn types() -> MutMap { add_type( Symbol::LIST_GET, top_level_function( - vec![list_type(flex(TVAR1)), int_type()], + vec![list_type(flex(TVAR1)), int_type(flex(TVAR2))], Box::new(result_type(flex(TVAR1), index_out_of_bounds)), ), ); @@ -512,11 +560,11 @@ pub fn types() -> MutMap { ), ); - // set : List elem, Int, elem -> List elem + // set : List elem, Int a, elem -> List elem add_type( Symbol::LIST_SET, top_level_function( - vec![list_type(flex(TVAR1)), int_type(), flex(TVAR1)], + vec![list_type(flex(TVAR1)), int_type(flex(TVAR2)), flex(TVAR1)], Box::new(list_type(flex(TVAR1))), ), ); @@ -631,11 +679,11 @@ pub fn types() -> MutMap { top_level_function(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))), ); - // repeat : Int, elem -> List elem + // repeat : Int a, elem -> List elem add_type( Symbol::LIST_REPEAT, top_level_function( - vec![int_type(), flex(TVAR1)], + vec![int_type(flex(TVAR1)), flex(TVAR2)], Box::new(list_type(flex(TVAR1))), ), ); @@ -649,10 +697,13 @@ pub fn types() -> MutMap { ), ); - // len : List * -> Int + // len : List * -> Int a add_type( Symbol::LIST_LEN, - top_level_function(vec![list_type(flex(TVAR1))], Box::new(int_type())), + top_level_function( + vec![list_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR2))), + ), ); // isEmpty : List * -> Bool diff --git a/compiler/builtins/src/unique.rs b/compiler/builtins/src/unique.rs index ce7a5e75c8..317715cf03 100644 --- a/compiler/builtins/src/unique.rs +++ b/compiler/builtins/src/unique.rs @@ -166,8 +166,8 @@ pub fn types() -> MutMap { // addWrap : Int, Int -> Int add_type(Symbol::NUM_ADD_WRAP, { - let_tvars! { u, v, w }; - unique_function(vec![int_type(u), int_type(v)], int_type(w)) + let_tvars! { u, v, w, int }; + unique_function(vec![int_type(u, int), int_type(v, int)], int_type(w, int)) }); // sub or (-) : Num a, Num a -> Num a @@ -199,8 +199,8 @@ pub fn types() -> MutMap { // mulWrap : Int, Int -> Int add_type(Symbol::NUM_MUL_WRAP, { - let_tvars! { u, v, w }; - unique_function(vec![int_type(u), int_type(v)], int_type(w)) + let_tvars! { u, v, w , int }; + unique_function(vec![int_type(u, int), int_type(v, int)], int_type(w, int)) }); // mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]* @@ -257,38 +257,41 @@ pub fn types() -> MutMap { // rem : Attr * Int, Attr * Int -> Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*)) add_type(Symbol::NUM_REM, { - let_tvars! { star1, star2, star3, star4, star5 }; + let_tvars! { star1, star2, star3, star4, star5, int }; unique_function( - vec![int_type(star1), int_type(star2)], - result_type(star3, int_type(star4), lift(star5, div_by_zero())), + vec![int_type(star1, int), int_type(star2, int)], + result_type(star3, int_type(star4, int), lift(star5, div_by_zero())), ) }); // maxInt : Int add_type(Symbol::NUM_MAX_INT, { - let_tvars! { star }; - int_type(star) + let_tvars! { star, int }; + int_type(star, int) }); // minInt : Int add_type(Symbol::NUM_MIN_INT, { - let_tvars! { star }; - int_type(star) + let_tvars! { star, int }; + int_type(star, int) }); // divFloor or (//) : Int, Int -> Result Int [ DivByZero ]* add_type(Symbol::NUM_DIV_INT, { - let_tvars! { star1, star2, star3, star4, star5 }; + let_tvars! { star1, star2, star3, star4, star5, int }; unique_function( - vec![int_type(star1), int_type(star2)], - result_type(star3, int_type(star4), lift(star5, div_by_zero())), + vec![int_type(star1, int), int_type(star2, int)], + result_type(star3, int_type(star4, int), lift(star5, div_by_zero())), ) }); // bitwiseAnd : Attr * Int, Attr * Int -> Attr * Int add_type(Symbol::NUM_BITWISE_AND, { - let_tvars! { star1, star2, star3 }; - unique_function(vec![int_type(star1), int_type(star2)], int_type(star3)) + let_tvars! { star1, star2, star3, int }; + unique_function( + vec![int_type(star1, int), int_type(star2, int)], + int_type(star3, int), + ) }); // divFloat : Float, Float -> Float @@ -302,8 +305,8 @@ pub fn types() -> MutMap { // round : Float -> Int add_type(Symbol::NUM_ROUND, { - let_tvars! { star1, star2 }; - unique_function(vec![float_type(star1)], int_type(star2)) + let_tvars! { star1, star2, int }; + unique_function(vec![float_type(star1)], int_type(star2, int)) }); // sqrt : Float -> Float @@ -391,20 +394,23 @@ pub fn types() -> MutMap { // ceiling : Float -> Int add_type(Symbol::NUM_CEILING, { - let_tvars! { star1, star2 }; - unique_function(vec![float_type(star1)], int_type(star2)) + let_tvars! { star1, star2, int }; + unique_function(vec![float_type(star1)], int_type(star2, int)) }); // powInt : Int, Int -> Int add_type(Symbol::NUM_POW_INT, { - let_tvars! { star1, star2, star3 }; - unique_function(vec![int_type(star1), int_type(star2)], int_type(star3)) + let_tvars! { star1, star2, star3 , int }; + unique_function( + vec![int_type(star1, int), int_type(star2, int)], + int_type(star3, int), + ) }); // floor : Float -> Int add_type(Symbol::NUM_FLOOR, { - let_tvars! { star1, star2 }; - unique_function(vec![float_type(star1)], int_type(star2)) + let_tvars! { star1, star2 , int}; + unique_function(vec![float_type(star1)], int_type(star2, int)) }); // atan : Float -> Float @@ -479,8 +485,8 @@ pub fn types() -> MutMap { // len : Attr * (List *) -> Attr * Int add_type(Symbol::LIST_LEN, { - let_tvars! { star1, a, star2 }; - unique_function(vec![list_type(star1, a)], int_type(star2)) + let_tvars! { star1, a, star2 , int }; + unique_function(vec![list_type(star1, a)], int_type(star2, int)) }); fn list_was_empty() -> SolvedType { @@ -536,7 +542,7 @@ pub fn types() -> MutMap { ); add_type(Symbol::LIST_GET, { - let_tvars! { a, u, star1, star2, star3, star4 }; + let_tvars! { a, u, star1, star2, star3, star4, int}; unique_function( vec![ @@ -547,7 +553,7 @@ pub fn types() -> MutMap { SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]), ], ), - int_type(star2), + int_type(star2, int), ], result_type(star3, attr_type(u, a), lift(star4, index_out_of_bounds)), ) @@ -559,7 +565,7 @@ pub fn types() -> MutMap { // Attr (u | v) a // -> Attr * (List (Attr u a)) add_type(Symbol::LIST_SET, { - let_tvars! { u, v, w, star1, star2, a }; + let_tvars! { u, v, w, star1, star2, a, int}; unique_function( vec![ @@ -570,7 +576,7 @@ pub fn types() -> MutMap { SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]), ], ), - int_type(star1), + int_type(star1, int), SolvedType::Apply(Symbol::ATTR_ATTR, vec![container(u, vec![v]), flex(a)]), ], SolvedType::Apply( @@ -627,10 +633,10 @@ pub fn types() -> MutMap { // , Attr Shared a // -> Attr * (List (Attr Shared a)) add_type(Symbol::LIST_REPEAT, { - let_tvars! { a, star1, star2 }; + let_tvars! { a, star1, star2, int }; unique_function( - vec![int_type(star1), shared(flex(a))], + vec![int_type(star1, int), shared(flex(a))], SolvedType::Apply( Symbol::ATTR_ATTR, vec![ @@ -1162,14 +1168,14 @@ pub fn types() -> MutMap { // Str.countGraphemes : Attr * Str, -> Attr * Int add_type(Symbol::STR_COUNT_GRAPHEMES, { - let_tvars! { star1, star2 }; - unique_function(vec![str_type(star1)], int_type(star2)) + let_tvars! { star1, star2, int }; + unique_function(vec![str_type(star1)], int_type(star2, int)) }); // fromInt : Attr * Int -> Attr * Str add_type(Symbol::STR_FROM_INT, { - let_tvars! { star1, star2 }; - unique_function(vec![int_type(star1)], str_type(star2)) + let_tvars! { star1, star2, int }; + unique_function(vec![int_type(star1, int)], str_type(star2)) }); // Result module @@ -1242,9 +1248,8 @@ fn lift(u: VarId, a: SolvedType) -> SolvedType { #[inline(always)] fn float_type(u: VarId) -> SolvedType { - let b_64 = builtin_aliases::binary64_type(); - let attr_b_64 = lift(u, b_64); - let fp = builtin_aliases::floatingpoint_type(attr_b_64); + let inner_type = lift(u, flex(u)); + let fp = builtin_aliases::floatingpoint_type(inner_type.clone()); let attr_fb = lift(u, fp); let num = builtin_aliases::num_type(attr_fb); @@ -1252,16 +1257,19 @@ fn float_type(u: VarId) -> SolvedType { Symbol::ATTR_ATTR, vec![ flex(u), - SolvedType::Alias(Symbol::NUM_F64, Vec::new(), Box::new(num)), + SolvedType::Alias( + Symbol::NUM_FLOAT, + vec![("range".into(), inner_type)], + Box::new(num), + ), ], ) } #[inline(always)] -fn int_type(u: VarId) -> SolvedType { - let signed_64 = builtin_aliases::signed64_type(); - let attr_signed_64 = lift(u, signed_64); - let integer = builtin_aliases::integer_type(attr_signed_64); +fn int_type(u: VarId, range: VarId) -> SolvedType { + let inner_type = lift(u, flex(range)); + let integer = builtin_aliases::integer_type(inner_type.clone()); let attr_fb = lift(u, integer); let num = builtin_aliases::num_type(attr_fb); @@ -1269,7 +1277,11 @@ fn int_type(u: VarId) -> SolvedType { Symbol::ATTR_ATTR, vec![ flex(u), - SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(num)), + SolvedType::Alias( + Symbol::NUM_INT, + vec![("range".into(), inner_type)], + Box::new(num), + ), ], ) } diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 8d9a0ae2f1..007c48bee8 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -212,7 +212,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { /// Num.maxInt : Int fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); - let body = Int(int_var, i64::MAX); + let int_percision_var = var_store.fresh(); + let body = Int(int_var, int_percision_var, i64::MAX); Def { annotation: None, @@ -226,7 +227,8 @@ fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def { /// Num.minInt : Int fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); - let body = Int(int_var, i64::MIN); + let int_percision_var = var_store.fresh(); + let body = Int(int_var, int_percision_var, i64::MIN); Def { annotation: None, @@ -846,7 +848,7 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op: LowLevel::Eq, args: vec![ - (arg_var, Int(var_store.fresh(), 1)), + (arg_var, Int(var_store.fresh(), var_store.fresh(), 1)), ( arg_var, RunLowLevel { @@ -930,6 +932,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def { let bool_var = var_store.fresh(); let float_var = var_store.fresh(); let unbound_zero_var = var_store.fresh(); + let percision_var = var_store.fresh(); let ret_var = var_store.fresh(); let body = If { @@ -943,7 +946,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NotEq, args: vec![ (float_var, Var(Symbol::ARG_1)), - (float_var, Float(unbound_zero_var, 0.0)), + (float_var, Float(unbound_zero_var, percision_var, 0.0)), ], ret_var: bool_var, }, @@ -1896,6 +1899,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def { let bool_var = var_store.fresh(); let num_var = var_store.fresh(); let unbound_zero_var = var_store.fresh(); + let percision_var = var_store.fresh(); let ret_var = var_store.fresh(); let body = If { @@ -1909,7 +1913,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NotEq, args: vec![ (num_var, Var(Symbol::ARG_2)), - (num_var, Float(unbound_zero_var, 0.0)), + (num_var, Float(unbound_zero_var, percision_var, 0.0)), ], ret_var: bool_var, }, @@ -1958,6 +1962,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let bool_var = var_store.fresh(); let num_var = var_store.fresh(); let unbound_zero_var = var_store.fresh(); + let unbound_zero_percision_var = var_store.fresh(); let ret_var = var_store.fresh(); let body = If { @@ -1971,7 +1976,10 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NotEq, args: vec![ (num_var, Var(Symbol::ARG_2)), - (num_var, Int(unbound_zero_var, 0)), + ( + num_var, + Int(unbound_zero_var, unbound_zero_percision_var, 0), + ), ], ret_var: bool_var, }, @@ -2025,6 +2033,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_var = var_store.fresh(); let len_var = var_store.fresh(); let zero_var = var_store.fresh(); + let zero_percision_var = var_store.fresh(); let list_elem_var = var_store.fresh(); let ret_var = var_store.fresh(); @@ -2039,7 +2048,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NotEq, args: vec![ - (len_var, Int(zero_var, 0)), + (len_var, Int(zero_var, zero_percision_var, 0)), ( len_var, RunLowLevel { @@ -2061,7 +2070,10 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { // List.#getUnsafe list 0 RunLowLevel { op: LowLevel::ListGetUnsafe, - args: vec![(list_var, Var(Symbol::ARG_1)), (len_var, Int(zero_var, 0))], + args: vec![ + (list_var, Var(Symbol::ARG_1)), + (len_var, Int(zero_var, zero_percision_var, 0)), + ], ret_var: list_elem_var, }, ], @@ -2102,6 +2114,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_var = var_store.fresh(); let len_var = var_store.fresh(); let num_var = var_store.fresh(); + let num_percision_var = var_store.fresh(); let list_elem_var = var_store.fresh(); let ret_var = var_store.fresh(); @@ -2116,7 +2129,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NotEq, args: vec![ - (len_var, Int(num_var, 0)), + (len_var, Int(num_var, num_percision_var, 0)), ( len_var, RunLowLevel { @@ -2155,7 +2168,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def { ret_var: len_var, }, ), - (arg_var, Int(num_var, 1)), + (arg_var, Int(num_var, num_percision_var, 1)), ], ret_var: len_var, }, diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index e58cb21df1..5a7d3cfbe0 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -735,8 +735,8 @@ fn pattern_to_vars_by_symbol( } NumLiteral(_, _) - | IntLiteral(_) - | FloatLiteral(_) + | IntLiteral(_, _) + | FloatLiteral(_, _) | StrLiteral(_) | Underscore | MalformedPattern(_, _) @@ -864,7 +864,9 @@ fn canonicalize_pending_def<'a>( } } - Alias { name, ann, vars } => { + Alias { + name, ann, vars, .. + } => { let symbol = name.value; let can_ann = canonicalize_annotation(env, scope, &ann.value, ann.region, var_store); @@ -1407,7 +1409,9 @@ fn to_pending_def<'a>( } } - Alias { name, vars, ann } => { + Alias { + name, vars, ann, .. + } => { let region = Region::span_across(&name.region, &ann.region); match scope.introduce( diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 18d486ca1d..a2248b7391 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -56,8 +56,8 @@ pub enum Expr { Num(Variable, i64), // Int and Float store a variable to generate better error messages - Int(Variable, i64), - Float(Variable, f64), + Int(Variable, Variable, i64), + Float(Variable, Variable, f64), Str(InlinableString), List { list_var: Variable, // required for uniqueness of the list @@ -1168,8 +1168,8 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> // Num stores the `a` variable in `Num a`. Not the same as the variable // stored in Int and Float below, which is strictly for better error messages other @ Num(_, _) - | other @ Int(_, _) - | other @ Float(_, _) + | other @ Int(_, _, _) + | other @ Float(_, _, _) | other @ Str { .. } | other @ RuntimeError(_) | other @ EmptyRecord diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 5f100a7a1b..3c59d13f7b 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -363,8 +363,8 @@ fn fix_values_captured_in_closure_pattern( } Identifier(_) | NumLiteral(_, _) - | IntLiteral(_) - | FloatLiteral(_) + | IntLiteral(_, _) + | FloatLiteral(_, _) | StrLiteral(_) | Underscore | Shadowed(_, _) @@ -414,8 +414,8 @@ fn fix_values_captured_in_closure_expr( } Num(_, _) - | Int(_, _) - | Float(_, _) + | Int(_, _, _) + | Float(_, _, _) | Str(_) | Var(_) | EmptyRecord diff --git a/compiler/can/src/num.rs b/compiler/can/src/num.rs index 8163441e5c..f741853cf4 100644 --- a/compiler/can/src/num.rs +++ b/compiler/can/src/num.rs @@ -45,7 +45,7 @@ pub fn int_expr_from_result( ) -> Expr { // Int stores a variable to generate better error messages match result { - Ok(int) => Expr::Int(var_store.fresh(), int), + Ok(int) => Expr::Int(var_store.fresh(), var_store.fresh(), int), Err((raw, error)) => { let runtime_error = InvalidInt(error, base, region, raw.into()); @@ -65,7 +65,7 @@ pub fn float_expr_from_result( ) -> Expr { // Float stores a variable to generate better error messages match result { - Ok(float) => Expr::Float(var_store.fresh(), float), + Ok(float) => Expr::Float(var_store.fresh(), var_store.fresh(), float), Err((raw, error)) => { let runtime_error = InvalidFloat(error, region, raw.into()); diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index 02a271856c..a26e9ddd90 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -25,9 +25,9 @@ pub enum Pattern { ext_var: Variable, destructs: Vec>, }, - IntLiteral(i64), + IntLiteral(Variable, i64), NumLiteral(Variable, i64), - FloatLiteral(f64), + FloatLiteral(Variable, f64), StrLiteral(Box), Underscore, @@ -86,8 +86,8 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec) { } NumLiteral(_, _) - | IntLiteral(_) - | FloatLiteral(_) + | IntLiteral(_, _) + | FloatLiteral(_, _) | StrLiteral(_) | Underscore | MalformedPattern(_, _) @@ -191,7 +191,7 @@ pub fn canonicalize_pattern<'a>( let problem = MalformedPatternProblem::MalformedFloat; malformed_pattern(env, problem, region) } - Ok(float) => Pattern::FloatLiteral(float), + Ok(float) => Pattern::FloatLiteral(var_store.fresh(), float), }, ptype => unsupported_pattern(env, ptype, region), }, @@ -224,9 +224,9 @@ pub fn canonicalize_pattern<'a>( } Ok(int) => { if *is_negative { - Pattern::IntLiteral(-int) + Pattern::IntLiteral(var_store.fresh(), -int) } else { - Pattern::IntLiteral(int) + Pattern::IntLiteral(var_store.fresh(), int) } } }, @@ -455,8 +455,8 @@ fn add_bindings_from_patterns( } } NumLiteral(_, _) - | IntLiteral(_) - | FloatLiteral(_) + | IntLiteral(_, _) + | FloatLiteral(_, _) | StrLiteral(_) | Underscore | Shadowed(_, _) diff --git a/compiler/can/tests/test_can.rs b/compiler/can/tests/test_can.rs index b4aa3366f0..49ee90ade6 100644 --- a/compiler/can/tests/test_can.rs +++ b/compiler/can/tests/test_can.rs @@ -32,7 +32,7 @@ mod test_can { let actual_out = can_expr_with(&arena, test_home(), input); match actual_out.loc_expr.value { - Expr::Float(_, actual) => { + Expr::Float(_, _, actual) => { assert_eq!(expected, actual); } actual => { @@ -46,11 +46,11 @@ mod test_can { let actual_out = can_expr_with(&arena, test_home(), input); match actual_out.loc_expr.value { - Expr::Int(_, actual) => { + Expr::Int(_, _, actual) => { assert_eq!(expected, actual); } actual => { - panic!("Expected an I64, but got: {:?}", actual); + panic!("Expected an Int *, but got: {:?}", actual); } } } @@ -249,7 +249,7 @@ mod test_can { fn correct_annotated_body() { let src = indoc!( r#" - f : I64 -> I64 + f : Int * -> Int * f = \ a -> a f @@ -265,7 +265,7 @@ mod test_can { fn correct_annotated_body_with_comments() { let src = indoc!( r#" - f : I64 -> I64 # comment + f : Int * -> Int * # comment f = \ a -> a f @@ -281,7 +281,7 @@ mod test_can { fn name_mismatch_annotated_body() { let src = indoc!( r#" - f : I64 -> I64 + f : Int * -> Int * g = \ a -> a g @@ -307,7 +307,7 @@ mod test_can { fn name_mismatch_annotated_body_with_comment() { let src = indoc!( r#" - f : I64 -> I64 # comment + f : Int * -> Int * # comment g = \ a -> a g @@ -333,7 +333,7 @@ mod test_can { fn separated_annotated_body() { let src = indoc!( r#" - f : I64 -> I64 + f : Int * -> Int * f = \ a -> a @@ -354,7 +354,7 @@ mod test_can { fn separated_annotated_body_with_comment() { let src = indoc!( r#" - f : I64 -> I64 + f : Int * -> Int * # comment f = \ a -> a @@ -375,9 +375,9 @@ mod test_can { fn shadowed_annotation() { let src = indoc!( r#" - f : I64 -> I64 + f : Int * -> Int * - f : I64 -> I64 + f : Int * -> Int * f "# @@ -397,7 +397,7 @@ mod test_can { fn correct_nested_unannotated_body() { let src = indoc!( r#" - f : I64 + f : Int * f = g = 42 @@ -416,9 +416,9 @@ mod test_can { fn correct_nested_annotated_body() { let src = indoc!( r#" - f : I64 + f : Int * f = - g : I64 + g : Int * g = 42 g + 1 @@ -436,11 +436,11 @@ mod test_can { fn correct_nested_body_annotated_multiple_lines() { let src = indoc!( r#" - f : I64 + f : Int * f = - g : I64 + g : Int * g = 42 - h : I64 + h : Int * h = 5 z = 4 g + h + z @@ -458,10 +458,10 @@ mod test_can { fn correct_nested_body_unannotated_multiple_lines() { let src = indoc!( r#" - f : I64 + f : Int * f = g = 42 - h : I64 + h : Int * h = 5 z = 4 g + h + z @@ -478,7 +478,7 @@ mod test_can { fn correct_double_nested_body() { let src = indoc!( r#" - f : I64 + f : Int * f = g = h = 42 @@ -499,7 +499,7 @@ mod test_can { fn annotation_followed_with_unrelated_affectation() { let src = indoc!( r#" - F : I64 + F : Int * x = 1 @@ -520,9 +520,9 @@ mod test_can { fn two_annotations_followed_with_unrelated_affectation() { let src = indoc!( r#" - G : I64 + G : Int * - F : I64 + F : Int * x = 1 diff --git a/compiler/constrain/src/builtins.rs b/compiler/constrain/src/builtins.rs index 1df15d5aef..f799db9438 100644 --- a/compiler/constrain/src/builtins.rs +++ b/compiler/constrain/src/builtins.rs @@ -11,30 +11,48 @@ use roc_types::types::Reason; use roc_types::types::Type::{self, *}; #[inline(always)] -pub fn int_literal(num_var: Variable, expected: Expected, region: Region) -> Constraint { +pub fn int_literal( + num_var: Variable, + percision_var: Variable, + expected: Expected, + region: Region, +) -> Constraint { let num_type = Variable(num_var); let reason = Reason::IntLiteral; - let expected_literal = ForReason(reason, num_int(), region); exists( vec![num_var], And(vec![ - Eq(num_type.clone(), expected_literal, Category::Int, region), + Eq( + num_type.clone(), + ForReason(reason, num_int(Type::Variable(percision_var)), region), + Category::Int, + region, + ), Eq(num_type, expected, Category::Int, region), ]), ) } #[inline(always)] -pub fn float_literal(num_var: Variable, expected: Expected, region: Region) -> Constraint { +pub fn float_literal( + num_var: Variable, + percision_var: Variable, + expected: Expected, + region: Region, +) -> Constraint { let num_type = Variable(num_var); let reason = Reason::FloatLiteral; - let expected_literal = ForReason(reason, num_float(), region); exists( vec![num_var], And(vec![ - Eq(num_type.clone(), expected_literal, Category::Float, region), + Eq( + num_type.clone(), + ForReason(reason, num_float(Type::Variable(percision_var)), region), + Category::Float, + region, + ), Eq(num_type, expected, Category::Float, region), ]), ) @@ -72,11 +90,11 @@ pub fn str_type() -> Type { } #[inline(always)] -pub fn num_float() -> Type { +pub fn num_float(range: Type) -> Type { Type::Alias( - Symbol::NUM_F64, - vec![], - Box::new(num_num(num_floatingpoint(num_binary64()))), + Symbol::NUM_FLOAT, + vec![("range".into(), range.clone())], + Box::new(num_num(num_floatingpoint(range))), ) } @@ -108,11 +126,11 @@ pub fn num_binary64() -> Type { } #[inline(always)] -pub fn num_int() -> Type { +pub fn num_int(range: Type) -> Type { Type::Alias( - Symbol::NUM_I64, - vec![], - Box::new(num_num(num_integer(num_signed64()))), + Symbol::NUM_INT, + vec![("range".into(), range.clone())], + Box::new(num_num(num_integer(range))), ) } diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 000bbde915..2bde1b52da 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -96,7 +96,7 @@ pub fn constrain_expr( expected: Expected, ) -> Constraint { match expr { - Int(var, _) => int_literal(*var, expected, region), + Int(var, percision, _) => int_literal(*var, *percision, expected, region), Num(var, _) => exists( vec![*var], Eq( @@ -106,7 +106,7 @@ pub fn constrain_expr( region, ), ), - Float(var, _) => float_literal(*var, expected, region), + Float(var, percision, _) => float_literal(*var, *percision, expected, region), EmptyRecord => constrain_empty_record(region, expected), Expr::Record { record_var, fields } => { if fields.is_empty() { diff --git a/compiler/constrain/src/pattern.rs b/compiler/constrain/src/pattern.rs index 164df10b34..8a2f5d9250 100644 --- a/compiler/constrain/src/pattern.rs +++ b/compiler/constrain/src/pattern.rs @@ -57,8 +57,8 @@ fn headers_from_annotation_help( | MalformedPattern(_, _) | UnsupportedPattern(_) | NumLiteral(_, _) - | IntLiteral(_) - | FloatLiteral(_) + | IntLiteral(_, _) + | FloatLiteral(_, _) | StrLiteral(_) => true, RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() { @@ -154,20 +154,20 @@ pub fn constrain_pattern( )); } - IntLiteral(_) => { + IntLiteral(precision_var, _) => { state.constraints.push(Constraint::Pattern( region, PatternCategory::Int, - builtins::num_int(), + builtins::num_int(Type::Variable(*precision_var)), expected, )); } - FloatLiteral(_) => { + FloatLiteral(precision_var, _) => { state.constraints.push(Constraint::Pattern( region, PatternCategory::Float, - builtins::num_float(), + builtins::num_float(Type::Variable(*precision_var)), expected, )); } diff --git a/compiler/constrain/src/uniq.rs b/compiler/constrain/src/uniq.rs index c3ce919dd3..4d6552ea83 100644 --- a/compiler/constrain/src/uniq.rs +++ b/compiler/constrain/src/uniq.rs @@ -1,4 +1,4 @@ -use crate::builtins::{num_binary64, num_floatingpoint, num_integer, num_num, num_signed64}; +use crate::builtins::{num_floatingpoint, num_integer, num_num}; use crate::expr::{exists, Info}; use roc_can::annotation::IntroducedVariables; use roc_can::constraint::Constraint::{self, *}; @@ -173,18 +173,19 @@ fn constrain_pattern( )); } - IntLiteral(_) => { + IntLiteral(inner_var, _) => { let (a, b, c, num_type) = unique_int(var_store); + state.constraints.push(exists( - vec![a, b, c], + vec![*inner_var, a, b, c], Constraint::Pattern(pattern.region, PatternCategory::Int, num_type, expected), )); } - FloatLiteral(_) => { + FloatLiteral(inner_var, _) => { let (a, b, c, num_type) = unique_float(var_store); state.constraints.push(exists( - vec![a, b, c], + vec![*inner_var, a, b, c], Constraint::Pattern(pattern.region, PatternCategory::Float, num_type, expected), )); } @@ -423,10 +424,9 @@ fn unique_int(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type) let num_uvar1 = var_store.fresh(); let num_uvar2 = var_store.fresh(); let num_uvar3 = var_store.fresh(); + let num_uvar4 = var_store.fresh(); - let signed_64 = num_signed64(); - let attr_signed_64 = attr_type(Bool::variable(num_uvar1), signed_64); - let integer = num_integer(attr_signed_64); + let integer = num_integer(Type::Variable(num_uvar4)); let attr_int = attr_type(Bool::variable(num_uvar2), integer); let num = num_num(attr_int); let attr_num = attr_type(Bool::variable(num_uvar3), num); @@ -438,10 +438,9 @@ fn unique_float(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type let num_uvar1 = var_store.fresh(); let num_uvar2 = var_store.fresh(); let num_uvar3 = var_store.fresh(); + let num_uvar4 = var_store.fresh(); - let binary_64 = num_binary64(); - let attr_binary_64 = attr_type(Bool::variable(num_uvar1), binary_64); - let fp = num_floatingpoint(attr_binary_64); + let fp = num_floatingpoint(Type::Variable(num_uvar4)); let attr_fp = attr_type(Bool::variable(num_uvar2), fp); let num = num_num(attr_fp); let attr_num = attr_type(Bool::variable(num_uvar3), num); @@ -478,7 +477,7 @@ pub fn constrain_expr( ]), ) } - Int(var, _) => { + Int(var, _, _) => { let (a, b, c, num_type) = unique_int(var_store); exists( @@ -494,7 +493,7 @@ pub fn constrain_expr( ]), ) } - Float(var, _) => { + Float(var, _, _) => { let (a, b, c, num_type) = unique_float(var_store); exists( @@ -532,7 +531,9 @@ pub fn constrain_expr( ), ) } - Record { record_var, fields } => { + Record { + record_var, fields, .. + } => { // NOTE: canonicalization guarantees at least one field // zero fields generates an EmptyRecord let mut field_types = SendMap::default(); diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index bd99d58dd2..7d8fd25577 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -762,8 +762,8 @@ mod test_fmt { expr_formats_to( indoc!( r#" - f: { y : I64, - x : I64 , + f: { y : Int *, + x : Int * , } f"# @@ -772,8 +772,8 @@ mod test_fmt { r#" f : { - y : I64, - x : I64, + y : Int *, + x : Int *, } f"# @@ -787,8 +787,8 @@ mod test_fmt { r#" f : { - y : I64, - x : I64, + y : Int *, + x : Int *, } f"# @@ -800,7 +800,7 @@ mod test_fmt { expr_formats_same(indoc!( r#" f : - I64 + Int * f"# )); @@ -880,7 +880,7 @@ mod test_fmt { r#" f : { - x: I64 # comment 1 + x: Int * # comment 1 , # comment 2 } @@ -891,7 +891,7 @@ mod test_fmt { r#" f : { - x : I64, + x : Int *, # comment 1 # comment 2 } @@ -2557,7 +2557,7 @@ mod test_fmt { fn record_type() { expr_formats_same(indoc!( r#" - f : { foo : I64 } + f : { foo : Int * } f = { foo: 1000 } a @@ -2608,11 +2608,11 @@ mod test_fmt { // r#" // f : // Result a - // { x : I64 + // { x : Int * // , y : Float // } // c - // -> I64 + // -> Int * // f = // \_ -> 4 // "# diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index e09529c8d2..ecb40b74ea 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -486,13 +486,28 @@ fn get_inplace_from_layout(layout: &Layout<'_>) -> InPlace { pub fn build_exp_literal<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + layout: &Layout<'_>, literal: &roc_mono::ir::Literal<'a>, ) -> BasicValueEnum<'ctx> { use roc_mono::ir::Literal::*; match literal { - Int(num) => env.context.i64_type().const_int(*num as u64, true).into(), - Float(num) => env.context.f64_type().const_float(*num).into(), + Int(int) => + (match layout { + Layout::Builtin(Builtin::Usize) => ptr_int(env.context, env.ptr_bytes), + Layout::Builtin(Builtin::Int128) => env.context.i128_type(), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */ + Layout::Builtin(Builtin::Int64) => env.context.i64_type(), + Layout::Builtin(Builtin::Int32) => env.context.i32_type(), + Layout::Builtin(Builtin::Int16) => env.context.i16_type(), + Layout::Builtin(Builtin::Int8) => env.context.i8_type(), + _ => panic!("Invalid layout for int literal = {:?}", layout), + }).const_int(*int as u64, false).into(), + Float(num) => + (match layout { + Layout::Builtin(Builtin::Float64) => env.context.f64_type(), + Layout::Builtin(Builtin::Float32) => env.context.f32_type(), + _ => panic!("Invalid layout for float literal = {:?}", layout), + }).const_float(*num).into(), Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(), Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(), Str(str_literal) => { @@ -774,7 +789,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( use roc_mono::ir::Expr::*; match expr { - Literal(literal) => build_exp_literal(env, literal), + Literal(literal) => build_exp_literal(env, layout, literal), Call(call) => build_exp_call(env, layout_ids, scope, parent, layout, call), @@ -1805,6 +1820,15 @@ fn build_switch_ir<'a, 'ctx, 'env>( .build_bitcast(full_cond, env.context.i64_type(), "") .into_int_value() } + Layout::Builtin(Builtin::Float32) => { + // float matches are done on the bit pattern + cond_layout = Layout::Builtin(Builtin::Int32); + let full_cond = load_symbol(env, scope, cond_symbol); + + builder + .build_bitcast(full_cond, env.context.i32_type(), "") + .into_int_value() + } Layout::Union(_) => { // we match on the discriminant, not the whole Tag cond_layout = Layout::Builtin(Builtin::Int64); @@ -1832,8 +1856,11 @@ fn build_switch_ir<'a, 'ctx, 'env>( // // they either need to all be i8, or i64 let int_val = match cond_layout { - Layout::Builtin(Builtin::Int128) => context.i128_type().const_int(*int as u64, false), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */ + Layout::Builtin(Builtin::Usize) => { + ptr_int(env.context, env.ptr_bytes).const_int(*int as u64, false) + } Layout::Builtin(Builtin::Int64) => context.i64_type().const_int(*int as u64, false), + Layout::Builtin(Builtin::Int128) => context.i128_type().const_int(*int as u64, false), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */ Layout::Builtin(Builtin::Int32) => context.i32_type().const_int(*int as u64, false), Layout::Builtin(Builtin::Int16) => context.i16_type().const_int(*int as u64, false), Layout::Builtin(Builtin::Int8) => context.i8_type().const_int(*int as u64, false), @@ -3053,7 +3080,7 @@ fn run_low_level<'a, 'ctx, 'env>( use roc_mono::layout::Builtin::*; match arg_builtin { - Int128 | Int64 | Int32 | Int16 | Int8 => { + Usize | Int128 | Int64 | Int32 | Int16 | Int8 => { build_int_unary_op(env, arg.into_int_value(), arg_builtin, op) } Float128 | Float64 | Float32 | Float16 => { @@ -3091,7 +3118,7 @@ fn run_low_level<'a, 'ctx, 'env>( let tag_lt = env.context.i8_type().const_int(2_u64, false); match lhs_builtin { - Int128 | Int64 | Int32 | Int16 | Int8 => { + Usize | Int128 | Int64 | Int32 | Int16 | Int8 => { let are_equal = env.builder.build_int_compare( IntPredicate::EQ, lhs_arg.into_int_value(), @@ -3515,7 +3542,7 @@ pub fn build_num_binop<'a, 'ctx, 'env>( use roc_mono::layout::Builtin::*; match lhs_builtin { - Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop( + Usize | Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop( env, parent, lhs_arg.into_int_value(), @@ -3766,6 +3793,7 @@ fn build_int_unary_op<'a, 'ctx, 'env>( int_abs_raise_on_overflow(env, arg, arg_layout) } NumToFloat => { + // TODO: Handle differnt sized numbers // This is an Int, so we need to convert it. bd.build_cast( InstructionOpcode::SIToFP, @@ -3892,6 +3920,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>( let bd = env.builder; + // TODO: Handle differnt sized floats match op { NumNeg => bd.build_float_neg(arg, "negate_float").into(), NumAbs => env.call_intrinsic(LLVM_FABS_F64, &[arg.into()]), diff --git a/compiler/gen/src/llvm/convert.rs b/compiler/gen/src/llvm/convert.rs index 37e062ee63..958a986c42 100644 --- a/compiler/gen/src/llvm/convert.rs +++ b/compiler/gen/src/llvm/convert.rs @@ -165,6 +165,7 @@ pub fn basic_type_from_builtin<'ctx>( Int16 => context.i16_type().as_basic_type_enum(), Int8 => context.i8_type().as_basic_type_enum(), Int1 => context.bool_type().as_basic_type_enum(), + Usize => ptr_int(context, ptr_bytes).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(), diff --git a/compiler/gen/tests/gen_list.rs b/compiler/gen/tests/gen_list.rs index 9f6243c325..8916d07586 100644 --- a/compiler/gen/tests/gen_list.rs +++ b/compiler/gen/tests/gen_list.rs @@ -1273,7 +1273,7 @@ mod gen_list { app "quicksort" provides [ main ] to "./platform" - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -1396,7 +1396,7 @@ mod gen_list { quicksortHelp list 0 (n - 1) - quicksortHelp : List (Num a), I64, I64 -> List (Num a) + quicksortHelp : List (Num a), Int *, Int * -> List (Num a) quicksortHelp = \list, low, high -> if low < high then when partition low high list is @@ -1408,7 +1408,7 @@ mod gen_list { list - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -1419,7 +1419,7 @@ mod gen_list { _ -> [] - partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] + partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -1431,7 +1431,7 @@ mod gen_list { Pair (low - 1) initialList - partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ] + partitionHelp : Int *, Int *, List (Num a), Int *, (Num a) -> [ Pair (Int *) (List (Num a)) ] partitionHelp = \i, j, list, high, pivot -> if j < high then when List.get list j is @@ -1466,7 +1466,7 @@ mod gen_list { quicksortHelp list 0 (List.len list - 1) - quicksortHelp : List (Num a), I64, I64 -> List (Num a) + quicksortHelp : List (Num a), Int *, Int * -> List (Num a) quicksortHelp = \list, low, high -> if low < high then when partition low high list is @@ -1478,7 +1478,7 @@ mod gen_list { list - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -1489,7 +1489,7 @@ mod gen_list { _ -> [] - partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] + partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -1501,7 +1501,7 @@ mod gen_list { Pair (low - 1) initialList - partitionHelp : I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ] + partitionHelp : Int *, Int *, List (Num a), Int *, Num a -> [ Pair (Int *) (List (Num a)) ] partitionHelp = \i, j, list, high, pivot -> # if j < high then if False then @@ -1539,7 +1539,7 @@ mod gen_list { quicksortHelp list 0 (List.len list - 1) - quicksortHelp : List (Num a), I64, I64 -> List (Num a) + quicksortHelp : List (Num a), Int *, Int * -> List (Num a) quicksortHelp = \list, low, high -> if low < high then when partition low high list is @@ -1551,7 +1551,7 @@ mod gen_list { list - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -1562,7 +1562,7 @@ mod gen_list { _ -> [] - partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] + partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -1574,7 +1574,7 @@ mod gen_list { Pair (low - 1) initialList - partitionHelp : I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ] + partitionHelp : Int *, Int *, List (Num a), Int *, Num a -> [ Pair (Int *) (List (Num a)) ] partitionHelp = \i, j, list, high, pivot -> if j < high then when List.get list j is diff --git a/compiler/gen/tests/gen_num.rs b/compiler/gen/tests/gen_num.rs index 2dc79e07f8..d041b17833 100644 --- a/compiler/gen/tests/gen_num.rs +++ b/compiler/gen/tests/gen_num.rs @@ -15,6 +15,357 @@ mod helpers; mod gen_num { use roc_std::RocOrder; + #[test] + fn nat_alias() { + assert_evals_to!( + indoc!( + r#" + i : Nat + i = 1 + + i + "# + ), + 1, + usize + ); + } + + #[test] + fn i128_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : I128 + i = 128 + + i + "# + ), + 128, + i128 + ); + } + #[test] + fn i64_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : I64 + i = 64 + + i + "# + ), + 64, + i64 + ); + } + #[test] + fn i32_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : I32 + i = 32 + + i + "# + ), + 32, + i32 + ); + } + #[test] + fn i16_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : I16 + i = 16 + + i + "# + ), + 16, + i16 + ); + } + #[test] + fn i8_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : I8 + i = 8 + + i + "# + ), + 8, + i8 + ); + } + + #[test] + fn i128_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : I128 + f = 0x123 + + f + "# + ), + 0x123, + i128 + ); + } + #[test] + fn i64_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : I64 + f = 0x123 + + f + "# + ), + 0x123, + i64 + ); + } + #[test] + fn i32_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : I32 + f = 0x123 + + f + "# + ), + 0x123, + i32 + ); + } + #[test] + fn i16_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : I16 + f = 0x123 + + f + "# + ), + 0x123, + i16 + ); + } + #[test] + fn i8_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : I8 + f = 0xA + + f + "# + ), + 0xA, + i8 + ); + } + + #[test] + fn u128_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : U128 + i = 128 + + i + "# + ), + 128, + u128 + ); + } + #[test] + fn u64_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : U64 + i = 64 + + i + "# + ), + 64, + u64 + ); + } + #[test] + fn u32_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : U32 + i = 32 + + i + "# + ), + 32, + u32 + ); + } + #[test] + fn u16_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : U16 + i = 16 + + i + "# + ), + 16, + u16 + ); + } + #[test] + fn u8_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : U8 + i = 8 + + i + "# + ), + 8, + u8 + ); + } + + #[test] + fn u128_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : U128 + f = 0x123 + + f + "# + ), + 0x123, + i128 + ); + } + #[test] + fn u64_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : U64 + f = 0x123 + + f + "# + ), + 0x123, + u64 + ); + } + #[test] + fn u32_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : U32 + f = 0x123 + + f + "# + ), + 0x123, + u32 + ); + } + #[test] + fn u16_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : U16 + f = 0x123 + + f + "# + ), + 0x123, + u16 + ); + } + #[test] + fn u8_hex_int_alias() { + assert_evals_to!( + indoc!( + r#" + f : U8 + f = 0xA + + f + "# + ), + 0xA, + u8 + ); + } + + #[test] + fn f64_float_alias() { + assert_evals_to!( + indoc!( + r#" + f : F64 + f = 3.6 + + f + "# + ), + 3.6, + f64 + ); + } + #[test] + fn f32_float_alias() { + assert_evals_to!( + indoc!( + r#" + f : F32 + f = 3.6 + + f + "# + ), + 3.6, + f32 + ); + } + #[test] fn f64_sqrt() { // FIXME this works with normal types, but fails when checking uniqueness types diff --git a/compiler/gen/tests/gen_primitives.rs b/compiler/gen/tests/gen_primitives.rs index b30b4cfa4e..eea164e70a 100644 --- a/compiler/gen/tests/gen_primitives.rs +++ b/compiler/gen/tests/gen_primitives.rs @@ -135,7 +135,7 @@ mod gen_primitives { assert_evals_to!( indoc!( r#" - x : [ Pair I64 I64 ] + x : [ Pair (Int *) (Int *) ] x = Pair 0x2 0x3 when x is @@ -152,7 +152,7 @@ mod gen_primitives { assert_evals_to!( indoc!( r#" - x : [A I64, B I64] + x : [A (Int *), B (Int *)] x = A 0x2 when x is @@ -170,7 +170,7 @@ mod gen_primitives { assert_evals_to!( indoc!( r#" - x : [A I64, B I64] + x : [A (Int *), B (Int *)] x = B 0x3 when x is @@ -293,7 +293,7 @@ mod gen_primitives { indoc!( r#" wrapper = \{} -> - alwaysFloatIdentity : I64 -> (F64 -> F64) + alwaysFloatIdentity : Int * -> (Float * -> Float *) alwaysFloatIdentity = \_ -> (\a -> a) @@ -557,14 +557,14 @@ mod gen_primitives { LinkedList a : [ Nil, Cons a (LinkedList a) ] - len : LinkedList a -> I64 + len : LinkedList a -> Int * len = \list -> when list is Nil -> 0 Cons _ rest -> 1 + len rest main = - nil : LinkedList I64 + nil : LinkedList (Int *) nil = Nil len nil @@ -584,10 +584,10 @@ mod gen_primitives { LinkedList a : [ Nil, Cons a (LinkedList a) ] - nil : LinkedList I64 + nil : LinkedList (Int *) nil = Nil - length : LinkedList a -> I64 + length : LinkedList a -> Int * length = \list -> when list is Nil -> 0 @@ -611,10 +611,10 @@ mod gen_primitives { LinkedList a : [ Nil, Cons a (LinkedList a) ] - one : LinkedList I64 + one : LinkedList (Int *) one = Cons 1 Nil - length : LinkedList a -> I64 + length : LinkedList a -> Int * length = \list -> when list is Nil -> 0 @@ -638,10 +638,10 @@ mod gen_primitives { LinkedList a : [ Nil, Cons a (LinkedList a) ] - one : LinkedList I64 + one : LinkedList (Int *) one = Cons 1 Nil - length : LinkedList a -> I64 + length : LinkedList a -> Int * length = \list -> when list is Nil -> 0 @@ -665,10 +665,10 @@ mod gen_primitives { LinkedList a : [ Nil, Cons a (LinkedList a) ] - three : LinkedList I64 + three : LinkedList (Int *) three = Cons 3 (Cons 2 (Cons 1 Nil)) - length : LinkedList a -> I64 + length : LinkedList a -> Int * length = \list -> when list is Nil -> 0 @@ -693,7 +693,7 @@ mod gen_primitives { LinkedList a : [ Nil, Cons a (LinkedList a) ] - three : LinkedList I64 + three : LinkedList (Int *) three = Cons 3 (Cons 2 (Cons 1 Nil)) @@ -721,10 +721,10 @@ mod gen_primitives { LinkedList a : [ Nil, Cons a (LinkedList a) ] - zero : LinkedList I64 + zero : LinkedList (Int *) zero = Nil - sum : LinkedList I64 -> I64 + sum : LinkedList (Int *) -> Int * sum = \list -> when list is Nil -> 0 @@ -748,7 +748,7 @@ mod gen_primitives { LinkedList a : [ Nil, Cons a (LinkedList a) ] - three : LinkedList I64 + three : LinkedList (Int *) three = Cons 3 (Cons 2 (Cons 1 Nil)) sum : LinkedList (Num a) -> Num a @@ -779,7 +779,7 @@ mod gen_primitives { r#" Maybe a : [ Nothing, Just a ] - x : Maybe (Maybe I64) + x : Maybe (Maybe (Int *)) x = Just (Just 41) when x is @@ -796,7 +796,7 @@ mod gen_primitives { r#" Maybe a : [ Nothing, Just a ] - x : Maybe (Maybe I64) + x : Maybe (Maybe (Int *)) x = Just Nothing when x is @@ -814,7 +814,7 @@ mod gen_primitives { r#" Maybe a : [ Nothing, Just a ] - x : Maybe (Maybe I64) + x : Maybe (Maybe (Int *)) x = Nothing when x is @@ -908,7 +908,7 @@ mod gen_primitives { assert_evals_to!( indoc!( r#" - foo : I64 + foo : Int * foo @@ -1033,11 +1033,11 @@ mod gen_primitives { runEffect : Effect a -> a runEffect = \@Effect thunk -> thunk {} - foo : Effect F64 + foo : Effect (Float *) foo = succeed 3.14 - main : F64 + main : Float * main = runEffect foo @@ -1058,14 +1058,14 @@ mod gen_primitives { # succeed : a -> ({} -> a) succeed = \x -> \{} -> x - foo : {} -> F64 + foo : {} -> Float * foo = succeed 3.14 # runEffect : ({} -> a) -> a runEffect = \thunk -> thunk {} - main : F64 + main : Float * main = runEffect foo "# @@ -1145,7 +1145,7 @@ mod gen_primitives { main : Bool main = - myList : ConsList I64 + myList : ConsList (Int *) myList = empty isEmpty myList @@ -1176,7 +1176,7 @@ mod gen_primitives { main : Bool main = - myList : ConsList I64 + myList : ConsList (Int *) myList = Cons 0x1 Nil isEmpty myList @@ -1194,16 +1194,16 @@ mod gen_primitives { r#" app "test" provides [ main ] to "./platform" - State a : { count : I64, x : a } + State a : { count : Int *, x : a } - foo : State a -> I64 + foo : State a -> Int * foo = \state -> if state.count == 0 then 0 else 1 + foo { count: state.count - 1, x: state.x } - main : I64 + main : Int * main = foo { count: 3, x: {} } "# @@ -1284,7 +1284,7 @@ mod gen_primitives { _ -> Node color key value left right - main : RedBlackTree I64 {} + main : RedBlackTree (Int *) {} main = insert 0 {} Empty "# @@ -1325,7 +1325,7 @@ mod gen_primitives { _ -> Empty - main : RedBlackTree I64 + main : RedBlackTree (Int *) main = balance Red 0 Empty Empty "# @@ -1348,7 +1348,7 @@ mod gen_primitives { balance = \key, left -> Node key left Empty - main : RedBlackTree I64 + main : RedBlackTree (Int *) main = balance 0 Empty "# @@ -1395,7 +1395,7 @@ mod gen_primitives { _ -> Empty - main : RedBlackTree I64 I64 + main : RedBlackTree (Int *) (Int *) main = balance Red 0 0 Empty Empty "# @@ -1445,7 +1445,7 @@ mod gen_primitives { _ -> Node color key value left right - main : RedBlackTree I64 I64 + main : RedBlackTree (Int *) (Int *) main = balance Red 0 0 Empty Empty "# @@ -1465,7 +1465,7 @@ mod gen_primitives { ConsList a : [ Cons a (ConsList a), Nil ] - balance : ConsList I64 -> I64 + balance : ConsList (Int *) -> Int * balance = \right -> when right is Cons 1 foo -> @@ -1474,7 +1474,7 @@ mod gen_primitives { _ -> 3 _ -> 3 - main : I64 + main : Int * main = when balance Nil is _ -> 3 @@ -1491,13 +1491,13 @@ mod gen_primitives { ConsList a : [ Cons a (ConsList a), Nil ] - balance : ConsList I64 -> I64 + balance : ConsList (Int *) -> Int * balance = \right -> when right is Cons 1 (Cons 1 _) -> 3 _ -> 3 - main : I64 + main : Int * main = when balance Nil is _ -> 3 @@ -1519,7 +1519,7 @@ mod gen_primitives { ConsList a : [ Cons a (ConsList a), Nil ] - balance : ConsList I64 -> I64 + balance : ConsList (Int *) -> Int * balance = \right -> when right is Cons 1 foo -> @@ -1528,7 +1528,7 @@ mod gen_primitives { _ -> 3 _ -> 3 - main : I64 + main : Int * main = when balance Nil is _ -> 3 @@ -1548,13 +1548,13 @@ mod gen_primitives { ConsList a : [ Cons a (ConsList a), Nil ] - foo : ConsList I64 -> I64 + foo : ConsList (Int *) -> Int * foo = \list -> when list is Cons _ (Cons x _) -> x _ -> 0 - main : I64 + main : Int * main = foo (Cons 1 (Cons 32 Nil)) "# @@ -1571,15 +1571,15 @@ mod gen_primitives { r#" app "test" provides [ main ] to "./platform" - BTree : [ Node BTree BTree, Leaf I64 ] + BTree : [ Node BTree BTree, Leaf (Int *) ] - foo : BTree -> I64 + foo : BTree -> Int * foo = \btree -> when btree is Node (Node (Leaf x) _) _ -> x _ -> 0 - main : I64 + main : Int * main = foo (Node (Node (Leaf 32) (Leaf 0)) (Leaf 0)) "# @@ -1603,7 +1603,7 @@ mod gen_primitives { A -> (\_ -> 3.14) B -> (\_ -> 3.14) - main : F64 + main : Float * main = (foo {}) 0 "# @@ -1646,7 +1646,7 @@ mod gen_primitives { Ok x -> transform x Err e -> fail e - main : Task {} F64 + main : Task {} (Float *) main = after (always "foo") (\_ -> always {}) "# @@ -1676,7 +1676,7 @@ mod gen_primitives { @Effect inner - main : Task {} F64 + main : Task {} (Float *) main = always {} "# ), @@ -1707,7 +1707,7 @@ mod gen_primitives { Task a err : Effect (Result a err) - always : a -> Task a F64 + always : a -> Task a (Float *) always = \x -> effectAlways (Ok x) # the problem is that this restricts to `Task {} *` @@ -1722,7 +1722,7 @@ mod gen_primitives { # but here it must be `forall b. Task b {}` Err e -> fail e - main : Task {} F64 + main : Task {} (Float *) main = after (always "foo") (\_ -> always {}) "# @@ -1774,7 +1774,7 @@ mod gen_primitives { assert_evals_to!( indoc!( r#" - x : Result I64 F64 + x : Result (Int *) (Float *) x = Ok 4 (Ok y) = x diff --git a/compiler/gen/tests/helpers/eval.rs b/compiler/gen/tests/helpers/eval.rs index bb52bf63a0..3fb2ff590e 100644 --- a/compiler/gen/tests/helpers/eval.rs +++ b/compiler/gen/tests/helpers/eval.rs @@ -41,6 +41,9 @@ pub fn helper<'a>( module_src = &temp; } + let target = target_lexicon::Triple::host(); + let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; + let exposed_types = MutMap::default(); let loaded = roc_load::file::load_and_monomorphize_from_str( arena, @@ -49,6 +52,7 @@ pub fn helper<'a>( stdlib, src_dir, exposed_types, + ptr_bytes, ); let mut loaded = loaded.expect("failed to load module"); @@ -73,9 +77,6 @@ pub fn helper<'a>( ), }; - let target = target_lexicon::Triple::host(); - let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; - let mut lines = Vec::new(); // errors whose reporting we delay (so we can see that code gen generates runtime errors) let mut delayed_errors = Vec::new(); @@ -308,7 +309,9 @@ macro_rules! assert_opt_evals_to { let context = Context::create(); - let stdlib = roc_builtins::unique::uniq_stdlib(); + // don't use uniqueness types any more + // let stdlib = roc_builtins::unique::uniq_stdlib(); + let stdlib = roc_builtins::std::standard_stdlib(); let (main_fn_name, errors, lib) = $crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context); diff --git a/compiler/gen_dev/tests/helpers/eval.rs b/compiler/gen_dev/tests/helpers/eval.rs index de46a74498..b464a0c10c 100644 --- a/compiler/gen_dev/tests/helpers/eval.rs +++ b/compiler/gen_dev/tests/helpers/eval.rs @@ -50,6 +50,7 @@ pub fn helper<'a>( stdlib, src_dir, exposed_types, + 8, ); let mut loaded = loaded.expect("failed to load module"); diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 3a632df6c6..4e28a4db4a 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -947,6 +947,7 @@ pub fn load_and_typecheck( stdlib: StdLib, src_dir: &Path, exposed_types: SubsByModule, + ptr_bytes: u32, ) -> Result { use LoadResult::*; @@ -959,6 +960,7 @@ pub fn load_and_typecheck( src_dir, exposed_types, Phase::SolveTypes, + ptr_bytes, )? { Monomorphized(_) => unreachable!(""), TypeChecked(module) => Ok(module), @@ -971,6 +973,7 @@ pub fn load_and_monomorphize<'a>( stdlib: StdLib, src_dir: &Path, exposed_types: SubsByModule, + ptr_bytes: u32, ) -> Result, LoadingProblem> { use LoadResult::*; @@ -983,6 +986,7 @@ pub fn load_and_monomorphize<'a>( src_dir, exposed_types, Phase::MakeSpecializations, + ptr_bytes, )? { Monomorphized(module) => Ok(module), TypeChecked(_) => unreachable!(""), @@ -996,6 +1000,7 @@ pub fn load_and_monomorphize_from_str<'a>( stdlib: StdLib, src_dir: &Path, exposed_types: SubsByModule, + ptr_bytes: u32, ) -> Result, LoadingProblem> { use LoadResult::*; @@ -1008,6 +1013,7 @@ pub fn load_and_monomorphize_from_str<'a>( src_dir, exposed_types, Phase::MakeSpecializations, + ptr_bytes, )? { Monomorphized(module) => Ok(module), TypeChecked(_) => unreachable!(""), @@ -1144,6 +1150,7 @@ fn load<'a>( src_dir: &Path, exposed_types: SubsByModule, goal_phase: Phase, + ptr_bytes: u32, ) -> Result, LoadingProblem> where { @@ -1259,8 +1266,14 @@ where // added. In that case, do nothing, and keep waiting // until we receive a Shutdown message. if let Some(task) = find_task(&worker, injector, stealers) { - run_task(task, worker_arena, src_dir, msg_tx.clone()) - .expect("Msg channel closed unexpectedly."); + run_task( + task, + worker_arena, + src_dir, + msg_tx.clone(), + ptr_bytes, + ) + .expect("Msg channel closed unexpectedly."); } } } @@ -3341,6 +3354,7 @@ fn make_specializations<'a>( mut layout_cache: LayoutCache<'a>, specializations_we_must_make: ExternalSpecializations, mut module_timing: ModuleTiming, + ptr_bytes: u32, ) -> Msg<'a> { let make_specializations_start = SystemTime::now(); let mut mono_problems = Vec::new(); @@ -3351,6 +3365,7 @@ fn make_specializations<'a>( subs: &mut subs, home, ident_ids: &mut ident_ids, + ptr_bytes, }; procs @@ -3396,6 +3411,7 @@ fn build_pending_specializations<'a>( decls: Vec, mut module_timing: ModuleTiming, mut layout_cache: LayoutCache<'a>, + ptr_bytes: u32, // TODO remove exposed_to_host: MutMap, ) -> Msg<'a> { @@ -3410,6 +3426,7 @@ fn build_pending_specializations<'a>( subs: &mut subs, home, ident_ids: &mut ident_ids, + ptr_bytes, }; // Add modules' decls to Procs @@ -3613,6 +3630,7 @@ fn run_task<'a>( arena: &'a Bump, src_dir: &Path, msg_tx: MsgSender<'a>, + ptr_bytes: u32, ) -> Result<(), LoadingProblem> { use BuildTask::*; @@ -3685,6 +3703,7 @@ fn run_task<'a>( decls, module_timing, layout_cache, + ptr_bytes, exposed_to_host, )), MakeSpecializations { @@ -3704,6 +3723,7 @@ fn run_task<'a>( layout_cache, specializations_we_must_make, module_timing, + ptr_bytes, )), }?; diff --git a/compiler/load/tests/fixtures/build/app_with_deps/Quicksort.roc b/compiler/load/tests/fixtures/build/app_with_deps/Quicksort.roc index 8ea4ef5b59..4bc1d9d9c8 100644 --- a/compiler/load/tests/fixtures/build/app_with_deps/Quicksort.roc +++ b/compiler/load/tests/fixtures/build/app_with_deps/Quicksort.roc @@ -1,6 +1,6 @@ app "quicksort" provides [ swap, partition, partitionHelp, quicksort ] to "./platform" -quicksort : List (Num a), I64, I64 -> List (Num a) +quicksort : List (Num a), Int *, Int * -> List (Num a) quicksort = \list, low, high -> when partition low high list is Pair partitionIndex partitioned -> @@ -9,7 +9,7 @@ quicksort = \list, low, high -> |> quicksort (partitionIndex + 1) high -swap : I64, I64, List a -> List a +swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -21,7 +21,7 @@ swap = \i, j, list -> [] -partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] +partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -33,7 +33,7 @@ partition = \low, high, initialList -> Pair (low - 1) initialList -partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ] +partitionHelp : Int *, Int *, List (Num a), Int *, (Num a) -> [ Pair (Int *) (List (Num a)) ] partitionHelp = \i, j, list, high, pivot -> if j < high then when List.get list j is diff --git a/compiler/load/tests/fixtures/build/app_with_deps/QuicksortOneDef.roc b/compiler/load/tests/fixtures/build/app_with_deps/QuicksortOneDef.roc index 602d82f511..f2219ede08 100644 --- a/compiler/load/tests/fixtures/build/app_with_deps/QuicksortOneDef.roc +++ b/compiler/load/tests/fixtures/build/app_with_deps/QuicksortOneDef.roc @@ -1,7 +1,7 @@ app "quicksort" provides [ quicksort ] to "./platform" quicksort = \originalList -> - quicksortHelp : List (Num a), I64, I64 -> List (Num a) + quicksortHelp : List (Num a), Int *, Int * -> List (Num a) quicksortHelp = \list, low, high -> if low < high then when partition low high list is @@ -13,7 +13,7 @@ quicksort = \originalList -> list - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -24,7 +24,7 @@ quicksort = \originalList -> _ -> [] - partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] + partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -36,7 +36,7 @@ quicksort = \originalList -> Pair (low - 1) initialList - partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ] + partitionHelp : Int *, Int *, List (Num a), Int *, (Num a) -> [ Pair (Int *) (List (Num a)) ] partitionHelp = \i, j, list, high, pivot -> if j < high then when List.get list j is diff --git a/compiler/load/tests/fixtures/build/interface_with_deps/Quicksort.roc b/compiler/load/tests/fixtures/build/interface_with_deps/Quicksort.roc index 341cf99126..98f07a4f20 100644 --- a/compiler/load/tests/fixtures/build/interface_with_deps/Quicksort.roc +++ b/compiler/load/tests/fixtures/build/interface_with_deps/Quicksort.roc @@ -2,7 +2,7 @@ interface Quicksort exposes [ swap, partition, quicksort ] imports [] -quicksort : List (Num a), I64, I64 -> List (Num a) +quicksort : List (Num a), Int *, Int * -> List (Num a) quicksort = \list, low, high -> when partition low high list is Pair partitionIndex partitioned -> @@ -11,7 +11,7 @@ quicksort = \list, low, high -> |> quicksort (partitionIndex + 1) high -swap : I64, I64, List a -> List a +swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -23,7 +23,7 @@ swap = \i, j, list -> [] -partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] +partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -35,7 +35,7 @@ partition = \low, high, initialList -> Pair (low - 1) initialList -partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ] +partitionHelp : Int *, Int *, List (Num a), Int *, (Num a) -> [ Pair (Int *) (List (Num a)) ] partitionHelp = \i, j, list, high, pivot -> if j < high then when List.get list j is diff --git a/compiler/load/tests/test_load.rs b/compiler/load/tests/test_load.rs index 75b18597a1..1477e4fcf7 100644 --- a/compiler/load/tests/test_load.rs +++ b/compiler/load/tests/test_load.rs @@ -85,6 +85,7 @@ mod test_load { stdlib, dir.path(), exposed_types, + 8, ) }; @@ -126,6 +127,7 @@ mod test_load { roc_builtins::std::standard_stdlib(), src_dir.as_path(), subs_by_module, + 8, ); let mut loaded_module = loaded.expect("Test module failed to load"); @@ -288,6 +290,7 @@ mod test_load { roc_builtins::std::standard_stdlib(), src_dir.as_path(), subs_by_module, + 8, ); let mut loaded_module = loaded.expect("Test module failed to load"); @@ -357,14 +360,14 @@ mod test_load { expect_types( loaded_module, hashmap! { - "floatTest" => "F64", - "divisionFn" => "F64, F64 -> Result F64 [ DivByZero ]*", - "divisionTest" => "Result F64 [ DivByZero ]*", - "intTest" => "I64", - "x" => "F64", + "floatTest" => "Float *", + "divisionFn" => "Float a, Float a -> Result (Float a) [ DivByZero ]*", + "divisionTest" => "Result (Float *) [ DivByZero ]*", + "intTest" => "Int *", + "x" => "Float *", "constantNum" => "Num *", - "divDep1ByDep2" => "Result F64 [ DivByZero ]*", - "fromDep2" => "F64", + "divDep1ByDep2" => "Result (Float *) [ DivByZero ]*", + "fromDep2" => "Float *", }, ); } @@ -377,10 +380,10 @@ mod test_load { expect_types( loaded_module, hashmap! { - "swap" => "I64, I64, List a -> List a", - "partition" => "I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]", - "partitionHelp" => "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]", - "quicksort" => "List (Num a), I64, I64 -> List (Num a)", + "swap" => "Int *, Int *, List a -> List a", + "partition" => "Int b, Int b, List (Num a) -> [ Pair (Int b) (List (Num a)) ]", + "partitionHelp" => "Int b, Int c, List (Num a), Int c, Num a -> [ Pair (Int b) (List (Num a)) ]", + "quicksort" => "List (Num a), Int b, Int b -> List (Num a)", }, ); } @@ -406,10 +409,10 @@ mod test_load { expect_types( loaded_module, hashmap! { - "swap" => "I64, I64, List a -> List a", - "partition" => "I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]", - "partitionHelp" => "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]", - "quicksort" => "List (Num a), I64, I64 -> List (Num a)", + "swap" => "Int *, Int *, List a -> List a", + "partition" => "Int b, Int b, List (Num a) -> [ Pair (Int b) (List (Num a)) ]", + "partitionHelp" => "Int b, Int c, List (Num a), Int c, Num a -> [ Pair (Int b) (List (Num a)) ]", + "quicksort" => "List (Num a), Int b, Int b -> List (Num a)", }, ); } @@ -454,7 +457,7 @@ mod test_load { expect_types( loaded_module, hashmap! { - "blah2" => "F64", + "blah2" => "Float *", "blah3" => "Str", "str" => "Str", "alwaysThree" => "* -> Str", @@ -476,7 +479,7 @@ mod test_load { expect_types( loaded_module, hashmap! { - "blah2" => "F64", + "blah2" => "Float *", "blah3" => "Str", "str" => "Str", "alwaysThree" => "* -> Str", diff --git a/compiler/load/tests/test_uniq_load.rs b/compiler/load/tests/test_uniq_load.rs deleted file mode 100644 index 643bc1dd29..0000000000 --- a/compiler/load/tests/test_uniq_load.rs +++ /dev/null @@ -1,423 +0,0 @@ -#[macro_use] -extern crate pretty_assertions; -#[macro_use] -extern crate maplit; - -extern crate bumpalo; -extern crate inlinable_string; -extern crate roc_collections; -extern crate roc_load; -extern crate roc_module; - -mod helpers; - -#[cfg(test)] -mod test_uniq_load { - use crate::helpers::fixtures_dir; - use bumpalo::Bump; - use inlinable_string::InlinableString; - use roc_builtins::unique; - use roc_can::def::Declaration::*; - use roc_can::def::Def; - use roc_collections::all::MutMap; - use roc_constrain::module::SubsByModule; - use roc_load::file::LoadedModule; - use roc_module::ident::ModuleName; - use roc_module::symbol::{Interns, ModuleId}; - use roc_types::pretty_print::{content_to_string, name_all_type_vars}; - use roc_types::subs::Subs; - use std::collections::HashMap; - - // HELPERS - - fn load_fixture( - dir_name: &str, - module_name: &str, - subs_by_module: SubsByModule, - ) -> LoadedModule { - let arena = Bump::new(); - let src_dir = fixtures_dir().join(dir_name); - let filename = src_dir.join(format!("{}.roc", module_name)); - let loaded = roc_load::file::load_and_typecheck( - &arena, - filename, - unique::uniq_stdlib(), - src_dir.as_path(), - subs_by_module, - ); - let mut loaded_module = loaded.expect("Test module failed to load"); - - let home = loaded_module.module_id; - - assert_eq!( - loaded_module.can_problems.remove(&home).unwrap_or_default(), - Vec::new() - ); - assert_eq!( - loaded_module - .type_problems - .remove(&home) - .unwrap_or_default(), - Vec::new() - ); - - let expected_name = loaded_module - .interns - .module_ids - .get_name(loaded_module.module_id) - .expect("Test ModuleID not found in module_ids"); - - // App module names are hardcoded and not based on anything user-specified - if expected_name != ModuleName::APP { - assert_eq!(expected_name, &InlinableString::from(module_name)); - } - - loaded_module - } - - fn expect_def( - interns: &Interns, - subs: &mut Subs, - home: ModuleId, - def: &Def, - expected_types: &mut HashMap<&str, &str>, - ) { - for (symbol, expr_var) in &def.pattern_vars { - let content = subs.get(*expr_var).content; - - name_all_type_vars(*expr_var, subs); - - let actual_str = content_to_string(content, subs, home, &interns); - let fully_qualified = symbol.fully_qualified(&interns, home).to_string(); - let expected_type = expected_types - .remove(fully_qualified.as_str()) - .unwrap_or_else(|| { - panic!("Defs included an unexpected symbol: {:?}", fully_qualified) - }); - - assert_eq!((&symbol, expected_type), (&symbol, actual_str.as_str())); - } - } - - fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&str, &str>) { - let home = loaded_module.module_id; - let mut subs = loaded_module.solved.into_inner(); - - assert_eq!( - loaded_module.can_problems.remove(&home).unwrap_or_default(), - Vec::new() - ); - assert_eq!( - loaded_module - .type_problems - .remove(&home) - .unwrap_or_default(), - Vec::new() - ); - - for decl in loaded_module.declarations_by_id.remove(&home).unwrap() { - match decl { - Declare(def) => expect_def( - &loaded_module.interns, - &mut subs, - home, - &def, - &mut expected_types, - ), - DeclareRec(defs) => { - for def in defs { - expect_def( - &loaded_module.interns, - &mut subs, - home, - &def, - &mut expected_types, - ); - } - } - Builtin(_) => {} - cycle @ InvalidCycle(_, _) => { - panic!("Unexpected cyclic def in module declarations: {:?}", cycle); - } - }; - } - - assert_eq!( - expected_types, - HashMap::default(), - "Some expected types were not found in the defs" - ); - } - - // TESTS - - #[test] - fn interface_with_deps() { - let arena = Bump::new(); - let subs_by_module = MutMap::default(); - let src_dir = fixtures_dir().join("interface_with_deps"); - let filename = src_dir.join("Primary.roc"); - let loaded = roc_load::file::load_and_typecheck( - &arena, - filename, - roc_builtins::std::standard_stdlib(), - src_dir.as_path(), - subs_by_module, - ); - - let mut loaded_module = loaded.expect("Test module failed to load"); - let home = loaded_module.module_id; - - assert_eq!( - loaded_module.can_problems.remove(&home).unwrap_or_default(), - Vec::new() - ); - assert_eq!( - loaded_module - .type_problems - .remove(&home) - .unwrap_or_default(), - Vec::new() - ); - - let def_count: usize = loaded_module - .declarations_by_id - .remove(&loaded_module.module_id) - .unwrap() - .into_iter() - .map(|decl| decl.def_count()) - .sum(); - - let expected_name = loaded_module - .interns - .module_ids - .get_name(loaded_module.module_id) - .expect("Test ModuleID not found in module_ids"); - - assert_eq!(expected_name, &InlinableString::from("Primary")); - assert_eq!(def_count, 10); - } - - #[test] - fn load_unit() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("no_deps", "Unit", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "unit" => "Attr * Unit", - }, - ); - } - - #[test] - fn import_alias() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("interface_with_deps", "ImportAlias", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "unit" => "Attr * Dep1.Unit", - }, - ); - } - - #[test] - fn load_and_typecheck() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("interface_with_deps", "WithBuiltins", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "floatTest" => "Attr Shared F64", - "divisionFn" => "Attr Shared (Attr * F64, Attr * F64 -> Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*)))", - "divisionTest" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))", - "intTest" => "Attr * I64", - "x" => "Attr * F64", - "constantNum" => "Attr * (Num (Attr * *))", - "divDep1ByDep2" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))", - "fromDep2" => "Attr * F64", - }, - ); - } - - #[test] - #[ignore] - fn load_astar() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("interface_with_deps", "AStar", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "findPath" => "Attr * (Attr * { costFunction : Attr Shared (Attr Shared position, Attr Shared position -> Attr * F64), end : Attr Shared position, moveFunction : Attr Shared (Attr Shared position -> Attr * (Set (Attr * position))), start : Attr Shared position } -> Attr * (Result (Attr * (List (Attr Shared position))) (Attr * [ KeyNotFound ]*)))", - "initialModel" => "Attr * (Attr Shared position -> Attr * (Model (Attr Shared position)))", - "reconstructPath" => "Attr Shared (Attr Shared (Map (Attr * position) (Attr Shared position)), Attr Shared position -> Attr * (List (Attr Shared position)))", - "updateCost" => "Attr * (Attr Shared position, Attr Shared position, Attr Shared (Model (Attr Shared position)) -> Attr Shared (Model (Attr Shared position)))", - "cheapestOpen" => "Attr * (Attr * (Attr Shared position -> Attr * F64), Attr (* | a | b | c) (Model (Attr Shared position)) -> Attr * (Result (Attr Shared position) (Attr * [ KeyNotFound ]*)))", - "astar" => "Attr Shared (Attr Shared (Attr Shared position, Attr Shared position -> Attr * F64), Attr Shared (Attr Shared position -> Attr * (Set (Attr * position))), Attr Shared position, Attr Shared (Model (Attr Shared position)) -> Attr * [ Err (Attr * [ KeyNotFound ]*), Ok (Attr * (List (Attr Shared position))) ]*)", - }, - ); - } - - #[test] - fn load_and_typecheck_quicksort() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("interface_with_deps", "Quicksort", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "swap" => "Attr * (Attr * I64, Attr * I64, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))", - "partition" => "Attr * (Attr Shared I64, Attr Shared I64, Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr * [ Pair (Attr * I64) (Attr b (List (Attr Shared (Num (Attr Shared a))))) ])", - - "partitionHelp" => "Attr Shared (Attr b I64, Attr Shared I64, Attr c (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared (Num (Attr Shared a)) -> Attr * [ Pair (Attr b I64) (Attr c (List (Attr Shared (Num (Attr Shared a))))) ])", - "quicksort" => "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared I64 -> Attr b (List (Attr Shared (Num (Attr Shared a)))))", - }, - ); - } - - #[test] - fn quickcheck_nested_let() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("app_with_deps", "QuicksortOneDef", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "quicksort" => "Attr * (Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr b (List (Attr Shared (Num (Attr Shared a)))))", - }, - ); - } - - #[test] - fn load_principal_types() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("no_deps", "Principal", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "intVal" => "Attr * Str", - "identity" => "Attr * (a -> a)", - }, - ); - } - - #[test] - #[ignore] - fn load_dep_types() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("interface_with_deps", "Primary", subs_by_module); - - // the inferred signature for withDefault is wrong, part of the alias in alias issue. - // "withDefault" => "Attr * (Attr * (Res.Res (Attr a b) (Attr * *)), Attr a b -> Attr a b)", - expect_types( - loaded_module, - hashmap! { - "blah2" => "Attr * F64", - "blah3" => "Attr * Str", - "str" => "Attr * Str", - "alwaysThree" => "Attr * (* -> Attr * Str)", - "identity" => "Attr * (a -> a)", - "z" => "Attr * Str", - "w" => "Attr * (Dep1.Identity (Attr * {}))", - "succeed" => "Attr * (Attr b a -> Attr * (Dep1.Identity (Attr b a)))", - "yay" => "Attr * (Res.Res (Attr * {}) (Attr * err))", - "withDefault" => "Attr * (Attr (* | b | c) (Res.Res (Attr b a) (Attr c *)), Attr b a -> Attr b a)", - }, - ); - } - - #[test] - #[ignore] - fn load_custom_res() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("interface_with_deps", "Res", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "withDefault" =>"Attr * (Attr (* | b | c) (Res (Attr b a) (Attr c err)), Attr b a -> Attr b a)", - "map" => "Attr * (Attr (* | c | d) (Res (Attr c a) (Attr d err)), Attr * (Attr c a -> Attr e b) -> Attr * (Res (Attr e b) (Attr d err)))", - "andThen" => "Attr * (Attr (* | c | d) (Res (Attr c a) (Attr d err)), Attr * (Attr c a -> Attr f (Res (Attr e b) (Attr d err))) -> Attr f (Res (Attr e b) (Attr d err)))", - }, - ); - } - - #[test] - fn imported_dep_regression() { - let subs_by_module = MutMap::default(); - let loaded_module = load_fixture("interface_with_deps", "OneDep", subs_by_module); - - expect_types( - loaded_module, - hashmap! { - "str" => "Attr * Str", - }, - ); - } - - // #[test] - // fn load_records() { - // test_async(async { - // use roc::types::{ErrorType, Mismatch, Problem, TypeExt}; - - // let subs_by_module = MutMap::default(); - // let loaded_module = - // load_fixture("interface_with_deps", "Records", subs_by_module); - - // // NOTE: `a` here is unconstrained, so unifies with - // let expected_types = hashmap! { - // "Records.intVal" => "a", - // }; - - // let a = ErrorType::FlexVar("a".into()); - - // let mut record = SendMap::default(); - // record.insert("x".into(), a); - - // let problem = Problem::Mismatch( - // Mismatch::TypeMismatch, - // ErrorType::Record(SendMap::default(), TypeExt::Closed), - // ErrorType::Record(record, TypeExt::FlexOpen("b".into())), - // ); - - // assert_eq!(loaded_module.problems, vec![problem]); - // assert_eq!(expected_types.len(), loaded_module.declarations.len()); - - // let mut subs = loaded_module.solved.into_inner(); - - // for decl in loaded_module.declarations { - // let def = match decl { - // Declare(def) => def, - // rec_decl @ DeclareRec(_) => { - // panic!( - // "Unexpected recursive def in module declarations: {:?}", - // rec_decl - // ); - // } - // cycle @ InvalidCycle(_, _) => { - // panic!("Unexpected cyclic def in module declarations: {:?}", cycle); - // } - // }; - - // for (symbol, expr_var) in def.pattern_vars { - // let content = subs.get(expr_var).content; - - // name_all_type_vars(expr_var, &mut subs); - - // let actual_str = content_to_string(content, &mut subs); - // let expected_type = expected_types.get(symbol.as_str()).unwrap_or_else(|| { - // panic!("Defs included an unexpected symbol: {:?}", symbol) - // }); - - // assert_eq!((&symbol, expected_type), (&symbol, &actual_str.as_str())); - // } - // } - // }); - // } -} diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 7d226160ac..c749068f1a 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -827,6 +827,11 @@ define_builtins! { 83 NUM_SUB_CHECKED: "subChecked" 84 NUM_MUL_WRAP: "mulWrap" 85 NUM_MUL_CHECKED: "mulChecked" + 86 NUM_INT: "Int" imported + 87 NUM_FLOAT: "Float" imported + 88 NUM_AT_NATURAL: "@Natural" + 89 NUM_NATURAL: "Natural" imported + 90 NUM_NAT: "Nat" imported } 2 BOOL: "Bool" => { 0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index d64020b9af..6c0015129f 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -445,10 +445,10 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V num_alts: union.alternatives.len(), }); } - IntLiteral(v) => { + IntLiteral(_, v) => { all_tests.push(guarded(IsInt(*v))); } - FloatLiteral(v) => { + FloatLiteral(_, v) => { all_tests.push(IsFloat(*v)); } StrLiteral(v) => { @@ -636,7 +636,7 @@ fn to_relevant_branch_help<'a>( _ => None, }, - IntLiteral(int) => match test { + IntLiteral(_, int) => match test { IsInt(is_int) if int == *is_int => { start.extend(end); Some(Branch { @@ -647,7 +647,7 @@ fn to_relevant_branch_help<'a>( _ => None, }, - FloatLiteral(float) => match test { + FloatLiteral(_, float) => match test { IsFloat(test_float) if float == *test_float => { start.extend(end); Some(Branch { @@ -740,8 +740,8 @@ fn needs_tests(pattern: &Pattern) -> bool { | AppliedTag { .. } | BitLiteral { .. } | EnumLiteral { .. } - | IntLiteral(_) - | FloatLiteral(_) + | IntLiteral(_, _) + | FloatLiteral(_, _) | StrLiteral(_) => true, } } diff --git a/compiler/mono/src/exhaustive.rs b/compiler/mono/src/exhaustive.rs index 13a3e113fe..8e6442cf08 100644 --- a/compiler/mono/src/exhaustive.rs +++ b/compiler/mono/src/exhaustive.rs @@ -48,8 +48,8 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern { use crate::ir::Pattern::*; match pattern { - IntLiteral(v) => Literal(Literal::Int(*v)), - FloatLiteral(v) => Literal(Literal::Float(*v)), + IntLiteral(_, v) => Literal(Literal::Int(*v)), + FloatLiteral(_, v) => Literal(Literal::Float(*v)), StrLiteral(v) => Literal(Literal::Str(v.clone())), // To make sure these are exhaustive, we have to "fake" a union here diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index b8c2a8221d..5942e55e2d 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1,6 +1,6 @@ use self::InProgressProc::*; use crate::exhaustive::{Ctor, Guard, RenderAs, TagId}; -use crate::layout::{Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem}; +use crate::layout::{Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem, TAG_SIZE}; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_collections::all::{default_hasher, MutMap, MutSet}; @@ -691,6 +691,7 @@ pub struct Env<'a, 'i> { pub problems: &'i mut std::vec::Vec, pub home: ModuleId, pub ident_ids: &'i mut IdentIds, + pub ptr_bytes: u32, } impl<'a, 'i> Env<'a, 'i> { @@ -1492,7 +1493,7 @@ fn pattern_to_when<'a>( (symbol, Located::at_zero(wrapped_body)) } - IntLiteral(_) | NumLiteral(_, _) | FloatLiteral(_) | StrLiteral(_) => { + IntLiteral(_, _) | NumLiteral(_, _) | FloatLiteral(_, _) | StrLiteral(_) => { // These patters are refutable, and thus should never occur outside a `when` expression // They should have been replaced with `UnsupportedPattern` during canonicalization unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value) @@ -2357,19 +2358,41 @@ pub fn with_hole<'a>( let arena = env.arena; match can_expr { - Int(_, num) => Stmt::Let( - assigned, - Expr::Literal(Literal::Int(num)), - Layout::Builtin(Builtin::Int64), - hole, - ), + Int(_, precision, num) => { + match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, false) { + IntOrFloat::SignedIntType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Int(num)), + Layout::Builtin(int_precision_to_builtin(precision)), + hole, + ), + IntOrFloat::UnsignedIntType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Int(num)), + Layout::Builtin(int_precision_to_builtin(precision)), + hole, + ), + _ => unreachable!("unexpected float precision for integer"), + } + } - Float(_, num) => Stmt::Let( - assigned, - Expr::Literal(Literal::Float(num)), - Layout::Builtin(Builtin::Float64), - hole, - ), + Float(_, precision, num) => { + match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, true) { + IntOrFloat::BinaryFloatType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Float(num as f64)), + Layout::Builtin(float_precision_to_builtin(precision)), + hole, + ), + IntOrFloat::DecimalFloatType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Float(num as f64)), + Layout::Builtin(float_precision_to_builtin(precision)), + hole, + ), + _ => unreachable!("unexpected float precision for integer"), + } + } Str(string) => Stmt::Let( assigned, @@ -2378,17 +2401,29 @@ pub fn with_hole<'a>( hole, ), - Num(var, num) => match num_argument_to_int_or_float(env.subs, var) { - IntOrFloat::IntType => Stmt::Let( + Num(var, num) => match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) { + IntOrFloat::SignedIntType(precision) => Stmt::Let( assigned, Expr::Literal(Literal::Int(num)), - Layout::Builtin(Builtin::Int64), + Layout::Builtin(int_precision_to_builtin(precision)), hole, ), - IntOrFloat::FloatType => Stmt::Let( + IntOrFloat::UnsignedIntType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Int(num)), + Layout::Builtin(int_precision_to_builtin(precision)), + hole, + ), + IntOrFloat::BinaryFloatType(precision) => Stmt::Let( assigned, Expr::Literal(Literal::Float(num as f64)), - Layout::Builtin(Builtin::Float64), + Layout::Builtin(float_precision_to_builtin(precision)), + hole, + ), + IntOrFloat::DecimalFloatType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Float(num as f64)), + Layout::Builtin(float_precision_to_builtin(precision)), hole, ), }, @@ -2778,7 +2813,7 @@ pub fn with_hole<'a>( stmt = Stmt::Let( tag_id_symbol, Expr::Literal(Literal::Int(tag_id as i64)), - Layout::Builtin(Builtin::Int64), + Layout::Builtin(TAG_SIZE), arena.alloc(stmt), ); @@ -4739,8 +4774,8 @@ fn store_pattern<'a>( Underscore => { // do nothing } - IntLiteral(_) - | FloatLiteral(_) + IntLiteral(_, _) + | FloatLiteral(_, _) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => {} @@ -4754,7 +4789,7 @@ fn store_pattern<'a>( if write_tag { // add an element for the tag discriminant - arg_layouts.push(Layout::Builtin(Builtin::Int64)); + arg_layouts.push(Layout::Builtin(TAG_SIZE)); } for (_, layout) in arguments { @@ -4779,8 +4814,8 @@ fn store_pattern<'a>( Underscore => { // ignore } - IntLiteral(_) - | FloatLiteral(_) + IntLiteral(_, _) + | FloatLiteral(_, _) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => {} @@ -4874,8 +4909,8 @@ fn store_record_destruct<'a>( // // internally. But `y` is never used, so we must make sure it't not stored/loaded. } - IntLiteral(_) - | FloatLiteral(_) + IntLiteral(_, _) + | FloatLiteral(_, _) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => {} @@ -5619,9 +5654,8 @@ fn call_by_name<'a>( pub enum Pattern<'a> { Identifier(Symbol), Underscore, - - IntLiteral(i64), - FloatLiteral(u64), + IntLiteral(Variable, i64), + FloatLiteral(Variable, u64), BitLiteral { value: bool, tag_name: TagName, @@ -5694,23 +5728,28 @@ fn from_can_pattern_help<'a>( match can_pattern { Underscore => Ok(Pattern::Underscore), Identifier(symbol) => Ok(Pattern::Identifier(*symbol)), - IntLiteral(v) => Ok(Pattern::IntLiteral(*v)), - FloatLiteral(v) => Ok(Pattern::FloatLiteral(f64::to_bits(*v))), + IntLiteral(precision_var, int) => Ok(Pattern::IntLiteral(*precision_var, *int)), + FloatLiteral(precision_var, float) => { + Ok(Pattern::FloatLiteral(*precision_var, f64::to_bits(*float))) + } StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())), Shadowed(region, ident) => Err(RuntimeError::Shadowing { original_region: *region, shadow: ident.clone(), }), UnsupportedPattern(region) => Err(RuntimeError::UnsupportedPattern(*region)), - MalformedPattern(_problem, region) => { // TODO preserve malformed problem information here? Err(RuntimeError::UnsupportedPattern(*region)) } - NumLiteral(var, num) => match num_argument_to_int_or_float(env.subs, *var) { - IntOrFloat::IntType => Ok(Pattern::IntLiteral(*num)), - IntOrFloat::FloatType => Ok(Pattern::FloatLiteral(*num as u64)), - }, + NumLiteral(var, num) => { + match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) { + IntOrFloat::SignedIntType(_) => Ok(Pattern::IntLiteral(*var, *num)), + IntOrFloat::UnsignedIntType(_) => Ok(Pattern::IntLiteral(*var, *num)), + IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(*var, *num as u64)), + IntOrFloat::DecimalFloatType(_) => Ok(Pattern::FloatLiteral(*var, *num as u64)), + } + } AppliedTag { whole_var, @@ -5798,13 +5837,11 @@ fn from_can_pattern_help<'a>( let mut arguments = arguments.clone(); arguments.sort_by(|arg1, arg2| { - let ptr_bytes = 8; - let layout1 = layout_cache.from_var(env.arena, arg1.0, env.subs).unwrap(); let layout2 = layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap(); - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(env.ptr_bytes); + let size2 = layout2.alignment_bytes(env.ptr_bytes); size2.cmp(&size1) }); @@ -5855,13 +5892,11 @@ fn from_can_pattern_help<'a>( let mut arguments = arguments.clone(); arguments.sort_by(|arg1, arg2| { - let ptr_bytes = 8; - let layout1 = layout_cache.from_var(env.arena, arg1.0, env.subs).unwrap(); let layout2 = layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap(); - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(env.ptr_bytes); + let size2 = layout2.alignment_bytes(env.ptr_bytes); size2.cmp(&size1) }); @@ -6084,40 +6119,150 @@ fn optimize_low_level( } } +pub enum IntPrecision { + I128, + I64, + I32, + I16, + I8, +} + +pub enum FloatPrecision { + F64, + F32, +} + pub enum IntOrFloat { - IntType, - FloatType, + SignedIntType(IntPrecision), + UnsignedIntType(IntPrecision), + BinaryFloatType(FloatPrecision), + DecimalFloatType(FloatPrecision), +} + +fn float_precision_to_builtin(precision: FloatPrecision) -> Builtin<'static> { + use FloatPrecision::*; + match precision { + F64 => Builtin::Float64, + F32 => Builtin::Float32, + } +} + +fn int_precision_to_builtin(precision: IntPrecision) -> Builtin<'static> { + use IntPrecision::*; + match precision { + I128 => Builtin::Int128, + I64 => Builtin::Int64, + I32 => Builtin::Int32, + I16 => Builtin::Int16, + I8 => Builtin::Int8, + } } /// Given the `a` in `Num a`, determines whether it's an int or a float -pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat { +pub fn num_argument_to_int_or_float( + subs: &Subs, + ptr_bytes: u32, + var: Variable, + known_to_be_float: bool, +) -> IntOrFloat { match subs.get_without_compacting(var).content { - Content::Alias(Symbol::NUM_INTEGER, args, _) => { + Content::FlexVar(_) if known_to_be_float => IntOrFloat::BinaryFloatType(FloatPrecision::F64), + Content::FlexVar(_) => IntOrFloat::SignedIntType(IntPrecision::I64), // We default (Num *) to I64 + + Content::Alias(Symbol::NUM_INTEGER, args, _) => { debug_assert!(args.len() == 1); - // TODO: we probably need to match on the type of the arg - IntOrFloat::IntType + // Recurse on the second argument + num_argument_to_int_or_float(subs, ptr_bytes, args[0].1, false) } - Content::FlexVar(_) => { - // If this was still a (Num *), assume compiling it to an Int - IntOrFloat::IntType - } - Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => { - debug_assert!(args.len() == 1); - // TODO: we probably need to match on the type of the arg - IntOrFloat::FloatType + Content::Alias(Symbol::NUM_I128, _, _) + | Content::Alias(Symbol::NUM_SIGNED128, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED128, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I128) + } + Content::Alias(Symbol::NUM_INT, _, _)// We default Integer to I64 + | Content::Alias(Symbol::NUM_I64, _, _) + | Content::Alias(Symbol::NUM_SIGNED64, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED64, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I64) + } + Content::Alias(Symbol::NUM_I32, _, _) + | Content::Alias(Symbol::NUM_SIGNED32, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED32, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I32) + } + Content::Alias(Symbol::NUM_I16, _, _) + | Content::Alias(Symbol::NUM_SIGNED16, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED16, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I16) + } + Content::Alias(Symbol::NUM_I8, _, _) + | Content::Alias(Symbol::NUM_SIGNED8, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED8, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I8) + } + Content::Alias(Symbol::NUM_U128, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED128, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED128, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I128) + } + Content::Alias(Symbol::NUM_U64, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED64, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED64, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I64) + } + Content::Alias(Symbol::NUM_U32, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED32, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED32, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I32) + } + Content::Alias(Symbol::NUM_U16, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED16, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED16, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I16) + } + Content::Alias(Symbol::NUM_U8, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED8, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED8, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I8) } Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => { debug_assert!(attr_args.len() == 2); // Recurse on the second argument - num_argument_to_int_or_float(subs, attr_args[1]) + num_argument_to_int_or_float(subs, ptr_bytes, attr_args[1], false) } - Content::Alias(Symbol::NUM_F64, args, _) | Content::Alias(Symbol::NUM_F32, args, _) => { - debug_assert!(args.is_empty()); + Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => { + debug_assert!(args.len() == 1); - IntOrFloat::FloatType + // Recurse on the second argument + num_argument_to_int_or_float(subs, ptr_bytes, args[0].1, true) + } + Content::Alias(Symbol::NUM_FLOAT, _, _) // We default FloatingPoint to F64 + | Content::Alias(Symbol::NUM_F64, _, _) + | Content::Alias(Symbol::NUM_BINARY64, _, _) + | Content::Alias(Symbol::NUM_AT_BINARY64, _, _) => { + IntOrFloat::BinaryFloatType(FloatPrecision::F64) + } + Content::Alias(Symbol::NUM_F32, _, _) + | Content::Alias(Symbol::NUM_BINARY32, _, _) + | Content::Alias(Symbol::NUM_AT_BINARY32, _, _) => { + IntOrFloat::BinaryFloatType(FloatPrecision::F32) + } + Content::Alias(Symbol::NUM_NAT, _, _) + | Content::Alias(Symbol::NUM_NATURAL, _, _) + | Content::Alias(Symbol::NUM_AT_NATURAL, _, _) => { + match ptr_bytes { + 1 => IntOrFloat::UnsignedIntType(IntPrecision::I8), + 2 => IntOrFloat::UnsignedIntType(IntPrecision::I16), + 4 => IntOrFloat::UnsignedIntType(IntPrecision::I32), + 8 => IntOrFloat::UnsignedIntType(IntPrecision::I64), + _ => panic!( + "Invalid target for Num type arguement: Roc does't support compiling to {}-bit systems.", + ptr_bytes * 8 + ), + } } other => { panic!( diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 4ead37215a..1a595b5301 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -11,6 +11,7 @@ pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::() * 8) as usize; /// If a (Num *) gets translated to a Layout, this is the numeric type it defaults to. const DEFAULT_NUM_BUILTIN: Builtin<'_> = Builtin::Int64; +pub const TAG_SIZE: Builtin<'_> = Builtin::Int64; #[derive(Debug, Clone)] pub enum LayoutProblem { @@ -312,6 +313,7 @@ pub enum Builtin<'a> { Int16, Int8, Int1, + Usize, Float128, Float64, Float32, @@ -362,14 +364,60 @@ impl<'a> Layout<'a> { } Structure(flat_type) => layout_from_flat_type(env, flat_type), + // Ints + Alias(Symbol::NUM_I128, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int128)) + } Alias(Symbol::NUM_I64, args, _) => { debug_assert!(args.is_empty()); Ok(Layout::Builtin(Builtin::Int64)) } + Alias(Symbol::NUM_I32, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int32)) + } + Alias(Symbol::NUM_I16, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int16)) + } + Alias(Symbol::NUM_I8, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int8)) + } + + // I think unsigned and signed use the same layout + Alias(Symbol::NUM_U128, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int128)) + } + Alias(Symbol::NUM_U64, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int64)) + } + Alias(Symbol::NUM_U32, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int32)) + } + Alias(Symbol::NUM_U16, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int16)) + } + Alias(Symbol::NUM_U8, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int8)) + } + + // Floats Alias(Symbol::NUM_F64, args, _) => { debug_assert!(args.is_empty()); Ok(Layout::Builtin(Builtin::Float64)) } + Alias(Symbol::NUM_F32, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Float32)) + } + Alias(_, _, var) => Self::from_var(env, var), Error => Err(LayoutProblem::Erroneous), } @@ -654,6 +702,7 @@ impl<'a> Builtin<'a> { const I16_SIZE: u32 = std::mem::size_of::() as u32; const I8_SIZE: u32 = std::mem::size_of::() as u32; const I1_SIZE: u32 = std::mem::size_of::() as u32; + const USIZE_SIZE: u32 = std::mem::size_of::() as u32; const F128_SIZE: u32 = 16; const F64_SIZE: u32 = std::mem::size_of::() as u32; const F32_SIZE: u32 = std::mem::size_of::() as u32; @@ -682,6 +731,7 @@ impl<'a> Builtin<'a> { Int16 => Builtin::I16_SIZE, Int8 => Builtin::I8_SIZE, Int1 => Builtin::I1_SIZE, + Usize => Builtin::USIZE_SIZE, Float128 => Builtin::F128_SIZE, Float64 => Builtin::F64_SIZE, Float32 => Builtin::F32_SIZE, @@ -707,6 +757,7 @@ impl<'a> Builtin<'a> { Int16 => align_of::() as u32, Int8 => align_of::() as u32, Int1 => align_of::() as u32, + Usize => align_of::() as u32, Float128 => align_of::() as u32, Float64 => align_of::() as u32, Float32 => align_of::() as u32, @@ -722,7 +773,7 @@ impl<'a> Builtin<'a> { use Builtin::*; match self { - Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float128 | Float64 | Float32 + Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize | Float128 | Float64 | Float32 | Float16 | EmptyStr | EmptyDict | EmptyList | EmptySet => true, Str | Dict(_, _) | Set(_) | List(_, _) => false, } @@ -733,7 +784,7 @@ impl<'a> Builtin<'a> { use Builtin::*; match self { - Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float128 | Float64 | Float32 + Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize | Float128 | Float64 | Float32 | Float16 | EmptyStr | EmptyDict | EmptyList | EmptySet => false, List(mode, element_layout) => match mode { MemoryMode::Refcounted => true, @@ -757,14 +808,64 @@ fn layout_from_flat_type<'a>( match flat_type { Apply(symbol, args) => { match symbol { + // Ints + Symbol::NUM_NAT => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Usize)) + } + + Symbol::NUM_I128 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int128)) + } Symbol::NUM_I64 => { debug_assert_eq!(args.len(), 0); Ok(Layout::Builtin(Builtin::Int64)) } + Symbol::NUM_I32 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int32)) + } + Symbol::NUM_I16 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int16)) + } + Symbol::NUM_I8 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int8)) + } + + Symbol::NUM_U128 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int128)) + } + Symbol::NUM_U64 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int64)) + } + Symbol::NUM_U32 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int32)) + } + Symbol::NUM_U16 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int16)) + } + Symbol::NUM_U8 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int8)) + } + + // Floats Symbol::NUM_F64 => { debug_assert_eq!(args.len(), 0); Ok(Layout::Builtin(Builtin::Float64)) } + Symbol::NUM_F32 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Float32)) + } + Symbol::NUM_NUM | Symbol::NUM_AT_NUM => { // Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer debug_assert_eq!(args.len(), 1); @@ -774,6 +875,7 @@ fn layout_from_flat_type<'a>( layout_from_num_content(content) } + Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)), Symbol::LIST_LIST => list_layout_from_elem(env, args[0]), Symbol::ATTR_ATTR => { @@ -888,7 +990,7 @@ fn layout_from_flat_type<'a>( let mut tag_layout = Vec::with_capacity_in(variables.len() + 1, arena); // store the discriminant - tag_layout.push(Layout::Builtin(Builtin::Int64)); + tag_layout.push(Layout::Builtin(TAG_SIZE)); for var in variables { // TODO does this cause problems with mutually recursive unions? @@ -1119,7 +1221,7 @@ pub fn union_sorted_tags_help<'a>( let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena); // add the tag discriminant (size currently always hardcoded to i64) - arg_layouts.push(Layout::Builtin(Builtin::Int64)); + arg_layouts.push(Layout::Builtin(TAG_SIZE)); for var in arguments { match Layout::from_var(&mut env, var) { @@ -1257,8 +1359,27 @@ fn layout_from_num_content<'a>(content: Content) -> Result, LayoutPro Ok(Layout::Builtin(DEFAULT_NUM_BUILTIN)) } Structure(Apply(symbol, args)) => match symbol { + // Ints + Symbol::NUM_NAT => Ok(Layout::Builtin(Builtin::Usize)), + Symbol::NUM_INTEGER => Ok(Layout::Builtin(Builtin::Int64)), + Symbol::NUM_I128 => Ok(Layout::Builtin(Builtin::Int128)), + Symbol::NUM_I64 => Ok(Layout::Builtin(Builtin::Int64)), + Symbol::NUM_I32 => Ok(Layout::Builtin(Builtin::Int32)), + Symbol::NUM_I16 => Ok(Layout::Builtin(Builtin::Int16)), + Symbol::NUM_I8 => Ok(Layout::Builtin(Builtin::Int8)), + + Symbol::NUM_U128 => Ok(Layout::Builtin(Builtin::Int128)), + Symbol::NUM_U64 => Ok(Layout::Builtin(Builtin::Int64)), + Symbol::NUM_U32 => Ok(Layout::Builtin(Builtin::Int32)), + Symbol::NUM_U16 => Ok(Layout::Builtin(Builtin::Int16)), + Symbol::NUM_U8 => Ok(Layout::Builtin(Builtin::Int8)), + + // Floats Symbol::NUM_FLOATINGPOINT => Ok(Layout::Builtin(Builtin::Float64)), + Symbol::NUM_F64 => Ok(Layout::Builtin(Builtin::Float64)), + Symbol::NUM_F32 => Ok(Layout::Builtin(Builtin::Float32)), + _ => { panic!( "Invalid Num.Num type application: {:?}", @@ -1293,16 +1414,62 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result, LayoutPr Content::Alias(Symbol::NUM_INTEGER, args, _) => { debug_assert!(args.len() == 1); - // TODO: we probably need to match on the type of the arg - // and return the correct builtin ex: Builtin::{Int32, Int16} - Ok(Layout::Builtin(Builtin::Int64)) + let (_, precision_var) = args[0]; + + let precision = subs.get_without_compacting(precision_var).content; + + match precision { + Content::Alias(symbol, args, _) => { + debug_assert!(args.is_empty()); + + let builtin = match symbol { + Symbol::NUM_SIGNED128 => Builtin::Int128, + Symbol::NUM_SIGNED64 => Builtin::Int64, + Symbol::NUM_SIGNED32 => Builtin::Int32, + Symbol::NUM_SIGNED16 => Builtin::Int16, + Symbol::NUM_SIGNED8 => Builtin::Int8, + Symbol::NUM_UNSIGNED128 => Builtin::Int128, + Symbol::NUM_UNSIGNED64 => Builtin::Int64, + Symbol::NUM_UNSIGNED32 => Builtin::Int32, + Symbol::NUM_UNSIGNED16 => Builtin::Int16, + Symbol::NUM_UNSIGNED8 => Builtin::Int8, + Symbol::NUM_NATURAL => Builtin::Usize, + _ => unreachable!("not a valid int variant: {:?} {:?}", symbol, args), + }; + + Ok(Layout::Builtin(builtin)) + } + Content::FlexVar(_) | Content::RigidVar(_) => { + // default to i64 + Ok(Layout::Builtin(Builtin::Int64)) + } + _ => unreachable!("not a valid int variant: {:?}", precision), + } } Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => { debug_assert!(args.len() == 1); - // TODO: we probably need to match on the type of the arg - // and return the correct builtin ex: Builtin::Float32 - Ok(Layout::Builtin(Builtin::Float64)) + let (_, precision_var) = args[0]; + + let precision = subs.get_without_compacting(precision_var).content; + + match precision { + Content::Alias(Symbol::NUM_BINARY32, args, _) => { + debug_assert!(args.is_empty()); + + Ok(Layout::Builtin(Builtin::Float32)) + } + Content::Alias(Symbol::NUM_BINARY64, args, _) => { + debug_assert!(args.is_empty()); + + Ok(Layout::Builtin(Builtin::Float64)) + } + Content::FlexVar(_) | Content::RigidVar(_) => { + // default to f64 + Ok(Layout::Builtin(Builtin::Float64)) + } + _ => unreachable!("not a valid float variant: {:?}", precision), + } } Content::FlexVar(_) | Content::RigidVar(_) => { // If this was still a (Num *) then default to compiling it to i64 diff --git a/compiler/mono/tests/test_mono.rs b/compiler/mono/tests/test_mono.rs index 28253f53e2..7c0842cd6e 100644 --- a/compiler/mono/tests/test_mono.rs +++ b/compiler/mono/tests/test_mono.rs @@ -61,6 +61,7 @@ mod test_mono { stdlib, src_dir, exposed_types, + 8, ); let mut loaded = loaded.expect("failed to load module"); diff --git a/compiler/reporting/src/error/type.rs b/compiler/reporting/src/error/type.rs index 634f8865bf..42ade1863e 100644 --- a/compiler/reporting/src/error/type.rs +++ b/compiler/reporting/src/error/type.rs @@ -1624,8 +1624,8 @@ fn to_diff<'b>( let right = to_doc(alloc, Parens::Unnecessary, type2); let is_int = |t: &ErrorType| match t { - ErrorType::Type(Symbol::NUM_I64, _) => true, - ErrorType::Alias(Symbol::NUM_I64, _, _) => true, + ErrorType::Type(Symbol::NUM_INT, _) => true, + ErrorType::Alias(Symbol::NUM_INT, _, _) => true, ErrorType::Type(Symbol::NUM_NUM, args) => { matches!( &args.get(0) ,Some(ErrorType::Type(Symbol::NUM_INTEGER, _)) | Some(ErrorType::Alias(Symbol::NUM_INTEGER, _, _))) @@ -1636,8 +1636,8 @@ fn to_diff<'b>( _ => false, }; let is_float = |t: &ErrorType| match t { - ErrorType::Type(Symbol::NUM_F64, _) => true, - ErrorType::Alias(Symbol::NUM_F64, _, _) => true, + ErrorType::Type(Symbol::NUM_FLOAT, _) => true, + ErrorType::Alias(Symbol::NUM_FLOAT, _, _) => true, ErrorType::Type(Symbol::NUM_NUM, args) => { matches!(&args.get(0), Some(ErrorType::Type(Symbol::NUM_FLOATINGPOINT, _)) | Some(ErrorType::Alias(Symbol::NUM_FLOATINGPOINT, _, _))) @@ -1891,7 +1891,7 @@ fn diff_tag_union<'b>( alloc.tag_name(field.clone()), // TODO add spaces between args args.iter() - .map(|arg| to_doc(alloc, Parens::Unnecessary, arg.clone())) + .map(|arg| to_doc(alloc, Parens::InTypeParam, arg.clone())) .collect(), ) }; diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index a865091b61..5b58ebe4d2 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -94,6 +94,7 @@ mod test_reporting { problems: &mut mono_problems, home, ident_ids: &mut ident_ids, + ptr_bytes: 8, }; let _mono_expr = Stmt::new( &mut mono_env, @@ -446,9 +447,9 @@ mod test_reporting { these names seem close though: baz + Nat Str U8 - F64 "# ), ) @@ -960,7 +961,7 @@ mod test_reporting { r#" bar = { bar : 0x3 } - f : { foo : I64 } -> Bool + f : { foo : Int * } -> Bool f = \_ -> True f bar @@ -977,11 +978,11 @@ mod test_reporting { This `bar` value is a: - { bar : I64 } + { bar : Int a } But `f` needs the 1st argument to be: - { foo : I64 } + { foo : Int a } Tip: Seems like a record field typo. Maybe `bar` should be `foo`? @@ -1036,7 +1037,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : [ Red I64, Green Bool ] -> Bool + f : [ Red (Int *), Green Bool ] -> Bool f = \_ -> True f (Blue 3.14) @@ -1053,11 +1054,11 @@ mod test_reporting { This `Blue` global tag application has the type: - [ Blue F64 ]a + [ Blue (Float a) ]b But `f` needs the 1st argument to be: - [ Green Bool, Red I64 ] + [ Green Bool, Red (Int a) ] Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? @@ -1074,7 +1075,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - x : I64 + x : Int * x = if True then 3.14 else 4 x @@ -1091,11 +1092,11 @@ mod test_reporting { The 1st branch is a float of type: - F64 + Float a But the type annotation on `x` says it should be: - I64 + Int b Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -1109,7 +1110,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - x : I64 + x : Int * x = when True is _ -> 3.14 @@ -1123,18 +1124,18 @@ mod test_reporting { Something is off with the body of the `x` definition: - 1│ x : I64 + 1│ x : Int * 2│ x = 3│> when True is 4│> _ -> 3.14 This `when`expression produces: - F64 + Float a But the type annotation on `x` says it should be: - I64 + Int b Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -1148,7 +1149,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - x : I64 -> I64 + x : Int * -> Int * x = \_ -> 3.14 x @@ -1160,17 +1161,17 @@ mod test_reporting { Something is off with the body of the `x` definition: - 1│ x : I64 -> I64 + 1│ x : Int * -> Int * 2│ x = \_ -> 3.14 ^^^^ The body is a float of type: - F64 + Float a But the type annotation on `x` says it should be: - I64 + Int b Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -1373,7 +1374,7 @@ mod test_reporting { Bool U8 F64 - Str + Nat "# ), ) @@ -1482,7 +1483,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - { x } : { x : I64 } + { x } : { x : Int * } { x } = { x: 4.0 } x @@ -1494,17 +1495,17 @@ mod test_reporting { Something is off with the body of this definition: - 1│ { x } : { x : I64 } + 1│ { x } : { x : Int * } 2│ { x } = { x: 4.0 } ^^^^^^^^^^ The body is a record of type: - { x : F64 } + { x : Float a } But the type annotation says it should be: - { x : I64 } + { x : Int b } Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -1643,7 +1644,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - x : { a : I64, b : F64, c : Bool } + x : { a : Int *, b : Float *, c : Bool } x = { b: 4.0 } x @@ -1655,17 +1656,17 @@ mod test_reporting { Something is off with the body of the `x` definition: - 1│ x : { a : I64, b : F64, c : Bool } + 1│ x : { a : Int *, b : Float *, c : Bool } 2│ x = { b: 4.0 } ^^^^^^^^^^ The body is a record of type: - { b : F64 } + { b : Float a } But the type annotation on `x` says it should be: - { a : I64, b : F64, c : Bool } + { a : Int a, b : Float b, c : Bool } Tip: Looks like the c and a fields are missing. "# @@ -1787,7 +1788,7 @@ mod test_reporting { The body is an integer of type: - I64 + Int a But the type annotation on `f` says it should be: @@ -1795,7 +1796,7 @@ mod test_reporting { Tip: The type annotation uses the type variable `msg` to say that this definition can produce any type of value. But in the body I see that - it will only produce a `I64` value of a single specific type. Maybe + it will only produce a `Int` value of a single specific type. Maybe change the type annotation to be more specific? Maybe change the code to be more general? "# @@ -2093,7 +2094,7 @@ mod test_reporting { But `add` needs the 2nd argument to be: - Num (Integer Signed64) + Num (Integer a) "# ), ) @@ -2118,11 +2119,11 @@ mod test_reporting { This argument is a float of type: - F64 + Float a But `add` needs the 2nd argument to be: - Num (Integer Signed64) + Num (Integer a) Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -2580,9 +2581,9 @@ mod test_reporting { report_problem_as( indoc!( r#" - Foo : { x : I64 } + Foo : { x : Int * } - f : Foo -> I64 + f : Foo -> Int * f = \r -> r.x f { y: 3.14 } @@ -2600,11 +2601,11 @@ mod test_reporting { This argument is a record of type: - { y : F64 } + { y : Float a } But `f` needs the 1st argument to be: - { x : I64 } + { x : Int a } Tip: Seems like a record field typo. Maybe `y` should be `x`? diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index c81241be43..03bb306ef5 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -62,6 +62,7 @@ mod solve_expr { stdlib, dir.path(), exposed_types, + 8, ); dir.close()?; @@ -169,7 +170,7 @@ mod solve_expr { #[test] fn float_literal() { - infer_eq("0.5", "F64"); + infer_eq("0.5", "Float *"); } #[test] @@ -762,7 +763,7 @@ mod solve_expr { (\a -> a) 3.14 "# ), - "F64", + "Float *", ); } @@ -1026,7 +1027,7 @@ mod solve_expr { #[test] fn two_field_record() { - infer_eq("{ x: 5, y : 3.14 }", "{ x : Num *, y : F64 }"); + infer_eq("{ x: 5, y : 3.14 }", "{ x : Num *, y : Float * }"); } #[test] @@ -2385,7 +2386,7 @@ mod solve_expr { threePointZero "# ), - "F64", + "Float *", ); } @@ -2982,7 +2983,7 @@ mod solve_expr { infer_eq_without_problem( indoc!( r#" - partition : I64, I64, List I64 -> [ Pair I64 (List I64) ] + partition : Int a, Int *, List (Int b) -> [ Pair (Int a) (List (Int b)) ] partition = \low, high, initialList -> when List.get initialList high is Ok _ -> @@ -2994,7 +2995,7 @@ mod solve_expr { partition "# ), - "I64, I64, List I64 -> [ Pair I64 (List I64) ]", + "Int a, Int *, List (Int b) -> [ Pair (Int a) (List (Int b)) ]", ); } @@ -3004,7 +3005,7 @@ mod solve_expr { infer_eq_without_problem( indoc!( r#" - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -3015,7 +3016,7 @@ mod solve_expr { _ -> list - partition : I64, I64, List I64 -> [ Pair I64 (List I64) ] + partition : I64, I64, List (Int a) -> [ Pair I64 (List (Int a)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -3043,7 +3044,7 @@ mod solve_expr { partition "# ), - "I64, I64, List I64 -> [ Pair I64 (List I64) ]", + "I64, I64, List (Int a) -> [ Pair I64 (List (Int a)) ]", ); }); } @@ -3077,6 +3078,15 @@ mod solve_expr { ), "Result (Num *) [ OutOfBounds ]*", ); + + infer_eq_without_problem( + indoc!( + r#" + List.get + "# + ), + "List a, Int * -> Result a [ OutOfBounds ]*", + ); } #[test] @@ -3117,7 +3127,7 @@ mod solve_expr { Num.toFloat "# ), - "Num * -> F64", + "Num * -> Float *", ); } @@ -3129,7 +3139,7 @@ mod solve_expr { Num.pow "# ), - "F64, F64 -> F64", + "Float a, Float a -> Float a", ); } @@ -3141,7 +3151,7 @@ mod solve_expr { Num.ceiling "# ), - "F64 -> I64", + "Float * -> Int *", ); } @@ -3153,7 +3163,7 @@ mod solve_expr { Num.floor "# ), - "F64 -> I64", + "Float * -> Int *", ); } @@ -3165,7 +3175,7 @@ mod solve_expr { Num.powInt "# ), - "I64, I64 -> I64", + "Int a, Int a -> Int a", ); } @@ -3177,7 +3187,7 @@ mod solve_expr { Num.atan "# ), - "F64 -> F64", + "Float a -> Float a", ); } @@ -3444,7 +3454,7 @@ mod solve_expr { negatePoint { x: 1, y: 2.1, z: 0x3 } "# ), - "{ x : Num a, y : F64, z : I64 }", + "{ x : Num a, y : F64, z : Int * }", ); } @@ -4096,7 +4106,7 @@ mod solve_expr { r#" app "test" provides [ partitionHelp ] to "./platform" - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> diff --git a/compiler/solve/tests/solve_uniq_expr.rs b/compiler/solve/tests/solve_uniq_expr.rs deleted file mode 100644 index 6f943c8bbe..0000000000 --- a/compiler/solve/tests/solve_uniq_expr.rs +++ /dev/null @@ -1,3213 +0,0 @@ -#[macro_use] -extern crate pretty_assertions; -#[macro_use] -extern crate indoc; - -extern crate bumpalo; - -mod helpers; - -#[cfg(test)] -mod solve_uniq_expr { - use crate::helpers::{ - assert_correct_variable_usage, infer_expr, uniq_expr, with_larger_debug_stack, - }; - use roc_types::pretty_print::{content_to_string, name_all_type_vars}; - - // HELPERS - - fn infer_eq_help(src: &str) -> (Vec, String) { - let (_loc_expr, output, mut can_problems, mut subs, variable, constraint, home, interns) = - uniq_expr(src); - - // Disregard UnusedDef problems, because those are unavoidable when - // returning a function from the test expression. - can_problems.retain(|prob| match prob { - roc_problem::can::Problem::UnusedDef(_, _) => false, - _ => true, - }); - - assert_eq!(can_problems, Vec::new(), "Canonicalization problems"); - - assert_correct_variable_usage(&constraint); - - for (var, name) in output.introduced_variables.name_by_var { - subs.rigid_var(var, name); - } - - let mut unify_problems = Vec::new(); - let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, variable); - - name_all_type_vars(variable, &mut subs); - - let actual_str = content_to_string(content, &mut subs, home, &interns); - - (unify_problems, actual_str) - } - fn infer_eq_ignore_problems(src: &str, expected: &str) { - let (_, actual) = infer_eq_help(src); - - assert_eq!(actual, expected.to_string()); - } - - fn infer_eq(src: &str, expected: &str) { - let (problems, actual) = infer_eq_help(src); - - if !problems.is_empty() { - panic!( - "expected:\n{:?}\ninferred:\n{:?}\nproblems:\n{:?}", - expected, actual, problems - ); - } - - assert_eq!(actual, expected.to_string()); - } - - #[test] - fn empty_record() { - infer_eq("{}", "Attr * {}"); - } - - #[test] - fn int_literal() { - infer_eq("5", "Attr * (Num (Attr * *))"); - } - - #[test] - fn float_literal() { - infer_eq("0.5", "Attr * F64"); - } - - #[test] - fn string_literal() { - infer_eq( - indoc!( - r#" - "type inference!" - "# - ), - "Attr * Str", - ); - } - - #[test] - fn empty_string() { - infer_eq( - indoc!( - r#" - "" - "# - ), - "Attr * Str", - ); - } - - // #[test] - // fn block_string_literal() { - // infer_eq( - // indoc!( - // r#" - // """type - // inference!""" - // "# - // ), - // "Str", - // ); - // } - - // LIST - - #[test] - fn empty_list_literal() { - with_larger_debug_stack(|| { - infer_eq( - indoc!( - r#" - [] - "# - ), - "Attr * (List *)", - ); - }) - } - - #[test] - fn list_of_lists() { - infer_eq( - indoc!( - r#" - [[]] - "# - ), - "Attr * (List (Attr * (List *)))", - ); - } - - #[test] - fn triple_nested_list() { - infer_eq( - indoc!( - r#" - [[[]]] - "# - ), - "Attr * (List (Attr * (List (Attr * (List *)))))", - ); - } - - #[test] - fn nested_empty_list() { - infer_eq( - indoc!( - r#" - [ [], [ [] ] ] - "# - ), - "Attr * (List (Attr * (List (Attr * (List *)))))", - ); - } - - // #[test] - // fn concat_different_types() { - // infer_eq( - // indoc!( - // r#" - // empty = [] - // one = List.concat [ 1 ] empty - // str = List.concat [ "blah" ] empty - - // empty - // "# - // ), - // "List *", - // ); - // } - - #[test] - fn list_of_one_int() { - infer_eq( - indoc!( - r#" - [42] - "# - ), - "Attr * (List (Attr * (Num (Attr * *))))", - ); - } - - #[test] - fn triple_nested_int_list() { - infer_eq( - indoc!( - r#" - [[[ 5 ]]] - "# - ), - "Attr * (List (Attr * (List (Attr * (List (Attr * (Num (Attr * *))))))))", - ); - } - - #[test] - fn list_of_ints() { - infer_eq( - indoc!( - r#" - [ 1, 2, 3 ] - "# - ), - "Attr * (List (Attr * (Num (Attr * *))))", - ); - } - - #[test] - fn nested_list_of_ints() { - infer_eq( - indoc!( - r#" - [ [ 1 ], [ 2, 3 ] ] - "# - ), - "Attr * (List (Attr * (List (Attr * (Num (Attr * *))))))", - ); - } - - #[test] - fn list_of_one_string() { - infer_eq( - indoc!( - r#" - [ "cowabunga" ] - "# - ), - "Attr * (List (Attr * Str))", - ); - } - - #[test] - fn triple_nested_string_list() { - infer_eq( - indoc!( - r#" - [[[ "foo" ]]] - "# - ), - "Attr * (List (Attr * (List (Attr * (List (Attr * Str))))))", - ); - } - - #[test] - fn list_of_strings() { - infer_eq( - indoc!( - r#" - [ "foo", "bar" ] - "# - ), - "Attr * (List (Attr * Str))", - ); - } - - // // INTERPOLATED STRING - - // #[test] - // fn infer_interpolated_string() { - // infer_eq( - // indoc!( - // r#" - // whatItIs = "great" - - // "type inference is \(whatItIs)!" - // "# - // ), - // "Str", - // ); - // } - - // LIST MISMATCH - - #[test] - fn mismatch_heterogeneous_list() { - infer_eq_ignore_problems( - indoc!( - r#" - [ "foo", 5 ] - "# - ), - "Attr * (List )", - ); - } - - #[test] - fn mismatch_heterogeneous_nested_list() { - infer_eq_ignore_problems( - indoc!( - r#" - [ [ "foo", 5 ] ] - "# - ), - "Attr * (List (Attr * (List )))", - ); - } - - #[test] - fn mismatch_heterogeneous_nested_empty_list() { - infer_eq_ignore_problems( - indoc!( - r#" - [ [ 1 ], [ [] ] ] - "# - ), - "Attr * (List )", - ); - } - - // CLOSURE - - #[test] - fn always_return_empty_record() { - infer_eq( - indoc!( - r#" - \_ -> {} - "# - ), - "Attr * (* -> Attr * {})", - ); - } - - #[test] - fn two_arg_return_int() { - infer_eq( - indoc!( - r#" - \_, _ -> 42 - "# - ), - "Attr * (*, * -> Attr * (Num (Attr * *)))", - ); - } - - #[test] - fn three_arg_return_string() { - infer_eq( - indoc!( - r#" - \_, _, _ -> "test!" - "# - ), - "Attr * (*, *, * -> Attr * Str)", - ); - } - - // DEF - - #[test] - fn def_empty_record() { - infer_eq( - indoc!( - r#" - foo = {} - - foo - "# - ), - "Attr * {}", - ); - } - - #[test] - fn def_string() { - infer_eq( - indoc!( - r#" - str = "thing" - - str - "# - ), - "Attr * Str", - ); - } - - #[test] - fn def_1_arg_closure() { - infer_eq( - indoc!( - r#" - fn = \_ -> {} - - fn - "# - ), - "Attr * (* -> Attr * {})", - ); - } - - #[test] - fn def_2_arg_closure() { - infer_eq( - indoc!( - r#" - func = \_, _ -> 42 - - func - "# - ), - "Attr * (*, * -> Attr * (Num (Attr * *)))", - ); - } - - #[test] - fn def_3_arg_closure() { - infer_eq( - indoc!( - r#" - f = \_, _, _ -> "test!" - - f - "# - ), - "Attr * (*, *, * -> Attr * Str)", - ); - } - - #[test] - fn def_multiple_functions() { - infer_eq( - indoc!( - r#" - a = \_, _, _ -> "test!" - - b = a - - b - "# - ), - "Attr * (*, *, * -> Attr * Str)", - ); - } - - #[test] - fn def_multiple_strings() { - infer_eq( - indoc!( - r#" - a = "test!" - - b = a - - b - "# - ), - "Attr * Str", - ); - } - - #[test] - fn def_multiple_ints() { - infer_eq( - indoc!( - r#" - c = b - - b = a - - a = 42 - - c - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - // #[test] - // fn def_returning_closure() { - // infer_eq( - // indoc!( - // r#" - // f = \z -> z - // g = \z -> z - // - // (\x -> - // a = f x - // b = g x - // x - // ) - // "# - // ), - // // x is used 3 times, so must be shared - // "Attr * (Attr Shared a -> Attr Shared a)", - // ); - // } - - // CALLING FUNCTIONS - - #[test] - fn call_returns_int() { - infer_eq( - indoc!( - r#" - alwaysFive = \_ -> 5 - - alwaysFive "stuff" - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn identity_returns_given_type() { - infer_eq( - indoc!( - r#" - identity = \a -> a - - identity "hi" - "# - ), - "Attr * Str", - ); - } - - #[test] - fn identity_infers_principal_type() { - infer_eq( - indoc!( - r#" - identity = \a -> a - x = identity 5 - - identity - "# - ), - "Attr Shared (a -> a)", - ); - } - - #[test] - fn identity_works_on_incompatible_types() { - infer_eq( - indoc!( - r#" - identity = \a -> a - - x = identity 5 - y = identity "hi" - - x - "# - ), - // TODO investigate why is this not shared? - // maybe because y is not used it is dropped? - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn call_returns_list() { - infer_eq( - indoc!( - r#" - enlist = \val -> [ val ] - - enlist 5 - "# - ), - "Attr * (List (Attr * (Num (Attr * *))))", - ); - } - - #[test] - fn indirect_always() { - infer_eq( - indoc!( - r#" - always = \val -> (\_ -> val) - alwaysFoo = always "foo" - - alwaysFoo 42 - "# - ), - "Attr * Str", - ); - } - - #[test] - fn pizza_desugar() { - infer_eq( - indoc!( - r#" - 1 |> (\a -> a) - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn pizza_desugared() { - infer_eq( - indoc!( - r#" - (\a -> a) 1 - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn pizza_desugar_two_arguments() { - infer_eq( - indoc!( - r#" - always2 = \a, _ -> a - - 1 |> always2 "foo" - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn anonymous_identity() { - infer_eq( - indoc!( - r#" - (\a -> a) 3.14 - "# - ), - "Attr * F64", - ); - } - - // TODO when symbols are unique, this should work again - // #[test] - // fn identity_of_identity() { - // infer_eq( - // indoc!( - // r#" - // (\val -> val) (\val -> val) - // "# - // ), - // "Attr * (a -> a)", - // ); - // } - - #[test] - fn recursive_identity() { - infer_eq( - indoc!( - r#" - identity = \val -> val - - identity identity - "# - ), - "Attr Shared (a -> a)", - ); - } - - #[test] - fn identity_function() { - infer_eq( - indoc!( - r#" - \val -> val - "# - ), - "Attr * (a -> a)", - ); - } - - #[test] - fn use_apply() { - infer_eq( - indoc!( - r#" - apply = \f, x -> f x - identity = \a -> a - - apply identity 5 - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn apply_function() { - infer_eq( - indoc!( - r#" - \f, x -> f x - "# - ), - "Attr * (Attr * (a -> b), a -> b)", - ); - } - - // #[test] - // TODO FIXME this should pass, but instead fails to canonicalize - // fn use_flip() { - // infer_eq( - // indoc!( - // r#" - // flip = \f -> (\a b -> f b a) - // neverendingNum = \f int -> f int - // x = neverendingNum (\a -> a) 5 - - // flip neverendingNum - // "# - // ), - // "((Num (Attr * *)), (a -> a)) -> (Num (Attr * *))", - // ); - // } - - #[test] - fn flip_function() { - infer_eq( - indoc!( - r#" - \f -> (\a, b -> f b a), - "# - ), - "Attr * (Attr * (a, b -> c) -> Attr * (b, a -> c))", - ); - } - - #[test] - fn always_function() { - infer_eq( - indoc!( - r#" - \val -> \_ -> val - "# - ), - "Attr * (a -> Attr * (* -> a))", - ); - } - - #[test] - fn pass_a_function() { - infer_eq( - indoc!( - r#" - \f -> f {} - "# - ), - "Attr * (Attr * (Attr * {} -> a) -> a)", - ); - } - - // OPERATORS - - // #[test] - // fn div_operator() { - // infer_eq( - // indoc!( - // r#" - // \l r -> l / r - // "# - // ), - // "F64, F64 -> F64", - // ); - // } - - // #[test] - // fn basic_float_division() { - // infer_eq( - // indoc!( - // r#" - // 1 / 2 - // "# - // ), - // "F64", - // ); - // } - - // #[test] - // fn basic_int_division() { - // infer_eq( - // indoc!( - // r#" - // 1 // 2 - // "# - // ), - // "(Num (Attr * *))", - // ); - // } - - // #[test] - // fn basic_addition() { - // infer_eq( - // indoc!( - // r#" - // 1 + 2 - // "# - // ), - // "(Num (Attr * *))", - // ); - // } - - // #[test] - // fn basic_circular_type() { - // infer_eq( - // indoc!( - // r#" - // \x -> x x - // "# - // ), - // "", - // ); - // } - - // #[test] - // fn y_combinator_has_circular_type() { - // assert_eq!( - // infer(indoc!(r#" - // \f -> (\x -> f x x) (\x -> f x x) - // "#)), - // Erroneous(Problem::CircularType) - // ); - // } - - // #[test] - // fn no_higher_ranked_types() { - // // This should error because it can't type of alwaysFive - // infer_eq( - // indoc!( - // r#" - // \always -> [ always [], always "" ] - // "# - // ), - // "", - // ); - // } - - #[test] - fn always_with_list() { - infer_eq( - indoc!( - r#" - alwaysFive = \_ -> 5 - - [ alwaysFive "foo", alwaysFive [] ] - "# - ), - "Attr * (List (Attr * (Num (Attr * *))))", - ); - } - - #[test] - fn if_with_int_literals() { - infer_eq( - indoc!( - r#" - if True then - 42 - else - 24 - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn when_with_int_literals() { - infer_eq( - indoc!( - r#" - when 1 is - 1 -> 2 - 3 -> 4 - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn record() { - infer_eq("{ foo: 42 }", "Attr * { foo : Attr * (Num (Attr * *)) }"); - } - - #[test] - fn record_access() { - infer_eq("{ foo: 42 }.foo", "Attr * (Num (Attr * *))"); - } - - #[test] - fn empty_record_pattern() { - infer_eq( - indoc!( - r#" - # technically, an empty record can be destructured - {} = {} - thunk = \{} -> 42 - - xEmpty = if thunk {} == 42 then { x: {} } else { x: {} } - - when xEmpty is - { x: {} } -> {} - "# - ), - "Attr * {}", - ); - } - - #[test] - fn record_update() { - infer_eq( - indoc!( - r#" - user = { year: "foo", name: "Sam" } - - { user & year: "foo" } - "# - ), - "Attr * { name : Attr * Str, year : Attr * Str }", - ); - } - - #[test] - fn bare_tag() { - infer_eq( - indoc!( - r#" - Foo - "# - ), - "Attr * [ Foo ]*", - ); - } - - #[test] - fn single_tag_pattern() { - infer_eq( - indoc!( - r#" - \Foo -> 42 - "# - ), - "Attr * (Attr * [ Foo ]* -> Attr * (Num (Attr * *)))", - ); - } - - #[test] - fn single_private_tag_pattern() { - infer_eq( - indoc!( - r#" - \@Foo -> 42 - "# - ), - "Attr * (Attr * [ @Foo ]* -> Attr * (Num (Attr * *)))", - ); - } - - #[test] - fn two_tag_pattern() { - infer_eq( - indoc!( - r#" - \x -> - when x is - True -> 1 - False -> 0 - "# - ), - "Attr * (Attr * [ False, True ]* -> Attr * (Num (Attr * *)))", - ); - } - - #[test] - fn tag_application() { - infer_eq( - indoc!( - r#" - Foo "happy" 2020 - "# - ), - "Attr * [ Foo (Attr * Str) (Attr * (Num (Attr * *))) ]*", - ); - } - - #[test] - fn private_tag_application() { - infer_eq( - indoc!( - r#" - @Foo "happy" 2020 - "# - ), - "Attr * [ @Foo (Attr * Str) (Attr * (Num (Attr * *))) ]*", - ); - } - - #[test] - fn record_field_accessor_function() { - infer_eq( - indoc!( - r#" - .left - "# - ), - "Attr * (Attr (* | b) { left : Attr b a }* -> Attr b a)", - ); - } - - #[test] - fn record_field_access_syntax() { - infer_eq( - indoc!( - r#" - \rec -> rec.left - "# - ), - "Attr * (Attr (* | b) { left : Attr b a }* -> Attr b a)", - ); - } - - #[test] - fn record_field_pattern_match_two() { - infer_eq( - indoc!( - r#" - \{ left, right } -> { left, right } - "# - ), - "Attr * (Attr (* | b | d) { left : Attr b a, right : Attr d c }* -> Attr * { left : Attr b a, right : Attr d c })" - ); - } - - #[test] - fn record_field_pattern_match_with_guard() { - infer_eq( - indoc!( - r#" - when { x: 5 } is - { x: 4 } -> 4 - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn tag_union_pattern_match() { - infer_eq( - indoc!( - r#" - \Foo x -> Foo x - "# - ), - // NOTE: Foo loses the relation to the uniqueness attribute `a` - // That is fine. Whenever we try to extract from it, the relation will be enforced - "Attr * (Attr (* | b) [ Foo (Attr b a) ]* -> Attr * [ Foo (Attr b a) ]*)", - ); - } - - #[test] - fn tag_union_pattern_match_ignored_field() { - infer_eq( - indoc!( - r#" - \Foo x _ -> Foo x "y" - "# - ), - // TODO: is it safe to ignore uniqueness constraints from patterns that bind no identifiers? - // i.e. the `b` could be ignored in this example, is that true in general? - // seems like it because we don't really extract anything. - "Attr * (Attr (* | b | c) [ Foo (Attr b a) (Attr c *) ]* -> Attr * [ Foo (Attr b a) (Attr * Str) ]*)" - ); - } - - #[test] - fn global_tag_with_field() { - infer_eq( - indoc!( - r#" - when Foo 4 is - Foo x -> x - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn private_tag_with_field() { - infer_eq( - indoc!( - r#" - when @Foo 4 is - @Foo x -> x - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn type_annotation() { - infer_eq( - indoc!( - r#" - x : Num.Num (Num.Integer Num.Signed64) - x = 4 - - x - "# - ), - "Attr * I64", - ); - } - - #[test] - fn record_field_pattern_match() { - infer_eq( - indoc!( - r#" - \{ left } -> left - "# - ), - "Attr * (Attr (* | b) { left : Attr b a }* -> Attr b a)", - ); - } - - #[test] - fn sharing_analysis_record_one_field_pattern() { - infer_eq( - indoc!( - r#" - \{ left } -> left - "# - ), - "Attr * (Attr (* | b) { left : Attr b a }* -> Attr b a)", - ); - } - - #[test] - fn num_identity_def() { - infer_eq( - indoc!( - r#" - numIdentity : Num.Num p -> Num.Num p - numIdentity = \x -> x - - numIdentity - "# - ), - "Attr * (Attr b (Num (Attr a p)) -> Attr b (Num (Attr a p)))", - ); - } - - #[test] - fn record_field_access_binding() { - infer_eq( - indoc!( - r#" - \r -> - x = r.left - - x - "# - ), - "Attr * (Attr (* | b) { left : Attr b a }* -> Attr b a)", - ); - } - - #[test] - fn sharing_analysis_record_one_field_access() { - infer_eq( - indoc!( - r#" - \r -> - x = r.left - - x - "# - ), - "Attr * (Attr (* | b) { left : Attr b a }* -> Attr b a)", - ); - } - - #[test] - fn num_identity_applied() { - infer_eq( - indoc!( - r#" - numIdentity : Num.Num p -> Num.Num p - numIdentity = \foo -> foo - - p = numIdentity 42 - q = numIdentity 3.14 - - { numIdentity, p, q } - "# - ), - "Attr * { numIdentity : Attr Shared (Attr b (Num (Attr a p)) -> Attr b (Num (Attr a p))), p : Attr * (Num (Attr * p)), q : Attr * F64 }" - ); - } - - #[test] - fn sharing_analysis_record_twice_access() { - infer_eq( - indoc!( - r#" - \r -> - v = r.x - w = r.x - - r - "# - ), - "Attr * (Attr c { x : Attr Shared a }b -> Attr c { x : Attr Shared a }b)", - ); - } - - #[test] - fn sharing_analysis_record_access_two_fields() { - infer_eq( - indoc!( - r#" - \r -> - v = r.x - w = r.y - - r - "# - ), - "Attr * (Attr d { x : Attr Shared a, y : Attr Shared b }c -> Attr d { x : Attr Shared a, y : Attr Shared b }c)", - ); - } - - #[test] - fn sharing_analysis_record_alias() { - infer_eq( - indoc!( - r#" - \r -> - v = r.x - w = r.y - - # force alias after access (nested let-block) - (p = r - - p) - "# - ), - "Attr * (Attr d { x : Attr Shared a, y : Attr Shared b }c -> Attr d { x : Attr Shared a, y : Attr Shared b }c)" - ); - } - - #[test] - fn sharing_analysis_record_access_field_twice() { - infer_eq( - indoc!( - r#" - \r -> - n = r.x - m = r.x - - r - "# - ), - "Attr * (Attr c { x : Attr Shared a }b -> Attr c { x : Attr Shared a }b)", - ); - } - - #[test] - fn sharing_analysis_record_update_duplicate_field() { - infer_eq( - indoc!( - r#" - \r -> { r & x: r.x, y: r.x } - "# - ), - "Attr * (Attr c { x : Attr Shared a, y : Attr Shared a }b -> Attr c { x : Attr Shared a, y : Attr Shared a }b)" - ); - } - - #[test] - fn record_access_nested_field() { - infer_eq( - indoc!( - r#" - \r -> - v = r.foo.bar - w = r.foo.baz - - r - "# - ), - "Attr * (Attr (f | d) { foo : Attr d { bar : Attr Shared a, baz : Attr Shared b }c }e -> Attr (f | d) { foo : Attr d { bar : Attr Shared a, baz : Attr Shared b }c }e)" - ); - } - - #[test] - fn record_access_nested_field_is_safe() { - infer_eq( - indoc!( - r#" - \r -> - v = r.foo.bar - - x = v - y = v - - r - "# - ), - "Attr * (Attr (e | c) { foo : Attr c { bar : Attr Shared a }b }d -> Attr (e | c) { foo : Attr c { bar : Attr Shared a }b }d)" - ); - } - - #[test] - fn record_update_is_safe() { - infer_eq( - indoc!( - r#" - \r -> - - s = { r & y: r.x } - - p = s.x - q = s.y - - s - "# - ), - "Attr * (Attr c { x : Attr Shared a, y : Attr Shared a }b -> Attr c { x : Attr Shared a, y : Attr Shared a }b)", - ); - } - - #[test] - fn triple_nested_record() { - infer_eq( - indoc!( - r#" - \r -> - if True then - r.foo.bar.baz - else - r.tic.tac.toe - "# - ), - "Attr * (Attr (* | b | c | d | e | f) { foo : Attr (d | b | c) { bar : Attr (c | b) { baz : Attr b a }* }*, tic : Attr (f | b | e) { tac : Attr (e | b) { toe : Attr b a }* }* }* -> Attr b a)" - ); - } - - #[test] - fn when_with_annotation() { - infer_eq( - indoc!( - r#" - x : I64 - x = - when 2 is - 3 -> 4 - _ -> 5 - - x - "# - ), - "Attr * I64", - ); - } - - // TODO add more realistic recursive example when able - #[test] - fn factorial_is_shared() { - infer_eq( - indoc!( - r#" - factorial = \n -> - when n is - 0 -> 1 - 1 -> 1 - m -> factorial m - - factorial - "# - ), - "Attr Shared (Attr * (Num (Attr * *)) -> Attr * (Num (Attr * *)))", - ); - } - - #[test] - fn quicksort_swap() { - infer_eq( - indoc!( - r#" - swap : I64, I64, List a -> List a - swap = \i, j, list -> - when Pair (List.get list i) (List.get list j) is - Pair (Ok atI) (Ok atJ) -> - list - |> List.set i atJ - |> List.set j atI - _ -> - [] - - swap - "# - ), - "Attr * (Attr * I64, Attr * I64, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))" - ); - } - - #[test] - #[ignore] - fn quicksort() { - // theory: partition is handled before swap, so swap is not known, and therefore not taken - // out of its closure - with_larger_debug_stack(|| { - infer_eq( - indoc!( - r#" - quicksort : List (Num a), I64, I64 -> List (Num a) - quicksort = \list, low, high -> - when partition low high list is - Pair partitionIndex partitioned -> - partitioned - |> quicksort low (partitionIndex - 1) - |> quicksort (partitionIndex + 1) high - - - swap : I64, I64, List a -> List a - swap = \i, j, list -> - when Pair (List.get list i) (List.get list j) is - Pair (Ok atI) (Ok atJ) -> - list - |> List.set i atJ - |> List.set j atI - - _ -> - [] - - - partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] - partition = \low, high, initialList -> - when List.get initialList high is - Ok pivot -> - go = \i, j, list -> - if j < high then - when List.get list j is - Ok value -> - if value <= pivot then - go (i + 1) (j + 1) (swap (i + 1) j list) - else - go i (j + 1) list - - Err _ -> - Pair i list - else - Pair i list - - when go (low - 1) low initialList is - Pair newI newList -> - Pair (newI + 1) (swap (newI + 1) high newList) - - Err _ -> - Pair (low - 1) initialList - - quicksort - "# - ), - "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared I64 -> Attr b (List (Attr Shared (Num (Attr Shared a)))))" - ); - }) - } - - #[test] - fn shared_branch_unique_branch_access() { - infer_eq( - indoc!( - r#" - r = { left: 20 } - s = { left: 20 } - - if True then - r.left - else - v = s.left - s.left - "# - ), - "Attr Shared (Num (Attr * *))", - ); - } - - #[test] - fn shared_branch_unique_branch_nested_access() { - infer_eq( - indoc!( - r#" - r = { left: 20 } - s = { left: 20 } - - if True then - { y: r.left } - else - v = s.left - { y: s.left } - "# - ), - "Attr * { y : Attr Shared (Num (Attr * *)) }", - ); - } - - #[test] - fn shared_branch_unique_branch_current() { - infer_eq( - indoc!( - r#" - r = "foo" - s = { left : "foo" } - - when 0 is - 1 -> { x: s.left, y: s.left } - 0 -> { x: s.left, y: r } - "# - ), - "Attr * { x : Attr Shared Str, y : Attr Shared Str }", - ); - } - - #[test] - fn shared_branch_unique_branch_curr() { - infer_eq( - indoc!( - r#" - r = "foo" - s = { left : "foo" } - - v = s.left - - when 0 is - 1 -> { x: v, y: v } - 0 -> { x: v, y: r } - "# - ), - "Attr * { x : Attr Shared Str, y : Attr Shared Str }", - ); - } - - #[test] - fn duplicated_record() { - infer_eq( - indoc!( - r#" - s = { left: 20, right: 20 } - - { left: s, right: s } - "# - ), - // it's fine that the inner fields are not shared: only shared extraction is possible - "Attr * { left : Attr Shared { left : Attr * (Num (Attr * *)), right : Attr * (Num (Attr * *)) }, right : Attr Shared { left : Attr * (Num (Attr * *)), right : Attr * (Num (Attr * *)) } }", - ); - } - - #[test] - #[ignore] - fn result_succeed_alias() { - infer_eq( - indoc!( - r#" - Res e a : [ Err e, Ok a ] - - succeed : q -> Res p q - succeed = \x -> Ok x - - succeed - "# - ), - "Attr * (Attr a q -> Attr * (Res (Attr * p) (Attr a q)))", - ); - } - - #[test] - fn result_succeed() { - infer_eq( - indoc!( - r#" - succeed : p -> [ Err e, Ok p ] - succeed = \x -> Ok x - - succeed - "# - ), - "Attr * (Attr a p -> Attr * [ Err (Attr * e), Ok (Attr a p) ])", - ); - } - - #[test] - #[ignore] - fn list_singleton_alias() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - - singleton : p -> ConsList p - singleton = \x -> Cons x Nil - - singleton - "# - ), - "Attr * (Attr a p -> Attr * (ConsList (Attr a p)))", - ); - } - - #[test] - #[ignore] - fn list_singleton_as() { - infer_eq( - indoc!( - r#" - singleton : p -> [ Cons p (ConsList p), Nil ] as ConsList p - singleton = \x -> Cons x Nil - - singleton - "# - ), - "Attr * (Attr a p -> Attr * (ConsList (Attr a p)))", - ); - } - - #[test] - fn list_singleton_infer() { - infer_eq( - indoc!( - r#" - singleton = \x -> Cons x Nil - - singleton - "# - ), - "Attr * (a -> Attr * [ Cons a (Attr * [ Nil ]*) ]*)", - ); - } - - #[test] - #[ignore] - fn list_map_alias() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - - map : (p -> q), ConsList p -> ConsList q - map = \f, list -> - when list is - Nil -> Nil - Cons x xs -> - a = f x - b = map f xs - - Cons a b - - - map - "# - ), - "Attr Shared (Attr Shared (Attr a p -> Attr b q), Attr (* | a) (ConsList (Attr a p)) -> Attr * (ConsList (Attr b q)))" , - ); - } - - #[test] - fn list_map_infer() { - infer_eq( - indoc!( - r#" - map = \f, list -> - when list is - Nil -> Nil - Cons x xs -> - a = f x - b = map f xs - - Cons a b - - - map - "# - ), - "Attr Shared (Attr Shared (Attr b a -> c), Attr (e | b) [ Cons (Attr b a) (Attr (e | b) d), Nil ]* as d -> Attr g [ Cons c (Attr g f), Nil ]* as f)" , - ); - } - - #[test] - #[ignore] - fn peano_map_alias() { - infer_eq( - indoc!( - r#" - Peano : [ S Peano, Z ] - - map : Peano -> Peano - map = \peano -> - when peano is - Z -> Z - S rest -> - map rest |> S - - - map - "# - ), - "Attr Shared (Attr * Peano -> Attr * Peano)", - ); - } - - #[test] - fn peano_map_infer() { - infer_eq( - indoc!( - r#" - map = \peano -> - when peano is - Z -> Z - S rest -> - map rest |> S - - - map - "# - ), - "Attr Shared (Attr b [ S (Attr b a), Z ]* as a -> Attr d [ S (Attr d c), Z ]* as c)", - ); - } - - #[test] - #[ignore] - fn rigids_in_signature() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - - map : (p -> q), ConsList p -> ConsList q - map = \f, list -> - when list is - Cons x xs -> - Cons (f x) (map f xs) - - Nil -> - Nil - - map - "# - ), - "Attr Shared (Attr Shared (Attr a p -> Attr b q), Attr (* | a) (ConsList (Attr a p)) -> Attr * (ConsList (Attr b q)))", - ); - } - - #[test] - fn rigid_in_letnonrec() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - - toEmpty : ConsList p -> ConsList p - toEmpty = \_ -> - result : ConsList p - result = Nil - - result - - toEmpty - "# - ), - "Attr * (Attr * (ConsList (Attr * p)) -> Attr * (ConsList (Attr * p)))", - ); - } - - #[test] - fn rigid_in_letrec() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - - toEmpty : ConsList p -> ConsList p - toEmpty = \_ -> - result : ConsList p - result = Nil - - toEmpty result - - toEmpty - "# - ), - "Attr Shared (Attr * (ConsList (Attr * p)) -> Attr * (ConsList (Attr * p)))", - ); - } - - #[test] - fn let_record_pattern_with_annotation_inline() { - infer_eq( - indoc!( - r#" - { x, y } : { x : Str.Str, y : F64 } - { x, y } = { x : "foo", y : 3.14 } - - x - "# - ), - "Attr * Str", - ); - } - - #[test] - fn let_record_pattern_with_annotation_alias() { - infer_eq( - indoc!( - r#" - Foo : { x : Str, y : F64 } - - { x, y } : Foo - { x, y } = { x : "foo", y : 3.14 } - - x - "# - ), - "Attr * Str", - ); - } - - #[test] - fn alias_of_alias() { - infer_eq( - indoc!( - r#" - Foo : { x : Str, y : F64 } - - Bar : Foo - - f : Bar -> Str - f = \{ x } -> x - - f - "# - ), - "Attr * (Attr (* | a) Bar -> Attr a Str)", - ); - } - - #[test] - #[ignore] - fn alias_of_alias_with_type_variable() { - infer_eq( - indoc!( - r#" - Identity a : [ Identity a ] - - ID a : Identity a - - f : ID a -> a - f = \Identity x -> x - - f - "# - ), - "Attr * (Attr (* | b) (ID (Attr b a)) -> Attr b a)", - ); - } - - #[test] - #[ignore] - fn alias_assoc_list_head() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - AssocList a b : ConsList { key: a, value : b } - Maybe a : [ Just a, Nothing ] - - # AssocList2 a b : [ Cons { key: a, value : b } (AssocList2 a b), Nil ] - - head : AssocList k v -> Maybe { key: k , value: v } - head = \alist -> - when alist is - Cons first _ -> - Just first - - Nil -> - Nothing - - head - "# - ), - "Attr * (Attr (* | c) (AssocList (Attr a k) (Attr b v)) -> Attr * (Maybe (Attr c { key : Attr a k, value : Attr b v })))" - ); - } - - #[test] - #[ignore] - fn cons_list_as_assoc_list_head() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - Maybe a : [ Just a, Nothing ] - - head : ConsList { key: k, value: v } -> Maybe { key: k , value: v } - head = \alist -> - when alist is - Cons first _ -> - Just first - - Nil -> - Nothing - - head - "# - ), - "Attr * (Attr (* | c) (ConsList (Attr c { key : Attr a k, value : Attr b v })) -> Attr * (Maybe (Attr c { key : Attr a k, value : Attr b v })))" - ); - } - - #[test] - #[ignore] - fn assoc_list_map() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - - map : ConsList a -> ConsList a - map = \list -> - when list is - Cons r xs -> Cons r xs - Nil -> Nil - - map - "# - ), - "Attr * (Attr (c | b) (ConsList (Attr b a)) -> Attr (c | b) (ConsList (Attr b a)))", - ); - } - - #[test] - fn same_uniqueness_builtin_list() { - infer_eq( - indoc!( - r#" - toAs : (q -> p), p, q -> List p - toAs = - \f, x, y -> [ x, (f y) ] - toAs - "# - ), - "Attr * (Attr * (Attr a q -> Attr b p), Attr b p, Attr a q -> Attr * (List (Attr b p)))" - ); - } - - #[test] - #[ignore] - fn same_uniqueness_cons_list() { - infer_eq( - indoc!( - r#" - ConsList q : [ Cons q (ConsList q), Nil ] - - toAs : (q -> p), p, q -> ConsList p - toAs = - \f, x, y -> - # Cons (f y) (Cons x Nil) - Cons x (Cons (f y) Nil) - toAs - "# - ), - "Attr * (Attr * (Attr a q -> Attr b p), Attr b p, Attr a q -> Attr * (ConsList (Attr b p)))" - ); - } - - #[test] - #[ignore] - fn typecheck_mutually_recursive_tag_union() { - infer_eq( - indoc!( - r#" - ListA a b : [ Cons a (ListB b a), Nil ] - ListB a b : [ Cons a (ListA b a), Nil ] - - ConsList q : [ Cons q (ConsList q), Nil ] - - toAs : (q -> p), ListA p q -> ConsList p - toAs = - \f, lista -> - when lista is - Nil -> Nil - Cons a listb -> - when listb is - Nil -> Nil - Cons b newLista -> - Cons a (Cons (f b) (toAs f newLista)) - - foo = \_ -> - x = 4 - { a : x, b : x }.a - - toAs - "# - ), - "Attr Shared (Attr Shared (Attr a q -> Attr b p), Attr (c | a | b) (ListA (Attr b p) (Attr a q)) -> Attr * (ConsList (Attr b p)))" - ); - } - - #[test] - #[ignore] - fn typecheck_triple_mutually_recursive_tag_union() { - infer_eq( - indoc!( - r#" - ListA a b : [ Cons a (ListB b a), Nil ] - ListB a b : [ Cons a (ListC b a), Nil ] - ListC a b : [ Cons a (ListA b a), Nil ] - - ConsList q : [ Cons q (ConsList q), Nil ] - - toAs : (q -> p), ListA p q -> ConsList p - toAs = - \f, lista -> - when lista is - Nil -> Nil - Cons a listb -> - when listb is - Nil -> Nil - Cons b listc -> - when listc is - Nil -> - Nil - - Cons c newListA -> - Cons a (Cons (f b) (Cons c (toAs f newListA))) - - toAs - "# - ), - "Attr Shared (Attr Shared (Attr a q -> Attr b p), Attr (c | a | b) (ListA (Attr b p) (Attr a q)) -> Attr * (ConsList (Attr b p)))" - ); - } - - #[test] - fn infer_mutually_recursive_tag_union() { - infer_eq( - indoc!( - r#" - toAs = \f, lista -> - when lista is - Nil -> Nil - Cons a listb -> - when listb is - Nil -> Nil - Cons b newConsLista -> - Cons a (Cons (f b) (toAs f newConsLista)) - - toAs - "# - ), - "Attr Shared (Attr Shared (Attr b a -> c), Attr (g | b | e) [ Cons (Attr e d) (Attr (g | b | e) [ Cons (Attr b a) (Attr (g | b | e) f), Nil ]*), Nil ]* as f -> Attr i [ Cons (Attr e d) (Attr * [ Cons c (Attr i h) ]*), Nil ]* as h)" - ); - } - - #[test] - fn addition() { - infer_eq( - indoc!( - r#" - 4 + 4 - "# - ), - "Attr a (Num (Attr a *))", - ); - } - - #[test] - fn list_get_at() { - infer_eq( - indoc!( - r#" - [1,2,3,4] - |> List.get 2 - "# - ), - "Attr * (Result (Attr * (Num (Attr * *))) (Attr * [ OutOfBounds ]*))", - ); - } - - #[test] - fn float_div_builtins() { - infer_eq( - indoc!( - r#" - Num.maxFloat / Num.maxFloat - "# - ), - "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))", - ); - } - - #[test] - fn float_div_literals() { - infer_eq( - indoc!( - r#" - 3.0 / 4.0 - "# - ), - "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))", - ); - } - - #[test] - fn float_div_literal_builtin() { - infer_eq( - indoc!( - r#" - 3.0 / Num.maxFloat - "# - ), - "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))", - ); - } - - #[test] - fn int_div_builtins() { - infer_eq( - indoc!( - r#" - Num.maxInt // Num.maxInt - "# - ), - "Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))", - ); - } - - #[test] - fn int_div_literals() { - infer_eq( - indoc!( - r#" - 3 // 4 - "# - ), - "Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))", - ); - } - - #[test] - fn int_div_literal_builtin() { - infer_eq( - indoc!( - r#" - 3 // Num.maxInt - "# - ), - "Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))", - ); - } - - #[test] - fn list_repeated_get() { - infer_eq( - indoc!( - r#" - \list -> - p = List.get list 1 - q = List.get list 1 - - { p, q } - "# - ), - "Attr * (Attr * (List (Attr Shared a)) -> Attr * { p : Attr * (Result (Attr Shared a) (Attr * [ OutOfBounds ]*)), q : Attr * (Result (Attr Shared a) (Attr * [ OutOfBounds ]*)) })" - ); - } - - #[test] - fn list_get_then_set() { - infer_eq( - indoc!( - r#" - \list -> - when List.get list 0 is - Ok v -> - List.set list 0 (v + 1) - - Err _ -> - list - "# - ), - "Attr * (Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr b (List (Attr Shared (Num (Attr Shared a)))))", - ); - } - - #[test] - fn list_is_empty_then_set() { - infer_eq( - indoc!( - r#" - \list -> - if List.isEmpty list then - list - else - List.set list 0 42 - "# - ), - "Attr * (Attr (d | c) (List (Attr c (Num (Attr b a)))) -> Attr (d | c) (List (Attr c (Num (Attr b a)))))", - ); - } - - #[test] - fn list_map_identity() { - infer_eq( - indoc!(r#"\list -> List.map list (\x -> x)"#), - "Attr * (Attr * (List a) -> Attr * (List a))", - ); - } - - #[test] - fn list_walk_backwards_sum() { - infer_eq( - indoc!( - r#" - sum = \list -> List.walkBackwards list Num.add 0 - - sum - "# - ), - "Attr * (Attr (* | b) (List (Attr b (Num (Attr b a)))) -> Attr c (Num (Attr c a)))", - ); - } - - #[test] - fn num_add() { - infer_eq( - "Num.add", - "Attr * (Attr b (Num (Attr b a)), Attr c (Num (Attr c a)) -> Attr d (Num (Attr d a)))", - ); - } - - #[test] - fn list_isempty() { - infer_eq("List.isEmpty", "Attr * (Attr * (List *) -> Attr * Bool)"); - } - - #[test] - fn list_len() { - infer_eq("List.len", "Attr * (Attr * (List *) -> Attr * I64)"); - } - - #[test] - fn list_get() { - infer_eq("List.get", "Attr * (Attr (* | b) (List (Attr b a)), Attr * I64 -> Attr * (Result (Attr b a) (Attr * [ OutOfBounds ]*)))"); - } - - #[test] - fn list_set() { - infer_eq("List.set", "Attr * (Attr (* | b | c) (List (Attr b a)), Attr * I64, Attr (b | c) a -> Attr * (List (Attr b a)))"); - } - - #[test] - fn list_single() { - infer_eq("List.single", "Attr * (a -> Attr * (List a))"); - } - - #[test] - fn list_repeat() { - infer_eq( - "List.repeat", - "Attr * (Attr * I64, Attr Shared a -> Attr * (List (Attr Shared a)))", - ); - } - - #[test] - fn list_append() { - infer_eq( - "List.append", - "Attr * (Attr * (List a), a -> Attr * (List a))", - ); - } - - #[test] - fn list_map() { - infer_eq( - "List.map", - "Attr * (Attr * (List a), Attr Shared (a -> b) -> Attr * (List b))", - ); - } - - #[test] - fn list_push_singleton() { - infer_eq( - indoc!( - r#" - singleton = \x -> List.append [] x - - singleton - "# - ), - "Attr * (a -> Attr * (List a))", - ); - } - - #[test] - fn list_walk_backwards_reverse() { - infer_eq( - indoc!( - r#" - reverse = \list -> List.walkBackwards list (\e, l -> List.append l e) [] - - reverse - "# - ), - "Attr * (Attr (* | b) (List (Attr b a)) -> Attr * (List (Attr b a)))", - ); - } - - #[test] - fn set_then_get() { - infer_eq( - indoc!( - r#" - when List.get (List.set [ 12, 9, 7, 3 ] 1 42) 1 is - Ok num -> num - Err OutOfBounds -> 0 - "# - ), - "Attr * (Num (Attr * *))", - ); - } - - #[test] - fn set_empty() { - infer_eq("Set.empty", "Attr * (Set *)"); - } - - #[test] - fn set_singelton() { - infer_eq("Set.singleton", "Attr * (a -> Attr * (Set a))"); - } - - #[test] - fn set_union() { - infer_eq( - "Set.union", - "Attr * (Attr * (Set (Attr * a)), Attr * (Set (Attr * a)) -> Attr * (Set (Attr * a)))", - ); - } - - #[test] - fn set_diff() { - infer_eq( - "Set.diff", - "Attr * (Attr * (Set (Attr * a)), Attr * (Set (Attr * a)) -> Attr * (Set (Attr * a)))", - ); - } - - #[test] - fn set_foldl() { - infer_eq( - "Set.foldl", - "Attr * (Attr (* | b) (Set (Attr b a)), Attr Shared (Attr b a, c -> c), c -> c)", - ); - } - - #[test] - fn set_insert() { - infer_eq("Set.insert", "Attr * (Attr * (Set a), a -> Attr * (Set a))"); - } - - #[test] - fn set_remove() { - infer_eq( - "Set.remove", - "Attr * (Attr * (Set (Attr b a)), Attr * a -> Attr * (Set (Attr b a)))", - ); - } - - #[test] - fn map_empty() { - infer_eq("Dict.empty", "Attr * (Dict * *)"); - } - - #[test] - fn map_singelton() { - infer_eq("Dict.singleton", "Attr * (a, b -> Attr * (Dict a b))"); - } - - #[test] - fn map_get() { - infer_eq("Dict.get", "Attr * (Attr (* | c) (Dict (Attr * a) (Attr c b)), Attr * a -> Attr * (Result (Attr c b) (Attr * [ KeyNotFound ]*)))"); - } - - #[test] - fn map_insert() { - infer_eq( - "Dict.insert", - "Attr * (Attr * (Dict a b), a, b -> Attr * (Dict a b))", - ); - } - - #[test] - fn str_is_empty() { - infer_eq("Str.isEmpty", "Attr * (Attr * Str -> Attr * Bool)"); - } - - #[test] - fn str_concat() { - infer_eq( - "Str.concat", - "Attr * (Attr * Str, Attr * Str -> Attr * Str)", - ); - } - - #[test] - fn result_map() { - infer_eq( - "Result.map", - "Attr * (Attr * (Result a b), Attr * (a -> c) -> Attr * (Result c b))", - ); - } - - #[test] - #[ignore] - fn list_roc_head() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - Maybe a : [ Just a, Nothing ] - - head : ConsList a -> Maybe a - head = \list -> - when list is - Cons x _ -> Just x - Nil -> Nothing - - head - "# - ), - "Attr * (Attr (* | b) (ConsList (Attr b a)) -> Attr * (Maybe (Attr b a)))", - ); - } - - #[test] - #[ignore] - fn list_roc_is_empty() { - infer_eq( - indoc!( - r#" - ConsList a : [ Cons a (ConsList a), Nil ] - - isEmpty : ConsList a -> Bool - isEmpty = \list -> - when list is - Cons _ _ -> False - Nil -> True - - isEmpty - "# - ), - "Attr * (Attr (* | b) (ConsList (Attr b a)) -> Attr * Bool)", - ); - } - - #[test] - fn hidden_uniqueness_one() { - infer_eq( - indoc!( - r#" - Model : { foo : I64 } - - extract : Model -> I64 - extract = \{ foo } -> foo - - extract - "# - ), - "Attr * (Attr (* | a) Model -> Attr a I64)", - ); - } - - #[test] - fn hidden_uniqueness_two() { - infer_eq( - indoc!( - r#" - Model : { foo : I64, bar : I64 } - - extract : Model -> I64 - extract = \{ foo } -> foo - - extract - "# - ), - "Attr * (Attr (* | a) Model -> Attr a I64)", - ); - } - - #[test] - fn hidden_uniqueness_three() { - infer_eq( - indoc!( - r#" - Model : { foo : I64, bar : I64 } - - extract : Model -> I64 - extract = \{foo, bar} -> foo + bar - - extract - "# - ), - "Attr * (Attr (* | * | *) Model -> Attr * I64)", - ); - } - - #[test] - #[ignore] - fn peano_roc_is_empty() { - infer_eq( - indoc!( - r#" - Peano : [ Z, S Peano ] - - isEmpty : Peano -> Bool - isEmpty = \list -> - when list is - S _ -> False - Z -> True - - isEmpty - "# - ), - "Attr * (Attr * Peano -> Attr * Bool)", - ); - } - - #[test] - #[ignore] - fn result_roc_map() { - infer_eq( - indoc!( - r#" - map : Result a e, (a -> b) -> Result b e - map = \result, f -> - when result is - Ok v -> Ok (f v) - Err e -> Err e - - map - "# - ), - "Attr * (Attr (* | c | d) (Result (Attr c a) (Attr d e)), Attr * (Attr c a -> Attr f b) -> Attr * (Result (Attr f b) (Attr d e)))" - ); - } - - #[test] - #[ignore] - fn result_roc_with_default_with_signature() { - infer_eq( - indoc!( - r#" - withDefault : Result a e, a -> a - withDefault = \result, default -> - when result is - Ok v -> v - Err _ -> default - - withDefault - "# - ), - "Attr * (Attr (* | b | c) (Result (Attr b a) (Attr c e)), Attr b a -> Attr b a)", - ); - } - - #[test] - fn result_roc_with_default_no_signature() { - infer_eq( - indoc!( - r#" - \result, default -> - when result is - Ok x -> x - Err _ -> default - "# - ), - "Attr * (Attr (* | a | c) [ Err (Attr a *), Ok (Attr c b) ]*, Attr c b -> Attr c b)", - ); - } - - #[test] - fn record_pattern_match_field() { - infer_eq( - indoc!( - r#" - f : { x : b } -> b - f = \{ x } -> x - - f - "# - ), - "Attr * (Attr (* | a) { x : Attr a b } -> Attr a b)", - ); - } - - #[test] - fn int_addition_with_annotation() { - infer_eq( - indoc!( - r#" - f : I64, I64 -> I64 - f = \a, b -> a + b - - f - "# - ), - "Attr * (Attr * I64, Attr * I64 -> Attr * I64)", - ); - } - - #[test] - fn int_abs_with_annotation() { - infer_eq( - indoc!( - r#" - foobar : I64 -> I64 - foobar = \x -> Num.abs x - - foobar - "# - ), - "Attr * (Attr * I64 -> Attr * I64)", - ); - } - - #[test] - fn int_addition_without_annotation() { - infer_eq( - indoc!( - r#" - f = \a, b -> a + b + 0x0 - - f - "# - ), - "Attr * (Attr b I64, Attr c I64 -> Attr d I64)", - ); - } - - #[test] - fn num_addition_with_annotation() { - infer_eq( - indoc!( - r#" - f : Num a, Num a -> Num a - f = \a, b -> a + b - - f - "# - ), - "Attr * (Attr b (Num (Attr b a)), Attr c (Num (Attr c a)) -> Attr d (Num (Attr d a)))", - ); - } - - #[test] - fn num_addition_without_annotation() { - infer_eq( - indoc!( - r#" - f = \a, b -> a + b - - f - "# - ), - "Attr * (Attr b (Num (Attr b a)), Attr c (Num (Attr c a)) -> Attr d (Num (Attr d a)))", - ); - } - - #[test] - fn num_abs_with_annotation() { - infer_eq( - indoc!( - r#" - f : Num a -> Num a - f = \x -> Num.abs x - - f - "# - ), - "Attr * (Attr b (Num (Attr b a)) -> Attr c (Num (Attr c a)))", - ); - } - - #[test] - fn num_abs_without_annotation() { - infer_eq( - indoc!( - r#" - \x -> Num.abs x - "# - ), - "Attr * (Attr b (Num (Attr b a)) -> Attr c (Num (Attr c a)))", - ); - } - - #[test] - fn use_correct_ext_var() { - infer_eq( - indoc!( - r#" - f = \r -> - g = r.q - h = r.p - - 42 - - f - "# - ), - //"Attr * (Attr (* | a | b) { p : Attr a *, q : Attr b * }* -> Attr * (Num (Attr * *)))", - "Attr * (Attr (* | a | b) { p : Attr a *, q : Attr b * }* -> Attr * (Num (Attr * *)))", - ); - } - - #[test] - #[ignore] - fn reconstruct_path() { - infer_eq( - indoc!( - r#" - reconstructPath : Dict position position, position -> List position - reconstructPath = \cameFrom, goal -> - when Dict.get cameFrom goal is - Err KeyNotFound -> - [] - - Ok next -> - List.append (reconstructPath cameFrom next) goal - - reconstructPath - "# - ), - "Attr Shared (Attr Shared (Dict (Attr * position) (Attr Shared position)), Attr Shared position -> Attr * (List (Attr Shared position)))" - ); - } - - #[test] - #[ignore] - fn cheapest_open() { - with_larger_debug_stack(|| { - infer_eq( - indoc!( - r#" - Model position : { evaluated : Set position - , openSet : Set position - , costs : Dict.Dict position F64 - , cameFrom : Dict.Dict position position - } - - cheapestOpen : (position -> F64), Model position -> Result position [ KeyNotFound ]* - cheapestOpen = \costFunction, model -> - - folder = \position, resSmallestSoFar -> - when Dict.get model.costs position is - Err e -> - Err e - - Ok cost -> - positionCost = costFunction position - - when resSmallestSoFar is - Err _ -> Ok { position, cost: cost + positionCost } - Ok smallestSoFar -> - if positionCost + cost < smallestSoFar.cost then - Ok { position, cost: cost + positionCost } - - else - Ok smallestSoFar - - Set.foldl model.openSet folder (Err KeyNotFound) - |> Result.map (\x -> x.position) - - cheapestOpen - "# - ), - "Attr * (Attr * (Attr Shared position -> Attr * F64), Attr (* | * | a | b) (Model (Attr Shared position)) -> Attr * (Result (Attr Shared position) (Attr * [ KeyNotFound ]*)))" - ) - }); - } - - #[test] - #[ignore] - fn update_cost() { - infer_eq( - indoc!( - r#" - Model position : { evaluated : Set position - , openSet : Set position - , costs : Dict.Dict position F64 - , cameFrom : Dict.Dict position position - } - - reconstructPath : Dict position position, position -> List position - reconstructPath = \cameFrom, goal -> - when Dict.get cameFrom goal is - Err KeyNotFound -> - [] - - Ok next -> - List.append (reconstructPath cameFrom next) goal - - updateCost : position, position, Model position -> Model position - updateCost = \current, neighbour, model -> - newCameFrom = Dict.insert model.cameFrom neighbour current - - newCosts = Dict.insert model.costs neighbour distanceTo - - distanceTo = reconstructPath newCameFrom neighbour - |> List.len - |> Num.toFloat - - newModel = { model & costs : newCosts , cameFrom : newCameFrom } - - when Dict.get model.costs neighbour is - Err KeyNotFound -> - newModel - - Ok previousDistance -> - if distanceTo < previousDistance then - newModel - - else - model - updateCost - "# - ), - "Attr * (Attr Shared position, Attr Shared position, Attr Shared (Model (Attr Shared position)) -> Attr Shared (Model (Attr Shared position)))" - ); - } - - #[test] - #[ignore] - fn astar_full_code() { - // theory: things are canonicalized in an order that leaves too much captured - with_larger_debug_stack(|| { - infer_eq( - indoc!( - r#" - Model position : { evaluated : Set position - , openSet : Set position - , costs : Dict.Dict position F64 - , cameFrom : Dict.Dict position position - } - - - initialModel : position -> Model position - initialModel = \start -> - { evaluated : Set.empty - , openSet : Set.singleton start - , costs : Dict.singleton start 0.0 - , cameFrom : Dict.empty - } - - - cheapestOpen : (position -> F64), Model position -> Result position [ KeyNotFound ]* - cheapestOpen = \costFunction, model -> - - folder = \position, resSmallestSoFar -> - when Dict.get model.costs position is - Err e -> - Err e - - Ok cost -> - positionCost = costFunction position - - when resSmallestSoFar is - Err _ -> Ok { position, cost: cost + positionCost } - Ok smallestSoFar -> - if positionCost + cost < smallestSoFar.cost then - Ok { position, cost: cost + positionCost } - - else - Ok smallestSoFar - - - Set.foldl model.openSet folder (Err KeyNotFound) - |> Result.map (\x -> x.position) - - - reconstructPath : Dict position position, position -> List position - reconstructPath = \cameFrom, goal -> - when Dict.get cameFrom goal is - Err KeyNotFound -> - [] - - Ok next -> - List.append (reconstructPath cameFrom next) goal - - - updateCost : position, position, Model position -> Model position - updateCost = \current, neighbour, model -> - newCameFrom = Dict.insert model.cameFrom neighbour current - - newCosts = Dict.insert model.costs neighbour distanceTo - - distanceTo = - reconstructPath newCameFrom neighbour - |> List.len - |> Num.toFloat - - newModel = { model & costs : newCosts , cameFrom : newCameFrom } - - when Dict.get model.costs neighbour is - Err KeyNotFound -> - newModel - - Ok previousDistance -> - if distanceTo < previousDistance then - newModel - - else - model - - - findPath : { costFunction: (position, position -> F64), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [ KeyNotFound ]* - findPath = \{ costFunction, moveFunction, start, end } -> - astar costFunction moveFunction end (initialModel start) - - - astar : (position, position -> F64), (position -> Set position), position, Model position -> [ Err [ KeyNotFound ]*, Ok (List position) ]* - astar = \costFn, moveFn, goal, model -> - when cheapestOpen (\position -> costFn goal position) model is - Err _ -> - Err KeyNotFound - - Ok current -> - if current == goal then - Ok (reconstructPath model.cameFrom goal) - - else - modelPopped = { model & openSet : Set.remove model.openSet current, evaluated : Set.insert model.evaluated current } - - neighbours = moveFn current - - newNeighbours = Set.diff neighbours modelPopped.evaluated - - modelWithNeighbours = { modelPopped & openSet : Set.union modelPopped.openSet newNeighbours } - - modelWithCosts = Set.foldl newNeighbours (\nb, md -> updateCost current nb md) modelWithNeighbours - - astar costFn moveFn goal modelWithCosts - - findPath - "# - ), - "Attr * (Attr * { costFunction : Attr Shared (Attr Shared position, Attr Shared position -> Attr * F64), end : Attr Shared position, moveFunction : Attr Shared (Attr Shared position -> Attr * (Set (Attr * position))), start : Attr Shared position } -> Attr * (Result (Attr * (List (Attr Shared position))) (Attr * [ KeyNotFound ]*)))" - ) - }); - } - - #[test] - fn bool_eq() { - infer_eq( - "\\a, b -> a == b", - "Attr * (Attr * a, Attr * a -> Attr * Bool)", - ); - } - - #[test] - fn bool_neq() { - infer_eq( - "\\a, b -> a != b", - "Attr * (Attr * a, Attr * a -> Attr * Bool)", - ); - } - - #[test] - fn instantiated_alias() { - infer_eq( - indoc!( - r#" - Model a : { foo : Set a } - - - initialModel : position -> Model I64 - initialModel = \_ -> { foo : Set.empty } - - initialModel - "# - ), - "Attr * (Attr * position -> Attr * (Model (Attr * I64)))", - ); - } - - #[test] - fn when_with_or_pattern_and_guard() { - infer_eq( - indoc!( - r#" - \x -> - when x is - 2 | 3 -> 0 - a if a < 20 -> 1 - 3 | 4 if False -> 2 - _ -> 3 - "# - ), - "Attr * (Attr Shared (Num (Attr Shared *)) -> Attr * (Num (Attr * *)))", - ); - } - - // OPTIONAL RECORD FIELDS - - #[test] - fn optional_field_unifies_with_missing() { - infer_eq( - indoc!( - r#" - negatePoint : { x : I64, y : I64, z ? Num c } -> { x : I64, y : I64, z : Num c } - - negatePoint { x: 1, y: 2 } - "# - ), - "Attr * { x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) }", - ); - } - - #[test] - fn open_optional_field_unifies_with_missing() { - infer_eq( - indoc!( - r#" - negatePoint : { x : I64, y : I64, z ? Num c }r -> { x : I64, y : I64, z : Num c }r - - a = negatePoint { x: 1, y: 2 } - b = negatePoint { x: 1, y: 2, blah : "hi" } - - { a, b } - "# - ), - "Attr * { a : Attr * { x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) }, b : Attr * { blah : Attr * Str, x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) } }" - ); - } - - #[test] - fn optional_field_unifies_with_present() { - infer_eq( - indoc!( - r#" - negatePoint : { x : Num a, y : Num b, z ? c } -> { x : Num a, y : Num b, z : c } - - negatePoint { x: 1, y: 2.1, z: 0x3 } - "# - ), - "Attr * { x : Attr * (Num (Attr * a)), y : Attr * F64, z : Attr * I64 }", - ); - } - - #[test] - fn open_optional_field_unifies_with_present() { - infer_eq( - indoc!( - r#" - negatePoint : { x : Num a, y : Num b, z ? c }r -> { x : Num a, y : Num b, z : c }r - - a = negatePoint { x: 1, y: 2.1 } - b = negatePoint { x: 1, y: 2.1, blah : "hi" } - - { a, b } - "# - ), - "Attr * { a : Attr * { x : Attr * (Num (Attr * a)), y : Attr * F64, z : Attr * c }, b : Attr * { blah : Attr * Str, x : Attr * (Num (Attr * a)), y : Attr * F64, z : Attr * c } }" - ); - } - - #[test] - fn optional_field_function() { - infer_eq( - indoc!( - r#" - \{ x, y ? 0 } -> x + y - "# - ), - "Attr * (Attr (* | b | c) { x : Attr b (Num (Attr b a)), y ? Attr c (Num (Attr c a)) }* -> Attr d (Num (Attr d a)))" - ); - } - - #[test] - fn optional_field_let() { - infer_eq( - indoc!( - r#" - { x, y ? 0 } = { x: 32 } - - x + y - "# - ), - "Attr a (Num (Attr a *))", - ); - } - - #[test] - fn optional_field_when() { - infer_eq( - indoc!( - r#" - \r -> - when r is - { x, y ? 0 } -> x + y - "# - ), - "Attr * (Attr (* | b | c) { x : Attr b (Num (Attr b a)), y ? Attr c (Num (Attr c a)) }* -> Attr d (Num (Attr d a)))" - ); - } - - #[test] - fn list_walk_backwards() { - infer_eq( - indoc!( - r#" - List.walkBackwards - "# - ), - "Attr * (Attr (* | b) (List (Attr b a)), Attr Shared (Attr b a, c -> c), c -> c)", - ); - } - - #[test] - fn list_walk_backwards_example() { - infer_eq( - indoc!( - r#" - empty : List I64 - empty = - [] - - List.walkBackwards empty (\a, b -> a + b) 0 - "# - ), - "Attr a I64", - ); - } - - #[test] - fn list_set_out_of_bounds_num() { - infer_eq( - indoc!( - r#" - List.set [2] 1337 0 - "# - ), - "Attr * (List (Attr * (Num (Attr * *))))", - ); - } - - #[test] - fn list_set_out_of_bounds_int() { - infer_eq( - indoc!( - r#" - List.set [0x2] 1337 0 - "# - ), - "Attr * (List (Attr * I64))", - ); - } - - #[test] - fn list_set_out_of_bounds_float() { - infer_eq( - indoc!( - r#" - List.set [0.2] 1337 0 - "# - ), - "Attr * (List (Attr * F64))", - ); - } - - #[test] - #[ignore] - fn list_set_out_of_bounds_int_int() { - // the unification of an integer list with a new integer element is a problem - // same for floats, but it's fine with the unspecified Num - infer_eq( - indoc!( - r#" - List.set [0x2] 1337 0x1 - "# - ), - "Attr * (List (Attr a I64))", - ); - } -} diff --git a/compiler/types/src/builtin_aliases.rs b/compiler/types/src/builtin_aliases.rs index 8e734fe99a..2cd3e55de3 100644 --- a/compiler/types/src/builtin_aliases.rs +++ b/compiler/types/src/builtin_aliases.rs @@ -28,6 +28,26 @@ pub fn aliases() -> MutMap { aliases.insert(symbol, alias); }; + // Int range : [ @Int range ] + add_alias( + Symbol::NUM_INT, + BuiltinAlias { + region: Region::zero(), + vars: vec![Located::at(Region::zero(), "range".into())], + typ: int_alias_content(flex(TVAR1)), + }, + ); + + // Float range : [ @Float range ] + add_alias( + Symbol::NUM_FLOAT, + BuiltinAlias { + region: Region::zero(), + vars: vec![Located::at(Region::zero(), "range".into())], + typ: float_alias_content(flex(TVAR1)), + }, + ); + // Num range : [ @Num range ] add_alias( Symbol::NUM_NUM, @@ -48,6 +68,26 @@ pub fn aliases() -> MutMap { }, ); + // Natural : [ @Natural ] + add_alias( + Symbol::NUM_NATURAL, + BuiltinAlias { + region: Region::zero(), + vars: vec![], + typ: natural_alias_content(), + }, + ); + + // Nat : Int Natural + add_alias( + Symbol::NUM_NAT, + BuiltinAlias { + region: Region::zero(), + vars: Vec::new(), + typ: int_alias_content(natural_type()), + }, + ); + // Signed128 : [ @Signed128 ] add_alias( Symbol::NUM_SIGNED128, @@ -58,7 +98,7 @@ pub fn aliases() -> MutMap { }, ); - // I128 : Num (Integer Signed128) + // I128 : Int Signed128 add_alias( Symbol::NUM_I128, BuiltinAlias { @@ -68,7 +108,7 @@ pub fn aliases() -> MutMap { }, ); - // U128 : Num (Integer Unsigned128) + // U128 : Int Unsigned128 add_alias( Symbol::NUM_U128, BuiltinAlias { @@ -88,7 +128,7 @@ pub fn aliases() -> MutMap { }, ); - // I64 : Num (Integer Signed64) + // I64 : Int Signed64 add_alias( Symbol::NUM_I64, BuiltinAlias { @@ -98,7 +138,7 @@ pub fn aliases() -> MutMap { }, ); - // U64 : Num (Integer Unsigned64) + // U64 : Int Unsigned64 add_alias( Symbol::NUM_U64, BuiltinAlias { @@ -118,7 +158,7 @@ pub fn aliases() -> MutMap { }, ); - // I32 : Num (Integer Signed32) + // I32 : Int Signed32 add_alias( Symbol::NUM_I32, BuiltinAlias { @@ -128,7 +168,7 @@ pub fn aliases() -> MutMap { }, ); - // U32 : Num (Integer Unsigned32) + // U32 : Int Unsigned32 add_alias( Symbol::NUM_U32, BuiltinAlias { @@ -148,7 +188,7 @@ pub fn aliases() -> MutMap { }, ); - // I16 : Num (Integer Signed16) + // I16 : Int Signed16 add_alias( Symbol::NUM_I16, BuiltinAlias { @@ -158,7 +198,7 @@ pub fn aliases() -> MutMap { }, ); - // U16 : Num (Integer Unsigned16) + // U16 : Int Unsigned16 add_alias( Symbol::NUM_U16, BuiltinAlias { @@ -178,7 +218,7 @@ pub fn aliases() -> MutMap { }, ); - // I8 : Num (Integer Signed8) + // I8 : Int Signed8 add_alias( Symbol::NUM_I8, BuiltinAlias { @@ -188,7 +228,7 @@ pub fn aliases() -> MutMap { }, ); - // U8 : Num (Integer Unsigned8) + // U8 : Int Unsigned8 add_alias( Symbol::NUM_U8, BuiltinAlias { @@ -228,7 +268,7 @@ pub fn aliases() -> MutMap { }, ); - // F64 : Num (FloatingPoint Binary64) + // F64 : Float Binary64 add_alias( Symbol::NUM_F64, BuiltinAlias { @@ -238,7 +278,7 @@ pub fn aliases() -> MutMap { }, ); - // F32 : Num (FloatingPoint Binary32) + // F32 : Float Binary32 add_alias( Symbol::NUM_F32, BuiltinAlias { @@ -312,27 +352,39 @@ fn floatingpoint_alias_content(range: SolvedType) -> SolvedType { // FLOAT #[inline(always)] -pub fn float_type() -> SolvedType { +pub fn float_type(range: SolvedType) -> SolvedType { SolvedType::Alias( - Symbol::NUM_F64, - Vec::new(), - Box::new(float_alias_content(binary64_type())), + Symbol::NUM_FLOAT, + vec![("range".into(), range.clone())], + Box::new(float_alias_content(range)), ) } #[inline(always)] -fn float_alias_content(typ: SolvedType) -> SolvedType { - num_type(floatingpoint_type(typ)) +fn float_alias_content(range: SolvedType) -> SolvedType { + num_type(floatingpoint_type(range)) +} + +// Nat + +#[inline(always)] +pub fn nat_type() -> SolvedType { + SolvedType::Alias(Symbol::NUM_NAT, vec![], Box::new(nat_alias_content())) +} + +#[inline(always)] +fn nat_alias_content() -> SolvedType { + int_alias_content(natural_type()) } // INT #[inline(always)] -pub fn int_type() -> SolvedType { +pub fn int_type(range: SolvedType) -> SolvedType { SolvedType::Alias( - Symbol::NUM_I64, - Vec::new(), - Box::new(int_alias_content(signed64_type())), + Symbol::NUM_INT, + vec![("range".into(), range.clone())], + Box::new(int_alias_content(range)), ) } @@ -385,6 +437,20 @@ fn binary32_alias_content() -> SolvedType { single_private_tag(Symbol::NUM_AT_BINARY32, vec![]) } +#[inline(always)] +pub fn natural_type() -> SolvedType { + SolvedType::Alias( + Symbol::NUM_NATURAL, + vec![], + Box::new(natural_alias_content()), + ) +} + +#[inline(always)] +fn natural_alias_content() -> SolvedType { + single_private_tag(Symbol::NUM_AT_NATURAL, vec![]) +} + #[inline(always)] pub fn signed128_type() -> SolvedType { SolvedType::Alias( diff --git a/compiler/uniq/src/builtins.rs b/compiler/uniq/src/builtins.rs index f15ec9f7ec..d0d396d1c7 100644 --- a/compiler/uniq/src/builtins.rs +++ b/compiler/uniq/src/builtins.rs @@ -14,7 +14,7 @@ use roc_types::types::Type::{self, *}; pub fn int_literal(num_var: Variable, expected: Expected, region: Region) -> Constraint { let num_type = Variable(num_var); let reason = Reason::IntLiteral; - let int_type = builtin_type(Symbol::NUM_I64, vec![]); + let int_type = builtin_type(Symbol::NUM_INT, vec![]); let expected_literal = ForReason(reason, int_type, region); exists( @@ -30,7 +30,7 @@ pub fn int_literal(num_var: Variable, expected: Expected, region: Region) pub fn float_literal(num_var: Variable, expected: Expected, region: Region) -> Constraint { let num_type = Variable(num_var); let reason = Reason::FloatLiteral; - let float_type = builtin_type(Symbol::NUM_F64, vec![]); + let float_type = builtin_type(Symbol::NUM_FLOAT, vec![]); let expected_literal = ForReason(reason, float_type, region); exists( diff --git a/compiler/uniq/src/sharing.rs b/compiler/uniq/src/sharing.rs index 736e45e6d2..f991617599 100644 --- a/compiler/uniq/src/sharing.rs +++ b/compiler/uniq/src/sharing.rs @@ -752,9 +752,9 @@ fn annotate_usage_pattern(pattern: &Pattern, usage: &mut VarUsage) { match pattern { Identifier(_) - | IntLiteral(_) + | IntLiteral(_, _) | NumLiteral(_, _) - | FloatLiteral(_) + | FloatLiteral(_, _) | StrLiteral(_) | Underscore | Shadowed(_, _) @@ -785,8 +785,8 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) { match expr { RuntimeError(_) | Num(_, _) - | Int(_, _) - | Float(_, _) + | Int(_, _, _) + | Float(_, _, _) | Str { .. } | EmptyRecord | Accessor { .. } diff --git a/docs/src/main.rs b/docs/src/main.rs index 8e644c9780..1089b54686 100644 --- a/docs/src/main.rs +++ b/docs/src/main.rs @@ -132,6 +132,7 @@ fn files_to_documentations( std_lib.clone(), src_dir, MutMap::default(), + 8, // TODO: Is it okay to hardcode ptr_bytes here? I think it should be fine since we'er only type checking (also, 8 => 32bit system) ) .expect("TODO gracefully handle load failing"); files_docs.extend(loaded.documentation.drain().map(|x| x.1)); diff --git a/examples/closure/Closure.roc b/examples/closure/Closure.roc index 329cc8ff25..03131b7dd1 100644 --- a/examples/closure/Closure.roc +++ b/examples/closure/Closure.roc @@ -1,6 +1,6 @@ app "closure" provides [ makeClosure ] to "./platform/" -makeClosure : ({} -> I64) as MyClosure +makeClosure : ({} -> Int *) as MyClosure makeClosure = x = 42 y = 42 diff --git a/examples/effect/ConsList.roc b/examples/effect/ConsList.roc index a86281ca5f..2536d82160 100644 --- a/examples/effect/ConsList.roc +++ b/examples/effect/ConsList.roc @@ -5,7 +5,7 @@ ConsList a : [ Cons a (ConsList a), Nil ] empty : ConsList a empty = Nil -len : ConsList a -> I64 +len : ConsList a -> Int * len = \list -> when list is Cons _ rest -> 1 + len rest diff --git a/examples/effect/RBTree.roc b/examples/effect/RBTree.roc index 71c35f8f9b..2c487ca28e 100644 --- a/examples/effect/RBTree.roc +++ b/examples/effect/RBTree.roc @@ -21,11 +21,11 @@ singleton = \key, value -> Node Black key value Empty Empty # {-| Determine the number of key-value pairs in the dictionary. -} -size : Dict k v -> I64 +size : Dict k v -> Int * size = \dict -> sizeHelp 0 dict -sizeHelp : I64, Dict k v -> I64 +sizeHelp : Int *, Dict k v -> Int * sizeHelp = \n, dict -> when dict is Empty -> diff --git a/examples/quicksort/Quicksort.roc b/examples/quicksort/Quicksort.roc index 59bb6d47f0..c75bf0e554 100644 --- a/examples/quicksort/Quicksort.roc +++ b/examples/quicksort/Quicksort.roc @@ -5,7 +5,7 @@ app "quicksort" quicksort = \originalList -> - quicksortHelp : List (Num a), I64, I64 -> List (Num a) + quicksortHelp : List (Num a), Int *, Int * -> List (Num a) quicksortHelp = \list, low, high -> if low < high then when partition low high list is @@ -17,7 +17,7 @@ quicksort = \originalList -> list - partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] + partition : Int a, Int a, List (Num b) -> [ Pair (Int a) (List (Num b)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -28,7 +28,7 @@ quicksort = \originalList -> Err _ -> Pair (low - 1) initialList - partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ] + partitionHelp : Int a, Int b, List (Num c), Int b, (Num c) -> [ Pair (Int a) (List (Num c)) ] partitionHelp = \i, j, list, high, pivot -> if j < high then when List.get list j is @@ -44,7 +44,7 @@ quicksort = \originalList -> Pair i list - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> diff --git a/examples/shared-quicksort/Quicksort.roc b/examples/shared-quicksort/Quicksort.roc index 63e395bcd6..61e0d7acfa 100644 --- a/examples/shared-quicksort/Quicksort.roc +++ b/examples/shared-quicksort/Quicksort.roc @@ -1,12 +1,12 @@ app "quicksort" packages { base: "./platform" } provides [ quicksort ] to base -quicksort : List I64 -> List I64 +quicksort : List Int * -> List Int * quicksort = \originalList -> helper originalList -helper : List I64 -> List I64 +helper : List Int * -> List Int * helper = \originalList -> - quicksortHelp : List (Num a), I64, I64 -> List (Num a) + quicksortHelp : List (Num a), Int *, Int * -> List (Num a) quicksortHelp = \list, low, high -> if low < high then when partition low high list is @@ -18,7 +18,7 @@ helper = \originalList -> list - swap : I64, I64, List a -> List a + swap : Int *, Int *, List a -> List a swap = \i, j, list -> when Pair (List.get list i) (List.get list j) is Pair (Ok atI) (Ok atJ) -> @@ -29,7 +29,7 @@ helper = \originalList -> _ -> [] - partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ] + partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ] partition = \low, high, initialList -> when List.get initialList high is Ok pivot -> @@ -41,7 +41,7 @@ helper = \originalList -> Pair (low - 1) initialList - partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ] + partitionHelp : Int *, Int *, List (Num a), Int *, (Num a) -> [ Pair (Int *) (List (Num a)) ] partitionHelp = \i, j, list, high, pivot -> if j < high then when List.get list j is