Fix generic parameter resolution in inline functions with static member constraints
Fix generic parameter resolution in inline functions with static member constraints
Note: Claude Code did this for me, so it could be terrible
Description
This PR fixes a bug (https://github.com/fable-compiler/Fable/issues/4093) 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.
The Issue
When using inline functions with multiple generic parameters that have static member constraints, all static member calls would resolve to the first type parameter:
type A = static member M() = "A"
type B = static member M() = "B"
let inline test<'a, 'b when 'a: (static member M: unit -> string)
and 'b: (static member M: unit -> string)> () =
'a.M(), 'b.M() // Both calls incorrectly returned "A"
test<A, B>() // Would return ("A", "A") instead of ("A", "B")
Root Cause
The issue occurred in two places:
- During trait call resolution, when multiple witnesses matched the same trait, the code always selected the first one
- For nested inline functions, generic parameter mappings were being replaced instead of composed
Solution
-
Enhanced Witness Resolution: Added
tryFindWitnessWithSourceTypesthat uses source type information to select the correct witness when multiple witnesses match. It determines which generic parameter is being resolved and selects the corresponding witness based on position. -
Fixed Nested Inline Functions: Modified the generic argument handling in
inlineExprto compose mappings rather than replace them, ensuring that inner inline functions can resolve their generic parameters through the outer function's context.
Changes
- Added
tryFindWitnessWithSourceTypesinFSharp2Fable.Util.fsto handle witness disambiguation - Updated trait call resolution in
FSharp2Fable.fsto use source types for witness selection - Fixed generic argument composition for nested inline functions to preserve outer context mappings
Testing
The fix has been tested with:
- Basic two-parameter inline functions
- Three or more parameter inline functions
- Different parameter orders
- Multiple constraints per type
- Nested inline function calls
- Various type combinations
All scenarios now work correctly.
Impact
This fix enables proper use of inline functions with multiple generic parameters having static member constraints, which is important for generic programming patterns in F#.
Hello,
Can you please add some test suits so we can check if the behavior is correct ?
Let me know if that works!
Not going to lie, I really didn't think Claude could have pulled that off. Actually works. 👀. Nice!