rigortype 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +69 -56
- data/lib/rigor/analysis/buffer_binding.rb +36 -0
- data/lib/rigor/analysis/check_rules.rb +11 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +14 -1
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +105 -0
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +32 -12
- data/lib/rigor/analysis/fact_store.rb +15 -3
- data/lib/rigor/analysis/project_scan.rb +39 -0
- data/lib/rigor/analysis/result.rb +11 -3
- data/lib/rigor/analysis/run_stats.rb +193 -0
- data/lib/rigor/analysis/runner.rb +681 -19
- data/lib/rigor/analysis/worker_session.rb +339 -0
- data/lib/rigor/builtins/hkt_builtins.rb +342 -0
- data/lib/rigor/builtins/imported_refinements.rb +6 -2
- data/lib/rigor/builtins/regex_refinement.rb +17 -12
- data/lib/rigor/builtins/static_return_refinements.rb +120 -0
- data/lib/rigor/cache/rbs_descriptor.rb +3 -1
- data/lib/rigor/cache/store.rb +72 -9
- data/lib/rigor/cli/lsp_command.rb +129 -0
- data/lib/rigor/cli/type_of_command.rb +44 -5
- data/lib/rigor/cli.rb +122 -10
- data/lib/rigor/configuration.rb +168 -7
- data/lib/rigor/environment/bundle_sig_discovery.rb +198 -0
- data/lib/rigor/environment/class_registry.rb +12 -3
- data/lib/rigor/environment/hkt_registry_holder.rb +33 -0
- data/lib/rigor/environment/lockfile_resolver.rb +125 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +126 -0
- data/lib/rigor/environment/rbs_coverage_report.rb +112 -0
- data/lib/rigor/environment/rbs_loader.rb +238 -7
- data/lib/rigor/environment/reflection.rb +152 -0
- data/lib/rigor/environment/reporters.rb +40 -0
- data/lib/rigor/environment.rb +179 -10
- data/lib/rigor/inference/acceptance.rb +83 -4
- data/lib/rigor/inference/builtins/method_catalog.rb +12 -5
- data/lib/rigor/inference/builtins/numeric_catalog.rb +15 -4
- data/lib/rigor/inference/expression_typer.rb +59 -2
- data/lib/rigor/inference/hkt_body.rb +171 -0
- data/lib/rigor/inference/hkt_body_parser.rb +363 -0
- data/lib/rigor/inference/hkt_reducer.rb +256 -0
- data/lib/rigor/inference/hkt_registry.rb +223 -0
- data/lib/rigor/inference/macro_block_self_type.rb +96 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +29 -29
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -4
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +18 -1
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +126 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +87 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +46 -40
- data/lib/rigor/inference/method_dispatcher.rb +282 -6
- data/lib/rigor/inference/method_parameter_binder.rb +21 -11
- data/lib/rigor/inference/narrowing.rb +127 -8
- data/lib/rigor/inference/project_patched_methods.rb +70 -0
- data/lib/rigor/inference/project_patched_scanner.rb +210 -0
- data/lib/rigor/inference/scope_indexer.rb +156 -12
- data/lib/rigor/inference/statement_evaluator.rb +106 -6
- data/lib/rigor/inference/synthetic_method.rb +86 -0
- data/lib/rigor/inference/synthetic_method_index.rb +82 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +599 -0
- data/lib/rigor/language_server/buffer_table.rb +63 -0
- data/lib/rigor/language_server/completion_provider.rb +438 -0
- data/lib/rigor/language_server/debouncer.rb +86 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +167 -0
- data/lib/rigor/language_server/document_symbol_provider.rb +142 -0
- data/lib/rigor/language_server/folding_range_provider.rb +75 -0
- data/lib/rigor/language_server/hover_provider.rb +74 -0
- data/lib/rigor/language_server/hover_renderer.rb +312 -0
- data/lib/rigor/language_server/loop.rb +71 -0
- data/lib/rigor/language_server/project_context.rb +145 -0
- data/lib/rigor/language_server/selection_range_provider.rb +93 -0
- data/lib/rigor/language_server/server.rb +384 -0
- data/lib/rigor/language_server/signature_help_provider.rb +249 -0
- data/lib/rigor/language_server/synchronized_writer.rb +28 -0
- data/lib/rigor/language_server/uri.rb +40 -0
- data/lib/rigor/language_server.rb +29 -0
- data/lib/rigor/plugin/base.rb +63 -0
- data/lib/rigor/plugin/blueprint.rb +60 -0
- data/lib/rigor/plugin/loader.rb +3 -1
- data/lib/rigor/plugin/macro/block_as_method.rb +131 -0
- data/lib/rigor/plugin/macro/external_file.rb +143 -0
- data/lib/rigor/plugin/macro/heredoc_template.rb +315 -0
- data/lib/rigor/plugin/macro/trait_registry.rb +198 -0
- data/lib/rigor/plugin/macro.rb +31 -0
- data/lib/rigor/plugin/manifest.rb +127 -9
- data/lib/rigor/plugin/registry.rb +51 -2
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/rbs_extended/hkt_directives.rb +326 -0
- data/lib/rigor/rbs_extended.rb +82 -2
- data/lib/rigor/sig_gen/generator.rb +12 -3
- data/lib/rigor/trinary.rb +15 -11
- data/lib/rigor/type/app.rb +107 -0
- data/lib/rigor/type/bot.rb +6 -3
- data/lib/rigor/type/combinator.rb +12 -1
- data/lib/rigor/type/integer_range.rb +7 -7
- data/lib/rigor/type/refined.rb +18 -12
- data/lib/rigor/type/top.rb +4 -3
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +7 -1
- data/lib/rigor/type_node/identifier.rb +9 -1
- data/lib/rigor/type_node/string_literal.rb +4 -1
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/environment.rbs +11 -4
- data/sig/rigor/inference.rbs +2 -0
- data/sig/rigor/plugin/blueprint.rbs +7 -0
- data/sig/rigor/plugin/manifest.rbs +1 -1
- data/sig/rigor/plugin/registry.rbs +14 -1
- data/sig/rigor.rbs +37 -2
- metadata +92 -1
|
@@ -76,6 +76,21 @@ module Rigor
|
|
|
76
76
|
return nil unless receiver.is_a?(Type::BoundMethod)
|
|
77
77
|
return nil unless backward_method?(method_name)
|
|
78
78
|
|
|
79
|
+
# `Method#curry` is treated as identity on the carrier
|
|
80
|
+
# — `<bound>.curry` keeps the same
|
|
81
|
+
# `(receiver_type, method_name)` so a subsequent
|
|
82
|
+
# `<curried>.call` still routes through the recursive
|
|
83
|
+
# dispatch below. This is correct for the dominant
|
|
84
|
+
# no-arg form (`.curry.call`); partially-applied
|
|
85
|
+
# forms (`.curry(n).call(a)`) lose precision and fall
|
|
86
|
+
# through to RBS via the trailing
|
|
87
|
+
# `Type::Combinator.untyped`. A faithful
|
|
88
|
+
# `Type::CurriedBoundMethod(receiver_type,
|
|
89
|
+
# method_name, accumulated_args)` carrier is reserved
|
|
90
|
+
# for a future slice when concrete user demand
|
|
91
|
+
# surfaces.
|
|
92
|
+
return receiver if method_name == :curry
|
|
93
|
+
|
|
79
94
|
MethodDispatcher.dispatch(
|
|
80
95
|
receiver_type: receiver.receiver_type,
|
|
81
96
|
method_name: receiver.method_name,
|
|
@@ -92,7 +107,9 @@ module Rigor
|
|
|
92
107
|
# commonly used as a case-equality predicate, so we
|
|
93
108
|
# do NOT fold through it (the case/when narrowing path
|
|
94
109
|
# already special-cases `===` for branch typing).
|
|
95
|
-
|
|
110
|
+
# `Method#curry` rides through as identity (see the
|
|
111
|
+
# comment in `try_backward`).
|
|
112
|
+
BACKWARD_METHOD_NAMES = %i[call [] curry].freeze
|
|
96
113
|
private_constant :BACKWARD_METHOD_NAMES
|
|
97
114
|
|
|
98
115
|
def backward_method?(method_name)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "../../type"
|
|
4
4
|
require_relative "../acceptance"
|
|
5
5
|
require_relative "../rbs_type_translator"
|
|
6
|
+
require_relative "receiver_affinity"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
8
9
|
module Inference
|
|
@@ -26,7 +27,7 @@ module Rigor
|
|
|
26
27
|
# `Array#[](Range) -> Array[Elem]?` overload for a Range
|
|
27
28
|
# argument. (Surfaced during v0.1.1 self-analysis; see the
|
|
28
29
|
# "Interface-strictness on overload selection" item in
|
|
29
|
-
# `docs/
|
|
30
|
+
# `docs/ROADMAP.md`.)
|
|
30
31
|
# 3. **Pass 2 — gradual fall-back.** If no fully strict overload
|
|
31
32
|
# matches, accept the first arity-and-gradual-accept match
|
|
32
33
|
# (the v0.1.1 behaviour). Alias / Interface / Intersection
|
|
@@ -44,6 +45,33 @@ module Rigor
|
|
|
44
45
|
module OverloadSelector
|
|
45
46
|
module_function
|
|
46
47
|
|
|
48
|
+
# Canonical RBS-core aliases shipped by `core/builtin.rbs`
|
|
49
|
+
# whose body is `<Nominal> | _DuckType`. Matching an
|
|
50
|
+
# overload against an Integer literal should pick the
|
|
51
|
+
# `(int) -> Array[Elem]` body over the `(string) -> String`
|
|
52
|
+
# body because Integer satisfies `int`'s strict arm and
|
|
53
|
+
# not `string`'s. The translator collapses both aliases
|
|
54
|
+
# to `Dynamic[Top]` (interfaces are not structurally
|
|
55
|
+
# matched yet), so a dedicated pass 1.5 between strict
|
|
56
|
+
# and gradual consults this map to pick the alias whose
|
|
57
|
+
# strict arm matches.
|
|
58
|
+
#
|
|
59
|
+
# Symbol keys are the alias names as they appear under
|
|
60
|
+
# `RBS::Types::Alias#name.to_s` (the `name` is a
|
|
61
|
+
# `TypeName` whose `to_s` includes the `::` prefix).
|
|
62
|
+
# Values are an Array of class names whose Nominal[..]
|
|
63
|
+
# form is the alias's strict-arm matcher.
|
|
64
|
+
ALIAS_STRICT_NOMINALS = {
|
|
65
|
+
"::int" => ["Integer"],
|
|
66
|
+
"::string" => ["String"],
|
|
67
|
+
"::interned" => %w[Symbol String],
|
|
68
|
+
"::io" => ["IO"],
|
|
69
|
+
"::encoding" => %w[Encoding String],
|
|
70
|
+
"::path" => ["String"],
|
|
71
|
+
"::boolean" => %w[TrueClass FalseClass]
|
|
72
|
+
}.freeze
|
|
73
|
+
private_constant :ALIAS_STRICT_NOMINALS
|
|
74
|
+
|
|
47
75
|
# @param method_definition [RBS::Definition::Method]
|
|
48
76
|
# @param arg_types [Array<Rigor::Type>] caller-provided types in
|
|
49
77
|
# positional order. Empty when there are no arguments.
|
|
@@ -77,36 +105,19 @@ module Rigor
|
|
|
77
105
|
# compatibility.
|
|
78
106
|
param_overrides = RbsExtended.param_type_override_map(method_definition, environment: environment)
|
|
79
107
|
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
# `
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
overloads,
|
|
94
|
-
arg_types: arg_types,
|
|
95
|
-
self_type: self_type,
|
|
96
|
-
instance_type: instance_type,
|
|
97
|
-
type_vars: type_vars,
|
|
98
|
-
block_required: block_required,
|
|
99
|
-
param_overrides: param_overrides,
|
|
100
|
-
strict: true
|
|
101
|
-
) || find_matching_overload(
|
|
102
|
-
overloads,
|
|
103
|
-
arg_types: arg_types,
|
|
104
|
-
self_type: self_type,
|
|
105
|
-
instance_type: instance_type,
|
|
106
|
-
type_vars: type_vars,
|
|
107
|
-
block_required: block_required,
|
|
108
|
-
param_overrides: param_overrides,
|
|
109
|
-
strict: false
|
|
108
|
+
# Pre-sort: demote overloads whose param class is a
|
|
109
|
+
# disjoint sibling of the receiver class (e.g.
|
|
110
|
+
# `Integer#+(BigDecimal) -> BigDecimal` from the
|
|
111
|
+
# `bigdecimal` RBS reopen). Honors the coerce
|
|
112
|
+
# convention so `5 + ?` for unknown `?` resolves to
|
|
113
|
+
# the receiver-class-preserving arm rather than an
|
|
114
|
+
# arbitrary sibling-class arm that only wins by
|
|
115
|
+
# overload-list position.
|
|
116
|
+
overloads = ReceiverAffinity.reorder(overloads, self_type: self_type, environment: environment)
|
|
117
|
+
|
|
118
|
+
match = run_selection_passes(
|
|
119
|
+
overloads, arg_types: arg_types, self_type: self_type, instance_type: instance_type,
|
|
120
|
+
type_vars: type_vars, block_required: block_required, param_overrides: param_overrides
|
|
110
121
|
)
|
|
111
122
|
return match if match
|
|
112
123
|
return overloads.find { |mt| overload_has_block?(mt) } if block_required
|
|
@@ -121,6 +132,30 @@ module Rigor
|
|
|
121
132
|
class << self
|
|
122
133
|
private
|
|
123
134
|
|
|
135
|
+
# Three-pass overload search:
|
|
136
|
+
# - Pass 1 (strict): skipped when any arg is
|
|
137
|
+
# `Dynamic[Top]`, because gradual acceptance against
|
|
138
|
+
# an untyped arg accepts every param indiscriminately
|
|
139
|
+
# and would let pass 1 lock in an arbitrary strict
|
|
140
|
+
# overload (e.g. `Regexp#=~(nil) -> nil` over the
|
|
141
|
+
# `(::interned?) -> Integer?` overload).
|
|
142
|
+
# - Pass 1.5 (alias-resolved): consults each `RBS::Types::Alias`'s
|
|
143
|
+
# strict arm so e.g. `Array#*(int)` wins over the
|
|
144
|
+
# `Array#*(string) -> String` overload for Integer args.
|
|
145
|
+
# - Pass 2 (gradual): the original gradual matcher so
|
|
146
|
+
# overloads that legitimately rely on duck-typed
|
|
147
|
+
# params still resolve when nothing stricter applies.
|
|
148
|
+
def run_selection_passes(overloads, arg_types:, self_type:, instance_type:, type_vars:, block_required:,
|
|
149
|
+
param_overrides:)
|
|
150
|
+
shared = {
|
|
151
|
+
arg_types: arg_types, self_type: self_type, instance_type: instance_type,
|
|
152
|
+
type_vars: type_vars, block_required: block_required, param_overrides: param_overrides
|
|
153
|
+
}
|
|
154
|
+
find_matching_overload(overloads, **shared, strict: true) ||
|
|
155
|
+
find_matching_overload_via_aliases(overloads, arg_types: arg_types, block_required: block_required) ||
|
|
156
|
+
find_matching_overload(overloads, **shared, strict: false)
|
|
157
|
+
end
|
|
158
|
+
|
|
124
159
|
# rubocop:disable Metrics/ParameterLists
|
|
125
160
|
def find_matching_overload(overloads, arg_types:, self_type:, instance_type:, type_vars:, block_required:,
|
|
126
161
|
param_overrides:, strict:)
|
|
@@ -150,6 +185,66 @@ module Rigor
|
|
|
150
185
|
type.is_a?(Type::Dynamic) && type.static_facet.is_a?(Type::Top)
|
|
151
186
|
end
|
|
152
187
|
|
|
188
|
+
# Pass 1.5: for arity-compatible overloads whose every
|
|
189
|
+
# positional param is either a strict nominal OR a
|
|
190
|
+
# well-known core alias (`int` / `string` / `interned`
|
|
191
|
+
# / etc.), check the arg against the alias's STRICT
|
|
192
|
+
# arm. An Integer literal arg matches `int` here but
|
|
193
|
+
# not `string`, so `Array#*(int)` wins over the
|
|
194
|
+
# `Array#*(string) -> String` overload — even though
|
|
195
|
+
# both translate to `Dynamic[Top]` at the param level.
|
|
196
|
+
# Only fires when EVERY positional param has a known
|
|
197
|
+
# alias-or-strict shape; otherwise gradual matching
|
|
198
|
+
# takes over.
|
|
199
|
+
def find_matching_overload_via_aliases(overloads, arg_types:, block_required:)
|
|
200
|
+
overloads.find do |method_type|
|
|
201
|
+
next false if block_required && !OverloadSelector.overload_has_block?(method_type)
|
|
202
|
+
|
|
203
|
+
fun = method_type.type
|
|
204
|
+
next false unless arity_compatible?(fun, arg_types.size)
|
|
205
|
+
|
|
206
|
+
params = positional_params_for(fun, arg_types.size)
|
|
207
|
+
next false unless params.size == arg_types.size
|
|
208
|
+
|
|
209
|
+
params.zip(arg_types).all? { |param, arg| alias_param_accepts?(param.type, arg) }
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Checks the param's RBS type against an arg using
|
|
214
|
+
# alias-strict-arm matching. Optional / Union wrappers
|
|
215
|
+
# are flattened; alias resolution is one level deep
|
|
216
|
+
# (the canonical core aliases all have non-alias
|
|
217
|
+
# strict arms).
|
|
218
|
+
def alias_param_accepts?(rbs_type, arg)
|
|
219
|
+
nominal_names = strict_nominal_names_for(rbs_type)
|
|
220
|
+
return false if nominal_names.nil? || nominal_names.empty?
|
|
221
|
+
|
|
222
|
+
nominal_names.any? do |class_name|
|
|
223
|
+
result = Type::Combinator.nominal_of(class_name).accepts(arg, mode: :gradual)
|
|
224
|
+
result.yes? || result.maybe?
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Returns the candidate class names a param's RBS type
|
|
229
|
+
# accepts under alias-resolved strict matching, or nil
|
|
230
|
+
# when the shape cannot be reduced to a closed set of
|
|
231
|
+
# nominals (e.g. an Interface or an unrecognised alias).
|
|
232
|
+
def strict_nominal_names_for(rbs_type)
|
|
233
|
+
case rbs_type
|
|
234
|
+
when RBS::Types::ClassInstance
|
|
235
|
+
[rbs_type.name.to_s.delete_prefix("::")]
|
|
236
|
+
when RBS::Types::Alias
|
|
237
|
+
ALIAS_STRICT_NOMINALS[rbs_type.name.to_s]
|
|
238
|
+
when RBS::Types::Optional
|
|
239
|
+
strict_nominal_names_for(rbs_type.type)
|
|
240
|
+
when RBS::Types::Union
|
|
241
|
+
parts = rbs_type.types.map { |t| strict_nominal_names_for(t) }
|
|
242
|
+
return nil if parts.any?(&:nil?)
|
|
243
|
+
|
|
244
|
+
parts.flatten
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
153
248
|
# Returns true when every positional param the call
|
|
154
249
|
# site engages translates to a non-`Dynamic[Top]`
|
|
155
250
|
# carrier. Alias / Interface / Intersection RBS types
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
module MethodDispatcher
|
|
8
|
+
# Stable-sort an overload list so that "receiver-affinity"
|
|
9
|
+
# arms come first. An overload is receiver-affinity-matching
|
|
10
|
+
# when every positional param's class equals `self_type`'s
|
|
11
|
+
# class name OR is one of its proper RBS ancestors. The
|
|
12
|
+
# canonical case the helper exists for: when `bigdecimal`'s
|
|
13
|
+
# stdlib RBS reopens `Integer#+` at the FRONT of the
|
|
14
|
+
# overload list with `(BigDecimal) -> BigDecimal`, that
|
|
15
|
+
# disjoint-sibling arm would win every dispatch for
|
|
16
|
+
# `Integer#+(?)` by overload-list position alone, returning
|
|
17
|
+
# a spurious `BigDecimal` for plain integer arithmetic.
|
|
18
|
+
# Demoting the arm honours the coerce convention: when the
|
|
19
|
+
# arg type is unknown or itself an Integer, the
|
|
20
|
+
# receiver-preserving `(Integer) -> Integer` arm should win.
|
|
21
|
+
#
|
|
22
|
+
# No-op when (a) the environment can't answer
|
|
23
|
+
# `class_ordering` (nil env), or (b) the receiver isn't a
|
|
24
|
+
# nominal / singleton carrying a class name. The partition
|
|
25
|
+
# is stable, so within each bucket the RBS-declared order
|
|
26
|
+
# is preserved.
|
|
27
|
+
module ReceiverAffinity
|
|
28
|
+
module_function
|
|
29
|
+
|
|
30
|
+
def reorder(overloads, self_type:, environment:)
|
|
31
|
+
return overloads if environment.nil?
|
|
32
|
+
|
|
33
|
+
self_class_name = self_type_class_name(self_type)
|
|
34
|
+
return overloads if self_class_name.nil?
|
|
35
|
+
|
|
36
|
+
affinity, other = overloads.partition do |mt|
|
|
37
|
+
overload_param_classes_in_ancestry?(mt, self_class_name, environment)
|
|
38
|
+
end
|
|
39
|
+
affinity + other
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def self_type_class_name(self_type)
|
|
46
|
+
case self_type
|
|
47
|
+
when Type::Nominal, Type::Singleton then self_type.class_name
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def overload_param_classes_in_ancestry?(method_type, self_class_name, environment)
|
|
52
|
+
fun = method_type.type
|
|
53
|
+
params = fun.required_positionals + fun.optional_positionals + fun.trailing_positionals
|
|
54
|
+
return false if params.empty?
|
|
55
|
+
|
|
56
|
+
params.all? { |param| param_class_in_ancestry?(param.type, self_class_name, environment) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Walks Optional and Union one level so `(Numeric?)` and
|
|
60
|
+
# `(Integer | Float)` still classify when every branch
|
|
61
|
+
# sits in the ancestry. Non-`ClassInstance` shapes
|
|
62
|
+
# (Alias / Interface / Intersection / type variables)
|
|
63
|
+
# don't carry a clean class identity and therefore
|
|
64
|
+
# disqualify the overload from the affinity bucket.
|
|
65
|
+
def param_class_in_ancestry?(rbs_type, self_class_name, environment)
|
|
66
|
+
case rbs_type
|
|
67
|
+
when RBS::Types::ClassInstance
|
|
68
|
+
class_in_ancestry?(rbs_type.name.to_s.delete_prefix("::"), self_class_name, environment)
|
|
69
|
+
when RBS::Types::Optional
|
|
70
|
+
param_class_in_ancestry?(rbs_type.type, self_class_name, environment)
|
|
71
|
+
when RBS::Types::Union
|
|
72
|
+
rbs_type.types.all? { |t| param_class_in_ancestry?(t, self_class_name, environment) }
|
|
73
|
+
else
|
|
74
|
+
false
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def class_in_ancestry?(param_class_name, self_class_name, environment)
|
|
79
|
+
return true if param_class_name == self_class_name
|
|
80
|
+
|
|
81
|
+
environment.class_ordering(self_class_name, param_class_name) == :subclass
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -155,13 +155,13 @@ module Rigor
|
|
|
155
155
|
# tier ahead of RBS sees the more precise carrier so
|
|
156
156
|
# downstream narrowing (`if size > 0; …`) actually has a
|
|
157
157
|
# range to intersect with.
|
|
158
|
-
SIZE_RETURNING_NOMINALS = {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
SIZE_RETURNING_NOMINALS = Ractor.make_shareable({
|
|
159
|
+
"Array" => %i[size length count],
|
|
160
|
+
"String" => %i[length size bytesize],
|
|
161
|
+
"Hash" => %i[size length count],
|
|
162
|
+
"Set" => %i[size length count],
|
|
163
|
+
"Range" => %i[size length count]
|
|
164
|
+
})
|
|
165
165
|
private_constant :SIZE_RETURNING_NOMINALS
|
|
166
166
|
|
|
167
167
|
# When the difference removes the empty value of the
|
|
@@ -323,39 +323,45 @@ module Rigor
|
|
|
323
323
|
# `dispatch_nominal_size` so size-returning calls on
|
|
324
324
|
# a `Refined[String, *]` still tighten to
|
|
325
325
|
# `non_negative_int`.
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
326
|
+
# ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`)
|
|
327
|
+
# because the keys are two-element Symbol arrays whose
|
|
328
|
+
# inner arrays are unfrozen under shallow `.freeze`.
|
|
329
|
+
# Surfaced on Discourse via `Ractor::IsolationError` when
|
|
330
|
+
# the dispatch loop's `REFINED_STRING_PROJECTIONS[[id, sym]]`
|
|
331
|
+
# lookup ran from a worker Ractor.
|
|
332
|
+
REFINED_STRING_PROJECTIONS = Ractor.make_shareable({
|
|
333
|
+
%i[lowercase downcase] => :refined_self,
|
|
334
|
+
%i[lowercase upcase] => :uppercase_string,
|
|
335
|
+
%i[uppercase upcase] => :refined_self,
|
|
336
|
+
%i[uppercase downcase] => :lowercase_string,
|
|
337
|
+
%i[numeric downcase] => :refined_self,
|
|
338
|
+
%i[numeric upcase] => :refined_self,
|
|
339
|
+
# Digit-only strings are case-invariant; the prefix
|
|
340
|
+
# letters in `0o…` / `0x…` are accepted by the
|
|
341
|
+
# predicate in either case so the predicate-subset
|
|
342
|
+
# is preserved across `#downcase` / `#upcase` even
|
|
343
|
+
# though the value-set element changes.
|
|
344
|
+
%i[decimal_int downcase] => :refined_self,
|
|
345
|
+
%i[decimal_int upcase] => :refined_self,
|
|
346
|
+
%i[octal_int downcase] => :refined_self,
|
|
347
|
+
%i[octal_int upcase] => :refined_self,
|
|
348
|
+
%i[hex_int downcase] => :refined_self,
|
|
349
|
+
%i[hex_int upcase] => :refined_self,
|
|
350
|
+
# v0.1.1 Track 1 slice 2 — `to_i` / `to_int` on a
|
|
351
|
+
# known digit-only string. `decimal-int-string`
|
|
352
|
+
# (`/\A\d+\z/`) and `numeric-string` (Rigor's
|
|
353
|
+
# numeric-string predicate, ASCII digits) are
|
|
354
|
+
# predicates over digit-only strings, so the parse
|
|
355
|
+
# is total over the carrier domain and the result
|
|
356
|
+
# is always `>= 0`. `non-negative-int` is the
|
|
357
|
+
# tightest carrier that captures both the lower
|
|
358
|
+
# bound and the integer-ness without inventing a
|
|
359
|
+
# narrower carrier.
|
|
360
|
+
%i[decimal_int to_i] => :non_negative_int,
|
|
361
|
+
%i[decimal_int to_int] => :non_negative_int,
|
|
362
|
+
%i[numeric to_i] => :non_negative_int,
|
|
363
|
+
%i[numeric to_int] => :non_negative_int
|
|
364
|
+
})
|
|
359
365
|
private_constant :REFINED_STRING_PROJECTIONS
|
|
360
366
|
|
|
361
367
|
def dispatch_refined(refined, method_name, args)
|