Skip to content

Fix false positive in _final send checking for generic classes#4982

Open
SeanTAllen wants to merge 1 commit intomainfrom
fix-finaliser-generic-false-positives
Open

Fix false positive in _final send checking for generic classes#4982
SeanTAllen wants to merge 1 commit intomainfrom
fix-finaliser-generic-false-positives

Conversation

@SeanTAllen
Copy link
Member

@SeanTAllen SeanTAllen commented Mar 8, 2026

The finalisers pass uses is_known(type) to decide whether a method call might send a message. When _final calls a method on a generic class with concrete type args (like Generic[Prim]), the pass follows into the method body where expressions still reference the bare type parameter A. Since A's constraint is a trait, is_known(A) returns false and the pass conservatively reports the call might send — even though at the call site, the concrete type is fully known and clearly doesn't send anything.

Two changes:

  1. Reify the method body with the entity's concrete type arguments before analyzing it. Type parameters get replaced with their concrete types, so is_known sees Prim instead of A and correctly determines no message is sent.

  2. Replace ast_get with lookup_try for method resolution. ast_get only searches the entity's own symbol table, missing methods inherited through the provides chain.

This expands the range of generic code accepted in _final methods. Cases where the concrete type doesn't have the called method in its own scope (e.g., Range calling finite() which exists on the Real trait but not on USize) still produce false positives. The union type false positive mentioned in the issue is also not addressed here.

Closes #4249

@SeanTAllen SeanTAllen added the changelog - fixed Automatically add "Fixed" CHANGELOG entry on merge label Mar 8, 2026
@ponylang-main ponylang-main added the discuss during sync Should be discussed during an upcoming sync label Mar 8, 2026
@SeanTAllen SeanTAllen added the do not merge This PR should not be merged at this time label Mar 8, 2026
The finalisers pass conservatively reports FINAL_MAY_SEND when it
encounters a method call on an unknown type. When _final calls a method
on a generic class instantiated with concrete type args (e.g.,
Generic[Prim]), the pass follows into the method body where expressions
still reference the generic type parameter A. Since A's constraint is a
trait, is_known(A) returns false and the pass reports a false positive.

Two changes fix this:

1. Reify the method body with the entity's concrete type arguments
before analyzing it. This replaces type parameters with their concrete
types so is_known sees the actual type. The recursion guard is set on
the original body (not the reified copy) so that recursive lookups
correctly detect cycles.

2. Replace ast_get with lookup_try for method resolution. ast_get only
searches the entity's own symbol table, missing methods inherited
through the provides chain. lookup_try handles the full provides chain.

This expands the range of generic code accepted in _final methods.
Cases where the concrete type doesn't have the called method in its
own scope (e.g., Range calling finite() which exists on the Real trait
but not on USize) still fall back to FINAL_MAY_SEND conservatively.

Closes #4249
@SeanTAllen SeanTAllen force-pushed the fix-finaliser-generic-false-positives branch from 06236f9 to b32cd9f Compare March 8, 2026 22:58
@SeanTAllen SeanTAllen removed the do not merge This PR should not be merged at this time label Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog - fixed Automatically add "Fixed" CHANGELOG entry on merge discuss during sync Should be discussed during an upcoming sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positives in the type checking which identifies potential message sending classes.

2 participants