rdl 2.0.1 → 2.1.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 +4 -4
- data/.travis.yml +13 -0
- data/CHANGES.md +35 -0
- data/README.md +153 -116
- data/bin/rdl_query +1 -1
- data/extras/type_tests/typetests.rb +905 -0
- data/lib/rdl.rb +0 -1
- data/lib/rdl/boot.rb +108 -77
- data/lib/rdl/boot_rails.rb +2 -8
- data/lib/rdl/config.rb +44 -17
- data/lib/rdl/contracts/flat.rb +1 -1
- data/lib/rdl/info.rb +3 -3
- data/lib/rdl/query.rb +11 -11
- data/lib/rdl/typecheck.rb +399 -136
- data/lib/rdl/types/finite_hash.rb +3 -2
- data/lib/rdl/types/generic.rb +7 -6
- data/lib/rdl/types/intersection.rb +3 -2
- data/lib/rdl/types/lexer.rex +2 -3
- data/lib/rdl/types/lexer.rex.rb +1 -4
- data/lib/rdl/types/method.rb +7 -6
- data/lib/rdl/types/nominal.rb +10 -1
- data/lib/rdl/types/parser.racc +7 -8
- data/lib/rdl/types/parser.tab.rb +108 -109
- data/lib/rdl/types/structural.rb +1 -0
- data/lib/rdl/types/tuple.rb +2 -2
- data/lib/rdl/types/type.rb +8 -8
- data/lib/rdl/types/type_inferencer.rb +1 -1
- data/lib/rdl/types/union.rb +2 -1
- data/lib/rdl/util.rb +28 -3
- data/lib/rdl/wrap.rb +216 -165
- data/lib/rdl_disable.rb +22 -15
- data/lib/types/core.rb +2 -4
- data/lib/types/core/_aliases.rb +14 -0
- data/lib/types/core/abbrev.rb +3 -0
- data/lib/types/core/array.rb +139 -0
- data/lib/types/core/base64.rb +8 -0
- data/lib/types/core/basic_object.rb +12 -0
- data/lib/types/core/benchmark.rb +9 -0
- data/lib/types/core/bigdecimal.rb +223 -0
- data/lib/types/core/bigmath.rb +10 -0
- data/lib/types/core/bignum.rb +214 -0
- data/lib/types/core/class.rb +15 -0
- data/lib/types/core/complex.rb +123 -0
- data/lib/types/core/coverage.rb +4 -0
- data/lib/types/core/csv.rb +3 -0
- data/lib/types/core/date.rb +4 -0
- data/lib/types/core/dir.rb +37 -0
- data/lib/types/core/encoding.rb +21 -0
- data/lib/types/core/enumerable.rb +96 -0
- data/lib/types/core/enumerator.rb +24 -0
- data/lib/types/core/exception.rb +15 -0
- data/lib/types/core/file.rb +125 -0
- data/lib/types/core/fileutils.rb +4 -0
- data/lib/types/core/fixnum.rb +213 -0
- data/lib/types/core/float.rb +199 -0
- data/lib/types/core/gem.rb +19 -0
- data/lib/types/core/hash.rb +72 -0
- data/lib/types/core/integer.rb +194 -0
- data/lib/types/core/io.rb +101 -0
- data/lib/types/core/kernel.rb +89 -0
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/matchdata.rb +24 -0
- data/lib/types/core/math.rb +50 -0
- data/lib/types/core/module.rb +81 -0
- data/lib/types/core/nil.rb +11 -0
- data/lib/types/core/numeric.rb +56 -0
- data/lib/types/core/object.rb +73 -0
- data/lib/types/core/pathname.rb +104 -0
- data/lib/types/core/proc.rb +12 -0
- data/lib/types/core/process.rb +110 -0
- data/lib/types/core/random.rb +13 -0
- data/lib/types/core/range.rb +37 -0
- data/lib/types/core/rational.rb +207 -0
- data/lib/types/core/regexp.rb +28 -0
- data/lib/types/core/set.rb +56 -0
- data/lib/types/core/string.rb +140 -0
- data/lib/types/core/strscan.rb +6 -0
- data/lib/types/core/symbol.rb +27 -0
- data/lib/types/core/time.rb +66 -0
- data/lib/types/core/uri.rb +18 -0
- data/lib/types/core/yaml.rb +3 -0
- data/lib/types/devise.rb +1 -0
- data/lib/types/devise/controller_helpers.rb +3 -0
- data/lib/types/devise/parameter_sanitizer.rb +2 -0
- data/lib/types/pundit.rb +2 -0
- data/lib/types/rails/_helpers.rb +50 -0
- data/lib/types/rails/abstract_controller/translation.rb +2 -0
- data/lib/types/rails/action_controller/base.rb +3 -0
- data/lib/types/rails/action_controller/instrumentation.rb +6 -0
- data/lib/types/rails/action_controller/metal.rb +3 -0
- data/lib/types/rails/action_controller/mime_responds.rb +13 -0
- data/lib/types/rails/action_controller/parameters.rb +3 -0
- data/lib/types/{rails-5.x → rails}/action_controller/strong_parameters.rb +4 -8
- data/lib/types/rails/action_dispatch/flashhash.rb +8 -0
- data/lib/types/rails/action_dispatch/routing.rb +10 -0
- data/lib/types/rails/action_mailer/base.rb +2 -0
- data/lib/types/rails/action_mailer/message_delivery.rb +2 -0
- data/lib/types/rails/action_view/helpers_sanitizehelper.rb +2 -0
- data/lib/types/rails/action_view/helpers_urlhelper.rb +5 -0
- data/lib/types/rails/active_model/errors.rb +14 -0
- data/lib/types/rails/active_model/validations.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +208 -0
- data/lib/types/rails/active_record/base.rb +2 -0
- data/lib/types/rails/active_record/core.rb +2 -0
- data/lib/types/rails/active_record/finder_methods.rb +2 -0
- data/lib/types/rails/active_record/model_schema.rb +37 -0
- data/lib/types/rails/active_record/relation.rb +11 -0
- data/lib/types/rails/active_record/schema_types.rb +51 -0
- data/lib/types/rails/active_record/validations.rb +2 -0
- data/lib/types/rails/active_support/base.rb +2 -0
- data/lib/types/rails/active_support/logger.rb +3 -0
- data/lib/types/rails/active_support/tagged_logging.rb +2 -0
- data/lib/types/rails/active_support/time_with_zone.rb +13 -0
- data/lib/types/rails/active_support/time_zone.rb +2 -0
- data/lib/types/rails/fixnum.rb +2 -0
- data/lib/types/rails/integer.rb +2 -0
- data/lib/types/rails/rack/request.rb +2 -0
- data/lib/types/rails/string.rb +3 -0
- data/lib/types/rails/time.rb +1 -0
- data/rdl.gemspec +2 -2
- data/test/disabled_test_rdoc.rb +8 -8
- data/test/test_alias.rb +1 -0
- data/test/test_dsl.rb +4 -4
- data/test/test_generic.rb +45 -38
- data/test/test_intersection.rb +10 -10
- data/test/test_le.rb +103 -102
- data/test/test_member.rb +33 -33
- data/test/test_parser.rb +101 -96
- data/test/test_query.rb +84 -84
- data/test/test_rdl.rb +87 -52
- data/test/test_rdl_type.rb +26 -9
- data/test/test_type_contract.rb +32 -31
- data/test/test_typecheck.rb +802 -436
- data/test/test_types.rb +39 -39
- data/test/test_wrap.rb +3 -2
- metadata +91 -120
- data/extras/type_tests/%.rb +0 -171
- data/extras/type_tests/&.rb +0 -159
- data/extras/type_tests/**.rb +0 -222
- data/extras/type_tests/*.rb +0 -177
- data/extras/type_tests/+.rb +0 -170
- data/extras/type_tests/-.rb +0 -171
- data/extras/type_tests/1scomp.rb +0 -157
- data/extras/type_tests/<.rb +0 -170
- data/extras/type_tests/<<.rb +0 -159
- data/extras/type_tests/>>.rb +0 -159
- data/extras/type_tests/[].rb +0 -163
- data/extras/type_tests/^.rb +0 -159
- data/extras/type_tests/abs.rb +0 -155
- data/extras/type_tests/abs2.rb +0 -164
- data/extras/type_tests/angle.rb +0 -157
- data/extras/type_tests/arg.rb +0 -157
- data/extras/type_tests/bit_length.rb +0 -157
- data/extras/type_tests/ceil.rb +0 -157
- data/extras/type_tests/ceilRational.rb +0 -160
- data/extras/type_tests/conj.rb +0 -158
- data/extras/type_tests/defwhere.rb +0 -86
- data/extras/type_tests/denominator.rb +0 -157
- data/extras/type_tests/div.rb +0 -172
- data/extras/type_tests/divslash.rb +0 -179
- data/extras/type_tests/even?.rb +0 -157
- data/extras/type_tests/fdiv.rb +0 -244
- data/extras/type_tests/finite?.rb +0 -157
- data/extras/type_tests/floor.rb +0 -157
- data/extras/type_tests/floorRational.rb +0 -161
- data/extras/type_tests/hash.rb +0 -157
- data/extras/type_tests/imag.rb +0 -158
- data/extras/type_tests/infinite?.rb +0 -157
- data/extras/type_tests/modulo.rb +0 -171
- data/extras/type_tests/nan?.rb +0 -157
- data/extras/type_tests/neg.rb +0 -155
- data/extras/type_tests/next.rb +0 -157
- data/extras/type_tests/next_float.rb +0 -157
- data/extras/type_tests/numerator.rb +0 -157
- data/extras/type_tests/phase.rb +0 -157
- data/extras/type_tests/prev_float.rb +0 -157
- data/extras/type_tests/quo.rb +0 -179
- data/extras/type_tests/rationalize.rb +0 -157
- data/extras/type_tests/rationalizeArg.rb +0 -198
- data/extras/type_tests/real.rb +0 -157
- data/extras/type_tests/real?.rb +0 -157
- data/extras/type_tests/round.rb +0 -157
- data/extras/type_tests/roundArg.rb +0 -169
- data/extras/type_tests/size.rb +0 -157
- data/extras/type_tests/to_c.rb +0 -157
- data/extras/type_tests/to_f.rb +0 -155
- data/extras/type_tests/to_i.rb +0 -157
- data/extras/type_tests/to_r.rb +0 -157
- data/extras/type_tests/to_s.rb +0 -157
- data/extras/type_tests/truncate.rb +0 -157
- data/extras/type_tests/truncateArg.rb +0 -166
- data/extras/type_tests/type tests +0 -1
- data/extras/type_tests/zero?.rb +0 -155
- data/extras/type_tests/|.rb +0 -159
- data/lib/types/core-ruby-2.x/_aliases.rb +0 -15
- data/lib/types/core-ruby-2.x/abbrev.rb +0 -5
- data/lib/types/core-ruby-2.x/array.rb +0 -137
- data/lib/types/core-ruby-2.x/base64.rb +0 -10
- data/lib/types/core-ruby-2.x/basic_object.rb +0 -14
- data/lib/types/core-ruby-2.x/benchmark.rb +0 -11
- data/lib/types/core-ruby-2.x/bigdecimal.rb +0 -224
- data/lib/types/core-ruby-2.x/bigmath.rb +0 -12
- data/lib/types/core-ruby-2.x/bignum.rb +0 -214
- data/lib/types/core-ruby-2.x/class.rb +0 -17
- data/lib/types/core-ruby-2.x/complex.rb +0 -124
- data/lib/types/core-ruby-2.x/coverage.rb +0 -6
- data/lib/types/core-ruby-2.x/csv.rb +0 -5
- data/lib/types/core-ruby-2.x/date.rb +0 -6
- data/lib/types/core-ruby-2.x/dir.rb +0 -38
- data/lib/types/core-ruby-2.x/encoding.rb +0 -23
- data/lib/types/core-ruby-2.x/enumerable.rb +0 -98
- data/lib/types/core-ruby-2.x/enumerator.rb +0 -26
- data/lib/types/core-ruby-2.x/exception.rb +0 -17
- data/lib/types/core-ruby-2.x/file.rb +0 -126
- data/lib/types/core-ruby-2.x/fileutils.rb +0 -6
- data/lib/types/core-ruby-2.x/fixnum.rb +0 -213
- data/lib/types/core-ruby-2.x/float.rb +0 -199
- data/lib/types/core-ruby-2.x/gem.rb +0 -247
- data/lib/types/core-ruby-2.x/hash.rb +0 -72
- data/lib/types/core-ruby-2.x/integer.rb +0 -197
- data/lib/types/core-ruby-2.x/io.rb +0 -103
- data/lib/types/core-ruby-2.x/kernel.rb +0 -90
- data/lib/types/core-ruby-2.x/marshal.rb +0 -5
- data/lib/types/core-ruby-2.x/matchdata.rb +0 -26
- data/lib/types/core-ruby-2.x/math.rb +0 -53
- data/lib/types/core-ruby-2.x/module.rb +0 -83
- data/lib/types/core-ruby-2.x/nil.rb +0 -12
- data/lib/types/core-ruby-2.x/numeric.rb +0 -56
- data/lib/types/core-ruby-2.x/object.rb +0 -75
- data/lib/types/core-ruby-2.x/pathname.rb +0 -106
- data/lib/types/core-ruby-2.x/proc.rb +0 -16
- data/lib/types/core-ruby-2.x/process.rb +0 -127
- data/lib/types/core-ruby-2.x/random.rb +0 -17
- data/lib/types/core-ruby-2.x/range.rb +0 -39
- data/lib/types/core-ruby-2.x/rational.rb +0 -209
- data/lib/types/core-ruby-2.x/regexp.rb +0 -30
- data/lib/types/core-ruby-2.x/set.rb +0 -58
- data/lib/types/core-ruby-2.x/string.rb +0 -143
- data/lib/types/core-ruby-2.x/strscan.rb +0 -7
- data/lib/types/core-ruby-2.x/symbol.rb +0 -29
- data/lib/types/core-ruby-2.x/time.rb +0 -68
- data/lib/types/core-ruby-2.x/uri.rb +0 -20
- data/lib/types/core-ruby-2.x/yaml.rb +0 -5
- data/lib/types/rails-5.x/_helpers.rb +0 -52
- data/lib/types/rails-5.x/action_controller/mime_responds.rb +0 -11
- data/lib/types/rails-5.x/action_dispatch/routing.rb +0 -10
- data/lib/types/rails-5.x/active_model/errors.rb +0 -15
- data/lib/types/rails-5.x/active_model/validations.rb +0 -5
- data/lib/types/rails-5.x/active_record/associations.rb +0 -190
- data/lib/types/rails-5.x/active_record/core.rb +0 -3
- data/lib/types/rails-5.x/active_record/model_schema.rb +0 -39
- data/lib/types/rails-5.x/fixnum.rb +0 -3
data/lib/rdl.rb
CHANGED
data/lib/rdl/boot.rb
CHANGED
|
@@ -12,52 +12,72 @@ def RDL.config
|
|
|
12
12
|
end
|
|
13
13
|
require 'rdl/info.rb'
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# :
|
|
19
|
-
# :
|
|
20
|
-
# :
|
|
21
|
-
# :
|
|
22
|
-
# :
|
|
23
|
-
# :
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# class
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# class
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
15
|
+
module RDL::Globals
|
|
16
|
+
# Method/variable info table with kinds:
|
|
17
|
+
# For methods
|
|
18
|
+
# :pre to array of precondition contracts
|
|
19
|
+
# :post to array of postcondition contracts
|
|
20
|
+
# :type to array of types
|
|
21
|
+
# :source_location to [filename, linenumber] location of most recent definition
|
|
22
|
+
# :typecheck - boolean that is true if method should be statically type checked
|
|
23
|
+
# :otype to set of types that were observed at run time, where a type is a finite hash {:args => Array<Class>, :ret => Class, :block => %bool}
|
|
24
|
+
# :context_types to array of [klass, meth, Type] - method types that exist only within this method. An icky hack to deal with Rails `params`.
|
|
25
|
+
# For variables
|
|
26
|
+
# :type to type
|
|
27
|
+
@info = RDL::Info.new
|
|
28
|
+
|
|
29
|
+
# Map from full_method_name to number of times called when wrapped
|
|
30
|
+
@wrapped_calls = Hash.new 0
|
|
31
|
+
|
|
32
|
+
# Hash from class name to array of symbols that are the class's type parameters
|
|
33
|
+
@type_params = Hash.new
|
|
34
|
+
|
|
35
|
+
# Hash from class name to method name to its alias method name
|
|
36
|
+
# class names are strings
|
|
37
|
+
# method names are symbols
|
|
38
|
+
@aliases = Hash.new
|
|
39
|
+
|
|
40
|
+
# Set of [class, method] pairs to wrap.
|
|
41
|
+
# class is a string
|
|
42
|
+
# method is a symbol
|
|
43
|
+
@to_wrap = Set.new
|
|
44
|
+
|
|
45
|
+
# Map from symbols to set of [class, method] pairs to type check when those symbols are rdl_do_typecheck'd
|
|
46
|
+
# (or the methods are defined, for the symbol :now)
|
|
47
|
+
@to_typecheck = Hash.new
|
|
48
|
+
@to_typecheck[:now] = Set.new
|
|
49
|
+
|
|
50
|
+
# Map from symbols to Array<Proc> where the Procs are called when those symbols are rdl_do_typecheck'd
|
|
51
|
+
@to_do_at = Hash.new
|
|
52
|
+
|
|
53
|
+
# List of contracts that should be applied to the next method definition
|
|
54
|
+
@deferred = []
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class << RDL::Globals # add accessors and readers for module variables
|
|
58
|
+
attr_accessor :info
|
|
59
|
+
attr_accessor :wrapped_calls
|
|
60
|
+
attr_accessor :type_params
|
|
61
|
+
attr_reader :aliases
|
|
62
|
+
attr_accessor :to_wrap
|
|
63
|
+
attr_accessor :to_typecheck
|
|
64
|
+
attr_accessor :to_do_at
|
|
65
|
+
attr_accessor :deferred
|
|
66
|
+
end
|
|
54
67
|
|
|
55
68
|
# Create switches to control whether wrapping happens and whether
|
|
56
69
|
# contracts are checked. These need to be created before rdl/wrap.rb
|
|
57
70
|
# is loaded.
|
|
58
71
|
require 'rdl/switch.rb'
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
module RDL::Globals
|
|
73
|
+
@wrap_switch = RDL::Switch.new
|
|
74
|
+
@contract_switch = RDL::Switch.new
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class << RDL::Globals
|
|
78
|
+
attr_reader :wrap_switch
|
|
79
|
+
attr_reader :contract_switch
|
|
80
|
+
end
|
|
61
81
|
|
|
62
82
|
require 'rdl/types/type.rb'
|
|
63
83
|
require 'rdl/types/annotated_arg.rb'
|
|
@@ -95,39 +115,50 @@ require 'rdl/query.rb'
|
|
|
95
115
|
require 'rdl/typecheck.rb'
|
|
96
116
|
#require_relative 'rdl/stats.rb'
|
|
97
117
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
#
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
118
|
+
module RDL::Globals
|
|
119
|
+
FIXBIG_VERSIONS = ['>= 2.0.0', '< 2.4.0']
|
|
120
|
+
# INTEGER_VERSIONS = '>= 2.4.0'
|
|
121
|
+
|
|
122
|
+
@parser = RDL::Type::Parser.new
|
|
123
|
+
|
|
124
|
+
# Map from file names to [digest, cache] where 2nd elt maps
|
|
125
|
+
# :ast to the AST
|
|
126
|
+
# :line_defs maps linenumber to AST for def at that line
|
|
127
|
+
@parser_cache = Hash.new
|
|
128
|
+
|
|
129
|
+
# Some generally useful types; not really a big deal to do this since
|
|
130
|
+
# NominalTypes are cached, but these names are shorter to type
|
|
131
|
+
@types = Hash.new
|
|
132
|
+
@types[:nil] = RDL::Type::NominalType.new NilClass # actually creates singleton type
|
|
133
|
+
@types[:top] = RDL::Type::TopType.new
|
|
134
|
+
@types[:bot] = RDL::Type::BotType.new
|
|
135
|
+
@types[:object] = RDL::Type::NominalType.new Object
|
|
136
|
+
@types[:true] = RDL::Type::NominalType.new TrueClass # actually creates singleton type
|
|
137
|
+
@types[:false] = RDL::Type::NominalType.new FalseClass # also singleton type
|
|
138
|
+
@types[:bool] = RDL::Type::UnionType.new(@types[:true], @types[:false])
|
|
139
|
+
@types[:float] = RDL::Type::NominalType.new Float
|
|
140
|
+
@types[:complex] = RDL::Type::NominalType.new Complex
|
|
141
|
+
@types[:rational] = RDL::Type::NominalType.new Rational
|
|
142
|
+
@types[:integer] = RDL::Type::NominalType.new Integer
|
|
143
|
+
@types[:numeric] = RDL::Type::NominalType.new Numeric
|
|
144
|
+
@types[:string] = RDL::Type::NominalType.new String
|
|
145
|
+
@types[:array] = RDL::Type::NominalType.new Array
|
|
146
|
+
@types[:hash] = RDL::Type::NominalType.new Hash
|
|
147
|
+
@types[:symbol] = RDL::Type::NominalType.new Symbol
|
|
148
|
+
@types[:range] = RDL::Type::NominalType.new Range
|
|
149
|
+
@types[:regexp] = RDL::Type::NominalType.new Regexp
|
|
150
|
+
@types[:standard_error] = RDL::Type::NominalType.new StandardError
|
|
151
|
+
@types[:proc] = RDL::Type::NominalType.new Proc
|
|
152
|
+
|
|
153
|
+
# Hash from special type names to their values
|
|
154
|
+
@special_types = {'%any' => @types[:top],
|
|
155
|
+
'%bot' => @types[:bot],
|
|
156
|
+
'%bool' => @types[:bool]}
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
class << RDL::Globals
|
|
160
|
+
attr_reader :parser
|
|
161
|
+
attr_accessor :parser_cache
|
|
162
|
+
attr_reader :types
|
|
163
|
+
attr_reader :special_types
|
|
164
|
+
end
|
data/lib/rdl/boot_rails.rb
CHANGED
|
@@ -2,14 +2,8 @@ if Rails.env.development? || Rails.env.test?
|
|
|
2
2
|
require 'rdl/boot'
|
|
3
3
|
require 'types/core'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
begin
|
|
8
|
-
require_relative "../types/rails-#{version}/_helpers.rb" # load type aliases first
|
|
9
|
-
Dir[File.dirname(__FILE__) + "/../types/rails-#{version}/**/*.rb"].each { |f| require f }
|
|
10
|
-
rescue LoadError
|
|
11
|
-
$stderr.puts("rdl could not load type definitions for Rails v#{version}")
|
|
12
|
-
end
|
|
5
|
+
require_relative "../types/rails/_helpers.rb" # load type aliases first
|
|
6
|
+
Dir[File.dirname(__FILE__) + "/../types/rails/**/*.rb"].each { |f| require f }
|
|
13
7
|
elsif Rails.env.production?
|
|
14
8
|
require 'rdl_disable'
|
|
15
9
|
class ActionController::Base
|
data/lib/rdl/config.rb
CHANGED
|
@@ -5,26 +5,47 @@ class RDL::Config
|
|
|
5
5
|
|
|
6
6
|
attr_accessor :nowrap
|
|
7
7
|
attr_accessor :gather_stats
|
|
8
|
-
|
|
9
|
-
attr_accessor :guess_types
|
|
8
|
+
attr_reader :report # writer is custom defined
|
|
10
9
|
attr_accessor :type_defaults, :pre_defaults, :post_defaults
|
|
11
10
|
|
|
12
11
|
def initialize
|
|
13
|
-
@nowrap = Set.new
|
|
12
|
+
@nowrap = Set.new # Set of symbols
|
|
14
13
|
@gather_stats = false
|
|
15
|
-
@report = false
|
|
16
|
-
@guess_types = []
|
|
14
|
+
@report = false # if this is enabled by default, modify @at_exit_installed
|
|
15
|
+
@guess_types = [] # same as above
|
|
16
|
+
@at_exit_installed = false
|
|
17
17
|
@type_defaults = { wrap: true, typecheck: false }
|
|
18
18
|
@pre_defaults = { wrap: true }
|
|
19
19
|
@post_defaults = { wrap: true }
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def report=(val)
|
|
23
|
+
install_at_exit
|
|
24
|
+
@report = val
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def guess_types
|
|
28
|
+
install_at_exit
|
|
29
|
+
return @guess_types
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def guess_types=(val)
|
|
33
|
+
install_at_exit
|
|
34
|
+
@guess_types = val
|
|
35
|
+
end
|
|
36
|
+
|
|
22
37
|
def add_nowrap(*klasses)
|
|
23
|
-
klasses.each { |klass|
|
|
38
|
+
klasses.each { |klass|
|
|
39
|
+
@nowrap.add klass.to_s.to_sym
|
|
40
|
+
@nowrap.add RDL::Util.add_singleton_marker(klass.to_s).to_sym
|
|
41
|
+
}
|
|
24
42
|
end
|
|
25
43
|
|
|
26
44
|
def remove_nowrap(*klasses)
|
|
27
|
-
klasses.each { |klass|
|
|
45
|
+
klasses.each { |klass|
|
|
46
|
+
@nowrap.delete klass.to_s.to_sym
|
|
47
|
+
@nowrap.delete RDL::Util.add_singleton_marker(klass.to_s).to_sym
|
|
48
|
+
}
|
|
28
49
|
end
|
|
29
50
|
|
|
30
51
|
# To use, copy these 3 lines to the test file of a gem
|
|
@@ -39,7 +60,7 @@ RDL::Config.instance.profile_stats
|
|
|
39
60
|
|
|
40
61
|
at_exit do
|
|
41
62
|
Profiler__.stop_profile
|
|
42
|
-
|
|
63
|
+
RDL::Globals.contract_switch.off {
|
|
43
64
|
puts "START."
|
|
44
65
|
puts "Performing Profile Analysis"
|
|
45
66
|
# Class Name => [Times Contract Called | Times Called | Time | Time | Class Profile]
|
|
@@ -75,7 +96,7 @@ RDL::Config.instance.profile_stats
|
|
|
75
96
|
}
|
|
76
97
|
|
|
77
98
|
p "Scanning RDL Contract Log"
|
|
78
|
-
|
|
99
|
+
RDL::Globals.wrapped_calls.each{ |mname,ct|
|
|
79
100
|
if (totals[mname]) then
|
|
80
101
|
if (totals[mname][0]) then
|
|
81
102
|
totals[mname][0] = ct
|
|
@@ -132,7 +153,7 @@ RDL::Config.instance.profile_stats
|
|
|
132
153
|
puts "------------------------------"
|
|
133
154
|
typechecked = []
|
|
134
155
|
missing = []
|
|
135
|
-
|
|
156
|
+
RDL::Globals.info.info.each_pair { |klass, meths|
|
|
136
157
|
meths.each { |meth, kinds|
|
|
137
158
|
if kinds[:typecheck]
|
|
138
159
|
if kinds[:typechecked]
|
|
@@ -186,16 +207,16 @@ RDL::Config.instance.profile_stats
|
|
|
186
207
|
puts " -> XXXX'"
|
|
187
208
|
|
|
188
209
|
# next print based on observed types
|
|
189
|
-
otypes =
|
|
210
|
+
otypes = RDL::Globals.info.get(klass, meth, :otype) if RDL::Globals.info.has?(klass, meth, :otype) # observed types
|
|
190
211
|
return if otypes.nil?
|
|
191
212
|
first = true
|
|
192
213
|
print " type #{if is_sing then '\'self.' + meth + '\'' else ':' + meth end}, '("
|
|
193
214
|
otargs = []
|
|
194
|
-
otret =
|
|
215
|
+
otret = RDL::Globals.types[:bot]
|
|
195
216
|
otblock = false
|
|
196
217
|
otypes.each { |ot|
|
|
197
218
|
ot[:args].each_with_index { |t, i|
|
|
198
|
-
otargs[i] =
|
|
219
|
+
otargs[i] = RDL::Globals.types[:bot] if otargs[i].nil?
|
|
199
220
|
otargs[i] = RDL::Type::UnionType.new(otargs[i], RDL::Type::NominalType.new(t)).canonical
|
|
200
221
|
}
|
|
201
222
|
otret = RDL::Type::UnionType.new(otret, RDL::Type::NominalType.new(ot[:ret])).canonical
|
|
@@ -233,7 +254,13 @@ RDL::Config.instance.profile_stats
|
|
|
233
254
|
end
|
|
234
255
|
end
|
|
235
256
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
257
|
+
private
|
|
258
|
+
|
|
259
|
+
def install_at_exit
|
|
260
|
+
return if @at_exit_installed
|
|
261
|
+
at_exit do
|
|
262
|
+
RDL::Config.instance.do_report
|
|
263
|
+
RDL::Config.instance.do_guess_types
|
|
264
|
+
end
|
|
265
|
+
@at_exit_installed = true
|
|
266
|
+
end
|
data/lib/rdl/contracts/flat.rb
CHANGED
|
@@ -8,7 +8,7 @@ module RDL::Contract
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def check(slf, *v, &blk)
|
|
11
|
-
|
|
11
|
+
RDL::Globals.contract_switch.off {
|
|
12
12
|
if @pred && v.length >= @pred.arity
|
|
13
13
|
unless blk ? slf.instance_exec(*v, blk, &@pred) : slf.instance_exec(*v, &@pred) # TODO: Fix blk
|
|
14
14
|
# unless blk ? pred.call(*v, &blk) : pred.call(*v)
|
data/lib/rdl/info.rb
CHANGED
|
@@ -23,7 +23,7 @@ class RDL::Info
|
|
|
23
23
|
# if no prev info for kind, set to val and return true
|
|
24
24
|
# if prev info for kind, return true if prev == val and false otherwise
|
|
25
25
|
def set(klass, label, kind, val)
|
|
26
|
-
klass = klass
|
|
26
|
+
klass = RDL::Util.to_class_str(klass)
|
|
27
27
|
label = label.to_sym
|
|
28
28
|
@info[klass] = {} unless @info[klass]
|
|
29
29
|
@info[klass][label] = {} unless @info[klass][label]
|
|
@@ -73,8 +73,8 @@ class RDL::Info
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def get_with_aliases(klass, label, kind)
|
|
76
|
-
while
|
|
77
|
-
label =
|
|
76
|
+
while RDL::Globals.aliases[klass] && RDL::Globals.aliases[klass][label]
|
|
77
|
+
label = RDL::Globals.aliases[klass][label]
|
|
78
78
|
end
|
|
79
79
|
get(klass, label, kind)
|
|
80
80
|
end
|
data/lib/rdl/query.rb
CHANGED
|
@@ -17,25 +17,25 @@ class RDL::Query
|
|
|
17
17
|
# klass = self.class.to_s
|
|
18
18
|
# meth = q.to_sym
|
|
19
19
|
end
|
|
20
|
-
return
|
|
20
|
+
return RDL::Globals.info.get(klass, meth, :type)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# Return an ordered list of all method types of a class. The query should be a class name.
|
|
24
24
|
def self.class_query(q)
|
|
25
25
|
klass = q.to_s
|
|
26
|
-
return nil unless
|
|
26
|
+
return nil unless RDL::Globals.info.info.has_key? klass
|
|
27
27
|
cls_meths = []
|
|
28
28
|
cls_klass = RDL::Util.add_singleton_marker(klass)
|
|
29
|
-
if
|
|
30
|
-
|
|
29
|
+
if RDL::Globals.info.info.has_key? cls_klass then
|
|
30
|
+
RDL::Globals.info.info[cls_klass].each { |meth, kinds|
|
|
31
31
|
if kinds.has_key? :type then
|
|
32
32
|
kinds[:type].each { |t| cls_meths << [meth.to_s, t] }
|
|
33
33
|
end
|
|
34
34
|
}
|
|
35
35
|
end
|
|
36
36
|
inst_meths = []
|
|
37
|
-
if
|
|
38
|
-
|
|
37
|
+
if RDL::Globals.info.info.has_key? klass then
|
|
38
|
+
RDL::Globals.info.info[klass].each { |meth, kinds|
|
|
39
39
|
if kinds.has_key? :type then
|
|
40
40
|
kinds[:type].each { |t| inst_meths << [meth.to_s, t] }
|
|
41
41
|
end
|
|
@@ -49,9 +49,9 @@ class RDL::Query
|
|
|
49
49
|
|
|
50
50
|
# Returns sorted list of pairs [method name, type] matching query. The query should be a string containing a method type query.
|
|
51
51
|
def self.method_type_query(q)
|
|
52
|
-
q =
|
|
52
|
+
q = RDL::Globals.parser.scan_str "#Q #{q}"
|
|
53
53
|
result = []
|
|
54
|
-
|
|
54
|
+
RDL::Globals.info.info.each { |klass, meths|
|
|
55
55
|
meths.each { |meth, kinds|
|
|
56
56
|
if kinds.has_key? :type then
|
|
57
57
|
kinds[:type].each { |t|
|
|
@@ -67,10 +67,10 @@ class RDL::Query
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
module RDL
|
|
71
71
|
|
|
72
|
-
def
|
|
73
|
-
|
|
72
|
+
def self.query(q)
|
|
73
|
+
RDL::Globals.contract_switch.off {
|
|
74
74
|
if q =~ /^[A-Z]\w*(#|\.)([a-z_]\w*(!|\?|=)?|!|~|\+|\*\*|-|\*|\/|%|<<|>>|&|\||\^|<|<=|=>|>|==|===|!=|=~|!~|<=>|\[\]|\[\]=)$/
|
|
75
75
|
typs = RDL::Query.method_query(q)
|
|
76
76
|
if typs.nil? then
|
data/lib/rdl/typecheck.rb
CHANGED
|
@@ -124,7 +124,7 @@ module RDL::Typecheck
|
|
|
124
124
|
error :inconsistent_var_type_type, [var.to_s, (first_typ + neq).map { |t| t.to_s }.join(' and ')], e unless neq.empty?
|
|
125
125
|
env.env[var] = {type: h[:type], fixed: true}
|
|
126
126
|
else
|
|
127
|
-
typ = RDL::Type::UnionType.new(first_typ, *rest.map { |other| ((other.has_key? var) && other[var]) ||
|
|
127
|
+
typ = RDL::Type::UnionType.new(first_typ, *rest.map { |other| ((other.has_key? var) && other[var]) || RDL::Globals.types[:nil] })
|
|
128
128
|
typ = typ.canonical
|
|
129
129
|
env.env[var] = {type: typ, fixed: false}
|
|
130
130
|
end
|
|
@@ -155,15 +155,30 @@ module RDL::Typecheck
|
|
|
155
155
|
|
|
156
156
|
# report msg at ast's loc
|
|
157
157
|
def self.error(reason, args, ast)
|
|
158
|
-
raise StaticTypeError, ("\n" + (
|
|
158
|
+
raise StaticTypeError, ("\n" + (Diagnostic.new :error, reason, args, ast.loc.expression).render.join("\n"))
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
def self.note(reason, args, ast)
|
|
162
|
-
puts (
|
|
162
|
+
puts (Diagnostic.new :note, reason, args, ast.loc.expression).render
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def self.get_leaves(node, r=[])
|
|
166
|
+
node.children.each {|n|
|
|
167
|
+
if n.is_a? AST::Node
|
|
168
|
+
get_leaves(n, r)
|
|
169
|
+
elsif n
|
|
170
|
+
r.push n
|
|
171
|
+
end
|
|
172
|
+
}
|
|
173
|
+
r
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def self.is_RDL(node)
|
|
177
|
+
return node != nil && node.type == :const && node.children[0] == nil && node.children[1] == :RDL
|
|
163
178
|
end
|
|
164
179
|
|
|
165
180
|
def self.get_ast(klass, meth)
|
|
166
|
-
file, line =
|
|
181
|
+
file, line = RDL::Globals.info.get(klass, meth, :source_location)
|
|
167
182
|
raise RuntimeError, "No file for #{RDL::Util.pp_klass_method(klass, meth)}" if file.nil?
|
|
168
183
|
raise RuntimeError, "static type checking in irb not supported" if file == "(irb)"
|
|
169
184
|
if file == "(pry)"
|
|
@@ -179,23 +194,24 @@ module RDL::Typecheck
|
|
|
179
194
|
end
|
|
180
195
|
|
|
181
196
|
digest = Digest::MD5.file file
|
|
182
|
-
cache_hit = ((
|
|
183
|
-
(
|
|
197
|
+
cache_hit = ((RDL::Globals.parser_cache.has_key? file) &&
|
|
198
|
+
(RDL::Globals.parser_cache[file][0] == digest))
|
|
184
199
|
unless cache_hit
|
|
185
200
|
file_ast = Parser::CurrentRuby.parse_file file
|
|
186
201
|
mapper = ASTMapper.new(file)
|
|
187
202
|
mapper.process(file_ast)
|
|
188
203
|
cache = {ast: file_ast, line_defs: mapper.line_defs}
|
|
189
|
-
|
|
204
|
+
RDL::Globals.parser_cache[file] = [digest, cache]
|
|
190
205
|
end
|
|
191
|
-
ast =
|
|
206
|
+
ast = RDL::Globals.parser_cache[file][1][:line_defs][line]
|
|
192
207
|
raise RuntimeError, "Can't find source for class #{RDL::Util.pp_klass_method(klass, meth)}" if ast.nil?
|
|
193
208
|
return ast
|
|
194
209
|
end
|
|
195
210
|
|
|
196
211
|
def self.typecheck(klass, meth)
|
|
212
|
+
@cur_meth = [klass, meth]
|
|
197
213
|
ast = get_ast(klass, meth)
|
|
198
|
-
types =
|
|
214
|
+
types = RDL::Globals.info.get(klass, meth, :type)
|
|
199
215
|
raise RuntimeError, "Can't typecheck method with no types?!" if types.nil? or types == []
|
|
200
216
|
|
|
201
217
|
if ast.type == :def
|
|
@@ -206,7 +222,7 @@ module RDL::Typecheck
|
|
|
206
222
|
raise RuntimeError, "Unexpected ast type #{ast.type}"
|
|
207
223
|
end
|
|
208
224
|
raise RuntimeError, "Method #{name} defined where method #{meth} expected" if name.to_sym != meth
|
|
209
|
-
context_types =
|
|
225
|
+
context_types = RDL::Globals.info.get(klass, meth, :context_types)
|
|
210
226
|
types.each { |type|
|
|
211
227
|
if RDL::Util.has_singleton_marker(klass)
|
|
212
228
|
# to_class gets the class object itself, so remove singleton marker to get class rather than singleton class
|
|
@@ -214,6 +230,10 @@ module RDL::Typecheck
|
|
|
214
230
|
else
|
|
215
231
|
self_type = RDL::Type::NominalType.new(klass)
|
|
216
232
|
end
|
|
233
|
+
if meth == :initialize
|
|
234
|
+
# initialize method must always return "self" or GenericType where base is "self"
|
|
235
|
+
error :bad_initialize_type, [], ast unless ((type.ret.is_a?(RDL::Type::VarType) && type.ret.name == :self) || (type.ret.is_a?(RDL::Type::GenericType) && type.ret.base.is_a?(RDL::Type::VarType) && type.ret.base.name == :self))
|
|
236
|
+
end
|
|
217
237
|
inst = {self: self_type}
|
|
218
238
|
type = type.instantiate inst
|
|
219
239
|
_, targs = args_hash({}, Env.new, type, args, ast, 'method')
|
|
@@ -222,14 +242,14 @@ module RDL::Typecheck
|
|
|
222
242
|
begin
|
|
223
243
|
old_captured = scope[:captured].dup
|
|
224
244
|
if body.nil?
|
|
225
|
-
body_type =
|
|
245
|
+
body_type = RDL::Globals.types[:nil]
|
|
226
246
|
else
|
|
227
247
|
_, body_type = tc(scope, Env.new(targs.merge(scope[:captured])), body)
|
|
228
248
|
end
|
|
229
249
|
end until old_captured == scope[:captured]
|
|
230
|
-
error :bad_return_type, [body_type.to_s, type.ret.to_s], body unless body.nil? || body_type <= type.ret
|
|
250
|
+
error :bad_return_type, [body_type.to_s, type.ret.to_s], body unless body.nil? || meth == :initialize || body_type <= type.ret
|
|
231
251
|
}
|
|
232
|
-
|
|
252
|
+
RDL::Globals.info.set(klass, meth, :typechecked, true)
|
|
233
253
|
end
|
|
234
254
|
|
|
235
255
|
# [+ scope +] is used to typecheck default values for optional arguments
|
|
@@ -262,7 +282,7 @@ module RDL::Typecheck
|
|
|
262
282
|
elsif arg.type == :restarg
|
|
263
283
|
error :type_arg_kind_mismatch, [kind, 'optional', 'vararg'], arg if targ.optional?
|
|
264
284
|
error :type_arg_kind_mismatch, [kind, 'required', 'vararg'], arg if !targ.vararg?
|
|
265
|
-
targs[arg.children[0]] = RDL::Type::GenericType.new(
|
|
285
|
+
targs[arg.children[0]] = RDL::Type::GenericType.new(RDL::Globals.types[:array], targ.type)
|
|
266
286
|
tpos += 1
|
|
267
287
|
elsif arg.type == :kwarg
|
|
268
288
|
error :type_args_no_kws, [kind], arg unless targ.is_a?(RDL::Type::FiniteHashType)
|
|
@@ -285,7 +305,7 @@ module RDL::Typecheck
|
|
|
285
305
|
elsif arg.type == :kwrestarg
|
|
286
306
|
error :type_args_no_kws, [kind], e unless targ.is_a?(RDL::Type::FiniteHashType)
|
|
287
307
|
error :type_args_no_kw_rest, [kind], arg if targ.rest.nil?
|
|
288
|
-
targs[arg.children[0]] = RDL::Type::GenericType.new(
|
|
308
|
+
targs[arg.children[0]] = RDL::Type::GenericType.new(RDL::Globals.types[:hash], RDL::Globals.types[:symbol], targ.rest)
|
|
289
309
|
kw_rest_matched = true
|
|
290
310
|
elsif arg.type == :blockarg
|
|
291
311
|
error :type_arg_block, [kind, kind], arg unless type.block
|
|
@@ -305,6 +325,27 @@ module RDL::Typecheck
|
|
|
305
325
|
return [env, targs]
|
|
306
326
|
end
|
|
307
327
|
|
|
328
|
+
def self.get_super_owner(slf, m)
|
|
329
|
+
case slf
|
|
330
|
+
when RDL::Type::SingletonType
|
|
331
|
+
if slf.nominal.name == 'Class'
|
|
332
|
+
trecv_owner = get_super_owner_from_class(slf.val.singleton_class, m)
|
|
333
|
+
RDL::Type::SingletonType.new(RDL::Util.singleton_class_to_class(trecv_owner))
|
|
334
|
+
else
|
|
335
|
+
raise Exception, 'self is singleton class but nominal is not Class'
|
|
336
|
+
end
|
|
337
|
+
when RDL::Type::NominalType
|
|
338
|
+
RDL::Type::NominalType.new(get_super_owner_from_class(slf.klass, m))
|
|
339
|
+
else
|
|
340
|
+
raise Exception, 'unsupported self #{slf} in get_super_owner'
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def self.get_super_owner_from_class(cls, m)
|
|
345
|
+
raise Exception, "cls #{cls} is not a Class" if cls.class != Class
|
|
346
|
+
cls.superclass.instance_method(m).owner
|
|
347
|
+
end
|
|
348
|
+
|
|
308
349
|
# The actual type checking logic.
|
|
309
350
|
# [+ scope +] tracks flow-insensitive information about the current scope, excluding local variables
|
|
310
351
|
# [+ env +] is the (local variable) Env
|
|
@@ -314,11 +355,11 @@ module RDL::Typecheck
|
|
|
314
355
|
def self.tc(scope, env, e)
|
|
315
356
|
case e.type
|
|
316
357
|
when :nil
|
|
317
|
-
[env,
|
|
358
|
+
[env, RDL::Globals.types[:nil]]
|
|
318
359
|
when :true
|
|
319
|
-
[env,
|
|
360
|
+
[env, RDL::Globals.types[:true]]
|
|
320
361
|
when :false
|
|
321
|
-
[env,
|
|
362
|
+
[env, RDL::Globals.types[:false]]
|
|
322
363
|
when :complex, :rational, :str, :string # constants
|
|
323
364
|
[env, RDL::Type::NominalType.new(e.children[0].class)]
|
|
324
365
|
when :int, :float, :sym # singletons
|
|
@@ -326,15 +367,15 @@ module RDL::Typecheck
|
|
|
326
367
|
when :dstr, :xstr # string (or execute-string) with interpolation
|
|
327
368
|
envi = env
|
|
328
369
|
e.children.each { |ei| envi, _ = tc(scope, envi, ei) }
|
|
329
|
-
[envi,
|
|
370
|
+
[envi, RDL::Globals.types[:string]]
|
|
330
371
|
when :dsym # symbol with interpolation
|
|
331
372
|
envi = env
|
|
332
373
|
e.children.each { |ei| envi, _ = tc(scope, envi, ei) }
|
|
333
|
-
[envi,
|
|
374
|
+
[envi, RDL::Globals.types[:symbol]]
|
|
334
375
|
when :regexp
|
|
335
376
|
envi = env
|
|
336
377
|
e.children.each { |ei| envi, _ = tc(scope, envi, ei) unless ei.type == :regopt }
|
|
337
|
-
[envi,
|
|
378
|
+
[envi, RDL::Globals.types[:regexp]]
|
|
338
379
|
when :array
|
|
339
380
|
envi = env
|
|
340
381
|
tis = []
|
|
@@ -350,16 +391,16 @@ module RDL::Typecheck
|
|
|
350
391
|
ti.elts.each_pair { |k, t|
|
|
351
392
|
tis << RDL::Type::TupleType.new(RDL::Type::SingletonType.new(k), t)
|
|
352
393
|
}
|
|
353
|
-
elsif ti.is_a?(RDL::Type::GenericType) && ti.base ==
|
|
394
|
+
elsif ti.is_a?(RDL::Type::GenericType) && ti.base == RDL::Globals.types[:array]
|
|
354
395
|
is_array = true
|
|
355
396
|
tis << ti.params[0]
|
|
356
|
-
elsif ti.is_a?(RDL::Type::GenericType) && ti.base ==
|
|
397
|
+
elsif ti.is_a?(RDL::Type::GenericType) && ti.base == RDL::Globals.types[:hash]
|
|
357
398
|
is_array = true
|
|
358
399
|
tis << RDL::Type::TupleType.new(*ti.params)
|
|
359
400
|
elsif ti.is_a?(RDL::Type::SingletonType) && ti.val.nil?
|
|
360
401
|
# nil gets thrown out
|
|
361
|
-
elsif (
|
|
362
|
-
(
|
|
402
|
+
elsif (RDL::Globals.types[:array] <= ti) || (ti <= RDL::Globals.types[:array]) ||
|
|
403
|
+
(RDL::Globals.types[:hash] <= ti) || (ti <= RDL::Globals.types[:hash])
|
|
363
404
|
# might or might not be array...can't splat...
|
|
364
405
|
error :cant_splat, [ti], ei
|
|
365
406
|
else
|
|
@@ -371,7 +412,7 @@ module RDL::Typecheck
|
|
|
371
412
|
end
|
|
372
413
|
}
|
|
373
414
|
if is_array
|
|
374
|
-
[envi, RDL::Type::GenericType.new(
|
|
415
|
+
[envi, RDL::Type::GenericType.new(RDL::Globals.types[:array], RDL::Type::UnionType.new(*tis).canonical)]
|
|
375
416
|
else
|
|
376
417
|
[envi, RDL::Type::TupleType.new(*tis)]
|
|
377
418
|
end
|
|
@@ -394,7 +435,7 @@ module RDL::Typecheck
|
|
|
394
435
|
tkwsplat.cant_promote! # must remain finite hash
|
|
395
436
|
tlefts.concat(tkwsplat.elts.keys.map { |k| RDL::Type::SingletonType.new(k) })
|
|
396
437
|
trights.concat(tkwsplat.elts.values)
|
|
397
|
-
elsif tkwsplat.is_a?(RDL::Type::GenericType) && tkwsplat.base ==
|
|
438
|
+
elsif tkwsplat.is_a?(RDL::Type::GenericType) && tkwsplat.base == RDL::Globals.types[:hash]
|
|
398
439
|
is_fh = false
|
|
399
440
|
tlefts << tkwsplat.params[0]
|
|
400
441
|
trights << tkwsplat.params[1]
|
|
@@ -412,7 +453,7 @@ module RDL::Typecheck
|
|
|
412
453
|
else
|
|
413
454
|
tleft = RDL::Type::UnionType.new(*tlefts)
|
|
414
455
|
tright = RDL::Type::UnionType.new(*trights)
|
|
415
|
-
[envi, RDL::Type::GenericType.new(
|
|
456
|
+
[envi, RDL::Type::GenericType.new(RDL::Globals.types[:hash], tleft, tright)]
|
|
416
457
|
end
|
|
417
458
|
#TODO test!
|
|
418
459
|
# when :kwsplat # TODO!
|
|
@@ -423,7 +464,7 @@ module RDL::Typecheck
|
|
|
423
464
|
t1 = RDL::Type::NominalType.new(t1.val.class) if t1.is_a? RDL::Type::SingletonType
|
|
424
465
|
t2 = RDL::Type::NominalType.new(t2.val.class) if t2.is_a? RDL::Type::SingletonType
|
|
425
466
|
error :nonmatching_range_type, [t1, t2], e unless t1 <= t2 || t2 <= t1
|
|
426
|
-
[env2, RDL::Type::GenericType.new(
|
|
467
|
+
[env2, RDL::Type::GenericType.new(RDL::Globals.types[:range], t1)]
|
|
427
468
|
when :self
|
|
428
469
|
[env, env[:self]]
|
|
429
470
|
when :lvar, :ivar, :cvar, :gvar
|
|
@@ -431,7 +472,7 @@ module RDL::Typecheck
|
|
|
431
472
|
when :lvasgn, :ivasgn, :cvasgn, :gvasgn
|
|
432
473
|
x = e.children[0]
|
|
433
474
|
# if local var, lhs is bound to nil before assignment is executed! only matters in type checking for locals
|
|
434
|
-
env = env.bind(x,
|
|
475
|
+
env = env.bind(x, RDL::Globals.types[:nil]) if ((e.type == :lvasgn) && (not (env.has_key? x)))
|
|
435
476
|
envright, tright = tc(scope, env, e.children[1])
|
|
436
477
|
tc_vasgn(scope, envright, e.type, x, tright, e)
|
|
437
478
|
when :masgn
|
|
@@ -439,7 +480,7 @@ module RDL::Typecheck
|
|
|
439
480
|
e.children[0].children.each { |asgn|
|
|
440
481
|
next unless asgn.type == :lvasgn
|
|
441
482
|
x = e.children[0]
|
|
442
|
-
env = env.bind(x,
|
|
483
|
+
env = env.bind(x, RDL::Globals.types[:nil]) if (not (env.has_key? x)) # see lvasgn
|
|
443
484
|
# Note don't need to check outer_env here because will be checked by tc_vasgn below
|
|
444
485
|
}
|
|
445
486
|
envi, tright = tc(scope, env, e.children[1])
|
|
@@ -471,7 +512,7 @@ module RDL::Typecheck
|
|
|
471
512
|
}
|
|
472
513
|
[envi, tright]
|
|
473
514
|
end
|
|
474
|
-
elsif (tright.is_a? RDL::Type::GenericType) && (tright.base ==
|
|
515
|
+
elsif (tright.is_a? RDL::Type::GenericType) && (tright.base == RDL::Globals.types[:array])
|
|
475
516
|
tasgn = tright.params[0]
|
|
476
517
|
lhs.each { |asgn|
|
|
477
518
|
if asgn.type == :splat
|
|
@@ -489,16 +530,27 @@ module RDL::Typecheck
|
|
|
489
530
|
# (op-asgn (send recv meth) :op operand)
|
|
490
531
|
meth = e.children[0].children[1]
|
|
491
532
|
envleft, trecv = tc(scope, env, e.children[0].children[0]) # recv
|
|
492
|
-
|
|
533
|
+
elargs = e.children[0].children[2]
|
|
534
|
+
|
|
535
|
+
if elargs
|
|
536
|
+
envleft, elargs = tc(scope, envleft, elargs)
|
|
537
|
+
largs = [elargs]
|
|
538
|
+
else
|
|
539
|
+
largs = []
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
tloperand = tc_send(scope, envleft, trecv, meth, largs, nil, e.children[0]) # call recv.meth()
|
|
493
543
|
envoperand, troperand = tc(scope, envleft, e.children[2]) # operand
|
|
494
544
|
tright = tc_send(scope, envoperand, tloperand, e.children[1], [troperand], nil, e) # recv.meth().op(operand)
|
|
545
|
+
|
|
546
|
+
tright = largs.push(tright) if largs
|
|
495
547
|
mutation_meth = (meth.to_s + '=').to_sym
|
|
496
|
-
tres = tc_send(scope, envoperand, trecv, mutation_meth,
|
|
548
|
+
tres = tc_send(scope, envoperand, trecv, mutation_meth, tright, nil, e) # call recv.meth=(recvt.meth().op(operand))
|
|
497
549
|
[envoperand, tres]
|
|
498
550
|
else
|
|
499
551
|
# (op-asgn (Xvasgn var-name) :op operand)
|
|
500
552
|
x = e.children[0].children[0] # Note don't need to check outer_env here because will be checked by tc_vasgn below
|
|
501
|
-
env = env.bind(x,
|
|
553
|
+
env = env.bind(x, RDL::Globals.types[:nil]) if ((e.children[0].type == :lvasgn) && (not (env.has_key? x))) # see :lvasgn
|
|
502
554
|
envi, trecv = tc_var(scope, env, @@asgn_to_var[e.children[0].type], x, e.children[0]) # var being assigned to
|
|
503
555
|
envright, tright = tc(scope, envi, e.children[2]) # operand
|
|
504
556
|
trhs = tc_send(scope, envright, trecv, e.children[1], [tright], nil, e)
|
|
@@ -513,7 +565,7 @@ module RDL::Typecheck
|
|
|
513
565
|
envright, tright = tc(scope, envleft, e.children[1]) # operand
|
|
514
566
|
else
|
|
515
567
|
x = e.children[0].children[0] # Note don't need to check outer_env here because will be checked by tc_var below
|
|
516
|
-
env = env.bind(x,
|
|
568
|
+
env = env.bind(x, RDL::Globals.types[:nil]) if ((e.children[0].type == :lvasgn) && (not (env.has_key? x))) # see :lvasgn
|
|
517
569
|
envleft, tleft = tc_var(scope, env, @@asgn_to_var[e.children[0].type], x, e.children[0]) # var being assigned to
|
|
518
570
|
envright, tright = tc(scope, envleft, e.children[1])
|
|
519
571
|
end
|
|
@@ -534,20 +586,51 @@ module RDL::Typecheck
|
|
|
534
586
|
tc_vasgn(scope, envi, e.children[0].type, x, trhs, e)
|
|
535
587
|
end
|
|
536
588
|
when :nth_ref, :back_ref
|
|
537
|
-
[env,
|
|
589
|
+
[env, RDL::Globals.types[:string]]
|
|
538
590
|
when :const
|
|
539
591
|
c = nil
|
|
540
592
|
if e.children[0].nil?
|
|
541
|
-
|
|
593
|
+
case env[:self]
|
|
594
|
+
when RDL::Type::SingletonType
|
|
595
|
+
sclass = env[:self].val
|
|
596
|
+
when RDL::Type::NominalType
|
|
597
|
+
sclass = env[:self].klass
|
|
598
|
+
else
|
|
599
|
+
raise Exception, "unsupported env[self]=#{env[:self]}"
|
|
600
|
+
end
|
|
601
|
+
c1_str = RDL::Util.to_class_str(e.children[1])
|
|
602
|
+
self_klass_str = RDL::Util.to_class_str(sclass)
|
|
603
|
+
if self_klass_str.end_with?('::' + c1_str)
|
|
604
|
+
i = self_klass_str.rindex('::' + c1_str)
|
|
605
|
+
pc = RDL::Util.to_class self_klass_str[0..i-1]
|
|
606
|
+
c = pc.const_get(e.children[1])
|
|
607
|
+
else
|
|
608
|
+
if self_klass_str['::']
|
|
609
|
+
i = self_klass_str.rindex('::')
|
|
610
|
+
sclass = RDL::Util.to_class self_klass_str[0..i-1]
|
|
611
|
+
end
|
|
612
|
+
c = sclass.const_get(e.children[1])
|
|
613
|
+
end
|
|
542
614
|
elsif e.children[0].type == :cbase
|
|
543
615
|
raise "const cbase not implemented yet" # TODO!
|
|
544
616
|
elsif e.children[0].type == :lvar
|
|
545
617
|
raise "const lvar not implemented yet" # TODO!
|
|
618
|
+
elsif e.children[0].type == :const
|
|
619
|
+
if env[:self]
|
|
620
|
+
if env[:self].is_a?(RDL::Type::SingletonType)
|
|
621
|
+
ic = env[:self].val
|
|
622
|
+
else
|
|
623
|
+
ic = env[:self].class
|
|
624
|
+
end
|
|
625
|
+
else
|
|
626
|
+
ic = Object
|
|
627
|
+
end
|
|
628
|
+
c = get_leaves(e).inject(ic) {|m, c2| m.const_get(c2)}
|
|
546
629
|
else
|
|
547
630
|
raise "const other not implemented yet"
|
|
548
631
|
end
|
|
549
632
|
case c
|
|
550
|
-
when TrueClass, FalseClass, Complex, Rational,
|
|
633
|
+
when TrueClass, FalseClass, Complex, Rational, Integer, Float, Symbol, Class
|
|
551
634
|
[env, RDL::Type::SingletonType.new(c)]
|
|
552
635
|
when Module
|
|
553
636
|
t = RDL::Type::SingletonType.new(const_get(e.children[1]))
|
|
@@ -557,24 +640,25 @@ module RDL::Typecheck
|
|
|
557
640
|
end
|
|
558
641
|
when :defined?
|
|
559
642
|
# do not type check subexpression, since it may not be type correct, e.g., undefined variable
|
|
560
|
-
[env,
|
|
643
|
+
[env, RDL::Globals.types[:string]]
|
|
561
644
|
when :send, :csend
|
|
562
645
|
# children[0] = receiver; if nil, receiver is self
|
|
563
646
|
# children[1] = method name, a symbol
|
|
564
647
|
# children [2..] = actual args
|
|
565
|
-
return tc_var_type(scope, env, e) if e.children[
|
|
566
|
-
return tc_type_cast(scope, env, e) if e.children[1] == :type_cast && scope[:block].nil?
|
|
567
|
-
return tc_note_type(scope, env, e) if e.children[
|
|
648
|
+
return tc_var_type(scope, env, e) if (e.children[0].nil? || is_RDL(e.children[0])) && e.children[1] == :var_type
|
|
649
|
+
return tc_type_cast(scope, env, e) if is_RDL(e.children[0]) && e.children[1] == :type_cast && scope[:block].nil?
|
|
650
|
+
return tc_note_type(scope, env, e) if is_RDL(e.children[0]) && e.children[1] == :rdl_note_type
|
|
651
|
+
return tc_instantiate!(scope, env, e) if is_RDL(e.children[0]) && e.children[1] == :instantiate!
|
|
568
652
|
envi = env
|
|
569
653
|
tactuals = []
|
|
570
654
|
block = scope[:block]
|
|
571
|
-
scope_merge(scope, block: nil) { |sscope|
|
|
655
|
+
scope_merge(scope, block: nil, break: env, next: env) { |sscope|
|
|
572
656
|
e.children[2..-1].each { |ei|
|
|
573
657
|
if ei.type == :splat
|
|
574
658
|
envi, ti = tc(sscope, envi, ei.children[0])
|
|
575
659
|
if ti.is_a? RDL::Type::TupleType
|
|
576
660
|
tactuals.concat ti.params
|
|
577
|
-
elsif ti.is_a?(RDL::Type::GenericType) && ti.base ==
|
|
661
|
+
elsif ti.is_a?(RDL::Type::GenericType) && ti.base == RDL::Globals.types[:array]
|
|
578
662
|
tactuals << RDL::Type::VarargType.new(ti.params[0]) # Turn Array<t> into *t
|
|
579
663
|
else
|
|
580
664
|
error :cant_splat, [ti], ei.children[0]
|
|
@@ -631,15 +715,15 @@ RUBY
|
|
|
631
715
|
# when :not # in latest Ruby, not is a method call that could be redefined, so can't count on its behavior
|
|
632
716
|
# a1, t1 = tc(scope, a, e.children[0])
|
|
633
717
|
# if t1.is_a? RDL::Type::SingletonType
|
|
634
|
-
# if t1.val then [a1,
|
|
718
|
+
# if t1.val then [a1, RDL::Globals.types[:false]] else [a1, RDL::Globals.types[:true]] end
|
|
635
719
|
# else
|
|
636
|
-
# [a1,
|
|
720
|
+
# [a1, RDL::Globals.types[:bool]]
|
|
637
721
|
# end
|
|
638
722
|
when :if
|
|
639
723
|
envi, tguard = tc(scope, env, e.children[0]) # guard; any type allowed
|
|
640
724
|
# always type check both sides
|
|
641
|
-
envleft, tleft = if e.children[1].nil? then [envi,
|
|
642
|
-
envright, tright = if e.children[2].nil? then [envi,
|
|
725
|
+
envleft, tleft = if e.children[1].nil? then [envi, RDL::Globals.types[:nil]] else tc(scope, envi, e.children[1]) end # then
|
|
726
|
+
envright, tright = if e.children[2].nil? then [envi, RDL::Globals.types[:nil]] else tc(scope, envi, e.children[2]) end # else
|
|
643
727
|
if tguard.is_a? RDL::Type::SingletonType
|
|
644
728
|
if tguard.val then [envleft, tleft] else [envright, tright] end
|
|
645
729
|
else
|
|
@@ -685,7 +769,13 @@ RUBY
|
|
|
685
769
|
# previous, unrefined type
|
|
686
770
|
end
|
|
687
771
|
end
|
|
688
|
-
|
|
772
|
+
if wclause.children[-1] == nil
|
|
773
|
+
envbody = initial_env
|
|
774
|
+
tbody = RDL::Globals.types[:nil]
|
|
775
|
+
else
|
|
776
|
+
envbody, tbody = tc(scope, initial_env, wclause.children[-1]) # last wclause child is body
|
|
777
|
+
end
|
|
778
|
+
|
|
689
779
|
tbodies << tbody
|
|
690
780
|
envbodies << envbody
|
|
691
781
|
}
|
|
@@ -705,7 +795,7 @@ RUBY
|
|
|
705
795
|
# retry: not allowed
|
|
706
796
|
# redo: after loop guard, which is same as break
|
|
707
797
|
env_break, _ = tc(scope, env, e.children[0]) # guard can have any type, may exit after checking guard
|
|
708
|
-
scope_merge(scope, break: env_break, tbreak:
|
|
798
|
+
scope_merge(scope, break: env_break, tbreak: RDL::Globals.types[:nil], next: env, redo: env_break) { |lscope|
|
|
709
799
|
begin
|
|
710
800
|
old_break = lscope[:break]
|
|
711
801
|
old_next = lscope[:next]
|
|
@@ -724,7 +814,7 @@ RUBY
|
|
|
724
814
|
# next: before loop guard; argument not allowed
|
|
725
815
|
# retry: not allowed
|
|
726
816
|
# redo: beginning of body, which is same as after guard, i.e., same as break
|
|
727
|
-
scope_merge(scope, break: nil, tbreak:
|
|
817
|
+
scope_merge(scope, break: nil, tbreak: RDL::Globals.types[:nil], next: nil, redo: nil) { |lscope|
|
|
728
818
|
if e.children[1]
|
|
729
819
|
env_body, _ = tc(lscope, env, e.children[1])
|
|
730
820
|
lscope[:next] = Env.join(e, lscope[:next], env_body)
|
|
@@ -807,12 +897,16 @@ RUBY
|
|
|
807
897
|
scope[tkw_name] = RDL::Type::UnionType.new(scope[tkw_name], tkw)
|
|
808
898
|
end
|
|
809
899
|
scope[e.type] = Env.join(e, scope[e.type], env)
|
|
810
|
-
[env,
|
|
900
|
+
[env, RDL::Globals.types[:bot]]
|
|
811
901
|
when :return
|
|
812
902
|
# TODO return in lambda returns from lambda and not outer scope
|
|
813
|
-
|
|
903
|
+
if e.children[0]
|
|
904
|
+
env1, t1 = tc(scope, env, e.children[0])
|
|
905
|
+
else
|
|
906
|
+
env1, t1 = [env, RDL::Globals.types[:nil]]
|
|
907
|
+
end
|
|
814
908
|
error :bad_return_type, [t1.to_s, scope[:tret]], e unless t1 <= scope[:tret]
|
|
815
|
-
[env1,
|
|
909
|
+
[env1, RDL::Globals.types[:bot]] # return is a void value expression
|
|
816
910
|
when :begin, :kwbegin # sequencing
|
|
817
911
|
envi = env
|
|
818
912
|
ti = nil
|
|
@@ -864,12 +958,68 @@ RUBY
|
|
|
864
958
|
texns << RDL::Type::NominalType.new(texn.val)
|
|
865
959
|
}
|
|
866
960
|
else
|
|
867
|
-
texns = [
|
|
961
|
+
texns = [RDL::Globals.types[:standard_error]]
|
|
868
962
|
end
|
|
869
963
|
if e.children[1]
|
|
870
964
|
envi, _ = tc_vasgn(scope, envi, :lvasgn, e.children[1].children[0], RDL::Type::UnionType.new(*texns), e.children[1])
|
|
871
965
|
end
|
|
872
966
|
tc(scope, envi, e.children[2])
|
|
967
|
+
when :super
|
|
968
|
+
envi = env
|
|
969
|
+
tactuals = []
|
|
970
|
+
block = scope[:block]
|
|
971
|
+
|
|
972
|
+
if block
|
|
973
|
+
raise Exception, 'block in super method with block not supported'
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
scope_merge(scope, block: nil, break: env, next: env) { |sscope|
|
|
977
|
+
e.children.each { |ei|
|
|
978
|
+
if ei.type == :splat
|
|
979
|
+
envi, ti = tc(sscope, envi, ei.children[0])
|
|
980
|
+
if ti.is_a? RDL::Type::TupleType
|
|
981
|
+
tactuals.concat ti.params
|
|
982
|
+
elsif ti.is_a?(RDL::Type::GenericType) && ti.base == $__rdl_array_type
|
|
983
|
+
tactuals << RDL::Type::VarargType.new(ti.params[0]) # Turn Array<t> into *t
|
|
984
|
+
else
|
|
985
|
+
error :cant_splat, [ti], ei.children[0]
|
|
986
|
+
end
|
|
987
|
+
elsif ei.type == :block_pass
|
|
988
|
+
raise RuntimeError, "impossible to pass block arg and literal block" if scope[:block]
|
|
989
|
+
envi, ti = tc(sscope, envi, ei.children[0])
|
|
990
|
+
# convert using to_proc if necessary
|
|
991
|
+
ti = tc_send(sscope, envi, ti, :to_proc, [], nil, ei) unless ti.is_a? RDL::Type::MethodType
|
|
992
|
+
block = [ti, ei]
|
|
993
|
+
else
|
|
994
|
+
envi, ti = tc(sscope, envi, ei)
|
|
995
|
+
tactuals << ti
|
|
996
|
+
end
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
trecv = get_super_owner(envi[:self], @cur_meth[1])
|
|
1000
|
+
[envi, tc_send(sscope, envi, trecv, @cur_meth[1], tactuals, block, e).canonical]
|
|
1001
|
+
}
|
|
1002
|
+
when :zsuper
|
|
1003
|
+
envi = env
|
|
1004
|
+
block = scope[:block]
|
|
1005
|
+
|
|
1006
|
+
if block
|
|
1007
|
+
raise Exception, 'super method not supported'
|
|
1008
|
+
end
|
|
1009
|
+
|
|
1010
|
+
klass = RDL::Util.to_class @cur_meth[0]
|
|
1011
|
+
mname = @cur_meth[1]
|
|
1012
|
+
sklass = get_super_owner_from_class klass, mname
|
|
1013
|
+
sklass_str = RDL::Util.to_class_str sklass
|
|
1014
|
+
stype = RDL::Globals.info.get_with_aliases(sklass_str, mname, :type)
|
|
1015
|
+
error :no_instance_method_type, [sklass_str, mname], e unless stype
|
|
1016
|
+
raise Exception, "unsupported intersection type in super, e = #{e}" if stype.size > 1
|
|
1017
|
+
tactuals = stype[0].args
|
|
1018
|
+
|
|
1019
|
+
scope_merge(scope, block: nil, break: env, next: env) { |sscope|
|
|
1020
|
+
trecv = get_super_owner(envi[:self], @cur_meth[1])
|
|
1021
|
+
[envi, tc_send(sscope, envi, trecv, @cur_meth[1], tactuals, block, e).canonical]
|
|
1022
|
+
}
|
|
873
1023
|
else
|
|
874
1024
|
raise RuntimeError, "Expression kind #{e.type} unsupported"
|
|
875
1025
|
end
|
|
@@ -890,13 +1040,13 @@ RUBY
|
|
|
890
1040
|
end
|
|
891
1041
|
when :ivar, :cvar, :gvar
|
|
892
1042
|
klass = (if kind == :gvar then RDL::Util::GLOBAL_NAME else env[:self] end)
|
|
893
|
-
unless
|
|
1043
|
+
unless RDL::Globals.info.has?(klass, name, :type)
|
|
894
1044
|
kind_text = (if kind == :ivar then "instance"
|
|
895
1045
|
elsif kind == :cvar then "class"
|
|
896
1046
|
else "global" end)
|
|
897
|
-
error :untyped_var, [kind_text, name], e
|
|
1047
|
+
error :untyped_var, [kind_text, name, klass], e
|
|
898
1048
|
end
|
|
899
|
-
[env,
|
|
1049
|
+
[env, RDL::Globals.info.get(klass, name, :type).canonical]
|
|
900
1050
|
else
|
|
901
1051
|
raise RuntimeError, "unknown kind #{kind}"
|
|
902
1052
|
end
|
|
@@ -919,13 +1069,13 @@ RUBY
|
|
|
919
1069
|
end
|
|
920
1070
|
when :ivasgn, :cvasgn, :gvasgn
|
|
921
1071
|
klass = (if kind == :gvasgn then RDL::Util::GLOBAL_NAME else env[:self] end)
|
|
922
|
-
unless
|
|
1072
|
+
unless RDL::Globals.info.has?(klass, name, :type)
|
|
923
1073
|
kind_text = (if kind == :ivasgn then "instance"
|
|
924
1074
|
elsif kind == :cvasgn then "class"
|
|
925
1075
|
else "global" end)
|
|
926
|
-
error :untyped_var, [kind_text, name], e
|
|
1076
|
+
error :untyped_var, [kind_text, name, klass], e
|
|
927
1077
|
end
|
|
928
|
-
tleft =
|
|
1078
|
+
tleft = RDL::Globals.info.get(klass, name, :type)
|
|
929
1079
|
error :vasgn_incompat, [tright.to_s, tleft.to_s], e unless tright <= tleft
|
|
930
1080
|
[env, tright.canonical]
|
|
931
1081
|
when :send
|
|
@@ -955,24 +1105,24 @@ RUBY
|
|
|
955
1105
|
typ_str = e.children[3].children[0] if (e.children[3].type == :str) || (e.children[3].type == :string)
|
|
956
1106
|
error :var_type_format, [], e.children[3] if typ_str.nil?
|
|
957
1107
|
begin
|
|
958
|
-
typ =
|
|
1108
|
+
typ = RDL::Globals.parser.scan_str("#T " + typ_str)
|
|
959
1109
|
rescue Racc::ParseError => err
|
|
960
1110
|
error :generic_error, [err.to_s[1..-1]], e.children[3] # remove initial newline
|
|
961
1111
|
end
|
|
962
|
-
[env.fix(var, typ),
|
|
1112
|
+
[env.fix(var, typ), RDL::Globals.types[:nil]]
|
|
963
1113
|
end
|
|
964
1114
|
|
|
965
1115
|
def self.tc_type_cast(scope, env, e)
|
|
966
|
-
error :type_cast_format, [], e unless e.children.length <=
|
|
967
|
-
typ_str = e.children[
|
|
968
|
-
error :type_cast_format, [], e.children[
|
|
1116
|
+
error :type_cast_format, [], e unless e.children.length <= 5
|
|
1117
|
+
typ_str = e.children[3].children[0] if (e.children[3].type == :str) || (e.children[3].type == :string)
|
|
1118
|
+
error :type_cast_format, [], e.children[3] if typ_str.nil?
|
|
969
1119
|
begin
|
|
970
|
-
typ =
|
|
1120
|
+
typ = RDL::Globals.parser.scan_str("#T " + typ_str)
|
|
971
1121
|
rescue Racc::ParseError => err
|
|
972
|
-
error :generic_error, [err.to_s[1..-1]], e.children[
|
|
1122
|
+
error :generic_error, [err.to_s[1..-1]], e.children[3] # remove initial newline
|
|
973
1123
|
end
|
|
974
|
-
if e.children[
|
|
975
|
-
fh = e.children[
|
|
1124
|
+
if e.children[4]
|
|
1125
|
+
fh = e.children[4]
|
|
976
1126
|
error :type_cast_format, [], fh unless fh.type == :hash && fh.children.length == 1
|
|
977
1127
|
pair = fh.children[0]
|
|
978
1128
|
error :type_cast_format, [], fh unless pair.type == :pair && pair.children[0].type == :sym && pair.children[0].children[0] == :force
|
|
@@ -983,12 +1133,66 @@ RUBY
|
|
|
983
1133
|
end
|
|
984
1134
|
|
|
985
1135
|
def self.tc_note_type(scope, env, e)
|
|
986
|
-
error :note_type_format, [], e unless e.children.length ==
|
|
987
|
-
env, typ = tc(scope, env, e.children[
|
|
988
|
-
note :note_type, [typ], e.children[
|
|
1136
|
+
error :note_type_format, [], e unless e.children.length == 4 && scope[:block].nil?
|
|
1137
|
+
env, typ = tc(scope, env, e.children[3])
|
|
1138
|
+
note :note_type, [typ], e.children[3]
|
|
989
1139
|
[env, typ]
|
|
990
1140
|
end
|
|
991
1141
|
|
|
1142
|
+
def self.tc_instantiate!(scope, env, e)
|
|
1143
|
+
error :instantiate_format, [], e if e.children.length < 4
|
|
1144
|
+
env, obj_typ = tc(scope, env, e.children[2])
|
|
1145
|
+
case obj_typ
|
|
1146
|
+
when RDL::Type::GenericType
|
|
1147
|
+
klass = obj_typ.base.name.to_s
|
|
1148
|
+
when RDL::Type::NominalType
|
|
1149
|
+
klass = obj_typ.name.to_s
|
|
1150
|
+
when RDL::Type::TupleType
|
|
1151
|
+
klass = "Array"
|
|
1152
|
+
when RDL::Type::FiniteHashType
|
|
1153
|
+
klass = "Hash"
|
|
1154
|
+
when RDL::Type::SingletonType
|
|
1155
|
+
klass = if obj_typ.val.is_a?(Class) then obj_typ.val.to_s else obj_typ.val.class.to_s end
|
|
1156
|
+
else
|
|
1157
|
+
error :bad_inst_type, [obj_typ], e
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
formals, _, _ = RDL::Globals.type_params[klass]
|
|
1161
|
+
|
|
1162
|
+
if e.children.last.type == :hash
|
|
1163
|
+
typ_args = e.children[3..-2]
|
|
1164
|
+
else
|
|
1165
|
+
typ_args = e.children[3..-1]
|
|
1166
|
+
end
|
|
1167
|
+
error :inst_not_param, [klass], e unless formals
|
|
1168
|
+
error :inst_num_args, [formals.size, typ_args.size], e unless formals.size == typ_args.size
|
|
1169
|
+
|
|
1170
|
+
new_typs = []
|
|
1171
|
+
typ_args.each { |a|
|
|
1172
|
+
env, arg_typ = tc(scope, env, a)
|
|
1173
|
+
case arg_typ
|
|
1174
|
+
when RDL::Type::SingletonType
|
|
1175
|
+
error :instantiate_format, [], a unless arg_typ.val.is_a?(Class)
|
|
1176
|
+
new_typs << RDL::Globals.parser.scan_str("#T #{arg_typ.val}")
|
|
1177
|
+
else
|
|
1178
|
+
error :instantiate_format, [], a unless (a.type == :str) || (a.type == :string) || (a.type == :sym)
|
|
1179
|
+
new_typs << RDL::Globals.parser.scan_str("#T #{a.children[0]}")
|
|
1180
|
+
end
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
t = RDL::Type::GenericType.new(RDL::Type::NominalType.new(klass), *new_typs)
|
|
1184
|
+
case e.children[2].type
|
|
1185
|
+
when :lvar
|
|
1186
|
+
var_name = e.children[2].children[0]
|
|
1187
|
+
else
|
|
1188
|
+
raise RuntimeError, "instantiate! expects local variable as receiver"
|
|
1189
|
+
error :inst_lvar, [], e
|
|
1190
|
+
end
|
|
1191
|
+
|
|
1192
|
+
env = env.bind(var_name, t)
|
|
1193
|
+
[env, t]
|
|
1194
|
+
end
|
|
1195
|
+
|
|
992
1196
|
# Type check a send
|
|
993
1197
|
# [+ scope +] is the scope; used only for checking block arguments
|
|
994
1198
|
# [+ env +] is the environment; used only for checking block arguments.
|
|
@@ -1016,14 +1220,24 @@ RUBY
|
|
|
1016
1220
|
# Like tc_send but trecv should never be a union type
|
|
1017
1221
|
# Returns array of possible return types, or throws exception if there are none
|
|
1018
1222
|
def self.tc_send_one_recv(scope, env, trecv, meth, tactuals, block, e)
|
|
1223
|
+
return tc_send_class(trecv, e) if (meth == :class) && (tactuals.empty?)
|
|
1019
1224
|
tmeth_inter = [] # Array<MethodType>, i.e., an intersection types
|
|
1020
1225
|
case trecv
|
|
1021
1226
|
when RDL::Type::SingletonType
|
|
1022
1227
|
if trecv.val.is_a? Class or trecv.val.is_a? Module
|
|
1023
|
-
|
|
1228
|
+
if meth == :new then
|
|
1229
|
+
meth_lookup = :initialize
|
|
1230
|
+
trecv_lookup = trecv.val.to_s
|
|
1231
|
+
self_inst = RDL::Type::NominalType.new(trecv.val)
|
|
1232
|
+
else
|
|
1233
|
+
meth_lookup = meth
|
|
1234
|
+
trecv_lookup = RDL::Util.add_singleton_marker(trecv.val.to_s)
|
|
1235
|
+
self_inst = trecv
|
|
1236
|
+
end
|
|
1237
|
+
ts = lookup(scope, trecv_lookup, meth_lookup, e)
|
|
1024
1238
|
ts = [RDL::Type::MethodType.new([], nil, RDL::Type::NominalType.new(trecv.val))] if (meth == :new) && (ts.nil?) # there's always a nullary new if initialize is undefined
|
|
1025
1239
|
error :no_singleton_method_type, [trecv.val, meth], e unless ts
|
|
1026
|
-
inst = {self:
|
|
1240
|
+
inst = {self: self_inst}
|
|
1027
1241
|
tmeth_inter = ts.map { |t| t.instantiate(inst) }
|
|
1028
1242
|
elsif trecv.val.is_a?(Symbol) && meth == :to_proc
|
|
1029
1243
|
# Symbol#to_proc on a singleton symbol type produces a Proc for the method of the same name
|
|
@@ -1064,10 +1278,10 @@ RUBY
|
|
|
1064
1278
|
tmeth_inter = [trecv]
|
|
1065
1279
|
else
|
|
1066
1280
|
# treat as Proc
|
|
1067
|
-
tc_send_one_recv(scope, env,
|
|
1281
|
+
tc_send_one_recv(scope, env, RDL::Globals.types[:proc], meth, tactuals, block, e)
|
|
1068
1282
|
end
|
|
1069
1283
|
else
|
|
1070
|
-
raise RuntimeError, "receiver type #{trecv} not supported yet"
|
|
1284
|
+
raise RuntimeError, "receiver type #{trecv} not supported yet, meth=#{meth}"
|
|
1071
1285
|
end
|
|
1072
1286
|
|
|
1073
1287
|
trets = [] # all possible return types
|
|
@@ -1082,7 +1296,19 @@ RUBY
|
|
|
1082
1296
|
tmeth_inst = tc_arg_types(tmeth, tactuals_expanded)
|
|
1083
1297
|
if tmeth_inst
|
|
1084
1298
|
tc_block(scope, env, tmeth.block, block, tmeth_inst) if block
|
|
1085
|
-
|
|
1299
|
+
if trecv.is_a?(RDL::Type::SingletonType) && meth == :new
|
|
1300
|
+
init_typ = RDL::Type::NominalType.new(trecv.val)
|
|
1301
|
+
if (tmeth.ret.instance_of?(RDL::Type::GenericType))
|
|
1302
|
+
error :bad_initialize_type, [], e unless (tmeth.ret.base == init_typ)
|
|
1303
|
+
elsif (tmeth.ret.instance_of?(RDL::Type::AnnotatedArgType) || tmeth.ret.instance_of?(RDL::Type::DependentArgType))
|
|
1304
|
+
error :bad_initialize_type, [], e unless (tmeth.ret.type == init_typ)
|
|
1305
|
+
else
|
|
1306
|
+
error :bad_initialize_type, [], e unless (tmeth.ret == init_typ)
|
|
1307
|
+
end
|
|
1308
|
+
trets_tmp << init_typ
|
|
1309
|
+
else
|
|
1310
|
+
trets_tmp << tmeth.ret.instantiate(tmeth_inst) # found a match for this subunion; add its return type to trets_tmp
|
|
1311
|
+
end
|
|
1086
1312
|
end
|
|
1087
1313
|
end
|
|
1088
1314
|
}
|
|
@@ -1119,6 +1345,33 @@ RUBY
|
|
|
1119
1345
|
return trets
|
|
1120
1346
|
end
|
|
1121
1347
|
|
|
1348
|
+
def self.tc_send_class(trecv, e)
|
|
1349
|
+
case trecv
|
|
1350
|
+
when RDL::Type::SingletonType
|
|
1351
|
+
if trecv.val.is_a? Class
|
|
1352
|
+
[RDL::Type::SingletonType.new(Class)]
|
|
1353
|
+
elsif trecv.val.is_a? Module
|
|
1354
|
+
[RDL::Type::SingletonType.new(Module)]
|
|
1355
|
+
else
|
|
1356
|
+
[RDL::Type::SingletonType.new(trecv.val.class)]
|
|
1357
|
+
end
|
|
1358
|
+
when RDL::Type::NominalType
|
|
1359
|
+
[RDL::Type::SingletonType.new(trecv.klass)]
|
|
1360
|
+
when RDL::Type::GenericType
|
|
1361
|
+
[RDL::Type::SingletonType.new(trecv.base.klass)]
|
|
1362
|
+
when RDL::Type::TupleType
|
|
1363
|
+
[RDL::Type::SingletonType.new(Array)]
|
|
1364
|
+
when RDL::Type::FiniteHashType
|
|
1365
|
+
[RDL::Type::SingletonType.new(Hash)]
|
|
1366
|
+
when RDL::Type::VarType
|
|
1367
|
+
error :recv_var_type, [trecv], e
|
|
1368
|
+
when RDL::Type::MethodType
|
|
1369
|
+
[RDL::Type::SingletonType.new(Proc)]
|
|
1370
|
+
else
|
|
1371
|
+
raise RuntimeError, "Unexpected receiver type #{trecv}"
|
|
1372
|
+
end
|
|
1373
|
+
end
|
|
1374
|
+
|
|
1122
1375
|
# [+ tmeth +] is MethodType
|
|
1123
1376
|
# [+ actuals +] is Array<Type> containing the actual argument types
|
|
1124
1377
|
# return instiation (possibly empty) that makes actuals match method type (if any), nil otherwise
|
|
@@ -1194,7 +1447,7 @@ RUBY
|
|
|
1194
1447
|
scope_merge(scope, outer_env: env) { |bscope|
|
|
1195
1448
|
# note: okay if outer_env shadows, since nested scope will include outer scope by next line
|
|
1196
1449
|
env = env.merge(Env.new(targs))
|
|
1197
|
-
_, body_type = if body.nil? then [nil,
|
|
1450
|
+
_, body_type = if body.nil? then [nil, RDL::Globals.types[:nil]] else tc(bscope, env.merge(Env.new(targs)), body) end
|
|
1198
1451
|
error :bad_return_type, [body_type, tblock.ret], body unless body.nil? || RDL::Type::Type.leq(body_type, tblock.ret, inst, false)
|
|
1199
1452
|
#
|
|
1200
1453
|
}
|
|
@@ -1223,7 +1476,7 @@ RUBY
|
|
|
1223
1476
|
return t if k == klass && m = name
|
|
1224
1477
|
}
|
|
1225
1478
|
end
|
|
1226
|
-
t =
|
|
1479
|
+
t = RDL::Globals.info.get_with_aliases(klass, name, :type)
|
|
1227
1480
|
return t if t # simplest case, no need to walk inheritance hierarchy
|
|
1228
1481
|
the_klass = RDL::Util.to_class(klass)
|
|
1229
1482
|
is_singleton = RDL::Util.has_singleton_marker(the_klass.to_s)
|
|
@@ -1234,11 +1487,11 @@ RUBY
|
|
|
1234
1487
|
next if (ancestor.instance_of? Module) && (included.member? ancestor) && is_singleton
|
|
1235
1488
|
# extended (i.e., not included) modules' instance methods get added as singleton methods, so can't be in class
|
|
1236
1489
|
next if (ancestor.instance_of? Module) && (not (included.member? ancestor)) && (not is_singleton)
|
|
1237
|
-
tancestor =
|
|
1490
|
+
tancestor = RDL::Globals.info.get_with_aliases(ancestor.to_s, name, :type)
|
|
1238
1491
|
return tancestor if tancestor
|
|
1239
1492
|
# special caes: Kernel's singleton methods are *also* added when included?!
|
|
1240
1493
|
if ancestor == Kernel
|
|
1241
|
-
tancestor =
|
|
1494
|
+
tancestor = RDL::Globals.info.get_with_aliases(RDL::Util.add_singleton_marker('Kernel'), name, :type)
|
|
1242
1495
|
return tancestor if tancestor
|
|
1243
1496
|
end
|
|
1244
1497
|
if ancestor.instance_methods(false).member?(name)
|
|
@@ -1255,56 +1508,66 @@ RUBY
|
|
|
1255
1508
|
end
|
|
1256
1509
|
end
|
|
1257
1510
|
|
|
1258
|
-
#
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1511
|
+
# Use parser's Diagnostic to output RDL typechecker error messages
|
|
1512
|
+
class Diagnostic < Parser::Diagnostic
|
|
1513
|
+
|
|
1514
|
+
def message
|
|
1515
|
+
RDL_MESSAGES[@reason] % @arguments
|
|
1516
|
+
end
|
|
1517
|
+
|
|
1518
|
+
RDL_MESSAGES = {
|
|
1519
|
+
bad_return_type: "got type `%s' where return type `%s' expected",
|
|
1520
|
+
bad_inst_type: "instantiate! called on object of type `%s' where Generic Type was expected",
|
|
1521
|
+
inst_not_param: "instantiate! receiver is of class `%s' which is not parameterized",
|
|
1522
|
+
inst_num_args: "instantiate! expecting `%s' type parameters, got `%s' parameters",
|
|
1523
|
+
inst_lvar: "instantiate! expects local variable as receiver",
|
|
1524
|
+
bad_initialize_type: 'initialize method must always be annotated to return type "self" or a GenericType where the base is "self"',
|
|
1525
|
+
undefined_local_or_method: "undefined local variable or method `%s'",
|
|
1526
|
+
nonmatching_range_type: "attempt to construct range with non-matching types `%s' and `%s'",
|
|
1527
|
+
no_instance_method_type: "no type information for instance method `%s#%s'",
|
|
1528
|
+
no_singleton_method_type: "no type information for class/singleton method `%s.%s'",
|
|
1529
|
+
arg_type_single_receiver_error: "argument type error for instance method `%s#%s'\n%s",
|
|
1530
|
+
untyped_var: "no type for %s variable `%s' in class %s",
|
|
1531
|
+
vasgn_incompat: "incompatible types: `%s' can't be assigned to variable of type `%s'",
|
|
1532
|
+
inconsistent_var_type: "local variable `%s' has declared type on some paths but not all",
|
|
1533
|
+
inconsistent_var_type_type: "local variable `%s' declared with inconsistent types %s",
|
|
1534
|
+
no_each_type: "can't find `each' method with signature `() { (t1) -> t2 } -> t3' in class `%s'",
|
|
1535
|
+
tuple_finite_hash_promote: "can't promote `%s' to `%s'",
|
|
1536
|
+
masgn_bad_rhs: "multiple assignment has right-hand side of type `%s' where tuple or array expected",
|
|
1537
|
+
masgn_num: "can't multiple-assign %d values to %d variables",
|
|
1538
|
+
masgn_bad_lhs: "no corresponding right-hand side elemnt for left-hand side assignee",
|
|
1539
|
+
kw_not_allowed: "can't use `%s' in current scope",
|
|
1540
|
+
kw_arg_not_allowed: "argument to `%s' not allowed in current scope",
|
|
1541
|
+
no_block: "attempt to call yield in method not declared to take a block argument",
|
|
1542
|
+
block_block: "can't call yield on a block expecting another block argument",
|
|
1543
|
+
block_type_error: "argument type error for block\n%s",
|
|
1544
|
+
missing_ancestor_type: "ancestor `%s' of `%s' has method `%s' but no type for it",
|
|
1545
|
+
type_cast_format: "type_cast must be called as `type_cast obj, type-string' or `type_cast obj, type-string, force: expr'",
|
|
1546
|
+
instantiate_format: "instantiate! must be called as `instantiate! type*' or `instantiate! type*, check: bool' where type is a string, symbol, or class for static type checking.",
|
|
1547
|
+
var_type_format: "var_type must be called as `var_type :var-name, type-string'",
|
|
1548
|
+
puts_type_format: "puts_type must be called as `puts_type e'",
|
|
1549
|
+
generic_error: "%s",
|
|
1550
|
+
exn_type: "can't determine exception type",
|
|
1551
|
+
cant_splat: "can't type splat with element of type `%s'",
|
|
1552
|
+
for_collection: "can't type for with collection of type `%s'",
|
|
1553
|
+
note_type: "Type is `%s'",
|
|
1554
|
+
note_message: "%s",
|
|
1555
|
+
recv_var_type: "receiver whose type is unconstrained variable `%s' not allowed",
|
|
1556
|
+
type_args_more: "%s type accepts more arguments than actual %s definition",
|
|
1557
|
+
type_args_fewer: "%s type accepts fewer arguments than actual %s definition",
|
|
1558
|
+
type_arg_kind_mismatch: "%s type has %s argument but actual argument is %s",
|
|
1559
|
+
type_args_no_kws: "%s type does not expect keyword arguments but actual expects keywords",
|
|
1560
|
+
type_args_no_kw: "%s type does not expect keyword argument `%s'",
|
|
1561
|
+
type_args_kw_mismatch: "%s type has %s keyword `%s' but actual argument is %s",
|
|
1562
|
+
type_args_kw_more: "%s type expects keywords `%s' that are not expected by actual %s",
|
|
1563
|
+
type_args_no_kw_rest: "%s type has no rest keyword but actual method accepts rest keywords",
|
|
1564
|
+
type_args_kw_rest: "%s type has rest keyword but actual method does not accept rest keywords",
|
|
1565
|
+
optional_default_type: "default value has type `%s' where type `%s' expected",
|
|
1566
|
+
optional_default_kw_type: "default value for `%s' has type `%s' where type `%s' expected",
|
|
1567
|
+
type_arg_block: "%s type does not expect block but actual %s takes block",
|
|
1568
|
+
bad_block_arg_type: "block argument has type `%s' but expecting type `%s'",
|
|
1569
|
+
non_block_block_arg: "block argument should have a block type but instead has type `%s'",
|
|
1570
|
+
proc_block_arg_type: "block argument is a Proc; can't tell if it matches expected type `%s'",
|
|
1571
|
+
no_type_for_symbol: "can't find type for method corresponding to `%s.to_proc'",
|
|
1572
|
+
}
|
|
1573
|
+
end
|