diff --git a/src/Fable.Build/Test/Rust.fs b/src/Fable.Build/Test/Rust.fs index 9e91d51723..a576b758f7 100644 --- a/src/Fable.Build/Test/Rust.fs +++ b/src/Fable.Build/Test/Rust.fs @@ -60,11 +60,11 @@ let handle (args: string list) = let cargoTestArgs = if noStd then - "cargo test --features no_std" + "cargo test --features no_std -- --test-threads=1" elif threaded then "cargo test --features threaded" else - "cargo test" + "cargo test -- --test-threads=1" let fableArgs = CmdLine.concat diff --git a/src/Fable.Transforms/Python/Fable2Python.fs b/src/Fable.Transforms/Python/Fable2Python.fs index e10d846e8e..c796b7a758 100644 --- a/src/Fable.Transforms/Python/Fable2Python.fs +++ b/src/Fable.Transforms/Python/Fable2Python.fs @@ -579,8 +579,6 @@ module Helpers = | Fable.Any -> true | _ -> false - let index = (Seq.initInfinite id).GetEnumerator() - let removeNamespace (fullName: string) = fullName.Split('.') |> Array.last @@ -588,8 +586,7 @@ module Helpers = |> Helpers.clean let getUniqueIdentifier (name: string) : Identifier = - do index.MoveNext() |> ignore - let idx = index.Current.ToString() + let idx = Naming.getUniqueIndex () let deliminator = if Char.IsLower name[0] then diff --git a/src/Fable.Transforms/Python/Prelude.fs b/src/Fable.Transforms/Python/Prelude.fs index 14fee7615c..99bc25d09f 100644 --- a/src/Fable.Transforms/Python/Prelude.fs +++ b/src/Fable.Transforms/Python/Prelude.fs @@ -162,6 +162,12 @@ module Naming = let reflectionSuffix = "_reflection" + let mutable uniqueIndex = 0 + + let getUniqueIndex () = + let idx = uniqueIndex + uniqueIndex <- uniqueIndex + 1 + idx let preventConflicts conflicts originalName = let rec check originalName n = diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs index a234f882ed..1c33dbf271 100644 --- a/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs +++ b/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs @@ -1058,6 +1058,8 @@ module Types = let mkInferTy () : Ty = TyKind.Infer |> mkTy + let mkNeverTy () : Ty = TyKind.Never |> mkTy + let mkImplSelfTy () : Ty = TyKind.ImplicitSelf |> mkTy let mkTraitTy bounds : Ty = diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index 0164df9b14..77f7f56ec2 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -362,9 +362,9 @@ module TypeInfo = let makeBoxTy com ctx (ty: Rust.Ty) : Rust.Ty = [ ty ] |> makeImportType com ctx "Native" "Box" - // TODO: emit Lazy or SyncLazy depending on threading. - let makeLazyTy com ctx (ty: Rust.Ty) : Rust.Ty = - [ ty ] |> makeImportType com ctx "Native" "Lazy" + // // TODO: emit Lazy or SyncLazy depending on threading. + // let makeLazyTy com ctx (ty: Rust.Ty) : Rust.Ty = + // [ ty ] |> makeImportType com ctx "Native" "Lazy" // TODO: emit MutCell or AtomicCell depending on threading. let makeMutTy com ctx (ty: Rust.Ty) : Rust.Ty = @@ -1011,7 +1011,7 @@ module TypeInfo = let transformGenericParamType com ctx name isMeasure : Rust.Ty = if isInferredGenericParam com ctx name isMeasure then - mkInferTy () // makeAnyTy com ctx + mkInferTy () // mnNeverTy com ctx else primitiveType name @@ -1494,14 +1494,7 @@ module Util = makeLibCall com ctx None "Seq" "ofArray" [ ar ] // casts to generic param - | _, Fable.GenericParam(name, _isMeasure, _constraints) -> - makeCall - [ - name - "from" - ] - None - [ expr ] // e.g. T::from(value) + | _, Fable.GenericParam(name, _isMeasure, _constraints) -> makeCall (name :: "from" :: []) None [ expr ] // e.g. T::from(value) // casts to IDictionary, for now does nothing // TODO: fix it | Replacements.Util.IsEntity (Types.dictionary) _, Replacements.Util.IsEntity (Types.idictionary) _ -> expr @@ -1533,41 +1526,37 @@ module Util = let callee = mkGenericPathExpr pathNames genArgs mkCallExpr callee args - let makeNew com ctx moduleName typeName (value: Rust.Expr) = + let makeNew com ctx moduleName typeName (values: Rust.Expr list) = let importName = getLibraryImportName com ctx moduleName typeName - - makeCall - [ - importName - "new" - ] - None - [ value ] + makeCall (importName :: "new" :: []) None values // let makeFrom com ctx moduleName typeName (value: Rust.Expr) = // let importName = getLibraryImportName com ctx moduleName typeName // makeCall [importName; "from"] None [value] let makeFluentValue com ctx (value: Rust.Expr) = - makeLibCall com ctx None "Native" "fromFluent" [ value ] + [ value ] |> makeLibCall com ctx None "Native" "fromFluent" let makeLrcPtrValue com ctx (value: Rust.Expr) = - value |> makeNew com ctx "Native" "LrcPtr" + [ value ] |> makeNew com ctx "Native" "LrcPtr" // let makeLrcValue com ctx (value: Rust.Expr) = - // value |> makeNew com ctx "Native" "Lrc" + // [ value ] |> makeNew com ctx "Native" "Lrc" - let makeRcValue com ctx (value: Rust.Expr) = value |> makeNew com ctx "Native" "Rc" + let makeRcValue com ctx (value: Rust.Expr) = + [ value ] |> makeNew com ctx "Native" "Rc" - let makeArcValue com ctx (value: Rust.Expr) = value |> makeNew com ctx "Native" "Arc" + let makeArcValue com ctx (value: Rust.Expr) = + [ value ] |> makeNew com ctx "Native" "Arc" - let makeBoxValue com ctx (value: Rust.Expr) = value |> makeNew com ctx "Native" "Box" + let makeBoxValue com ctx (value: Rust.Expr) = + [ value ] |> makeNew com ctx "Native" "Box" let makeMutValue com ctx (value: Rust.Expr) = - value |> makeNew com ctx "Native" "MutCell" + [ value ] |> makeNew com ctx "Native" "MutCell" - let makeLazyValue com ctx (value: Rust.Expr) = - value |> makeNew com ctx "Native" "Lazy" + // let makeLazyValue com ctx (value: Rust.Expr) = + // [ value ] |> makeNew com ctx "Native" "Lazy" let makeFuncValue com ctx (ident: Fable.Ident) = let argTypes = @@ -1585,13 +1574,7 @@ module Util = let funcWrap = getLibraryImportName com ctx "Native" ("Func" + argCount) let expr = transformIdent com ctx None ident - makeCall - [ - funcWrap - "from" - ] - None - [ expr ] + makeCall (funcWrap :: "from" :: []) None [ expr ] let maybeWrapSmartPtr com ctx ent expr = match ent with @@ -2432,7 +2415,8 @@ module Util = let callee = transformCallee com ctx calleeExpr mkCallExpr callee args - let mutableGet expr = mkMethodCallExpr "get" None expr [] + let mutableGet expr = + mkMethodCallExpr "get" None expr [] |> makeClone let mutableGetMut expr = mkMethodCallExpr "get_mut" None expr [] @@ -3978,14 +3962,13 @@ module Util = let transformModuleLetValue (com: IRustCompiler) ctx (memb: Fable.MemberFunctionOrValue) (decl: Fable.MemberDecl) = // expected output: // pub fn value() -> T { - // static value: MutCell> = MutCell::new(None); - // value.get_or_init(|| initValue) + // static value: OnceInit = OnceInit::new(); + // value.get_or_init(|| initValue).clone() // } let name = splitLast decl.Name let typ = decl.Body.Type - let initNone = mkGenericPathExpr [ rawIdent "None" ] None |> makeMutValue com ctx - + let initNone = makeNew com ctx "Native" "OnceInit" [] let value = transformLeaveContext com ctx None decl.Body let value = @@ -4002,8 +3985,7 @@ module Util = else ty - let staticTy = ty |> makeOptionTy |> makeMutTy com ctx - + let staticTy = [ ty ] |> makeImportType com ctx "Native" "OnceInit" let staticStmt = mkStaticItem [] name staticTy (Some initNone) |> mkItemStmt let callee = com.TransformExpr(ctx, makeIdentExpr name) @@ -4013,7 +3995,9 @@ module Util = mkClosureExpr false fnDecl value let valueStmt = - mkMethodCallExpr "get_or_init" None callee [ closureExpr ] |> mkExprStmt + mkMethodCallExpr "get_or_init" None callee [ closureExpr ] + |> makeClone + |> mkExprStmt let attrs = transformAttributes com ctx memb.Attributes @@ -4372,7 +4356,10 @@ module Util = let traitItem = let assocItems = makeInterfaceItems com ctx false ent let generics = makeGenerics com ctx genArgs - mkTraitItem [] entName assocItems [] generics + // let sendBound = mkTypeTraitGenericBound [ rawIdent "Send" ] None + // let syncBound = mkTypeTraitGenericBound [ rawIdent "Sync" ] None + let traitBounds = [] // [ sendBound; syncBound ] + mkTraitItem [] entName assocItems traitBounds generics let implItem = let memberItems = makeInterfaceItems com ctx true ent diff --git a/src/Fable.Transforms/Rust/Replacements.fs b/src/Fable.Transforms/Rust/Replacements.fs index 4724b9956c..3be8193296 100644 --- a/src/Fable.Transforms/Rust/Replacements.fs +++ b/src/Fable.Transforms/Rust/Replacements.fs @@ -119,8 +119,11 @@ let makeDecimal com r t (x: decimal) = let makeRef (value: Expr) = Operation(Unary(UnaryAddressOf, value), Tags.empty, value.Type, None) +let makeClone com r t (expr: Expr) = + Helper.InstanceCall(expr, "clone", t, [], ?loc = r) + let getRefCell com r t (expr: Expr) = - Helper.InstanceCall(expr, "get", t, [], ?loc = r) + Helper.InstanceCall(expr, "get", t, [], ?loc = r) |> makeClone com r t let setRefCell com r (expr: Expr) (value: Expr) = Set(expr, ValueSet, value.Type, value, r) diff --git a/src/fable-library-rust/src/Encoding.rs b/src/fable-library-rust/src/Encoding.rs index 9ead76aa06..b2bbcc33b4 100644 --- a/src/fable-library-rust/src/Encoding.rs +++ b/src/fable-library-rust/src/Encoding.rs @@ -1,9 +1,9 @@ pub mod Encoding_ { use crate::NativeArray_::{array_from, Array}; - use crate::Native_::{Lrc, LrcPtr, MutCell, String, Vec}; + use crate::Native_::{Lrc, LrcPtr, OnceInit, String, Vec}; use crate::String_::{fromChars2, fromSlice, fromString, string, substring2_safe}; - pub trait Encoding { + pub trait Encoding: Send + Sync { fn getBytes(&self, s: string) -> Array; fn getBytes2(&self, s: string, index: i32, count: i32) -> Array; fn getBytesFromChars(&self, chars: Array) -> Array; @@ -35,8 +35,10 @@ pub mod Encoding_ { pub struct UTF16LE {} pub fn get_Unicode() -> LrcPtr { - static utf16le: MutCell>> = MutCell::new(None); - utf16le.get_or_init(move || LrcPtr::from(Lrc::from(UTF16LE {}) as Lrc)) + static utf16le: OnceInit> = OnceInit::new(); + utf16le + .get_or_init(move || LrcPtr::from(Lrc::from(UTF16LE {}) as Lrc)) + .clone() } impl UTF16LE { @@ -134,8 +136,9 @@ pub mod Encoding_ { pub struct UTF8 {} pub fn get_UTF8() -> LrcPtr { - static utf8: MutCell>> = MutCell::new(None); + static utf8: OnceInit> = OnceInit::new(); utf8.get_or_init(move || LrcPtr::from(Lrc::from(UTF8 {}) as Lrc)) + .clone() } impl UTF8 { diff --git a/src/fable-library-rust/src/Lazy.rs b/src/fable-library-rust/src/Lazy.rs index 553c2becae..ce43de3177 100644 --- a/src/fable-library-rust/src/Lazy.rs +++ b/src/fable-library-rust/src/Lazy.rs @@ -1,45 +1,86 @@ -/// Lazy values and one-time initialization. +// Lazy values and one-time initialization. -use core::fmt::Debug; -use crate::Native_::MutCell; +#[cfg(feature = "threaded")] +pub type OnceInit = std::sync::OnceLock; +#[cfg(not(feature = "threaded"))] +pub type OnceInit = NonSyncLazy::OnceInit; -pub struct Lazy T> { - cell: MutCell>, - init: MutCell>, -} +#[cfg(not(feature = "threaded"))] +mod NonSyncLazy { + use crate::Native_::MutCell; + use core::fmt::Debug; -impl Debug for Lazy { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() + #[repr(transparent)] + pub struct OnceInit { + value: MutCell>, } -} -impl Lazy { - pub const fn new(init: F) -> Lazy { - Lazy { cell: MutCell::new(None), init: MutCell::new(Some(init)) } + impl OnceInit { + #[inline] + pub const fn new() -> OnceInit { + OnceInit { + value: MutCell::new(None), + } + } + + #[inline] + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.value.get() { + Some(v) => v, + None => { + self.value.set(Some(f())); + self.value.get().as_ref().unwrap() + } + } + } + } + + pub struct Lazy T> { + cell: MutCell>, + init: MutCell>, } -} -impl T> Lazy { - pub fn force(self: &Lazy) -> T { - match self.cell.get() { - Some(val) => val, - None => { - let val = - match self.init.take() { + impl Debug for Lazy { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Lazy") + .field("cell", &self.cell) + .field("init", &"..") + .finish() + } + } + + impl Lazy { + pub const fn new(init: F) -> Lazy { + Lazy { + cell: MutCell::new(None), + init: MutCell::new(Some(init)), + } + } + } + + impl T> Lazy { + pub fn force(self: &Lazy) -> &T { + match self.cell.get() { + Some(val) => val, + None => { + let val = match self.init.take() { Some(f) => f(), None => panic!("`Lazy` instance has already been initialized"), }; - self.cell.set(Some(val.clone())); - val + self.cell.set(Some(val)); + self.cell.get().as_ref().unwrap() + } } } } -} -impl Default for Lazy { - /// Creates a new lazy value using `Default` as the initializing function. - fn default() -> Lazy { - Lazy::new(T::default) + impl Default for Lazy { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> Lazy { + Lazy::new(T::default) + } } } diff --git a/src/fable-library-rust/src/Mutable.rs b/src/fable-library-rust/src/Mutable.rs index bc6e7d92fe..f35c52db73 100644 --- a/src/fable-library-rust/src/Mutable.rs +++ b/src/fable-library-rust/src/Mutable.rs @@ -1,213 +1,194 @@ -extern crate alloc; -use alloc::vec::Vec; - -use core::cell::UnsafeCell; -use core::cmp::Ordering; -use core::convert::{AsMut, AsRef}; -use core::fmt::{Debug, Display, Formatter, Result}; -use core::hash::{Hash, Hasher}; -use core::ops::{Deref, DerefMut, Index}; - -#[repr(transparent)] -pub struct MutCell { - value: UnsafeCell, -} +// In .NET, thread safety is not guaranteed, and it is expected that the users handle +// thread safety themselves via constructs such as System.Threading.Monitor or lock. -impl AsRef for MutCell { - #[inline] - fn as_ref(&self) -> &T { - // SAFETY: This can cause data races if called from a separate thread. - unsafe { &*self.value.get() } - } -} +// TODO: a proper Send + Sync (atomic) implementation. -impl AsMut for MutCell { - #[inline] - fn as_mut(&mut self) -> &mut T { - self.get_mut() - } -} +#[cfg(feature = "threaded")] +pub type MutCell = NonSyncCell::MutCell; //TODO: Use AtomicCell +#[cfg(not(feature = "threaded"))] +pub type MutCell = NonSyncCell::MutCell; -impl Deref for MutCell { - type Target = T; +// #[cfg(not(feature = "threaded"))] +mod NonSyncCell { + extern crate alloc; + use alloc::vec::Vec; - #[inline] - fn deref(&self) -> &Self::Target { - self.as_ref() - } -} + use core::cell::UnsafeCell; + use core::cmp::Ordering; + use core::convert::{AsMut, AsRef}; + use core::fmt::{Debug, Display, Formatter, Result}; + use core::hash::{Hash, Hasher}; + use core::ops::{Deref, DerefMut, Index}; -impl DerefMut for MutCell { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - self.get_mut() + #[repr(transparent)] + pub struct MutCell { + value: UnsafeCell, } -} -impl Debug for MutCell { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.debug_tuple("MutCell").field(&self.get()).finish() - } -} + // UNSAFE: marked as Send + Sync so it can be used in static variables. + unsafe impl Send for MutCell {} + unsafe impl Sync for MutCell {} -impl Display for MutCell { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - self.get().fmt(f) - } -} + impl core::panic::UnwindSafe for MutCell {} + impl core::panic::RefUnwindSafe for MutCell {} -impl Default for MutCell { - #[inline] - fn default() -> MutCell { - MutCell::new(Default::default()) - } -} - -impl Clone for MutCell { - #[inline] - fn clone(&self) -> MutCell { - MutCell::new(self.get()) + impl AsRef for MutCell { + #[inline] + fn as_ref(&self) -> &T { + self.get() + } } -} -impl Hash for MutCell { - fn hash(&self, state: &mut H) { - self.get().hash(state); + impl AsMut for MutCell { + #[inline] + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } } -} -impl Index for MutCell> { - type Output = T; + impl Deref for MutCell { + type Target = T; - #[inline] - fn index(&self, idx: i32) -> &Self::Output { - &self.as_ref()[idx as usize] + #[inline] + fn deref(&self) -> &Self::Target { + self.as_ref() + } } -} -impl PartialEq for MutCell { - #[inline] - fn eq(&self, other: &MutCell) -> bool { - self.get() == other.get() + impl DerefMut for MutCell { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_mut() + } } -} - -impl Eq for MutCell {} -impl PartialOrd for MutCell { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.get().partial_cmp(&other.get()) + impl Debug for MutCell { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_tuple("MutCell").field(&self.get()).finish() + } } - #[inline] - fn lt(&self, other: &Self) -> bool { - self.get() < other.get() + impl Display for MutCell { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + self.get().fmt(f) + } } - #[inline] - fn le(&self, other: &Self) -> bool { - self.get() <= other.get() + impl Default for MutCell { + #[inline] + fn default() -> MutCell { + MutCell::new(Default::default()) + } } - #[inline] - fn gt(&self, other: &Self) -> bool { - self.get() > other.get() + impl Clone for MutCell { + #[inline] + fn clone(&self) -> MutCell { + MutCell::new(self.get().clone()) + } } - #[inline] - fn ge(&self, other: &Self) -> bool { - self.get() >= other.get() + impl Hash for MutCell { + fn hash(&self, state: &mut H) { + self.get().hash(state); + } } -} -impl Ord for MutCell { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.get().cmp(&other.get()) - } -} + impl Index for MutCell> { + type Output = T; -impl From for MutCell { - fn from(t: T) -> MutCell { - MutCell::new(t) + #[inline] + fn index(&self, idx: i32) -> &Self::Output { + &self.as_ref()[idx as usize] + } } -} -impl MutCell { - #[inline] - pub const fn new(value: T) -> MutCell { - MutCell { - value: UnsafeCell::new(value), + impl PartialEq for MutCell { + #[inline] + fn eq(&self, other: &MutCell) -> bool { + self.get() == other.get() } } - #[inline] - pub fn get(&self) -> T - where - T: Clone, - { - // SAFETY: This can cause data races if called from a separate thread. - unsafe { (*self.value.get()).clone() } - } + impl Eq for MutCell {} - #[inline] - pub fn get_mut(&self) -> &mut T { - // SAFETY: This can cause data races if called from a separate thread. - unsafe { &mut *self.value.get() } - } + impl PartialOrd for MutCell { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.get().partial_cmp(&other.get()) + } - #[inline] - pub fn replace(&self, val: T) -> T { - // SAFETY: This can cause data races if called from a separate thread. - core::mem::replace(unsafe { &mut *self.value.get() }, val) - } + #[inline] + fn lt(&self, other: &Self) -> bool { + self.get() < other.get() + } + + #[inline] + fn le(&self, other: &Self) -> bool { + self.get() <= other.get() + } + + #[inline] + fn gt(&self, other: &Self) -> bool { + self.get() > other.get() + } - #[inline] - pub fn set(&self, val: T) { - let old = self.replace(val); - drop(old); + #[inline] + fn ge(&self, other: &Self) -> bool { + self.get() >= other.get() + } } -} -impl MutCell { - #[inline] - pub fn take(&self) -> T { - self.replace(Default::default()) + impl Ord for MutCell { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.get().cmp(&other.get()) + } } -} -impl MutCell> { - #[inline] - pub fn get_or_init(&self, f: F) -> T - where - F: FnOnce() -> T, - { - match self.get() { - Some(v) => v, - None => { - // TODO: use atomic swap - self.set(Some(f())); - self.get().unwrap() - } + impl From for MutCell { + fn from(t: T) -> MutCell { + MutCell::new(t) } } -} -// In .NET, thread safety is not guaranteed, and it is expected that the users handle -// thread safety themselves via constructs such as System.Threading.Monitor or lock. + impl MutCell { + #[inline] + pub const fn new(value: T) -> MutCell { + MutCell { + value: UnsafeCell::new(value), + } + } -// In order to allow this pattern, Send and Sync guards must be conditional, which is -// why they are hidden behind the "atomic" feature switch. Use at your own risk! + #[inline] + pub fn get(&self) -> &T { + // UNSAFE: This can cause data races if called from a separate thread. + unsafe { &*self.value.get() } + } -// Currently it is always marked as Send + Sync so it can be used in static variables. -// TODO: a proper Send + Sync (atomic) implementation. + #[inline] + pub fn get_mut(&self) -> &mut T { + // UNSAFE: This can cause data races if called from a separate thread. + unsafe { &mut *self.value.get() } + } -// #[cfg(feature = "atomic")] -unsafe impl Send for MutCell {} + #[inline] + pub fn replace(&self, val: T) -> T { + // UNSAFE: This can cause data races if called from a separate thread. + core::mem::replace(self.get_mut(), val) + } -// #[cfg(feature = "atomic")] -unsafe impl Sync for MutCell {} + #[inline] + pub fn set(&self, val: T) { + let old = self.replace(val); + drop(old); + } + } -impl core::panic::UnwindSafe for MutCell {} -impl core::panic::RefUnwindSafe for MutCell {} + impl MutCell { + #[inline] + pub fn take(&self) -> T { + self.replace(Default::default()) + } + } +} diff --git a/tests/Rust/Cargo.toml b/tests/Rust/Cargo.toml index 5334aa7f5b..d293d88049 100644 --- a/tests/Rust/Cargo.toml +++ b/tests/Rust/Cargo.toml @@ -9,4 +9,4 @@ threaded = ["fable_library_rust/threaded"] # default = ["threaded"] # Uncomment when attempting to debug/use rust analyzer to switch to threaded mode [dependencies] -fable_library_rust = { path = "../../fable-library-rust" } +fable_library_rust = { path = "./fable_modules/fable-library-rust" } diff --git a/tests/Rust/tests/src/InterfaceTests.fs b/tests/Rust/tests/src/InterfaceTests.fs index e91cdba1bc..916e038b94 100644 --- a/tests/Rust/tests/src/InterfaceTests.fs +++ b/tests/Rust/tests/src/InterfaceTests.fs @@ -18,6 +18,21 @@ type Adder3 (m: int) = let addWithAdder (i: IHasAdd) = i.Add 3 4 +// let adderObj = Adder() +// let adderInt = Adder() :> IHasAdd + +// [] +// let ``Module let object value works`` () = +// let a = adderObj :> IHasAdd +// let res = a.Add 2 1 +// res |> equal 3 + +// [] +// let ``Module let interface value works`` () = +// let a = adderInt +// let res = a.Add 2 1 +// res |> equal 3 + [] let ``Class interface impl works`` () = let a = Adder()