Module: Rigor::Inference::MethodDispatcher
- Defined in:
- lib/rigor/inference/method_dispatcher.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/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, 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”.
-
.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”.
56 57 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/rigor/inference/method_dispatcher.rb', line 56 def dispatch(receiver_type:, method_name:, arg_types:, block_type: nil, environment: nil) return nil if receiver_type.nil? = (receiver_type, method_name) return if constant_result = ConstantFolding.try_fold( receiver: receiver_type, method_name: method_name, args: arg_types ) return constant_result if constant_result shape_result = ShapeDispatch.try_dispatch( receiver: receiver_type, method_name: method_name, args: arg_types ) return shape_result if shape_result 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 |
.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 |
# 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? 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 |