Skip to content

Commit

Permalink
[Rust] Updated derived traits mapping (fable-compiler#3944)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncave authored Oct 27, 2024
1 parent d1e647e commit 974f63e
Show file tree
Hide file tree
Showing 15 changed files with 264 additions and 171 deletions.
1 change: 1 addition & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* [Rust] Updated derived traits mapping (by @ncave)
* [Rust] Fixed try finally handler order of execution (by @ncave)
* [JS/TS/Python/Rust] Fixed String.StartsWith/EndsWith (#3934) (by @ncave)
* [All/Rust] Removed Regex.Replace from hot paths (by @ncave)
Expand Down
189 changes: 87 additions & 102 deletions src/Fable.Transforms/Rust/Fable2Rust.fs
Original file line number Diff line number Diff line change
Expand Up @@ -402,75 +402,58 @@ module TypeInfo =
ent.IsValueType
| _ -> false

let isEntityOfType (com: IRustCompiler) isTypeOf entNames (ent: Fable.Entity) =
let hasTypeOfType (com: IRustCompiler) isTypeOf isEntityOf entNames typ =
match typ with
| Fable.Option(genArg, _) -> isTypeOf com entNames genArg
| Fable.Array(genArg, _) -> isTypeOf com entNames genArg
| Fable.List genArg -> isTypeOf com entNames genArg
| Fable.Tuple(genArgs, _) -> List.exists (isTypeOf com entNames) genArgs
| Fable.AnonymousRecordType(_, genArgs, _) -> List.exists (isTypeOf com entNames) genArgs
| Replacements.Util.Builtin(Replacements.Util.FSharpSet genArg) -> isTypeOf com entNames genArg
| Replacements.Util.Builtin(Replacements.Util.FSharpMap(k, v)) ->
isTypeOf com entNames k || isTypeOf com entNames v
| Fable.DeclaredType(entRef, _genArgs) ->
let ent = com.GetEntity(entRef)
isEntityOf com entNames ent //|| hasFieldOfType com isTypeOf entNames ent
| _ -> false

let hasFieldOfType (com: IRustCompiler) isTypeOf entNames (ent: Fable.Entity) =
if Set.contains ent.FullName entNames then
true // already checked, avoids circular checks
false // already checked, avoids circular checks
else
let entNames = Set.add ent.FullName entNames

if ent.IsFSharpUnion then
ent.UnionCases
|> Seq.forall (fun uci ->
|> Seq.exists (fun uci ->
uci.UnionCaseFields
|> List.forall (fun field -> isTypeOf com entNames field.FieldType)
|> List.exists (fun fi -> isTypeOf com entNames fi.FieldType)
)
else
ent.FSharpFields |> Seq.forall (fun fi -> isTypeOf com entNames fi.FieldType)
ent.FSharpFields |> Seq.exists (fun fi -> isTypeOf com entNames fi.FieldType)

let isTypeOfType (com: IRustCompiler) isTypeOf isEntityOf entNames typ =
let isNonPrintableType (com: IRustCompiler) entNames typ =
match typ with
| Fable.Option(genArg, _) -> isTypeOf com entNames genArg
| Fable.Array(genArg, _) -> isTypeOf com entNames genArg
| Fable.List genArg -> isTypeOf com entNames genArg
| Fable.Tuple(genArgs, _) -> List.forall (isTypeOf com entNames) genArgs
| Fable.AnonymousRecordType(_, genArgs, _isStruct) -> List.forall (isTypeOf com entNames) genArgs
| Replacements.Util.Builtin(Replacements.Util.FSharpSet genArg) -> isTypeOf com entNames genArg
| Replacements.Util.Builtin(Replacements.Util.FSharpMap(k, v)) ->
isTypeOf com entNames k && isTypeOf com entNames v
| Fable.DeclaredType(entRef, _) ->
let ent = com.GetEntity(entRef)
isEntityOf com entNames ent
| _ -> true
// TODO: more unprintable types?
| _ -> hasTypeOfType com isNonPrintableType isNonPrintableEntity entNames typ

let isPrintableType (com: IRustCompiler) entNames typ =
match typ with
// TODO: Any unprintable types?
| _ -> isTypeOfType com isPrintableType isPrintableEntity entNames typ

let isPrintableEntity com entNames (ent: Fable.Entity) =
not (ent.IsInterface) && (isEntityOfType com isPrintableType entNames ent)
let isNonPrintableEntity com entNames (ent: Fable.Entity) = false
// || (hasFieldOfType com isNonPrintableType entNames ent)

let isDefaultableType (com: IRustCompiler) entNames typ =
let isNonDefaultableType (com: IRustCompiler) entNames typ =
match typ with
// TODO: more undefaultable types?
| Fable.LambdaType _
| Fable.DelegateType _ -> false
| _ -> isTypeOfType com isDefaultableType isDefaultableEntity entNames typ

let isDefaultableEntity com entNames (ent: Fable.Entity) =
ent.IsValueType
&& not (ent.IsInterface)
&& not (ent.IsFSharpUnion) // deriving 'Default' on enums is experimental
&& (isEntityOfType com isDefaultableType entNames ent)

let isHashableType (com: IRustCompiler) entNames typ =
match typ with
// TODO: more unhashable types?
| Fable.Any
| Fable.Unit
| Fable.Measure _
| Fable.MetaType
| Fable.Number((Float32 | Float64), _)
| Fable.LambdaType _
| Fable.DelegateType _ -> false
| _ -> isTypeOfType com isHashableType isHashableEntity entNames typ
| Fable.DelegateType _ -> true
| _ -> hasTypeOfType com isNonDefaultableType isNonDefaultableEntity entNames typ

let isHashableEntity com entNames (ent: Fable.Entity) =
not (ent.IsInterface)
&& (FSharp2Fable.Util.hasStructuralEquality ent)
&& (isEntityOfType com isHashableType entNames ent)
let isNonDefaultableEntity com entNames (ent: Fable.Entity) =
ent.IsInterface
|| not ent.IsValueType
|| ent.IsFSharpUnion // deriving 'Default' on enums is experimental
|| (hasFieldOfType com isNonDefaultableType entNames ent)

let isCopyableType (com: IRustCompiler) entNames typ =
let isNonCopyableType (com: IRustCompiler) entNames typ =
match typ with
// TODO: more uncopyable types?
| Fable.Any
Expand All @@ -481,37 +464,34 @@ module TypeInfo =
| Fable.DelegateType _
| Fable.GenericParam _
| Fable.String
| Fable.Regex -> false
| Fable.Tuple(genArgs, isStruct) -> isStruct && (List.forall (isCopyableType com entNames) genArgs)
| Fable.AnonymousRecordType(_, genArgs, isStruct) ->
isStruct && (List.forall (isCopyableType com entNames) genArgs)
| _ -> isTypeOfType com isCopyableType isCopyableEntity entNames typ

let isCopyableEntity com entNames (ent: Fable.Entity) =
ent.IsValueType
&& not (ent.IsInterface)
&& not (hasMutableFields com ent)
&& (isEntityOfType com isCopyableType entNames ent)

let isEquatableType (com: IRustCompiler) entNames typ =
| Fable.Regex -> true
| Fable.Tuple(genArgs, isStruct) when not isStruct -> true
| Fable.AnonymousRecordType(_, genArgs, isStruct) when not isStruct -> true
| _ -> hasTypeOfType com isNonCopyableType isNonCopyableEntity entNames typ

let isNonCopyableEntity com entNames (ent: Fable.Entity) =
not ent.IsValueType
|| (hasMutableFields com ent)
|| (hasFieldOfType com isNonCopyableType entNames ent)

let isNonEquatableType (com: IRustCompiler) entNames typ =
match typ with
// TODO: more unequatable types?
| Fable.Any
| Fable.Unit
| Fable.Measure _
| Fable.MetaType
| Fable.LambdaType _
| Fable.DelegateType _ -> false
| Fable.DelegateType _ -> true
// | Fable.GenericParam(_, _, constraints) ->
// constraints |> List.contains Fable.Constraint.HasEquality
| _ -> isTypeOfType com isEquatableType isEquatableEntity entNames typ
// not (constraints |> List.contains Fable.Constraint.HasEquality)
| _ -> hasTypeOfType com isNonEquatableType isNonEquatableEntity entNames typ

let isEquatableEntity com entNames (ent: Fable.Entity) =
not (ent.IsInterface)
&& (FSharp2Fable.Util.hasStructuralEquality ent)
&& (isEntityOfType com isEquatableType entNames ent)
let isNonEquatableEntity com entNames (ent: Fable.Entity) =
ent.IsInterface || not (FSharp2Fable.Util.hasStructuralEquality ent)
// || (hasFieldOfType com isNonEquatableType entNames ent)

let isComparableType (com: IRustCompiler) entNames typ =
let isNonComparableType (com: IRustCompiler) entNames typ =
match typ with
// TODO: more uncomparable types?
| Fable.Any
Expand All @@ -520,15 +500,32 @@ module TypeInfo =
| Fable.MetaType
| Fable.LambdaType _
| Fable.DelegateType _
| Fable.Regex -> false
| Fable.Regex -> true
// | Fable.GenericParam(_, _, constraints) ->
// constraints |> List.contains Fable.Constraint.HasComparison
| _ -> isTypeOfType com isComparableType isComparableEntity entNames typ
// not (constraints |> List.contains Fable.Constraint.HasComparison)
| _ -> hasTypeOfType com isNonComparableType isNonComparableEntity entNames typ

let isComparableEntity com entNames (ent: Fable.Entity) =
not (ent.IsInterface)
&& (FSharp2Fable.Util.hasStructuralComparison ent)
&& (isEntityOfType com isComparableType entNames ent)
let isNonComparableEntity com entNames (ent: Fable.Entity) =
ent.IsInterface
|| not (FSharp2Fable.Util.hasStructuralComparison ent)
|| (hasFieldOfType com isNonComparableType entNames ent)

let isNonHashableType com entNames typ =
match typ with
// TODO: more unhashable types?
| Fable.Any
| Fable.Unit
| Fable.Measure _
| Fable.MetaType
| Fable.Number((Float32 | Float64), _)
| Fable.LambdaType _
| Fable.DelegateType _ -> true
| _ -> hasTypeOfType com isNonHashableType isNonHashableEntity entNames typ

let isNonHashableEntity com entNames (ent: Fable.Entity) =
ent.IsInterface
|| not (FSharp2Fable.Util.hasStructuralEquality ent)
|| (hasFieldOfType com isNonHashableType entNames ent)

let isWrappedType com typ =
match typ with
Expand Down Expand Up @@ -1301,11 +1298,6 @@ module Util =
else
ty

let memberFromName (memberName: string) : Rust.Expr * bool =
match memberName with
| "ToString" -> (mkGenericPathExpr [ "ToString" ] None), false
| n -> (mkGenericPathExpr [ n ] None), false

let getField r (expr: Rust.Expr) (fieldName: string) =
mkFieldExpr expr (fieldName |> sanitizeMember) // ?loc=r)

Expand Down Expand Up @@ -3890,10 +3882,7 @@ module Util =
| Fable.Constraint.HasDefaultConstructor -> []
| Fable.Constraint.HasComparison -> [ makeRawBound "PartialOrd" ]
| Fable.Constraint.HasEquality ->
[
makeGenBound ("core" :: "hash" :: "Hash" :: []) []
makeRawBound "PartialEq" // "Eq"
]
[ makeGenBound ("core" :: "hash" :: "Hash" :: []) []; makeRawBound "PartialEq" ]
| Fable.Constraint.IsUnmanaged -> []
| Fable.Constraint.IsEnum -> []

Expand Down Expand Up @@ -4259,32 +4248,28 @@ module Util =
fnItem

let makeDerivedFrom com (ent: Fable.Entity) =
let isCopyable = ent |> isCopyableEntity com Set.empty
let isPrintable = ent |> isPrintableEntity com Set.empty
let isDefaultable = ent |> isDefaultableEntity com Set.empty
let isComparable = ent |> isComparableEntity com Set.empty
let isEquatable = ent |> isEquatableEntity com Set.empty
let isHashable = ent |> isHashableEntity com Set.empty
// let isCopyable = not (ent |> isNonCopyableEntity com Set.empty)
let isPrintable = not (ent |> isNonPrintableEntity com Set.empty)
let isDefaultable = not (ent |> isNonDefaultableEntity com Set.empty)
let isHashable = not (ent |> isNonHashableEntity com Set.empty)
let isEquatable = not (ent |> isNonEquatableEntity com Set.empty)
let isComparable = not (ent |> isNonComparableEntity com Set.empty)

let derivedFrom =
[
rawIdent "Clone"
if isCopyable then
rawIdent "Copy"
// if isCopyable then
// rawIdent "Copy"
if isPrintable then
rawIdent "Debug"
if isDefaultable then
rawIdent "Default"
if isHashable then
rawIdent "Hash"
if isEquatable then
rawIdent "PartialEq"
if isComparable then
rawIdent "PartialOrd"
if isHashable then
rawIdent "Hash"
if isEquatable && isHashable then
rawIdent "Eq"
// if isComparable && isHashable then
// rawIdent "Ord"
]

derivedFrom
Expand Down
9 changes: 6 additions & 3 deletions src/Fable.Transforms/Rust/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@ let isCompatibleWithNativeComparison =
| GenericParam _
| Array _
| List _
| Builtin(BclGuid | BclTimeSpan) -> true
| Builtin(BclGuid) -> true
| Builtin(BclTimeSpan) -> true
| _ -> false

// Overview of hash rules:
Expand Down Expand Up @@ -474,7 +475,8 @@ let equals (com: ICompiler) ctx r (left: Expr) (right: Expr) =
| Boolean
| Char
| String
| Number _ -> makeEqOp r left right BinaryEqual
| Number _
| Builtin(FSharpChoice _ | FSharpResult _) -> makeEqOp r left right BinaryEqual
| Builtin kind -> Helper.LibCall(com, coreModFor kind, "equals", t, [ left; right ], ?loc = r)
| Array _ -> Helper.LibCall(com, "Array", "equals", t, [ left; right ], ?loc = r)
| List _ -> Helper.LibCall(com, "List", "equals", t, [ left; right ], ?loc = r)
Expand All @@ -499,7 +501,8 @@ let compare (com: ICompiler) ctx r (left: Expr) (right: Expr) =
| Boolean
| Char
| String
| Number _ -> Helper.LibCall(com, "Native", "compare", t, [ left; right ], ?loc = r)
| Number _
| Builtin(FSharpChoice _ | FSharpResult _) -> Helper.LibCall(com, "Native", "compare", t, [ left; right ], ?loc = r)
| Builtin kind -> Helper.LibCall(com, coreModFor kind, "compareTo", t, [ left; right ], ?loc = r)
| Array _ -> Helper.LibCall(com, "Array", "compareTo", t, [ left; right ], ?loc = r)
| List _ -> Helper.LibCall(com, "List", "compareTo", t, [ left; right ], ?loc = r)
Expand Down
20 changes: 10 additions & 10 deletions src/fable-library-rust/src/Async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ pub mod Monitor_ {
use std::thread;
use std::time::Duration;

use crate::Native_::{Func0, Lrc};
use crate::Native_::{Func0, LrcPtr};

static mut LOCKS: Option<RwLock<HashSet<usize>>> = None;
fn try_init_and_get_locks() -> &'static RwLock<HashSet<usize>> {
Expand All @@ -175,7 +175,7 @@ pub mod Monitor_ {
}
}

pub fn enter<T>(o: Lrc<T>) {
pub fn enter<T>(o: LrcPtr<T>) {
let p = Arc::<T>::as_ptr(&o) as usize;
loop {
let otherHasLock = try_init_and_get_locks().read().unwrap().get(&p).is_some();
Expand All @@ -188,7 +188,7 @@ pub mod Monitor_ {
}
}

pub fn exit<T>(o: Lrc<T>) {
pub fn exit<T>(o: LrcPtr<T>) {
let p = Arc::<T>::as_ptr(&o) as usize;
let hasRemoved = try_init_and_get_locks().write().unwrap().remove(&p);
if (!hasRemoved) {
Expand All @@ -197,7 +197,7 @@ pub mod Monitor_ {
}

// Not technically part of monitor, but it needs to be behind a feature switch, so cannot just dump this in Native
pub fn lock<T: Clone + Send + Sync, U: 'static>(toLock: Arc<T>, f: Func0<U>) -> U {
pub fn lock<T: Clone + Send + Sync, U: 'static>(toLock: LrcPtr<T>, f: Func0<U>) -> U {
enter(toLock.clone());
let returnVal = f();
// panics will bypass this - need some finally mechanism
Expand Down Expand Up @@ -401,7 +401,7 @@ pub mod Task_ {

#[cfg(feature = "threaded")]
pub mod TaskBuilder_ {
use super::super::Native_::Lrc;
use super::super::Native_::LrcPtr;
use super::Task_::Task;
use std::sync::Arc;

Expand All @@ -414,8 +414,8 @@ pub mod TaskBuilder_ {
}
}

pub fn new() -> Lrc<TaskBuilder> {
Lrc::from(TaskBuilder {})
pub fn new() -> LrcPtr<TaskBuilder> {
LrcPtr::new(TaskBuilder {})
}
}

Expand All @@ -424,7 +424,7 @@ pub mod Thread_ {
use std::thread;
use std::time::Duration;

use crate::Native_::{Func0, Lrc, MutCell};
use crate::Native_::{Func0, LrcPtr, MutCell};

enum ThreadInt {
New(Func0<()>),
Expand All @@ -439,8 +439,8 @@ pub mod Thread_ {
}
pub struct Thread(MutCell<ThreadInt>);

pub fn new(f: Func0<()>) -> Lrc<Thread> {
Lrc::from(Thread(MutCell::from(ThreadInt::New(f))))
pub fn new(f: Func0<()>) -> LrcPtr<Thread> {
LrcPtr::new(Thread(MutCell::from(ThreadInt::New(f))))
}

impl Thread {
Expand Down
2 changes: 1 addition & 1 deletion src/fable-library-rust/src/Char.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub mod Char_ {
use crate::NativeArray_::{array_from, Array};
use crate::Native_::{compare, Lrc, MutCell, ToString, Vec};
use crate::Native_::{compare, MutCell, ToString};
use crate::String_::{getCharAt, length, string, toString};

// https://docs.microsoft.com/en-us/dotnet/api/system.globalization.unicodecategory?view=net-6.0
Expand Down
Loading

0 comments on commit 974f63e

Please sign in to comment.