diff --git a/Makefile.toml b/Makefile.toml index e11428955..bb5921059 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -202,6 +202,7 @@ exec --fail-on-error npm run test [tasks.test-dart-example] category = "Tests" script_runner = "@duckscript" +dependencies = ["build-example"] script = ''' exit_on_error true cd example/dart/ @@ -212,6 +213,7 @@ exec --fail-on-error dart --enable-experiment=native-assets test [tasks.test-dart-feature] category = "Tests" script_runner = "@duckscript" +dependencies = ["build-feature"] script = ''' exit_on_error true cd feature_tests/dart/ diff --git a/core/src/ast/attrs.rs b/core/src/ast/attrs.rs index 7be6704e2..fb4a0504c 100644 --- a/core/src/ast/attrs.rs +++ b/core/src/ast/attrs.rs @@ -11,13 +11,19 @@ use syn::{Attribute, Ident, LitStr, Meta, Token}; pub struct Attrs { pub cfg: Vec, pub attrs: Vec, + /// AST backends only. For using features that may panic AST backends, like returning references. + /// + /// This isn't a regular attribute since AST backends do not handle regular attributes. Do not use + /// in HIR backends, + pub skip_if_unsupported: bool, } impl Attrs { fn add_attr(&mut self, attr: Attr) { match attr { Attr::Cfg(attr) => self.cfg.push(attr), - Attr::DiplomatBackendAttr(attr) => self.attrs.push(attr), + Attr::DiplomatBackend(attr) => self.attrs.push(attr), + Attr::SkipIfUnsupported => self.skip_if_unsupported = true, } } @@ -45,21 +51,25 @@ impl From<&[Attribute]> for Attrs { enum Attr { Cfg(Attribute), - DiplomatBackendAttr(DiplomatBackendAttr), + DiplomatBackend(DiplomatBackendAttr), + SkipIfUnsupported, // More goes here } fn syn_attr_to_ast_attr(attrs: &[Attribute]) -> impl Iterator + '_ { let cfg_path: syn::Path = syn::parse_str("cfg").unwrap(); let dattr_path: syn::Path = syn::parse_str("diplomat::attr").unwrap(); + let skipast: syn::Path = syn::parse_str("diplomat::skip_if_unsupported").unwrap(); attrs.iter().filter_map(move |a| { if a.path() == &cfg_path { Some(Attr::Cfg(a.clone())) } else if a.path() == &dattr_path { - Some(Attr::DiplomatBackendAttr( + Some(Attr::DiplomatBackend( a.parse_args() .expect("Failed to parse malformed diplomat::attr"), )) + } else if a.path() == &skipast { + Some(Attr::SkipIfUnsupported) } else { None } diff --git a/example/dart/lib/src/ICU4XDataProvider.g.dart b/example/dart/lib/src/ICU4XDataProvider.g.dart index 3bf3a4360..09f1b5812 100644 --- a/example/dart/lib/src/ICU4XDataProvider.g.dart +++ b/example/dart/lib/src/ICU4XDataProvider.g.dart @@ -11,8 +11,10 @@ part of 'lib.g.dart'; final class ICU4XDataProvider implements ffi.Finalizable { final ffi.Pointer _underlying; - ICU4XDataProvider._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + ICU4XDataProvider._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_ICU4XDataProvider_destroy)); @@ -20,7 +22,7 @@ final class ICU4XDataProvider implements ffi.Finalizable { /// See the [Rust documentation for `get_static_provider`](https://docs.rs/icu_testdata/latest/icu_testdata/fn.get_static_provider.html) for more information. factory ICU4XDataProvider.static_() { final result = _ICU4XDataProvider_new_static(); - return ICU4XDataProvider._(result); + return ICU4XDataProvider._(result, true); } /// This exists as a regression test for https://github.com/rust-diplomat/diplomat/issues/155 diff --git a/example/dart/lib/src/ICU4XFixedDecimal.g.dart b/example/dart/lib/src/ICU4XFixedDecimal.g.dart index efb130603..34bfbe11d 100644 --- a/example/dart/lib/src/ICU4XFixedDecimal.g.dart +++ b/example/dart/lib/src/ICU4XFixedDecimal.g.dart @@ -9,8 +9,10 @@ part of 'lib.g.dart'; final class ICU4XFixedDecimal implements ffi.Finalizable { final ffi.Pointer _underlying; - ICU4XFixedDecimal._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + ICU4XFixedDecimal._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_ICU4XFixedDecimal_destroy)); @@ -18,7 +20,7 @@ final class ICU4XFixedDecimal implements ffi.Finalizable { /// Construct an [`ICU4XFixedDecimal`] from an integer. factory ICU4XFixedDecimal(int v) { final result = _ICU4XFixedDecimal_new(v); - return ICU4XFixedDecimal._(result); + return ICU4XFixedDecimal._(result, true); } /// Multiply the [`ICU4XFixedDecimal`] by a given power of ten. diff --git a/example/dart/lib/src/ICU4XFixedDecimalFormatter.g.dart b/example/dart/lib/src/ICU4XFixedDecimalFormatter.g.dart index a0d50b179..2cacca5f2 100644 --- a/example/dart/lib/src/ICU4XFixedDecimalFormatter.g.dart +++ b/example/dart/lib/src/ICU4XFixedDecimalFormatter.g.dart @@ -11,8 +11,10 @@ part of 'lib.g.dart'; final class ICU4XFixedDecimalFormatter implements ffi.Finalizable { final ffi.Pointer _underlying; - ICU4XFixedDecimalFormatter._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + ICU4XFixedDecimalFormatter._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_ICU4XFixedDecimalFormatter_destroy)); @@ -27,7 +29,7 @@ final class ICU4XFixedDecimalFormatter implements ffi.Finalizable { if (!result.isOk) { throw VoidError(); } - return ICU4XFixedDecimalFormatter._(result.union.ok); + return ICU4XFixedDecimalFormatter._(result.union.ok, true); } /// Formats a [`ICU4XFixedDecimal`] to a string. diff --git a/example/dart/lib/src/ICU4XLocale.g.dart b/example/dart/lib/src/ICU4XLocale.g.dart index e096592c7..1ca9c095b 100644 --- a/example/dart/lib/src/ICU4XLocale.g.dart +++ b/example/dart/lib/src/ICU4XLocale.g.dart @@ -11,8 +11,10 @@ part of 'lib.g.dart'; final class ICU4XLocale implements ffi.Finalizable { final ffi.Pointer _underlying; - ICU4XLocale._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + ICU4XLocale._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_ICU4XLocale_destroy)); @@ -23,7 +25,7 @@ final class ICU4XLocale implements ffi.Finalizable { final nameView = name.utf8View; final result = _ICU4XLocale_new(nameView.pointer(temp), nameView.length); temp.releaseAll(); - return ICU4XLocale._(result); + return ICU4XLocale._(result, true); } } diff --git a/feature_tests/c/include/Bar.h b/feature_tests/c/include/Bar.h index f4467f2dd..f7a93e618 100644 --- a/feature_tests/c/include/Bar.h +++ b/feature_tests/c/include/Bar.h @@ -14,11 +14,13 @@ typedef struct Bar Bar; #ifdef __cplusplus } // namespace capi #endif +#include "Foo.h" #ifdef __cplusplus namespace capi { extern "C" { #endif +const Foo* Bar_foo(const Bar* self); void Bar_destroy(Bar* self); #ifdef __cplusplus diff --git a/feature_tests/c2/include/Bar.h b/feature_tests/c2/include/Bar.h index 1c6201f79..56943a954 100644 --- a/feature_tests/c2/include/Bar.h +++ b/feature_tests/c2/include/Bar.h @@ -6,6 +6,8 @@ #include #include #include "diplomat_runtime.h" +#include "Foo.d.h" +#include "Foo.h" #include "Bar.d.h" @@ -15,6 +17,8 @@ extern "C" { #endif // __cplusplus +const Foo* Bar_foo(const Bar* self); + void Bar_destroy(Bar* self); diff --git a/feature_tests/cpp/docs/source/lifetimes_ffi.rst b/feature_tests/cpp/docs/source/lifetimes_ffi.rst index 16857dfc8..4644317fa 100644 --- a/feature_tests/cpp/docs/source/lifetimes_ffi.rst +++ b/feature_tests/cpp/docs/source/lifetimes_ffi.rst @@ -3,6 +3,7 @@ .. cpp:class:: Bar + .. cpp:struct:: BorrowedFields .. cpp:member:: std::u16string_view a diff --git a/feature_tests/cpp/include/Bar.h b/feature_tests/cpp/include/Bar.h index f4467f2dd..f7a93e618 100644 --- a/feature_tests/cpp/include/Bar.h +++ b/feature_tests/cpp/include/Bar.h @@ -14,11 +14,13 @@ typedef struct Bar Bar; #ifdef __cplusplus } // namespace capi #endif +#include "Foo.h" #ifdef __cplusplus namespace capi { extern "C" { #endif +const Foo* Bar_foo(const Bar* self); void Bar_destroy(Bar* self); #ifdef __cplusplus diff --git a/feature_tests/cpp/include/Bar.hpp b/feature_tests/cpp/include/Bar.hpp index 9298da47b..895516e97 100644 --- a/feature_tests/cpp/include/Bar.hpp +++ b/feature_tests/cpp/include/Bar.hpp @@ -11,6 +11,7 @@ #include "Bar.h" +class Foo; /** * A destruction policy for using Bar with std::unique_ptr. @@ -32,5 +33,6 @@ class Bar { std::unique_ptr inner; }; +#include "Foo.hpp" #endif diff --git a/feature_tests/cpp2/include/Bar.d.hpp b/feature_tests/cpp2/include/Bar.d.hpp index 089d2f72e..91e24a10d 100644 --- a/feature_tests/cpp2/include/Bar.d.hpp +++ b/feature_tests/cpp2/include/Bar.d.hpp @@ -10,10 +10,14 @@ #include "diplomat_runtime.hpp" #include "Bar.d.h" +class Foo; + class Bar { public: + inline const Foo& foo() const; + inline const capi::Bar* AsFFI() const; inline capi::Bar* AsFFI(); inline static const Bar* FromFFI(const capi::Bar* ptr); diff --git a/feature_tests/cpp2/include/Bar.h b/feature_tests/cpp2/include/Bar.h index 1c6201f79..56943a954 100644 --- a/feature_tests/cpp2/include/Bar.h +++ b/feature_tests/cpp2/include/Bar.h @@ -6,6 +6,8 @@ #include #include #include "diplomat_runtime.h" +#include "Foo.d.h" +#include "Foo.h" #include "Bar.d.h" @@ -15,6 +17,8 @@ extern "C" { #endif // __cplusplus +const Foo* Bar_foo(const Bar* self); + void Bar_destroy(Bar* self); diff --git a/feature_tests/cpp2/include/Bar.hpp b/feature_tests/cpp2/include/Bar.hpp index 346331677..caf1fd600 100644 --- a/feature_tests/cpp2/include/Bar.hpp +++ b/feature_tests/cpp2/include/Bar.hpp @@ -11,8 +11,14 @@ #include #include "diplomat_runtime.hpp" #include "Bar.h" +#include "Foo.hpp" +inline const Foo& Bar::foo() const { + auto result = capi::Bar_foo(this->AsFFI()); + return *Foo::FromFFI(result); +} + inline const capi::Bar* Bar::AsFFI() const { return reinterpret_cast(this); } diff --git a/feature_tests/dart/lib/src/AttrOpaque1.g.dart b/feature_tests/dart/lib/src/AttrOpaque1.g.dart index db49c97db..2c6d7c403 100644 --- a/feature_tests/dart/lib/src/AttrOpaque1.g.dart +++ b/feature_tests/dart/lib/src/AttrOpaque1.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class AttrOpaque1 implements ffi.Finalizable { final ffi.Pointer _underlying; - AttrOpaque1._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + AttrOpaque1._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_AttrOpaque1_destroy)); diff --git a/feature_tests/dart/lib/src/AttrOpaque2.g.dart b/feature_tests/dart/lib/src/AttrOpaque2.g.dart index b5156aa47..0b1de2993 100644 --- a/feature_tests/dart/lib/src/AttrOpaque2.g.dart +++ b/feature_tests/dart/lib/src/AttrOpaque2.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class AttrOpaque2 implements ffi.Finalizable { final ffi.Pointer _underlying; - AttrOpaque2._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + AttrOpaque2._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_AttrOpaque2_destroy)); diff --git a/feature_tests/dart/lib/src/Bar.g.dart b/feature_tests/dart/lib/src/Bar.g.dart index c729a120f..985cdc689 100644 --- a/feature_tests/dart/lib/src/Bar.g.dart +++ b/feature_tests/dart/lib/src/Bar.g.dart @@ -8,13 +8,24 @@ part of 'lib.g.dart'; final class Bar implements ffi.Finalizable { final ffi.Pointer _underlying; - Bar._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + Bar._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_Bar_destroy)); + + Foo get foo { + final result = _Bar_foo(_underlying); + return Foo._(result, false); + } } @ffi.Native)>(isLeaf: true, symbol: 'Bar_destroy') // ignore: non_constant_identifier_names external void _Bar_destroy(ffi.Pointer self); + +@ffi.Native Function(ffi.Pointer)>(isLeaf: true, symbol: 'Bar_foo') +// ignore: non_constant_identifier_names +external ffi.Pointer _Bar_foo(ffi.Pointer self); diff --git a/feature_tests/dart/lib/src/Float64Vec.g.dart b/feature_tests/dart/lib/src/Float64Vec.g.dart index 2c9c246fb..5cce123e6 100644 --- a/feature_tests/dart/lib/src/Float64Vec.g.dart +++ b/feature_tests/dart/lib/src/Float64Vec.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class Float64Vec implements ffi.Finalizable { final ffi.Pointer _underlying; - Float64Vec._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + Float64Vec._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_Float64Vec_destroy)); @@ -19,7 +21,7 @@ final class Float64Vec implements ffi.Finalizable { final vView = v.float64View; final result = _Float64Vec_new(vView.pointer(temp), vView.length); temp.releaseAll(); - return Float64Vec._(result); + return Float64Vec._(result, true); } factory Float64Vec.bool(core.List v) { @@ -27,7 +29,7 @@ final class Float64Vec implements ffi.Finalizable { final vView = v.boolView; final result = _Float64Vec_new_bool(vView.pointer(temp), vView.length); temp.releaseAll(); - return Float64Vec._(result); + return Float64Vec._(result, true); } factory Float64Vec.i16(core.List v) { @@ -35,7 +37,7 @@ final class Float64Vec implements ffi.Finalizable { final vView = v.int16View; final result = _Float64Vec_new_i16(vView.pointer(temp), vView.length); temp.releaseAll(); - return Float64Vec._(result); + return Float64Vec._(result, true); } factory Float64Vec.u16(core.List v) { @@ -43,7 +45,7 @@ final class Float64Vec implements ffi.Finalizable { final vView = v.uint16View; final result = _Float64Vec_new_u16(vView.pointer(temp), vView.length); temp.releaseAll(); - return Float64Vec._(result); + return Float64Vec._(result, true); } factory Float64Vec.isize(core.List v) { @@ -51,7 +53,7 @@ final class Float64Vec implements ffi.Finalizable { final vView = v.isizeView; final result = _Float64Vec_new_isize(vView.pointer(temp), vView.length); temp.releaseAll(); - return Float64Vec._(result); + return Float64Vec._(result, true); } factory Float64Vec.usize(core.List v) { @@ -59,7 +61,7 @@ final class Float64Vec implements ffi.Finalizable { final vView = v.usizeView; final result = _Float64Vec_new_usize(vView.pointer(temp), vView.length); temp.releaseAll(); - return Float64Vec._(result); + return Float64Vec._(result, true); } factory Float64Vec.f64BeBytes(ByteBuffer v) { @@ -67,7 +69,7 @@ final class Float64Vec implements ffi.Finalizable { final vView = v; final result = _Float64Vec_new_f64_be_bytes(vView.pointer(temp), vView.length); temp.releaseAll(); - return Float64Vec._(result); + return Float64Vec._(result, true); } void fillSlice(core.List v) { diff --git a/feature_tests/dart/lib/src/Foo.g.dart b/feature_tests/dart/lib/src/Foo.g.dart index a12779fe3..9f80fcd7b 100644 --- a/feature_tests/dart/lib/src/Foo.g.dart +++ b/feature_tests/dart/lib/src/Foo.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class Foo implements ffi.Finalizable { final ffi.Pointer _underlying; - Foo._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + Foo._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_Foo_destroy)); @@ -19,12 +21,12 @@ final class Foo implements ffi.Finalizable { final xView = x.utf8View; final result = _Foo_new(xView.pointer(temp), xView.length); temp.releaseAll(); - return Foo._(result); + return Foo._(result, true); } Bar get getBar { final result = _Foo_get_bar(_underlying); - return Bar._(result); + return Bar._(result, true); } factory Foo.static_(String x) { @@ -32,7 +34,7 @@ final class Foo implements ffi.Finalizable { final xView = x.utf8View; final result = _Foo_new_static(xView.pointer(temp), xView.length); temp.releaseAll(); - return Foo._(result); + return Foo._(result, true); } BorrowedFieldsReturning get asReturning { @@ -42,7 +44,7 @@ final class Foo implements ffi.Finalizable { factory Foo.extractFromFields(BorrowedFields fields) { final result = _Foo_extract_from_fields(fields._underlying); - return Foo._(result); + return Foo._(result, true); } } diff --git a/feature_tests/dart/lib/src/MyString.g.dart b/feature_tests/dart/lib/src/MyString.g.dart index fc2345534..561d0e777 100644 --- a/feature_tests/dart/lib/src/MyString.g.dart +++ b/feature_tests/dart/lib/src/MyString.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class MyString implements ffi.Finalizable { final ffi.Pointer _underlying; - MyString._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + MyString._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_MyString_destroy)); @@ -19,7 +21,7 @@ final class MyString implements ffi.Finalizable { final vView = v.utf8View; final result = _MyString_new(vView.pointer(temp), vView.length); temp.releaseAll(); - return MyString._(result); + return MyString._(result, true); } factory MyString.unsafe(String v) { @@ -27,7 +29,7 @@ final class MyString implements ffi.Finalizable { final vView = v.utf8View; final result = _MyString_new_unsafe(vView.pointer(temp), vView.length); temp.releaseAll(); - return MyString._(result); + return MyString._(result, true); } void setStr(String newStr) { diff --git a/feature_tests/dart/lib/src/One.g.dart b/feature_tests/dart/lib/src/One.g.dart index 95900ef2c..81cdb30e4 100644 --- a/feature_tests/dart/lib/src/One.g.dart +++ b/feature_tests/dart/lib/src/One.g.dart @@ -8,65 +8,67 @@ part of 'lib.g.dart'; final class One implements ffi.Finalizable { final ffi.Pointer _underlying; - One._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + One._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_One_destroy)); factory One.transitivity(One hold, One nohold) { final result = _One_transitivity(hold._underlying, nohold._underlying); - return One._(result); + return One._(result, true); } factory One.cycle(Two hold, One nohold) { final result = _One_cycle(hold._underlying, nohold._underlying); - return One._(result); + return One._(result, true); } factory One.manyDependents(One a, One b, Two c, Two d, Two nohold) { final result = _One_many_dependents(a._underlying, b._underlying, c._underlying, d._underlying, nohold._underlying); - return One._(result); + return One._(result, true); } factory One.returnOutlivesParam(Two hold, One nohold) { final result = _One_return_outlives_param(hold._underlying, nohold._underlying); - return One._(result); + return One._(result, true); } factory One.diamondTop(One top, One left, One right, One bottom) { final result = _One_diamond_top(top._underlying, left._underlying, right._underlying, bottom._underlying); - return One._(result); + return One._(result, true); } factory One.diamondLeft(One top, One left, One right, One bottom) { final result = _One_diamond_left(top._underlying, left._underlying, right._underlying, bottom._underlying); - return One._(result); + return One._(result, true); } factory One.diamondRight(One top, One left, One right, One bottom) { final result = _One_diamond_right(top._underlying, left._underlying, right._underlying, bottom._underlying); - return One._(result); + return One._(result, true); } factory One.diamondBottom(One top, One left, One right, One bottom) { final result = _One_diamond_bottom(top._underlying, left._underlying, right._underlying, bottom._underlying); - return One._(result); + return One._(result, true); } factory One.diamondAndNestedTypes(One a, One b, One c, One d, One nohold) { final result = _One_diamond_and_nested_types(a._underlying, b._underlying, c._underlying, d._underlying, nohold._underlying); - return One._(result); + return One._(result, true); } factory One.implicitBounds(One explicitHold, One implicitHold, One nohold) { final result = _One_implicit_bounds(explicitHold._underlying, implicitHold._underlying, nohold._underlying); - return One._(result); + return One._(result, true); } factory One.implicitBoundsDeep(One explicit, One implicit1, One implicit2, One nohold) { final result = _One_implicit_bounds_deep(explicit._underlying, implicit1._underlying, implicit2._underlying, nohold._underlying); - return One._(result); + return One._(result, true); } } diff --git a/feature_tests/dart/lib/src/Opaque.g.dart b/feature_tests/dart/lib/src/Opaque.g.dart index 190d7745f..8ede311e1 100644 --- a/feature_tests/dart/lib/src/Opaque.g.dart +++ b/feature_tests/dart/lib/src/Opaque.g.dart @@ -8,15 +8,17 @@ part of 'lib.g.dart'; final class Opaque implements ffi.Finalizable { final ffi.Pointer _underlying; - Opaque._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + Opaque._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_Opaque_destroy)); factory Opaque() { final result = _Opaque_new(); - return Opaque._(result); + return Opaque._(result, true); } /// See the [Rust documentation for `something`](https://docs.rs/Something/latest/struct.Something.html#method.something) for more information. diff --git a/feature_tests/dart/lib/src/OptionOpaque.g.dart b/feature_tests/dart/lib/src/OptionOpaque.g.dart index fe42ebbcf..0322b5797 100644 --- a/feature_tests/dart/lib/src/OptionOpaque.g.dart +++ b/feature_tests/dart/lib/src/OptionOpaque.g.dart @@ -8,20 +8,22 @@ part of 'lib.g.dart'; final class OptionOpaque implements ffi.Finalizable { final ffi.Pointer _underlying; - OptionOpaque._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + OptionOpaque._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_OptionOpaque_destroy)); static OptionOpaque? new_(int i) { final result = _OptionOpaque_new(i); - return result.address == 0 ? null : OptionOpaque._(result); + return result.address == 0 ? null : OptionOpaque._(result, true); } static final OptionOpaque? none = () { final result = _OptionOpaque_new_none(); - return result.address == 0 ? null : OptionOpaque._(result); + return result.address == 0 ? null : OptionOpaque._(result, true); }(); static final OptionStruct struct = () { diff --git a/feature_tests/dart/lib/src/OptionOpaqueChar.g.dart b/feature_tests/dart/lib/src/OptionOpaqueChar.g.dart index c594bb3fe..7916aebd5 100644 --- a/feature_tests/dart/lib/src/OptionOpaqueChar.g.dart +++ b/feature_tests/dart/lib/src/OptionOpaqueChar.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class OptionOpaqueChar implements ffi.Finalizable { final ffi.Pointer _underlying; - OptionOpaqueChar._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + OptionOpaqueChar._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_OptionOpaqueChar_destroy)); diff --git a/feature_tests/dart/lib/src/OptionStruct.g.dart b/feature_tests/dart/lib/src/OptionStruct.g.dart index 010360a5e..de2ea2861 100644 --- a/feature_tests/dart/lib/src/OptionStruct.g.dart +++ b/feature_tests/dart/lib/src/OptionStruct.g.dart @@ -18,13 +18,13 @@ final class OptionStruct { OptionStruct._(this._underlying); - OptionOpaque? get a => _underlying.a.address == 0 ? null : OptionOpaque._(_underlying.a); + OptionOpaque? get a => _underlying.a.address == 0 ? null : OptionOpaque._(_underlying.a, true); - OptionOpaqueChar? get b => _underlying.b.address == 0 ? null : OptionOpaqueChar._(_underlying.b); + OptionOpaqueChar? get b => _underlying.b.address == 0 ? null : OptionOpaqueChar._(_underlying.b, true); int get c => _underlying.c; - OptionOpaque? get d => _underlying.d.address == 0 ? null : OptionOpaque._(_underlying.d); + OptionOpaque? get d => _underlying.d.address == 0 ? null : OptionOpaque._(_underlying.d, true); @override bool operator ==(Object other) => diff --git a/feature_tests/dart/lib/src/RefList.g.dart b/feature_tests/dart/lib/src/RefList.g.dart index c9e72a0d3..2623b7f30 100644 --- a/feature_tests/dart/lib/src/RefList.g.dart +++ b/feature_tests/dart/lib/src/RefList.g.dart @@ -8,15 +8,17 @@ part of 'lib.g.dart'; final class RefList implements ffi.Finalizable { final ffi.Pointer _underlying; - RefList._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + RefList._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_RefList_destroy)); factory RefList.node(RefListParameter data) { final result = _RefList_node(data._underlying); - return RefList._(result); + return RefList._(result, true); } } diff --git a/feature_tests/dart/lib/src/RefListParameter.g.dart b/feature_tests/dart/lib/src/RefListParameter.g.dart index f4659584e..527165158 100644 --- a/feature_tests/dart/lib/src/RefListParameter.g.dart +++ b/feature_tests/dart/lib/src/RefListParameter.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class RefListParameter implements ffi.Finalizable { final ffi.Pointer _underlying; - RefListParameter._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + RefListParameter._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_RefListParameter_destroy)); diff --git a/feature_tests/dart/lib/src/ResultOpaque.g.dart b/feature_tests/dart/lib/src/ResultOpaque.g.dart index b739ecda2..b6ed21396 100644 --- a/feature_tests/dart/lib/src/ResultOpaque.g.dart +++ b/feature_tests/dart/lib/src/ResultOpaque.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class ResultOpaque implements ffi.Finalizable { final ffi.Pointer _underlying; - ResultOpaque._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + ResultOpaque._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_ResultOpaque_destroy)); @@ -22,7 +24,7 @@ final class ResultOpaque implements ffi.Finalizable { if (!result.isOk) { throw ErrorEnum.values[result.union.err]; } - return ResultOpaque._(result.union.ok); + return ResultOpaque._(result.union.ok, true); } /// @@ -33,7 +35,7 @@ final class ResultOpaque implements ffi.Finalizable { if (!result.isOk) { throw ErrorEnum.values[result.union.err]; } - return ResultOpaque._(result.union.ok); + return ResultOpaque._(result.union.ok, true); } /// @@ -44,7 +46,7 @@ final class ResultOpaque implements ffi.Finalizable { if (!result.isOk) { throw ErrorEnum.values[result.union.err]; } - return ResultOpaque._(result.union.ok); + return ResultOpaque._(result.union.ok, true); } /// @@ -55,7 +57,7 @@ final class ResultOpaque implements ffi.Finalizable { if (!result.isOk) { throw VoidError(); } - return ResultOpaque._(result.union.ok); + return ResultOpaque._(result.union.ok, true); } /// @@ -66,7 +68,7 @@ final class ResultOpaque implements ffi.Finalizable { if (!result.isOk) { throw ErrorStruct._(result.union.err); } - return ResultOpaque._(result.union.ok); + return ResultOpaque._(result.union.ok, true); } /// @@ -75,7 +77,7 @@ final class ResultOpaque implements ffi.Finalizable { static void newInErr(int i) { final result = _ResultOpaque_new_in_err(i); if (!result.isOk) { - throw ResultOpaque._(result.union.err); + throw ResultOpaque._(result.union.err, true); } } @@ -96,7 +98,7 @@ final class ResultOpaque implements ffi.Finalizable { static ErrorEnum newInEnumErr(int i) { final result = _ResultOpaque_new_in_enum_err(i); if (!result.isOk) { - throw ResultOpaque._(result.union.err); + throw ResultOpaque._(result.union.err, true); } return ErrorEnum.values[result.union.ok]; } diff --git a/feature_tests/dart/lib/src/Two.g.dart b/feature_tests/dart/lib/src/Two.g.dart index 0f4236e6c..7c032346a 100644 --- a/feature_tests/dart/lib/src/Two.g.dart +++ b/feature_tests/dart/lib/src/Two.g.dart @@ -8,8 +8,10 @@ part of 'lib.g.dart'; final class Two implements ffi.Finalizable { final ffi.Pointer _underlying; - Two._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + Two._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_Two_destroy)); diff --git a/feature_tests/dotnet/Lib/Generated/RawBar.cs b/feature_tests/dotnet/Lib/Generated/RawBar.cs index 4618861f7..a55d8af91 100644 --- a/feature_tests/dotnet/Lib/Generated/RawBar.cs +++ b/feature_tests/dotnet/Lib/Generated/RawBar.cs @@ -16,6 +16,9 @@ public partial struct Bar { private const string NativeLib = "diplomat_feature_tests"; + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Bar_foo", ExactSpelling = true)] + public static unsafe extern Foo* Foo(Bar* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Bar_destroy", ExactSpelling = true)] public static unsafe extern void Destroy(Bar* self); } diff --git a/feature_tests/js/api/Bar.d.ts b/feature_tests/js/api/Bar.d.ts index 327e5a611..29c81c708 100644 --- a/feature_tests/js/api/Bar.d.ts +++ b/feature_tests/js/api/Bar.d.ts @@ -1,5 +1,10 @@ +import { Foo } from "./Foo"; /** */ export class Bar { + + /** + */ + foo(): Foo; } diff --git a/feature_tests/js/api/Bar.mjs b/feature_tests/js/api/Bar.mjs index 66613b0e3..fd8a9cc22 100644 --- a/feature_tests/js/api/Bar.mjs +++ b/feature_tests/js/api/Bar.mjs @@ -1,5 +1,6 @@ import wasm from "./diplomat-wasm.mjs" import * as diplomatRuntime from "./diplomat-runtime.mjs" +import { Foo } from "./Foo.mjs" const Bar_box_destroy_registry = new FinalizationRegistry(underlying => { wasm.Bar_destroy(underlying); @@ -14,4 +15,8 @@ export class Bar { Bar_box_destroy_registry.register(this, underlying); } } + + foo() { + return new Foo(wasm.Bar_foo(this.underlying), false, [this]); + } } diff --git a/feature_tests/js/docs/source/lifetimes_ffi.rst b/feature_tests/js/docs/source/lifetimes_ffi.rst index 3bb763315..6b1865aac 100644 --- a/feature_tests/js/docs/source/lifetimes_ffi.rst +++ b/feature_tests/js/docs/source/lifetimes_ffi.rst @@ -3,6 +3,8 @@ .. js:class:: Bar + .. js:method:: foo() + .. js:class:: BorrowedFields .. js:attribute:: a diff --git a/feature_tests/src/lifetimes.rs b/feature_tests/src/lifetimes.rs index 16f4f8785..a0b6fa9d2 100644 --- a/feature_tests/src/lifetimes.rs +++ b/feature_tests/src/lifetimes.rs @@ -39,11 +39,12 @@ pub mod ffi { } // FIXME(#191): This test breaks the C++ codegen - // impl<'b, 'a: 'b> Bar<'b, 'a> { - // pub fn foo(&'b self) -> &'b Foo<'a> { - // self.0 - // } - // } + impl<'b, 'a: 'b> Bar<'b, 'a> { + #[diplomat::skip_if_unsupported] + pub fn foo(&'b self) -> &'b Foo<'a> { + self.0 + } + } #[derive(Copy, Clone)] #[diplomat::opaque] diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 19d35e645..1507c71fa 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -273,7 +273,11 @@ impl AttributeInfo { } else if seg == "out" { is_out = true; return false; - } else if seg == "rust_link" || seg == "out" || seg == "attr" { + } else if seg == "rust_link" + || seg == "out" + || seg == "attr" + || seg == "skip_if_unsupported" + { // diplomat-tool reads these, not diplomat::bridge. // throw them away so rustc doesn't complain about unknown attributes return false; diff --git a/tool/src/cpp/docs.rs b/tool/src/cpp/docs.rs index 4eae1c6ac..34058f6c9 100644 --- a/tool/src/cpp/docs.rs +++ b/tool/src/cpp/docs.rs @@ -161,6 +161,12 @@ pub fn gen_method_docs( docs_url_gen: &ast::DocsUrlGenerator, out: &mut W, ) -> fmt::Result { + if method.attrs.skip_if_unsupported + && matches!(method.return_type, Some(ast::TypeName::Reference(..))) + { + // We don't support returning references + return Ok(()); + } // This method should rearrange the writeable let rearranged_writeable = method.is_writeable_out() && writeable_to_string; diff --git a/tool/src/cpp/structs.rs b/tool/src/cpp/structs.rs index 600b6e562..414ca83d0 100644 --- a/tool/src/cpp/structs.rs +++ b/tool/src/cpp/structs.rs @@ -177,6 +177,12 @@ fn gen_method( docs_url_gen: &ast::DocsUrlGenerator, out: &mut W, ) -> fmt::Result { + if method.attrs.skip_if_unsupported + && matches!(method.return_type, Some(ast::TypeName::Reference(..))) + { + // We don't support returning references + return Ok(()); + } // This method should rearrange the writeable let rearranged_writeable = method.is_writeable_out() && writeable_to_string; diff --git a/tool/src/dart/mod.rs b/tool/src/dart/mod.rs index e532ef24b..c6b2fe5da 100644 --- a/tool/src/dart/mod.rs +++ b/tool/src/dart/mod.rs @@ -691,12 +691,11 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { let id = op.tcx_id.into(); let type_name = self.formatter.fmt_type_name(id); - match (op.owner.is_owned(), op.is_optional()) { - (false, _) => unimplemented!(), - (true, false) => format!("{type_name}._({var_name})").into(), - (true, true) => { - format!("{var_name}.address == 0 ? null : {type_name}._({var_name})").into() - } + let owned = op.owner.is_owned(); + if op.is_optional() { + format!("{var_name}.address == 0 ? null : {type_name}._({var_name}, {owned})").into() + } else { + format!("{type_name}._({var_name}, {owned})").into() } } Type::Struct(ref st) => { diff --git a/tool/src/dotnet/idiomatic.rs b/tool/src/dotnet/idiomatic.rs index 1ad5184c0..30a49207b 100644 --- a/tool/src/dotnet/idiomatic.rs +++ b/tool/src/dotnet/idiomatic.rs @@ -313,6 +313,12 @@ fn gen_method( docs_url_gen: &ast::DocsUrlGenerator, out: &mut CodeWriter, ) -> fmt::Result { + if method.attrs.skip_if_unsupported + && matches!(method.return_type, Some(ast::TypeName::Reference(..))) + { + // We don't support returning references + return Ok(()); + } // This method should rearrange the writeable let rearranged_writeable = method.is_writeable_out() && writeable_to_string; diff --git a/tool/templates/dart/opaque.dart.jinja b/tool/templates/dart/opaque.dart.jinja index a28ed0dc4..97f63fb10 100644 --- a/tool/templates/dart/opaque.dart.jinja +++ b/tool/templates/dart/opaque.dart.jinja @@ -4,8 +4,10 @@ final class {{type_name}} implements ffi.Finalizable { final ffi.Pointer _underlying; - {{type_name}}._(this._underlying) { - _finalizer.attach(this, _underlying.cast()); + {{type_name}}._(this._underlying, bool isOwned) { + if (isOwned) { + _finalizer.attach(this, _underlying.cast()); + } } static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_{{destructor}}));