Sophie

Sophie

distrib > Mandriva > 2010.2 > i586 > by-pkgid > df29c83ca401d91ec9c00bfcf7fea4ea > files > 284

shedskin-0.8-2mdv2010.2.i586.rpm

'''
*** SHED SKIN Python-to-C++ Compiler ***
Copyright 2005-2009 Mark Dufour; License GNU GPL version 3 (See LICENSE)

cpp.py: output C++ code

output equivalent C++ code, using templates and virtuals to support data and OO polymorphism.

class generateVisitor: inherits visitor pattern from compiler.visitor.ASTVisitor, to recursively generate C++ code for each syntactical Python construct. the constraint graph, with inferred types, is first 'merged' back to program dimensions (getgx().merged_inh).

'''

import textwrap, string
from distutils import sysconfig

from shared import *
import extmod

# --- code generation visitor; use type information
class generateVisitor(ASTVisitor):
    def __init__(self, module):
        self.output_base = os.path.join(getgx().output_dir, module.filename[:-3])
        self.out = file(self.output_base+'.cpp','w')
        self.indentation = ''
        self.consts = {}
        self.mergeinh = merged(getgx().types, inheritance=True)
        self.module = module
        self.name = module.ident
        self.filling_consts = False
        self.with_count = 0
        self.bool_wrapper = {}

    def insert_consts(self, declare): # XXX ugly
        if not self.consts: return
        self.filling_consts = True

        if declare: suffix = '.hpp'
        else: suffix = '.cpp'

        lines = file(self.output_base+suffix,'r').readlines()
        newlines = []
        j = -1
        for (i,line) in enumerate(lines):
            if line.startswith('namespace ') and not 'XXX' in line: # XXX
                j = i+1
            newlines.append(line)

            if i == j:
                pairs = []
                done = set()
                for (node, name) in self.consts.items():
                    if not name in done and node in self.mergeinh and self.mergeinh[node]: # XXX
                        ts = nodetypestr(node, inode(node).parent)
                        if declare: ts = 'extern '+ts
                        pairs.append((ts, name))
                        done.add(name)

                newlines.extend(self.group_declarations(pairs))
                newlines.append('\n')

        newlines2 = []
        j = -1
        for (i,line) in enumerate(newlines):
            if line.startswith('void __init() {'):
                j = i
            newlines2.append(line)

            if i == j:
                todo = {}
                for (node, name) in self.consts.items():
                    if not name in todo:
                        todo[int(name[6:])] = node
                todolist = todo.keys()
                todolist.sort()
                for number in todolist:
                    if self.mergeinh[todo[number]]: # XXX
                        name = 'const_'+str(number)
                        self.start('    '+name+' = ')
                        if isinstance(todo[number], Const) and isinstance(todo[number].value, str) and len(todo[number].value) == 1:
                            self.append("__char_cache[%d];" % ord(todo[number].value))
                        else:
                            self.visit(todo[number], inode(todo[number]).parent)
                        newlines2.append(self.line+';\n')

                newlines2.append('\n')

        file(self.output_base+suffix,'w').writelines(newlines2)
        self.filling_consts = False

    def insert_includes(self): # XXX ugly
        includes = get_includes(self.module)
        prop_paths = [mod.include_path() for mod in self.module.prop_includes]
        prop_includes = set(prop_paths) - set(includes)
        lines = file(self.output_base+'.hpp','r').readlines()
        newlines = []
        prev = ''
        for line in lines:
            if prev.startswith('#include') and not line.strip():
                for include in prop_includes:
                    newlines.append('#include "%s"\n' % include)
                newlines.extend(self.fwd_class_refs())
            newlines.append(line)
            prev = line
        file(self.output_base+'.hpp','w').writelines(newlines)

    def fwd_class_refs(self):
        lines = []
        for module in self.module.prop_includes:
            if module.builtin:
                continue
            for mod in module.mod_path:
                lines.append('namespace __%s__ { /* XXX */\n' % mod)
            for cl in module.mv.classes.values():
                lines.append('class %s;\n' % nokeywords(cl.ident)) # XXX cppname?
            for mod in module.mod_path:
                lines.append('}\n')
        if lines: 
            lines.insert(0, '\n')
        return lines
        
    # --- group pairs of (type, name) declarations, while paying attention to '*'
    def group_declarations(self, pairs):
        group = {}
        for (type, name) in pairs:
            group.setdefault(type, []).append(name)
        result = []
        for (type, names) in group.items():
            names.sort()
            if type.endswith('*'):
                result.append(type+(', *'.join(names))+';\n')
            else:
                result.append(type+(', '.join(names))+';\n')
        return result

    def header_file(self):
        self.out = file(self.output_base+'.hpp','w')
        self.visit(self.module.ast, True)
        self.out.close()

    def classes(self, node):
        return set([t[0].ident for t in inode(node).types()])

    def output(self, text):
        print >>self.out, self.indentation+text

    def start(self, text=None):
        self.line = self.indentation
        if text: self.line += text
    def append(self, text):
        self.line += text
    def eol(self, text=None):
        if text: self.append(text)
        if self.line.strip():
            print >>self.out, self.line+';'

    def indent(self):
        self.indentation += 4*' '
    def deindent(self):
        self.indentation = self.indentation[:-4]

    def connector(self, node, func):
        if singletype(node, module):
            return '::'
        return '->'

    def declaredefs(self, vars, declare): # XXX use group_declarations
        decl = {}
        for (name,var) in vars:
            if singletype(var, module) or var.invisible: # XXX buh
                continue
            typehu = nodetypestr(var, var.parent)
            if not var.name in ['__exception', '__exception2']: # XXX
                decl.setdefault(typehu, []).append(self.cpp_name(name))
        decl2 = []
        for (t,names) in decl.items():
            names.sort()
            prefix=''
            if declare: 
                prefix='extern '
                if 'for_in_loop' in t: # XXX
                    continue
            if t.endswith('*'):
                decl2.append(prefix+t+(', *'.join(names)))
            else:
                decl2.append(prefix+t+(', '.join(names)))
        return ';\n'.join(decl2)

    def get_constant(self, node):
        parent = inode(node).parent
        while isinstance(parent, function) and parent.listcomp: # XXX
            parent = parent.parent
        if isinstance(parent, function) and (parent.inherited or not self.inhcpa(parent)): # XXX
            return
        for other in self.consts: # XXX use mapping
            if node.value == other.value:
                return self.consts[other]
        self.consts[node] = 'const_'+str(len(self.consts))
        return self.consts[node]

    def module_hpp(self, node):
        define = '_'.join(self.module.mod_path).upper()+'_HPP'
        print >>self.out, '#ifndef __'+define
        print >>self.out, '#define __'+define+'\n'

        # --- include header files
        if self.module.dir == '': depth = 0
        else: depth = self.module.dir.count('/')+1

        includes = get_includes(self.module)
        if 'getopt.hpp' in includes: # XXX
            includes.add('os/__init__.hpp')
            includes.add('os/path.hpp')
            includes.add('stat.hpp')
        if 'os/__init__.hpp' in includes: # XXX
            includes.add('os/path.hpp')
            includes.add('stat.hpp')
        for include in includes:
            print >>self.out, '#include "'+include+'"'
        if includes: print >>self.out

        # --- namespaces
        print >>self.out, 'using namespace __shedskin__;'
        for n in self.module.mod_path:
            print >>self.out, 'namespace __'+n+'__ {'
        print >>self.out

        for child in node.node.getChildNodes():
            if isinstance(child, From) and child.modname != '__future__':
                mod = getgx().from_mod[child]
                using = 'using '+mod.full_path()+'::'
                for (name, pseudonym) in child.names:
                    pseudonym = pseudonym or name
                    if name == '*':
                        for func in mod.mv.funcs.values():
                            if func.cp: # XXX 
                                print >>self.out, using+self.cpp_name(func.ident)+';';
                        for cl in mod.mv.classes.values():
                            print >>self.out, using+nokeywords(cl.ident)+';';
                    elif pseudonym not in self.module.mv.globals:
                        if name in mod.mv.funcs:
                            func = mod.mv.funcs[name]
                            if func.cp:
                                print >>self.out, using+self.cpp_name(func.ident)+';';
                        else:
                            print >>self.out, using+nokeywords(name)+';'
        print >>self.out

        # class declarations
        for child in node.node.getChildNodes():
            if isinstance(child, Class):
                cl = defclass(child.name)
                print >>self.out, 'class '+nokeywords(cl.ident)+';'
        print >>self.out

        # --- lambda typedefs
        self.func_pointers()

        # globals
        defs = self.declaredefs(list(getmv().globals.items()), declare=True);
        if defs:
            self.output(defs+';')
            print >>self.out

        # --- class definitions
        for child in node.node.getChildNodes():
            if isinstance(child, Class):
                self.class_hpp(child)

        # --- defaults
        if self.module.mv.defaults.items():
            for default, nr in self.module.mv.defaults.items():
                print >>self.out, 'extern '+nodetypestr(default, None)+' '+('default_%d;'%nr)
            print >>self.out

        # function declarations
        if self.module != getgx().main_module:
            print >>self.out, 'void __init();'
        for child in node.node.getChildNodes():
            if isinstance(child, Function):
                func = getmv().funcs[child.name]
                if self.inhcpa(func):
                    self.visitFunction(func.node, declare=True)
        print >>self.out

        if getgx().extension_module:
            extmod.pyinit_func(self)

        for n in self.module.mod_path:
            print >>self.out, '} // module namespace'

        if getgx().extension_module:
            extmod.convert_methods2(self)

        print >>self.out, '#endif'

    def module_cpp(self, node):
        # --- external dependencies
        if self.module.filename.endswith('__init__.py'): # XXX nicer check
            print >>self.out, '#include "__init__.hpp"\n'
        else:
            print >>self.out, '#include "%s.hpp"\n' % self.module.ident

        # --- comments
        if node.doc:
            self.do_comment(node.doc)
            print >>self.out

        # --- namespace
        for n in self.module.mod_path:
            print >>self.out, 'namespace __'+n+'__ {'
        print >>self.out

        # --- globals
        defs = self.declaredefs(list(getmv().globals.items()), declare=False);
        if defs:
            self.output(defs+';')
            print >>self.out

        # --- defaults
        if self.module.mv.defaults.items():
            for default, nr in self.module.mv.defaults.items():
                print >>self.out, nodetypestr(default, None)+' '+('default_%d;'%nr)
            print >>self.out

        # --- declarations
        self.listcomps = {}
        for (listcomp,lcfunc,func) in getmv().listcomps:
            self.listcomps[listcomp] = (lcfunc, func)
        self.do_listcomps(True)
        self.do_lambdas(True)
        print >>self.out

        # --- definitions
        self.do_listcomps(False)
        self.do_lambdas(False)
        for child in node.node.getChildNodes():
            if isinstance(child, Class):
                self.class_cpp(child)
            elif isinstance(child, Function):
                self.do_comments(child)
                self.visit(child)

        # --- __init
        self.output('void __init() {')
        self.indent()
        if self.module == getgx().main_module and not getgx().extension_module: self.output('__name__ = new str("__main__");\n')
        else: self.output('__name__ = new str("%s");\n' % self.module.ident)

        for child in node.node.getChildNodes():
            if isinstance(child, Function):
                for default in child.defaults:
                    if default in getmv().defaults:
                        self.start('')
                        self.visitm('default_%d = ' % getmv().defaults[default], default, ';')
                        self.eol()

            elif isinstance(child, Class):
                for child2 in child.code.getChildNodes():
                    if isinstance(child2, Function):
                        for default in child2.defaults:
                            if default in getmv().defaults:
                                self.start('')
                                self.visitm('default_%d = ' % getmv().defaults[default], default, ';')
                                self.eol()
                if child.name in getmv().classes:
                    cl = getmv().classes[child.name]
                    self.output('cl_'+cl.cpp_name+' = new class_("%s", %d, %d);' % (cl.cpp_name, cl.low, cl.high))
                    for varname in cl.parent.varorder:
                        var = cl.parent.vars[varname]
                        if var.initexpr:
                            self.start()
                            self.visitm(cl.ident+'::'+self.cpp_name(var.name)+' = ', var.initexpr, cl)
                            self.eol()

            elif isinstance(child, Discard):
                if isinstance(child.expr, Const) and child.expr.value == None: # XXX merge with visitStmt
                    continue
                if isinstance(child.expr, Const) and type(child.expr.value) == str:
                    continue

                self.start('')
                self.visit(child)
                self.eol()

            elif isinstance(child, From) and child.modname != '__future__':
                mod = getgx().from_mod[child]
                for (name, pseudonym) in child.names:
                    pseudonym = pseudonym or name
                    if name == '*':
                        for var in mod.mv.globals.values():
                            if not var.invisible and not var.imported and not var.name.startswith('__') and var.types():
                                self.start(nokeywords(var.name)+' = '+mod.full_path()+'::'+nokeywords(var.name))
                                self.eol()
                    elif pseudonym in self.module.mv.globals and not [t for t in self.module.mv.globals[pseudonym].types() if isinstance(t[0], module)]:
                        self.start(nokeywords(pseudonym)+' = '+mod.full_path()+'::'+nokeywords(name))
                        self.eol()

            elif not isinstance(child, (Class, Function)):
                self.do_comments(child)
                self.visit(child)

        self.deindent()
        self.output('}\n')

        # --- close namespace
        for n in self.module.mod_path:
            print >>self.out, '} // module namespace'
        print >>self.out

        # --- c++ main/extension module setup
        if getgx().extension_module:
            extmod.do_extmod(self)
        if self.module == getgx().main_module:
            self.do_main()

    def visitModule(self, node, declare=False):
        if declare:
            self.module_hpp(node)
        else:
            self.module_cpp(node)

    def do_main(self):
        mods = getgx().modules.values()
        if [mod for mod in mods if mod.builtin and mod.ident == 'sys'] and not getgx().extension_module:
            print >>self.out, 'int main(int argc, char **argv) {'
        else:
            print >>self.out, 'int main(int, char **) {'
        self.do_init_modules()
        print >>self.out, '    __shedskin__::__start(__%s__::__init);' % self.module.ident
        print >>self.out, '}'

    def do_init_modules(self):
        mods = getgx().modules.values()
        mods.sort(key=lambda x: x.import_order)
        print >>self.out, '    __shedskin__::__init();'
        for mod in mods:
            if mod != getgx().main_module and mod.ident != 'builtin':
                if mod.ident == 'sys':
                    if getgx().extension_module:
                        print >>self.out, '    __sys__::__init(0, 0);'
                    else:
                        print >>self.out, '    __sys__::__init(argc, argv);'
                else:
                    print >>self.out, '    '+mod.full_path()+'::__init();'

    def do_comment(self, s):
        if not s: return
        doc = s.replace('/*', '//').replace('*/', '//').split('\n')
        self.output('/**')
        if doc[0].strip():
            self.output(doc[0])
        # re-indent the rest of the doc string
        rest = textwrap.dedent('\n'.join(doc[1:])).splitlines()
        for l in rest:
            self.output(l)
        self.output('*/')

    def do_comments(self, child):
        if child in getgx().comments:
            for n in getgx().comments[child]:
                self.do_comment(n)

    def visitContinue(self, node, func=None):
        self.start('continue')
        self.eol()

    def visitWith(self, node, func=None):
        self.start()
        if node.vars:
            self.visitm('WITH_VAR(', node.expr, ',', node.vars, func)
        else:
            self.visitm('WITH(', node.expr, func)
        self.append(',%d)' % self.with_count)
        self.with_count += 1
        print >>self.out, self.line
        self.indent()
        self.visit(node.body, func)
        self.deindent()
        self.output('END_WITH')

    def visitWhile(self, node, func=None):
        print >>self.out
        if node.else_:
            self.output('%s = 0;' % getmv().tempcount[node.else_])

        self.start('while (')
        self.bool_test(node.test, func)
        self.append(') {')
        print >>self.out, self.line
        self.indent()
        getgx().loopstack.append(node)
        self.visit(node.body, func)
        getgx().loopstack.pop()
        self.deindent()
        self.output('}')

        if node.else_:
            self.output('if (!%s) {' % getmv().tempcount[node.else_])
            self.indent()
            self.visit(node.else_, func)
            self.deindent()
            self.output('}')

    def class_hpp(self, node):
        declare = True
        cl = getmv().classes[node.name]
        self.output('extern class_ *cl_'+cl.cpp_name+';')

        # --- header
        clnames = [namespaceclass(b) for b in cl.bases if b.ident != 'object']
        if not clnames:
            clnames = ['pyobj']
            if '__iter__' in cl.funcs: # XXX get return type of 'next'
                typestr = nodetypestr(cl.funcs['__iter__'].retnode.thing)
                if typestr.startswith('__iter<'):
                    typestr = typestr[typestr.find('<')+1:typestr.find('>')]
                    clnames = ['pyiter<%s>' % typestr] # XXX use iterable interface
            if '__call__' in cl.funcs:
                callfunc = cl.funcs['__call__']
                r_typestr = nodetypestr(callfunc.retnode.thing).strip()
                nargs = len(callfunc.formals)-1
                argtypes = [nodetypestr(callfunc.vars[callfunc.formals[i+1]]).strip() for i in range(nargs)]
                clnames = ['pycall%d<%s,%s>' % (nargs, r_typestr, ','.join(argtypes))]

        self.output('class '+nokeywords(cl.ident)+' : '+', '.join(['public '+clname for clname in clnames])+' {')

        self.do_comment(node.doc)
        self.output('public:')
        self.indent()

        self.class_variables(cl)

        # --- constructor
        need_init = False
        if '__init__' in cl.funcs:
            initfunc = cl.funcs['__init__']
            if self.inhcpa(initfunc):
                 need_init = True

        # --- default constructor
        if need_init:
            self.output(nokeywords(cl.ident)+'() {}')
        else:
            self.output(nokeywords(cl.ident)+'() { this->__class__ = cl_'+cl.cpp_name+'; }')

        # --- init constructor
        if need_init:
            self.func_header(initfunc, declare=True, is_init=True)
            self.indent()
            self.output('this->__class__ = cl_'+cl.cpp_name+';')
            self.output('__init__('+', '.join([self.cpp_name(f) for f in initfunc.formals[1:]])+');')
            self.deindent()
            self.output('}')

        # --- virtual methods
        if cl.virtuals:
            self.virtuals(cl, declare)

        # --- regular methods
        for func in cl.funcs.values():
            if func.node and not (func.ident=='__init__' and func.inherited):
                self.visitFunction(func.node, cl, declare)

        if cl.has_copy and not 'copy' in cl.funcs:
            self.copy_method(cl, '__copy__', declare)
        if cl.has_deepcopy and not 'deepcopy' in cl.funcs:
            self.copy_method(cl, '__deepcopy__', declare)

        if getgx().extension_module:
            extmod.convert_methods(self, cl, declare)

        self.deindent()
        self.output('};\n')

    def class_cpp(self, node):
        declare = False
        cl = getmv().classes[node.name]

        if cl.virtuals:
            self.virtuals(cl, declare)

        if node in getgx().comments:
            self.do_comments(node)
        else:
            self.output('/**\nclass %s\n*/\n' % cl.ident)
        self.output('class_ *cl_'+cl.cpp_name+';\n')

        # --- method definitions
        for func in cl.funcs.values():
            if func.node and not (func.ident=='__init__' and func.inherited):
                self.visitFunction(func.node, cl, declare)
        if cl.has_copy and not 'copy' in cl.funcs:
            self.copy_method(cl, '__copy__', declare)
        if cl.has_deepcopy and not 'deepcopy' in cl.funcs:
            self.copy_method(cl, '__deepcopy__', declare)

        # --- class variable declarations
        if cl.parent.vars: # XXX merge with visitModule
            for var in cl.parent.vars.values():
                if var in getgx().merged_inh and getgx().merged_inh[var]:
                    self.start(nodetypestr(var, cl.parent)+cl.ident+'::'+self.cpp_name(var.name))
                    self.eol()
            print >>self.out

    def class_variables(self, cl):
        # --- class variables
        if cl.parent.vars:
            for var in cl.parent.vars.values():
                if var in getgx().merged_inh and getgx().merged_inh[var]:
                    self.output('static '+nodetypestr(var, cl.parent)+self.cpp_name(var.name)+';')
            print >>self.out

        # --- instance variables
        for var in cl.vars.values():
            if var.invisible: continue # var.name in cl.virtualvars: continue

            # var is masked by ancestor var
            vars = set()
            for ancestor in cl.ancestors():
                vars.update(ancestor.vars)
                #vars.update(ancestor.virtualvars)
            if var.name in vars:
                continue

            cppname = self.cpp_name(var.name)
            if var.masks_global():
                cppname = '_'+var.name

            # virtual
            if var.name in cl.virtualvars:
                ident = var.name
                subclasses = cl.virtualvars[ident]

                merged = set()
                for m in [getgx().merged_inh[subcl.vars[ident]] for subcl in subclasses if ident in subcl.vars and subcl.vars[ident] in getgx().merged_inh]: # XXX
                    merged.update(m)

                ts = self.padme(typestr(merged))
                if merged:
                    self.output(ts+cppname+';')

            # non-virtual
            elif var in getgx().merged_inh and getgx().merged_inh[var]:
                self.output(nodetypestr(var, cl)+cppname+';')

        if [v for v in cl.vars if not v.startswith('__')]:
            print >>self.out

    def copy_method(self, cl, name, declare): # XXX merge?
        header = nokeywords(cl.ident)+' *'
        if not declare:
            header += nokeywords(cl.ident)+'::'
        header += name+'('
        self.start(header)

        if name == '__deepcopy__':
            self.append('dict<void *, pyobj *> *memo')
        self.append(')')

        if not declare:
            print >>self.out, self.line+' {'
            self.indent()
            self.output(nokeywords(cl.ident)+' *c = new '+nokeywords(cl.ident)+'();')
            if name == '__deepcopy__':
                self.output('memo->__setitem__(this, c);')

            for var in cl.vars.values():
                if var in getgx().merged_inh and getgx().merged_inh[var]:
                #if var not in cl.funcs and not var.invisible:
                    if name == '__deepcopy__':
                        self.output('c->%s = __deepcopy(%s);' % (var.name, var.name))
                    else:
                        self.output('c->%s = %s;' % (var.name, var.name))
            self.output('return c;')

            self.deindent()
            self.output('}\n')
        else:
            self.eol()

    def padme(self, x):
        if not x.endswith('*'): return x+' '
        return x

    def virtuals(self, cl, declare):
        for ident, subclasses in cl.virtuals.items():
            if not subclasses: continue

            # --- merge arg/return types
            formals = []
            retexpr = False

            for subcl in subclasses:
                if ident not in subcl.funcs: continue

                func = subcl.funcs[ident]
                sig_types = []

                if func.returnexpr:
                    retexpr = True
                    if func.retnode.thing in self.mergeinh:
                        sig_types.append(self.mergeinh[func.retnode.thing]) # XXX mult returns; some targets with return some without..
                    else:
                        sig_types.append(set()) # XXX

                for name in func.formals[1:]:
                    var = func.vars[name]
                    sig_types.append(self.mergeinh[var])
                formals.append(sig_types)

            merged = []
            for z in zip(*formals):
                merge = set()
                for types in z: merge.update(types)
                merged.append(merge)

            formals = list(subclasses)[0].funcs[ident].formals[1:]
            ftypes = [self.padme(typestr(m)) for m in merged]

            # --- prepare for having to cast back arguments (virtual function call means multiple targets)
            for subcl in subclasses:
                subcl.funcs[ident].ftypes = ftypes

            # --- virtual function declaration
            if declare:
                self.start('virtual ')
                if retexpr:
                    self.append(ftypes[0])
                    ftypes = ftypes[1:]
                else:
                    self.append('void ')
                self.append(self.cpp_name(ident)+'(')

                self.append(', '.join([t+f for (t,f) in zip(ftypes, formals)]))

                if ident in cl.funcs and self.inhcpa(cl.funcs[ident]):
                    self.eol(')')
                else:
                    if retexpr and self.mergeinh[func.retnode.thing] == set([(defclass('bool_'), 0)]):
                        self.eol(') { return False; }') # XXX msvc needs return statement
                    else:
                        self.eol(') { return 0; }') # XXX msvc needs return statement

                if ident in cl.funcs: cl.funcs[ident].declared = True

    def inhcpa(self, func):
        return hmcpa(func) or (func in getgx().inheritance_relations and [1 for f in getgx().inheritance_relations[func] if hmcpa(f)])

    def visitSlice(self, node, func=None):
        if node.flags == 'OP_DELETE':
            self.start()
            self.visit(inode(node.expr).fakefunc, func)
            self.eol()
        else:
            self.visit(inode(node.expr).fakefunc, func)

    def visitLambda(self, node, parent=None):
        self.append(getmv().lambdaname[node])

    def subtypes(self, types, varname):
        subtypes = set()
        for t in types:
            if isinstance(t[0], class_):
                var = t[0].vars.get(varname)
                if var and (var,t[1],0) in getgx().cnode: # XXX yeah?
                    subtypes.update(getgx().cnode[var,t[1],0].types())
        return subtypes

    def bin_tuple(self, types):
        for t in types:
            if isinstance(t[0], class_) and t[0].ident == 'tuple2':
                var1 = t[0].vars.get('first')
                var2 = t[0].vars.get('second')
                if var1 and var2:
                    if (var1,t[1],0) in getgx().cnode and \
                       (var2,t[1],0) in getgx().cnode:
                            if getgx().cnode[var1,t[1],0].types() != \
                               getgx().cnode[var2,t[1],0].types():
                                return True
        return False

    def visit_child(self, child, varname, func, argtypes):
        type_child = self.subtypes(argtypes, varname)
        actualtypes = getgx().merged_inh[child]
        inttype = set([(defclass('int_'),0)])
        floattype = (defclass('float_'),0)
        double_cast = (actualtypes == inttype and floattype in type_child)
        if double_cast:
            self.append('(double)(')
        if child in getmv().tempcount: # XXX
            self.append(getmv().tempcount[child])
        elif isinstance(child, Dict): # XXX
            self.visitDict(child, func, argtypes=type_child)
        elif isinstance(child, Tuple): # XXX
            self.visitTuple(child, func, argtypes=type_child)
        elif isinstance(child, List): # XXX
            self.visitList(child, func, argtypes=type_child)
        else:
            self.visit(child, func)
        if double_cast:
            self.append(')')

    def instance_new(self, node, argtypes):
        if argtypes is None:
            argtypes = getgx().merged_inh[node]
        ts = typestr(argtypes)
        if ts.startswith('pyseq') or ts.startswith('pyiter'): # XXX
            argtypes = getgx().merged_inh[node]
        ts = typestr(argtypes)
        self.append('(new '+ts[:-2]+'(')
        return argtypes

    def visitDict(self, node, func=None, argtypes=None):
        argtypes = self.instance_new(node, argtypes)
        if node.items:
            self.append(str(len(node.items))+', ')
        ts_key = typestr(self.subtypes(argtypes, 'unit'))
        ts_value = typestr(self.subtypes(argtypes, 'value'))
        for (key, value) in node.items:
            self.visitm('(new tuple2<%s, %s>(2,' % (ts_key, ts_value), func)
            self.visit_child(key, 'unit', func, argtypes)
            self.append(',')
            self.visit_child(value, 'value', func, argtypes)
            self.append('))')
            if (key, value) != node.items[-1]:
                self.append(',')
        self.append('))')

    def visittuplelist(self, node, func=None, argtypes=None):
        if isinstance(func, class_): # XXX
            func=None
        argtypes = self.instance_new(node, argtypes)
        children = node.getChildNodes()
        if children:
            self.append(str(len(children))+',')
        if self.bin_tuple(argtypes):
            self.visit_child(children[0], 'first', func, argtypes)
            self.append(',')
            self.visit_child(children[1], 'second', func, argtypes)
        else:
            for child in children:
                self.visit_child(child, 'unit', func, argtypes)
                if child != children[-1]:
                    self.append(',')
        self.append('))')

    def visitTuple(self, node, func=None, argtypes=None):
        self.visittuplelist(node, func, argtypes)

    def visitList(self, node, func=None, argtypes=None):
        self.visittuplelist(node, func, argtypes)

    def visitAssert(self, node, func=None):
        self.start('ASSERT(')
        self.visitm(node.test, ', ', func)
        if len(node.getChildNodes()) > 1:
            self.visit(node.getChildNodes()[1], func)
        else:
            self.append('0')
        self.eol(')')

    def visitm(self, *args):
        if args and isinstance(args[-1], (function, class_)):
            func = args[-1]
        else:
            func = None

        for arg in args[:-1]:
            if not arg: return
            if isinstance(arg, str):
                self.append(arg)
            else:
                self.visit(arg, func)

    def visitRaise(self, node, func=None):
        cl = None # XXX sep func
        t = [t[0] for t in self.mergeinh[node.expr1]]
        if len(t) == 1:
            cl = t[0]

        self.start('throw (')
        # --- raise class [, constructor args]
        if isinstance(node.expr1, Name) and not lookupvar(node.expr1.name, func): # XXX lookupclass
            self.append('new %s(' % node.expr1.name)
            if node.expr2:
                if isinstance(node.expr2, Tuple) and node.expr2.nodes:
                    for n in node.expr2.nodes:
                        self.visit(n, func)
                        if n != node.expr2.nodes[-1]: self.append(', ') # XXX visitcomma(nodes)
                else:
                    self.visit(node.expr2, func)
            self.append(')')
        # --- raise instance
        elif isinstance(cl, class_) and cl.mv.module.ident == 'builtin' and not [a for a in cl.ancestors_upto(None) if a.ident == 'BaseException']:
            self.append('new Exception()')
        else:
            self.visit(node.expr1, func)
        self.eol(')')

    def visitTryExcept(self, node, func=None):
        # try
        self.start('try {')
        print >>self.out, self.line
        self.indent()
        if node.else_:
            self.output('%s = 0;' % getmv().tempcount[node.else_])
        self.visit(node.body, func)
        if node.else_:
            self.output('%s = 1;' % getmv().tempcount[node.else_])
        self.deindent()
        self.start('}')

        # except
        for handler in node.handlers:
            if isinstance(handler[0], Tuple):
                pairs = [(n, handler[1], handler[2]) for n in handler[0].nodes]
            else:
                pairs = [(handler[0], handler[1], handler[2])]

            for (h0, h1, h2) in pairs:
                if isinstance(h0, Name) and h0.name in ['int', 'float', 'str', 'class']:
                    continue # XXX lookupclass
                elif h0:
                    cl = lookupclass(h0, getmv())
                    if cl.mv.module.builtin and cl.ident in ['KeyboardInterrupt', 'FloatingPointError', 'OverflowError', 'ZeroDivisionError', 'SystemExit']:
                        error("system '%s' is not caught" % cl.ident, h0, warning=True, mv=getmv())
                    arg = namespaceclass(cl)+' *'
                else:
                    arg = 'Exception *'

                if h1:
                    arg += h1.name

                self.append(' catch (%s) {' % arg)
                print >>self.out, self.line

                self.indent()
                self.visit(h2, func)
                self.deindent()
                self.start('}')

        print >>self.out, self.line

        # else
        if node.else_:
            self.output('if(%s) { // else' % getmv().tempcount[node.else_])
            self.indent()
            self.visit(node.else_, func)
            self.deindent()
            self.output('}')

    def do_fastfor(self, node, qual, quals, iter, func, genexpr):
        if len(qual.list.args) == 3 and not const_literal(qual.list.args[2]): 
            for arg in qual.list.args: # XXX simplify
                if arg in getmv().tempcount:
                    self.start()
                    self.visitm(getmv().tempcount[arg], ' = ', arg, func)
                    self.eol()
        self.fastfor(qual, iter, func)
        self.forbody(node, quals, iter, func, False, genexpr)

    def fastfor(self, node, assname, func=None):
        # --- for i in range(..) -> for( i=l, u=expr; i < u; i++ ) ..
        ivar, evar = getmv().tempcount[node.assign], getmv().tempcount[node.list]
        self.start('FAST_FOR(%s,' % assname)

        if len(node.list.args) == 1:
            self.append('0,')
            if node.list.args[0] in getmv().tempcount: # XXX in visit?
                self.append(getmv().tempcount[node.list.args[0]])
            else:
                self.visit(node.list.args[0], func)
            self.append(',')
        else:
            if node.list.args[0] in getmv().tempcount: # XXX in visit?
                self.append(getmv().tempcount[node.list.args[0]])
            else:
                self.visit(node.list.args[0], func)
            self.append(',')
            if node.list.args[1] in getmv().tempcount: # XXX in visit?
                self.append(getmv().tempcount[node.list.args[1]])
            else:
                self.visit(node.list.args[1], func)
            self.append(',')

        if len(node.list.args) != 3:
            self.append('1')
        else:
            if node.list.args[2] in getmv().tempcount: # XXX in visit?
                self.append(getmv().tempcount[node.list.args[2]])
            else:
                self.visit(node.list.args[2], func)
        self.append(',%s,%s)' % (ivar[2:],evar[2:]))
        print >>self.out, self.line

    def fastenum(self, node):
        return is_enum(node) and self.only_classes(node.list.args[0], ('tuple', 'list'))

    def fastzip2(self, node):
        names = ('tuple', 'list')
        return is_zip2(node) and self.only_classes(node.list.args[0], names) and self.only_classes(node.list.args[1], names)

    def only_classes(self, node, names):
        classes = [defclass(name) for name in names]+[defclass('none')]
        return not [t for t in self.mergeinh[node] if t[0] not in classes]

    def visitFor(self, node, func=None):
        if isinstance(node.assign, AssName):
            assname = node.assign.name
        elif isinstance(node.assign, AssAttr):
            self.start('')
            self.visitAssAttr(node.assign, func)
            assname = self.line.strip() # XXX yuck
        else:
            assname = getmv().tempcount[node.assign]
        assname = self.cpp_name(assname)
        print >>self.out
        if node.else_:
            self.output('%s = 0;' % getmv().tempcount[node.else_])
        if fastfor(node):
            self.do_fastfor(node, node, None, assname, func, False)
        elif self.fastenum(node):
            self.do_fastenum(node, func, False)
            self.forbody(node, None, assname, func, True, False)
        elif self.fastzip2(node):
            self.do_fastzip2(node, func, False)
            self.forbody(node, None, assname, func, True, False)
        else:
            pref, tail = self.forin_preftail(node)
            self.start('FOR_IN%s(%s,' % (pref, assname))
            self.visit(node.list, func)
            print >>self.out, self.line+','+tail+')'
            self.forbody(node, None, assname, func, False, False)
        print >>self.out

    def do_fastzip2(self, node, func, genexpr):
        self.start('FOR_IN_ZIP(')
        left, right = node.assign.nodes
        self.do_fastzip2_one(left, func)
        self.do_fastzip2_one(right, func)
        self.visitm(node.list.args[0], ',', node.list.args[1], ',', func)
        tail1 = getmv().tempcount[(node,2)][2:]+','+getmv().tempcount[(node,3)][2:]+','
        tail2 = getmv().tempcount[(node.list)][2:]+','+getmv().tempcount[(node,4)][2:]
        print >>self.out, self.line+tail1+tail2+')'
        self.indent()
        if isinstance(left, (AssTuple, AssList)):
            self.tuple_assign(left, getmv().tempcount[left], func)
        if isinstance(right, (AssTuple, AssList)):
            self.tuple_assign(right, getmv().tempcount[right], func)

    def do_fastzip2_one(self, node, func):
        if isinstance(node, (AssTuple, AssList)):
            self.append(getmv().tempcount[node])
        else:
            self.visit(node, func)
        self.append(',')

    def do_fastenum(self, node, func, genexpr):
        self.start('FOR_IN_ENUM(')
        left, right = node.assign.nodes
        self.do_fastzip2_one(right, func)
        self.visit(node.list.args[0], func)
        tail = getmv().tempcount[(node,2)][2:]+','+getmv().tempcount[node.list][2:]
        print >>self.out, self.line+','+tail+')'
        self.indent()
        self.start()
        self.visitm(left, ' = '+getmv().tempcount[node.list], func)
        self.eol()
        if isinstance(right, (AssTuple, AssList)):
            self.tuple_assign(right, getmv().tempcount[right], func)

    def forin_preftail(self, node):
        pref = '_NEW'
        tail = getmv().tempcount[node][2:]+','+getmv().tempcount[node.list][2:]
        tail += ','+getmv().tempcount[(node,5)][2:]
        return pref, tail

    def forbody(self, node, quals, iter, func, skip, genexpr):
        if quals != None:
            self.listcompfor_body(node, quals, iter, func, False, genexpr)
            return

        if not skip:
            self.indent()
            if isinstance(node.assign, (AssTuple, AssList)):
                self.tuple_assign(node.assign, getmv().tempcount[node.assign], func)

        getgx().loopstack.append(node)
        self.visit(node.body, func)
        getgx().loopstack.pop()
        self.deindent()
        self.output('END_FOR')

        if node.else_:
            self.output('if (!%s) {' % getmv().tempcount[node.else_])
            self.indent()
            self.visit(node.else_, func)
            self.deindent()
            self.output('}')

    def func_pointers(self):
        for func in getmv().lambdas.values():
            argtypes = [nodetypestr(func.vars[formal], func).rstrip() for formal in func.formals]
            if func.largs != None:
                argtypes = argtypes[:func.largs]
            rettype = nodetypestr(func.retnode.thing,func)
            print >>self.out, 'typedef %s(*lambda%d)(' % (rettype, func.lambdanr) + ', '.join(argtypes)+');'
        print >>self.out

    # --- function/method header
    def func_header(self, func, declare, is_init=False):
        method = isinstance(func.parent, class_)
        if method:
            formals = [f for f in func.formals if f != 'self']
        else:
            formals = [f for f in func.formals]
        if func.largs != None:
            formals = formals[:func.largs]

        ident = func.ident
        self.start()

        # --- return expression
        header = ''
        if is_init:
            ident = nokeywords(func.parent.ident)
        elif func.ident in ['__hash__']:
            header += 'int ' # XXX __ss_int leads to problem with virtual parent
        elif func.returnexpr:
            header += nodetypestr(func.retnode.thing, func) # XXX mult
        else:
            header += 'void '
            ident = self.cpp_name(ident)

        ftypes = [nodetypestr(func.vars[f], func) for f in formals]

        # if arguments type too precise (e.g. virtually called) cast them back
        oldftypes = ftypes
        if func.ftypes:
            ftypes = func.ftypes[1:]

        # --- method header
        if method and not declare:
            header += nokeywords(func.parent.ident)+'::'

        if is_init:
            header += ident
        else:
            header += self.cpp_name(ident)

        # --- cast arguments if necessary (explained above)
        casts = []
        if func.ftypes:
            for i in range(min(len(oldftypes), len(ftypes))): # XXX this is 'cast on specialize'.. how about generalization?
                if oldftypes[i] != ftypes[i]:
                    casts.append(oldftypes[i]+formals[i]+' = ('+oldftypes[i]+')__'+formals[i]+';')
                    if not declare:
                        formals[i] = '__'+formals[i]

        formals2 = formals[:]
        for (i,f) in enumerate(formals2): # XXX
            formals2[i] = self.cpp_name(f)

        formaldecs = [o+f for (o,f) in zip(ftypes, formals2)]

        if declare and isinstance(func.parent, class_) and func.ident in func.parent.staticmethods:
            header = 'static '+header

        if is_init and not formaldecs:
            formaldecs = ['int __ss_init']

        if func.ident.startswith('__lambda'): # XXX
            header = 'static inline ' + header

        # --- output
        self.append(header+'('+', '.join(formaldecs)+')')
        if is_init:
            print >>self.out, self.line+' {'
        elif declare:
            self.eol()
        else:
            print >>self.out, self.line+' {'
            self.indent()

            if not declare and func.doc:
                self.do_comment(func.doc)

            for cast in casts:
                self.output(cast)
            self.deindent()

    def cpp_name(self, name, func=None): # XXX breakup and remove
        if ((self.module == getgx().main_module and name == 'init'+self.module.ident) or \
            name in [cl.ident for cl in getgx().allclasses] or \
            name+'_' in [cl.ident for cl in getgx().allclasses]):
            return '_'+name
        elif name in self.module.mv.funcs and func and isinstance(func.parent, class_) and name in func.parent.funcs:
            return '__'+func.mv.module.ident+'__::'+name
        return nokeywords(name)

    def visitFunction(self, node, parent=None, declare=False):
        # locate right func instance
        if parent and isinstance(parent, class_):
            func = parent.funcs[node.name]
        elif node.name in getmv().funcs:
            func = getmv().funcs[node.name]
        else:
            func = getmv().lambdas[node.name]
        if func.invisible or (func.inherited and not func.ident == '__init__'):
            return
        if declare and func.declared: # XXX
            return

        # check whether function is called at all (possibly via inheritance)
        if not self.inhcpa(func):
            if func.ident in ['__iadd__', '__isub__', '__imul__']:
                return
            if func.lambdanr is None and not repr(node.code).startswith("Stmt([Raise(CallFunc(Name('NotImplementedError')"):
                error(repr(func)+' not called!', node, warning=True, mv=getmv())
            if not (declare and func.parent and func.ident in func.parent.virtuals):
                return

        if func.isGenerator and not declare:
            self.generator_class(func)

        self.func_header(func, declare)
        if declare:
            return
        self.indent()

        if func.isGenerator:
            self.generator_body(func)
            return

        # --- local declarations
        pairs = []
        for (name, var) in func.vars.items():
            if not var.invisible and name not in func.formals:
                name = self.cpp_name(name)
                ts = nodetypestr(var, func)
                pairs.append((ts, name))
        self.output(self.indentation.join(self.group_declarations(pairs)))

        # --- function body
        for fake_unpack in func.expand_args.values():
            self.visit(fake_unpack, func)
        self.visit(node.code, func)
        if func.fakeret:
            self.visit(func.fakeret, func)

        # --- add Return(None) (sort of) if function doesn't already end with a Return
        if node.getChildNodes():
            lastnode = node.getChildNodes()[-1]
            if not func.ident == '__init__' and not func.fakeret and not isinstance(lastnode, Return) and not (isinstance(lastnode, Stmt) and isinstance(lastnode.nodes[-1], Return)): # XXX use Stmt in moduleVisitor
                if not [1 for t in self.mergeinh[func.retnode.thing] if isinstance(t[0], class_) and t[0].ident == 'bool_']:
                    self.output('return 0;')

        self.deindent()
        self.output('}\n')

    def generator_ident(self, func): # XXX merge?
        if func.parent:
            return func.parent.ident + '_' + func.ident
        return func.ident
    
    def generator_class(self, func):
        ident = self.generator_ident(func)
        self.output('class __gen_%s : public %s {' % (ident, nodetypestr(func.retnode.thing, func)[:-2]))
        self.output('public:')
        self.indent()
        pairs = [(nodetypestr(func.vars[f], func), self.cpp_name(f)) for f in func.vars]
        self.output(self.indentation.join(self.group_declarations(pairs)))
        self.output('int __last_yield;\n')

        args = []
        for f in func.formals:
            args.append(nodetypestr(func.vars[f], func)+self.cpp_name(f))
        self.output(('__gen_%s(' % ident) + ','.join(args)+') {')
        self.indent()
        for f in func.formals:
            self.output('this->%s = %s;' % (self.cpp_name(f),self.cpp_name(f)))
        self.output('__last_yield = -1;')
        self.deindent()
        self.output('}\n')

        self.output('%s __get_next() {' % nodetypestr(func.retnode.thing, func)[7:-3])
        self.indent()
        self.output('switch(__last_yield) {')
        self.indent()
        for (i,n) in enumerate(func.yieldNodes):
            self.output('case %d: goto __after_yield_%d;' % (i,i))
        self.output('default: break;')
        self.deindent()
        self.output('}')

        for child in func.node.code.getChildNodes():
            self.visit(child, func)
        self.output('__stop_iteration = true;')
        self.deindent()
        self.output('}\n')

        self.deindent()
        self.output('};\n')

    def generator_body(self, func):
        ident = self.generator_ident(func)
        if not (func.isGenerator and func.parent):
            formals = [self.cpp_name(f) for f in func.formals]
        else:
            formals = ['this'] + [self.cpp_name(f) for f in func.formals if f != 'self']
        self.output('return new __gen_%s(%s);\n' % (ident, ','.join(formals)))
        self.deindent()
        self.output('}\n')

    def visitYield(self, node, func):
        self.output('__last_yield = %d;' % func.yieldNodes.index(node))
        self.return_expr(node.value, func.yieldnode, func, yield_=True)
        self.output('__after_yield_%d:;' % func.yieldNodes.index(node))
        self.start()

    def visitNot(self, node, func=None):
        self.append('__NOT(')
        self.bool_test(node.expr, func)
        self.append(')')

    def visitBackquote(self, node, func=None):
        self.visitm('repr(', inode(node.expr).fakefunc.node.expr, ')', func)

    def zeropointernone(self, node):
        return [t for t in self.mergeinh[node] if t[0].ident == 'none']

    def visitIf(self, node, func=None):
        for test in node.tests:
            self.start()
            if test != node.tests[0]:
                self.append('else ')
            self.append('if (')
            self.bool_test(test[0], func)
            print >>self.out, self.line+') {'
            self.indent()
            self.visit(test[1], func)
            self.deindent()
            self.output('}')

        if node.else_:
            self.output('else {')
            self.indent()
            self.visit(node.else_, func)
            self.deindent()
            self.output('}')

    def visitIfExp(self, node, func=None):
        self.append('((')
        self.bool_test(node.test, func)
        self.visitm(')?(', node.then, '):(', node.else_, '))', func)

    def visitBreak(self, node, func=None):
        if getgx().loopstack[-1].else_ in getmv().tempcount:
            self.output('%s = 1;' % getmv().tempcount[getgx().loopstack[-1].else_])
        self.output('break;')

    def visitStmt(self, node, func=None):
        for b in node.nodes:
            if isinstance(b, Discard):
                if isinstance(b.expr, Const) and b.expr.value == None:
                    continue
                if isinstance(b.expr, Const) and type(b.expr.value) == str:
                    self.do_comment(b.expr.value)
                    continue
                self.start('')
                self.visit(b, func)
                self.eol()
            else:
                self.visit(b, func)

    def visitOr(self, node, func=None):
        self.visitandor(node, node.nodes, '__OR', 'or', func)

    def visitAnd(self, node, func=None):
        self.visitandor(node, node.nodes,  '__AND', 'and', func)

    def visitandor(self, node, nodes, op, mix, func=None):
        if node in getgx().bool_test_only:
            self.append('(')
            for n in nodes:
                self.bool_test(n, func)
                if n != node.nodes[-1]:
                    self.append(' '+mix+' ')
            self.append(')')
        else:
            child = nodes[0]
            if len(nodes) > 1:
                self.append(op+'(')
            cast = ''
            if assign_needs_cast(child, func, node, func):
                cast = '(('+nodetypestr(node, func)+')'
                self.append(cast)
            self.visit(child, func)
            if cast:
                self.append(')')
            if len(nodes) > 1:
                self.append(', ')
                self.visitandor(node, nodes[1:], op, mix, func)
                self.append(', '+getmv().tempcount[child][2:]+')')

    def visitCompare(self, node, func=None, wrapper=True):
        if not node in self.bool_wrapper:
            self.append('___bool(')
        self.done = set() # (tvar=fun())

        left = node.expr
        for op, right in node.ops:
            if op == '>': msg, short, pre = '__gt__', '>', None # XXX map = {}!
            elif op == '<': msg, short, pre = '__lt__', '<', None
            elif op == 'in': msg, short, pre = '__contains__', None, None
            elif op == 'not in': msg, short, pre = '__contains__', None, '!'
            elif op == '!=': msg, short, pre = '__ne__', '!=', None
            elif op == '==': msg, short, pre = '__eq__', '==', None
            elif op == 'is': msg, short, pre = None, '==', None
            elif op == 'is not': msg, short, pre = None, '!=', None
            elif op == '<=': msg, short, pre = '__le__', '<=', None
            elif op == '>=': msg, short, pre = '__ge__', '>=', None

            if msg == '__contains__':
                self.visitBinary(right, left, msg, short, func, pre)
            else:
                self.visitBinary(left, right, msg, short, func, pre)

            if right != node.ops[-1][1]:
                self.append('&&')
            left = right

        if not node in self.bool_wrapper:
            self.append(')')

    def visitAugAssign(self, node, func=None):
        if isinstance(node.node, Subscript):
            self.start()
            if set([t[0].ident for t in self.mergeinh[node.node.expr] if isinstance(t[0], class_)]) in [set(['dict']), set(['defaultdict'])] and node.op == '+=':
                self.visitm(node.node.expr, '->__addtoitem__(', inode(node).subs, ', ', node.expr, ')', func)
                self.eol()
                return

            self.visitm(inode(node).temp1+' = ', node.node.expr, func)
            self.eol()
            self.start()
            self.visitm(inode(node).temp2+' = ', inode(node).subs, func)
            self.eol()
        self.visit(inode(node).assignhop, func)

    def visitAdd(self, node, func=None):
        str_nodes = self.rec_string_addition(node)
        if str_nodes and len(str_nodes) > 2:
            self.append('__add_strs(%d, ' % len(str_nodes))
            for (i, node) in enumerate(str_nodes):
                self.visit(node, func)
                if i < len(str_nodes)-1:
                    self.append(', ')
            self.append(')')
        else:
            self.visitBinary(node.left, node.right, augmsg(node, 'add'), '+', func)

    def rec_string_addition(self, node):
        if isinstance(node, Add):
            l, r = self.rec_string_addition(node.left), self.rec_string_addition(node.right)
            if l and r:
                return l+r
        elif self.mergeinh[node] == set([(defclass('str_'),0)]):
            return [node]

    def visitBitand(self, node, func=None):
        self.visitBitop(node, augmsg(node, 'and'), '&', func)

    def visitBitor(self, node, func=None):
        self.visitBitop(node, augmsg(node, 'or'), '|', func)

    def visitBitxor(self, node, func=None):
        self.visitBitop(node, augmsg(node, 'xor'), '^', func)

    def visitBitop(self, node, msg, inline, func=None):
        self.visitBitpair(Bitpair(node.nodes, msg, inline), func)

    def visitBitpair(self, node, func=None):
        if len(node.nodes) == 1:
            self.visit(node.nodes[0], func)
        else:
            self.visitBinary(node.nodes[0], Bitpair(node.nodes[1:], node.msg, node.inline), node.msg, node.inline, func)

    def visitRightShift(self, node, func=None):
        self.visitBinary(node.left, node.right, augmsg(node, 'rshift'), '>>', func)

    def visitLeftShift(self, node, func=None):
        self.visitBinary(node.left, node.right, augmsg(node, 'lshift'), '<<', func)

    def visitMul(self, node, func=None):
        self.visitBinary(node.left, node.right, augmsg(node, 'mul'), '*', func)

    def visitDiv(self, node, func=None):
        self.visitBinary(node.left, node.right, augmsg(node, 'div'), '/', func)

    def visitInvert(self, node, func=None): # XXX visitUnarySub merge, template function __invert?
        if unboxable(self.mergeinh[node.expr]):
            self.visitm('~', node.expr, func)
        else:
            self.visitCallFunc(inode(node.expr).fakefunc, func)

    def visitFloorDiv(self, node, func=None):
        self.visitBinary(node.left, node.right, augmsg(node, 'floordiv'), '//', func)

    def visitPower(self, node, func=None):
        self.power(node.left, node.right, None, func)

    def power(self, left, right, mod, func=None):
        inttype = set([(defclass('int_'),0)]) # XXX merge
        if self.mergeinh[left] == inttype and self.mergeinh[right] == inttype:
            if not isinstance(right, Const):
                error("pow(int, int) returns int after compilation", left, warning=True, mv=getmv())
        if mod: 
            self.visitm('__power(', left, ', ', right, ', ', mod, ')', func)
        else: 
            self.visitm('__power(', left, ', ', right, ')', func)

    def visitSub(self, node, func=None):
        self.visitBinary(node.left, node.right, augmsg(node, 'sub'), '-', func)

    def par(self, node, thingy):
        if (isinstance(node, Const) and not isinstance(node.value, (int, float))) or not isinstance(node, (Name, Const)):
            return thingy
        return ''

    def visitBinary(self, left, right, middle, inline, func=None, prefix=''): # XXX cleanup please
        ltypes = self.mergeinh[left]
        origright = right
        if isinstance(right, Bitpair):
            right = right.nodes[0]
        rtypes = self.mergeinh[right]
        ul, ur = unboxable(ltypes), unboxable(rtypes)

        inttype = set([(defclass('int_'),0)]) # XXX new type?
        floattype = set([(defclass('float_'),0)]) # XXX new type?

        # --- inline mod/div
        if (floattype.intersection(ltypes) or inttype.intersection(ltypes)):
            if inline in ['%'] or (inline in ['/'] and not (floattype.intersection(ltypes) or floattype.intersection(rtypes))):
                if not defclass('complex') in [t[0] for t in rtypes]: # XXX
                    self.append({'%': '__mods', '/': '__divs'}[inline]+'(')
                    self.visit2(left, func)
                    self.append(', ')
                    self.visit2(origright, func)
                    self.append(')')
                    return

        # --- inline floordiv # XXX merge above?
        if (inline and ul and ur) and inline in ['//']:
            self.append({'//': '__floordiv'}[inline]+'(')
            self.visit2(left, func)
            self.append(',')
            self.visit2(right, func)
            self.append(')')
            return

        # --- inline other
        if inline and ((ul and ur) or not middle or (isinstance(left, Name) and left.name == 'None') or (isinstance(origright, Name) and origright.name == 'None')): # XXX not middle, cleanup?
            self.append('(')
            self.visit2(left, func)
            self.append(inline)
            self.visit2(origright, func)
            self.append(')')
            return

        # --- prefix '!'
        postfix = ''
        if prefix:
            self.append('('+prefix)
            postfix = ')'

        # --- comparison
        if middle in ['__eq__', '__ne__', '__gt__', '__ge__', '__lt__', '__le__']:
            self.append(middle[:-2]+'(')
            self.visit2(left, func)
            self.append(', ')
            if nodetypestr(left, func) != nodetypestr(origright, func):
                self.append('((%s)(' % nodetypestr(left, func))
                self.visit2(origright, func)
                self.append('))')
            else:
                self.visit2(origright, func)
            self.append(')'+postfix)
            return

        # --- 1 +- ..j
        if inline in ['+', '-'] and isinstance(origright, Const) and isinstance(origright.value, complex):
            if floattype.intersection(ltypes) or inttype.intersection(ltypes):
                self.append('(new complex(')
                self.visit2(left, func)
                self.append(', '+{'+':'', '-':'-'}[inline]+str(origright.value.imag)+'))')
                return

        # --- 'a.__mul__(b)': use template to call to b.__mul__(a), while maintaining evaluation order
        if inline in ['+', '*', '-', '/'] and ul and not ur:
            self.append('__'+{'+':'add', '*':'mul', '-':'sub', '/':'div'}[inline]+'2(')
            self.visit2(left, func)
            self.append(', ')
            self.visit2(origright, func)
            self.append(')'+postfix)
            return

        # --- default: left, connector, middle, right
        self.append(self.par(left, '('))
        self.visit2(left, func)
        self.append(self.par(left, ')'))
        if middle == '==':
            self.append('==(')
        else:
            self.append(self.connector(left, func)+middle+'(')
        self.refer(origright, func, visit2=True) # XXX bleh
        self.append(')'+postfix)

    def visit2(self, node, func): # XXX use temp vars in comparisons, e.g. (t1=fun())
        if node in getmv().tempcount:
            if node in self.done:
                self.append(getmv().tempcount[node])
            else:
                self.visitm('('+getmv().tempcount[node]+'=', node, ')', func)
                self.done.add(node)
        else:
            self.visit(node, func)

    def visitUnarySub(self, node, func=None):
        self.visitm('(', func)
        if unboxable(self.mergeinh[node.expr]):
            self.visitm('-', node.expr, func)
        else:
            self.visitCallFunc(inode(node.expr).fakefunc, func)
        self.visitm(')', func)

    def visitUnaryAdd(self, node, func=None):
        self.visitm('(', func)
        if unboxable(self.mergeinh[node.expr]):
            self.visitm('+', node.expr, func)
        else:
            self.visitCallFunc(inode(node.expr).fakefunc, func)
        self.visitm(')', func)

    def refer(self, node, func, visit2=False):
        if isinstance(node, str):
            var = lookupvar(node, func)
            return node

        if isinstance(node, Name) and not node.name in ['None','True','False']:
            var = lookupvar(node.name, func)
        if visit2:
            self.visit2(node, func)
        else:
            self.visit(node, func)

    def library_func(self, funcs, modname, clname, funcname):
        for func in funcs:
            if not func.mv.module.builtin or func.mv.module.ident != modname:
                continue
            if clname != None:
                if not func.parent or func.parent.ident != clname:
                    continue
            return func.ident == funcname

    def add_args_arg(self, node, funcs):
        ''' append argument that describes which formals are actually filled in '''
        if self.library_func(funcs, 'datetime', 'time', 'replace') or \
           self.library_func(funcs, 'datetime', 'datetime', 'replace'):

            formals = funcs[0].formals[1:] # skip self
            formal_pos = dict([(v,k) for k,v in enumerate(formals)])
            positions = []

            for i, arg in enumerate(node.args):
                if isinstance(arg, Keyword):
                    positions.append(formal_pos[arg.name])
                else:
                    positions.append(i)

            if positions:
                self.append(str(reduce(lambda a,b: a|b, [(1<<x) for x in positions]))+', ')
            else:
                self.append('0, ')

    def visitCallFunc(self, node, func=None):
        objexpr, ident, direct_call, method_call, constructor, parent_constr, anon_func = analyze_callfunc(node, merge=getgx().merged_inh)
        funcs = callfunc_targets(node, getgx().merged_inh)

        if self.library_func(funcs, 're', None, 'findall') or \
           self.library_func(funcs, 're', 're_object', 'findall'):
            error("'findall' does not work with groups (use 'finditer' instead)", node, warning=True, mv=getmv())
        if self.library_func(funcs, 'socket', 'socket', 'settimeout') or \
           self.library_func(funcs, 'socket', 'socket', 'gettimeout'):
            error("socket.set/gettimeout do not accept/return None", node, warning=True, mv=getmv())
        if self.library_func(funcs, 'builtin', None, 'map') and len(node.args) > 2:
            error("default fillvalue for 'map' becomes 0 for integers", node, warning=True, mv=getmv())
        if self.library_func(funcs, 'itertools', None, 'izip_longest'):
            error("default fillvalue for 'izip_longest' becomes 0 for integers", node, warning=True, mv=getmv())

        nrargs = len(node.args)
        if isinstance(func, function) and func.largs:
            nrargs = func.largs

        # --- target expression
        if node.node in self.mergeinh and [t for t in self.mergeinh[node.node] if isinstance(t[0], function)]: # anonymous function
            self.visitm(node.node, '(', func)

        elif constructor:
            self.append('(new '+nokeywords(nodetypestr(node, func)[:-2])+'(')
            if funcs and len(funcs[0].formals) == 1 and not funcs[0].mv.module.builtin:
                self.append('1') # don't call default constructor

        elif parent_constr:
            cl = lookupclass(node.node.expr, getmv())
            self.append(namespaceclass(cl)+'::'+node.node.attrname+'(')

        elif direct_call: # XXX no namespace (e.g., math.pow), check nr of args
            if ident == 'float' and node.args and self.mergeinh[node.args[0]] == set([(defclass('float_'), 0)]):
                self.visit(node.args[0], func)
                return
            if ident in ['abs', 'int', 'float', 'str', 'dict', 'tuple', 'list', 'type', 'cmp', 'sum', 'zip']:
                self.append('__'+ident+'(')
            elif ident in ['min', 'max', 'iter', 'round']:
                self.append('___'+ident+'(')
            elif ident == 'bool':
                self.bool_test(node.args[0], func, always_wrap=True)
                return
            elif ident == 'pow' and direct_call.mv.module.ident == 'builtin':
                if nrargs==3: third = node.args[2]
                else: third = None
                self.power(node.args[0], node.args[1], third, func)
                return
            elif ident == 'hash':
                self.append('hasher(') # XXX cleanup
            elif ident == '__print': # XXX
                self.append('print(')
            elif ident == 'isinstance' and isinstance(node.args[1], Name) and node.args[1].name in ['float','int']:
                error("'isinstance' cannot be used with ints or floats; assuming always true", node, warning=True, mv=getmv())
                self.append('1')
                return
            else:
                if ident in self.module.mv.ext_funcs: # XXX using as? :P
                     ident = self.module.mv.ext_funcs[ident].ident

                if isinstance(node.node, Name): # XXX ugly
                    self.append(self.cpp_name(ident, func))
                else:
                    self.visit(node.node)
                self.append('(')

        elif method_call:
            for cl, _ in self.mergeinh[objexpr]:
                if isinstance(cl, class_) and cl.ident != 'none' and ident not in cl.funcs:
                    conv = {'int_': 'int', 'float_': 'float', 'str_': 'str', 'class_': 'class', 'none': 'none'}
                    clname = conv.get(cl.ident, cl.ident)
                    error("class '%s' has no method '%s'" % (clname, ident), node, warning=True, mv=getmv())

            # tuple2.__getitem -> __getfirst__/__getsecond
            if ident == '__getitem__' and isinstance(node.args[0], Const) and node.args[0].value in (0,1) and self.only_classes(objexpr, ('tuple2',)):
                self.visit(node.node.expr, func)
                self.append('->%s()' % ['__getfirst__', '__getsecond__'][node.args[0].value])
                return

            if ident == '__call__':
                self.visitm(node.node, '->__call__(', func)
            else:
                self.visitm(node.node, '(', func)

        else:
            error("unbound identifier '"+ident+"'", node, mv=getmv())

        if not funcs:
            if constructor: self.append(')')
            self.append(')')
            return

        self.visit_callfunc_args(funcs, node, func)

        self.append(')')
        if constructor:
            self.append(')')

    def bool_test(self, node, func, always_wrap=False):
        wrapper = always_wrap or not self.only_classes(node, ('int_', 'bool_'))
        if node in getgx().bool_test_only:
            self.visit(node, func)
        elif wrapper:
            self.append('___bool(')
            self.visit(node, func)
            is_func = bool([1 for t in self.mergeinh[node] if isinstance(t[0], function)])
            self.append(('', '!=NULL')[is_func]+')') # XXX
        else:
            self.bool_wrapper[node] = True
            self.visit(node, func)

    def visit_callfunc_args(self, funcs, node, func):
        objexpr, ident, direct_call, method_call, constructor, parent_constr, anon_func = analyze_callfunc(node, merge=getgx().merged_inh)
        target = funcs[0] # XXX

        print_function = self.library_func(funcs, 'builtin', None, '__print')

        castnull = False # XXX
        if (self.library_func(funcs, 'random', None, 'seed') or \
            self.library_func(funcs, 'random', None, 'triangular') or \
            self.library_func(funcs, 'random', 'Random', 'seed') or \
            self.library_func(funcs, 'random', 'Random', 'triangular')):
            castnull = True
        for itertools_func in ['islice', 'izip_longest', 'permutations']:
            if self.library_func(funcs, 'itertools', None, itertools_func):
                castnull = True
                break

        for f in funcs:
            if len(f.formals) != len(target.formals):
                error('calling functions with different numbers of arguments', node, warning=True, mv=getmv())
                self.append(')')
                return

        if parent_constr and target.inherited_from: # XXX
            target = target.inherited_from

        pairs, rest = connect_actual_formal(node, target, parent_constr, merge=self.mergeinh)
        if isinstance(func, function) and func.lambdawrapper:
            rest = func.largs

        if target.node.varargs:
            self.append('%d' % rest)
            if rest or pairs:
                self.append(', ')

        double = False
        if ident in ['min', 'max']:
            for arg in node.args:
                if arg in self.mergeinh and (defclass('float_'),0) in self.mergeinh[arg]:
                    double = True

        self.add_args_arg(node, funcs)

        if isinstance(func, function) and func.largs != None:
            kw = [p for p in pairs if p[1].name.startswith('__kw_')]
            nonkw = [p for p in pairs if not p[1].name.startswith('__kw_')]
            pairs = kw+nonkw[:func.largs]

        for (arg, formal) in pairs:
            cast = False
            builtin_cast = self.cast_to_builtin(arg, func, formal, target, method_call, objexpr)

            if double and self.mergeinh[arg] == set([(defclass('int_'),0)]):
                cast = True
                self.append('((double)(')
            elif builtin_cast:
                cast = True
                self.append('(('+builtin_cast+')(')
            elif not target.mv.module.builtin and assign_needs_cast(arg, func, formal, target): # XXX builtin (dict.fromkeys?)
                cast = True
                self.append('(('+nodetypestr(formal, target)+')(')
            elif castnull and isinstance(arg, Name) and arg.name == 'None':
                cast = True
                self.append('((void *)(')

            if print_function and not formal.name.startswith('__kw_'):
                types = [t[0].ident for t in self.mergeinh[arg]]
                if 'float_' in types or 'int_' in types or 'bool_' in types:
                    cast = True
                    self.append('___box((')

            if arg in target.mv.defaults:
                if self.mergeinh[arg] == set([(defclass('none'),0)]):
                    self.append('NULL')
                elif target.mv.module == getmv().module:
                    self.append('default_%d' % (target.mv.defaults[arg]))
                else:
                    self.append('%s::default_%d' % (target.mv.module.full_path(), target.mv.defaults[arg]))

            elif arg in self.consts:
                self.append(self.consts[arg])
            else:
                if constructor and ident in ['set', 'frozenset'] and nodetypestr(arg, func) in ['list<void *> *', 'tuple<void *> *', 'pyiter<void *> *', 'pyseq<void *> *', 'pyset<void *>']: # XXX to needs_cast
                    pass # XXX assign_needs_cast
                else:
                    self.refer(arg, func)

            if cast:
                self.append('))')
            if (arg, formal) != pairs[-1]:
                self.append(', ')

        if constructor and ident == 'frozenset':
            if pairs: self.append(',')
            self.append('1')

    def cast_to_builtin(self, arg, func, formal, target, method_call, objexpr):
        # type inference cannot deduce all necessary casts to builtin formals
        vars = {'u': 'unit', 'v': 'value', 'o': None}
        if target.mv.module.builtin and method_call and formal.name in vars and target.parent.ident in ('list', 'dict', 'set'):
            to_ts = nodetypestr(objexpr, func, var=vars[formal.name])
            if nodetypestr(arg, func) != to_ts:
                return to_ts

    def cast_to_builtin2(self, arg, func, objexpr, msg, formal_nr):
        # shortcut for outside of visitCallFunc XXX merge with visitCallFunc?
        cls = [t[0] for t in self.mergeinh[objexpr] if isinstance(t[0], class_)]
        if cls:
            cl = cls.pop()
            if msg in cl.funcs:
                target = cl.funcs[msg]
                if formal_nr < len(target.formals):
                    formal = target.vars[target.formals[formal_nr]]
                    return self.cast_to_builtin(arg, func, formal, target, True, objexpr)

    def visitReturn(self, node, func=None):
        if func.isGenerator:
            self.output('__stop_iteration = true;')
            self.output('return 0;')
            return
        self.return_expr(node.value, func.retnode, func)

    def return_expr(self, expr, retnode, func, yield_=False):
        if yield_:
            self.start('__result = ')
        else:
            self.start('return ')
        cast = assign_needs_cast(expr, func, retnode.thing, func)
        if cast:
            self.append('(('+nodetypestr(retnode.thing, func)+')(')

        elif isinstance(expr, Name) and expr.name == 'self': # XXX integrate with assign_needs_cast!? # XXX self?
            lcp = lowest_common_parents(polymorphic_t(self.mergeinh[retnode.thing])) # XXX simplify
            if lcp:
                cl = lcp[0] # XXX simplify
                if not (cl == func.parent or cl in func.parent.ancestors()):
                    self.append('('+cl.ident+' *)')

        self.visit(expr, func)
        if cast: self.append('))')
        self.eol()
        if yield_:
            self.output('return __result;')

    def tuple_assign(self, lvalue, rvalue, func):
        temp = getmv().tempcount[lvalue]
        if isinstance(lvalue, tuple): nodes = lvalue
        else: nodes = lvalue.nodes

        # --- nested unpacking assignment: a, (b,c) = d, e
        if [item for item in nodes if not isinstance(item, AssName)]:
            self.start(temp+' = ')
            if isinstance(rvalue, str):
                self.append(rvalue)
            else:
                self.visit(rvalue, func)
            self.eol()

            for i, item in enumerate(nodes):
                selector = self.get_selector(temp, item, i)
                if isinstance(item, AssName):
                    self.output('%s = %s;' % (item.name, selector))
                elif isinstance(item, (AssTuple, AssList)): # recursion
                    self.tuple_assign(item, selector, func)
                elif isinstance(item, Subscript):
                    self.assign_pair(item, selector, func)
                elif isinstance(item, AssAttr):
                    self.assign_pair(item, selector, func)
                    self.eol(' = ' + selector)

        # --- non-nested unpacking assignment: a,b,c = d
        else:
            self.start()
            self.visitm(temp, ' = ', rvalue, func)
            self.eol()
            for i, item in enumerate(lvalue.nodes):
                self.start()
                self.visitm(item, ' = ', self.get_selector(temp, item, i), func)
                self.eol()

    def one_class(self, node, names):
        for clname in names:
            if self.only_classes(node, (clname,)):
                return True
        return False

    def get_selector(self, temp, item, i):
        rvalue_node = getgx().item_rvalue[item]
        sel = '__getitem__(%d)' % i
        if i < 2 and self.only_classes(rvalue_node, ('tuple2',)):
            sel = ['__getfirst__()', '__getsecond__()'][i]
        elif self.one_class(rvalue_node, ('list', 'str_', 'tuple')):
            sel = '__getfast__(%d)' % i
        return '%s->%s' % (temp, sel)

    def subs_assign(self, lvalue, func):
        if len(lvalue.subs) > 1:
            subs = inode(lvalue.expr).faketuple
        else:
            subs = lvalue.subs[0]
        self.visitm(lvalue.expr, self.connector(lvalue.expr, func), '__setitem__(', subs, ', ', func)

    def visitAssign(self, node, func=None):
        #temp vars
        if len(node.nodes) > 1 or isinstance(node.expr, Tuple):
            if isinstance(node.expr, Tuple):
                if [n for n in node.nodes if isinstance(n, AssTuple)]: # XXX a,b=d[i,j]=..?
                    for child in node.expr.nodes:
                        if not (child,0,0) in getgx().cnode: # (a,b) = (1,2): (1,2) never visited
                            continue
                        if not isinstance(child, Const) and not (isinstance(child, Name) and child.name == 'None'):
                            self.start(getmv().tempcount[child]+' = ')
                            self.visit(child, func)
                            self.eol()
            elif not isinstance(node.expr, Const) and not (isinstance(node.expr, Name) and node.expr.name == 'None'):
                self.start(getmv().tempcount[node.expr]+' = ')
                self.visit(node.expr, func)
                self.eol()

        # a = (b,c) = .. = expr
        right = node.expr
        for left in node.nodes:
            pairs = assign_rec(left, node.expr)
            tempvars = len(pairs) > 1

            for (lvalue, rvalue) in pairs:
                cast = None
                self.start('') # XXX remove?

                # expr[expr] = expr
                if isinstance(lvalue, Subscript) and not isinstance(lvalue.subs[0], Sliceobj):
                    self.assign_pair(lvalue, rvalue, func)
                    continue

                # expr.attr = expr
                elif isinstance(lvalue, AssAttr):
                    lcp = lowest_common_parents(polymorphic_t(self.mergeinh[lvalue.expr]))
                    # property
                    if len(lcp) == 1 and isinstance(lcp[0], class_) and lvalue.attrname in lcp[0].properties:
                        self.visitm(lvalue.expr, '->'+self.cpp_name(lcp[0].properties[lvalue.attrname][1])+'(', rvalue, ')', func)
                        self.eol()
                        continue
                    cast = self.var_assign_needs_cast(lvalue, rvalue, func)
                    self.assign_pair(lvalue, rvalue, func)
                    self.append(' = ')

                # name = expr
                elif isinstance(lvalue, AssName):
                    cast = self.var_assign_needs_cast(lvalue, rvalue, func)
                    self.visit(lvalue, func)
                    self.append(' = ')

                # (a,(b,c), ..) = expr
                elif isinstance(lvalue, (AssTuple, AssList)):
                    self.tuple_assign(lvalue, rvalue, func)
                    continue

                # expr[a:b] = expr
                elif isinstance(lvalue, Slice):
                    if isinstance(rvalue, Slice) and lvalue.upper == rvalue.upper == None and lvalue.lower == rvalue.lower == None:
                        self.visitm(lvalue.expr, self.connector(lvalue.expr, func), 'units = ', rvalue.expr, self.connector(rvalue.expr, func), 'units', func)
                    else:
                        fakefunc = inode(lvalue.expr).fakefunc
                        self.visitm('(', fakefunc.node.expr, ')->__setslice__(', fakefunc.args[0], ',', fakefunc.args[1], ',', fakefunc.args[2], ',', fakefunc.args[3], ',', func)
                        cast = self.var_assign_needs_cast(lvalue.expr, rvalue, func)
                        if cast: self.visitm('(('+cast+')', fakefunc.args[4], '))', func)
                        else: self.visitm(fakefunc.args[4], ')', func)
                    self.eol()
                    continue

                # expr[a:b:c] = expr
                elif isinstance(lvalue, Subscript) and isinstance(lvalue.subs[0], Sliceobj):
                    self.visit(inode(lvalue.expr).fakefunc, func)
                    self.eol()
                    continue

                # rvalue, optionally with cast
                if cast:
                    self.append('(('+cast+')(')
                if rvalue in getmv().tempcount:
                    self.append(getmv().tempcount[rvalue])
                else:
                    self.visit(rvalue, func)
                if cast: self.append('))')

                self.eol()

    def var_assign_needs_cast(self, expr, rvalue, func):
        cast = None
        if isinstance(expr, AssName):
            var = lookupvar(expr.name, func)
            if assign_needs_cast(rvalue, func, var, func):
                cast = nodetypestr(var, func)
        elif isinstance(expr, AssAttr):
            lcp = lowest_common_parents(polymorphic_t(self.mergeinh[expr.expr]))
            if len(lcp) == 1 and isinstance(lcp[0], class_):
                var = lookupvar(expr.attrname, lcp[0])
                if assign_needs_cast(rvalue, func, var, lcp[0]):
                    cast = nodetypestr(var, lcp[0])
        else:
            if assign_needs_cast(rvalue, func, expr, func):
                cast = nodetypestr(expr, func)
        return cast

    def assign_pair(self, lvalue, rvalue, func):
        self.start('')

        # expr[expr] = expr
        if isinstance(lvalue, Subscript) and not isinstance(lvalue.subs[0], Sliceobj):
            self.subs_assign(lvalue, func)
            if isinstance(rvalue, str):
                self.append(rvalue)
            elif rvalue in getmv().tempcount:
                self.append(getmv().tempcount[rvalue])
            else:
                cast = self.cast_to_builtin2(rvalue, func, lvalue.expr, '__setitem__', 2)
                if cast: self.append('((%s)' % cast)
                self.visit(rvalue, func)
                if cast: self.append(')')
            self.append(')')
            self.eol()

        # expr.x = expr
        elif isinstance(lvalue, AssAttr):
            self.visitAssAttr(lvalue, func)

    def do_lambdas(self, declare):
        for l in getmv().lambdas.values():
            if l.ident not in getmv().funcs:
                self.visitFunction(l.node, declare=declare)

    def do_listcomps(self, declare):
        for (listcomp, lcfunc, func) in getmv().listcomps: # XXX cleanup
            if lcfunc.mv.module.builtin:
                continue

            parent = func
            while isinstance(parent, function) and parent.listcomp:
                parent = parent.parent

            if isinstance(parent, function):
                if not self.inhcpa(parent) or parent.inherited:
                    continue

            genexpr = listcomp in getgx().genexp_to_lc.values()
            if declare:
                self.listcomp_head(listcomp, True, genexpr)
            elif genexpr:
                self.genexpr_class(listcomp, declare)
            else:
                self.listcomp_func(listcomp)

    def listcomp_head(self, node, declare, genexpr):
        lcfunc, func = self.listcomps[node]
        args = [a+b for a,b in self.lc_args(lcfunc, func)]
        ts = nodetypestr(node, lcfunc)
        if not ts.endswith('*'): ts += ' '
        if genexpr:
            self.genexpr_class(node, declare)
        else:
            self.output('static inline '+ts+lcfunc.ident+'('+', '.join(args)+')'+[' {', ';'][declare])

    def lc_args(self, lcfunc, func):
        args = []
        for name in lcfunc.misses:
            if lookupvar(name, func).parent:
                args.append((nodetypestr(lookupvar(name, lcfunc), lcfunc), self.cpp_name(name)))
        return args

    def listcomp_func(self, node):
        lcfunc, func = self.listcomps[node]
        self.listcomp_head(node, False, False)
        self.indent()
        self.lc_locals(lcfunc)
        self.output(nodetypestr(node, lcfunc)+'__ss_result = new '+nodetypestr(node, lcfunc)[:-2]+'();\n')
        self.listcomp_rec(node, node.quals, lcfunc, False)
        self.output('return __ss_result;')
        self.deindent();
        self.output('}\n')

    def genexpr_class(self, node, declare):
        lcfunc, func = self.listcomps[node]
        args = self.lc_args(lcfunc, func)
        func1 = lcfunc.ident+'('+', '.join([a+b for a,b in args])+')'
        func2 = nodetypestr(node, lcfunc)[7:-3]
        if declare:
            ts = nodetypestr(node, lcfunc)
            if not ts.endswith('*'): ts += ' '
            self.output('class '+lcfunc.ident+' : public '+ts[:-2]+' {')
            self.output('public:')
            self.indent()
            self.lc_locals(lcfunc)
            for a,b in args:
                self.output(a+b+';');
            self.output('int __last_yield;\n')
            self.output(func1+';')
            self.output(func2+' __get_next();')
            self.deindent();
            self.output('};\n')
        else:
            self.output(lcfunc.ident+'::'+func1+' {')
            for a,b in args:
                self.output('    this->%s = %s;' % (b,b))
            self.output('    __last_yield = -1;')
            self.output('}\n')
            self.output(func2+' '+lcfunc.ident+'::__get_next() {')
            self.indent();
            self.output('if(!__last_yield) goto __after_yield_0;')
            self.output('__last_yield = 0;\n')
            self.listcomp_rec(node, node.quals, lcfunc, True)
            self.output('__stop_iteration = true;')
            self.deindent();
            self.output('}\n')

    def lc_locals(self, lcfunc):
        decl = {}
        for (name,var) in lcfunc.vars.items(): # XXX merge with visitFunction
            name = self.cpp_name(name)
            decl.setdefault(nodetypestr(var, lcfunc), []).append(name)
        for ts, names in decl.items():
            if ts.endswith('*'):
                self.output(ts+', *'.join(names)+';')
            else:
                self.output(ts+', '.join(names)+';')

    # --- nested for loops: loop headers, if statements
    def listcomp_rec(self, node, quals, lcfunc, genexpr):
        if not quals:
            if genexpr:
                self.start('__result = ')
                self.visit(node.expr, lcfunc)
                self.eol()
                self.output('return __result;')
                self.start('__after_yield_0:')
            elif len(node.quals) == 1 and not fastfor(node.quals[0]) and not self.fastenum(node.quals[0]) and not self.fastzip2(node.quals[0]) and not node.quals[0].ifs and self.one_class(node.quals[0].list, ('tuple', 'list', 'str_', 'dict','set')):
                self.start('__ss_result->units['+getmv().tempcount[node.quals[0].list]+'] = ')
                self.visit(node.expr, lcfunc)
            else:
                self.start('__ss_result->append(')
                self.visit(node.expr, lcfunc)
                self.append(')')
            self.eol()
            return

        qual = quals[0]

        # iter var
        if isinstance(qual.assign, AssName):
            var = lookupvar(qual.assign.name, lcfunc)
        else:
            var = lookupvar(getmv().tempcount[qual.assign], lcfunc)
        iter = self.cpp_name(var.name)

        if fastfor(qual):
            self.do_fastfor(node, qual, quals, iter, lcfunc, genexpr)
        elif self.fastenum(qual):
            self.do_fastenum(qual, lcfunc, genexpr)
            self.listcompfor_body(node, quals, iter, lcfunc, True, genexpr)
        elif self.fastzip2(qual):
            self.do_fastzip2(qual, lcfunc, genexpr)
            self.listcompfor_body(node, quals, iter, lcfunc, True, genexpr)
        else:
            if not isinstance(qual.list, Name):
                itervar = getmv().tempcount[qual]
                self.start('')
                self.visitm(itervar, ' = ', qual.list, lcfunc)
                self.eol()
            else:
                itervar = self.cpp_name(qual.list.name)

            pref, tail = self.forin_preftail(qual)

            if len(node.quals) == 1 and not qual.ifs and not genexpr:
                if self.one_class(qual.list, ('list', 'tuple', 'str_', 'dict', 'set')):
                    self.output('__ss_result->resize(len('+itervar+'));')

            self.start('FOR_IN'+pref+'('+iter+','+itervar+','+tail)
            print >>self.out, self.line+')'
            self.listcompfor_body(node, quals, iter, lcfunc, False, genexpr)

    def listcompfor_body(self, node, quals, iter, lcfunc, skip, genexpr):
        qual = quals[0]

        if not skip:
            self.indent()
            if isinstance(qual.assign, (AssTuple, AssList)):
                self.tuple_assign(qual.assign, iter, lcfunc)

        # if statements
        if qual.ifs:
            self.start('if (')
            self.indent()
            for cond in qual.ifs:
                self.bool_test(cond.test, lcfunc)
                if cond != qual.ifs[-1]:
                    self.append(' && ')
            self.append(') {')
            print >>self.out, self.line

        # recurse
        self.listcomp_rec(node, quals[1:], lcfunc, genexpr)

        # --- nested for loops: loop tails
        if qual.ifs:
            self.deindent();
            self.output('}')
        self.deindent();
        self.output('END_FOR\n')

    def visitGenExpr(self, node, func=None):
        self.visit(getgx().genexp_to_lc[node], func)

    def visitListComp(self, node, func=None):
        lcfunc, _ = self.listcomps[node]
        args = []
        temp = self.line

        for name in lcfunc.misses:
            var = lookupvar(name, func)
            if var.parent:
                if name == 'self' and not func.listcomp: # XXX parent?
                    args.append('this')
                else:
                    args.append(self.cpp_name(name))

        self.line = temp
        if node in getgx().genexp_to_lc.values():
            self.append('new ')
        self.append(lcfunc.ident+'('+', '.join(args)+')')

    def visitSubscript(self, node, func=None):
        if node.flags == 'OP_DELETE':
            self.start()
            if isinstance(node.subs[0], Sliceobj):
                self.visitCallFunc(inode(node.expr).fakefunc, func)
            else:
                self.visitCallFunc(inode(node.expr).fakefunc, func)
            self.eol()
        else:
            self.visitCallFunc(inode(node.expr).fakefunc, func)

    def visitMod(self, node, func=None):
        # --- non-str % ..
        if [t for t in getgx().merged_inh[node.left] if t[0].ident != 'str_']:
            self.visitBinary(node.left, node.right, '__mod__', '%', func)
            return

        # --- str % non-constant dict/tuple
        if not isinstance(node.right, (Tuple, Dict)) and node.right in getgx().merged_inh: # XXX
            if [t for t in getgx().merged_inh[node.right] if t[0].ident == 'dict']:
                self.visitm('__moddict(', node.left, ', ', node.right, ')', func)
                return
            elif [t for t in getgx().merged_inh[node.right] if t[0].ident in ['tuple', 'tuple2']]:
                self.visitm('__modtuple(', node.left, ', ', node.right, ')', func)
                return

        # --- str % constant-dict:
        if isinstance(node.right, Dict): # XXX geen str keys
            self.visitm('__modcd(', node.left, ', ', 'new list<str *>(%d, ' % len(node.right.items), func)
            self.append(', '.join([('new str("%s")' % key.value) for key, value in node.right.items]))
            self.append(')')
            nodes = [value for (key,value) in node.right.items]
        else:
            self.visitm('__modct(', node.left, func)
            # --- str % constant-tuple
            if isinstance(node.right, Tuple):
                nodes = node.right.nodes

            # --- str % non-tuple/non-dict
            else:
                nodes = [node.right]
            self.append(', %d' % len(nodes))

        # --- visit nodes, boxing scalars
        for n in nodes:
            if [clname for clname in ('float_', 'int_', 'bool_') if (defclass(clname), 0) in self.mergeinh[n]]:
                self.visitm(', ___box(', n, ')', func)
            else:
                self.visitm(', ', n, func)
        self.append(')')

    def visitPrintnl(self, node, func=None):
        self.visitPrint(node, func, print_space=False)

    def visitPrint(self, node, func=None, print_space=True):
        self.start('print2(')
        if node.dest:
            self.visitm(node.dest, ', ', func)
        else:
            self.append('NULL,')
        if print_space: 
            self.append('1,')
        else: 
            self.append('0,')
        self.append(str(len(node.nodes)))
        for n in node.nodes:
            types = [t[0].ident for t in self.mergeinh[n]]
            if 'float_' in types or 'int_' in types or 'bool_' in types:
                self.visitm(', ___box(', n, ')', func)
            else:
                self.visitm(', ', n, func)
        self.eol(')')

    def visitGetattr(self, node, func=None):
        cl, module = lookup_class_module(node.expr, inode(node).mv, func)

        # module.attr
        if module:
            self.append(module.full_path()+'::')

        # class.attr: staticmethod
        elif cl and node.attrname in cl.staticmethods:
            ident = cl.ident
            if cl.ident in ['dict', 'defaultdict']: # own namespace because of template vars
                self.append('__'+cl.ident+'__::')
            elif isinstance(node.expr, Getattr):
                submod = lookupmodule(node.expr.expr, inode(node).mv)
                self.append(submod.full_path()+'::'+ident+'::')
            else:
                self.append(ident+'::')

        # class.attr
        elif cl: # XXX merge above?
            ident = cl.ident
            if isinstance(node.expr, Getattr):
                submod = lookupmodule(node.expr.expr, inode(node).mv)
                self.append(submod.full_path()+'::'+cl.cpp_name+'::')
            else:
                self.append(ident+'::')

        # obj.attr
        else:
            for t in self.mergeinh[node.expr]:
                if isinstance(t[0], class_) and node.attrname in t[0].parent.vars:
                    error("class attribute '"+node.attrname+"' accessed without using class name", node, warning=True, mv=getmv())
                    break

            if not isinstance(node.expr, (Name)):
                self.append('(')
            if isinstance(node.expr, Name) and not lookupvar(node.expr.name, func): # XXX XXX
                self.append(node.expr.name)
            else:
                self.visit(node.expr, func)
            if not isinstance(node.expr, (Name)):
                self.append(')')

            self.append(self.connector(node.expr, func))

        ident = node.attrname

        # property
        lcp = lowest_common_parents(polymorphic_t(self.mergeinh[node.expr]))
        if len(lcp) == 1 and isinstance(lcp[0], class_) and node.attrname in lcp[0].properties:
            self.append(self.cpp_name(lcp[0].properties[node.attrname][0])+'()')
            return

        # getfast
        if ident == '__getitem__' and self.one_class(node.expr, ('list', 'str_', 'tuple')):
            ident = '__getfast__'

        self.append(self.attr_var_ref(node, ident))

    def attr_var_ref(self, node, ident): # XXX cpp_name(node)?
        var = lookupvariable(node, self)
        if var and var.masks_global():
            return '_'+ident
        return self.cpp_name(ident)

    def visitAssAttr(self, node, func=None): # XXX merge with visitGetattr
        if node.flags == 'OP_DELETE':
            return

        cl, module = lookup_class_module(node.expr, inode(node).mv, func)

        # module.attr
        if module:
            self.append(module.full_path()+'::')

        # class.attr
        elif cl:
            if isinstance(node.expr, Getattr):
                submod = lookupmodule(node.expr.expr, inode(node).mv)
                self.append(submod.full_path()+'::'+cl.cpp_name+'::')
            else:
                self.append(cl.ident+'::')

        # obj.attr
        else:
            if isinstance(node.expr, Name) and not lookupvar(node.expr.name, func): # XXX
                self.append(node.expr.name)
            else:
                self.visit(node.expr, func)
            self.append(self.connector(node.expr, func)) # XXX '->'

        self.append(self.attr_var_ref(node, node.attrname))

    def visitAssName(self, node, func=None):
        self.append(self.cpp_name(node.name))

    def visitName(self, node, func=None, add_cl=True):
        map = {'True': 'True', 'False': 'False', 'self': 'this'}
        if node in getmv().lwrapper:
            self.append(getmv().lwrapper[node])
        elif node.name == 'None':
            self.append('NULL')
        elif node.name == 'self' and \
            ((not func or func.listcomp or not isinstance(func.parent, class_)) or \
             (func and func.parent and func.isGenerator)): # XXX lookupvar?
            self.append('self')
        elif node.name in map:
            self.append(map[node.name])

        else: # XXX clean up
            if not self.mergeinh[node] and not inode(node).parent in getgx().inheritance_relations:
                error("variable '"+node.name+"' has no type", node, warning=True, mv=getmv())
                self.append(node.name)
            elif singletype(node, module):
                self.append('__'+singletype(node, module).ident+'__')
            else:
                if (defclass('class_'),0) in self.mergeinh[node]:
                    self.append(namespaceclass(lookupclass(node, getmv()), add_cl='cl_'))
                elif add_cl and [t for t in self.mergeinh[node] if isinstance(t[0], static_class)]:
                    self.append(namespaceclass(lookupclass(node, getmv()), add_cl='cl_'))
                else:
                    if isinstance(func, class_) and node.name in func.parent.vars: # XXX
                        self.append(func.ident+'::')
                    self.append(self.cpp_name(node.name))

    def expandspecialchars(self, value):
        value = list(value)
        replace = dict(['\\\\', '\nn', '\tt', '\rr', '\ff', '\bb', '\vv', '""'])

        for i in range(len(value)):
            if value[i] in replace:
                value[i] = '\\'+replace[value[i]]
            elif value[i] not in string.printable:
                value[i] = '\\'+oct(ord(value[i])).zfill(4)[1:]

        return ''.join(value)

    def visitConst(self, node, func=None):
        if not self.filling_consts and isinstance(node.value, str):
            self.append(self.get_constant(node))
            return

        if node.value == None:
            self.append('NULL')
            return

        t = list(inode(node).types())[0]

        if t[0].ident == 'int_':
            self.append(str(node.value))
            if getgx().longlong:
                self.append('LL')
        elif t[0].ident == 'float_':
            if str(node.value) in ['inf', '1.#INF', 'Infinity']: self.append('INFINITY')
            elif str(node.value) in ['-inf', '-1.#INF', 'Infinity']: self.append('-INFINITY')
            else: self.append(str(node.value))
        elif t[0].ident == 'str_':
            self.append('new str("%s"' % self.expandspecialchars(node.value))
            if '\0' in node.value: # '\0' delimiter in C
                self.append(', %d' % len(node.value))
            self.append(')')
        elif t[0].ident == 'complex':
            self.append('new complex(%s, %s)' % (node.value.real, node.value.imag))
        else:
            self.append('new %s(%s)' % (t[0].ident, node.value))

# --- helper functions

def singletype(node, type):
    types = [t[0] for t in inode(node).types()]
    if len(types) == 1 and isinstance(types[0], type):
        return types[0]

def singletype2(types, type):
    ltypes = list(types)
    if len(types) == 1 and isinstance(ltypes[0][0], type):
        return ltypes[0][0]

def namespaceclass(cl, add_cl=''):
    module = cl.mv.module

    if module.ident != 'builtin' and module != getmv().module and module.mod_path:
        return module.full_path()+'::'+add_cl+nokeywords(cl.ident)
    else:
        return add_cl+nokeywords(cl.ident)

def nodetypestr(node, parent=None, cplusplus=True, check_extmod=False, check_ret=False, var=None): # XXX minimize
    if cplusplus and isinstance(node, variable) and node.looper: # XXX to declaredefs?
        return nodetypestr(node.looper, None, cplusplus)[:-2]+'::for_in_loop '
    types = getgx().merged_inh[node]
    return typestr(types, None, cplusplus, node, check_extmod, 0, check_ret, var)

def typestr(types, parent=None, cplusplus=True, node=None, check_extmod=False, depth=0, check_ret=False, var=None):
    try:
        ts = typestrnew(types, cplusplus, node, check_extmod, depth, check_ret, var)
    except RuntimeError:
        if not getmv().module.builtin and isinstance(node, variable) and not node.name.startswith('__'): # XXX startswith
            if node.parent: varname = repr(node)
            else: varname = "'%s'" % node.name
            error("variable %s has dynamic (sub)type" % varname, node, warning=True)
        ts = 'ERROR'
    if cplusplus:
        if not ts.endswith('*'): ts += ' '
        return ts
    return '['+ts+']'

