Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d1b1e07
Add ImplicitDIMCoverage language feature with implementation
T-Gro Jan 22, 2026
f7da87c
Implement implicit DIM coverage for interface hierarchies (Sprint 2)
T-Gro Jan 22, 2026
b0b6cfb
Add edge case tests for DIM slot coverage
T-Gro Jan 22, 2026
9f09bff
Add object expression tests for DIM slot coverage
T-Gro Jan 22, 2026
1bd1aeb
Add Sprint 5 tests: re-abstraction, generics, possiblyNoMostSpecific
T-Gro Jan 22, 2026
98d8b99
Add language version gating tests for DIM coverage feature
T-Gro Jan 22, 2026
0cccb59
Sprint 7: Verify existing tests and baselines unchanged
T-Gro Jan 22, 2026
68f1732
Sprint 8: Add release notes and update RFC for DIM slot coverage feature
T-Gro Jan 22, 2026
04edd07
Refactor: Extract HasImplicitDIMCoverage member to RequiredSlot type
T-Gro Jan 22, 2026
b7de4fe
Clean up: Remove .ralph directory and temporary files
T-Gro Jan 22, 2026
e26de95
Consolidate C# libraries and reduce test count to 14
T-Gro Jan 23, 2026
bdb26c1
Sprint 1: Restore all 20 tests with consolidated C# library
T-Gro Jan 23, 2026
de35807
Remove duplicate DIM tests, reduce from 20 to 15 tests
T-Gro Jan 23, 2026
61f30c4
Sprint 3: Extract helper functions for DIM tests
T-Gro Jan 23, 2026
5f1d910
Enhance DIM Slot Coverage Tests module documentation
T-Gro Jan 23, 2026
9bc552e
Enhance DIM Slot Coverage Tests module documentation
T-Gro Jan 23, 2026
f1134e1
Merge branch 'main' into feature-equally-named-abstract-slots
T-Gro Jan 23, 2026
6ceda46
Merge branch 'feature-equally-named-abstract-slots' of https://github…
T-Gro Jan 23, 2026
d46ae5a
Apply suggestion from @T-Gro
T-Gro Jan 23, 2026
675751f
Apply suggestion from @T-Gro
T-Gro Jan 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Added

