From c0e976f544441b2bd0d8eb01836bef1a9a1bb32f Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 25 Jul 2022 17:36:46 -0400 Subject: [PATCH] Wrap unification subs in Env --- Cargo.lock | 2 + crates/ast/src/solve_type.rs | 9 +- crates/compiler/derive/src/encoding.rs | 8 +- crates/compiler/late_solve/src/lib.rs | 4 +- crates/compiler/solve/src/ability.rs | 15 +- crates/compiler/solve/src/solve.rs | 25 +- crates/compiler/unify/Cargo.toml | 6 + crates/compiler/unify/src/unify.rs | 662 +++++++++++++------------ 8 files changed, 375 insertions(+), 356 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 48959caf2d..7f4c1a39fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4124,10 +4124,12 @@ name = "roc_unify" version = "0.1.0" dependencies = [ "bitflags", + "roc_can", "roc_collections", "roc_debug_flags", "roc_error_macros", "roc_module", + "roc_solve_problem", "roc_types", ] diff --git a/crates/ast/src/solve_type.rs b/crates/ast/src/solve_type.rs index d976a85b65..b1c9d0db67 100644 --- a/crates/ast/src/solve_type.rs +++ b/crates/ast/src/solve_type.rs @@ -17,6 +17,7 @@ use roc_types::types::{ RecordField, }; use roc_unify::unify::unify; +use roc_unify::unify::Env as UEnv; use roc_unify::unify::Mode; use roc_unify::unify::Unified::*; @@ -227,7 +228,7 @@ fn solve<'a>( expectation.get_type_ref(), ); - match unify(subs, actual, expected, Mode::EQ) { + match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) { Success { vars, must_implement_ability: _, @@ -326,7 +327,7 @@ fn solve<'a>( expectation.get_type_ref(), ); - match unify(subs, actual, expected, Mode::EQ) { + match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) { Success { vars, must_implement_ability: _, @@ -403,7 +404,7 @@ fn solve<'a>( ); // TODO(ayazhafiz): presence constraints for Expr2/Type2 - match unify(subs, actual, expected, Mode::EQ) { + match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) { Success { vars, must_implement_ability: _, @@ -717,7 +718,7 @@ fn solve<'a>( ); let includes = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, &tag_ty); - match unify(subs, actual, includes, Mode::PRESENT) { + match unify(&mut UEnv::new(subs), actual, includes, Mode::PRESENT) { Success { vars, must_implement_ability: _, diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index 3efcd835e9..d5d03ac942 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -83,9 +83,9 @@ impl Env<'_> { } fn unify(&mut self, left: Variable, right: Variable) { - use roc_unify::unify::{unify, Mode, Unified}; + use roc_unify::unify::{unify, Env, Mode, Unified}; - let unified = unify(self.subs, left, right, Mode::EQ); + let unified = unify(&mut Env::new(self.subs), left, right, Mode::EQ); match unified { Unified::Success { @@ -109,12 +109,12 @@ impl Env<'_> { specialization_type: Variable, ability_member: Symbol, ) -> SpecializationLambdaSets { - use roc_unify::unify::{unify_introduced_ability_specialization, Mode, Unified}; + use roc_unify::unify::{unify_introduced_ability_specialization, Env, Mode, Unified}; let member_signature = self.import_encode_symbol(ability_member); let unified = unify_introduced_ability_specialization( - self.subs, + &mut Env::new(self.subs), member_signature, specialization_type, Mode::EQ, diff --git a/crates/compiler/late_solve/src/lib.rs b/crates/compiler/late_solve/src/lib.rs index cce4b78fdf..34b3467dbc 100644 --- a/crates/compiler/late_solve/src/lib.rs +++ b/crates/compiler/late_solve/src/lib.rs @@ -13,7 +13,7 @@ use roc_module::symbol::ModuleId; use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools}; use roc_types::subs::{get_member_lambda_sets_at_region, Content, FlatType, LambdaSet}; use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable}; -use roc_unify::unify::{unify as unify_unify, Mode, Unified}; +use roc_unify::unify::{unify as unify_unify, Env, Mode, Unified}; pub use roc_solve::ability::resolve_ability_specialization; pub use roc_solve::ability::Resolved; @@ -260,7 +260,7 @@ pub fn unify( ModuleId::DERIVED_SYNTH, "derived module can only unify its subs in its own context!" ); - let unified = unify_unify(subs, left, right, Mode::EQ); + let unified = unify_unify(&mut Env::new(subs), left, right, Mode::EQ); match unified { Unified::Success { diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index 50d1d85f0c..c398fca961 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -7,7 +7,7 @@ use roc_region::all::{Loc, Region}; use roc_solve_problem::{TypeError, UnderivableReason, Unfulfilled}; use roc_types::subs::{instantiate_rigids, Content, FlatType, GetSubsSlice, Rank, Subs, Variable}; use roc_types::types::{AliasKind, Category, PatternCategory}; -use roc_unify::unify::MustImplementConstraints; +use roc_unify::unify::{Env, MustImplementConstraints}; use roc_unify::unify::{MustImplementAbility, Obligated}; use crate::solve::type_to_var; @@ -573,10 +573,15 @@ pub fn resolve_ability_specialization( let signature_var = member_def.signature_var(); instantiate_rigids(subs, signature_var); - let (_vars, must_implement_ability, _lambda_sets_to_specialize, _meta) = - unify(subs, specialization_var, signature_var, Mode::EQ).expect_success( - "If resolving a specialization, the specialization must be known to typecheck.", - ); + let (_vars, must_implement_ability, _lambda_sets_to_specialize, _meta) = unify( + &mut Env::new(subs), + specialization_var, + signature_var, + Mode::EQ, + ) + .expect_success( + "If resolving a specialization, the specialization must be known to typecheck.", + ); subs.rollback_to(snapshot); diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 8256df0710..c4b926ddcb 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -33,8 +33,8 @@ use roc_types::types::{ OptAbleVar, Reason, TypeExtension, Uls, }; use roc_unify::unify::{ - unify, unify_introduced_ability_specialization, Mode, MustImplementConstraints, Obligated, - SpecializationLsetCollector, Unified::*, + unify, unify_introduced_ability_specialization, Env as UEnv, Mode, MustImplementConstraints, + Obligated, SpecializationLsetCollector, Unified::*, }; // Type checking system adapted from Elm by Evan Czaplicki, BSD-3-Clause Licensed @@ -963,7 +963,7 @@ fn solve( let expectation = &constraints.expectations[expectation_index.index()]; let expected = type_to_var(subs, rank, pools, aliases, expectation.get_type_ref()); - match unify(subs, actual, expected, Mode::EQ) { + match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) { Success { vars, must_implement_ability, @@ -1021,7 +1021,7 @@ fn solve( ); let target = *target; - match unify(subs, actual, target, Mode::EQ) { + match unify(&mut UEnv::new(subs), actual, target, Mode::EQ) { Success { vars, // ERROR NOT REPORTED @@ -1081,7 +1081,7 @@ fn solve( let expected = type_to_var(subs, rank, pools, aliases, expectation.get_type_ref()); - match unify(subs, actual, expected, Mode::EQ) { + match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) { Success { vars, must_implement_ability, @@ -1165,7 +1165,7 @@ fn solve( _ => Mode::EQ, }; - match unify(subs, actual, expected, mode) { + match unify(&mut UEnv::new(subs), actual, expected, mode) { Success { vars, must_implement_ability, @@ -1334,7 +1334,7 @@ fn solve( ); let includes = type_to_var(subs, rank, pools, aliases, &tag_ty); - match unify(subs, actual, includes, Mode::PRESENT) { + match unify(&mut UEnv::new(subs), actual, includes, Mode::PRESENT) { Success { vars, must_implement_ability, @@ -1442,7 +1442,7 @@ fn solve( ); let snapshot = subs.snapshot(); - let outcome = unify(subs, real_var, branches_var, Mode::EQ); + let outcome = unify(&mut UEnv::new(subs), real_var, branches_var, Mode::EQ); let should_check_exhaustiveness; match outcome { @@ -1477,7 +1477,7 @@ fn solve( // open_tag_union(subs, real_var); open_tag_union(subs, branches_var); let almost_eq = matches!( - unify(subs, real_var, branches_var, Mode::EQ), + unify(&mut UEnv::new(subs), real_var, branches_var, Mode::EQ), Success { .. } ); @@ -1489,7 +1489,7 @@ fn solve( } else { // Case 4: incompatible types, report type error. // Re-run first failed unification to get the type diff. - match unify(subs, real_var, branches_var, Mode::EQ) { + match unify(&mut UEnv::new(subs), real_var, branches_var, Mode::EQ) { Failure(vars, actual_type, expected_type, _bad_impls) => { introduce(subs, rank, pools, &vars); @@ -1720,7 +1720,7 @@ fn check_ability_specialization( deep_copy_var_in(subs, Rank::toplevel(), pools, root_signature_var, arena); let snapshot = subs.snapshot(); let unified = unify_introduced_ability_specialization( - subs, + &mut UEnv::new(subs), root_signature_var, symbol_loc_var.value, Mode::EQ, @@ -2238,7 +2238,8 @@ fn compact_lambda_set( // 3. Unify `t_f1 ~ t_f2`. trace_compact!(3iter_start. subs, this_lambda_set, t_f1, t_f2); let (vars, new_must_implement_ability, new_lambda_sets_to_specialize, _meta) = - unify(subs, t_f1, t_f2, Mode::EQ).expect_success("ambient functions don't unify"); + unify(&mut UEnv::new(subs), t_f1, t_f2, Mode::EQ) + .expect_success("ambient functions don't unify"); trace_compact!(3iter_end. subs, t_f1); introduce(subs, target_rank, pools, &vars); diff --git a/crates/compiler/unify/Cargo.toml b/crates/compiler/unify/Cargo.toml index 3cb709c120..653eece5c0 100644 --- a/crates/compiler/unify/Cargo.toml +++ b/crates/compiler/unify/Cargo.toml @@ -22,3 +22,9 @@ path = "../types" [dependencies.roc_debug_flags] path = "../debug_flags" + +[dependencies.roc_can] +path = "../can" + +[dependencies.roc_solve_problem] +path = "../solve_problem" diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 60729c7e57..0f974a8a64 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -287,24 +287,34 @@ impl Outcome { } } +pub struct Env<'a> { + pub subs: &'a mut Subs, +} + +impl<'a> Env<'a> { + pub fn new(subs: &'a mut Subs) -> Self { + Self { subs } + } +} + #[inline(always)] -pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable, mode: Mode) -> Unified { - unify_help(subs, var1, var2, mode) +pub fn unify(env: &mut Env, var1: Variable, var2: Variable, mode: Mode) -> Unified { + unify_help(env, var1, var2, mode) } #[inline(always)] pub fn unify_introduced_ability_specialization( - subs: &mut Subs, + env: &mut Env, ability_member_signature: Variable, specialization_var: Variable, mode: Mode, ) -> Unified { - unify_help(subs, ability_member_signature, specialization_var, mode) + unify_help(env, ability_member_signature, specialization_var, mode) } #[inline(always)] fn unify_help( - subs: &mut Subs, + env: &mut Env, var1: Variable, var2: Variable, mode: Mode, @@ -315,7 +325,7 @@ fn unify_help( must_implement_ability, lambda_sets_to_specialize, extra_metadata, - } = unify_pool(subs, &mut vars, var1, var2, mode); + } = unify_pool(env, &mut vars, var1, var2, mode); if mismatches.is_empty() { Unified::Success { @@ -331,12 +341,12 @@ fn unify_help( ErrorTypeContext::None }; - let (type1, mut problems) = subs.var_to_error_type_contextual(var1, error_context); - let (type2, problems2) = subs.var_to_error_type_contextual(var2, error_context); + let (type1, mut problems) = env.subs.var_to_error_type_contextual(var1, error_context); + let (type2, problems2) = env.subs.var_to_error_type_contextual(var2, error_context); problems.extend(problems2); - subs.union(var1, var2, Content::Error.into()); + env.subs.union(var1, var2, Content::Error.into()); if !problems.is_empty() { Unified::BadType(vars, problems.remove(0)) @@ -346,7 +356,7 @@ fn unify_help( .filter_map(|mismatch| match mismatch { Mismatch::DoesNotImplementAbiity(var, ab) => { let (err_type, _new_problems) = - subs.var_to_error_type_contextual(var, error_context); + env.subs.var_to_error_type_contextual(var, error_context); Some((err_type, ab)) } _ => None, @@ -360,24 +370,24 @@ fn unify_help( #[inline(always)] pub fn unify_pool( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, var1: Variable, var2: Variable, mode: Mode, ) -> Outcome { - if subs.equivalent(var1, var2) { + if env.subs.equivalent(var1, var2) { Outcome::default() } else { let ctx = Context { first: var1, - first_desc: subs.get(var1), + first_desc: env.subs.get(var1), second: var2, - second_desc: subs.get(var2), + second_desc: env.subs.get(var2), mode, }; - unify_context(subs, pool, ctx) + unify_context(env, pool, ctx) } } @@ -386,7 +396,7 @@ pub fn unify_pool( /// NOTE: Only run this on individual tests! Run on multiple threads, this would clobber each others' output. #[cfg(debug_assertions)] fn debug_print_unified_types( - subs: &mut Subs, + env: &mut Env, ctx: &Context, opt_outcome: Option<&Outcome>, ) { @@ -419,71 +429,71 @@ fn debug_print_unified_types( // println!("\n --- \n"); // dbg!(ctx.second, type2); // println!("\n --------------- \n"); - let content_1 = subs.get(ctx.first).content; - let content_2 = subs.get(ctx.second).content; + let content_1 = env.subs.get(ctx.first).content; + let content_2 = env.subs.get(ctx.second).content; let mode = ctx.mode.pretty_print(); eprintln!( "{}{}({:?}-{:?}): {:?} {:?} {} {:?} {:?}", " ".repeat(use_depth), prefix, - subs.get_root_key_without_compacting(ctx.first), - subs.get_root_key_without_compacting(ctx.second), + env.subs.get_root_key_without_compacting(ctx.first), + env.subs.get_root_key_without_compacting(ctx.second), ctx.first, - SubsFmtContent(&content_1, subs), + SubsFmtContent(&content_1, env.subs), mode, ctx.second, - SubsFmtContent(&content_2, subs), + SubsFmtContent(&content_2, env.subs), ); unsafe { UNIFICATION_DEPTH = new_depth }; }) } -fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome { +fn unify_context(env: &mut Env, pool: &mut Pool, ctx: Context) -> Outcome { #[cfg(debug_assertions)] - debug_print_unified_types::(subs, &ctx, None); + debug_print_unified_types::(env, &ctx, None); // This #[allow] is needed in release builds, where `result` is no longer used. #[allow(clippy::let_and_return)] let result = match &ctx.first_desc.content { - FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content), + FlexVar(opt_name) => unify_flex(env, &ctx, opt_name, &ctx.second_desc.content), FlexAbleVar(opt_name, ability) => { - unify_flex_able(subs, &ctx, opt_name, *ability, &ctx.second_desc.content) + unify_flex_able(env, &ctx, opt_name, *ability, &ctx.second_desc.content) } RecursionVar { opt_name, structure, } => unify_recursion( - subs, + env, pool, &ctx, opt_name, *structure, &ctx.second_desc.content, ), - RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content), + RigidVar(name) => unify_rigid(env, &ctx, name, &ctx.second_desc.content), RigidAbleVar(name, ability) => { - unify_rigid_able(subs, &ctx, name, *ability, &ctx.second_desc.content) + unify_rigid_able(env, &ctx, name, *ability, &ctx.second_desc.content) } Structure(flat_type) => { - unify_structure(subs, pool, &ctx, flat_type, &ctx.second_desc.content) + unify_structure(env, pool, &ctx, flat_type, &ctx.second_desc.content) } Alias(symbol, args, real_var, AliasKind::Structural) => { - unify_alias(subs, pool, &ctx, *symbol, *args, *real_var) + unify_alias(env, pool, &ctx, *symbol, *args, *real_var) } Alias(symbol, args, real_var, AliasKind::Opaque) => { - unify_opaque(subs, pool, &ctx, *symbol, *args, *real_var) + unify_opaque(env, pool, &ctx, *symbol, *args, *real_var) } - LambdaSet(lset) => unify_lambda_set(subs, pool, &ctx, *lset, &ctx.second_desc.content), - &RangedNumber(range_vars) => unify_ranged_number(subs, pool, &ctx, range_vars), + LambdaSet(lset) => unify_lambda_set(env, pool, &ctx, *lset, &ctx.second_desc.content), + &RangedNumber(range_vars) => unify_ranged_number(env, pool, &ctx, range_vars), Error => { // Error propagates. Whatever we're comparing it to doesn't matter! - merge(subs, &ctx, Error) + merge(env, &ctx, Error) } }; #[cfg(debug_assertions)] - debug_print_unified_types(subs, &ctx, Some(&result)); + debug_print_unified_types(env, &ctx, Some(&result)); result } @@ -499,7 +509,7 @@ fn not_in_range_mismatch() -> Outcome { #[inline(always)] fn unify_ranged_number( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, range_vars: NumericRange, @@ -509,26 +519,26 @@ fn unify_ranged_number( match other_content { FlexVar(_) => { // Ranged number wins - merge(subs, ctx, RangedNumber(range_vars)) + merge(env, ctx, RangedNumber(range_vars)) } RigidVar(name) => { // Int a vs Int , the rigid wins - merge(subs, ctx, RigidVar(*name)) + merge(env, ctx, RigidVar(*name)) } RecursionVar { .. } | Alias(..) | Structure(..) | RigidAbleVar(..) | FlexAbleVar(..) => { - check_and_merge_valid_range(subs, pool, ctx, ctx.first, range_vars, ctx.second) + check_and_merge_valid_range(env, pool, ctx, ctx.first, range_vars, ctx.second) } &RangedNumber(other_range_vars) => match range_vars.intersection(&other_range_vars) { - Some(range) => merge(subs, ctx, RangedNumber(range)), + Some(range) => merge(env, ctx, RangedNumber(range)), None => not_in_range_mismatch(), }, LambdaSet(..) => mismatch!(), - Error => merge(subs, ctx, Error), + Error => merge(env, ctx, Error), } } fn check_and_merge_valid_range( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, range_var: Variable, @@ -536,12 +546,12 @@ fn check_and_merge_valid_range( var: Variable, ) -> Outcome { use Content::*; - let content = *subs.get_content_without_compacting(var); + let content = *env.subs.get_content_without_compacting(var); macro_rules! merge_if { ($cond:expr) => { if $cond { - merge(subs, ctx, content) + merge(env, ctx, content) } else { not_in_range_mismatch() } @@ -552,9 +562,9 @@ fn check_and_merge_valid_range( RangedNumber(other_range) => match range.intersection(&other_range) { Some(r) => { if r == range { - merge(subs, ctx, RangedNumber(range)) + merge(env, ctx, RangedNumber(range)) } else { - merge(subs, ctx, RangedNumber(other_range)) + merge(env, ctx, RangedNumber(other_range)) } } None => not_in_range_mismatch(), @@ -609,22 +619,22 @@ fn check_and_merge_valid_range( } NumericRange::NumAtLeastSigned(_) | NumericRange::NumAtLeastEitherSign(_) => { debug_assert_eq!(args.len(), 1); - let arg = subs.get_subs_slice(args.all_variables())[0]; - let new_range_var = wrap_range_var(subs, symbol, range_var, kind); - unify_pool(subs, pool, new_range_var, arg, ctx.mode) + let arg = env.subs.get_subs_slice(args.all_variables())[0]; + let new_range_var = wrap_range_var(env, symbol, range_var, kind); + unify_pool(env, pool, new_range_var, arg, ctx.mode) } }, Symbol::NUM_NUM => { debug_assert_eq!(args.len(), 1); - let arg = subs.get_subs_slice(args.all_variables())[0]; - let new_range_var = wrap_range_var(subs, symbol, range_var, kind); - unify_pool(subs, pool, new_range_var, arg, ctx.mode) + let arg = env.subs.get_subs_slice(args.all_variables())[0]; + let new_range_var = wrap_range_var(env, symbol, range_var, kind); + unify_pool(env, pool, new_range_var, arg, ctx.mode) } Symbol::NUM_INT | Symbol::NUM_INTEGER => { debug_assert_eq!(args.len(), 1); - let arg = subs.get_subs_slice(args.all_variables())[0]; - let new_range_var = wrap_range_var(subs, symbol, range_var, kind); - unify_pool(subs, pool, new_range_var, arg, ctx.mode) + let arg = env.subs.get_subs_slice(args.all_variables())[0]; + let new_range_var = wrap_range_var(env, symbol, range_var, kind); + unify_pool(env, pool, new_range_var, arg, ctx.mode) } _ => mismatch!(), @@ -640,15 +650,15 @@ fn check_and_merge_valid_range( /// on the right (which this function does) and then unify /// Num (Int a) ~ Num (Int (NumericRange )) fn wrap_range_var( - subs: &mut Subs, + env: &mut Env, symbol: Symbol, range_var: Variable, alias_kind: AliasKind, ) -> Variable { - let range_desc = subs.get(range_var); - let new_range_var = subs.fresh(range_desc); - let var_slice = AliasVariables::insert_into_subs(subs, [new_range_var], []); - subs.set_content( + let range_desc = env.subs.get(range_var); + let new_range_var = env.subs.fresh(range_desc); + let var_slice = AliasVariables::insert_into_subs(env.subs, [new_range_var], []); + env.subs.set_content( range_var, Alias(symbol, var_slice, new_range_var, alias_kind), ); @@ -658,7 +668,7 @@ fn wrap_range_var( #[inline(always)] #[allow(clippy::too_many_arguments)] fn unify_two_aliases( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, // _symbol has an underscore because it's unused in --release builds @@ -676,26 +686,26 @@ fn unify_two_aliases( .into_iter() .zip(other_args.all_variables().into_iter()); - let length_before = subs.len(); + let length_before = env.subs.len(); for (l, r) in it { - let l_var = subs[l]; - let r_var = subs[r]; - outcome.union(unify_pool(subs, pool, l_var, r_var, ctx.mode)); + let l_var = env.subs[l]; + let r_var = env.subs[r]; + outcome.union(unify_pool(env, pool, l_var, r_var, ctx.mode)); } if outcome.mismatches.is_empty() { - outcome.union(merge(subs, ctx, *other_content)); + outcome.union(merge(env, ctx, *other_content)); } - let length_after = subs.len(); + let length_after = env.subs.len(); let args_unification_had_changes = length_after != length_before; if !args.is_empty() && args_unification_had_changes && outcome.mismatches.is_empty() { // We need to unify the real vars because unification of type variables // may have made them larger, which then needs to be reflected in the `real_var`. - outcome.union(unify_pool(subs, pool, real_var, other_real_var, ctx.mode)); + outcome.union(unify_pool(env, pool, real_var, other_real_var, ctx.mode)); } outcome @@ -707,7 +717,7 @@ fn unify_two_aliases( // Unifies a structural alias #[inline(always)] fn unify_alias( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, symbol: Symbol, @@ -721,17 +731,17 @@ fn unify_alias( match other_content { FlexVar(_) => { // Alias wins - merge(subs, ctx, Alias(symbol, args, real_var, kind)) + merge(env, ctx, Alias(symbol, args, real_var, kind)) } - RecursionVar { structure, .. } => unify_pool(subs, pool, real_var, *structure, ctx.mode), + RecursionVar { structure, .. } => unify_pool(env, pool, real_var, *structure, ctx.mode), RigidVar(_) | RigidAbleVar(..) | FlexAbleVar(..) => { - unify_pool(subs, pool, real_var, ctx.second, ctx.mode) + unify_pool(env, pool, real_var, ctx.second, ctx.mode) } - Alias(_, _, _, AliasKind::Opaque) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode), + Alias(_, _, _, AliasKind::Opaque) => unify_pool(env, pool, real_var, ctx.second, ctx.mode), Alias(other_symbol, other_args, other_real_var, AliasKind::Structural) => { if symbol == *other_symbol { unify_two_aliases( - subs, + env, pool, ctx, symbol, @@ -742,15 +752,15 @@ fn unify_alias( other_content, ) } else { - unify_pool(subs, pool, real_var, *other_real_var, ctx.mode) + unify_pool(env, pool, real_var, *other_real_var, ctx.mode) } } - Structure(_) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode), + Structure(_) => unify_pool(env, pool, real_var, ctx.second, ctx.mode), RangedNumber(other_range_vars) => { - check_and_merge_valid_range(subs, pool, ctx, ctx.second, *other_range_vars, ctx.first) + check_and_merge_valid_range(env, pool, ctx, ctx.second, *other_range_vars, ctx.first) } LambdaSet(..) => mismatch!("cannot unify alias {:?} with lambda set {:?}: lambda sets should never be directly behind an alias!", ctx.first, other_content), - Error => merge(subs, ctx, Error), + Error => merge(env, ctx, Error), } } @@ -765,7 +775,7 @@ fn opaque_obligation(opaque: Symbol, opaque_var: Variable) -> Obligated { #[inline(always)] fn unify_opaque( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, symbol: Symbol, @@ -779,12 +789,12 @@ fn unify_opaque( match other_content { FlexVar(_) => { // Alias wins - merge(subs, ctx, Alias(symbol, args, real_var, kind)) + merge(env, ctx, Alias(symbol, args, real_var, kind)) } FlexAbleVar(_, ability) => { // Opaque type wins merge_flex_able_with_concrete( - subs, + env, ctx, ctx.second, *ability, @@ -793,14 +803,14 @@ fn unify_opaque( ) } Alias(_, _, other_real_var, AliasKind::Structural) => { - unify_pool(subs, pool, ctx.first, *other_real_var, ctx.mode) + unify_pool(env, pool, ctx.first, *other_real_var, ctx.mode) } - RecursionVar { structure, .. } => unify_pool(subs, pool, ctx.first, *structure, ctx.mode), + RecursionVar { structure, .. } => unify_pool(env, pool, ctx.first, *structure, ctx.mode), Alias(other_symbol, other_args, other_real_var, AliasKind::Opaque) => { // Opaques types are only equal if the opaque symbols are equal! if symbol == *other_symbol { unify_two_aliases( - subs, + env, pool, ctx, symbol, @@ -816,9 +826,9 @@ fn unify_opaque( } RangedNumber(other_range_vars) => { // This opaque might be a number, check if it unifies with the target ranged number var. - check_and_merge_valid_range(subs, pool, ctx, ctx.second, *other_range_vars, ctx.first) + check_and_merge_valid_range(env, pool, ctx, ctx.second, *other_range_vars, ctx.first) } - Error => merge(subs, ctx, Error), + Error => merge(env, ctx, Error), // _other has an underscore because it's unused in --release builds _other => { // The type on the left is an opaque, but the one on the right is not! @@ -829,7 +839,7 @@ fn unify_opaque( #[inline(always)] fn unify_structure( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, flat_type: &FlatType, @@ -838,12 +848,12 @@ fn unify_structure( match other { FlexVar(_) => { // If the other is flex, Structure wins! - merge(subs, ctx, Structure(*flat_type)) + merge(env, ctx, Structure(*flat_type)) } FlexAbleVar(_, ability) => { // Structure wins merge_flex_able_with_concrete( - subs, + env, ctx, ctx.second, *ability, @@ -871,16 +881,16 @@ fn unify_structure( RecursionVar { structure, .. } => match flat_type { FlatType::TagUnion(_, _) => { // unify the structure with this unrecursive tag union - unify_pool(subs, pool, ctx.first, *structure, ctx.mode) + unify_pool(env, pool, ctx.first, *structure, ctx.mode) } FlatType::RecursiveTagUnion(rec, _, _) => { - debug_assert!(is_recursion_var(subs, *rec)); + debug_assert!(is_recursion_var(env.subs, *rec)); // unify the structure with this recursive tag union - unify_pool(subs, pool, ctx.first, *structure, ctx.mode) + unify_pool(env, pool, ctx.first, *structure, ctx.mode) } FlatType::FunctionOrTagUnion(_, _, _) => { // unify the structure with this unrecursive tag union - unify_pool(subs, pool, ctx.first, *structure, ctx.mode) + unify_pool(env, pool, ctx.first, *structure, ctx.mode) } // Only tag unions can be recursive; everything else is an error. _ => mismatch!( @@ -892,14 +902,14 @@ fn unify_structure( Structure(ref other_flat_type) => { // Unify the two flat types - unify_flat_type(subs, pool, ctx, flat_type, other_flat_type) + unify_flat_type(env, pool, ctx, flat_type, other_flat_type) } // _sym has an underscore because it's unused in --release builds Alias(_sym, _, real_var, kind) => match kind { AliasKind::Structural => { // NB: not treating this as a presence constraint seems pivotal! I // can't quite figure out why, but it doesn't seem to impact other types. - unify_pool(subs, pool, ctx.first, *real_var, ctx.mode.as_eq()) + unify_pool(env, pool, ctx.first, *real_var, ctx.mode.as_eq()) } AliasKind::Opaque => { mismatch!( @@ -912,22 +922,22 @@ fn unify_structure( LambdaSet(..) => { mismatch!( "Cannot unify structure \n{:?} \nwith lambda set\n {:?}", - roc_types::subs::SubsFmtContent(&Content::Structure(*flat_type), subs), - roc_types::subs::SubsFmtContent(other, subs), + roc_types::subs::SubsFmtContent(&Content::Structure(*flat_type), env.subs), + roc_types::subs::SubsFmtContent(other, env.subs), // &flat_type, // other ) } RangedNumber(other_range_vars) => { - check_and_merge_valid_range(subs, pool, ctx, ctx.second, *other_range_vars, ctx.first) + check_and_merge_valid_range(env, pool, ctx, ctx.second, *other_range_vars, ctx.first) } - Error => merge(subs, ctx, Error), + Error => merge(env, ctx, Error), } } #[inline(always)] fn unify_lambda_set( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, lambda_set: LambdaSet, @@ -942,36 +952,36 @@ fn unify_lambda_set( solved: UnionLabels::default(), recursion_var: OptVariable::NONE, unspecialized: SubsSlice::default(), - ambient_function: subs.fresh_unnamed_flex_var(), + ambient_function: env.subs.fresh_unnamed_flex_var(), }; - extract_specialization_lambda_set(subs, ctx, lambda_set, zero_lambda_set) + extract_specialization_lambda_set(env, ctx, lambda_set, zero_lambda_set) } else { - merge(subs, ctx, Content::LambdaSet(lambda_set)) + merge(env, ctx, Content::LambdaSet(lambda_set)) } } Content::LambdaSet(other_lambda_set) => { if M::UNIFYING_SPECIALIZATION { - extract_specialization_lambda_set(subs, ctx, lambda_set, *other_lambda_set) + extract_specialization_lambda_set(env, ctx, lambda_set, *other_lambda_set) } else { - unify_lambda_set_help(subs, pool, ctx, lambda_set, *other_lambda_set) + unify_lambda_set_help(env, pool, ctx, lambda_set, *other_lambda_set) } } RecursionVar { structure, .. } => { // suppose that the recursion var is a lambda set - unify_pool(subs, pool, ctx.first, *structure, ctx.mode) + unify_pool(env, pool, ctx.first, *structure, ctx.mode) } RigidVar(..) | RigidAbleVar(..) => mismatch!("Lambda sets never unify with rigid"), FlexAbleVar(..) => mismatch!("Lambda sets should never have abilities attached to them"), Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"), RangedNumber(..) => mismatch!("Lambda sets are never numbers"), Alias(..) => mismatch!("Lambda set can never be directly under an alias!"), - Error => merge(subs, ctx, Error), + Error => merge(env, ctx, Error), } } fn extract_specialization_lambda_set( - subs: &mut Subs, + env: &mut Env, ctx: &Context, ability_member_proto_lset: LambdaSet, specialization_lset: LambdaSet, @@ -997,7 +1007,7 @@ fn extract_specialization_lambda_set( ); debug_assert!(member_rec_var.is_none()); - let member_uls = subs.get_subs_slice(member_uls_slice); + let member_uls = env.subs.get_subs_slice(member_uls_slice); debug_assert_eq!( member_uls.len(), 1, @@ -1006,7 +1016,7 @@ fn extract_specialization_lambda_set( let Uls(_, member, region) = member_uls[0]; - let mut outcome: Outcome = merge(subs, ctx, Content::LambdaSet(specialization_lset)); + let mut outcome: Outcome = merge(env, ctx, Content::LambdaSet(specialization_lset)); outcome .extra_metadata @@ -1037,20 +1047,20 @@ struct SeparatedUnionLambdas { } fn separate_union_lambdas( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, fields1: UnionLambdas, fields2: UnionLambdas, ) -> (Outcome, SeparatedUnionLambdas) { debug_assert!( - fields1.is_sorted_allow_duplicates(subs), + fields1.is_sorted_allow_duplicates(env.subs), "not sorted: {:?}", - fields1.iter_from_subs(subs).collect::>() + fields1.iter_from_subs(env.subs).collect::>() ); debug_assert!( - fields2.is_sorted_allow_duplicates(subs), + fields2.is_sorted_allow_duplicates(env.subs), "not sorted: {:?}", - fields2.iter_from_subs(subs).collect::>() + fields2.iter_from_subs(env.subs).collect::>() ); // lambda names -> (the captures for that lambda on the left side, the captures for that lambda on the right side) @@ -1068,7 +1078,7 @@ fn separate_union_lambdas( use std::cmp::Ordering; let ord = match (fields_left.peek(), fields_right.peek()) { - (Some((l, _)), Some((r, _))) => Some((subs[*l]).cmp(&subs[*r])), + (Some((l, _)), Some((r, _))) => Some((env.subs[*l]).cmp(&env.subs[*r])), (Some(_), None) => Some(Ordering::Less), (None, Some(_)) => Some(Ordering::Greater), (None, None) => None, @@ -1077,22 +1087,22 @@ fn separate_union_lambdas( match ord { Some(Ordering::Less) => { let (sym, vars) = fields_left.next().unwrap(); - let bucket = buckets.get_or_insert(subs[sym], Sides::default); - bucket.left.push((subs[sym], subs[vars])); + let bucket = buckets.get_or_insert(env.subs[sym], Sides::default); + bucket.left.push((env.subs[sym], env.subs[vars])); } Some(Ordering::Greater) => { let (sym, vars) = fields_right.next().unwrap(); - let bucket = buckets.get_or_insert(subs[sym], Sides::default); - bucket.right.push((subs[sym], subs[vars])); + let bucket = buckets.get_or_insert(env.subs[sym], Sides::default); + bucket.right.push((env.subs[sym], env.subs[vars])); } Some(Ordering::Equal) => { let (sym, left_vars) = fields_left.next().unwrap(); let (_sym, right_vars) = fields_right.next().unwrap(); - debug_assert_eq!(subs[sym], subs[_sym]); + debug_assert_eq!(env.subs[sym], env.subs[_sym]); - let bucket = buckets.get_or_insert(subs[sym], Sides::default); - bucket.left.push((subs[sym], subs[left_vars])); - bucket.right.push((subs[sym], subs[right_vars])); + let bucket = buckets.get_or_insert(env.subs[sym], Sides::default); + bucket.left.push((env.subs[sym], env.subs[left_vars])); + bucket.right.push((env.subs[sym], env.subs[right_vars])); } None => break, } @@ -1133,9 +1143,9 @@ fn separate_union_lambdas( continue 'try_next_right; } - let snapshot = subs.snapshot(); + let snapshot = env.subs.snapshot(); for (var1, var2) in (left_slice.into_iter()).zip(right_slice.into_iter()) { - let (var1, var2) = (subs[var1], subs[var2]); + let (var1, var2) = (env.subs[var1], env.subs[var2]); // Lambda sets are effectively tags under another name, and their usage can also result // in the arguments of a lambda name being recursive. It very well may happen that @@ -1144,13 +1154,13 @@ fn separate_union_lambdas( // // Like with tag unions, if it has, we'll always pass through this branch. So, take // this opportunity to promote the lambda set to recursive if need be. - maybe_mark_union_recursive(subs, var1); - maybe_mark_union_recursive(subs, var2); + maybe_mark_union_recursive(env, var1); + maybe_mark_union_recursive(env, var2); - let outcome = unify_pool(subs, pool, var1, var2, Mode::EQ); + let outcome = unify_pool(env, pool, var1, var2, Mode::EQ); if !outcome.mismatches.is_empty() { - subs.rollback_to(snapshot); + env.subs.rollback_to(snapshot); continue 'try_next_right; } @@ -1189,7 +1199,7 @@ fn separate_union_lambdas( } fn unify_unspecialized_lambdas( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, uls1: SubsSlice, uls2: SubsSlice, @@ -1208,11 +1218,11 @@ fn unify_unspecialized_lambdas( (false, true) => Ok((uls1, Default::default())), (true, false) => Ok((uls2, Default::default())), (false, false) => { - let mut all_uls = (subs.get_subs_slice(uls1).iter()) - .chain(subs.get_subs_slice(uls2)) + let mut all_uls = (env.subs.get_subs_slice(uls1).iter()) + .chain(env.subs.get_subs_slice(uls2)) .map(|&Uls(var, sym, region)| { // Take the root key to deduplicate - Uls(subs.get_root_key_without_compacting(var), sym, region) + Uls(env.subs.get_root_key_without_compacting(var), sym, region) }) .collect::>(); // Arrange into partitions of (_, member, region). @@ -1227,7 +1237,7 @@ fn unify_unspecialized_lambdas( let Uls(var_i, sym_i, region_i) = all_uls[i]; let Uls(var_j, sym_j, region_j) = all_uls[j]; if sym_i == sym_j && region_i == region_j { - let outcome = unify_pool(subs, pool, var_i, var_j, Mode::EQ); + let outcome = unify_pool(env, pool, var_i, var_j, Mode::EQ); if !outcome.mismatches.is_empty() { return Err(outcome); } @@ -1241,7 +1251,7 @@ fn unify_unspecialized_lambdas( } Ok(( - SubsSlice::extend_new(&mut subs.unspecialized_lambda_sets, all_uls), + SubsSlice::extend_new(&mut env.subs.unspecialized_lambda_sets, all_uls), whole_outcome, )) } @@ -1249,7 +1259,7 @@ fn unify_unspecialized_lambdas( } fn unify_lambda_set_help( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, lset1: self::LambdaSet, @@ -1279,7 +1289,7 @@ fn unify_lambda_set_help( debug_assert!( (rec1.into_variable().into_iter()) .chain(rec2.into_variable().into_iter()) - .all(|v| is_recursion_var(subs, v)), + .all(|v| is_recursion_var(env.subs, v)), "Recursion var is present, but it doesn't have a recursive content!" ); @@ -1290,23 +1300,23 @@ fn unify_lambda_set_help( only_in_right, joined, }, - ) = separate_union_lambdas(subs, pool, solved1, solved2); + ) = separate_union_lambdas(env, pool, solved1, solved2); let all_lambdas = joined .into_iter() - .map(|(name, slice)| (name, subs.get_subs_slice(slice).to_vec())); + .map(|(name, slice)| (name, env.subs.get_subs_slice(slice).to_vec())); let all_lambdas = merge_sorted_preserving_duplicates( all_lambdas, only_in_left.into_iter().map(|(name, subs_slice)| { - let vec = subs.get_subs_slice(subs_slice).to_vec(); + let vec = env.subs.get_subs_slice(subs_slice).to_vec(); (name, vec) }), ); let all_lambdas = merge_sorted_preserving_duplicates( all_lambdas, only_in_right.into_iter().map(|(name, subs_slice)| { - let vec = subs.get_subs_slice(subs_slice).to_vec(); + let vec = env.subs.get_subs_slice(subs_slice).to_vec(); (name, vec) }), ); @@ -1317,7 +1327,7 @@ fn unify_lambda_set_help( (None, None) => OptVariable::NONE, }; - let merged_unspecialized = match unify_unspecialized_lambdas(subs, pool, uls1, uls2) { + let merged_unspecialized = match unify_unspecialized_lambdas(env, pool, uls1, uls2) { Ok((merged, outcome)) => { whole_outcome.union(outcome); merged @@ -1328,7 +1338,7 @@ fn unify_lambda_set_help( } }; - let new_solved = UnionLabels::insert_into_subs(subs, all_lambdas); + let new_solved = UnionLabels::insert_into_subs(env.subs, all_lambdas); let new_lambda_set = Content::LambdaSet(LambdaSet { solved: new_solved, recursion_var, @@ -1336,13 +1346,13 @@ fn unify_lambda_set_help( ambient_function: ambient_function_var, }); - let merge_outcome = merge(subs, ctx, new_lambda_set); + let merge_outcome = merge(env, ctx, new_lambda_set); whole_outcome.union(merge_outcome); whole_outcome } fn unify_record( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, fields1: RecordFields, @@ -1350,6 +1360,8 @@ fn unify_record( fields2: RecordFields, ext2: Variable, ) -> Outcome { + let subs = &mut env.subs; + let (separate, ext1, ext2) = separate_record_fields(subs, fields1, ext1, fields2, ext2); let shared_fields = separate.in_both; @@ -1357,14 +1369,14 @@ fn unify_record( if separate.only_in_1.is_empty() { if separate.only_in_2.is_empty() { // these variable will be the empty record, but we must still unify them - let ext_outcome = unify_pool(subs, pool, ext1, ext2, ctx.mode); + let ext_outcome = unify_pool(env, pool, ext1, ext2, ctx.mode); if !ext_outcome.mismatches.is_empty() { return ext_outcome; } let mut field_outcome = - unify_shared_fields(subs, pool, ctx, shared_fields, OtherFields::None, ext1); + unify_shared_fields(env, pool, ctx, shared_fields, OtherFields::None, ext1); field_outcome.union(ext_outcome); @@ -1372,21 +1384,15 @@ fn unify_record( } else { let only_in_2 = RecordFields::insert_into_subs(subs, separate.only_in_2); let flat_type = FlatType::Record(only_in_2, ext2); - let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); - let ext_outcome = unify_pool(subs, pool, ext1, sub_record, ctx.mode); + let sub_record = fresh(env, pool, ctx, Structure(flat_type)); + let ext_outcome = unify_pool(env, pool, ext1, sub_record, ctx.mode); if !ext_outcome.mismatches.is_empty() { return ext_outcome; } - let mut field_outcome = unify_shared_fields( - subs, - pool, - ctx, - shared_fields, - OtherFields::None, - sub_record, - ); + let mut field_outcome = + unify_shared_fields(env, pool, ctx, shared_fields, OtherFields::None, sub_record); field_outcome.union(ext_outcome); @@ -1395,21 +1401,15 @@ fn unify_record( } else if separate.only_in_2.is_empty() { let only_in_1 = RecordFields::insert_into_subs(subs, separate.only_in_1); let flat_type = FlatType::Record(only_in_1, ext1); - let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); - let ext_outcome = unify_pool(subs, pool, sub_record, ext2, ctx.mode); + let sub_record = fresh(env, pool, ctx, Structure(flat_type)); + let ext_outcome = unify_pool(env, pool, sub_record, ext2, ctx.mode); if !ext_outcome.mismatches.is_empty() { return ext_outcome; } - let mut field_outcome = unify_shared_fields( - subs, - pool, - ctx, - shared_fields, - OtherFields::None, - sub_record, - ); + let mut field_outcome = + unify_shared_fields(env, pool, ctx, shared_fields, OtherFields::None, sub_record); field_outcome.union(ext_outcome); @@ -1420,25 +1420,25 @@ fn unify_record( let other_fields = OtherFields::Other(only_in_1, only_in_2); - let ext = fresh(subs, pool, ctx, Content::FlexVar(None)); + let ext = fresh(env, pool, ctx, Content::FlexVar(None)); let flat_type1 = FlatType::Record(only_in_1, ext); let flat_type2 = FlatType::Record(only_in_2, ext); - let sub1 = fresh(subs, pool, ctx, Structure(flat_type1)); - let sub2 = fresh(subs, pool, ctx, Structure(flat_type2)); + let sub1 = fresh(env, pool, ctx, Structure(flat_type1)); + let sub2 = fresh(env, pool, ctx, Structure(flat_type2)); - let rec1_outcome = unify_pool(subs, pool, ext1, sub2, ctx.mode); + let rec1_outcome = unify_pool(env, pool, ext1, sub2, ctx.mode); if !rec1_outcome.mismatches.is_empty() { return rec1_outcome; } - let rec2_outcome = unify_pool(subs, pool, sub1, ext2, ctx.mode); + let rec2_outcome = unify_pool(env, pool, sub1, ext2, ctx.mode); if !rec2_outcome.mismatches.is_empty() { return rec2_outcome; } let mut field_outcome = - unify_shared_fields(subs, pool, ctx, shared_fields, other_fields, ext); + unify_shared_fields(env, pool, ctx, shared_fields, other_fields, ext); field_outcome .mismatches @@ -1458,7 +1458,7 @@ enum OtherFields { type SharedFields = Vec<(Lowercase, (RecordField, RecordField))>; fn unify_shared_fields( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, shared_fields: SharedFields, @@ -1472,7 +1472,7 @@ fn unify_shared_fields( for (name, (actual, expected)) in shared_fields { let local_outcome = unify_pool( - subs, + env, pool, actual.into_inner(), expected.into_inner(), @@ -1510,16 +1510,17 @@ fn unify_shared_fields( if num_shared_fields == matching_fields.len() { // pull fields in from the ext_var - let (ext_fields, new_ext_var) = RecordFields::empty().sorted_iterator_and_ext(subs, ext); + let (ext_fields, new_ext_var) = + RecordFields::empty().sorted_iterator_and_ext(env.subs, ext); let ext_fields: Vec<_> = ext_fields.into_iter().collect(); let fields: RecordFields = match other_fields { OtherFields::None => { if ext_fields.is_empty() { - RecordFields::insert_into_subs(subs, matching_fields) + RecordFields::insert_into_subs(env.subs, matching_fields) } else { let all_fields = merge_sorted(matching_fields, ext_fields); - RecordFields::insert_into_subs(subs, all_fields) + RecordFields::insert_into_subs(env.subs, all_fields) } } OtherFields::Other(other1, other2) => { @@ -1527,9 +1528,9 @@ fn unify_shared_fields( all_fields = merge_sorted( all_fields, other1.iter_all().map(|(i1, i2, i3)| { - let field_name: Lowercase = subs[i1].clone(); - let variable = subs[i2]; - let record_field: RecordField = subs[i3].map(|_| variable); + let field_name: Lowercase = env.subs[i1].clone(); + let variable = env.subs[i2]; + let record_field: RecordField = env.subs[i3].map(|_| variable); (field_name, record_field) }), @@ -1538,21 +1539,21 @@ fn unify_shared_fields( all_fields = merge_sorted( all_fields, other2.iter_all().map(|(i1, i2, i3)| { - let field_name: Lowercase = subs[i1].clone(); - let variable = subs[i2]; - let record_field: RecordField = subs[i3].map(|_| variable); + let field_name: Lowercase = env.subs[i1].clone(); + let variable = env.subs[i2]; + let record_field: RecordField = env.subs[i3].map(|_| variable); (field_name, record_field) }), ); - RecordFields::insert_into_subs(subs, all_fields) + RecordFields::insert_into_subs(env.subs, all_fields) } }; let flat_type = FlatType::Record(fields, new_ext_var); - let merge_outcome = merge(subs, ctx, Structure(flat_type)); + let merge_outcome = merge(env, ctx, Structure(flat_type)); whole_outcome.union(merge_outcome); whole_outcome } else { @@ -1723,7 +1724,7 @@ enum Rec { #[allow(clippy::too_many_arguments)] fn unify_tag_unions( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, tags1: UnionTags, @@ -1733,12 +1734,12 @@ fn unify_tag_unions( recursion_var: Rec, ) -> Outcome { let (separate, mut ext1, ext2) = - separate_union_tags(subs, tags1, initial_ext1, tags2, initial_ext2); + separate_union_tags(env.subs, tags1, initial_ext1, tags2, initial_ext2); let shared_tags = separate.in_both; if let (true, Content::Structure(FlatType::EmptyTagUnion)) = - (ctx.mode.is_present(), subs.get(ext1).content) + (ctx.mode.is_present(), env.subs.get(ext1).content) { if !separate.only_in_2.is_empty() { // Create a new extension variable that we'll fill in with the @@ -1755,11 +1756,11 @@ fn unify_tag_unions( // [A M, B] += [A N] // the nested tag `A` **will** grow, but we don't need to modify // the top level extension variable for that! - let new_ext = fresh(subs, pool, ctx, Content::FlexVar(None)); + let new_ext = fresh(env, pool, ctx, Content::FlexVar(None)); let new_union = Structure(FlatType::TagUnion(tags1, new_ext)); let mut new_desc = ctx.first_desc; new_desc.content = new_union; - subs.set(ctx.first, new_desc); + env.subs.set(ctx.first, new_desc); ext1 = new_ext; } @@ -1768,7 +1769,7 @@ fn unify_tag_unions( if separate.only_in_1.is_empty() { if separate.only_in_2.is_empty() { let ext_outcome = if ctx.mode.is_eq() { - unify_pool(subs, pool, ext1, ext2, ctx.mode) + unify_pool(env, pool, ext1, ext2, ctx.mode) } else { // In a presence context, we don't care about ext2 being equal to ext1 Outcome::default() @@ -1779,7 +1780,7 @@ fn unify_tag_unions( } let mut shared_tags_outcome = unify_shared_tags_new( - subs, + env, pool, ctx, shared_tags, @@ -1792,17 +1793,17 @@ fn unify_tag_unions( shared_tags_outcome } else { - let unique_tags2 = UnionTags::insert_slices_into_subs(subs, separate.only_in_2); + let unique_tags2 = UnionTags::insert_slices_into_subs(env.subs, separate.only_in_2); let flat_type = FlatType::TagUnion(unique_tags2, ext2); - let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); - let ext_outcome = unify_pool(subs, pool, ext1, sub_record, ctx.mode); + let sub_record = fresh(env, pool, ctx, Structure(flat_type)); + let ext_outcome = unify_pool(env, pool, ext1, sub_record, ctx.mode); if !ext_outcome.mismatches.is_empty() { return ext_outcome; } let mut shared_tags_outcome = unify_shared_tags_new( - subs, + env, pool, ctx, shared_tags, @@ -1816,15 +1817,15 @@ fn unify_tag_unions( shared_tags_outcome } } else if separate.only_in_2.is_empty() { - let unique_tags1 = UnionTags::insert_slices_into_subs(subs, separate.only_in_1); + let unique_tags1 = UnionTags::insert_slices_into_subs(env.subs, separate.only_in_1); let flat_type = FlatType::TagUnion(unique_tags1, ext1); - let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); + let sub_record = fresh(env, pool, ctx, Structure(flat_type)); let mut total_outcome = Outcome::default(); // In a presence context, we don't care about ext2 being equal to tags1 if ctx.mode.is_eq() { - let ext_outcome = unify_pool(subs, pool, sub_record, ext2, ctx.mode); + let ext_outcome = unify_pool(env, pool, sub_record, ext2, ctx.mode); if !ext_outcome.mismatches.is_empty() { return ext_outcome; @@ -1833,7 +1834,7 @@ fn unify_tag_unions( } let shared_tags_outcome = unify_shared_tags_new( - subs, + env, pool, ctx, shared_tags, @@ -1846,20 +1847,20 @@ fn unify_tag_unions( } else { let other_tags = OtherTags2::Union(separate.only_in_1.clone(), separate.only_in_2.clone()); - let unique_tags1 = UnionTags::insert_slices_into_subs(subs, separate.only_in_1); - let unique_tags2 = UnionTags::insert_slices_into_subs(subs, separate.only_in_2); + let unique_tags1 = UnionTags::insert_slices_into_subs(env.subs, separate.only_in_1); + let unique_tags2 = UnionTags::insert_slices_into_subs(env.subs, separate.only_in_2); let ext_content = if ctx.mode.is_present() { Content::Structure(FlatType::EmptyTagUnion) } else { Content::FlexVar(None) }; - let ext = fresh(subs, pool, ctx, ext_content); + let ext = fresh(env, pool, ctx, ext_content); let flat_type1 = FlatType::TagUnion(unique_tags1, ext); let flat_type2 = FlatType::TagUnion(unique_tags2, ext); - let sub1 = fresh(subs, pool, ctx, Structure(flat_type1)); - let sub2 = fresh(subs, pool, ctx, Structure(flat_type2)); + let sub1 = fresh(env, pool, ctx, Structure(flat_type1)); + let sub2 = fresh(env, pool, ctx, Structure(flat_type2)); // NOTE: for clearer error messages, we rollback unification of the ext vars when either fails // @@ -1876,28 +1877,28 @@ fn unify_tag_unions( // TODO is this also required for the other cases? let mut total_outcome = Outcome::default(); - let snapshot = subs.snapshot(); + let snapshot = env.subs.snapshot(); - let ext1_outcome = unify_pool(subs, pool, ext1, sub2, ctx.mode); + let ext1_outcome = unify_pool(env, pool, ext1, sub2, ctx.mode); if !ext1_outcome.mismatches.is_empty() { - subs.rollback_to(snapshot); + env.subs.rollback_to(snapshot); return ext1_outcome; } total_outcome.union(ext1_outcome); if ctx.mode.is_eq() { - let ext2_outcome = unify_pool(subs, pool, sub1, ext2, ctx.mode); + let ext2_outcome = unify_pool(env, pool, sub1, ext2, ctx.mode); if !ext2_outcome.mismatches.is_empty() { - subs.rollback_to(snapshot); + env.subs.rollback_to(snapshot); return ext2_outcome; } total_outcome.union(ext2_outcome); } - subs.commit_snapshot(snapshot); + env.subs.commit_snapshot(snapshot); let shared_tags_outcome = - unify_shared_tags_new(subs, pool, ctx, shared_tags, other_tags, ext, recursion_var); + unify_shared_tags_new(env, pool, ctx, shared_tags, other_tags, ext, recursion_var); total_outcome.union(shared_tags_outcome); total_outcome } @@ -1914,7 +1915,8 @@ enum OtherTags2 { /// Promotes a non-recursive tag union or lambda set to its recursive variant, if it is found to be /// recursive. -fn maybe_mark_union_recursive(subs: &mut Subs, union_var: Variable) { +fn maybe_mark_union_recursive(env: &mut Env, union_var: Variable) { + let subs = &mut env.subs; 'outer: while let Err((_, chain)) = subs.occurs(union_var) { // walk the chain till we find a tag union or lambda set, starting from the variable that // occurred recursively, which is always at the end of the chain. @@ -1953,7 +1955,7 @@ fn maybe_mark_union_recursive(subs: &mut Subs, union_var: Variable) { } fn unify_shared_tags_new( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, shared_tags: Vec<(TagName, (VariableSubsSlice, VariableSubsSlice))>, @@ -1974,8 +1976,8 @@ fn unify_shared_tags_new( for (actual_index, expected_index) in actual_vars.into_iter().zip(expected_vars.into_iter()) { - let actual = subs[actual_index]; - let expected = subs[expected_index]; + let actual = env.subs[actual_index]; + let expected = env.subs[expected_index]; // NOTE the arguments of a tag can be recursive. For instance in the expression // // ConsList a : [Nil, Cons a (ConsList a)] @@ -2004,12 +2006,12 @@ fn unify_shared_tags_new( // since we're expanding tag unions to equal depths as described above, // we'll always pass through this branch. So, we promote tag unions to recursive // ones here if it turns out they are that. - maybe_mark_union_recursive(subs, actual); - maybe_mark_union_recursive(subs, expected); + maybe_mark_union_recursive(env, actual); + maybe_mark_union_recursive(env, expected); let mut outcome = Outcome::::default(); - outcome.union(unify_pool(subs, pool, actual, expected, ctx.mode)); + outcome.union(unify_pool(env, pool, actual, expected, ctx.mode)); if outcome.mismatches.is_empty() { // If one of the variables is a recursion var, keep that one, so that we avoid inlining @@ -2043,8 +2045,8 @@ fn unify_shared_tags_new( // // See tests labeled "issue_2810" for more examples. let merged_var = match ( - (actual, subs.get_content_unchecked(actual)), - (expected, subs.get_content_unchecked(expected)), + (actual, env.subs.get_content_unchecked(actual)), + (expected, env.subs.get_content_unchecked(expected)), ) { ((var, Content::RecursionVar { .. }), _) | (_, (var, Content::RecursionVar { .. })) => var, @@ -2066,7 +2068,7 @@ fn unify_shared_tags_new( if num_shared_tags == matching_tags.len() { // pull fields in from the ext_var - let (ext_fields, new_ext_var) = UnionTags::default().sorted_iterator_and_ext(subs, ext); + let (ext_fields, new_ext_var) = UnionTags::default().sorted_iterator_and_ext(env.subs, ext); let ext_fields: Vec<_> = ext_fields .into_iter() .map(|(label, variables)| (label, variables.to_vec())) @@ -2075,10 +2077,10 @@ fn unify_shared_tags_new( let new_tags: UnionTags = match other_tags { OtherTags2::Empty => { if ext_fields.is_empty() { - UnionTags::insert_into_subs(subs, matching_tags) + UnionTags::insert_into_subs(env.subs, matching_tags) } else { let all_fields = merge_sorted(matching_tags, ext_fields); - UnionTags::insert_into_subs(subs, all_fields) + UnionTags::insert_into_subs(env.subs, all_fields) } } OtherTags2::Union(other1, other2) => { @@ -2086,7 +2088,7 @@ fn unify_shared_tags_new( all_fields = merge_sorted( all_fields, other1.into_iter().map(|(field_name, subs_slice)| { - let vec = subs.get_subs_slice(subs_slice).to_vec(); + let vec = env.subs.get_subs_slice(subs_slice).to_vec(); (field_name, vec) }), @@ -2095,18 +2097,18 @@ fn unify_shared_tags_new( all_fields = merge_sorted( all_fields, other2.into_iter().map(|(field_name, subs_slice)| { - let vec = subs.get_subs_slice(subs_slice).to_vec(); + let vec = env.subs.get_subs_slice(subs_slice).to_vec(); (field_name, vec) }), ); - UnionTags::insert_into_subs(subs, all_fields) + UnionTags::insert_into_subs(env.subs, all_fields) } }; let merge_outcome = - unify_shared_tags_merge_new(subs, ctx, new_tags, new_ext_var, recursion_var); + unify_shared_tags_merge_new(env, ctx, new_tags, new_ext_var, recursion_var); total_outcome.union(merge_outcome); total_outcome @@ -2120,7 +2122,7 @@ fn unify_shared_tags_new( } fn unify_shared_tags_merge_new( - subs: &mut Subs, + env: &mut Env, ctx: &Context, new_tags: UnionTags, new_ext_var: Variable, @@ -2129,17 +2131,17 @@ fn unify_shared_tags_merge_new( let flat_type = match recursion_var { Rec::None => FlatType::TagUnion(new_tags, new_ext_var), Rec::Left(rec) | Rec::Right(rec) | Rec::Both(rec, _) => { - debug_assert!(is_recursion_var(subs, rec)); + debug_assert!(is_recursion_var(env.subs, rec)); FlatType::RecursiveTagUnion(rec, new_tags, new_ext_var) } }; - merge(subs, ctx, Structure(flat_type)) + merge(env, ctx, Structure(flat_type)) } #[inline(always)] fn unify_flat_type( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, left: &FlatType, @@ -2148,67 +2150,67 @@ fn unify_flat_type( use roc_types::subs::FlatType::*; match (left, right) { - (EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(*left)), + (EmptyRecord, EmptyRecord) => merge(env, ctx, Structure(*left)), - (Record(fields, ext), EmptyRecord) if fields.has_only_optional_fields(subs) => { - unify_pool(subs, pool, *ext, ctx.second, ctx.mode) + (Record(fields, ext), EmptyRecord) if fields.has_only_optional_fields(env.subs) => { + unify_pool(env, pool, *ext, ctx.second, ctx.mode) } - (EmptyRecord, Record(fields, ext)) if fields.has_only_optional_fields(subs) => { - unify_pool(subs, pool, ctx.first, *ext, ctx.mode) + (EmptyRecord, Record(fields, ext)) if fields.has_only_optional_fields(env.subs) => { + unify_pool(env, pool, ctx.first, *ext, ctx.mode) } (Record(fields1, ext1), Record(fields2, ext2)) => { - unify_record(subs, pool, ctx, *fields1, *ext1, *fields2, *ext2) + unify_record(env, pool, ctx, *fields1, *ext1, *fields2, *ext2) } - (EmptyTagUnion, EmptyTagUnion) => merge(subs, ctx, Structure(*left)), + (EmptyTagUnion, EmptyTagUnion) => merge(env, ctx, Structure(*left)), (TagUnion(tags, ext), EmptyTagUnion) if tags.is_empty() => { - unify_pool(subs, pool, *ext, ctx.second, ctx.mode) + unify_pool(env, pool, *ext, ctx.second, ctx.mode) } (EmptyTagUnion, TagUnion(tags, ext)) if tags.is_empty() => { - unify_pool(subs, pool, ctx.first, *ext, ctx.mode) + unify_pool(env, pool, ctx.first, *ext, ctx.mode) } (TagUnion(tags1, ext1), TagUnion(tags2, ext2)) => { - unify_tag_unions(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, Rec::None) + unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2, Rec::None) } (RecursiveTagUnion(recursion_var, tags1, ext1), TagUnion(tags2, ext2)) => { - debug_assert!(is_recursion_var(subs, *recursion_var)); + debug_assert!(is_recursion_var(env.subs, *recursion_var)); // this never happens in type-correct programs, but may happen if there is a type error let rec = Rec::Left(*recursion_var); - unify_tag_unions(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec) + unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec) } (TagUnion(tags1, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => { - debug_assert!(is_recursion_var(subs, *recursion_var)); + debug_assert!(is_recursion_var(env.subs, *recursion_var)); let rec = Rec::Right(*recursion_var); - unify_tag_unions(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec) + unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec) } (RecursiveTagUnion(rec1, tags1, ext1), RecursiveTagUnion(rec2, tags2, ext2)) => { - debug_assert!(is_recursion_var(subs, *rec1)); - debug_assert!(is_recursion_var(subs, *rec2)); + debug_assert!(is_recursion_var(env.subs, *rec1)); + debug_assert!(is_recursion_var(env.subs, *rec2)); let rec = Rec::Both(*rec1, *rec2); - let mut outcome = unify_tag_unions(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec); - outcome.union(unify_pool(subs, pool, *rec1, *rec2, ctx.mode)); + let mut outcome = unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec); + outcome.union(unify_pool(env, pool, *rec1, *rec2, ctx.mode)); outcome } (Apply(l_symbol, l_args), Apply(r_symbol, r_args)) if l_symbol == r_symbol => { - let mut outcome = unify_zip_slices(subs, pool, *l_args, *r_args); + let mut outcome = unify_zip_slices(env, pool, *l_args, *r_args); if outcome.mismatches.is_empty() { - outcome.union(merge(subs, ctx, Structure(Apply(*r_symbol, *r_args)))); + outcome.union(merge(env, ctx, Structure(Apply(*r_symbol, *r_args)))); } outcome @@ -2216,9 +2218,9 @@ fn unify_flat_type( (Func(l_args, l_closure, l_ret), Func(r_args, r_closure, r_ret)) if l_args.len() == r_args.len() => { - let arg_outcome = unify_zip_slices(subs, pool, *l_args, *r_args); - let ret_outcome = unify_pool(subs, pool, *l_ret, *r_ret, ctx.mode); - let closure_outcome = unify_pool(subs, pool, *l_closure, *r_closure, ctx.mode); + let arg_outcome = unify_zip_slices(env, pool, *l_args, *r_args); + let ret_outcome = unify_pool(env, pool, *l_ret, *r_ret, ctx.mode); + let closure_outcome = unify_pool(env, pool, *l_closure, *r_closure, ctx.mode); let mut outcome = ret_outcome; @@ -2227,7 +2229,7 @@ fn unify_flat_type( if outcome.mismatches.is_empty() { outcome.union(merge( - subs, + env, ctx, Structure(Func(*r_args, *r_closure, *r_ret)), )); @@ -2237,7 +2239,7 @@ fn unify_flat_type( } (FunctionOrTagUnion(tag_name, tag_symbol, ext), Func(args, closure, ret)) => { unify_function_or_tag_union_and_func( - subs, + env, pool, ctx, tag_name, @@ -2251,7 +2253,7 @@ fn unify_flat_type( } (Func(args, closure, ret), FunctionOrTagUnion(tag_name, tag_symbol, ext)) => { unify_function_or_tag_union_and_func( - subs, + env, pool, ctx, tag_name, @@ -2264,14 +2266,14 @@ fn unify_flat_type( ) } (FunctionOrTagUnion(tag_name_1, _, ext1), FunctionOrTagUnion(tag_name_2, _, ext2)) => { - let tag_name_1_ref = &subs[*tag_name_1]; - let tag_name_2_ref = &subs[*tag_name_2]; + let tag_name_1_ref = &env.subs[*tag_name_1]; + let tag_name_2_ref = &env.subs[*tag_name_2]; if tag_name_1_ref == tag_name_2_ref { - let outcome = unify_pool(subs, pool, *ext1, *ext2, ctx.mode); + let outcome = unify_pool(env, pool, *ext1, *ext2, ctx.mode); if outcome.mismatches.is_empty() { - let content = *subs.get_content_without_compacting(ctx.second); - merge(subs, ctx, content) + let content = *env.subs.get_content_without_compacting(ctx.second); + merge(env, ctx, content) } else { outcome } @@ -2279,37 +2281,37 @@ fn unify_flat_type( let tags1 = UnionTags::from_tag_name_index(*tag_name_1); let tags2 = UnionTags::from_tag_name_index(*tag_name_2); - unify_tag_unions(subs, pool, ctx, tags1, *ext1, tags2, *ext2, Rec::None) + unify_tag_unions(env, pool, ctx, tags1, *ext1, tags2, *ext2, Rec::None) } } (TagUnion(tags1, ext1), FunctionOrTagUnion(tag_name, _, ext2)) => { let tags2 = UnionTags::from_tag_name_index(*tag_name); - unify_tag_unions(subs, pool, ctx, *tags1, *ext1, tags2, *ext2, Rec::None) + unify_tag_unions(env, pool, ctx, *tags1, *ext1, tags2, *ext2, Rec::None) } (FunctionOrTagUnion(tag_name, _, ext1), TagUnion(tags2, ext2)) => { let tags1 = UnionTags::from_tag_name_index(*tag_name); - unify_tag_unions(subs, pool, ctx, tags1, *ext1, *tags2, *ext2, Rec::None) + unify_tag_unions(env, pool, ctx, tags1, *ext1, *tags2, *ext2, Rec::None) } (RecursiveTagUnion(recursion_var, tags1, ext1), FunctionOrTagUnion(tag_name, _, ext2)) => { // this never happens in type-correct programs, but may happen if there is a type error - debug_assert!(is_recursion_var(subs, *recursion_var)); + debug_assert!(is_recursion_var(env.subs, *recursion_var)); let tags2 = UnionTags::from_tag_name_index(*tag_name); let rec = Rec::Left(*recursion_var); - unify_tag_unions(subs, pool, ctx, *tags1, *ext1, tags2, *ext2, rec) + unify_tag_unions(env, pool, ctx, *tags1, *ext1, tags2, *ext2, rec) } (FunctionOrTagUnion(tag_name, _, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => { - debug_assert!(is_recursion_var(subs, *recursion_var)); + debug_assert!(is_recursion_var(env.subs, *recursion_var)); let tags1 = UnionTags::from_tag_name_index(*tag_name); let rec = Rec::Right(*recursion_var); - unify_tag_unions(subs, pool, ctx, tags1, *ext1, *tags2, *ext2, rec) + unify_tag_unions(env, pool, ctx, tags1, *ext1, *tags2, *ext2, rec) } // these have underscores because they're unused in --release builds @@ -2317,15 +2319,15 @@ fn unify_flat_type( // any other combination is a mismatch mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", - roc_types::subs::SubsFmtFlatType(_other1, subs), - roc_types::subs::SubsFmtFlatType(_other2, subs) + roc_types::subs::SubsFmtFlatType(_other1, env.subs), + roc_types::subs::SubsFmtFlatType(_other2, env.subs) ) } } } fn unify_zip_slices( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, left: SubsSlice, right: SubsSlice, @@ -2335,10 +2337,10 @@ fn unify_zip_slices( let it = left.into_iter().zip(right.into_iter()); for (l_index, r_index) in it { - let l_var = subs[l_index]; - let r_var = subs[r_index]; + let l_var = env.subs[l_index]; + let r_var = env.subs[r_index]; - outcome.union(unify_pool(subs, pool, l_var, r_var, Mode::EQ)); + outcome.union(unify_pool(env, pool, l_var, r_var, Mode::EQ)); } outcome @@ -2346,7 +2348,7 @@ fn unify_zip_slices( #[inline(always)] fn unify_rigid( - subs: &mut Subs, + env: &mut Env, ctx: &Context, name: &SubsIndex, other: &Content, @@ -2354,7 +2356,7 @@ fn unify_rigid( match other { FlexVar(_) => { // If the other is flex, rigid wins! - merge(subs, ctx, RigidVar(*name)) + merge(env, ctx, RigidVar(*name)) } FlexAbleVar(_, other_ability) => { // Mismatch - Rigid can unify with FlexAble only when the Rigid has an ability @@ -2366,7 +2368,7 @@ fn unify_rigid( } RangedNumber(..) => { // Int a vs Int , the rigid wins - merge(subs, ctx, RigidVar(*name)) + merge(env, ctx, RigidVar(*name)) } RigidVar(_) @@ -2382,14 +2384,14 @@ fn unify_rigid( Error => { // Error propagates. - merge(subs, ctx, Error) + merge(env, ctx, Error) } } } #[inline(always)] fn unify_rigid_able( - subs: &mut Subs, + env: &mut Env, ctx: &Context, name: &SubsIndex, ability: Symbol, @@ -2398,12 +2400,12 @@ fn unify_rigid_able( match other { FlexVar(_) => { // If the other is flex, rigid wins! - merge(subs, ctx, RigidVar(*name)) + merge(env, ctx, RigidVar(*name)) } FlexAbleVar(_, other_ability) => { if ability == *other_ability { // The ability bounds are the same, so rigid wins! - merge(subs, ctx, RigidAbleVar(*name, ability)) + merge(env, ctx, RigidAbleVar(*name, ability)) } else { // Mismatch for now. // TODO check ability hierarchies. @@ -2431,14 +2433,14 @@ fn unify_rigid_able( Error => { // Error propagates. - merge(subs, ctx, Error) + merge(env, ctx, Error) } } } #[inline(always)] fn unify_flex( - subs: &mut Subs, + env: &mut Env, ctx: &Context, opt_name: &Option>, other: &Content, @@ -2447,13 +2449,13 @@ fn unify_flex( FlexVar(other_opt_name) => { // Prefer using right's name. let opt_name = opt_name.or(*other_opt_name); - merge(subs, ctx, FlexVar(opt_name)) + merge(env, ctx, FlexVar(opt_name)) } FlexAbleVar(opt_other_name, ability) => { // Prefer using right's name. let opt_name = (opt_other_name).or(*opt_name); - merge(subs, ctx, FlexAbleVar(opt_name, *ability)) + merge(env, ctx, FlexAbleVar(opt_name, *ability)) } RigidVar(_) @@ -2465,16 +2467,16 @@ fn unify_flex( | LambdaSet(..) => { // TODO special-case boolean here // In all other cases, if left is flex, defer to right. - merge(subs, ctx, *other) + merge(env, ctx, *other) } - Error => merge(subs, ctx, Error), + Error => merge(env, ctx, Error), } } #[inline(always)] fn unify_flex_able( - subs: &mut Subs, + env: &mut Env, ctx: &Context, opt_name: &Option>, ability: Symbol, @@ -2484,7 +2486,7 @@ fn unify_flex_able( FlexVar(opt_other_name) => { // Prefer using right's name. let opt_name = (opt_other_name).or(*opt_name); - merge(subs, ctx, FlexAbleVar(opt_name, ability)) + merge(env, ctx, FlexAbleVar(opt_name, ability)) } FlexAbleVar(opt_other_name, other_ability) => { @@ -2492,7 +2494,7 @@ fn unify_flex_able( let opt_name = (opt_other_name).or(*opt_name); if ability == *other_ability { - merge(subs, ctx, FlexAbleVar(opt_name, ability)) + merge(env, ctx, FlexAbleVar(opt_name, ability)) } else { // Ability names differ; mismatch for now. // TODO check ability hierarchies. @@ -2508,7 +2510,7 @@ fn unify_flex_able( RigidAbleVar(_, other_ability) => { if ability == *other_ability { - merge(subs, ctx, *other) + merge(env, ctx, *other) } else { mismatch!(%not_able, ctx.second, ability, "RigidAble {:?} vs {:?}", ability, other_ability) } @@ -2521,7 +2523,7 @@ fn unify_flex_able( Alias(name, _args, _real_var, AliasKind::Opaque) => { // Opaque type wins merge_flex_able_with_concrete( - subs, + env, ctx, ctx.first, ability, @@ -2533,7 +2535,7 @@ fn unify_flex_able( Structure(_) | Alias(_, _, _, AliasKind::Structural) | RangedNumber(..) => { // Structural type wins. merge_flex_able_with_concrete( - subs, + env, ctx, ctx.first, ability, @@ -2542,19 +2544,19 @@ fn unify_flex_able( ) } - Error => merge(subs, ctx, Error), + Error => merge(env, ctx, Error), } } fn merge_flex_able_with_concrete( - subs: &mut Subs, + env: &mut Env, ctx: &Context, flex_able_var: Variable, ability: Symbol, concrete_content: Content, concrete_obligation: Obligated, ) -> Outcome { - let mut outcome = merge(subs, ctx, concrete_content); + let mut outcome = merge(env, ctx, concrete_content); let must_implement_ability = MustImplementAbility { typ: concrete_obligation, ability, @@ -2568,7 +2570,9 @@ fn merge_flex_able_with_concrete( // // If we ever organize ability implementations so that they are well-known before any other // unification is done, they can be solved in-band here! - let uls_of_concrete = subs.remove_dependent_unspecialized_lambda_sets(flex_able_var); + let uls_of_concrete = env + .subs + .remove_dependent_unspecialized_lambda_sets(flex_able_var); outcome .lambda_sets_to_specialize .extend(flex_able_var, uls_of_concrete); @@ -2578,7 +2582,7 @@ fn merge_flex_able_with_concrete( #[inline(always)] fn unify_recursion( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, opt_name: &Option>, @@ -2594,7 +2598,7 @@ fn unify_recursion( // we should not do that here, it would create an infinite loop! let name = (*opt_name).or(*other_opt_name); merge( - subs, + env, ctx, RecursionVar { opt_name: name, @@ -2605,7 +2609,7 @@ fn unify_recursion( Structure(_) => { // unify the structure variable with this Structure - unify_pool(subs, pool, structure, ctx.second, ctx.mode) + unify_pool(env, pool, structure, ctx.second, ctx.mode) } RigidVar(_) => { mismatch!("RecursionVar {:?} with rigid {:?}", ctx.first, &other) @@ -2616,7 +2620,7 @@ fn unify_recursion( } FlexVar(_) => merge( - subs, + env, ctx, RecursionVar { structure, @@ -2626,12 +2630,12 @@ fn unify_recursion( Alias(_, _, actual, AliasKind::Structural) => { // look at the type the alias stands for - unify_pool(subs, pool, ctx.first, *actual, ctx.mode) + unify_pool(env, pool, ctx.first, *actual, ctx.mode) } Alias(_, _, _, AliasKind::Opaque) => { // look at the type the recursion var stands for - unify_pool(subs, pool, structure, ctx.second, ctx.mode) + unify_pool(env, pool, structure, ctx.second, ctx.mode) } RangedNumber(..) => mismatch!( @@ -2644,14 +2648,14 @@ fn unify_recursion( debug_assert!(!M::UNIFYING_SPECIALIZATION); // suppose that the recursion var is a lambda set - unify_pool(subs, pool, structure, ctx.second, ctx.mode) + unify_pool(env, pool, structure, ctx.second, ctx.mode) } - Error => merge(subs, ctx, Error), + Error => merge(env, ctx, Error), } } -pub fn merge(subs: &mut Subs, ctx: &Context, content: Content) -> Outcome { +pub fn merge(env: &mut Env, ctx: &Context, content: Content) -> Outcome { let rank = ctx.first_desc.rank.min(ctx.second_desc.rank); let desc = Descriptor { content, @@ -2660,22 +2664,22 @@ pub fn merge(subs: &mut Subs, ctx: &Context, content: Content) copy: OptVariable::NONE, }; - subs.union(ctx.first, ctx.second, desc); + env.subs.union(ctx.first, ctx.second, desc); Outcome::default() } -fn register(subs: &mut Subs, desc: Descriptor, pool: &mut Pool) -> Variable { - let var = subs.fresh(desc); +fn register(env: &mut Env, desc: Descriptor, pool: &mut Pool) -> Variable { + let var = env.subs.fresh(desc); pool.push(var); var } -fn fresh(subs: &mut Subs, pool: &mut Pool, ctx: &Context, content: Content) -> Variable { +fn fresh(env: &mut Env, pool: &mut Pool, ctx: &Context, content: Content) -> Variable { register( - subs, + env, Descriptor { content, rank: ctx.first_desc.rank.min(ctx.second_desc.rank), @@ -2695,7 +2699,7 @@ fn is_recursion_var(subs: &Subs, var: Variable) -> bool { #[allow(clippy::too_many_arguments)] fn unify_function_or_tag_union_and_func( - subs: &mut Subs, + env: &mut Env, pool: &mut Pool, ctx: &Context, tag_name_index: &SubsIndex, @@ -2706,21 +2710,21 @@ fn unify_function_or_tag_union_and_func( function_lambda_set: Variable, left: bool, ) -> Outcome { - let tag_name = subs[*tag_name_index].clone(); + let tag_name = env.subs[*tag_name_index].clone(); - let union_tags = UnionTags::insert_slices_into_subs(subs, [(tag_name, function_arguments)]); + let union_tags = UnionTags::insert_slices_into_subs(env.subs, [(tag_name, function_arguments)]); let content = Content::Structure(FlatType::TagUnion(union_tags, tag_ext)); - let new_tag_union_var = fresh(subs, pool, ctx, content); + let new_tag_union_var = fresh(env, pool, ctx, content); let mut outcome = if left { - unify_pool(subs, pool, new_tag_union_var, function_return, ctx.mode) + unify_pool(env, pool, new_tag_union_var, function_return, ctx.mode) } else { - unify_pool(subs, pool, function_return, new_tag_union_var, ctx.mode) + unify_pool(env, pool, function_return, new_tag_union_var, ctx.mode) }; { - let union_tags = UnionLambdas::tag_without_arguments(subs, tag_symbol); + let union_tags = UnionLambdas::tag_without_arguments(env.subs, tag_symbol); let ambient_function_var = if left { ctx.first } else { ctx.second }; let lambda_set_content = LambdaSet(self::LambdaSet { solved: union_tags, @@ -2730,7 +2734,7 @@ fn unify_function_or_tag_union_and_func( }); let tag_lambda_set = register( - subs, + env, Descriptor { content: lambda_set_content, rank: ctx.first_desc.rank.min(ctx.second_desc.rank), @@ -2741,9 +2745,9 @@ fn unify_function_or_tag_union_and_func( ); let closure_outcome = if left { - unify_pool(subs, pool, tag_lambda_set, function_lambda_set, ctx.mode) + unify_pool(env, pool, tag_lambda_set, function_lambda_set, ctx.mode) } else { - unify_pool(subs, pool, function_lambda_set, tag_lambda_set, ctx.mode) + unify_pool(env, pool, function_lambda_set, tag_lambda_set, ctx.mode) }; outcome.union(closure_outcome); @@ -2751,12 +2755,12 @@ fn unify_function_or_tag_union_and_func( if outcome.mismatches.is_empty() { let desc = if left { - subs.get(ctx.second) + env.subs.get(ctx.second) } else { - subs.get(ctx.first) + env.subs.get(ctx.first) }; - subs.union(ctx.first, ctx.second, desc); + env.subs.union(ctx.first, ctx.second, desc); } outcome