From 074161733e8e02735c28005ecd722b7a374f875a Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 13 Nov 2024 21:19:16 -0500 Subject: [PATCH] wip --- .../specialize_types/src/mono_type.rs | 15 +- .../specialize_types/src/specialize_type.rs | 45 ++++- .../specialize_types/tests/helpers/mod.rs | 126 ++++++++++++ .../tests/specialize_primitives.rs | 181 ++++++++++++++++++ .../tests/test_specialize_expr.rs | 158 --------------- 5 files changed, 359 insertions(+), 166 deletions(-) create mode 100644 crates/compiler/specialize_types/tests/helpers/mod.rs create mode 100644 crates/compiler/specialize_types/tests/specialize_primitives.rs delete mode 100644 crates/compiler/specialize_types/tests/test_specialize_expr.rs diff --git a/crates/compiler/specialize_types/src/mono_type.rs b/crates/compiler/specialize_types/src/mono_type.rs index 31725d231c..11101d662b 100644 --- a/crates/compiler/specialize_types/src/mono_type.rs +++ b/crates/compiler/specialize_types/src/mono_type.rs @@ -106,8 +106,7 @@ impl MonoTypes { } } pub fn get(&self, id: MonoTypeId) -> &MonoType { - todo!("builtins are stored inline"); - // Overall strategy: + // Future strategy: // - Look at the three high bits to figure out which of the 8 MonoTypes we're dealing with // - The non-parameterized builtins have 000 as their high bits, and the whole MonoTypeId can be cast to a Primitive. // - The parameterized builtins don't need to store a length, just an index. We store that index inline. @@ -116,6 +115,18 @@ impl MonoTypes { // - This means we use 2 bits for discriminant and another 2 bits for which parameterized type it is // - This means we get 29-bit indices, so a maximum of ~500M MonoTypes per module. Should be plenty. // - In the future, we can promote common collection types (e.g. List Str, List U8) to Primitives. + + let opt = self.entries.get(id.inner.index()); + + #[cfg(debug_assertions)] + { + opt.expect("A MonoTypeId corresponded to an index that wasn't in MonoTypes. This should never happen!") + } + + #[cfg(not(debug_assertions))] + unsafe { + opt.unwrap_unchecked() + } } pub(crate) fn add_primitive(&mut self, primitive: Primitive) -> MonoTypeId { diff --git a/crates/compiler/specialize_types/src/specialize_type.rs b/crates/compiler/specialize_types/src/specialize_type.rs index b32d404eaa..50fbf2d0de 100644 --- a/crates/compiler/specialize_types/src/specialize_type.rs +++ b/crates/compiler/specialize_types/src/specialize_type.rs @@ -244,17 +244,30 @@ fn lower_var>( | Content::RecursionVar { .. } => Content::Structure(FlatType::EmptyTagUnion), Content::LambdaSet(lambda_set) => Content::LambdaSet(lambda_set), Content::ErasedLambda => Content::ErasedLambda, - Content::Alias(symbol, args, real, kind) => { - let todo = (); // TODO we should unwrap this, but doing that probably means changing this from root_var to other stuff. - let new_real = lower_var(cache, subs, problems, *real); - Content::Alias(*symbol, *args, new_real, *kind) - } Content::Error => Content::Error, */ }, - _ => { + Content::RangedNumber(range) => { + use roc_types::num::NumericRange::*; + + match range { + IntAtLeastSigned(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width), + IntAtLeastEitherSign(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width), + NumAtLeastSigned(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width), + NumAtLeastEitherSign(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width), + } + } + Content::Alias(_symbol, args, real, kind) => { + let mono_id = lower_var(env, subs, real)?; + // let mono_args = args + // .into_iter() + // .flat_map(|arg| lower_var(env, subs, subs[arg])); + todo!(); } + content => { + todo!("specialize this Content: {content:?}"); + } }; // This var is now known to be monomorphic, so we don't repeat this work again later. @@ -264,6 +277,26 @@ fn lower_var>( Some(mono_id) } +fn int_lit_width_to_mono_type_id(int_lit_width: roc_can::num::IntLitWidth) -> MonoTypeId { + use roc_can::num::IntLitWidth; + + match int_lit_width { + IntLitWidth::U8 => MonoTypeId::U8, + IntLitWidth::U16 => MonoTypeId::U16, + IntLitWidth::U32 => MonoTypeId::U32, + IntLitWidth::U64 => MonoTypeId::U64, + IntLitWidth::U128 => MonoTypeId::U128, + IntLitWidth::I8 => MonoTypeId::I8, + IntLitWidth::I16 => MonoTypeId::I16, + IntLitWidth::I32 => MonoTypeId::I32, + IntLitWidth::I64 => MonoTypeId::I64, + IntLitWidth::I128 => MonoTypeId::I128, + IntLitWidth::F32 => MonoTypeId::F32, + IntLitWidth::F64 => MonoTypeId::F64, + IntLitWidth::Dec => MonoTypeId::DEC, + } +} + fn num_args_to_mono_id( args: SubsSlice, subs: &Subs, diff --git a/crates/compiler/specialize_types/tests/helpers/mod.rs b/crates/compiler/specialize_types/tests/helpers/mod.rs new file mode 100644 index 0000000000..f9d49c6bdd --- /dev/null +++ b/crates/compiler/specialize_types/tests/helpers/mod.rs @@ -0,0 +1,126 @@ +use bumpalo::Bump; +use roc_load::LoadedModule; +use roc_solve::FunctionKind; +use roc_specialize_types::{ + DebugInfo, Env, Interns, MonoCache, MonoExpr, MonoExprs, MonoTypes, RecordFieldIds, + TupleElemIds, +}; +use test_compile::{trim_and_deindent, SpecializedExprOut}; +use test_solve_helpers::{format_problems, run_load_and_infer}; + +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, + } +} + +#[track_caller] +pub fn expect_no_expr(input: impl AsRef) { + 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()); +} + +#[track_caller] +pub fn expect_mono_expr(input: impl AsRef, mono_expr: MonoExpr) { + expect_mono_expr_with_interns(|_, _| {}, input, |_| mono_expr); +} + +#[track_caller] +pub 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); +} diff --git a/crates/compiler/specialize_types/tests/specialize_primitives.rs b/crates/compiler/specialize_types/tests/specialize_primitives.rs new file mode 100644 index 0000000000..6366e20c31 --- /dev/null +++ b/crates/compiler/specialize_types/tests/specialize_primitives.rs @@ -0,0 +1,181 @@ +#[macro_use] +extern crate pretty_assertions; + +extern crate bumpalo; + +mod helpers; + +#[cfg(test)] +mod specialize_primitives { + use roc_specialize_types::{MonoExpr, Number}; + + use super::helpers::{expect_mono_expr, expect_mono_expr_with_interns, expect_no_expr}; + + #[test] + fn empty_record() { + 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_zero() { + let expected = 0; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I8(expected)), + ); + } + + #[test] + fn unbound_negative_i8() { + let expected = -42; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I8(expected)), + ); + } + + #[test] + fn unbound_positive_i8() { + let expected = 42; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I8(expected)), + ); + } + + #[test] + fn unbound_u8() { + let expected = 128; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U8(expected)), + ); + } + + #[test] + fn unbound_negative_i16() { + let expected = -5_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I16(expected)), + ); + } + + #[test] + fn unbound_positive_i16() { + let expected = 5_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I16(expected)), + ); + } + + #[test] + fn unbound_u16() { + let expected = 65_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U16(expected)), + ); + } + #[test] + fn unbound_negative_i32() { + let expected = -2_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I32(expected)), + ); + } + + #[test] + fn unbound_positive_i32() { + let expected = 2_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I32(expected)), + ); + } + + #[test] + fn unbound_u32() { + let expected = 4_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U32(expected)), + ); + } + + #[test] + fn unbound_negative_i64() { + let expected = -9_000_000_000_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I64(expected)), + ); + } + + #[test] + fn unbound_positive_i64() { + let expected = 9_000_000_000_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I64(expected)), + ); + } + + #[test] + fn unbound_u64() { + let expected = 18_000_000_000_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U64(expected)), + ); + } + + #[test] + fn unbound_negative_i128() { + let expected = -170_141_183_460_469_231_731_687_303_715_884_105_728; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I128(expected)), + ); + } + + #[test] + fn unbound_positive_i128() { + let expected = 170_141_183_460_469_231_731_687_303_715_884_105_727; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I128(expected)), + ); + } + + #[test] + fn unbound_u128() { + let expected = 340_282_366_920_938_463_463_374_607_431_768_211_455; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U128(expected)), + ); + } + + #[test] + fn unbound_f64() { + let expected = 3.14159265359; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::Dec(expected)), + ); + } +} diff --git a/crates/compiler/specialize_types/tests/test_specialize_expr.rs b/crates/compiler/specialize_types/tests/test_specialize_expr.rs deleted file mode 100644 index 18c04d9482..0000000000 --- a/crates/compiler/specialize_types/tests/test_specialize_expr.rs +++ /dev/null @@ -1,158 +0,0 @@ -#[macro_use] -extern crate pretty_assertions; - -extern crate bumpalo; - -#[cfg(test)] -mod specialize_types { - 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 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) { - expect_mono_expr_with_interns(|_, _| {}, input, |_| mono_expr); - } - - 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() { - 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; - expect_mono_expr( - format!("{expected}"), - MonoExpr::Number(Number::I64(expected)), - ); - } -}