diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 36d8a931e4..40ff5b9330 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -639,6 +639,45 @@ fn separate_implemented_and_required_members( } } +fn synthesize_derived_to_encoder<'a>( + env: &mut Env<'a>, + at_opaque: &'a str, + region: Region, +) -> ast::Expr<'a> { + let alloc_pat = |it| env.arena.alloc(Loc::at(region, it)); + let alloc_expr = |it| env.arena.alloc(Loc::at(region, it)); + + let payload = env.arena.alloc_str("#payload"); + + // \@Opaq payload + let opaque_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque)); + let opaque_apply_pattern = ast::Pattern::Apply( + opaque_ref, + &*env + .arena + .alloc([Loc::at(region, ast::Pattern::Identifier(payload))]), + ); + + // Encode.toEncoder payload + let call_member = alloc_expr(ast::Expr::Apply( + alloc_expr(ast::Expr::Var { + module_name: "Encode", + ident: "toEncoder", + }), + &*env.arena.alloc([&*alloc_expr(ast::Expr::Var { + module_name: "", + ident: payload, + })]), + roc_module::called_via::CalledVia::Space, + )); + + // \@Opaq payload -> Encode.toEncoder payload + ast::Expr::Closure( + env.arena.alloc([Loc::at(region, opaque_apply_pattern)]), + call_member, + ) +} + fn synthesize_derived_hash<'a>( env: &mut Env<'a>, at_opaque: &'a str, @@ -756,14 +795,9 @@ fn synthesize_derived_member_impl<'a>( let (impl_name, def_body): (String, ast::Expr<'a>) = match ability_member { Symbol::ENCODE_TO_ENCODER => ( format!("#{}_toEncoder", opaque_name), - todo!(), - //synthesize_derived_to_encoder(env, scope, at_opaque, region), - ), - Symbol::DECODE_DECODER => ( - format!("#{}_decoder", opaque_name), - todo!(), - //synthesize_derived_decoder(env, scope, at_opaque, region), + synthesize_derived_to_encoder(env, at_opaque, region), ), + Symbol::DECODE_DECODER => (format!("#{}_decoder", opaque_name), todo!()), Symbol::HASH_HASH => ( format!("#{}_hash", opaque_name), synthesize_derived_hash(env, at_opaque, region), diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index 625a3d97d7..e42d40f76a 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -8045,6 +8045,23 @@ mod solve_expr { ); } + #[test] + fn derive_to_encoder_for_opaque() { + infer_queries!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + N := U8 has [Encoding] + + main = Encode.toEncoder (@N 15) + # ^^^^^^^^^^^^^^^^ + "# + ), + @"N#Encode.toEncoder(3) : N -[[#N_toEncoder(3)]]-> Encoder fmt | fmt has EncoderFormatting" + ); + } + #[test] fn derive_hash_for_opaque() { infer_queries!( diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index f398887c87..a00f33261f 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -374,6 +374,30 @@ fn encode_use_stdlib_without_wrapping_custom() { ) } +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn encode_derive_to_encoder_for_opaque() { + assert_evals_to!( + indoc!( + r#" + app "test" + imports [Json] + provides [main] to "./platform" + + HelloWorld := { a: Str } has [Encoding] + + main = + result = Str.fromUtf8 (Encode.toBytes (@HelloWorld { a: "Hello, World!" }) Json.toUtf8) + when result is + Ok s -> s + _ -> "" + "# + ), + RocStr::from(r#"{"a":"Hello, World!"}"#), + RocStr + ) +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn to_encoder_encode_custom_has_capture() {