Bug 560000: Allow IPDL unions and structs to be recursively defined. r=benjamn
authorChris Jones <jones.chris.g@gmail.com>
Sat, 22 May 2010 14:35:38 -0500
changeset 42566 e8b83755bbe5a5212876de0840fd151b259acc5f
parent 42565 dc2985f8ad9f94128d39bb383b102a28b91ac0c4
child 42567 8a903cf91b031f8af8927d4c249c88516062b7b9
push id13404
push usercjones@mozilla.com
push dateSat, 22 May 2010 19:33:57 +0000
treeherdermozilla-central@721817fd5753 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbenjamn
bugs560000
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 560000: Allow IPDL unions and structs to be recursively defined. r=benjamn
ipc/ipdl/ipdl/cxx/ast.py
ipc/ipdl/ipdl/cxx/cgen.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/ipdl/type.py
--- a/ipc/ipdl/ipdl/cxx/ast.py
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -454,79 +454,93 @@ class Inherit(Node):
 
 class FriendClassDecl(Node):
     def __init__(self, friend):
         Node.__init__(self)
         self.friend = friend
 
 class MethodDecl(Node):
     def __init__(self, name, params=[ ], ret=Type('void'),
-                 virtual=0, const=0, pure=0, static=0, warn_unused=0, inline=0,
+                 virtual=0, const=0, pure=0, static=0, warn_unused=0,
+                 inline=0, force_inline=0,
                  typeop=None,
                  T=None):
         assert not (virtual and static)
         assert not pure or virtual      # pure => virtual
         assert not (static and typeop)
         assert not (name and typeop)
         assert name is None or isinstance(name, str)
         assert not isinstance(ret, list)
+        for decl in params:  assert not isinstance(decl, str)
+        assert not isinstance(T, int)
 
         if typeop is not None:
             ret = None
 
         Node.__init__(self)
         self.name = name
         self.params = params            # [ Param ]
         self.ret = ret                  # Type or None
         self.virtual = virtual          # bool
         self.const = const              # bool
         self.pure = pure                # bool
         self.static = static            # bool
         self.warn_unused = warn_unused  # bool
-        self.inline = (inline or T)     # bool
+        self.force_inline = (force_inline or T) # bool
+        self.inline = inline            # bool
         self.typeop = typeop            # Type or None
         self.T = T                      # Type or None
 
     def __deepcopy__(self, memo):
         return MethodDecl(
             self.name,
-            copy.deepcopy(self.params, memo),
-            copy.deepcopy(self.ret, memo),
-            self.virtual, self.const, self.pure, self.static, self.warn_unused,
-            copy.deepcopy(self.typeop, memo),
-            copy.deepcopy(self.T, memo),
-            self.inline)
+            params=copy.deepcopy(self.params, memo),
+            ret=copy.deepcopy(self.ret, memo),
+            virtual=self.virtual,
+            const=self.const,
+            pure=self.pure,
+            static=self.static,
+            warn_unused=self.warn_unused,
+            inline=self.inline,
+            force_inline=self.force_inline,
+            typeop=copy.deepcopy(self.typeop, memo),
+            T=copy.deepcopy(self.T, memo))
 
 class MethodDefn(Block):
     def __init__(self, decl):
         Block.__init__(self)
         self.decl = decl
 
 class ConstructorDecl(MethodDecl):
-    def __init__(self, name, params=[ ], explicit=0):
-        MethodDecl.__init__(self, name, params=params, ret=None)
+    def __init__(self, name, params=[ ], explicit=0, force_inline=0):
+        MethodDecl.__init__(self, name, params=params, ret=None,
+                            force_inline=force_inline)
         self.explicit = explicit
 
     def __deepcopy__(self, memo):
         return ConstructorDecl(self.name,
                                copy.deepcopy(self.params, memo),
                                self.explicit)
 
 class ConstructorDefn(MethodDefn):
     def __init__(self, decl, memberinits=[ ]):
         MethodDefn.__init__(self, decl)
         self.memberinits = memberinits
 
 class DestructorDecl(MethodDecl):
-    def __init__(self, name, virtual=0):
+    def __init__(self, name, virtual=0, force_inline=0, inline=0):
         MethodDecl.__init__(self, name, params=[ ], ret=None,
-                            virtual=virtual)
+                            virtual=virtual,
+                            force_inline=force_inline, inline=inline)
 
     def __deepcopy__(self, memo):
-        return DestructorDecl(self.name, self.virtual)
+        return DestructorDecl(self.name,
+                              virtual=self.virtual,
+                              force_inline=self.force_inline,
+                              inline=self.inline)
 
         
 class DestructorDefn(MethodDefn):
     def __init__(self, decl):  MethodDefn.__init__(self, decl)
 
 ##------------------------------
 # expressions
 class ExprVar(Node):
@@ -747,15 +761,17 @@ class StmtSwitch(Block):
         self.nr_cases += 1
 
 class StmtBreak(Node):
     def __init__(self):
         Node.__init__(self)
 
 class StmtExpr(Node):
     def __init__(self, expr):
+        assert expr is not None
+        
         Node.__init__(self)
         self.expr = expr
 
 class StmtReturn(Node):
     def __init__(self, expr=None):
         Node.__init__(self)
         self.expr = expr
--- a/ipc/ipdl/ipdl/cxx/cgen.py
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -203,16 +203,18 @@ class CxxCodeGen(CodePrinter, Visitor):
 
         if md.T:
             self.write('template<')
             self.write('typename ')
             md.T.accept(self)
             self.println('>')
             self.printdent()
 
+        if md.inline:
+            self.write('inline ')
         if md.static:
             self.write('static ')
         if md.virtual:
             self.write('virtual ')
         if md.ret:
             md.ret.accept(self)
             self.println()
             self.printdent()
@@ -271,16 +273,18 @@ class CxxCodeGen(CodePrinter, Visitor):
 
         self.visitBlock(cd)
 
         self.dedent()
         self.printdentln('}')
 
 
     def visitDestructorDecl(self, dd):
+        if dd.inline:
+            self.write('inline ')
         if dd.virtual:
             self.write('virtual ')
 
         # hack alert
         parts = dd.name.split('::')
         parts[-1] = '~'+ parts[-1]
 
         self.write('::'.join(parts) +'()')
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -192,33 +192,40 @@ def _shmemForget(shmemexpr):
 
 def _shmemRevokeRights(shmemexpr):
     return ExprCall(ExprSelect(shmemexpr, '.', 'RevokeRights'),
                     args=[ _shmemBackstagePass() ])
 
 def _lookupShmem(idexpr):
     return ExprCall(ExprVar('LookupSharedMemory'), args=[ idexpr ])
 
-def _makeForwardDecl(ptype, side):
-    clsname = _actorName(ptype.qname.baseid, side)
-
+
+def _makeForwardDeclForQClass(clsname, quals):
     fd = ForwardDecl(clsname, cls=1)
-    if 0 == len(ptype.qname.quals):
+    if 0 == len(quals):
         return fd
 
-    outerns = Namespace(ptype.qname.quals[0])
+    outerns = Namespace(quals[0])
     innerns = outerns
-    for ns in ptype.qname.quals[1:]:
+    for ns in quals[1:]:
         tmpns = Namespace(ns)
         innerns.addstmt(tmpns)
         innerns = tmpns
 
     innerns.addstmt(fd)
     return outerns
 
+def _makeForwardDeclForActor(ptype, side):
+    return _makeForwardDeclForQClass(_actorName(ptype.qname.baseid, side),
+                                     ptype.qname.quals)
+
+def _makeForwardDecl(type):
+    return _makeForwardDeclForQClass(type.name(), type.qname.quals)
+
+
 def _putInNamespaces(cxxthing, namespaces):
     """|namespaces| is in order [ outer, ..., inner ]"""
     if 0 == len(namespaces):  return cxxthing
 
     outerns = Namespace(namespaces[0].name)
     innerns = outerns
     for ns in namespaces[1:]:
         newns = Namespace(ns.name)
