예제 #1
0
 def __init__(self, tf):
     self.tf = tf
     self.func_tf = FuncTransformer(tf)
예제 #2
0
 def __init__(self, tf: 'mypy.transform.DyncheckTransformVisitor') -> None:
     self.tf = tf
     self.func_tf = FuncTransformer(tf)
예제 #3
0
class TypeTransformer:
    """Class for transforming type definitions for runtime type checking.

    Transform a type definition by modifying it in-place.

    The following transformations are performed:

      * Represent generic type variables explicitly as attributes.
      * Create generic wrapper classes used by coercions to different type
        args.
      * Create wrapper methods needed when overriding methods with different
        signatures.
      * Create wrapper methods for calling methods in dynamically typed code.
        These perform the necessary coercions for arguments and return values
        to/from 'any'.
    
    This is used by DyncheckTransformVisitor and is logically aggregated within
    that class.
    """
    
    # Used for common transformation operations.
    tf = None
    # Used for transforming methods.
    func_tf = None
    
    def __init__(self, tf):
        self.tf = tf
        self.func_tf = FuncTransformer(tf)
    
    def transform_type_def(self, tdef):        
        """Transform a type definition.

        The result may be one or two definitions.  The first is the
        transformation of the original TypeDef. The second is a
        wrapper type, which is generated for generic types only.
        """
        defs = []
        
        if tdef.info.type_vars:
            # This is a generic type. Insert type variable slots in
            # the class definition for new type variables, i.e. type
            # variables not mapped to superclass type variables.
            defs.extend(self.make_tvar_representation(tdef.info))
        
        # Iterate over definitions and transform each of them.
        vars = set()
        for d in tdef.defs.body:
            if isinstance(d, FuncDef):
                # Implicit cast from FuncDef[] to Node[] is safe below.
                defs.extend(self.func_tf.transform_method(d))
            elif isinstance(d, VarDef):
                vdef = d
                defs.extend(self.transform_var_def(vdef))
                for n in vdef.items:
                    vars.add(n)

        # Add accessors for implicitly defined attributes.
        for v in tdef.info.vars.values():
            if v.info == tdef.info and v not in vars:
                defs.extend(self.make_accessors(v))
        
        # For generic classes, add an implicit __init__ wrapper.
        defs.extend(self.make_init_wrapper(tdef))
        
        if tdef.is_generic() or (tdef.info.base and
                                 tdef.info.base.is_generic()):
            self.make_instance_tvar_initializer(
                tdef.info.methods['__init__'])

        if not defs:
            defs.append(PassStmt())

        if tdef.is_generic():
            gen_wrapper = self.generic_class_wrapper(tdef)

        tdef.defs = Block(defs)

        dyn_wrapper = self.make_type_object_wrapper(tdef)
        
        if not tdef.is_generic():
            return [tdef, dyn_wrapper]
        else:
            return [tdef, dyn_wrapper, gen_wrapper]
    
    def make_init_wrapper(self, tdef):
        """Make and return an implicit __init__ if class needs it.
        
        Otherwise, return an empty list. We include an implicit
        __init__ if the class is generic or if it extends a generic class
        and if it does not define __init__.
        
        The __init__ of a generic class requires one or more extra type
        variable arguments. The inherited __init__ may not accept these.

        For example, assume these definitions:
        
        . class A<T>: pass
        . class B(A<int>): pass
        
        The constructor for B will be (equivalent to)
        
        . void __init__(B self):
        .     self.__tv = <int>
        .     super().__init__(<int>)
        """
        
        # FIX overloading, default args / varargs, keyword args

        info = tdef.info
        
        if '__init__' not in info.methods and (
                tdef.is_generic() or (info.base and info.base.is_generic())):
            # Generic class with no explicit __init__ method
            # (i.e. __init__ inherited from superclass). Generate a
            # wrapper that initializes type variable slots and calls
            # the superclass __init__ method.
            
            selftype = self_type(info)    
            callee_type = analyse_member_access(
                '__init__', selftype, None, False, True, None, None,
                info.base)
            
            # Now the callee type may contain the type variables of a
            # grandparent as bound type variables, but we want the
            # type variables of the parent class. Explicitly set the
            # bound type variables.
            callee_type = self.fix_bound_init_tvars(callee_type,
                map_instance_to_supertype(selftype, info.base))
            
            super_init = info.base.get_method('__init__')
            
            # Build argument list.
            args = [Var('self')]
            for i in range(1, len(super_init.args)):
                args.append(Var(super_init.args[i].name()))
                args[-1].type = callee_type.arg_types[i - 1]

            selft = self_type(self.tf.type_context())
            callee_type = prepend_arg_type(callee_type, selft)
            
            creat = FuncDef('__init__', args,
                            super_init.arg_kinds, [None] * len(args),
                            Block([]))
            creat.info = tdef.info
            creat.type = callee_type
            creat.is_implicit = False
            tdef.info.methods['__init__'] = creat
            
            # Insert a call to superclass constructor. If the
            # superclass is object, the constructor does nothing =>
            # omit the call.
            if tdef.info.base.full_name() != 'builtins.object':
                creat.body.body.append(
                    self.make_superclass_constructor_call(tdef.info,
                                                          callee_type))
            
            # Implicit cast from FuncDef[] to Node[] is safe below.
            return self.func_tf.transform_method(creat)
        else:
            return []
    
    def fix_bound_init_tvars(self, callable, typ):
        """Replace bound type vars of callable with args from instance type."""
        a = []
        for i in range(len(typ.args)):
            a.append((i + 1, typ.args[i]))
        return Callable(callable.arg_types, callable.arg_kinds,
                        callable.arg_names, callable.ret_type,
                        callable.is_type_obj(), callable.name,
                        callable.variables, a)
    
    def make_superclass_constructor_call(self, info, callee_type):
        """Construct a statement that calls the superclass constructor.

        In particular, it passes any type variables arguments as needed.
        """
        callee = SuperExpr('__init__')
        callee.info = info
        
        # We do not handle generic constructors. Either pass runtime
        # type variables from the current scope or perhaps require
        # explicit constructor in this case.
        
        selftype = self_type(info)    
        
        # FIX overloading
        # FIX default args / varargs
        
        # Map self type to the superclass context.
        selftype = map_instance_to_supertype(selftype, info.base)
        
        super_init = info.base.get_method('__init__')
        
        # Add constructor arguments.
        args = []
        for n in range(1, callee_type.min_args):            
            args.append(NameExpr(super_init.args[n].name()))
            self.tf.set_type(args[-1], callee_type.arg_types[n])

        # Store callee type after stripping away the 'self' type.
        self.tf.set_type(callee, nodes.method_callable(callee_type))
        
        call = CallExpr(callee, args, [nodes.ARG_POS] * len(args))
        return ExpressionStmt(call)
    
    def transform_var_def(self, o):
        """Transform a member variable definition.

        The result may be one or more definitions.
        """
        res = [o]
        
        self.tf.visit_var_def(o)
        
        # Add $x and set$x accessor wrappers for data attributes. These let
        # derived classes redefine a data attribute as a property.
        for n in o.items:
            res.extend(self.make_accessors(n))
        
        return res

    def make_accessors(self, n):
        if n.type:
            t = n.type
        else:
            t = Any()
        return [self.make_getter_wrapper(n.name(), t),
                self.make_setter_wrapper(n.name(), t),
                self.make_dynamic_getter_wrapper(n.name(), t),
                self.make_dynamic_setter_wrapper(n.name(), t)]
    
    def make_getter_wrapper(self, name, typ):
        """Create a getter wrapper for a data attribute.

        The getter will be of this form:
        
        . int $name*(C self):
        .     return self.name!
        """
        scope = self.make_scope()
        selft = self.self_type()
        selfv = scope.add('self', selft)
        
        member_expr = MemberExpr(scope.name_expr('self'), name, direct=True)
        ret = ReturnStmt(member_expr)

        wrapper_name = '$' + name
        sig = Callable([selft], [nodes.ARG_POS], [None], typ, False)
        fdef = FuncDef(wrapper_name,
                       [selfv],
                       [nodes.ARG_POS],
                       [None],
                       Block([ret]), sig)
        fdef.info = self.tf.type_context()
        return fdef
    
    def make_dynamic_getter_wrapper(self, name, typ):
        """Create a dynamically-typed getter wrapper for a data attribute.

        The getter will be of this form:
        
        . any $name*(C self):
        .     return {any <= typ self.name!}
        """
        scope = self.make_scope()
        selft = self.self_type()
        selfv = scope.add('self', selft)
        
        member_expr = MemberExpr(scope.name_expr('self'), name, direct=True)
        coerce_expr = coerce(member_expr, Any(), typ, self.tf.type_context())
        ret = ReturnStmt(coerce_expr)

        wrapper_name = '$' + name + self.tf.dynamic_suffix()
        sig = Callable([selft], [nodes.ARG_POS], [None], Any(), False)
        return FuncDef(wrapper_name,
                       [selfv],
                       [nodes.ARG_POS],
                       [None],
                       Block([ret]), sig)
    
    def make_setter_wrapper(self, name, typ):
        """Create a setter wrapper for a data attribute.

        The setter will be of this form:
        
        . void set$name(C self, typ name):
        .     self.name! = name
        """
        scope = self.make_scope()
        selft = self.self_type()
        selfv = scope.add('self', selft)
        namev = scope.add(name, typ)
        
        lvalue = MemberExpr(scope.name_expr('self'), name, direct=True)
        rvalue = scope.name_expr(name)
        ret = AssignmentStmt([lvalue], rvalue)

        wrapper_name = 'set$' + name
        sig = Callable([selft, typ],
                       [nodes.ARG_POS, nodes.ARG_POS],
                       [None, None],
                       Void(), False)
        fdef = FuncDef(wrapper_name,
                       [selfv, namev],
                       [nodes.ARG_POS, nodes.ARG_POS],
                       [None, None],
                       Block([ret]), sig)
        fdef.info = self.tf.type_context()
        return fdef
    
    def make_dynamic_setter_wrapper(self, name, typ):
        """Create a dynamically-typed setter wrapper for a data attribute.

        The setter will be of this form:
        
        . void set$name*(C self, any name):
        .     self.name! = {typ name}
        """
        lvalue = MemberExpr(self_expr(), name, direct=True)
        name_expr = NameExpr(name)
        rvalue = coerce(name_expr, typ, Any(), self.tf.type_context())
        ret = AssignmentStmt([lvalue], rvalue)

        wrapper_name = 'set$' + name + self.tf.dynamic_suffix()
        selft = self_type(self.tf.type_context())            
        sig = Callable([selft, Any()],
                       [nodes.ARG_POS, nodes.ARG_POS],
                       [None, None],
                       Void(), False)
        return FuncDef(wrapper_name,
                       [Var('self'), Var(name)],
                       [nodes.ARG_POS, nodes.ARG_POS],
                       [None, None],
                       Block([ret]), sig)
    
    def generic_accessor_wrappers(self, vdef):
        """Construct wrapper class methods for attribute accessors."""
        res = []
        for n in vdef.items:
            if n.type:
                t = n.type
            else:
                t = Any()
            for fd in [self.make_getter_wrapper(n.name(), t),
                      self.make_setter_wrapper(n.name(), t)]:
                res.extend(self.func_tf.generic_method_wrappers(fd))
        return res
    
    def generic_class_wrapper(self, tdef):
        """Construct a wrapper class for a generic type."""
        # FIX semanal meta-info for nodes + TypeInfo
        
        defs = []
        
        # Does the type have a superclass, other than builtins.object?
        has_proper_superclass = tdef.info.base.full_name() != 'builtins.object'
        
        if not has_proper_superclass or self.tf.is_java:
            # Generate member variables for wrapper object.
            defs.extend(self.make_generic_wrapper_member_vars(tdef))
        
        for alt in [False, BOUND_VAR]:
            defs.extend(self.make_tvar_representation(tdef.info, alt))
        
        # Generate constructor.
        defs.append(self.make_generic_wrapper_init(tdef.info))
        
        # Generate method wrappers.
        for d in tdef.defs.body:
            if isinstance(d, FuncDef):
                if not (d).is_constructor():
                    defs.extend(self.func_tf.generic_method_wrappers(
                        d))
            elif isinstance(d, VarDef):
                defs.extend(self.generic_accessor_wrappers(d))
            elif not isinstance(d, PassStmt):
                raise RuntimeError(
                    'Definition {} at line {} not supported'.format(
                        type(d), d.line))
        
        base_type = self.tf.named_type('builtins.object')
        # Inherit superclass wrapper if there is one.
        if has_proper_superclass:
            base = self.find_generic_base_class(tdef.info)
            if base:
                # TODO bind the type somewhere
                base_type = UnboundType(base.defn.name +
                                        self.tf.wrapper_class_suffix())
        
        # Build the type definition.
        wrapper = TypeDef(tdef.name + self.tf.wrapper_class_suffix(),
                          Block(defs),
                          None,
                          [base_type],
                          False)          # Interface?
        # FIX fullname
        
        self.tf.add_line_mapping(tdef, wrapper)
        
        return wrapper
    
    def find_generic_base_class(self, info):
        base = info.base
        while base:
            if base.type_vars != []:
                return base
            base = base.base
    
    def make_generic_wrapper_member_vars(self, tdef):
        """Generate member variable definition for wrapped object (__o).
        
        This is added to a generic wrapper class.
        """
        # The type is 'any' since it should behave covariantly in subclasses.
        return [VarDef([Var(self.object_member_name(tdef.info),
                            Any())], False, None)]
    
    def object_member_name(self, info):
        if self.tf.is_java:
            return '__o_{}'.format(info.name)
        else:
            return '__o'
    
    def make_generic_wrapper_init(self, info):
        """Build constructor of a generic wrapper class."""
        nslots = num_slots(info)
        
        cdefs = []
        
        # Build superclass constructor call.
        if info.base.full_name() != 'builtins.object' and self.tf.is_java:
            s = SuperExpr('__init__')
            cargs = [NameExpr('__o')]
            for n in range(num_slots(info.base)):
                cargs.append(NameExpr(tvar_arg_name(n + 1)))
            for n in range(num_slots(info.base)):
                cargs.append(NameExpr(tvar_arg_name(n + 1, BOUND_VAR)))
            c = CallExpr(s, cargs, [nodes.ARG_POS] * len(cargs))
            cdefs.append(ExpressionStmt(c))
        
        # Create initialization of the wrapped object.
        cdefs.append(AssignmentStmt([MemberExpr(
                                         self_expr(),
                                         self.object_member_name(info),
                                         direct=True)],
                                    NameExpr('__o')))
        
        # Build constructor arguments.
        args = [Var('self'), Var('__o')]
        init = [None, None]
        
        for alt in [False, BOUND_VAR]:
            for n in range(nslots):
                args.append(Var(tvar_arg_name(n + 1, alt)))
                init.append(None)

        nargs = nslots * 2 + 2
        fdef = FuncDef('__init__',
                       args,
                       [nodes.ARG_POS] * nargs,
                       init,
                       Block(cdefs),
                       Callable( [Any()] * nargs,
                                [nodes.ARG_POS] * nargs, [None] * nargs,
                                Void(),
                                is_type_obj=False))
        fdef.info = info
        
        self.make_wrapper_slot_initializer(fdef)
        
        return fdef
    
    def make_tvar_representation(self, info, is_alt=False):
        """Return type variable slot member definitions.

        There are of form 'any __tv*'. Only include new slots defined in the
        type.
        """
        defs = []
        base_slots = num_slots(info.base)
        for n in range(len(info.type_vars)):
            # Only include a type variable if it introduces a new slot.
            slot = get_tvar_access_path(info, n + 1)[0] - 1
            if slot >= base_slots:
                defs.append(VarDef([Var(tvar_slot_name(slot, is_alt),
                                        Any())], False, None))
        return defs
    
    def make_instance_tvar_initializer(self, creat):
        """Add type variable member initialization code to a constructor.

        Modify the constructor body directly.
        """
        for n in range(num_slots(creat.info)):
            rvalue = self.make_tvar_init_expression(creat.info, n)
            init = AssignmentStmt([MemberExpr(self_expr(),
                                              tvar_slot_name(n),
                                              direct=True)],
                                  rvalue)
            self.tf.set_type(init.lvalues[0], Any())
            self.tf.set_type(init.rvalue, Any())
            creat.body.body.insert(n, init)
    
    def make_wrapper_slot_initializer(self, creat):
        """Add type variable member initializations to a wrapper constructor.

        The function must be a constructor of a generic wrapper class. Modify
        the constructor body directly.
        """
        for alt in [BOUND_VAR, False]:
            for n in range(num_slots(creat.info)):
                rvalue = TypeExpr(
                    RuntimeTypeVar(NameExpr(tvar_slot_name(n, alt))))
                init = AssignmentStmt(
                    [MemberExpr(self_expr(),
                                tvar_slot_name(n, alt), direct=True)],
                    rvalue)
                self.tf.set_type(init.lvalues[0], Any())
                self.tf.set_type(init.rvalue, Any())
                creat.body.body.insert(n, init)
    
    def make_tvar_init_expression(self, info, slot):
        """Return the initializer for the given slot in the given type.
        
        This is the type expression that initializes the given slot
        using the type arguments given to the constructor.
        
        Examples:
          - In 'class C<T> ...', the initializer for the slot 0 is
            TypeExpr(RuntimeTypeVar(NameExpr('__tv'))).
          - In 'class D(C<int>) ...', the initializer for the slot 0 is
            TypeExpr(<int instance>).
        """
        # Figure out the superclass which defines the slot; also figure out
        # the tvar index that maps to the slot.
        origin, tv = find_slot_origin(info, slot)
        
        # Map self type to the superclass -> extract tvar with target index
        # (only contains subclass tvars?? PROBABLY NOT).
        selftype = self_type(info)
        selftype = map_instance_to_supertype(selftype, origin)
        tvar = selftype.args[tv - 1]
        
        # Map tvar to an expression; refer to local vars instead of member
        # vars always.
        tvar = translate_runtime_type_vars_locally(tvar)
        
        # Build the rvalue (initializer) expression
        return TypeExpr(tvar)

    def make_type_object_wrapper(self, tdef):
        """Construct dynamically typed wrapper function for a class.

        It simple calls the type object and returns the result.
        """
        
        # TODO keyword args, default args and varargs
        # TODO overloads

        type_sig = type_object_type(tdef.info, None)
        type_sig = erasetype.erase_typevars(type_sig)
        
        init = tdef.info.get_method('__init__')
        arg_kinds = type_sig.arg_kinds

        # The wrapper function has a dynamically typed signature.
        wrapper_sig = Callable( [Any()] * len(arg_kinds),
                               arg_kinds, [None] * len(arg_kinds),
                               Any(), False)
        
        n = NameExpr(tdef.name) # TODO full name
        args = self.func_tf.call_args(
            init.args[1:],
            type_sig,
            wrapper_sig,
            True, False)
        call = CallExpr(n, args, arg_kinds)
        ret = ReturnStmt(call)
        

        fdef = FuncDef(tdef.name + self.tf.dynamic_suffix(),
                       init.args[1:],
                       arg_kinds, [None] * len(arg_kinds),
                       Block([ret]))
        
        fdef.type = wrapper_sig
        return fdef

    def self_type(self):
        return self_type(self.tf.type_context())

    def make_scope(self):
        return Scope(self.tf.type_map)
