diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index db3edf7173..61ec3b2c44 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -498,7 +498,7 @@ pub fn types() -> MutMap { ), ); - // push : List a -> a -> List a + // push : List elem -> elem -> List elem add_type( Symbol::LIST_PUSH, SolvedType::Func( @@ -513,6 +513,15 @@ pub fn types() -> MutMap { SolvedType::Func(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))), ); + // repeat : Int, elem -> List elem + add_type( + Symbol::LIST_REPEAT, + SolvedType::Func( + vec![int_type(), flex(TVAR1)], + Box::new(list_type(flex(TVAR1))), + ), + ); + // len : List * -> Int add_type( Symbol::LIST_LEN, diff --git a/compiler/builtins/src/unique.rs b/compiler/builtins/src/unique.rs index 546747f06a..826a8d56f8 100644 --- a/compiler/builtins/src/unique.rs +++ b/compiler/builtins/src/unique.rs @@ -7,49 +7,49 @@ use roc_types::solved_types::{BuiltinAlias, SolvedAtom, SolvedType}; use roc_types::subs::VarId; use std::collections::HashMap; +/// Example: +/// +/// let_tvars! { a, b, c } +/// +/// This is equivalent to: +/// +/// let a = VarId::from_u32(1); +/// let b = VarId::from_u32(2); +/// let c = VarId::from_u32(3); +/// +/// The idea is that this is less error-prone than assigning hardcoded IDs by hand. +macro_rules! let_tvars { + ($($name:ident,)+) => { let_tvars!($($name),+) }; + ($($name:ident),*) => { + let mut _current_tvar = 0; + + $( + _current_tvar += 1; + + let $name = VarId::from_u32(_current_tvar); + )* + }; +} + /// Keep this up to date by hand! /// const NUM_BUILTIN_IMPORTS: usize = 7; -/// These can be shared between definitions, they will get instantiated when converted to Type -const TVAR1: VarId = VarId::from_u32(1); -const TVAR2: VarId = VarId::from_u32(2); -const TVAR3: VarId = VarId::from_u32(3); - /// These can be shared between definitions, they will get instantiated when converted to Type const FUVAR: VarId = VarId::from_u32(1000); -const UVAR1: VarId = VarId::from_u32(1001); -const UVAR2: VarId = VarId::from_u32(1002); -const UVAR3: VarId = VarId::from_u32(1003); -const UVAR4: VarId = VarId::from_u32(1004); -const UVAR5: VarId = VarId::from_u32(1005); -const UVAR6: VarId = VarId::from_u32(1006); -pub struct IDStore(u32); - -impl IDStore { - fn new() -> Self { - IDStore(2000) - } - - fn fresh(&mut self) -> VarId { - let result = VarId::from_u32(self.0); - - self.0 += 1; - - result - } -} - -fn shared() -> SolvedType { - SolvedType::Boolean(SolvedAtom::Zero, vec![]) +fn shared(base: SolvedType) -> SolvedType { + SolvedType::Apply( + Symbol::ATTR_ATTR, + vec![SolvedType::Boolean(SolvedAtom::Zero, vec![]), base], + ) } fn boolean(b: VarId) -> SolvedType { SolvedType::Boolean(SolvedAtom::Variable(b), vec![]) } -fn disjunction(free: VarId, rest: Vec) -> SolvedType { +fn container(free: VarId, rest: Vec) -> SolvedType { let solved_rest = rest.into_iter().map(SolvedAtom::Variable).collect(); SolvedType::Boolean(SolvedAtom::Variable(free), solved_rest) @@ -118,7 +118,6 @@ pub fn uniq_stdlib() -> StdLib { } pub fn aliases() -> MutMap { - // let mut aliases = builtins::aliases(); let mut aliases = MutMap::default(); let mut add_alias = |symbol, alias| { @@ -141,13 +140,16 @@ pub fn aliases() -> MutMap { ) }; + // NOTE: `a` must be the first variable bound here! + let_tvars! { a, err, star }; + // Num : Num Integer add_alias( Symbol::NUM_NUM, BuiltinAlias { region: Region::zero(), vars: vec![Located::at(Region::zero(), "a".into())], - typ: single_private_tag(Symbol::NUM_AT_NUM, vec![flex(TVAR1)]), + typ: single_private_tag(Symbol::NUM_AT_NUM, vec![flex(a)]), }, ); @@ -180,7 +182,7 @@ pub fn aliases() -> MutMap { typ: SolvedType::Apply( Symbol::NUM_NUM, vec![lift( - UVAR1, + star, SolvedType::Apply(Symbol::INT_INTEGER, Vec::new()), )], ), @@ -196,7 +198,7 @@ pub fn aliases() -> MutMap { typ: SolvedType::Apply( Symbol::NUM_NUM, vec![lift( - UVAR1, + star, SolvedType::Apply(Symbol::FLOAT_FLOATINGPOINT, Vec::new()), )], ), @@ -230,8 +232,8 @@ pub fn aliases() -> MutMap { ], typ: SolvedType::TagUnion( vec![ - (TagName::Global("Ok".into()), vec![flex(TVAR1)]), - (TagName::Global("Err".into()), vec![flex(TVAR2)]), + (TagName::Global("Ok".into()), vec![flex(a)]), + (TagName::Global("Err".into()), vec![flex(err)]), ], Box::new(SolvedType::EmptyTagUnion), ), @@ -266,322 +268,323 @@ pub fn types() -> MutMap { // Num module - // add or (+) : Attr u1 (Num a), Attr u2 (Num a) -> Attr u3 (Num a) - add_type( - Symbol::NUM_ADD, - unique_function( - vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)], - num_type(UVAR3, TVAR1), - ), - ); + // add or (+) : Attr u (Num (Attr u num)) + // , Attr v (Num (Attr v num)) + // -> Attr w (Num (Attr w num)) + add_type(Symbol::NUM_ADD, { + let_tvars! { u, v, w, num }; + unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num)) + }); // sub or (-) : Num a, Num a -> Num a - add_type( - Symbol::NUM_SUB, - unique_function( - vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)], - num_type(UVAR3, TVAR1), - ), - ); + add_type(Symbol::NUM_SUB, { + let_tvars! { u, v, w, num }; + unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num)) + }); // mul or (*) : Num a, Num a -> Num a - add_type( - Symbol::NUM_MUL, - unique_function( - vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)], - num_type(UVAR3, TVAR1), - ), - ); + add_type(Symbol::NUM_MUL, { + let_tvars! { u, v, w, num }; + unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num)) + }); // abs : Num a -> Num a - add_type( - Symbol::NUM_ABS, - unique_function(vec![num_type(UVAR1, TVAR1)], num_type(UVAR2, TVAR1)), - ); + add_type(Symbol::NUM_ABS, { + let_tvars! { u, v, num }; + unique_function(vec![num_type(u, num)], num_type(v, num)) + }); // neg : Num a -> Num a - add_type( - Symbol::NUM_NEG, - unique_function(vec![num_type(UVAR1, TVAR1)], num_type(UVAR2, TVAR1)), - ); + add_type(Symbol::NUM_NEG, { + let_tvars! { u, v, num }; + unique_function(vec![num_type(u, num)], num_type(v, num)) + }); + + let mut add_num_comparison = |symbol| { + add_type(symbol, { + let_tvars! { u, v, w, num }; + unique_function(vec![num_type(u, num), num_type(v, num)], bool_type(w)) + }); + }; // isLt or (<) : Num a, Num a -> Bool - add_type( - Symbol::NUM_LT, - unique_function( - vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)], - bool_type(UVAR3), - ), - ); + add_num_comparison(Symbol::NUM_LT); // isLte or (<=) : Num a, Num a -> Bool - add_type( - Symbol::NUM_LTE, - unique_function( - vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)], - bool_type(UVAR3), - ), - ); + add_num_comparison(Symbol::NUM_LTE); // isGt or (>) : Num a, Num a -> Bool - add_type( - Symbol::NUM_GT, - unique_function( - vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)], - bool_type(UVAR3), - ), - ); + add_num_comparison(Symbol::NUM_GT); // isGte or (>=) : Num a, Num a -> Bool - add_type( - Symbol::NUM_GTE, - unique_function( - vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)], - bool_type(UVAR3), - ), - ); + add_num_comparison(Symbol::NUM_GTE); // toFloat : Num a -> Float - add_type( - Symbol::NUM_TO_FLOAT, - unique_function(vec![num_type(UVAR1, TVAR1)], float_type(UVAR2)), - ); + add_type(Symbol::NUM_TO_FLOAT, { + let_tvars! { star1, star2, a }; + unique_function(vec![num_type(star1, a)], float_type(star2)) + }); // Int module // isLt or (<) : Num a, Num a -> Bool - add_type( - Symbol::INT_LT, - unique_function(vec![int_type(UVAR1), int_type(UVAR2)], bool_type(UVAR3)), - ); + add_type(Symbol::INT_LT, { + let_tvars! { u, v, w }; + unique_function(vec![int_type(u), int_type(v)], bool_type(w)) + }); // equals or (==) : Int, Int -> Bool - add_type( - Symbol::INT_EQ_I64, - unique_function(vec![int_type(UVAR1), int_type(UVAR2)], bool_type(UVAR3)), - ); + add_type(Symbol::INT_EQ_I64, { + let_tvars! { u, v, w }; + unique_function(vec![int_type(u), int_type(v)], bool_type(w)) + }); // not equals or (!=) : Int, Int -> Bool - add_type( - Symbol::INT_NEQ_I64, - unique_function(vec![int_type(UVAR1), int_type(UVAR2)], bool_type(UVAR3)), - ); + add_type(Symbol::INT_NEQ_I64, { + let_tvars! { u, v, w }; + unique_function(vec![int_type(u), int_type(v)], bool_type(w)) + }); // abs : Int -> Int - add_type( - Symbol::INT_ABS, - unique_function(vec![int_type(UVAR1)], int_type(UVAR2)), - ); + add_type(Symbol::INT_ABS, { + let_tvars! { u, v }; + unique_function(vec![int_type(u)], int_type(v)) + }); - // rem : Int, Int -> Result Int [ DivByZero ]* - add_type( - Symbol::INT_REM, + // rem : Attr * Int, Attr * Int -> Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*)) + add_type(Symbol::INT_REM, { + let_tvars! { star1, star2, star3, star4, star5 }; unique_function( - vec![int_type(UVAR1), int_type(UVAR2)], - result_type(UVAR3, int_type(UVAR4), lift(UVAR5, div_by_zero())), - ), - ); + vec![int_type(star1), int_type(star2)], + result_type(star3, int_type(star4), lift(star5, div_by_zero())), + ) + }); - add_type( - Symbol::INT_REM_UNSAFE, - unique_function(vec![int_type(UVAR1), int_type(UVAR2)], int_type(UVAR3)), - ); + add_type(Symbol::INT_REM_UNSAFE, { + let_tvars! { star1, star2, star3, }; + unique_function(vec![int_type(star1), int_type(star2)], int_type(star3)) + }); // highest : Int - add_type(Symbol::INT_HIGHEST, int_type(UVAR1)); + add_type(Symbol::INT_HIGHEST, { + let_tvars! { star }; + int_type(star) + }); // lowest : Int - add_type(Symbol::INT_LOWEST, int_type(UVAR1)); + add_type(Symbol::INT_LOWEST, { + let_tvars! { star }; + int_type(star) + }); // div or (//) : Int, Int -> Result Int [ DivByZero ]* - add_type( - Symbol::INT_DIV, + add_type(Symbol::INT_DIV, { + let_tvars! { star1, star2, star3, star4, star5 }; unique_function( - vec![int_type(UVAR1), int_type(UVAR2)], - result_type(UVAR3, int_type(UVAR4), lift(UVAR5, div_by_zero())), - ), - ); + vec![int_type(star1), int_type(star2)], + result_type(star3, int_type(star4), lift(star5, div_by_zero())), + ) + }); - add_type( - Symbol::INT_DIV_UNSAFE, - unique_function(vec![int_type(UVAR1), int_type(UVAR2)], int_type(UVAR3)), - ); + add_type(Symbol::INT_DIV_UNSAFE, { + let_tvars! { star1, star2, star3, }; + unique_function(vec![int_type(star1), int_type(star2)], int_type(star3)) + }); // mod : Int, Int -> Int - add_type( - Symbol::INT_MOD, - unique_function(vec![int_type(UVAR1), int_type(UVAR2)], int_type(UVAR3)), - ); + add_type(Symbol::INT_MOD, { + let_tvars! { star1, star2, star3, }; + unique_function(vec![int_type(star1), int_type(star2)], int_type(star3)) + }); // Float module // isGt or (>) : Num a, Num a -> Bool - add_type( - Symbol::FLOAT_GT, - unique_function(vec![float_type(UVAR1), float_type(UVAR2)], bool_type(UVAR3)), - ); + add_type(Symbol::FLOAT_GT, { + let_tvars! { star1, star2, star3} + unique_function(vec![float_type(star1), float_type(star2)], bool_type(star3)) + }); // eq or (==) : Num a, Num a -> Bool - add_type( - Symbol::FLOAT_EQ, - unique_function(vec![float_type(UVAR1), float_type(UVAR2)], bool_type(UVAR3)), - ); + add_type(Symbol::FLOAT_EQ, { + let_tvars! { star1, star2, star3} + unique_function(vec![float_type(star1), float_type(star2)], bool_type(star3)) + }); // div : Float, Float -> Float - add_type( - Symbol::FLOAT_DIV, + add_type(Symbol::FLOAT_DIV, { + let_tvars! { star1, star2, star3}; unique_function( - vec![float_type(UVAR1), float_type(UVAR2)], - float_type(UVAR3), - ), - ); + vec![float_type(star1), float_type(star2)], + float_type(star3), + ) + }); // mod : Float, Float -> Float - add_type( - Symbol::FLOAT_MOD, + add_type(Symbol::FLOAT_MOD, { + let_tvars! { star1, star2, star3}; unique_function( - vec![float_type(UVAR1), float_type(UVAR2)], - float_type(UVAR3), - ), - ); - - // sqrt : Float -> Float - add_type( - Symbol::FLOAT_SQRT, - unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), - ); + vec![float_type(star1), float_type(star2)], + float_type(star3), + ) + }); // round : Float -> Int - add_type( - Symbol::FLOAT_ROUND, - unique_function(vec![float_type(UVAR1)], int_type(UVAR2)), - ); + add_type(Symbol::FLOAT_ROUND, { + let_tvars! { star1, star2 }; + unique_function(vec![float_type(star1)], int_type(star2)) + }); + + // sqrt : Float -> Float + add_type(Symbol::FLOAT_SQRT, { + let_tvars! { star1, star2 }; + unique_function(vec![float_type(star1)], float_type(star2)) + }); // abs : Float -> Float - add_type( - Symbol::FLOAT_ABS, - unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), - ); + add_type(Symbol::FLOAT_ABS, { + let_tvars! { star1, star2 }; + unique_function(vec![float_type(star1)], float_type(star2)) + }); // sin : Float -> Float - add_type( - Symbol::FLOAT_SIN, - unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), - ); + add_type(Symbol::FLOAT_SIN, { + let_tvars! { star1, star2 }; + unique_function(vec![float_type(star1)], float_type(star2)) + }); // cos : Float -> Float - add_type( - Symbol::FLOAT_COS, - unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), - ); + add_type(Symbol::FLOAT_COS, { + let_tvars! { star1, star2 }; + unique_function(vec![float_type(star1)], float_type(star2)) + }); // tan : Float -> Float - add_type( - Symbol::FLOAT_TAN, - unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), - ); + add_type(Symbol::FLOAT_TAN, { + let_tvars! { star1, star2 }; + unique_function(vec![float_type(star1)], float_type(star2)) + }); // highest : Float - add_type(Symbol::FLOAT_HIGHEST, float_type(UVAR1)); + add_type(Symbol::FLOAT_HIGHEST, { + let_tvars! { star }; + float_type(star) + }); // lowest : Float - add_type(Symbol::FLOAT_LOWEST, float_type(UVAR1)); + add_type(Symbol::FLOAT_LOWEST, { + let_tvars! { star }; + float_type(star) + }); // Bool module - // isEq or (==) : a, a -> Attr u Bool - add_type( - Symbol::BOOL_EQ, - unique_function(vec![flex(TVAR1), flex(TVAR1)], bool_type(UVAR3)), - ); + // isEq or (==) : Attr * a, Attr * a -> Attr * Bool + add_type(Symbol::BOOL_EQ, { + let_tvars! { star1, star2, star3, a }; + unique_function( + vec![attr_type(star1, a), attr_type(star2, a)], + bool_type(star3), + ) + }); - // isNeq or (!=) : a, a -> Attr u Bool - add_type( - Symbol::BOOL_NEQ, - unique_function(vec![flex(TVAR1), flex(TVAR1)], bool_type(UVAR3)), - ); + // isNeq or (!=) : Attr * a, Attr * a -> Attr * Bool + add_type(Symbol::BOOL_NEQ, { + let_tvars! { star1, star2, star3, a }; + unique_function( + vec![attr_type(star1, a), attr_type(star2, a)], + bool_type(star3), + ) + }); // and or (&&) : Attr u1 Bool, Attr u2 Bool -> Attr u3 Bool - add_type( - Symbol::BOOL_AND, - unique_function(vec![bool_type(UVAR1), bool_type(UVAR2)], bool_type(UVAR3)), - ); + add_type(Symbol::BOOL_AND, { + let_tvars! { star1, star2, star3}; + unique_function(vec![bool_type(star1), bool_type(star2)], bool_type(star3)) + }); // or or (||) : Attr u1 Bool, Attr u2 Bool -> Attr u3 Bool - add_type( - Symbol::BOOL_OR, - unique_function(vec![bool_type(UVAR1), bool_type(UVAR2)], bool_type(UVAR3)), - ); + add_type(Symbol::BOOL_OR, { + let_tvars! { star1, star2, star3}; + unique_function(vec![bool_type(star1), bool_type(star2)], bool_type(star3)) + }); // xor : Attr u1 Bool, Attr u2 Bool -> Attr u3 Bool - add_type( - Symbol::BOOL_XOR, - unique_function(vec![bool_type(UVAR1), bool_type(UVAR2)], bool_type(UVAR3)), - ); + add_type(Symbol::BOOL_XOR, { + let_tvars! { star1, star2, star3}; + unique_function(vec![bool_type(star1), bool_type(star2)], bool_type(star3)) + }); // not : Attr u1 Bool -> Attr u2 Bool - add_type( - Symbol::BOOL_NOT, - unique_function(vec![bool_type(UVAR1)], bool_type(UVAR2)), - ); + add_type(Symbol::BOOL_NOT, { + let_tvars! { star1, star2 }; + unique_function(vec![bool_type(star1)], bool_type(star2)) + }); // List module - // isEmpty : Attr u (List *) -> Attr v Bool - add_type( - Symbol::LIST_IS_EMPTY, - unique_function(vec![list_type(UVAR1, TVAR1)], bool_type(UVAR2)), - ); + // isEmpty : Attr * (List *) -> Attr * Bool + add_type(Symbol::LIST_IS_EMPTY, { + let_tvars! { star1, a, star2 }; + unique_function(vec![list_type(star1, a)], bool_type(star2)) + }); - // len : Attr u (List *) -> Attr v Int - add_type( - Symbol::LIST_LEN, - unique_function(vec![list_type(UVAR1, TVAR1)], int_type(UVAR2)), - ); + // 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)) + }); - // get : List a, Int -> Result a [ OutOfBounds ]* + // get : Attr (* | u) (List (Attr u a)) + // , Attr * Int + // -> Attr * (Result (Attr u a) (Attr * [ OutOfBounds ]*)) let index_out_of_bounds = SolvedType::TagUnion( vec![(TagName::Global("OutOfBounds".into()), vec![])], Box::new(SolvedType::Wildcard), ); - add_type( - Symbol::LIST_GET, - unique_function( - vec![list_type(UVAR1, TVAR1), int_type(UVAR2)], - result_type(UVAR3, flex(TVAR1), lift(UVAR4, index_out_of_bounds)), - ), - ); - - add_type( - Symbol::LIST_GET_UNSAFE, - unique_function(vec![list_type(UVAR1, TVAR1), int_type(UVAR2)], flex(TVAR1)), - ); - - // set : Attr (w | u | v) (List (Attr u a)) - // , Attr * Int - // , Attr (u | v) a - // -> List a - add_type(Symbol::LIST_SET, { - let u = UVAR1; - let v = UVAR2; - let w = UVAR3; - let star1 = UVAR4; - let star2 = UVAR5; - - let a = TVAR1; + add_type(Symbol::LIST_GET, { + let_tvars! { a, u, star1, star2, star3, star4 }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(w, vec![u, v]), + container(star1, vec![u]), + SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]), + ], + ), + int_type(star2), + ], + result_type(star3, attr_type(u, a), lift(star4, index_out_of_bounds)), + ) + }); + + // is LIST_GET_UNSAFE still used? + add_type(Symbol::LIST_GET_UNSAFE, { + let_tvars! { star1, star2, a }; + unique_function(vec![list_type(star1, a), int_type(star2)], flex(a)) + }); + + // set : Attr (w | u | v) (List (Attr u a)) + // , Attr * Int + // , Attr (u | v) a + // -> List a + add_type(Symbol::LIST_SET, { + let_tvars! { u, v, w, star1, star2, a }; + + unique_function( + vec![ + SolvedType::Apply( + Symbol::ATTR_ATTR, + vec![ + container(w, vec![u, v]), SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]), ], ), int_type(star1), - SolvedType::Apply(Symbol::ATTR_ATTR, vec![disjunction(u, vec![v]), flex(a)]), + SolvedType::Apply(Symbol::ATTR_ATTR, vec![container(u, vec![v]), flex(a)]), ], SolvedType::Apply( Symbol::ATTR_ATTR, @@ -593,112 +596,143 @@ pub fn types() -> MutMap { ) }); + // single : a -> Attr * (List a) add_type(Symbol::LIST_SINGLE, { - let u = UVAR1; - let v = UVAR2; - let star = UVAR4; - - let a = TVAR1; + let_tvars! { a, star }; unique_function( - vec![SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![disjunction(u, vec![v]), flex(a)], - )], + vec![flex(a)], SolvedType::Apply( Symbol::ATTR_ATTR, vec![ boolean(star), - SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]), + SolvedType::Apply(Symbol::LIST_LIST, vec![flex(a)]), ], ), ) }); - // push : Attr (w | u | v) (List (Attr u a)) - // , Attr (u | v) a - // -> Attr * (List (Attr u a)) - add_type(Symbol::LIST_PUSH, { - let u = UVAR1; - let v = UVAR2; - let w = UVAR3; - let star = UVAR4; + // To repeat an item, it must be shared! + // + // repeat : Attr * Int + // , Attr Shared a + // -> Attr * (List (Attr Shared a)) + add_type(Symbol::LIST_REPEAT, { + let_tvars! { a, star1, star2 }; - let a = TVAR1; + unique_function( + vec![int_type(star1), shared(flex(a))], + SolvedType::Apply( + Symbol::ATTR_ATTR, + vec![ + boolean(star2), + SolvedType::Apply(Symbol::LIST_LIST, vec![shared(flex(a))]), + ], + ), + ) + }); + + // push : Attr * (List a) + // , a + // -> Attr * (List a) + // + // NOTE: we demand the new item to have the same uniqueness as the other list items. + // It could be allowed to add unique items to shared lists, but that requires special code gen + add_type(Symbol::LIST_PUSH, { + let_tvars! { a, star1, star2 }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(w, vec![u, v]), + flex(star1), + SolvedType::Apply(Symbol::LIST_LIST, vec![flex(a)]), + ], + ), + flex(a), + ], + SolvedType::Apply( + Symbol::ATTR_ATTR, + vec![ + boolean(star2), + SolvedType::Apply(Symbol::LIST_LIST, vec![flex(a)]), + ], + ), + ) + }); + + // List.map does not need to check the container rule on the input list. + // There is no way in which this signature can cause unique values to be duplicated + // + // foo : Attr Shared (List (Attr u a)) + // + // List.map : Attr * (List (Attr u a)) -> (Attr u a -> b) -> Attr * (List b) + // List.unsafeGet : Attr (* | u) (List (Attr u a)) -> Attr u a + // + // -- the elements still have uniqueness `u`, and will be made shared whenever accessing an element in `foo` + // bar1 : Attr * (List (Attr u a)) + // bar1 = List.map foo (\x -> x) + // + // -- no reference to `foo`'s elements can escape + // bar2 : Attr * (List (Attr * Int)) + // bar2 = List.map foo (\_ -> 32) + + // map : Attr * (List a) + // , Attr Shared (a -> b) + // -> Attr * (List b) + add_type(Symbol::LIST_MAP, { + let_tvars! { a, b, star1, star2 }; + + unique_function( + vec![ + list_type(star1, a), + shared(SolvedType::Func(vec![flex(a)], Box::new(flex(b)))), + ], + list_type(star2, b), + ) + }); + + // foldr : Attr (* | u) (List (Attr u a)) + // , Attr Shared (Attr u a -> b -> b) + // , b + // -> b + add_type(Symbol::LIST_FOLDR, { + let_tvars! { u, a, b, star1 }; + + unique_function( + vec![ + SolvedType::Apply( + Symbol::ATTR_ATTR, + vec![ + container(star1, vec![u]), SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]), ], ), - SolvedType::Apply(Symbol::ATTR_ATTR, vec![disjunction(u, vec![v]), flex(a)]), + shared(SolvedType::Func( + vec![attr_type(u, a), flex(b)], + Box::new(flex(b)), + )), + flex(b), ], - SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![ - boolean(star), - SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]), - ], - ), + flex(b), ) }); - // map : List a, (a -> b) -> List b - add_type( - Symbol::LIST_MAP, - unique_function( - vec![ - list_type(UVAR1, TVAR1), - SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![ - shared(), - SolvedType::Func(vec![flex(TVAR1)], Box::new(flex(TVAR2))), - ], - ), - ], - list_type(UVAR2, TVAR2), - ), - ); - - // foldr : List a, (a -> b -> b), b -> b - add_type( - Symbol::LIST_FOLDR, - unique_function( - vec![ - list_type(UVAR1, TVAR1), - SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![ - shared(), - SolvedType::Func(vec![flex(TVAR1), flex(TVAR2)], Box::new(flex(TVAR2))), - ], - ), - flex(TVAR2), - ], - flex(TVAR2), - ), - ); - // Map module - // empty : Map k v - add_type(Symbol::MAP_EMPTY, map_type(UVAR1, TVAR1, TVAR2)); + // empty : Attr * (Map k v) + add_type(Symbol::MAP_EMPTY, { + let_tvars! { star, k , v }; + map_type(star, k, v) + }); - // singleton : k, v -> Map k v - add_type( - Symbol::MAP_SINGLETON, - unique_function( - vec![flex(TVAR1), flex(TVAR2)], - map_type(UVAR1, TVAR1, TVAR2), - ), - ); + // singleton : k, v -> Attr * (Map k v) + add_type(Symbol::MAP_SINGLETON, { + let_tvars! { star, k , v }; + unique_function(vec![flex(k), flex(v)], map_type(star, k, v)) + }); - // get : Attr (u | v | *) (Map (Attr u key) (Attr v val), (Attr * key) -> Attr * (Result (Attr v val) [ KeyNotFound ]*) let key_not_found = SolvedType::Apply( Symbol::ATTR_ATTR, vec![ @@ -710,85 +744,63 @@ pub fn types() -> MutMap { ], ); + // get : Attr (* | u) (Map (Attr * key) (Attr u val)) + // , Attr * key + // -> Attr * (Result (Attr u val) [ KeyNotFound ]*) add_type(Symbol::MAP_GET, { - let mut store = IDStore::new(); - - let u = store.fresh(); - let v = store.fresh(); - let key = store.fresh(); - let val = store.fresh(); - let star1 = store.fresh(); - let star2 = store.fresh(); - let star3 = store.fresh(); + let_tvars! { u, key, val, star1, star2, star3, star4 }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(star1, vec![u, v]), + container(star1, vec![u]), SolvedType::Apply( Symbol::MAP_MAP, - vec![attr_type(u, key), attr_type(v, val)], + vec![attr_type(star2, key), attr_type(u, val)], ), ], ), - SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![disjunction(star2, vec![u]), flex(key)], - ), + SolvedType::Apply(Symbol::ATTR_ATTR, vec![flex(star3), flex(key)]), ], SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - flex(star3), + flex(star4), SolvedType::Apply( Symbol::RESULT_RESULT, - vec![attr_type(v, val), key_not_found], + vec![attr_type(u, val), key_not_found], ), ], ), ) }); - // insert : Attr (u | v | *) (Map (Attr u key) (Attr v val)), Attr (u | *) key, Attr (v | *) val -> Attr * (Map (Attr u key) (Attr v val)) + // insert : Attr * (Map key value) + // , key + // , value + // , Attr * (Map key value) add_type(Symbol::MAP_INSERT, { - let mut store = IDStore::new(); - - let u = store.fresh(); - let v = store.fresh(); - let key = store.fresh(); - let val = store.fresh(); - let star1 = store.fresh(); - let star2 = store.fresh(); - let star3 = store.fresh(); + let_tvars! { star1, star2, key, value }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(star1, vec![u, v]), - SolvedType::Apply( - Symbol::MAP_MAP, - vec![attr_type(u, key), attr_type(v, val)], - ), + flex(star1), + SolvedType::Apply(Symbol::MAP_MAP, vec![flex(key), flex(value)]), ], ), - SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![disjunction(star2, vec![u]), flex(key)], - ), - SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![disjunction(star2, vec![v]), flex(val)], - ), + flex(key), + flex(value), ], SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - flex(star3), - SolvedType::Apply(Symbol::MAP_MAP, vec![attr_type(u, key), attr_type(v, val)]), + flex(star2), + SolvedType::Apply(Symbol::MAP_MAP, vec![flex(key), flex(value)]), ], ), ) @@ -797,139 +809,127 @@ pub fn types() -> MutMap { // Set module // empty : Set a - add_type(Symbol::SET_EMPTY, set_type(UVAR1, TVAR1)); + add_type(Symbol::SET_EMPTY, { + let_tvars! { star, a }; + set_type(star, a) + }); // singleton : a -> Set a - add_type( - Symbol::SET_SINGLETON, - unique_function(vec![flex(TVAR1)], set_type(UVAR1, TVAR1)), - ); + add_type(Symbol::SET_SINGLETON, { + let_tvars! { star, a }; + unique_function(vec![flex(a)], set_type(star, a)) + }); - // op : Attr (u | *) (Set (Attr u a)), Attr (u | *) (Set (Attr u a)) -> Attr * Set (Attr u a) + // union : Attr * (Set * a) + // , Attr * (Set * a) + // -> Attr * (Set * a) let set_combine = { - let mut store = IDStore::new(); - - let u = store.fresh(); - let a = store.fresh(); - let star1 = store.fresh(); - let star2 = store.fresh(); - let star3 = store.fresh(); + let_tvars! { star1, star2, star3, star4, star5, star6, a }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(star1, vec![u]), - SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]), + flex(star1), + SolvedType::Apply(Symbol::SET_SET, vec![attr_type(star2, a)]), ], ), SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(star2, vec![u]), - SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]), + flex(star3), + SolvedType::Apply(Symbol::SET_SET, vec![attr_type(star4, a)]), ], ), ], SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - flex(star3), - SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]), + flex(star5), + SolvedType::Apply(Symbol::SET_SET, vec![attr_type(star6, a)]), ], ), ) }; - // union : Set a, Set a -> Set a + // union : Attr * (Set * a) + // , Attr * (Set * a) + // -> Attr * (Set * a) add_type(Symbol::SET_UNION, set_combine.clone()); - // diff : Set a, Set a -> Set a + // diff : Attr * (Set * a) + // , Attr * (Set * a) + // -> Attr * (Set * a) add_type(Symbol::SET_DIFF, set_combine); - // foldl : Attr (u | *) (Set (Attr u a)), Attr Shared (Attr u a -> b -> b), b -> b + // foldl : Attr (* | u) (Set (Attr u a)) + // , Attr Shared (Attr u a -> b -> b) + // , b + // -> b add_type(Symbol::SET_FOLDL, { - let mut store = IDStore::new(); - - let u = store.fresh(); - let a = store.fresh(); - let b = store.fresh(); - let star1 = store.fresh(); + let_tvars! { star, u, a, b }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(star1, vec![u]), + container(star, vec![u]), SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]), ], ), - SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![ - shared(), - SolvedType::Func(vec![attr_type(u, a), flex(b)], Box::new(flex(b))), - ], - ), + shared(SolvedType::Func( + vec![attr_type(u, a), flex(b)], + Box::new(flex(b)), + )), flex(b), ], flex(b), ) }); - // insert : Attr (u | *) (Set (Attr u a)), Attr (u | *) a -> Attr * (Set (Attr u a)) + // insert : Attr * (Set a) + // , a + // , Attr * (Set a) add_type(Symbol::SET_INSERT, { - let mut store = IDStore::new(); - - let u = store.fresh(); - let a = store.fresh(); - let star1 = store.fresh(); - let star2 = store.fresh(); - let star3 = store.fresh(); + let_tvars! { star1, star2, a }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(star1, vec![u]), - SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]), + flex(star1), + SolvedType::Apply(Symbol::SET_SET, vec![flex(a)]), ], ), - SolvedType::Apply( - Symbol::ATTR_ATTR, - vec![disjunction(star2, vec![u]), flex(a)], - ), + flex(a), ], SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - flex(star3), - SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]), + flex(star2), + SolvedType::Apply(Symbol::SET_SET, vec![flex(a)]), ], ), ) }); // we can remove a key that is shared from a set of unique keys - // remove : Attr (u | *) (Set (Attr u a)), Attr * a -> Attr * (Set (Attr u a)) + // + // remove : Attr * (Set (Attr u a)) + // , Attr * a + // , Attr * (Set (Attr u a)) add_type(Symbol::SET_REMOVE, { - let mut store = IDStore::new(); - - let u = store.fresh(); - let a = store.fresh(); - let star1 = store.fresh(); - let star2 = store.fresh(); - let star3 = store.fresh(); + let_tvars! { u, a, star1, star2, star3 }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(star1, vec![u]), + flex(star1), SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]), ], ), @@ -947,37 +947,39 @@ pub fn types() -> MutMap { // Str module - // isEmpty : Attr u Str -> Attr v Bool + // isEmpty : Attr * Str -> Attr * Bool add_type(Symbol::STR_ISEMPTY, { - unique_function(vec![str_type(UVAR1)], bool_type(UVAR2)) + let_tvars! { star1, star2 }; + unique_function(vec![str_type(star1)], bool_type(star2)) + }); + + // append : Attr * Str, Attr * Str -> Attr * Str + add_type(Symbol::STR_APPEND, { + let_tvars! { star1, star2, star3 }; + unique_function(vec![str_type(star1), str_type(star2)], str_type(star3)) }); // Result module - // map : Attr (* | u | v) (Result (Attr u a) e), Attr * (Attr u a -> b) -> Attr * (Result b e) + // map : Attr * (Result (Attr a e)) + // , Attr * (a -> b) + // -> Attr * (Result b e) add_type(Symbol::RESULT_MAP, { - let u = UVAR1; - let star1 = UVAR4; - let star2 = UVAR5; - let star3 = UVAR6; - - let a = TVAR1; - let b = TVAR2; - let e = TVAR3; + let_tvars! { star1, star2, star3, a, b, e }; unique_function( vec![ SolvedType::Apply( Symbol::ATTR_ATTR, vec![ - disjunction(star1, vec![u]), - SolvedType::Apply(Symbol::RESULT_RESULT, vec![attr_type(u, a), flex(e)]), + flex(star1), + SolvedType::Apply(Symbol::RESULT_RESULT, vec![flex(a), flex(e)]), ], ), SolvedType::Apply( Symbol::ATTR_ATTR, vec![ flex(star2), - SolvedType::Func(vec![attr_type(u, a)], Box::new(flex(b))), + SolvedType::Func(vec![flex(a)], Box::new(flex(b))), ], ), ], @@ -1054,7 +1056,10 @@ fn str_type(u: VarId) -> SolvedType { fn num_type(u: VarId, a: VarId) -> SolvedType { SolvedType::Apply( Symbol::ATTR_ATTR, - vec![flex(u), SolvedType::Apply(Symbol::NUM_NUM, vec![flex(a)])], + vec![ + flex(u), + SolvedType::Apply(Symbol::NUM_NUM, vec![attr_type(u, a)]), + ], ) } diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 9b88df164a..d96cf718c4 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -433,11 +433,7 @@ pub fn build_expr<'a, 'ctx, 'env>( let builder = env.builder; if elems.is_empty() { - let struct_type = collection(ctx, env.ptr_bytes); - - // The pointer should be null (aka zero) and the length should be zero, - // so the whole struct should be a const_zero - BasicValueEnum::StructValue(struct_type.const_zero()) + empty_list(env) } else { let len_u64 = elems.len() as u64; let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64; @@ -1471,6 +1467,131 @@ fn call_with_args<'a, 'ctx, 'env>( "cast_collection", ) } + Symbol::LIST_REPEAT => { + // List.repeat : Int, elem -> List elem + debug_assert!(args.len() == 2); + + // Number of repeats + let list_len = args[0].0.into_int_value(); + + let builder = env.builder; + let ctx = env.context; + + let (elem, elem_layout) = args[1]; + let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes); + + // list_len > 0 + // We have to do a loop below, continuously adding the `elem` + // to the output list `List elem` until we have reached the + // number of repeats. This `comparison` is used to check + // if we need to do any looping; because if we dont, then we + // dont need to allocate memory for the index or the check + // if index != 0 + let comparison = builder.build_int_compare( + IntPredicate::UGT, + list_len, + ctx.i64_type().const_int(0, false), + "atleastzero", + ); + + let build_then = || { + // Allocate space for the new array that we'll copy into. + let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64; + + let list_ptr = { + let bytes_len = elem_bytes; + let len_type = env.ptr_int(); + let len = len_type.const_int(bytes_len, false); + + env.builder + .build_array_malloc(elem_type, len, "create_list_ptr") + .unwrap() + + // TODO check if malloc returned null; if so, runtime error for OOM! + }; + + let index_name = "#index"; + let start_alloca = builder.build_alloca(ctx.i64_type(), index_name); + + builder.build_store(start_alloca, list_len); + + let loop_bb = ctx.append_basic_block(parent, "loop"); + builder.build_unconditional_branch(loop_bb); + builder.position_at_end(loop_bb); + + // #index = #index - 1 + let curr_index = builder + .build_load(start_alloca, index_name) + .into_int_value(); + let next_index = builder.build_int_sub( + curr_index, + ctx.i64_type().const_int(1, false), + "nextindex", + ); + + builder.build_store(start_alloca, next_index); + + let elem_ptr = + unsafe { builder.build_in_bounds_gep(list_ptr, &[curr_index], "load_index") }; + + // Mutate the new array in-place to change the element. + builder.build_store(elem_ptr, elem); + + // #index != 0 + let end_cond = builder.build_int_compare( + IntPredicate::NE, + ctx.i64_type().const_int(0, false), + curr_index, + "loopcond", + ); + + let after_bb = ctx.append_basic_block(parent, "afterloop"); + + builder.build_conditional_branch(end_cond, loop_bb, after_bb); + builder.position_at_end(after_bb); + + let ptr_bytes = env.ptr_bytes; + let int_type = ptr_int(ctx, ptr_bytes); + let ptr_as_int = builder.build_ptr_to_int(list_ptr, int_type, "list_cast_ptr"); + let struct_type = collection(ctx, ptr_bytes); + + let mut struct_val; + + // Store the pointer + struct_val = builder + .build_insert_value( + struct_type.get_undef(), + ptr_as_int, + Builtin::WRAPPER_PTR, + "insert_ptr", + ) + .unwrap(); + + // Store the length + struct_val = builder + .build_insert_value(struct_val, list_len, Builtin::WRAPPER_LEN, "insert_len") + .unwrap(); + + builder.build_bitcast( + struct_val.into_struct_value(), + collection(ctx, ptr_bytes), + "cast_collection", + ) + }; + + let build_else = || empty_list(env); + + let struct_type = collection(ctx, env.ptr_bytes); + + build_basic_phi2( + env, + parent, + comparison, + build_then, + build_else, + BasicTypeEnum::StructType(struct_type), + ) + } Symbol::INT_DIV_UNSAFE => { debug_assert!(args.len() == 2); @@ -1637,6 +1758,16 @@ enum InPlace { Clone, } +fn empty_list<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> { + let ctx = env.context; + + let struct_type = collection(ctx, env.ptr_bytes); + + // The pointer should be null (aka zero) and the length should be zero, + // so the whole struct should be a const_zero + BasicValueEnum::StructValue(struct_type.const_zero()) +} + fn bounds_check_comparison<'ctx>( builder: &Builder<'ctx>, elem_index: IntValue<'ctx>, diff --git a/compiler/gen/tests/gen_builtins.rs b/compiler/gen/tests/gen_builtins.rs index c98286e74d..ce22a95410 100644 --- a/compiler/gen/tests/gen_builtins.rs +++ b/compiler/gen/tests/gen_builtins.rs @@ -507,6 +507,15 @@ mod gen_builtins { assert_evals_to!("List.single 5.6", &[5.6], &'static [f64]); } + #[test] + fn list_repeat() { + assert_evals_to!("List.repeat 5 1", &[1, 1, 1, 1, 1], &'static [i64]); + assert_evals_to!("List.repeat 4 2", &[2, 2, 2, 2], &'static [i64]); + + assert_evals_to!("List.repeat 0 []", &[], &'static [i64]); + assert_evals_to!("List.repeat 2 []", &[&[], &[]], &'static [&'static [i64]]); + } + #[test] fn empty_list_len() { with_larger_debug_stack(|| { diff --git a/compiler/load/tests/test_uniq_load.rs b/compiler/load/tests/test_uniq_load.rs index 3423568608..4697d29eb2 100644 --- a/compiler/load/tests/test_uniq_load.rs +++ b/compiler/load/tests/test_uniq_load.rs @@ -242,8 +242,8 @@ mod test_uniq_load { loaded_module, hashmap! { "swap" => "Attr * (Attr Shared Int, Attr Shared Int, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))", - "partition" => "Attr * (Attr Shared Int, Attr Shared Int, Attr b (List (Attr Shared (Num (Attr c a)))) -> Attr * [ Pair (Attr * Int) (Attr b (List (Attr Shared (Num (Attr c a))))) ])", - "quicksort" => "Attr Shared (Attr b (List (Attr Shared (Num (Attr c a)))), Attr Shared Int, Attr Shared Int -> Attr b (List (Attr Shared (Num (Attr c a)))))", + "partition" => "Attr * (Attr Shared Int, Attr Shared Int, Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr * [ Pair (Attr Shared Int) (Attr b (List (Attr Shared (Num (Attr Shared a))))) ])", + "quicksort" => "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared Int, Attr Shared Int -> Attr b (List (Attr Shared (Num (Attr Shared a)))))", }, ); }); diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index af54fd1ff8..e228169ff5 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -672,6 +672,7 @@ define_builtins! { 0 STR_STR: "Str" imported // the Str.Str type alias 1 STR_AT_STR: "@Str" // the Str.@Str private tag 2 STR_ISEMPTY: "isEmpty" + 3 STR_APPEND: "append" } 6 LIST: "List" => { 0 LIST_LIST: "List" imported // the List.List type alias @@ -692,6 +693,7 @@ define_builtins! { 15 LIST_FIRST: "first" 16 LIST_FIRST_ARG: "first#list" 17 LIST_SINGLE: "single" + 18 LIST_REPEAT: "repeat" } 7 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/solve/tests/test_uniq_solve.rs b/compiler/solve/tests/test_uniq_solve.rs index 4a81e568fb..cfe210200b 100644 --- a/compiler/solve/tests/test_uniq_solve.rs +++ b/compiler/solve/tests/test_uniq_solve.rs @@ -1475,7 +1475,7 @@ mod test_uniq_solve { quicksort "# ), - "Attr Shared (Attr b (List (Attr Shared (Num (Attr c a)))), Attr Shared Int, Attr Shared Int -> Attr b (List (Attr Shared (Num (Attr c a)))))" + "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared Int, Attr Shared Int -> Attr b (List (Attr Shared (Num (Attr Shared a)))))" ); }) } @@ -1885,12 +1885,12 @@ mod test_uniq_solve { 4 + 4 "# ), - "Attr * (Num (Attr * *))", + "Attr a (Num (Attr a *))", ); } #[test] - fn list_get() { + fn list_get_at() { infer_eq( indoc!( r#" @@ -2004,7 +2004,7 @@ mod test_uniq_solve { list "# ), - "Attr * (Attr a (List (Attr Shared (Num (Attr b c)))) -> Attr a (List (Attr Shared (Num (Attr b c)))))", + "Attr * (Attr a (List (Attr Shared (Num (Attr Shared b)))) -> Attr a (List (Attr Shared (Num (Attr Shared b)))))", ); } @@ -2024,19 +2024,6 @@ mod test_uniq_solve { ); } - #[test] - fn list_set() { - infer_eq(indoc!(r#"List.set"#), "Attr * (Attr (* | a | b) (List (Attr a c)), Attr * Int, Attr (a | b) c -> Attr * (List (Attr a c)))"); - } - - #[test] - fn list_map() { - infer_eq( - indoc!(r#"List.map"#), - "Attr * (Attr * (List a), Attr Shared (a -> b) -> Attr * (List b))", - ); - } - #[test] fn list_map_identity() { infer_eq( @@ -2045,14 +2032,6 @@ mod test_uniq_solve { ); } - #[test] - fn list_foldr() { - infer_eq( - indoc!(r#"List.foldr"#), - "Attr * (Attr * (List a), Attr Shared (a, b -> b), b -> b)", - ); - } - #[test] fn list_foldr_sum() { infer_eq( @@ -2063,15 +2042,72 @@ mod test_uniq_solve { sum "# ), - "Attr * (Attr * (List (Attr * (Num (Attr a b)))) -> Attr * (Num (Attr a b)))", + "Attr * (Attr (* | a) (List (Attr a (Num (Attr a b)))) -> Attr c (Num (Attr c b)))", + ); + } + + #[test] + fn num_add() { + infer_eq( + "Num.add", + "Attr * (Attr a (Num (Attr a b)), Attr c (Num (Attr c b)) -> Attr d (Num (Attr d b)))", + ); + } + + #[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 * Int)"); + } + + #[test] + fn list_get() { + infer_eq("List.get", "Attr * (Attr (* | a) (List (Attr a b)), Attr * Int -> Attr * (Result (Attr a b) (Attr * [ OutOfBounds ]*)))"); + } + + #[test] + fn list_set() { + infer_eq("List.set", "Attr * (Attr (* | a | b) (List (Attr a c)), Attr * Int, Attr (a | b) c -> Attr * (List (Attr a c)))"); + } + + #[test] + fn list_single() { + infer_eq("List.single", "Attr * (a -> Attr * (List a))"); + } + + #[test] + fn list_repeat() { + infer_eq( + "List.repeat", + "Attr * (Attr * Int, Attr Shared a -> Attr * (List (Attr Shared a)))", ); } #[test] fn list_push() { infer_eq( - indoc!(r#"List.push"#), - "Attr * (Attr (* | a | b) (List (Attr a c)), Attr (a | b) c -> Attr * (List (Attr a c)))" + "List.push", + "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_foldr() { + infer_eq( + "List.foldr", + "Attr * (Attr (* | a) (List (Attr a b)), Attr Shared (Attr a b, c -> c), c -> c)", ); } @@ -2085,7 +2121,7 @@ mod test_uniq_solve { singleton "# ), - "Attr * (Attr (* | a) b -> Attr * (List (Attr a b)))", + "Attr * (a -> Attr * (List a))", ); } @@ -2099,8 +2135,7 @@ mod test_uniq_solve { reverse "# ), - "Attr * (Attr * (List (Attr (a | b) c)) -> Attr (* | a | b) (List (Attr b c)))", - // "Attr * (Attr * (List (Attr (a | b) c)) -> Attr (* | a | b) (List (Attr a c)))", + "Attr * (Attr (* | a) (List (Attr a b)) -> Attr * (List (Attr a b)))", ); } @@ -2116,6 +2151,97 @@ mod test_uniq_solve { ); } + #[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 (* | a) (Set (Attr a b)), Attr Shared (Attr a b, 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 a b)), Attr * b -> Attr * (Set (Attr a b)))", + ); + } + + #[test] + fn map_empty() { + infer_eq("Map.empty", "Attr * (Map * *)"); + } + + #[test] + fn map_singelton() { + infer_eq("Map.singleton", "Attr * (a, b -> Attr * (Map a b))"); + } + + #[test] + fn map_get() { + infer_eq("Map.get", "Attr * (Attr (* | a) (Map (Attr * b) (Attr a c)), Attr * b -> Attr * (Result (Attr a c) (Attr * [ KeyNotFound ]*)))"); + } + + #[test] + fn map_insert() { + infer_eq( + "Map.insert", + "Attr * (Attr * (Map a b), a, b -> Attr * (Map a b))", + ); + } + + #[test] + fn str_is_empty() { + infer_eq("Str.isEmpty", "Attr * (Attr * Str -> Attr * Bool)"); + } + + #[test] + fn str_append() { + infer_eq( + "Str.append", + "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] fn use_correct_ext_var() { infer_eq( @@ -2367,14 +2493,18 @@ mod test_uniq_solve { } #[test] - fn equals() { + fn bool_eq() { infer_eq( - indoc!( - r#" - \a, b -> a == b - "# - ), - "Attr * (a, a -> Attr * Bool)", + "\\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)", ); } @@ -2409,7 +2539,7 @@ mod test_uniq_solve { _ -> 3 "# ), - "Attr * (Attr Shared (Num (Attr * *)) -> Attr * (Num (Attr * *)))", + "Attr * (Attr Shared (Num (Attr Shared *)) -> Attr * (Num (Attr * *)))", ); } } diff --git a/editor/src/lib.rs b/editor/src/lib.rs index cc40041b12..62c1fae962 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -129,11 +129,11 @@ fn run_event_loop() -> Result<(), Box> { .. } => { match ch { - '\u{8}' => { - // In Linux, we get one of these when you press - // backspace, but in macOS we don't. In both, we + '\u{8}' | '\u{7f}' => { + // In Linux, we get a '\u{8}' when you press backspace, + // but in macOS we get '\u{7f}'. In both, we // get a Back keydown event. Therefore, we use the - // Back keydown event and ignore this, resulting + // Back keydown event and ignore these, resulting // in a system that works in both Linux and macOS. } '\u{e000}'..='\u{f8ff}'