Skip to content

Commit de4ede0

Browse files
authored
Add a bindgen test that exercises using error types from a different interface (#6802)
1 parent 217de07 commit de4ede0

1 file changed

Lines changed: 181 additions & 0 deletions

File tree

tests/all/component_model/bindgen/results.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,187 @@ mod variant_error {
710710
}
711711
}
712712

713+
mod multiple_interfaces_error {
714+
use super::*;
715+
use exports::foo;
716+
use inline::inline::imports;
717+
use inline::inline::types;
718+
719+
wasmtime::component::bindgen!({
720+
inline: "
721+
package inline:inline
722+
interface types {
723+
enum e1 { a, b, c }
724+
enum-error: func(a: float64) -> result<float64, e1>
725+
}
726+
interface imports {
727+
use types.{e1}
728+
enum-error: func(a: float64) -> result<float64, e1>
729+
}
730+
world result-playground {
731+
import imports
732+
export foo: interface {
733+
enum e1 { a, b, c }
734+
enum-error: func(a: float64) -> result<float64, e1>
735+
}
736+
}",
737+
trappable_error_type: { "inline:inline/types"::e1: TrappableE1 }
738+
});
739+
740+
#[test]
741+
fn run() -> Result<(), Error> {
742+
let engine = engine();
743+
// NOTE: this component doesn't make use of a types import, and relies instead on
744+
// subtyping.
745+
let component = Component::new(
746+
&engine,
747+
format!(
748+
r#"
749+
(component
750+
(type $err' (enum "a" "b" "c"))
751+
(import (interface "inline:inline/imports") (instance $i
752+
(export $e1 "e1" (type (eq $err')))
753+
(export "enum-error" (func (param "a" float64) (result (result float64 (error $e1)))))
754+
))
755+
(core module $libc
756+
(memory (export "memory") 1)
757+
{REALLOC_AND_FREE}
758+
)
759+
(core instance $libc (instantiate $libc))
760+
(core module $m
761+
(import "" "core_enum_error" (func $f (param f64 i32)))
762+
(import "libc" "memory" (memory 0))
763+
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
764+
(func (export "core_enum_error_export") (param f64) (result i32)
765+
(local $retptr i32)
766+
(local.set $retptr
767+
(call $realloc
768+
(i32.const 0)
769+
(i32.const 0)
770+
(i32.const 4)
771+
(i32.const 16)))
772+
(call $f (local.get 0) (local.get $retptr))
773+
(local.get $retptr)
774+
)
775+
)
776+
(core func $core_enum_error
777+
(canon lower (func $i "enum-error") (memory $libc "memory") (realloc (func $libc "realloc")))
778+
)
779+
(core instance $i (instantiate $m
780+
(with "" (instance (export "core_enum_error" (func $core_enum_error))))
781+
(with "libc" (instance $libc))
782+
))
783+
(func $f_enum_error
784+
(param "a" float64)
785+
(result (result float64 (error $err')))
786+
(canon lift (core func $i "core_enum_error_export") (memory $libc "memory"))
787+
)
788+
789+
(component $nested
790+
(import "f-err" (type $err (eq $err')))
791+
(import "f" (func $f (param "a" float64) (result (result float64 (error $err)))))
792+
(export $err2 "err" (type $err'))
793+
(export "enum-error" (func $f) (func (param "a" float64) (result (result float64 (error $err2)))))
794+
)
795+
796+
(instance $n (instantiate $nested
797+
(with "f-err" (type $err'))
798+
(with "f" (func $f_enum_error))
799+
))
800+
(export "foo" (instance $n))
801+
)
802+
"#
803+
),
804+
)?;
805+
806+
// You can create concrete trap types which make it all the way out to the
807+
// host caller, via downcast_ref below.
808+
#[derive(Debug)]
809+
struct MyTrap;
810+
811+
impl std::fmt::Display for MyTrap {
812+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
813+
write!(f, "{:?}", self)
814+
}
815+
}
816+
impl std::error::Error for MyTrap {}
817+
818+
// It is possible to define From impls that target these generated trappable
819+
// types. This allows you to integrate libraries with other error types, or
820+
// use your own more descriptive error types, and use ? to convert them at
821+
// their throw site.
822+
impl From<MyTrap> for types::TrappableE1 {
823+
fn from(t: MyTrap) -> types::TrappableE1 {
824+
types::TrappableE1::trap(anyhow!(t))
825+
}
826+
}
827+
828+
#[derive(Default)]
829+
struct MyImports {}
830+
831+
impl types::Host for MyImports {
832+
fn enum_error(&mut self, a: f64) -> Result<f64, types::TrappableE1> {
833+
if a == 0.0 {
834+
Ok(a)
835+
} else if a == 1.0 {
836+
Err(imports::E1::A)?
837+
} else {
838+
Err(MyTrap)?
839+
}
840+
}
841+
}
842+
843+
impl imports::Host for MyImports {
844+
fn enum_error(&mut self, a: f64) -> Result<f64, types::TrappableE1> {
845+
if a == 0.0 {
846+
Ok(a)
847+
} else if a == 1.0 {
848+
Err(imports::E1::A)?
849+
} else {
850+
Err(MyTrap)?
851+
}
852+
}
853+
}
854+
855+
let mut linker = Linker::new(&engine);
856+
imports::add_to_linker(&mut linker, |f: &mut MyImports| f)?;
857+
858+
let mut store = Store::new(&engine, MyImports::default());
859+
let (results, _) = ResultPlayground::instantiate(&mut store, &component, &linker)?;
860+
861+
assert_eq!(
862+
results
863+
.foo()
864+
.call_enum_error(&mut store, 0.0)
865+
.expect("no trap")
866+
.expect("no error returned"),
867+
0.0
868+
);
869+
870+
let e = results
871+
.foo()
872+
.call_enum_error(&mut store, 1.0)
873+
.expect("no trap")
874+
.err()
875+
.expect("error returned");
876+
assert_eq!(e, foo::E1::A);
877+
878+
let e = results
879+
.foo()
880+
.call_enum_error(&mut store, 2.0)
881+
.err()
882+
.expect("trap");
883+
assert_eq!(
884+
format!("{}", e.source().expect("trap message is stored in source")),
885+
"MyTrap"
886+
);
887+
e.downcast_ref::<MyTrap>()
888+
.expect("downcast trap to concrete MyTrap type");
889+
890+
Ok(())
891+
}
892+
}
893+
713894
mod with_remapping {
714895
use super::*;
715896

0 commit comments

Comments
 (0)