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/core/src/hir/type_context.rs b/core/src/hir/type_context.rs index 747261783..7c7fc3c41 100644 --- a/core/src/hir/type_context.rs +++ b/core/src/hir/type_context.rs @@ -103,7 +103,7 @@ impl TypeContext { ) } - pub fn all_traits<'tcx>(&'tcx self) -> impl Iterator { + pub fn all_traits<'tcx>(&'tcx self) -> impl Iterator { self.traits .iter() .enumerate() @@ -203,7 +203,7 @@ impl TypeContext { pub(super) fn from_ast_without_validation<'ast>( env: &'ast Env, attr_validator: impl AttributeValidator + 'static, - ) -> Result<(LoweringContext, Self), Vec> { + ) -> Result<(LoweringContext<'ast>, Self), Vec> { let mut ast_out_structs = SmallVec::<[_; 16]>::new(); let mut ast_structs = SmallVec::<[_; 16]>::new(); let mut ast_opaques = SmallVec::<[_; 16]>::new(); 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 04522f675..281b67de2 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 8cb1515a8..855871df2 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/c/ty.rs b/tool/src/c/ty.rs index be3e16929..744838f2c 100644 --- a/tool/src/c/ty.rs +++ b/tool/src/c/ty.rs @@ -78,7 +78,7 @@ pub struct TyGenContext<'cx, 'tcx> { pub impl_header_path: &'cx String, } -impl<'cx, 'tcx> TyGenContext<'cx, 'tcx> { +impl<'tcx> TyGenContext<'_, 'tcx> { pub fn gen_enum_def(&self, def: &'tcx hir::EnumDef) -> Header { let mut decl_header = Header::new(self.decl_header_path.clone(), self.is_for_cpp); let ty_name = self.formatter.fmt_type_name(self.id.try_into().unwrap()); 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/cpp/ty.rs b/tool/src/cpp/ty.rs index 73af006a5..92e774e6e 100644 --- a/tool/src/cpp/ty.rs +++ b/tool/src/cpp/ty.rs @@ -61,7 +61,7 @@ pub(super) struct TyGenContext<'ccx, 'tcx, 'header> { pub generating_struct_fields: bool, } -impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> { +impl<'ccx, 'tcx: 'ccx> TyGenContext<'ccx, 'tcx, '_> { /// Adds an enum definition to the current decl and impl headers. /// /// The enum is defined in C++ using a `class` with a single private field that is the diff --git a/tool/src/dart/mod.rs b/tool/src/dart/mod.rs index dc1baeff6..c4a524422 100644 --- a/tool/src/dart/mod.rs +++ b/tool/src/dart/mod.rs @@ -146,7 +146,7 @@ struct TyGenContext<'a, 'cx> { helper_classes: &'a mut BTreeMap, } -impl<'a, 'cx> TyGenContext<'a, 'cx> { +impl<'cx> TyGenContext<'_, 'cx> { fn gen(&mut self, id: TypeId) -> (String, String) { let ty = self.tcx.resolve_type(id); diff --git a/tool/src/demo_gen/terminus.rs b/tool/src/demo_gen/terminus.rs index 1169b2f07..e9e621cda 100644 --- a/tool/src/demo_gen/terminus.rs +++ b/tool/src/demo_gen/terminus.rs @@ -144,7 +144,7 @@ pub(super) struct TerminusInfo { pub imports: BTreeSet, } -impl<'ctx, 'tcx> RenderTerminusContext<'ctx, 'tcx> { +impl RenderTerminusContext<'_, '_> { /// See [`TerminusInfo`] for more information on termini. /// /// Right now, we only check for the existence of `&mut DiplomatWrite` in the function parameters to determine a valid render termini. diff --git a/tool/src/js/converter.rs b/tool/src/js/converter.rs index 7a3b74b2d..616d59262 100644 --- a/tool/src/js/converter.rs +++ b/tool/src/js/converter.rs @@ -58,7 +58,7 @@ pub(super) enum JsToCConversionContext { WriteToBuffer(&'static str, usize), } -impl<'jsctx, 'tcx> TyGenContext<'jsctx, 'tcx> { +impl<'tcx> TyGenContext<'_, 'tcx> { // #region C to JS /// Given a type from Rust, convert it into something Typescript will understand. /// We use this to double-check our Javascript work as well. diff --git a/tool/src/js/gen.rs b/tool/src/js/gen.rs index ae3bb1001..12413817d 100644 --- a/tool/src/js/gen.rs +++ b/tool/src/js/gen.rs @@ -33,7 +33,7 @@ pub(super) struct TyGenContext<'ctx, 'tcx> { pub imports: RefCell>, } -impl<'ctx, 'tcx> TyGenContext<'ctx, 'tcx> { +impl<'tcx> TyGenContext<'_, 'tcx> { /// Generates the code at the top of every `.d.ts` and `.mjs` file. /// /// This could easily be an [inherited template](https://djc.github.io/askama/template_syntax.html#template-inheritance), if you want to be a little more strict about how templates are used. 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 771c93ecd..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 } @@ -261,7 +263,7 @@ struct TyGenContext<'a, 'cx> { callback_params: &'a mut Vec, } -impl<'a, 'cx> TyGenContext<'a, 'cx> { +impl<'cx> TyGenContext<'_, 'cx> { fn gen_infallible_return_type_name(&self, success_type: &SuccessType) -> Cow<'cx, str> { match success_type { SuccessType::Unit => self.formatter.fmt_void().into(), @@ -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/src/lib.rs b/tool/src/lib.rs index dbf7b714d..5dae5355e 100644 --- a/tool/src/lib.rs +++ b/tool/src/lib.rs @@ -211,7 +211,7 @@ struct ErrorContext<'tcx> { method: Option>, } -impl<'tcx> fmt::Display for ErrorContext<'tcx> { +impl fmt::Display for ErrorContext<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let ty = &self.ty; if let Some(ref method) = self.method { @@ -226,7 +226,7 @@ impl<'tcx> fmt::Display for ErrorContext<'tcx> { #[must_use] pub struct ErrorContextGuard<'a, 'tcx, E>(&'a ErrorStore<'tcx, E>, ErrorContext<'tcx>); -impl<'a, 'tcx, E> Drop for ErrorContextGuard<'a, 'tcx, E> { +impl Drop for ErrorContextGuard<'_, '_, E> { fn drop(&mut self) { let _ = mem::replace(&mut *self.0.context.borrow_mut(), mem::take(&mut self.1)); } 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 %}