Skip to content

Fix a hang and a crash with omitted type arguments - #494

Merged
mame merged 3 commits into
ruby:masterfrom
mame:fix-untyped-type-args
Sep 28, 2026
Merged

mame merged 3 commits into
ruby:masterfrom
mame:fix-untyped-type-args

Conversation

@mame

@mame mame commented Sep 28, 2026

Copy link
Copy Markdown
Member

Found while reviewing #480.

Hang

get_instance_type and similar places created a fresh Source.new for an omitted type argument on every call. When a box typechecks against such a type, it adds an edge from that vertex to itself, so each run adds a new edge and removes the old one. Both re-queue the box, and the analysis never terminates:

class Gen[T]
  def self.create: () -> instance
end
class Object
  def accept: (Gen[Integer]) -> String
end
class Sub < Gen; end
accept(Sub.create)

This PR uses a single shared empty Source (GlobalEnv#untyped_arg) instead. An empty Source neither holds nor propagates types, so sharing it is safe.

Crash

A generic type written without arguments in RBS (e.g., -> Gen for Gen[T]) produced an Instance with no arguments. Matching it against Gen[Integer] crashed with undefined method 'each_type' for nil. The missing arguments are now filled in with their defaults or untyped.

mame and others added 3 commits September 28, 2026 19:13
When a type argument was not given (e.g., `class Sub < Gen` in Ruby for
`Gen[T]` in RBS), get_instance_type and similar places created a fresh
`Source.new` for it on every call. A box that typechecks against such a
type adds an edge from that vertex to itself, so each run produced a
different edge; ChangeSet#reinstall then added the new edge and removed
the old one, both of which re-queue the box, and the analysis never
terminated:

    class Sub < Gen; end
    accept(Sub.create)  # accept: (Gen[Integer]) -> String

Use a single empty Source, GlobalEnv#untyped_arg, instead. An empty
Source neither holds nor propagates types, so sharing it among type
variables does not leak types between unrelated places, and the edges
and Instance types built from it stay the same across runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A generic class or interface written without type arguments in RBS
(e.g., `def gen: () -> Gen` for `Gen[T]`) made an Instance type with no
arguments, and matching it against `Gen[Integer]` passed nil as the
actual argument, which crashed with "undefined method 'each_type' for
nil". Fill in the missing arguments with their default types or
untyped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mame
mame enabled auto-merge (rebase) September 28, 2026 13:12
@mame
mame merged commit 95b7d7a into ruby:master Sep 28, 2026
6 checks passed
@mame
mame deleted the fix-untyped-type-args branch September 28, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant