mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Do not discard empty types in specialize_types
Currently, `to_mono_expr` returns `Nothing` when it encounters an empty record and it discards fields that are empty. For simplicity, we decided to do this at a later stage, so this changes it to return a new `MonoExpr::Unit` type instead.
This commit is contained in:
parent
a5bcf55d08
commit
4b28136143
5 changed files with 61 additions and 64 deletions
|
@ -84,7 +84,7 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_mono_expr(&mut self, can_expr: &Expr) -> Option<MonoExpr> {
|
||||
pub fn to_mono_expr(&mut self, can_expr: &Expr) -> MonoExpr {
|
||||
let problems = &mut self.problems;
|
||||
let mono_types = &mut self.mono_types;
|
||||
let mut mono_from_var = |var| {
|
||||
|
@ -103,7 +103,7 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
|||
macro_rules! compiler_bug {
|
||||
($problem:expr) => {{
|
||||
problems.push($problem);
|
||||
Some(MonoExpr::CompilerBug($problem))
|
||||
MonoExpr::CompilerBug($problem)
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -112,13 +112,11 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
|||
match self.subs.get_content_without_compacting(*var) {
|
||||
Content::FlexVar(_) => {
|
||||
// Plain decimal number literals like `4.2` can still have an unbound var.
|
||||
Some(MonoExpr::Number(Number::Dec(*val)))
|
||||
MonoExpr::Number(Number::Dec(*val))
|
||||
}
|
||||
_ => match mono_from_var(*var) {
|
||||
Some(mono_id) => match mono_types.get(mono_id) {
|
||||
MonoType::Primitive(primitive) => {
|
||||
Some(to_frac(*primitive, *val, problems))
|
||||
}
|
||||
MonoType::Primitive(primitive) => to_frac(*primitive, *val, problems),
|
||||
other => {
|
||||
compiler_bug!(Problem::NumSpecializedToWrongType(Some(*other)))
|
||||
}
|
||||
|
@ -133,9 +131,7 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
|||
// Number literals and int literals both specify integer numbers, so to_num() can work on both.
|
||||
match mono_from_var(*var) {
|
||||
Some(mono_id) => match mono_types.get(mono_id) {
|
||||
MonoType::Primitive(primitive) => {
|
||||
Some(to_num(*primitive, *int_value, problems))
|
||||
}
|
||||
MonoType::Primitive(primitive) => to_num(*primitive, *int_value, problems),
|
||||
other => compiler_bug!(Problem::NumSpecializedToWrongType(Some(*other))),
|
||||
},
|
||||
None => compiler_bug!(Problem::NumSpecializedToWrongType(None)),
|
||||
|
@ -147,21 +143,19 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
|||
// or keeping a separate value but storing an IntValue instead of a char), then
|
||||
// even though we verify them differently, we can combine this branch with Num and Int.
|
||||
Some(mono_id) => match mono_types.get(mono_id) {
|
||||
MonoType::Primitive(primitive) => {
|
||||
Some(char_to_int(*primitive, *char, problems))
|
||||
}
|
||||
MonoType::Primitive(primitive) => char_to_int(*primitive, *char, problems),
|
||||
other => compiler_bug!(Problem::CharSpecializedToWrongType(Some(*other))),
|
||||
},
|
||||
None => compiler_bug!(Problem::CharSpecializedToWrongType(None)),
|
||||
},
|
||||
Expr::Str(contents) => Some(MonoExpr::Str(self.string_interns.get_id(
|
||||
Expr::Str(contents) => MonoExpr::Str(self.string_interns.get_id(
|
||||
self.arena,
|
||||
// TODO should be able to remove this alloc_str() once canonical Expr stores an arena-allocated string.
|
||||
self.arena.alloc_str(contents),
|
||||
))),
|
||||
)),
|
||||
Expr::EmptyRecord => {
|
||||
// Empty records are zero-sized and should be discarded.
|
||||
None
|
||||
MonoExpr::Unit
|
||||
}
|
||||
Expr::Record {
|
||||
record_var: _,
|
||||
|
@ -172,10 +166,10 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
|||
// Check for records with 0-1 fields before sorting or reserving a slice of IDs (which might be unnecessary).
|
||||
// We'll check again after discarding zero-sized fields, because we might end up with 0 or 1 fields remaining.
|
||||
if fields.len() <= 1 {
|
||||
return fields
|
||||
.into_iter()
|
||||
.next()
|
||||
.and_then(|(_, field)| self.to_mono_expr(&field.loc_expr.value));
|
||||
return match fields.into_iter().next() {
|
||||
Some((_, field)) => self.to_mono_expr(&field.loc_expr.value),
|
||||
None => MonoExpr::Unit,
|
||||
};
|
||||
}
|
||||
|
||||
// Sort the fields alphabetically by name.
|
||||
|
@ -189,21 +183,18 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
|||
let mut buf: Vec<(MonoExpr, Region)> =
|
||||
Vec::with_capacity_in(fields.len(), self.arena);
|
||||
|
||||
buf.extend(
|
||||
// flat_map these so we discard all the fields that monomorphized to None
|
||||
fields.into_iter().flat_map(|(_name, field)| {
|
||||
self.to_mono_expr(&field.loc_expr.value)
|
||||
.map(|mono_expr| (mono_expr, field.loc_expr.region))
|
||||
}),
|
||||
);
|
||||
buf.extend(fields.into_iter().map(|(_name, field)| {
|
||||
(
|
||||
self.to_mono_expr(&field.loc_expr.value),
|
||||
field.loc_expr.region,
|
||||
)
|
||||
}));
|
||||
|
||||
// If we ended up with exactly 1 field, return it unwrapped.
|
||||
if buf.len() == 1 {
|
||||
return buf.pop().map(|(expr, _region)| expr);
|
||||
}
|
||||
let slice = unsafe {
|
||||
NonEmptySlice::from_slice_unchecked(self.mono_exprs.extend(buf.into_iter()))
|
||||
};
|
||||
|
||||
NonEmptySlice::from_slice(self.mono_exprs.extend(buf.iter().copied()))
|
||||
.map(MonoExpr::Struct)
|
||||
MonoExpr::Struct(slice)
|
||||
}
|
||||
// Expr::Call((fn_var, fn_expr, capture_var, ret_var), args, called_via) => {
|
||||
// let opt_ret_type = mono_from_var(*var);
|
||||
|
|
|
@ -129,14 +129,18 @@ impl MonoExprs {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn extend(
|
||||
&mut self,
|
||||
exprs: impl Iterator<Item = (MonoExpr, Region)> + Clone,
|
||||
) -> Slice<MonoExpr> {
|
||||
pub fn extend(&mut self, exprs: impl Iterator<Item = (MonoExpr, Region)>) -> Slice<MonoExpr> {
|
||||
let start = self.exprs.len();
|
||||
|
||||
self.exprs.extend(exprs.clone().map(|(expr, _region)| expr));
|
||||
self.regions.extend(exprs.map(|(_expr, region)| region));
|
||||
let (size_hint, _) = exprs.size_hint();
|
||||
|
||||
self.exprs.reserve(size_hint);
|
||||
self.regions.reserve(size_hint);
|
||||
|
||||
for (expr, region) in exprs {
|
||||
self.exprs.push(expr);
|
||||
self.regions.push(region);
|
||||
}
|
||||
|
||||
let len = self.exprs.len() - start;
|
||||
|
||||
|
@ -279,6 +283,8 @@ pub enum MonoExpr {
|
|||
recursive: Recursive,
|
||||
},
|
||||
|
||||
Unit,
|
||||
|
||||
/// A record literal or a tuple literal.
|
||||
/// These have already been sorted alphabetically.
|
||||
Struct(NonEmptySlice<MonoExpr>),
|
||||
|
|
|
@ -86,9 +86,8 @@ fn specialize_expr<'a>(
|
|||
assert_eq!(0, home_decls.expressions.len());
|
||||
|
||||
let region = Region::zero();
|
||||
let mono_expr_id = env
|
||||
.to_mono_expr(&main_expr)
|
||||
.map(|mono_expr| mono_exprs.add(mono_expr, region));
|
||||
let mono_expr = env.to_mono_expr(&main_expr);
|
||||
let mono_expr_id = mono_exprs.add(mono_expr, region);
|
||||
|
||||
SpecializedExprOut {
|
||||
mono_expr_id,
|
||||
|
@ -100,13 +99,13 @@ fn specialize_expr<'a>(
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn expect_no_expr(input: impl AsRef<str>) {
|
||||
pub fn expect_unit(input: impl AsRef<str>) {
|
||||
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_expr(id));
|
||||
let actual = out.mono_exprs.get_expr(out.mono_expr_id);
|
||||
|
||||
assert_eq!(None, actual, "This input expr should have specialized to being dicarded as zero-sized, but it didn't: {:?}", input.as_ref());
|
||||
assert_eq!(MonoExpr::Unit, *actual, "This input expr should have specialized to being dicarded as zero-sized, but it didn't: {:?}", input.as_ref());
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
@ -165,6 +164,9 @@ fn dbg_mono_expr_help<'a>(
|
|||
|
||||
write!(buf, "])").unwrap();
|
||||
}
|
||||
MonoExpr::Unit => {
|
||||
write!(buf, "{{}}").unwrap();
|
||||
}
|
||||
// MonoExpr::List { elem_type, elems } => todo!(),
|
||||
// MonoExpr::Lookup(symbol, mono_type_id) => todo!(),
|
||||
// MonoExpr::ParameterizedLookup {
|
||||
|
@ -270,11 +272,8 @@ pub fn expect_mono_expr_custom<T: PartialEq + core::fmt::Debug>(
|
|||
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_expr(mono_expr_id); // Must run first, to populate string interns!
|
||||
let actual_expr = out.mono_exprs.get_expr(out.mono_expr_id); // Must run first, to populate string interns!
|
||||
let actual = to_actual(&arena, &out.mono_exprs, &string_interns, actual_expr);
|
||||
let expected = to_expected(&arena, &out.mono_exprs, &string_interns);
|
||||
|
||||
|
|
|
@ -10,16 +10,16 @@ mod specialize_structs {
|
|||
|
||||
use crate::helpers::expect_mono_expr_str;
|
||||
|
||||
use super::helpers::{expect_mono_expr_with_interns, expect_no_expr};
|
||||
use super::helpers::{expect_mono_expr_with_interns, expect_unit};
|
||||
|
||||
#[test]
|
||||
fn empty_record() {
|
||||
expect_no_expr("{}");
|
||||
expect_unit("{}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_field_with_empty_record() {
|
||||
expect_no_expr("{ discardedField: {} }");
|
||||
expect_unit("{ discardedField: {} }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -31,16 +31,6 @@ mod specialize_structs {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_field_after_dropping_zero_sized() {
|
||||
let string = "foo";
|
||||
let expected =
|
||||
format!("{{ discarded: {{}}, discardedToo: \"{string}\", alsoDiscarded: {{}} }}");
|
||||
expect_mono_expr_with_interns(expected, |arena, interns| {
|
||||
MonoExpr::Str(interns.try_get_id(arena, string).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_fields() {
|
||||
let one = 42;
|
||||
|
@ -51,4 +41,15 @@ mod specialize_structs {
|
|||
format!("Struct([Number(I8({one})), Number(I8({two}))])"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_fields_one_unit() {
|
||||
let one = 42;
|
||||
let two = "{}";
|
||||
|
||||
expect_mono_expr_str(
|
||||
format!("{{ one: {one}, two: {two} }}"),
|
||||
format!("Struct([Number(I8({one})), {{}}])"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use roc_specialize_types::{
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct SpecializedExprOut {
|
||||
pub mono_expr_id: Option<MonoExprId>,
|
||||
pub mono_expr_id: MonoExprId,
|
||||
pub region: Region,
|
||||
pub mono_types: MonoTypes,
|
||||
pub mono_exprs: MonoExprs,
|
||||
|
@ -47,8 +47,8 @@ impl SpecializedExpr {
|
|||
&mut problems,
|
||||
);
|
||||
|
||||
env.to_mono_expr(&solved_out.expr)
|
||||
.map(|mono_expr| mono_exprs.add(mono_expr, Region::zero()))
|
||||
let mono_expr = env.to_mono_expr(&solved_out.expr);
|
||||
mono_exprs.add(mono_expr, Region::zero())
|
||||
};
|
||||
|
||||
SpecializedExprOut {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue