Repository: soutaro/steep (Ruby)

Issue:
Steep 1.7.0 stuck when running check
After upgrading to 1.7.0 the `check` command will stuck when getting this error message

<details>

```
[Steep 1.7.0] [typecheck:typecheck@0] [background] Unexpected error: #<RuntimeError: self_type cannot include 'instance' type: ::FFI::_DataConverter[::FFI::Pointer, instance, untyped]>
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/interface/builder.rb:40:in `validate_fvs'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/interface/builder.rb:27:in `validate'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/interface/builder.rb:13:in `initialize'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:413:in `new'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:413:in `block (2 levels) in check_type0'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/relation.rb:89:in `map'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:410:in `block in check_type0'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/result.rb:57:in `initialize'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/result.rb:301:in `new'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/result.rb:301:in `Expand'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:408:in `check_type0'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:209:in `block (2 levels) in check_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:37:in `push_assumption'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:208:in `block in check_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:135:in `block in tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:39:in `tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:135:in `tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:198:in `check_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:187:in `block in check'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:26:in `with_context'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:186:in `check'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:75:in `block in validate_type_application_constraints'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:66:in `each'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:66:in `validate_type_application_constraints'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:127:in `validate_type_application'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:140:in `validate_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:229:in `block (3 levels) in validate_definition_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/types.rb:1040:in `each_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/method_type.rb:86:in `each_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:228:in `block (2 levels) in validate_definition_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/subtyping/check.rb:53:in `push_variable_bounds'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:227:in `block in validate_definition_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:199:in `block (2 levels) in each_method_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:198:in `each'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:198:in `block in each_method_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:196:in `each_value'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:196:in `each_method_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:222:in `validate_definition_type'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:389:in `block (3 levels) in validate_one_class_decl'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   <internal:kernel>:90:in `tap'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:301:in `block (2 levels) in validate_one_class_decl'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:135:in `block in tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:39:in `tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:135:in `tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:243:in `block in validate_one_class_decl'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:556:in `rescue_validation_errors'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:240:in `validate_one_class_decl'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/signature/validator.rb:400:in `validate_one_class'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/services/type_check_service.rb:256:in `block (6 levels) in validate_signature'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep.rb:221:in `sample'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/services/type_check_service.rb:253:in `block (5 levels) in validate_signature'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/3.3.0/set.rb:501:in `each_key'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/3.3.0/set.rb:501:in `each'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/services/type_check_service.rb:252:in `block (4 levels) in validate_signature'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep.rb:256:in `measure2'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/services/type_check_service.rb:251:in `block (3 levels) in validate_signature'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/services/type_check_service.rb:225:in `each'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/services/type_check_service.rb:225:in `block (2 levels) in validate_signature'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep.rb:192:in `measure'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/services/type_check_service.rb:221:in `block in validate_signature'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:135:in `block in tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:39:in `tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:135:in `tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/services/type_check_service.rb:220:in `validate_signature'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/server/type_check_worker.rb:187:in `handle_job'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/server/base_worker.rb:56:in `block (2 levels) in run'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:135:in `block in tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:39:in `tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.4/lib/active_support/tagged_logging.rb:135:in `tagged'
[Steep 1.7.0] [typecheck:typecheck@0] [background]   /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/server/base_worker.rb:46:in `block in run'
```

</details>

Pressed `Ctrl + C` then got this

```
/Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/server/base_worker.rb:91:in `join': No live threads left. Deadlock? (fatal)
2 threads, 2 sleeps current:0x000000013d604ea0 main thread:0x000000013d604ea0
* #<Thread:0x00000001003ab320 sleep_forever>
   rb_thread_t:0x000000013d604ea0 native:0x000000020454fac0 int:0

* #<Thread:0x0000000138cf0588 /Users/elct9620/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/steep-1.7.0/lib/steep/server/base_worker.rb:42 sleep_forever>
   rb_thread_t:0x0000000100bedc10 native:0x000000016fcab000 int:0
    depended by: tb_thread_id:0x000000013d604ea0
```

* Ruby version: 3.3.0
* Platform: Apple M3 (macOS 14.4.1)

> Tried `steep check --jobs 1` still stuck

It can be reproduced in this repository:
https://github.com/elct9620/hanami-lambda

* Remove `gem "steep"` in `Gemfile`
* Add `spec.add_development_dependency "steep"` to upgrade to `1.7.0`
* Run `rbs collection update`
* Run `steep check`


Discussion:
Confirmed. I'm working to fix the `self_type cannot include 'instance' type` error (by delaying the validation) but type checking FFI still reports some problems.

```
sig/sig-ffi/ffi/auto_pointer.rbs:15:65: [error] Type application of `::FFI::AutoPointer::Releaser::_Proc` doesn't satisfy the constraints: self <: ::FFI::Pointer
│ Diagnostic ID: RBS::UnsatisfiableTypeApplication
│
└     def initialize: (Pointer pointer, Method | ^(self) -> void | Releaser::_Proc[self] callable) -> self
                                                                   ~~~~~~~~~~~~~~~~~~~~~

