diff --git a/crates/compiler/specialize_types/src/mono_expr.rs b/crates/compiler/specialize_types/src/mono_expr.rs index 80052c8be8..f5e64954b2 100644 --- a/crates/compiler/specialize_types/src/mono_expr.rs +++ b/crates/compiler/specialize_types/src/mono_expr.rs @@ -12,7 +12,7 @@ use roc_collections::Push; use roc_solve::module::Solved; use roc_types::subs::Subs; -pub struct Env<'a, 'c, 'd, 's, 't, P> { +pub struct Env<'a, 'c, 'd, 'i, 's, 't, P> { arena: &'a Bump, subs: &'s mut Subs, types_cache: &'c mut MonoCache, @@ -21,11 +21,11 @@ pub struct Env<'a, 'c, 'd, 's, 't, P> { record_field_ids: RecordFieldIds, tuple_elem_ids: TupleElemIds, debug_info: &'d mut Option, - string_interns: &'a mut Interns<'a>, + string_interns: &'i mut Interns<'a>, problems: P, } -impl<'a, 'c, 'd, 's, 't, P: Push> Env<'a, 'c, 'd, 's, 't, P> { +impl<'a, 'c, 'd, 'i, 's, 't, P: Push> Env<'a, 'c, 'd, 'i, 's, 't, P> { pub fn new( arena: &'a Bump, subs: &'s mut Solved, @@ -34,7 +34,7 @@ impl<'a, 'c, 'd, 's, 't, P: Push> Env<'a, 'c, 'd, 's, 't, P> { mono_exprs: &'t mut MonoExprs, record_field_ids: RecordFieldIds, tuple_elem_ids: TupleElemIds, - string_interns: &'a mut Interns<'a>, + string_interns: &'i mut Interns<'a>, debug_info: &'d mut Option, problems: P, ) -> Self { @@ -88,6 +88,7 @@ impl<'a, 'c, 'd, 's, 't, P: Push> Env<'a, 'c, 'd, 's, 't, P> { } }, Expr::Num(var, _str, int_value, _) | Expr::Int(var, _, _str, int_value, _) => { + // Numbers can specialize match mono_from_var(var) { Some(mono_id) => match mono_types.get(mono_id) { MonoType::Primitive(primitive) => to_num(*primitive, int_value, problems), @@ -115,9 +116,10 @@ impl<'a, 'c, 'd, 's, 't, P: Push> Env<'a, 'c, 'd, 's, 't, P> { return compiler_bug!(Problem::CharSpecializedToWrongType(None)); } }, - Expr::Str(contents) => { - MonoExpr::Str(self.string_interns.get(self.arena.alloc(contents))) - } + Expr::Str(contents) => MonoExpr::Str( + self.string_interns + .get(self.arena, self.arena.alloc(contents)), + ), Expr::EmptyRecord => { // Empty records are zero-sized and should be discarded. return None; diff --git a/crates/compiler/specialize_types/src/mono_module.rs b/crates/compiler/specialize_types/src/mono_module.rs index f1d7214dae..4ddc741204 100644 --- a/crates/compiler/specialize_types/src/mono_module.rs +++ b/crates/compiler/specialize_types/src/mono_module.rs @@ -1,3 +1,4 @@ +use bumpalo::Bump; use roc_solve::module::Solved; use roc_types::subs::Subs; @@ -26,6 +27,7 @@ impl MonoModule { pub struct InternedStrId(u32); /// TODO move this to its own crate +#[derive(Debug)] pub struct Interns<'a> { interned: Vec<&'a str>, } @@ -37,7 +39,7 @@ impl<'a> Interns<'a> { } } - pub fn get(&mut self, string: &'a str) -> InternedStrId { + pub fn get(&mut self, _arena: &'a Bump, string: &'a str) -> InternedStrId { match self .interned .iter() @@ -53,4 +55,15 @@ impl<'a> Interns<'a> { } } } + + pub fn try_get(&self, _arena: &'a Bump, string: &'a str) -> Option { + match self + .interned + .iter() + .position(|&interned| interned == string) + { + Some(index) => Some(InternedStrId(index as u32)), + None => None, + } + } } diff --git a/crates/compiler/specialize_types/src/specialize_type.rs b/crates/compiler/specialize_types/src/specialize_type.rs index 24ea5994b0..3e4eb2dd81 100644 --- a/crates/compiler/specialize_types/src/specialize_type.rs +++ b/crates/compiler/specialize_types/src/specialize_type.rs @@ -48,7 +48,7 @@ pub struct MonoCache { } impl MonoCache { - pub fn from_subs(subs: &Solved) -> Self { + pub fn from_solved_subs(subs: &Solved) -> Self { Self { inner: VecMap::with_capacity(subs.inner().len()), } diff --git a/crates/compiler/specialize_types/tests/test_specialize_expr.rs b/crates/compiler/specialize_types/tests/test_specialize_expr.rs index 040a87b5b2..18c04d9482 100644 --- a/crates/compiler/specialize_types/tests/test_specialize_expr.rs +++ b/crates/compiler/specialize_types/tests/test_specialize_expr.rs @@ -5,31 +5,148 @@ extern crate bumpalo; #[cfg(test)] mod specialize_types { - use roc_specialize_types::{MonoExpr, Number}; - use test_compile::specialize_expr; + use bumpalo::Bump; + use roc_load::LoadedModule; + use roc_solve::FunctionKind; + use roc_specialize_types::{ + DebugInfo, Env, Interns, MonoCache, MonoExpr, MonoExprs, MonoTypes, Number, RecordFieldIds, + TupleElemIds, + }; + use test_compile::{trim_and_deindent, SpecializedExprOut}; + use test_solve_helpers::{format_problems, run_load_and_infer}; + + // HELPERS + + fn specialize_expr<'a>( + arena: &'a Bump, + src: &str, + string_interns: &mut Interns<'a>, + ) -> SpecializedExprOut { + let ( + LoadedModule { + module_id: home, + mut declarations_by_id, + mut can_problems, + mut type_problems, + interns, + mut solved, + mut exposed_to_host, + abilities_store, + .. + }, + src, + ) = run_load_and_infer( + trim_and_deindent(&arena, src), + [], + false, + FunctionKind::LambdaSet, + ) + .unwrap(); + + let mut can_problems = can_problems.remove(&home).unwrap_or_default(); + let type_problems = type_problems.remove(&home).unwrap_or_default(); + + // Disregard UnusedDef problems, because those are unavoidable when + // returning a function from the test expression. + can_problems.retain(|prob| { + !matches!( + prob, + roc_problem::can::Problem::UnusedDef(_, _) + | roc_problem::can::Problem::UnusedBranchDef(..) + ) + }); + + let (can_problems, type_problems) = + format_problems(&src, home, &interns, can_problems, type_problems); + + assert_eq!(can_problems, String::new()); + assert_eq!(type_problems, String::new()); + + exposed_to_host.retain(|s, _| !abilities_store.is_specialization_name(*s)); + + let mut problems = Vec::new(); + let mut debug_info: Option = None; + let mut types_cache = MonoCache::from_solved_subs(&solved); + let mut mono_types = MonoTypes::new(); + let mut mono_exprs = MonoExprs::new(); + + let mut env = Env::new( + &arena, + &mut solved, + &mut types_cache, + &mut mono_types, + &mut mono_exprs, + RecordFieldIds::default(), + TupleElemIds::default(), + string_interns, + &mut debug_info, + &mut problems, + ); + + let mut home_decls = declarations_by_id.remove(&home).unwrap(); + let main_expr = home_decls.expressions.pop().unwrap().value; + + // This should be our only expr + assert_eq!(0, home_decls.expressions.len()); + + let mono_expr_id = env.to_mono_expr(main_expr); + + SpecializedExprOut { + mono_expr_id, + problems, + mono_types, + mono_exprs, + } + } fn expect_no_expr(input: impl AsRef) { - let out = specialize_expr(input.as_ref()); + let arena = Bump::new(); + let mut interns = Interns::new(); + let out = specialize_expr(&arena, input.as_ref(), &mut interns); let actual = out.mono_expr_id.map(|id| out.mono_exprs.get(id)); assert_eq!(None, actual, "This input expr should have specialized to being dicarded as zero-sized, but it didn't: {:?}", input.as_ref()); } fn expect_mono_expr(input: impl AsRef, mono_expr: MonoExpr) { - let out = specialize_expr(input.as_ref()); - let mono_expr_id = out - .mono_expr_id - .expect("This input expr should not have been discarded as zero-sized, but it was discarded: {input:?}"); + expect_mono_expr_with_interns(|_, _| {}, input, |_| mono_expr); + } - assert_eq!(&mono_expr, out.mono_exprs.get(mono_expr_id)); + fn expect_mono_expr_with_interns( + from_interns: impl for<'a> FnOnce(&'a Bump, &Interns<'a>) -> T, + input: impl AsRef, + to_mono_expr: impl FnOnce(T) -> MonoExpr, + ) { + let arena = Bump::new(); + let mut string_interns = Interns::new(); + let out = specialize_expr(&arena, input.as_ref(), &mut string_interns); + let mono_expr_id = out + .mono_expr_id + .expect("This input expr should not have been discarded as zero-sized, but it was discarded: {input:?}"); + + let actual_expr = out.mono_exprs.get(mono_expr_id); // Must run first, to populate string interns! + + let expected_expr = to_mono_expr(from_interns(&arena, &string_interns)); + + assert_eq!(&expected_expr, actual_expr); } #[test] fn empty_record() { - let todo = (); // TODO need to get earlier stage working, specifically constraint + solve, by replicating existing solve tests. expect_no_expr("{}"); } + #[test] + fn string_literal() { + let string = "foo"; + let expected = format!("\"{string}\""); + expect_mono_expr_with_interns( + |arena, interns| interns.try_get(arena, string).unwrap(), + expected, + |id| MonoExpr::Str(id), + ); + } + #[test] fn unbound_num() { let expected = 42; diff --git a/crates/compiler/specialize_types/tests/test_specialize_types.rs b/crates/compiler/specialize_types/tests/test_specialize_types.rs index 192036408c..8a25b6c841 100644 --- a/crates/compiler/specialize_types/tests/test_specialize_types.rs +++ b/crates/compiler/specialize_types/tests/test_specialize_types.rs @@ -51,7 +51,7 @@ mod specialize_types { debug_assert!(exposed_to_host.len() == 1, "{exposed_to_host:?}"); let (_symbol, variable) = exposed_to_host.into_iter().next().unwrap(); - let mut mono_cache = MonoCache::from_subs(&solved); + let mut mono_cache = MonoCache::from_solved_subs(&solved); let mut mono_types = MonoTypes::new(); let debug_info = DebugInfo::new(); let mut record_field_ids = RecordFieldIds::new(); diff --git a/crates/test_compile/src/help_specialize.rs b/crates/test_compile/src/help_specialize.rs index 812174e996..ff83266b90 100644 --- a/crates/test_compile/src/help_specialize.rs +++ b/crates/test_compile/src/help_specialize.rs @@ -19,29 +19,34 @@ pub struct SpecializedExpr { } impl SpecializedExpr { - pub fn specialize_expr<'a>(&'a self, input: &'a str) -> SpecializedExprOut { + pub fn specialize_expr<'a>( + &'a self, + input: &'a str, + string_interns: &'a mut Interns<'a>, + ) -> SpecializedExprOut { let mut solved_out = self.solved_expr.solve_expr(input); let mut problems = Vec::new(); let mut debug_info: Option = None; - let mut types_cache = MonoCache::from_subs(&solved_out.subs); + let mut types_cache = MonoCache::from_solved_subs(&solved_out.subs); let mut mono_types = MonoTypes::new(); let mut mono_exprs = MonoExprs::new(); - let mut string_interns = Interns::new(); - let mut env = Env::new( - self.solved_expr.arena(), - &mut solved_out.subs, - &mut types_cache, - &mut mono_types, - &mut mono_exprs, - RecordFieldIds::default(), - TupleElemIds::default(), - &mut string_interns, - &mut debug_info, - &mut problems, - ); + let mono_expr_id = { + let mut env = Env::new( + self.solved_expr.arena(), + &mut solved_out.subs, + &mut types_cache, + &mut mono_types, + &mut mono_exprs, + RecordFieldIds::default(), + TupleElemIds::default(), + string_interns, + &mut debug_info, + &mut problems, + ); - let mono_expr_id = env.to_mono_expr(solved_out.expr); + env.to_mono_expr(solved_out.expr) + }; SpecializedExprOut { mono_expr_id, diff --git a/crates/test_compile/src/lib.rs b/crates/test_compile/src/lib.rs index 16ec9d17ae..69e28ce482 100644 --- a/crates/test_compile/src/lib.rs +++ b/crates/test_compile/src/lib.rs @@ -5,6 +5,7 @@ mod help_parse; mod help_solve; mod help_specialize; +pub use deindent::trim_and_deindent; pub use help_can::{CanExpr, CanExprOut}; pub use help_parse::ParseExpr; pub use help_solve::{SolvedExpr, SolvedExprOut}; @@ -17,7 +18,3 @@ pub fn can_expr<'a>(input: &'a str) -> CanExprOut { pub fn solve_expr<'a>(input: &'a str) -> SolvedExprOut { SolvedExpr::default().solve_expr(input) } - -pub fn specialize_expr<'a>(input: &'a str) -> SpecializedExprOut { - SpecializedExpr::default().specialize_expr(input) -}