@@ -527,40 +534,28 @@ necessarily a C++ reference."""
         return t
 
 ##--------------------------------------------------
 
 class HasFQName:
     def fqClassName(self):
         return self.decl.type.fullname()
 
-
-class StructDecl(ipdl.ast.StructDecl, HasFQName):
-    @staticmethod
-    def upgrade(structDecl):
-        assert isinstance(structDecl, ipdl.ast.StructDecl)
-        structDecl.__class__ = StructDecl
-        return structDecl
-
-class _StructField(_HybridDecl):
-    def __init__(self, ipdltype, name, sd, side=None, other=None):
-        special = _hasVisibleActor(ipdltype)
-        fname = name
-        if special:
-            fname += side.title()
-
-        _HybridDecl.__init__(self, ipdltype, fname)
+class _CompoundTypeComponent(_HybridDecl):
+    def __init__(self, ipdltype, name, side, ct):
+        _HybridDecl.__init__(self, ipdltype, name)
         self.side = side
-        self.special = special
-        if special:
-            if other is not None:
-                self.other = other
-            else:
-                self.other = _StructField(ipdltype, name, sd, _otherSide(side), self)
-        self.sd = sd
+        self.special = _hasVisibleActor(ipdltype)
+        self.recursive = ct.decl.type.mutuallyRecursiveWith(ipdltype)
+
+    def internalType(self):
+        if self.recursive:
+            return self.ptrToType()
+        else:
+            return self.bareType()
 
     # @override the following methods to pass |self.side| instead of
     # forcing the caller to remember which side we're declared to
     # represent.
     def bareType(self, side=None):
         return _HybridDecl.bareType(self, self.side)
     def refType(self, side=None):
         return _HybridDecl.refType(self, self.side)
@@ -569,62 +564,118 @@ class _StructField(_HybridDecl):
     def ptrToType(self, side=None):
         return _HybridDecl.ptrToType(self, self.side)
     def constPtrToType(self, side=None):
         return _HybridDecl.constPtrToType(self, self.side)
     def inType(self, side=None):
         return _HybridDecl.inType(self, self.side)
 
 
+class StructDecl(ipdl.ast.StructDecl, HasFQName):
+    @staticmethod
+    def upgrade(structDecl):
+        assert isinstance(structDecl, ipdl.ast.StructDecl)
+        structDecl.__class__ = StructDecl
+        return structDecl
+
+class _StructField(_CompoundTypeComponent):
+    def __init__(self, ipdltype, name, sd, side=None):
+        fname = name
+        special = _hasVisibleActor(ipdltype)
+        if special:
+            fname += side.title()
+
+        _CompoundTypeComponent.__init__(self, ipdltype, fname, side, sd)
+
+    def getMethod(self, thisexpr=None, sel='.'):
+        meth = self.var()
+        if thisexpr is not None:
+            return ExprSelect(thisexpr, sel, meth.name)
+        return meth
+
+    def initExpr(self, thisexpr):
+        expr = ExprCall(self.getMethod(thisexpr=thisexpr))
+        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+            expr = ExprCast(expr, self.bareType(), const=1)
+        return expr
+
+    def refExpr(self, thisexpr=None):
+        ref = self.memberVar()
+        if thisexpr is not None:
+            ref = ExprSelect(thisexpr, '.', ref.name)
+        if self.recursive:
+            ref = ExprDeref(ref)
+        return ref
+
+    def argVar(self):
+        return ExprVar('_'+ self.name)
+
+    def memberVar(self):
+        return ExprVar(self.name + '_')
+
+    def initStmts(self):
+        if self.recursive:
+            return [ StmtExpr(ExprAssn(self.memberVar(),
+                                       ExprNew(self.bareType()))) ]
+        else:
+            return []
+
+    def destructStmts(self):
+        if self.recursive:
+            return [ StmtExpr(ExprDelete(self.memberVar())) ]
+        else:
+            return []
+
+
 class UnionDecl(ipdl.ast.UnionDecl, HasFQName):
     def callType(self, var=None):
         func = ExprVar('type')
         if var is not None:
             func = ExprSelect(var, '.', func.name)
         return ExprCall(func)
 
     @staticmethod
     def upgrade(unionDecl):
         assert isinstance(unionDecl, ipdl.ast.UnionDecl)
         unionDecl.__class__ = UnionDecl
         return unionDecl
 
 
-class _UnionMember(_HybridDecl):
+class _UnionMember(_CompoundTypeComponent):
     """Not in the AFL sense, but rather a member (e.g. |int;|) of an
 IPDL union type."""
     def __init__(self, ipdltype, ud, side=None, other=None):
         flatname = _flatTypeName(ipdltype)
         special = _hasVisibleActor(ipdltype)
         if special:
             flatname += side.title()
 
-        _HybridDecl.__init__(self, ipdltype, 'V'+ flatname)
+        _CompoundTypeComponent.__init__(self, ipdltype, 'V'+ flatname, side, ud)
         self.flattypename = flatname
-        self.side = side
-        self.special = special
         if special:
             if other is not None:
                 self.other = other
             else:
                 self.other = _UnionMember(ipdltype, ud, _otherSide(side), self)
-        self.ud = ud
 
     def enum(self):
         return 'T' + self.flattypename
 
     def pqEnum(self):
         return self.ud.name +'::'+ self.enum()
 
     def enumvar(self):
         return ExprVar(self.enum())
 
     def unionType(self):
         """Type used for storage in generated C union decl."""
-        return TypeArray(Type('char'), ExprSizeof(self.bareType()))
+        if self.recursive:
+            return self.ptrToType()
+        else:
+            return TypeArray(Type('char'), ExprSizeof(self.internalType()))
 
     def unionValue(self):
         # NB: knows that Union's storage C union is named |mValue|
         return ExprSelect(ExprVar('mValue'), '.', self.name)
 
     def typedef(self):
         return self.flattypename +'__tdef'
 
@@ -636,76 +687,77 @@ IPDL union type."""
         """Return an expression of type self.ptrToSelfType()"""
         return ExprCall(ExprVar(self.getPtrName()))
 
     def callOperatorEq(self, rhs):
         if self.ipdltype.isIPDL() and self.ipdltype.isActor():
             rhs = ExprCast(rhs, self.bareType(), const=1)
         return ExprAssn(ExprDeref(self.callGetPtr()), rhs)
 
-    def callPlacementCtor(self, expr=None):
+    def callCtor(self, expr=None):
         assert not isinstance(expr, list)
         
         if expr is None:
             args = None
         elif self.ipdltype.isIPDL() and self.ipdltype.isActor():
             args = [ ExprCast(expr, self.bareType(), const=1) ]
         else:
             args = [ expr ]
 
-        return ExprNew(self.bareType(self.side),
-                     args=args,
-                     newargs=[ self.callGetPtr() ])
-
-    def callPlacementDtor(self):
-        return ExprCall(
-            ExprSelect(self.callGetPtr(), '->', '~'+ self.typedef()))
+        if self.recursive:
+            return ExprAssn(self.callGetPtr(),
+                            ExprNew(self.bareType(self.side),
+                                    args=args))
+        else:
+            return ExprNew(self.bareType(self.side),
+                           args=args,
+                           newargs=[ self.callGetPtr() ])
+
+    def callDtor(self):
+        if self.recursive:
+            return ExprDelete(self.callGetPtr())
+        else:
+            return ExprCall(
+                ExprSelect(self.callGetPtr(), '->', '~'+ self.typedef()))
 
     def getTypeName(self): return 'get_'+ self.flattypename
     def getConstTypeName(self): return 'get_'+ self.flattypename
 
     def getOtherTypeName(self): return 'get_'+ self.otherflattypename
 
     def getPtrName(self): return 'ptr_'+ self.flattypename
     def getConstPtrName(self): return 'constptr_'+ self.flattypename
 
     def ptrToSelfExpr(self):
         """|*ptrToSelfExpr()| has type |self.bareType()|"""
