rdl 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +7 -6
- data/CHANGES.md +29 -0
- data/README.md +94 -26
- data/lib/rdl/boot.rb +82 -41
- data/lib/rdl/boot_rails.rb +5 -0
- data/lib/rdl/config.rb +9 -1
- data/lib/rdl/query.rb +2 -2
- data/lib/rdl/typecheck.rb +972 -225
- data/lib/rdl/types/annotated_arg.rb +8 -0
- data/lib/rdl/types/ast_node.rb +73 -0
- data/lib/rdl/types/bot.rb +8 -0
- data/lib/rdl/types/bound_arg.rb +63 -0
- data/lib/rdl/types/computed.rb +48 -0
- data/lib/rdl/types/dependent_arg.rb +9 -0
- data/lib/rdl/types/dynamic.rb +61 -0
- data/lib/rdl/types/finite_hash.rb +54 -9
- data/lib/rdl/types/generic.rb +33 -0
- data/lib/rdl/types/intersection.rb +8 -0
- data/lib/rdl/types/lexer.rex +6 -1
- data/lib/rdl/types/lexer.rex.rb +13 -1
- data/lib/rdl/types/method.rb +14 -0
- data/lib/rdl/types/nominal.rb +8 -0
- data/lib/rdl/types/non_null.rb +8 -0
- data/lib/rdl/types/optional.rb +8 -0
- data/lib/rdl/types/parser.racc +31 -5
- data/lib/rdl/types/parser.tab.rb +540 -302
- data/lib/rdl/types/rdl_types.rb +45 -0
- data/lib/rdl/types/singleton.rb +14 -1
- data/lib/rdl/types/string.rb +104 -0
- data/lib/rdl/types/structural.rb +8 -0
- data/lib/rdl/types/top.rb +8 -0
- data/lib/rdl/types/tuple.rb +32 -8
- data/lib/rdl/types/type.rb +54 -11
- data/lib/rdl/types/union.rb +41 -2
- data/lib/rdl/types/var.rb +10 -0
- data/lib/rdl/types/vararg.rb +8 -0
- data/lib/rdl/util.rb +13 -10
- data/lib/rdl/wrap.rb +271 -27
- data/lib/rdl_disable.rb +16 -2
- data/lib/types/active_record.rb +1 -0
- data/lib/types/core/array.rb +442 -23
- data/lib/types/core/basic_object.rb +3 -3
- data/lib/types/core/bigdecimal.rb +5 -0
- data/lib/types/core/class.rb +2 -0
- data/lib/types/core/dir.rb +3 -3
- data/lib/types/core/enumerable.rb +4 -4
- data/lib/types/core/enumerator.rb +1 -1
- data/lib/types/core/file.rb +4 -4
- data/lib/types/core/float.rb +203 -0
- data/lib/types/core/hash.rb +390 -15
- data/lib/types/core/integer.rb +223 -10
- data/lib/types/core/io.rb +2 -2
- data/lib/types/core/kernel.rb +8 -5
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/module.rb +3 -3
- data/lib/types/core/numeric.rb +0 -2
- data/lib/types/core/object.rb +5 -5
- data/lib/types/core/pathname.rb +2 -2
- data/lib/types/core/process.rb +1 -3
- data/lib/types/core/range.rb +1 -1
- data/lib/types/core/regexp.rb +2 -2
- data/lib/types/core/set.rb +1 -1
- data/lib/types/core/string.rb +408 -16
- data/lib/types/core/symbol.rb +3 -3
- data/lib/types/core/time.rb +1 -1
- data/lib/types/core/uri.rb +13 -13
- data/lib/types/rails/_helpers.rb +7 -1
- data/lib/types/rails/action_controller/mime_responds.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +42 -30
- data/lib/types/rails/active_record/comp_types.rb +637 -0
- data/lib/types/rails/active_record/finder_methods.rb +1 -1
- data/lib/types/rails/active_record/model_schema.rb +28 -16
- data/lib/types/rails/active_record/relation.rb +5 -3
- data/lib/types/rails/active_record/sql-strings.rb +166 -0
- data/lib/types/rails/string.rb +1 -1
- data/lib/types/sequel.rb +1 -0
- data/lib/types/sequel/comp_types.rb +581 -0
- data/rdl.gemspec +5 -4
- data/test/test_alias.rb +4 -0
- data/test/test_array_types.rb +244 -0
- data/test/test_bound_types.rb +80 -0
- data/test/test_contract.rb +4 -0
- data/test/test_dsl.rb +5 -0
- data/test/test_dyn_comptype_checks.rb +206 -0
- data/test/test_generic.rb +21 -20
- data/test/test_hash_types.rb +322 -0
- data/test/test_intersection.rb +1 -0
- data/test/test_le.rb +29 -4
- data/test/test_member.rb +3 -1
- data/test/test_parser.rb +5 -0
- data/test/test_query.rb +1 -0
- data/test/test_rdl.rb +63 -28
- data/test/test_rdl_type.rb +4 -0
- data/test/test_string_types.rb +102 -0
- data/test/test_type_contract.rb +59 -37
- data/test/test_typecheck.rb +480 -75
- data/test/test_types.rb +17 -0
- data/test/test_wrap.rb +5 -0
- metadata +35 -5
- data/lib/types/rails/active_record/schema_types.rb +0 -51
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
|
+
|