Class: Object

Inherits:
BasicObject
Defined in:
lib/rdl/wrap.rb,
lib/rdl/query.rb,
lib/rdl_disable.rb,
lib/types/core-ruby-2.x/object.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.method_added(meth) ⇒ Object



397
398
399
400
401
402
# File 'lib/rdl/wrap.rb', line 397

def self.method_added(meth)
  klass = self.to_s
  klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
  RDL::Wrap.do_method_added(self, false, klass, meth)
  nil
end

.singleton_method_added(meth) ⇒ Object



404
405
406
407
408
409
410
# File 'lib/rdl/wrap.rb', line 404

def self.singleton_method_added(meth)
  klass = self.to_s
  klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
  sklass = RDL::Util.add_singleton_marker(klass)
  RDL::Wrap.do_method_added(self, true, sklass, meth)
  nil
end

Instance Method Details

#attr_accessor_type(*args) ⇒ Object

In the following three methods

+ args +

is a sequence of symbol, typ. attr_reader is called for each symbol,

and var_type is called to assign the immediately following type to the attribute named after that symbol.



367
368
369
370
371
372
373
374
375
# File 'lib/rdl/wrap.rb', line 367

def attr_accessor_type(*args)
  args.each_slice(2) { |name, typ|
    attr_accessor name
    var_type ("@" + name.to_s), typ
    type name, "() -> #{typ}"
    type name.to_s + "=", "(#{typ}) -> #{typ}"
  }
  nil
end

#attr_reader_type(*args) ⇒ Object Also known as: attr_type



377
378
379
380
381
382
383
384
# File 'lib/rdl/wrap.rb', line 377

def attr_reader_type(*args)
  args.each_slice(2) { |name, typ|
    attr_reader name
    var_type ("@" + name.to_s), typ
    type name, "() -> #{typ}"
  }
  nil
end

#attr_writer_type(*args) ⇒ Object



388
389
390
391
392
393
394
395
# File 'lib/rdl/wrap.rb', line 388

def attr_writer_type(*args)
  args.each_slice(2) { |name, typ|
    attr_writer name
    var_type ("@" + name.to_s), typ
    type name.to_s + "=", "(#{typ}) -> #{typ}"
  }
  nil
end

#deinstantaite!(*args) ⇒ Object



16
# File 'lib/rdl_disable.rb', line 16

def deinstantaite!(*args); self; end

#deinstantiate!Object

Raises:



504
505
506
507
508
509
# File 'lib/rdl/wrap.rb', line 504

def deinstantiate!
  raise RuntimeError, "Class #{self.to_s} is not parameterized" unless $__rdl_type_params[klass]
  raise RuntimeError, "Instance is not instantiated" unless @__rdl_type && @@__rdl_type.instance_of?(RDL::Type::GenericType)
  @__rdl_type = nil
  self
end

#instantaite!(*args) ⇒ Object



15
# File 'lib/rdl_disable.rb', line 15

def instantaite!(*args); self; end

#instantiate!(*typs) ⇒ Object

typs

is an array of types, classes, symbols, or strings to instantiate

the type parameters. If a class, symbol, or string is given, it is converted to a NominalType.

Raises:



476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/rdl/wrap.rb', line 476

def instantiate!(*typs)
  klass = self.class.to_s
  klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
  formals, _, all = $__rdl_type_params[klass]
  raise RuntimeError, "Receiver is of class #{klass}, which is not parameterized" unless formals
  raise RuntimeError, "Expecting #{params.size} type parameters, got #{typs.size}" unless formals.size == typs.size
  raise RuntimeError, "Instance already has type instantiation" if (defined? @__rdl_type) && @rdl_type
  new_typs = typs.map { |t| if t.is_a? RDL::Type::Type then t else $__rdl_parser.scan_str "#T #{t}" end }
  t = RDL::Type::GenericType.new(RDL::Type::NominalType.new(klass), *new_typs)
  if all.instance_of? Symbol
    self.send(all) { |*objs|
      new_typs.zip(objs).each { |nt, obj|
        if nt.instance_of? RDL::Type::GenericType # require obj to be instantiated
          t_obj = RDL::Util.rdl_type(obj)
          raise RDL::Type::TypeError, "Expecting element of type #{nt.to_s}, but got uninstantiated object #{obj.inspect}" unless t_obj
          raise RDL::Type::TypeError, "Expecting type #{nt.to_s}, got type #{t_obj.to_s}" unless t_obj <= nt
        else
          raise RDL::Type::TypeError, "Expecting type #{nt.to_s}, got #{obj.inspect}" unless nt.member? obj
        end
      }
    }
  else
    raise RDL::Type::TypeError, "Not an instance of #{t}" unless instance_exec(*new_typs, &all)
  end
  @__rdl_type = t
  self
