Skip to content

Commit 8b6b464

Browse files
Merge main into darc-main-716c4452-51f7-4564-a66a-ad1ad92cc4ec
2 parents 2934704 + 95d5971 commit 8b6b464

File tree

19 files changed

+334
-0
lines changed

19 files changed

+334
-0
lines changed

docs/release-notes/.FSharp.Compiler.Service/10.0.200.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Type relations cache: optimize key generation ([Issue #19116](https://github.com/dotnet/fsharp/issues/18767)) ([PR #19120](https://github.com/dotnet/fsharp/pull/19120))
44
* Fixed QuickParse to correctly handle optional parameter syntax with `?` prefix, resolving syntax highlighting issues. ([Issue #11008753](https://developercommunity.visualstudio.com/t/F-Highlighting-fails-on-optional-parame/11008753)) ([PR #XXXXX](https://github.com/dotnet/fsharp/pull/XXXXX))
55
* Fix `--preferreduilang` switch leaking into `fsi.CommandLineArgs` when positioned after script file ([PR #19151](https://github.com/dotnet/fsharp/pull/19151))
6+
* Fixed runtime crash when using interfaces with unimplemented static abstract members as constrained type arguments. ([Issue #19184](https://github.com/dotnet/fsharp/issues/19184))
67

78
### Added
89

src/Compiler/Checking/InfoReader.fs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,26 @@ type InfoReader(g: TcGlobals, amap: ImportMap) as this =
775775
))
776776
g amap m AllowMultiIntfInstantiations.Yes ty
777777

778+
let GetUnimplementedStaticAbstractMemberOfTypeUncached (_flags, m, interfaceTy) =
779+
if not (isInterfaceTy g interfaceTy) then
780+
None
781+
else
782+
let checkMembersOfInterface (ty: TType) =
783+
let meths = this.GetIntrinsicMethInfosOfType None AccessibleFromSomeFSharpCode AllowMultiIntfInstantiations.Yes IgnoreOverrides m ty
784+
meths |> List.tryPick (fun (minfo: MethInfo) ->
785+
// Static abstract non-sealed (non-DIM) members
786+
if not minfo.IsInstance && minfo.IsAbstract && not minfo.IsFinal then
787+
Some minfo.DisplayNameCore
788+
else
789+
None
790+
)
791+
792+
match checkMembersOfInterface interfaceTy with
793+
| Some name -> Some name
794+
| None ->
795+
let baseInterfaces = AllInterfacesOfType g amap m AllowMultiIntfInstantiations.Yes interfaceTy
796+
baseInterfaces |> List.tryPick checkMembersOfInterface
797+
778798
let hashFlags0 =
779799
{ new IEqualityComparer<string option * AccessorDomain * AllowMultiIntfInstantiations> with
780800
member _.GetHashCode((filter: string option, ad: AccessorDomain, _allowMultiIntfInst1)) = hash filter + AccessorDomain.CustomGetHashCode ad
@@ -815,6 +835,11 @@ type InfoReader(g: TcGlobals, amap: ImportMap) as this =
815835
let primaryTypeHierarchyCache = MakeInfoCache "primaryTypeHierarchyCache" GetPrimaryTypeHierarchyUncached HashIdentity.Structural
816836
let implicitConversionCache = MakeInfoCache "implicitConversionCache" FindImplicitConversionsUncached hashFlags3
817837
let isInterfaceWithStaticAbstractMethodCache = MakeInfoCache "isInterfaceWithStaticAbstractMethodCache" IsInterfaceTypeWithMatchingStaticAbstractMemberUncached hashFlags4
838+
let unimplementedStaticAbstractMemberCache =
839+
MakeInfoCache
840+
"unimplementedStaticAbstractMemberCache"
841+
GetUnimplementedStaticAbstractMemberOfTypeUncached
842+
hashFlags0
818843

819844
// Runtime feature support
820845

@@ -992,6 +1017,14 @@ type InfoReader(g: TcGlobals, amap: ImportMap) as this =
9921017
member _.IsInterfaceTypeWithMatchingStaticAbstractMember m nm ad ty =
9931018
isInterfaceWithStaticAbstractMethodCache.Apply((ad, nm), m, ty)
9941019

1020+
member _.TryFindUnimplementedStaticAbstractMemberOfType (m: range) (interfaceTy: TType) : string option =
1021+
if not (isInterfaceTy g interfaceTy) then
1022+
None
1023+
elif not (g.langVersion.SupportsFeature LanguageFeature.InterfacesWithAbstractStaticMembers) then
1024+
None
1025+
else
1026+
unimplementedStaticAbstractMemberCache.Apply(((None, AccessibleFromSomewhere, AllowMultiIntfInstantiations.Yes), m, interfaceTy))
1027+
9951028
let checkLanguageFeatureRuntimeAndRecover (infoReader: InfoReader) langFeature m =
9961029
if not (infoReader.IsLanguageFeatureRuntimeSupported langFeature) then
9971030
let featureStr = LanguageVersion.GetFeatureString langFeature

src/Compiler/Checking/InfoReader.fsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ type InfoReader =
225225
member IsInterfaceTypeWithMatchingStaticAbstractMember:
226226
m: range -> nm: string -> ad: AccessorDomain -> ty: TType -> bool
227227

228+
/// Check if an interface type has an unimplemented static abstract member.
229+
/// Returns Some(memberLogicalName) if found, None otherwise.
230+
/// Results are cached per interface type definition.
231+
member TryFindUnimplementedStaticAbstractMemberOfType: m: range -> interfaceTy: TType -> string option
232+
228233
val checkLanguageFeatureRuntimeAndRecover:
229234
infoReader: InfoReader -> langFeature: Features.LanguageFeature -> m: range -> unit
230235

src/Compiler/Checking/PostInferenceChecks.fs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,24 @@ let rec mkArgsForAppliedExpr isBaseCall argsl x =
635635
| Expr.Op (TOp.Coerce, _, [f], _) -> mkArgsForAppliedExpr isBaseCall argsl f
636636
| _ -> []
637637

638+
/// Check if a type argument is an interface with unimplemented static abstract members
639+
/// when used with a type parameter that has interface constraints.
640+
/// See: https://github.com/dotnet/fsharp/issues/19184
641+
let CheckInterfaceTypeArgForUnimplementedStaticAbstractMembers (cenv: cenv) m (typar: Typar) (typeArg: TType) =
642+
if cenv.reportErrors then
643+
// Only check if the type parameter has interface constraints
644+
let hasInterfaceConstraint =
645+
typar.Constraints |> List.exists (function
646+
| TyparConstraint.CoercesTo(constraintTy, _) -> isInterfaceTy cenv.g constraintTy
647+
| _ -> false)
648+
649+
if hasInterfaceConstraint && isInterfaceTy cenv.g typeArg then
650+
match cenv.infoReader.TryFindUnimplementedStaticAbstractMemberOfType m typeArg with
651+
| Some memberName ->
652+
let interfaceTypeName = NicePrint.minimalStringOfType cenv.denv typeArg
653+
errorR(Error(FSComp.SR.chkInterfaceWithUnimplementedStaticAbstractMemberUsedAsTypeArgument(interfaceTypeName, memberName), m))
654+
| None -> ()
655+
638656
/// Check types occurring in the TAST.
639657
let CheckTypeAux permitByRefLike (cenv: cenv) env m ty onInnerByrefError =
640658
if cenv.reportErrors then
@@ -681,6 +699,15 @@ let CheckTypeAux permitByRefLike (cenv: cenv) env m ty onInnerByrefError =
681699
if isByrefTyconRef cenv.g tcref2 then
682700
errorR(Error(FSComp.SR.chkNoByrefsOfByrefs(NicePrint.minimalStringOfType cenv.denv ty), m))
683701
CheckTypesDeep cenv (visitType, None, None, None, None) cenv.g env tinst
702+
703+
// Check for interfaces with unimplemented static abstract members used as type arguments
704+
// This only applies when the type parameter has an interface constraint - using interfaces
705+
// with unconstrained generics (like List<ITest> or Dictionary<K, ITest>) is fine.
706+
// See: https://github.com/dotnet/fsharp/issues/19184
707+
if tcref.CanDeref then
708+
let typars = tcref.Typars m
709+
if typars.Length = tinst.Length then
710+
(typars, tinst) ||> List.iter2 (CheckInterfaceTypeArgForUnimplementedStaticAbstractMembers cenv m)
684711

685712
let visitTraitSolution info =
686713
match info with
@@ -1374,6 +1401,20 @@ and CheckApplication cenv env expr (f, tyargs, argsl, m) ctxt =
13741401
let env = { env with isInAppExpr = true }
13751402

13761403
CheckTypeInstNoByrefs cenv env m tyargs
1404+
1405+
// Check for interfaces with unimplemented static abstract members used as type arguments
1406+
// See: https://github.com/dotnet/fsharp/issues/19184
1407+
if not tyargs.IsEmpty then
1408+
match f with
1409+
| Expr.Val (vref, _, _) ->
1410+
match vref.TryDeref with
1411+
| ValueSome v ->
1412+
let typars = v.Typars
1413+
if typars.Length = tyargs.Length then
1414+
(typars, tyargs) ||> List.iter2 (CheckInterfaceTypeArgForUnimplementedStaticAbstractMembers cenv m)
1415+
| _ -> ()
1416+
| _ -> ()
1417+
13771418
CheckExprNoByrefs cenv env f
13781419

13791420
let hasReceiver =

src/Compiler/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,6 +1783,7 @@ featureReuseSameFieldsInStructUnions,"Share underlying fields in a [<Struct>] di
17831783
3865,parsOnlySimplePatternsAreAllowedInConstructors,"Only simple patterns are allowed in primary constructors"
17841784
3866,chkStaticAbstractInterfaceMembers,"A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.%s)."
17851785
3867,chkStaticAbstractMembersOnClasses,"Classes cannot contain static abstract members."
1786+
3868,chkInterfaceWithUnimplementedStaticAbstractMemberUsedAsTypeArgument,"The interface '%s' cannot be used as a type argument because the static abstract member '%s' does not have a most specific implementation in the interface."
17861787
3868,tcActivePatternArgsCountNotMatchNoArgsNoPat,"This active pattern does not expect any arguments, i.e., it should be used like '%s' instead of '%s x'."
17871788
3868,tcActivePatternArgsCountNotMatchOnlyPat,"This active pattern expects exactly one pattern argument, e.g., '%s pat'."
17881789
3868,tcActivePatternArgsCountNotMatchArgs,"This active pattern expects %d expression argument(s), e.g., '%s%s'."

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.fr.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.it.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)