예제 #4
0
class TypeTransformer:
    """Class for transforming type definitions for runtime type checking.

    Transform a type definition by modifying it in-place.

    The following transformations are performed:

      * Represent generic type variables explicitly as attributes.
      * Create generic wrapper classes used by coercions to different type
        args.
      * Create wrapper methods needed when overriding methods with different
        signatures.
      * Create wrapper methods for calling methods in dynamically typed code.
        These perform the necessary coercions for arguments and return values
        to/from 'Any'.
    
    This is used by DyncheckTransformVisitor and is logically aggregated within
    that class.
    """
    
    # Used for common transformation operations.
    tf = Undefined('mypy.transform.DyncheckTransformVisitor')
    # Used for transforming methods.
    func_tf = Undefined(FuncTransformer)
    
    def __init__(self, tf: 'mypy.transform.DyncheckTransformVisitor') -> None:
        self.tf = tf
        self.func_tf = FuncTransformer(tf)
    
    def transform_class_def(self, tdef: ClassDef) -> List[Node]:        
        """Transform a type definition.

        The result may be one or two definitions.  The first is the
        transformation of the original ClassDef. The second is a
        wrapper type, which is generated for generic types only.
        """
        defs = [] # type: List[Node]
        
        if tdef.info.type_vars:
            # This is a generic type. Insert type variable slots in
            # the class definition for new type variables, i.e. type
            # variables not mapped to superclass type variables.
            defs.extend(self.make_tvar_representation(tdef.info))
        
        # Iterate over definitions and transform each of them.
        vars = set() # type: Set[Var]
        for d in tdef.defs.body:
            if isinstance(d, FuncDef):
                # Implicit cast from FuncDef[] to Node[] is safe below.
                defs.extend(Any(self.func_tf.transform_method(d)))
            elif isinstance(d, VarDef):
                defs.extend(self.transform_var_def(d))
                for n in d.items:
                    vars.add(n)
            elif isinstance(d, AssignmentStmt):
                self.transform_assignment(d)
                defs.append(d)

        # Add accessors for implicitly defined attributes.
        for node in tdef.info.names.values():
            if isinstance(node.node, Var):
                v = cast(Var, node.node)
                if v.info == tdef.info and v not in vars:
                    defs.extend(self.make_accessors(v))
        
        # For generic classes, add an implicit __init__ wrapper.
        defs.extend(self.make_init_wrapper(tdef))
        
        if tdef.is_generic() or (tdef.info.bases and
                                 tdef.info.mro[1].is_generic()):
            self.make_instance_tvar_initializer(
                cast(FuncDef, tdef.info.get_method('__init__')))

        if not defs:
            defs.append(PassStmt())

        if tdef.is_generic():
            gen_wrapper = self.generic_class_wrapper(tdef)

        tdef.defs = Block(defs)

        dyn_wrapper = self.make_type_object_wrapper(tdef)
        
        if not tdef.is_generic():
            return [tdef, dyn_wrapper]
        else:
            return [tdef, dyn_wrapper, gen_wrapper]
    
    def make_init_wrapper(self, tdef: ClassDef) -> List[Node]:
        """Make and return an implicit __init__ if class needs it.
        
        Otherwise, return an empty list. We include an implicit
        __init__ if the class is generic or if it extends a generic class
        and if it does not define __init__.
        
        The __init__ of a generic class requires one or more extra type
        variable arguments. The inherited __init__ may not accept these.

        For example, assume these definitions:
        
        . class A(Generic[T]): pass
        . class B(A[int]): pass
        
        The constructor for B will be (equivalent to)
        
        . def __init__(self: B) -> None:
        .     self.__tv = <int>
        .     super().__init__(<int>)
        """
        
        # FIX overloading, default args / varargs, keyword args

        info = tdef.info
        
        if '__init__' not in info.names and (
                tdef.is_generic() or (info.bases and
                                      info.mro[1].is_generic())):
            # Generic class with no explicit __init__ method
            # (i.e. __init__ inherited from superclass). Generate a
            # wrapper that initializes type variable slots and calls
            # the superclass __init__ method.

            base = info.mro[1]
            selftype = self_type(info)    
            callee_type = cast(Callable, analyse_member_access(
                '__init__', selftype, None, False, True, None, None,
                base))
            
            # Now the callee type may contain the type variables of a
            # grandparent as bound type variables, but we want the
            # type variables of the parent class. Explicitly set the
            # bound type variables.
            callee_type = self.fix_bound_init_tvars(callee_type,
                map_instance_to_supertype(selftype, base))
            
            super_init = cast(FuncDef, base.get_method('__init__'))
            
            # Build argument list.
            args = [Var('self')]
            for i in range(1, len(super_init.args)):
                args.append(Var(super_init.args[i].name()))
                args[-1].type = callee_type.arg_types[i - 1]

            selft = self_type(self.tf.type_context())
            callee_type = prepend_arg_type(callee_type, selft)
            
            creat = FuncDef('__init__', args,
                            super_init.arg_kinds, [None] * len(args),
                            Block([]))
            creat.info = tdef.info
            creat.type = callee_type
            creat.is_implicit = False
            tdef.info.names['__init__'] = SymbolTableNode(MDEF, creat,
                                                          typ=creat.type)
            
            # Insert a call to superclass constructor. If the
            # superclass is object, the constructor does nothing =>
            # omit the call.
            if base.fullname() != 'builtins.object':
                creat.body.body.append(
                    self.make_superclass_constructor_call(tdef.info,
                                                          callee_type))
            
            # Implicit cast from FuncDef[] to Node[] is safe below.
            return Any(self.func_tf.transform_method(creat))
        else:
            return []
    
    def fix_bound_init_tvars(self, callable: Callable,
                             typ: Instance) -> Callable:
        """Replace bound type vars of callable with args from instance type."""
        a = [] # type: List[Tuple[int, Type]]
        for i in range(len(typ.args)):
            a.append((i + 1, typ.args[i]))
        return Callable(callable.arg_types, callable.arg_kinds,
                        callable.arg_names, callable.ret_type,
                        callable.is_type_obj(), callable.name,
                        callable.variables, a)
    
    def make_superclass_constructor_call(
            self, info: TypeInfo, callee_type: Callable) -> ExpressionStmt:
        """Construct a statement that calls the superclass constructor.

        In particular, it passes any type variables arguments as needed.
        """
        callee = SuperExpr('__init__')
        callee.info = info
        
        # We do not handle generic constructors. Either pass runtime
        # type variables from the current scope or perhaps require
        # explicit constructor in this case.
        
        selftype = self_type(info)    
        
        # FIX overloading
        # FIX default args / varargs
        
        # Map self type to the superclass context.
        base = info.mro[1]
        selftype = map_instance_to_supertype(selftype, base)
        
        super_init = cast(FuncDef, base.get_method('__init__'))
        
        # Add constructor arguments.
        args = [] # type: List[Node]
        for n in range(1, callee_type.min_args):            
            args.append(NameExpr(super_init.args[n].name()))
            self.tf.set_type(args[-1], callee_type.arg_types[n])

        # Store callee type after stripping away the 'self' type.
        self.tf.set_type(callee, nodes.method_callable(callee_type))
        
        call = CallExpr(callee, args, [nodes.ARG_POS] * len(args))
        return ExpressionStmt(call)
    
    def transform_var_def(self, o: VarDef) -> List[Node]:
        """Transform a member variable definition.

        The result may be one or more definitions.
        """
        res = [o] # type: List[Node]
        
        self.tf.visit_var_def(o)
        
        # Add $x and set$x accessor wrappers for data attributes. These let
        # derived classes redefine a data attribute as a property.
        for n in o.items:
            res.extend(self.make_accessors(n))
        
        return res
    
    def transform_assignment(self, o: AssignmentStmt) -> None:
        """Transform an assignment statement in class body."""
        self.tf.visit_assignment_stmt(o)

    def make_accessors(self, n: Var) -> List[Node]:
        if n.type:
            t = n.type
        else:
            t = AnyType()
        return [self.make_getter_wrapper(n.name(), t),
                self.make_setter_wrapper(n.name(), t),
                self.make_dynamic_getter_wrapper(n.name(), t),
                self.make_dynamic_setter_wrapper(n.name(), t)]
    
    def make_getter_wrapper(self, name: str, typ: Type) -> FuncDef:
        """Create a getter wrapper for a data attribute.

        The getter will be of this form:
        
        . def $name*(self: C) -> type:
        .     return self.name!
        """
        scope = self.make_scope()
        selft = self.self_type()
        selfv = scope.add('self', selft)
        
        member_expr = MemberExpr(scope.name_expr('self'), name, direct=True)
        ret = ReturnStmt(member_expr)

        wrapper_name = '$' + name
        sig = Callable([selft], [nodes.ARG_POS], [None], typ, False)
        fdef = FuncDef(wrapper_name,
                       [selfv],
                       [nodes.ARG_POS],
                       [None],
                       Block([ret]), sig)
        fdef.info = self.tf.type_context()
        return fdef
    
    def make_dynamic_getter_wrapper(self, name: str, typ: Type) -> FuncDef:
        """Create a dynamically-typed getter wrapper for a data attribute.

        The getter will be of this form:
        
        . def $name*(self: C) -> Any:
        .     return {Any <= typ self.name!}
        """
        scope = self.make_scope()
        selft = self.self_type()
        selfv = scope.add('self', selft)
        
        member_expr = MemberExpr(scope.name_expr('self'), name, direct=True)
        coerce_expr = coerce(member_expr, AnyType(), typ,
                             self.tf.type_context())
        ret = ReturnStmt(coerce_expr)

        wrapper_name = '$' + name + self.tf.dynamic_suffix()
        sig = Callable([selft], [nodes.ARG_POS], [None], AnyType(), False)
        return FuncDef(wrapper_name,
                       [selfv],
                       [nodes.ARG_POS],
                       [None],
                       Block([ret]), sig)
    
    def make_setter_wrapper(self, name: str, typ: Type) -> FuncDef:
        """Create a setter wrapper for a data attribute.

        The setter will be of this form:
        
        . def set$name(self: C, name: typ) -> None:
        .     self.name! = name
        """
        scope = self.make_scope()
        selft = self.self_type()
        selfv = scope.add('self', selft)
        namev = scope.add(name, typ)
        
        lvalue = MemberExpr(scope.name_expr('self'), name, direct=True)
        rvalue = scope.name_expr(name)
        ret = AssignmentStmt([lvalue], rvalue)

        wrapper_name = 'set$' + name
        sig = Callable([selft, typ],
                       [nodes.ARG_POS, nodes.ARG_POS],
                       [None, None],
                       Void(), False)
        fdef = FuncDef(wrapper_name,
                       [selfv, namev],
                       [nodes.ARG_POS, nodes.ARG_POS],
                       [None, None],
                       Block([ret]), sig)
        fdef.info = self.tf.type_context()
        return fdef
    
    def make_dynamic_setter_wrapper(self, name: str, typ: Type) -> FuncDef:
        """Create a dynamically-typed setter wrapper for a data attribute.

        The setter will be of this form:
        
        . def set$name*(self: C, name; Any) -> None:
        .     self.name! = {typ name}
        """
        lvalue = MemberExpr(self_expr(), name, direct=True)
        name_expr = NameExpr(name)
        rvalue = coerce(name_expr, typ, AnyType(), self.tf.type_context())
        ret = AssignmentStmt([lvalue], rvalue)

        wrapper_name = 'set$' + name + self.tf.dynamic_suffix()
        selft = self_type(self.tf.type_context())            
        sig = Callable([selft, AnyType()],
                       [nodes.ARG_POS, nodes.ARG_POS],
                       [None, None],
                       Void(), False)
        return FuncDef(wrapper_name,
                       [Var('self'), Var(name)],
                       [nodes.ARG_POS, nodes.ARG_POS],
                       [None, None],
                       Block([ret]), sig)
    
    def generic_accessor_wrappers(self, s: AssignmentStmt) -> List[Node]:
        """Construct wrapper class methods for attribute accessors."""
        res = [] # type: List[Node]
        assert len(s.lvalues) == 1
        assert isinstance(s.lvalues[0], NameExpr)
        assert s.type is not None
        name = cast(NameExpr, s.lvalues[0])
        for fd in [self.make_getter_wrapper(name.name, s.type),
                   self.make_setter_wrapper(name.name, s.type)]:
            res.extend(self.func_tf.generic_method_wrappers(fd))
        return res
    
    def generic_class_wrapper(self, tdef: ClassDef) -> ClassDef:
        """Construct a wrapper class for a generic type."""
        # FIX semanal meta-info for nodes + TypeInfo
        
        defs = [] # type: List[Node]
        
        # Does the type have a superclass, other than builtins.object?
        base = tdef.info.mro[1]
        has_proper_superclass = base.fullname() != 'builtins.object'
        
        if not has_proper_superclass or self.tf.is_java:
            # Generate member variables for wrapper object.
            defs.extend(self.make_generic_wrapper_member_vars(tdef))
        
        for alt in [False, BOUND_VAR]:
            defs.extend(self.make_tvar_representation(tdef.info, alt))
        
        # Generate constructor.
        defs.append(self.make_generic_wrapper_init(tdef.info))
        
        # Generate method wrappers.
        for d in tdef.defs.body:
            if isinstance(d, FuncDef):
                if not d.is_constructor():
                    defs.extend(self.func_tf.generic_method_wrappers(d))
            elif isinstance(d, AssignmentStmt):
                defs.extend(self.generic_accessor_wrappers(d))
            elif not isinstance(d, PassStmt):
                raise RuntimeError(
                    'Definition {} at line {} not supported'.format(
                        type(d), d.line))
        
        base_type = self.tf.named_type('builtins.object') # type: Type
        # Inherit superclass wrapper if there is one.
        if has_proper_superclass:
            base = self.find_generic_base_class(tdef.info)
            if base:
                # TODO bind the type somewhere
                base_type = UnboundType(base.defn.name +
                                        self.tf.wrapper_class_suffix())
        
        # Build the type definition.
        wrapper = ClassDef(tdef.name + self.tf.wrapper_class_suffix(),
                          Block(defs),
                          None,
                          [base_type])
        # FIX fullname
        
        self.tf.add_line_mapping(tdef, wrapper)
        
        return wrapper
    
    def find_generic_base_class(self, info: TypeInfo) -> TypeInfo:
        base = info.mro[1]
        while True:
            if base.type_vars != []:
                return base
            if len(base.mro) <= 1:
                return None
            base = base.mro[1]
    
    def make_generic_wrapper_member_vars(self, tdef: ClassDef) -> List[Node]:
        """Generate member variable definition for wrapped object (__o).
        
        This is added to a generic wrapper class.
        """
        # The type is 'Any' since it should behave covariantly in subclasses.
        return [VarDef([Var(self.object_member_name(tdef.info),
                            AnyType())], False, None)]
    
    def object_member_name(self, info: TypeInfo) -> str:
        if self.tf.is_java:
            return '__o_{}'.format(info.name)
        else:
            return '__o'
    
    def make_generic_wrapper_init(self, info: TypeInfo) -> FuncDef:
        """Build constructor of a generic wrapper class."""
        nslots = num_slots(info)
        
        cdefs = [] # type: List[Node]
        
        # Build superclass constructor call.
        base = info.mro[1]
        if base.fullname() != 'builtins.object' and self.tf.is_java:
            s = SuperExpr('__init__')
            cargs = [NameExpr('__o')] # type: List[Node]
            for n in range(num_slots(base)):
                cargs.append(NameExpr(tvar_arg_name(n + 1)))
            for n in range(num_slots(base)):
                cargs.append(NameExpr(tvar_arg_name(n + 1, BOUND_VAR)))
            c = CallExpr(s, cargs, [nodes.ARG_POS] * len(cargs))
            cdefs.append(ExpressionStmt(c))
        
        # Create initialization of the wrapped object.
        cdefs.append(AssignmentStmt([MemberExpr(
                                         self_expr(),
                                         self.object_member_name(info),
                                         direct=True)],
                                    NameExpr('__o')))
        
        # Build constructor arguments.
        args = [Var('self'), Var('__o')]
        init = [None, None] # type: List[Node]
        
        for alt in [False, BOUND_VAR]:
            for n in range(nslots):
                args.append(Var(tvar_arg_name(n + 1, alt)))
                init.append(None)

        nargs = nslots * 2 + 2
        fdef = FuncDef('__init__',
                       args,
                       [nodes.ARG_POS] * nargs,
                       init,
                       Block(cdefs),
                       Callable( [AnyType()] * nargs,
                                [nodes.ARG_POS] * nargs, [None] * nargs,
                                Void(),
                                is_type_obj=False))
        fdef.info = info
        
        self.make_wrapper_slot_initializer(fdef)
        
        return fdef
    
    def make_tvar_representation(self, info: TypeInfo,
                                 is_alt: Any = False) -> List[Node]:
        """Return type variable slot member definitions.

        There are of form '__tv*: Any'. Only include new slots defined in the
        type.
        """
        defs = [] # type: List[Node]
        base_slots = num_slots(info.mro[1])
        for n in range(len(info.type_vars)):
            # Only include a type variable if it introduces a new slot.
            slot = get_tvar_access_path(info, n + 1)[0] - 1
            if slot >= base_slots:
                defs.append(VarDef([Var(tvar_slot_name(slot, is_alt),
                                        AnyType())], False, None))
        return defs
    
    def make_instance_tvar_initializer(self, creat: FuncDef) -> None:
        """Add type variable member initialization code to a constructor.

        Modify the constructor body directly.
        """
        for n in range(num_slots(creat.info)):
            rvalue = self.make_tvar_init_expression(creat.info, n)
            init = AssignmentStmt([MemberExpr(self_expr(),
                                              tvar_slot_name(n),
                                              direct=True)],
                                  rvalue)
            self.tf.set_type(init.lvalues[0], AnyType())
            self.tf.set_type(init.rvalue, AnyType())
            creat.body.body.insert(n, init)
    
    def make_wrapper_slot_initializer(self, creat: FuncDef) -> None:
        """Add type variable member initializations to a wrapper constructor.

        The function must be a constructor of a generic wrapper class. Modify
        the constructor body directly.
        """
        for alt in [BOUND_VAR, False]:
            for n in range(num_slots(creat.info)):
                rvalue = TypeExpr(
                    RuntimeTypeVar(NameExpr(tvar_slot_name(n, alt))))
                init = AssignmentStmt(
                    [MemberExpr(self_expr(),
                                tvar_slot_name(n, alt), direct=True)],
                    rvalue)
                self.tf.set_type(init.lvalues[0], AnyType())
                self.tf.set_type(init.rvalue, AnyType())
                creat.body.body.insert(n, init)
    
    def make_tvar_init_expression(self, info: TypeInfo, slot: int) -> TypeExpr:
        """Return the initializer for the given slot in the given type.
        
        This is the type expression that initializes the given slot
        using the type arguments given to the constructor.
        
        Examples:
          - In 'class C(Generic[T]) ...', the initializer for the slot 0 is
            TypeExpr(RuntimeTypeVar(NameExpr('__tv'))).
          - In 'class D(C[int]) ...', the initializer for the slot 0 is
            TypeExpr(<int instance>).
        """
        # Figure out the superclass which defines the slot; also figure out
        # the tvar index that maps to the slot.
        origin, tv = find_slot_origin(info, slot)
        
        # Map self type to the superclass -> extract tvar with target index
        # (only contains subclass tvars?? PROBABLY NOT).
        selftype = self_type(info)
        selftype = map_instance_to_supertype(selftype, origin)
        tvar = selftype.args[tv - 1]
        
        # Map tvar to an expression; refer to local vars instead of member
        # vars always.
        tvar = translate_runtime_type_vars_locally(tvar)
        
        # Build the rvalue (initializer) expression
        return TypeExpr(tvar)

    def make_type_object_wrapper(self, tdef: ClassDef) -> FuncDef:
        """Construct dynamically typed wrapper function for a class.

        It simple calls the type object and returns the result.
        """
        
        # TODO keyword args, default args and varargs
        # TODO overloads

        type_sig = cast(Callable, type_object_type(tdef.info, None))
        type_sig = cast(Callable, erasetype.erase_typevars(type_sig))
        
        init = cast(FuncDef, tdef.info.get_method('__init__'))
        arg_kinds = type_sig.arg_kinds

        # The wrapper function has a dynamically typed signature.
        wrapper_sig = Callable( [AnyType()] * len(arg_kinds),
                               arg_kinds, [None] * len(arg_kinds),
                               AnyType(), False)
        
        n = NameExpr(tdef.name) # TODO full name
        args = self.func_tf.call_args(
            init.args[1:],
            type_sig,
            wrapper_sig,
            True, False)
        call = CallExpr(n, args, arg_kinds)
        ret = ReturnStmt(call)
        

        fdef = FuncDef(tdef.name + self.tf.dynamic_suffix(),
                       init.args[1:],
                       arg_kinds, [None] * len(arg_kinds),
                       Block([ret]))
        
        fdef.type = wrapper_sig
        return fdef

    def self_type(self) -> Instance:
        return self_type(self.tf.type_context())

    def make_scope(self) -> 'Scope':
        return Scope(self.tf.type_map)