-        return ExprCast(ExprAddrOf(self.unionValue()),
-                        self.ptrToType(),
-                        reinterpret=1)
+        v = self.unionValue()
+        if self.recursive:
+            return v
+        else:
+            return ExprCast(ExprAddrOf(v), self.ptrToType(), reinterpret=1)
 
     def constptrToSelfExpr(self):
         """|*constptrToSelfExpr()| has type |self.constType()|"""
-        return ExprCast(ExprAddrOf(self.unionValue()),
-                        self.constPtrToType(),
-                        reinterpret=1)
+        v = self.unionValue()
+        if self.recursive:
+            return v
+        return ExprCast(ExprAddrOf(v), self.constPtrToType(), reinterpret=1)
+
+    def ptrToInternalType(self):
+        t = self.ptrToType()
+        if self.recursive:
+            t.ref = 1
+        return t
 
     def defaultValue(self):
         if self.ipdltype.isIPDL() and self.ipdltype.isActor():
             return ExprCast(ExprLiteral.NULL, self.bareType(), static=1)
         # XXX sneaky here, maybe need ExprCtor()?
         return ExprCall(self.bareType())
 
-    # @override the following methods to pass |self.side| instead of
-    # forcing the caller to remember which side we're declared to
-    # represent.
-    def bareType(self, side=None):
-        return _HybridDecl.bareType(self, self.side)
-    def refType(self, side=None):
-        return _HybridDecl.refType(self, self.side)
-    def constRefType(self, side=None):
-        return _HybridDecl.constRefType(self, self.side)
-    def ptrToType(self, side=None):
-        return _HybridDecl.ptrToType(self, self.side)
-    def constPtrToType(self, side=None):
-        return _HybridDecl.constPtrToType(self, self.side)
-    def inType(self, side=None):
-        return _HybridDecl.inType(self, self.side)
-
 ##--------------------------------------------------
 
 class MessageDecl(ipdl.ast.MessageDecl):
     def baseName(self):
         return self.name
     
     def recvMethod(self):
         name = _recvPrefix(self.decl.type) + self.baseName()
@@ -1187,16 +1239,17 @@ with some new IPDL/C++ nodes that are tu
 ##-----------------------------------------------------------------------------
 
 class _GenerateProtocolHeader(ipdl.ast.Visitor):
     '''Creates a header containing code common to both the parent and
 child actors.'''
     def __init__(self):
         self.protocol = None     # protocol we're generating a class for
         self.file = None         # File stuff is stuck in
+        self.structUnionDefns = []
 
     def lower(self, tu, outcxxfile):
         self.protocol = tu.protocol
         self.file = outcxxfile
         tu.accept(self)
 
     def visitTranslationUnit(self, tu):
         f = self.file
@@ -1207,29 +1260,55 @@ child actors.'''
 //
 
 '''))
         f.addthings(_includeGuardStart(f))
         f.addthing(Whitespace.NL)
 
         ipdl.ast.Visitor.visitTranslationUnit(self, tu)
 
+        f.addthings(self.structUnionDefns)
+
         f.addthing(Whitespace.NL)
         f.addthings(_includeGuardEnd(f))
 
 
     def visitCxxInclude(self, inc):
         self.file.addthing(CppDirective('include', '"'+ inc.file +'"'))
 
+    def processStructOrUnionClass(self, su, which, forwarddecls, cls):
+        clsdecl, methoddefns = _splitClassDeclDefn(cls, inlinedefns=1)
+        
+        self.file.addthings(
+            [  Whitespace.NL ]
+            + forwarddecls
+            + [ Whitespace("""
+//-----------------------------------------------------------------------------
+// Declaration of the IPDL type |%s %s|
+//
+"""% (which, su.name)),
+                _putInNamespaces(clsdecl, su.namespaces),
+            ])
+
+        self.structUnionDefns.extend([
+            Whitespace("""
+//-----------------------------------------------------------------------------
+// Method definitions for the IPDL type |%s %s|
+//
+"""% (which, su.name)),
+            _putInNamespaces(methoddefns, su.namespaces),
+        ])
+
     def visitStructDecl(self, sd):
-        self.file.addthings(_generateCxxStruct(sd))
+        return self.processStructOrUnionClass(sd, 'struct',
+                                              *_generateCxxStruct(sd))
 
     def visitUnionDecl(self, ud):
-        self.file.addthings(_generateCxxUnionStuff(ud))
-
+        return self.processStructOrUnionClass(ud, 'union',
+                                              *_generateCxxUnion(ud))
 
     def visitProtocol(self, p):
         self.file.addthing(Whitespace("""
 //-----------------------------------------------------------------------------
 // Code common to %sChild and %sParent
 //
 """% (p.name, p.name)))
 
