···122122 end
123123 end
124124125125- def self.subtype?(type, supertype)
125125+ def self.subtype?(type, supertype, context: nil)
126126+ context ||= SubtypeContext.new
127127+ raw_key = [type.object_id, supertype.object_id]
128128+ raw_acquired = false
129129+ resolved_key = nil
130130+ resolved_acquired = false
131131+132132+ return context.fetch(raw_key) if context.memoized?(raw_key)
133133+ return false unless context.acquire(raw_key)
134134+135135+ raw_acquired = true
136136+126137 subtype = type
127138128139 subtype = subtype.block.call if Types::DeferredType === subtype
129140 supertype = supertype.block.call if Types::DeferredType === supertype
141141+ resolved_key = [subtype.object_id, supertype.object_id]
130142131131- return true if subtype == Types::NeverType::Instance
143143+ if resolved_key != raw_key
144144+ if context.memoized?(resolved_key)
145145+ result = context.fetch(resolved_key)
146146+ context.store(raw_key, result)
147147+ return result
148148+ end
149149+150150+ return false unless context.acquire(resolved_key)
132151133133- return true if supertype == subtype
152152+ resolved_acquired = true
153153+ end
134154135135- case supertype
136136- when Literal::Type
137137- supertype >= subtype
138138- when Module
139139- case subtype
155155+ result = if subtype == Types::NeverType::Instance
156156+ true
157157+ elsif supertype == subtype
158158+ true
159159+ else
160160+ case supertype
161161+ when Literal::Type
162162+ supertype.>=(subtype, context:)
140163 when Module
141141- supertype >= subtype
142142- when Numeric
143143- Numeric >= supertype
144144- when String
145145- String >= supertype
146146- when Symbol
147147- Symbol >= supertype
148148- when ::Array
149149- ::Array >= supertype
150150- when ::Hash
151151- ::Hash >= supertype
152152- when Literal::Type
153153- subtype <= supertype
164164+ case subtype
165165+ when Module
166166+ supertype >= subtype
167167+ when Numeric
168168+ Numeric >= supertype
169169+ when String
170170+ String >= supertype
171171+ when Symbol
172172+ Symbol >= supertype
173173+ when ::Array
174174+ ::Array >= supertype
175175+ when ::Hash
176176+ ::Hash >= supertype
177177+ when Literal::Type
178178+ subtype.<=(supertype, context:)
179179+ else
180180+ false
181181+ end
182182+ when Range
183183+ supertype.cover?(subtype)
154184 else
155185 false
156186 end
157157- when Range
158158- supertype.cover?(subtype)
159159- else
160160- false
187187+ end
188188+189189+ context.store(resolved_key, result)
190190+ context.store(raw_key, result)
191191+ result
192192+ ensure
193193+ if context
194194+ context.release(resolved_key) if resolved_acquired
195195+ context.release(raw_key) if raw_acquired
161196 end
162197 end
163198end
+2-2
lib/literal/array.rb
···2020 Literal::Array === value && Literal.subtype?(value.__type__, @type)
2121 end
22222323- def >=(other)
2323+ def >=(other, context: nil)
2424 case other
2525 when Literal::Array::Generic
2626- Literal.subtype?(other.type, @type)
2626+ Literal.subtype?(other.type, @type, context:)
2727 else
2828 false
2929 end
+33
lib/literal/subtype_context.rb
···11+# frozen_string_literal: true
22+33+class Literal::SubtypeContext
44+ def initialize
55+ @memo = {}
66+ @in_progress = {}
77+ end
88+99+ attr_reader :memo, :in_progress
1010+1111+ def memoized?(key)
1212+ @memo.key?(key)
1313+ end
1414+1515+ def fetch(key)
1616+ @memo[key]
1717+ end
1818+1919+ def store(key, result)
2020+ @memo[key] = result
2121+ end
2222+2323+ def acquire(key)
2424+ return false if @in_progress.key?(key)
2525+2626+ @in_progress[key] = true
2727+ true
2828+ end
2929+3030+ def release(key)
3131+ @in_progress.delete(key)
3232+ end
3333+end
+2-2
lib/literal/tuple.rb
···2626 true
2727 end
28282929- def >=(other)
2929+ def >=(other, context: nil)
3030 case other
3131 when Literal::Tuple::Generic
3232 types = @types
···36363737 i, len = 0, types.size
3838 while i < len
3939- return false unless Literal.subtype?(other_types[i], types[i])
3939+ return false unless Literal.subtype?(other_types[i], types[i], context:)
4040 i += 1
4141 end
4242
+3-3
lib/literal/type.rb
···11# frozen_string_literal: true
2233module Literal::Type
44- def >=(other)
44+ def >=(other, context: nil)
55 self == other
66 end
7788- def <=(other)
88+ def <=(other, context: nil)
99 case other
1010 when Literal::Type
1111- other >= self
1111+ other.>=(self, context:)
1212 else
1313 false
1414 end
···2424 Array === value && value.all?(@type)
2525 end
26262727- def >=(other)
2727+ def >=(other, context: nil)
2828 case other
2929 when Literal::Types::ArrayType
3030- Literal.subtype?(other.type, @type)
3030+ Literal.subtype?(other.type, @type, context:)
3131 else
3232 false
3333 end
+1-1
lib/literal/types/boolean_type.rb
···1414 true == value || false == value
1515 end
16161717- def >=(other)
1717+ def >=(other, context: nil)
1818 case other
1919 when true, false, Literal::Types::BooleanType
2020 true
+3-3
lib/literal/types/class_type.rb
···1919 Class === value && (value == @type || value < @type)
2020 end
21212222- def >=(other)
2222+ def >=(other, context: nil)
2323 case other
2424 when Literal::Types::ClassType
2525- Literal.subtype?(other.type, @type)
2525+ Literal.subtype?(other.type, @type, context:)
2626 when Literal::Types::DescendantType
2727- (Class === other.type) && Literal.subtype?(other.type, @type)
2727+ (Class === other.type) && Literal.subtype?(other.type, @type, context:)
2828 else
2929 false
3030 end
+7-7
lib/literal/types/constraint_type.rb
···4141 result
4242 end
43434444- def >=(other)
4444+ def >=(other, context: nil)
4545 case other
4646 when Literal::Types::ConstraintType
4747 other_object_constraints = other.object_constraints
4848 return false unless @object_constraints.all? do |constraint|
4949- other_object_constraints.any? { |c| Literal.subtype?(c, constraint) }
4949+ other_object_constraints.any? { |c| Literal.subtype?(c, constraint, context:) }
5050 end
51515252 other_property_constraints = other.property_constraints
5353 return false unless @property_constraints.all? do |k, v|
5454- Literal.subtype?(other_property_constraints[k], v)
5454+ Literal.subtype?(other_property_constraints[k], v, context:)
5555 end
56565757 true
5858 when Literal::Types::IntersectionType
5959 other_object_constraints = other.types
6060 return false unless @object_constraints.all? do |constraint|
6161- other_object_constraints.any? { |c| Literal.subtype?(c, constraint) }
6161+ other_object_constraints.any? { |c| Literal.subtype?(c, constraint, context:) }
6262 end
63636464 true
6565 when Literal::Types::FrozenType
6666- @object_constraints.all? { |constraint| Literal.subtype?(other.type, constraint) }
6666+ @object_constraints.all? { |constraint| Literal.subtype?(other.type, constraint, context:) }
6767 else
6868 false
6969 end
7070 end
71717272- def <=(other)
7272+ def <=(other, context: nil)
7373 case other
7474 when Module
7575- @object_constraints.any? { |constraint| Literal.subtype?(constraint, other) }
7575+ @object_constraints.any? { |constraint| Literal.subtype?(constraint, other, context:) }
7676 end
7777 end
7878
+2-2
lib/literal/types/deferred_type.rb
···1717 @block.call === other
1818 end
19192020- def >=(other)
2121- Literal.subtype?(other, @block.call)
2020+ def >=(other, context: nil)
2121+ Literal.subtype?(other, @block.call, context:)
2222 end
2323end
+2-2
lib/literal/types/descendant_type.rb
···1818 Module === value && value < @type
1919 end
20202121- def >=(other)
2121+ def >=(other, context: nil)
2222 case other
2323 when Literal::Types::DescendantType, Literal::Types::ClassType
2424- Literal.subtype?(other.type, @type)
2424+ Literal.subtype?(other.type, @type, context:)
2525 else
2626 false
2727 end
+2-2
lib/literal/types/enumerable_type.rb
···1919 Enumerable === value && value.all?(@type)
2020 end
21212222- def >=(other)
2222+ def >=(other, context: nil)
2323 case other
2424 when Literal::Types::EnumerableType
2525- Literal.subtype?(other.type, @type)
2525+ Literal.subtype?(other.type, @type, context:)
2626 else
2727 false
2828 end
+1-1
lib/literal/types/falsy_type.rb
···1818 !value
1919 end
20202121- def >=(other)
2121+ def >=(other, context: nil)
2222 case other
2323 when Literal::Types::FalsyType, nil, false
2424 true
+4-4
lib/literal/types/frozen_type.rb
···2525 value.frozen? && @type === value
2626 end
27272828- def >=(other)
2828+ def >=(other, context: nil)
2929 case other
3030 when Literal::Types::FrozenType
3131- Literal.subtype?(other.type, @type)
3131+ Literal.subtype?(other.type, @type, context:)
3232 when Literal::Types::ConstraintType
3333 type_match = false
3434- frozen_match = Literal.subtype?(other.property_constraints[:frozen?], true)
3434+ frozen_match = Literal.subtype?(other.property_constraints[:frozen?], true, context:)
35353636 other.object_constraints.each do |constraint|
3737 frozen_match ||= ALWAYS_FROZEN.include?(constraint)
3838- type_match ||= Literal.subtype?(constraint, @type)
3838+ type_match ||= Literal.subtype?(constraint, @type, context:)
3939 return true if frozen_match && type_match
4040 end
4141
···1313 !!value
1414 end
15151616- def >=(other)
1616+ def >=(other, context: nil)
1717 case other
1818 when Literal::Types::TruthyType, true
1919 true
+1-1
lib/literal/types/tuple_type.rb
···4747 end
4848 end
49495050- def >=(other)
5050+ def >=(other, context: nil)
5151 case other
5252 when Literal::Types::TupleType
5353 @types == other.types