sig/sig-ffi/ffi/struct.rbs:23:29: [error] Type application of `::FFI::Type::Mapped` doesn't satisfy the constraints: ::FFI::Struct::ManagedStructConverter[::FFI::ManagedStruct[::FFI::AutoPointer, untyped], ::FFI::AutoPointer] <: ::FFI::_DataConverter[::FFI::Pointer, instance, untyped]
│ Diagnostic ID: RBS::UnsatisfiableTypeApplication
│
└     def self.auto_ptr: () -> Type::Mapped[
                               ~~~~~~~~~~~~~

sig/sig-ffi/ffi/struct.rbs:5:15: [error] Type application of `::FFI::Type::Mapped` doesn't satisfy the constraints: ::FFI::StructByReference[::FFI::Struct[::FFI::AbstractMemory, untyped], ::FFI::AbstractMemory] <: ::FFI::_DataConverter[::FFI::AbstractMemory, instance, untyped]
│ Diagnostic ID: RBS::UnsatisfiableTypeApplication
│
└     type ptr = Type::Mapped[
                 ~~~~~~~~~~~~~

sig/sig-ffi/ffi/library.rbs:36:45: [error] Type `::FFI::DataConverter` is generic but used as a non generic type
│ Diagnostic ID: RBS::InvalidTypeApplication
│
└     def typedef: [T < Type] (T old, Symbol | DataConverter add, ?untyped) -> T
```

(Moved the RBS files from the gem package to `sig` dir for quick testing.)

@ParadoxV5 Do you think the errors are by some bugs of Steep? Or, it may be because of the type definition itself? (The last `DataConverter` problem should be a true problem in the type definition.)
Re: FFI problems:
* ffi/ffi#1107
  * (Likely) depends on #916
  * Relates to ffi/ffi#1108
This problem is `self` type in `#initialize` method. Using `instance` type in the method type solves the problem.

```
sig/sig-ffi/ffi/auto_pointer.rbs:15:65: [error] Type application of `::FFI::AutoPointer::Releaser::_Proc` doesn't satisfy the constraints: self <: ::FFI::Pointer
│ Diagnostic ID: RBS::UnsatisfiableTypeApplication
│
└     def initialize: (Pointer pointer, Method | ^(self) -> void | Releaser::_Proc[self] callable) -> self
                                                                   ~~~~~~~~~~~~~~~~~~~~~
```

The `#initialize` type is copied to `.new` method type, which means the `self` type also appears different context, `instance` for instance methods, `class` for singleton methods.

We should prohibit using `self` type in `#initialize` method types to avoid this confusion.
> The `#initialize` type is copied to `.new` method type, which means the `self` type also appears different context, `instance` for instance methods, `class` for singleton methods.

We should clarify this context-dependent behavior in [the RBS docs](https://github.com/ruby/rbs/blob/master/docs/syntax.md#base-types).

> We should prohibit using `self` type in `#initialize` method types to avoid this confusion.

No. We should automatically adapt uses of `self` type in `#initialize` to remedy this.

`self` and `instance` have differences, even if only intuitively but not implemented.
```ruby
o = Object.new.extend Comparable #: Object & Comparable
o.dup.is_a? Comparable #=> false (instance ➡ Object)
o.itself.is_a? Comparable #=> true (self ➡ Object & Comparable)
```
Released Steep 1.7.1, that fixes the issue.

Type checking FFI type definition still fails, and I think these are the problem of the type definition itself.

The `self` types in `initialize` method type causes issues as mentioned above.

```
sig/sig-ffi/ffi/struct.rbs:23:29: [error] Type application of `::FFI::Type::Mapped` doesn't satisfy the constraints: ::FFI::Struct::ManagedStructConverter[::FFI::ManagedStruct[::FFI::AutoPointer, untyped], ::FFI::AutoPointer] <: ::FFI::_DataConverter[::FFI::Pointer, instance, untyped]
│ Diagnostic ID: RBS::UnsatisfiableTypeApplication
│
└     def self.auto_ptr: () -> Type::Mapped[
                               ~~~~~~~~~~~~~
```

This error still reports because of `Struct[...] <: ManagedStruct[...]`, which doesn't hold. (Using `ManagedStruct[AutoPointer, untyped]` instead of `instance` solves the type error, but I'm not sure if it's something the FFI gem wants.)



Patch:
diff --git a/doc/shape.md b/doc/shape.md
index 03b021e41..6d784964d 100644
--- a/doc/shape.md
+++ b/doc/shape.md
@@ -24,7 +24,9 @@ Shape (_Foo) {
 Note that the `self` type in the example is resolved to `_Foo` during shape calculation.
 
 The shape calculation of an object is straightforward. Calculate a `RBS::Definition` of a class singleton/instance, or an interface, and translate the data structure to a `Shape` object. But there are a few things to consider.
+
 ## Tuple, record, and proc types
+
 The shape of tuple, record, or proc types are based on their base types -- Array, Hash, or Proc classes --, but with specialized method types.
 
 ```
@@ -37,16 +39,22 @@ Shape ([Integer, String]) {
 ```
 
 The specialization is implemented as a part of shape calculation.
+
 ## Special methods
+
 Steep recognizes some special methods for type narrowing, including `#is_a?`, `#===`, `#nil?`, ... These methods are defined with normal RBS syntax, but the method types in shapes are transformed to types using logic types.
 
 The shape calculation inserts the specialized methods with these special methods.
+
 ## `self` types
+
 There are two cases of `self` types to consider during shape calculation.
 
 1. `self` types included in the shape of a type
 2. `self` types included in given types
+
 ### 1. `self` types included in the shape of a type
+
 `self` types may be included in a class or interface definition.
 
 ```rbs
@@ -62,7 +70,9 @@ Shape (_Foo) {
   itself: () -> _Foo
 }
 ```
+
 ### 2. `self` types included in given types
+
 Unlike `self` types included in definitions, `self` types in given types should be preserved.
 
 ```rbs
@@ -100,7 +110,9 @@ end
 ```
 
 We want the type of `foo.get` to be `self`, not `Foo`, to avoid a type error being detected.
+
 ## Shape of `self` types
+
 We also want `self` type if `self` is the type of the shape.
 
 ```rb
@@ -154,7 +166,9 @@ Shape (Foo | Bar) {
 So, the resulting type of `self.foo` where the type of `self` is `Foo | Bar`, would be `Integer | Foo | Bar`. But, actually, it won't be `Foo` because the `self` comes from `Bar`.
 
 This is an incorrect result, but Steep is doing this right now.
+
 ## `class` and `instance` types
+
 The shape calculation provides limited support for `class` and `instance` types.
 
 1. `class`/`instance` types from the definition are resolved
@@ -162,13 +176,17 @@ The shape calculation provides limited support for `class` and `instance` types.
 3. Shape of `class`/`instance` types are resolved to configuration's `class_type` and `instance_type`, and the translated types are used to calculate the shape
 
 It's different from `self` types except case #2. The relationship between `self`/`class`/`instance` is not trivial in Ruby. All of them might be resolved to any type, which means calculating one from another of them is simply impossible.
+
 ## Public methods, private methods
+
 `Shape` objects have a flag of if the shape is for *public* method calls or *private* method calls. Private method call is a form of `foo()` or `self.foo()` -- when the receiver is omitted or `self`. Public method calls are anything else.
 
 The shape calculation starts with *private methods*, and the `Shape#public_shape` method returns another shape that only has *public* methods.
 
 > Note that the private shape calculation is required even on public method calls. This means a possible chance of future optimizations.
+
 ## Lazy method type calculation
+
 We rarely need all of the methods available for an object. If we want to type check a method call, we only need the method type of that method. All other methods can be just ignored.
 
 *Lazy method type calculation* is introduced for that case. Instead of calculating the types of all of the methods, it registers a block that computes the method type.
diff --git a/lib/steep/interface/builder.rb b/lib/steep/interface/builder.rb
index 4d5933300..859332b33 100644
--- a/lib/steep/interface/builder.rb
+++ b/lib/steep/interface/builder.rb
@@ -9,8 +9,6 @@ def initialize(self_type:, class_type: nil, instance_type: nil, variable_bounds:
           @class_type = class_type
           @instance_type = instance_type
           @variable_bounds = variable_bounds
-
-          validate
         end
 
         def self.empty
@@ -23,11 +21,16 @@ def subst
           end
         end
 
-        def validate
+        def validate_self_type
           validate_fvs(:self_type, self_type)
+        end
+
+        def validate_instance_type
           validate_fvs(:instance_type, instance_type)
+        end
+
+        def validate_class_type
           validate_fvs(:class_type, class_type)
-          self
         end
 
         def validate_fvs(name, type)
@@ -37,6 +40,7 @@ def validate_fvs(name, type)
               raise "#{name} cannot include 'self' type: #{type}"
             end
             if fvs.include?(AST::Types::Instance.instance)
+              Steep.logger.fatal { "#{name} cannot include 'instance' type: #{type}" }
               raise "#{name} cannot include 'instance' type: #{type}"
             end
             if fvs.include?(AST::Types::Class.instance)
@@ -87,12 +91,15 @@ def fetch_cache(cache, key)
       def raw_shape(type, config)
         case type
         when AST::Types::Self
+          config.validate_self_type
           self_type = config.self_type or raise
           self_shape(self_type, config)
         when AST::Types::Instance
+          config.validate_instance_type
           instance_type = config.instance_type or raise
           raw_shape(instance_type, config)
         when AST::Types::Class
+          config.validate_class_type
           klass_type = config.class_type or raise
           raw_shape(klass_type, config)
         when AST::Types::Name::Singleton
diff --git a/lib/steep/signature/validator.rb b/lib/steep/signature/validator.rb
index 22e9a881d..354d0201e 100644
--- a/lib/steep/signature/validator.rb
+++ b/lib/steep/signature/validator.rb
@@ -5,10 +5,23 @@ class Validator
       Declarations = RBS::AST::Declarations
 
       attr_reader :checker
+      attr_reader :context
 
       def initialize(checker:)
         @checker = checker
         @errors = []
+        @context = []
+      end
+
+      def push_context(self_type: latest_context[0], class_type: latest_context[1], instance_type: latest_context[2])
+        @context.push([self_type, class_type, instance_type])
+        yield
+      ensure
+        @context.pop
+      end
+
+      def latest_context
+        context.last || [nil, nil, nil]
       end
 
       def has_error?
@@ -72,11 +85,13 @@ def validate_type_application_constraints(type_name, type_params, type_args, loc
 
               constraints = Subtyping::Constraints.empty
 
+              self_type, class_type, instance_type = latest_context
+
               checker.check(
                 Subtyping::Relation.new(sub_type: arg_type, super_type: upper_bound_type),
-                self_type: AST::Types::Self.instance,
-                class_type: nil,
-                instance_type: nil,
+                self_type: self_type,
+                class_type: class_type,
+                instance_type: instance_type,
                 constraints: constraints
               ).else do |result|
                 @errors << Diagnostic::Signature::UnsatisfiableTypeApplication.new(
@@ -236,25 +251,113 @@ def validate_definition_type(definition)
         end
       end
 
-      def validate_one_class_decl(name)
+      def validate_one_class_decl(name, entry)
         rescue_validation_errors(name) do
           Steep.logger.debug { "Validating class definition `#{name}`..." }
 
+          class_type = AST::Types::Name::Singleton.new(name: name, location: nil)
+          instance_type = AST::Types::Name::Instance.new(
+            name: name,
+            args: entry.type_params.map { AST::Types::Any.new(location: nil) },
+            location: nil
+          )
+
           Steep.logger.tagged "#{name}" do
             builder.build_instance(name).tap do |definition|
               upper_bounds = definition.type_params_decl.each.with_object({}) do |param, bounds|
                 bounds[param.name] = factory.type_opt(param.upper_bound)
               end
 
-              checker.push_variable_bounds(upper_bounds) do
+              self_type = AST::Types::Name::Instance.new(
+                name: name,
+                args: entry.type_params.map { AST::Types::Var.new(name: _1.name) },
+                location: nil
+              )
+
+              push_context(self_type: self_type, class_type: class_type, instance_type: instance_type) do
+                checker.push_variable_bounds(upper_bounds) do
+                  definition.instance_variables.each do |name, var|
+                    if parent = var.parent_variable
+                      var_type = checker.factory.type(var.type)
+                      parent_type = checker.factory.type(parent.type)
+
+                      relation = Subtyping::Relation.new(sub_type: var_type, super_type: parent_type)
+                      result1 = checker.check(relation, self_type: nil, instance_type: nil, class_type: nil, constraints: Subtyping::Constraints.empty)
+                      result2 = checker.check(relation.flip, self_type: nil, instance_type: nil, class_type: nil, constraints: Subtyping::Constraints.empty)
+
+                      unless result1.success? and result2.success?
+                        @errors << Diagnostic::Signature::InstanceVariableTypeError.new(
+                          name: name,
+                          location: var.type.location,
+                          var_type: var_type,
+                          parent_type: parent_type
+                        )
+                      end
+                    end
+                  end
+
+                  ancestors = builder.ancestor_builder.one_instance_ancestors(name)
+                  mixin_constraints(definition, ancestors.included_modules || raise, immediate_self_types: ancestors.self_types).each do |relation, ancestor|
+                    checker.check(
+                      relation,
+                      self_type: AST::Types::Self.instance,
+                      instance_type: AST::Types::Instance.instance,
+                      class_type: AST::Types::Class.instance,
+                      constraints: Subtyping::Constraints.empty
+                    ).else do
+                      raise if ancestor.source.is_a?(Symbol)
+
+                      @errors << Diagnostic::Signature::ModuleSelfTypeError.new(
+                        name: name,
+                        location: ancestor.source&.location || raise,
+                        ancestor: ancestor,
+                        relation: relation
+                      )
+                    end
+                  end
+
+                  ancestors.each_ancestor do |ancestor|
+                    case ancestor
+                    when RBS::Definition::Ancestor::Instance
+                      validate_ancestor_application(name, ancestor)
+                    end
+                  end
+
+                  validate_definition_type(definition)
+                end
+              end
+            end
+
+            builder.build_singleton(name).tap do |definition|
+              entry =
+                case definition.entry
+                when RBS::Environment::ClassEntry, RBS::Environment::ModuleEntry
+                  definition.entry
+                else
+                  raise
+                end
+
+              push_context(self_type: class_type, class_type: class_type, instance_type: instance_type) do
                 definition.instance_variables.each do |name, var|
                   if parent = var.parent_variable
                     var_type = checker.factory.type(var.type)
                     parent_type = checker.factory.type(parent.type)
 
                     relation = Subtyping::Relation.new(sub_type: var_type, super_type: parent_type)
-                    result1 = checker.check(relation, self_type: nil, instance_type: nil, class_type: nil, constraints: Subtyping::Constraints.empty)
-                    result2 = checker.check(relation.flip, self_type: nil, instance_type: nil, class_type: nil, constraints: Subtyping::Constraints.empty)
+                    result1 = checker.check(
+                      relation,
+                      self_type: AST::Types::Self.instance,
+                      instance_type: AST::Types::Instance.instance,
+                      class_type: AST::Types::Class.instance,
+                      constraints: Subtyping::Constraints.empty
+                    )
+                    result2 = checker.check(
+                      relation.flip,
+                      self_type: AST::Types::Self.instance,
+                      instance_type: AST::Types::Instance.instance,
+                      class_type: AST::Types::Class.instance,
+                      constraints: Subtyping::Constraints.empty
+                    )
 
                     unless result1.success? and result2.success?
                       @errors << Diagnostic::Signature::InstanceVariableTypeError.new(
@@ -267,11 +370,32 @@ def validate_one_class_decl(name)
                   end
                 end
 
-                ancestors = builder.ancestor_builder.one_instance_ancestors(name)
-                mixin_constraints(definition, ancestors.included_modules || raise, immediate_self_types: ancestors.self_types).each do |relation, ancestor|
+                definition.class_variables.each do |name, var|
+                  if var.declared_in == definition.type_name
+                    if (parent = var.parent_variable) && var.declared_in != parent.declared_in
+                      class_var = entry.decls.flat_map {|decl| decl.decl.members }.find do |member|
+                        member.is_a?(RBS::AST::Members::ClassVariable) && member.name == name
+                      end
+
+                      if class_var
+                        loc = class_var.location #: RBS::Location[untyped, untyped]?
+                        @errors << Diagnostic::Signature::ClassVariableDuplicationError.new(
+                          class_name: definition.type_name,
+                          other_class_name: parent.declared_in,
+                          variable_name: name,
+                          location: loc&.[](:name) || raise
+                        )
+                      end
+                    end
+                  end
+                end
+
+                ancestors = builder.ancestor_builder.one_singleton_ancestors(name)
+                ancestors.extended_modules or raise
+                mixin_constraints(definition, ancestors.extended_modules, immediate_self_types: ancestors.self_types).each do |relation, ancestor|
                   checker.check(
                     relation,
-                    self_type: AST::Types::Self.instance,
+                    self_type: AST::Types::Self.instance ,
                     instance_type: AST::Types::Instance.instance,
                     class_type: AST::Types::Class.instance,
                     constraints: Subtyping::Constraints.empty
@@ -286,7 +410,6 @@ def validate_one_class_decl(name)
                     )
                   end
                 end
-
                 ancestors.each_ancestor do |ancestor|
                   case ancestor
                   when RBS::Definition::Ancestor::Instance
@@ -297,97 +420,6 @@ def validate_one_class_decl(name)
                 validate_definition_type(definition)
               end
             end
-
-            builder.build_singleton(name).tap do |definition|
-              entry =
-                case definition.entry
-                when RBS::Environment::ClassEntry, RBS::Environment::ModuleEntry
-                  definition.entry
-                else
-                  raise
-                end
-
-              definition.instance_variables.each do |name, var|
-                if parent = var.parent_variable
-                  var_type = checker.factory.type(var.type)
-                  parent_type = checker.factory.type(parent.type)
-
-                  relation = Subtyping::Relation.new(sub_type: var_type, super_type: parent_type)
-                  result1 = checker.check(
-                    relation,
-                    self_type: AST::Types::Self.instance,
-                    instance_type: AST::Types::Instance.instance,
-                    class_type: AST::Types::Class.instance,
-                    constraints: Subtyping::Constraints.empty
-                  )
-                  result2 = checker.check(
-                    relation.flip,
-                    self_type: AST::Types::Self.instance,
-                    instance_type: AST::Types::Instance.instance,
-                    class_type: AST::Types::Class.instance,
-                    constraints: Subtyping::Constraints.empty
-                  )
-
-                  unless result1.success? and result2.success?
-                    @errors << Diagnostic::Signature::InstanceVariableTypeError.new(
-                      name: name,
-                      location: var.type.location,
-                      var_type: var_type,
-                      parent_type: parent_type
-                    )
-                  end
-                end
-              end
-
-              definition.class_variables.each do |name, var|
-                if var.declared_in == definition.type_name
-                  if (parent = var.parent_variable) && var.declared_in != parent.declared_in
-                    class_var = entry.decls.flat_map {|decl| decl.decl.members }.find do |member|
-                      member.is_a?(RBS::AST::Members::ClassVariable) && member.name == name
-                    end
-
-                    if class_var
-                      loc = class_var.location #: RBS::Location[untyped, untyped]?
-                      @errors << Diagnostic::Signature::ClassVariableDuplicationError.new(
-                        class_name: definition.type_name,
-                        other_class_name: parent.declared_in,
-                        variable_name: name,
-                        location: loc&.[](:name) || raise
-                      )
-                    end
-                  end
-                end
-              end
-
-              ancestors = builder.ancestor_builder.one_singleton_ancestors(name)
-              ancestors.extended_modules or raise
-              mixin_constraints(definition, ancestors.extended_modules, immediate_self_types: ancestors.self_types).each do |relation, ancestor|
-                checker.check(
-                  relation,
-                  self_type: AST::Types::Self.instance ,
-                  instance_type: AST::Types::Instance.instance,
-                  class_type: AST::Types::Class.instance,
-                  constraints: Subtyping::Constraints.empty
-                ).else do
-                  raise if ancestor.source.is_a?(Symbol)
-
-                  @errors << Diagnostic::Signature::ModuleSelfTypeError.new(
-                    name: name,
-                    location: ancestor.source&.location || raise,
-                    ancestor: ancestor,
-                    relation: relation
-                  )
-                end
-              end
-              ancestors.each_ancestor do |ancestor|
-                case ancestor
-                when RBS::Definition::Ancestor::Instance
-                  validate_ancestor_application(name, ancestor)
-                end
-              end
-
-              validate_definition_type(definition)
-            end
           end
         end
       end
@@ -397,7 +429,7 @@ def validate_one_class(name)
 
         case entry
         when RBS::Environment::ClassEntry, RBS::Environment::ModuleEntry
-          validate_one_class_decl(name)
+          validate_one_class_decl(name, entry)
         when RBS::Environment::ClassAliasEntry, RBS::Environment::ModuleAliasEntry
           validate_one_class_alias(name, entry)
         end
@@ -453,23 +485,31 @@ def validate_one_interface(name)
               bounds[param.name] = factory.type_opt(param.upper_bound)
             end
 
-            checker.push_variable_bounds(upper_bounds) do
-              validate_definition_type(definition)
-
-              ancestors = builder.ancestor_builder.one_interface_ancestors(name)
-              ancestors.each_ancestor do |ancestor|
-                case ancestor
-                when RBS::Definition::Ancestor::Instance
-                  # Interface ancestor cannot be other than Interface
-                  ancestor.source.is_a?(Symbol) and raise
-
-                  defn = builder.build_interface(ancestor.name)
-                  validate_type_application_constraints(
-                    ancestor.name,
-                    defn.type_params_decl,
-                    ancestor.args,
-                    location: ancestor.source&.location || raise
-                  )
+            self_type = AST::Types::Name::Interface.new(
+              name: name,
+              args: definition.type_params.map { AST::Types::Var.new(name: _1) },
+              location: nil
+            )
+
+            push_context(self_type: self_type, class_type: nil, instance_type: nil) do
+              checker.push_variable_bounds(upper_bounds) do
+                validate_definition_type(definition)
+
+                ancestors = builder.ancestor_builder.one_interface_ancestors(name)
+                ancestors.each_ancestor do |ancestor|
+                  case ancestor
+                  when RBS::Definition::Ancestor::Instance
+                    # Interface ancestor cannot be other than Interface
+                    ancestor.source.is_a?(Symbol) and raise
+
+                    defn = builder.build_interface(ancestor.name)
+                    validate_type_application_constraints(
+                      ancestor.name,
+                      defn.type_params_decl,
+                      ancestor.args,
+                      location: ancestor.source&.location || raise
+                    )
+                  end
                 end
               end
             end
@@ -519,21 +559,33 @@ def validate_one_global(name, entry)
       end
 
       def validate_one_alias(name, entry = env.type_alias_decls[name])
-        rescue_validation_errors(name) do
-          Steep.logger.debug "Validating alias `#{name}`..."
+        *, inner_most_outer_module = entry.outer
+        if inner_most_outer_module
+          class_type = AST::Types::Name::Singleton.new(name: inner_most_outer_module.name, location: nil)
+          instance_type = AST::Types::Name::Instance.new(
+            name: inner_most_outer_module.name,
+            args: inner_most_outer_module.type_params.map { AST::Types::Any.new(location: nil) },
+            location: nil
+          )
+        end
 
-          unless name.namespace.empty?
-            outer = name.namespace.to_type_name
-            builder.validate_type_name(outer, entry.decl.location&.aref(:name))
-          end
+        push_context(class_type: class_type, instance_type: instance_type, self_type: nil) do
+          rescue_validation_errors(name) do
+            Steep.logger.debug "Validating alias `#{name}`..."
 
-          upper_bounds = entry.decl.type_params.each.with_object({}) do |param, bounds|
-            bounds[param.name] = factory.type_opt(param.upper_bound)
-          end
+            unless name.namespace.empty?
+              outer = name.namespace.to_type_name
+              builder.validate_type_name(outer, entry.decl.location&.aref(:name))
+            end
+
+            upper_bounds = entry.decl.type_params.each.with_object({}) do |param, bounds|
+              bounds[param.name] = factory.type_opt(param.upper_bound)
+            end
 
-          validator.validate_type_alias(entry: entry) do |type|
-            checker.push_variable_bounds(upper_bounds) do
-              validate_type(entry.decl.type)
+            validator.validate_type_alias(entry: entry) do |type|
+              checker.push_variable_bounds(upper_bounds) do
+                validate_type(entry.decl.type)
+              end
             end
           end
         end
diff --git a/lib/steep/subtyping/check.rb b/lib/steep/subtyping/check.rb
index 11a813fbd..5dff60815 100644
--- a/lib/steep/subtyping/check.rb
+++ b/lib/steep/subtyping/check.rb
@@ -798,33 +798,37 @@ def check_constraints(relation, variables:, variance:)
       end
 
       def check_method_type(name, relation)
-        relation.method!
+        Steep.logger.tagged "#{name} : #{relation.sub_type} <: #{relation.super_type}" do
+          relation.method!
 
-        sub_type, super_type = relation
+          sub_type, super_type = relation
 
-        sub_type.type_params.empty? or raise "Expected monomorphic method type: #{sub_type}"
-        super_type.type_params.empty? or raise "Expected monomorphic method type: #{super_type}"
+          sub_type.type_params.empty? or raise "Expected monomorphic method type: #{sub_type}"
+          super_type.type_params.empty? or raise "Expected monomorphic method type: #{super_type}"
 
-        All(relation) do |result|
-          type_relation = Relation.new(sub_type: sub_type.type, super_type: super_type.type)
-
-          ret = expand_block_given(name, Relation.new(sub_type: sub_type.block, super_type: super_type.block))
-
-          case ret
-          when true
-            result.add(type_relation) { check_function(name, type_relation) }
-          when Relation
-            result.add(type_relation) { check_function(name, type_relation) }
-            result.add(ret) do
-              All(ret) do |result|
-                result.add_result(check_self_type_binding(ret, ret.super_type.self_type, ret.sub_type.self_type))
-                result.add(Relation(ret.super_type.type, ret.sub_type.type)) do |block_relation|
-                  check_function(name, block_relation)
+          All(relation) do |result|
+            type_relation = Relation.new(sub_type: sub_type.type, super_type: super_type.type)
+
+            ret = expand_block_given(name, Relation.new(sub_type: sub_type.block, super_type: super_type.block))
+
+            case ret
+            when true
+              result.add(type_relation) { check_function(name, type_relation) }
+            when Relation
+              result.add(type_relation) { check_function(name, type_relation) }
+              result.add(ret) do
+                All(ret) do |result|
+                  result.add_result(check_self_type_binding(ret, ret.super_type.self_type, ret.sub_type.self_type))
+                  result.add(Relation(ret.super_type.type, ret.sub_type.type)) do |block_relation|
+                    check_function(name, block_relation)
+                  end
                 end
               end
+            when Result::Failure
+              result.add(ret.relation) { ret }
+            end.tap do |ret|
+              Steep.logger.debug "result=#{ret.class}"
             end
-          when Result::Failure
-            result.add(ret.relation) { ret }
           end
         end
       end
diff --git a/rbs_collection.steep.yaml b/rbs_collection.steep.yaml
index 6e05ade13..54f164181 100644
--- a/rbs_collection.steep.yaml
+++ b/rbs_collection.steep.yaml
@@ -21,3 +21,5 @@ gems:
   - name: csv
   - name: pathname
   - name: securerandom
+  - name: ffi
+    ignore: true
diff --git a/sig/steep/interface/builder.rbs b/sig/steep/interface/builder.rbs
index 4915abf43..f31ef2367 100644
--- a/sig/steep/interface/builder.rbs
+++ b/sig/steep/interface/builder.rbs
@@ -28,9 +28,16 @@ module Steep
 
         def upper_bound: (Symbol) -> AST::Types::t?
 
-        private
+        # Validates `self_type` attribute, and raises an error if it's not valid
+        def validate_self_type: () -> void
+
+        # Validates `instance_type` attribute, and raises an error if it's not valid
+        def validate_instance_type: () -> void
 
-        def validate: () -> self
+        # Validates `class_type` attribute, and raises an error if it's not valid
+        def validate_class_type: () -> void
+
+        private
 
         def validate_fvs: (Symbol name, AST::Types::t?) -> void
       end
diff --git a/sig/steep/signature/validator.rbs b/sig/steep/signature/validator.rbs
index 577bd5ca7..142ade732 100644
--- a/sig/steep/signature/validator.rbs
+++ b/sig/steep/signature/validator.rbs
@@ -13,6 +13,14 @@ module Steep
 
       @validator: RBS::Validator?
 
+      # Stack of `self_type`, `class_type`, `instance_type` tuple
+      #
+      attr_reader context: Array[[AST::Types::t?, AST::Types::t?, AST::Types::t?]]
+
+      def latest_context: -> [AST::Types::t?, AST::Types::t?, AST::Types::t?]
+
+      def push_context: [T] (?self_type: AST::Types::t?, ?class_type: AST::Types::t?, ?instance_type: AST::Types::t?) { () -> T } -> T
+
       def initialize: (checker: Subtyping::Check) -> void
 
       def has_error?: () -> bool
@@ -70,10 +78,10 @@ module Steep
       #
       # 1. Make sure the outer namespace of given `name` exists
       # 2. Make sure the type alias is valid with respect to `RBS::Validator`
-      # 
+      #
       def validate_one_alias: (RBS::TypeName name, ?RBS::Environment::TypeAliasEntry entry) -> void
 
-      def validate_one_class_decl: (RBS::TypeName) -> void
+      def validate_one_class_decl: (RBS::TypeName, RBS::Environment::ClassEntry | RBS::Environment::ModuleEntry) -> void
 
       def validate_one_class_alias: (RBS::TypeName, RBS::Environment::ClassAliasEntry | RBS::Environment::ModuleAliasEntry) -> void
 


Test patch:
diff --git a/test/validation_test.rb b/test/validation_test.rb
index 0fc4d8507..b68948e09 100644
--- a/test/validation_test.rb
+++ b/test/validation_test.rb
@@ -1156,4 +1156,27 @@ module Bar = Foo
       end
     end
   end
+
+  def test_validate_type_app__classish_bounded
+    with_checker <<~RBS do |checker|
+        interface _Generic[T < Object]
+        end
+
+        class Foo < BasicObject
+          class User
+            type t1 = _Generic[instance]
+            type t2 = _Generic[class]
+
+            include _Generic[class]
+            extend _Generic[instance]
+          end
+        end
+      RBS
+
+      Validator.new(checker: checker).tap do |validator|
+        validator.validate
+        assert_predicate validator.each_error.to_a, :empty?
+      end
+    end
+  end
 end