@@ -1349,56 +1428,60 @@ def _generateMessageClass(clsname, msgid
 ##--------------------------------------------------
 
 class _ComputeTypeDeps(TypeVisitor):
     '''Pass that gathers the C++ types that a particular IPDL type
 (recursively) depends on.  There are two kinds of dependencies: (i)
 types that need forward declaration; (ii) types that need a |using|
 stmt.  Some types generate both kinds.'''
 
-    def __init__(self):
+    def __init__(self, fortype):
+        ipdl.type.TypeVisitor.__init__(self)
         self.usingTypedefs = [ ]
         self.forwardDeclStmts = [ ]
-        self.seen = set()
+        self.fortype = fortype
 
     def maybeTypedef(self, fqname, name):
         if fqname != name:
             self.usingTypedefs.append(Typedef(Type(fqname), name))
         
     def visitBuiltinCxxType(self, t):
-        if t in self.seen: return
-        self.seen.add(t)
+        if t in self.visited: return
+        self.visited.add(t)
         self.maybeTypedef(t.fullname(), t.name())
 
     def visitImportedCxxType(self, t):
-        if t in self.seen: return
-        self.seen.add(t)
+        if t in self.visited: return
+        self.visited.add(t)
         self.maybeTypedef(t.fullname(), t.name())
 
     def visitActorType(self, t):
-        if t in self.seen: return
-        self.seen.add(t)
-            
+        if t in self.visited: return
+        self.visited.add(t)
+
         fqname, name = t.fullname(), t.name()
 
         self.maybeTypedef(_actorName(fqname, 'Parent'),
                           _actorName(name, 'Parent'))
         self.maybeTypedef(_actorName(fqname, 'Child'),
                           _actorName(name, 'Child'))
 
         self.forwardDeclStmts.extend([
-            _makeForwardDecl(t.protocol, 'parent'), Whitespace.NL,
-            _makeForwardDecl(t.protocol, 'child'), Whitespace.NL
+            _makeForwardDeclForActor(t.protocol, 'parent'), Whitespace.NL,
+            _makeForwardDeclForActor(t.protocol, 'child'), Whitespace.NL
         ])
 
     def visitStructOrUnionType(self, su, defaultVisit):
-        if su in self.seen: return
-        self.seen.add(su)
+        if su in self.visited or su == self.fortype: return
+        self.visited.add(su)
         self.maybeTypedef(su.fullname(), su.name())
 
+        if su.mutuallyRecursiveWith(self.fortype):
+            self.forwardDeclStmts.append(_makeForwardDecl(su))
+
         return defaultVisit(self, su)
 
     def visitStructType(self, t):
         return self.visitStructOrUnionType(t, TypeVisitor.visitStructType)
 
     def visitUnionType(self, t):
         return self.visitStructOrUnionType(t, TypeVisitor.visitUnionType)
 
@@ -1409,61 +1492,122 @@ stmt.  Some types generate both kinds.''
     def visitMessageType(self, v): assert 0
     def visitProtocolType(self, v): assert 0
     def visitStateType(self, v): assert 0
 
 
 def _generateCxxStruct(sd):
     ''' '''
     # compute all the typedefs and forward decls we need to make
-    gettypedeps = _ComputeTypeDeps()
+    gettypedeps = _ComputeTypeDeps(sd.decl.type)
     for f in sd.fields:
         f.ipdltype.accept(gettypedeps)
 
     usingTypedefs = gettypedeps.usingTypedefs
     forwarddeclstmts = gettypedeps.forwardDeclStmts
 
-    struct = Class(sd.name, struct=1)
-    struct.addstmts(
-        [ Label.PRIVATE ]
-        + usingTypedefs
-        + [ Whitespace.NL,
-            Label.PUBLIC,
-            ConstructorDefn(ConstructorDecl(sd.name)),
-            Whitespace.NL,
-            ConstructorDefn(
-                ConstructorDecl(
-                    sd.name,
-                    params=[ Decl(f.constRefType(), '_'+ f.name)
-                             for f in sd.fields ]),
-                memberinits=[ ExprCall(f.var(),
-                                       args=[ ExprVar('_'+ f.name) ])
-                              for f in sd.fields ]),
-            Whitespace.NL,
-            Whitespace('// Default copy ctor, op=, and dtor are OK\n\n',
-                       indent=1)
-        ]
-        + [ StmtDecl(Decl(f.bareType(), f.name)) for f in sd.fields ])
-
-
-    return (
-        [
-            Whitespace("""
-//-----------------------------------------------------------------------------
-// Definition of the IPDL type |struct %s|
-//
-"""% (sd.name))
-        ]
-        + forwarddeclstmts
-        + [ _putInNamespaces(struct, sd.namespaces) ])
-
+    struct = Class(sd.name, struct=1, final=1)
+    struct.addstmts([ Label.PRIVATE ]
+                    + usingTypedefs
+                    + [ Whitespace.NL, Label.PUBLIC ])
+
+    constreftype = Type(sd.name, const=1, ref=1)
+    initvar = ExprVar('Init')
+    callinit = ExprCall(initvar)
+    assignvar = ExprVar('Assign')
+
+    def fieldsAsParamList():
+        return [ Decl(f.inType(), f.argVar().name) for f in sd.fields ]
+
+    def assignFromOther(oexpr):
+        return ExprCall(assignvar,
+                        args=[ f.initExpr(oexpr) for f in sd.fields ])
+
+    # Struct()
+    defctor = ConstructorDefn(ConstructorDecl(sd.name))
+    defctor.addstmt(StmtExpr(callinit))
+    struct.addstmts([ defctor, Whitespace.NL ])
+
+    # Struct(const field1& _f1, ...)
+    valctor = ConstructorDefn(ConstructorDecl(sd.name,
+                                              params=fieldsAsParamList(),
+                                              force_inline=1))
+    valctor.addstmts([
+        StmtExpr(callinit),
+        StmtExpr(ExprCall(assignvar,
+                          args=[ f.argVar() for f in sd.fields ]))
+    ])
+    struct.addstmts([ valctor, Whitespace.NL ])
+
+    # Struct(const Struct& _o)
+    ovar = ExprVar('_o')
+    copyctor = ConstructorDefn(ConstructorDecl(
+        sd.name,
+        params=[ Decl(constreftype, ovar.name) ],
+        force_inline=1))
+    copyctor.addstmts([
+        StmtExpr(callinit),
+        StmtExpr(assignFromOther(ovar))
+    ])
+    struct.addstmts([ copyctor, Whitespace.NL ])
+
+    # ~Struct()
+    dtor = DestructorDefn(DestructorDecl(sd.name))
+    for f in sd.fields:
+        dtor.addstmts(f.destructStmts())
+    struct.addstmts([ dtor, Whitespace.NL ])
+
+    # Struct& operator=(const Struct& _o)
+    opeq = MethodDefn(MethodDecl(
+        'operator=',
+        params=[ Decl(constreftype, ovar.name) ],
+        force_inline=1))
+    opeq.addstmt(StmtExpr(assignFromOther(ovar)))
+    struct.addstmts([ opeq, Whitespace.NL ])
+
+    # field1& f1()
+    # const field1& f1() const
+    for f in sd.fields:
+        get = MethodDefn(MethodDecl(f.getMethod().name,
+                                    params=[ ],
+                                    ret=f.refType(),
+                                    force_inline=1))
+        get.addstmt(StmtReturn(f.refExpr()))
+
+        getconst = deepcopy(get)
+        getconst.decl.ret = f.constRefType()
+        getconst.decl.const = 1
+
+        struct.addstmts([ get, getconst, Whitespace.NL ])
+
+    # private:
+    struct.addstmt(Label.PRIVATE)
+
+    # Init()
+    init = MethodDefn(MethodDecl(initvar.name))
+    for f in sd.fields:
+        init.addstmts(f.initStmts())
+    struct.addstmts([ init, Whitespace.NL ])
+
+    # Assign(const field1& _f1, ...)
+    assign = MethodDefn(MethodDecl(assignvar.name,
+                                   params=fieldsAsParamList()))
+    assign.addstmts([ StmtExpr(ExprAssn(f.refExpr(), f.argVar()))
+                      for f in sd.fields ])
+    struct.addstmts([ assign, Whitespace.NL ])
+
+    # members
+    struct.addstmts([ StmtDecl(Decl(f.internalType(), f.memberVar().name))
+                      for f in sd.fields ])
+
+    return forwarddeclstmts, struct
 
 ##--------------------------------------------------
 
-def _generateCxxUnionStuff(ud):
+def _generateCxxUnion(ud):
     # This Union class basically consists of a type (enum) and a
     # union for storage.  The union can contain POD and non-POD
     # types.  Each type needs a copy ctor, assignment operator,
     # and dtor.
     #
     # Rather than templating this class and only providing
     # specializations for the types we support, which is slightly
     # "unsafe" in that C++ code can add additional specializations
@@ -1517,21 +1661,21 @@ def _generateCxxUnionStuff(ud):
             args.append(expectTypeVar)
         return ExprCall(func, args=args)
 
     def callMaybeDestroy(newTypeVar):
         return ExprCall(maybedtorvar, args=[ newTypeVar ])
 
     def maybeReconstruct(memb, newTypeVar):
         ifdied = StmtIf(callMaybeDestroy(newTypeVar))
-        ifdied.addifstmt(StmtExpr(memb.callPlacementCtor()))
+        ifdied.addifstmt(StmtExpr(memb.callCtor()))
         return ifdied
 
     # compute all the typedefs and forward decls we need to make
-    gettypedeps = _ComputeTypeDeps()
+    gettypedeps = _ComputeTypeDeps(ud.decl.type)
     for c in ud.components:
         c.ipdltype.accept(gettypedeps)
 
     usingTypedefs = gettypedeps.usingTypedefs
     forwarddeclstmts = gettypedeps.forwardDeclStmts
 
     # the |Type| enum, used to switch on the discunion's real type
     cls.addstmt(Label.PUBLIC)
@@ -1544,37 +1688,39 @@ def _generateCxxUnionStuff(ud):
     typeenum.addId(tfirstvar.name, firstid)
     typeenum.addId(tlastvar.name, ud.components[-1].enum())
     cls.addstmts([ StmtDecl(Decl(typeenum,'')),
                    Whitespace.NL ])
 
     cls.addstmt(Label.PRIVATE)
     cls.addstmts(
         usingTypedefs
-                # hacky typedef's that allow placement dtors of builtins
-        + [ Typedef(c.bareType(), c.typedef()) for c in ud.components ])
+        # hacky typedef's that allow placement dtors of builtins
+        + [ Typedef(c.internalType(), c.typedef()) for c in ud.components ])
     cls.addstmt(Whitespace.NL)
 
     # the C++ union the discunion use for storage
     valueunion = TypeUnion(valuetype.name)
     for c in ud.components:
         valueunion.addComponent(c.unionType(), c.name)
     cls.addstmts([ StmtDecl(Decl(valueunion,'')),
                        Whitespace.NL ])
 
     # for each constituent type T, add private accessors that
     # return a pointer to the Value union storage casted to |T*|
     # and |const T*|
     for c in ud.components:
         getptr = MethodDefn(MethodDecl(
-            c.getPtrName(), params=[ ], ret=c.ptrToType()))
+            c.getPtrName(), params=[ ], ret=c.ptrToInternalType(),
+            force_inline=1))
         getptr.addstmt(StmtReturn(c.ptrToSelfExpr()))
 
         getptrconst = MethodDefn(MethodDecl(
-            c.getConstPtrName(), params=[ ], ret=c.constPtrToType(), const=1))
+            c.getConstPtrName(), params=[ ], ret=c.constPtrToType(),
+            const=1, force_inline=1))
         getptrconst.addstmt(StmtReturn(c.constptrToSelfExpr()))
 
         cls.addstmts([ getptr, getptrconst ])
     cls.addstmt(Whitespace.NL)
 
     # add a helper method that invokes the placement dtor on the
     # current underlying value, only if |aNewType| is different
     # than the current type, and returns true if the underlying