class ExtmodError(Exception):
    pass

def typestrnew(types, cplusplus=True, node=None, check_extmod=False, depth=0, check_ret=False, var=None):
    if depth==10:
        raise RuntimeError()

    # --- annotation or c++ code
    conv1 = {'int_': '__ss_int', 'float_': 'double', 'str_': 'str', 'none': 'int', 'bool_':'__ss_bool'}
    conv2 = {'int_': 'int', 'float_': 'float', 'str_': 'str', 'class_': 'class', 'none': 'None','bool_': 'bool'}
    if cplusplus: sep, ptr, conv = '<>', ' *', conv1
    else: sep, ptr, conv = '()', '', conv2

    def map(ident):
        if cplusplus: return ident+' *'
        return conv.get(ident, ident)

    anon_funcs = set([t[0] for t in types if isinstance(t[0], function)])
    if anon_funcs and check_extmod:
        raise ExtmodError()
    if anon_funcs:
        f = anon_funcs.pop()
        if f.mv != getmv():
            return f.mv.module.full_path()+'::'+'lambda%d' % f.lambdanr
        return 'lambda%d' % f.lambdanr

    classes = polymorphic_cl(types_classes(types))
    lcp = lowest_common_parents(classes)

    # --- multiple parent classes
    if len(lcp) > 1:
        if set(lcp) == set([defclass('int_'),defclass('float_')]):
            return conv['float_']
        elif not node or inode(node).mv.module.builtin:
            return '***ERROR*** '
        elif isinstance(node, variable):
            if not node.name.startswith('__') : # XXX startswith
                if node.parent: varname = "%s" % node
                else: varname = "'%s'" % node
                error("variable %s has dynamic (sub)type: {%s}" % (varname, ', '.join([conv2.get(cl.ident, cl.ident) for cl in lcp])), node, warning=True)
        elif node not in getgx().bool_test_only:
            error("expression has dynamic (sub)type: {%s}" % ', '.join([conv2.get(cl.ident, cl.ident) for cl in lcp]), node, warning=True)

    elif not classes:
        if cplusplus: return 'void *'
        return ''

    cl = lcp.pop()

    if check_ret and cl.mv.module.ident == 'collections' and cl.ident == 'defaultdict':
        print '*WARNING* defaultdicts are returned as dicts'
    elif check_extmod and cl.mv.module.builtin and not (cl.mv.module.ident == 'builtin' and cl.ident in ['int_', 'float_', 'complex', 'str_', 'list', 'tuple', 'tuple2', 'dict', 'set', 'frozenset', 'none', 'bool_']) and not (cl.mv.module.ident == 'collections' and cl.ident == 'defaultdict'):
        raise ExtmodError()

    # --- simple built-in types
    if cl.ident in ['int_', 'float_', 'bool_']:
        return conv[cl.ident]
    elif cl.ident == 'str_':
        return 'str'+ptr
    elif cl.ident == 'none':
        if cplusplus: return 'void *'
        return 'None'

    # --- namespace prefix
    namespace = ''
    if cl.module not in [getmv().module, getgx().modules['builtin']]:
        if not (cl.ident in getmv().ext_funcs or cl.ident in getmv().ext_classes): # XXX too smart? can remove it, plus 'using'? 
            if cplusplus: namespace = cl.module.full_path()+'::'
            else: namespace = '::'.join(cl.module.mod_path)+'::'
        getmv().module.prop_includes.add(cl.module)

    template_vars = cl.tvar_names()
    if template_vars:
        subtypes = []
        for tvar in template_vars:
            vartypes = types_var_types(types, tvar)
            ts = typestrnew(vartypes, cplusplus, node, check_extmod, depth+1)
            if tvar == var:
                return ts
            if [t[0] for t in vartypes if isinstance(t[0], function)]:
                ident = cl.ident
                if ident == 'tuple2': ident = 'tuple'
                error("'%s' instance containing function reference" % ident, node, warning=True) # XXX test
            subtypes.append(ts)
    else:
        if cl.ident in getgx().cpp_keywords:
            return namespace+getgx().ss_prefix+map(cl.ident)
        return namespace+map(cl.ident)

    ident = cl.ident

    # --- binary tuples
    if ident == 'tuple2':
        if subtypes[0] == subtypes[1]:
            ident, subtypes = 'tuple', [subtypes[0]]
    if ident == 'tuple2' and not cplusplus:
        ident = 'tuple'
    elif ident == 'tuple' and cplusplus:
        return namespace+'tuple2'+sep[0]+subtypes[0]+', '+subtypes[0]+sep[1]+ptr

    if ident in ['frozenset', 'pyset'] and cplusplus:
        ident = 'set'

    if ident in getgx().cpp_keywords:
        ident = getgx().ss_prefix+ident

    # --- final type representation
    return namespace+ident+sep[0]+', '.join(subtypes)+sep[1]+ptr

