rdl 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|