@@ -1590,83 +1736,83 @@ def _generateCxxUnionStuff(ud):
     # same type, nothing to see here
     ifnochange = StmtIf(ExprBinary(mtypevar, '==', newtypevar))
     ifnochange.addifstmt(StmtReturn(ExprLiteral.FALSE))
     # need to destroy.  switch on underlying type
     dtorswitch = StmtSwitch(mtypevar)
     for c in ud.components:
         dtorswitch.addcase(
             CaseLabel(c.enum()),
-            StmtBlock([ StmtExpr(c.callPlacementDtor()),
+            StmtBlock([ StmtExpr(c.callDtor()),
                         StmtBreak() ]))
     dtorswitch.addcase(
         DefaultLabel(),
         StmtBlock([ _runtimeAbort("not reached"), StmtBreak() ]))
     maybedtor.addstmts([
         ifnone,
         ifnochange,
         dtorswitch,
         StmtReturn(ExprLiteral.TRUE)
     ])
     cls.addstmts([ maybedtor, Whitespace.NL ])
 
     # add helper methods that ensure the discunion has a
     # valid type
     sanity = MethodDefn(MethodDecl(
-        assertsanityvar.name, ret=Type.VOID, const=1))
+        assertsanityvar.name, ret=Type.VOID, const=1, force_inline=1))
     sanity.addstmts([
         _abortIfFalse(ExprBinary(tfirstvar, '<=', mtypevar),
                       'invalid type tag'),
         _abortIfFalse(ExprBinary(mtypevar, '<=', tlastvar),
                       'invalid type tag') ])
     cls.addstmt(sanity)
 
     atypevar = ExprVar('aType')
     sanity2 = MethodDefn(
         MethodDecl(assertsanityvar.name,
                        params=[ Decl(typetype, atypevar.name) ],
                        ret=Type.VOID,
-                       const=1))
+                       const=1, force_inline=1))
     sanity2.addstmts([
         StmtExpr(ExprCall(assertsanityvar)),
         _abortIfFalse(ExprBinary(mtypevar, '==', atypevar),
                       'unexpected type tag') ])
     cls.addstmts([ sanity2, Whitespace.NL ])
 
     ## ---- begin public methods -----
 
     # Union() default ctor
     cls.addstmts([
         Label.PUBLIC,
         ConstructorDefn(
-            ConstructorDecl(ud.name),
+            ConstructorDecl(ud.name, force_inline=1),
             memberinits=[ ExprMemberInit(mtypevar, [ tnonevar ]) ]),
         Whitespace.NL
     ])
 
     # Union(const T&) copy ctors
     othervar = ExprVar('aOther')
     for c in ud.components:
         copyctor = ConstructorDefn(ConstructorDecl(
             ud.name, params=[ Decl(c.inType(), othervar.name) ]))
         copyctor.addstmts([
-            StmtExpr(c.callPlacementCtor(othervar)),
+            StmtExpr(c.callCtor(othervar)),
             StmtExpr(ExprAssn(mtypevar, c.enumvar())) ])
         cls.addstmts([ copyctor, Whitespace.NL ])
 
     # Union(const Union&) copy ctor
     copyctor = ConstructorDefn(ConstructorDecl(
         ud.name, params=[ Decl(inClsType, othervar.name) ]))
     othertype = ud.callType(othervar)
     copyswitch = StmtSwitch(othertype)
     for c in ud.components:
         copyswitch.addcase(
             CaseLabel(c.enum()),
             StmtBlock([
-                StmtExpr(c.callPlacementCtor(
+                StmtExpr(c.callCtor(
                     ExprCall(ExprSelect(othervar,
                                         '.', c.getConstTypeName())))),
                 StmtBreak()
             ]))
     copyswitch.addcase(
         DefaultLabel(),
         StmtBlock([ _runtimeAbort('unreached'), StmtReturn() ]))
     copyctor.addstmts([
@@ -1677,17 +1823,18 @@ def _generateCxxUnionStuff(ud):
     cls.addstmts([ copyctor, Whitespace.NL ])
 
     # ~Union()
     dtor = DestructorDefn(DestructorDecl(ud.name))
     dtor.addstmt(StmtExpr(callMaybeDestroy(tnonevar)))
     cls.addstmts([ dtor, Whitespace.NL ])
 
     # type()
-    typemeth = MethodDefn(MethodDecl('type', ret=typetype, const=1))
+    typemeth = MethodDefn(MethodDecl('type', ret=typetype,
+                                     const=1, force_inline=1))
     typemeth.addstmt(StmtReturn(mtypevar))
     cls.addstmts([ typemeth, Whitespace.NL ])
 
     # Union& operator=(const T&) methods
     rhsvar = ExprVar('aRhs')
     for c in ud.components:
         opeq = MethodDefn(MethodDecl(
             'operator=',
@@ -1731,57 +1878,50 @@ def _generateCxxUnionStuff(ud):
     cls.addstmts([ opeq, Whitespace.NL ])
 
     # accessors for each type: operator T&, operator const T&,
     # T& get(), const T& get()
     for c in ud.components:
         getValueVar = ExprVar(c.getTypeName())
         getConstValueVar = ExprVar(c.getConstTypeName())
 
-        getvalue = MethodDefn(MethodDecl(getValueVar.name, ret=c.refType()))
+        getvalue = MethodDefn(MethodDecl(getValueVar.name,
+                                         ret=c.refType(),
+                                         force_inline=1))
         getvalue.addstmts([
             StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
             StmtReturn(ExprDeref(c.callGetPtr()))
         ])
 
         getconstvalue = MethodDefn(MethodDecl(
-            getConstValueVar.name, ret=c.constRefType(), const=1))
+            getConstValueVar.name, ret=c.constRefType(),
+            const=1, force_inline=1))
         getconstvalue.addstmts([
             StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
             StmtReturn(ExprDeref(c.callGetConstPtr()))
         ])
 
-        optype = MethodDefn(MethodDecl('', typeop=c.refType()))
+        optype = MethodDefn(MethodDecl('', typeop=c.refType(), force_inline=1))
         optype.addstmt(StmtReturn(ExprCall(getValueVar)))
         opconsttype = MethodDefn(MethodDecl(
-            '', const=1, typeop=c.constRefType()))
+            '', const=1, typeop=c.constRefType(), force_inline=1))
         opconsttype.addstmt(StmtReturn(ExprCall(getConstValueVar)))
 
         cls.addstmts([ getvalue, getconstvalue,
                        optype, opconsttype,
                        Whitespace.NL ])
 
     # private vars
     cls.addstmts([
         Label.PRIVATE,
         StmtDecl(Decl(valuetype, mvaluevar.name)),
         StmtDecl(Decl(typetype, mtypevar.name))
     ])
 
-    return (
-        [
-            Whitespace("""
-//-----------------------------------------------------------------------------
-// Definition of the IPDL type |union %s|
-//
-"""% (ud.name))
-        ]
-        + forwarddeclstmts
-        + [  _putInNamespaces(cls, ud.namespaces) ])
-
+    return forwarddeclstmts, cls
 
 ##-----------------------------------------------------------------------------
 
 class _FindFriends(ipdl.ast.Visitor):
     def __init__(self):
         self.mytype = None              # ProtocolType
         self.vtype = None               # ProtocolType
         self.friends = set()            # set<ProtocolType>
@@ -1875,17 +2015,17 @@ class _GenerateProtocolActorCode(ipdl.as
             ])
 
         for pinc in tu.protocolIncludes:
             pinc.accept(self)
 
         # this generates the actor's full impl in self.cls
         tu.protocol.accept(self)
 
-        clsdecl, clsdefn = _ClassDeclDefn().split(self.cls)
+        clsdecl, clsdefn = _splitClassDeclDefn(self.cls)
 
         # XXX damn C++ ... return types in the method defn aren't in
         # class scope
         for stmt in clsdefn.stmts:
             if isinstance(stmt, MethodDefn):
                 if stmt.decl.ret and stmt.decl.ret.name == 'Result':
                     stmt.decl.ret.name = clsdecl.name +'::'+ stmt.decl.ret.name
 
@@ -1952,17 +2092,17 @@ class _GenerateProtocolActorCode(ipdl.as
             Whitespace.NL
         ])
 
 
     def visitProtocolInclude(self, pi):
         ip = pi.tu.protocol
 
         self.hdrfile.addthings([
-            _makeForwardDecl(ip.decl.type, self.side),
+            _makeForwardDeclForActor(ip.decl.type, self.side),
             Whitespace.NL
         ])
         self.protocolCxxIncludes.append(
             CppDirective(
                 'include',
                 '"%s.h"'% (_protocolHeaderName(ip, self.side))))
 
         if ip.decl.fullname is not None:
