From fc01ece1415141f8c0965aa7fbcf987d2de6ea7e Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Mon, 17 Aug 2026 01:56:00 +0200 Subject: [PATCH 1/2] Fix inherited IL base-call crash and add release notes --- .../.FSharp.Compiler.Service/11.0.100.md | 1 + src/Compiler/Checking/PostInferenceChecks.fs | 17 ++++++---- .../Interop/SimpleInteropTests.fs | 34 +++++++++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index 54236b3864c..2e58a160d38 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -95,6 +95,7 @@ * Fix Debug-mode compilation when mixing resumable and standard computation expressions. ([Issue #19625](https://github.com/dotnet/fsharp/issues/19625), [PR #19630](https://github.com/dotnet/fsharp/pull/19630)) * IlxGen: fix missing CompilationMapping attribute for generic values ([PR #19643](https://github.com/dotnet/fsharp/pull/19643)) * Fix internal error `FS0192: encodeCustomAttrElemType` when using arrays of user-defined types as custom attribute arguments. Empty arrays (e.g. `[]`) now compile successfully; non-empty arrays of unencodable types report a proper diagnostic (FS3887) instead of an internal error. ([Issue #12796](https://github.com/dotnet/fsharp/issues/12796), [PR #19472](https://github.com/dotnet/fsharp/pull/19472)) +* Fix a compiler crash when calling an inherited base method on an external generic IL type, such as `base.OnDetaching()` on `Microsoft.Xaml.Interactivity.Behavior<'T>`. The IL base-call check now only applies the abstract-base guard when the method actually exists on the raw metadata type instead of crashing while trying to resolve an inherited method. ([Issue #20264](https://github.com/dotnet/fsharp/issues/20264)) * Fix internal compiler error in `use` bindings when a C#-style `Dispose` extension method is in scope alongside `IDisposable.Dispose`. ([Issue #19552](https://github.com/dotnet/fsharp/issues/19552), [PR #19568](https://github.com/dotnet/fsharp/pull/19568)) * Fix signature generation: single-case struct DU gets spurious bar causing FS0300. ([Issue #19597](https://github.com/dotnet/fsharp/issues/19597), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) * Fix signature generation: backticked active pattern case names lose escaping. ([Issue #19592](https://github.com/dotnet/fsharp/issues/19592), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 234783d7f3e..634d6247bd2 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -1366,15 +1366,18 @@ and CheckILBaseCall cenv env (ilMethRef, enclTypeInst, methInst, retTypes, tyarg // Disallow calls to abstract base methods on IL types. match tryTcrefOfAppTy g baseVal.Type with | ValueSome tcref when tcref.IsILTycon -> - try - let mdef = - match tcref.ILTyconInfo with - | TILObjectReprData(scoref, _, _) -> + match tcref.ILTyconInfo with + | TILObjectReprData(scoref, _, _) -> + let baseMethodExists = + tcref.ILTyconRawMetadata.Methods.FindByName ilMethRef.Name + |> List.exists (fun x -> List.length x.Parameters = ilMethRef.ArgTypes.Length) + + if baseMethodExists then + let mdef = resolveILMethodRefWithRescope (rescopeILType scoref) tcref.ILTyconRawMetadata ilMethRef - if mdef.IsAbstract then - errorR(Error(FSComp.SR.tcCannotCallAbstractBaseMember(RichText.mkMethod mdef.Name), m)) - with _ -> () + if mdef.IsAbstract then + errorR(Error(FSComp.SR.tcCannotCallAbstractBaseMember(RichText.mkMethod mdef.Name), m)) | _ -> () CheckTypeInstNoByrefs cenv env m tyargs diff --git a/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs b/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs index 00a5781553f..4c7a0996e95 100644 --- a/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs @@ -223,3 +223,37 @@ let main _ = |> asExe |> compileExeAndRun |> shouldSucceed + + [] + let ``Issue 20264 - inherited generic base method call does not crash`` () = + let csLib = + CSharp + """ +namespace External +{ + public class Behavior + { + public virtual void OnDetaching() { } + } +} + """ + |> withName "ExternalBehavior" + + let fsLib = + FSharp + """ +module TestIssue20264 + +open External + +type MyBehavior() = + inherit Behavior() + + override _.OnDetaching() = + base.OnDetaching() + """ + |> withReferences [ csLib ] + + fsLib + |> compile + |> shouldSucceed From 2156d70db685fa7c304f4f5590ff1b45ab5ad3f3 Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Tue, 18 Aug 2026 15:20:59 +0200 Subject: [PATCH 2/2] Address review: inherit the IL method in the 20264 test, resolve via FindByNameAndArity, and keep a try/with around signature matching. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/Compiler/AbstractIL/il.fsi | 2 ++ src/Compiler/Checking/PostInferenceChecks.fs | 17 ++++++++--------- .../Interop/SimpleInteropTests.fs | 6 ++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Compiler/AbstractIL/il.fsi b/src/Compiler/AbstractIL/il.fsi index aa2e7c69b2f..0f186dc5c09 100644 --- a/src/Compiler/AbstractIL/il.fsi +++ b/src/Compiler/AbstractIL/il.fsi @@ -1262,6 +1262,8 @@ type ILMethodDefs = member FindByName: string -> ILMethodDef list + member internal FindByNameAndArity: string * int -> ILMethodDef list + member TryFindInstanceByNameAndCallingSignature: string * ILCallingSignature -> ILMethodDef option /// Field definitions. diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 634d6247bd2..f737b5f43b5 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -1368,16 +1368,15 @@ and CheckILBaseCall cenv env (ilMethRef, enclTypeInst, methInst, retTypes, tyarg | ValueSome tcref when tcref.IsILTycon -> match tcref.ILTyconInfo with | TILObjectReprData(scoref, _, _) -> - let baseMethodExists = - tcref.ILTyconRawMetadata.Methods.FindByName ilMethRef.Name - |> List.exists (fun x -> List.length x.Parameters = ilMethRef.ArgTypes.Length) - - if baseMethodExists then - let mdef = - resolveILMethodRefWithRescope (rescopeILType scoref) tcref.ILTyconRawMetadata ilMethRef + if not (isNil (tcref.ILTyconRawMetadata.Methods.FindByNameAndArity(ilMethRef.Name, ilMethRef.ArgTypes.Length))) then + try + let mdef = + resolveILMethodRefWithRescope (rescopeILType scoref) tcref.ILTyconRawMetadata ilMethRef - if mdef.IsAbstract then - errorR(Error(FSComp.SR.tcCannotCallAbstractBaseMember(RichText.mkMethod mdef.Name), m)) + if mdef.IsAbstract then + errorR(Error(FSComp.SR.tcCannotCallAbstractBaseMember(RichText.mkMethod mdef.Name), m)) + with _ -> + () | _ -> () CheckTypeInstNoByrefs cenv env m tyargs diff --git a/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs b/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs index 4c7a0996e95..e8b686f7fb8 100644 --- a/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs @@ -231,10 +231,8 @@ let main _ = """ namespace External { - public class Behavior - { - public virtual void OnDetaching() { } - } + public class BehaviorBase { public virtual void OnDetaching() { } } + public class Behavior : BehaviorBase { } } """ |> withName "ExternalBehavior"