diff --git a/crates/compiler/derive/src/lib.rs b/crates/compiler/derive/src/lib.rs index 18a8bfbbb5..511bfc1a10 100644 --- a/crates/compiler/derive/src/lib.rs +++ b/crates/compiler/derive/src/lib.rs @@ -102,38 +102,70 @@ impl DerivedModule { debug_assert!(!self.stolen, "attempting to add to stolen symbols!"); } - // TODO: can we get rid of the clone? - let entry = self.map.entry(key.clone()); + match self.map.get(&key) { + Some(entry) => { + // Might happen that during rollback in specializing the Derived module, we wink + // away the variables associated with a derived impl. In this case, just rebuild + // the impl. + dbg!(entry.0); + if entry.2.iter().all(|(_, var)| self.subs.contains(*var)) { + // rustc won't let us return an immutable reference *and* continue using + // `self.map` immutably below, but this is safe, because we are not returning + // an immutable reference to the entry. + return unsafe { std::mem::transmute(entry) }; + } + dbg!(entry.0); + } + None => {} + } - entry.or_insert_with(|| { - let ident_id = if cfg!(debug_assertions) || cfg!(feature = "debug-derived-symbols") { - let debug_name = key.debug_name(); - debug_assert!( - self.derived_ident_ids.get_id(&debug_name).is_none(), - "duplicate debug name for different derive key" - ); - let ident_id = self.derived_ident_ids.get_or_insert(&debug_name); + let ident_id = if cfg!(debug_assertions) || cfg!(feature = "debug-derived-symbols") { + let debug_name = key.debug_name(); + // debug_assert!( + // self.derived_ident_ids.get_id(&debug_name).is_none(), + // "duplicate debug name for different derive key" + // ); + let ident_id = self.derived_ident_ids.get_or_insert(&debug_name); - // This is expensive, but yields much better symbols when debugging. - // TODO: hide behind debug_flags? - DERIVED_MODULE.register_debug_idents(&self.derived_ident_ids); + // This is expensive, but yields much better symbols when debugging. + // TODO: hide behind debug_flags? + DERIVED_MODULE.register_debug_idents(&self.derived_ident_ids); - ident_id - } else { - self.derived_ident_ids.gen_unique() - }; + ident_id + } else { + // TODO this is WRONG when we're re-instantiating the derived impl + self.derived_ident_ids.gen_unique() + }; - let derived_symbol = Symbol::new(DERIVED_MODULE, ident_id); - let (derived_def, specialization_lsets) = build_derived_body( - &mut self.subs, - &mut self.derived_ident_ids, - exposed_by_module, - derived_symbol, - key.clone(), - ); + let derived_symbol = Symbol::new(DERIVED_MODULE, ident_id); + let (derived_def, specialization_lsets) = build_derived_body( + &mut self.subs, + &mut self.derived_ident_ids, + exposed_by_module, + derived_symbol, + key.clone(), + ); - (derived_symbol, derived_def, specialization_lsets) - }) + let triple = (derived_symbol, derived_def, specialization_lsets); + self.map.remove(&key); + self.map.entry(key).or_insert(triple) + } + + /// TERRIBLE HACK to deal with rollbacks: in specializing the Derived module, we snapshot and + /// rollback Subs every time we specialize a particular procedure. However in specializing a + /// procedure we may have added a new derived impl to the [`DerivedModule`] whose types are + /// then rolled back! Hence, we need to re-initialize the derived impl in such cases. + /// + /// But there must be a better way!! + pub fn refresh_stale_specializations(&mut self, exposed_by_module: &ExposedByModule) { + let all_derived: Vec<_> = self.map.keys().cloned().collect(); + //dbg!(&all_derived); + all_derived + .into_iter() + // get_or_insert will handle re-initializing stale defs + .for_each(|derive_key| { + let _ = self.get_or_insert(exposed_by_module, derive_key); + }); } pub fn iter_all( @@ -190,9 +222,11 @@ impl DerivedModule { self.derived_ident_ids = ident_ids; } + // TODO: just pass and copy the ambient function directly, don't pass the lambda set var. pub fn copy_lambda_set_ambient_function_to_subs( &self, lambda_set_var: Variable, + target_subs_home: ModuleId, target: &mut Subs, target_rank: Rank, ) -> Variable { @@ -203,23 +237,27 @@ impl DerivedModule { let ambient_function_var = self.subs.get_lambda_set(lambda_set_var).ambient_function; - let copied_import = copy_import_to( - &self.subs, - target, - // bookkeep unspecialized lambda sets of var - I think we want this here - true, - ambient_function_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 - // and lower ranks, and never generalize. - // - // However this is a bad coupling and maybe not a good assumption, we should revisit - // this when possible. - Rank::import(), - // target_rank, - ); + if target_subs_home == ModuleId::DERIVED { + ambient_function_var + } else { + let copied_import = copy_import_to( + &self.subs, + target, + // bookkeep unspecialized lambda sets of var - I think we want this here + true, + ambient_function_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 + // and lower ranks, and never generalize. + // + // However this is a bad coupling and maybe not a good assumption, we should revisit + // this when possible. + Rank::import(), + // target_rank, + ); - copied_import.variable + copied_import.variable + } } } diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index fa9402a351..d129053cef 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -5021,7 +5021,26 @@ fn load_derived_partial_procs<'a>( let mut update_mode_ids = UpdateModeIds::new(); let derives_to_add: Vec<_> = { - let derived_module = derived_module.lock().unwrap(); + let mut derived_module = derived_module.lock().unwrap(); + + // TERRIBLE HACK remove me + + // TODO HACK FIXME + dbg!(derived_module + .iter_all() + .map(|(_, (s, _, _))| *s) + .collect::>()); + //derived_module.refresh_stale_specializations(exposed_by_module); + derived_module.get_or_insert( + exposed_by_module, + roc_derive_key::DeriveKey::ToEncoder( + roc_derive_key::encoding::FlatEncodableKey::String, + ), + ); + dbg!(derived_module + .iter_all() + .map(|(_, (s, _, _))| *s) + .collect::>()); derived_module .iter_all() diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index 572c4434a6..0b60915f5f 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -162,7 +162,6 @@ impl<'a> DeclarationToIndex<'a> { } } } - dbg!(&self.elements); unreachable!( "symbol/layout {:?} {:#?} combo must be in DeclarationToIndex", needle_symbol, needle_layout diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 12d7097125..2503874399 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2932,16 +2932,7 @@ 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(_)); @@ -4758,12 +4749,6 @@ 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, @@ -7586,6 +7571,10 @@ fn call_by_name<'a>( hole: &'a Stmt<'a>, ) -> Stmt<'a> { // Register a pending_specialization for this function + // dbg!(roc_types::subs::SubsFmtContent( + // env.subs.get_content_without_compacting(fn_var), + // env.subs + // )); match layout_cache.raw_from_var(env.arena, fn_var, env.subs) { Err(LayoutProblem::UnresolvedTypeVar(var)) => { let msg = format!( @@ -7811,7 +7800,11 @@ fn call_by_name_help<'a>( assigned, hole, ) - } else if env.is_imported_symbol(proc_name.name()) { + } else if env.is_imported_symbol(proc_name.name()) + // TODO HACK FIXME + || (proc_name.name().module_id() == ModuleId::DERIVED + && !procs.partial_procs.contains_key(proc_name.name())) + { add_needed_external(procs, env, original_fn_var, proc_name); debug_assert_ne!(proc_name.name().module_id(), ModuleId::ATTR); diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index d2e0b22d65..f05eae92e4 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -1236,20 +1236,6 @@ impl<'a> Layout<'a> { match content { FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)), 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, .. } => { @@ -2115,13 +2101,6 @@ 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)?; @@ -2139,13 +2118,6 @@ 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/solve.rs b/crates/compiler/solve/src/solve.rs index 83d1cbe53e..ecd5f0a002 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1817,18 +1817,13 @@ impl<'a> SubsProxy<'a> { lambda_set_var_in_derived: Variable, target_rank: Rank, ) -> Variable { - if self.home == ModuleId::DERIVED { - self.subs - .get_lambda_set(lambda_set_var_in_derived) - .ambient_function - } else { - let derived_module = self.derived_module.lock().unwrap(); - derived_module.copy_lambda_set_ambient_function_to_subs( - lambda_set_var_in_derived, - self.subs, - target_rank, - ) - } + let derived_module = self.derived_module.lock().unwrap(); + derived_module.copy_lambda_set_ambient_function_to_subs( + lambda_set_var_in_derived, + self.home, + self.subs, + target_rank, + ) } } diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index 47b52c0b67..cd1237d85e 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -427,9 +427,8 @@ fn encode_derived_string() { } #[test] -#[ignore] #[cfg(any(feature = "gen-llvm"))] -fn encode_derived_record() { +fn encode_derived_record_one_field_string() { assert_evals_to!( indoc!( r#" @@ -444,7 +443,7 @@ fn encode_derived_record() { _ -> "" "# ), - RocStr::from("foo"), + RocStr::from(r#"{"a":"foo",}"#), RocStr ) } diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index b9d345325b..48c60e6d25 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1797,3 +1797,40 @@ fn instantiate_annotated_as_recursive_alias_multiple_polymorphic_expr() { "# ) } + +#[mono_test] +fn encode_derived_record_one_field_string() { + 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 + _ -> "" + "# + ) +} + +#[mono_test] +#[ignore = "TODO make this work (one ULS var is missing)"] +fn encode_derived_tag_one_field_string() { + indoc!( + r#" + app "test" + imports [Encode.{ toEncoder }, Json] + provides [main] to "./platform" + + main = + x : [A Str] + x = A "foo" + result = Str.fromUtf8 (Encode.toBytes x Json.format) + when result is + Ok s -> s + _ -> "" + "# + ) +}