def types_classes(types):
    return set([t[0] for t in types if isinstance(t[0], class_)])

def types_var_types(types, varname):
    subtypes = set()
    for t in types:
        if not varname in t[0].vars:
            continue
        var = t[0].vars[varname]
        if (var, t[1], 0) in getgx().cnode:
            subtypes.update(getgx().cnode[var, t[1], 0].types())
    return subtypes

# --- assignment (incl. passing arguments, returning values) may require a cast
def assign_needs_cast(arg, func, formal, target):
    argtypes = getgx().merged_inh[arg]
    formaltypes = getgx().merged_inh[formal]
    try:
        return assign_needs_cast_rec(argtypes, formaltypes)
    except RuntimeError:
        return False

def assign_needs_cast_rec(argtypes, formaltypes, depth=0):
    if depth == 10:
        raise RuntimeError()

    argclasses = types_classes(argtypes)
    formalclasses = types_classes(formaltypes)

    # void *
    noneset = set([defclass('none')])
    if argclasses == noneset and formalclasses != noneset:
        return True

    # no type, e.g. [[]]
    if not argclasses and formalclasses:
        return True

    # subtype
    lcp_args, lcp_formals = lowest_common_parents(polymorphic_cl(argclasses)), lowest_common_parents(polymorphic_cl(formalclasses)) # XXX lcp(pol_cl) shortcut
    if depth > 0 and len(lcp_args) == 1 and len(lcp_formals) == 1 and lcp_args != lcp_formals:
        return True

    # recurse on tvars
    if not lcp_formals:
        return False
    tvars = lcp_formals[0].tvar_names()
    if tvars:
        for tvar in tvars:
            argvartypes = types_var_types(argtypes, tvar)
            formalvartypes = types_var_types(formaltypes, tvar)
            if assign_needs_cast_rec(argvartypes, formalvartypes, depth+1):
                return True
    return False

