Module: Rigor::Inference::MethodDispatcher
- Defined in:
- lib/rigor/inference/method_dispatcher.rb,
lib/rigor/inference/method_dispatcher/file_folding.rb,
lib/rigor/inference/method_dispatcher/rbs_dispatch.rb,
lib/rigor/inference/method_dispatcher/shape_dispatch.rb,
lib/rigor/inference/method_dispatcher/constant_folding.rb,
lib/rigor/inference/method_dispatcher/iterator_dispatch.rb,
lib/rigor/inference/method_dispatcher/overload_selector.rb
Overview
Coordinates method dispatch for the inference engine.
Given ‘(receiver_type, method_name, arg_types, block_type, environment)`, the dispatcher returns the inferred result type or `nil` when no rule matches. `nil` is a deliberately blunt “I don’t know” signal: callers (today only ‘ExpressionTyper`) own the fail-soft fallback and decide whether to record a `FallbackTracer` event.
Tiers (in order):
-
ConstantFolding: executes the Ruby operation directly when the receiver and argument are ‘Constant` carriers and the method is on the curated whitelist. Slice 2.
-
ShapeDispatch: returns the precise element/value type for a curated catalogue of ‘Tuple`/`HashShape` element-access methods (`first`, `last`, `[]` with a static integer/key, `fetch`, `dig`, `size`/`length`/`count`). Slice 5 phase 2.
-
RbsDispatch: looks up the receiver’s class in the RBS environment carried by the scope and translates the method’s return type into a Rigor::Type. Slice 4.
‘ShapeDispatch` deliberately runs above RbsDispatch so the precise per-position/per-key answer wins over the projected `Array#[]`/`Hash#fetch` answer; it falls through (`nil`) when the call cannot be proved against the static shape, in which case the projection answer from RbsDispatch applies.
The dispatcher’s public signature reserves space for ‘block_type:` and ADR-2 plugin extensions (later slices), so call sites added now do not have to be rewritten when those tiers arrive.
Defined Under Namespace
Modules: ConstantFolding, FileFolding, IteratorDispatch, OverloadSelector, RbsDispatch, ShapeDispatch
Class Method Summary collapse
- .constant_metaclass(value) ⇒ Object
-
.dispatch(receiver_type:, method_name:, arg_types:, block_type: nil, environment: nil) ⇒ Rigor::Type?
Inferred result type, or ‘nil` for “no rule”.
-
.dispatch_precise_tiers(receiver_type, method_name, arg_types) ⇒ Object
Runs the precision tiers (constant fold, shape dispatch, file-path fold) in order and returns the first non-nil answer.
-
.expected_block_param_types(receiver_type:, method_name:, arg_types:, environment: nil) ⇒ Array<Rigor::Type>
Returns the positional block parameter types declared by the receiving method’s selected RBS overload, translated into ‘Rigor::Type`.
- .meta_class(receiver_type) ⇒ Object
- .meta_new(receiver_type) ⇒ Object
-
.try_meta_introspection(receiver_type, method_name) ⇒ Object
Slice 7 phase 8 — meta-introspection shortcuts.
- .try_user_class_fallback(receiver_type, method_name, arg_types, environment, block_type) ⇒ Object
- .user_class_fallback_receiver(receiver_type, environment) ⇒ Object
Class Method Details
.constant_metaclass(value) ⇒ Object
176 177 178 179 180 181 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 176 def (value) CONSTANT_METACLASSES.each do |klass, name| return Type::Combinator.singleton_of(name) if value.is_a?(klass) end nil end |
.dispatch(receiver_type:, method_name:, arg_types:, block_type: nil, environment: nil) ⇒ Rigor::Type?
Returns inferred result type, or ‘nil` for “no rule”.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 58 def dispatch(receiver_type:, method_name:, arg_types:, block_type: nil, environment: nil) return nil if receiver_type.nil? precise = dispatch_precise_tiers(receiver_type, method_name, arg_types) return precise if precise rbs_result = RbsDispatch.try_dispatch( receiver: receiver_type, method_name: method_name, args: arg_types, environment: environment, block_type: block_type ) return rbs_result if rbs_result # Slice 7 phase 10 — user-class ancestor fallback. When # the receiver is `Nominal[T]` or `Singleton[T]` for a # class not in the RBS environment (typically a # user-defined class), retry the dispatch against the # implicit ancestor: `Nominal[Object]` for instance # receivers and `Singleton[Object]` for singleton # receivers. This resolves Kernel intrinsics # (`require`, `raise`, `puts`, ...) and Module/Class # introspection (`attr_reader`, `private`, ...) on # user classes without requiring the user to author # their own RBS. try_user_class_fallback(receiver_type, method_name, arg_types, environment, block_type) end |
.dispatch_precise_tiers(receiver_type, method_name, arg_types) ⇒ Object
Runs the precision tiers (constant fold, shape dispatch, file-path fold) in order and returns the first non-nil answer. Each tier owns its own receiver/argument shape checks; a tier that does not recognise the receiver returns nil so the next tier can try. The RBS tier sits below this chain and is invoked by the outer ‘dispatch` method.
90 91 92 93 94 95 96 97 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 90 def dispatch_precise_tiers(receiver_type, method_name, arg_types) = (receiver_type, method_name) return if ConstantFolding.try_fold(receiver: receiver_type, method_name: method_name, args: arg_types) || ShapeDispatch.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) || FileFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) end |
.expected_block_param_types(receiver_type:, method_name:, arg_types:, environment: nil) ⇒ Array<Rigor::Type>
Returns the positional block parameter types declared by the receiving method’s selected RBS overload, translated into ‘Rigor::Type`. Used by the StatementEvaluator’s CallNode handler to bind block parameter names before evaluating the block body.
The probe is best-effort: it returns an empty array whenever the receiver, environment, method definition, or selected overload does not provide statically declared block parameter types. Callers MUST treat the empty array as “no information”; the binder falls back to ‘Dynamic` for every parameter slot in that case.
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 201 def expected_block_param_types(receiver_type:, method_name:, arg_types:, environment: nil) return [] if receiver_type.nil? iterator_result = IteratorDispatch.block_param_types( receiver: receiver_type, method_name: method_name, args: arg_types ) return iterator_result if iterator_result RbsDispatch.block_param_types( receiver: receiver_type, method_name: method_name, args: arg_types, environment: environment ) end |
.meta_class(receiver_type) ⇒ Object
149 150 151 152 153 154 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 149 def (receiver_type) case receiver_type when Type::Nominal then Type::Combinator.singleton_of(receiver_type.class_name) when Type::Constant then (receiver_type.value) end end |
.meta_new(receiver_type) ⇒ Object
162 163 164 165 166 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 162 def (receiver_type) return nil unless receiver_type.is_a?(Type::Singleton) Type::Combinator.nominal_of(receiver_type.class_name) end |
.try_meta_introspection(receiver_type, method_name) ⇒ Object
Slice 7 phase 8 — meta-introspection shortcuts. The default ‘Object#class` RBS return type is `Class`, but for a receiver of known nominal identity we can do better: `instance_of(Foo).class` is `Singleton` (the class object itself), which downstream dispatch uses to resolve `self.class.some_class_method`. The same logic answers `Foo.class` as `Singleton` (deliberate; calling `.class` on a class object yields `Class`, the metaclass). We also special-case `is_a?`- adjacent calls and the trivial `instance_of?(self)` later as the rule catalogue grows; for now only `class` is handled.
142 143 144 145 146 147 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 142 def (receiver_type, method_name) case method_name when :class then (receiver_type) when :new then (receiver_type) end end |
.try_user_class_fallback(receiver_type, method_name, arg_types, environment, block_type) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 99 def try_user_class_fallback(receiver_type, method_name, arg_types, environment, block_type) return nil if environment.nil? fallback_receiver = user_class_fallback_receiver(receiver_type, environment) return nil if fallback_receiver.nil? RbsDispatch.try_dispatch( receiver: fallback_receiver, method_name: method_name, args: arg_types, environment: environment, block_type: block_type ) end |
.user_class_fallback_receiver(receiver_type, environment) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 114 def user_class_fallback_receiver(receiver_type, environment) loader = environment.rbs_loader return nil if loader.nil? case receiver_type when Type::Nominal return nil if loader.class_known?(receiver_type.class_name) environment.nominal_for_name("Object") when Type::Singleton return nil if loader.class_known?(receiver_type.class_name) environment.singleton_for_name("Class") end end |