From f4f9ae7a5dfe038b90b388414c6caffe6e49d970 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 10:00:51 -0500 Subject: [PATCH 01/24] Implement obligation checking of tuples for all abilities --- crates/compiler/solve/src/ability.rs | 36 +++++ crates/reporting/tests/test_reporting.rs | 185 +++++++++++++++++++++++ 2 files changed, 221 insertions(+) diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index 3419adc5b0..b2e858ac3a 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -861,6 +861,15 @@ impl DerivableVisitor for DeriveEncoding { Ok(Descend(true)) } + #[inline(always)] + fn visit_tuple( + _subs: &Subs, + _var: Variable, + _elems: TupleElems, + ) -> Result { + Ok(Descend(true)) + } + #[inline(always)] fn visit_tag_union(_var: Variable) -> Result { Ok(Descend(true)) @@ -967,6 +976,15 @@ impl DerivableVisitor for DeriveDecoding { Ok(Descend(true)) } + #[inline(always)] + fn visit_tuple( + _subs: &Subs, + _var: Variable, + _elems: TupleElems, + ) -> Result { + Ok(Descend(true)) + } + #[inline(always)] fn visit_tag_union(_var: Variable) -> Result { Ok(Descend(true)) @@ -1073,6 +1091,15 @@ impl DerivableVisitor for DeriveHash { Ok(Descend(true)) } + #[inline(always)] + fn visit_tuple( + _subs: &Subs, + _var: Variable, + _elems: TupleElems, + ) -> Result { + Ok(Descend(true)) + } + #[inline(always)] fn visit_tag_union(_var: Variable) -> Result { Ok(Descend(true)) @@ -1178,6 +1205,15 @@ impl DerivableVisitor for DeriveEq { Ok(Descend(true)) } + #[inline(always)] + fn visit_tuple( + _subs: &Subs, + _var: Variable, + _elems: TupleElems, + ) -> Result { + Ok(Descend(true)) + } + #[inline(always)] fn visit_tag_union(_var: Variable) -> Result { Ok(Descend(true)) diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index a50debc07c..eb4948828e 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -11177,6 +11177,55 @@ I recommend using camelCase. It's the standard style in Roc code! "### ); + test_no_problem!( + derive_hash_for_tuple, + indoc!( + r#" + app "test" provides [main] to "./platform" + + foo : a -> {} | a has Hash + + main = foo ("", 1) + "# + ) + ); + + test_report!( + cannot_hash_tuple_with_non_hash_element, + indoc!( + r#" + app "test" provides [main] to "./platform" + + foo : a -> {} | a has Hash + + main = foo ("", \{} -> {}) + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression has a type that does not implement the abilities it's expected to: + + 5│ main = foo ("", \{} -> {}) + ^^^^^^^^^^^^^^^ + + I can't generate an implementation of the `Hash` ability for + + ( + Str, + {}a -> {}, + )a + + In particular, an implementation for + + {}a -> {} + + cannot be generated. + + Note: `Hash` cannot be generated for functions. + "### + ); + test_report!( shift_by_negative, indoc!( @@ -11559,6 +11608,58 @@ I recommend using camelCase. It's the standard style in Roc code! "### ); + test_no_problem!( + derive_eq_for_tuple, + indoc!( + r#" + app "test" provides [main] to "./platform" + + foo : a -> {} | a has Eq + + main = foo ("", 1) + "# + ) + ); + + test_report!( + cannot_eq_tuple_with_non_eq_element, + indoc!( + r#" + app "test" provides [main] to "./platform" + + foo : a -> {} | a has Eq + + main = foo ("", 1.0f64) + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression has a type that does not implement the abilities it's expected to: + + 5│ main = foo ("", 1.0f64) + ^^^^^^^^^^^^ + + I can't generate an implementation of the `Eq` ability for + + ( + Str, + F64, + )a + + In particular, an implementation for + + F64 + + cannot be generated. + + Note: I can't derive `Bool.isEq` for floating-point types. That's + because Roc's floating-point numbers cannot be compared for total + equality - in Roc, `NaN` is never comparable to `NaN`. If a type + doesn't support total equality, it cannot support the `Eq` ability! + "### + ); + test_report!( cannot_import_structural_eq_not_eq, indoc!( @@ -13127,6 +13228,90 @@ I recommend using camelCase. It's the standard style in Roc code! "### ); + test_no_problem!( + derive_decoding_for_tuple, + indoc!( + r#" + app "test" imports [Decode.{decoder}] provides [main] to "./platform" + + main = + myDecoder : Decoder (U32, Str) fmt | fmt has DecoderFormatting + myDecoder = decoder + + myDecoder + "# + ) + ); + + test_report!( + cannot_decode_tuple_with_non_decode_element, + indoc!( + r#" + app "test" imports [Decode.{decoder}] provides [main] to "./platform" + + main = + myDecoder : Decoder (U32, {} -> {}) fmt | fmt has DecoderFormatting + myDecoder = decoder + + myDecoder + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression has a type that does not implement the abilities it's expected to: + + 5│ myDecoder = decoder + ^^^^^^^ + + I can't generate an implementation of the `Decoding` ability for + + U32, {} -> {} + + Note: `Decoding` cannot be generated for functions. + "### + ); + + test_no_problem!( + derive_encoding_for_tuple, + indoc!( + r#" + app "test" imports [] provides [main] to "./platform" + + x : (U32, Str) + + main = Encode.toEncoder x + "# + ) + ); + + test_report!( + cannot_encode_tuple_with_non_encode_element, + indoc!( + r#" + app "test" imports [] provides [main] to "./platform" + + x : (U32, {} -> {}) + + main = Encode.toEncoder x + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression has a type that does not implement the abilities it's expected to: + + 5│ main = Encode.toEncoder x + ^ + + I can't generate an implementation of the `Encoding` ability for + + U32, {} -> {} + + Note: `Encoding` cannot be generated for functions. + "### + ); + test_report!( exhaustiveness_check_function_or_tag_union_issue_4994, indoc!( From e6cac71ca531c67ea01d2a6e8b120079d45c8f9a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 10:09:57 -0500 Subject: [PATCH 02/24] Add support for hash key of tuples --- crates/compiler/derive/src/hash.rs | 1 + crates/compiler/derive_key/src/hash.rs | 14 +++++++++++--- crates/compiler/derive_key/src/util.rs | 4 ++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/compiler/derive/src/hash.rs b/crates/compiler/derive/src/hash.rs index 6a786dfb66..cff13cc931 100644 --- a/crates/compiler/derive/src/hash.rs +++ b/crates/compiler/derive/src/hash.rs @@ -30,6 +30,7 @@ use crate::{synth_var, util::Env, DerivedBody}; pub(crate) fn derive_hash(env: &mut Env<'_>, key: FlatHashKey, def_symbol: Symbol) -> DerivedBody { let (body_type, body) = match key { FlatHashKey::Record(fields) => hash_record(env, def_symbol, fields), + FlatHashKey::Tuple(_fields) => todo!(), FlatHashKey::TagUnion(tags) => { if tags.len() == 1 { hash_newtype_tag_union(env, def_symbol, tags.into_iter().next().unwrap()) diff --git a/crates/compiler/derive_key/src/hash.rs b/crates/compiler/derive_key/src/hash.rs index 00e765974b..c890409fe9 100644 --- a/crates/compiler/derive_key/src/hash.rs +++ b/crates/compiler/derive_key/src/hash.rs @@ -5,7 +5,7 @@ use roc_module::{ use roc_types::subs::{Content, FlatType, GetSubsSlice, Subs, Variable}; use crate::{ - util::{check_derivable_ext_var, debug_name_record, debug_name_tag}, + util::{check_derivable_ext_var, debug_name_record, debug_name_tag, debug_name_tuple}, DeriveError, }; @@ -21,6 +21,7 @@ pub enum FlatHash { pub enum FlatHashKey { // Unfortunate that we must allocate here, c'est la vie Record(Vec), + Tuple(u32), TagUnion(Vec<(TagName, u16)>), } @@ -28,6 +29,7 @@ impl FlatHashKey { pub(crate) fn debug_name(&self) -> String { match self { FlatHashKey::Record(fields) => debug_name_record(fields), + FlatHashKey::Tuple(arity) => debug_name_tuple(*arity), FlatHashKey::TagUnion(tags) => debug_name_tag(tags), } } @@ -65,8 +67,14 @@ impl FlatHash { Ok(Key(FlatHashKey::Record(field_names))) } - FlatType::Tuple(_elems, _ext) => { - todo!(); + FlatType::Tuple(elems, ext) => { + let (elems_iter, ext) = elems.sorted_iterator_and_ext(subs, ext); + + check_derivable_ext_var(subs, ext, |ext| { + matches!(ext, Content::Structure(FlatType::EmptyTuple)) + })?; + + Ok(Key(FlatHashKey::Tuple(elems_iter.count() as _))) } FlatType::TagUnion(tags, ext) | FlatType::RecursiveTagUnion(_, tags, ext) => { // The recursion var doesn't matter, because the derived implementation will only diff --git a/crates/compiler/derive_key/src/util.rs b/crates/compiler/derive_key/src/util.rs index a834dcb2d0..8fbab82957 100644 --- a/crates/compiler/derive_key/src/util.rs +++ b/crates/compiler/derive_key/src/util.rs @@ -43,6 +43,10 @@ pub(crate) fn debug_name_record(fields: &[Lowercase]) -> String { str } +pub(crate) fn debug_name_tuple(arity: u32) -> String { + format!("(arity:{arity})") +} + pub(crate) fn debug_name_tag(tags: &[(TagName, u16)]) -> String { let mut str = String::from('['); tags.iter().enumerate().for_each(|(i, (tag, arity))| { From d9c70101047a1d6b1d63e2bd9289416321663529 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 10:19:14 -0500 Subject: [PATCH 03/24] Add derive-key tests for derived hash --- crates/compiler/test_derive/src/hash.rs | 8 ++++++++ crates/compiler/test_derive/src/util.rs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/crates/compiler/test_derive/src/hash.rs b/crates/compiler/test_derive/src/hash.rs index ee46d23257..22ba64f732 100644 --- a/crates/compiler/test_derive/src/hash.rs +++ b/crates/compiler/test_derive/src/hash.rs @@ -28,6 +28,11 @@ test_key_eq! { explicit_empty_record_and_implicit_empty_record: v!(EMPTY_RECORD), v!({}) + same_tuple: + v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16),)) + same_tuple_fields_diff_types: + v!((v!(U8), v!(U16),)), v!((v!(U32), v!(U64),)) + same_tag_union: v!([ A v!(U8) v!(STR), B v!(STR) ]), v!([ A v!(U8) v!(STR), B v!(STR) ]) same_tag_union_tags_diff_types: @@ -51,6 +56,9 @@ test_key_neq! { record_empty_vs_nonempty: v!(EMPTY_RECORD), v!({ a: v!(U8), }) + different_tuple_arities: + v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16), v!(U32),)) + different_tag_union_tags: v!([ A v!(U8) ]), v!([ B v!(U8) ]) tag_union_empty_vs_nonempty: diff --git a/crates/compiler/test_derive/src/util.rs b/crates/compiler/test_derive/src/util.rs index 9d5c9daaac..566d433c9c 100644 --- a/crates/compiler/test_derive/src/util.rs +++ b/crates/compiler/test_derive/src/util.rs @@ -89,6 +89,22 @@ macro_rules! v { roc_derive::synth_var(subs, Content::Structure(FlatType::Record(fields, ext))) } }}; + (( $($make_v:expr,)* )$( $($ext:tt)+ )?) => {{ + #[allow(unused)] + use roc_types::subs::{Subs, RecordFields, Content, FlatType, Variable, TupleElems}; + |subs: &mut Subs| { + let elems = [ + $($make_v(subs),)* + ].into_iter().enumerate(); + let elems = TupleElems::insert_into_subs(subs, elems); + + #[allow(unused_mut, unused)] + let mut ext = Variable::EMPTY_TUPLE; + $( ext = $crate::v!($($ext)+)(subs); )? + + roc_derive::synth_var(subs, Content::Structure(FlatType::Tuple(elems, ext))) + } + }}; ([ $($tag:ident $($payload:expr)*),* ] as $rec_var:ident) => {{ use roc_types::subs::{Subs, SubsIndex, Variable, Content, FlatType, TagExt, UnionTags}; use roc_module::ident::TagName; From 66fb2f476ef903922b729b2199c9c97fd96fc94d Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 10:27:49 -0500 Subject: [PATCH 04/24] Pretty-print tuple access --- crates/compiler/can/src/debug/pretty_print.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index 361cdf0b70..64a8fe3137 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -330,7 +330,11 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, } => expr(c, AppArg, f, &loc_expr.value) .append(f.text(format!(".{}", field.as_str()))) .group(), - TupleAccess { .. } => todo!(), + TupleAccess { + loc_expr, index, .. + } => expr(c, AppArg, f, &loc_expr.value) + .append(f.text(format!(".{index}"))) + .group(), OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => { f.text(format!("@{}", opaque_name.as_str(c.interns))) } From 8f7b6aaeaa459ccdbd945ad658e4fee71fce9f2a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 10:28:13 -0500 Subject: [PATCH 05/24] Implement hash derivation for tuple --- crates/compiler/derive/src/hash.rs | 76 ++++++++++++++++++++++++- crates/compiler/test_derive/src/hash.rs | 17 ++++++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/crates/compiler/derive/src/hash.rs b/crates/compiler/derive/src/hash.rs index cff13cc931..1f597f2dda 100644 --- a/crates/compiler/derive/src/hash.rs +++ b/crates/compiler/derive/src/hash.rs @@ -19,8 +19,8 @@ use roc_types::{ num::int_lit_width_to_variable, subs::{ Content, ExhaustiveMark, FlatType, GetSubsSlice, LambdaSet, OptVariable, RecordFields, - RedundantMark, Subs, SubsIndex, SubsSlice, TagExt, UnionLambdas, UnionTags, Variable, - VariableSubsSlice, + RedundantMark, Subs, SubsIndex, SubsSlice, TagExt, TupleElems, UnionLambdas, UnionTags, + Variable, VariableSubsSlice, }, types::RecordField, }; @@ -30,7 +30,7 @@ use crate::{synth_var, util::Env, DerivedBody}; pub(crate) fn derive_hash(env: &mut Env<'_>, key: FlatHashKey, def_symbol: Symbol) -> DerivedBody { let (body_type, body) = match key { FlatHashKey::Record(fields) => hash_record(env, def_symbol, fields), - FlatHashKey::Tuple(_fields) => todo!(), + FlatHashKey::Tuple(arity) => hash_tuple(env, def_symbol, arity), FlatHashKey::TagUnion(tags) => { if tags.len() == 1 { hash_newtype_tag_union(env, def_symbol, tags.into_iter().next().unwrap()) @@ -123,6 +123,76 @@ fn hash_record(env: &mut Env<'_>, fn_name: Symbol, fields: Vec) -> (V ) } +fn hash_tuple(env: &mut Env<'_>, fn_name: Symbol, arity: u32) -> (Variable, Expr) { + // Suppose tup = (v1, ..., vn). + // Build a generalized type t_tup = (t1, ..., tn), with fresh t1, ..., tn, + // so that we can re-use the derived impl for many tuples of the same arity. + let (tuple_var, tuple_elems) = { + // TODO: avoid an allocation here by pre-allocating the indices and variables `TupleElems` + // will be instantiated with. + let flex_elems: Vec<_> = (0..arity) + .into_iter() + .map(|i| (i as usize, env.subs.fresh_unnamed_flex_var())) + .collect(); + let elems = TupleElems::insert_into_subs(env.subs, flex_elems); + let tuple_var = synth_var( + env.subs, + Content::Structure(FlatType::Tuple(elems, Variable::EMPTY_TUPLE)), + ); + + (tuple_var, elems) + }; + + // Now, a hasher for this tuple is + // + // hash_tup : hasher, (t1, ..., tn) -> hasher | hasher has Hasher + // hash_tup = \hasher, tup -> + // Hash.hash ( + // Hash.hash + // ... + // (Hash.hash hasher tup.0) + // ... + // tup.n1) + // tup.n + // + // So, just a build a fold travelling up the elements. + let tup_sym = env.new_symbol("tup"); + + let hasher_sym = env.new_symbol("hasher"); + let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Subs::AB_HASHER)); + + let (body_var, body) = tuple_elems.iter_all().fold( + (hasher_var, Expr::Var(hasher_sym, hasher_var)), + |total_hasher, (elem_idx, elem_var)| { + let index = env.subs[elem_idx]; + let elem_var = env.subs[elem_var]; + + let elem_access = Expr::TupleAccess { + tuple_var, + elem_var, + ext_var: env.subs.fresh_unnamed_flex_var(), + loc_expr: Box::new(Loc::at_zero(Expr::Var( + tup_sym, + env.subs.fresh_unnamed_flex_var(), + ))), + index, + }; + + call_hash_hash(env, total_hasher, (elem_var, elem_access)) + }, + ); + + // Finally, build the closure + // \hasher, rcd -> body + build_outer_derived_closure( + env, + fn_name, + (hasher_var, hasher_sym), + (tuple_var, Pattern::Identifier(tup_sym)), + (body_var, body), + ) +} + /// Build a `hash` implementation for a non-singleton tag union. fn hash_tag_union( env: &mut Env<'_>, diff --git a/crates/compiler/test_derive/src/hash.rs b/crates/compiler/test_derive/src/hash.rs index 22ba64f732..4847f1044e 100644 --- a/crates/compiler/test_derive/src/hash.rs +++ b/crates/compiler/test_derive/src/hash.rs @@ -209,6 +209,23 @@ fn two_field_record() { }) } +#[test] +fn two_element_tuple() { + derive_test(Hash, v!((v!(U8), v!(STR),)), |golden| { + assert_snapshot!(golden, @r###" + # derived for ( U8, Str )* + # hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher + # hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher + # Specialization lambda sets: + # @<1>: [[hash_(arity:2)(0)]] + #Derived.hash_(arity:2) = + \#Derived.hasher, #Derived.tup -> + hash (hash #Derived.hasher #Derived.tup.0) #Derived.tup.1 + "### + ) + }) +} + #[test] fn tag_one_label_no_payloads() { derive_test(Hash, v!([A]), |golden| { From c32bc5f1523bedb78242cd2b50e2a41d02a2f31e Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 10:52:31 -0500 Subject: [PATCH 06/24] Offset tuple element indices correctly when importing --- crates/compiler/types/src/subs.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index d59df274da..df259af3cf 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -4459,6 +4459,7 @@ pub struct StorageSubs { struct StorageSubsOffsets { utable: u32, variables: u32, + tuple_elem_indices: u32, tag_names: u32, symbol_names: u32, field_names: u32, @@ -4542,6 +4543,7 @@ impl StorageSubs { let self_offsets = StorageSubsOffsets { utable: self.subs.utable.len() as u32, variables: self.subs.variables.len() as u32, + tuple_elem_indices: self.subs.tuple_elem_indices.len() as u32, tag_names: self.subs.tag_names.len() as u32, symbol_names: self.subs.symbol_names.len() as u32, field_names: self.subs.field_names.len() as u32, @@ -4553,6 +4555,7 @@ impl StorageSubs { let offsets = StorageSubsOffsets { utable: (target.utable.len() - Variable::NUM_RESERVED_VARS) as u32, variables: target.variables.len() as u32, + tuple_elem_indices: target.tuple_elem_indices.len() as u32, tag_names: target.tag_names.len() as u32, symbol_names: target.symbol_names.len() as u32, field_names: target.field_names.len() as u32, @@ -4593,6 +4596,10 @@ impl StorageSubs { .map(|v| Self::offset_variable(&offsets, *v)), ); + target + .tuple_elem_indices + .extend(self.subs.tuple_elem_indices); + target.variable_slices.extend( self.subs .variable_slices @@ -4613,6 +4620,11 @@ impl StorageSubs { (self_offsets.utable + offsets.utable) as usize ); + debug_assert_eq!( + target.tuple_elem_indices.len(), + (self_offsets.tuple_elem_indices + offsets.tuple_elem_indices) as usize + ); + debug_assert_eq!( target.tag_names.len(), (self_offsets.tag_names + offsets.tag_names) as usize @@ -4756,6 +4768,7 @@ impl StorageSubs { } fn offset_tuple_elems(offsets: &StorageSubsOffsets, mut tuple_elems: TupleElems) -> TupleElems { + tuple_elems.elem_index_start += offsets.tuple_elem_indices; tuple_elems.variables_start += offsets.variables; tuple_elems From 143f39d273c04e0186250aa465a37fdc2b385e72 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 10:53:14 -0500 Subject: [PATCH 07/24] Gen tests for derivation of tuple hash --- crates/compiler/test_gen/src/gen_abilities.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index 498293824e..8c6362ab1c 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -1493,6 +1493,35 @@ mod hash { ) } + #[test] + fn tuple_of_u8_and_str() { + assert_evals_to!( + &build_test(r#"(15u8, "bc")"#), + RocList::from_slice(&[15, 98, 99]), + RocList + ) + } + + #[test] + fn tuple_of_tuples() { + assert_evals_to!( + &build_test(r#"( (15u8, "bc"), (23u8, "ef") )"#), + RocList::from_slice(&[15, 98, 99, 23, 101, 102]), + RocList + ) + } + + #[test] + fn tuple_of_list_of_tuples() { + assert_evals_to!( + &build_test( + r#"( [ ( 15u8, 32u8 ), ( 23u8, 41u8 ) ], [ (45u8, 63u8), (58u8, 73u8) ] )"# + ), + RocList::from_slice(&[15, 32, 23, 41, 45, 63, 58, 73]), + RocList + ) + } + #[test] fn hash_singleton_union() { assert_evals_to!( From 72c9b76f23b9c8e6cd7c81d425630aa84c0c43b2 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 10:55:15 -0500 Subject: [PATCH 08/24] Gen Eq test for tuple --- crates/compiler/test_gen/src/gen_abilities.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index 8c6362ab1c..77084affcf 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -1790,6 +1790,22 @@ mod eq { use indoc::indoc; use roc_std::RocStr; + #[test] + fn eq_tuple() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + ("a", "b") == ("a", "b") + "# + ), + true, + bool + ) + } + #[test] fn custom_eq_impl() { assert_evals_to!( From 8a7d9f8f23122e5389e86b6b9efc9da659d04541 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 11:13:00 -0500 Subject: [PATCH 09/24] Better debugging when lambda set region is missing --- crates/compiler/solve/src/specialize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/solve/src/specialize.rs b/crates/compiler/solve/src/specialize.rs index 53db854639..c6f6b017b9 100644 --- a/crates/compiler/solve/src/specialize.rs +++ b/crates/compiler/solve/src/specialize.rs @@ -743,7 +743,7 @@ fn get_specialization_lambda_set_ambient_function( let specialized_lambda_set = *specialization .specialization_lambda_sets .get(&lset_region) - .expect("lambda set region not resolved"); + .unwrap_or_else(|| panic!("lambda set region not resolved: {:?}", (spec_symbol, specialization))); Ok(specialized_lambda_set) } MemberImpl::Error => todo_abilities!(), From a361c4cfc1a823a86a7a5213fc094c6a5efa8303 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 11:14:46 -0500 Subject: [PATCH 10/24] Add the `tuple` ability member to EncoderFormatting --- crates/compiler/builtins/roc/Encode.roc | 2 ++ crates/compiler/builtins/roc/Json.roc | 20 ++++++++++++++++++++ crates/compiler/module/src/symbol.rs | 11 ++++++----- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/crates/compiler/builtins/roc/Encode.roc b/crates/compiler/builtins/roc/Encode.roc index ec0d10ef0a..2ac900e3e7 100644 --- a/crates/compiler/builtins/roc/Encode.roc +++ b/crates/compiler/builtins/roc/Encode.roc @@ -22,6 +22,7 @@ interface Encode list, record, tag, + tuple, custom, appendWith, append, @@ -69,6 +70,7 @@ EncoderFormatting has string : Str -> Encoder fmt | fmt has EncoderFormatting list : List elem, (elem -> Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting record : List { key : Str, value : Encoder fmt } -> Encoder fmt | fmt has EncoderFormatting + tuple : List (Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting tag : Str, List (Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting diff --git a/crates/compiler/builtins/roc/Json.roc b/crates/compiler/builtins/roc/Json.roc index 45672009e6..45a817e105 100644 --- a/crates/compiler/builtins/roc/Json.roc +++ b/crates/compiler/builtins/roc/Json.roc @@ -96,6 +96,7 @@ Json := {} has [ string: encodeString, list: encodeList, record: encodeRecord, + tuple: encodeTuple, tag: encodeTag, }, DecoderFormatting { @@ -207,6 +208,25 @@ encodeRecord = \fields -> List.append bytesWithRecord (Num.toU8 '}') +encodeTuple = \elems -> + Encode.custom \bytes, @Json {} -> + writeTuple = \{ buffer, elemsLeft }, elemEncoder -> + bufferWithElem = + appendWith buffer elemEncoder (@Json {}) + + bufferWithSuffix = + if elemsLeft > 1 then + List.append bufferWithElem (Num.toU8 ',') + else + bufferWithElem + + { buffer: bufferWithSuffix, elemsLeft: elemsLeft - 1 } + + bytesHead = List.append bytes (Num.toU8 '[') + { buffer: bytesWithRecord } = List.walk elems { buffer: bytesHead, elemsLeft: List.len elems } writeTuple + + List.append bytesWithRecord (Num.toU8 ']') + encodeTag = \name, payload -> Encode.custom \bytes, @Json {} -> # Idea: encode `A v1 v2` as `{"A": [v1, v2]}` diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 51c0fd6bac..72453d7b28 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1500,11 +1500,12 @@ define_builtins! { 18 ENCODE_STRING: "string" 19 ENCODE_LIST: "list" 20 ENCODE_RECORD: "record" - 21 ENCODE_TAG: "tag" - 22 ENCODE_CUSTOM: "custom" - 23 ENCODE_APPEND_WITH: "appendWith" - 24 ENCODE_APPEND: "append" - 25 ENCODE_TO_BYTES: "toBytes" + 21 ENCODE_TUPLE: "tuple" + 22 ENCODE_TAG: "tag" + 23 ENCODE_CUSTOM: "custom" + 24 ENCODE_APPEND_WITH: "appendWith" + 25 ENCODE_APPEND: "append" + 26 ENCODE_TO_BYTES: "toBytes" } 12 DECODE: "Decode" => { 0 DECODE_DECODE_ERROR: "DecodeError" exposed_type=true From e5fcb05a2d548b0ee12311668d8e81cc52bc7d03 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 11:19:47 -0500 Subject: [PATCH 11/24] Implement derivation keys for tuple encoders --- crates/compiler/derive/src/encoding.rs | 1 + crates/compiler/derive_key/src/encoding.rs | 14 +++++++++++--- crates/compiler/test_derive/src/encoding.rs | 8 ++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index f51bf545e3..fcbe1b4409 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -30,6 +30,7 @@ pub(crate) fn derive_to_encoder( FlatEncodableKey::List() => to_encoder_list(env, def_symbol), FlatEncodableKey::Set() => todo!(), FlatEncodableKey::Dict() => todo!(), + FlatEncodableKey::Tuple(_arity) => todo!(), FlatEncodableKey::Record(fields) => { // Generalized record var so we can reuse this impl between many records: // if fields = { a, b }, this is { a: t1, b: t2 } for fresh t1, t2. diff --git a/crates/compiler/derive_key/src/encoding.rs b/crates/compiler/derive_key/src/encoding.rs index 6945358c60..f1f54328b7 100644 --- a/crates/compiler/derive_key/src/encoding.rs +++ b/crates/compiler/derive_key/src/encoding.rs @@ -5,7 +5,7 @@ use roc_module::{ use roc_types::subs::{Content, FlatType, GetSubsSlice, Subs, Variable}; use crate::{ - util::{check_derivable_ext_var, debug_name_record, debug_name_tag}, + util::{check_derivable_ext_var, debug_name_record, debug_name_tag, debug_name_tuple}, DeriveError, }; @@ -22,6 +22,7 @@ pub enum FlatEncodableKey { Dict(/* takes two variables */), // Unfortunate that we must allocate here, c'est la vie Record(Vec), + Tuple(u32), TagUnion(Vec<(TagName, u16)>), } @@ -32,6 +33,7 @@ impl FlatEncodableKey { FlatEncodableKey::Set() => "set".to_string(), FlatEncodableKey::Dict() => "dict".to_string(), FlatEncodableKey::Record(fields) => debug_name_record(fields), + FlatEncodableKey::Tuple(arity) => debug_name_tuple(*arity), FlatEncodableKey::TagUnion(tags) => debug_name_tag(tags), } } @@ -66,8 +68,14 @@ impl FlatEncodable { Ok(Key(FlatEncodableKey::Record(field_names))) } - FlatType::Tuple(_elems, _ext) => { - todo!() + FlatType::Tuple(elems, ext) => { + let (elems_iter, ext) = elems.sorted_iterator_and_ext(subs, ext); + + check_derivable_ext_var(subs, ext, |ext| { + matches!(ext, Content::Structure(FlatType::EmptyTuple)) + })?; + + Ok(Key(FlatEncodableKey::Tuple(elems_iter.count() as _))) } FlatType::TagUnion(tags, ext) | FlatType::RecursiveTagUnion(_, tags, ext) => { // The recursion var doesn't matter, because the derived implementation will only diff --git a/crates/compiler/test_derive/src/encoding.rs b/crates/compiler/test_derive/src/encoding.rs index 4f6a15d7f3..cf6f48090c 100644 --- a/crates/compiler/test_derive/src/encoding.rs +++ b/crates/compiler/test_derive/src/encoding.rs @@ -33,6 +33,11 @@ test_key_eq! { v!({ a: v!(U8), b: v!(U8), }), v!({ ?a: v!(U8), ?b: v!(U8), }) + same_tuple: + v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16),)) + same_tuple_fields_diff_types: + v!((v!(U8), v!(U16),)), v!((v!(U32), v!(U64),)) + same_tag_union: v!([ A v!(U8) v!(STR), B v!(STR) ]), v!([ A v!(U8) v!(STR), B v!(STR) ]) same_tag_union_tags_diff_types: @@ -78,6 +83,9 @@ test_key_neq! { record_empty_vs_nonempty: v!(EMPTY_RECORD), v!({ a: v!(U8), }) + different_tuple_arities: + v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16), v!(U32),)) + different_tag_union_tags: v!([ A v!(U8) ]), v!([ B v!(U8) ]) tag_union_empty_vs_nonempty: From 0b4a45c70d412aad467a19974cc3d81445ff6c44 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 11:37:47 -0500 Subject: [PATCH 12/24] Derive encoding for tuples --- crates/compiler/derive/src/encoding.rs | 202 +++++++++++++++++++- crates/compiler/test_derive/src/encoding.rs | 23 +++ 2 files changed, 223 insertions(+), 2 deletions(-) diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index fcbe1b4409..183d489db1 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -14,7 +14,8 @@ use roc_module::symbol::Symbol; use roc_region::all::{Loc, Region}; use roc_types::subs::{ Content, ExhaustiveMark, FlatType, GetSubsSlice, LambdaSet, OptVariable, RecordFields, - RedundantMark, SubsSlice, TagExt, UnionLambdas, UnionTags, Variable, VariableSubsSlice, + RedundantMark, SubsSlice, TagExt, TupleElems, UnionLambdas, UnionTags, Variable, + VariableSubsSlice, }; use roc_types::types::RecordField; @@ -30,7 +31,6 @@ pub(crate) fn derive_to_encoder( FlatEncodableKey::List() => to_encoder_list(env, def_symbol), FlatEncodableKey::Set() => todo!(), FlatEncodableKey::Dict() => todo!(), - FlatEncodableKey::Tuple(_arity) => todo!(), FlatEncodableKey::Record(fields) => { // Generalized record var so we can reuse this impl between many records: // if fields = { a, b }, this is { a: t1, b: t2 } for fresh t1, t2. @@ -51,6 +51,21 @@ pub(crate) fn derive_to_encoder( to_encoder_record(env, record_var, fields, def_symbol) } + FlatEncodableKey::Tuple(arity) => { + // Generalized tuple var so we can reuse this impl between many tuples: + // if arity = n, this is (t1, ..., tn) for fresh t1, ..., tn. + let flex_elems = (0..arity) + .into_iter() + .map(|idx| (idx as usize, env.subs.fresh_unnamed_flex_var())) + .collect::>(); + let elems = TupleElems::insert_into_subs(env.subs, flex_elems); + let tuple_var = synth_var( + env.subs, + Content::Structure(FlatType::Tuple(elems, Variable::EMPTY_TUPLE)), + ); + + to_encoder_tuple(env, tuple_var, elems, def_symbol) + } FlatEncodableKey::TagUnion(tags) => { // Generalized tag union var so we can reuse this impl between many unions: // if tags = [ A arity=2, B arity=1 ], this is [ A t1 t2, B t3 ] for fresh t1, t2, t3 @@ -491,6 +506,189 @@ fn to_encoder_record( (clos, fn_var) } +fn to_encoder_tuple( + env: &mut Env<'_>, + tuple_var: Variable, + elems: TupleElems, + fn_name: Symbol, +) -> (Expr, Variable) { + // Suppose tup = (t1, t2). Build + // + // \tup -> Encode.tuple [ + // Encode.toEncoder tup.0, + // Encode.toEncoder tup.1, + // ] + + let tup_sym = env.new_symbol("tup"); + let whole_encoder_in_list_var = env.subs.fresh_unnamed_flex_var(); // type of the encoder in the list + + use Expr::*; + + let elem_encoders_list = elems + .iter_all() + .map(|(elem_index, elem_var_index)| { + let index = env.subs[elem_index]; + let elem_var = env.subs[elem_var_index]; + let elem_var_slice = VariableSubsSlice::new(elem_var_index.index, 1); + + // tup.0 + let tuple_access = TupleAccess { + tuple_var, + ext_var: env.subs.fresh_unnamed_flex_var(), + elem_var, + loc_expr: Box::new(Loc::at_zero(Var( + tup_sym, + env.subs.fresh_unnamed_flex_var(), + ))), + index, + }; + + // build `toEncoder tup.0` type + // val -[uls]-> Encoder fmt | fmt has EncoderFormatting + let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER); + + // (typeof tup.0) -[clos]-> t1 + let to_encoder_clos_var = env.subs.fresh_unnamed_flex_var(); // clos + let encoder_var = env.subs.fresh_unnamed_flex_var(); // t1 + let this_to_encoder_fn_var = synth_var( + env.subs, + Content::Structure(FlatType::Func( + elem_var_slice, + to_encoder_clos_var, + encoder_var, + )), + ); + + // val -[uls]-> Encoder fmt | fmt has EncoderFormatting + // ~ (typeof tup.0) -[clos]-> t1 + env.unify(to_encoder_fn_var, this_to_encoder_fn_var); + + // toEncoder : (typeof tup.0) -[clos]-> Encoder fmt | fmt has EncoderFormatting + let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, to_encoder_fn_var); + let to_encoder_fn = Box::new(( + to_encoder_fn_var, + Loc::at_zero(to_encoder_var), + to_encoder_clos_var, + encoder_var, + )); + + // toEncoder tup.0 + let to_encoder_call = Call( + to_encoder_fn, + vec![(elem_var, Loc::at_zero(tuple_access))], + CalledVia::Space, + ); + + // NOTE: must be done to unify the lambda sets under `encoder_var` + env.unify(encoder_var, whole_encoder_in_list_var); + + Loc::at_zero(to_encoder_call) + }) + .collect::>(); + + // typeof [ toEncoder tup.0, toEncoder tup.1 ] + let whole_encoder_in_list_var_slice = + VariableSubsSlice::insert_into_subs(env.subs, once(whole_encoder_in_list_var)); + let elem_encoders_list_var = synth_var( + env.subs, + Content::Structure(FlatType::Apply( + Symbol::LIST_LIST, + whole_encoder_in_list_var_slice, + )), + ); + + // [ toEncoder tup.0, toEncoder tup.1 ] + let elem_encoders_list = List { + elem_var: whole_encoder_in_list_var, + loc_elems: elem_encoders_list, + }; + + // build `Encode.tuple [ toEncoder tup.0, toEncoder tup.1 ]` type + // List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting + let encode_tuple_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TUPLE); + + // elem_encoders_list_var -[clos]-> t1 + let elem_encoders_list_var_slice = + VariableSubsSlice::insert_into_subs(env.subs, once(elem_encoders_list_var)); + let encode_tuple_clos_var = env.subs.fresh_unnamed_flex_var(); // clos + let encoder_var = env.subs.fresh_unnamed_flex_var(); // t1 + let this_encode_tuple_fn_var = synth_var( + env.subs, + Content::Structure(FlatType::Func( + elem_encoders_list_var_slice, + encode_tuple_clos_var, + encoder_var, + )), + ); + + // List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting + // ~ elem_encoders_list_var -[clos]-> t1 + env.unify(encode_tuple_fn_var, this_encode_tuple_fn_var); + + // Encode.tuple : elem_encoders_list_var -[clos]-> Encoder fmt | fmt has EncoderFormatting + let encode_tuple_var = AbilityMember(Symbol::ENCODE_TUPLE, None, encode_tuple_fn_var); + let encode_tuple_fn = Box::new(( + encode_tuple_fn_var, + Loc::at_zero(encode_tuple_var), + encode_tuple_clos_var, + encoder_var, + )); + + // Encode.tuple [ { key: .., value: .. }, .. ] + let encode_tuple_call = Call( + encode_tuple_fn, + vec![(elem_encoders_list_var, Loc::at_zero(elem_encoders_list))], + CalledVia::Space, + ); + + // Encode.custom \bytes, fmt -> Encode.appendWith bytes (Encode.tuple_var ..) fmt + let (body, this_encoder_var) = + wrap_in_encode_custom(env, encode_tuple_call, encoder_var, tup_sym, tuple_var); + + // Create fn_var for ambient capture; we fix it up below. + let fn_var = synth_var(env.subs, Content::Error); + + // -[fn_name]-> + let fn_name_labels = UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![]))); + let fn_clos_var = synth_var( + env.subs, + Content::LambdaSet(LambdaSet { + solved: fn_name_labels, + recursion_var: OptVariable::NONE, + unspecialized: SubsSlice::default(), + ambient_function: fn_var, + }), + ); + // typeof tup -[fn_name]-> (typeof Encode.tuple [ .. ] = Encoder fmt) + let tuple_var_slice = SubsSlice::insert_into_subs(env.subs, once(tuple_var)); + env.subs.set_content( + fn_var, + Content::Structure(FlatType::Func( + tuple_var_slice, + fn_clos_var, + this_encoder_var, + )), + ); + + // \tup -[fn_name]-> Encode.tuple [ { key: .., value: .. }, .. ] + let clos = Closure(ClosureData { + function_type: fn_var, + closure_type: fn_clos_var, + return_type: this_encoder_var, + name: fn_name, + captured_symbols: vec![], + recursive: Recursive::NotRecursive, + arguments: vec![( + tuple_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(tup_sym)), + )], + loc_body: Box::new(Loc::at_zero(body)), + }); + + (clos, fn_var) +} + fn to_encoder_tag_union( env: &mut Env<'_>, tag_union_var: Variable, diff --git a/crates/compiler/test_derive/src/encoding.rs b/crates/compiler/test_derive/src/encoding.rs index cf6f48090c..bfb807f111 100644 --- a/crates/compiler/test_derive/src/encoding.rs +++ b/crates/compiler/test_derive/src/encoding.rs @@ -273,6 +273,29 @@ fn two_field_record() { }) } +#[test] +fn two_field_tuple() { + derive_test(ToEncoder, v!((v!(U8), v!(STR),)), |golden| { + assert_snapshot!(golden, @r###" + # derived for ( U8, Str )* + # ( val, val1 )* -[[toEncoder_(arity:2)(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding + # ( val, val1 )a -[[toEncoder_(arity:2)(0)]]-> (List U8, fmt -[[custom(2) ( val, val1 )a]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding + # Specialization lambda sets: + # @<1>: [[toEncoder_(arity:2)(0)]] + # @<2>: [[custom(2) ( val, val1 )*]] | val has Encoding, val1 has Encoding + #Derived.toEncoder_(arity:2) = + \#Derived.tup -> + custom + \#Derived.bytes, #Derived.fmt -> + appendWith + #Derived.bytes + (tuple [toEncoder #Derived.tup.0, toEncoder #Derived.tup.1]) + #Derived.fmt + "### + ) + }) +} + #[test] #[ignore = "NOTE: this would never actually happen, because [] is uninhabited, and hence toEncoder can never be called with a value of []! Rightfully it induces broken assertions in other parts of the compiler, so we ignore it."] From c7bd43002615426c9c8088bf235f180e36d588e1 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 11:40:55 -0500 Subject: [PATCH 13/24] Gen tests for derived tuple encoding --- crates/compiler/test_gen/src/gen_abilities.rs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index 77084affcf..cb55dad8a9 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -787,6 +787,52 @@ fn encode_derived_record_with_many_types() { ) } +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn encode_derived_tuple_two_fields() { + assert_evals_to!( + indoc!( + r#" + app "test" + imports [Encode, Json] + provides [main] to "./platform" + + main = + tup = ("foo", 10u8) + result = Str.fromUtf8 (Encode.toBytes tup Json.toUtf8) + when result is + Ok s -> s + _ -> "" + "# + ), + RocStr::from(r#"["foo",10]"#), + RocStr + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn encode_derived_tuple_of_tuples() { + assert_evals_to!( + indoc!( + r#" + app "test" + imports [Encode, Json] + provides [main] to "./platform" + + main = + tup = ( ("foo", 10u8), (23u8, "bar", 15u8) ) + result = Str.fromUtf8 (Encode.toBytes tup Json.toUtf8) + when result is + Ok s -> s + _ -> "" + "# + ), + RocStr::from(r#"[["foo",10],[23,"bar",15]]"#), + RocStr + ) +} + #[test] #[cfg(all(any(feature = "gen-llvm", feature = "gen-wasm")))] fn encode_derived_generic_record_with_different_field_types() { From 0b3ee772326899bbfd4190613fd93a770fdab8a7 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 12:18:04 -0500 Subject: [PATCH 14/24] Add in Decode.tuple ability member --- crates/compiler/builtins/roc/Decode.roc | 11 ++++++++ crates/compiler/builtins/roc/Json.roc | 37 +++++++++++++++++++++++++ crates/compiler/module/src/symbol.rs | 11 ++++---- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/crates/compiler/builtins/roc/Decode.roc b/crates/compiler/builtins/roc/Decode.roc index 95407be0d0..765988fffa 100644 --- a/crates/compiler/builtins/roc/Decode.roc +++ b/crates/compiler/builtins/roc/Decode.roc @@ -23,6 +23,7 @@ interface Decode string, list, record, + tuple, custom, decodeWith, fromBytesPartial, @@ -43,6 +44,7 @@ interface Decode I32, I64, I128, + Nat, F32, F64, Dec, @@ -78,6 +80,15 @@ DecoderFormatting has list : Decoder elem fmt -> Decoder (List elem) fmt | fmt has DecoderFormatting record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting + ## `tuple state stepElem finalizer` decodes a tuple element-by-element. + ## + ## `stepElem` returns a decoder for the nth index in the tuple, or + ## `TooLong` if the index is larger than the expected size of the tuple. The + ## index passed to `stepElem` is 0-indexed. + ## + ## `finalizer` should produce the tuple value from the decoded `state`. + tuple : state, (state, Nat -> [Next (Decoder state fmt), TooLong]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting + custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting custom = \decode -> @Decoder decode diff --git a/crates/compiler/builtins/roc/Json.roc b/crates/compiler/builtins/roc/Json.roc index 45a817e105..09f7df875c 100644 --- a/crates/compiler/builtins/roc/Json.roc +++ b/crates/compiler/builtins/roc/Json.roc @@ -117,6 +117,7 @@ Json := {} has [ string: decodeString, list: decodeList, record: decodeRecord, + tuple: decodeTuple, }, ] @@ -497,6 +498,12 @@ openBrace = \bytes -> parseExactChar bytes '{' closingBrace : List U8 -> DecodeResult {} closingBrace = \bytes -> parseExactChar bytes '}' +openBracket : List U8 -> DecodeResult {} +openBracket = \bytes -> parseExactChar bytes '[' + +closingBracket : List U8 -> DecodeResult {} +closingBracket = \bytes -> parseExactChar bytes ']' + recordKey : List U8 -> DecodeResult Str recordKey = \bytes -> jsonString bytes @@ -547,6 +554,36 @@ decodeRecord = \initialState, stepField, finalizer -> Decode.custom \bytes, @Jso Ok val -> { result: Ok val, rest: afterRecordBytes } Err e -> { result: Err e, rest: afterRecordBytes } +decodeTuple = \initialState, stepElem, finalizer -> Decode.custom \initialBytes, @Json {} -> + # NB: the stepper function must be passed explicitly until #2894 is resolved. + decodeElems = \stepper, state, index, bytes -> + { val: newState, rest: beforeCommaOrBreak } <- tryDecode + ( + when stepper state index is + TooLong -> + { rest: beforeCommaOrBreak } <- bytes |> anything |> tryDecode + { result: Ok state, rest: beforeCommaOrBreak } + + Next decoder -> + Decode.decodeWith bytes decoder (@Json {}) + ) + + { result: commaResult, rest: nextBytes } = comma beforeCommaOrBreak + + when commaResult is + Ok {} -> decodeElems stepElem newState (index + 1) nextBytes + Err _ -> { result: Ok newState, rest: nextBytes } + + { rest: afterBracketBytes } <- initialBytes |> openBracket |> tryDecode + + { val: endStateResult, rest: beforeClosingBracketBytes } <- decodeElems stepElem initialState 0 afterBracketBytes |> tryDecode + + { rest: afterTupleBytes } <- beforeClosingBracketBytes |> closingBracket |> tryDecode + + when finalizer endStateResult is + Ok val -> { result: Ok val, rest: afterTupleBytes } + Err e -> { result: Err e, rest: afterTupleBytes } + # Helper to eat leading Json whitespace characters eatWhitespace = \input -> when input is diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 72453d7b28..2e6a25d7f5 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1531,11 +1531,12 @@ define_builtins! { 20 DECODE_STRING: "string" 21 DECODE_LIST: "list" 22 DECODE_RECORD: "record" - 23 DECODE_CUSTOM: "custom" - 24 DECODE_DECODE_WITH: "decodeWith" - 25 DECODE_FROM_BYTES_PARTIAL: "fromBytesPartial" - 26 DECODE_FROM_BYTES: "fromBytes" - 27 DECODE_MAP_RESULT: "mapResult" + 23 DECODE_TUPLE: "tuple" + 24 DECODE_CUSTOM: "custom" + 25 DECODE_DECODE_WITH: "decodeWith" + 26 DECODE_FROM_BYTES_PARTIAL: "fromBytesPartial" + 27 DECODE_FROM_BYTES: "fromBytes" + 28 DECODE_MAP_RESULT: "mapResult" } 13 HASH: "Hash" => { 0 HASH_HASH_ABILITY: "Hash" exposed_type=true From 92aff265ae7914cabf348133abd35b0b0e25ebec Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 12:19:20 -0500 Subject: [PATCH 15/24] Add doc comment to Decode.record --- crates/compiler/builtins/roc/Decode.roc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/compiler/builtins/roc/Decode.roc b/crates/compiler/builtins/roc/Decode.roc index 765988fffa..a6bfef53b7 100644 --- a/crates/compiler/builtins/roc/Decode.roc +++ b/crates/compiler/builtins/roc/Decode.roc @@ -78,6 +78,13 @@ DecoderFormatting has bool : Decoder Bool fmt | fmt has DecoderFormatting string : Decoder Str fmt | fmt has DecoderFormatting list : Decoder elem fmt -> Decoder (List elem) fmt | fmt has DecoderFormatting + + ## `record state stepField finalizer` decodes a record field-by-field. + ## + ## `stepField` returns a decoder for the given field in the record, or + ## `Skip` if the field is not a part of the decoded record. + ## + ## `finalizer` should produce the record value from the decoded `state`. record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting ## `tuple state stepElem finalizer` decodes a tuple element-by-element. From 5e0a6e5926d9cc9c90eaa999e2b65fae7e5f515d Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 12:23:16 -0500 Subject: [PATCH 16/24] Derive key for Decoding tuples --- crates/compiler/derive/src/decoding.rs | 1 + crates/compiler/derive_key/src/decoding.rs | 14 +++++++++++--- crates/compiler/test_derive/src/decoding.rs | 8 ++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 77bc98c1ad..66cf275bca 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -28,6 +28,7 @@ pub(crate) fn derive_decoder( let (body, body_type) = match key { FlatDecodableKey::List() => decoder_list(env, def_symbol), FlatDecodableKey::Record(fields) => decoder_record(env, def_symbol, fields), + FlatDecodableKey::Tuple(_arity) => todo!(), }; let specialization_lambda_sets = diff --git a/crates/compiler/derive_key/src/decoding.rs b/crates/compiler/derive_key/src/decoding.rs index ec0d732363..0e9008def6 100644 --- a/crates/compiler/derive_key/src/decoding.rs +++ b/crates/compiler/derive_key/src/decoding.rs @@ -2,7 +2,7 @@ use roc_module::{ident::Lowercase, symbol::Symbol}; use roc_types::subs::{Content, FlatType, Subs, Variable}; use crate::{ - util::{check_derivable_ext_var, debug_name_record}, + util::{check_derivable_ext_var, debug_name_record, debug_name_tuple}, DeriveError, }; @@ -18,6 +18,7 @@ pub enum FlatDecodableKey { // Unfortunate that we must allocate here, c'est la vie Record(Vec), + Tuple(u32), } impl FlatDecodableKey { @@ -25,6 +26,7 @@ impl FlatDecodableKey { match self { FlatDecodableKey::List() => "list".to_string(), FlatDecodableKey::Record(fields) => debug_name_record(fields), + FlatDecodableKey::Tuple(arity) => debug_name_tuple(*arity), } } } @@ -61,8 +63,14 @@ impl FlatDecodable { Ok(Key(FlatDecodableKey::Record(field_names))) } - FlatType::Tuple(_elems, _ext) => { - todo!() + FlatType::Tuple(elems, ext) => { + let (elems_iter, ext) = elems.sorted_iterator_and_ext(subs, ext); + + check_derivable_ext_var(subs, ext, |ext| { + matches!(ext, Content::Structure(FlatType::EmptyTuple)) + })?; + + Ok(Key(FlatDecodableKey::Tuple(elems_iter.count() as _))) } FlatType::TagUnion(_tags, _ext) | FlatType::RecursiveTagUnion(_, _tags, _ext) => { Err(Underivable) // yet diff --git a/crates/compiler/test_derive/src/decoding.rs b/crates/compiler/test_derive/src/decoding.rs index 624ebc5847..8fb618975e 100644 --- a/crates/compiler/test_derive/src/decoding.rs +++ b/crates/compiler/test_derive/src/decoding.rs @@ -28,6 +28,11 @@ test_key_eq! { explicit_empty_record_and_implicit_empty_record: v!(EMPTY_RECORD), v!({}) + same_tuple: + v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16),)) + same_tuple_fields_diff_types: + v!((v!(U8), v!(U16),)), v!((v!(U32), v!(U64),)) + list_list_diff_types: v!(Symbol::LIST_LIST v!(STR)), v!(Symbol::LIST_LIST v!(U8)) str_str: @@ -41,6 +46,9 @@ test_key_neq! { v!({ a: v!(U8), }), v!({ b: v!(U8), }) record_empty_vs_nonempty: v!(EMPTY_RECORD), v!({ a: v!(U8), }) + + different_tuple_arities: + v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16), v!(U32),)) } #[test] From e96be7c7464b6a827e21116fb3249a0c135023d8 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 12:29:21 -0500 Subject: [PATCH 17/24] Factor out decoders into separate files --- crates/compiler/derive/src/decoding.rs | 1091 +---------------- crates/compiler/derive/src/decoding/list.rs | 104 ++ crates/compiler/derive/src/decoding/record.rs | 982 +++++++++++++++ crates/compiler/derive/src/decoding/tuple.rs | 0 4 files changed, 1104 insertions(+), 1073 deletions(-) create mode 100644 crates/compiler/derive/src/decoding/list.rs create mode 100644 crates/compiler/derive/src/decoding/record.rs create mode 100644 crates/compiler/derive/src/decoding/tuple.rs diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 66cf275bca..3f738542ab 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -1,33 +1,31 @@ //! Derivers for the `Decoding` ability. -use roc_can::expr::{ - AnnotatedMark, ClosureData, Expr, Field, Recursive, WhenBranch, WhenBranchPattern, -}; +use roc_can::expr::{AnnotatedMark, ClosureData, Expr, Recursive}; use roc_can::pattern::Pattern; -use roc_collections::SendMap; -use roc_derive_key::decoding::FlatDecodableKey; -use roc_error_macros::internal_error; -use roc_module::called_via::CalledVia; -use roc_module::ident::Lowercase; -use roc_module::symbol::Symbol; -use roc_region::all::{Loc, Region}; -use roc_types::subs::{ - Content, ExhaustiveMark, FlatType, GetSubsSlice, LambdaSet, OptVariable, RecordFields, - RedundantMark, SubsSlice, TagExt, UnionLambdas, UnionTags, Variable, -}; -use roc_types::types::{AliasKind, RecordField}; -use crate::util::{Env, ExtensionKind}; +use roc_derive_key::decoding::FlatDecodableKey; +use roc_module::called_via::CalledVia; +use roc_module::symbol::Symbol; +use roc_region::all::Loc; +use roc_types::subs::{ + Content, FlatType, LambdaSet, OptVariable, SubsSlice, UnionLambdas, Variable, +}; + +use crate::util::Env; use crate::{synth_var, DerivedBody}; +mod list; +mod record; +mod tuple; + pub(crate) fn derive_decoder( env: &mut Env<'_>, key: FlatDecodableKey, def_symbol: Symbol, ) -> DerivedBody { let (body, body_type) = match key { - FlatDecodableKey::List() => decoder_list(env, def_symbol), - FlatDecodableKey::Record(fields) => decoder_record(env, def_symbol, fields), + FlatDecodableKey::List() => list::decoder(env, def_symbol), + FlatDecodableKey::Record(fields) => record::decoder(env, def_symbol, fields), FlatDecodableKey::Tuple(_arity) => todo!(), }; @@ -41,1062 +39,9 @@ pub(crate) fn derive_decoder( } } -// Implements decoding of a record. For example, for -// -// {first: a, second: b} -// -// we'd like to generate an impl like -// -// decoder : Decoder {first: a, second: b} fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting -// decoder = -// initialState : {f0: Result a [NoField], f1: Result b [NoField]} -// initialState = {f0: Err NoField, f1: Err NoField} -// -// stepField = \state, field -> -// when field is -// "first" -> -// Keep (Decode.custom \bytes, fmt -> -// when Decode.decodeWith bytes Decode.decoder fmt is -// {result, rest} -> -// {result: Result.map result \val -> {state & f0: Ok val}, rest}) -// "second" -> -// Keep (Decode.custom \bytes, fmt -> -// when Decode.decodeWith bytes Decode.decoder fmt is -// {result, rest} -> -// {result: Result.map result \val -> {state & f1: Ok val}, rest}) -// _ -> Skip -// -// finalizer = \{f0, f1} -> -// when f0 is -// Ok first -> -// when f1 is -// Ok second -> Ok {first, second} -// Err NoField -> Err TooShort -// Err NoField -> Err TooShort -// -// Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.record initialState stepField finalizer) fmt -fn decoder_record(env: &mut Env, _def_symbol: Symbol, fields: Vec) -> (Expr, Variable) { - // The decoded type of each field in the record, e.g. {first: a, second: b}. - let mut field_vars = Vec::with_capacity(fields.len()); - // The type of each field in the decoding state, e.g. {first: Result a [NoField], second: Result b [NoField]} - let mut result_field_vars = Vec::with_capacity(fields.len()); - - // initialState = ... - let (initial_state_var, initial_state) = - decoder_record_initial_state(env, &fields, &mut field_vars, &mut result_field_vars); - - // finalizer = ... - let (finalizer, finalizer_var, decode_err_var) = decoder_record_finalizer( - env, - initial_state_var, - &fields, - &field_vars, - &result_field_vars, - ); - - // stepField = ... - let (step_field, step_var) = decoder_record_step_field( - env, - fields, - &field_vars, - &result_field_vars, - initial_state_var, - decode_err_var, - ); - - // Build up the type of `Decode.record` we expect - let record_decoder_var = env.subs.fresh_unnamed_flex_var(); - let decode_record_lambda_set = env.subs.fresh_unnamed_flex_var(); - let decode_record_var = env.import_builtin_symbol_var(Symbol::DECODE_RECORD); - let this_decode_record_var = { - let flat_type = FlatType::Func( - SubsSlice::insert_into_subs(env.subs, [initial_state_var, step_var, finalizer_var]), - decode_record_lambda_set, - record_decoder_var, - ); - - synth_var(env.subs, Content::Structure(flat_type)) - }; - - env.unify(decode_record_var, this_decode_record_var); - - // Decode.record initialState stepField finalizer - let call_decode_record = Expr::Call( - Box::new(( - this_decode_record_var, - Loc::at_zero(Expr::AbilityMember( - Symbol::DECODE_RECORD, - None, - this_decode_record_var, - )), - decode_record_lambda_set, - record_decoder_var, - )), - vec![ - (initial_state_var, Loc::at_zero(initial_state)), - (step_var, Loc::at_zero(step_field)), - (finalizer_var, Loc::at_zero(finalizer)), - ], - CalledVia::Space, - ); - - let (call_decode_custom, decode_custom_ret_var) = { - let bytes_sym = env.new_symbol("bytes"); - let fmt_sym = env.new_symbol("fmt"); - let fmt_var = env.subs.fresh_unnamed_flex_var(); - - let (decode_custom, decode_custom_var) = wrap_in_decode_custom_decode_with( - env, - bytes_sym, - (fmt_sym, fmt_var), - vec![], - (call_decode_record, record_decoder_var), - ); - - (decode_custom, decode_custom_var) - }; - - (call_decode_custom, decode_custom_ret_var) -} - -// Example: -// stepField = \state, field -> -// when field is -// "first" -> -// Keep (Decode.custom \bytes, fmt -> -// # Uses a single-branch `when` because `let` is more expensive to monomorphize -// # due to checks for polymorphic expressions, and `rec` would be polymorphic. -// when Decode.decodeWith bytes Decode.decoder fmt is -// rec -> -// { -// rest: rec.rest, -// result: when rec.result is -// Ok val -> Ok {state & first: Ok val}, -// Err err -> Err err -// }) -// -// "second" -> -// Keep (Decode.custom \bytes, fmt -> -// when Decode.decodeWith bytes Decode.decoder fmt is -// rec -> -// { -// rest: rec.rest, -// result: when rec.result is -// Ok val -> Ok {state & second: Ok val}, -// Err err -> Err err -// }) -// -// _ -> Skip -fn decoder_record_step_field( - env: &mut Env, - fields: Vec, - field_vars: &[Variable], - result_field_vars: &[Variable], - state_record_var: Variable, - decode_err_var: Variable, -) -> (Expr, Variable) { - let state_arg_symbol = env.new_symbol("stateRecord"); - let field_arg_symbol = env.new_symbol("field"); - - // +1 because of the default branch. - let mut branches = Vec::with_capacity(fields.len() + 1); - let keep_payload_var = env.subs.fresh_unnamed_flex_var(); - let keep_or_skip_var = { - let keep_payload_subs_slice = SubsSlice::insert_into_subs(env.subs, [keep_payload_var]); - let flat_type = FlatType::TagUnion( - UnionTags::insert_slices_into_subs( - env.subs, - [ - ("Keep".into(), keep_payload_subs_slice), - ("Skip".into(), Default::default()), - ], - ), - TagExt::Any(Variable::EMPTY_TAG_UNION), - ); - - synth_var(env.subs, Content::Structure(flat_type)) - }; - - for ((field_name, &field_var), &result_field_var) in fields - .into_iter() - .zip(field_vars.iter()) - .zip(result_field_vars.iter()) - { - // Example: - // "first" -> - // Keep (Decode.custom \bytes, fmt -> - // # Uses a single-branch `when` because `let` is more expensive to monomorphize - // # due to checks for polymorphic expressions, and `rec` would be polymorphic. - // when Decode.decodeWith bytes Decode.decoder fmt is - // rec -> - // { - // rest: rec.rest, - // result: when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - // } - // ) - - let this_custom_callback_var; - let custom_callback_ret_var; - let custom_callback = { - // \bytes, fmt -> - // when Decode.decodeWith bytes Decode.decoder fmt is - // rec -> - // { - // rest: rec.rest, - // result: when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - // } - let bytes_arg_symbol = env.new_symbol("bytes"); - let fmt_arg_symbol = env.new_symbol("fmt"); - let bytes_arg_var = env.subs.fresh_unnamed_flex_var(); - let fmt_arg_var = env.subs.fresh_unnamed_flex_var(); - - // rec.result : [Ok field_var, Err DecodeError] - let rec_dot_result = { - let tag_union = FlatType::TagUnion( - UnionTags::for_result(env.subs, field_var, decode_err_var), - TagExt::Any(Variable::EMPTY_TAG_UNION), - ); - - synth_var(env.subs, Content::Structure(tag_union)) - }; - - // rec : { rest: List U8, result: (typeof rec.result) } - let rec_var = { - let fields = RecordFields::insert_into_subs( - env.subs, - [ - ("rest".into(), RecordField::Required(Variable::LIST_U8)), - ("result".into(), RecordField::Required(rec_dot_result)), - ], - ); - let record = FlatType::Record(fields, Variable::EMPTY_RECORD); - - synth_var(env.subs, Content::Structure(record)) - }; - - // `Decode.decoder` for the field's value - let decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER); - let decode_with_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODE_WITH); - let lambda_set_var = env.subs.fresh_unnamed_flex_var(); - let this_decode_with_var = { - let subs_slice = SubsSlice::insert_into_subs( - env.subs, - [bytes_arg_var, decoder_var, fmt_arg_var], - ); - let this_decode_with_var = synth_var( - env.subs, - Content::Structure(FlatType::Func(subs_slice, lambda_set_var, rec_var)), - ); - - env.unify(decode_with_var, this_decode_with_var); - - this_decode_with_var - }; - - // The result of decoding this field's value - either the updated state, or a decoding error. - let when_expr_var = { - let flat_type = FlatType::TagUnion( - UnionTags::for_result(env.subs, state_record_var, decode_err_var), - TagExt::Any(Variable::EMPTY_TAG_UNION), - ); - - synth_var(env.subs, Content::Structure(flat_type)) - }; - - // What our decoder passed to `Decode.custom` returns - the result of decoding the - // field's value, and the remaining bytes. - custom_callback_ret_var = { - let rest_field = RecordField::Required(Variable::LIST_U8); - let result_field = RecordField::Required(when_expr_var); - let flat_type = FlatType::Record( - RecordFields::insert_into_subs( - env.subs, - [("rest".into(), rest_field), ("result".into(), result_field)], - ), - Variable::EMPTY_RECORD, - ); - - synth_var(env.subs, Content::Structure(flat_type)) - }; - - let custom_callback_body = { - let rec_symbol = env.new_symbol("rec"); - - // # Uses a single-branch `when` because `let` is more expensive to monomorphize - // # due to checks for polymorphic expressions, and `rec` would be polymorphic. - // when Decode.decodeWith bytes Decode.decoder fmt is - // rec -> - // { - // rest: rec.rest, - // result: when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - // } - let branch_body = { - let result_val = { - // result: when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - let ok_val_symbol = env.new_symbol("val"); - let err_val_symbol = env.new_symbol("err"); - let ok_branch_expr = { - // Ok {state & first: Ok val}, - let mut updates = SendMap::default(); - - updates.insert( - field_name.clone(), - Field { - var: result_field_var, - region: Region::zero(), - loc_expr: Box::new(Loc::at_zero(Expr::Tag { - tag_union_var: result_field_var, - ext_var: env.new_ext_var(ExtensionKind::TagUnion), - name: "Ok".into(), - arguments: vec![( - field_var, - Loc::at_zero(Expr::Var(ok_val_symbol, field_var)), - )], - })), - }, - ); - - let updated_record = Expr::RecordUpdate { - record_var: state_record_var, - ext_var: env.new_ext_var(ExtensionKind::Record), - symbol: state_arg_symbol, - updates, - }; - - Expr::Tag { - tag_union_var: when_expr_var, - ext_var: env.new_ext_var(ExtensionKind::TagUnion), - name: "Ok".into(), - arguments: vec![(state_record_var, Loc::at_zero(updated_record))], - } - }; - - let branches = vec![ - // Ok val -> Ok {state & first: Ok val}, - WhenBranch { - patterns: vec![WhenBranchPattern { - pattern: Loc::at_zero(Pattern::AppliedTag { - whole_var: rec_dot_result, - ext_var: Variable::EMPTY_TAG_UNION, - tag_name: "Ok".into(), - arguments: vec![( - field_var, - Loc::at_zero(Pattern::Identifier(ok_val_symbol)), - )], - }), - degenerate: false, - }], - value: Loc::at_zero(ok_branch_expr), - guard: None, - redundant: RedundantMark::known_non_redundant(), - }, - // Err err -> Err err - WhenBranch { - patterns: vec![WhenBranchPattern { - pattern: Loc::at_zero(Pattern::AppliedTag { - whole_var: rec_dot_result, - ext_var: Variable::EMPTY_TAG_UNION, - tag_name: "Err".into(), - arguments: vec![( - decode_err_var, - Loc::at_zero(Pattern::Identifier(err_val_symbol)), - )], - }), - degenerate: false, - }], - value: Loc::at_zero(Expr::Tag { - tag_union_var: when_expr_var, - ext_var: env.new_ext_var(ExtensionKind::TagUnion), - name: "Err".into(), - arguments: vec![( - decode_err_var, - Loc::at_zero(Expr::Var(err_val_symbol, decode_err_var)), - )], - }), - guard: None, - redundant: RedundantMark::known_non_redundant(), - }, - ]; - - // when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - Expr::When { - loc_cond: Box::new(Loc::at_zero(Expr::RecordAccess { - record_var: rec_var, - ext_var: env.new_ext_var(ExtensionKind::Record), - field_var: rec_dot_result, - loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol, rec_var))), - field: "result".into(), - })), - cond_var: rec_dot_result, - expr_var: when_expr_var, - region: Region::zero(), - branches, - branches_cond_var: rec_dot_result, - exhaustive: ExhaustiveMark::known_exhaustive(), - } - }; - - // { - // rest: rec.rest, - // result: when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - // } - let mut fields_map = SendMap::default(); - - fields_map.insert( - "rest".into(), - Field { - var: Variable::LIST_U8, - region: Region::zero(), - loc_expr: Box::new(Loc::at_zero(Expr::RecordAccess { - record_var: rec_var, - ext_var: env.new_ext_var(ExtensionKind::Record), - field_var: Variable::LIST_U8, - loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol, rec_var))), - field: "rest".into(), - })), - }, - ); - - // result: when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - fields_map.insert( - "result".into(), - Field { - var: when_expr_var, - region: Region::zero(), - loc_expr: Box::new(Loc::at_zero(result_val)), - }, - ); - - Expr::Record { - record_var: custom_callback_ret_var, - fields: fields_map, - } - }; - - let branch = WhenBranch { - patterns: vec![WhenBranchPattern { - pattern: Loc::at_zero(Pattern::Identifier(rec_symbol)), - degenerate: false, - }], - value: Loc::at_zero(branch_body), - guard: None, - redundant: RedundantMark::known_non_redundant(), - }; - - let condition_expr = Expr::Call( - Box::new(( - this_decode_with_var, - Loc::at_zero(Expr::Var(Symbol::DECODE_DECODE_WITH, this_decode_with_var)), - lambda_set_var, - rec_var, - )), - vec![ - ( - Variable::LIST_U8, - Loc::at_zero(Expr::Var(bytes_arg_symbol, Variable::LIST_U8)), - ), - ( - decoder_var, - Loc::at_zero(Expr::AbilityMember( - Symbol::DECODE_DECODER, - None, - decoder_var, - )), - ), - ( - fmt_arg_var, - Loc::at_zero(Expr::Var(fmt_arg_symbol, fmt_arg_var)), - ), - ], - CalledVia::Space, - ); - - // when Decode.decodeWith bytes Decode.decoder fmt is - Expr::When { - loc_cond: Box::new(Loc::at_zero(condition_expr)), - cond_var: rec_var, - expr_var: custom_callback_ret_var, - region: Region::zero(), - branches: vec![branch], - branches_cond_var: rec_var, - exhaustive: ExhaustiveMark::known_exhaustive(), - } - }; - - let custom_closure_symbol = env.new_symbol("customCallback"); - this_custom_callback_var = env.subs.fresh_unnamed_flex_var(); - let custom_callback_lambda_set_var = { - let content = Content::LambdaSet(LambdaSet { - solved: UnionLambdas::insert_into_subs( - env.subs, - [(custom_closure_symbol, [state_record_var])], - ), - recursion_var: OptVariable::NONE, - unspecialized: Default::default(), - ambient_function: this_custom_callback_var, - }); - let custom_callback_lambda_set_var = synth_var(env.subs, content); - let subs_slice = - SubsSlice::insert_into_subs(env.subs, [bytes_arg_var, fmt_arg_var]); - - env.subs.set_content( - this_custom_callback_var, - Content::Structure(FlatType::Func( - subs_slice, - custom_callback_lambda_set_var, - custom_callback_ret_var, - )), - ); - - custom_callback_lambda_set_var - }; - - // \bytes, fmt -> … - Expr::Closure(ClosureData { - function_type: this_custom_callback_var, - closure_type: custom_callback_lambda_set_var, - return_type: custom_callback_ret_var, - name: custom_closure_symbol, - captured_symbols: vec![(state_arg_symbol, state_record_var)], - recursive: Recursive::NotRecursive, - arguments: vec![ - ( - bytes_arg_var, - AnnotatedMark::known_exhaustive(), - Loc::at_zero(Pattern::Identifier(bytes_arg_symbol)), - ), - ( - fmt_arg_var, - AnnotatedMark::known_exhaustive(), - Loc::at_zero(Pattern::Identifier(fmt_arg_symbol)), - ), - ], - loc_body: Box::new(Loc::at_zero(custom_callback_body)), - }) - }; - - let decode_custom_ret_var = env.subs.fresh_unnamed_flex_var(); - let decode_custom = { - let decode_custom_var = env.import_builtin_symbol_var(Symbol::DECODE_CUSTOM); - let decode_custom_closure_var = env.subs.fresh_unnamed_flex_var(); - let this_decode_custom_var = { - let subs_slice = SubsSlice::insert_into_subs(env.subs, [this_custom_callback_var]); - let flat_type = - FlatType::Func(subs_slice, decode_custom_closure_var, decode_custom_ret_var); - - synth_var(env.subs, Content::Structure(flat_type)) - }; - - env.unify(decode_custom_var, this_decode_custom_var); - - // Decode.custom \bytes, fmt -> … - Expr::Call( - Box::new(( - this_decode_custom_var, - Loc::at_zero(Expr::Var(Symbol::DECODE_CUSTOM, this_decode_custom_var)), - decode_custom_closure_var, - decode_custom_ret_var, - )), - vec![(this_custom_callback_var, Loc::at_zero(custom_callback))], - CalledVia::Space, - ) - }; - - env.unify(keep_payload_var, decode_custom_ret_var); - - let keep = { - // Keep (Decode.custom \bytes, fmt -> - // when Decode.decodeWith bytes Decode.decoder fmt is - // rec -> - // { - // rest: rec.rest, - // result: when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - // } - // ) - Expr::Tag { - tag_union_var: keep_or_skip_var, - ext_var: env.new_ext_var(ExtensionKind::TagUnion), - name: "Keep".into(), - arguments: vec![(decode_custom_ret_var, Loc::at_zero(decode_custom))], - } - }; - - let branch = { - // "first" -> - // Keep (Decode.custom \bytes, fmt -> - // when Decode.decodeWith bytes Decode.decoder fmt is - // rec -> - // { - // rest: rec.rest, - // result: when rec.result is - // Ok val -> Ok {state & first: Ok val}, - // Err err -> Err err - // } - // ) - WhenBranch { - patterns: vec![WhenBranchPattern { - pattern: Loc::at_zero(Pattern::StrLiteral(field_name.into())), - degenerate: false, - }], - value: Loc::at_zero(keep), - guard: None, - redundant: RedundantMark::known_non_redundant(), - } - }; - - branches.push(branch); - } - - // Example: `_ -> Skip` - let default_branch = WhenBranch { - patterns: vec![WhenBranchPattern { - pattern: Loc::at_zero(Pattern::Underscore), - degenerate: false, - }], - value: Loc::at_zero(Expr::Tag { - tag_union_var: keep_or_skip_var, - ext_var: env.new_ext_var(ExtensionKind::TagUnion), - name: "Skip".into(), - arguments: Vec::new(), - }), - guard: None, - redundant: RedundantMark::known_non_redundant(), - }; - - branches.push(default_branch); - - // when field is - let body = Expr::When { - loc_cond: Box::new(Loc::at_zero(Expr::Var(field_arg_symbol, Variable::STR))), - cond_var: Variable::STR, - expr_var: keep_or_skip_var, - region: Region::zero(), - branches, - branches_cond_var: Variable::STR, - exhaustive: ExhaustiveMark::known_exhaustive(), - }; - - let step_field_closure = env.new_symbol("stepField"); - let function_type = env.subs.fresh_unnamed_flex_var(); - let closure_type = { - let lambda_set = LambdaSet { - solved: UnionLambdas::tag_without_arguments(env.subs, step_field_closure), - recursion_var: OptVariable::NONE, - unspecialized: Default::default(), - ambient_function: function_type, - }; - - synth_var(env.subs, Content::LambdaSet(lambda_set)) - }; - - { - let args_slice = SubsSlice::insert_into_subs(env.subs, [state_record_var, Variable::STR]); - - env.subs.set_content( - function_type, - Content::Structure(FlatType::Func(args_slice, closure_type, keep_or_skip_var)), - ) - }; - - let expr = Expr::Closure(ClosureData { - function_type, - closure_type, - return_type: keep_or_skip_var, - name: step_field_closure, - captured_symbols: Vec::new(), - recursive: Recursive::NotRecursive, - arguments: vec![ - ( - state_record_var, - AnnotatedMark::known_exhaustive(), - Loc::at_zero(Pattern::Identifier(state_arg_symbol)), - ), - ( - Variable::STR, - AnnotatedMark::known_exhaustive(), - Loc::at_zero(Pattern::Identifier(field_arg_symbol)), - ), - ], - loc_body: Box::new(Loc::at_zero(body)), - }); - - (expr, function_type) -} - -// Example: -// finalizer = \rec -> -// when rec.first is -// Ok first -> -// when rec.second is -// Ok second -> Ok {first, second} -// Err NoField -> Err TooShort -// Err NoField -> Err TooShort -fn decoder_record_finalizer( - env: &mut Env, - state_record_var: Variable, - fields: &[Lowercase], - field_vars: &[Variable], - result_field_vars: &[Variable], -) -> (Expr, Variable, Variable) { - let state_arg_symbol = env.new_symbol("stateRecord"); - let mut fields_map = SendMap::default(); - let mut pattern_symbols = Vec::with_capacity(fields.len()); - let decode_err_var = { - let flat_type = FlatType::TagUnion( - UnionTags::tag_without_arguments(env.subs, "TooShort".into()), - TagExt::Any(Variable::EMPTY_TAG_UNION), - ); - - synth_var(env.subs, Content::Structure(flat_type)) - }; - - for (field_name, &field_var) in fields.iter().zip(field_vars.iter()) { - let symbol = env.new_symbol(field_name.as_str()); - - pattern_symbols.push(symbol); - - let field_expr = Expr::Var(symbol, field_var); - let field = Field { - var: field_var, - region: Region::zero(), - loc_expr: Box::new(Loc::at_zero(field_expr)), - }; - - fields_map.insert(field_name.clone(), field); - } - - // The bottom of the happy path - return the decoded record {first: a, second: b} wrapped with - // "Ok". - let return_type_var; - let mut body = { - let subs = &mut env.subs; - let record_field_iter = fields - .iter() - .zip(field_vars.iter()) - .map(|(field_name, &field_var)| (field_name.clone(), RecordField::Required(field_var))); - let flat_type = FlatType::Record( - RecordFields::insert_into_subs(subs, record_field_iter), - Variable::EMPTY_RECORD, - ); - let done_record_var = synth_var(subs, Content::Structure(flat_type)); - let done_record = Expr::Record { - record_var: done_record_var, - fields: fields_map, - }; - - return_type_var = { - let flat_type = FlatType::TagUnion( - UnionTags::for_result(subs, done_record_var, decode_err_var), - TagExt::Any(Variable::EMPTY_TAG_UNION), - ); - - synth_var(subs, Content::Structure(flat_type)) - }; - - Expr::Tag { - tag_union_var: return_type_var, - ext_var: env.new_ext_var(ExtensionKind::TagUnion), - name: "Ok".into(), - arguments: vec![(done_record_var, Loc::at_zero(done_record))], - } - }; - - // Unwrap each result in the decoded state - // - // when rec.first is - // Ok first -> ...happy path... - // Err NoField -> Err TooShort - for (((symbol, field_name), &field_var), &result_field_var) in pattern_symbols - .iter() - .rev() - .zip(fields.iter().rev()) - .zip(field_vars.iter().rev()) - .zip(result_field_vars.iter().rev()) - { - // when rec.first is - let cond_expr = Expr::RecordAccess { - record_var: state_record_var, - ext_var: env.new_ext_var(ExtensionKind::Record), - field_var: result_field_var, - loc_expr: Box::new(Loc::at_zero(Expr::Var(state_arg_symbol, state_record_var))), - field: field_name.clone(), - }; - - // Example: `Ok x -> expr` - let ok_branch = WhenBranch { - patterns: vec![WhenBranchPattern { - pattern: Loc::at_zero(Pattern::AppliedTag { - whole_var: result_field_var, - ext_var: Variable::EMPTY_TAG_UNION, - tag_name: "Ok".into(), - arguments: vec![(field_var, Loc::at_zero(Pattern::Identifier(*symbol)))], - }), - degenerate: false, - }], - value: Loc::at_zero(body), - guard: None, - redundant: RedundantMark::known_non_redundant(), - }; - - // Example: `_ -> Err TooShort` - let err_branch = WhenBranch { - patterns: vec![WhenBranchPattern { - pattern: Loc::at_zero(Pattern::Underscore), - degenerate: false, - }], - value: Loc::at_zero(Expr::Tag { - tag_union_var: return_type_var, - ext_var: env.new_ext_var(ExtensionKind::TagUnion), - name: "Err".into(), - arguments: vec![( - decode_err_var, - Loc::at_zero(Expr::Tag { - tag_union_var: decode_err_var, - ext_var: Variable::EMPTY_TAG_UNION, - name: "TooShort".into(), - arguments: Vec::new(), - }), - )], - }), - guard: None, - redundant: RedundantMark::known_non_redundant(), - }; - - body = Expr::When { - loc_cond: Box::new(Loc::at_zero(cond_expr)), - cond_var: result_field_var, - expr_var: return_type_var, - region: Region::zero(), - branches: vec![ok_branch, err_branch], - branches_cond_var: result_field_var, - exhaustive: ExhaustiveMark::known_exhaustive(), - }; - } - - let function_var = synth_var(env.subs, Content::Error); // We'll fix this up in subs later. - let function_symbol = env.new_symbol("finalizer"); - let lambda_set = LambdaSet { - solved: UnionLambdas::tag_without_arguments(env.subs, function_symbol), - recursion_var: OptVariable::NONE, - unspecialized: Default::default(), - ambient_function: function_var, - }; - let closure_type = synth_var(env.subs, Content::LambdaSet(lambda_set)); - let flat_type = FlatType::Func( - SubsSlice::insert_into_subs(env.subs, [state_record_var]), - closure_type, - return_type_var, - ); - - // Fix up function_var so it's not Content::Error anymore - env.subs - .set_content(function_var, Content::Structure(flat_type)); - - let finalizer = Expr::Closure(ClosureData { - function_type: function_var, - closure_type, - return_type: return_type_var, - name: function_symbol, - captured_symbols: Vec::new(), - recursive: Recursive::NotRecursive, - arguments: vec![( - state_record_var, - AnnotatedMark::known_exhaustive(), - Loc::at_zero(Pattern::Identifier(state_arg_symbol)), - )], - loc_body: Box::new(Loc::at_zero(body)), - }); - - (finalizer, function_var, decode_err_var) -} - -// Example: -// initialState : {first: Result a [NoField], second: Result b [NoField]} -// initialState = {first: Err NoField, second: Err NoField} -fn decoder_record_initial_state( - env: &mut Env<'_>, - field_names: &[Lowercase], - field_vars: &mut Vec, - result_field_vars: &mut Vec, -) -> (Variable, Expr) { - let mut initial_state_fields = SendMap::default(); - - for field_name in field_names { - let subs = &mut env.subs; - let field_var = subs.fresh_unnamed_flex_var(); - - field_vars.push(field_var); - - let no_field_label = "NoField"; - let union_tags = UnionTags::tag_without_arguments(subs, no_field_label.into()); - let no_field_var = synth_var( - subs, - Content::Structure(FlatType::TagUnion( - union_tags, - TagExt::Any(Variable::EMPTY_TAG_UNION), - )), - ); - let no_field = Expr::Tag { - tag_union_var: no_field_var, - ext_var: Variable::EMPTY_TAG_UNION, - name: no_field_label.into(), - arguments: Vec::new(), - }; - let err_label = "Err"; - let union_tags = UnionTags::for_result(subs, field_var, no_field_var); - let result_var = synth_var( - subs, - Content::Structure(FlatType::TagUnion( - union_tags, - TagExt::Any(Variable::EMPTY_TAG_UNION), - )), - ); - let field_expr = Expr::Tag { - tag_union_var: result_var, - ext_var: env.new_ext_var(ExtensionKind::TagUnion), - name: err_label.into(), - arguments: vec![(no_field_var, Loc::at_zero(no_field))], - }; - result_field_vars.push(result_var); - let field = Field { - var: result_var, - region: Region::zero(), - loc_expr: Box::new(Loc::at_zero(field_expr)), - }; - - initial_state_fields.insert(field_name.clone(), field); - } - - let subs = &mut env.subs; - let record_field_iter = field_names - .iter() - .zip(result_field_vars.iter()) - .map(|(field_name, &var)| (field_name.clone(), RecordField::Required(var))); - let flat_type = FlatType::Record( - RecordFields::insert_into_subs(subs, record_field_iter), - Variable::EMPTY_RECORD, - ); - - let state_record_var = synth_var(subs, Content::Structure(flat_type)); - - ( - state_record_var, - Expr::Record { - record_var: state_record_var, - fields: initial_state_fields, - }, - ) -} - -fn decoder_list(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { - // Build - // - // def_symbol : Decoder (List elem) fmt | elem has Decoding, fmt has DecoderFormatting - // def_symbol = Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.list Decode.decoder) fmt - // - // TODO try to reduce to `Decode.list Decode.decoder` - - use Expr::*; - - // Decode.list Decode.decoder : Decoder (List elem) fmt - let (decode_list_call, this_decode_list_ret_var) = { - // List elem - let elem_var = env.subs.fresh_unnamed_flex_var(); - - // Decode.decoder : Decoder elem fmt | elem has Decoding, fmt has EncoderFormatting - let (elem_decoder, elem_decoder_var) = { - // build `Decode.decoder : Decoder elem fmt` type - // Decoder val fmt | val has Decoding, fmt has EncoderFormatting - let elem_decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER); - - // set val ~ elem - let val_var = match env.subs.get_content_without_compacting(elem_decoder_var) { - Content::Alias(Symbol::DECODE_DECODER_OPAQUE, vars, _, AliasKind::Opaque) - if vars.type_variables_len == 2 => - { - env.subs.get_subs_slice(vars.type_variables())[0] - } - _ => internal_error!("Decode.decode not an opaque type"), - }; - - env.unify(val_var, elem_var); - - ( - AbilityMember(Symbol::DECODE_DECODER, None, elem_decoder_var), - elem_decoder_var, - ) - }; - - // Build `Decode.list Decode.decoder` type - // Decoder val fmt -[uls]-> Decoder (List val) fmt | fmt has DecoderFormatting - let decode_list_fn_var = env.import_builtin_symbol_var(Symbol::DECODE_LIST); - - // Decoder elem fmt -a-> b - let elem_decoder_var_slice = SubsSlice::insert_into_subs(env.subs, [elem_decoder_var]); - let this_decode_list_clos_var = env.subs.fresh_unnamed_flex_var(); - let this_decode_list_ret_var = env.subs.fresh_unnamed_flex_var(); - let this_decode_list_fn_var = synth_var( - env.subs, - Content::Structure(FlatType::Func( - elem_decoder_var_slice, - this_decode_list_clos_var, - this_decode_list_ret_var, - )), - ); - - // Decoder val fmt -[uls]-> Decoder (List val) fmt | fmt has DecoderFormatting - // ~ Decoder elem fmt -a -> b - env.unify(decode_list_fn_var, this_decode_list_fn_var); - - let decode_list_member = AbilityMember(Symbol::DECODE_LIST, None, this_decode_list_fn_var); - let decode_list_fn = Box::new(( - decode_list_fn_var, - Loc::at_zero(decode_list_member), - this_decode_list_clos_var, - this_decode_list_ret_var, - )); - - let decode_list_call = Call( - decode_list_fn, - vec![(elem_decoder_var, Loc::at_zero(elem_decoder))], - CalledVia::Space, - ); - - (decode_list_call, this_decode_list_ret_var) - }; - - let bytes_sym = env.new_symbol("bytes"); - let fmt_sym = env.new_symbol("fmt"); - let fmt_var = env.subs.fresh_unnamed_flex_var(); - let captures = vec![]; - - wrap_in_decode_custom_decode_with( - env, - bytes_sym, - (fmt_sym, fmt_var), - captures, - (decode_list_call, this_decode_list_ret_var), - ) -} - // Wraps `myDecoder` in `Decode.custom \bytes, fmt -> Decode.decodeWith bytes myDecoder fmt`. -// I think most can be removed when https://github.com/roc-lang/roc/issues/3724 is resolved. +// +// Needed to work around the Higher-Region Restriction. See https://github.com/roc-lang/roc/issues/3724. fn wrap_in_decode_custom_decode_with( env: &mut Env, bytes: Symbol, diff --git a/crates/compiler/derive/src/decoding/list.rs b/crates/compiler/derive/src/decoding/list.rs new file mode 100644 index 0000000000..a4c19c7538 --- /dev/null +++ b/crates/compiler/derive/src/decoding/list.rs @@ -0,0 +1,104 @@ +use roc_can::expr::Expr; + +use roc_error_macros::internal_error; +use roc_module::called_via::CalledVia; + +use roc_module::symbol::Symbol; +use roc_region::all::Loc; +use roc_types::subs::{Content, FlatType, GetSubsSlice, SubsSlice, Variable}; +use roc_types::types::AliasKind; + +use crate::decoding::wrap_in_decode_custom_decode_with; +use crate::synth_var; +use crate::util::Env; + +pub fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { + // Build + // + // def_symbol : Decoder (List elem) fmt | elem has Decoding, fmt has DecoderFormatting + // def_symbol = Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.list Decode.decoder) fmt + // + // NB: reduction to `Decode.list Decode.decoder` is not possible to the HRR. + + use Expr::*; + + // Decode.list Decode.decoder : Decoder (List elem) fmt + let (decode_list_call, this_decode_list_ret_var) = { + // List elem + let elem_var = env.subs.fresh_unnamed_flex_var(); + + // Decode.decoder : Decoder elem fmt | elem has Decoding, fmt has EncoderFormatting + let (elem_decoder, elem_decoder_var) = { + // build `Decode.decoder : Decoder elem fmt` type + // Decoder val fmt | val has Decoding, fmt has EncoderFormatting + let elem_decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER); + + // set val ~ elem + let val_var = match env.subs.get_content_without_compacting(elem_decoder_var) { + Content::Alias(Symbol::DECODE_DECODER_OPAQUE, vars, _, AliasKind::Opaque) + if vars.type_variables_len == 2 => + { + env.subs.get_subs_slice(vars.type_variables())[0] + } + _ => internal_error!("Decode.decode not an opaque type"), + }; + + env.unify(val_var, elem_var); + + ( + AbilityMember(Symbol::DECODE_DECODER, None, elem_decoder_var), + elem_decoder_var, + ) + }; + + // Build `Decode.list Decode.decoder` type + // Decoder val fmt -[uls]-> Decoder (List val) fmt | fmt has DecoderFormatting + let decode_list_fn_var = env.import_builtin_symbol_var(Symbol::DECODE_LIST); + + // Decoder elem fmt -a-> b + let elem_decoder_var_slice = SubsSlice::insert_into_subs(env.subs, [elem_decoder_var]); + let this_decode_list_clos_var = env.subs.fresh_unnamed_flex_var(); + let this_decode_list_ret_var = env.subs.fresh_unnamed_flex_var(); + let this_decode_list_fn_var = synth_var( + env.subs, + Content::Structure(FlatType::Func( + elem_decoder_var_slice, + this_decode_list_clos_var, + this_decode_list_ret_var, + )), + ); + + // Decoder val fmt -[uls]-> Decoder (List val) fmt | fmt has DecoderFormatting + // ~ Decoder elem fmt -a -> b + env.unify(decode_list_fn_var, this_decode_list_fn_var); + + let decode_list_member = AbilityMember(Symbol::DECODE_LIST, None, this_decode_list_fn_var); + let decode_list_fn = Box::new(( + decode_list_fn_var, + Loc::at_zero(decode_list_member), + this_decode_list_clos_var, + this_decode_list_ret_var, + )); + + let decode_list_call = Call( + decode_list_fn, + vec![(elem_decoder_var, Loc::at_zero(elem_decoder))], + CalledVia::Space, + ); + + (decode_list_call, this_decode_list_ret_var) + }; + + let bytes_sym = env.new_symbol("bytes"); + let fmt_sym = env.new_symbol("fmt"); + let fmt_var = env.subs.fresh_unnamed_flex_var(); + let captures = vec![]; + + wrap_in_decode_custom_decode_with( + env, + bytes_sym, + (fmt_sym, fmt_var), + captures, + (decode_list_call, this_decode_list_ret_var), + ) +} diff --git a/crates/compiler/derive/src/decoding/record.rs b/crates/compiler/derive/src/decoding/record.rs new file mode 100644 index 0000000000..e725c72d24 --- /dev/null +++ b/crates/compiler/derive/src/decoding/record.rs @@ -0,0 +1,982 @@ +use roc_can::expr::{ + AnnotatedMark, ClosureData, Expr, Field, Recursive, WhenBranch, WhenBranchPattern, +}; +use roc_can::pattern::Pattern; +use roc_collections::SendMap; +use roc_module::called_via::CalledVia; +use roc_module::ident::Lowercase; +use roc_module::symbol::Symbol; +use roc_region::all::{Loc, Region}; +use roc_types::subs::{ + Content, ExhaustiveMark, FlatType, LambdaSet, OptVariable, RecordFields, RedundantMark, + SubsSlice, TagExt, UnionLambdas, UnionTags, Variable, +}; +use roc_types::types::RecordField; + +use crate::synth_var; +use crate::util::{Env, ExtensionKind}; + +use super::wrap_in_decode_custom_decode_with; + +/// Implements decoding of a record. For example, for +/// +/// {first: a, second: b} +/// +/// we'd like to generate an impl like +/// +/// decoder : Decoder {first: a, second: b} fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting +/// decoder = +/// initialState : {f0: Result a [NoField], f1: Result b [NoField]} +/// initialState = {f0: Err NoField, f1: Err NoField} +/// +/// stepField = \state, field -> +/// when field is +/// "first" -> +/// Keep (Decode.custom \bytes, fmt -> +/// when Decode.decodeWith bytes Decode.decoder fmt is +/// {result, rest} -> +/// {result: Result.map result \val -> {state & f0: Ok val}, rest}) +/// "second" -> +/// Keep (Decode.custom \bytes, fmt -> +/// when Decode.decodeWith bytes Decode.decoder fmt is +/// {result, rest} -> +/// {result: Result.map result \val -> {state & f1: Ok val}, rest}) +/// _ -> Skip +/// +/// finalizer = \{f0, f1} -> +/// when f0 is +/// Ok first -> +/// when f1 is +/// Ok second -> Ok {first, second} +/// Err NoField -> Err TooShort +/// Err NoField -> Err TooShort +/// +/// Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.record initialState stepField finalizer) fmt +pub fn decoder(env: &mut Env, _def_symbol: Symbol, fields: Vec) -> (Expr, Variable) { + // The decoded type of each field in the record, e.g. {first: a, second: b}. + let mut field_vars = Vec::with_capacity(fields.len()); + // The type of each field in the decoding state, e.g. {first: Result a [NoField], second: Result b [NoField]} + let mut result_field_vars = Vec::with_capacity(fields.len()); + + // initialState = ... + let (initial_state_var, initial_state) = + initial_state(env, &fields, &mut field_vars, &mut result_field_vars); + + // finalizer = ... + let (finalizer, finalizer_var, decode_err_var) = finalizer( + env, + initial_state_var, + &fields, + &field_vars, + &result_field_vars, + ); + + // stepField = ... + let (step_field, step_var) = step_field( + env, + fields, + &field_vars, + &result_field_vars, + initial_state_var, + decode_err_var, + ); + + // Build up the type of `Decode.record` we expect + let record_decoder_var = env.subs.fresh_unnamed_flex_var(); + let decode_record_lambda_set = env.subs.fresh_unnamed_flex_var(); + let decode_record_var = env.import_builtin_symbol_var(Symbol::DECODE_RECORD); + let this_decode_record_var = { + let flat_type = FlatType::Func( + SubsSlice::insert_into_subs(env.subs, [initial_state_var, step_var, finalizer_var]), + decode_record_lambda_set, + record_decoder_var, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + env.unify(decode_record_var, this_decode_record_var); + + // Decode.record initialState stepField finalizer + let call_decode_record = Expr::Call( + Box::new(( + this_decode_record_var, + Loc::at_zero(Expr::AbilityMember( + Symbol::DECODE_RECORD, + None, + this_decode_record_var, + )), + decode_record_lambda_set, + record_decoder_var, + )), + vec![ + (initial_state_var, Loc::at_zero(initial_state)), + (step_var, Loc::at_zero(step_field)), + (finalizer_var, Loc::at_zero(finalizer)), + ], + CalledVia::Space, + ); + + let (call_decode_custom, decode_custom_ret_var) = { + let bytes_sym = env.new_symbol("bytes"); + let fmt_sym = env.new_symbol("fmt"); + let fmt_var = env.subs.fresh_unnamed_flex_var(); + + let (decode_custom, decode_custom_var) = wrap_in_decode_custom_decode_with( + env, + bytes_sym, + (fmt_sym, fmt_var), + vec![], + (call_decode_record, record_decoder_var), + ); + + (decode_custom, decode_custom_var) + }; + + (call_decode_custom, decode_custom_ret_var) +} + +// Example: +// stepField = \state, field -> +// when field is +// "first" -> +// Keep (Decode.custom \bytes, fmt -> +// # Uses a single-branch `when` because `let` is more expensive to monomorphize +// # due to checks for polymorphic expressions, and `rec` would be polymorphic. +// when Decode.decodeWith bytes Decode.decoder fmt is +// rec -> +// { +// rest: rec.rest, +// result: when rec.result is +// Ok val -> Ok {state & first: Ok val}, +// Err err -> Err err +// }) +// +// "second" -> +// Keep (Decode.custom \bytes, fmt -> +// when Decode.decodeWith bytes Decode.decoder fmt is +// rec -> +// { +// rest: rec.rest, +// result: when rec.result is +// Ok val -> Ok {state & second: Ok val}, +// Err err -> Err err +// }) +// +// _ -> Skip +fn step_field( + env: &mut Env, + fields: Vec, + field_vars: &[Variable], + result_field_vars: &[Variable], + state_record_var: Variable, + decode_err_var: Variable, +) -> (Expr, Variable) { + let state_arg_symbol = env.new_symbol("stateRecord"); + let field_arg_symbol = env.new_symbol("field"); + + // +1 because of the default branch. + let mut branches = Vec::with_capacity(fields.len() + 1); + let keep_payload_var = env.subs.fresh_unnamed_flex_var(); + let keep_or_skip_var = { + let keep_payload_subs_slice = SubsSlice::insert_into_subs(env.subs, [keep_payload_var]); + let flat_type = FlatType::TagUnion( + UnionTags::insert_slices_into_subs( + env.subs, + [ + ("Keep".into(), keep_payload_subs_slice), + ("Skip".into(), Default::default()), + ], + ), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + for ((field_name, &field_var), &result_field_var) in fields + .into_iter() + .zip(field_vars.iter()) + .zip(result_field_vars.iter()) + { + // Example: + // "first" -> + // Keep (Decode.custom \bytes, fmt -> + // # Uses a single-branch `when` because `let` is more expensive to monomorphize + // # due to checks for polymorphic expressions, and `rec` would be polymorphic. + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + // ) + + let this_custom_callback_var; + let custom_callback_ret_var; + let custom_callback = { + // \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + let bytes_arg_symbol = env.new_symbol("bytes"); + let fmt_arg_symbol = env.new_symbol("fmt"); + let bytes_arg_var = env.subs.fresh_unnamed_flex_var(); + let fmt_arg_var = env.subs.fresh_unnamed_flex_var(); + + // rec.result : [Ok field_var, Err DecodeError] + let rec_dot_result = { + let tag_union = FlatType::TagUnion( + UnionTags::for_result(env.subs, field_var, decode_err_var), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(env.subs, Content::Structure(tag_union)) + }; + + // rec : { rest: List U8, result: (typeof rec.result) } + let rec_var = { + let fields = RecordFields::insert_into_subs( + env.subs, + [ + ("rest".into(), RecordField::Required(Variable::LIST_U8)), + ("result".into(), RecordField::Required(rec_dot_result)), + ], + ); + let record = FlatType::Record(fields, Variable::EMPTY_RECORD); + + synth_var(env.subs, Content::Structure(record)) + }; + + // `Decode.decoder` for the field's value + let decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER); + let decode_with_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODE_WITH); + let lambda_set_var = env.subs.fresh_unnamed_flex_var(); + let this_decode_with_var = { + let subs_slice = SubsSlice::insert_into_subs( + env.subs, + [bytes_arg_var, decoder_var, fmt_arg_var], + ); + let this_decode_with_var = synth_var( + env.subs, + Content::Structure(FlatType::Func(subs_slice, lambda_set_var, rec_var)), + ); + + env.unify(decode_with_var, this_decode_with_var); + + this_decode_with_var + }; + + // The result of decoding this field's value - either the updated state, or a decoding error. + let when_expr_var = { + let flat_type = FlatType::TagUnion( + UnionTags::for_result(env.subs, state_record_var, decode_err_var), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + // What our decoder passed to `Decode.custom` returns - the result of decoding the + // field's value, and the remaining bytes. + custom_callback_ret_var = { + let rest_field = RecordField::Required(Variable::LIST_U8); + let result_field = RecordField::Required(when_expr_var); + let flat_type = FlatType::Record( + RecordFields::insert_into_subs( + env.subs, + [("rest".into(), rest_field), ("result".into(), result_field)], + ), + Variable::EMPTY_RECORD, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + let custom_callback_body = { + let rec_symbol = env.new_symbol("rec"); + + // # Uses a single-branch `when` because `let` is more expensive to monomorphize + // # due to checks for polymorphic expressions, and `rec` would be polymorphic. + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + let branch_body = { + let result_val = { + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + let ok_val_symbol = env.new_symbol("val"); + let err_val_symbol = env.new_symbol("err"); + let ok_branch_expr = { + // Ok {state & first: Ok val}, + let mut updates = SendMap::default(); + + updates.insert( + field_name.clone(), + Field { + var: result_field_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(Expr::Tag { + tag_union_var: result_field_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![( + field_var, + Loc::at_zero(Expr::Var(ok_val_symbol, field_var)), + )], + })), + }, + ); + + let updated_record = Expr::RecordUpdate { + record_var: state_record_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + symbol: state_arg_symbol, + updates, + }; + + Expr::Tag { + tag_union_var: when_expr_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![(state_record_var, Loc::at_zero(updated_record))], + } + }; + + let branches = vec![ + // Ok val -> Ok {state & first: Ok val}, + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: rec_dot_result, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Ok".into(), + arguments: vec![( + field_var, + Loc::at_zero(Pattern::Identifier(ok_val_symbol)), + )], + }), + degenerate: false, + }], + value: Loc::at_zero(ok_branch_expr), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }, + // Err err -> Err err + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: rec_dot_result, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Pattern::Identifier(err_val_symbol)), + )], + }), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: when_expr_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Expr::Var(err_val_symbol, decode_err_var)), + )], + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }, + ]; + + // when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + Expr::When { + loc_cond: Box::new(Loc::at_zero(Expr::RecordAccess { + record_var: rec_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: rec_dot_result, + loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol, rec_var))), + field: "result".into(), + })), + cond_var: rec_dot_result, + expr_var: when_expr_var, + region: Region::zero(), + branches, + branches_cond_var: rec_dot_result, + exhaustive: ExhaustiveMark::known_exhaustive(), + } + }; + + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + let mut fields_map = SendMap::default(); + + fields_map.insert( + "rest".into(), + Field { + var: Variable::LIST_U8, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(Expr::RecordAccess { + record_var: rec_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: Variable::LIST_U8, + loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol, rec_var))), + field: "rest".into(), + })), + }, + ); + + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + fields_map.insert( + "result".into(), + Field { + var: when_expr_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(result_val)), + }, + ); + + Expr::Record { + record_var: custom_callback_ret_var, + fields: fields_map, + } + }; + + let branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Identifier(rec_symbol)), + degenerate: false, + }], + value: Loc::at_zero(branch_body), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + let condition_expr = Expr::Call( + Box::new(( + this_decode_with_var, + Loc::at_zero(Expr::Var(Symbol::DECODE_DECODE_WITH, this_decode_with_var)), + lambda_set_var, + rec_var, + )), + vec![ + ( + Variable::LIST_U8, + Loc::at_zero(Expr::Var(bytes_arg_symbol, Variable::LIST_U8)), + ), + ( + decoder_var, + Loc::at_zero(Expr::AbilityMember( + Symbol::DECODE_DECODER, + None, + decoder_var, + )), + ), + ( + fmt_arg_var, + Loc::at_zero(Expr::Var(fmt_arg_symbol, fmt_arg_var)), + ), + ], + CalledVia::Space, + ); + + // when Decode.decodeWith bytes Decode.decoder fmt is + Expr::When { + loc_cond: Box::new(Loc::at_zero(condition_expr)), + cond_var: rec_var, + expr_var: custom_callback_ret_var, + region: Region::zero(), + branches: vec![branch], + branches_cond_var: rec_var, + exhaustive: ExhaustiveMark::known_exhaustive(), + } + }; + + let custom_closure_symbol = env.new_symbol("customCallback"); + this_custom_callback_var = env.subs.fresh_unnamed_flex_var(); + let custom_callback_lambda_set_var = { + let content = Content::LambdaSet(LambdaSet { + solved: UnionLambdas::insert_into_subs( + env.subs, + [(custom_closure_symbol, [state_record_var])], + ), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: this_custom_callback_var, + }); + let custom_callback_lambda_set_var = synth_var(env.subs, content); + let subs_slice = + SubsSlice::insert_into_subs(env.subs, [bytes_arg_var, fmt_arg_var]); + + env.subs.set_content( + this_custom_callback_var, + Content::Structure(FlatType::Func( + subs_slice, + custom_callback_lambda_set_var, + custom_callback_ret_var, + )), + ); + + custom_callback_lambda_set_var + }; + + // \bytes, fmt -> … + Expr::Closure(ClosureData { + function_type: this_custom_callback_var, + closure_type: custom_callback_lambda_set_var, + return_type: custom_callback_ret_var, + name: custom_closure_symbol, + captured_symbols: vec![(state_arg_symbol, state_record_var)], + recursive: Recursive::NotRecursive, + arguments: vec![ + ( + bytes_arg_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(bytes_arg_symbol)), + ), + ( + fmt_arg_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(fmt_arg_symbol)), + ), + ], + loc_body: Box::new(Loc::at_zero(custom_callback_body)), + }) + }; + + let decode_custom_ret_var = env.subs.fresh_unnamed_flex_var(); + let decode_custom = { + let decode_custom_var = env.import_builtin_symbol_var(Symbol::DECODE_CUSTOM); + let decode_custom_closure_var = env.subs.fresh_unnamed_flex_var(); + let this_decode_custom_var = { + let subs_slice = SubsSlice::insert_into_subs(env.subs, [this_custom_callback_var]); + let flat_type = + FlatType::Func(subs_slice, decode_custom_closure_var, decode_custom_ret_var); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + env.unify(decode_custom_var, this_decode_custom_var); + + // Decode.custom \bytes, fmt -> … + Expr::Call( + Box::new(( + this_decode_custom_var, + Loc::at_zero(Expr::Var(Symbol::DECODE_CUSTOM, this_decode_custom_var)), + decode_custom_closure_var, + decode_custom_ret_var, + )), + vec![(this_custom_callback_var, Loc::at_zero(custom_callback))], + CalledVia::Space, + ) + }; + + env.unify(keep_payload_var, decode_custom_ret_var); + + let keep = { + // Keep (Decode.custom \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + // ) + Expr::Tag { + tag_union_var: keep_or_skip_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Keep".into(), + arguments: vec![(decode_custom_ret_var, Loc::at_zero(decode_custom))], + } + }; + + let branch = { + // "first" -> + // Keep (Decode.custom \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + // ) + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::StrLiteral(field_name.into())), + degenerate: false, + }], + value: Loc::at_zero(keep), + guard: None, + redundant: RedundantMark::known_non_redundant(), + } + }; + + branches.push(branch); + } + + // Example: `_ -> Skip` + let default_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Underscore), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: keep_or_skip_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Skip".into(), + arguments: Vec::new(), + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + branches.push(default_branch); + + // when field is + let body = Expr::When { + loc_cond: Box::new(Loc::at_zero(Expr::Var(field_arg_symbol, Variable::STR))), + cond_var: Variable::STR, + expr_var: keep_or_skip_var, + region: Region::zero(), + branches, + branches_cond_var: Variable::STR, + exhaustive: ExhaustiveMark::known_exhaustive(), + }; + + let step_field_closure = env.new_symbol("stepField"); + let function_type = env.subs.fresh_unnamed_flex_var(); + let closure_type = { + let lambda_set = LambdaSet { + solved: UnionLambdas::tag_without_arguments(env.subs, step_field_closure), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: function_type, + }; + + synth_var(env.subs, Content::LambdaSet(lambda_set)) + }; + + { + let args_slice = SubsSlice::insert_into_subs(env.subs, [state_record_var, Variable::STR]); + + env.subs.set_content( + function_type, + Content::Structure(FlatType::Func(args_slice, closure_type, keep_or_skip_var)), + ) + }; + + let expr = Expr::Closure(ClosureData { + function_type, + closure_type, + return_type: keep_or_skip_var, + name: step_field_closure, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments: vec![ + ( + state_record_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(state_arg_symbol)), + ), + ( + Variable::STR, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(field_arg_symbol)), + ), + ], + loc_body: Box::new(Loc::at_zero(body)), + }); + + (expr, function_type) +} + +// Example: +// finalizer = \rec -> +// when rec.first is +// Ok first -> +// when rec.second is +// Ok second -> Ok {first, second} +// Err NoField -> Err TooShort +// Err NoField -> Err TooShort +fn finalizer( + env: &mut Env, + state_record_var: Variable, + fields: &[Lowercase], + field_vars: &[Variable], + result_field_vars: &[Variable], +) -> (Expr, Variable, Variable) { + let state_arg_symbol = env.new_symbol("stateRecord"); + let mut fields_map = SendMap::default(); + let mut pattern_symbols = Vec::with_capacity(fields.len()); + let decode_err_var = { + let flat_type = FlatType::TagUnion( + UnionTags::tag_without_arguments(env.subs, "TooShort".into()), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + for (field_name, &field_var) in fields.iter().zip(field_vars.iter()) { + let symbol = env.new_symbol(field_name.as_str()); + + pattern_symbols.push(symbol); + + let field_expr = Expr::Var(symbol, field_var); + let field = Field { + var: field_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(field_expr)), + }; + + fields_map.insert(field_name.clone(), field); + } + + // The bottom of the happy path - return the decoded record {first: a, second: b} wrapped with + // "Ok". + let return_type_var; + let mut body = { + let subs = &mut env.subs; + let record_field_iter = fields + .iter() + .zip(field_vars.iter()) + .map(|(field_name, &field_var)| (field_name.clone(), RecordField::Required(field_var))); + let flat_type = FlatType::Record( + RecordFields::insert_into_subs(subs, record_field_iter), + Variable::EMPTY_RECORD, + ); + let done_record_var = synth_var(subs, Content::Structure(flat_type)); + let done_record = Expr::Record { + record_var: done_record_var, + fields: fields_map, + }; + + return_type_var = { + let flat_type = FlatType::TagUnion( + UnionTags::for_result(subs, done_record_var, decode_err_var), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(subs, Content::Structure(flat_type)) + }; + + Expr::Tag { + tag_union_var: return_type_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![(done_record_var, Loc::at_zero(done_record))], + } + }; + + // Unwrap each result in the decoded state + // + // when rec.first is + // Ok first -> ...happy path... + // Err NoField -> Err TooShort + for (((symbol, field_name), &field_var), &result_field_var) in pattern_symbols + .iter() + .rev() + .zip(fields.iter().rev()) + .zip(field_vars.iter().rev()) + .zip(result_field_vars.iter().rev()) + { + // when rec.first is + let cond_expr = Expr::RecordAccess { + record_var: state_record_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: result_field_var, + loc_expr: Box::new(Loc::at_zero(Expr::Var(state_arg_symbol, state_record_var))), + field: field_name.clone(), + }; + + // Example: `Ok x -> expr` + let ok_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: result_field_var, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Ok".into(), + arguments: vec![(field_var, Loc::at_zero(Pattern::Identifier(*symbol)))], + }), + degenerate: false, + }], + value: Loc::at_zero(body), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + // Example: `_ -> Err TooShort` + let err_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Underscore), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: return_type_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Expr::Tag { + tag_union_var: decode_err_var, + ext_var: Variable::EMPTY_TAG_UNION, + name: "TooShort".into(), + arguments: Vec::new(), + }), + )], + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + body = Expr::When { + loc_cond: Box::new(Loc::at_zero(cond_expr)), + cond_var: result_field_var, + expr_var: return_type_var, + region: Region::zero(), + branches: vec![ok_branch, err_branch], + branches_cond_var: result_field_var, + exhaustive: ExhaustiveMark::known_exhaustive(), + }; + } + + let function_var = synth_var(env.subs, Content::Error); // We'll fix this up in subs later. + let function_symbol = env.new_symbol("finalizer"); + let lambda_set = LambdaSet { + solved: UnionLambdas::tag_without_arguments(env.subs, function_symbol), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: function_var, + }; + let closure_type = synth_var(env.subs, Content::LambdaSet(lambda_set)); + let flat_type = FlatType::Func( + SubsSlice::insert_into_subs(env.subs, [state_record_var]), + closure_type, + return_type_var, + ); + + // Fix up function_var so it's not Content::Error anymore + env.subs + .set_content(function_var, Content::Structure(flat_type)); + + let finalizer = Expr::Closure(ClosureData { + function_type: function_var, + closure_type, + return_type: return_type_var, + name: function_symbol, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments: vec![( + state_record_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(state_arg_symbol)), + )], + loc_body: Box::new(Loc::at_zero(body)), + }); + + (finalizer, function_var, decode_err_var) +} + +// Example: +// initialState : {first: Result a [NoField], second: Result b [NoField]} +// initialState = {first: Err NoField, second: Err NoField} +fn initial_state( + env: &mut Env<'_>, + field_names: &[Lowercase], + field_vars: &mut Vec, + result_field_vars: &mut Vec, +) -> (Variable, Expr) { + let mut initial_state_fields = SendMap::default(); + + for field_name in field_names { + let subs = &mut env.subs; + let field_var = subs.fresh_unnamed_flex_var(); + + field_vars.push(field_var); + + let no_field_label = "NoField"; + let union_tags = UnionTags::tag_without_arguments(subs, no_field_label.into()); + let no_field_var = synth_var( + subs, + Content::Structure(FlatType::TagUnion( + union_tags, + TagExt::Any(Variable::EMPTY_TAG_UNION), + )), + ); + let no_field = Expr::Tag { + tag_union_var: no_field_var, + ext_var: Variable::EMPTY_TAG_UNION, + name: no_field_label.into(), + arguments: Vec::new(), + }; + let err_label = "Err"; + let union_tags = UnionTags::for_result(subs, field_var, no_field_var); + let result_var = synth_var( + subs, + Content::Structure(FlatType::TagUnion( + union_tags, + TagExt::Any(Variable::EMPTY_TAG_UNION), + )), + ); + let field_expr = Expr::Tag { + tag_union_var: result_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: err_label.into(), + arguments: vec![(no_field_var, Loc::at_zero(no_field))], + }; + result_field_vars.push(result_var); + let field = Field { + var: result_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(field_expr)), + }; + + initial_state_fields.insert(field_name.clone(), field); + } + + let subs = &mut env.subs; + let record_field_iter = field_names + .iter() + .zip(result_field_vars.iter()) + .map(|(field_name, &var)| (field_name.clone(), RecordField::Required(var))); + let flat_type = FlatType::Record( + RecordFields::insert_into_subs(subs, record_field_iter), + Variable::EMPTY_RECORD, + ); + + let state_record_var = synth_var(subs, Content::Structure(flat_type)); + + ( + state_record_var, + Expr::Record { + record_var: state_record_var, + fields: initial_state_fields, + }, + ) +} diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs new file mode 100644 index 0000000000..e69de29bb2 From cb5a21cb201e5dd4d71be3c7e4c6a18dc1d88022 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 16:43:52 -0500 Subject: [PATCH 18/24] Derive decoding for tuples --- crates/compiler/derive/src/decoding.rs | 2 +- crates/compiler/derive/src/decoding/list.rs | 2 +- crates/compiler/derive/src/decoding/record.rs | 6 +- crates/compiler/derive/src/decoding/tuple.rs | 990 ++++++++++++++++++ crates/compiler/derive/src/util.rs | 7 +- crates/compiler/test_derive/src/decoding.rs | 56 + 6 files changed, 1057 insertions(+), 6 deletions(-) diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 3f738542ab..2ba0b8cff8 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -26,7 +26,7 @@ pub(crate) fn derive_decoder( let (body, body_type) = match key { FlatDecodableKey::List() => list::decoder(env, def_symbol), FlatDecodableKey::Record(fields) => record::decoder(env, def_symbol, fields), - FlatDecodableKey::Tuple(_arity) => todo!(), + FlatDecodableKey::Tuple(arity) => tuple::decoder(env, def_symbol, arity), }; let specialization_lambda_sets = diff --git a/crates/compiler/derive/src/decoding/list.rs b/crates/compiler/derive/src/decoding/list.rs index a4c19c7538..53c89fd1bd 100644 --- a/crates/compiler/derive/src/decoding/list.rs +++ b/crates/compiler/derive/src/decoding/list.rs @@ -12,7 +12,7 @@ use crate::decoding::wrap_in_decode_custom_decode_with; use crate::synth_var; use crate::util::Env; -pub fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { +pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { // Build // // def_symbol : Decoder (List elem) fmt | elem has Decoding, fmt has DecoderFormatting diff --git a/crates/compiler/derive/src/decoding/record.rs b/crates/compiler/derive/src/decoding/record.rs index e725c72d24..754eb1279c 100644 --- a/crates/compiler/derive/src/decoding/record.rs +++ b/crates/compiler/derive/src/decoding/record.rs @@ -52,7 +52,11 @@ use super::wrap_in_decode_custom_decode_with; /// Err NoField -> Err TooShort /// /// Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.record initialState stepField finalizer) fmt -pub fn decoder(env: &mut Env, _def_symbol: Symbol, fields: Vec) -> (Expr, Variable) { +pub(crate) fn decoder( + env: &mut Env, + _def_symbol: Symbol, + fields: Vec, +) -> (Expr, Variable) { // The decoded type of each field in the record, e.g. {first: a, second: b}. let mut field_vars = Vec::with_capacity(fields.len()); // The type of each field in the decoding state, e.g. {first: Result a [NoField], second: Result b [NoField]} diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs index e69de29bb2..0c0cf13d01 100644 --- a/crates/compiler/derive/src/decoding/tuple.rs +++ b/crates/compiler/derive/src/decoding/tuple.rs @@ -0,0 +1,990 @@ +use roc_can::expr::{ + AnnotatedMark, ClosureData, Expr, Field, IntValue, Recursive, WhenBranch, WhenBranchPattern, +}; +use roc_can::num::IntBound; +use roc_can::pattern::Pattern; +use roc_collections::SendMap; +use roc_module::called_via::CalledVia; +use roc_module::ident::Lowercase; +use roc_module::symbol::Symbol; +use roc_region::all::{Loc, Region}; +use roc_types::subs::{ + Content, ExhaustiveMark, FlatType, LambdaSet, OptVariable, RecordFields, RedundantMark, + SubsSlice, TagExt, TupleElems, UnionLambdas, UnionTags, Variable, +}; +use roc_types::types::RecordField; + +use crate::synth_var; +use crate::util::{Env, ExtensionKind}; + +use super::wrap_in_decode_custom_decode_with; + +/// Implements decoding of a tuple. For example, for +/// +/// (a, b) +/// +/// we'd like to generate an impl like +/// +/// decoder : Decoder (a, b) fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting +/// decoder = +/// initialState : {e0: Result a [NoElem], e1: Result b [NoElem]} +/// initialState = {e0: Err NoElem, e1: Err NoElem} +/// +/// stepElem = \state, index -> +/// when index is +/// 0 -> +/// Next (Decode.custom \bytes, fmt -> +/// when Decode.decodeWith bytes Decode.decoder fmt is +/// {result, rest} -> +/// {result: Result.map result \val -> {state & e0: Ok val}, rest}) +/// 1 -> +/// Next (Decode.custom \bytes, fmt -> +/// when Decode.decodeWith bytes Decode.decoder fmt is +/// {result, rest} -> +/// {result: Result.map result \val -> {state & e1: Ok val}, rest}) +/// _ -> TooLong +/// +/// finalizer = \st -> +/// when st.e0 is +/// Ok e0 -> +/// when st.e1 is +/// Ok e1 -> Ok (e0, e1) +/// Err NoElem -> Err TooShort +/// Err NoElem -> Err TooShort +/// +/// Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.tuple initialState stepElem finalizer) fmt +pub(crate) fn decoder(env: &mut Env, _def_symbol: Symbol, arity: u32) -> (Expr, Variable) { + // The decoded type of each index in the tuple, e.g. (a, b). + let mut index_vars = Vec::with_capacity(arity as _); + // The type of each index in the decoding state, e.g. {e0: Result a [NoElem], e1: Result b [NoElem]} + let mut state_fields = Vec::with_capacity(arity as _); + let mut state_field_vars = Vec::with_capacity(arity as _); + + // initialState = ... + let (state_var, initial_state) = initial_state( + env, + arity, + &mut index_vars, + &mut state_fields, + &mut state_field_vars, + ); + + // finalizer = ... + let (finalizer, finalizer_var, decode_err_var) = finalizer( + env, + &index_vars, + state_var, + &state_fields, + &state_field_vars, + ); + + // stepElem = ... + let (step_elem, step_var) = step_elem( + env, + &index_vars, + state_var, + &state_fields, + &state_field_vars, + decode_err_var, + ); + + // Build up the type of `Decode.tuple` we expect + let tuple_decoder_var = env.subs.fresh_unnamed_flex_var(); + let decode_record_lambda_set = env.subs.fresh_unnamed_flex_var(); + let decode_record_var = env.import_builtin_symbol_var(Symbol::DECODE_TUPLE); + let this_decode_record_var = { + let flat_type = FlatType::Func( + SubsSlice::insert_into_subs(env.subs, [state_var, step_var, finalizer_var]), + decode_record_lambda_set, + tuple_decoder_var, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + env.unify(decode_record_var, this_decode_record_var); + + // Decode.tuple initialState stepElem finalizer + let call_decode_record = Expr::Call( + Box::new(( + this_decode_record_var, + Loc::at_zero(Expr::AbilityMember( + Symbol::DECODE_TUPLE, + None, + this_decode_record_var, + )), + decode_record_lambda_set, + tuple_decoder_var, + )), + vec![ + (state_var, Loc::at_zero(initial_state)), + (step_var, Loc::at_zero(step_elem)), + (finalizer_var, Loc::at_zero(finalizer)), + ], + CalledVia::Space, + ); + + let (call_decode_custom, decode_custom_ret_var) = { + let bytes_sym = env.new_symbol("bytes"); + let fmt_sym = env.new_symbol("fmt"); + let fmt_var = env.subs.fresh_unnamed_flex_var(); + + let (decode_custom, decode_custom_var) = wrap_in_decode_custom_decode_with( + env, + bytes_sym, + (fmt_sym, fmt_var), + vec![], + (call_decode_record, tuple_decoder_var), + ); + + (decode_custom, decode_custom_var) + }; + + (call_decode_custom, decode_custom_ret_var) +} + +// Example: +// stepElem = \state, index -> +// when index is +// 0 -> +// Next (Decode.custom \bytes, fmt -> +// # Uses a single-branch `when` because `let` is more expensive to monomorphize +// # due to checks for polymorphic expressions, and `rec` would be polymorphic. +// when Decode.decodeWith bytes Decode.decoder fmt is +// rec -> +// { +// rest: rec.rest, +// result: when rec.result is +// Ok val -> Ok {state & e0: Ok val}, +// Err err -> Err err +// }) +// +// "e1" -> +// Next (Decode.custom \bytes, fmt -> +// when Decode.decodeWith bytes Decode.decoder fmt is +// rec -> +// { +// rest: rec.rest, +// result: when rec.result is +// Ok val -> Ok {state & e1: Ok val}, +// Err err -> Err err +// }) +// +// _ -> TooLong +fn step_elem( + env: &mut Env, + index_vars: &[Variable], + state_record_var: Variable, + state_fields: &[Lowercase], + state_field_vars: &[Variable], + decode_err_var: Variable, +) -> (Expr, Variable) { + let state_arg_symbol = env.new_symbol("stateRecord"); + let index_arg_symbol = env.new_symbol("index"); + + // +1 because of the default branch. + let mut branches = Vec::with_capacity(index_vars.len() + 1); + let keep_payload_var = env.subs.fresh_unnamed_flex_var(); + let keep_or_skip_var = { + let keep_payload_subs_slice = SubsSlice::insert_into_subs(env.subs, [keep_payload_var]); + let flat_type = FlatType::TagUnion( + UnionTags::insert_slices_into_subs( + env.subs, + [ + ("Next".into(), keep_payload_subs_slice), + ("TooLong".into(), Default::default()), + ], + ), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + for (((index, state_field), &index_var), &result_index_var) in state_fields + .into_iter() + .enumerate() + .zip(index_vars) + .zip(state_field_vars) + { + // Example: + // 0 -> + // Next (Decode.custom \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + // } + // ) + + let this_custom_callback_var; + let custom_callback_ret_var; + let custom_callback = { + // \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + // } + let bytes_arg_symbol = env.new_symbol("bytes"); + let fmt_arg_symbol = env.new_symbol("fmt"); + let bytes_arg_var = env.subs.fresh_unnamed_flex_var(); + let fmt_arg_var = env.subs.fresh_unnamed_flex_var(); + + // rec.result : [Ok index_var, Err DecodeError] + let rec_dot_result = { + let tag_union = FlatType::TagUnion( + UnionTags::for_result(env.subs, index_var, decode_err_var), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(env.subs, Content::Structure(tag_union)) + }; + + // rec : { rest: List U8, result: (typeof rec.result) } + let rec_var = { + let indexs = RecordFields::insert_into_subs( + env.subs, + [ + ("rest".into(), RecordField::Required(Variable::LIST_U8)), + ("result".into(), RecordField::Required(rec_dot_result)), + ], + ); + let record = FlatType::Record(indexs, Variable::EMPTY_RECORD); + + synth_var(env.subs, Content::Structure(record)) + }; + + // `Decode.decoder` for the index's value + let decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER); + let decode_with_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODE_WITH); + let lambda_set_var = env.subs.fresh_unnamed_flex_var(); + let this_decode_with_var = { + let subs_slice = SubsSlice::insert_into_subs( + env.subs, + [bytes_arg_var, decoder_var, fmt_arg_var], + ); + let this_decode_with_var = synth_var( + env.subs, + Content::Structure(FlatType::Func(subs_slice, lambda_set_var, rec_var)), + ); + + env.unify(decode_with_var, this_decode_with_var); + + this_decode_with_var + }; + + // The result of decoding this index's value - either the updated state, or a decoding error. + let when_expr_var = { + let flat_type = FlatType::TagUnion( + UnionTags::for_result(env.subs, state_record_var, decode_err_var), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + // What our decoder passed to `Decode.custom` returns - the result of decoding the + // index's value, and the remaining bytes. + custom_callback_ret_var = { + let rest_index = RecordField::Required(Variable::LIST_U8); + let result_index = RecordField::Required(when_expr_var); + let flat_type = FlatType::Record( + RecordFields::insert_into_subs( + env.subs, + [("rest".into(), rest_index), ("result".into(), result_index)], + ), + Variable::EMPTY_RECORD, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + let custom_callback_body = { + let rec_symbol = env.new_symbol("rec"); + + // # Uses a single-branch `when` because `let` is more expensive to monomorphize + // # due to checks for polymorphic expressions, and `rec` would be polymorphic. + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + // } + let branch_body = { + let result_val = { + // result: when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + let ok_val_symbol = env.new_symbol("val"); + let err_val_symbol = env.new_symbol("err"); + let ok_branch_expr = { + // Ok {state & e0: Ok val}, + let mut updates = SendMap::default(); + + updates.insert( + state_field.clone(), + Field { + var: result_index_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(Expr::Tag { + tag_union_var: result_index_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![( + index_var, + Loc::at_zero(Expr::Var(ok_val_symbol, index_var)), + )], + })), + }, + ); + + let updated_record = Expr::RecordUpdate { + record_var: state_record_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + symbol: state_arg_symbol, + updates, + }; + + Expr::Tag { + tag_union_var: when_expr_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![(state_record_var, Loc::at_zero(updated_record))], + } + }; + + let branches = vec![ + // Ok val -> Ok {state & e0: Ok val}, + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: rec_dot_result, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Ok".into(), + arguments: vec![( + index_var, + Loc::at_zero(Pattern::Identifier(ok_val_symbol)), + )], + }), + degenerate: false, + }], + value: Loc::at_zero(ok_branch_expr), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }, + // Err err -> Err err + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: rec_dot_result, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Pattern::Identifier(err_val_symbol)), + )], + }), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: when_expr_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Expr::Var(err_val_symbol, decode_err_var)), + )], + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }, + ]; + + // when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + Expr::When { + loc_cond: Box::new(Loc::at_zero(Expr::RecordAccess { + record_var: rec_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: rec_dot_result, + loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol, rec_var))), + field: "result".into(), + })), + cond_var: rec_dot_result, + expr_var: when_expr_var, + region: Region::zero(), + branches, + branches_cond_var: rec_dot_result, + exhaustive: ExhaustiveMark::known_exhaustive(), + } + }; + + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + // } + let mut fields_map = SendMap::default(); + + fields_map.insert( + "rest".into(), + Field { + var: Variable::LIST_U8, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(Expr::RecordAccess { + record_var: rec_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: Variable::LIST_U8, + loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol, rec_var))), + field: "rest".into(), + })), + }, + ); + + // result: when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + fields_map.insert( + "result".into(), + Field { + var: when_expr_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(result_val)), + }, + ); + + Expr::Record { + record_var: custom_callback_ret_var, + fields: fields_map, + } + }; + + let branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Identifier(rec_symbol)), + degenerate: false, + }], + value: Loc::at_zero(branch_body), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + let condition_expr = Expr::Call( + Box::new(( + this_decode_with_var, + Loc::at_zero(Expr::Var(Symbol::DECODE_DECODE_WITH, this_decode_with_var)), + lambda_set_var, + rec_var, + )), + vec![ + ( + Variable::LIST_U8, + Loc::at_zero(Expr::Var(bytes_arg_symbol, Variable::LIST_U8)), + ), + ( + decoder_var, + Loc::at_zero(Expr::AbilityMember( + Symbol::DECODE_DECODER, + None, + decoder_var, + )), + ), + ( + fmt_arg_var, + Loc::at_zero(Expr::Var(fmt_arg_symbol, fmt_arg_var)), + ), + ], + CalledVia::Space, + ); + + // when Decode.decodeWith bytes Decode.decoder fmt is + Expr::When { + loc_cond: Box::new(Loc::at_zero(condition_expr)), + cond_var: rec_var, + expr_var: custom_callback_ret_var, + region: Region::zero(), + branches: vec![branch], + branches_cond_var: rec_var, + exhaustive: ExhaustiveMark::known_exhaustive(), + } + }; + + let custom_closure_symbol = env.new_symbol("customCallback"); + this_custom_callback_var = env.subs.fresh_unnamed_flex_var(); + let custom_callback_lambda_set_var = { + let content = Content::LambdaSet(LambdaSet { + solved: UnionLambdas::insert_into_subs( + env.subs, + [(custom_closure_symbol, [state_record_var])], + ), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: this_custom_callback_var, + }); + let custom_callback_lambda_set_var = synth_var(env.subs, content); + let subs_slice = + SubsSlice::insert_into_subs(env.subs, [bytes_arg_var, fmt_arg_var]); + + env.subs.set_content( + this_custom_callback_var, + Content::Structure(FlatType::Func( + subs_slice, + custom_callback_lambda_set_var, + custom_callback_ret_var, + )), + ); + + custom_callback_lambda_set_var + }; + + // \bytes, fmt -> … + Expr::Closure(ClosureData { + function_type: this_custom_callback_var, + closure_type: custom_callback_lambda_set_var, + return_type: custom_callback_ret_var, + name: custom_closure_symbol, + captured_symbols: vec![(state_arg_symbol, state_record_var)], + recursive: Recursive::NotRecursive, + arguments: vec![ + ( + bytes_arg_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(bytes_arg_symbol)), + ), + ( + fmt_arg_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(fmt_arg_symbol)), + ), + ], + loc_body: Box::new(Loc::at_zero(custom_callback_body)), + }) + }; + + let decode_custom_ret_var = env.subs.fresh_unnamed_flex_var(); + let decode_custom = { + let decode_custom_var = env.import_builtin_symbol_var(Symbol::DECODE_CUSTOM); + let decode_custom_closure_var = env.subs.fresh_unnamed_flex_var(); + let this_decode_custom_var = { + let subs_slice = SubsSlice::insert_into_subs(env.subs, [this_custom_callback_var]); + let flat_type = + FlatType::Func(subs_slice, decode_custom_closure_var, decode_custom_ret_var); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + env.unify(decode_custom_var, this_decode_custom_var); + + // Decode.custom \bytes, fmt -> … + Expr::Call( + Box::new(( + this_decode_custom_var, + Loc::at_zero(Expr::Var(Symbol::DECODE_CUSTOM, this_decode_custom_var)), + decode_custom_closure_var, + decode_custom_ret_var, + )), + vec![(this_custom_callback_var, Loc::at_zero(custom_callback))], + CalledVia::Space, + ) + }; + + env.unify(keep_payload_var, decode_custom_ret_var); + + let keep = { + // Next (Decode.custom \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + // } + // ) + Expr::Tag { + tag_union_var: keep_or_skip_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Next".into(), + arguments: vec![(decode_custom_ret_var, Loc::at_zero(decode_custom))], + } + }; + + let branch = { + // 0 -> + // Next (Decode.custom \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & e0: Ok val}, + // Err err -> Err err + // } + // ) + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::IntLiteral( + env.subs.fresh_unnamed_flex_var(), + env.subs.fresh_unnamed_flex_var(), + index.to_string().into_boxed_str(), + IntValue::I128((index as i128).to_ne_bytes()), + IntBound::None, + )), + degenerate: false, + }], + value: Loc::at_zero(keep), + guard: None, + redundant: RedundantMark::known_non_redundant(), + } + }; + + branches.push(branch); + } + + // Example: `_ -> TooLong` + let default_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Underscore), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: keep_or_skip_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "TooLong".into(), + arguments: Vec::new(), + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + branches.push(default_branch); + + // when index is + let body = Expr::When { + loc_cond: Box::new(Loc::at_zero(Expr::Var(index_arg_symbol, Variable::NAT))), + cond_var: Variable::NAT, + expr_var: keep_or_skip_var, + region: Region::zero(), + branches, + branches_cond_var: Variable::NAT, + exhaustive: ExhaustiveMark::known_exhaustive(), + }; + + let step_elem_closure = env.new_symbol("stepElem"); + let function_type = env.subs.fresh_unnamed_flex_var(); + let closure_type = { + let lambda_set = LambdaSet { + solved: UnionLambdas::tag_without_arguments(env.subs, step_elem_closure), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: function_type, + }; + + synth_var(env.subs, Content::LambdaSet(lambda_set)) + }; + + { + let args_slice = SubsSlice::insert_into_subs(env.subs, [state_record_var, Variable::NAT]); + + env.subs.set_content( + function_type, + Content::Structure(FlatType::Func(args_slice, closure_type, keep_or_skip_var)), + ) + }; + + let expr = Expr::Closure(ClosureData { + function_type, + closure_type, + return_type: keep_or_skip_var, + name: step_elem_closure, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments: vec![ + ( + state_record_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(state_arg_symbol)), + ), + ( + Variable::NAT, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(index_arg_symbol)), + ), + ], + loc_body: Box::new(Loc::at_zero(body)), + }); + + (expr, function_type) +} + +// Example: +// finalizer = \rec -> +// when rec.e0 is +// Ok e0 -> +// when rec.e1 is +// Ok e1 -> Ok (e0, e1) +// Err NoElem -> Err TooShort +// Err NoElem -> Err TooShort +fn finalizer( + env: &mut Env, + index_vars: &[Variable], + state_record_var: Variable, + state_fields: &[Lowercase], + state_field_vars: &[Variable], +) -> (Expr, Variable, Variable) { + let state_arg_symbol = env.new_symbol("stateRecord"); + let mut tuple_elems = Vec::with_capacity(index_vars.len()); + let mut pattern_symbols = Vec::with_capacity(index_vars.len()); + let decode_err_var = { + let flat_type = FlatType::TagUnion( + UnionTags::tag_without_arguments(env.subs, "TooShort".into()), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + for (i, &index_var) in index_vars.iter().enumerate() { + let symbol = env.new_symbol(i); + + pattern_symbols.push(symbol); + + let index_expr = Expr::Var(symbol, index_var); + + tuple_elems.push((index_var, Box::new(Loc::at_zero(index_expr)))); + } + + // The bottom of the happy path - return the decoded tuple (a, b) wrapped with + // "Ok". + let return_type_var; + let mut body = { + let subs = &mut env.subs; + let tuple_indices_iter = index_vars.iter().copied().enumerate(); + let flat_type = FlatType::Tuple( + TupleElems::insert_into_subs(subs, tuple_indices_iter), + Variable::EMPTY_TUPLE, + ); + let done_tuple_var = synth_var(subs, Content::Structure(flat_type)); + let done_record = Expr::Tuple { + tuple_var: done_tuple_var, + elems: tuple_elems, + }; + + return_type_var = { + let flat_type = FlatType::TagUnion( + UnionTags::for_result(subs, done_tuple_var, decode_err_var), + TagExt::Any(Variable::EMPTY_TAG_UNION), + ); + + synth_var(subs, Content::Structure(flat_type)) + }; + + Expr::Tag { + tag_union_var: return_type_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![(done_tuple_var, Loc::at_zero(done_record))], + } + }; + + // Unwrap each result in the decoded state + // + // when rec.e0 is + // Ok e0 -> ...happy path... + // Err NoElem -> Err TooShort + for (((symbol, field), &index_var), &result_index_var) in pattern_symbols + .iter() + .zip(state_fields) + .zip(index_vars) + .zip(state_field_vars) + .rev() + { + // when rec.e0 is + let cond_expr = Expr::RecordAccess { + record_var: state_record_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: result_index_var, + loc_expr: Box::new(Loc::at_zero(Expr::Var(state_arg_symbol, state_record_var))), + field: field.clone(), + }; + + // Example: `Ok x -> expr` + let ok_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: result_index_var, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Ok".into(), + arguments: vec![(index_var, Loc::at_zero(Pattern::Identifier(*symbol)))], + }), + degenerate: false, + }], + value: Loc::at_zero(body), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + // Example: `_ -> Err TooShort` + let err_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Underscore), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: return_type_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Expr::Tag { + tag_union_var: decode_err_var, + ext_var: Variable::EMPTY_TAG_UNION, + name: "TooShort".into(), + arguments: Vec::new(), + }), + )], + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + body = Expr::When { + loc_cond: Box::new(Loc::at_zero(cond_expr)), + cond_var: result_index_var, + expr_var: return_type_var, + region: Region::zero(), + branches: vec![ok_branch, err_branch], + branches_cond_var: result_index_var, + exhaustive: ExhaustiveMark::known_exhaustive(), + }; + } + + let function_var = synth_var(env.subs, Content::Error); // We'll fix this up in subs later. + let function_symbol = env.new_symbol("finalizer"); + let lambda_set = LambdaSet { + solved: UnionLambdas::tag_without_arguments(env.subs, function_symbol), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: function_var, + }; + let closure_type = synth_var(env.subs, Content::LambdaSet(lambda_set)); + let flat_type = FlatType::Func( + SubsSlice::insert_into_subs(env.subs, [state_record_var]), + closure_type, + return_type_var, + ); + + // Fix up function_var so it's not Content::Error anymore + env.subs + .set_content(function_var, Content::Structure(flat_type)); + + let finalizer = Expr::Closure(ClosureData { + function_type: function_var, + closure_type, + return_type: return_type_var, + name: function_symbol, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments: vec![( + state_record_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(state_arg_symbol)), + )], + loc_body: Box::new(Loc::at_zero(body)), + }); + + (finalizer, function_var, decode_err_var) +} + +// Example: +// initialState : {e0: Result a [NoElem], e1: Result b [NoElem]} +// initialState = {e0: Err NoElem, e1: Err NoElem} +fn initial_state( + env: &mut Env<'_>, + arity: u32, + index_vars: &mut Vec, + state_fields: &mut Vec, + state_field_vars: &mut Vec, +) -> (Variable, Expr) { + let mut initial_state_fields = SendMap::default(); + + for i in 0..arity { + let subs = &mut env.subs; + let index_var = subs.fresh_unnamed_flex_var(); + + index_vars.push(index_var); + + let state_field = Lowercase::from(format!("e{i}")); + state_fields.push(state_field.clone()); + + let no_index_label = "NoElem"; + let union_tags = UnionTags::tag_without_arguments(subs, no_index_label.into()); + let no_index_var = synth_var( + subs, + Content::Structure(FlatType::TagUnion( + union_tags, + TagExt::Any(Variable::EMPTY_TAG_UNION), + )), + ); + let no_index = Expr::Tag { + tag_union_var: no_index_var, + ext_var: Variable::EMPTY_TAG_UNION, + name: no_index_label.into(), + arguments: Vec::new(), + }; + let err_label = "Err"; + let union_tags = UnionTags::for_result(subs, index_var, no_index_var); + let result_var = synth_var( + subs, + Content::Structure(FlatType::TagUnion( + union_tags, + TagExt::Any(Variable::EMPTY_TAG_UNION), + )), + ); + let index_expr = Expr::Tag { + tag_union_var: result_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: err_label.into(), + arguments: vec![(no_index_var, Loc::at_zero(no_index))], + }; + state_field_vars.push(result_var); + let index = Field { + var: result_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(index_expr)), + }; + + initial_state_fields.insert(state_field, index); + } + + let subs = &mut env.subs; + let record_index_iter = state_fields + .iter() + .zip(state_field_vars.iter()) + .map(|(index_name, &var)| (index_name.clone(), RecordField::Required(var))); + let flat_type = FlatType::Record( + RecordFields::insert_into_subs(subs, record_index_iter), + Variable::EMPTY_RECORD, + ); + + let state_record_var = synth_var(subs, Content::Structure(flat_type)); + + ( + state_record_var, + Expr::Record { + record_var: state_record_var, + fields: initial_state_fields, + }, + ) +} diff --git a/crates/compiler/derive/src/util.rs b/crates/compiler/derive/src/util.rs index 7dab0d1606..2319dd2e12 100644 --- a/crates/compiler/derive/src/util.rs +++ b/crates/compiler/derive/src/util.rs @@ -18,19 +18,20 @@ pub(crate) struct Env<'a> { } impl Env<'_> { - pub fn new_symbol(&mut self, name_hint: &str) -> Symbol { + pub fn new_symbol(&mut self, name_hint: impl std::string::ToString) -> Symbol { if cfg!(any( debug_assertions, test, feature = "debug-derived-symbols" )) { let mut i = 0; + let hint = name_hint.to_string(); let debug_name = loop { i += 1; let name = if i == 1 { - name_hint.to_owned() + hint.clone() } else { - format!("{}{}", name_hint, i) + format!("{}{}", hint, i) }; if self.derived_ident_ids.get_id(&name).is_none() { break name; diff --git a/crates/compiler/test_derive/src/decoding.rs b/crates/compiler/test_derive/src/decoding.rs index 8fb618975e..14fe23abd1 100644 --- a/crates/compiler/test_derive/src/decoding.rs +++ b/crates/compiler/test_derive/src/decoding.rs @@ -175,3 +175,59 @@ fn record_2_fields() { ) }) } + +#[test] +fn tuple_2_fields() { + derive_test(Decoder, v!((v!(STR), v!(U8),)), |golden| { + assert_snapshot!(golden, @r###" + # derived for ( Str, U8 )* + # Decoder ( val, val1 )* fmt | fmt has DecoderFormatting, val has Decoding, val1 has Decoding + # List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok ( val, val1 )a] } | fmt has DecoderFormatting, val has Decoding, val1 has Decoding + # Specialization lambda sets: + # @<1>: [[custom(22)]] + #Derived.decoder_(arity:2) = + custom + \#Derived.bytes3, #Derived.fmt3 -> + decodeWith + #Derived.bytes3 + (tuple + { e1: Err NoElem, e0: Err NoElem } + \#Derived.stateRecord2, #Derived.index -> + when #Derived.index is + 0 -> + Next (custom + \#Derived.bytes, #Derived.fmt -> + when decodeWith #Derived.bytes decoder #Derived.fmt is + #Derived.rec -> + { + result: when #Derived.rec.result is + Ok #Derived.val -> + Ok { stateRecord2 & e0: Ok #Derived.val } + Err #Derived.err -> Err #Derived.err, + rest: #Derived.rec.rest + }) + 1 -> + Next (custom + \#Derived.bytes2, #Derived.fmt2 -> + when decodeWith #Derived.bytes2 decoder #Derived.fmt2 is + #Derived.rec2 -> + { + result: when #Derived.rec2.result is + Ok #Derived.val2 -> + Ok { stateRecord2 & e1: Ok #Derived.val2 } + Err #Derived.err2 -> Err #Derived.err2, + rest: #Derived.rec2.rest + }) + _ -> TooLong + \#Derived.stateRecord -> + when #Derived.stateRecord.e0 is + Ok #Derived.0 -> + when #Derived.stateRecord.e1 is + Ok #Derived.1 -> Ok ( #Derived.0, #Derived.1 ) + _ -> Err TooShort + _ -> Err TooShort) + #Derived.fmt3 + "### + ) + }) +} From 4d3bda51e17cfbe099f6e571271486f1f39d6fa5 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 16:55:23 -0500 Subject: [PATCH 19/24] Define naturality of tuple indices --- crates/compiler/derive/src/decoding/tuple.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs index 0c0cf13d01..2e0273d3f5 100644 --- a/crates/compiler/derive/src/decoding/tuple.rs +++ b/crates/compiler/derive/src/decoding/tuple.rs @@ -1,7 +1,7 @@ use roc_can::expr::{ AnnotatedMark, ClosureData, Expr, Field, IntValue, Recursive, WhenBranch, WhenBranchPattern, }; -use roc_can::num::IntBound; +use roc_can::num::{IntBound, IntLitWidth}; use roc_can::pattern::Pattern; use roc_collections::SendMap; use roc_module::called_via::CalledVia; @@ -635,11 +635,11 @@ fn step_elem( WhenBranch { patterns: vec![WhenBranchPattern { pattern: Loc::at_zero(Pattern::IntLiteral( - env.subs.fresh_unnamed_flex_var(), - env.subs.fresh_unnamed_flex_var(), + Variable::NAT, + Variable::NATURAL, index.to_string().into_boxed_str(), IntValue::I128((index as i128).to_ne_bytes()), - IntBound::None, + IntBound::Exact(IntLitWidth::Nat), )), degenerate: false, }], From 1b087df2879e5ccedf1e48fa5c725c1aa1bf5def Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 16:56:22 -0500 Subject: [PATCH 20/24] Add gen tests for decoding tuples --- crates/compiler/test_gen/src/gen_abilities.rs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index cb55dad8a9..91ce2bee60 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -1279,6 +1279,50 @@ fn decode_record_of_record() { ) } +#[test] +#[cfg(all( + any(feature = "gen-llvm", feature = "gen-wasm"), + not(debug_assertions) // https://github.com/roc-lang/roc/issues/3898 +))] +fn decode_tuple_two_elements() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Json] provides [main] to "./platform" + + main = + when Str.toUtf8 "[\"ab\",10]" |> Decode.fromBytes Json.fromUtf8 is + Ok ("ab", 10u8) -> "abcd" + _ -> "something went wrong" + "# + ), + RocStr::from("abcd"), + RocStr + ) +} + +#[test] +#[cfg(all( + any(feature = "gen-llvm", feature = "gen-wasm"), + not(debug_assertions) // https://github.com/roc-lang/roc/issues/3898 +))] +fn decode_tuple_of_tuples() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Json] provides [main] to "./platform" + + main = + when Str.toUtf8 "[[\"ab\",10],[\"cd\",25]]" |> Decode.fromBytes Json.fromUtf8 is + Ok ( ("ab", 10u8), ("cd", 25u8) ) -> "abcd" + _ -> "something went wrong" + "# + ), + RocStr::from("abcd"), + RocStr + ) +} + #[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))] mod hash { #[cfg(feature = "gen-llvm")] From f9f90a368415f9f10b4fcbc389146a9efd889816 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 16:58:12 -0500 Subject: [PATCH 21/24] Lints --- crates/compiler/derive/src/decoding/tuple.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs index 2e0273d3f5..8c4205322e 100644 --- a/crates/compiler/derive/src/decoding/tuple.rs +++ b/crates/compiler/derive/src/decoding/tuple.rs @@ -202,7 +202,7 @@ fn step_elem( }; for (((index, state_field), &index_var), &result_index_var) in state_fields - .into_iter() + .iter() .enumerate() .zip(index_vars) .zip(state_field_vars) From 825ed1c8b383910ae9e369095982c0dd9c0ca80c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 22 Mar 2023 17:18:23 -0500 Subject: [PATCH 22/24] Fully implement Decoding in EnvDecoding --- examples/cli/cli-platform/EnvDecoding.roc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/cli/cli-platform/EnvDecoding.roc b/examples/cli/cli-platform/EnvDecoding.roc index 62b25f3f9d..ccc4a9f2bb 100644 --- a/examples/cli/cli-platform/EnvDecoding.roc +++ b/examples/cli/cli-platform/EnvDecoding.roc @@ -19,6 +19,7 @@ EnvFormat := {} has [ string: envString, list: envList, record: envRecord, + tuple: envTuple, }, ] @@ -93,3 +94,10 @@ envList = \decodeElem -> Decode.custom \bytes, @EnvFormat {} -> envRecord : _, (_, _ -> [Keep (Decoder _ _), Skip]), (_ -> _) -> Decoder _ _ envRecord = \_initialState, _stepField, _finalizer -> Decode.custom \bytes, @EnvFormat {} -> { result: Err TooShort, rest: bytes } + +# TODO: we must currently annotate the arrows here so that the lambda sets are +# exercised, and the solver can find an ambient lambda set for the +# specialization. +envTuple : _, (_, _ -> [Next (Decoder _ _), TooLong]), (_ -> _) -> Decoder _ _ +envTuple = \_initialState, _stepElem, _finalizer -> Decode.custom \bytes, @EnvFormat {} -> + { result: Err TooShort, rest: bytes } From 5069d926bbbea21baa4aaeff0914721c2a1290ee Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 23 Mar 2023 10:18:04 -0500 Subject: [PATCH 23/24] Update mono tests --- .../encode_derived_nested_record_string.txt | 334 ++++++++-------- ...encode_derived_record_one_field_string.txt | 188 ++++----- ...ncode_derived_record_two_field_strings.txt | 188 ++++----- .../generated/encode_derived_string.txt | 52 +-- .../encode_derived_tag_one_field_string.txt | 204 +++++----- ...encode_derived_tag_two_payloads_string.txt | 204 +++++----- .../test_mono/generated/issue_4749.txt | 364 +++++++++--------- ..._4772_weakened_monomorphic_destructure.txt | 310 +++++++-------- ...not_duplicate_identical_concrete_types.txt | 206 +++++----- ...types_without_unification_of_unifiable.txt | 342 ++++++++-------- 10 files changed, 1196 insertions(+), 1196 deletions(-) diff --git a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt index 36559bdbd8..1ca601475b 100644 --- a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt @@ -1,5 +1,5 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.0 : Str = CallByName Encode.22 #Derived.1; + let #Derived_gen.0 : Str = CallByName Encode.23 #Derived.1; ret #Derived_gen.0; procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): @@ -8,11 +8,11 @@ procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6]; let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5; - let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4; + let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; procedure #Derived.5 (#Derived.6): - let #Derived_gen.14 : Str = CallByName Encode.22 #Derived.6; + let #Derived_gen.14 : Str = CallByName Encode.23 #Derived.6; ret #Derived_gen.14; procedure #Derived.7 (#Derived.8, #Derived.9, #Derived.6): @@ -21,195 +21,195 @@ procedure #Derived.7 (#Derived.8, #Derived.9, #Derived.6): let #Derived_gen.20 : {Str, Str} = Struct {#Derived_gen.21, #Derived_gen.22}; let #Derived_gen.19 : List {Str, Str} = Array [#Derived_gen.20]; let #Derived_gen.18 : List {Str, Str} = CallByName Json.20 #Derived_gen.19; - let #Derived_gen.17 : List U8 = CallByName Encode.23 #Derived.8 #Derived_gen.18 #Derived.9; + let #Derived_gen.17 : List U8 = CallByName Encode.24 #Derived.8 #Derived_gen.18 #Derived.9; ret #Derived_gen.17; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102; - ret Encode.106; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.111 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; + ret Encode.111; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.114 Encode.94 Encode.96 Encode.102; - ret Encode.113; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.118 : List U8 = CallByName Json.118 Encode.99 Encode.101 Encode.107; + ret Encode.118; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.115 : List U8 = CallByName #Derived.7 Encode.94 Encode.96 Encode.102; - ret Encode.115; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.120 : List U8 = CallByName #Derived.7 Encode.99 Encode.101 Encode.107; + ret Encode.120; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.125 : List U8 = CallByName Json.114 Encode.94 Encode.96 Encode.102; - ret Encode.125; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.130 : List U8 = CallByName Json.118 Encode.99 Encode.101 Encode.107; + ret Encode.130; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.128 : List U8 = CallByName Json.98 Encode.94 Encode.96 Encode.102; - ret Encode.128; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.133 : List U8 = CallByName Json.102 Encode.99 Encode.101 Encode.107; + ret Encode.133; -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 Encode.26 (Encode.105, Encode.106): + let Encode.109 : List U8 = Array []; + let Encode.110 : Str = CallByName #Derived.0 Encode.105; + let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; + ret Encode.108; procedure Json.1 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.114 (Json.115, Json.428, Json.113): - let Json.461 : I64 = 123i64; - let Json.460 : U8 = CallByName Num.127 Json.461; - let Json.117 : List U8 = CallByName List.4 Json.115 Json.460; - let Json.459 : U64 = CallByName List.6 Json.113; - let Json.436 : {List U8, U64} = Struct {Json.117, Json.459}; - let Json.437 : {} = Struct {}; - let Json.435 : {List U8, U64} = CallByName List.18 Json.113 Json.436 Json.437; - dec Json.113; - let Json.119 : List U8 = StructAtIndex 0 Json.435; - inc Json.119; - dec Json.435; - let Json.434 : I64 = 125i64; - let Json.433 : U8 = CallByName Num.127 Json.434; - let Json.432 : List U8 = CallByName List.4 Json.119 Json.433; - ret Json.432; +procedure Json.102 (Json.103, Json.562, Json.101): + let Json.571 : I64 = 34i64; + let Json.570 : U8 = CallByName Num.127 Json.571; + let Json.568 : List U8 = CallByName List.4 Json.103 Json.570; + let Json.569 : List U8 = CallByName Str.12 Json.101; + let Json.565 : List U8 = CallByName List.8 Json.568 Json.569; + let Json.567 : I64 = 34i64; + let Json.566 : U8 = CallByName Num.127 Json.567; + let Json.564 : List U8 = CallByName List.4 Json.565 Json.566; + ret Json.564; -procedure Json.114 (Json.115, Json.428, Json.113): - let Json.501 : I64 = 123i64; - let Json.500 : U8 = CallByName Num.127 Json.501; - let Json.117 : List U8 = CallByName List.4 Json.115 Json.500; - let Json.499 : U64 = CallByName List.6 Json.113; - let Json.476 : {List U8, U64} = Struct {Json.117, Json.499}; - let Json.477 : {} = Struct {}; - let Json.475 : {List U8, U64} = CallByName List.18 Json.113 Json.476 Json.477; - dec Json.113; - let Json.119 : List U8 = StructAtIndex 0 Json.475; - inc Json.119; - dec Json.475; - let Json.474 : I64 = 125i64; - let Json.473 : U8 = CallByName Num.127 Json.474; - let Json.472 : List U8 = CallByName List.4 Json.119 Json.473; - ret Json.472; - -procedure Json.116 (Json.430, Json.431): - let Json.122 : Str = StructAtIndex 0 Json.431; - inc Json.122; - let Json.123 : Str = StructAtIndex 1 Json.431; +procedure Json.118 (Json.119, Json.486, Json.117): + let Json.519 : I64 = 123i64; + let Json.518 : U8 = CallByName Num.127 Json.519; + let Json.121 : List U8 = CallByName List.4 Json.119 Json.518; + let Json.517 : U64 = CallByName List.6 Json.117; + let Json.494 : {List U8, U64} = Struct {Json.121, Json.517}; + let Json.495 : {} = Struct {}; + let Json.493 : {List U8, U64} = CallByName List.18 Json.117 Json.494 Json.495; + dec Json.117; + let Json.123 : List U8 = StructAtIndex 0 Json.493; inc Json.123; - dec Json.431; - let Json.120 : List U8 = StructAtIndex 0 Json.430; - inc Json.120; - let Json.121 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.458 : I64 = 34i64; - let Json.457 : U8 = CallByName Num.127 Json.458; - let Json.455 : List U8 = CallByName List.4 Json.120 Json.457; - let Json.456 : List U8 = CallByName Str.12 Json.122; - let Json.452 : List U8 = CallByName List.8 Json.455 Json.456; - let Json.454 : I64 = 34i64; - let Json.453 : U8 = CallByName Num.127 Json.454; - let Json.449 : List U8 = CallByName List.4 Json.452 Json.453; - let Json.451 : I64 = 58i64; - let Json.450 : U8 = CallByName Num.127 Json.451; - let Json.447 : List U8 = CallByName List.4 Json.449 Json.450; - let Json.448 : {} = Struct {}; - let Json.124 : List U8 = CallByName Encode.23 Json.447 Json.123 Json.448; - joinpoint Json.442 Json.125: - let Json.440 : U64 = 1i64; - let Json.439 : U64 = CallByName Num.20 Json.121 Json.440; - let Json.438 : {List U8, U64} = Struct {Json.125, Json.439}; - ret Json.438; - in - let Json.446 : U64 = 1i64; - let Json.443 : Int1 = CallByName Num.24 Json.121 Json.446; - if Json.443 then - let Json.445 : I64 = 44i64; - let Json.444 : U8 = CallByName Num.127 Json.445; - let Json.441 : List U8 = CallByName List.4 Json.124 Json.444; - jump Json.442 Json.441; - else - jump Json.442 Json.124; + dec Json.493; + let Json.492 : I64 = 125i64; + let Json.491 : U8 = CallByName Num.127 Json.492; + let Json.490 : List U8 = CallByName List.4 Json.123 Json.491; + ret Json.490; -procedure Json.116 (Json.430, Json.431): - let Json.122 : Str = StructAtIndex 0 Json.431; - inc Json.122; - let Json.123 : Str = StructAtIndex 1 Json.431; +procedure Json.118 (Json.119, Json.486, Json.117): + let Json.559 : I64 = 123i64; + let Json.558 : U8 = CallByName Num.127 Json.559; + let Json.121 : List U8 = CallByName List.4 Json.119 Json.558; + let Json.557 : U64 = CallByName List.6 Json.117; + let Json.534 : {List U8, U64} = Struct {Json.121, Json.557}; + let Json.535 : {} = Struct {}; + let Json.533 : {List U8, U64} = CallByName List.18 Json.117 Json.534 Json.535; + dec Json.117; + let Json.123 : List U8 = StructAtIndex 0 Json.533; inc Json.123; - dec Json.431; - let Json.120 : List U8 = StructAtIndex 0 Json.430; - inc Json.120; - let Json.121 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.498 : I64 = 34i64; - let Json.497 : U8 = CallByName Num.127 Json.498; - let Json.495 : List U8 = CallByName List.4 Json.120 Json.497; - let Json.496 : List U8 = CallByName Str.12 Json.122; - let Json.492 : List U8 = CallByName List.8 Json.495 Json.496; - let Json.494 : I64 = 34i64; - let Json.493 : U8 = CallByName Num.127 Json.494; - let Json.489 : List U8 = CallByName List.4 Json.492 Json.493; - let Json.491 : I64 = 58i64; - let Json.490 : U8 = CallByName Num.127 Json.491; - let Json.487 : List U8 = CallByName List.4 Json.489 Json.490; - let Json.488 : {} = Struct {}; - let Json.124 : List U8 = CallByName Encode.23 Json.487 Json.123 Json.488; - joinpoint Json.482 Json.125: - let Json.480 : U64 = 1i64; - let Json.479 : U64 = CallByName Num.20 Json.121 Json.480; - let Json.478 : {List U8, U64} = Struct {Json.125, Json.479}; - ret Json.478; - in - let Json.486 : U64 = 1i64; - let Json.483 : Int1 = CallByName Num.24 Json.121 Json.486; - if Json.483 then - let Json.485 : I64 = 44i64; - let Json.484 : U8 = CallByName Num.127 Json.485; - let Json.481 : List U8 = CallByName List.4 Json.124 Json.484; - jump Json.482 Json.481; - else - jump Json.482 Json.124; + dec Json.533; + let Json.532 : I64 = 125i64; + let Json.531 : U8 = CallByName Num.127 Json.532; + let Json.530 : List U8 = CallByName List.4 Json.123 Json.531; + ret Json.530; -procedure Json.18 (Json.97): - let Json.502 : Str = CallByName Encode.22 Json.97; - ret Json.502; - -procedure Json.20 (Json.113): - let Json.426 : List {Str, Str} = CallByName Encode.22 Json.113; - ret Json.426; - -procedure Json.20 (Json.113): - let Json.468 : List {Str, Str} = CallByName Encode.22 Json.113; - ret Json.468; - -procedure Json.98 (Json.99, Json.504, Json.97): - let Json.513 : I64 = 34i64; - let Json.512 : U8 = CallByName Num.127 Json.513; - let Json.510 : List U8 = CallByName List.4 Json.99 Json.512; - let Json.511 : List U8 = CallByName Str.12 Json.97; - let Json.507 : List U8 = CallByName List.8 Json.510 Json.511; - let Json.509 : I64 = 34i64; +procedure Json.120 (Json.488, Json.489): + let Json.126 : Str = StructAtIndex 0 Json.489; + inc Json.126; + let Json.127 : Str = StructAtIndex 1 Json.489; + inc Json.127; + dec Json.489; + let Json.124 : List U8 = StructAtIndex 0 Json.488; + inc Json.124; + let Json.125 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.516 : I64 = 34i64; + let Json.515 : U8 = CallByName Num.127 Json.516; + let Json.513 : List U8 = CallByName List.4 Json.124 Json.515; + let Json.514 : List U8 = CallByName Str.12 Json.126; + let Json.510 : List U8 = CallByName List.8 Json.513 Json.514; + let Json.512 : I64 = 34i64; + let Json.511 : U8 = CallByName Num.127 Json.512; + let Json.507 : List U8 = CallByName List.4 Json.510 Json.511; + let Json.509 : I64 = 58i64; let Json.508 : U8 = CallByName Num.127 Json.509; - let Json.506 : List U8 = CallByName List.4 Json.507 Json.508; - ret Json.506; + let Json.505 : List U8 = CallByName List.4 Json.507 Json.508; + let Json.506 : {} = Struct {}; + let Json.128 : List U8 = CallByName Encode.24 Json.505 Json.127 Json.506; + joinpoint Json.500 Json.129: + let Json.498 : U64 = 1i64; + let Json.497 : U64 = CallByName Num.20 Json.125 Json.498; + let Json.496 : {List U8, U64} = Struct {Json.129, Json.497}; + ret Json.496; + in + let Json.504 : U64 = 1i64; + let Json.501 : Int1 = CallByName Num.24 Json.125 Json.504; + if Json.501 then + let Json.503 : I64 = 44i64; + let Json.502 : U8 = CallByName Num.127 Json.503; + let Json.499 : List U8 = CallByName List.4 Json.128 Json.502; + jump Json.500 Json.499; + else + jump Json.500 Json.128; + +procedure Json.120 (Json.488, Json.489): + let Json.126 : Str = StructAtIndex 0 Json.489; + inc Json.126; + let Json.127 : Str = StructAtIndex 1 Json.489; + inc Json.127; + dec Json.489; + let Json.124 : List U8 = StructAtIndex 0 Json.488; + inc Json.124; + let Json.125 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.556 : I64 = 34i64; + let Json.555 : U8 = CallByName Num.127 Json.556; + let Json.553 : List U8 = CallByName List.4 Json.124 Json.555; + let Json.554 : List U8 = CallByName Str.12 Json.126; + let Json.550 : List U8 = CallByName List.8 Json.553 Json.554; + let Json.552 : I64 = 34i64; + let Json.551 : U8 = CallByName Num.127 Json.552; + let Json.547 : List U8 = CallByName List.4 Json.550 Json.551; + let Json.549 : I64 = 58i64; + let Json.548 : U8 = CallByName Num.127 Json.549; + let Json.545 : List U8 = CallByName List.4 Json.547 Json.548; + let Json.546 : {} = Struct {}; + let Json.128 : List U8 = CallByName Encode.24 Json.545 Json.127 Json.546; + joinpoint Json.540 Json.129: + let Json.538 : U64 = 1i64; + let Json.537 : U64 = CallByName Num.20 Json.125 Json.538; + let Json.536 : {List U8, U64} = Struct {Json.129, Json.537}; + ret Json.536; + in + let Json.544 : U64 = 1i64; + let Json.541 : Int1 = CallByName Num.24 Json.125 Json.544; + if Json.541 then + let Json.543 : I64 = 44i64; + let Json.542 : U8 = CallByName Num.127 Json.543; + let Json.539 : List U8 = CallByName List.4 Json.128 Json.542; + jump Json.540 Json.539; + else + jump Json.540 Json.128; + +procedure Json.18 (Json.101): + let Json.560 : Str = CallByName Encode.23 Json.101; + ret Json.560; + +procedure Json.20 (Json.117): + let Json.484 : List {Str, Str} = CallByName Encode.23 Json.117; + ret Json.484; + +procedure Json.20 (Json.117): + let Json.526 : List {Str, Str} = CallByName Encode.23 Json.117; + ret Json.526; procedure List.139 (List.140, List.141, List.138): - let List.535 : {List U8, U64} = CallByName Json.116 List.140 List.141; + let List.535 : {List U8, U64} = CallByName Json.120 List.140 List.141; ret List.535; procedure List.139 (List.140, List.141, List.138): - let List.608 : {List U8, U64} = CallByName Json.116 List.140 List.141; + let List.608 : {List U8, U64} = CallByName Json.120 List.140 List.141; ret List.608; procedure List.18 (List.136, List.137, List.138): @@ -348,7 +348,7 @@ procedure Str.9 (Str.77): procedure Test.0 (): let Test.12 : Str = "bar"; let Test.10 : {} = CallByName Json.1; - let Test.8 : List U8 = CallByName Encode.25 Test.12 Test.10; + let Test.8 : List U8 = CallByName Encode.26 Test.12 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; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt index 9b55419aa9..83c6ab0798 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt @@ -1,5 +1,5 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.0 : Str = CallByName Encode.22 #Derived.1; + let #Derived_gen.0 : Str = CallByName Encode.23 #Derived.1; ret #Derived_gen.0; procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): @@ -8,117 +8,117 @@ procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6]; let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5; - let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4; + let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102; - ret Encode.106; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.111 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; + ret Encode.111; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.114 Encode.94 Encode.96 Encode.102; - ret Encode.113; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.118 : List U8 = CallByName Json.118 Encode.99 Encode.101 Encode.107; + ret Encode.118; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.116 : List U8 = CallByName Json.98 Encode.94 Encode.96 Encode.102; - ret Encode.116; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.121 : List U8 = CallByName Json.102 Encode.99 Encode.101 Encode.107; + ret Encode.121; -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 Encode.26 (Encode.105, Encode.106): + let Encode.109 : List U8 = Array []; + let Encode.110 : Str = CallByName #Derived.0 Encode.105; + let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; + ret Encode.108; procedure Json.1 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.114 (Json.115, Json.428, Json.113): - let Json.461 : I64 = 123i64; - let Json.460 : U8 = CallByName Num.127 Json.461; - let Json.117 : List U8 = CallByName List.4 Json.115 Json.460; - let Json.459 : U64 = CallByName List.6 Json.113; - let Json.436 : {List U8, U64} = Struct {Json.117, Json.459}; - let Json.437 : {} = Struct {}; - let Json.435 : {List U8, U64} = CallByName List.18 Json.113 Json.436 Json.437; - dec Json.113; - let Json.119 : List U8 = StructAtIndex 0 Json.435; - inc Json.119; - dec Json.435; - let Json.434 : I64 = 125i64; - let Json.433 : U8 = CallByName Num.127 Json.434; - let Json.432 : List U8 = CallByName List.4 Json.119 Json.433; - ret Json.432; +procedure Json.102 (Json.103, Json.522, Json.101): + let Json.531 : I64 = 34i64; + let Json.530 : U8 = CallByName Num.127 Json.531; + let Json.528 : List U8 = CallByName List.4 Json.103 Json.530; + let Json.529 : List U8 = CallByName Str.12 Json.101; + let Json.525 : List U8 = CallByName List.8 Json.528 Json.529; + let Json.527 : I64 = 34i64; + let Json.526 : U8 = CallByName Num.127 Json.527; + let Json.524 : List U8 = CallByName List.4 Json.525 Json.526; + ret Json.524; -procedure Json.116 (Json.430, Json.431): - let Json.122 : Str = StructAtIndex 0 Json.431; - inc Json.122; - let Json.123 : Str = StructAtIndex 1 Json.431; +procedure Json.118 (Json.119, Json.486, Json.117): + let Json.519 : I64 = 123i64; + let Json.518 : U8 = CallByName Num.127 Json.519; + let Json.121 : List U8 = CallByName List.4 Json.119 Json.518; + let Json.517 : U64 = CallByName List.6 Json.117; + let Json.494 : {List U8, U64} = Struct {Json.121, Json.517}; + let Json.495 : {} = Struct {}; + let Json.493 : {List U8, U64} = CallByName List.18 Json.117 Json.494 Json.495; + dec Json.117; + let Json.123 : List U8 = StructAtIndex 0 Json.493; inc Json.123; - dec Json.431; - let Json.120 : List U8 = StructAtIndex 0 Json.430; - inc Json.120; - let Json.121 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.458 : I64 = 34i64; - let Json.457 : U8 = CallByName Num.127 Json.458; - let Json.455 : List U8 = CallByName List.4 Json.120 Json.457; - let Json.456 : List U8 = CallByName Str.12 Json.122; - let Json.452 : List U8 = CallByName List.8 Json.455 Json.456; - let Json.454 : I64 = 34i64; - let Json.453 : U8 = CallByName Num.127 Json.454; - let Json.449 : List U8 = CallByName List.4 Json.452 Json.453; - let Json.451 : I64 = 58i64; - let Json.450 : U8 = CallByName Num.127 Json.451; - let Json.447 : List U8 = CallByName List.4 Json.449 Json.450; - let Json.448 : {} = Struct {}; - let Json.124 : List U8 = CallByName Encode.23 Json.447 Json.123 Json.448; - joinpoint Json.442 Json.125: - let Json.440 : U64 = 1i64; - let Json.439 : U64 = CallByName Num.20 Json.121 Json.440; - let Json.438 : {List U8, U64} = Struct {Json.125, Json.439}; - ret Json.438; + dec Json.493; + let Json.492 : I64 = 125i64; + let Json.491 : U8 = CallByName Num.127 Json.492; + let Json.490 : List U8 = CallByName List.4 Json.123 Json.491; + ret Json.490; + +procedure Json.120 (Json.488, Json.489): + let Json.126 : Str = StructAtIndex 0 Json.489; + inc Json.126; + let Json.127 : Str = StructAtIndex 1 Json.489; + inc Json.127; + dec Json.489; + let Json.124 : List U8 = StructAtIndex 0 Json.488; + inc Json.124; + let Json.125 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.516 : I64 = 34i64; + let Json.515 : U8 = CallByName Num.127 Json.516; + let Json.513 : List U8 = CallByName List.4 Json.124 Json.515; + let Json.514 : List U8 = CallByName Str.12 Json.126; + let Json.510 : List U8 = CallByName List.8 Json.513 Json.514; + let Json.512 : I64 = 34i64; + let Json.511 : U8 = CallByName Num.127 Json.512; + let Json.507 : List U8 = CallByName List.4 Json.510 Json.511; + let Json.509 : I64 = 58i64; + let Json.508 : U8 = CallByName Num.127 Json.509; + let Json.505 : List U8 = CallByName List.4 Json.507 Json.508; + let Json.506 : {} = Struct {}; + let Json.128 : List U8 = CallByName Encode.24 Json.505 Json.127 Json.506; + joinpoint Json.500 Json.129: + let Json.498 : U64 = 1i64; + let Json.497 : U64 = CallByName Num.20 Json.125 Json.498; + let Json.496 : {List U8, U64} = Struct {Json.129, Json.497}; + ret Json.496; in - let Json.446 : U64 = 1i64; - let Json.443 : Int1 = CallByName Num.24 Json.121 Json.446; - if Json.443 then - let Json.445 : I64 = 44i64; - let Json.444 : U8 = CallByName Num.127 Json.445; - let Json.441 : List U8 = CallByName List.4 Json.124 Json.444; - jump Json.442 Json.441; + let Json.504 : U64 = 1i64; + let Json.501 : Int1 = CallByName Num.24 Json.125 Json.504; + if Json.501 then + let Json.503 : I64 = 44i64; + let Json.502 : U8 = CallByName Num.127 Json.503; + let Json.499 : List U8 = CallByName List.4 Json.128 Json.502; + jump Json.500 Json.499; else - jump Json.442 Json.124; + jump Json.500 Json.128; -procedure Json.18 (Json.97): - let Json.462 : Str = CallByName Encode.22 Json.97; - ret Json.462; +procedure Json.18 (Json.101): + let Json.520 : Str = CallByName Encode.23 Json.101; + ret Json.520; -procedure Json.20 (Json.113): - let Json.426 : List {Str, Str} = CallByName Encode.22 Json.113; - ret Json.426; - -procedure Json.98 (Json.99, Json.464, Json.97): - let Json.473 : I64 = 34i64; - let Json.472 : U8 = CallByName Num.127 Json.473; - let Json.470 : List U8 = CallByName List.4 Json.99 Json.472; - let Json.471 : List U8 = CallByName Str.12 Json.97; - let Json.467 : List U8 = CallByName List.8 Json.470 Json.471; - let Json.469 : I64 = 34i64; - let Json.468 : U8 = CallByName Num.127 Json.469; - let Json.466 : List U8 = CallByName List.4 Json.467 Json.468; - ret Json.466; +procedure Json.20 (Json.117): + let Json.484 : List {Str, Str} = CallByName Encode.23 Json.117; + ret Json.484; procedure List.139 (List.140, List.141, List.138): - let List.541 : {List U8, U64} = CallByName Json.116 List.140 List.141; + let List.541 : {List U8, U64} = CallByName Json.120 List.140 List.141; ret List.541; procedure List.18 (List.136, List.137, List.138): @@ -225,7 +225,7 @@ procedure Str.9 (Str.77): procedure Test.0 (): let Test.11 : Str = "foo"; let Test.10 : {} = CallByName Json.1; - let Test.8 : List U8 = CallByName Encode.25 Test.11 Test.10; + let Test.8 : List U8 = CallByName Encode.26 Test.11 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; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt index 656cdbd948..48701aa52b 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt @@ -1,5 +1,5 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.0 : {Str, Str} = CallByName Encode.22 #Derived.1; + let #Derived_gen.0 : {Str, Str} = CallByName Encode.23 #Derived.1; ret #Derived_gen.0; procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): @@ -16,117 +16,117 @@ procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.7 : {Str, Str} = Struct {#Derived_gen.8, #Derived_gen.9}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6, #Derived_gen.7]; let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5; - let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4; + let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102; - ret Encode.106; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.111 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; + ret Encode.111; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.114 Encode.94 Encode.96 Encode.102; - ret Encode.113; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.118 : List U8 = CallByName Json.118 Encode.99 Encode.101 Encode.107; + ret Encode.118; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.117 : List U8 = CallByName Json.98 Encode.94 Encode.96 Encode.102; - ret Encode.117; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.122 : List U8 = CallByName Json.102 Encode.99 Encode.101 Encode.107; + ret Encode.122; -procedure Encode.25 (Encode.100, Encode.101): - let Encode.104 : List U8 = Array []; - let Encode.105 : {Str, Str} = CallByName #Derived.0 Encode.100; - let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; - ret Encode.103; +procedure Encode.26 (Encode.105, Encode.106): + let Encode.109 : List U8 = Array []; + let Encode.110 : {Str, Str} = CallByName #Derived.0 Encode.105; + let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; + ret Encode.108; procedure Json.1 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.114 (Json.115, Json.428, Json.113): - let Json.461 : I64 = 123i64; - let Json.460 : U8 = CallByName Num.127 Json.461; - let Json.117 : List U8 = CallByName List.4 Json.115 Json.460; - let Json.459 : U64 = CallByName List.6 Json.113; - let Json.436 : {List U8, U64} = Struct {Json.117, Json.459}; - let Json.437 : {} = Struct {}; - let Json.435 : {List U8, U64} = CallByName List.18 Json.113 Json.436 Json.437; - dec Json.113; - let Json.119 : List U8 = StructAtIndex 0 Json.435; - inc Json.119; - dec Json.435; - let Json.434 : I64 = 125i64; - let Json.433 : U8 = CallByName Num.127 Json.434; - let Json.432 : List U8 = CallByName List.4 Json.119 Json.433; - ret Json.432; +procedure Json.102 (Json.103, Json.522, Json.101): + let Json.531 : I64 = 34i64; + let Json.530 : U8 = CallByName Num.127 Json.531; + let Json.528 : List U8 = CallByName List.4 Json.103 Json.530; + let Json.529 : List U8 = CallByName Str.12 Json.101; + let Json.525 : List U8 = CallByName List.8 Json.528 Json.529; + let Json.527 : I64 = 34i64; + let Json.526 : U8 = CallByName Num.127 Json.527; + let Json.524 : List U8 = CallByName List.4 Json.525 Json.526; + ret Json.524; -procedure Json.116 (Json.430, Json.431): - let Json.122 : Str = StructAtIndex 0 Json.431; - inc Json.122; - let Json.123 : Str = StructAtIndex 1 Json.431; +procedure Json.118 (Json.119, Json.486, Json.117): + let Json.519 : I64 = 123i64; + let Json.518 : U8 = CallByName Num.127 Json.519; + let Json.121 : List U8 = CallByName List.4 Json.119 Json.518; + let Json.517 : U64 = CallByName List.6 Json.117; + let Json.494 : {List U8, U64} = Struct {Json.121, Json.517}; + let Json.495 : {} = Struct {}; + let Json.493 : {List U8, U64} = CallByName List.18 Json.117 Json.494 Json.495; + dec Json.117; + let Json.123 : List U8 = StructAtIndex 0 Json.493; inc Json.123; - dec Json.431; - let Json.120 : List U8 = StructAtIndex 0 Json.430; - inc Json.120; - let Json.121 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.458 : I64 = 34i64; - let Json.457 : U8 = CallByName Num.127 Json.458; - let Json.455 : List U8 = CallByName List.4 Json.120 Json.457; - let Json.456 : List U8 = CallByName Str.12 Json.122; - let Json.452 : List U8 = CallByName List.8 Json.455 Json.456; - let Json.454 : I64 = 34i64; - let Json.453 : U8 = CallByName Num.127 Json.454; - let Json.449 : List U8 = CallByName List.4 Json.452 Json.453; - let Json.451 : I64 = 58i64; - let Json.450 : U8 = CallByName Num.127 Json.451; - let Json.447 : List U8 = CallByName List.4 Json.449 Json.450; - let Json.448 : {} = Struct {}; - let Json.124 : List U8 = CallByName Encode.23 Json.447 Json.123 Json.448; - joinpoint Json.442 Json.125: - let Json.440 : U64 = 1i64; - let Json.439 : U64 = CallByName Num.20 Json.121 Json.440; - let Json.438 : {List U8, U64} = Struct {Json.125, Json.439}; - ret Json.438; + dec Json.493; + let Json.492 : I64 = 125i64; + let Json.491 : U8 = CallByName Num.127 Json.492; + let Json.490 : List U8 = CallByName List.4 Json.123 Json.491; + ret Json.490; + +procedure Json.120 (Json.488, Json.489): + let Json.126 : Str = StructAtIndex 0 Json.489; + inc Json.126; + let Json.127 : Str = StructAtIndex 1 Json.489; + inc Json.127; + dec Json.489; + let Json.124 : List U8 = StructAtIndex 0 Json.488; + inc Json.124; + let Json.125 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.516 : I64 = 34i64; + let Json.515 : U8 = CallByName Num.127 Json.516; + let Json.513 : List U8 = CallByName List.4 Json.124 Json.515; + let Json.514 : List U8 = CallByName Str.12 Json.126; + let Json.510 : List U8 = CallByName List.8 Json.513 Json.514; + let Json.512 : I64 = 34i64; + let Json.511 : U8 = CallByName Num.127 Json.512; + let Json.507 : List U8 = CallByName List.4 Json.510 Json.511; + let Json.509 : I64 = 58i64; + let Json.508 : U8 = CallByName Num.127 Json.509; + let Json.505 : List U8 = CallByName List.4 Json.507 Json.508; + let Json.506 : {} = Struct {}; + let Json.128 : List U8 = CallByName Encode.24 Json.505 Json.127 Json.506; + joinpoint Json.500 Json.129: + let Json.498 : U64 = 1i64; + let Json.497 : U64 = CallByName Num.20 Json.125 Json.498; + let Json.496 : {List U8, U64} = Struct {Json.129, Json.497}; + ret Json.496; in - let Json.446 : U64 = 1i64; - let Json.443 : Int1 = CallByName Num.24 Json.121 Json.446; - if Json.443 then - let Json.445 : I64 = 44i64; - let Json.444 : U8 = CallByName Num.127 Json.445; - let Json.441 : List U8 = CallByName List.4 Json.124 Json.444; - jump Json.442 Json.441; + let Json.504 : U64 = 1i64; + let Json.501 : Int1 = CallByName Num.24 Json.125 Json.504; + if Json.501 then + let Json.503 : I64 = 44i64; + let Json.502 : U8 = CallByName Num.127 Json.503; + let Json.499 : List U8 = CallByName List.4 Json.128 Json.502; + jump Json.500 Json.499; else - jump Json.442 Json.124; + jump Json.500 Json.128; -procedure Json.18 (Json.97): - let Json.474 : Str = CallByName Encode.22 Json.97; - ret Json.474; +procedure Json.18 (Json.101): + let Json.532 : Str = CallByName Encode.23 Json.101; + ret Json.532; -procedure Json.20 (Json.113): - let Json.426 : List {Str, Str} = CallByName Encode.22 Json.113; - ret Json.426; - -procedure Json.98 (Json.99, Json.464, Json.97): - let Json.473 : I64 = 34i64; - let Json.472 : U8 = CallByName Num.127 Json.473; - let Json.470 : List U8 = CallByName List.4 Json.99 Json.472; - let Json.471 : List U8 = CallByName Str.12 Json.97; - let Json.467 : List U8 = CallByName List.8 Json.470 Json.471; - let Json.469 : I64 = 34i64; - let Json.468 : U8 = CallByName Num.127 Json.469; - let Json.466 : List U8 = CallByName List.4 Json.467 Json.468; - ret Json.466; +procedure Json.20 (Json.117): + let Json.484 : List {Str, Str} = CallByName Encode.23 Json.117; + ret Json.484; procedure List.139 (List.140, List.141, List.138): - let List.541 : {List U8, U64} = CallByName Json.116 List.140 List.141; + let List.541 : {List U8, U64} = CallByName Json.120 List.140 List.141; ret List.541; procedure List.18 (List.136, List.137, List.138): @@ -235,7 +235,7 @@ procedure Test.0 (): let Test.12 : Str = "bar"; let Test.9 : {Str, Str} = Struct {Test.11, Test.12}; let Test.10 : {} = CallByName Json.1; - let Test.8 : List U8 = CallByName Encode.25 Test.9 Test.10; + let Test.8 : List U8 = CallByName Encode.26 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; diff --git a/crates/compiler/test_mono/generated/encode_derived_string.txt b/crates/compiler/test_mono/generated/encode_derived_string.txt index 573cb87570..daf607feda 100644 --- a/crates/compiler/test_mono/generated/encode_derived_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_string.txt @@ -1,34 +1,34 @@ -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName Json.98 Encode.94 Encode.96 Encode.102; - ret Encode.106; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.111 : List U8 = CallByName Json.102 Encode.99 Encode.101 Encode.107; + ret Encode.111; -procedure Encode.25 (Encode.100, Encode.101): - let Encode.104 : List U8 = Array []; - let Encode.105 : Str = CallByName Json.18 Encode.100; - let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; - ret Encode.103; +procedure Encode.26 (Encode.105, Encode.106): + let Encode.109 : List U8 = Array []; + let Encode.110 : Str = CallByName Json.18 Encode.105; + let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; + ret Encode.108; procedure Json.1 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.18 (Json.97): - let Json.426 : Str = CallByName Encode.22 Json.97; - ret Json.426; +procedure Json.102 (Json.103, Json.486, Json.101): + let Json.495 : I64 = 34i64; + let Json.494 : U8 = CallByName Num.127 Json.495; + let Json.492 : List U8 = CallByName List.4 Json.103 Json.494; + let Json.493 : List U8 = CallByName Str.12 Json.101; + let Json.489 : List U8 = CallByName List.8 Json.492 Json.493; + let Json.491 : I64 = 34i64; + let Json.490 : U8 = CallByName Num.127 Json.491; + let Json.488 : List U8 = CallByName List.4 Json.489 Json.490; + ret Json.488; -procedure Json.98 (Json.99, Json.428, Json.97): - let Json.437 : I64 = 34i64; - let Json.436 : U8 = CallByName Num.127 Json.437; - let Json.434 : List U8 = CallByName List.4 Json.99 Json.436; - let Json.435 : List U8 = CallByName Str.12 Json.97; - let Json.431 : List U8 = CallByName List.8 Json.434 Json.435; - let Json.433 : I64 = 34i64; - let Json.432 : U8 = CallByName Num.127 Json.433; - let Json.430 : List U8 = CallByName List.4 Json.431 Json.432; - ret Json.430; +procedure Json.18 (Json.101): + let Json.484 : Str = CallByName Encode.23 Json.101; + ret Json.484; procedure List.4 (List.107, List.108): let List.503 : U64 = 1i64; @@ -86,7 +86,7 @@ procedure Str.9 (Str.77): 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.8 : List U8 = CallByName Encode.26 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; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt index b9b05fbcd2..179e245b1f 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt @@ -1,133 +1,133 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.0 : Str = CallByName Encode.22 #Derived.1; + let #Derived_gen.0 : Str = CallByName Encode.23 #Derived.1; ret #Derived_gen.0; procedure #Derived.3 (#Derived.4, #Derived.5, #Derived.1): joinpoint #Derived_gen.5 #Derived_gen.4: - let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.4 #Derived_gen.4 #Derived.5; + let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.4 #Derived_gen.4 #Derived.5; ret #Derived_gen.3; in let #Derived_gen.7 : Str = "A"; let #Derived_gen.9 : Str = CallByName Json.18 #Derived.1; let #Derived_gen.8 : List Str = Array [#Derived_gen.9]; - let #Derived_gen.6 : {Str, List Str} = CallByName Json.21 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.6 : {Str, List Str} = CallByName Json.22 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName #Derived.3 Encode.94 Encode.96 Encode.102; - ret Encode.106; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.111 : List U8 = CallByName #Derived.3 Encode.99 Encode.101 Encode.107; + ret Encode.111; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.128 Encode.94 Encode.96 Encode.102; - ret Encode.113; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.118 : List U8 = CallByName Json.144 Encode.99 Encode.101 Encode.107; + ret Encode.118; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.116 : List U8 = CallByName Json.98 Encode.94 Encode.96 Encode.102; - ret Encode.116; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.121 : List U8 = CallByName Json.102 Encode.99 Encode.101 Encode.107; + ret Encode.121; -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 Encode.26 (Encode.105, Encode.106): + let Encode.109 : List U8 = Array []; + let Encode.110 : Str = CallByName #Derived.0 Encode.105; + let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; + ret Encode.108; procedure Json.1 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.128 (Json.129, Json.428, #Attr.12): - let Json.127 : List Str = StructAtIndex 1 #Attr.12; - inc Json.127; - let Json.126 : Str = StructAtIndex 0 #Attr.12; - inc Json.126; +procedure Json.102 (Json.103, Json.527, Json.101): + let Json.536 : I64 = 34i64; + let Json.535 : U8 = CallByName Num.127 Json.536; + let Json.533 : List U8 = CallByName List.4 Json.103 Json.535; + let Json.534 : List U8 = CallByName Str.12 Json.101; + let Json.530 : List U8 = CallByName List.8 Json.533 Json.534; + let Json.532 : I64 = 34i64; + let Json.531 : U8 = CallByName Num.127 Json.532; + let Json.529 : List U8 = CallByName List.4 Json.530 Json.531; + ret Json.529; + +procedure Json.144 (Json.145, Json.486, #Attr.12): + let Json.143 : List Str = StructAtIndex 1 #Attr.12; + inc Json.143; + let Json.142 : Str = StructAtIndex 0 #Attr.12; + inc Json.142; dec #Attr.12; - let Json.466 : I64 = 123i64; - let Json.465 : U8 = CallByName Num.127 Json.466; - let Json.462 : List U8 = CallByName List.4 Json.129 Json.465; - let Json.464 : I64 = 34i64; - let Json.463 : U8 = CallByName Num.127 Json.464; - let Json.460 : List U8 = CallByName List.4 Json.462 Json.463; - let Json.461 : List U8 = CallByName Str.12 Json.126; - let Json.457 : List U8 = CallByName List.8 Json.460 Json.461; - let Json.459 : I64 = 34i64; - let Json.458 : U8 = CallByName Num.127 Json.459; - let Json.454 : List U8 = CallByName List.4 Json.457 Json.458; - let Json.456 : I64 = 58i64; - let Json.455 : U8 = CallByName Num.127 Json.456; - let Json.451 : List U8 = CallByName List.4 Json.454 Json.455; - let Json.453 : I64 = 91i64; - let Json.452 : U8 = CallByName Num.127 Json.453; - let Json.131 : List U8 = CallByName List.4 Json.451 Json.452; - let Json.450 : U64 = CallByName List.6 Json.127; - let Json.438 : {List U8, U64} = Struct {Json.131, Json.450}; - let Json.439 : {} = Struct {}; - let Json.437 : {List U8, U64} = CallByName List.18 Json.127 Json.438 Json.439; - dec Json.127; - let Json.133 : List U8 = StructAtIndex 0 Json.437; - inc Json.133; - dec Json.437; - let Json.436 : I64 = 93i64; - let Json.435 : U8 = CallByName Num.127 Json.436; - let Json.432 : List U8 = CallByName List.4 Json.133 Json.435; - let Json.434 : I64 = 125i64; - let Json.433 : U8 = CallByName Num.127 Json.434; - let Json.431 : List U8 = CallByName List.4 Json.432 Json.433; - ret Json.431; + let Json.524 : I64 = 123i64; + let Json.523 : U8 = CallByName Num.127 Json.524; + let Json.520 : List U8 = CallByName List.4 Json.145 Json.523; + let Json.522 : I64 = 34i64; + let Json.521 : U8 = CallByName Num.127 Json.522; + let Json.518 : List U8 = CallByName List.4 Json.520 Json.521; + let Json.519 : List U8 = CallByName Str.12 Json.142; + let Json.515 : List U8 = CallByName List.8 Json.518 Json.519; + let Json.517 : I64 = 34i64; + let Json.516 : U8 = CallByName Num.127 Json.517; + let Json.512 : List U8 = CallByName List.4 Json.515 Json.516; + let Json.514 : I64 = 58i64; + let Json.513 : U8 = CallByName Num.127 Json.514; + let Json.509 : List U8 = CallByName List.4 Json.512 Json.513; + let Json.511 : I64 = 91i64; + let Json.510 : U8 = CallByName Num.127 Json.511; + let Json.147 : List U8 = CallByName List.4 Json.509 Json.510; + let Json.508 : U64 = CallByName List.6 Json.143; + let Json.496 : {List U8, U64} = Struct {Json.147, Json.508}; + let Json.497 : {} = Struct {}; + let Json.495 : {List U8, U64} = CallByName List.18 Json.143 Json.496 Json.497; + dec Json.143; + let Json.149 : List U8 = StructAtIndex 0 Json.495; + inc Json.149; + dec Json.495; + let Json.494 : I64 = 93i64; + let Json.493 : U8 = CallByName Num.127 Json.494; + let Json.490 : List U8 = CallByName List.4 Json.149 Json.493; + let Json.492 : I64 = 125i64; + let Json.491 : U8 = CallByName Num.127 Json.492; + let Json.489 : List U8 = CallByName List.4 Json.490 Json.491; + ret Json.489; -procedure Json.130 (Json.430, Json.136): - let Json.134 : List U8 = StructAtIndex 0 Json.430; - inc Json.134; - let Json.135 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.449 : {} = Struct {}; - let Json.137 : List U8 = CallByName Encode.23 Json.134 Json.136 Json.449; - joinpoint Json.444 Json.138: - let Json.442 : U64 = 1i64; - let Json.441 : U64 = CallByName Num.20 Json.135 Json.442; - let Json.440 : {List U8, U64} = Struct {Json.138, Json.441}; - ret Json.440; +procedure Json.146 (Json.488, Json.152): + let Json.150 : List U8 = StructAtIndex 0 Json.488; + inc Json.150; + let Json.151 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.507 : {} = Struct {}; + let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.507; + joinpoint Json.502 Json.154: + let Json.500 : U64 = 1i64; + let Json.499 : U64 = CallByName Num.20 Json.151 Json.500; + let Json.498 : {List U8, U64} = Struct {Json.154, Json.499}; + ret Json.498; in - let Json.448 : U64 = 1i64; - let Json.445 : Int1 = CallByName Num.24 Json.135 Json.448; - if Json.445 then - let Json.447 : I64 = 44i64; - let Json.446 : U8 = CallByName Num.127 Json.447; - let Json.443 : List U8 = CallByName List.4 Json.137 Json.446; - jump Json.444 Json.443; + let Json.506 : U64 = 1i64; + let Json.503 : Int1 = CallByName Num.24 Json.151 Json.506; + if Json.503 then + let Json.505 : I64 = 44i64; + let Json.504 : U8 = CallByName Num.127 Json.505; + let Json.501 : List U8 = CallByName List.4 Json.153 Json.504; + jump Json.502 Json.501; else - jump Json.444 Json.137; + jump Json.502 Json.153; -procedure Json.18 (Json.97): - let Json.467 : Str = CallByName Encode.22 Json.97; - ret Json.467; +procedure Json.18 (Json.101): + let Json.525 : Str = CallByName Encode.23 Json.101; + ret Json.525; -procedure Json.21 (Json.126, Json.127): - let Json.427 : {Str, List Str} = Struct {Json.126, Json.127}; - let Json.426 : {Str, List Str} = CallByName Encode.22 Json.427; - ret Json.426; - -procedure Json.98 (Json.99, Json.469, Json.97): - let Json.478 : I64 = 34i64; - let Json.477 : U8 = CallByName Num.127 Json.478; - let Json.475 : List U8 = CallByName List.4 Json.99 Json.477; - let Json.476 : List U8 = CallByName Str.12 Json.97; - let Json.472 : List U8 = CallByName List.8 Json.475 Json.476; - let Json.474 : I64 = 34i64; - let Json.473 : U8 = CallByName Num.127 Json.474; - let Json.471 : List U8 = CallByName List.4 Json.472 Json.473; - ret Json.471; +procedure Json.22 (Json.142, Json.143): + let Json.485 : {Str, List Str} = Struct {Json.142, Json.143}; + let Json.484 : {Str, List Str} = CallByName Encode.23 Json.485; + ret Json.484; procedure List.139 (List.140, List.141, List.138): - let List.547 : {List U8, U64} = CallByName Json.130 List.140 List.141; + let List.547 : {List U8, U64} = CallByName Json.146 List.140 List.141; ret List.547; procedure List.18 (List.136, List.137, List.138): @@ -234,7 +234,7 @@ procedure Str.9 (Str.77): procedure Test.0 (): let Test.12 : Str = "foo"; let Test.11 : {} = CallByName Json.1; - let Test.10 : List U8 = CallByName Encode.25 Test.12 Test.11; + let Test.10 : List U8 = CallByName Encode.26 Test.12 Test.11; let Test.2 : [C {U64, U8}, C Str] = CallByName Str.9 Test.10; let Test.7 : U8 = 1i64; let Test.8 : U8 = GetTagId Test.2; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt index a9a3ea5dba..fbb31a4ace 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt @@ -1,10 +1,10 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.0 : {Str, Str} = CallByName Encode.22 #Derived.1; + let #Derived_gen.0 : {Str, Str} = CallByName Encode.23 #Derived.1; ret #Derived_gen.0; procedure #Derived.4 (#Derived.5, #Derived.6, #Derived.1): joinpoint #Derived_gen.5 #Derived_gen.4: - let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.5 #Derived_gen.4 #Derived.6; + let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.5 #Derived_gen.4 #Derived.6; ret #Derived_gen.3; in let #Derived.2 : Str = StructAtIndex 0 #Derived.1; @@ -16,124 +16,124 @@ procedure #Derived.4 (#Derived.5, #Derived.6, #Derived.1): let #Derived_gen.9 : Str = CallByName Json.18 #Derived.2; let #Derived_gen.10 : Str = CallByName Json.18 #Derived.3; let #Derived_gen.8 : List Str = Array [#Derived_gen.9, #Derived_gen.10]; - let #Derived_gen.6 : {Str, List Str} = CallByName Json.21 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.6 : {Str, List Str} = CallByName Json.22 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName #Derived.4 Encode.94 Encode.96 Encode.102; - ret Encode.106; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.111 : List U8 = CallByName #Derived.4 Encode.99 Encode.101 Encode.107; + ret Encode.111; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.128 Encode.94 Encode.96 Encode.102; - ret Encode.113; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.118 : List U8 = CallByName Json.144 Encode.99 Encode.101 Encode.107; + ret Encode.118; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.117 : List U8 = CallByName Json.98 Encode.94 Encode.96 Encode.102; - ret Encode.117; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.122 : List U8 = CallByName Json.102 Encode.99 Encode.101 Encode.107; + ret Encode.122; -procedure Encode.25 (Encode.100, Encode.101): - let Encode.104 : List U8 = Array []; - let Encode.105 : {Str, Str} = CallByName #Derived.0 Encode.100; - let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; - ret Encode.103; +procedure Encode.26 (Encode.105, Encode.106): + let Encode.109 : List U8 = Array []; + let Encode.110 : {Str, Str} = CallByName #Derived.0 Encode.105; + let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; + ret Encode.108; procedure Json.1 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.128 (Json.129, Json.428, #Attr.12): - let Json.127 : List Str = StructAtIndex 1 #Attr.12; - inc Json.127; - let Json.126 : Str = StructAtIndex 0 #Attr.12; - inc Json.126; +procedure Json.102 (Json.103, Json.527, Json.101): + let Json.536 : I64 = 34i64; + let Json.535 : U8 = CallByName Num.127 Json.536; + let Json.533 : List U8 = CallByName List.4 Json.103 Json.535; + let Json.534 : List U8 = CallByName Str.12 Json.101; + let Json.530 : List U8 = CallByName List.8 Json.533 Json.534; + let Json.532 : I64 = 34i64; + let Json.531 : U8 = CallByName Num.127 Json.532; + let Json.529 : List U8 = CallByName List.4 Json.530 Json.531; + ret Json.529; + +procedure Json.144 (Json.145, Json.486, #Attr.12): + let Json.143 : List Str = StructAtIndex 1 #Attr.12; + inc Json.143; + let Json.142 : Str = StructAtIndex 0 #Attr.12; + inc Json.142; dec #Attr.12; - let Json.466 : I64 = 123i64; - let Json.465 : U8 = CallByName Num.127 Json.466; - let Json.462 : List U8 = CallByName List.4 Json.129 Json.465; - let Json.464 : I64 = 34i64; - let Json.463 : U8 = CallByName Num.127 Json.464; - let Json.460 : List U8 = CallByName List.4 Json.462 Json.463; - let Json.461 : List U8 = CallByName Str.12 Json.126; - let Json.457 : List U8 = CallByName List.8 Json.460 Json.461; - let Json.459 : I64 = 34i64; - let Json.458 : U8 = CallByName Num.127 Json.459; - let Json.454 : List U8 = CallByName List.4 Json.457 Json.458; - let Json.456 : I64 = 58i64; - let Json.455 : U8 = CallByName Num.127 Json.456; - let Json.451 : List U8 = CallByName List.4 Json.454 Json.455; - let Json.453 : I64 = 91i64; - let Json.452 : U8 = CallByName Num.127 Json.453; - let Json.131 : List U8 = CallByName List.4 Json.451 Json.452; - let Json.450 : U64 = CallByName List.6 Json.127; - let Json.438 : {List U8, U64} = Struct {Json.131, Json.450}; - let Json.439 : {} = Struct {}; - let Json.437 : {List U8, U64} = CallByName List.18 Json.127 Json.438 Json.439; - dec Json.127; - let Json.133 : List U8 = StructAtIndex 0 Json.437; - inc Json.133; - dec Json.437; - let Json.436 : I64 = 93i64; - let Json.435 : U8 = CallByName Num.127 Json.436; - let Json.432 : List U8 = CallByName List.4 Json.133 Json.435; - let Json.434 : I64 = 125i64; - let Json.433 : U8 = CallByName Num.127 Json.434; - let Json.431 : List U8 = CallByName List.4 Json.432 Json.433; - ret Json.431; + let Json.524 : I64 = 123i64; + let Json.523 : U8 = CallByName Num.127 Json.524; + let Json.520 : List U8 = CallByName List.4 Json.145 Json.523; + let Json.522 : I64 = 34i64; + let Json.521 : U8 = CallByName Num.127 Json.522; + let Json.518 : List U8 = CallByName List.4 Json.520 Json.521; + let Json.519 : List U8 = CallByName Str.12 Json.142; + let Json.515 : List U8 = CallByName List.8 Json.518 Json.519; + let Json.517 : I64 = 34i64; + let Json.516 : U8 = CallByName Num.127 Json.517; + let Json.512 : List U8 = CallByName List.4 Json.515 Json.516; + let Json.514 : I64 = 58i64; + let Json.513 : U8 = CallByName Num.127 Json.514; + let Json.509 : List U8 = CallByName List.4 Json.512 Json.513; + let Json.511 : I64 = 91i64; + let Json.510 : U8 = CallByName Num.127 Json.511; + let Json.147 : List U8 = CallByName List.4 Json.509 Json.510; + let Json.508 : U64 = CallByName List.6 Json.143; + let Json.496 : {List U8, U64} = Struct {Json.147, Json.508}; + let Json.497 : {} = Struct {}; + let Json.495 : {List U8, U64} = CallByName List.18 Json.143 Json.496 Json.497; + dec Json.143; + let Json.149 : List U8 = StructAtIndex 0 Json.495; + inc Json.149; + dec Json.495; + let Json.494 : I64 = 93i64; + let Json.493 : U8 = CallByName Num.127 Json.494; + let Json.490 : List U8 = CallByName List.4 Json.149 Json.493; + let Json.492 : I64 = 125i64; + let Json.491 : U8 = CallByName Num.127 Json.492; + let Json.489 : List U8 = CallByName List.4 Json.490 Json.491; + ret Json.489; -procedure Json.130 (Json.430, Json.136): - let Json.134 : List U8 = StructAtIndex 0 Json.430; - inc Json.134; - let Json.135 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.449 : {} = Struct {}; - let Json.137 : List U8 = CallByName Encode.23 Json.134 Json.136 Json.449; - joinpoint Json.444 Json.138: - let Json.442 : U64 = 1i64; - let Json.441 : U64 = CallByName Num.20 Json.135 Json.442; - let Json.440 : {List U8, U64} = Struct {Json.138, Json.441}; - ret Json.440; +procedure Json.146 (Json.488, Json.152): + let Json.150 : List U8 = StructAtIndex 0 Json.488; + inc Json.150; + let Json.151 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.507 : {} = Struct {}; + let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.507; + joinpoint Json.502 Json.154: + let Json.500 : U64 = 1i64; + let Json.499 : U64 = CallByName Num.20 Json.151 Json.500; + let Json.498 : {List U8, U64} = Struct {Json.154, Json.499}; + ret Json.498; in - let Json.448 : U64 = 1i64; - let Json.445 : Int1 = CallByName Num.24 Json.135 Json.448; - if Json.445 then - let Json.447 : I64 = 44i64; - let Json.446 : U8 = CallByName Num.127 Json.447; - let Json.443 : List U8 = CallByName List.4 Json.137 Json.446; - jump Json.444 Json.443; + let Json.506 : U64 = 1i64; + let Json.503 : Int1 = CallByName Num.24 Json.151 Json.506; + if Json.503 then + let Json.505 : I64 = 44i64; + let Json.504 : U8 = CallByName Num.127 Json.505; + let Json.501 : List U8 = CallByName List.4 Json.153 Json.504; + jump Json.502 Json.501; else - jump Json.444 Json.137; + jump Json.502 Json.153; -procedure Json.18 (Json.97): - let Json.479 : Str = CallByName Encode.22 Json.97; - ret Json.479; +procedure Json.18 (Json.101): + let Json.537 : Str = CallByName Encode.23 Json.101; + ret Json.537; -procedure Json.21 (Json.126, Json.127): - let Json.427 : {Str, List Str} = Struct {Json.126, Json.127}; - let Json.426 : {Str, List Str} = CallByName Encode.22 Json.427; - ret Json.426; - -procedure Json.98 (Json.99, Json.469, Json.97): - let Json.478 : I64 = 34i64; - let Json.477 : U8 = CallByName Num.127 Json.478; - let Json.475 : List U8 = CallByName List.4 Json.99 Json.477; - let Json.476 : List U8 = CallByName Str.12 Json.97; - let Json.472 : List U8 = CallByName List.8 Json.475 Json.476; - let Json.474 : I64 = 34i64; - let Json.473 : U8 = CallByName Num.127 Json.474; - let Json.471 : List U8 = CallByName List.4 Json.472 Json.473; - ret Json.471; +procedure Json.22 (Json.142, Json.143): + let Json.485 : {Str, List Str} = Struct {Json.142, Json.143}; + let Json.484 : {Str, List Str} = CallByName Encode.23 Json.485; + ret Json.484; procedure List.139 (List.140, List.141, List.138): - let List.547 : {List U8, U64} = CallByName Json.130 List.140 List.141; + let List.547 : {List U8, U64} = CallByName Json.146 List.140 List.141; ret List.547; procedure List.18 (List.136, List.137, List.138): @@ -242,7 +242,7 @@ procedure Test.0 (): let Test.12 : Str = "foo"; let Test.1 : {Str, Str} = Struct {Test.12, Test.13}; let Test.11 : {} = CallByName Json.1; - let Test.10 : List U8 = CallByName Encode.25 Test.1 Test.11; + let Test.10 : List U8 = CallByName Encode.26 Test.1 Test.11; let Test.2 : [C {U64, U8}, C Str] = CallByName Str.9 Test.10; let Test.7 : U8 = 1i64; let Test.8 : U8 = GetTagId Test.2; diff --git a/crates/compiler/test_mono/generated/issue_4749.txt b/crates/compiler/test_mono/generated/issue_4749.txt index 3017d1fd0c..be73677d6c 100644 --- a/crates/compiler/test_mono/generated/issue_4749.txt +++ b/crates/compiler/test_mono/generated/issue_4749.txt @@ -28,229 +28,229 @@ procedure Bool.7 (Bool.19, Bool.20): let Bool.37 : Int1 = CallByName Bool.12 Bool.19 Bool.20; ret Bool.37; -procedure Decode.23 (Decode.94): - ret Decode.94; +procedure Decode.24 (Decode.101): + ret Decode.101; -procedure Decode.24 (Decode.95, Decode.114, Decode.97): - let Decode.127 : {List U8, [C {}, C Str]} = CallByName Json.299 Decode.95 Decode.97; - ret Decode.127; +procedure Decode.25 (Decode.102, Decode.121, Decode.104): + let Decode.134 : {List U8, [C {}, C Str]} = CallByName Json.315 Decode.102 Decode.104; + ret Decode.134; -procedure Decode.25 (Decode.98, Decode.99): - let Decode.126 : {} = CallByName Json.42; - let Decode.125 : {List U8, [C {}, C Str]} = CallByName Decode.24 Decode.98 Decode.126 Decode.99; - ret Decode.125; +procedure Decode.26 (Decode.105, Decode.106): + let Decode.133 : {} = CallByName Json.43; + let Decode.132 : {List U8, [C {}, C Str]} = CallByName Decode.25 Decode.105 Decode.133 Decode.106; + ret Decode.132; -procedure Decode.26 (Decode.100, Decode.101): - let Decode.115 : {List U8, [C {}, C Str]} = CallByName Decode.25 Decode.100 Decode.101; - let Decode.103 : List U8 = StructAtIndex 0 Decode.115; - inc Decode.103; - let Decode.102 : [C {}, C Str] = StructAtIndex 1 Decode.115; - inc Decode.102; - dec Decode.115; - let Decode.118 : Int1 = CallByName List.1 Decode.103; - if Decode.118 then - dec Decode.103; - let Decode.122 : U8 = 1i64; - let Decode.123 : U8 = GetTagId Decode.102; - let Decode.124 : Int1 = lowlevel Eq Decode.122 Decode.123; - if Decode.124 then - let Decode.104 : Str = UnionAtIndex (Id 1) (Index 0) Decode.102; - inc Decode.104; - dec Decode.102; - let Decode.119 : [C [C List U8, C ], C Str] = TagId(1) Decode.104; - ret Decode.119; +procedure Decode.27 (Decode.107, Decode.108): + let Decode.122 : {List U8, [C {}, C Str]} = CallByName Decode.26 Decode.107 Decode.108; + let Decode.110 : List U8 = StructAtIndex 0 Decode.122; + inc Decode.110; + let Decode.109 : [C {}, C Str] = StructAtIndex 1 Decode.122; + inc Decode.109; + dec Decode.122; + let Decode.125 : Int1 = CallByName List.1 Decode.110; + if Decode.125 then + dec Decode.110; + let Decode.129 : U8 = 1i64; + let Decode.130 : U8 = GetTagId Decode.109; + let Decode.131 : Int1 = lowlevel Eq Decode.129 Decode.130; + if Decode.131 then + let Decode.111 : Str = UnionAtIndex (Id 1) (Index 0) Decode.109; + inc Decode.111; + dec Decode.109; + let Decode.126 : [C [C List U8, C ], C Str] = TagId(1) Decode.111; + ret Decode.126; else - dec Decode.102; - let Decode.121 : [C List U8, C ] = TagId(1) ; - let Decode.120 : [C [C List U8, C ], C Str] = TagId(0) Decode.121; - ret Decode.120; + dec Decode.109; + let Decode.128 : [C List U8, C ] = TagId(1) ; + let Decode.127 : [C [C List U8, C ], C Str] = TagId(0) Decode.128; + ret Decode.127; else - dec Decode.102; - let Decode.117 : [C List U8, C ] = TagId(0) Decode.103; - let Decode.116 : [C [C List U8, C ], C Str] = TagId(0) Decode.117; - ret Decode.116; + dec Decode.109; + let Decode.124 : [C List U8, C ] = TagId(0) Decode.110; + let Decode.123 : [C [C List U8, C ], C Str] = TagId(0) Decode.124; + ret Decode.123; -procedure Json.144 (Json.512, Json.513): - joinpoint Json.450 Json.447 Json.143: - let Json.146 : List U8 = StructAtIndex 0 Json.447; - inc Json.146; - let Json.145 : List U8 = StructAtIndex 1 Json.447; - inc Json.145; - dec Json.447; - joinpoint Json.490: - let Json.487 : {List U8, List U8} = Struct {Json.146, Json.145}; - ret Json.487; +procedure Json.160 (Json.570, Json.571): + joinpoint Json.508 Json.505 Json.159: + let Json.162 : List U8 = StructAtIndex 0 Json.505; + inc Json.162; + let Json.161 : List U8 = StructAtIndex 1 Json.505; + inc Json.161; + dec Json.505; + joinpoint Json.548: + let Json.545 : {List U8, List U8} = Struct {Json.162, Json.161}; + ret Json.545; in - let Json.496 : U64 = lowlevel ListLen Json.146; - let Json.497 : U64 = 2i64; - let Json.498 : Int1 = lowlevel NumGte Json.496 Json.497; - if Json.498 then - let Json.489 : U64 = 0i64; - let Json.147 : U8 = lowlevel ListGetUnsafe Json.146 Json.489; - let Json.488 : U64 = 1i64; - let Json.148 : U8 = lowlevel ListGetUnsafe Json.146 Json.488; - let Json.458 : Int1 = CallByName Json.22 Json.147 Json.148; - if Json.458 then - let Json.465 : U64 = 2i64; - let Json.462 : List U8 = CallByName List.29 Json.146 Json.465; - let Json.464 : List U8 = CallByName List.4 Json.145 Json.147; - let Json.463 : List U8 = CallByName List.4 Json.464 Json.148; - let Json.460 : {List U8, List U8} = Struct {Json.462, Json.463}; - jump Json.450 Json.460 Json.143; + let Json.554 : U64 = lowlevel ListLen Json.162; + let Json.555 : U64 = 2i64; + let Json.556 : Int1 = lowlevel NumGte Json.554 Json.555; + if Json.556 then + let Json.547 : U64 = 0i64; + let Json.163 : U8 = lowlevel ListGetUnsafe Json.162 Json.547; + let Json.546 : U64 = 1i64; + let Json.164 : U8 = lowlevel ListGetUnsafe Json.162 Json.546; + let Json.516 : Int1 = CallByName Json.23 Json.163 Json.164; + if Json.516 then + let Json.523 : U64 = 2i64; + let Json.520 : List U8 = CallByName List.29 Json.162 Json.523; + let Json.522 : List U8 = CallByName List.4 Json.161 Json.163; + let Json.521 : List U8 = CallByName List.4 Json.522 Json.164; + let Json.518 : {List U8, List U8} = Struct {Json.520, Json.521}; + jump Json.508 Json.518 Json.159; else - let Json.452 : Int1 = CallByName Json.289 Json.147; - if Json.452 then - let Json.456 : List U8 = CallByName List.38 Json.146; - let Json.457 : List U8 = CallByName List.4 Json.145 Json.147; - let Json.454 : {List U8, List U8} = Struct {Json.456, Json.457}; - jump Json.450 Json.454 Json.143; + let Json.510 : Int1 = CallByName Json.305 Json.163; + if Json.510 then + let Json.514 : List U8 = CallByName List.38 Json.162; + let Json.515 : List U8 = CallByName List.4 Json.161 Json.163; + let Json.512 : {List U8, List U8} = Struct {Json.514, Json.515}; + jump Json.508 Json.512 Json.159; else - let Json.451 : {List U8, List U8} = Struct {Json.146, Json.145}; - ret Json.451; + let Json.509 : {List U8, List U8} = Struct {Json.162, Json.161}; + ret Json.509; else - let Json.493 : U64 = lowlevel ListLen Json.146; - let Json.494 : U64 = 1i64; - let Json.495 : Int1 = lowlevel NumGte Json.493 Json.494; - if Json.495 then - let Json.492 : U64 = 0i64; - let Json.149 : U8 = lowlevel ListGetUnsafe Json.146 Json.492; - joinpoint Json.485 Json.491: - if Json.491 then - let Json.483 : List U8 = CallByName List.38 Json.146; - let Json.484 : List U8 = CallByName List.4 Json.145 Json.149; - let Json.481 : {List U8, List U8} = Struct {Json.483, Json.484}; - jump Json.450 Json.481 Json.143; + let Json.551 : U64 = lowlevel ListLen Json.162; + let Json.552 : U64 = 1i64; + let Json.553 : Int1 = lowlevel NumGte Json.551 Json.552; + if Json.553 then + let Json.550 : U64 = 0i64; + let Json.165 : U8 = lowlevel ListGetUnsafe Json.162 Json.550; + joinpoint Json.543 Json.549: + if Json.549 then + let Json.541 : List U8 = CallByName List.38 Json.162; + let Json.542 : List U8 = CallByName List.4 Json.161 Json.165; + let Json.539 : {List U8, List U8} = Struct {Json.541, Json.542}; + jump Json.508 Json.539 Json.159; else - jump Json.490; + jump Json.548; in - let Json.486 : Int1 = CallByName Json.289 Json.149; - jump Json.485 Json.486; + let Json.544 : Int1 = CallByName Json.305 Json.165; + jump Json.543 Json.544; else - jump Json.490; + jump Json.548; in - jump Json.450 Json.512 Json.513; + jump Json.508 Json.570 Json.571; procedure Json.2 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.22 (Json.139, Json.140): - let Json.466 : {U8, U8} = Struct {Json.139, Json.140}; - joinpoint Json.475: - let Json.474 : Int1 = CallByName Bool.1; - ret Json.474; +procedure Json.23 (Json.155, Json.156): + let Json.524 : {U8, U8} = Struct {Json.155, Json.156}; + joinpoint Json.533: + let Json.532 : Int1 = CallByName Bool.1; + ret Json.532; in - let Json.477 : U8 = StructAtIndex 0 Json.466; - let Json.478 : U8 = 92i64; - let Json.479 : Int1 = lowlevel Eq Json.478 Json.477; - if Json.479 then - let Json.476 : U8 = StructAtIndex 1 Json.466; - switch Json.476: + let Json.535 : U8 = StructAtIndex 0 Json.524; + let Json.536 : U8 = 92i64; + let Json.537 : Int1 = lowlevel Eq Json.536 Json.535; + if Json.537 then + let Json.534 : U8 = StructAtIndex 1 Json.524; + switch Json.534: case 98: - let Json.467 : Int1 = CallByName Bool.2; - ret Json.467; + let Json.525 : Int1 = CallByName Bool.2; + ret Json.525; case 102: - let Json.468 : Int1 = CallByName Bool.2; - ret Json.468; + let Json.526 : Int1 = CallByName Bool.2; + ret Json.526; case 110: - let Json.469 : Int1 = CallByName Bool.2; - ret Json.469; + let Json.527 : Int1 = CallByName Bool.2; + ret Json.527; case 114: - let Json.470 : Int1 = CallByName Bool.2; - ret Json.470; + let Json.528 : Int1 = CallByName Bool.2; + ret Json.528; case 116: - let Json.471 : Int1 = CallByName Bool.2; - ret Json.471; + let Json.529 : Int1 = CallByName Bool.2; + ret Json.529; case 34: - let Json.472 : Int1 = CallByName Bool.2; - ret Json.472; + let Json.530 : Int1 = CallByName Bool.2; + ret Json.530; case 92: - let Json.473 : Int1 = CallByName Bool.2; - ret Json.473; + let Json.531 : Int1 = CallByName Bool.2; + ret Json.531; default: - jump Json.475; + jump Json.533; else - jump Json.475; + jump Json.533; -procedure Json.23 (Json.142, Json.143): - let Json.500 : List U8 = Array []; - let Json.449 : {List U8, List U8} = Struct {Json.142, Json.500}; - let Json.448 : {List U8, List U8} = CallByName Json.144 Json.449 Json.143; - ret Json.448; +procedure Json.24 (Json.158, Json.159): + let Json.558 : List U8 = Array []; + let Json.507 : {List U8, List U8} = Struct {Json.158, Json.558}; + let Json.506 : {List U8, List U8} = CallByName Json.160 Json.507 Json.159; + ret Json.506; -procedure Json.289 (Json.290): - let Json.502 : U8 = 34i64; - let Json.501 : Int1 = CallByName Bool.7 Json.290 Json.502; - ret Json.501; +procedure Json.305 (Json.306): + let Json.560 : U8 = 34i64; + let Json.559 : Int1 = CallByName Bool.7 Json.306 Json.560; + ret Json.559; -procedure Json.299 (Json.300, Json.428): - let Json.429 : {List U8, [C {}, C Str]} = CallByName Json.41 Json.300; - ret Json.429; +procedure Json.315 (Json.316, Json.486): + let Json.487 : {List U8, [C {}, C Str]} = CallByName Json.42 Json.316; + ret Json.487; -procedure Json.41 (Json.282): - let Json.506 : U64 = 1i64; - inc Json.282; - let Json.505 : {List U8, List U8} = CallByName List.52 Json.282 Json.506; - let Json.283 : List U8 = StructAtIndex 0 Json.505; - inc Json.283; - let Json.285 : List U8 = StructAtIndex 1 Json.505; - inc Json.285; - dec Json.505; - let Json.504 : U8 = 34i64; - let Json.503 : List U8 = Array [Json.504]; - let Json.433 : Int1 = CallByName Bool.11 Json.283 Json.503; - dec Json.503; - dec Json.283; - if Json.433 then - dec Json.282; - let Json.446 : {} = Struct {}; - let Json.445 : {List U8, List U8} = CallByName Json.23 Json.285 Json.446; - let Json.288 : List U8 = StructAtIndex 0 Json.445; - inc Json.288; - let Json.287 : List U8 = StructAtIndex 1 Json.445; - inc Json.287; - dec Json.445; - let Json.434 : [C {U64, U8}, C Str] = CallByName Str.9 Json.287; - let Json.442 : U8 = 1i64; - let Json.443 : U8 = GetTagId Json.434; - let Json.444 : Int1 = lowlevel Eq Json.442 Json.443; - if Json.444 then - let Json.291 : Str = UnionAtIndex (Id 1) (Index 0) Json.434; - inc Json.291; - dec Json.434; - let Json.438 : U64 = 1i64; - let Json.437 : {List U8, List U8} = CallByName List.52 Json.288 Json.438; - let Json.293 : List U8 = StructAtIndex 1 Json.437; - inc Json.293; - dec Json.437; - let Json.436 : [C {}, C Str] = TagId(1) Json.291; - let Json.435 : {List U8, [C {}, C Str]} = Struct {Json.293, Json.436}; - ret Json.435; +procedure Json.42 (Json.298): + let Json.564 : U64 = 1i64; + inc Json.298; + let Json.563 : {List U8, List U8} = CallByName List.52 Json.298 Json.564; + let Json.299 : List U8 = StructAtIndex 0 Json.563; + inc Json.299; + let Json.301 : List U8 = StructAtIndex 1 Json.563; + inc Json.301; + dec Json.563; + let Json.562 : U8 = 34i64; + let Json.561 : List U8 = Array [Json.562]; + let Json.491 : Int1 = CallByName Bool.11 Json.299 Json.561; + dec Json.561; + dec Json.299; + if Json.491 then + dec Json.298; + let Json.504 : {} = Struct {}; + let Json.503 : {List U8, List U8} = CallByName Json.24 Json.301 Json.504; + let Json.304 : List U8 = StructAtIndex 0 Json.503; + inc Json.304; + let Json.303 : List U8 = StructAtIndex 1 Json.503; + inc Json.303; + dec Json.503; + let Json.492 : [C {U64, U8}, C Str] = CallByName Str.9 Json.303; + let Json.500 : U8 = 1i64; + let Json.501 : U8 = GetTagId Json.492; + let Json.502 : Int1 = lowlevel Eq Json.500 Json.501; + if Json.502 then + let Json.307 : Str = UnionAtIndex (Id 1) (Index 0) Json.492; + inc Json.307; + dec Json.492; + let Json.496 : U64 = 1i64; + let Json.495 : {List U8, List U8} = CallByName List.52 Json.304 Json.496; + let Json.309 : List U8 = StructAtIndex 1 Json.495; + inc Json.309; + dec Json.495; + let Json.494 : [C {}, C Str] = TagId(1) Json.307; + let Json.493 : {List U8, [C {}, C Str]} = Struct {Json.309, Json.494}; + ret Json.493; else - dec Json.434; - let Json.441 : {} = Struct {}; - let Json.440 : [C {}, C Str] = TagId(0) Json.441; - let Json.439 : {List U8, [C {}, C Str]} = Struct {Json.288, Json.440}; - ret Json.439; + dec Json.492; + let Json.499 : {} = Struct {}; + let Json.498 : [C {}, C Str] = TagId(0) Json.499; + let Json.497 : {List U8, [C {}, C Str]} = Struct {Json.304, Json.498}; + ret Json.497; else - dec Json.285; - let Json.432 : {} = Struct {}; - let Json.431 : [C {}, C Str] = TagId(0) Json.432; - let Json.430 : {List U8, [C {}, C Str]} = Struct {Json.282, Json.431}; - ret Json.430; + dec Json.301; + let Json.490 : {} = Struct {}; + let Json.489 : [C {}, C Str] = TagId(0) Json.490; + let Json.488 : {List U8, [C {}, C Str]} = Struct {Json.298, Json.489}; + ret Json.488; -procedure Json.42 (): - let Json.427 : {} = Struct {}; - let Json.426 : {} = CallByName Decode.23 Json.427; - ret Json.426; +procedure Json.43 (): + let Json.485 : {} = Struct {}; + let Json.484 : {} = CallByName Decode.24 Json.485; + ret Json.484; procedure List.1 (List.95): let List.495 : U64 = CallByName List.6 List.95; @@ -374,7 +374,7 @@ procedure Test.3 (): let Test.0 : List U8 = Array [82i64, 111i64, 99i64]; let Test.8 : {} = CallByName Json.2; inc Test.0; - let Test.1 : [C [C List U8, C ], C Str] = CallByName Decode.26 Test.0 Test.8; + let Test.1 : [C [C List U8, C ], C Str] = CallByName Decode.27 Test.0 Test.8; let Test.7 : Str = "Roc"; let Test.6 : [C [C List U8, C ], C Str] = TagId(1) Test.7; inc Test.1; diff --git a/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt b/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt index dc1552a722..98a24f5b81 100644 --- a/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt +++ b/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt @@ -32,199 +32,199 @@ procedure Bool.7 (Bool.19, Bool.20): let Bool.37 : Int1 = CallByName Bool.12 Bool.19 Bool.20; ret Bool.37; -procedure Decode.23 (Decode.94): - ret Decode.94; +procedure Decode.24 (Decode.101): + ret Decode.101; -procedure Decode.24 (Decode.95, Decode.114, Decode.97): - let Decode.117 : {List U8, [C {}, C Str]} = CallByName Json.299 Decode.95 Decode.97; - ret Decode.117; +procedure Decode.25 (Decode.102, Decode.121, Decode.104): + let Decode.124 : {List U8, [C {}, C Str]} = CallByName Json.315 Decode.102 Decode.104; + ret Decode.124; -procedure Decode.25 (Decode.98, Decode.99): - let Decode.116 : {} = CallByName Json.42; - let Decode.115 : {List U8, [C {}, C Str]} = CallByName Decode.24 Decode.98 Decode.116 Decode.99; - ret Decode.115; +procedure Decode.26 (Decode.105, Decode.106): + let Decode.123 : {} = CallByName Json.43; + let Decode.122 : {List U8, [C {}, C Str]} = CallByName Decode.25 Decode.105 Decode.123 Decode.106; + ret Decode.122; -procedure Json.144 (Json.512, Json.513): - joinpoint Json.450 Json.447 Json.143: - let Json.146 : List U8 = StructAtIndex 0 Json.447; - inc Json.146; - let Json.145 : List U8 = StructAtIndex 1 Json.447; - inc Json.145; - dec Json.447; - joinpoint Json.490: - let Json.487 : {List U8, List U8} = Struct {Json.146, Json.145}; - ret Json.487; +procedure Json.160 (Json.570, Json.571): + joinpoint Json.508 Json.505 Json.159: + let Json.162 : List U8 = StructAtIndex 0 Json.505; + inc Json.162; + let Json.161 : List U8 = StructAtIndex 1 Json.505; + inc Json.161; + dec Json.505; + joinpoint Json.548: + let Json.545 : {List U8, List U8} = Struct {Json.162, Json.161}; + ret Json.545; in - let Json.496 : U64 = lowlevel ListLen Json.146; - let Json.497 : U64 = 2i64; - let Json.498 : Int1 = lowlevel NumGte Json.496 Json.497; - if Json.498 then - let Json.489 : U64 = 0i64; - let Json.147 : U8 = lowlevel ListGetUnsafe Json.146 Json.489; - let Json.488 : U64 = 1i64; - let Json.148 : U8 = lowlevel ListGetUnsafe Json.146 Json.488; - let Json.458 : Int1 = CallByName Json.22 Json.147 Json.148; - if Json.458 then - let Json.465 : U64 = 2i64; - let Json.462 : List U8 = CallByName List.29 Json.146 Json.465; - let Json.464 : List U8 = CallByName List.4 Json.145 Json.147; - let Json.463 : List U8 = CallByName List.4 Json.464 Json.148; - let Json.460 : {List U8, List U8} = Struct {Json.462, Json.463}; - jump Json.450 Json.460 Json.143; + let Json.554 : U64 = lowlevel ListLen Json.162; + let Json.555 : U64 = 2i64; + let Json.556 : Int1 = lowlevel NumGte Json.554 Json.555; + if Json.556 then + let Json.547 : U64 = 0i64; + let Json.163 : U8 = lowlevel ListGetUnsafe Json.162 Json.547; + let Json.546 : U64 = 1i64; + let Json.164 : U8 = lowlevel ListGetUnsafe Json.162 Json.546; + let Json.516 : Int1 = CallByName Json.23 Json.163 Json.164; + if Json.516 then + let Json.523 : U64 = 2i64; + let Json.520 : List U8 = CallByName List.29 Json.162 Json.523; + let Json.522 : List U8 = CallByName List.4 Json.161 Json.163; + let Json.521 : List U8 = CallByName List.4 Json.522 Json.164; + let Json.518 : {List U8, List U8} = Struct {Json.520, Json.521}; + jump Json.508 Json.518 Json.159; else - let Json.452 : Int1 = CallByName Json.289 Json.147; - if Json.452 then - let Json.456 : List U8 = CallByName List.38 Json.146; - let Json.457 : List U8 = CallByName List.4 Json.145 Json.147; - let Json.454 : {List U8, List U8} = Struct {Json.456, Json.457}; - jump Json.450 Json.454 Json.143; + let Json.510 : Int1 = CallByName Json.305 Json.163; + if Json.510 then + let Json.514 : List U8 = CallByName List.38 Json.162; + let Json.515 : List U8 = CallByName List.4 Json.161 Json.163; + let Json.512 : {List U8, List U8} = Struct {Json.514, Json.515}; + jump Json.508 Json.512 Json.159; else - let Json.451 : {List U8, List U8} = Struct {Json.146, Json.145}; - ret Json.451; + let Json.509 : {List U8, List U8} = Struct {Json.162, Json.161}; + ret Json.509; else - let Json.493 : U64 = lowlevel ListLen Json.146; - let Json.494 : U64 = 1i64; - let Json.495 : Int1 = lowlevel NumGte Json.493 Json.494; - if Json.495 then - let Json.492 : U64 = 0i64; - let Json.149 : U8 = lowlevel ListGetUnsafe Json.146 Json.492; - joinpoint Json.485 Json.491: - if Json.491 then - let Json.483 : List U8 = CallByName List.38 Json.146; - let Json.484 : List U8 = CallByName List.4 Json.145 Json.149; - let Json.481 : {List U8, List U8} = Struct {Json.483, Json.484}; - jump Json.450 Json.481 Json.143; + let Json.551 : U64 = lowlevel ListLen Json.162; + let Json.552 : U64 = 1i64; + let Json.553 : Int1 = lowlevel NumGte Json.551 Json.552; + if Json.553 then + let Json.550 : U64 = 0i64; + let Json.165 : U8 = lowlevel ListGetUnsafe Json.162 Json.550; + joinpoint Json.543 Json.549: + if Json.549 then + let Json.541 : List U8 = CallByName List.38 Json.162; + let Json.542 : List U8 = CallByName List.4 Json.161 Json.165; + let Json.539 : {List U8, List U8} = Struct {Json.541, Json.542}; + jump Json.508 Json.539 Json.159; else - jump Json.490; + jump Json.548; in - let Json.486 : Int1 = CallByName Json.289 Json.149; - jump Json.485 Json.486; + let Json.544 : Int1 = CallByName Json.305 Json.165; + jump Json.543 Json.544; else - jump Json.490; + jump Json.548; in - jump Json.450 Json.512 Json.513; + jump Json.508 Json.570 Json.571; procedure Json.2 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.22 (Json.139, Json.140): - let Json.466 : {U8, U8} = Struct {Json.139, Json.140}; - joinpoint Json.475: - let Json.474 : Int1 = CallByName Bool.1; - ret Json.474; +procedure Json.23 (Json.155, Json.156): + let Json.524 : {U8, U8} = Struct {Json.155, Json.156}; + joinpoint Json.533: + let Json.532 : Int1 = CallByName Bool.1; + ret Json.532; in - let Json.477 : U8 = StructAtIndex 0 Json.466; - let Json.478 : U8 = 92i64; - let Json.479 : Int1 = lowlevel Eq Json.478 Json.477; - if Json.479 then - let Json.476 : U8 = StructAtIndex 1 Json.466; - switch Json.476: + let Json.535 : U8 = StructAtIndex 0 Json.524; + let Json.536 : U8 = 92i64; + let Json.537 : Int1 = lowlevel Eq Json.536 Json.535; + if Json.537 then + let Json.534 : U8 = StructAtIndex 1 Json.524; + switch Json.534: case 98: - let Json.467 : Int1 = CallByName Bool.2; - ret Json.467; + let Json.525 : Int1 = CallByName Bool.2; + ret Json.525; case 102: - let Json.468 : Int1 = CallByName Bool.2; - ret Json.468; + let Json.526 : Int1 = CallByName Bool.2; + ret Json.526; case 110: - let Json.469 : Int1 = CallByName Bool.2; - ret Json.469; + let Json.527 : Int1 = CallByName Bool.2; + ret Json.527; case 114: - let Json.470 : Int1 = CallByName Bool.2; - ret Json.470; + let Json.528 : Int1 = CallByName Bool.2; + ret Json.528; case 116: - let Json.471 : Int1 = CallByName Bool.2; - ret Json.471; + let Json.529 : Int1 = CallByName Bool.2; + ret Json.529; case 34: - let Json.472 : Int1 = CallByName Bool.2; - ret Json.472; + let Json.530 : Int1 = CallByName Bool.2; + ret Json.530; case 92: - let Json.473 : Int1 = CallByName Bool.2; - ret Json.473; + let Json.531 : Int1 = CallByName Bool.2; + ret Json.531; default: - jump Json.475; + jump Json.533; else - jump Json.475; + jump Json.533; -procedure Json.23 (Json.142, Json.143): - let Json.500 : List U8 = Array []; - let Json.449 : {List U8, List U8} = Struct {Json.142, Json.500}; - let Json.448 : {List U8, List U8} = CallByName Json.144 Json.449 Json.143; - ret Json.448; +procedure Json.24 (Json.158, Json.159): + let Json.558 : List U8 = Array []; + let Json.507 : {List U8, List U8} = Struct {Json.158, Json.558}; + let Json.506 : {List U8, List U8} = CallByName Json.160 Json.507 Json.159; + ret Json.506; -procedure Json.289 (Json.290): - let Json.502 : U8 = 34i64; - let Json.501 : Int1 = CallByName Bool.7 Json.290 Json.502; - ret Json.501; +procedure Json.305 (Json.306): + let Json.560 : U8 = 34i64; + let Json.559 : Int1 = CallByName Bool.7 Json.306 Json.560; + ret Json.559; -procedure Json.299 (Json.300, Json.428): - let Json.429 : {List U8, [C {}, C Str]} = CallByName Json.41 Json.300; - ret Json.429; +procedure Json.315 (Json.316, Json.486): + let Json.487 : {List U8, [C {}, C Str]} = CallByName Json.42 Json.316; + ret Json.487; -procedure Json.41 (Json.282): - let Json.506 : U64 = 1i64; - inc Json.282; - let Json.505 : {List U8, List U8} = CallByName List.52 Json.282 Json.506; - let Json.283 : List U8 = StructAtIndex 0 Json.505; - inc Json.283; - let Json.285 : List U8 = StructAtIndex 1 Json.505; - inc Json.285; - dec Json.505; - let Json.504 : U8 = 34i64; - let Json.503 : List U8 = Array [Json.504]; - let Json.433 : Int1 = CallByName Bool.11 Json.283 Json.503; - dec Json.503; - dec Json.283; - if Json.433 then - dec Json.282; - let Json.446 : {} = Struct {}; - let Json.445 : {List U8, List U8} = CallByName Json.23 Json.285 Json.446; - let Json.288 : List U8 = StructAtIndex 0 Json.445; - inc Json.288; - let Json.287 : List U8 = StructAtIndex 1 Json.445; - inc Json.287; - dec Json.445; - let Json.434 : [C {U64, U8}, C Str] = CallByName Str.9 Json.287; - let Json.442 : U8 = 1i64; - let Json.443 : U8 = GetTagId Json.434; - let Json.444 : Int1 = lowlevel Eq Json.442 Json.443; - if Json.444 then - let Json.291 : Str = UnionAtIndex (Id 1) (Index 0) Json.434; - inc Json.291; - dec Json.434; - let Json.438 : U64 = 1i64; - let Json.437 : {List U8, List U8} = CallByName List.52 Json.288 Json.438; - let Json.293 : List U8 = StructAtIndex 1 Json.437; - inc Json.293; - dec Json.437; - let Json.436 : [C {}, C Str] = TagId(1) Json.291; - let Json.435 : {List U8, [C {}, C Str]} = Struct {Json.293, Json.436}; - ret Json.435; +procedure Json.42 (Json.298): + let Json.564 : U64 = 1i64; + inc Json.298; + let Json.563 : {List U8, List U8} = CallByName List.52 Json.298 Json.564; + let Json.299 : List U8 = StructAtIndex 0 Json.563; + inc Json.299; + let Json.301 : List U8 = StructAtIndex 1 Json.563; + inc Json.301; + dec Json.563; + let Json.562 : U8 = 34i64; + let Json.561 : List U8 = Array [Json.562]; + let Json.491 : Int1 = CallByName Bool.11 Json.299 Json.561; + dec Json.561; + dec Json.299; + if Json.491 then + dec Json.298; + let Json.504 : {} = Struct {}; + let Json.503 : {List U8, List U8} = CallByName Json.24 Json.301 Json.504; + let Json.304 : List U8 = StructAtIndex 0 Json.503; + inc Json.304; + let Json.303 : List U8 = StructAtIndex 1 Json.503; + inc Json.303; + dec Json.503; + let Json.492 : [C {U64, U8}, C Str] = CallByName Str.9 Json.303; + let Json.500 : U8 = 1i64; + let Json.501 : U8 = GetTagId Json.492; + let Json.502 : Int1 = lowlevel Eq Json.500 Json.501; + if Json.502 then + let Json.307 : Str = UnionAtIndex (Id 1) (Index 0) Json.492; + inc Json.307; + dec Json.492; + let Json.496 : U64 = 1i64; + let Json.495 : {List U8, List U8} = CallByName List.52 Json.304 Json.496; + let Json.309 : List U8 = StructAtIndex 1 Json.495; + inc Json.309; + dec Json.495; + let Json.494 : [C {}, C Str] = TagId(1) Json.307; + let Json.493 : {List U8, [C {}, C Str]} = Struct {Json.309, Json.494}; + ret Json.493; else - dec Json.434; - let Json.441 : {} = Struct {}; - let Json.440 : [C {}, C Str] = TagId(0) Json.441; - let Json.439 : {List U8, [C {}, C Str]} = Struct {Json.288, Json.440}; - ret Json.439; + dec Json.492; + let Json.499 : {} = Struct {}; + let Json.498 : [C {}, C Str] = TagId(0) Json.499; + let Json.497 : {List U8, [C {}, C Str]} = Struct {Json.304, Json.498}; + ret Json.497; else - dec Json.285; - let Json.432 : {} = Struct {}; - let Json.431 : [C {}, C Str] = TagId(0) Json.432; - let Json.430 : {List U8, [C {}, C Str]} = Struct {Json.282, Json.431}; - ret Json.430; + dec Json.301; + let Json.490 : {} = Struct {}; + let Json.489 : [C {}, C Str] = TagId(0) Json.490; + let Json.488 : {List U8, [C {}, C Str]} = Struct {Json.298, Json.489}; + ret Json.488; -procedure Json.42 (): - let Json.427 : {} = Struct {}; - let Json.426 : {} = CallByName Decode.23 Json.427; - ret Json.426; +procedure Json.43 (): + let Json.485 : {} = Struct {}; + let Json.484 : {} = CallByName Decode.24 Json.485; + ret Json.484; procedure List.29 (List.298, List.299): let List.543 : U64 = CallByName List.6 List.298; @@ -368,7 +368,7 @@ procedure Test.0 (): let Test.37 : Str = "-1234"; let Test.35 : List U8 = CallByName Str.12 Test.37; let Test.36 : {} = CallByName Json.2; - let Test.34 : {List U8, [C {}, C Str]} = CallByName Decode.25 Test.35 Test.36; + let Test.34 : {List U8, [C {}, C Str]} = CallByName Decode.26 Test.35 Test.36; let Test.2 : List U8 = StructAtIndex 0 Test.34; inc Test.2; let Test.1 : [C {}, C Str] = StructAtIndex 1 Test.34; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt index 5bea9d2e72..e4c9053060 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt @@ -2,121 +2,121 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName Test.5 Encode.94 Encode.96 Encode.102; - ret Encode.106; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.111 : List U8 = CallByName Test.5 Encode.99 Encode.101 Encode.107; + ret Encode.111; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.128 Encode.94 Encode.96 Encode.102; - ret Encode.113; - -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.118 : List U8 = CallByName Json.98 Encode.94 Encode.96 Encode.102; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.118 : List U8 = CallByName Json.144 Encode.99 Encode.101 Encode.107; ret Encode.118; -procedure Encode.25 (Encode.100, Encode.101): - let Encode.104 : List U8 = Array []; - let Encode.105 : {Str, Str} = CallByName Test.2 Encode.100; - let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; - ret Encode.103; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.123 : List U8 = CallByName Json.102 Encode.99 Encode.101 Encode.107; + ret Encode.123; + +procedure Encode.26 (Encode.105, Encode.106): + let Encode.109 : List U8 = Array []; + let Encode.110 : {Str, Str} = CallByName Test.2 Encode.105; + let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; + ret Encode.108; procedure Json.1 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.128 (Json.129, Json.428, #Attr.12): - let Json.127 : List Str = StructAtIndex 1 #Attr.12; - inc Json.127; - let Json.126 : Str = StructAtIndex 0 #Attr.12; - inc Json.126; +procedure Json.102 (Json.103, Json.530, Json.101): + let Json.539 : I64 = 34i64; + let Json.538 : U8 = CallByName Num.127 Json.539; + let Json.536 : List U8 = CallByName List.4 Json.103 Json.538; + let Json.537 : List U8 = CallByName Str.12 Json.101; + let Json.533 : List U8 = CallByName List.8 Json.536 Json.537; + let Json.535 : I64 = 34i64; + let Json.534 : U8 = CallByName Num.127 Json.535; + let Json.532 : List U8 = CallByName List.4 Json.533 Json.534; + ret Json.532; + +procedure Json.144 (Json.145, Json.486, #Attr.12): + let Json.143 : List Str = StructAtIndex 1 #Attr.12; + inc Json.143; + let Json.142 : Str = StructAtIndex 0 #Attr.12; + inc Json.142; dec #Attr.12; - let Json.466 : I64 = 123i64; - let Json.465 : U8 = CallByName Num.127 Json.466; - let Json.462 : List U8 = CallByName List.4 Json.129 Json.465; - let Json.464 : I64 = 34i64; - let Json.463 : U8 = CallByName Num.127 Json.464; - let Json.460 : List U8 = CallByName List.4 Json.462 Json.463; - let Json.461 : List U8 = CallByName Str.12 Json.126; - let Json.457 : List U8 = CallByName List.8 Json.460 Json.461; - let Json.459 : I64 = 34i64; - let Json.458 : U8 = CallByName Num.127 Json.459; - let Json.454 : List U8 = CallByName List.4 Json.457 Json.458; - let Json.456 : I64 = 58i64; - let Json.455 : U8 = CallByName Num.127 Json.456; - let Json.451 : List U8 = CallByName List.4 Json.454 Json.455; - let Json.453 : I64 = 91i64; - let Json.452 : U8 = CallByName Num.127 Json.453; - let Json.131 : List U8 = CallByName List.4 Json.451 Json.452; - let Json.450 : U64 = CallByName List.6 Json.127; - let Json.438 : {List U8, U64} = Struct {Json.131, Json.450}; - let Json.439 : {} = Struct {}; - let Json.437 : {List U8, U64} = CallByName List.18 Json.127 Json.438 Json.439; - dec Json.127; - let Json.133 : List U8 = StructAtIndex 0 Json.437; - inc Json.133; - dec Json.437; - let Json.436 : I64 = 93i64; - let Json.435 : U8 = CallByName Num.127 Json.436; - let Json.432 : List U8 = CallByName List.4 Json.133 Json.435; - let Json.434 : I64 = 125i64; - let Json.433 : U8 = CallByName Num.127 Json.434; - let Json.431 : List U8 = CallByName List.4 Json.432 Json.433; - ret Json.431; + let Json.524 : I64 = 123i64; + let Json.523 : U8 = CallByName Num.127 Json.524; + let Json.520 : List U8 = CallByName List.4 Json.145 Json.523; + let Json.522 : I64 = 34i64; + let Json.521 : U8 = CallByName Num.127 Json.522; + let Json.518 : List U8 = CallByName List.4 Json.520 Json.521; + let Json.519 : List U8 = CallByName Str.12 Json.142; + let Json.515 : List U8 = CallByName List.8 Json.518 Json.519; + let Json.517 : I64 = 34i64; + let Json.516 : U8 = CallByName Num.127 Json.517; + let Json.512 : List U8 = CallByName List.4 Json.515 Json.516; + let Json.514 : I64 = 58i64; + let Json.513 : U8 = CallByName Num.127 Json.514; + let Json.509 : List U8 = CallByName List.4 Json.512 Json.513; + let Json.511 : I64 = 91i64; + let Json.510 : U8 = CallByName Num.127 Json.511; + let Json.147 : List U8 = CallByName List.4 Json.509 Json.510; + let Json.508 : U64 = CallByName List.6 Json.143; + let Json.496 : {List U8, U64} = Struct {Json.147, Json.508}; + let Json.497 : {} = Struct {}; + let Json.495 : {List U8, U64} = CallByName List.18 Json.143 Json.496 Json.497; + dec Json.143; + let Json.149 : List U8 = StructAtIndex 0 Json.495; + inc Json.149; + dec Json.495; + let Json.494 : I64 = 93i64; + let Json.493 : U8 = CallByName Num.127 Json.494; + let Json.490 : List U8 = CallByName List.4 Json.149 Json.493; + let Json.492 : I64 = 125i64; + let Json.491 : U8 = CallByName Num.127 Json.492; + let Json.489 : List U8 = CallByName List.4 Json.490 Json.491; + ret Json.489; -procedure Json.130 (Json.430, Json.136): - let Json.134 : List U8 = StructAtIndex 0 Json.430; - inc Json.134; - let Json.135 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.449 : {} = Struct {}; - let Json.137 : List U8 = CallByName Encode.23 Json.134 Json.136 Json.449; - joinpoint Json.444 Json.138: - let Json.442 : U64 = 1i64; - let Json.441 : U64 = CallByName Num.20 Json.135 Json.442; - let Json.440 : {List U8, U64} = Struct {Json.138, Json.441}; - ret Json.440; +procedure Json.146 (Json.488, Json.152): + let Json.150 : List U8 = StructAtIndex 0 Json.488; + inc Json.150; + let Json.151 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.507 : {} = Struct {}; + let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.507; + joinpoint Json.502 Json.154: + let Json.500 : U64 = 1i64; + let Json.499 : U64 = CallByName Num.20 Json.151 Json.500; + let Json.498 : {List U8, U64} = Struct {Json.154, Json.499}; + ret Json.498; in - let Json.448 : U64 = 1i64; - let Json.445 : Int1 = CallByName Num.24 Json.135 Json.448; - if Json.445 then - let Json.447 : I64 = 44i64; - let Json.446 : U8 = CallByName Num.127 Json.447; - let Json.443 : List U8 = CallByName List.4 Json.137 Json.446; - jump Json.444 Json.443; + let Json.506 : U64 = 1i64; + let Json.503 : Int1 = CallByName Num.24 Json.151 Json.506; + if Json.503 then + let Json.505 : I64 = 44i64; + let Json.504 : U8 = CallByName Num.127 Json.505; + let Json.501 : List U8 = CallByName List.4 Json.153 Json.504; + jump Json.502 Json.501; else - jump Json.444 Json.137; + jump Json.502 Json.153; -procedure Json.18 (Json.97): - let Json.482 : Str = CallByName Encode.22 Json.97; - ret Json.482; +procedure Json.18 (Json.101): + let Json.540 : Str = CallByName Encode.23 Json.101; + ret Json.540; -procedure Json.21 (Json.126, Json.127): - let Json.468 : {Str, List Str} = Struct {Json.126, Json.127}; - let Json.467 : {Str, List Str} = CallByName Encode.22 Json.468; - ret Json.467; - -procedure Json.98 (Json.99, Json.472, Json.97): - let Json.481 : I64 = 34i64; - let Json.480 : U8 = CallByName Num.127 Json.481; - let Json.478 : List U8 = CallByName List.4 Json.99 Json.480; - let Json.479 : List U8 = CallByName Str.12 Json.97; - let Json.475 : List U8 = CallByName List.8 Json.478 Json.479; - let Json.477 : I64 = 34i64; - let Json.476 : U8 = CallByName Num.127 Json.477; - let Json.474 : List U8 = CallByName List.4 Json.475 Json.476; - ret Json.474; +procedure Json.22 (Json.142, Json.143): + let Json.526 : {Str, List Str} = Struct {Json.142, Json.143}; + let Json.525 : {Str, List Str} = CallByName Encode.23 Json.526; + ret Json.525; procedure List.139 (List.140, List.141, List.138): - let List.545 : {List U8, U64} = CallByName Json.130 List.140 List.141; + let List.545 : {List U8, U64} = CallByName Json.146 List.140 List.141; ret List.545; procedure List.18 (List.136, List.137, List.138): @@ -194,7 +194,7 @@ procedure Str.12 (#Attr.2): ret Str.267; procedure Test.2 (Test.10): - let Test.15 : {Str, Str} = CallByName Encode.22 Test.10; + let Test.15 : {Str, Str} = CallByName Encode.23 Test.10; ret Test.15; procedure Test.3 (): @@ -205,7 +205,7 @@ procedure Test.3 (): procedure Test.5 (Test.6, Test.7, Test.4): joinpoint Test.20 Test.8: - let Test.18 : List U8 = CallByName Encode.23 Test.6 Test.8 Test.7; + let Test.18 : List U8 = CallByName Encode.24 Test.6 Test.8 Test.7; ret Test.18; in let Test.25 : Int1 = CallByName Bool.2; @@ -216,7 +216,7 @@ procedure Test.5 (Test.6, Test.7, Test.4): dec Test.4; let Test.28 : Str = CallByName Json.18 Test.29; let Test.27 : List Str = Array [Test.28]; - let Test.19 : {Str, List Str} = CallByName Json.21 Test.26 Test.27; + let Test.19 : {Str, List Str} = CallByName Json.22 Test.26 Test.27; jump Test.20 Test.19; else let Test.21 : Str = "B"; @@ -225,11 +225,11 @@ procedure Test.5 (Test.6, Test.7, Test.4): dec Test.4; let Test.23 : Str = CallByName Json.18 Test.24; let Test.22 : List Str = Array [Test.23]; - let Test.19 : {Str, List Str} = CallByName Json.21 Test.21 Test.22; + let Test.19 : {Str, List Str} = CallByName Json.22 Test.21 Test.22; jump Test.20 Test.19; procedure Test.0 (): let Test.12 : {Str, Str} = CallByName Test.3; let Test.13 : {} = CallByName Json.1; - let Test.11 : List U8 = CallByName Encode.25 Test.12 Test.13; + let Test.11 : List U8 = CallByName Encode.26 Test.12 Test.13; ret Test.11; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt index ffcd5ebbf5..9099e68523 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt @@ -1,230 +1,230 @@ procedure #Derived.0 (#Derived.1): let #Derived_gen.10 : [C {}, C {}] = TagId(0) #Derived.1; - let #Derived_gen.9 : [C {}, C {}] = CallByName Encode.22 #Derived_gen.10; + let #Derived_gen.9 : [C {}, C {}] = CallByName Encode.23 #Derived_gen.10; ret #Derived_gen.9; procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12): let #Derived.1 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; joinpoint #Derived_gen.14 #Derived_gen.13: - let #Derived_gen.12 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.13 #Derived.4; + let #Derived_gen.12 : List U8 = CallByName Encode.24 #Derived.3 #Derived_gen.13 #Derived.4; ret #Derived_gen.12; in let #Derived_gen.16 : Str = "A"; let #Derived_gen.17 : List [] = Array []; - let #Derived_gen.15 : {Str, List []} = CallByName Json.21 #Derived_gen.16 #Derived_gen.17; + let #Derived_gen.15 : {Str, List []} = CallByName Json.22 #Derived_gen.16 #Derived_gen.17; jump #Derived_gen.14 #Derived_gen.15; procedure #Derived.5 (#Derived.6): let #Derived_gen.1 : [C {}, C {}] = TagId(1) #Derived.6; - let #Derived_gen.0 : [C {}, C {}] = CallByName Encode.22 #Derived_gen.1; + let #Derived_gen.0 : [C {}, C {}] = CallByName Encode.23 #Derived_gen.1; ret #Derived_gen.0; procedure #Derived.7 (#Derived.8, #Derived.9, #Attr.12): let #Derived.6 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; joinpoint #Derived_gen.5 #Derived_gen.4: - let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.8 #Derived_gen.4 #Derived.9; + let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.8 #Derived_gen.4 #Derived.9; ret #Derived_gen.3; in let #Derived_gen.7 : Str = "B"; let #Derived_gen.8 : List [] = Array []; - let #Derived_gen.6 : {Str, List []} = CallByName Json.21 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.6 : {Str, List []} = CallByName Json.22 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.22 (Encode.93): - ret Encode.93; +procedure Encode.23 (Encode.98): + ret Encode.98; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName Test.5 Encode.94 Encode.96 Encode.102; - ret Encode.106; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.111 : List U8 = CallByName Test.5 Encode.99 Encode.101 Encode.107; + ret Encode.111; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.128 Encode.94 Encode.96 Encode.102; - ret Encode.113; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.118 : List U8 = CallByName Json.144 Encode.99 Encode.101 Encode.107; + ret Encode.118; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.117 : U8 = GetTagId Encode.102; - switch Encode.117: +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.122 : U8 = GetTagId Encode.107; + switch Encode.122: case 0: - let Encode.116 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102; - ret Encode.116; + let Encode.121 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; + ret Encode.121; default: - let Encode.116 : List U8 = CallByName #Derived.7 Encode.94 Encode.96 Encode.102; - ret Encode.116; + let Encode.121 : List U8 = CallByName #Derived.7 Encode.99 Encode.101 Encode.107; + ret Encode.121; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.129 : List U8 = CallByName Json.128 Encode.94 Encode.96 Encode.102; - ret Encode.129; +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.134 : List U8 = CallByName Json.144 Encode.99 Encode.101 Encode.107; + ret Encode.134; -procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.133 : Str = "a Lambda Set is empty. Most likely there is a type error in your program."; - Crash Encode.133 +procedure Encode.24 (Encode.99, Encode.107, Encode.101): + let Encode.138 : Str = "a Lambda Set is empty. Most likely there is a type error in your program."; + Crash Encode.138 -procedure Encode.25 (Encode.100, Encode.101): - let Encode.104 : List U8 = Array []; - let Encode.105 : {{}, {}} = CallByName Test.2 Encode.100; - let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; - ret Encode.103; +procedure Encode.26 (Encode.105, Encode.106): + let Encode.109 : List U8 = Array []; + let Encode.110 : {{}, {}} = CallByName Test.2 Encode.105; + let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; + ret Encode.108; procedure Json.1 (): - let Json.425 : {} = Struct {}; - ret Json.425; + let Json.483 : {} = Struct {}; + ret Json.483; -procedure Json.128 (Json.129, Json.428, #Attr.12): - let Json.127 : List [C {}, C {}] = StructAtIndex 1 #Attr.12; - inc Json.127; - let Json.126 : Str = StructAtIndex 0 #Attr.12; - inc Json.126; +procedure Json.144 (Json.145, Json.486, #Attr.12): + let Json.143 : List [C {}, C {}] = StructAtIndex 1 #Attr.12; + inc Json.143; + let Json.142 : Str = StructAtIndex 0 #Attr.12; + inc Json.142; dec #Attr.12; - let Json.466 : I64 = 123i64; - let Json.465 : U8 = CallByName Num.127 Json.466; - let Json.462 : List U8 = CallByName List.4 Json.129 Json.465; - let Json.464 : I64 = 34i64; - let Json.463 : U8 = CallByName Num.127 Json.464; - let Json.460 : List U8 = CallByName List.4 Json.462 Json.463; - let Json.461 : List U8 = CallByName Str.12 Json.126; - let Json.457 : List U8 = CallByName List.8 Json.460 Json.461; - let Json.459 : I64 = 34i64; - let Json.458 : U8 = CallByName Num.127 Json.459; - let Json.454 : List U8 = CallByName List.4 Json.457 Json.458; - let Json.456 : I64 = 58i64; - let Json.455 : U8 = CallByName Num.127 Json.456; - let Json.451 : List U8 = CallByName List.4 Json.454 Json.455; - let Json.453 : I64 = 91i64; - let Json.452 : U8 = CallByName Num.127 Json.453; - let Json.131 : List U8 = CallByName List.4 Json.451 Json.452; - let Json.450 : U64 = CallByName List.6 Json.127; - let Json.438 : {List U8, U64} = Struct {Json.131, Json.450}; - let Json.439 : {} = Struct {}; - let Json.437 : {List U8, U64} = CallByName List.18 Json.127 Json.438 Json.439; - dec Json.127; - let Json.133 : List U8 = StructAtIndex 0 Json.437; - inc Json.133; - dec Json.437; - let Json.436 : I64 = 93i64; - let Json.435 : U8 = CallByName Num.127 Json.436; - let Json.432 : List U8 = CallByName List.4 Json.133 Json.435; - let Json.434 : I64 = 125i64; - let Json.433 : U8 = CallByName Num.127 Json.434; - let Json.431 : List U8 = CallByName List.4 Json.432 Json.433; - ret Json.431; - -procedure Json.128 (Json.129, Json.428, #Attr.12): - let Json.127 : List [] = StructAtIndex 1 #Attr.12; - inc Json.127; - let Json.126 : Str = StructAtIndex 0 #Attr.12; - inc Json.126; - dec #Attr.12; - let Json.516 : I64 = 123i64; - let Json.515 : U8 = CallByName Num.127 Json.516; - let Json.512 : List U8 = CallByName List.4 Json.129 Json.515; - let Json.514 : I64 = 34i64; + let Json.524 : I64 = 123i64; + let Json.523 : U8 = CallByName Num.127 Json.524; + let Json.520 : List U8 = CallByName List.4 Json.145 Json.523; + let Json.522 : I64 = 34i64; + let Json.521 : U8 = CallByName Num.127 Json.522; + let Json.518 : List U8 = CallByName List.4 Json.520 Json.521; + let Json.519 : List U8 = CallByName Str.12 Json.142; + let Json.515 : List U8 = CallByName List.8 Json.518 Json.519; + let Json.517 : I64 = 34i64; + let Json.516 : U8 = CallByName Num.127 Json.517; + let Json.512 : List U8 = CallByName List.4 Json.515 Json.516; + let Json.514 : I64 = 58i64; let Json.513 : U8 = CallByName Num.127 Json.514; - let Json.510 : List U8 = CallByName List.4 Json.512 Json.513; - let Json.511 : List U8 = CallByName Str.12 Json.126; - let Json.507 : List U8 = CallByName List.8 Json.510 Json.511; - let Json.509 : I64 = 34i64; - let Json.508 : U8 = CallByName Num.127 Json.509; - let Json.504 : List U8 = CallByName List.4 Json.507 Json.508; - let Json.506 : I64 = 58i64; - let Json.505 : U8 = CallByName Num.127 Json.506; - let Json.501 : List U8 = CallByName List.4 Json.504 Json.505; - let Json.503 : I64 = 91i64; - let Json.502 : U8 = CallByName Num.127 Json.503; - let Json.131 : List U8 = CallByName List.4 Json.501 Json.502; - let Json.500 : U64 = CallByName List.6 Json.127; - let Json.488 : {List U8, U64} = Struct {Json.131, Json.500}; - let Json.489 : {} = Struct {}; - let Json.487 : {List U8, U64} = CallByName List.18 Json.127 Json.488 Json.489; - dec Json.127; - let Json.133 : List U8 = StructAtIndex 0 Json.487; - inc Json.133; - dec Json.487; - let Json.486 : I64 = 93i64; - let Json.485 : U8 = CallByName Num.127 Json.486; - let Json.482 : List U8 = CallByName List.4 Json.133 Json.485; - let Json.484 : I64 = 125i64; - let Json.483 : U8 = CallByName Num.127 Json.484; - let Json.481 : List U8 = CallByName List.4 Json.482 Json.483; - ret Json.481; + let Json.509 : List U8 = CallByName List.4 Json.512 Json.513; + let Json.511 : I64 = 91i64; + let Json.510 : U8 = CallByName Num.127 Json.511; + let Json.147 : List U8 = CallByName List.4 Json.509 Json.510; + let Json.508 : U64 = CallByName List.6 Json.143; + let Json.496 : {List U8, U64} = Struct {Json.147, Json.508}; + let Json.497 : {} = Struct {}; + let Json.495 : {List U8, U64} = CallByName List.18 Json.143 Json.496 Json.497; + dec Json.143; + let Json.149 : List U8 = StructAtIndex 0 Json.495; + inc Json.149; + dec Json.495; + let Json.494 : I64 = 93i64; + let Json.493 : U8 = CallByName Num.127 Json.494; + let Json.490 : List U8 = CallByName List.4 Json.149 Json.493; + let Json.492 : I64 = 125i64; + let Json.491 : U8 = CallByName Num.127 Json.492; + let Json.489 : List U8 = CallByName List.4 Json.490 Json.491; + ret Json.489; -procedure Json.130 (Json.430, Json.136): - let Json.134 : List U8 = StructAtIndex 0 Json.430; - inc Json.134; - let Json.135 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.449 : {} = Struct {}; - let Json.137 : List U8 = CallByName Encode.23 Json.134 Json.136 Json.449; - joinpoint Json.444 Json.138: - let Json.442 : U64 = 1i64; - let Json.441 : U64 = CallByName Num.20 Json.135 Json.442; - let Json.440 : {List U8, U64} = Struct {Json.138, Json.441}; - ret Json.440; +procedure Json.144 (Json.145, Json.486, #Attr.12): + let Json.143 : List [] = StructAtIndex 1 #Attr.12; + inc Json.143; + let Json.142 : Str = StructAtIndex 0 #Attr.12; + inc Json.142; + dec #Attr.12; + let Json.574 : I64 = 123i64; + let Json.573 : U8 = CallByName Num.127 Json.574; + let Json.570 : List U8 = CallByName List.4 Json.145 Json.573; + let Json.572 : I64 = 34i64; + let Json.571 : U8 = CallByName Num.127 Json.572; + let Json.568 : List U8 = CallByName List.4 Json.570 Json.571; + let Json.569 : List U8 = CallByName Str.12 Json.142; + let Json.565 : List U8 = CallByName List.8 Json.568 Json.569; + let Json.567 : I64 = 34i64; + let Json.566 : U8 = CallByName Num.127 Json.567; + let Json.562 : List U8 = CallByName List.4 Json.565 Json.566; + let Json.564 : I64 = 58i64; + let Json.563 : U8 = CallByName Num.127 Json.564; + let Json.559 : List U8 = CallByName List.4 Json.562 Json.563; + let Json.561 : I64 = 91i64; + let Json.560 : U8 = CallByName Num.127 Json.561; + let Json.147 : List U8 = CallByName List.4 Json.559 Json.560; + let Json.558 : U64 = CallByName List.6 Json.143; + let Json.546 : {List U8, U64} = Struct {Json.147, Json.558}; + let Json.547 : {} = Struct {}; + let Json.545 : {List U8, U64} = CallByName List.18 Json.143 Json.546 Json.547; + dec Json.143; + let Json.149 : List U8 = StructAtIndex 0 Json.545; + inc Json.149; + dec Json.545; + let Json.544 : I64 = 93i64; + let Json.543 : U8 = CallByName Num.127 Json.544; + let Json.540 : List U8 = CallByName List.4 Json.149 Json.543; + let Json.542 : I64 = 125i64; + let Json.541 : U8 = CallByName Num.127 Json.542; + let Json.539 : List U8 = CallByName List.4 Json.540 Json.541; + ret Json.539; + +procedure Json.146 (Json.488, Json.152): + let Json.150 : List U8 = StructAtIndex 0 Json.488; + inc Json.150; + let Json.151 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.507 : {} = Struct {}; + let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.507; + joinpoint Json.502 Json.154: + let Json.500 : U64 = 1i64; + let Json.499 : U64 = CallByName Num.20 Json.151 Json.500; + let Json.498 : {List U8, U64} = Struct {Json.154, Json.499}; + ret Json.498; in - let Json.448 : U64 = 1i64; - let Json.445 : Int1 = CallByName Num.24 Json.135 Json.448; - if Json.445 then - let Json.447 : I64 = 44i64; - let Json.446 : U8 = CallByName Num.127 Json.447; - let Json.443 : List U8 = CallByName List.4 Json.137 Json.446; - jump Json.444 Json.443; + let Json.506 : U64 = 1i64; + let Json.503 : Int1 = CallByName Num.24 Json.151 Json.506; + if Json.503 then + let Json.505 : I64 = 44i64; + let Json.504 : U8 = CallByName Num.127 Json.505; + let Json.501 : List U8 = CallByName List.4 Json.153 Json.504; + jump Json.502 Json.501; else - jump Json.444 Json.137; + jump Json.502 Json.153; -procedure Json.130 (Json.430, Json.136): - let Json.134 : List U8 = StructAtIndex 0 Json.430; - inc Json.134; - let Json.135 : U64 = StructAtIndex 1 Json.430; - dec Json.430; - let Json.499 : {} = Struct {}; - let Json.137 : List U8 = CallByName Encode.23 Json.134 Json.136 Json.499; - dec Json.134; - joinpoint Json.494 Json.138: - let Json.492 : U64 = 1i64; - let Json.491 : U64 = CallByName Num.20 Json.135 Json.492; - let Json.490 : {List U8, U64} = Struct {Json.138, Json.491}; - ret Json.490; +procedure Json.146 (Json.488, Json.152): + let Json.150 : List U8 = StructAtIndex 0 Json.488; + inc Json.150; + let Json.151 : U64 = StructAtIndex 1 Json.488; + dec Json.488; + let Json.557 : {} = Struct {}; + let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.557; + dec Json.150; + joinpoint Json.552 Json.154: + let Json.550 : U64 = 1i64; + let Json.549 : U64 = CallByName Num.20 Json.151 Json.550; + let Json.548 : {List U8, U64} = Struct {Json.154, Json.549}; + ret Json.548; in - let Json.498 : U64 = 1i64; - let Json.495 : Int1 = CallByName Num.24 Json.135 Json.498; - if Json.495 then - let Json.497 : I64 = 44i64; - let Json.496 : U8 = CallByName Num.127 Json.497; - let Json.493 : List U8 = CallByName List.4 Json.137 Json.496; - jump Json.494 Json.493; + let Json.556 : U64 = 1i64; + let Json.553 : Int1 = CallByName Num.24 Json.151 Json.556; + if Json.553 then + let Json.555 : I64 = 44i64; + let Json.554 : U8 = CallByName Num.127 Json.555; + let Json.551 : List U8 = CallByName List.4 Json.153 Json.554; + jump Json.552 Json.551; else - jump Json.494 Json.137; + jump Json.552 Json.153; -procedure Json.21 (Json.126, Json.127): - let Json.468 : {Str, List [C {}, C {}]} = Struct {Json.126, Json.127}; - let Json.467 : {Str, List [C {}, C {}]} = CallByName Encode.22 Json.468; - ret Json.467; +procedure Json.22 (Json.142, Json.143): + let Json.526 : {Str, List [C {}, C {}]} = Struct {Json.142, Json.143}; + let Json.525 : {Str, List [C {}, C {}]} = CallByName Encode.23 Json.526; + ret Json.525; -procedure Json.21 (Json.126, Json.127): - let Json.518 : {Str, List []} = Struct {Json.126, Json.127}; - let Json.517 : {Str, List []} = CallByName Encode.22 Json.518; - ret Json.517; +procedure Json.22 (Json.142, Json.143): + let Json.576 : {Str, List []} = Struct {Json.142, Json.143}; + let Json.575 : {Str, List []} = CallByName Encode.23 Json.576; + ret Json.575; procedure List.139 (List.140, List.141, List.138): - let List.539 : {List U8, U64} = CallByName Json.130 List.140 List.141; + let List.539 : {List U8, U64} = CallByName Json.146 List.140 List.141; ret List.539; procedure List.139 (List.140, List.141, List.138): - let List.612 : {List U8, U64} = CallByName Json.130 List.140 List.141; + let List.612 : {List U8, U64} = CallByName Json.146 List.140 List.141; ret List.612; procedure List.18 (List.136, List.137, List.138): @@ -334,7 +334,7 @@ procedure Str.12 (#Attr.2): ret Str.268; procedure Test.2 (Test.11): - let Test.18 : {{}, {}} = CallByName Encode.22 Test.11; + let Test.18 : {{}, {}} = CallByName Encode.23 Test.11; ret Test.18; procedure Test.3 (): @@ -345,7 +345,7 @@ procedure Test.3 (): procedure Test.5 (Test.6, Test.7, Test.4): joinpoint Test.23 Test.8: - let Test.21 : List U8 = CallByName Encode.23 Test.6 Test.8 Test.7; + let Test.21 : List U8 = CallByName Encode.24 Test.6 Test.8 Test.7; ret Test.21; in let Test.28 : Int1 = CallByName Bool.2; @@ -354,18 +354,18 @@ procedure Test.5 (Test.6, Test.7, Test.4): let Test.32 : {} = StructAtIndex 0 Test.4; let Test.31 : [C {}, C {}] = CallByName #Derived.0 Test.32; let Test.30 : List [C {}, C {}] = Array [Test.31]; - let Test.22 : {Str, List [C {}, C {}]} = CallByName Json.21 Test.29 Test.30; + let Test.22 : {Str, List [C {}, C {}]} = CallByName Json.22 Test.29 Test.30; jump Test.23 Test.22; else let Test.24 : Str = "B"; let Test.27 : {} = StructAtIndex 1 Test.4; let Test.26 : [C {}, C {}] = CallByName #Derived.5 Test.27; let Test.25 : List [C {}, C {}] = Array [Test.26]; - let Test.22 : {Str, List [C {}, C {}]} = CallByName Json.21 Test.24 Test.25; + let Test.22 : {Str, List [C {}, C {}]} = CallByName Json.22 Test.24 Test.25; jump Test.23 Test.22; procedure Test.0 (): let Test.13 : {{}, {}} = CallByName Test.3; let Test.14 : {} = CallByName Json.1; - let Test.12 : List U8 = CallByName Encode.25 Test.13 Test.14; + let Test.12 : List U8 = CallByName Encode.26 Test.13 Test.14; ret Test.12; From db3698c33c849007f55a0bbbebc7c37c519c9a0c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 23 Mar 2023 11:53:43 -0500 Subject: [PATCH 24/24] Fix code guards --- crates/compiler/derive/src/decoding/record.rs | 4 ++++ crates/compiler/derive/src/decoding/tuple.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/crates/compiler/derive/src/decoding/record.rs b/crates/compiler/derive/src/decoding/record.rs index 754eb1279c..d62ffe2f90 100644 --- a/crates/compiler/derive/src/decoding/record.rs +++ b/crates/compiler/derive/src/decoding/record.rs @@ -20,10 +20,13 @@ use super::wrap_in_decode_custom_decode_with; /// Implements decoding of a record. For example, for /// +/// ```text /// {first: a, second: b} +/// ``` /// /// we'd like to generate an impl like /// +/// ```roc /// decoder : Decoder {first: a, second: b} fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting /// decoder = /// initialState : {f0: Result a [NoField], f1: Result b [NoField]} @@ -52,6 +55,7 @@ use super::wrap_in_decode_custom_decode_with; /// Err NoField -> Err TooShort /// /// Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.record initialState stepField finalizer) fmt +/// ``` pub(crate) fn decoder( env: &mut Env, _def_symbol: Symbol, diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs index 8c4205322e..b6ce201691 100644 --- a/crates/compiler/derive/src/decoding/tuple.rs +++ b/crates/compiler/derive/src/decoding/tuple.rs @@ -21,10 +21,13 @@ use super::wrap_in_decode_custom_decode_with; /// Implements decoding of a tuple. For example, for /// +/// ```text /// (a, b) +/// ``` /// /// we'd like to generate an impl like /// +/// ```roc /// decoder : Decoder (a, b) fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting /// decoder = /// initialState : {e0: Result a [NoElem], e1: Result b [NoElem]} @@ -53,6 +56,7 @@ use super::wrap_in_decode_custom_decode_with; /// Err NoElem -> Err TooShort /// /// Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.tuple initialState stepElem finalizer) fmt +/// ``` pub(crate) fn decoder(env: &mut Env, _def_symbol: Symbol, arity: u32) -> (Expr, Variable) { // The decoded type of each index in the tuple, e.g. (a, b). let mut index_vars = Vec::with_capacity(arity as _);