rdl 2.1.0 → 2.2.0
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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +7 -6
- data/CHANGES.md +29 -0
- data/README.md +94 -26
- data/lib/rdl/boot.rb +82 -41
- data/lib/rdl/boot_rails.rb +5 -0
- data/lib/rdl/config.rb +9 -1
- data/lib/rdl/query.rb +2 -2
- data/lib/rdl/typecheck.rb +972 -225
- data/lib/rdl/types/annotated_arg.rb +8 -0
- data/lib/rdl/types/ast_node.rb +73 -0
- data/lib/rdl/types/bot.rb +8 -0
- data/lib/rdl/types/bound_arg.rb +63 -0
- data/lib/rdl/types/computed.rb +48 -0
- data/lib/rdl/types/dependent_arg.rb +9 -0
- data/lib/rdl/types/dynamic.rb +61 -0
- data/lib/rdl/types/finite_hash.rb +54 -9
- data/lib/rdl/types/generic.rb +33 -0
- data/lib/rdl/types/intersection.rb +8 -0
- data/lib/rdl/types/lexer.rex +6 -1
- data/lib/rdl/types/lexer.rex.rb +13 -1
- data/lib/rdl/types/method.rb +14 -0
- data/lib/rdl/types/nominal.rb +8 -0
- data/lib/rdl/types/non_null.rb +8 -0
- data/lib/rdl/types/optional.rb +8 -0
- data/lib/rdl/types/parser.racc +31 -5
- data/lib/rdl/types/parser.tab.rb +540 -302
- data/lib/rdl/types/rdl_types.rb +45 -0
- data/lib/rdl/types/singleton.rb +14 -1
- data/lib/rdl/types/string.rb +104 -0
- data/lib/rdl/types/structural.rb +8 -0
- data/lib/rdl/types/top.rb +8 -0
- data/lib/rdl/types/tuple.rb +32 -8
- data/lib/rdl/types/type.rb +54 -11
- data/lib/rdl/types/union.rb +41 -2
- data/lib/rdl/types/var.rb +10 -0
- data/lib/rdl/types/vararg.rb +8 -0
- data/lib/rdl/util.rb +13 -10
- data/lib/rdl/wrap.rb +271 -27
- data/lib/rdl_disable.rb +16 -2
- data/lib/types/active_record.rb +1 -0
- data/lib/types/core/array.rb +442 -23
- data/lib/types/core/basic_object.rb +3 -3
- data/lib/types/core/bigdecimal.rb +5 -0
- data/lib/types/core/class.rb +2 -0
- data/lib/types/core/dir.rb +3 -3
- data/lib/types/core/enumerable.rb +4 -4
- data/lib/types/core/enumerator.rb +1 -1
- data/lib/types/core/file.rb +4 -4
- data/lib/types/core/float.rb +203 -0
- data/lib/types/core/hash.rb +390 -15
- data/lib/types/core/integer.rb +223 -10
- data/lib/types/core/io.rb +2 -2
- data/lib/types/core/kernel.rb +8 -5
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/module.rb +3 -3
- data/lib/types/core/numeric.rb +0 -2
- data/lib/types/core/object.rb +5 -5
- data/lib/types/core/pathname.rb +2 -2
- data/lib/types/core/process.rb +1 -3
- data/lib/types/core/range.rb +1 -1
- data/lib/types/core/regexp.rb +2 -2
- data/lib/types/core/set.rb +1 -1
- data/lib/types/core/string.rb +408 -16
- data/lib/types/core/symbol.rb +3 -3
- data/lib/types/core/time.rb +1 -1
- data/lib/types/core/uri.rb +13 -13
- data/lib/types/rails/_helpers.rb +7 -1
- data/lib/types/rails/action_controller/mime_responds.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +42 -30
- data/lib/types/rails/active_record/comp_types.rb +637 -0
- data/lib/types/rails/active_record/finder_methods.rb +1 -1
- data/lib/types/rails/active_record/model_schema.rb +28 -16
- data/lib/types/rails/active_record/relation.rb +5 -3
- data/lib/types/rails/active_record/sql-strings.rb +166 -0
- data/lib/types/rails/string.rb +1 -1
- data/lib/types/sequel.rb +1 -0
- data/lib/types/sequel/comp_types.rb +581 -0
- data/rdl.gemspec +5 -4
- data/test/test_alias.rb +4 -0
- data/test/test_array_types.rb +244 -0
- data/test/test_bound_types.rb +80 -0
- data/test/test_contract.rb +4 -0
- data/test/test_dsl.rb +5 -0
- data/test/test_dyn_comptype_checks.rb +206 -0
- data/test/test_generic.rb +21 -20
- data/test/test_hash_types.rb +322 -0
- data/test/test_intersection.rb +1 -0
- data/test/test_le.rb +29 -4
- data/test/test_member.rb +3 -1
- data/test/test_parser.rb +5 -0
- data/test/test_query.rb +1 -0
- data/test/test_rdl.rb +63 -28
- data/test/test_rdl_type.rb +4 -0
- data/test/test_string_types.rb +102 -0
- data/test/test_type_contract.rb +59 -37
- data/test/test_typecheck.rb +480 -75
- data/test/test_types.rb +17 -0
- data/test/test_wrap.rb +5 -0
- metadata +35 -5
- data/lib/types/rails/active_record/schema_types.rb +0 -51
@@ -0,0 +1,45 @@
|
|
1
|
+
RDL.type_params 'RDL::Type::SingletonType', [:t], :satisfies?
|
2
|
+
|
3
|
+
RDL.type 'RDL::Type::SingletonType', :initialize, "(x) -> self<x>", wrap: false, effect: [:+, :+]
|
4
|
+
RDL.type 'RDL::Type::SingletonType', :val, "() -> t", wrap: false, effect: [:+, :+]
|
5
|
+
RDL.type 'RDL::Type::SingletonType', :nominal, "() -> RDL::Type::NominalType", wrap: false, effect: [:+, :+]
|
6
|
+
|
7
|
+
RDL.type 'RDL::Type::NominalType', :initialize, "(Class or String) -> self", wrap: false, effect: [:+, :+]
|
8
|
+
RDL.type 'RDL::Type::NominalType', :klass, "() -> Class", wrap: false, effect: [:+, :+]
|
9
|
+
RDL.type 'RDL::Type::NominalType', :name, "() -> String", wrap: false, effect: [:+, :+]
|
10
|
+
|
11
|
+
RDL.type 'RDL::Type::GenericType', :initialize, "(RDL::Type::Type, *RDL::Type::Type) -> self", wrap: false, effect: [:+, :+]
|
12
|
+
RDL.type 'RDL::Type::GenericType', :params, "() -> Array<RDL::Type::Type>", wrap: false, effect: [:+, :+]
|
13
|
+
RDL.type 'RDL::Type::GenericType', :base, "() -> RDL::Type::NominalType", wrap: false, effect: [:+, :+]
|
14
|
+
|
15
|
+
RDL.type 'RDL::Type::UnionType', :initialize, "(*RDL::Type::Type) -> self", wrap: false, effect: [:+, :+]
|
16
|
+
RDL.type 'RDL::Type::UnionType', :canonical, "() -> RDL::Type::Type", wrap: false, effect: [:+, :+]
|
17
|
+
RDL.type 'RDL::Type::UnionType', :types, "() -> Array<RDL::Type::Type>", wrap: false, effect: [:+, :+]
|
18
|
+
|
19
|
+
RDL.type 'RDL::Type::TupleType', :initialize, "(*RDL::Type::Type) -> self", wrap: false, effect: [:+, :+]
|
20
|
+
RDL.type 'RDL::Type::TupleType', :params, "() -> Array<RDL::Type::Type>", wrap: false, effect: [:+, :+]
|
21
|
+
RDL.type 'RDL::Type::TupleType', :promote, "(?RDL::Type::Type) -> RDL::Type::GenericType", wrap: false, effect: [:+, :+]
|
22
|
+
RDL.type 'RDL::Type::TupleType', :promote!, "(?RDL::Type::Type) -> %bool", wrap: false, effect: [:-, :+]
|
23
|
+
RDL.type 'RDL::Type::TupleType', :check_bounds, "(?%bool) -> %bool", wrap: false, effect: [:+, :+]
|
24
|
+
|
25
|
+
RDL.type 'RDL::Type::FiniteHashType', :elts, "() -> Hash<%any, RDL::Type::Type>", wrap: false, effect: [:+, :+]
|
26
|
+
RDL.type 'RDL::Type::FiniteHashType', :elts=, "(Hash<%any, RDL::Type::Type>) -> Hash<%any, RDL::Type::Type>", wrap: false, effect: [:-, :+]
|
27
|
+
RDL.type 'RDL::Type::FiniteHashType', :promote, "(?%any, ?RDL::Type::Type) -> RDL::Type::GenericType", wrap: false, effect: [:+, :+]
|
28
|
+
RDL.type 'RDL::Type::FiniteHashType', :promote!, "(?%any, ?RDL::Type::Type) -> %bool", wrap: false, effect: [:-, :+]
|
29
|
+
RDL.type 'RDL::Type::FiniteHashType', :initialize, "(Hash<%any, RDL::Type::Type> or {}, ?RDL::Type::Type) -> self", wrap: false, effect: [:+, :+]
|
30
|
+
RDL.type 'RDL::Type::FiniteHashType', :check_bounds, "(?%bool) -> %bool", wrap: false, effect: [:+, :+]
|
31
|
+
|
32
|
+
RDL.type 'RDL::Type::OptionalType', :initialize, "(RDL::Type::Type) -> self", wrap: false, effect: [:+, :+]
|
33
|
+
|
34
|
+
RDL.type 'RDL::Type::VarargType', :initialize, '(RDL::Type::Type) -> self', wrap: false, effect: [:+, :+]
|
35
|
+
|
36
|
+
RDL.type 'RDL::Type::VarType', :initialize, "(String) -> self", wrap: false, effect: [:+, :+]
|
37
|
+
|
38
|
+
RDL.type "RDL::Type::Type", 'self.leq', "(RDL::Type::Type, RDL::Type::Type) -> %bool", wrap: false, effect: [:+, :+]
|
39
|
+
|
40
|
+
RDL.type 'RDL::Globals', 'self.parser', "() -> RDL::Type::Parser", wrap: false, effect: [:+, :+]
|
41
|
+
RDL.type 'RDL::Globals', 'self.types', "() -> Hash<Symbol, RDL::Type::Type>", wrap: false, effect: [:+, :+]
|
42
|
+
RDL.type 'RDL::Type::Parser', :scan_str, "(String) -> RDL::Type::Type", wrap: false, effect: [:+, :+]
|
43
|
+
RDL.type 'RDL::Config', 'self.instance', "() -> RDL::Config", wrap: false, effect: [:+, :+]
|
44
|
+
RDL.type 'RDL::Config', 'weak_update_promote', "() -> %bool", wrap: false, effect: [:+, :+]
|
45
|
+
RDL.type "Object", '__getobj__', "() -> self", wrap: false, effect: [:+, :+] ## needed due to type casting
|
data/lib/rdl/types/singleton.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module RDL::Type
|
2
2
|
class SingletonType < Type
|
3
|
-
|
3
|
+
attr_accessor :val
|
4
4
|
attr_reader :nominal
|
5
5
|
|
6
6
|
@@cache = {}
|
@@ -59,11 +59,24 @@ module RDL::Type
|
|
59
59
|
def member?(obj, *args)
|
60
60
|
t = RDL::Util.rdl_type obj
|
61
61
|
return t <= self if t
|
62
|
+
return true if obj.nil?
|
62
63
|
obj.equal?(@val)
|
63
64
|
end
|
64
65
|
|
65
66
|
def instantiate(inst)
|
66
67
|
return self
|
67
68
|
end
|
69
|
+
|
70
|
+
def widen
|
71
|
+
return self
|
72
|
+
end
|
73
|
+
|
74
|
+
def copy
|
75
|
+
return self
|
76
|
+
end
|
77
|
+
|
78
|
+
def satisfies?
|
79
|
+
yield(val)
|
80
|
+
end
|
68
81
|
end
|
69
82
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module RDL::Type
|
2
|
+
# A specialized precise type for Strings
|
3
|
+
class PreciseStringType < Type
|
4
|
+
attr_accessor :vals
|
5
|
+
attr_accessor :interp, :ubounds, :lbounds
|
6
|
+
|
7
|
+
## @vals will be an array. For non-interpolated strings, it will only have one element, the string itself.
|
8
|
+
## For interpolated strings, it will include strings and RDL types, in order, where the types represent
|
9
|
+
## the types of the interpolated portions, and the strings represent the surrounding string parts.
|
10
|
+
def initialize(*vals)
|
11
|
+
@interp = false
|
12
|
+
vals.each { |v|
|
13
|
+
case v
|
14
|
+
when String
|
15
|
+
## do nothing
|
16
|
+
when Type
|
17
|
+
## interpolated string
|
18
|
+
@interp = true
|
19
|
+
else
|
20
|
+
raise RuntimeError, "Attempt to create precise string type with non-string or non-type value #{v}" unless vals.all? { |val| val.is_a?(Type) || val.is_a?(String) }
|
21
|
+
end
|
22
|
+
}
|
23
|
+
vals = [vals.join] if !@interp && vals.size > 1 ## if all elements of `vals` are strings, join them into one
|
24
|
+
|
25
|
+
@vals = vals
|
26
|
+
@ubounds = []
|
27
|
+
@lbounds = []
|
28
|
+
@promoted = false
|
29
|
+
@cant_promote = false
|
30
|
+
super()
|
31
|
+
end
|
32
|
+
|
33
|
+
def canonical
|
34
|
+
return RDL::Globals.types[:string] if @promoted
|
35
|
+
return self
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
return RDL::Globals.types[:string].to_s if @promoted
|
40
|
+
printed_vals = @vals.map { |v|
|
41
|
+
case v
|
42
|
+
when String
|
43
|
+
"'"+v+"'"
|
44
|
+
when Type
|
45
|
+
#'#{' + v.to_s.gsub('"',"'") + '}'
|
46
|
+
return "String"
|
47
|
+
end
|
48
|
+
}
|
49
|
+
return printed_vals.join
|
50
|
+
end
|
51
|
+
|
52
|
+
def ==(other)
|
53
|
+
return false if other.nil?
|
54
|
+
return RDL::Globals.types[:string] == other if @promoted
|
55
|
+
other = other.canonical
|
56
|
+
return (other.instance_of? PreciseStringType) && (other.vals == @vals)
|
57
|
+
end
|
58
|
+
|
59
|
+
def member?(obj, *args)
|
60
|
+
return false unless obj.is_a?(String)
|
61
|
+
raise "Checking membership of PreciseStringType not currently supported for interpolated strings." unless @vals.all? { |v| v.is_a?(String) }
|
62
|
+
return (@vals.join == obj)
|
63
|
+
end
|
64
|
+
|
65
|
+
def promote!
|
66
|
+
return false if @cant_promote
|
67
|
+
@promoted = true
|
68
|
+
check_bounds
|
69
|
+
end
|
70
|
+
|
71
|
+
def check_bounds
|
72
|
+
return (@lbounds.all? { |lbound| lbound <= self }) && (@ubounds.all? { |ubound| self <= ubound } )
|
73
|
+
end
|
74
|
+
|
75
|
+
def cant_promote!
|
76
|
+
raise RuntimeError, "already promoted!" if @promoted
|
77
|
+
@cant_promote = true
|
78
|
+
end
|
79
|
+
|
80
|
+
def <=(other, no_constraint=false)
|
81
|
+
return Type.leq(self, other, no_constraint: no_constraint)
|
82
|
+
end
|
83
|
+
|
84
|
+
def instantiate(inst)
|
85
|
+
return RDL::Globals.types[:string] if @promoted
|
86
|
+
@vals.map! { |v| if v.is_a?(Type) then v.instantiate(inst) else v end }
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def widen
|
91
|
+
@vals.map! { |v| if v.is_a?(Type) then v.widen else v end }
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def copy
|
96
|
+
return PreciseStringType.new(*@vals.map { |v| if v.is_a?(String) then v.clone else v.copy end })
|
97
|
+
end
|
98
|
+
|
99
|
+
def hash
|
100
|
+
99 * @vals.hash
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
data/lib/rdl/types/structural.rb
CHANGED
@@ -47,6 +47,14 @@ module RDL::Type
|
|
47
47
|
StructuralType.new(Hash[*@methods.each_pair.map { |m, t| [m, t.instantiate(inst)] }.flatten])
|
48
48
|
end
|
49
49
|
|
50
|
+
def widen
|
51
|
+
StructuralType.new(Hash[*@methods.each_pair.map { |m, t| [m, t.widen] }.flatten])
|
52
|
+
end
|
53
|
+
|
54
|
+
def copy
|
55
|
+
StructuralType.new(Hash[*@methods.each_pair.map { |m, t| [m, t.copy] }.flatten])
|
56
|
+
end
|
57
|
+
|
50
58
|
def ==(other) # :nodoc:
|
51
59
|
return false if other.nil?
|
52
60
|
other = other.canonical
|
data/lib/rdl/types/top.rb
CHANGED
data/lib/rdl/types/tuple.rb
CHANGED
@@ -44,12 +44,23 @@ module RDL::Type
|
|
44
44
|
return (other.instance_of? TupleType) && (@params.length == other.params.length) && (@params.zip(other.params).all? { |t,o| t.match(o) })
|
45
45
|
end
|
46
46
|
|
47
|
-
def promote
|
47
|
+
def promote(t=nil)
|
48
48
|
return false if @cant_promote
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
param = UnionType.new(*@params, t)
|
50
|
+
param = param.widen if RDL::Config.instance.promote_widen
|
51
|
+
GenericType.new(RDL::Globals.types[:array], param)
|
52
|
+
end
|
53
|
+
|
54
|
+
### TODO: similar question as in tuple types. Should [1,2,3] be promoted to Array<1 or 2 or 3> or Array<Integer>
|
55
|
+
def promote!(t=nil)
|
56
|
+
array = promote(t)
|
57
|
+
return false if !array
|
58
|
+
@array = array
|
59
|
+
check_bounds
|
60
|
+
end
|
61
|
+
|
62
|
+
def check_bounds(no_promote=false)
|
63
|
+
return (@lbounds.all? { |lbound| lbound.<=(self, no_promote )}) && (@ubounds.all? { |ubound| self.<=(ubound, no_promote) })
|
53
64
|
end
|
54
65
|
|
55
66
|
def cant_promote!
|
@@ -57,8 +68,8 @@ module RDL::Type
|
|
57
68
|
@cant_promote = true
|
58
69
|
end
|
59
70
|
|
60
|
-
def <=(other)
|
61
|
-
return Type.leq(self, other)
|
71
|
+
def <=(other, no_constraint=false)
|
72
|
+
return Type.leq(self, other, no_constraint: no_constraint)
|
62
73
|
end
|
63
74
|
|
64
75
|
def member?(obj, *args)
|
@@ -71,7 +82,20 @@ module RDL::Type
|
|
71
82
|
|
72
83
|
def instantiate(inst)
|
73
84
|
return @array.instantiate(inst) if @array
|
74
|
-
return TupleType.new(*@params.map { |t| t.instantiate(inst) })
|
85
|
+
#return TupleType.new(*@params.map { |t| t.instantiate(inst) })
|
86
|
+
@params.map! { |t| t.instantiate(inst) }
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def widen
|
91
|
+
return @array.widen if @array
|
92
|
+
#return TupleType.new(*@params.map { |t| t.widen })
|
93
|
+
@params.map! { |t| t.widen }
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def copy
|
98
|
+
return TupleType.new(*@params.map { |t| t.copy })
|
75
99
|
end
|
76
100
|
|
77
101
|
def hash
|
data/lib/rdl/types/type.rb
CHANGED
@@ -32,10 +32,11 @@ module RDL::Type
|
|
32
32
|
# [+ other +] is a Type
|
33
33
|
# [+ inst +] is a Hash<Symbol, Type> representing an instantiation
|
34
34
|
# [+ ileft +] is a %bool
|
35
|
+
# [+ no_constraint +] is a %bool indicating whether or not we should add to tuple/FHT constraints
|
35
36
|
# if inst is nil, returns self <= other
|
36
37
|
# if inst is non-nil and ileft, returns inst(self) <= other, possibly mutating inst to make this true
|
37
38
|
# if inst is non-nil and !ileft, returns self <= inst(other), again possibly mutating inst
|
38
|
-
def self.leq(left, right, inst=nil, ileft=true)
|
39
|
+
def self.leq(left, right, inst=nil, ileft=true, no_constraint: false)
|
39
40
|
left = inst[left.name] if inst && ileft && left.is_a?(VarType) && inst[left.name]
|
40
41
|
right = inst[right.name] if inst && !ileft && right.is_a?(VarType) && inst[right.name]
|
41
42
|
left = left.type if left.is_a? DependentArgType
|
@@ -49,6 +50,10 @@ module RDL::Type
|
|
49
50
|
return true if left.is_a? BotType
|
50
51
|
return true if right.is_a? TopType
|
51
52
|
|
53
|
+
# dynamic
|
54
|
+
return true if left.is_a? DynamicType
|
55
|
+
return true if right.is_a? DynamicType
|
56
|
+
|
52
57
|
# type variables
|
53
58
|
begin inst.merge!(left.name => right); return true end if inst && ileft && left.is_a?(VarType)
|
54
59
|
begin inst.merge!(right.name => left); return true end if inst && !ileft && right.is_a?(VarType)
|
@@ -72,12 +77,24 @@ module RDL::Type
|
|
72
77
|
|
73
78
|
# nominal
|
74
79
|
return left.klass.ancestors.member?(right.klass) if left.is_a?(NominalType) && right.is_a?(NominalType)
|
75
|
-
if left.is_a?(NominalType) && right.is_a?(StructuralType)
|
80
|
+
if (left.is_a?(NominalType) || left.is_a?(TupleType) || left.is_a?(FiniteHashType) || left.is_a?(PreciseStringType)) && right.is_a?(StructuralType)
|
81
|
+
case left
|
82
|
+
when TupleType
|
83
|
+
lklass = Array
|
84
|
+
when FiniteHashType
|
85
|
+
lklass = Hash
|
86
|
+
when PreciseStringType
|
87
|
+
lklass = String
|
88
|
+
else
|
89
|
+
lklass = left.klass
|
90
|
+
end
|
76
91
|
right.methods.each_pair { |m, t|
|
77
|
-
return false unless
|
78
|
-
types = RDL::Globals.info.get(
|
92
|
+
return false unless lklass.method_defined? m
|
93
|
+
types = RDL::Globals.info.get(lklass, m, :type)
|
79
94
|
if types
|
80
|
-
|
95
|
+
non_dep_types = RDL::Typecheck.filter_comp_types(types, false)
|
96
|
+
raise "Need non-dependent types for method #{m} of class #{lklass} in order to use a structural type." if non_dep_types.empty?
|
97
|
+
return false unless non_dep_types.all? { |tlm| leq(tlm, t, nil, ileft) }
|
81
98
|
# inst above is nil because the method types inside the class and
|
82
99
|
# inside the structural type have an implicit quantifier on them. So
|
83
100
|
# even if we're allowed to instantiate type variables we can't do that
|
@@ -98,7 +115,7 @@ module RDL::Type
|
|
98
115
|
# do check here to avoid hiding errors if generic type written
|
99
116
|
# with wrong number of parameters but never checked against
|
100
117
|
# instantiated instances
|
101
|
-
raise TypeError, "No type parameters defined for #{base.name}" unless formals
|
118
|
+
raise TypeError, "No type parameters defined for #{left.base.name}" unless formals
|
102
119
|
return false unless left.base == right.base
|
103
120
|
return variance.zip(left.params, right.params).all? { |v, tl, tr|
|
104
121
|
case v
|
@@ -116,7 +133,7 @@ module RDL::Type
|
|
116
133
|
if left.is_a?(GenericType) && right.is_a?(StructuralType)
|
117
134
|
# similar to logic above for leq(NominalType, StructuralType, ...)
|
118
135
|
formals, variance, _ = RDL::Globals.type_params[left.base.name]
|
119
|
-
raise TypeError, "No type parameters defined for #{base.name}" unless formals
|
136
|
+
raise TypeError, "No type parameters defined for #{left.base.name}" unless formals
|
120
137
|
base_inst = Hash[*formals.zip(left.params).flatten] # instantiation for methods in base's class
|
121
138
|
klass = left.base.klass
|
122
139
|
right.methods.each_pair { |meth, t|
|
@@ -162,8 +179,8 @@ module RDL::Type
|
|
162
179
|
return false unless left.params.length == right.params.length
|
163
180
|
return false unless left.params.zip(right.params).all? { |lt, rt| leq(lt, rt, inst, ileft) }
|
164
181
|
# subyping check passed
|
165
|
-
left.ubounds << right
|
166
|
-
right.lbounds << left
|
182
|
+
left.ubounds << right unless no_constraint
|
183
|
+
right.lbounds << left unless no_constraint
|
167
184
|
return true
|
168
185
|
end
|
169
186
|
if left.is_a?(TupleType) && right.is_a?(GenericType) && right.base == RDL::Globals.types[:array]
|
@@ -196,8 +213,8 @@ module RDL::Type
|
|
196
213
|
# If left has optional stuff, right needs to accept it
|
197
214
|
return false unless !(right.rest.nil?) && leq(left.rest, right.rest, inst, ileft)
|
198
215
|
end
|
199
|
-
left.ubounds << right
|
200
|
-
right.lbounds << left
|
216
|
+
left.ubounds << right unless no_constraint
|
217
|
+
right.lbounds << left unless no_constraint
|
201
218
|
return true
|
202
219
|
end
|
203
220
|
if left.is_a?(FiniteHashType) && right.is_a?(GenericType) && right.base == RDL::Globals.types[:hash]
|
@@ -206,6 +223,32 @@ module RDL::Type
|
|
206
223
|
return leq(left, right, inst, ileft) # recheck for promoted type
|
207
224
|
end
|
208
225
|
|
226
|
+
## precise string
|
227
|
+
if left.is_a?(PreciseStringType)
|
228
|
+
if right.is_a?(PreciseStringType)
|
229
|
+
return false if left.vals.size != right.vals.size
|
230
|
+
left.vals.each_with_index { |v, i|
|
231
|
+
if v.is_a?(String) && right.vals[i].is_a?(String)
|
232
|
+
return false unless v == right.vals[i]
|
233
|
+
elsif v.is_a?(Type) && right.vals[i].is_a?(Type)
|
234
|
+
return false unless v <= right.vals[i]
|
235
|
+
else
|
236
|
+
return false
|
237
|
+
end
|
238
|
+
}
|
239
|
+
left.ubounds << right unless no_constraint
|
240
|
+
right.lbounds << left unless no_constraint
|
241
|
+
return true
|
242
|
+
elsif right == RDL::Globals.types[:string]
|
243
|
+
return false unless left.promote!
|
244
|
+
return true
|
245
|
+
elsif right.is_a?(NominalType) && String.ancestors.include?(RDL::Util.to_class(right.name))
|
246
|
+
## necessary because of checking agains union types: we don't want to promote! unless it will work
|
247
|
+
left.promote!
|
248
|
+
return true
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
209
252
|
return false
|
210
253
|
end
|
211
254
|
end
|
data/lib/rdl/types/union.rb
CHANGED
@@ -14,11 +14,11 @@ module RDL::Type
|
|
14
14
|
if t.instance_of? UnionType
|
15
15
|
ts.concat t.types
|
16
16
|
else
|
17
|
-
raise RuntimeError, "Attempt to create union type with non-type" unless t.is_a?
|
17
|
+
raise RuntimeError, "Attempt to create union type with non-type" unless t.is_a?(Type) || t.nil?
|
18
18
|
raise RuntimeError, "Attempt to create union with optional type" if t.is_a? OptionalType
|
19
19
|
raise RuntimeError, "Attempt to create union with vararg type" if t.is_a? VarargType
|
20
20
|
raise RuntimeError, "Attempt to create union with annotated type" if t.is_a? AnnotatedArgType
|
21
|
-
ts << t
|
21
|
+
ts << t if t
|
22
22
|
end
|
23
23
|
}
|
24
24
|
return ts[0] if ts.size == 1
|
@@ -114,6 +114,45 @@ module RDL::Type
|
|
114
114
|
return UnionType.new(*(@types.map { |t| t.instantiate(inst) }))
|
115
115
|
end
|
116
116
|
|
117
|
+
def widen
|
118
|
+
return @canonical.widen if @canonical
|
119
|
+
sing_types = []
|
120
|
+
non_sing_types = []
|
121
|
+
@types.each { |t|
|
122
|
+
case t
|
123
|
+
when SingletonType
|
124
|
+
sing_types << t.nominal
|
125
|
+
when TupleType
|
126
|
+
sing_types << t.promote
|
127
|
+
when FiniteHashType
|
128
|
+
sing_types << t.promote
|
129
|
+
else
|
130
|
+
non_sing_types << t
|
131
|
+
end
|
132
|
+
}
|
133
|
+
UnionType.new(*sing_types, *non_sing_types).canonical
|
134
|
+
end
|
135
|
+
|
136
|
+
def copy
|
137
|
+
UnionType.new(*@types.map { |t| t.copy })
|
138
|
+
end
|
139
|
+
=begin
|
140
|
+
def self.widened_type(u)
|
141
|
+
return u unless u.instance_of?(UnionType)
|
142
|
+
nominal_class = nil
|
143
|
+
@types.each { |t|
|
144
|
+
return u if !t.instance_of?(SingletonType)
|
145
|
+
if nominal_class.nil?
|
146
|
+
nominal_class = t.nominal ## first type
|
147
|
+
elsif nominal_class <= t.nominal
|
148
|
+
nominal_class = t.nominal ## update to more general class (or just stay the same)
|
149
|
+
else
|
150
|
+
return u ## found a case with differing singleton classes, can't widen
|
151
|
+
end
|
152
|
+
}
|
153
|
+
nominal_class
|
154
|
+
end
|
155
|
+
=end
|
117
156
|
def hash # :nodoc:
|
118
157
|
return @hash
|
119
158
|
end
|