end

#post(*args) ⇒ Object

Add a postcondition to a method. Same possible invocations as pre.



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/rdl/wrap.rb', line 275

def post(*args, wrap: RDL::Config.instance.post_defaults[:wrap], &blk)
  klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Postcondition", *args, &blk)
  if meth
    $__rdl_info.add(klass, meth, :post, contract)
    if wrap
      if RDL::Util.method_defined?(klass, meth) || meth == :initialize
        RDL::Wrap.wrap(klass, meth)
      else
        $__rdl_to_wrap << [klass, meth]
      end
    end
  else
    $__rdl_deferred << [klass, :post, contract, {wrap: wrap}]
  end
  nil
end

#pre(*args) ⇒ Object

klass

may be Class, Symbol, or String

method

may be Symbol or String

contract

must be a Contract

wrap

indicates whether the contract should be enforced (true) or just recorded (false)

Add a precondition to a method. Possible invocations: pre(klass, meth, contract) pre(klass, meth) { block } = pre(klass, meth, FlatContract.new { block }) pre(meth, contract) = pre(self, meth, contract) pre(meth) { block } = pre(self, meth, FlatContract.new { block }) pre(contract) = pre(self, next method, contract) pre { block } = pre(self, next method, FlatContract.new { block })



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/rdl/wrap.rb', line 257

def pre(*args, wrap: RDL::Config.instance.pre_defaults[:wrap], &blk)
  klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Precondition", *args, &blk)
  if meth
    $__rdl_info.add(klass, meth, :pre, contract)
    if wrap
      if RDL::Util.method_defined?(klass, meth) || meth == :initialize # there is always an initialize
        RDL::Wrap.wrap(klass, meth)
      else
        $__rdl_to_wrap << [klass, meth]
      end
    end
  else
    $__rdl_deferred << [klass, :pre, contract, {wrap: wrap}]
  end
  nil
end

#rdl_alias(*args) ⇒ Object

Aliases contracts for meth_old and meth_new. Currently, this must be called for any aliases or they will not be wrapped with contracts. Only creates aliases in the current class.



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/rdl/wrap.rb', line 415

def rdl_alias(new_name, old_name)
  klass = self.to_s
  klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
  $__rdl_aliases[klass] = {} unless $__rdl_aliases[klass]
  if $__rdl_aliases[klass][new_name]
    raise RuntimeError,
          "Tried to alias #{new_name}, already aliased to #{$__rdl_aliases[klass][new_name]}"
  end
  $__rdl_aliases[klass][new_name] = old_name

  if self.method_defined? new_name
    RDL::Wrap.wrap(klass, new_name)
  else
    $__rdl_to_wrap << [klass, old_name]
  end
  nil
end

#rdl_at(sym, &blk) ⇒ Object

