From fe9c67ef6cbf535ceb86ada4f8aa9be6f4276b22 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Thu, 5 Nov 2026 15:04:15 +0100 Subject: [PATCH 1/4] fix(all): pass defaults for DefaultValue fields to ctors --- src/Fable.Transforms/FSharp2Fable.fs | 31 ++++++++++++++++++++++++ tests/Beam/TypeTests.fs | 36 ++++++++++++++++++++++++++++ tests/Js/Main/TypeTests.fs | 32 +++++++++++++++++++++++++ tests/Python/TestType.fs | 36 ++++++++++++++++++++++++++++ tests/Rust/tests/src/TypeTests.fs | 36 ++++++++++++++++++++++++++++ 5 files changed, 171 insertions(+) diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 724142ae23..db40fa665a 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -741,6 +741,35 @@ let private transformUnionCaseTest return Fable.Test(unionExpr, Fable.UnionCaseTest(tag), r) } +let private withDefaultValueFields (com: IFableCompiler) (ctx: Context) (fsType: FSharpType) (args: Fable.Expr list) = + let ent = fsType.TypeDefinition + + let isDefaultValueField (field: FSharpField) = + not field.IsStatic && hasAttrib Atts.defaultValue field.FieldAttributes + + let fields = + ent.FSharpFields |> Seq.filter (fun field -> not field.IsStatic) |> List.ofSeq + + if fields |> List.exists isDefaultValueField then + let genArgs = + Seq.zip ent.GenericParameters (getGenericArguments fsType) + |> Seq.map (fun (param, typ) -> genParamName param, makeType ctx.GenericArgs typ) + |> Map.ofSeq + + let rec fill (fields: FSharpField list) args = + match fields, args with + | field :: fields, _ when isDefaultValueField field -> + let value = + makeType genArgs field.FieldType |> Replacements.Api.defaultof com ctx None + + value :: fill fields args + | _ :: fields, arg :: args -> arg :: fill fields args + | _, args -> args + + fill fields args + else + args + let rec private transformDecisionTargets (com: IFableCompiler) (ctx: Context) @@ -1594,6 +1623,7 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) appliedGenArgs fs let r = makeRangeFrom fsExpr let! baseValue = transformExpr com ctx [] first let! ownArgs = transformExprList com ctx ownArgs + let ownArgs = withDefaultValueFields com ctx fsType ownArgs let baseIdent = getIdentUniqueName com ctx "base" |> makeTypedIdent baseValue.Type @@ -1636,6 +1666,7 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) appliedGenArgs fs | FSharpExprPatterns.NewRecord(fsType, argExprs) -> let r = makeRangeFrom fsExpr let! argExprs = transformExprList com ctx argExprs + let argExprs = withDefaultValueFields com ctx fsType argExprs let genArgs = makeTypeGenArgs ctx.GenericArgs (getGenericArguments fsType) let entRef = FsEnt.Ref fsType.TypeDefinition diff --git a/tests/Beam/TypeTests.fs b/tests/Beam/TypeTests.fs index 51a3d08ffb..455f69b0e3 100644 --- a/tests/Beam/TypeTests.fs +++ b/tests/Beam/TypeTests.fs @@ -60,6 +60,23 @@ type Point2D = new(xy: float) = { X = xy; Y = xy } end +type ClassWithDefaultValueInstanceFields = + val X: int + [] + val mutable D: int + [] + val mutable S: string + val Y: string + new(x, y) = { X = x; Y = y } + +[] +type StructWithDefaultValueInstanceField = + val X: int + [] + val mutable D: int + val Y: string + new(x, y) = { X = x; Y = y } + exception MyEx of int * string type MyEx2(f: float) = @@ -652,6 +669,25 @@ let ``test Other Value Types work`` () = let p = Point2D(2.) p.Y |> equal 2. +[] +let ``test Instance DefaultValue fields are skipped by explicit constructors`` () = + let c = ClassWithDefaultValueInstanceFields(1, "y") + c.X |> equal 1 + c.D |> equal 0 + c.S |> equal null + c.Y |> equal "y" + c.D <- 5 + c.S <- "s" + c.D |> equal 5 + c.S |> equal "s" + + let mutable s = StructWithDefaultValueInstanceField(2, "z") + s.X |> equal 2 + s.D |> equal 0 + s.Y |> equal "z" + s.D <- 6 + s.D |> equal 6 + [] let ``test Struct with mutable fields works`` () = let x = ValueTypeR(-10.0) diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 76604f5c73..4d0df73179 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -439,6 +439,20 @@ type ClassWithDefaultValueStaticFields() = static member Decimal = ClassWithDefaultValueStaticFields.decimal static member StructTuple2 = ClassWithDefaultValueStaticFields.structTuple2 +type ClassWithDefaultValueInstanceFields = + val X: int + [] val mutable D: int + [] val mutable S: string + val Y: string + new (x, y) = { X = x; Y = y } + +[] +type StructWithDefaultValueInstanceField = + val X: int + [] val mutable D: int + val Y: string + new (x, y) = { X = x; Y = y } + [] type NestedStruct = @@ -1848,6 +1862,24 @@ let tests = ClassWithDefaultValueStaticFields.IncrCount() |> equal 1 + testCase "Instance [] fields are skipped by explicit constructors" <| fun () -> + let c = ClassWithDefaultValueInstanceFields(1, "y") + c.X |> equal 1 + c.D |> equal 0 + c.S |> equal null + c.Y |> equal "y" + c.D <- 5 + c.S <- "s" + c.D |> equal 5 + c.S |> equal "s" + + let mutable s = StructWithDefaultValueInstanceField(2, "z") + s.X |> equal 2 + s.D |> equal 0 + s.Y |> equal "z" + s.D <- 6 + s.D |> equal 6 + testCase "Unchecked.defaultof works for fields on structs" <| fun () -> let top = TopLevelStruct() top.A |> equal 0 diff --git a/tests/Python/TestType.fs b/tests/Python/TestType.fs index cad1b1c9e3..7152e55da4 100644 --- a/tests/Python/TestType.fs +++ b/tests/Python/TestType.fs @@ -369,6 +369,23 @@ type TypeWithDefaultValueTest() = [] val mutable ObjValue: System.Collections.Generic.Dictionary +type ClassWithDefaultValueInstanceFields = + val X: int + [] + val mutable D: int + [] + val mutable S: string + val Y: string + new(x, y) = { X = x; Y = y } + +[] +type StructWithDefaultValueInstanceField = + val X: int + [] + val mutable D: int + val Y: string + new(x, y) = { X = x; Y = y } + [] type NestedStruct = val A: float @@ -1840,6 +1857,25 @@ let ``test Different type parameter combinations work correctly`` () = let result3 = getTwoValues() result3 |> equal ("B", "C") +[] +let ``test Instance DefaultValue fields are skipped by explicit constructors`` () = + let c = ClassWithDefaultValueInstanceFields(1, "y") + c.X |> equal 1 + c.D |> equal 0 + c.S |> equal null + c.Y |> equal "y" + c.D <- 5 + c.S <- "s" + c.D |> equal 5 + c.S |> equal "s" + + let mutable s = StructWithDefaultValueInstanceField(2, "z") + s.X |> equal 2 + s.D |> equal 0 + s.Y |> equal "z" + s.D <- 6 + s.D |> equal 6 + [] let ``test Unchecked.defaultof works for fields on structs`` () = let top = TopLevelStruct() diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs index f7855de36e..2777e0f3c4 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -288,6 +288,23 @@ type Point2D = new(xy: float) = { X = xy; Y = xy } end +type ClassWithDefaultValueInstanceFields = + val X: int + [] + val mutable D: int + [] + val mutable S: string + val Y: string + new(x, y) = { X = x; Y = y } + +[] +type StructWithDefaultValueInstanceField = + val X: int + [] + val mutable D: int + val Y: string + new(x, y) = { X = x; Y = y } + exception MyEx of int * string // type MyEx2(f: float) = @@ -1352,6 +1369,25 @@ let ``struct without explicit ctor works`` () = t1 |> equal t2 (compare t1 t2) |> equal 0 +[] +let ``Instance DefaultValue fields are skipped by explicit constructors`` () = + let c = ClassWithDefaultValueInstanceFields(1, "y") + c.X |> equal 1 + c.D |> equal 0 + c.S |> equal null + c.Y |> equal "y" + c.D <- 5 + c.S <- "s" + c.D |> equal 5 + c.S |> equal "s" + + let mutable s = StructWithDefaultValueInstanceField(2, "z") + s.X |> equal 2 + s.D |> equal 0 + s.Y |> equal "z" + s.D <- 6 + s.D |> equal 6 + [] let ``Struct with mutable fields works`` () = let x = ValueTypeR(-10.0) From 2eb329225b0aa4fed7cd268d2e15f78ca353a1b7 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Thu, 5 Nov 2026 15:50:40 +0100 Subject: [PATCH 2/4] fix(all): leave DefaultValue fields out of quoted records --- src/Fable.Transforms/FSharp2Fable.fs | 2 +- src/Fable.Transforms/QuotationEmitter.fs | 6 +++++- tests/Js/Main/QuotationTests.fs | 8 ++++++++ tests/Rust/tests/src/QuotationTests.fs | 6 ++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index db40fa665a..37e6eb62d8 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -750,7 +750,7 @@ let private withDefaultValueFields (com: IFableCompiler) (ctx: Context) (fsType: let fields = ent.FSharpFields |> Seq.filter (fun field -> not field.IsStatic) |> List.ofSeq - if fields |> List.exists isDefaultValueField then + if not ctx.CapturingQuotation && fields |> List.exists isDefaultValueField then let genArgs = Seq.zip ent.GenericParameters (getGenericArguments fsType) |> Seq.map (fun (param, typ) -> genParamName param, makeType ctx.GenericArgs typ) diff --git a/src/Fable.Transforms/QuotationEmitter.fs b/src/Fable.Transforms/QuotationEmitter.fs index 39dbb6add7..dcc3d22d4a 100644 --- a/src/Fable.Transforms/QuotationEmitter.fs +++ b/src/Fable.Transforms/QuotationEmitter.fs @@ -434,7 +434,11 @@ and private emitQuotedValue (com: Compiler) (bound: Set) (kind: ValueKin | NewRecord(values, entRef, _genArgs) -> let fieldNames = match com.TryGetEntity(entRef) with - | Some ent -> ent.FSharpFields |> List.map (fun f -> makeStrConst f.Name) |> makeArray Any + | Some ent -> + ent.FSharpFields + |> List.filter (fun f -> not f.IsStatic && not f.HasDefaultValueAttribute) + |> List.map (fun f -> makeStrConst f.Name) + |> makeArray Any | None -> makeArray Any [] let emittedValues = diff --git a/tests/Js/Main/QuotationTests.fs b/tests/Js/Main/QuotationTests.fs index 9e603a7b20..ce5dd64b39 100644 --- a/tests/Js/Main/QuotationTests.fs +++ b/tests/Js/Main/QuotationTests.fs @@ -16,6 +16,8 @@ type QuotationTestUnion = let inline quotTestDouble x = x * 2 +type QuotationDefaultValueRecord = { A: int; [] mutable B: int; C: string } + type QuotationIndexerTest(values: int[]) = member _.Item with get (i: int) = values.[i] @@ -321,4 +323,10 @@ let tests = | Lambda(_, Var _) -> "Var" | _ -> "?") |> equal "Var" + + testCase "NewRecord leaves out DefaultValue fields" <| fun () -> + (match <@ { A = 1; C = "c" }: QuotationDefaultValueRecord @> with + | NewRecord(_, args) -> args |> Seq.map (function Value(v, _) -> string v | _ -> "?") |> String.concat "," + | _ -> "other") + |> equal "1,c" ] diff --git a/tests/Rust/tests/src/QuotationTests.fs b/tests/Rust/tests/src/QuotationTests.fs index 6a3312a302..9f9172ae8c 100644 --- a/tests/Rust/tests/src/QuotationTests.fs +++ b/tests/Rust/tests/src/QuotationTests.fs @@ -211,6 +211,8 @@ let ``Evaluate list quotation`` () = type private MyRec = { X: int; Y: int } +type private MyDefaultValueRec = { A: int; [] mutable B: int; C: int } + let private newUnionCaseName (e: Expr) = match e with | NewUnionCase(uci, _args) -> uci.Name @@ -247,6 +249,10 @@ let ``NewUnionCase exposes the tag`` () = let ``NewRecord exposes the argument count`` () = newRecordArgCount <@ ({ X = 1; Y = 2 }: MyRec) @> |> equal 2 +[] +let ``NewRecord leaves out DefaultValue fields`` () = + newRecordArgCount <@ ({ A = 1; C = 2 }: MyDefaultValueRec) @> |> equal 2 + // --- TupleGet evaluation (B3) --- // A tuple-deconstructing let lowers to a NewTuple bound to a var, then TupleGet nodes // pulling out each element. Evaluating it exercises both the ExprNewTuple arm (builds a From a279721fdc163eb5a88a733a97b7d4d346a87469 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Thu, 5 Nov 2026 15:50:41 +0100 Subject: [PATCH 3/4] fix(rust/beam): skip static fields in record values --- src/Fable.Transforms/Beam/Fable2Beam.fs | 7 ++++++- src/Fable.Transforms/Rust/Fable2Rust.fs | 1 + tests/Beam/TypeTests.fs | 16 ++++++++++++++++ tests/Js/Main/TypeTests.fs | 16 ++++++++++++++++ tests/Python/TestType.fs | 21 +++++++++++++++++++++ tests/Rust/tests/src/TypeTests.fs | 16 ++++++++++++++++ 6 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/Fable.Transforms/Beam/Fable2Beam.fs b/src/Fable.Transforms/Beam/Fable2Beam.fs index afa3b7c3ef..92b36b83f1 100644 --- a/src/Fable.Transforms/Beam/Fable2Beam.fs +++ b/src/Fable.Transforms/Beam/Fable2Beam.fs @@ -1646,7 +1646,12 @@ and transformValue match com.TryGetEntity(ref) with | Some entity -> let fieldEntries = - List.zip (entity.FSharpFields |> List.map (fun f -> f.Name)) values + let fieldNames = + entity.FSharpFields + |> List.filter (fun f -> not f.IsStatic) + |> List.map (fun f -> f.Name) + + List.zip fieldNames values |> List.map (fun (name, value) -> let fieldKey = if ctx.ClassFieldPrefix then diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index efeb1566ae..887d72d73f 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -4364,6 +4364,7 @@ module Util = let getEntityFieldsAsIdents com (ent: Fable.Entity) : Fable.Ident list = let fieldIdents = ent.FSharpFields + |> List.filter (fun field -> not field.IsStatic) |> List.map (fun field -> let name = field.Name let typ = FableTransforms.uncurryType field.FieldType diff --git a/tests/Beam/TypeTests.fs b/tests/Beam/TypeTests.fs index 455f69b0e3..0160ed16d0 100644 --- a/tests/Beam/TypeTests.fs +++ b/tests/Beam/TypeTests.fs @@ -77,6 +77,15 @@ type StructWithDefaultValueInstanceField = val Y: string new(x, y) = { X = x; Y = y } +type ClassWithDefaultValueStaticAndInstanceFields = + [] + static val mutable private count: int + val X: int + [] + val mutable D: int + val Y: int + new(x, y) = { X = x; Y = y } + exception MyEx of int * string type MyEx2(f: float) = @@ -688,6 +697,13 @@ let ``test Instance DefaultValue fields are skipped by explicit constructors`` ( s.D <- 6 s.D |> equal 6 +[] +let ``test Explicit constructors skip static fields next to DefaultValue fields`` () = + let c = ClassWithDefaultValueStaticAndInstanceFields(1, 2) + c.X |> equal 1 + c.D |> equal 0 + c.Y |> equal 2 + [] let ``test Struct with mutable fields works`` () = let x = ValueTypeR(-10.0) diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 4d0df73179..1b7932e198 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -453,6 +453,14 @@ type StructWithDefaultValueInstanceField = val Y: string new (x, y) = { X = x; Y = y } +type ClassWithDefaultValueStaticAndInstanceFields = + [] static val mutable private count: int + val X: int + [] val mutable D: int + val Y: int + new (x, y) = { X = x; Y = y } + static member Count with get () = ClassWithDefaultValueStaticAndInstanceFields.count and set v = ClassWithDefaultValueStaticAndInstanceFields.count <- v + [] type NestedStruct = @@ -1880,6 +1888,14 @@ let tests = s.D <- 6 s.D |> equal 6 + testCase "Explicit constructors skip static fields next to DefaultValue fields" <| fun () -> + let c = ClassWithDefaultValueStaticAndInstanceFields(1, 2) + ClassWithDefaultValueStaticAndInstanceFields.Count <- 4 + c.X |> equal 1 + c.D |> equal 0 + c.Y |> equal 2 + ClassWithDefaultValueStaticAndInstanceFields.Count |> equal 4 + testCase "Unchecked.defaultof works for fields on structs" <| fun () -> let top = TopLevelStruct() top.A |> equal 0 diff --git a/tests/Python/TestType.fs b/tests/Python/TestType.fs index 7152e55da4..a2eef539b1 100644 --- a/tests/Python/TestType.fs +++ b/tests/Python/TestType.fs @@ -386,6 +386,18 @@ type StructWithDefaultValueInstanceField = val Y: string new(x, y) = { X = x; Y = y } +type ClassWithDefaultValueStaticAndInstanceFields = + [] + static val mutable private count: int + val X: int + [] + val mutable D: int + val Y: int + new(x, y) = { X = x; Y = y } + static member Count + with get () = ClassWithDefaultValueStaticAndInstanceFields.count + and set v = ClassWithDefaultValueStaticAndInstanceFields.count <- v + [] type NestedStruct = val A: float @@ -1876,6 +1888,15 @@ let ``test Instance DefaultValue fields are skipped by explicit constructors`` ( s.D <- 6 s.D |> equal 6 +[] +let ``test Explicit constructors skip static fields next to DefaultValue fields`` () = + let c = ClassWithDefaultValueStaticAndInstanceFields(1, 2) + ClassWithDefaultValueStaticAndInstanceFields.Count <- 4 + c.X |> equal 1 + c.D |> equal 0 + c.Y |> equal 2 + ClassWithDefaultValueStaticAndInstanceFields.Count |> equal 4 + [] let ``test Unchecked.defaultof works for fields on structs`` () = let top = TopLevelStruct() diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs index 2777e0f3c4..42e093d1da 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -305,6 +305,15 @@ type StructWithDefaultValueInstanceField = val Y: string new(x, y) = { X = x; Y = y } +type ClassWithDefaultValueStaticAndInstanceFields = + [] + static val mutable private count: int + val X: int + [] + val mutable D: int + val Y: int + new(x, y) = { X = x; Y = y } + exception MyEx of int * string // type MyEx2(f: float) = @@ -1388,6 +1397,13 @@ let ``Instance DefaultValue fields are skipped by explicit constructors`` () = s.D <- 6 s.D |> equal 6 +[] +let ``Explicit constructors skip static fields next to DefaultValue fields`` () = + let c = ClassWithDefaultValueStaticAndInstanceFields(1, 2) + c.X |> equal 1 + c.D |> equal 0 + c.Y |> equal 2 + [] let ``Struct with mutable fields works`` () = let x = ValueTypeR(-10.0) From 97e9ba6586039ab71ed245370c25f1bb30bd9c0a Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Thu, 5 Nov 2026 15:50:42 +0100 Subject: [PATCH 4/4] fix(js/ts): keep function DefaultValue fields null --- src/fable-library-ts/Util.ts | 19 +++++++++++++++++++ tests/Js/Main/TypeTests.fs | 12 ++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/fable-library-ts/Util.ts b/src/fable-library-ts/Util.ts index 484846d3e7..382a5c1d25 100644 --- a/src/fable-library-ts/Util.ts +++ b/src/fable-library-ts/Util.ts @@ -834,6 +834,7 @@ export function uncurry2(f: (a1: T1) => (a2: T2) => TResult) { } export function curry2(f: (a1: T1, a2: T2) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as ((a1: T1) => (a2: T2) => TResult) ?? ((a1: T1) => (a2: T2) => f(a1, a2)); } @@ -845,6 +846,7 @@ export function uncurry3(f: (a1: T1) => (a2: T2) => (a3: T3 } export function curry3(f: (a1: T1, a2: T2, a3: T3) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => f(a1, a2, a3)); } @@ -859,6 +861,7 @@ export function uncurry4( } export function curry4(f: (a1: T1, a2: T2, a3: T3, a4: T4) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => f(a1, a2, a3, a4)); } @@ -873,6 +876,7 @@ export function uncurry5( } export function curry5(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => f(a1, a2, a3, a4, a5)); } @@ -887,6 +891,7 @@ export function uncurry6( } export function curry6(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => f(a1, a2, a3, a4, a5, a6)); } @@ -901,6 +906,7 @@ export function uncurry7( } export function curry7(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => f(a1, a2, a3, a4, a5, a6, a7)); } @@ -915,6 +921,7 @@ export function uncurry8( } export function curry8(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => f(a1, a2, a3, a4, a5, a6, a7, a8)); } @@ -929,6 +936,7 @@ export function uncurry9( } export function curry9(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9)); } @@ -943,6 +951,7 @@ export function uncurry10( } export function curry10(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)); } @@ -957,6 +966,7 @@ export function uncurry11 } export function curry11(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)); } @@ -971,6 +981,7 @@ export function uncurry12(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)); } @@ -985,6 +996,7 @@ export function uncurry13(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12, a13: T13) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)); } @@ -999,6 +1011,7 @@ export function uncurry14(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12, a13: T13, a14: T14) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14)); } @@ -1013,6 +1026,7 @@ export function uncurry15(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12, a13: T13, a14: T14, a15: T15) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)); } @@ -1027,6 +1041,7 @@ export function uncurry16(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12, a13: T13, a14: T14, a15: T15, a16: T16) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)); } @@ -1041,6 +1056,7 @@ export function uncurry17(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12, a13: T13, a14: T14, a15: T15, a16: T16, a17: T17) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => (a17: T17) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => (a17: T17) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17)); } @@ -1055,6 +1071,7 @@ export function uncurry18(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12, a13: T13, a14: T14, a15: T15, a16: T16, a17: T17, a18: T18) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => (a17: T17) => (a18: T18) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => (a17: T17) => (a18: T18) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18)); } @@ -1069,6 +1086,7 @@ export function uncurry19(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12, a13: T13, a14: T14, a15: T15, a16: T16, a17: T17, a18: T18, a19: T19) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => (a17: T17) => (a18: T18) => (a19: T19) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => (a17: T17) => (a18: T18) => (a19: T19) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19)); } @@ -1083,6 +1101,7 @@ export function uncurry20(f: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9, a10: T10, a11: T11, a12: T12, a13: T13, a14: T14, a15: T15, a16: T16, a17: T17, a18: T18, a19: T19, a20: T20) => TResult) { + if (f == null) { return null as never; } return getCurried(f) as (a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => (a17: T17) => (a18: T18) => (a19: T19) => (a20: T20) => TResult ?? ((a1: T1) => (a2: T2) => (a3: T3) => (a4: T4) => (a5: T5) => (a6: T6) => (a7: T7) => (a8: T8) => (a9: T9) => (a10: T10) => (a11: T11) => (a12: T12) => (a13: T13) => (a14: T14) => (a15: T15) => (a16: T16) => (a17: T17) => (a18: T18) => (a19: T19) => (a20: T20) => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)); } diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 1b7932e198..4f8b8e5bde 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -461,6 +461,11 @@ type ClassWithDefaultValueStaticAndInstanceFields = new (x, y) = { X = x; Y = y } static member Count with get () = ClassWithDefaultValueStaticAndInstanceFields.count and set v = ClassWithDefaultValueStaticAndInstanceFields.count <- v +type ClassWithDefaultValueFunctionField = + val F: int -> int -> int + [] val mutable G: int -> int -> int + new (f) = { F = f } + [] type NestedStruct = @@ -1896,6 +1901,13 @@ let tests = c.Y |> equal 2 ClassWithDefaultValueStaticAndInstanceFields.Count |> equal 4 + testCase "Function-typed DefaultValue fields are null" <| fun () -> + let c = ClassWithDefaultValueFunctionField(fun a b -> a + b) + isNull (box c.G) |> equal true + c.F 2 3 |> equal 5 + c.G <- fun a b -> a * b + c.G 2 3 |> equal 6 + testCase "Unchecked.defaultof works for fields on structs" <| fun () -> let top = TopLevelStruct() top.A |> equal 0