rdl 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +7 -6
- data/CHANGES.md +29 -0
- data/README.md +94 -26
- data/lib/rdl/boot.rb +82 -41
- data/lib/rdl/boot_rails.rb +5 -0
- data/lib/rdl/config.rb +9 -1
- data/lib/rdl/query.rb +2 -2
- data/lib/rdl/typecheck.rb +972 -225
- data/lib/rdl/types/annotated_arg.rb +8 -0
- data/lib/rdl/types/ast_node.rb +73 -0
- data/lib/rdl/types/bot.rb +8 -0
- data/lib/rdl/types/bound_arg.rb +63 -0
- data/lib/rdl/types/computed.rb +48 -0
- data/lib/rdl/types/dependent_arg.rb +9 -0
- data/lib/rdl/types/dynamic.rb +61 -0
- data/lib/rdl/types/finite_hash.rb +54 -9
- data/lib/rdl/types/generic.rb +33 -0
- data/lib/rdl/types/intersection.rb +8 -0
- data/lib/rdl/types/lexer.rex +6 -1
- data/lib/rdl/types/lexer.rex.rb +13 -1
- data/lib/rdl/types/method.rb +14 -0
- data/lib/rdl/types/nominal.rb +8 -0
- data/lib/rdl/types/non_null.rb +8 -0
- data/lib/rdl/types/optional.rb +8 -0
- data/lib/rdl/types/parser.racc +31 -5
- data/lib/rdl/types/parser.tab.rb +540 -302
- data/lib/rdl/types/rdl_types.rb +45 -0
- data/lib/rdl/types/singleton.rb +14 -1
- data/lib/rdl/types/string.rb +104 -0
- data/lib/rdl/types/structural.rb +8 -0
- data/lib/rdl/types/top.rb +8 -0
- data/lib/rdl/types/tuple.rb +32 -8
- data/lib/rdl/types/type.rb +54 -11
- data/lib/rdl/types/union.rb +41 -2
- data/lib/rdl/types/var.rb +10 -0
- data/lib/rdl/types/vararg.rb +8 -0
- data/lib/rdl/util.rb +13 -10
- data/lib/rdl/wrap.rb +271 -27
- data/lib/rdl_disable.rb +16 -2
- data/lib/types/active_record.rb +1 -0
- data/lib/types/core/array.rb +442 -23
- data/lib/types/core/basic_object.rb +3 -3
- data/lib/types/core/bigdecimal.rb +5 -0
- data/lib/types/core/class.rb +2 -0
- data/lib/types/core/dir.rb +3 -3
- data/lib/types/core/enumerable.rb +4 -4
- data/lib/types/core/enumerator.rb +1 -1
- data/lib/types/core/file.rb +4 -4
- data/lib/types/core/float.rb +203 -0
- data/lib/types/core/hash.rb +390 -15
- data/lib/types/core/integer.rb +223 -10
- data/lib/types/core/io.rb +2 -2
- data/lib/types/core/kernel.rb +8 -5
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/module.rb +3 -3
- data/lib/types/core/numeric.rb +0 -2
- data/lib/types/core/object.rb +5 -5
- data/lib/types/core/pathname.rb +2 -2
- data/lib/types/core/process.rb +1 -3
- data/lib/types/core/range.rb +1 -1
- data/lib/types/core/regexp.rb +2 -2
- data/lib/types/core/set.rb +1 -1
- data/lib/types/core/string.rb +408 -16
- data/lib/types/core/symbol.rb +3 -3
- data/lib/types/core/time.rb +1 -1
- data/lib/types/core/uri.rb +13 -13
- data/lib/types/rails/_helpers.rb +7 -1
- data/lib/types/rails/action_controller/mime_responds.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +42 -30
- data/lib/types/rails/active_record/comp_types.rb +637 -0
- data/lib/types/rails/active_record/finder_methods.rb +1 -1
- data/lib/types/rails/active_record/model_schema.rb +28 -16
- data/lib/types/rails/active_record/relation.rb +5 -3
- data/lib/types/rails/active_record/sql-strings.rb +166 -0
- data/lib/types/rails/string.rb +1 -1
- data/lib/types/sequel.rb +1 -0
- data/lib/types/sequel/comp_types.rb +581 -0
- data/rdl.gemspec +5 -4
- data/test/test_alias.rb +4 -0
- data/test/test_array_types.rb +244 -0
- data/test/test_bound_types.rb +80 -0
- data/test/test_contract.rb +4 -0
- data/test/test_dsl.rb +5 -0
- data/test/test_dyn_comptype_checks.rb +206 -0
- data/test/test_generic.rb +21 -20
- data/test/test_hash_types.rb +322 -0
- data/test/test_intersection.rb +1 -0
- data/test/test_le.rb +29 -4
- data/test/test_member.rb +3 -1
- data/test/test_parser.rb +5 -0
- data/test/test_query.rb +1 -0
- data/test/test_rdl.rb +63 -28
- data/test/test_rdl_type.rb +4 -0
- data/test/test_string_types.rb +102 -0
- data/test/test_type_contract.rb +59 -37
- data/test/test_typecheck.rb +480 -75
- data/test/test_types.rb +17 -0
- data/test/test_wrap.rb +5 -0
- metadata +35 -5
- data/lib/types/rails/active_record/schema_types.rb +0 -51
data/lib/rdl/types/var.rb
CHANGED
data/lib/rdl/types/vararg.rb
CHANGED
@@ -53,6 +53,14 @@ module RDL::Type
|
|
53
53
|
return VarargType.new(@type.instantiate(inst))
|
54
54
|
end
|
55
55
|
|
56
|
+
def widen
|
57
|
+
return VarargType.new(@type.widen)
|
58
|
+
end
|
59
|
+
|
60
|
+
def copy
|
61
|
+
return VarargType.new(@type.copy)
|
62
|
+
end
|
63
|
+
|
56
64
|
def hash # :nodoc:
|
57
65
|
return 59 + @type.hash
|
58
66
|
end
|
data/lib/rdl/util.rb
CHANGED
@@ -60,20 +60,15 @@ class RDL::Util
|
|
60
60
|
begin
|
61
61
|
sk = self.to_class klass
|
62
62
|
msym = method.to_sym
|
63
|
-
mstr = method.to_s
|
64
|
-
sk.instance_method msym
|
65
63
|
rescue NameError
|
66
64
|
return false
|
67
65
|
end
|
68
|
-
klass_str = RDL::Util.to_class_str(klass).hash
|
69
|
-
if mstr.start_with?('__rdl') and mstr.end_with?('_old_#{klass_str}')
|
70
|
-
mstr0 = RDL::Wrap.unwrapped_name(klass, mstr)
|
71
|
-
owner0 = sk.instance_method(mstr0).owner
|
72
|
-
owner = sk.instance_method(mstr).owner
|
73
|
-
return false if owner0 != owner
|
74
|
-
end
|
75
66
|
|
76
|
-
|
67
|
+
return sk.methods.include?(:new) if method == :new
|
68
|
+
|
69
|
+
sk.public_instance_methods(false).include?(msym) or
|
70
|
+
sk.protected_instance_methods(false).include?(msym) or
|
71
|
+
sk.private_instance_methods(false).include?(msym)
|
77
72
|
end
|
78
73
|
|
79
74
|
# Returns the @__rdl_type field of [+obj+]
|
@@ -93,4 +88,12 @@ class RDL::Util
|
|
93
88
|
klass + "#" + meth.to_s
|
94
89
|
end
|
95
90
|
end
|
91
|
+
|
92
|
+
def self.silent_warnings
|
93
|
+
old_stderr = $stderr
|
94
|
+
$stderr = StringIO.new
|
95
|
+
yield
|
96
|
+
ensure
|
97
|
+
$stderr = old_stderr
|
98
|
+
end
|
96
99
|
end
|
data/lib/rdl/wrap.rb
CHANGED
@@ -68,7 +68,13 @@ class RDL::Wrap
|
|
68
68
|
posts = RDL::Globals.info.get(klass, meth, :post)
|
69
69
|
RDL::Contract::AndContract.check_array(posts, self, ret, *args, &blk) if posts
|
70
70
|
if matches
|
71
|
-
|
71
|
+
if meth.to_sym == :initialize
|
72
|
+
types.each { |t|
|
73
|
+
raise ArgumentError, "Initialize method must be annotated with return type `self` or a generic type where base is `self`. #{full_method_name} has incorrect type." unless ((t.ret.is_a?(RDL::Type::VarType) && t.ret.name == :self) || (t.ret.is_a?(RDL::Type::GenericType) && t.ret.base.is_a?(RDL::Type::VarType) && t.ret.base.name == :self))
|
74
|
+
}
|
75
|
+
else
|
76
|
+
ret = RDL::Type::MethodType.check_ret_types(self, "#{full_method_name}", types, inst, matches, ret, bind, *args, &blk) unless (meth.to_sym == :initialize)
|
77
|
+
end
|
72
78
|
end
|
73
79
|
if RDL::Config.instance.guess_types.include?("#{klass_str_without_singleton}".to_sym)
|
74
80
|
RDL::Globals.info.add(klass, meth, :otype, { args: (args.map { |arg| arg.class }), ret: ret.class, block: block_given? })
|
@@ -151,13 +157,13 @@ RUBY
|
|
151
157
|
"__rdl_#{meth.to_s}_old_#{klass_str}".to_sym
|
152
158
|
end
|
153
159
|
|
154
|
-
def self.unwrapped_name(
|
155
|
-
if not
|
156
|
-
raise Exception, "cannot get unwrapped name for #{
|
160
|
+
def self.unwrapped_name(klass, meth_str)
|
161
|
+
if not meth_str.start_with?('__rdl_') and meth_str.include?('_old_')
|
162
|
+
raise Exception, "cannot get unwrapped name for #{meth_str}"
|
157
163
|
end
|
158
164
|
klass_str = RDL::Util.to_class_str(klass).hash.to_s
|
159
|
-
|
160
|
-
|
165
|
+
meth_str = meth_str.split("_#{klass_str}")[0]
|
166
|
+
meth_str[6..-5]
|
161
167
|
end
|
162
168
|
|
163
169
|
def self.class_to_string(klass)
|
@@ -213,6 +219,7 @@ RUBY
|
|
213
219
|
raise RuntimeError, "Deferred #{kind} contract from class #{prev_klass} being applied in class #{tmp_klass} to #{meth}"
|
214
220
|
end
|
215
221
|
RDL::Globals.info.add(klass, meth, kind, contract)
|
222
|
+
RDL::Globals.info.add(klass, meth, :effect, h[:effect]) if h.has_key?(:effect)
|
216
223
|
RDL::Wrap.wrap(klass, meth) if h[:wrap]
|
217
224
|
unless !h.has_key?(:typecheck) || RDL::Globals.info.set(klass, meth, :typecheck, h[:typecheck])
|
218
225
|
raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}"
|
@@ -317,7 +324,7 @@ module RDL::Annotate
|
|
317
324
|
# type(klass, meth, type)
|
318
325
|
# type(meth, type)
|
319
326
|
# type(type)
|
320
|
-
def type(*args, wrap: RDL::Config.instance.type_defaults[:wrap], typecheck: RDL::Config.instance.type_defaults[:typecheck], version: nil)
|
327
|
+
def type(*args, wrap: RDL::Config.instance.type_defaults[:wrap], typecheck: RDL::Config.instance.type_defaults[:typecheck], version: nil, effect: nil)
|
321
328
|
return if version && !(Gem::Requirement.new(version).satisfied_by? Gem.ruby_version)
|
322
329
|
klass, meth, type = begin
|
323
330
|
RDL::Wrap.process_type_args(self, *args)
|
@@ -331,12 +338,20 @@ module RDL::Annotate
|
|
331
338
|
err.set_backtrace bt
|
332
339
|
raise err
|
333
340
|
end
|
341
|
+
effect[0] = :- if effect && effect[0] == :~ ## For now, treating pure-ish :~ as :-, since we realized it doesn't actually affect termination checking.
|
342
|
+
typs = type.args + [type.ret]
|
343
|
+
if type.block
|
344
|
+
block_type = type.block.is_a?(RDL::Type::OptionalType) ? type.block.type : type.block
|
345
|
+
typs = typs + block_type.args + [block_type.ret]
|
346
|
+
end
|
347
|
+
RDL::Globals.dep_types << [klass, meth, type] if typs.any? { |t| t.is_a?(RDL::Type::ComputedType) || (t.is_a?(RDL::Type::BoundArgType) && t.type.is_a?(RDL::Type::ComputedType)) }
|
334
348
|
if meth
|
335
349
|
# It turns out Ruby core/stdlib don't always follow this convention...
|
336
350
|
# if (meth.to_s[-1] == "?") && (type.ret != RDL::Globals.types[:bool])
|
337
351
|
# warn "#{RDL::Util.pp_klass_method(klass, meth)}: methods that end in ? should have return type %bool"
|
338
352
|
# end
|
339
353
|
RDL::Globals.info.add(klass, meth, :type, type)
|
354
|
+
RDL::Globals.info.add(klass, meth, :effect, effect)
|
340
355
|
unless RDL::Globals.info.set(klass, meth, :typecheck, typecheck)
|
341
356
|
raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}"
|
342
357
|
end
|
@@ -351,29 +366,30 @@ module RDL::Annotate
|
|
351
366
|
end
|
352
367
|
RDL::Wrap.wrap(klass, meth) if wrap
|
353
368
|
else
|
354
|
-
if wrap
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
RDL::Globals.to_typecheck[typecheck].add([klass, meth])
|
359
|
-
end
|
369
|
+
RDL::Globals.to_wrap << [klass, meth] if wrap
|
370
|
+
if (typecheck && typecheck != :call)
|
371
|
+
RDL::Globals.to_typecheck[typecheck] = Set.new unless RDL::Globals.to_typecheck[typecheck]
|
372
|
+
RDL::Globals.to_typecheck[typecheck].add([klass, meth])
|
360
373
|
end
|
361
374
|
end
|
362
375
|
end
|
363
376
|
else
|
364
377
|
RDL::Globals.deferred << [klass, :type, type, {wrap: wrap,
|
365
|
-
typecheck: typecheck}]
|
378
|
+
typecheck: typecheck, effect: effect}]
|
366
379
|
end
|
367
380
|
nil
|
368
381
|
end
|
369
382
|
|
383
|
+
def readd_comp_types
|
384
|
+
RDL::Globals.dep_types.each { |klass, meth, t| RDL::Globals.info.add(klass, meth, :type, t) unless meth.nil? }
|
385
|
+
end
|
386
|
+
|
370
387
|
# [+ klass +] is the class containing the variable; self if omitted; ignored for local and global variables
|
371
388
|
# [+ var +] is a symbol or string containing the name of the variable
|
372
389
|
# [+ typ +] is a string containing the type
|
373
390
|
def var_type(klass=self, var, typ)
|
374
391
|
raise RuntimeError, "Variable cannot begin with capital" if var.to_s =~ /^[A-Z]/
|
375
392
|
return if var.to_s =~ /^[a-z]/ # local variables handled specially, inside type checker
|
376
|
-
raise RuntimeError, "Global variables can't be typed in a class" unless klass = self
|
377
393
|
klass = RDL::Util::GLOBAL_NAME if var.to_s =~ /^\$/
|
378
394
|
unless RDL::Globals.info.set(klass, var, :type, RDL::Globals.parser.scan_str("#T #{typ}"))
|
379
395
|
raise RuntimeError, "Type already declared for #{var}"
|
@@ -385,6 +401,7 @@ module RDL::Annotate
|
|
385
401
|
# [+ args +] is a sequence of symbol, typ. attr_reader is called for each symbol,
|
386
402
|
# and var_type is called to assign the immediately following type to the
|
387
403
|
# attribute named after that symbol.
|
404
|
+
# Note these three methods are duplicated in RDLAnnotate
|
388
405
|
def attr_accessor_type(*args)
|
389
406
|
args.each_slice(2) { |name, typ|
|
390
407
|
attr_accessor name
|
@@ -496,12 +513,40 @@ module RDL::RDLAnnotate
|
|
496
513
|
define_method :rdl_post, RDL::Annotate.instance_method(:post)
|
497
514
|
define_method :rdl_type, RDL::Annotate.instance_method(:type)
|
498
515
|
define_method :rdl_var_type, RDL::Annotate.instance_method(:var_type)
|
499
|
-
define_method :rdl_attr_accessor_type, RDL::Annotate.instance_method(:attr_accessor_type)
|
500
|
-
define_method :rdl_attr_reader_type, RDL::Annotate.instance_method(:attr_reader_type)
|
501
|
-
define_method :rdl_attr_type, RDL::Annotate.instance_method(:attr_type)
|
502
|
-
define_method :rdl_attr_writer_type, RDL::Annotate.instance_method(:attr_writer_type)
|
503
516
|
define_method :rdl_alias, RDL::Annotate.instance_method(:rdl_alias)
|
504
517
|
define_method :rdl_type_params, RDL::Annotate.instance_method(:type_params)
|
518
|
+
|
519
|
+
# Need to duplicate these methods because they need to call rdl_var_type and rdl_type
|
520
|
+
# and couldn't figure out how to do instance_method with a partial argument binding
|
521
|
+
def rdl_attr_accessor_type(*args)
|
522
|
+
args.each_slice(2) { |name, typ|
|
523
|
+
attr_accessor name
|
524
|
+
rdl_var_type ("@" + name.to_s), typ
|
525
|
+
rdl_type name, "() -> #{typ}"
|
526
|
+
rdl_type name.to_s + "=", "(#{typ}) -> #{typ}"
|
527
|
+
}
|
528
|
+
nil
|
529
|
+
end
|
530
|
+
|
531
|
+
def rdl_attr_reader_type(*args)
|
532
|
+
args.each_slice(2) { |name, typ|
|
533
|
+
attr_reader name
|
534
|
+
rdl_var_type ("@" + name.to_s), typ
|
535
|
+
rdl_type name, "() -> #{typ}"
|
536
|
+
}
|
537
|
+
nil
|
538
|
+
end
|
539
|
+
|
540
|
+
alias_method :rdl_attr_type, :rdl_attr_reader_type
|
541
|
+
|
542
|
+
def rdl_attr_writer_type(*args)
|
543
|
+
args.each_slice(2) { |name, typ|
|
544
|
+
attr_writer name
|
545
|
+
rdl_var_type ("@" + name.to_s), typ
|
546
|
+
rdl_type name.to_s + "=", "(#{typ}) -> #{typ}"
|
547
|
+
}
|
548
|
+
nil
|
549
|
+
end
|
505
550
|
end
|
506
551
|
|
507
552
|
module RDL
|
@@ -553,6 +598,146 @@ module RDL
|
|
553
598
|
nil
|
554
599
|
end
|
555
600
|
|
601
|
+
def self.load_sequel_schema(db)
|
602
|
+
db.tables.each { |table|
|
603
|
+
hash_str = "{ "
|
604
|
+
kl_name = table.to_s.camelize.singularize
|
605
|
+
db.schema(table).each { |col|
|
606
|
+
hash_str << "#{col[0]}: "
|
607
|
+
typ = col[1][:type].to_s.camelize
|
608
|
+
if typ == "Datetime"
|
609
|
+
typ = "DateTime or Time" ## Sequel accepts both
|
610
|
+
elsif typ == "Boolean"
|
611
|
+
typ = "%bool"
|
612
|
+
elsif typ == "Text"
|
613
|
+
typ = "String"
|
614
|
+
end
|
615
|
+
hash_str << "#{typ},"
|
616
|
+
RDL.type kl_name, col[0], "() -> #{typ}", wrap: false
|
617
|
+
RDL.type kl_name, "#{col[0]}=", "(#{typ}) -> #{typ}", wrap: false
|
618
|
+
}
|
619
|
+
hash_str.chomp!(",") << " }"
|
620
|
+
RDL::Globals.seq_db_schema[table] = RDL::Globals.parser.scan_str "#T #{hash_str}"
|
621
|
+
}
|
622
|
+
end
|
623
|
+
|
624
|
+
def self.load_rails_schema
|
625
|
+
return unless defined?(Rails)
|
626
|
+
::Rails.application.eager_load! # load Rails app
|
627
|
+
models = ActiveRecord::Base.descendants.each { |m|
|
628
|
+
begin
|
629
|
+
## load schema for each Rails model
|
630
|
+
m.send(:load_schema) unless m.abstract_class?
|
631
|
+
rescue
|
632
|
+
end }
|
633
|
+
|
634
|
+
models.each { |model|
|
635
|
+
next if model.to_s == "ApplicationRecord"
|
636
|
+
next if model.to_s == "GroupManager"
|
637
|
+
RDL.nowrap model
|
638
|
+
s1 = {}
|
639
|
+
model.columns_hash.each { |k, v| t_name = v.type.to_s.camelize
|
640
|
+
## Map SQL column types to the corresponding RDL type
|
641
|
+
if t_name == "Boolean"
|
642
|
+
t_name = "%bool"
|
643
|
+
s1[k] = RDL::Globals.types[:bool]
|
644
|
+
elsif t_name == "Datetime"
|
645
|
+
t_name = "DateTime or Time"
|
646
|
+
s1[k] = RDL::Type::UnionType.new(RDL::Type::NominalType.new(Time), RDL::Type::NominalType.new(DateTime))
|
647
|
+
elsif t_name == "Text"
|
648
|
+
## difference between `text` and `string` is in the SQL types they're mapped to, not in Ruby types
|
649
|
+
t_name = "String"
|
650
|
+
s1[k] = RDL::Globals.types[:string]
|
651
|
+
else
|
652
|
+
s1[k] = RDL::Type::NominalType.new(t_name)
|
653
|
+
end
|
654
|
+
RDL.type model, (k+"=").to_sym, "(#{t_name}) -> #{t_name}", wrap: false ## create method type for column setter
|
655
|
+
RDL.type model, (k).to_sym, "() -> #{t_name}", wrap: false ## create method type for column getter
|
656
|
+
}
|
657
|
+
s2 = s1.transform_keys { |k| k.to_sym }
|
658
|
+
assoc = {}
|
659
|
+
model.reflect_on_all_associations.each { |a|
|
660
|
+
## Generate method types based on associations
|
661
|
+
add_ar_assoc(assoc, a.macro, a.name)
|
662
|
+
if a.name.to_s.pluralize == a.name.to_s ## plural association
|
663
|
+
## This actually returns an Associations CollectionProxy, which is a descendant of ActiveRecord_Relation (see below actual type). This makes no difference in practice.
|
664
|
+
RDL.type model, a.name, "() -> ActiveRecord_Relation<#{a.name.to_s.camelize.singularize}>", wrap: false
|
665
|
+
#ActiveRecord_Associations_CollectionProxy<#{a.name.to_s.camelize.singularize}>'
|
666
|
+
else
|
667
|
+
## association is singular, we just return an instance of associated class
|
668
|
+
RDL.type model, a.name, "() -> #{a.name.to_s.camelize.singularize}", wrap: false
|
669
|
+
end
|
670
|
+
}
|
671
|
+
s2[:__associations] = RDL::Type::FiniteHashType.new(assoc, nil)
|
672
|
+
base_name = model.to_s
|
673
|
+
base_type = RDL::Type::NominalType.new(model.to_s)
|
674
|
+
hash_type = RDL::Type::FiniteHashType.new(s2, nil)
|
675
|
+
schema = RDL::Type::GenericType.new(base_type, hash_type)
|
676
|
+
RDL::Globals.ar_db_schema[base_name.to_sym] = schema
|
677
|
+
}
|
678
|
+
end
|
679
|
+
|
680
|
+
def self.check_type_code
|
681
|
+
RDL.config { |config| config.use_comp_types = false }
|
682
|
+
count = 1
|
683
|
+
#code_type = RDL::Globals.parser.scan_str "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type"
|
684
|
+
RDL::Globals.dep_types.each { |klass, meth, typ|
|
685
|
+
klass = RDL::Util.has_singleton_marker(klass) ? RDL::Util.remove_singleton_marker(klass) : klass
|
686
|
+
arg_list = "(trec, targs"
|
687
|
+
type_list = "(RDL::Type::Type, Array<RDL::Type::Type>"
|
688
|
+
(typ.args+[typ.ret]+[typ.block]).each { |t|
|
689
|
+
## First collect all bindings to be used during type checking.
|
690
|
+
if (t.is_a?(RDL::Type::BoundArgType))
|
691
|
+
arg_list << ", #{t.name}"
|
692
|
+
type_list << ", #{t.type.class}"
|
693
|
+
end
|
694
|
+
}
|
695
|
+
arg_list << ")"
|
696
|
+
type_list << ")"
|
697
|
+
code_type = RDL::Globals.parser.scan_str "#{type_list} -> RDL::Type::Type"
|
698
|
+
(typ.args+[typ.ret]+[typ.block]).each { |t|
|
699
|
+
if t.is_a?(RDL::Type::ComputedType)
|
700
|
+
meth = cleanse_meth_name(meth)
|
701
|
+
if klass.to_s.include?("::") ## hacky way around namespace issue
|
702
|
+
tmp_meth = "def klass.tc_#{meth}#{count}#{arg_list} #{t.code}; end"
|
703
|
+
tmp_eval = "klass = #{klass} ; #{tmp_meth}"
|
704
|
+
else
|
705
|
+
tmp_meth = tmp_eval = "def #{klass}.tc_#{meth}#{count}#{arg_list} #{t.code}; end"
|
706
|
+
end
|
707
|
+
eval tmp_eval
|
708
|
+
ast = Parser::CurrentRuby.parse tmp_meth
|
709
|
+
RDL::Typecheck.typecheck("[s]#{klass}", "tc_#{meth}#{count}".to_sym, ast, [code_type], [[:-, :+]])
|
710
|
+
count += 1
|
711
|
+
end
|
712
|
+
}
|
713
|
+
}
|
714
|
+
RDL.do_typecheck :type_code
|
715
|
+
RDL.config { |config| config.use_comp_types = true }
|
716
|
+
true
|
717
|
+
end
|
718
|
+
|
719
|
+
def self.cleanse_meth_name(meth)
|
720
|
+
meth = meth.to_s
|
721
|
+
meth.gsub!("%", "percent")
|
722
|
+
meth.gsub!("&", "ampersand")
|
723
|
+
meth.gsub!("*", "asterisk")
|
724
|
+
meth.gsub!("+", "plus")
|
725
|
+
meth.gsub!("-", "dash")
|
726
|
+
meth.gsub!("@", "at")
|
727
|
+
meth.gsub!("/", "slash")
|
728
|
+
meth.gsub!("<", "lt")
|
729
|
+
meth.gsub!(">", "gt")
|
730
|
+
meth.gsub!("=", "eq")
|
731
|
+
meth.gsub!("[", "lbracket")
|
732
|
+
meth.gsub!("]", "rbracket")
|
733
|
+
meth.gsub!("^", "carrot")
|
734
|
+
meth.gsub!("|", "line")
|
735
|
+
meth.gsub!("~", "line")
|
736
|
+
meth.gsub!("?", "qmark")
|
737
|
+
meth.gsub!("!", "bang")
|
738
|
+
meth
|
739
|
+
end
|
740
|
+
|
556
741
|
# Does nothing at run time
|
557
742
|
def self.note_type(x)
|
558
743
|
return x
|
@@ -567,7 +752,7 @@ module RDL
|
|
567
752
|
# Returns a new object that wraps self in a type cast. If force is true this cast is *unchecked*, so use with caution
|
568
753
|
def self.type_cast(obj, typ, force: false)
|
569
754
|
new_typ = if typ.is_a? RDL::Type::Type then typ else RDL::Globals.parser.scan_str "#T #{typ}" end
|
570
|
-
raise RuntimeError, "type cast error: self not a member of #{new_typ}" unless force ||
|
755
|
+
raise RuntimeError, "type cast error: self not a member of #{new_typ}" unless force || new_typ.member?(obj)
|
571
756
|
new_obj = SimpleDelegator.new(obj)
|
572
757
|
new_obj.instance_variable_set('@__rdl_type', new_typ)
|
573
758
|
new_obj
|
@@ -582,7 +767,7 @@ module RDL
|
|
582
767
|
formals, _, all = RDL::Globals.type_params[klass]
|
583
768
|
raise RuntimeError, "Receiver is of class #{klass}, which is not parameterized" unless formals
|
584
769
|
raise RuntimeError, "Expecting #{formals.size} type parameters, got #{typs.size}" unless formals.size == typs.size
|
585
|
-
raise RuntimeError, "Instance already has type instantiation" if obj.instance_variable_get(:@__rdl_type)
|
770
|
+
raise RuntimeError, "Instance already has type instantiation" if obj.instance_variable_defined?(:@__rdl_type) && obj.instance_variable_get(:@__rdl_type)
|
586
771
|
new_typs = typs.map { |t| if t.is_a? RDL::Type::Type then t else RDL::Globals.parser.scan_str "#T #{t}" end }
|
587
772
|
t = RDL::Type::GenericType.new(RDL::Type::NominalType.new(klass), *new_typs)
|
588
773
|
if check
|
@@ -614,9 +799,34 @@ module RDL
|
|
614
799
|
obj.instance_variable_set(:@__rdl_type, nil)
|
615
800
|
obj
|
616
801
|
end
|
802
|
+
|
803
|
+
private
|
804
|
+
def self.add_ar_assoc(hash, aname, aklass)
|
805
|
+
kl_type = RDL::Type::SingletonType.new(aklass)
|
806
|
+
if hash[aname]
|
807
|
+
hash[aname] = RDL::Type::UnionType.new(hash[aname], kl_type)
|
808
|
+
else
|
809
|
+
hash[aname] = kl_type unless hash[aname]
|
810
|
+
end
|
811
|
+
hash
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
class Object
|
816
|
+
def singleton_method_added(meth)
|
817
|
+
klass = self.to_s
|
818
|
+
klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
|
819
|
+
sklass = RDL::Util.add_singleton_marker(klass)
|
820
|
+
RDL::Wrap.do_method_added(self, true, sklass, meth)
|
821
|
+
nil
|
822
|
+
end
|
617
823
|
end
|
618
824
|
|
619
825
|
class Module
|
826
|
+
define_method :singleton_method_added, Object.instance_method(:singleton_method_added)
|
827
|
+
|
828
|
+
RDL::Util.silent_warnings {
|
829
|
+
|
620
830
|
def method_added(meth)
|
621
831
|
klass = self.to_s
|
622
832
|
klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
|
@@ -624,11 +834,45 @@ class Module
|
|
624
834
|
nil
|
625
835
|
end
|
626
836
|
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
837
|
+
}
|
838
|
+
end
|
839
|
+
|
840
|
+
class Class
|
841
|
+
def ===(x)
|
842
|
+
if x.method(:is_a?).owner == SimpleDelegator then super(x.__getobj__) else super(x) end
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
class SimpleDelegator
|
847
|
+
## pass methods through to wrapped object
|
848
|
+
## necessary when type casts are inside type-level code
|
849
|
+
def is_a?(c)
|
850
|
+
__getobj__.is_a?(c)
|
851
|
+
end
|
852
|
+
|
853
|
+
def instance_of?(c)
|
854
|
+
__getobj__.instance_of?(c)
|
855
|
+
end
|
856
|
+
|
857
|
+
def kind_of?(c)
|
858
|
+
__getobj__.kind_of?(c)
|
859
|
+
end
|
860
|
+
|
861
|
+
def ===(x)
|
862
|
+
__getobj__ === x
|
633
863
|
end
|
864
|
+
|
865
|
+
def ==(x)
|
866
|
+
__getobj__ == x
|
867
|
+
end
|
868
|
+
|
869
|
+
def class
|
870
|
+
__getobj__.class
|
871
|
+
end
|
872
|
+
|
873
|
+
def nil?
|
874
|
+
__getobj__.nil?
|
875
|
+
end
|
876
|
+
|
634
877
|
end
|
878
|
+
|