diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 13f60ea072..dd713960f1 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -837,25 +837,13 @@ fn type_to_variable<'a>( actual, lambda_set_variables, } => { - // the rank of these variables is NONE (encoded as 0 in practice) - // using them for other ranks causes issues - if rank.is_none() { - // TODO replace by arithmetic? - match *symbol { - Symbol::NUM_I128 => return Variable::I128, - Symbol::NUM_I64 => return Variable::I64, - Symbol::NUM_I32 => return Variable::I32, - Symbol::NUM_I16 => return Variable::I16, - Symbol::NUM_I8 => return Variable::I8, - - Symbol::NUM_U128 => return Variable::U128, - Symbol::NUM_U64 => return Variable::U64, - Symbol::NUM_U32 => return Variable::U32, - Symbol::NUM_U16 => return Variable::U16, - Symbol::NUM_U8 => return Variable::U8, - - Symbol::NUM_NAT => return Variable::NAT, - _ => {} + if let Some(reserved) = Variable::get_reserved(*symbol) { + if rank.is_none() { + // reserved variables are stored with rank NONE + return reserved; + } else { + // for any other rank, we need to copy; it takes care of adjusting the rank + return deep_copy_var(subs, rank, pools, reserved); } } @@ -868,7 +856,11 @@ fn type_to_variable<'a>( lambda_set_variables, ); - let alias_variable = type_to_variable(subs, rank, pools, arena, actual); + let alias_variable = if let Symbol::RESULT_RESULT = *symbol { + roc_result_to_var(subs, rank, pools, arena, actual) + } else { + type_to_variable(subs, rank, pools, arena, actual) + }; let content = Content::Alias(*symbol, alias_variables, alias_variable); register(subs, rank, pools, content) @@ -941,6 +933,52 @@ fn alias_to_var<'a>( } } +fn roc_result_to_var<'a>( + subs: &mut Subs, + rank: Rank, + pools: &mut Pools, + arena: &'a bumpalo::Bump, + result_type: &Type, +) -> Variable { + match result_type { + Type::TagUnion(tags, ext) => { + debug_assert!(ext.is_empty_tag_union()); + debug_assert!(tags.len() == 2); + + if let [(err, err_args), (ok, ok_args)] = &tags[..] { + debug_assert_eq!(err, &subs.tag_names[0]); + debug_assert_eq!(ok, &subs.tag_names[1]); + + if let ([err_type], [ok_type]) = (err_args.as_slice(), ok_args.as_slice()) { + let err_var = type_to_variable(subs, rank, pools, arena, err_type); + let ok_var = type_to_variable(subs, rank, pools, arena, ok_type); + + let start = subs.variables.len() as u32; + let err_slice = SubsSlice::new(start, 1); + let ok_slice = SubsSlice::new(start + 1, 1); + + subs.variables.push(err_var); + subs.variables.push(ok_var); + + let variables = SubsSlice::new(subs.variable_slices.len() as _, 2); + subs.variable_slices.push(err_slice); + subs.variable_slices.push(ok_slice); + + let union_tags = UnionTags::from_slices(Subs::RESULT_TAG_NAMES, variables); + let ext = Variable::EMPTY_TAG_UNION; + + let content = Content::Structure(FlatType::TagUnion(union_tags, ext)); + + return register(subs, rank, pools, content); + } + } + + unreachable!("invalid arguments to Result.Result; canonicalization should catch this!") + } + _ => unreachable!("not a valid type inside a Result.Result alias"), + } +} + fn insertion_sort_by(arr: &mut [T], mut compare: F) where F: FnMut(&T, &T) -> std::cmp::Ordering, diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index bbca449522..a3a517e138 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -708,6 +708,29 @@ impl Variable { pub const fn index(&self) -> u32 { self.0 } + + pub const fn get_reserved(symbol: Symbol) -> Option { + // Must be carefule here: the variables must in fact be in Subs + match symbol { + Symbol::NUM_I128 => Some(Variable::I128), + Symbol::NUM_I64 => Some(Variable::I64), + Symbol::NUM_I32 => Some(Variable::I32), + Symbol::NUM_I16 => Some(Variable::I16), + Symbol::NUM_I8 => Some(Variable::I8), + + Symbol::NUM_U128 => Some(Variable::U128), + Symbol::NUM_U64 => Some(Variable::U64), + Symbol::NUM_U32 => Some(Variable::U32), + Symbol::NUM_U16 => Some(Variable::U16), + Symbol::NUM_U8 => Some(Variable::U8), + + Symbol::NUM_NAT => Some(Variable::NAT), + + Symbol::BOOL_BOOL => Some(Variable::BOOL), + + _ => None, + } + } } impl From for OptVariable { @@ -1012,6 +1035,8 @@ fn define_integer_types(subs: &mut Subs) { } impl Subs { + pub const RESULT_TAG_NAMES: SubsSlice = SubsSlice::new(0, 2); + pub fn new() -> Self { Self::with_capacity(0) } @@ -1019,10 +1044,15 @@ impl Subs { pub fn with_capacity(capacity: usize) -> Self { let capacity = capacity.max(Variable::NUM_RESERVED_VARS); + let mut tag_names = Vec::with_capacity(32); + + tag_names.push(TagName::Global("Err".into())); + tag_names.push(TagName::Global("Ok".into())); + let mut subs = Subs { utable: UnificationTable::default(), variables: Default::default(), - tag_names: Default::default(), + tag_names, field_names: Default::default(), record_fields: Default::default(), // store an empty slice at the first position