rdl 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +7 -6
  4. data/CHANGES.md +29 -0
  5. data/README.md +94 -26
  6. data/lib/rdl/boot.rb +82 -41
  7. data/lib/rdl/boot_rails.rb +5 -0
  8. data/lib/rdl/config.rb +9 -1
  9. data/lib/rdl/query.rb +2 -2
  10. data/lib/rdl/typecheck.rb +972 -225
  11. data/lib/rdl/types/annotated_arg.rb +8 -0
  12. data/lib/rdl/types/ast_node.rb +73 -0
  13. data/lib/rdl/types/bot.rb +8 -0
  14. data/lib/rdl/types/bound_arg.rb +63 -0
  15. data/lib/rdl/types/computed.rb +48 -0
  16. data/lib/rdl/types/dependent_arg.rb +9 -0
  17. data/lib/rdl/types/dynamic.rb +61 -0
  18. data/lib/rdl/types/finite_hash.rb +54 -9
  19. data/lib/rdl/types/generic.rb +33 -0
  20. data/lib/rdl/types/intersection.rb +8 -0
  21. data/lib/rdl/types/lexer.rex +6 -1
  22. data/lib/rdl/types/lexer.rex.rb +13 -1
  23. data/lib/rdl/types/method.rb +14 -0
  24. data/lib/rdl/types/nominal.rb +8 -0
  25. data/lib/rdl/types/non_null.rb +8 -0
  26. data/lib/rdl/types/optional.rb +8 -0
  27. data/lib/rdl/types/parser.racc +31 -5
  28. data/lib/rdl/types/parser.tab.rb +540 -302
  29. data/lib/rdl/types/rdl_types.rb +45 -0
  30. data/lib/rdl/types/singleton.rb +14 -1
  31. data/lib/rdl/types/string.rb +104 -0
  32. data/lib/rdl/types/structural.rb +8 -0
  33. data/lib/rdl/types/top.rb +8 -0
  34. data/lib/rdl/types/tuple.rb +32 -8
  35. data/lib/rdl/types/type.rb +54 -11
  36. data/lib/rdl/types/union.rb +41 -2
  37. data/lib/rdl/types/var.rb +10 -0
  38. data/lib/rdl/types/vararg.rb +8 -0
  39. data/lib/rdl/util.rb +13 -10
  40. data/lib/rdl/wrap.rb +271 -27
  41. data/lib/rdl_disable.rb +16 -2
  42. data/lib/types/active_record.rb +1 -0
  43. data/lib/types/core/array.rb +442 -23
  44. data/lib/types/core/basic_object.rb +3 -3
  45. data/lib/types/core/bigdecimal.rb +5 -0
  46. data/lib/types/core/class.rb +2 -0
  47. data/lib/types/core/dir.rb +3 -3
  48. data/lib/types/core/enumerable.rb +4 -4
  49. data/lib/types/core/enumerator.rb +1 -1
  50. data/lib/types/core/file.rb +4 -4
  51. data/lib/types/core/float.rb +203 -0
  52. data/lib/types/core/hash.rb +390 -15
  53. data/lib/types/core/integer.rb +223 -10
  54. data/lib/types/core/io.rb +2 -2
  55. data/lib/types/core/kernel.rb +8 -5
  56. data/lib/types/core/marshal.rb +3 -0
  57. data/lib/types/core/module.rb +3 -3
  58. data/lib/types/core/numeric.rb +0 -2
  59. data/lib/types/core/object.rb +5 -5
  60. data/lib/types/core/pathname.rb +2 -2
  61. data/lib/types/core/process.rb +1 -3
  62. data/lib/types/core/range.rb +1 -1
  63. data/lib/types/core/regexp.rb +2 -2
  64. data/lib/types/core/set.rb +1 -1
  65. data/lib/types/core/string.rb +408 -16
  66. data/lib/types/core/symbol.rb +3 -3
  67. data/lib/types/core/time.rb +1 -1
  68. data/lib/types/core/uri.rb +13 -13
  69. data/lib/types/rails/_helpers.rb +7 -1
  70. data/lib/types/rails/action_controller/mime_responds.rb +2 -0
  71. data/lib/types/rails/active_record/associations.rb +42 -30
  72. data/lib/types/rails/active_record/comp_types.rb +637 -0
  73. data/lib/types/rails/active_record/finder_methods.rb +1 -1
  74. data/lib/types/rails/active_record/model_schema.rb +28 -16
  75. data/lib/types/rails/active_record/relation.rb +5 -3
  76. data/lib/types/rails/active_record/sql-strings.rb +166 -0
  77. data/lib/types/rails/string.rb +1 -1
  78. data/lib/types/sequel.rb +1 -0
  79. data/lib/types/sequel/comp_types.rb +581 -0
  80. data/rdl.gemspec +5 -4
  81. data/test/test_alias.rb +4 -0
  82. data/test/test_array_types.rb +244 -0
  83. data/test/test_bound_types.rb +80 -0
  84. data/test/test_contract.rb +4 -0
  85. data/test/test_dsl.rb +5 -0
  86. data/test/test_dyn_comptype_checks.rb +206 -0
  87. data/test/test_generic.rb +21 -20
  88. data/test/test_hash_types.rb +322 -0
  89. data/test/test_intersection.rb +1 -0
  90. data/test/test_le.rb +29 -4
  91. data/test/test_member.rb +3 -1
  92. data/test/test_parser.rb +5 -0
  93. data/test/test_query.rb +1 -0
  94. data/test/test_rdl.rb +63 -28
  95. data/test/test_rdl_type.rb +4 -0
  96. data/test/test_string_types.rb +102 -0
  97. data/test/test_type_contract.rb +59 -37
  98. data/test/test_typecheck.rb +480 -75
  99. data/test/test_types.rb +17 -0
  100. data/test/test_wrap.rb +5 -0
  101. metadata +35 -5
  102. 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
@@ -1,6 +1,6 @@
1
1
  module RDL::Type
2
2
  class SingletonType < Type
3
- attr_reader :val
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
@@ -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
@@ -48,6 +48,14 @@ module RDL::Type
48
48
  return self
49
49
  end
50
50
 
51
+ def widen
52
+ return self
53
+ end
54
+
55
+ def copy
56
+ return self
57
+ end
58
+
51
59
  def hash
52
60
  17
53
61
  end
@@ -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
- @array = GenericType.new(RDL::Globals.types[:array], UnionType.new(*@params))
50
- # note since we promoted this, lbounds and ubounds will be ignored in future constraints, which
51
- # is good because otherwise we'd get infinite loops
52
- return (@lbounds.all? { |lbound| lbound <= self }) && (@ubounds.all? { |ubound| self <= ubound })
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
@@ -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 left.klass.method_defined? m
78
- types = RDL::Globals.info.get(left.klass, m, :type)
92
+ return false unless lklass.method_defined? m
93
+ types = RDL::Globals.info.get(lklass, m, :type)
79
94
  if types
80
- return false unless types.all? { |tlm| leq(tlm, t, nil, ileft) }
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
@@ -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? Type
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