class Bitpair:
    def __init__(self, nodes, msg, inline):
        self.nodes = nodes
        self.msg = msg
        self.inline = inline

def unboxable(types):
    if not isinstance(types, set):
        types = inode(types).types()
    classes = set([t[0] for t in types])

    if [cl for cl in classes if cl.ident not in ['int_','float_','bool_']]:
        return None
    else:
        if classes:
            return classes.pop().ident
        return None

def get_includes(mod):
    imports = set()
    if mod == getgx().main_module:
        mods = getgx().modules.values()
    else:
        d = mod.mv.imports.copy()
        d.update(mod.mv.fake_imports)
        mods = d.values()
    for mod in mods:
        imports.add(mod.include_path())
    return imports

def subclass(a, b):
    if b in a.bases:
        return True
    else:
        return a.bases and subclass(a.bases[0], b) # XXX mult inh

# --- determine virtual methods and variables
def analyze_virtuals():
    for node in getgx().merged_inh: # XXX all:
        # --- for every message
        if isinstance(node, CallFunc) and not inode(node).mv.module.builtin: #ident == 'builtin':
            objexpr, ident, direct_call, method_call, constructor, parent_constr, anon_func = analyze_callfunc(node, merge=getgx().merged_inh)
            if not method_call or objexpr not in getgx().merged_inh:
                continue # XXX

            # --- determine abstract receiver class
            classes = polymorphic_t(getgx().merged_inh[objexpr])
            classes = [cl for cl in classes if isinstance(cl, class_)]
            if not classes:
                continue

            if isinstance(objexpr, Name) and objexpr.name == 'self' and inode(objexpr).parent: # XXX last check to avoid crash
                abstract_cl = inode(objexpr).parent.parent
            else:
                lcp = lowest_common_parents(classes)
                lcp = [x for x in lcp if isinstance(x, class_)] # XXX
                if not lcp:
                    continue
                abstract_cl = lcp[0]

            if not abstract_cl or not isinstance(abstract_cl, class_):
                continue
            subclasses = [cl for cl in classes if subclass(cl, abstract_cl)]

            # --- register virtual method
            if not ident.startswith('__'):
                redefined = False
                for concrete_cl in classes:
                    if [cl for cl in concrete_cl.ancestors_upto(abstract_cl) if ident in cl.funcs and not cl.funcs[ident].inherited]:
                        redefined = True

                if redefined:
                    abstract_cl.virtuals.setdefault(ident, set()).update(subclasses)

            # --- register virtual var
            elif ident in ['__getattr__','__setattr__'] and subclasses:
                var = defaultvar(node.args[0].value, abstract_cl)
                abstract_cl.virtualvars.setdefault(node.args[0].value, set()).update(subclasses)

