From 321602c582d9d7e2e18eba6d46e21f2153d07f81 Mon Sep 17 00:00:00 2001 From: Davis Davalos-DeLosh Date: Wed, 2 Jul 2025 23:22:57 -0600 Subject: [PATCH 1/7] Fix generic parameter resolution in inline functions with static member constraints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes an issue where inline functions with multiple generic type parameters and static member constraints would incorrectly resolve all trait calls to the first type parameter's witness. Changes: - Add tryFindWitnessWithSourceTypes to properly select witnesses based on source type information when multiple witnesses match - Update trait call resolution to use source types for disambiguation - Fix generic argument composition in nested inline functions to preserve outer context mappings The fix ensures that each generic parameter's static member constraints are resolved to the correct witness, including in complex scenarios like nested inline function calls. Fixes the issue where code like: ```fsharp let inline test<'a, 'b when 'a: (static member M: unit -> string) and 'b: (static member M: unit -> string)> () = 'a.M(), 'b.M() ``` Would incorrectly return the same value for both calls. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/Fable.Transforms/FSharp2Fable.Util.fs | 82 ++++++++++++++++++++++- src/Fable.Transforms/FSharp2Fable.fs | 12 ++-- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index 3ff82f83c..5027c2e8d 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -1728,6 +1728,68 @@ module TypeHelpers = && listEquals (typeEquals false) argTypes w.ArgTypes ) + // Enhanced version that handles multiple witnesses for the same trait + // by using source type information to pick the correct one + let tryFindWitnessWithSourceTypes (ctx: Context) sourceTypes argTypes isInstance traitName = + let matchingWitnesses = + ctx.Witnesses + |> List.filter (fun w -> + w.TraitName = traitName + && w.IsInstance = isInstance + && listEquals (typeEquals false) argTypes w.ArgTypes + ) + + match matchingWitnesses with + | [] -> None + | [ single ] -> Some single + | multiple -> + // Multiple witnesses for the same trait - need to pick the right one + // based on which generic parameter is being resolved + match sourceTypes with + | [ sourceType ] -> + // First, resolve the source type in case it's a generic parameter + // that needs to be mapped to a concrete type + let resolvedSourceType = + match sourceType with + | Fable.GenericParam(name, _, _) -> + // Check if this generic parameter has been mapped to a concrete type + match Map.tryFind name ctx.GenericArgs with + | Some concreteType -> concreteType + | None -> sourceType + | _ -> sourceType + + // Now find which witness to use based on the resolved source type + match resolvedSourceType with + | Fable.GenericParam(name, isMeasure, _) -> + // For generic parameters, find their position in the original function signature + let genParamNames = ctx.GenericArgs |> Map.toList |> List.map fst + let genParamPosition = genParamNames |> List.tryFindIndex ((=) name) + + match genParamPosition with + | Some idx when idx < List.length multiple -> + // Witnesses are provided in order corresponding to generic parameters + List.tryItem idx multiple + | _ -> List.tryHead multiple + + | Fable.DeclaredType(entity, _) -> + // Find the position of this entity type in the generic arguments + let genArgPosition = + ctx.GenericArgs + |> Map.toList + |> List.tryFindIndex (fun (_, argType) -> + match argType with + | Fable.DeclaredType(e, _) -> e.FullName = entity.FullName + | _ -> false + ) + + match genArgPosition with + | Some idx when idx < List.length multiple -> + // Witnesses are provided in order corresponding to generic parameters + List.tryItem idx multiple + | _ -> List.tryHead multiple + | _ -> List.tryHead multiple + | _ -> List.tryHead multiple + module Identifiers = open Helpers open TypeHelpers @@ -2733,9 +2795,27 @@ module Util = let genArgs = List.zipSafe inExpr.GenericArgs info.GenericArgs |> Map + // For nested inline functions, we need to preserve the outer context's generic args + // and resolve any references through them + let resolveTypeWithOuterContext (typ: Fable.Type) = + match typ with + | Fable.GenericParam(name, _, _) -> + // If this generic parameter is mapped in the outer context, use that + match Map.tryFind name ctx.GenericArgs with + | Some resolvedType -> resolvedType + | None -> typ + | _ -> typ + + // Create the composed generic args by resolving through the outer context + let composedGenArgs = + genArgs + |> Map.map (fun _ typ -> resolveTypeWithOuterContext typ) + // Also preserve any generic args from the outer context that aren't overridden + |> Map.fold (fun acc k v -> Map.add k v acc) ctx.GenericArgs + let ctx = { ctx with - GenericArgs = genArgs + GenericArgs = composedGenArgs InlinePath = { ToFile = inExpr.FileName diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 953e3d1f3..74f56d350 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -927,9 +927,10 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) appliedGenArgs fs return Fable.Unresolved(e, typ, r) | None -> - match tryFindWitness ctx argTypes flags.IsInstance traitName with + let sourceTypes = List.map (makeType ctx.GenericArgs) sourceTypes + + match tryFindWitnessWithSourceTypes ctx sourceTypes argTypes flags.IsInstance traitName with | None -> - let sourceTypes = List.map (makeType ctx.GenericArgs) sourceTypes return transformTraitCall com ctx r typ sourceTypes traitName flags.IsInstance argTypes argExprs | Some w -> let callInfo = makeCallInfo None argExprs argTypes @@ -2460,11 +2461,10 @@ let resolveInlineExpr (com: IFableCompiler) ctx info expr = let argExprs = argExprs |> List.map (resolveInlineExpr com ctx info) - match tryFindWitness ctx argTypes isInstance traitName with - | None -> - let sourceTypes = sourceTypes |> List.map (resolveInlineType ctx.GenericArgs) + let sourceTypes = sourceTypes |> List.map (resolveInlineType ctx.GenericArgs) - transformTraitCall com ctx r t sourceTypes traitName isInstance argTypes argExprs + match tryFindWitnessWithSourceTypes ctx sourceTypes argTypes isInstance traitName with + | None -> transformTraitCall com ctx r t sourceTypes traitName isInstance argTypes argExprs | Some w -> // As witnesses come from the context, idents may be duplicated, see #2855 let info = From a294576496676da3271d0a564dcaebba2df69b9a Mon Sep 17 00:00:00 2001 From: Davis Davalos-DeLosh Date: Thu, 3 Jul 2025 15:25:40 -0600 Subject: [PATCH 2/7] Add comprehensive test suite for generic parameter resolution fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added tests covering: - Two and three parameter inline functions with static member constraints - Multiple constraints per type parameter - Reversed parameter order - Nested inline function scenarios - Various type parameter combinations All tests pass successfully, verifying the fix for issue #4093. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- tests/Js/Main/TypeTests.fs | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 4be38a36b..27c1c98ae 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -639,6 +639,50 @@ type Model = unit let update (model: Model) = model, () +// Test types for generic parameter static member resolution +type TestTypeA = + static member GetValue() = "A" + static member Combine(x: int, y: int) = x + y + 100 + +type TestTypeB = + static member GetValue() = "B" + static member Combine(x: int, y: int) = x + y + 200 + +type TestTypeC = + static member GetValue() = "C" + static member Combine(x: int, y: int) = x + y + 300 + +// Inline functions for testing multiple generic parameters with static member constraints +let inline getTwoValues<'a, 'b when 'a: (static member GetValue: unit -> string) + and 'b: (static member GetValue: unit -> string)> () = + 'a.GetValue(), 'b.GetValue() + +let inline getThreeValues<'a, 'b, 'c when 'a: (static member GetValue: unit -> string) + and 'b: (static member GetValue: unit -> string) + and 'c: (static member GetValue: unit -> string)> () = + 'a.GetValue(), 'b.GetValue(), 'c.GetValue() + +let inline getValuesAndCombine<'a, 'b when 'a: (static member GetValue: unit -> string) + and 'a: (static member Combine: int * int -> int) + and 'b: (static member GetValue: unit -> string) + and 'b: (static member Combine: int * int -> int)> x y = + let aVal = 'a.GetValue() + let bVal = 'b.GetValue() + let aCombined = 'a.Combine(x, y) + let bCombined = 'b.Combine(x, y) + (aVal, aCombined), (bVal, bCombined) + +let inline getReversed<'x, 'y when 'x: (static member GetValue: unit -> string) + and 'y: (static member GetValue: unit -> string)> () = + 'y.GetValue(), 'x.GetValue() + +let inline innerGet<'t when 't: (static member GetValue: unit -> string)> () = + 't.GetValue() + +let inline outerGet<'a, 'b when 'a: (static member GetValue: unit -> string) + and 'b: (static member GetValue: unit -> string)> () = + innerGet<'a>(), innerGet<'b>() + let tests = testList "Types" [ @@ -1394,4 +1438,36 @@ let tests = (upper :> IGenericMangledInterface).ReadOnlyValue |> equal "value" (upper :> IGenericMangledInterface).SetterOnlyValue <- "setter only value" (upper :> IGenericMangledInterface).Value |> equal "setter only value" + + // Test for generic type parameter static member resolution in inline functions + // https://github.com/fable-compiler/Fable/issues/4093 + testCase "Inline function with two generic parameters resolves static members correctly" <| fun () -> + let result = getTwoValues() + result |> equal ("A", "B") + + testCase "Inline function with three generic parameters resolves static members correctly" <| fun () -> + let result = getThreeValues() + result |> equal ("A", "B", "C") + + testCase "Inline function with multiple constraints per type parameter works" <| fun () -> + let result = getValuesAndCombine 10 20 + result |> equal (("A", 130), ("B", 230)) + + testCase "Inline function with reversed type parameter order works" <| fun () -> + let result = getReversed() + result |> equal ("B", "A") + + testCase "Nested inline functions resolve generic parameters correctly" <| fun () -> + let result = outerGet() + result |> equal ("A", "B") + + testCase "Different type parameter combinations work correctly" <| fun () -> + let result1 = getTwoValues() + result1 |> equal ("B", "A") + + let result2 = getTwoValues() + result2 |> equal ("C", "A") + + let result3 = getTwoValues() + result3 |> equal ("B", "C") ] From d080d0c129b5021262da66063fce2fdad0c9fc3f Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Thu, 5 Mar 2026 20:18:52 +0100 Subject: [PATCH 3/7] feat: don't rely on indexes but instance use the generic name (simplify the logic a little too) --- src/Fable.AST/Fable.fs | 3 + src/Fable.Transforms/FSharp2Fable.Util.fs | 70 ++++++++--------------- src/Fable.Transforms/FSharp2Fable.fs | 53 ++++++++++++++--- 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/src/Fable.AST/Fable.fs b/src/Fable.AST/Fable.fs index abfe1db24..babda01ba 100644 --- a/src/Fable.AST/Fable.fs +++ b/src/Fable.AST/Fable.fs @@ -757,6 +757,9 @@ type Witness = IsInstance: bool FileName: string Expr: Expr + /// The name of the generic parameter this witness satisfies (e.g. "'a" from `'a: (static member M: unit -> string)`). + /// Used to disambiguate when multiple witnesses match the same trait name and arg types. + GenericParamName: string option } member this.ArgTypes = diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index 5027c2e8d..063bc0aad 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -1743,52 +1743,32 @@ module TypeHelpers = | [] -> None | [ single ] -> Some single | multiple -> - // Multiple witnesses for the same trait - need to pick the right one - // based on which generic parameter is being resolved - match sourceTypes with - | [ sourceType ] -> - // First, resolve the source type in case it's a generic parameter - // that needs to be mapped to a concrete type - let resolvedSourceType = - match sourceType with - | Fable.GenericParam(name, _, _) -> - // Check if this generic parameter has been mapped to a concrete type - match Map.tryFind name ctx.GenericArgs with - | Some concreteType -> concreteType - | None -> sourceType - | _ -> sourceType - - // Now find which witness to use based on the resolved source type - match resolvedSourceType with - | Fable.GenericParam(name, isMeasure, _) -> - // For generic parameters, find their position in the original function signature - let genParamNames = ctx.GenericArgs |> Map.toList |> List.map fst - let genParamPosition = genParamNames |> List.tryFindIndex ((=) name) - - match genParamPosition with - | Some idx when idx < List.length multiple -> - // Witnesses are provided in order corresponding to generic parameters - List.tryItem idx multiple - | _ -> List.tryHead multiple - - | Fable.DeclaredType(entity, _) -> - // Find the position of this entity type in the generic arguments - let genArgPosition = - ctx.GenericArgs - |> Map.toList - |> List.tryFindIndex (fun (_, argType) -> - match argType with - | Fable.DeclaredType(e, _) -> e.FullName = entity.FullName - | _ -> false - ) + // Multiple witnesses match by trait name and arg types. + // Use the source type to derive the original generic parameter name, then find + // the witness tagged with that name. + // + // Two cases: + // 1. sourceTypes contains a raw GenericParam (e.g. when compiling an inline function + // whose body is not yet specialized): extract the param name directly. + // 2. sourceTypes contains a resolved DeclaredType (e.g. 'b was resolved to TestTypeB + // via ctx.GenericArgs before being passed here): reverse-lookup in ctx.GenericArgs + // to recover the original param name ("b"). + let genParamName = + match sourceTypes with + | [ Fable.GenericParam(name, _, _) ] -> Some name + | [ resolvedType ] -> + // Reverse-lookup: find the generic param name whose resolved type matches + ctx.GenericArgs + |> Map.tryFindKey (fun _paramName paramType -> typeEquals false paramType resolvedType) + | _ -> None - match genArgPosition with - | Some idx when idx < List.length multiple -> - // Witnesses are provided in order corresponding to generic parameters - List.tryItem idx multiple - | _ -> List.tryHead multiple - | _ -> List.tryHead multiple - | _ -> List.tryHead multiple + multiple + |> List.tryFind (fun w -> + match genParamName, w.GenericParamName with + | Some gpName, Some wGpName -> gpName = wGpName + | _ -> false + ) + |> Option.orElse (List.tryHead multiple) module Identifiers = open Helpers diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 74f56d350..b54a88406 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -981,23 +981,55 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) appliedGenArgs fs match witnesses with | [] -> return ctx | witnesses -> + // Build a map from entity full name -> generic param name. + // Each membGenArg corresponds to a GenericParameter at the same index. + // When a concrete type (e.g. TestTypeA) satisfies a constraint for 'a, + // membGenArgs contains TestTypeA's FSharpType at the position of 'a in + // memb.GenericParameters. This lets us look up the param name from the + // declaring entity of the called member inside the witness body, + // without relying on witness ordering. + let entityFullNameToParamName = + (Map.empty, Seq.zip memb.GenericParameters membGenArgs) + ||> Seq.fold (fun map (gp, fsType) -> + if fsType.HasTypeDefinition then + // Protect against primitive types (e.g. int) throwing when accessing FullName + match fsType.TypeDefinition.TryFullName with + | Some fullName -> Map.add fullName gp.Name map + | None -> map + else + map + ) + let witnesses = witnesses - |> List.choose ( - function - // Index is not reliable, just append witnesses from parent call - | FSharpExprPatterns.WitnessArg _idx -> None + |> List.choose (fun w -> + match w with + // WitnessArg entries are pass-throughs from an outer scope; + // skip them so they are inherited from ctx.Witnesses as-is. + // | FSharpExprPatterns.WitnessArg _idx -> None | NestedLambda(args, body) -> match body with - | FSharpExprPatterns.Call(callee, memb, _, _, _args) -> - Some(memb.CompiledName, Option.isSome callee, args, body) + | FSharpExprPatterns.Call(callee, calledMemb, _, _, _args) -> + let genParamName = + calledMemb.DeclaringEntity + |> Option.bind (fun ent -> + Map.tryFind ent.FullName entityFullNameToParamName + ) + + Some( + calledMemb.CompiledName, + Option.isSome callee, + args, + body, + genParamName + ) | FSharpExprPatterns.AnonRecordGet(_, calleeType, fieldIndex) -> let fieldName = calleeType.AnonRecordTypeDetails.SortedFieldNames[fieldIndex] - Some("get_" + fieldName, true, args, body) + Some("get_" + fieldName, true, args, body, None) | FSharpExprPatterns.FSharpFieldGet(_, _, field) -> - Some("get_" + field.Name, true, args, body) + Some("get_" + field.Name, true, args, body, None) | _ -> None | _ -> None ) @@ -1006,18 +1038,21 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) appliedGenArgs fs // so a witness may need other witnesses to be resolved return! (ctx, List.rev witnesses) - ||> trampolineListFold (fun ctx (traitName, isInstance, args, body) -> + ||> trampolineListFold (fun ctx (traitName, isInstance, args, body, genParamName) -> trampoline { let ctx, args = makeFunctionArgs com ctx args let! body = transformExpr com ctx [] body + printfn "genParamName: %A" genParamName + let w: Fable.Witness = { TraitName = traitName IsInstance = isInstance FileName = com.CurrentFile Expr = Fable.Delegate(args, body, None, Fable.Tags.empty) + GenericParamName = genParamName } return { ctx with Witnesses = w :: ctx.Witnesses } From 4ecc2863558045790602801574b337889a935201 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Thu, 5 Mar 2026 20:26:21 +0100 Subject: [PATCH 4/7] chore: remove debug log --- src/Fable.Transforms/FSharp2Fable.fs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index b54a88406..72348c8a7 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -1044,8 +1044,6 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) appliedGenArgs fs let! body = transformExpr com ctx [] body - printfn "genParamName: %A" genParamName - let w: Fable.Witness = { TraitName = traitName From fae6225592404285cefcf23b235fba943b745ac5 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Thu, 5 Mar 2026 20:26:32 +0100 Subject: [PATCH 5/7] chore: remove non needed code? --- src/Fable.Transforms/FSharp2Fable.Util.fs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index 063bc0aad..e0b2eeb62 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -2775,27 +2775,9 @@ module Util = let genArgs = List.zipSafe inExpr.GenericArgs info.GenericArgs |> Map - // For nested inline functions, we need to preserve the outer context's generic args - // and resolve any references through them - let resolveTypeWithOuterContext (typ: Fable.Type) = - match typ with - | Fable.GenericParam(name, _, _) -> - // If this generic parameter is mapped in the outer context, use that - match Map.tryFind name ctx.GenericArgs with - | Some resolvedType -> resolvedType - | None -> typ - | _ -> typ - - // Create the composed generic args by resolving through the outer context - let composedGenArgs = - genArgs - |> Map.map (fun _ typ -> resolveTypeWithOuterContext typ) - // Also preserve any generic args from the outer context that aren't overridden - |> Map.fold (fun acc k v -> Map.add k v acc) ctx.GenericArgs - let ctx = { ctx with - GenericArgs = composedGenArgs + GenericArgs = genArgs InlinePath = { ToFile = inExpr.FileName From f2d50d3774c34983f1d01e54ff4daf276e214866 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Thu, 5 Mar 2026 20:27:28 +0100 Subject: [PATCH 6/7] chore: update changelog --- src/Fable.Cli/CHANGELOG.md | 3 +++ src/Fable.Compiler/CHANGELOG.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 92ffc2a89..d4fb78579 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Python] Fix `nonlocal`/`global` declarations generated inside `match/case` bodies causing `SyntaxError` (by @dbrattli) * [Python] Fix exception variable captured in deferred closures causing `NameError` (PEP 3110 scoping) (by @dbrattli) * [JS/TS] Support format specifiers and single hole in JSX string templates (by @MangelMaxime) +* [All] Fix generic parameter resolution in inline functions with static member constraints (by @Programmerino +wants to merge 3 commits into +and @MangelMaxime) ## 5.0.0-rc.2 - 2026-03-03 diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index d6f5ce9f3..ad7c75e39 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Python] Fix `nonlocal`/`global` declarations generated inside `match/case` bodies causing `SyntaxError` (by @dbrattli) * [Python] Fix exception variable captured in deferred closures causing `NameError` (PEP 3110 scoping) (by @dbrattli) * [JS/TS] Support format specifiers and single hole in JSX string templates (by @MangelMaxime) +* [All] Fix generic parameter resolution in inline functions with static member constraints (by @Programmerino +wants to merge 3 commits into +and @MangelMaxime) ## 5.0.0-rc.2 - 2026-03-03 From bce3a0da283a9dff3fc415f146a70a3824bac4db Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Mon, 9 Mar 2026 16:15:25 +0100 Subject: [PATCH 7/7] chore: fix changelog entry --- src/Fable.Cli/CHANGELOG.md | 4 +--- src/Fable.Compiler/CHANGELOG.md | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index d4fb78579..bd4979ab0 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -14,9 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Python] Fix `nonlocal`/`global` declarations generated inside `match/case` bodies causing `SyntaxError` (by @dbrattli) * [Python] Fix exception variable captured in deferred closures causing `NameError` (PEP 3110 scoping) (by @dbrattli) * [JS/TS] Support format specifiers and single hole in JSX string templates (by @MangelMaxime) -* [All] Fix generic parameter resolution in inline functions with static member constraints (by @Programmerino -wants to merge 3 commits into -and @MangelMaxime) +* [All] Fix generic parameter resolution in inline functions with static member constraints (by @Programmerino and @MangelMaxime) ## 5.0.0-rc.2 - 2026-03-03 diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index ad7c75e39..784594e31 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -13,9 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Python] Fix `nonlocal`/`global` declarations generated inside `match/case` bodies causing `SyntaxError` (by @dbrattli) * [Python] Fix exception variable captured in deferred closures causing `NameError` (PEP 3110 scoping) (by @dbrattli) * [JS/TS] Support format specifiers and single hole in JSX string templates (by @MangelMaxime) -* [All] Fix generic parameter resolution in inline functions with static member constraints (by @Programmerino -wants to merge 3 commits into -and @MangelMaxime) +* [All] Fix generic parameter resolution in inline functions with static member constraints (by @Programmerino and @MangelMaxime) ## 5.0.0-rc.2 - 2026-03-03