From 49cf2a84839f297d9ef56d0b8416e48c2b471d6e Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 12 May 2022 14:46:51 -0400 Subject: [PATCH] Bindgen as_Foo and into_Foo for variants --- bindgen/src/bindgen_rs.rs | 72 ++++++++++++++++++++++++++++++++++++--- bindgen/tests/gen_rs.rs | 48 ++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 4 deletions(-) diff --git a/bindgen/src/bindgen_rs.rs b/bindgen/src/bindgen_rs.rs index 57706df64f..91d2190aa8 100644 --- a/bindgen/src/bindgen_rs.rs +++ b/bindgen/src/bindgen_rs.rs @@ -190,10 +190,20 @@ fn write_tag_union( if let Some(payload_id) = opt_payload_id { let payload_type = types.get(*payload_id); - let payload_name = if payload_type.has_pointer(types) { - "core::mem::ManuallyDrop::new(payload)" + let (init_payload, get_payload, deref_for_as) = if payload_type.has_pointer(types) { + ( + "core::mem::ManuallyDrop::new(payload)", + format!( + "core::mem::ManuallyDrop::<{}>::into_inner(self.variant.{})", + type_name(*payload_id, types), + tag_name + ), + // Since this is a ManuallyDrop, our `as_` method will need + // to dereference the variant (e.g. `&self.variant.Foo`) + "&", + ) } else { - "payload" + ("payload", format!("self.variant.{}", tag_name), "") }; let payload_type_name = type_name(*payload_id, types); @@ -218,7 +228,37 @@ fn write_tag_union( tag_name, variant_name, tag_name, - payload_name + init_payload + )?; + + writeln!( + buf, + // Don't use indoc because this must be indented once! + r#" + /// Unsafely assume the given {} has a .tag() of {} and convert it to {}'s payload. + /// (always examine .tag() first to make sure this is the correct variant!) + pub unsafe fn into_{}(self) -> {} {{ + {} + }}"#, + name, tag_name, tag_name, tag_name, payload_type_name, get_payload + )?; + + writeln!( + buf, + // Don't use indoc because this must be indented once! + r#" + /// Unsafely assume the given {} has a .tag() of {} and return its payload. + /// (always examine .tag() first to make sure this is the correct variant!) + pub unsafe fn as_{}(&self) -> {}{} {{ + {}self.variant.{} + }}"#, + name, + tag_name, + tag_name, + deref_for_as, + payload_type_name, + deref_for_as, + tag_name )?; } else { writeln!( @@ -239,6 +279,30 @@ fn write_tag_union( }}"#, tag_name, tag_name, discriminant_name, tag_name, variant_name, variant_name, )?; + + writeln!( + buf, + // Don't use indoc because this must be indented once! + r#" + /// Other `into_` methods return a payload, but since the {} tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_{}(self) -> () {{ + () + }}"#, + tag_name, tag_name + )?; + + writeln!( + buf, + // Don't use indoc because this must be indented once! + r#" + /// Other `as` methods return a payload, but since the {} tag + /// has no payload, this does nothing and is only here for completeness. + pub unsafe fn as_{}(&self) -> () {{ + () + }}"#, + tag_name, tag_name + )?; } } diff --git a/bindgen/tests/gen_rs.rs b/bindgen/tests/gen_rs.rs index 047113db7c..1a0920127b 100644 --- a/bindgen/tests/gen_rs.rs +++ b/bindgen/tests/gen_rs.rs @@ -223,6 +223,18 @@ fn tag_union_aliased() { } } + /// Unsafely assume the given MyTagUnion has a .tag() of Bar and convert it to Bar's payload. + /// (always examine .tag() first to make sure this is the correct variant!) + pub unsafe fn into_Bar(self) -> u128 { + self.variant.Bar + } + + /// Unsafely assume the given MyTagUnion has a .tag() of Bar and return its payload. + /// (always examine .tag() first to make sure this is the correct variant!) + pub unsafe fn as_Bar(&self) -> u128 { + self.variant.Bar + } + /// Construct a tag named Baz pub fn Baz() -> Self { Self { @@ -236,6 +248,18 @@ fn tag_union_aliased() { } } + /// Other `into_` methods return a payload, but since the Baz tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_Baz(self) -> () { + () + } + + /// Other `as` methods return a payload, but since the Baz tag + /// has no payload, this does nothing and is only here for completeness. + pub unsafe fn as_Baz(&self) -> () { + () + } + /// Construct a tag named Blah, with the appropriate payload pub fn Blah(payload: i32) -> Self { Self { @@ -246,6 +270,18 @@ fn tag_union_aliased() { } } + /// Unsafely assume the given MyTagUnion has a .tag() of Blah and convert it to Blah's payload. + /// (always examine .tag() first to make sure this is the correct variant!) + pub unsafe fn into_Blah(self) -> i32 { + self.variant.Blah + } + + /// Unsafely assume the given MyTagUnion has a .tag() of Blah and return its payload. + /// (always examine .tag() first to make sure this is the correct variant!) + pub unsafe fn as_Blah(&self) -> i32 { + self.variant.Blah + } + /// Construct a tag named Foo, with the appropriate payload pub fn Foo(payload: roc_std::RocStr) -> Self { Self { @@ -255,6 +291,18 @@ fn tag_union_aliased() { }, } } + + /// Unsafely assume the given MyTagUnion has a .tag() of Foo and convert it to Foo's payload. + /// (always examine .tag() first to make sure this is the correct variant!) + pub unsafe fn into_Foo(self) -> roc_std::RocStr { + core::mem::ManuallyDrop::::into_inner(self.variant.Foo) + } + + /// Unsafely assume the given MyTagUnion has a .tag() of Foo and return its payload. + /// (always examine .tag() first to make sure this is the correct variant!) + pub unsafe fn as_Foo(&self) -> &roc_std::RocStr { + &self.variant.Foo + } } impl Drop for MyTagUnion {