Skip to content

Fix compiler crashes when calling inherited base method on external generic IL - #20272

Open
xperiandri wants to merge 2 commits into
dotnet:mainfrom
xperiandri:fix-crashes-when-calling-inherited-base-method-on-external-generic-IL
Open

Fix compiler crashes when calling inherited base method on external generic IL#20272
xperiandri wants to merge 2 commits into
dotnet:mainfrom
xperiandri:fix-crashes-when-calling-inherited-base-method-on-external-generic-IL

Conversation

@xperiandri

Copy link
Copy Markdown
Contributor

Summary

Fixes #20264 by guarding the IL abstract-base check so it only applies when the raw metadata type actually declares the method being called. This avoids the compiler crash when the method is inherited from an external generic base type, such as Microsoft.Xaml.Interactivity.Behavior<T>. The check now skips the unsafe resolve path instead of throwing an unhandled exception.

Changes

@github-actions

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Warning

No PR link found in some release notes, please consider adding it.

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.100.md No current pull request URL (#20272) found, please consider adding it

@xperiandri xperiandri changed the title Fix inherited IL base-call crash and add release notes Fix inherited IL base-call crash Aug 17, 2026
@xperiandri xperiandri changed the title Fix inherited IL base-call crash Fix compiler crashes when calling inherited base method on external generic IL Aug 17, 2026
@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Aug 17, 2026

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, focused fix — guarding the resolve with an explicit existence check is the right shape, and it reads much better than the old blanket try/with _ -> (). Two things worth addressing before merge, one of them important.

The regression test doesn't exercise the bug

In the test, OnDetaching is declared directly on the generic Behavior<T>:

public class Behavior<T> { public virtual void OnDetaching() { } }

But the crash in #20264 only happens when the method is inherited and therefore absent from the immediate generic type's own metadata. ILTypeDef.Methods.FindByName (and the resolveILMethodRefWithRescope lookup it feeds) only searches the type's own method table — it doesn't walk the base chain. When OnDetaching sits on Behavior<T> itself, the pre-fix resolveILMethodRefWithRescope finds it and never reaches the failwith, so this test compiles cleanly with or without the fix. It passes today, but it wouldn't have failed before the patch — so it doesn't guard against regression.

To actually reproduce the reported crash, the method needs to live one level up, matching the real Behavior<T> : Behavior shape:

namespace External
{
    public class BehaviorBase { public virtual void OnDetaching() { } }
    public class Behavior<T> : BehaviorBase { }
}

With that C#, the pre-fix compiler throws no method named OnDetaching found in type ...Behavior1and the post-fix compiler succeeds — which is the behavior this PR is meant to lock in. Worth confirming the test fails onmain` before the source change lands.

Removing the try/with narrows the crash class but doesn't close it

The old code caught every failwith inside resolveILMethodRefWithRescope. The new baseMethodExists guard only prevents the first one (empty name+arity match). The two later failwiths remain reachable and are now unguarded: if a same-name/same-arity method exists on the immediate type but the full signature filter (calling convention, rescoped arg/return types, generic arity) yields [] or multiple matches — e.g. an overload whose signature differs while the genuinely-called method is inherited — resolveILMethodRefWithRescope will throw straight through. It's a narrow window, but it's the same failure mode #20264 reported. Since there's no non-throwing resolve variant, either keep a defensive try/with around the resolve, or check the full signature (not just name+arity) before calling it.

Minor

FindByName name |> List.exists (fun x -> List.length x.Parameters = argCount) is exactly not (isNil (Methods.FindByNameAndArity(name, argCount))) — the existing FindByNameAndArity helper already does the name+arity filter, so calling it keeps this in step with resolveILMethodRefWithRescope's own lookup.

One behavioral note to be aware of (not necessarily a change request): skipping the check when the method isn't on the immediate type means a call to a genuinely abstract inherited base method no longer produces tcCannotCallAbstractBaseMember. Trading a hard crash for a missing diagnostic is fine; walking the base chain (option 1 in the issue) would preserve it if that ever matters.

@xperiandri
xperiandri force-pushed the fix-crashes-when-calling-inherited-base-method-on-external-generic-IL branch from 39f5c6c to 974be18 Compare August 17, 2026 11:24
@xperiandri
xperiandri force-pushed the fix-crashes-when-calling-inherited-base-method-on-external-generic-IL branch from 974be18 to 039b15b Compare August 18, 2026 13:02
…FindByNameAndArity, and keep a try/with around signature matching.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@xperiandri
xperiandri requested a review from T-Gro August 18, 2026 13:23

if mdef.IsAbstract then
errorR(Error(FSComp.SR.tcCannotCallAbstractBaseMember(RichText.mkMethod mdef.Name), m))
with _ ->

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it a good idea. Advise what to do here

@Juriyx

Juriyx commented Aug 18, 2026

Copy link
Copy Markdown

The 20264 test now declares OnDetaching on BehaviorBase so Behavior<T> only inherits it. The IL check uses FindByNameAndArity and keeps try/with around resolveILMethodRefWithRescope for same-name/same-arity signature mismatches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

F# compiler crashes when calling inherited base method on external generic IL behavior type (OnDetaching / Behavior<T>)

3 participants