Register [+ blk ] to be executed when ‘rdl_do_typecheck [ sym +]` is called. The blk will be called with sym as its argument. The order in which multiple blks for the same sym will be executed is unspecified



541
542
543
544
# File 'lib/rdl/wrap.rb', line 541

def rdl_at(sym, &blk)
  $__rdl_at[sym] = [] unless $__rdl_at[sym]
  $__rdl_at[sym] << blk
end

#rdl_do_typecheck(*args) ⇒ Object

Invokes all callbacks from rdl_at(sym), in unspecified order. Afterwards, type checks all methods that had annotation ‘typecheck: sym’ at type call, in unspecified order.



548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/rdl/wrap.rb', line 548

def rdl_do_typecheck(sym)
  if $__rdl_at[sym]
    $__rdl_at[sym].each { |blk| blk.call(sym) }
  end
  $__rdl_at[sym] = Array.new
  return unless $__rdl_to_typecheck[sym]
  $__rdl_to_typecheck[sym].each { |klass, meth|
    RDL::Typecheck.typecheck(klass, meth)
  }
  $__rdl_to_typecheck[sym] = Set.new
  nil
end

#rdl_note_type(*args) ⇒ Object

Does nothing at run time



562
563
564
# File 'lib/rdl/wrap.rb', line 562

def rdl_note_type(x)
  return x
end

#rdl_nowrap(*args) ⇒ Object



468
469
470
471
# File 'lib/rdl/wrap.rb', line 468

def rdl_nowrap
  RDL.config { |config| config.add_nowrap(self, self.singleton_class) }
  nil
end

#rdl_query(q) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/rdl/query.rb', line 72

def rdl_query(q)
  $__rdl_contract_switch.off {
    if q =~ /^[A-Z]\w*(#|\.)([a-z_]\w*(!|\?|=)?|!|~|\+|\*\*|-|\*|\/|%|<<|>>|&|\||\^|<|<=|=>|>|==|===|!=|=~|!~|<=>|\[\]|\[\]=)$/
      typs = RDL::Query.method_query(q)
      if typs.nil? then
        puts "No types for #{q}"
      else
        typs.each { |t|
          puts "#{q}: #{t}"
        }
      end
    elsif q =~ /^[A-Z]\w*$/
      typs = RDL::Query.class_query(q)
      if typs.nil? then
        puts "No method types for #{q}"
      else
        typs.each { |m, t| puts "#{m}: #{t}"}
      end
    elsif q =~ /\(.*\)/
      typs = RDL::Query.method_type_query(q)
      if typs.empty? then
        puts "No matching methods"
      else
        typs.each { |m, t| puts "#{m}: #{t}" }
      end
    else
      raise "Don't know how to handle query"
    end
    nil
  }
end

#rdl_remove_type(*args) ⇒ Object

Raises:



566
567
568
569
570
# File 'lib/rdl/wrap.rb', line 566

def rdl_remove_type(klass, meth)
  raise RuntimeError, "No existing type for #{RDL::Util.pp_klass_method(klass, meth)}" unless $__rdl_info.has? klass, meth, :type
  $__rdl_info.remove klass, meth, :type
  nil
end

#type(*args) ⇒ Object

+ klass +

may be Class, Symbol, or String

+ method +

may be Symbol or String

+ type +

may be Type or String

+ wrap +

indicates whether the type should be enforced (true) or just recorded (false)

+ typecheck +

indicates a method that should be statically type checked, as follows

if :call, indicates method should be typechecked when called
if :now, indicates method should be typechecked immediately
if other-symbol, indicates method should be typechecked when rdl_do_typecheck(other-symbol) is called

Set a method’s type. Possible invocations: type(klass, meth, type) type(meth, type) type(type)



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/rdl/wrap.rb', line 305

def type(*args, wrap: RDL::Config.instance.type_defaults[:wrap], typecheck: RDL::Config.instance.type_defaults[:typecheck])
  klass, meth, type = begin
                        RDL::Wrap.process_type_args(self, *args)
                      rescue Racc::ParseError => err
                        # Remove enough backtrace to only include actual source line
                        # Warning: Adjust the -5 below if the code (or this comment) changes
                        bt = err.backtrace
                        bt.shift until bt[0] =~ /^#{__FILE__}:#{__LINE__-5}/
                        bt.shift # remove $__rdl_contract_switch.off call
                        bt.shift # remove type call itself
                        err.set_backtrace bt
                        raise err
                      end
  if meth
# It turns out Ruby core/stdlib don't always follow this convention...
#        if (meth.to_s[-1] == "?") && (type.ret != $__rdl_type_bool)
#          warn "#{RDL::Util.pp_klass_method(klass, meth)}: methods that end in ? should have return type %bool"
#        end
    $__rdl_info.add(klass, meth, :type, type)
    unless $__rdl_info.set(klass, meth, :typecheck, typecheck)
      raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}"
    end
    if wrap || typecheck == :now
      if RDL::Util.method_defined?(klass, meth) || meth == :initialize
        $__rdl_info.set(klass, meth, :source_location, RDL::Util.to_class(klass).instance_method(meth).source_location)
        RDL::Typecheck.typecheck(klass, meth) if typecheck == :now
        RDL::Wrap.wrap(klass, meth) if wrap
      else
        if wrap
          $__rdl_to_wrap << [klass, meth]
          if (typecheck && typecheck != :call)
            $__rdl_to_typecheck[typecheck] = Set.new unless $__rdl_to_typecheck[typecheck]
            $__rdl_to_typecheck[typecheck].add([klass, meth])
          end
        end
      end
    end
  else
    $__rdl_deferred << [klass, :type, type, {wrap: wrap,
                                             typecheck: typecheck}]
  end
  nil
end

#type_alias(*args) ⇒ Object

Add a new type alias.

name

must be a string beginning with %.

typ

can be either a string, in which case it will be parsed

into a type, or a Type.

Raises:



524
525
526
527
528
529
530
531
532
533
534
535
536
# File 'lib/rdl/wrap.rb', line 524

def type_alias(name, typ)
  raise RuntimeError, "Attempt to redefine type #{name}" if $__rdl_special_types[name]
  case typ
  when String
    t = $__rdl_parser.scan_str "#T #{typ}"
    $__rdl_special_types[name] = t
  when RDL::Type::Type
    $__rdl_special_types[name] = typ
  else
    raise RuntimeError, "Unexpected typ argument #{typ.inspect}"
  end
  nil
end

#type_cast(*args) ⇒ Object

Returns a new object that wraps self in a type cast. If force is true this cast is unchecked, so use with caution

Raises:



512
513
514
515
516
517
518
# File 'lib/rdl/wrap.rb', line 512

def type_cast(typ, force: false)
  new_typ = if typ.is_a? RDL::Type::Type then typ else $__rdl_parser.scan_str "#T #{typ}" end
  raise RuntimeError, "type cast error: self not a member of #{new_typ}" unless force || typ.member?(self)
  obj = SimpleDelegator.new(self)
  obj.instance_variable_set('@__rdl_type', new_typ)
  obj
end

#type_params(*args) ⇒ Object

params

is an array of symbols or strings that are the

parameters of this (generic) type

variance

is an array of the corresponding variances, :+ for

covariant, :- for contravariant, and :~ for invariant. If omitted, all parameters are assumed to be invariant

all

should be a symbol naming an all? method that behaves like Array#all?, and that accepts

a block that takes arguments in the same order as the type parameters

blk

is for advanced use only. If present, [all] must be

nil. Whenever an instance of this class is instantiated!, the block will be passed an array typs corresponding to the type parameters of the class, and the block should return true if and only if self is a member of self.class<typs>.

Raises:



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/rdl/wrap.rb', line 445

def type_params(params, all, variance: nil, &blk)
  raise RuntimeError, "Empty type parameters not allowed" if params.empty?
  klass = self.to_s
  klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
  if $__rdl_type_params[klass]
    raise RuntimeError, "#{klass} already has type parameters #{$__rdl_type_params[klass]}"
  end
  params = params.map { |v|
    raise RuntimeError, "Type parameter #{v.inspect} is not symbol or string" unless v.class == String || v.class == Symbol
    v.to_sym
  }
  raise RuntimeError, "Duplicate type parameters not allowed" unless params.uniq.size == params.size
  raise RuntimeError, "Expecting #{params.size} variance annotations, got #{variance.size}" if variance && params.size != variance.size
  raise RuntimeError, "Only :+, +-, and :~ are allowed variance annotations" unless (not variance) || variance.all? { |v| [:+, :-, :~].member? v }
  raise RuntimeError, "Can't pass both all and a block" if all && blk
  raise RuntimeError, "all must be a symbol" unless (not all) || (all.instance_of? Symbol)
  chk = all || blk
  raise RuntimeError, "At least one of {all, blk} required" unless chk
  variance = params.map { |p| :~ } unless variance # default to invariant
  $__rdl_type_params[klass] = [params, variance, chk]
  nil
end

#var_type(*args) ⇒ Object

+ klass +

is the class containing the variable; self if omitted; ignored for local and global variables

+ var +

is a symbol or string containing the name of the variable

+ typ +

is a string containing the type

Raises:



352
353
354
355
356
357
358
359
360
361
# File 'lib/rdl/wrap.rb', line 352

def var_type(klass=self, var, typ)
  raise RuntimeError, "Variable cannot begin with capital" if var.to_s =~ /^[A-Z]/
  return if var.to_s =~ /^[a-z]/ # local variables handled specially, inside type checker
  raise RuntimeError, "Global variables can't be typed in a class" unless klass = self
  klass = RDL::Util::GLOBAL_NAME if var.to_s =~ /^\$/
  unless $__rdl_info.set(klass, var, :type, $__rdl_parser.scan_str("#T #{typ}"))
    raise RuntimeError, "Type already declared for #{var}"
  end
  nil
end