# --- merge variables assigned to via 'self.varname = ..' in inherited methods into base class
def upgrade_variables():
    for node, inheritnodes in getgx().inheritance_relations.items():
        if isinstance(node, AssAttr):
            baseclass = inode(node).parent.parent
            inhclasses = [inode(x).parent.parent for x in inheritnodes]
            var = defaultvar(node.attrname, baseclass)

            for inhclass in inhclasses:
                inhvar = lookupvar(node.attrname, inhclass)

                if (var, 1, 0) in getgx().cnode:
                    newnode = getgx().cnode[var,1,0]
                else:
                    newnode = cnode(var, 1, 0, parent=baseclass)
                    getgx().types[newnode] = set()

                if inhvar in getgx().merged_all: # XXX ?
                    getgx().types[newnode].update(getgx().merged_all[inhvar])

# --- generate C++ and Makefiles
def generate_code():
    ident = getgx().main_module.ident

    if sys.platform == 'win32':
        pyver = '%d%d' % sys.version_info[:2]
        prefix = sysconfig.get_config_var('prefix').replace('\\', '/')
    else:
        pyver = sysconfig.get_config_var('VERSION')
        includes = '-I' + sysconfig.get_python_inc() + ' ' + \
                   '-I' + sysconfig.get_python_inc(plat_specific=True)
        if sys.platform == 'darwin':
            ldflags = sysconfig.get_config_var('BASECFLAGS')
        else:
            ldflags = sysconfig.get_config_var('LIBS') + ' ' + \
                      sysconfig.get_config_var('SYSLIBS') + ' ' + \
                      '-lpython'+pyver
            if not sysconfig.get_config_var('Py_ENABLE_SHARED'):
                ldflags += ' -L' + sysconfig.get_config_var('LIBPL')

    if getgx().extension_module:
        if sys.platform == 'win32': ident += '.pyd'
        else: ident += '.so'

    # --- generate C++ files
    mods = getgx().modules.values()
    for module in mods:
        if not module.builtin:
            # create output directory if necessary
            if getgx().output_dir:
                output_dir = os.path.join(getgx().output_dir, module.dir)
                if not os.path.exists(output_dir):
                    os.makedirs(output_dir)

            gv = generateVisitor(module)
            mv = module.mv
            setmv(mv)
            walk(module.ast, gv)
            gv.out.close()
            gv.header_file()
            gv.out.close()
            gv.insert_consts(declare=False)
            gv.insert_consts(declare=True)
            gv.insert_includes()

    # --- generate Makefile
    makefile = file(os.path.join(getgx().output_dir, getgx().makefile_name), 'w')

    libdir = getgx().libdir
    print >>makefile, 'SHEDSKIN_LIBDIR=%s' % (libdir)
    filenames = []
    for mod in mods:
        filename = mod.filename[:-3] # strip .py
        filename = filename.replace(' ','\ ') # make paths valid
        filename = filename.replace(libdir,'${SHEDSKIN_LIBDIR}')
        filenames.append(filename)

    cppfiles = [fn+'.cpp' for fn in filenames]
    hppfiles = [fn+'.hpp' for fn in filenames]
    for always in ('re',):
        repath = connect_paths('${SHEDSKIN_LIBDIR}', always)
        if not repath in filenames:
            cppfiles.append(repath+'.cpp')
            hppfiles.append(repath+'.hpp')

    cppfiles.sort(reverse=True)
    hppfiles.sort(reverse=True)
    cppfiles = ' \\\n\t'.join(cppfiles)
    hppfiles = ' \\\n\t'.join(hppfiles)

    # import flags
    if getgx().flags: flags = getgx().flags
    elif os.path.isfile('FLAGS'): flags = 'FLAGS'
    elif getgx().msvc: flags = connect_paths(getgx().sysdir, 'FLAGS.msvc')
    elif sys.platform == 'win32': flags = connect_paths(getgx().sysdir, 'FLAGS.mingw')
    else: flags = connect_paths(getgx().sysdir, 'FLAGS')

    for line in file(flags):
        line = line[:-1]

        variable = line[:line.find('=')].strip()
        if variable == 'CCFLAGS':
            line += ' -I. -I${SHEDSKIN_LIBDIR}'
            if sys.platform == 'darwin' and os.path.isdir('/usr/local/include'):
                line += ' -I/usr/local/include' # XXX
            if sys.platform == 'darwin' and os.path.isdir('/opt/local/include'):
                line += ' -I/opt/local/include' # XXX
            if not getgx().wrap_around_check: line += ' -D__SS_NOWRAP'
            if not getgx().bounds_checking: line += ' -D__SS_NOBOUNDS'
            if getgx().fast_random: line += ' -D__SS_FASTRANDOM'
            if not getgx().assertions: line += ' -D__SS_NOASSERT'
            if getgx().fast_hash: line += ' -D__SS_FASTHASH'
            if getgx().longlong: line += ' -D__SS_LONG'
            if getgx().extension_module:
                if getgx().msvc: line += ' /DLL /LIBPATH:%s/libs /LIBPATH:python%s' % (prefix, pyver)
                elif sys.platform == 'win32': line += ' -I%s/include -D__SS_BIND' % prefix
                else: line += ' -g -fPIC -D__SS_BIND ' + includes

        elif variable == 'LFLAGS':
            if sys.platform == 'darwin' and os.path.isdir('/opt/local/lib'): # XXX
                line += ' -L/opt/local/lib'
            if sys.platform == 'darwin' and os.path.isdir('/usr/local/lib'): # XXX
                line += ' -L/usr/local/lib'
            if getgx().extension_module:
                if sys.platform == 'win32': line += ' -shared -L%s/libs -lpython%s' % (prefix, pyver)
                elif sys.platform == 'darwin': line += ' -bundle -undefined dynamic_lookup ' + ldflags
                elif sys.platform == 'sunos5': line += ' -shared -Xlinker ' + ldflags
                else: line += ' -shared -Xlinker -export-dynamic ' + ldflags

            if 'socket' in [m.ident for m in mods]:
                if sys.platform == 'win32':
                    line += ' -lws2_32'
                elif sys.platform == 'sunos5':
                    line += ' -lsocket -lnsl'
            if 'os' in [m.ident for m in mods]:
                if sys.platform not in ['win32', 'darwin', 'sunos5']:
                    line += ' -lutil'

        print >>makefile, line
    print >>makefile

    print >>makefile, 'CPPFILES=%s\n' % cppfiles
    print >>makefile, 'HPPFILES=%s\n' % hppfiles

    print >>makefile, 'all:\t'+ident+'\n'

    # executable (normal, debug, profile) or extension module
    _out = '-o '
    _ext=''
    if getgx().msvc:
        _out = '/out:'
        _ext = ''
        if not getgx().extension_module:
            _ext = '.exe'
    targets = [('', '')]
    if not getgx().extension_module:
        targets += [('_prof', '-pg -ggdb'), ('_debug', '-g -ggdb')]
    for suffix, options in targets:
        print >>makefile, ident+suffix+':\t$(CPPFILES) $(HPPFILES)'
        print >>makefile, '\t$(CC) '+options+' $(CCFLAGS) $(CPPFILES) $(LFLAGS) '+_out+ident+suffix+_ext + '\n'

    # clean
    ext = ''
    if sys.platform == 'win32' and not getgx().extension_module:
        ext = '.exe'
    print >>makefile, 'clean:'
    targets = [ident+ext]
    if not getgx().extension_module:
        targets += [ident+'_prof'+ext, ident+'_debug'+ext]
    print >>makefile, '\trm -f %s\n' % ' '.join(targets)

    # phony
    print >>makefile, '.PHONY: all clean\n'
    makefile.close()