From baa148b811ac9584f6c5edc0a03c03ae9485c01b Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Mon, 9 Dec 2024 10:46:49 -0800 Subject: [PATCH] Adding support for `Throwable` error result types in Kotlin (#741) * Adding support for returning opaques as the error type of a result, as a custom Kotlin exception type * adding same support for throwable structs * clippy * fixing multiple inheritance * moooore clippy * test fix * new error log * making custom `error` attribute * clippy --------- Co-authored-by: Ellen Arteca --- core/src/hir/attrs.rs | 32 ++++++++++++++- ...ir__elision__tests__elision_in_struct.snap | 4 ++ ...core__hir__elision__tests__simple_mod.snap | 11 +++++ .../dev/diplomattest/somelib/DataProvider.kt | 2 +- .../dev/diplomattest/somelib/FixedDecimal.kt | 2 +- .../somelib/FixedDecimalFormatter.kt | 2 +- .../kotlin/dev/diplomattest/somelib/Lib.kt | 6 ++- .../dev/diplomattest/somelib/ErrorStruct.kt | 2 +- .../kotlin/dev/diplomattest/somelib/Lib.kt | 6 ++- .../kotlin/dev/diplomattest/somelib/MyZst.kt | 2 +- .../dev/diplomattest/somelib/OptionString.kt | 2 +- .../dev/diplomattest/somelib/ResultOpaque.kt | 12 +++--- .../diplomattest/somelib/ResultOpaqueTest.kt | 8 ++-- feature_tests/src/result.rs | 2 + feature_tests/src/structs.rs | 1 + tool/src/c/mod.rs | 1 + tool/src/cpp/mod.rs | 1 + tool/src/js/mod.rs | 1 + tool/src/kotlin/mod.rs | 41 +++++++++++++++++-- ...plomat_tool__kotlin__test__opaque_gen.snap | 2 +- ...n__test__opaque_gen_multiple_ref_args.snap | 2 +- ...lin__test__opaque_gen_with_finalizers.snap | 2 +- .../diplomat_tool__kotlin__test__struct.snap | 5 ++- tool/templates/kotlin/Opaque.kt.jinja | 4 +- tool/templates/kotlin/Struct.kt.jinja | 2 +- tool/templates/kotlin/init.kt.jinja | 6 ++- 26 files changed, 128 insertions(+), 33 deletions(-) diff --git a/core/src/hir/attrs.rs b/core/src/hir/attrs.rs index c7c226190..9aa18ec7b 100644 --- a/core/src/hir/attrs.rs +++ b/core/src/hir/attrs.rs @@ -41,6 +41,8 @@ pub struct Attrs { /// This attribute does not participate in inheritance and must always /// be specified on individual methods pub special_method: Option, + /// This user-defined type can be used as the error type in a Result. + pub custom_errors: bool, /// From #[diplomat::demo()]. Created from [`crate::ast::attrs::Attrs::demo_attrs`]. /// List of attributes specific to automatic demo generation. @@ -349,9 +351,16 @@ impl Attrs { continue; } } + } else if path == "error" { + if !support.custom_errors { + maybe_error_unsupported(auto_found, "error", backend, errors); + continue; + } + auto_used = true; + this.custom_errors = true; } else { errors.push(LoweringError::Other(format!( - "Unknown diplomat attribute {path}: expected one of: `disable, rename, namespace, constructor, stringifier, comparison, named_constructor, getter, setter, indexer`" + "Unknown diplomat attribute {path}: expected one of: `disable, rename, namespace, constructor, stringifier, comparison, named_constructor, getter, setter, indexer, error`" ))); } if auto_found && !auto_used { @@ -361,7 +370,7 @@ impl Attrs { } } else { errors.push(LoweringError::Other(format!( - "Unknown diplomat attribute {path:?}: expected one of: `disable, rename, namespace, constructor, stringifier, comparison, named_constructor, getter, setter, indexer`" + "Unknown diplomat attribute {path:?}: expected one of: `disable, rename, namespace, constructor, stringifier, comparison, named_constructor, getter, setter, indexer, error`" ))); } } @@ -458,6 +467,7 @@ impl Attrs { rename, abi_rename, special_method, + custom_errors, demo_attrs: _, } = &self; @@ -745,6 +755,17 @@ impl Attrs { ))); } } + + if *custom_errors + && !matches!( + context, + AttributeContext::Type(..) | AttributeContext::Trait(..) + ) + { + errors.push(LoweringError::Other( + "`error` can only be used on types".to_string(), + )); + } } pub(crate) fn for_inheritance(&self, context: AttrInheritContext) -> Attrs { @@ -773,6 +794,8 @@ impl Attrs { abi_rename: Default::default(), // Never inherited special_method: None, + // Not inherited + custom_errors: false, demo_attrs: Default::default(), } } @@ -849,6 +872,8 @@ pub struct BackendAttrSupport { pub callbacks: bool, /// Allowing traits pub traits: bool, + /// Marking a user-defined type as being a valid error result type. + pub custom_errors: bool, } impl BackendAttrSupport { @@ -875,6 +900,7 @@ impl BackendAttrSupport { option: true, callbacks: true, traits: true, + custom_errors: true, } } } @@ -1007,6 +1033,7 @@ impl AttributeValidator for BasicAttributeValidator { option, callbacks, traits, + custom_errors, } = self.support; match value { "namespacing" => namespacing, @@ -1029,6 +1056,7 @@ impl AttributeValidator for BasicAttributeValidator { "option" => option, "callbacks" => callbacks, "traits" => traits, + "custom_errors" => custom_errors, _ => { return Err(LoweringError::Other(format!( "Unknown supports = value found: {value}" diff --git a/core/src/hir/snapshots/diplomat_core__hir__elision__tests__elision_in_struct.snap b/core/src/hir/snapshots/diplomat_core__hir__elision__tests__elision_in_struct.snap index 3fec47a51..26944dd95 100644 --- a/core/src/hir/snapshots/diplomat_core__hir__elision__tests__elision_in_struct.snap +++ b/core/src/hir/snapshots/diplomat_core__hir__elision__tests__elision_in_struct.snap @@ -1,5 +1,6 @@ --- source: core/src/hir/elision.rs +assertion_line: 536 expression: method --- Method { @@ -44,6 +45,7 @@ Method { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -97,6 +99,7 @@ Method { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -123,6 +126,7 @@ Method { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, diff --git a/core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap b/core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap index a19cedd5f..eb5bbf6fa 100644 --- a/core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap +++ b/core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap @@ -1,5 +1,6 @@ --- source: core/src/hir/elision.rs +assertion_line: 473 expression: tcx --- TypeContext { @@ -47,6 +48,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -104,6 +106,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -149,6 +152,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -172,6 +176,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -236,6 +241,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -295,6 +301,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -333,6 +340,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -372,6 +380,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -395,6 +404,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, @@ -441,6 +451,7 @@ TypeContext { pattern: None, }, special_method: None, + custom_errors: false, demo_attrs: DemoInfo { generate: false, default_constructor: false, diff --git a/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/DataProvider.kt b/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/DataProvider.kt index 8397c3886..0bf7fc61a 100644 --- a/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/DataProvider.kt +++ b/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/DataProvider.kt @@ -52,7 +52,7 @@ class DataProvider internal constructor ( if (returnVal.isOk == 1.toByte()) { return Unit.ok() } else { - return Unit.err() + return Unit.primitive_err() } } } diff --git a/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/FixedDecimal.kt b/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/FixedDecimal.kt index f3b6749d8..eb13db45a 100644 --- a/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/FixedDecimal.kt +++ b/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/FixedDecimal.kt @@ -66,7 +66,7 @@ class FixedDecimal internal constructor ( val returnString = DW.writeToString(write) return returnString.ok() } else { - return Unit.err() + return Unit.primitive_err() } } diff --git a/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/FixedDecimalFormatter.kt b/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/FixedDecimalFormatter.kt index 3b616a121..d995f9303 100644 --- a/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/FixedDecimalFormatter.kt +++ b/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/FixedDecimalFormatter.kt @@ -46,7 +46,7 @@ class FixedDecimalFormatter internal constructor ( CLEANER.register(returnOpaque, FixedDecimalFormatter.FixedDecimalFormatterCleaner(handle, FixedDecimalFormatter.lib)); return returnOpaque.ok() } else { - return Unit.err() + return Unit.primitive_err() } } } diff --git a/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Lib.kt b/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Lib.kt index a04049934..482c104f2 100644 --- a/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Lib.kt +++ b/example/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Lib.kt @@ -331,10 +331,14 @@ internal fun T.ok(): Result { return Result.success(this) } -internal fun E.err(): Result { +internal fun E.primitive_err(): Result { return Result.failure(RuntimeException("Received error $this")) } +internal fun Throwable.err(): Result { + return Result.failure(this) +} + internal class ResultPointerUnitUnion: Union() { @JvmField internal var ok: Pointer = Pointer(0) diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/ErrorStruct.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/ErrorStruct.kt index 3bbd89b73..f524466d5 100644 --- a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/ErrorStruct.kt +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/ErrorStruct.kt @@ -22,7 +22,7 @@ internal class ErrorStructNative: Structure(), Structure.ByValue { } class ErrorStruct internal constructor ( - internal val nativeStruct: ErrorStructNative) { + internal val nativeStruct: ErrorStructNative): Exception("Rust error result for ErrorStruct") { val i: Int = nativeStruct.i val j: Int = nativeStruct.j diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Lib.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Lib.kt index 8f5c948a5..9285fb030 100644 --- a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Lib.kt +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Lib.kt @@ -331,10 +331,14 @@ internal fun T.ok(): Result { return Result.success(this) } -internal fun E.err(): Result { +internal fun E.primitive_err(): Result { return Result.failure(RuntimeException("Received error $this")) } +internal fun Throwable.err(): Result { + return Result.failure(this) +} + internal class ResultIntUnitUnion: Union() { @JvmField internal var ok: Int = 0 diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/MyZst.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/MyZst.kt index 182df4327..e2d76ccac 100644 --- a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/MyZst.kt +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/MyZst.kt @@ -7,6 +7,6 @@ import com.sun.jna.Pointer import com.sun.jna.Structure class MyZst internal constructor ( - ) { + ): Exception("Rust error result for MyZst") { } diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/OptionString.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/OptionString.kt index 6e799de9c..e3bb88989 100644 --- a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/OptionString.kt +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/OptionString.kt @@ -51,7 +51,7 @@ class OptionString internal constructor ( val returnString = DW.writeToString(write) return returnString.ok() } else { - return Unit.err() + return Unit.primitive_err() } } diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/ResultOpaque.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/ResultOpaque.kt index c9ab4e2cf..83e665b14 100644 --- a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/ResultOpaque.kt +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/ResultOpaque.kt @@ -24,7 +24,7 @@ class ResultOpaque internal constructor ( // These ensure that anything that is borrowed is kept alive and not cleaned // up by the garbage collector. internal val selfEdges: List, -) { +): Exception("Rust error result for ResultOpaque") { internal class ResultOpaqueCleaner(val handle: Pointer, val lib: ResultOpaqueLib) : Runnable { override fun run() { @@ -46,7 +46,7 @@ class ResultOpaque internal constructor ( CLEANER.register(returnOpaque, ResultOpaque.ResultOpaqueCleaner(handle, ResultOpaque.lib)); return returnOpaque.ok() } else { - return ErrorEnum.fromNative(returnVal.union.err).err() + return ErrorEnum.fromNative(returnVal.union.err).primitive_err() } } @@ -60,7 +60,7 @@ class ResultOpaque internal constructor ( CLEANER.register(returnOpaque, ResultOpaque.ResultOpaqueCleaner(handle, ResultOpaque.lib)); return returnOpaque.ok() } else { - return ErrorEnum.fromNative(returnVal.union.err).err() + return ErrorEnum.fromNative(returnVal.union.err).primitive_err() } } @@ -74,7 +74,7 @@ class ResultOpaque internal constructor ( CLEANER.register(returnOpaque, ResultOpaque.ResultOpaqueCleaner(handle, ResultOpaque.lib)); return returnOpaque.ok() } else { - return ErrorEnum.fromNative(returnVal.union.err).err() + return ErrorEnum.fromNative(returnVal.union.err).primitive_err() } } @@ -88,7 +88,7 @@ class ResultOpaque internal constructor ( CLEANER.register(returnOpaque, ResultOpaque.ResultOpaqueCleaner(handle, ResultOpaque.lib)); return returnOpaque.ok() } else { - return Unit.err() + return Unit.primitive_err() } } @@ -128,7 +128,7 @@ class ResultOpaque internal constructor ( if (returnVal.isOk == 1.toByte()) { return (returnVal.union.ok).ok() } else { - return Unit.err() + return Unit.primitive_err() } } diff --git a/feature_tests/kotlin/somelib/src/test/kotlin/dev/diplomattest/somelib/ResultOpaqueTest.kt b/feature_tests/kotlin/somelib/src/test/kotlin/dev/diplomattest/somelib/ResultOpaqueTest.kt index fc795dbab..66e8860f7 100644 --- a/feature_tests/kotlin/somelib/src/test/kotlin/dev/diplomattest/somelib/ResultOpaqueTest.kt +++ b/feature_tests/kotlin/somelib/src/test/kotlin/dev/diplomattest/somelib/ResultOpaqueTest.kt @@ -16,7 +16,7 @@ class ResultOpaqueTest { assert(resultOpaque2.isFailure) val result2 = resultOpaque2.exceptionOrNull()?.message - val shouldRes: Result = ErrorEnum.Bar.err() + val shouldRes: Result = ErrorEnum.Bar.primitive_err() assertEquals(result2, shouldRes.exceptionOrNull()?.message) @@ -24,14 +24,14 @@ class ResultOpaqueTest { val resultOpaque3 = ResultOpaque.newFailingFoo() assert(resultOpaque3.isFailure) val result3 = resultOpaque3.exceptionOrNull()?.message - val shouldRes3: Result = ErrorEnum.Foo.err() + val shouldRes3: Result = ErrorEnum.Foo.primitive_err() assertEquals(result3, shouldRes3.exceptionOrNull()?.message) val resultOpaque4 = ResultOpaque.newInErr(8) assert(resultOpaque4.isFailure) val result4 = resultOpaque4.exceptionOrNull()?.message - val assertion = result4?.startsWith("Received error dev.diplomattest.somelib.ResultOpaque", true) + val assertion = result4?.startsWith("Rust error result for ResultOpaque", true) assert(assertion == true) } -} \ No newline at end of file +} diff --git a/feature_tests/src/result.rs b/feature_tests/src/result.rs index 0786a2b86..1012b6d78 100644 --- a/feature_tests/src/result.rs +++ b/feature_tests/src/result.rs @@ -2,6 +2,7 @@ pub mod ffi { #[diplomat::opaque] + #[diplomat::attr(auto, error)] pub struct ResultOpaque(i32); #[derive(PartialEq, Eq, Debug)] @@ -11,6 +12,7 @@ pub mod ffi { } #[derive(Debug)] + #[diplomat::attr(auto, error)] pub struct ErrorStruct { i: i32, j: i32, diff --git a/feature_tests/src/structs.rs b/feature_tests/src/structs.rs index 66039ca39..fb23abf32 100644 --- a/feature_tests/src/structs.rs +++ b/feature_tests/src/structs.rs @@ -45,6 +45,7 @@ pub mod ffi { g: MyEnum, } + #[diplomat::attr(auto, error)] pub struct MyZst; impl Opaque { diff --git a/tool/src/c/mod.rs b/tool/src/c/mod.rs index 322f65021..03498c6de 100644 --- a/tool/src/c/mod.rs +++ b/tool/src/c/mod.rs @@ -34,6 +34,7 @@ pub(crate) fn attr_support() -> BackendAttrSupport { a.option = true; a.callbacks = true; a.traits = true; + a.custom_errors = false; a } diff --git a/tool/src/cpp/mod.rs b/tool/src/cpp/mod.rs index e8a63f9b4..8c6f542f0 100644 --- a/tool/src/cpp/mod.rs +++ b/tool/src/cpp/mod.rs @@ -30,6 +30,7 @@ pub(crate) fn attr_support() -> BackendAttrSupport { a.option = true; a.callbacks = false; a.traits = false; + a.custom_errors = false; a } diff --git a/tool/src/js/mod.rs b/tool/src/js/mod.rs index d7b36da7f..ec92c5206 100644 --- a/tool/src/js/mod.rs +++ b/tool/src/js/mod.rs @@ -56,6 +56,7 @@ pub(crate) fn attr_support() -> BackendAttrSupport { a.callbacks = false; a.option = true; a.traits = false; + a.custom_errors = false; // TODO a } diff --git a/tool/src/kotlin/mod.rs b/tool/src/kotlin/mod.rs index 9b64e8975..e1d57bb09 100644 --- a/tool/src/kotlin/mod.rs +++ b/tool/src/kotlin/mod.rs @@ -3,8 +3,9 @@ use diplomat_core::hir::borrowing_param::{BorrowedLifetimeInfo, ParamBorrowInfo} use diplomat_core::hir::{ self, BackendAttrSupport, Borrow, Callback, DocsUrlGenerator, InputOnly, Lifetime, LifetimeEnv, Lifetimes, MaybeOwn, MaybeStatic, Method, Mutability, OpaquePath, Optional, OutType, Param, - PrimitiveType, ReturnableStructDef, SelfType, Slice, SpecialMethod, StringEncoding, - StructField, StructPath, StructPathLike, TraitIdGetter, TyPosition, Type, TypeContext, TypeDef, + PrimitiveType, ReturnableStructDef, ReturnableStructPath, SelfType, Slice, SpecialMethod, + StringEncoding, StructField, StructPath, StructPathLike, TraitIdGetter, TyPosition, Type, + TypeContext, TypeDef, }; use diplomat_core::hir::{ReturnType, SuccessType}; @@ -42,6 +43,7 @@ pub(crate) fn attr_support() -> BackendAttrSupport { a.indexing = true; a.callbacks = true; a.traits = true; + a.custom_errors = true; a } @@ -850,17 +852,44 @@ val intermediateOption = {val_name}.option() ?: return null let err_path = err .as_ref() .map(|err| { + let err_converter = if let OutType::Opaque(..) | OutType::Struct(..) = err { + match err { + OutType::Opaque(OpaquePath{tcx_id: id, ..}) => { + let resolved = self.tcx.resolve_opaque(*id); + if !resolved.attrs.custom_errors { + panic!("Opaque type {:?} must have the `error` attribute to be used as an error result", resolved.name); + } + }, + OutType::Struct(ReturnableStructPath::Struct(path)) => { + let resolved = self.tcx.resolve_struct(path.tcx_id); + if !resolved.attrs.custom_errors { + panic!("Struct type {:?} must have the `error` attribute to be used as an error result", resolved.name); + } + }, + OutType::Struct(ReturnableStructPath::OutStruct(path)) => { + let resolved = self.tcx.resolve_out_struct(path.tcx_id); + if !resolved.attrs.custom_errors { + panic!("Struct type {:?} must have the `error` attribute to be used as an error result", resolved.name); + } + } + _ => {} + } + ".err()" + } else { + ".primitive_err()" + }; + self.gen_out_type_return_conversion( method, &method_lifetimes_map, cleanups, "returnVal.union.err", - ".err()", + err_converter, err, use_finalizers_not_cleaners, ) }) - .unwrap_or_else(|| "return Unit.err()".into()); + .unwrap_or_else(|| "return Unit.primitive_err()".into()); #[derive(Template)] #[template(path = "kotlin/ResultReturn.kt.jinja", escape = "none")] @@ -1355,6 +1384,7 @@ returnVal.option() ?: return null callback_params: &'a [CallbackParamInfo], use_finalizers_not_cleaners: bool, docs: String, + is_custom_error: bool, } ( @@ -1372,6 +1402,7 @@ returnVal.option() ?: return null callback_params: self.callback_params.as_ref(), use_finalizers_not_cleaners, docs: self.formatter.fmt_docs(&ty.docs), + is_custom_error: ty.attrs.custom_errors, } .render() .expect("failed to generate struct"), @@ -1462,6 +1493,7 @@ returnVal.option() ?: return null callback_params: &'a [CallbackParamInfo], lifetimes: Vec>, docs: String, + is_custom_error: bool, } let fields = ty @@ -1498,6 +1530,7 @@ returnVal.option() ?: return null callback_params: self.callback_params.as_ref(), lifetimes, docs: self.formatter.fmt_docs(&ty.docs), + is_custom_error: ty.attrs.custom_errors, } .render() .expect("Failed to render struct template"), diff --git a/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen.snap b/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen.snap index 5f8583db2..dfff7f8bd 100644 --- a/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen.snap +++ b/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen.snap @@ -1,6 +1,6 @@ --- source: tool/src/kotlin/mod.rs -assertion_line: 1995 +assertion_line: 2352 expression: result --- package dev.gigapixel.somelib; diff --git a/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen_multiple_ref_args.snap b/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen_multiple_ref_args.snap index 37b3f5184..3116d7fc4 100644 --- a/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen_multiple_ref_args.snap +++ b/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen_multiple_ref_args.snap @@ -1,6 +1,6 @@ --- source: tool/src/kotlin/mod.rs -assertion_line: 2174 +assertion_line: 2243 expression: result --- package dev.gigapixel.somelib; diff --git a/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen_with_finalizers.snap b/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen_with_finalizers.snap index 6158e8ae0..16d3d1f94 100644 --- a/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen_with_finalizers.snap +++ b/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__opaque_gen_with_finalizers.snap @@ -1,6 +1,6 @@ --- source: tool/src/kotlin/mod.rs -assertion_line: 2328 +assertion_line: 2403 expression: result --- package dev.gigapixel.somelib; diff --git a/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__struct.snap b/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__struct.snap index 03edb42e7..d91788193 100644 --- a/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__struct.snap +++ b/tool/src/kotlin/snapshots/diplomat_tool__kotlin__test__struct.snap @@ -1,5 +1,6 @@ --- source: tool/src/kotlin/mod.rs +assertion_line: 2191 expression: struct_code --- package dev.gigapixel.somelib @@ -159,7 +160,7 @@ class MyNativeStruct internal constructor ( if (returnVal.isOk == 1.toByte()) { return (returnVal.union.ok > 0).ok() } else { - return Unit.err() + return Unit.primitive_err() } } @@ -169,7 +170,7 @@ class MyNativeStruct internal constructor ( if (returnVal.isOk == 1.toByte()) { return (returnVal.union.ok.toUByte()).ok() } else { - return Unit.err() + return Unit.primitive_err() } } } diff --git a/tool/templates/kotlin/Opaque.kt.jinja b/tool/templates/kotlin/Opaque.kt.jinja index d90d72b48..bac524a3f 100644 --- a/tool/templates/kotlin/Opaque.kt.jinja +++ b/tool/templates/kotlin/Opaque.kt.jinja @@ -41,8 +41,8 @@ class {{type_name}} internal constructor ( {%- if use_finalizers_not_cleaners %} internal var finalizer_registered: Boolean = false, {%- endif %} -) -{%- if special_methods.interfaces.is_empty() %} {% else %}: {% for interface in special_methods.interfaces %} +){%- if is_custom_error %}: Exception("Rust error result for {{type_name}}") {%- endif %} +{%- if special_methods.interfaces.is_empty() %} {% else %}{%- if is_custom_error %},{% else %}:{% endif %} {% for interface in special_methods.interfaces %} {%- if loop.first %}{% else %}, {% endif %}{{interface}} {%- endfor %} {%- endif %} { diff --git a/tool/templates/kotlin/Struct.kt.jinja b/tool/templates/kotlin/Struct.kt.jinja index 19730feca..c011afa78 100644 --- a/tool/templates/kotlin/Struct.kt.jinja +++ b/tool/templates/kotlin/Struct.kt.jinja @@ -50,7 +50,7 @@ class {{type_name}} internal constructor ( internal val {{lt}}Edges: List{% if !loop.last%},{% endif %} {%- endfor %} {% endif -%} - ) { + ){%- if is_custom_error %}: Exception("Rust error result for {{type_name}}") {%- endif %} { {%- for field in fields %} val {{field.name}}: {{field.field_type}} = {{field.native_to_kt}} diff --git a/tool/templates/kotlin/init.kt.jinja b/tool/templates/kotlin/init.kt.jinja index 072ed77c6..34f508ecb 100644 --- a/tool/templates/kotlin/init.kt.jinja +++ b/tool/templates/kotlin/init.kt.jinja @@ -333,10 +333,14 @@ internal fun T.ok(): Result { return Result.success(this) } -internal fun E.err(): Result { +internal fun E.primitive_err(): Result { return Result.failure(RuntimeException("Received error $this")) } +internal fun Throwable.err(): Result { + return Result.failure(this) +} + {% for native_result in native_results -%} {{native_result}} {% endfor %}