* Simplify implementation of interface hierarchies with equally named abstract slots: when a derived interface provides a Default Interface Member (DIM) implementation for a base interface slot, F# no longer requires explicit interface declarations for the DIM-covered slot. (RFC FS-1336, [Language suggestion #1430](https://github.com/fsharp/fslang-suggestions/issues/1430), [PR #19241](https://github.com/dotnet/fsharp/pull/19241))
* Better generic unmanaged structs handling. ([Language suggestion #692](https://github.com/fsharp/fslang-suggestions/issues/692), [PR #12154](https://github.com/dotnet/fsharp/pull/12154))
* Deprecate places where `seq` can be omitted. ([Language suggestion #1033](https://github.com/fsharp/fslang-suggestions/issues/1033), [PR #17772](https://github.com/dotnet/fsharp/pull/17772))
* Added type conversions cache, only enabled for compiler runs ([PR#17668](https://github.com/dotnet/fsharp/pull/17668))
Expand Down
67 changes: 46 additions & 21 deletions src/Compiler/Checking/MethodOverrides.fs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ type RequiredSlot =
| DefaultInterfaceImplementationSlot(_, _, possiblyNoMostSpecific) -> possiblyNoMostSpecific
| _ -> false

/// Checks if this slot has implicit DIM coverage (used when the ImplicitDIMCoverage feature is enabled).
/// A slot has DIM coverage when it has a default interface implementation AND is optional (not re-abstracted).
member this.HasImplicitDIMCoverage(g: TcGlobals) =
g.langVersion.SupportsFeature LanguageFeature.ImplicitDIMCoverage
&& this.HasDefaultInterfaceImplementation
&& this.IsOptional

/// Gets the method info.
member this.MethodInfo =
match this with
Expand Down Expand Up @@ -583,18 +590,19 @@ module DispatchSlotChecking =
[ (reqdTy, GetClassDispatchSlots infoReader ad m reqdTy) ]

/// Check all implementations implement some dispatch slot.
let CheckOverridesAreAllUsedOnce(denv, g, infoReader: InfoReader, isObjExpr, reqdTy,
let CheckOverridesAreAllUsedOnce(denv, g: TcGlobals.TcGlobals, infoReader: InfoReader, isObjExpr, reqdTy,
dispatchSlotsKeyed: NameMultiMap<RequiredSlot>,
availPriorOverrides: OverrideInfo list,
overrides: OverrideInfo list) =
let amap = infoReader.amap

let availPriorOverridesKeyed = availPriorOverrides |> NameMultiMap.initBy (fun ov -> ov.LogicalName)

for overrideBy in overrides do
if not overrideBy.IsFakeEventProperty then
let m = overrideBy.Range
let relevantVirts = NameMultiMap.find overrideBy.LogicalName dispatchSlotsKeyed
let relevantVirts = relevantVirts |> List.map (fun reqdSlot -> reqdSlot.MethodInfo)
let relevantSlots = NameMultiMap.find overrideBy.LogicalName dispatchSlotsKeyed
let relevantVirts = relevantSlots |> List.map (fun reqdSlot -> reqdSlot.MethodInfo)

match relevantVirts |> List.filter (fun dispatchSlot -> OverrideImplementsDispatchSlot g amap m dispatchSlot overrideBy) with
| [] ->
Expand Down Expand Up @@ -628,7 +636,22 @@ module DispatchSlotChecking =
if dispatchSlot.IsFinal && (isObjExpr || not (typeEquiv g reqdTy dispatchSlot.ApparentEnclosingType)) then
errorR(Error(FSComp.SR.typrelMethodIsSealed(NicePrint.stringOfMethInfo infoReader m denv dispatchSlot), m))
| dispatchSlots ->
match dispatchSlots |> List.filter (fun dispatchSlot ->
// Filter out slots that have DIM coverage (when feature is enabled)
// by looking up the original RequiredSlot information
let slotsWithoutDIMCoverage =
dispatchSlots
|> List.filter (fun dispatchSlot ->
// Find the corresponding RequiredSlot to check for DIM coverage
let correspondingSlot =
relevantSlots
|> List.tryFind (fun rs ->
rs.MethodInfo.LogicalName = dispatchSlot.LogicalName &&
typeEquiv g rs.MethodInfo.ApparentEnclosingType dispatchSlot.ApparentEnclosingType)
match correspondingSlot with
| Some slot -> not (slot.HasImplicitDIMCoverage g)
| None -> true)

match slotsWithoutDIMCoverage |> List.filter (fun dispatchSlot ->
(dispatchSlot.IsInstance = overrideBy.IsInstance) &&
isInterfaceTy g dispatchSlot.ApparentEnclosingType ||
not (DispatchSlotIsAlreadyImplemented g amap m availPriorOverridesKeyed dispatchSlot)) with
Expand Down Expand Up @@ -860,24 +883,26 @@ module DispatchSlotChecking =
let overrideByInfo = GetTypeMemberOverrideInfo g reqdTy overrideBy
let overriddenForThisSlotImplSet =
[ for reqdSlot in NameMultiMap.find overrideByInfo.LogicalName dispatchSlotsKeyed do
let dispatchSlot = reqdSlot.MethodInfo
if OverrideImplementsDispatchSlot g amap m dispatchSlot overrideByInfo then
if tyconRefEq g overrideByInfo.BoundingTyconRef dispatchSlot.DeclaringTyconRef then
match dispatchSlot.ArbitraryValRef with
| Some virtMember ->
if virtMember.MemberInfo.Value.IsImplemented then errorR(Error(FSComp.SR.tcDefaultImplementationAlreadyExists(), overrideByInfo.Range))
virtMember.MemberInfo.Value.IsImplemented <- true
| None -> () // not an F# slot

// Get the slotsig of the overridden method
let slotsig = dispatchSlot.GetSlotSig(amap, m)

// The slotsig from the overridden method is in terms of the type parameters on the parent type of the overriding method,
// Modify map the slotsig so it is in terms of the type parameters for the overriding method
let slotsig = ReparentSlotSigToUseMethodTypars g m overrideBy slotsig
// Skip slots that have DIM coverage (they don't need MethodImpl)
if not (reqdSlot.HasImplicitDIMCoverage g) then
let dispatchSlot = reqdSlot.MethodInfo
if OverrideImplementsDispatchSlot g amap m dispatchSlot overrideByInfo then
if tyconRefEq g overrideByInfo.BoundingTyconRef dispatchSlot.DeclaringTyconRef then
match dispatchSlot.ArbitraryValRef with
| Some virtMember ->
if virtMember.MemberInfo.Value.IsImplemented then errorR(Error(FSComp.SR.tcDefaultImplementationAlreadyExists(), overrideByInfo.Range))
virtMember.MemberInfo.Value.IsImplemented <- true
| None -> () // not an F# slot

// Get the slotsig of the overridden method
let slotsig = dispatchSlot.GetSlotSig(amap, m)

// The slotsig from the overridden method is in terms of the type parameters on the parent type of the overriding method,
// Modify map the slotsig so it is in terms of the type parameters for the overriding method
let slotsig = ReparentSlotSigToUseMethodTypars g m overrideBy slotsig

// Record the slotsig via mutation
yield slotsig ]
// Record the slotsig via mutation
yield slotsig ]
//if mustOverrideSomething reqdTy overrideBy then
// assert nonNil overriddenForThisSlotImplSet
yield! overriddenForThisSlotImplSet ]
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1803,5 +1803,6 @@ featureAllowLetOrUseBangTypeAnnotationWithoutParens,"Allow let! and use! type an
3878,tcAttributeIsNotValidForUnionCaseWithFields,"This attribute is not valid for use on union cases with fields."
3879,xmlDocNotFirstOnLine,"XML documentation comments should be the first non-whitespace text on a line."
featureReturnFromFinal,"Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder."
featureImplicitDIMCoverage,"Implicit dispatch slot coverage for default interface member implementations"
3880,optsLangVersionOutOfSupport,"Language version '%s' is out of support. The last .NET SDK supporting it is available at https://dotnet.microsoft.com/en-us/download/dotnet/%s"
3881,optsUnrecognizedLanguageFeature,"Unrecognized language feature name: '%s'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'."
3 changes: 3 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ type LanguageFeature =
| ErrorOnInvalidDeclsInTypeDefinitions
| AllowTypedLetUseAndBang
| ReturnFromFinal
| ImplicitDIMCoverage

/// LanguageVersion management
type LanguageVersion(versionText, ?disabledFeaturesArray: LanguageFeature array) =
Expand Down Expand Up @@ -251,6 +252,7 @@ type LanguageVersion(versionText, ?disabledFeaturesArray: LanguageFeature array)

// F# preview (still preview in 10.0)
LanguageFeature.FromEndSlicing, previewVersion // Unfinished features --- needs work
LanguageFeature.ImplicitDIMCoverage, previewVersion
]

static let defaultLanguageVersion = LanguageVersion("default")
Expand Down Expand Up @@ -440,6 +442,7 @@ type LanguageVersion(versionText, ?disabledFeaturesArray: LanguageFeature array)
| LanguageFeature.ErrorOnInvalidDeclsInTypeDefinitions -> FSComp.SR.featureErrorOnInvalidDeclsInTypeDefinitions ()
| LanguageFeature.AllowTypedLetUseAndBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens ()
| LanguageFeature.ReturnFromFinal -> FSComp.SR.featureReturnFromFinal ()
| LanguageFeature.ImplicitDIMCoverage -> FSComp.SR.featureImplicitDIMCoverage ()

/// Get a version string associated with the given feature.
static member GetFeatureVersionString feature =
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Facilities/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ type LanguageFeature =
| ErrorOnInvalidDeclsInTypeDefinitions
| AllowTypedLetUseAndBang
| ReturnFromFinal
| ImplicitDIMCoverage

/// LanguageVersion management
type LanguageVersion =
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.zh-Hans.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.zh-Hant.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@
<Compile Include="ConstraintSolver\MemberConstraints.fs" />
<Compile Include="ConstraintSolver\ObjInference.fs" />
<Compile Include="Interop\DeeplyNestedCSharpClasses.fs" />
<Compile Include="Interop\DIMSlotCoverageTests.fs" />
<Compile Include="Interop\SimpleInteropTests.fs" />
<Compile Include="Interop\RequiredAndInitOnlyProperties.fs" />
<Compile Include="Interop\StaticsInInterfaces.fs" />
Expand Down
Loading
Loading