class Yadriggy::C::ClangTypeChecker
Attributes
instance_variables[R]
@return [Set<IvarObj>] accessed instance variables.
local_vars_table[R]
@return [Hash<Def,Hash<Symbol,Type>>] a map from functions to their
local variables.
Public Class Methods
new(syntax=nil)
click to toggle source
Calls superclass method
# File lib/yadriggy/c/ctypecheck.rb, line 59 def initialize(syntax=nil) super(syntax || C::syntax) @local_vars_table = {} @instance_variables = Set.new end
Public Instance Methods
binary_cexpr_type(op, t1, t2)
click to toggle source
@api private
# File lib/yadriggy/c/ctypecheck.rb, line 208 def binary_cexpr_type(op, t1, t2) case op when :+, :-, :*, :/ if t1 <= RubyClass::Float || t2 <= RubyClass::Float return RubyClass::Float elsif t1 <= Float32Type || t2 <= Float32Type return Float32Type else return RubyClass::Integer end when :% type_assert(t1 <= RubyClass::Integer && t2 <= RubyClass::Integer, 'bad operand type') return RubyClass::Integer when :<, :>, :<=, :>=, :==, :'&&', :'||' return RubyClass::Boolean else type_assert(false, "bad operator: #{ast.op}") end end
check_duplicate(name, t)
click to toggle source
@api private
# File lib/yadriggy/c/ctypecheck.rb, line 139 def check_duplicate(name, t) old_type = type_env.bound_name?(name) type_assert(old_type.nil? || old_type == t, "incompatible or duplicate declaration: #{name}") end
declare_type(name, name_ast, t)
click to toggle source
@api private
# File lib/yadriggy/c/ctypecheck.rb, line 120 def declare_type(name, name_ast, t) if name == 'return' || name == 'foreign' type_assert(valid_type?(t) || t == Void, "bad return type: #{t.name}") check_duplicate(name, t) type_env.bind_name(name.to_sym, t) elsif name == 'native' type_assert(t.is_a?(String), 'bad native argument. not String.') type_assert(type_env.bound_name?(:native).nil?, 'duplicate declaration: native') type_env.bind_name(:native, InstanceType.new(t)) else type_assert(valid_type?(t), "bad parameter type: #{name}") check_duplicate(name, t) bind_local_var(type_env, name_ast, t, false) end end
is_subsumed_by?(sub_type, super_type)
click to toggle source
# File lib/yadriggy/c/ctypecheck.rb, line 78 def is_subsumed_by?(sub_type, super_type) (valid_var_type?(sub_type) && valid_var_type?(super_type)) || sub_type <= super_type end
method_with_block?(name)
click to toggle source
Specifies the names of methods with a block.
@param [String] name a method name. @see CodeGen#call_with_block
@see typecheck_call_with_block
# File lib/yadriggy/c/ctypecheck.rb, line 321 def method_with_block?(name) name == 'times' end
typecheck_call_with_block(call_ast)
click to toggle source
# File lib/yadriggy/c/ctypecheck.rb, line 325 def typecheck_call_with_block(call_ast) type_assert(ast.name.name == 'times', "no such method: #{ast.name.name}") type_assert(type(ast.receiver) == RubyClass::Integer, 'the receiver must be an integer') type_assert(ast.block.params.size == 1, "wrong number of block parameters") type_as(ast.block.params[0], RubyClass::Integer) tenv = type_env.new_tenv tenv.bind_name(ast.block.params[0], RubyClass::Integer) tenv.bind_name(:return, Void) type(ast.block, tenv) Void end
typedecl_type(type_expr)
click to toggle source
@api private @param [ASTnode] type_expr
# File lib/yadriggy/c/ctypecheck.rb, line 101 def typedecl_type(type_expr) if type_expr.is_a?(Call) type_assert(type_expr.args.size == 1, 'bad array type') etype = RubyClass[type_expr.args[0].value] type_assert(etype != Undef, 'cannot resolve a type name') type_assert(valid_var_type?(etype), "bad array type: #{etype}") ArrayType.new(etype) else rt = type_expr.value type_assert(rt != Undef, 'cannot resolve a type name') if rt.is_a?(Module) && rt <= FFIArray ArrayType.new(RubyClass[rt.element_type]) else RubyClass[rt] end end end
valid_type?(t)
click to toggle source
# File lib/yadriggy/c/ctypecheck.rb, line 74 def valid_type?(t) valid_var_type?(t) || ArrayType.role(t) end
valid_var_type?(t)
click to toggle source
# File lib/yadriggy/c/ctypecheck.rb, line 65 def valid_var_type?(t) if t.is_a?(Type) et = t.exact_type et == Integer || et == Float || et == String || et == Float32 else false end end
Private Instance Methods
def_block_rule(is_block, new_tenv)
click to toggle source
# File lib/yadriggy/c/ctypecheck.rb, line 360 def def_block_rule(is_block, new_tenv) type_block(ast, new_tenv) ptypes = ast.params.map { |v| new_tenv.bound_name?(v.name) } ptypes.each_with_index do |t, i| type_assert(t, "missing parameter type: #{ast.params[i].name}") end result_t = new_tenv.bound_name?(:return) result_t = new_tenv.bound_name?(:foreign) if result_t.nil? type_assert(result_t, 'no return type specified') mtype = MethodType.new(ast, ptypes, result_t) type_env.bind_name(ast.name.name, mtype.result) unless is_block code = new_tenv.bound_name?(:native) if code ins_t = InstanceType.role(code) type_assert(ins_t, 'bad native declaration') return NativeMethodType.new(mtype, ins_t.object) end foreign_value = new_tenv.bound_name?(:foreign) if foreign_value && !is_block mtype2 = MethodType.new(nil, DynType, result_t) return ForeignMethodType.new(mtype2) end type_assert_later_unless(is_block) do body_t = type(ast.body, new_tenv) wt = WithReturnType.role(body_t) if result_t == Void type_assert(wt.nil? || body_t == Void, 'non-void return statement') else type_assert(wt, 'no return statement') type_assert(is_subsumed_by?(result_t, body_t), 'bad result type') end local_vars = {} new_tenv.each do |name, type| lvt = LocalVarType.role(type) local_vars[name] = type unless lvt.nil? end ast.params.each do |p| local_vars.delete(p.name.to_sym) end @local_vars_table[ast] = local_vars end mtype end
type_assert_later_unless(value) { || ... }
click to toggle source
# File lib/yadriggy/c/ctypecheck.rb, line 411 def type_assert_later_unless(value, &proc) unless value check_later(&proc) else yield end end
type_block(block_ast, new_env)
click to toggle source
Do typing the given block according to typedecl.
# File lib/yadriggy/c/ctypecheck.rb, line 421 def type_block(block_ast, new_env) expr0 = nil expr1 = nil if block_ast.body.is_a?(Exprs) size = block_ast.body.expressions.size expr0 = block_ast.body.expressions[0] if size > 0 expr1 = block_ast.body.expressions[1] if size > 1 else expr0 = block_ast.body end if expr0&.usertype == :return_type new_env.bind_name(:return, type(expr0)) end if expr0&.usertype == :typedecl type(expr0, new_env) elsif expr1&.usertype == :typedecl type(expr1, new_env) end unless new_env.bound_name?(:return) || new_env.bound_name?(:foreign) new_env.bind_name(:return, Void) end end