From 982625378554ea81b8a2001c4c8ed27488a10df4 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 24 Jun 2022 17:50:46 -0400 Subject: [PATCH] Deriving string encoders works --- crates/compiler/derive/src/lib.rs | 6 +- crates/compiler/late_solve/src/lib.rs | 46 +++++++- crates/compiler/load_internal/src/file.rs | 14 ++- crates/compiler/mono/src/ir.rs | 16 +++ crates/compiler/mono/src/layout.rs | 32 +++++- crates/compiler/solve/src/module.rs | 6 +- crates/compiler/solve/src/solve.rs | 107 ++++++++++++++---- crates/compiler/test_gen/src/gen_abilities.rs | 44 +++++++ .../generated/encode_derived_string.txt | 106 +++++++++++++++++ crates/compiler/test_mono/src/tests.rs | 20 +++- crates/compiler/types/src/subs.rs | 8 +- 11 files changed, 372 insertions(+), 33 deletions(-) create mode 100644 crates/compiler/test_mono/generated/encode_derived_string.txt diff --git a/crates/compiler/derive/src/lib.rs b/crates/compiler/derive/src/lib.rs index b2c7ea484e..02e843425c 100644 --- a/crates/compiler/derive/src/lib.rs +++ b/crates/compiler/derive/src/lib.rs @@ -205,7 +205,7 @@ impl DerivedModule { &self.subs, target, // bookkeep unspecialized lambda sets of var - I think we want this here - true, + false, var, // TODO: I think this is okay because the only use of `copy_lambda_set_var_to_subs` // (at least right now) is for lambda set compaction, which will automatically unify @@ -213,8 +213,8 @@ impl DerivedModule { // // However this is a bad coupling and maybe not a good assumption, we should revisit // this when possible. - // Rank::toplevel(), - target_rank, + Rank::import(), + // target_rank, ); copied_import.variable diff --git a/crates/compiler/late_solve/src/lib.rs b/crates/compiler/late_solve/src/lib.rs index 700b543cee..19fcf8b4fa 100644 --- a/crates/compiler/late_solve/src/lib.rs +++ b/crates/compiler/late_solve/src/lib.rs @@ -10,7 +10,7 @@ use roc_collections::MutMap; use roc_derive::SharedDerivedModule; use roc_error_macros::internal_error; use roc_module::symbol::ModuleId; -use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools}; +use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools, SubsProxy}; use roc_types::subs::{Content, FlatType, LambdaSet}; use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable}; use roc_unify::unify::{unify as unify_unify, Mode, Unified}; @@ -174,7 +174,22 @@ pub fn unify( left: Variable, right: Variable, ) -> Result<(), UnificationFailed> { - let unified = unify_unify(subs, left, right, Mode::EQ); + let unified = if home == ModuleId::DERIVED { + // TODO: can we be smarter so more stuff can happen in parallel without us having to lock + // and steal from the derived module here? + let mut derived_module = derived_module.lock().unwrap(); + + let mut stolen = derived_module.steal(); + + let unified = unify_unify(&mut stolen.subs, left, right, Mode::EQ); + + derived_module.return_stolen(stolen); + + unified + } else { + unify_unify(subs, left, right, Mode::EQ) + }; + match unified { Unified::Success { vars: _, @@ -186,14 +201,14 @@ pub fn unify( let late_phase = LatePhase { home, abilities }; + let mut subs_proxy = SubsProxy::new(home, subs, derived_module); let must_implement_constraints = compact_lambda_sets_of_vars( - subs, + &mut subs_proxy, arena, &mut pools, lambda_sets_to_specialize, &late_phase, exposed_by_module, - derived_module, ); // At this point we can't do anything with must-implement constraints, since we're no // longer solving. We must assume that they were totally caught during solving. @@ -210,3 +225,26 @@ pub fn unify( Unified::Failure(..) | Unified::BadType(..) => Err(UnificationFailed), } } + +pub fn hack( + lset: Variable, + home: ModuleId, + arena: &Bump, + subs: &mut Subs, + abilities: &AbilitiesView, + derived_module: &SharedDerivedModule, + exposed_by_module: &ExposedByModule, +) { + let mut subs_proxy = SubsProxy::new(home, subs, derived_module); + let late_phase = LatePhase { home, abilities }; + let mut uls_of_var = roc_types::subs::UlsOfVar::default(); + uls_of_var.add(lset, lset); + compact_lambda_sets_of_vars( + &mut subs_proxy, + arena, + &mut Pools::default(), + uls_of_var, + &late_phase, + exposed_by_module, + ); +} diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 67b2c4e152..d34bbd6f7f 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -2706,7 +2706,15 @@ fn finish_specialization( .into_inner() .into_module_ids(); - let all_ident_ids = state.constrained_ident_ids; + let mut all_ident_ids = state.constrained_ident_ids; + // Steal the derived symbols and put them in the global ident ids. Since we're done, we won't + // need them again. + let StolenFromDerived { + ident_ids: derived_ident_ids, + subs: _, + } = state.derived_module.lock().unwrap().steal(); + ModuleId::DERIVED.register_debug_idents(&derived_ident_ids); + all_ident_ids.insert(ModuleId::DERIVED, derived_ident_ids); let interns = Interns { module_ids, @@ -4113,7 +4121,10 @@ fn run_solve_solve( } let (solved_subs, solved_specializations, exposed_vars_by_symbol, problems, abilities_store) = { + let module_id = module.module_id; + let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve( + module_id, &constraints, actual_constraint, rigid_variables, @@ -4125,7 +4136,6 @@ fn run_solve_solve( derived_module, ); - let module_id = module.module_id; // Figure out what specializations belong to this module let solved_specializations: ResolvedSpecializations = abilities_store .iter_specializations() diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index e5acc36141..014c25420a 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2923,6 +2923,7 @@ fn specialize_external<'a>( host_exposed_variables: &[(Symbol, Variable)], partial_proc_id: PartialProcId, ) -> Result, LayoutProblem> { + dbg!(("spec ", proc_name)); let partial_proc = procs.partial_procs.get_id(partial_proc_id); let captured_symbols = partial_proc.captured_symbols; @@ -2930,7 +2931,16 @@ fn specialize_external<'a>( let snapshot = env.subs.snapshot(); let cache_snapshot = layout_cache.snapshot(); + let ann = roc_types::subs::SubsFmtContent( + env.subs + .get_content_without_compacting(partial_proc.annotation), + env.subs, + ); + let spec = + roc_types::subs::SubsFmtContent(env.subs.get_content_without_compacting(fn_var), env.subs); + dbg!(ann, spec); let _unified = env.unify(partial_proc.annotation, fn_var); + dbg!("done"); // This will not hold for programs with type errors // let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_)); @@ -4747,6 +4757,12 @@ pub fn with_hole<'a>( match loc_expr.value { roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => { + dbg!(proc_name); + dbg!(roc_types::subs::SubsFmtContent( + env.subs.get_content_without_compacting(fn_var), + &env.subs + )); + // a call by a known name call_by_name( env, diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 48b8c07779..d2e0b22d65 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -1235,7 +1235,23 @@ impl<'a> Layout<'a> { use roc_types::subs::Content::*; match content { FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)), - FlexAbleVar(_, _) | RigidAbleVar(_, _) => todo_abilities!("Not reachable yet"), + FlexAbleVar(_, _) | RigidAbleVar(_, _) => { + unsafe { + dbg!( + roc_types::subs::SubsFmtContent( + env.subs + .get_content_without_compacting(Variable::from_index(1217)), + &env.subs + ), + roc_types::subs::SubsFmtContent( + env.subs + .get_content_without_compacting(Variable::from_index(1224)), + &env.subs + ), + ); + } + todo_abilities!("Not reachable yet") + } RecursionVar { structure, .. } => { let structure_content = env.subs.get_content_without_compacting(structure); Self::new_help(env, structure, *structure_content) @@ -2099,6 +2115,13 @@ fn layout_from_flat_type<'a>( } } Func(_, closure_var, _) => { + dbg!( + closure_var, + roc_types::subs::SubsFmtContent( + env.subs.get_content_without_compacting(closure_var), + &subs + ) + ); let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; @@ -2116,6 +2139,13 @@ fn layout_from_flat_type<'a>( for (label, field) in it { match field { RecordField::Required(field_var) | RecordField::Demanded(field_var) => { + dbg!( + label, + roc_types::subs::SubsFmtContent( + subs.get_content_without_compacting(field_var), + subs + ) + ); sortables.push((label, Layout::from_var(env, field_var)?)); } RecordField::Optional(_) => { diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index 6f37958b92..f4194a8b65 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -5,9 +5,9 @@ use roc_can::expr::PendingDerives; use roc_can::module::{ExposedByModule, RigidVariables}; use roc_collections::all::MutMap; use roc_collections::VecMap; -use roc_derive_key::GlobalDerivedSymbols; +use roc_derive::SharedDerivedModule; use roc_error_macros::internal_error; -use roc_module::symbol::Symbol; +use roc_module::symbol::{ModuleId, Symbol}; use roc_types::subs::{Content, ExposedTypesStorageSubs, FlatType, StorageSubs, Subs, Variable}; use roc_types::types::Alias; @@ -54,6 +54,7 @@ pub struct SolvedModule { #[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var pub fn run_solve( + home: ModuleId, constraints: &Constraints, constraint: ConstraintSoa, rigid_variables: RigidVariables, @@ -87,6 +88,7 @@ pub fn run_solve( // Run the solver to populate Subs. let (solved_subs, solved_env) = solve::run( + home, constraints, &mut problems, subs, diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index a16e0d26e4..da1794a75b 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -14,8 +14,8 @@ use roc_collections::all::MutMap; use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::{ROC_TRACE_COMPACTION, ROC_VERIFY_RIGID_LET_GENERALIZED}; -use roc_derive::SharedDerivedModule; -use roc_derive_key::{DeriveError, Derived}; +use roc_derive::{DerivedModule, SharedDerivedModule}; +use roc_derive_key::{DeriveError, DeriveKey}; use roc_error_macros::internal_error; use roc_module::ident::TagName; use roc_module::symbol::{ModuleId, Symbol}; @@ -545,6 +545,7 @@ struct State { #[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var pub fn run( + home: ModuleId, constraints: &Constraints, problems: &mut Vec, mut subs: Subs, @@ -556,6 +557,7 @@ pub fn run( derived_module: SharedDerivedModule, ) -> (Solved, Env) { let env = run_in_place( + home, constraints, problems, &mut subs, @@ -573,6 +575,7 @@ pub fn run( /// Modify an existing subs in-place instead #[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var fn run_in_place( + home: ModuleId, constraints: &Constraints, problems: &mut Vec, subs: &mut Subs, @@ -617,14 +620,15 @@ fn run_in_place( // Now that the module has been solved, we can run through and check all // types claimed to implement abilities. This will also tell us what derives // are legal, which we need to register. + let mut subs_proxy = SubsProxy::new(home, subs, &derived_module); + let new_must_implement = compact_lambda_sets_of_vars( - subs, + &mut subs_proxy, &arena, &mut pools, deferred_uls_to_resolve, &SolvePhase { abilities_store }, exposed_by_module, - &derived_module, ); deferred_obligations.add(new_must_implement, AbilityImplError::IncompleteAbility); @@ -1759,6 +1763,72 @@ fn check_ability_specialization( } } +/// A proxy for managing [`Subs`] and the derived module, in the presence of possibly having to +/// solve within the derived module's subs. +pub struct SubsProxy<'a> { + home: ModuleId, + subs: &'a mut Subs, + derived_module: &'a SharedDerivedModule, +} + +impl<'a> SubsProxy<'a> { + /// Creates a new subs proxy with the derived module. + /// The contract is: + /// - `subs` is for the `home` module + /// - if `home` is the derived module, then the derived module is not in a stolen state + pub fn new( + home: ModuleId, + subs: &'a mut Subs, + derived_module: &'a SharedDerivedModule, + ) -> Self { + Self { + home, + subs, + derived_module, + } + } + + fn with_subs(&mut self, f: F) -> T + where + F: FnOnce(&mut Subs) -> T, + { + if self.home != ModuleId::DERIVED { + f(self.subs) + } else { + let mut derived_module = self.derived_module.lock().unwrap(); + let mut stolen = derived_module.steal(); + let result = f(&mut stolen.subs); + derived_module.return_stolen(stolen); + result + } + } + + fn with_derived(&mut self, f: F) -> T + where + F: FnOnce(&mut DerivedModule) -> T, + { + let mut derived_module = self.derived_module.lock().unwrap(); + f(&mut derived_module) + } + + fn copy_lambda_set_var_from_derived_to_subs( + &mut self, + lambda_set_var_in_derived: Variable, + target_rank: Rank, + ) -> Variable { + if self.home == ModuleId::DERIVED { + lambda_set_var_in_derived + } else { + let derived_module = self.derived_module.lock().unwrap(); + derived_module.copy_lambda_set_var_to_subs( + lambda_set_var_in_derived, + self.subs, + target_rank, + ) + } + } +} + #[cfg(debug_assertions)] fn trace_compaction_step_1(subs: &Subs, c_a: Variable, uls_a: &[Variable]) { let c_a = roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(c_a), subs); @@ -1881,7 +1951,7 @@ fn unique_unspecialized_lambda(subs: &Subs, c_a: Variable, uls: &[Uls]) -> Optio #[must_use] pub fn compact_lambda_sets_of_vars( - subs: &mut Subs, + subs: &mut SubsProxy, arena: &Bump, pools: &mut Pools, uls_of_var: UlsOfVar, @@ -2307,13 +2377,14 @@ fn make_specialization_decision(subs: &Subs, var: Variable) -> SpecializeDecisio } fn get_specialization_lambda_set( - subs: &mut Subs, + subs: &mut SubsProxy, phase: &P, ability_member: Symbol, lset_region: u8, specialization_key: SpecializationTypeKey, exposed_by_module: &ExposedByModule, derived_module: &SharedDerivedModule, + target_rank: Rank, ) -> Result { match specialization_key { SpecializationTypeKey::Opaque(opaque) => { @@ -2344,27 +2415,25 @@ fn get_specialization_lambda_set( } })?; - let local_lset = phase.copy_lambda_set_var_to_home_subs( - external_specialized_lset, - opaque_home, - subs, - ); + let local_lset = subs.with_subs(|subs| { + phase.copy_lambda_set_var_to_home_subs(external_specialized_lset, opaque_home, subs) + }); Ok(local_lset) } SpecializationTypeKey::Derived(derive_key) => { - let mut derived_module = derived_module.lock().unwrap(); + let specialized_lambda_set = subs.with_derived(|derived_module| { + let (_, _, specialization_lambda_sets) = + derived_module.get_or_insert(exposed_by_module, derive_key); - let (_, _, specialization_lambda_sets) = - derived_module.get_or_insert(exposed_by_module, derive_key); - - let &specialized_lambda_set = specialization_lambda_sets - .get(&lset_region) - .expect("lambda set region not resolved"); + *specialization_lambda_sets + .get(&lset_region) + .expect("lambda set region not resolved") + }); let local_lset = - derived_module.copy_lambda_set_var_to_subs(specialized_lambda_set, subs); + subs.copy_lambda_set_var_from_derived_to_subs(specialized_lambda_set, target_rank); Ok(local_lset) } diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index 4162e6d152..7781afb0dc 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -403,3 +403,47 @@ fn to_encoder_encode_custom_has_capture() { RocStr ) } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn encode_derived_string() { + assert_evals_to!( + indoc!( + r#" + app "test" + imports [Encode.{ toEncoder }, Json] + provides [main] to "./platform" + + main = + result = Str.fromUtf8 (Encode.toBytes "foo" Json.format) + when result is + Ok s -> s + _ -> "" + "# + ), + RocStr::from("\"foo\""), + RocStr + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn encode_derived_record() { + assert_evals_to!( + indoc!( + r#" + app "test" + imports [Encode.{ toEncoder }, Json] + provides [main] to "./platform" + + main = + result = Str.fromUtf8 (Encode.toBytes {a: "foo"} Json.format) + when result is + Ok s -> s + _ -> "" + "# + ), + RocStr::from("foo"), + RocStr + ) +} diff --git a/crates/compiler/test_mono/generated/encode_derived_string.txt b/crates/compiler/test_mono/generated/encode_derived_string.txt new file mode 100644 index 0000000000..48a5b5b26f --- /dev/null +++ b/crates/compiler/test_mono/generated/encode_derived_string.txt @@ -0,0 +1,106 @@ +procedure #Derived.0 (#Derived.1): + let #Derived.6 : {Str} = Struct {#Derived.1}; + let #Derived.5 : {Str} = CallByName Encode.22 #Derived.6; + ret #Derived.5; + +procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12): + let #Derived.1 : Str = StructAtIndex 0 #Attr.12; + inc #Derived.1; + dec #Attr.12; + let #Derived.9 : {Str} = CallByName Json.17 #Derived.1; + let #Derived.8 : List U8 = CallByName Encode.23 #Derived.3 #Derived.9 #Derived.4; + ret #Derived.8; + +procedure Encode.22 (Encode.93): + ret Encode.93; + +procedure Encode.22 (Encode.93): + ret Encode.93; + +procedure Encode.23 (Encode.94, Encode.102, Encode.96): + let Encode.107 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102; + ret Encode.107; + +procedure Encode.23 (Encode.94, Encode.102, Encode.96): + let Encode.114 : List U8 = CallByName Json.65 Encode.94 Encode.96 Encode.102; + ret Encode.114; + +procedure Encode.25 (Encode.100, Encode.101): + let Encode.104 : List U8 = Array []; + let Encode.105 : {Str} = CallByName #Derived.0 Encode.100; + let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; + ret Encode.103; + +procedure Json.1 (): + let Json.102 : {} = Struct {}; + ret Json.102; + +procedure Json.17 (Json.64): + let Json.104 : {Str} = Struct {Json.64}; + let Json.103 : {Str} = CallByName Encode.22 Json.104; + ret Json.103; + +procedure Json.65 (Json.66, Json.105, #Attr.12): + let Json.64 : Str = StructAtIndex 0 #Attr.12; + inc Json.64; + dec #Attr.12; + let Json.114 : I32 = 34i64; + let Json.113 : U8 = CallByName Num.122 Json.114; + let Json.111 : List U8 = CallByName List.4 Json.66 Json.113; + let Json.112 : List U8 = CallByName Str.12 Json.64; + let Json.108 : List U8 = CallByName List.8 Json.111 Json.112; + let Json.110 : I32 = 34i64; + let Json.109 : U8 = CallByName Num.122 Json.110; + let Json.107 : List U8 = CallByName List.4 Json.108 Json.109; + ret Json.107; + +procedure List.4 (#Attr.2, #Attr.3): + let List.141 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.141; + +procedure List.8 (#Attr.2, #Attr.3): + let List.142 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.142; + +procedure Num.122 (#Attr.2): + let Num.272 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.272; + +procedure Str.12 (#Attr.2): + let Str.73 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.73; + +procedure Str.9 (#Attr.2): + let #Attr.3 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; + let Str.69 : Int1 = StructAtIndex 2 #Attr.3; + if Str.69 then + let Str.71 : Str = StructAtIndex 1 #Attr.3; + inc Str.71; + dec #Attr.3; + let Str.70 : [C {U64, U8}, C Str] = Ok Str.71; + ret Str.70; + else + let Str.67 : U8 = StructAtIndex 3 #Attr.3; + let Str.68 : U64 = StructAtIndex 0 #Attr.3; + dec #Attr.3; + let Str.66 : {U64, U8} = Struct {Str.68, Str.67}; + let Str.65 : [C {U64, U8}, C Str] = Err Str.66; + ret Str.65; + +procedure Test.0 (): + let Test.9 : Str = "abc"; + let Test.10 : {} = CallByName Json.1; + let Test.8 : List U8 = CallByName Encode.25 Test.9 Test.10; + let Test.1 : [C {U64, U8}, C Str] = CallByName Str.9 Test.8; + let Test.5 : U8 = 1i64; + let Test.6 : U8 = GetTagId Test.1; + let Test.7 : Int1 = lowlevel Eq Test.5 Test.6; + if Test.7 then + let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1; + inc Test.2; + dec Test.1; + ret Test.2; + else + dec Test.1; + let Test.4 : Str = ""; + ret Test.4; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index b806f19f83..b9d345325b 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1467,6 +1467,24 @@ fn encode_custom_type() { } #[mono_test] +fn encode_derived_string() { + indoc!( + r#" + app "test" + imports [Encode.{ toEncoder }, Json] + provides [main] to "./platform" + + main = + result = Str.fromUtf8 (Encode.toBytes "abc" Json.format) + when result is + Ok s -> s + _ -> "" + "# + ) +} + +#[mono_test] +#[ignore = "TODO"] fn encode_derived_record() { indoc!( r#" @@ -1475,7 +1493,7 @@ fn encode_derived_record() { provides [main] to "./platform" main = - result = Str.fromUtf8 (Encode.toBytes {a: "fieldA", b: "fieldB"} Json.format) + result = Str.fromUtf8 (Encode.toBytes {a: "a"} Json.format) when result is Ok s -> s _ -> "" diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index e2b96c56d7..8e5a637e5c 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -873,7 +873,13 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f FlatType::Apply(name, arguments) => { let slice = subs.get_subs_slice(*arguments); - write!(f, "Apply({:?}, {:?})", name, slice) + write!(f, "Apply({:?} ", name)?; + for var in slice { + let content = subs.get_content_without_compacting(*var); + write!(f, ", <{:?}>{:?}", *var, SubsFmtContent(content, subs))?; + } + write!(f, ")") + // write!(f, "Apply({:?}, {:?})", name, slice) } FlatType::Func(arguments, lambda_set, result) => { let slice = subs.get_subs_slice(*arguments);