diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 7d81e340b9..5c256ebac0 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [TS/Python] Fix invalid `this` argument type in structs (#4453) (by @ncave) * [JS/TS] Fix `N` format specifier (`ToString("N0")`, `String.Format("{0:N0}", ...)`) producing a trailing dot when precision is 0 (fix #2582) (by @MangelMaxime) * [JS/TS] Fix `C0` and `P0` format specifiers producing trailing dot (e.g., `"¤1,000."` → `"¤1,000"`) (by @MangelMaxime) * [All] Fix captured side-effect-free values (e.g. empty ResizeArray) being incorrectly inlined into object expression getters in release mode, causing a new instance to be created on each getter call (fixes #3779) (by @MangelMaxime) diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index 5093f10419..00cfda6def 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [TS/Python] Fix invalid `this` argument type in structs (#4453) (by @ncave) * [JS/TS] Fix `N` format specifier (`ToString("N0")`, `String.Format("{0:N0}", ...)`) producing a trailing dot when precision is 0 (fix #2582) (by @MangelMaxime) * [JS/TS] Fix `C0` and `P0` format specifiers producing trailing dot (e.g., `"¤1,000."` → `"¤1,000"`) (by @MangelMaxime) * [All] Fix captured side-effect-free values (e.g. empty ResizeArray) being incorrectly inlined into object expression getters in release mode, causing a new instance to be created on each getter call (fixes #3779) (by @MangelMaxime) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index e2528a7aa3..a0993e10c5 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -628,6 +628,14 @@ module Annotation = | Replacements.Util.Builtin kind -> makeBuiltinTypeAnnotation com ctx typ kind | Fable.DeclaredType(entRef, genArgs) -> com.GetEntity(entRef) |> makeEntityTypeAnnotation com ctx genArgs + let makeArgTypeAnnotation (com: IBabelCompiler) ctx (id: Fable.Ident) = + if com.IsTypeScript then + match id.Type, id with + | Replacements.Util.IsByRefType com typ, id when id.IsThisArgument -> makeTypeAnnotation com ctx typ |> Some + | _ -> makeTypeAnnotation com ctx id.Type |> Some + else + None + let makeTypeAnnotationIfTypeScript (com: IBabelCompiler) ctx typ expr = if com.IsTypeScript then match typ, expr with @@ -3322,7 +3330,7 @@ but thanks to the optimisation done below we get let args' = List.zip args tc.Args |> List.map (fun (id, tcArg) -> - let ta = makeTypeAnnotationIfTypeScript com ctx id.Type None + let ta = makeArgTypeAnnotation com ctx id Parameter.parameter (tcArg, ?typeAnnotation = ta) ) @@ -3348,7 +3356,7 @@ but thanks to the optimisation done below we get | _ -> args |> List.map (fun a -> - let ta = makeTypeAnnotationIfTypeScript com ctx a.Type None + let ta = makeArgTypeAnnotation com ctx a Parameter.parameter (a.Name, ?typeAnnotation = ta) ), body diff --git a/src/Fable.Transforms/Python/Fable2Python.Annotation.fs b/src/Fable.Transforms/Python/Fable2Python.Annotation.fs index 2d1f4f7bec..17a73dacc3 100644 --- a/src/Fable.Transforms/Python/Fable2Python.Annotation.fs +++ b/src/Fable.Transforms/Python/Fable2Python.Annotation.fs @@ -754,6 +754,12 @@ let makeBuiltinTypeAnnotation com ctx typ repeatedGenerics kind = fableModuleAnnotation com ctx "choice" name resolved, stmts | _ -> stdlibModuleTypeHint com ctx "typing" "Any" [] repeatedGenerics +let makeArgTypeAnnotation com ctx repeatedGenerics (id: Fable.Ident) = + match id.Type, id with + | Replacements.Util.IsByRefType com typ, id when id.IsThisArgument -> + typeAnnotation com ctx (Some repeatedGenerics) typ + | _ -> typeAnnotation com ctx (Some repeatedGenerics) id.Type + let transformFunctionWithAnnotations (com: IPythonCompiler) ctx diff --git a/src/Fable.Transforms/Python/Fable2Python.Transforms.fs b/src/Fable.Transforms/Python/Fable2Python.Transforms.fs index 2ec963ca0c..f430fe0e22 100644 --- a/src/Fable.Transforms/Python/Fable2Python.Transforms.fs +++ b/src/Fable.Transforms/Python/Fable2Python.Transforms.fs @@ -3083,12 +3083,10 @@ let transformFunction // Replace args, see NamedTailCallOpportunity constructor let args' = List.zip args tc.Args - |> List.map (fun (_id, { Arg = Identifier tcArg }) -> - let id = com.GetIdentifier(ctx, tcArg) - - let ta, _ = Annotation.typeAnnotation com ctx (Some repeatedGenerics) _id.Type - - Arg.arg (id, annotation = ta) + |> List.map (fun (id, { Arg = Identifier tcArg }) -> + let id2 = com.GetIdentifier(ctx, tcArg) + let ta, _ = Annotation.makeArgTypeAnnotation com ctx repeatedGenerics id + Arg.arg (id2, annotation = ta) ) let varDecls = @@ -3119,8 +3117,7 @@ let transformFunction let args' = args |> List.map (fun id -> - let ta, _ = Annotation.typeAnnotation com ctx (Some repeatedGenerics) id.Type - + let ta, _ = Annotation.makeArgTypeAnnotation com ctx repeatedGenerics id Arg.arg (ident com ctx id, annotation = ta) ) diff --git a/tests/Beam/TypeTests.fs b/tests/Beam/TypeTests.fs index d6a9795755..461f2e2b19 100644 --- a/tests/Beam/TypeTests.fs +++ b/tests/Beam/TypeTests.fs @@ -43,6 +43,12 @@ type ValueType1<'T>(value: 'T) = type ValueType2(i: int, j: int) = member x.Value = i + j +[] +type ValueTypeR = + val mutable X: float + new(x: float) = { X = x } + member this.IsEmpty() = this.X < 0.0 + [] type StructUnion = Value of string @@ -645,6 +651,12 @@ let ``test Other Value Types work`` () = let p = Point2D(2.) p.Y |> equal 2. +[] +let ``test Struct with mutable fields works`` () = + let x = ValueTypeR(-10.0) + x.X |> equal -10.0 + x.IsEmpty() |> equal true + [] let ``test Custom F# exceptions work`` () = try @@ -732,6 +744,7 @@ let ``test Type abbreviation works`` () = // let mutable t2 = ValueType3() // t2.X |> equal 0 + // Test types for generic parameter static member resolution type TestTypeA = static member GetValue() = "A" diff --git a/tests/Dart/src/TypeTests.fs b/tests/Dart/src/TypeTests.fs index 303db1d914..274fd7de8b 100644 --- a/tests/Dart/src/TypeTests.fs +++ b/tests/Dart/src/TypeTests.fs @@ -264,6 +264,12 @@ open Util // val mutable public X : int // end +// [] +// type ValueTypeR = +// val mutable X: float +// new(x: float) = { X = x } +// member this.IsEmpty() = this.X < 0.0 + // [] // type StructUnion = Value of string @@ -1083,6 +1089,11 @@ let tests() = // t1 |> equal t2 // (compare t1 t2) |> equal 0 +// testCase "Struct with mutable fields works" <| fun () -> +// let x = ValueTypeR(-10.0) +// x.X |> equal -10.0 +// x.IsEmpty() |> equal true + // testCase "copying struct records works" <| fun () -> // See #3371 // let simple : SimpleRecord = { A = ""; B = "B" } // let simpleRecord = { simple with A = "A" } diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 65615bdfa2..148b75b1ae 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -261,13 +261,18 @@ type ValueType2(i: int, j: int) = member x.Value = i + j #if !FABLE_COMPILER_TYPESCRIPT -[] type ValueType3 = struct val mutable public X : int end #endif +[] +type ValueTypeR = + val mutable X: float + new(x: float) = { X = x } + member this.IsEmpty() = this.X < 0.0 + [] type StructUnion = Value of string @@ -1303,6 +1308,11 @@ let tests = (compare t1 t2) |> equal 0 #endif + testCase "Struct with mutable fields works" <| fun () -> + let x = ValueTypeR(-10.0) + x.X |> equal -10.0 + x.IsEmpty() |> equal true + testCase "copying struct records works" <| fun () -> // See #3371 let simple : SimpleRecord = { A = ""; B = "B" } let simpleRecord = { simple with A = "A" } diff --git a/tests/Python/TestType.fs b/tests/Python/TestType.fs index 5b753e669d..990d2f9e44 100644 --- a/tests/Python/TestType.fs +++ b/tests/Python/TestType.fs @@ -298,6 +298,12 @@ type ValueType3 = val mutable public X: int end +[] +type ValueTypeR = + val mutable X: float + new(x: float) = { X = x } + member this.IsEmpty() = this.X < 0.0 + [] type StructUnion = Value of string @@ -1246,6 +1252,12 @@ let ``test struct without explicit ctor works`` () = (compare t1 t2) |> equal 0 +[] +let ``test Struct with mutable fields works`` () = + let x = ValueTypeR(-10.0) + x.X |> equal -10.0 + x.IsEmpty() |> equal true + [] let ``test Custom F# exceptions work`` () = try diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs index bd5e805ba2..802952dd3f 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -264,6 +264,12 @@ type ValueType3 = val mutable public X : int end +[] +type ValueTypeR = + val mutable X: float + new(x: float) = { X = x } + member this.IsEmpty() = this.X < 0.0 + [] type StructUnion = Value of string @@ -1120,6 +1126,12 @@ let ``struct without explicit ctor works`` () = t1 |> equal t2 (compare t1 t2) |> equal 0 +[] +let ``Struct with mutable fields works`` () = + let x = ValueTypeR(-10.0) + x.X |> equal -10.0 + x.IsEmpty() |> equal true + [] let ``Unchecked.defaultof works for fields on nested structs`` () = let top = TopLevelStruct()