@@ -2006,17 +2146,17 @@ class _GenerateProtocolActorCode(ipdl.as
         friends.update(ptype.manages)
 
         # don't friend ourself if we're a self-managed protocol
         friends.discard(ptype)
 
         for friend in friends:
             self.hdrfile.addthings([
                 Whitespace.NL,
-                _makeForwardDecl(friend, self.prettyside),
+                _makeForwardDeclForActor(friend, self.prettyside),
                 Whitespace.NL
             ])
             self.cls.addstmts([
                 FriendClassDecl(_actorName(friend.fullname(),
                                            self.prettyside)),
                 Whitespace.NL ])
 
         self.cls.addstmt(Label.PROTECTED)
@@ -3318,34 +3458,32 @@ class _GenerateProtocolActorCode(ipdl.as
         itervar = self.itervar
         lenvar = ExprVar('length')
         ivar = ExprVar('i')
         eltipdltype = arraytype.basetype
         intype = _cxxConstRefType(arraytype, self.side)
         outtype = _cxxPtrToType(arraytype, self.side)
 
         write = MethodDefn(self.writeMethodDecl(intype, var))
-        # FIXME hacky init of |i|
         forwrite = StmtFor(init=ExprAssn(Decl(Type.UINT32, ivar.name),
                                          ExprLiteral.ZERO),
                            cond=ExprBinary(ivar, '<', lenvar),
                            update=ExprPrefixUnop(ivar, '++'))
         forwrite.addstmt(StmtExpr(
             self.write(eltipdltype, ExprIndex(var, ivar), msgvar)))
         write.addstmts([
             StmtDecl(Decl(Type.UINT32, lenvar.name),
                      init=_callCxxArrayLength(var)),
             StmtExpr(self.write(None, lenvar, msgvar)),
             Whitespace.NL,
             forwrite
         ])
 
         read = MethodDefn(self.readMethodDecl(outtype, var))
         avar = ExprVar('a')
-        # FIXME hacky init of |i|
         forread = StmtFor(init=ExprAssn(Decl(Type.UINT32, ivar.name),
                                         ExprLiteral.ZERO),
                           cond=ExprBinary(ivar, '<', lenvar),
                           update=ExprPrefixUnop(ivar, '++'))
         forread.addstmt(
             self.checkedRead(eltipdltype, ExprAddrOf(ExprIndex(avar, ivar)),
                              msgvar, itervar, errfnRead))
         read.addstmts([
@@ -3415,17 +3553,17 @@ class _GenerateProtocolActorCode(ipdl.as
         intype = _cxxConstRefType(structtype, self.side)
         outtype = _cxxPtrToType(structtype, self.side)
         sd = _typeToAST[structtype]
 
         write = MethodDefn(self.writeMethodDecl(intype, var))
         read = MethodDefn(self.readMethodDecl(outtype, var))        
 
         def get(sel, f):
-            return ExprSelect(var, sel, f.name)
+            return ExprCall(f.getMethod(thisexpr=var, sel=sel))
 
         for f in sd.fields:
             writefield = StmtExpr(self.write(f.ipdltype, get('.', f), msgvar))
             readfield = self.checkedRead(f.ipdltype,
                                          ExprAddrOf(get('->', f)),
                                          msgvar, itervar,
                                          errfn=errfnRead)
             if f.special and f.side != self.side:
@@ -4157,42 +4295,44 @@ class _GenerateProtocolChildCode(_Genera
     def receivesMessage(self, md):
         return md.decl.type.isInout() or md.decl.type.isOut()
 
 
 ##-----------------------------------------------------------------------------
 ## Utility passes
 ##
 
-class _ClassDeclDefn:
-    def split(self, cls):
-        """Warning: destructively splits |cls|!"""
-        defns = Block()
-
-        for i, stmt in enumerate(cls.stmts):
-            if isinstance(stmt, MethodDefn) and not stmt.decl.inline:
-                decl, defn = self.splitMethodDefn(stmt, cls.name)
-                cls.stmts[i] = StmtDecl(decl)
-                defns.addstmts([ defn, Whitespace.NL ])
-
-        return cls, defns
-
-    def splitMethodDefn(self, md, clsname):
-        saveddecl = deepcopy(md.decl)
-        md.decl.name = (clsname +'::'+ md.decl.name)
-        md.decl.virtual = 0
-        md.decl.static = 0
-        md.decl.warn_unused = 0
-        for param in md.decl.params:
-            if isinstance(param, Param):
-                param.default = None
-        return saveddecl, md
-
-
-# XXX this is tantalizingly similar to _SplitDeclDefn, but just
+def _splitClassDeclDefn(cls, inlinedefns=0):
+    """Destructively split |cls| methods into declarations and
+definitions (if |not methodDecl.force_inline|).  Return classDecl,
+methodDefns."""
+    defns = Block()
+
+    for i, stmt in enumerate(cls.stmts):
+        if isinstance(stmt, MethodDefn) and not stmt.decl.force_inline:
+            decl, defn = _splitMethodDefn(stmt, cls.name, inlinedefns)
+            cls.stmts[i] = StmtDecl(decl)
+            defns.addstmts([ defn, Whitespace.NL ])
+
+    return cls, defns
+
+def _splitMethodDefn(md, clsname, inlinedefn):
+    saveddecl = deepcopy(md.decl)
+    md.decl.name = (clsname +'::'+ md.decl.name)
+    md.decl.virtual = 0
+    md.decl.static = 0
+    md.decl.warn_unused = 0
+    md.decl.inline = inlinedefn
+    for param in md.decl.params:
+        if isinstance(param, Param):
+            param.default = None
+    return saveddecl, md
+
+
+# XXX this is tantalizingly similar to _splitClassDeclDefn, but just
 # different enough that I don't see the need to define
 # _GenerateSkeleton in terms of that
 class _GenerateSkeletonImpl(Visitor):
     def __init__(self, name, namespaces):
         self.name = name
         self.cls = None
         self.namespaces = namespaces
         self.methodimpls = Block()
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -27,17 +27,17 @@
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 import os, sys
 
-from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, State, TransitionStmt, TypeSpec, UsingStmt, Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT, ANSWER, CALL, RECV, SEND
+from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, State, StructDecl, TransitionStmt, TypeSpec, UnionDecl, UsingStmt, Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT, ANSWER, CALL, RECV, SEND
 import ipdl.builtin as builtin
 
 _DELETE_MSG = '__delete__'
 
 
 def _otherside(side):
     if side == 'parent':  return 'child'
     elif side == 'child': return 'parent'
@@ -51,16 +51,19 @@ def unique_pairs(s):
 
 def cartesian_product(s1, s2):
     for e1 in s1:
         for e2 in s2:
             yield (e1, e2)
 
 
 class TypeVisitor:
+    def __init__(self):
+        self.visited = set()
+
     def defaultVisit(self, node, *args):
         raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
             node.__class__.__name__)
 
     def visitVoidType(self, v, *args):
         pass
 
     def visitBuiltinCxxType(self, t, *args):
@@ -85,50 +88,59 @@ class TypeVisitor:
         # could result in an infinite loop
         pass
 
     def visitActorType(self, a, *args):
         a.protocol.accept(self, *args)
         a.state.accept(self, *args)
 
     def visitStructType(self, s, *args):
+        if s in self.visited:
+            return
+
+        self.visited.add(s)
         for field in s.fields:
             field.accept(self, *args)
 
-    def visitFieldType(self, f, *args):
-        f.type.accept(self, *args)
+    def visitUnionType(self, u, *args):
+        if u in self.visited:
+            return
 
-    def visitUnionType(self, u, *args):
+        self.visited.add(u)
         for component in u.components:
             component.accept(self, *args)
 
     def visitArrayType(self, a, *args):
         a.basetype.accept(self, *args)
 
     def visitShmemType(self, s, *args):
         pass
 
     def visitShmemChmodType(self, c, *args):
         c.shmem.accept(self)
 
+
 class Type:
     def __cmp__(self, o):
         return cmp(self.fullname(), o.fullname())
     def __eq__(self, o):
         return (self.__class__ == o.__class__
                 and self.fullname() == o.fullname())
     def __hash__(self):
         return hash(self.fullname())
 
     # Is this a C++ type?
     def isCxx(self):
         return False
     # Is this an IPDL type?
     def isIPDL(self):
         return False
+    # Is this type neither compound nor an array?
+    def isAtom(self):
+        return False
     # Can this type appear in IPDL programs?
     def isVisible(self):
         return False
     def isVoid(self):
         return False
     def typename(self):
         return self.__class__.__name__
 
@@ -137,35 +149,38 @@ class Type:
 
     def accept(self, visitor, *args):
         visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
         if visit is None:
             return getattr(visitor, 'defaultVisit')(self, *args)
         return visit(self, *args)
 
 class VoidType(Type):
-    # the following are not type-o's (hah): void is both a Cxx and IPDL type
-    def isCxx():
+    def isCxx(self):
         return True
-    def isIPDL():
+    def isIPDL(self):
+        return False
+    def isAtom(self):
         return True
     def isVisible(self):
-        return True
+        return False
     def isVoid(self):
         return True
 
     def name(self): return 'void'
     def fullname(self): return 'void'
 
 VOID = VoidType()
 
 ##--------------------
 class CxxType(Type):
     def isCxx(self):
         return True
+    def isAtom(self):
+        return True
     def isBuiltin(self):
         return False
     def isImported(self):
         return False
     def isGenerated(self):
         return False
     def isVisible(self):
         return True
@@ -200,16 +215,18 @@ class IPDLType(Type):
     def isVisible(self): return True
     def isState(self): return False
     def isMessage(self): return False
     def isProtocol(self): return False
     def isActor(self): return False
     def isStruct(self): return False
     def isUnion(self): return False
     def isArray(self): return False
+    def isAtom(self):  return True
+    def isCompound(self): return False
     def isShmem(self): return False
     def isChmod(self): return False
 
     def isAsync(self): return self.sendSemantics is ASYNC
     def isSync(self): return self.sendSemantics is SYNC
     def isRpc(self): return self.sendSemantics is RPC
 
     def talksAsync(self): return True
@@ -335,71 +352,119 @@ class ActorType(IPDLType):
         self.nullable = nullable
     def isActor(self): return True
 
     def name(self):
         return self.protocol.name()
     def fullname(self):
         return self.protocol.fullname()
 
-class StructType(IPDLType):
+class _CompoundType(IPDLType):
+    def __init__(self):
+        self.defined = False            # bool
+        self.mutualRec = set()          # set(_CompoundType | ArrayType)
+    def isAtom(self):
+        return False
+    def isCompound(self):
+        return True
+    def itercomponents(self):
+        raise '"pure virtual" method'
+
+    def mutuallyRecursiveWith(self, t, exploring=None):
+        '''|self| is mutually recursive with |t| iff |self| and |t|
+are in a cycle in the type graph rooted at |self|.  This function
+looks for such a cycle and returns True if found.'''
+        if exploring is None:
+            exploring = set()
+
+        if t.isAtom():
+            return False
+        elif t is self or t in self.mutualRec:
+            return True
+        elif t.isArray():
+            isrec = self.mutuallyRecursiveWith(t.basetype, exploring)
+            if isrec:  self.mutualRec.add(t)
+            return isrec
+        elif t in exploring:
+            return False
+
+        exploring.add(t)
+        for c in t.itercomponents():
+            if self.mutuallyRecursiveWith(c, exploring):
+                self.mutualRec.add(c)
+                return True
+        exploring.remove(t)
+
+        return False
+
+class StructType(_CompoundType):
     def __init__(self, qname, fields):
+        _CompoundType.__init__(self)
         self.qname = qname
         self.fields = fields            # [ Type ]
 
-    def isStruct(self): return True
+    def isStruct(self):   return True
+    def itercomponents(self):
+        for f in self.fields:
+            yield f
+    
     def name(self): return self.qname.baseid
     def fullname(self): return str(self.qname)
 
-class UnionType(IPDLType):
+class UnionType(_CompoundType):
     def __init__(self, qname, components):
+        _CompoundType.__init__(self)
         self.qname = qname
-        self.components = components
+        self.components = components    # [ Type ]
 
-    def isUnion(self): return True
+    def isUnion(self):    return True
+    def itercomponents(self):
+        for c in self.components:
+            yield c
+
     def name(self): return self.qname.baseid
     def fullname(self): return str(self.qname)
 
 class ArrayType(IPDLType):
     def __init__(self, basetype):
         self.basetype = basetype
+    def isAtom(self):  return False
+    def isArray(self): return True
 
-    def isArray(self): return True
     def name(self): return self.basetype.name() +'[]'
     def fullname(self): return self.basetype.fullname() +'[]'
 
 class ShmemType(IPDLType):
     def __init__(self, qname):
         self.qname = qname
     def isShmem(self): return True
 
     def name(self):
         return self.qname.baseid
     def fullname(self):
         return str(self.qname)
 
-def iteractortypes(type):
+def iteractortypes(t, visited=None):
     """Iterate over any actor(s) buried in |type|."""
+    if visited is None:
+        visited = set()
+
     # XXX |yield| semantics makes it hard to use TypeVisitor
-    if not type or not type.isIPDL():
+    if not t.isIPDL():
         return
-    elif type.isActor():
-        yield type
-    elif type.isArray():
-        for actor in iteractortypes(type.basetype):
+    elif t.isActor():
+        yield t
+    elif t.isArray():
+        for actor in iteractortypes(t.basetype, visited):
             yield actor
-    elif type.isStruct():
-        for f in type.fields:
-            for actor in iteractortypes(f):
+    elif t.isCompound() and t not in visited:
+        visited.add(t)
+        for c in t.itercomponents():
+            for actor in iteractortypes(c, visited):
                 yield actor
-    elif type.isUnion():
-        for c in type.components:
-            for actor in iteractortypes(c):
-                yield actor
-
 
 def hasactor(type):
     """Return true iff |type| is an actor or has one buried within."""
     for _ in iteractortypes(type): return True
     return False
 
 def hasshmem(type):
     """Return true iff |type| is shmem or has it buried within."""
@@ -608,16 +673,38 @@ class GatherDecls(TcheckVisitor):
         # make sure we have decls for all dependent protocols
         for pinc in tu.protocolIncludes:
             pinc.accept(self)
 
         # declare imported (and builtin) C++ types
         for using in tu.using:
             using.accept(self)
 
+        # first pass to "forward-declare" all structs and unions in
+        # order to support recursive definitions
+        for su in tu.structsAndUnions:
+            qname = su.qname()
+            if 0 == len(qname.quals):
+                fullname = None
+            else:
+                fullname = str(qname)
+
+            if isinstance(su, StructDecl):
+                sutype = StructType(qname, [ ])
+            elif isinstance(su, UnionDecl):
+                sutype = UnionType(qname, [ ])
+            else: assert 0 and 'unknown type'
+
+            su.decl = self.declare(
+                loc=su.loc,
+                type=sutype,
+                shortname=su.name,
+                fullname=fullname)
+
+        # second pass to check each definition
         for su in tu.structsAndUnions:
             su.accept(self)
 
         # grab symbols in the protocol itself
         p.accept(self)
 
         tu.type = VOID
 
@@ -629,27 +716,16 @@ class GatherDecls(TcheckVisitor):
             self.error(
                 pi.loc,
                 "(type checking here will be unreliable because of an earlier error)")
             return
         pi.tu.accept(self)
         self.symtab.declare(pi.tu.protocol.decl)
 
     def visitStructDecl(self, sd):
-        qname = sd.qname()
-        if 0 == len(qname.quals):
-            fullname = None
-        else:
-            fullname = str(qname)
-
-        sd.decl = self.declare(
-            loc=sd.loc,
-            type=StructType(qname, [ ]),
-            shortname=sd.name,
-            fullname=fullname)
         stype = sd.decl.type
 
         self.symtab.enterScope(sd)
 
         for f in sd.fields:
             ftypedecl = self.symtab.lookup(str(f.type))
             if ftypedecl is None:
                 self.error(f.loc, "field `%s' of struct `%s' has unknown type `%s'",
@@ -661,36 +737,25 @@ class GatherDecls(TcheckVisitor):
                 type=self._canonicalType(ftypedecl.type, f.type),
                 shortname=f.name,
                 fullname=None)
             stype.fields.append(f.decl.type)
 
         self.symtab.exitScope(sd)
 
     def visitUnionDecl(self, ud):
-        qname = ud.qname()
-        if 0 == len(qname.quals):
-            fullname = None
-        else:
-            fullname = str(qname)
-        components = [ ]
-
+        utype = ud.decl.type
+        
         for c in ud.components:
             cdecl = self.symtab.lookup(str(c))
             if cdecl is None:
                 self.error(c.loc, "unknown component type `%s' of union `%s'",
                            str(c), ud.name)
                 continue
-            components.append(self._canonicalType(cdecl.type, c))
-
-        ud.decl = self.declare(
-            loc=ud.loc,
-            type=UnionType(qname, components),
-            shortname=ud.name,
-            fullname=fullname)
+            utype.components.append(self._canonicalType(cdecl.type, c))
 
     def visitUsingStmt(self, using):
         fullname = str(using.type)
         if using.type.basename() == fullname:
             fullname = None
         if fullname == 'mozilla::ipc::Shmem':
             ipdltype = ShmemType(using.type.spec)
         else:
@@ -939,45 +1004,37 @@ class GatherDecls(TcheckVisitor):
                               ctor=isctor, dtor=isdtor, cdtype=cdtype)
 
         # replace inparam Param nodes with proper Decls
         def paramToDecl(param):
             ptname = param.typespec.basename()
             ploc = param.typespec.loc
 
             ptdecl = self.symtab.lookup(ptname)
-
             if ptdecl is None:
                 self.error(
                     ploc,
                     "argument typename `%s' of message `%s' has not been declared",
                     ptname, msgname)
-                return None
+                ptype = VOID
             else:
                 ptype = self._canonicalType(ptdecl.type, param.typespec,
                                             chmodallowed=1)
-                return self.declare(
-                    loc=ploc,
-                    type=ptype,
-                    progname=param.name)
+            return self.declare(loc=ploc,
+                                type=ptype,
+                                progname=param.name)
 
         for i, inparam in enumerate(md.inParams):
             pdecl = paramToDecl(inparam)
-            if pdecl is not None:
-                msgtype.params.append(pdecl.type)
-                md.inParams[i] = pdecl
-            else:
-                md.inParams[i].type = None
+            msgtype.params.append(pdecl.type)
+            md.inParams[i] = pdecl
         for i, outparam in enumerate(md.outParams):
             pdecl = paramToDecl(outparam)
-            if pdecl is not None:
-                msgtype.returns.append(pdecl.type)
-                md.outParams[i] = pdecl
-            else:
-                md.outParams[i].type = None
+            msgtype.returns.append(pdecl.type)
+            md.outParams[i] = pdecl
 
         self.symtab.exitScope(md)
 
         md.decl = self.declare(
             loc=loc,
             type=msgtype,
             progname=msgname)
         md.protocolDecl = self.currentProtocolDecl
@@ -1091,30 +1148,80 @@ def checkcycles(p, stack=None):
 
 def formatcycles(cycles):
     r = []
     for cycle in cycles:
         s = " -> ".join([ptype.name() for ptype in cycle])
         r.append("`%s'" % s)
     return ", ".join(r)
 
+
+def fullyDefined(t, exploring=None):
+    '''The rules for "full definition" of a type are
+  defined(atom)             := true
+  defined(array basetype)   := defined(basetype)
+  defined(struct f1 f2...)  := defined(f1) and defined(f2) and ...
+  defined(union c1 c2 ...)  := defined(c1) or defined(c2) or ...
+'''
+    if exploring is None:
+        exploring = set()
+
+    if t.isAtom():
+        return True
+    elif t.isArray():
+        return fullyDefined(t.basetype, exploring)
+    elif t.defined:
+        return True
+    assert t.isCompound()
+
+    if t in exploring:
+        return False
+
+    exploring.add(t)
+    for c in t.itercomponents():
+        cdefined = fullyDefined(c, exploring)
+        if t.isStruct() and not cdefined:
+            t.defined = False
+            break
+        elif t.isUnion() and cdefined:
+            t.defined = True
+            break
+    else:
+        if t.isStruct():   t.defined = True
+        elif t.isUnion():  t.defined = False
+    exploring.remove(t)
+
+    return t.defined
+
+
 class CheckTypes(TcheckVisitor):
     def __init__(self, errors):
         # don't need the symbol table, we just want the error reporting
         TcheckVisitor.__init__(self, None, errors)
         self.visited = set()
         self.ptype = None
 
     def visitProtocolInclude(self, inc):
         if inc.tu.filename in self.visited:
             return
         self.visited.add(inc.tu.filename)
         inc.tu.protocol.accept(self)
 
 
+    def visitStructDecl(self, sd):
+        if not fullyDefined(sd.decl.type):
+            self.error(sd.decl.loc,
+                       "struct `%s' is only partially defined", sd.name)
+
+    def visitUnionDecl(self, ud):
+        if not fullyDefined(ud.decl.type):
+            self.error(ud.decl.loc,
+                       "union `%s' is only partially defined", ud.name)
+
+
     def visitProtocol(self, p):
         self.ptype = p.decl.type
         
         # check that we require no more "power" than our manager protocols
         ptype, pname = p.decl.type, p.decl.shortname
 
         if len(p.spawnsStmts) and not ptype.isToplevel():
             self.error(p.decl.loc,