rigortype 0.0.4 → 0.0.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 +215 -134
- data/data/builtins/ruby_core/comparable.yml +87 -0
- data/data/builtins/ruby_core/complex.yml +505 -0
- data/data/builtins/ruby_core/date.yml +1737 -0
- data/data/builtins/ruby_core/enumerable.yml +557 -0
- data/data/builtins/ruby_core/file.yml +9 -0
- data/data/builtins/ruby_core/pathname.yml +1067 -0
- data/data/builtins/ruby_core/rational.yml +365 -0
- data/data/builtins/ruby_core/string.yml +9 -0
- data/data/builtins/ruby_core/time.yml +6 -4
- data/lib/rigor/cli.rb +1 -1
- data/lib/rigor/inference/builtins/comparable_catalog.rb +27 -0
- data/lib/rigor/inference/builtins/complex_catalog.rb +41 -0
- data/lib/rigor/inference/builtins/date_catalog.rb +98 -0
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +27 -0
- data/lib/rigor/inference/builtins/pathname_catalog.rb +35 -0
- data/lib/rigor/inference/builtins/rational_catalog.rb +38 -0
- data/lib/rigor/inference/expression_typer.rb +285 -23
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +322 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +197 -12
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +99 -0
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +95 -0
- data/lib/rigor/inference/method_dispatcher.rb +20 -8
- data/lib/rigor/inference/narrowing.rb +210 -1
- data/lib/rigor/inference/scope_indexer.rb +87 -11
- data/lib/rigor/inference/statement_evaluator.rb +5 -1
- data/lib/rigor/rbs_extended.rb +11 -6
- data/lib/rigor/type/integer_range.rb +4 -2
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/environment.rbs +4 -6
- data/sig/rigor/inference.rbs +2 -1
- data/sig/rigor/type.rbs +41 -41
- metadata +15 -1
|
@@ -241,6 +241,55 @@ module Rigor
|
|
|
241
241
|
narrow_class_dispatch(type, class_name, context)
|
|
242
242
|
end
|
|
243
243
|
|
|
244
|
+
# Negation pair for `assert_value is ~refinement` /
|
|
245
|
+
# `predicate-if-* … is ~refinement` directives. Computes
|
|
246
|
+
# the complement of `refinement` within the current
|
|
247
|
+
# local's domain `current_type`.
|
|
248
|
+
#
|
|
249
|
+
# Carrier-by-carrier rules:
|
|
250
|
+
#
|
|
251
|
+
# - `Difference[base, Constant[v]]`. Complement of
|
|
252
|
+
# `base \ {v}` within `current_type`. Walk the current
|
|
253
|
+
# type's union members, keep each part disjoint from
|
|
254
|
+
# `base`, and add the removed-value Constant once when
|
|
255
|
+
# any current member covers it. `assert s is
|
|
256
|
+
# ~non-empty-string` over `s: String | nil` narrows to
|
|
257
|
+
# `Constant[""] | NilClass`.
|
|
258
|
+
# - `IntegerRange[a, b]` (v0.0.5+ slice). Complement is
|
|
259
|
+
# the two open halves `int<min, a-1>` and
|
|
260
|
+
# `int<b+1, max>`, each intersected with the
|
|
261
|
+
# integer-domain parts of `current_type`. Non-integer
|
|
262
|
+
# parts (nil, String, …) of a Union receiver survive
|
|
263
|
+
# unchanged. `assert n is ~int<5, 10>` over `n:
|
|
264
|
+
# Integer | nil` narrows to `int<min, 4> | int<11,
|
|
265
|
+
# max> | NilClass`.
|
|
266
|
+
# - `Type::Intersection[M1, M2, …]` (v0.0.5+ slice). De
|
|
267
|
+
# Morgan: `D \ (M1 ∩ M2) = (D \ M1) ∪ (D \ M2)`. Each
|
|
268
|
+
# member's complement is computed independently within
|
|
269
|
+
# `current_type` and the results are unioned. Members
|
|
270
|
+
# the algebra cannot complement (Refined, non-Constant
|
|
271
|
+
# Difference, …) contribute `current_type` itself, so
|
|
272
|
+
# the union widens the answer to `current_type` —
|
|
273
|
+
# sound but imprecise.
|
|
274
|
+
# - `Refined[base, predicate]`. Predicate complements are
|
|
275
|
+
# not reducible to a finite carrier without a richer
|
|
276
|
+
# shape (e.g. `~lowercase-string` is "uppercase OR
|
|
277
|
+
# mixed-case"); `current_type` is returned unchanged.
|
|
278
|
+
def narrow_not_refinement(current_type, refinement_type)
|
|
279
|
+
case refinement_type
|
|
280
|
+
when Type::Difference
|
|
281
|
+
return current_type unless refinement_type.removed.is_a?(Type::Constant)
|
|
282
|
+
|
|
283
|
+
complement_difference(current_type, refinement_type)
|
|
284
|
+
when Type::IntegerRange
|
|
285
|
+
complement_integer_range(current_type, refinement_type)
|
|
286
|
+
when Type::Intersection
|
|
287
|
+
complement_intersection(current_type, refinement_type)
|
|
288
|
+
else
|
|
289
|
+
current_type
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
244
293
|
# Public predicate analyser. Returns `[truthy_scope, falsey_scope]`,
|
|
245
294
|
# always; when no narrowing rule matches the predicate node both
|
|
246
295
|
# entries are the receiver scope unchanged.
|
|
@@ -321,6 +370,162 @@ module Rigor
|
|
|
321
370
|
class << self
|
|
322
371
|
private
|
|
323
372
|
|
|
373
|
+
# Complement of `Difference[base, Constant[v]]` within
|
|
374
|
+
# `current_type`. Walks the current type's union members,
|
|
375
|
+
# keeps each member disjoint from `base` (those values
|
|
376
|
+
# were never in the refinement to begin with), and adds
|
|
377
|
+
# the removed-value `Constant[v]` exactly once when any
|
|
378
|
+
# current member covers it. Members that are fully
|
|
379
|
+
# contained in the refinement (i.e. inside `base` and
|
|
380
|
+
# NOT equal to the removed value) are dropped — they are
|
|
381
|
+
# exactly the values the negation excludes.
|
|
382
|
+
def complement_difference(current_type, difference)
|
|
383
|
+
base = difference.base
|
|
384
|
+
removed = difference.removed
|
|
385
|
+
parts = current_type.is_a?(Type::Union) ? current_type.members : [current_type]
|
|
386
|
+
|
|
387
|
+
survivors = []
|
|
388
|
+
add_removed = false
|
|
389
|
+
parts.each do |part|
|
|
390
|
+
if base_disjoint?(base, part)
|
|
391
|
+
survivors << part
|
|
392
|
+
elsif part_covers_constant?(part, removed)
|
|
393
|
+
add_removed = true
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
survivors << removed if add_removed
|
|
397
|
+
|
|
398
|
+
return current_type if survivors.empty?
|
|
399
|
+
|
|
400
|
+
Type::Combinator.union(*survivors)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def base_disjoint?(base, part)
|
|
404
|
+
base.accepts(part, mode: :gradual).no?
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def part_covers_constant?(part, constant)
|
|
408
|
+
result = part.accepts(constant, mode: :gradual)
|
|
409
|
+
result.yes? || result.maybe?
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Complement of an `IntegerRange[a, b]` within
|
|
413
|
+
# `current_type`. Splits the range complement into the
|
|
414
|
+
# two open halves `int<min, a-1>` and `int<b+1, max>`
|
|
415
|
+
# (skipping a half when its bound is infinity), then
|
|
416
|
+
# intersects each half with the integer-domain parts of
|
|
417
|
+
# `current_type`. Non-integer parts of a Union receiver
|
|
418
|
+
# (nil, String, …) survive unchanged.
|
|
419
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
420
|
+
def complement_integer_range(current_type, range)
|
|
421
|
+
halves = integer_range_complement_halves(range)
|
|
422
|
+
parts = current_type.is_a?(Type::Union) ? current_type.members : [current_type]
|
|
423
|
+
|
|
424
|
+
survivors = []
|
|
425
|
+
parts.each do |part|
|
|
426
|
+
if integer_member?(part)
|
|
427
|
+
halves.each do |half|
|
|
428
|
+
meet = intersect_integer_part(part, half)
|
|
429
|
+
survivors << meet unless meet.nil? || meet.is_a?(Type::Bot)
|
|
430
|
+
end
|
|
431
|
+
else
|
|
432
|
+
survivors << part
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
return current_type if survivors.empty?
|
|
437
|
+
|
|
438
|
+
Type::Combinator.union(*survivors)
|
|
439
|
+
end
|
|
440
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
441
|
+
|
|
442
|
+
# Returns the two open halves of an IntegerRange's
|
|
443
|
+
# complement: the left half `int<-∞, a-1>` (when `a` is
|
|
444
|
+
# finite) and the right half `int<b+1, ∞>` (when `b` is
|
|
445
|
+
# finite). Universal ranges (both bounds infinite) yield
|
|
446
|
+
# an empty array — the complement is empty.
|
|
447
|
+
def integer_range_complement_halves(range)
|
|
448
|
+
halves = []
|
|
449
|
+
left_max = range.min
|
|
450
|
+
right_min = range.max
|
|
451
|
+
|
|
452
|
+
if left_max.is_a?(Integer)
|
|
453
|
+
halves << Type::Combinator.integer_range(Type::IntegerRange::NEG_INFINITY, left_max - 1)
|
|
454
|
+
end
|
|
455
|
+
if right_min.is_a?(Integer)
|
|
456
|
+
halves << Type::Combinator.integer_range(right_min + 1, Type::IntegerRange::POS_INFINITY)
|
|
457
|
+
end
|
|
458
|
+
halves
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def integer_member?(part)
|
|
462
|
+
case part
|
|
463
|
+
when Type::Constant then part.value.is_a?(Integer)
|
|
464
|
+
when Type::IntegerRange then true
|
|
465
|
+
when Type::Nominal then part.class_name == "Integer"
|
|
466
|
+
else false
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Intersect an integer-domain part with a complement
|
|
471
|
+
# half-range. For a Nominal[Integer] receiver the meet
|
|
472
|
+
# is the half itself; for an existing IntegerRange the
|
|
473
|
+
# meet narrows both bounds; for a Constant[Integer] the
|
|
474
|
+
# meet is the constant when the half covers it,
|
|
475
|
+
# otherwise nil.
|
|
476
|
+
def intersect_integer_part(part, half)
|
|
477
|
+
case part
|
|
478
|
+
when Type::Nominal
|
|
479
|
+
half
|
|
480
|
+
when Type::IntegerRange
|
|
481
|
+
integer_range_meet(part, half)
|
|
482
|
+
when Type::Constant
|
|
483
|
+
half.covers?(part.value) ? part : nil
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def integer_range_meet(left, right)
|
|
488
|
+
low = numeric_to_bound([integer_bound_value(left.min), integer_bound_value(right.min)].max)
|
|
489
|
+
high = numeric_to_bound([integer_bound_value(left.max), integer_bound_value(right.max)].min)
|
|
490
|
+
return nil if integer_range_disjoint?(low, high)
|
|
491
|
+
|
|
492
|
+
Type::Combinator.integer_range(low, high)
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
def integer_bound_value(bound)
|
|
496
|
+
return -Float::INFINITY if bound == Type::IntegerRange::NEG_INFINITY
|
|
497
|
+
return Float::INFINITY if bound == Type::IntegerRange::POS_INFINITY
|
|
498
|
+
|
|
499
|
+
bound
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def numeric_to_bound(value)
|
|
503
|
+
return Type::IntegerRange::NEG_INFINITY if value == -Float::INFINITY
|
|
504
|
+
return Type::IntegerRange::POS_INFINITY if value == Float::INFINITY
|
|
505
|
+
|
|
506
|
+
value.to_i
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def integer_range_disjoint?(low, high)
|
|
510
|
+
return false if low == Type::IntegerRange::NEG_INFINITY
|
|
511
|
+
return false if high == Type::IntegerRange::POS_INFINITY
|
|
512
|
+
|
|
513
|
+
low > high
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
# De Morgan: `D \ (M1 ∩ M2 ∩ …) = (D \ M1) ∪ (D \ M2) ∪
|
|
517
|
+
# …`. Each member's complement is computed independently
|
|
518
|
+
# within `current_type` and the results are unioned.
|
|
519
|
+
# Members the algebra cannot complement contribute
|
|
520
|
+
# `current_type` itself, so the union widens to
|
|
521
|
+
# `current_type` overall — sound but imprecise.
|
|
522
|
+
def complement_intersection(current_type, intersection)
|
|
523
|
+
per_member = intersection.members.map do |member|
|
|
524
|
+
Narrowing.narrow_not_refinement(current_type, member)
|
|
525
|
+
end
|
|
526
|
+
Type::Combinator.union(*per_member)
|
|
527
|
+
end
|
|
528
|
+
|
|
324
529
|
def falsey_value?(value)
|
|
325
530
|
value.nil? || value == false
|
|
326
531
|
end
|
|
@@ -1029,7 +1234,11 @@ module Rigor
|
|
|
1029
1234
|
# the effect's `negative?` flag. Shared between
|
|
1030
1235
|
# predicate-if-* and assert-if-* application paths.
|
|
1031
1236
|
def narrow_for_effect(current, effect, environment)
|
|
1032
|
-
|
|
1237
|
+
if effect.respond_to?(:refinement?) && effect.refinement?
|
|
1238
|
+
return narrow_not_refinement(current, effect.refinement_type) if effect.negative?
|
|
1239
|
+
|
|
1240
|
+
return effect.refinement_type
|
|
1241
|
+
end
|
|
1033
1242
|
|
|
1034
1243
|
if effect.negative?
|
|
1035
1244
|
narrow_not_class(current, effect.class_name, exact: false, environment: environment)
|
|
@@ -4,6 +4,7 @@ require "prism"
|
|
|
4
4
|
|
|
5
5
|
require_relative "../scope"
|
|
6
6
|
require_relative "../type"
|
|
7
|
+
require_relative "narrowing"
|
|
7
8
|
require_relative "statement_evaluator"
|
|
8
9
|
|
|
9
10
|
module Rigor
|
|
@@ -475,16 +476,9 @@ module Rigor
|
|
|
475
476
|
|
|
476
477
|
case node
|
|
477
478
|
when Prism::ModuleNode, Prism::ClassNode
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
singleton = Type::Combinator.singleton_of(full)
|
|
482
|
-
identity_table[node.constant_path] = singleton
|
|
483
|
-
discovered[full] = singleton
|
|
484
|
-
child_prefix = qualified_prefix + [name]
|
|
485
|
-
record_declarations(node.body, child_prefix, identity_table, discovered) if node.body
|
|
486
|
-
return
|
|
487
|
-
end
|
|
479
|
+
return if record_class_or_module?(node, qualified_prefix, identity_table, discovered)
|
|
480
|
+
when Prism::ConstantWriteNode
|
|
481
|
+
return if record_data_define_constant?(node, qualified_prefix, identity_table, discovered)
|
|
488
482
|
end
|
|
489
483
|
|
|
490
484
|
node.compact_child_nodes.each do |child|
|
|
@@ -492,6 +486,60 @@ module Rigor
|
|
|
492
486
|
end
|
|
493
487
|
end
|
|
494
488
|
|
|
489
|
+
def record_class_or_module?(node, qualified_prefix, identity_table, discovered)
|
|
490
|
+
name = qualified_name_for(node.constant_path)
|
|
491
|
+
return false unless name
|
|
492
|
+
|
|
493
|
+
full = (qualified_prefix + [name]).join("::")
|
|
494
|
+
singleton = Type::Combinator.singleton_of(full)
|
|
495
|
+
identity_table[node.constant_path] = singleton
|
|
496
|
+
discovered[full] = singleton
|
|
497
|
+
child_prefix = qualified_prefix + [name]
|
|
498
|
+
record_declarations(node.body, child_prefix, identity_table, discovered) if node.body
|
|
499
|
+
true
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Recognises `Const = Data.define(*Symbol) [do ... end]` and registers
|
|
503
|
+
# `Const` (qualified by the surrounding class/module path) as a
|
|
504
|
+
# discovered class. `Const.new(...)` then resolves to a fresh
|
|
505
|
+
# `Nominal[Const]` via `meta_new`, instead of the un-narrowed
|
|
506
|
+
# `Dynamic[top]` returned by the default `Class#new` envelope.
|
|
507
|
+
#
|
|
508
|
+
# The Data.define block body, if present, is recursed into so any
|
|
509
|
+
# nested class/module declarations in the override block (rare but
|
|
510
|
+
# legal) still feed the discovered table.
|
|
511
|
+
def record_data_define_constant?(node, qualified_prefix, identity_table, discovered)
|
|
512
|
+
return false unless data_define_call?(node.value)
|
|
513
|
+
|
|
514
|
+
full = (qualified_prefix + [node.name.to_s]).join("::")
|
|
515
|
+
discovered[full] = Type::Combinator.singleton_of(full)
|
|
516
|
+
record_declarations(node.value, qualified_prefix, identity_table, discovered)
|
|
517
|
+
true
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# Recognises `Data.define(*Symbol)` and `Data.define(*Symbol) do ... end`
|
|
521
|
+
# at constant-write rvalue position. The receiver MUST be the bare
|
|
522
|
+
# `Data` constant (or `::Data`); other receivers (a local variable, a
|
|
523
|
+
# method call return) are rejected because their identity is not
|
|
524
|
+
# statically known.
|
|
525
|
+
def data_define_call?(node)
|
|
526
|
+
return false unless node.is_a?(Prism::CallNode)
|
|
527
|
+
return false unless node.name == :define
|
|
528
|
+
return false unless data_constant_receiver?(node.receiver)
|
|
529
|
+
|
|
530
|
+
args = node.arguments&.arguments || []
|
|
531
|
+
args.all?(Prism::SymbolNode)
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def data_constant_receiver?(node)
|
|
535
|
+
case node
|
|
536
|
+
when Prism::ConstantReadNode
|
|
537
|
+
node.name == :Data
|
|
538
|
+
when Prism::ConstantPathNode
|
|
539
|
+
node.parent.nil? && node.name == :Data
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
495
543
|
def qualified_name_for(constant_path_node)
|
|
496
544
|
case constant_path_node
|
|
497
545
|
when Prism::ConstantReadNode
|
|
@@ -515,6 +563,13 @@ module Rigor
|
|
|
515
563
|
# Prism node the StatementEvaluator did not visit (i.e. expression-
|
|
516
564
|
# interior nodes like the receiver/args of a CallNode). Those
|
|
517
565
|
# nodes inherit their nearest recorded ancestor's scope.
|
|
566
|
+
#
|
|
567
|
+
# `IfNode` / `UnlessNode` are special-cased: the truthy and falsey
|
|
568
|
+
# branches each get their predicate's narrowed scope before
|
|
569
|
+
# recursing. This handles expression-position conditionals
|
|
570
|
+
# (e.g. `cache[k] = if cond; t; else; e; end` and conditionals
|
|
571
|
+
# nested as call arguments) which are typed by ExpressionTyper
|
|
572
|
+
# without going through `eval_if`'s narrowing path.
|
|
518
573
|
def propagate(node, table, parent_scope)
|
|
519
574
|
return unless node.is_a?(Prism::Node)
|
|
520
575
|
|
|
@@ -526,7 +581,28 @@ module Rigor
|
|
|
526
581
|
parent_scope
|
|
527
582
|
end
|
|
528
583
|
|
|
529
|
-
node
|
|
584
|
+
case node
|
|
585
|
+
when Prism::IfNode
|
|
586
|
+
propagate_if_branches(node, table, current_scope)
|
|
587
|
+
when Prism::UnlessNode
|
|
588
|
+
propagate_unless_branches(node, table, current_scope)
|
|
589
|
+
else
|
|
590
|
+
node.compact_child_nodes.each { |child| propagate(child, table, current_scope) }
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def propagate_if_branches(node, table, current_scope)
|
|
595
|
+
truthy_scope, falsey_scope = Narrowing.predicate_scopes(node.predicate, current_scope)
|
|
596
|
+
propagate(node.predicate, table, current_scope) if node.predicate
|
|
597
|
+
propagate(node.statements, table, truthy_scope) if node.statements
|
|
598
|
+
propagate(node.subsequent, table, falsey_scope) if node.subsequent
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def propagate_unless_branches(node, table, current_scope)
|
|
602
|
+
truthy_scope, falsey_scope = Narrowing.predicate_scopes(node.predicate, current_scope)
|
|
603
|
+
propagate(node.predicate, table, current_scope) if node.predicate
|
|
604
|
+
propagate(node.statements, table, falsey_scope) if node.statements
|
|
605
|
+
propagate(node.else_clause, table, truthy_scope) if node.else_clause
|
|
530
606
|
end
|
|
531
607
|
end
|
|
532
608
|
# rubocop:enable Metrics/ModuleLength
|
|
@@ -894,7 +894,11 @@ module Rigor
|
|
|
894
894
|
end
|
|
895
895
|
|
|
896
896
|
def narrow_for_assert_effect(current_type, effect, environment)
|
|
897
|
-
|
|
897
|
+
if effect.refinement?
|
|
898
|
+
return Narrowing.narrow_not_refinement(current_type, effect.refinement_type) if effect.negative?
|
|
899
|
+
|
|
900
|
+
return effect.refinement_type
|
|
901
|
+
end
|
|
898
902
|
|
|
899
903
|
if effect.negative?
|
|
900
904
|
Narrowing.narrow_not_class(current_type, effect.class_name, exact: false, environment: environment)
|
data/lib/rigor/rbs_extended.rb
CHANGED
|
@@ -124,8 +124,8 @@ module Rigor
|
|
|
124
124
|
\s+
|
|
125
125
|
(?<target>self|[a-z_][a-zA-Z0-9_]*)
|
|
126
126
|
\s+is\s+
|
|
127
|
+
(?<negation>~?)
|
|
127
128
|
(?:
|
|
128
|
-
(?<negation>~?)
|
|
129
129
|
(?<class_name>(?:::)?[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*)
|
|
130
130
|
|
|
|
131
131
|
(?<refinement>[a-z][a-z0-9-]*(?:[\[<][^\]>]*[\]>])?)
|
|
@@ -181,8 +181,8 @@ module Rigor
|
|
|
181
181
|
\s+
|
|
182
182
|
(?<target>self|[a-z_][a-zA-Z0-9_]*)
|
|
183
183
|
\s+is\s+
|
|
184
|
+
(?<negation>~?)
|
|
184
185
|
(?:
|
|
185
|
-
(?<negation>~?)
|
|
186
186
|
(?<class_name>(?:::)?[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*)
|
|
187
187
|
|
|
|
188
188
|
(?<refinement>[a-z][a-z0-9-]*(?:[\[<][^\]>]*[\]>])?)
|
|
@@ -231,14 +231,19 @@ module Rigor
|
|
|
231
231
|
# nil, `negative` reflects the optional `~` prefix.
|
|
232
232
|
# - Refinement arm matched: `class_name` is nil,
|
|
233
233
|
# `refinement_type` is the resolved `Rigor::Type`,
|
|
234
|
-
# `negative`
|
|
235
|
-
#
|
|
234
|
+
# `negative` reflects the `~` prefix. v0.0.5 supports
|
|
235
|
+
# refinement-form negation for the `Difference[base,
|
|
236
|
+
# Constant]` shape (the narrowing tier computes the
|
|
237
|
+
# complement decomposition); other refinement carriers
|
|
238
|
+
# under negation fall back to the conservative
|
|
239
|
+
# "current_type unchanged" answer.
|
|
236
240
|
# - Refinement payload unparseable: returns
|
|
237
241
|
# `[nil, nil, false]` so callers can drop the directive
|
|
238
242
|
# silently (fail-soft policy).
|
|
239
243
|
def resolve_directive_rhs(match)
|
|
244
|
+
negative = match[:negation].to_s == "~"
|
|
240
245
|
class_capture = match[:class_name]
|
|
241
|
-
return [class_capture.to_s.sub(/\A::/, ""), nil,
|
|
246
|
+
return [class_capture.to_s.sub(/\A::/, ""), nil, negative] if class_capture
|
|
242
247
|
|
|
243
248
|
refinement_capture = match[:refinement]
|
|
244
249
|
return [nil, nil, false] if refinement_capture.nil?
|
|
@@ -246,7 +251,7 @@ module Rigor
|
|
|
246
251
|
type = Builtins::ImportedRefinements.parse(refinement_capture)
|
|
247
252
|
return [nil, nil, false] if type.nil?
|
|
248
253
|
|
|
249
|
-
[nil, type,
|
|
254
|
+
[nil, type, negative]
|
|
250
255
|
end
|
|
251
256
|
|
|
252
257
|
def target_fields(target)
|
|
@@ -65,11 +65,13 @@ module Rigor
|
|
|
65
65
|
# `:neg_infinity`). Use this in arithmetic comparisons; never compare
|
|
66
66
|
# `:neg_infinity` directly with an `Integer`.
|
|
67
67
|
def lower
|
|
68
|
-
|
|
68
|
+
m = min
|
|
69
|
+
m.is_a?(Symbol) ? -Float::INFINITY : m
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
def upper
|
|
72
|
-
|
|
73
|
+
m = max
|
|
74
|
+
m.is_a?(Symbol) ? Float::INFINITY : m
|
|
73
75
|
end
|
|
74
76
|
|
|
75
77
|
ALIAS_NAMES = {
|
data/lib/rigor/version.rb
CHANGED
data/sig/rigor/environment.rbs
CHANGED
|
@@ -38,15 +38,13 @@ module Rigor
|
|
|
38
38
|
|
|
39
39
|
def initialize: (?libraries: Array[String], ?signature_paths: Array[String | _ToPath]) -> void
|
|
40
40
|
def class_known?: (String | Symbol name) -> bool
|
|
41
|
-
def instance_definition: (String | Symbol class_name) -> untyped
|
|
42
|
-
def instance_method: (class_name: String | Symbol, method_name: String | Symbol) -> untyped
|
|
43
|
-
def singleton_definition: (String | Symbol class_name) -> untyped
|
|
44
|
-
def singleton_method: (class_name: String | Symbol, method_name: String | Symbol) -> untyped
|
|
41
|
+
def instance_definition: (String | Symbol class_name) -> untyped?
|
|
42
|
+
def instance_method: (class_name: String | Symbol, method_name: String | Symbol) -> untyped?
|
|
43
|
+
def singleton_definition: (String | Symbol class_name) -> untyped?
|
|
44
|
+
def singleton_method: (class_name: String | Symbol, method_name: String | Symbol) -> untyped?
|
|
45
45
|
def class_type_param_names: (String | Symbol class_name) -> Array[Symbol]
|
|
46
46
|
def class_ordering: (String | Symbol lhs, String | Symbol rhs) -> ordering
|
|
47
47
|
def constant_type: (String name) -> Type::t?
|
|
48
|
-
def instance_definition: (String | Symbol class_name) -> untyped?
|
|
49
|
-
def singleton_definition: (String | Symbol class_name) -> untyped?
|
|
50
48
|
end
|
|
51
49
|
|
|
52
50
|
class RbsHierarchy
|
data/sig/rigor/inference.rbs
CHANGED
|
@@ -85,6 +85,7 @@ module Rigor
|
|
|
85
85
|
def self?.narrow_not_equal: (Type::t type, untyped literal) -> Type::t
|
|
86
86
|
def self?.narrow_class: (Type::t type, String class_name, ?exact: bool, ?environment: Environment) -> Type::t
|
|
87
87
|
def self?.narrow_not_class: (Type::t type, String class_name, ?exact: bool, ?environment: Environment) -> Type::t
|
|
88
|
+
def self?.narrow_not_refinement: (Type::t current_type, Type::t refinement_type) -> Type::t
|
|
88
89
|
def self?.predicate_scopes: (untyped node, Scope scope) -> [Scope, Scope]
|
|
89
90
|
def self?.case_when_scopes: (untyped subject, Array[untyped] conditions, Scope scope) -> [Scope, Scope]
|
|
90
91
|
def self?.analyse: (untyped node, Scope scope) -> untyped
|
|
@@ -131,7 +132,7 @@ module Rigor
|
|
|
131
132
|
module ScopeIndexer
|
|
132
133
|
def self?.index: (untyped root, default_scope: Scope) -> Hash[untyped, Scope]
|
|
133
134
|
def self?.build_declaration_overrides: (untyped root) -> Hash[untyped, Type::t]
|
|
134
|
-
def self?.record_declarations: (untyped node, Array[String] qualified_prefix, Hash[untyped, Type::t]
|
|
135
|
+
def self?.record_declarations: (untyped node, Array[String] qualified_prefix, Hash[untyped, Type::t] identity_table, Hash[String, Type::t] discovered) -> void
|
|
135
136
|
def self?.qualified_name_for: (untyped constant_path_node) -> String?
|
|
136
137
|
def self?.render_constant_path: (untyped node) -> String
|
|
137
138
|
def self?.propagate: (untyped node, Hash[untyped, Scope] table, Scope parent_scope) -> void
|
data/sig/rigor/type.rbs
CHANGED
|
@@ -8,9 +8,9 @@ module Rigor
|
|
|
8
8
|
def self.instance: () -> Top
|
|
9
9
|
def describe: (?Symbol verbosity) -> String
|
|
10
10
|
def erase_to_rbs: () -> String
|
|
11
|
-
def top: () ->
|
|
12
|
-
def bot: () ->
|
|
13
|
-
def dynamic: () ->
|
|
11
|
+
def top: () -> Trinary
|
|
12
|
+
def bot: () -> Trinary
|
|
13
|
+
def dynamic: () -> Trinary
|
|
14
14
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
15
15
|
def ==: (untyped other) -> bool
|
|
16
16
|
def hash: () -> Integer
|
|
@@ -21,9 +21,9 @@ module Rigor
|
|
|
21
21
|
def self.instance: () -> Bot
|
|
22
22
|
def describe: (?Symbol verbosity) -> String
|
|
23
23
|
def erase_to_rbs: () -> String
|
|
24
|
-
def top: () ->
|
|
25
|
-
def bot: () ->
|
|
26
|
-
def dynamic: () ->
|
|
24
|
+
def top: () -> Trinary
|
|
25
|
+
def bot: () -> Trinary
|
|
26
|
+
def dynamic: () -> Trinary
|
|
27
27
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
28
28
|
def ==: (untyped other) -> bool
|
|
29
29
|
def hash: () -> Integer
|
|
@@ -35,9 +35,9 @@ module Rigor
|
|
|
35
35
|
def initialize: (Type::t static_facet) -> void
|
|
36
36
|
def describe: (?Symbol verbosity) -> String
|
|
37
37
|
def erase_to_rbs: () -> String
|
|
38
|
-
def top: () ->
|
|
39
|
-
def bot: () ->
|
|
40
|
-
def dynamic: () ->
|
|
38
|
+
def top: () -> Trinary
|
|
39
|
+
def bot: () -> Trinary
|
|
40
|
+
def dynamic: () -> Trinary
|
|
41
41
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
42
42
|
def ==: (untyped other) -> bool
|
|
43
43
|
def hash: () -> Integer
|
|
@@ -49,9 +49,9 @@ module Rigor
|
|
|
49
49
|
def initialize: (untyped value) -> void
|
|
50
50
|
def describe: (?Symbol verbosity) -> String
|
|
51
51
|
def erase_to_rbs: () -> String
|
|
52
|
-
def top: () ->
|
|
53
|
-
def bot: () ->
|
|
54
|
-
def dynamic: () ->
|
|
52
|
+
def top: () -> Trinary
|
|
53
|
+
def bot: () -> Trinary
|
|
54
|
+
def dynamic: () -> Trinary
|
|
55
55
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
56
56
|
def ==: (untyped other) -> bool
|
|
57
57
|
def hash: () -> Integer
|
|
@@ -68,14 +68,14 @@ module Rigor
|
|
|
68
68
|
def finite?: () -> bool
|
|
69
69
|
def cardinality: () -> (Integer | Float)
|
|
70
70
|
def covers?: (untyped value) -> bool
|
|
71
|
-
def lower: () ->
|
|
72
|
-
def upper: () ->
|
|
71
|
+
def lower: () -> (Integer | Float)
|
|
72
|
+
def upper: () -> (Integer | Float)
|
|
73
73
|
def describe: (?Symbol verbosity) -> String
|
|
74
74
|
def generic_description: () -> String
|
|
75
75
|
def erase_to_rbs: () -> String
|
|
76
|
-
def top: () ->
|
|
77
|
-
def bot: () ->
|
|
78
|
-
def dynamic: () ->
|
|
76
|
+
def top: () -> Trinary
|
|
77
|
+
def bot: () -> Trinary
|
|
78
|
+
def dynamic: () -> Trinary
|
|
79
79
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
80
80
|
def ==: (untyped other) -> bool
|
|
81
81
|
def hash: () -> Integer
|
|
@@ -88,9 +88,9 @@ module Rigor
|
|
|
88
88
|
def initialize: (String class_name, ?Array[Type::t] type_args) -> void
|
|
89
89
|
def describe: (?Symbol verbosity) -> String
|
|
90
90
|
def erase_to_rbs: () -> String
|
|
91
|
-
def top: () ->
|
|
92
|
-
def bot: () ->
|
|
93
|
-
def dynamic: () ->
|
|
91
|
+
def top: () -> Trinary
|
|
92
|
+
def bot: () -> Trinary
|
|
93
|
+
def dynamic: () -> Trinary
|
|
94
94
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
95
95
|
def ==: (untyped other) -> bool
|
|
96
96
|
def hash: () -> Integer
|
|
@@ -102,9 +102,9 @@ module Rigor
|
|
|
102
102
|
def initialize: (String class_name) -> void
|
|
103
103
|
def describe: (?Symbol verbosity) -> String
|
|
104
104
|
def erase_to_rbs: () -> String
|
|
105
|
-
def top: () ->
|
|
106
|
-
def bot: () ->
|
|
107
|
-
def dynamic: () ->
|
|
105
|
+
def top: () -> Trinary
|
|
106
|
+
def bot: () -> Trinary
|
|
107
|
+
def dynamic: () -> Trinary
|
|
108
108
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
109
109
|
def ==: (untyped other) -> bool
|
|
110
110
|
def hash: () -> Integer
|
|
@@ -116,9 +116,9 @@ module Rigor
|
|
|
116
116
|
def initialize: (Array[Type::t] members) -> void
|
|
117
117
|
def describe: (?Symbol verbosity) -> String
|
|
118
118
|
def erase_to_rbs: () -> String
|
|
119
|
-
def top: () ->
|
|
120
|
-
def bot: () ->
|
|
121
|
-
def dynamic: () ->
|
|
119
|
+
def top: () -> Trinary
|
|
120
|
+
def bot: () -> Trinary
|
|
121
|
+
def dynamic: () -> Trinary
|
|
122
122
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
123
123
|
def ==: (untyped other) -> bool
|
|
124
124
|
def hash: () -> Integer
|
|
@@ -131,9 +131,9 @@ module Rigor
|
|
|
131
131
|
def initialize: (Type::t base, Type::t removed) -> void
|
|
132
132
|
def describe: (?Symbol verbosity) -> String
|
|
133
133
|
def erase_to_rbs: () -> String
|
|
134
|
-
def top: () ->
|
|
135
|
-
def bot: () ->
|
|
136
|
-
def dynamic: () ->
|
|
134
|
+
def top: () -> Trinary
|
|
135
|
+
def bot: () -> Trinary
|
|
136
|
+
def dynamic: () -> Trinary
|
|
137
137
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
138
138
|
def ==: (untyped other) -> bool
|
|
139
139
|
def hash: () -> Integer
|
|
@@ -146,9 +146,9 @@ module Rigor
|
|
|
146
146
|
def initialize: (Type::t base, Symbol predicate_id) -> void
|
|
147
147
|
def describe: (?Symbol verbosity) -> String
|
|
148
148
|
def erase_to_rbs: () -> String
|
|
149
|
-
def top: () ->
|
|
150
|
-
def bot: () ->
|
|
151
|
-
def dynamic: () ->
|
|
149
|
+
def top: () -> Trinary
|
|
150
|
+
def bot: () -> Trinary
|
|
151
|
+
def dynamic: () -> Trinary
|
|
152
152
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
153
153
|
def matches?: (untyped value) -> bool?
|
|
154
154
|
def ==: (untyped other) -> bool
|
|
@@ -161,9 +161,9 @@ module Rigor
|
|
|
161
161
|
def initialize: (Array[Type::t] members) -> void
|
|
162
162
|
def describe: (?Symbol verbosity) -> String
|
|
163
163
|
def erase_to_rbs: () -> String
|
|
164
|
-
def top: () ->
|
|
165
|
-
def bot: () ->
|
|
166
|
-
def dynamic: () ->
|
|
164
|
+
def top: () -> Trinary
|
|
165
|
+
def bot: () -> Trinary
|
|
166
|
+
def dynamic: () -> Trinary
|
|
167
167
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
168
168
|
def ==: (untyped other) -> bool
|
|
169
169
|
def hash: () -> Integer
|
|
@@ -175,9 +175,9 @@ module Rigor
|
|
|
175
175
|
def initialize: (Array[Type::t] elements) -> void
|
|
176
176
|
def describe: (?Symbol verbosity) -> String
|
|
177
177
|
def erase_to_rbs: () -> String
|
|
178
|
-
def top: () ->
|
|
179
|
-
def bot: () ->
|
|
180
|
-
def dynamic: () ->
|
|
178
|
+
def top: () -> Trinary
|
|
179
|
+
def bot: () -> Trinary
|
|
180
|
+
def dynamic: () -> Trinary
|
|
181
181
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
182
182
|
def ==: (untyped other) -> bool
|
|
183
183
|
def hash: () -> Integer
|
|
@@ -198,9 +198,9 @@ module Rigor
|
|
|
198
198
|
def required_key?: (untyped key) -> bool
|
|
199
199
|
def optional_key?: (untyped key) -> bool
|
|
200
200
|
def read_only_key?: (untyped key) -> bool
|
|
201
|
-
def top: () ->
|
|
202
|
-
def bot: () ->
|
|
203
|
-
def dynamic: () ->
|
|
201
|
+
def top: () -> Trinary
|
|
202
|
+
def bot: () -> Trinary
|
|
203
|
+
def dynamic: () -> Trinary
|
|
204
204
|
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
|
205
205
|
def ==: (untyped other) -> bool
|
|
206
206
|
def hash: () -> Integer
|