Skip to content
Snippets Groups Projects
writer.go 60 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		w.stmts(block.List)
    		w.pos(block.Rbrace)
    	}
    
    
    	return w.Flush(), w.closureVars
    
    }
    
    func (w *writer) funcargs(sig *types2.Signature) {
    	do := func(params *types2.Tuple, result bool) {
    		for i := 0; i < params.Len(); i++ {
    			w.funcarg(params.At(i), result)
    		}
    	}
    
    	if recv := sig.Recv(); recv != nil {
    		w.funcarg(recv, false)
    	}
    	do(sig.Params(), false)
    	do(sig.Results(), true)
    }
    
    func (w *writer) funcarg(param *types2.Var, result bool) {
    	if param.Name() != "" || result {
    		w.addLocal(param)
    	}
    }
    
    
    // addLocal records the declaration of a new local variable.
    
    func (w *writer) addLocal(obj *types2.Var) {
    
    	w.Sync(pkgbits.SyncAddLocal)
    
    	if w.localsIdx == nil {
    		w.localsIdx = make(map[*types2.Var]int)
    	}
    
    // useLocal writes a reference to the given local or free variable
    // into the bitstream.
    
    func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) {
    
    	w.Sync(pkgbits.SyncUseObjLocal)
    
    	if idx, ok := w.localsIdx[obj]; w.Bool(ok) {
    		w.Len(idx)
    
    		return
    	}
    
    	idx, ok := w.closureVarsIdx[obj]
    	if !ok {
    		if w.closureVarsIdx == nil {
    			w.closureVarsIdx = make(map[*types2.Var]int)
    		}
    		idx = len(w.closureVars)
    
    		w.closureVars = append(w.closureVars, posVar{pos, obj})
    
    }
    
    func (w *writer) openScope(pos syntax.Pos) {
    
    	w.Sync(pkgbits.SyncOpenScope)
    
    	w.pos(pos)
    }
    
    func (w *writer) closeScope(pos syntax.Pos) {
    
    	w.Sync(pkgbits.SyncCloseScope)
    
    	w.pos(pos)
    	w.closeAnotherScope()
    }
    
    func (w *writer) closeAnotherScope() {
    
    	w.Sync(pkgbits.SyncCloseAnotherScope)
    
    // stmt writes the given statement into the function body bitstream.
    
    func (w *writer) stmt(stmt syntax.Stmt) {
    	var stmts []syntax.Stmt
    	if stmt != nil {
    		stmts = []syntax.Stmt{stmt}
    	}
    	w.stmts(stmts)
    }
    
    func (w *writer) stmts(stmts []syntax.Stmt) {
    
    	for _, stmt := range stmts {
    		w.stmt1(stmt)
    	}
    
    	w.Code(stmtEnd)
    	w.Sync(pkgbits.SyncStmtsEnd)
    
    }
    
    func (w *writer) stmt1(stmt syntax.Stmt) {
    	switch stmt := stmt.(type) {
    	default:
    		w.p.unexpected("statement", stmt)
    
    	case nil, *syntax.EmptyStmt:
    		return
    
    	case *syntax.AssignStmt:
    		switch {
    		case stmt.Rhs == nil:
    
    			w.op(binOps[stmt.Op])
    			w.expr(stmt.Lhs)
    			w.pos(stmt)
    
    		case stmt.Op != 0 && stmt.Op != syntax.Def:
    
    			w.op(binOps[stmt.Op])
    			w.expr(stmt.Lhs)
    			w.pos(stmt)
    
    
    			var typ types2.Type
    			if stmt.Op != syntax.Shl && stmt.Op != syntax.Shr {
    				typ = w.p.typeOf(stmt.Lhs)
    			}
    
    			w.implicitConvExpr(stmt, typ, stmt.Rhs)
    
    			w.assignStmt(stmt, stmt.Lhs, stmt.Rhs)
    
    		w.blockStmt(stmt)
    
    	case *syntax.BranchStmt:
    
    		w.pos(stmt)
    		w.op(branchOps[stmt.Tok])
    		w.optLabel(stmt.Label)
    
    	case *syntax.CallStmt:
    
    		w.pos(stmt)
    		w.op(callOps[stmt.Tok])
    		w.expr(stmt.Call)
    
    	case *syntax.DeclStmt:
    		for _, decl := range stmt.DeclList {
    			w.declStmt(decl)
    		}
    
    	case *syntax.ExprStmt:
    
    		w.expr(stmt.X)
    
    	case *syntax.ForStmt:
    
    		w.forStmt(stmt)
    
    	case *syntax.IfStmt:
    
    		w.ifStmt(stmt)
    
    	case *syntax.LabeledStmt:
    
    		w.pos(stmt)
    		w.label(stmt.Label)
    		w.stmt1(stmt.Stmt)
    
    	case *syntax.ReturnStmt:
    
    		dstType := func(i int) types2.Type {
    			return resultTypes.At(i).Type()
    
    		w.multiExpr(stmt, dstType, unpackListExpr(stmt.Results))
    
    		w.selectStmt(stmt)
    
    	case *syntax.SendStmt:
    
    		chanType := types2.CoreType(w.p.typeOf(stmt.Chan)).(*types2.Chan)
    
    
    		w.implicitConvExpr(stmt, chanType.Elem(), stmt.Value)
    
    		w.switchStmt(stmt)
    	}
    }
    
    func (w *writer) assignList(expr syntax.Expr) {
    	exprs := unpackListExpr(expr)
    
    		w.assign(expr)
    	}
    }
    
    func (w *writer) assign(expr syntax.Expr) {
    	expr = unparen(expr)
    
    	if name, ok := expr.(*syntax.Name); ok {
    		if name.Value == "_" {
    			w.Code(assignBlank)
    			return
    
    		if obj, ok := w.p.info.Defs[name]; ok {
    			obj := obj.(*types2.Var)
    
    			w.Code(assignDef)
    			w.pos(obj)
    			w.localIdent(obj)
    			w.typ(obj.Type())
    
    			// TODO(mdempsky): Minimize locals index size by deferring
    			// this until the variables actually come into scope.
    			w.addLocal(obj)
    			return
    		}
    
    }
    
    func (w *writer) declStmt(decl syntax.Decl) {
    	switch decl := decl.(type) {
    	default:
    		w.p.unexpected("declaration", decl)
    
    
    	case *syntax.ConstDecl, *syntax.TypeDecl:
    
    		w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values)
    
    // assignStmt writes out an assignment for "lhs = rhs".
    
    func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) {
    	lhs := unpackListExpr(lhs0)
    	rhs := unpackListExpr(rhs0)
    
    
    
    	// As if w.assignList(lhs0).
    	w.Len(len(lhs))
    	for _, expr := range lhs {
    		w.assign(expr)
    	}
    
    
    	dstType := func(i int) types2.Type {
    		dst := lhs[i]
    
    		// Finding dstType is somewhat involved, because for VarDecl
    		// statements, the Names are only added to the info.{Defs,Uses}
    		// maps, not to info.Types.
    		if name, ok := unparen(dst).(*syntax.Name); ok {
    			if name.Value == "_" {
    				return nil // ok: no implicit conversion
    			} else if def, ok := w.p.info.Defs[name].(*types2.Var); ok {
    				return def.Type()
    			} else if use, ok := w.p.info.Uses[name].(*types2.Var); ok {
    				return use.Type()
    
    				w.p.fatalf(dst, "cannot find type of destination object: %v", dst)
    
    func (w *writer) blockStmt(stmt *syntax.BlockStmt) {
    
    	w.Sync(pkgbits.SyncBlockStmt)
    
    	w.openScope(stmt.Pos())
    	w.stmts(stmt.List)
    	w.closeScope(stmt.Rbrace)
    }
    
    func (w *writer) forStmt(stmt *syntax.ForStmt) {
    
    	w.Sync(pkgbits.SyncForStmt)
    
    	if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) {
    
    
    		xtyp := w.p.typeOf(rang.X)
    		if _, isMap := types2.CoreType(xtyp).(*types2.Map); isMap {
    			w.rtype(xtyp)
    		}
    
    		{
    			lhs := unpackListExpr(rang.Lhs)
    			assign := func(i int, src types2.Type) {
    				if i >= len(lhs) {
    					return
    				}
    				dst := unparen(lhs[i])
    				if name, ok := dst.(*syntax.Name); ok && name.Value == "_" {
    					return
    				}
    
    				var dstType types2.Type
    				if rang.Def {
    					// For `:=` assignments, the LHS names only appear in Defs,
    					// not Types (as used by typeOf).
    					dstType = w.p.info.Defs[dst.(*syntax.Name)].(*types2.Var).Type()
    				} else {
    					dstType = w.p.typeOf(dst)
    				}
    
    				w.convRTTI(src, dstType)
    			}
    
    			keyType, valueType := w.p.rangeTypes(rang.X)
    			assign(0, keyType)
    			assign(1, valueType)
    		}
    
    
    	} else {
    		w.pos(stmt)
    		w.stmt(stmt.Init)
    
    		w.stmt(stmt.Post)
    	}
    
    	w.blockStmt(stmt.Body)
    	w.closeAnotherScope()
    }
    
    
    // rangeTypes returns the types of values produced by ranging over
    // expr.
    func (pw *pkgWriter) rangeTypes(expr syntax.Expr) (key, value types2.Type) {
    	typ := pw.typeOf(expr)
    	switch typ := types2.CoreType(typ).(type) {
    	case *types2.Pointer: // must be pointer to array
    		return types2.Typ[types2.Int], types2.CoreType(typ.Elem()).(*types2.Array).Elem()
    	case *types2.Array:
    		return types2.Typ[types2.Int], typ.Elem()
    	case *types2.Slice:
    		return types2.Typ[types2.Int], typ.Elem()
    	case *types2.Basic:
    		if typ.Info()&types2.IsString != 0 {
    			return types2.Typ[types2.Int], runeTypeName.Type()
    		}
    	case *types2.Map:
    		return typ.Key(), typ.Elem()
    	case *types2.Chan:
    		return typ.Elem(), nil
    	}
    	pw.fatalf(expr, "unexpected range type: %v", typ)
    	panic("unreachable")
    }
    
    
    func (w *writer) ifStmt(stmt *syntax.IfStmt) {
    
    	w.Sync(pkgbits.SyncIfStmt)
    
    	w.openScope(stmt.Pos())
    	w.pos(stmt)
    	w.stmt(stmt.Init)
    	w.expr(stmt.Cond)
    	w.blockStmt(stmt.Then)
    	w.stmt(stmt.Else)
    	w.closeAnotherScope()
    }
    
    func (w *writer) selectStmt(stmt *syntax.SelectStmt) {
    
    	w.Sync(pkgbits.SyncSelectStmt)
    
    	for i, clause := range stmt.Body {
    		if i > 0 {
    			w.closeScope(clause.Pos())
    		}
    		w.openScope(clause.Pos())
    
    		w.pos(clause)
    		w.stmt(clause.Comm)
    		w.stmts(clause.Body)
    	}
    	if len(stmt.Body) > 0 {
    		w.closeScope(stmt.Rbrace)
    	}
    }
    
    func (w *writer) switchStmt(stmt *syntax.SwitchStmt) {
    
    	w.Sync(pkgbits.SyncSwitchStmt)
    
    
    	w.openScope(stmt.Pos())
    	w.pos(stmt)
    	w.stmt(stmt.Init)
    
    	if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) {
    
    		if tag := guard.Lhs; w.Bool(tag != nil) {
    
    		tag := stmt.Tag
    
    		if tag != nil {
    			tagType = w.p.typeOf(tag)
    		} else {
    			tagType = types2.Typ[types2.Bool]
    		}
    
    		// Walk is going to emit comparisons between the tag value and
    		// each case expression, and we want these comparisons to always
    		// have the same type. If there are any case values that can't be
    		// converted to the tag value's type, then convert everything to
    		// `any` instead.
    	Outer:
    		for _, clause := range stmt.Body {
    			for _, cas := range unpackListExpr(clause.Cases) {
    				if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) {
    					tagType = types2.NewInterfaceType(nil, nil)
    					break Outer
    				}
    			}
    		}
    
    		if w.Bool(tag != nil) {
    			w.implicitConvExpr(tag, tagType, tag)
    		}
    
    	for i, clause := range stmt.Body {
    		if i > 0 {
    			w.closeScope(clause.Pos())
    		}
    		w.openScope(clause.Pos())
    
    		w.pos(clause)
    
    		cases := unpackListExpr(clause.Cases)
    
    		if iface != nil {
    			w.Len(len(cases))
    			for _, cas := range cases {
    
    				if w.Bool(isNil(w.p.info, cas)) {
    					continue
    				}
    				w.exprType(iface, cas)
    
    			// As if w.exprList(clause.Cases),
    			// but with implicit conversions to tagType.
    
    			w.Sync(pkgbits.SyncExprList)
    			w.Sync(pkgbits.SyncExprs)
    			w.Len(len(cases))
    			for _, cas := range cases {
    				w.implicitConvExpr(cas, tagType, cas)
    			}
    
    
    		if obj, ok := w.p.info.Implicits[clause]; ok {
    			// TODO(mdempsky): These pos details are quirkish, but also
    			// necessary so the variable's position is correct for DWARF
    			// scope assignment later. It would probably be better for us to
    			// instead just set the variable's DWARF scoping info earlier so
    			// we can give it the correct position information.
    			pos := clause.Pos()
    			if typs := unpackListExpr(clause.Cases); len(typs) != 0 {
    				pos = typeExprEndPos(typs[len(typs)-1])
    			}
    			w.pos(pos)
    
    			obj := obj.(*types2.Var)
    			w.typ(obj.Type())
    			w.addLocal(obj)
    		}
    
    		w.stmts(clause.Body)
    	}
    	if len(stmt.Body) > 0 {
    		w.closeScope(stmt.Rbrace)
    	}
    
    	w.closeScope(stmt.Rbrace)
    }
    
    func (w *writer) label(label *syntax.Name) {
    
    
    	// TODO(mdempsky): Replace label strings with dense indices.
    
    }
    
    func (w *writer) optLabel(label *syntax.Name) {
    
    	w.Sync(pkgbits.SyncOptLabel)
    	if w.Bool(label != nil) {
    
    // expr writes the given expression into the function body bitstream.
    
    func (w *writer) expr(expr syntax.Expr) {
    
    	base.Assertf(expr != nil, "missing expression")
    
    
    	expr = unparen(expr) // skip parens; unneeded after typecheck
    
    
    	obj, inst := lookupObj(w.p.info, expr)
    	targs := inst.TypeArgs
    
    
    	if tv, ok := w.p.info.Types[expr]; ok {
    		if tv.IsType() {
    
    			w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr))
    
    
    			// TODO(mdempsky): These details are only important for backend
    			// diagnostics. Explore writing them out separately.
    			w.op(constExprOp(expr))
    
    			w.String(syntax.String(expr))
    
    
    		if _, isNil := obj.(*types2.Nil); isNil {
    			w.Code(exprNil)
    			w.pos(expr)
    			w.typ(tv.Type)
    			return
    		}
    
    		return
    	}
    
    	switch expr := expr.(type) {
    	default:
    		w.p.unexpected("expression", expr)
    
    	case *syntax.CompositeLit:
    
    		w.compLit(expr)
    
    	case *syntax.FuncLit:
    
    		w.funcLit(expr)
    
    	case *syntax.SelectorExpr:
    		sel, ok := w.p.info.Selections[expr]
    		assert(ok)
    
    
    			tv, ok := w.p.info.Types[expr.X]
    			assert(ok)
    			assert(tv.IsType())
    
    			typInfo := w.p.typIdx(tv.Type, w.dict)
    			if w.Bool(typInfo.derived) {
    				methodInfo := w.p.selectorIdx(sel.Obj())
    				idx := w.dict.methodExprIdx(typInfo, methodInfo)
    				w.Len(idx)
    				break
    			}
    
    			w.typInfo(typInfo)
    
    		w.pos(expr)
    		w.selector(sel.Obj())
    
    	case *syntax.IndexExpr:
    
    		_ = w.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation
    
    		if mapType, ok := types2.CoreType(xtyp).(*types2.Map); ok {
    
    		w.implicitConvExpr(expr, keyType, expr.Index)
    
    		w.expr(expr.X)
    		w.pos(expr)
    		for _, n := range &expr.Index {
    
    
    	case *syntax.Operation:
    		if expr.Y == nil {
    
    			w.op(unOps[expr.Op])
    			w.pos(expr)
    			w.expr(expr.X)
    			break
    		}
    
    
    		var commonType types2.Type
    		switch expr.Op {
    		case syntax.Shl, syntax.Shr:
    			// ok: operands are allowed to have different types
    		default:
    			xtyp := w.p.typeOf(expr.X)
    			ytyp := w.p.typeOf(expr.Y)
    			switch {
    			case types2.AssignableTo(xtyp, ytyp):
    				commonType = ytyp
    			case types2.AssignableTo(ytyp, xtyp):
    				commonType = xtyp
    			default:
    				w.p.fatalf(expr, "failed to find common type between %v and %v", xtyp, ytyp)
    			}
    		}
    
    
    		w.implicitConvExpr(expr, commonType, expr.X)
    
    		w.implicitConvExpr(expr, commonType, expr.Y)
    
    		tv, ok := w.p.info.Types[expr.Fun]
    		assert(ok)
    		if tv.IsType() {
    			assert(len(expr.ArgList) == 1)
    			assert(!expr.HasDots)
    
    
    			w.convRTTI(w.p.typeOf(expr.ArgList[0]), tv.Type)
    
    		var rtype types2.Type
    		if tv.IsBuiltin() {
    			switch obj, _ := lookupObj(w.p.info, expr.Fun); obj.Name() {
    
    			case "make":
    				assert(len(expr.ArgList) >= 1)
    				assert(!expr.HasDots)
    
    				w.Code(exprMake)
    				w.pos(expr)
    
    				w.exprType(nil, expr.ArgList[0])
    
    
    				typ := w.p.typeOf(expr)
    				switch coreType := types2.CoreType(typ).(type) {
    				default:
    					w.p.fatalf(expr, "unexpected core type: %v", coreType)
    				case *types2.Chan:
    					w.rtype(typ)
    				case *types2.Map:
    					w.rtype(typ)
    				case *types2.Slice:
    					w.rtype(sliceElem(typ))
    				}
    
    
    				return
    
    			case "new":
    				assert(len(expr.ArgList) == 1)
    				assert(!expr.HasDots)
    
    				w.Code(exprNew)
    				w.pos(expr)
    
    				w.exprType(nil, expr.ArgList[0])
    
    
    			case "append":
    				rtype = sliceElem(w.p.typeOf(expr))
    			case "copy":
    				typ := w.p.typeOf(expr.ArgList[0])
    				if tuple, ok := typ.(*types2.Tuple); ok { // "copy(g())"
    					typ = tuple.At(0).Type()
    				}
    				rtype = sliceElem(typ)
    			case "delete":
    				typ := w.p.typeOf(expr.ArgList[0])
    				if tuple, ok := typ.(*types2.Tuple); ok { // "delete(g())"
    					typ = tuple.At(0).Type()
    				}
    				rtype = typ
    			case "Slice":
    				rtype = sliceElem(w.p.typeOf(expr))
    
    		writeFunExpr := func() {
    			if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok {
    				if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal {
    					w.expr(selector.X)
    
    					w.Bool(true) // method call
    
    			w.Bool(false) // not a method call (i.e., normal function call)
    
    		sigType := types2.CoreType(tv.Type).(*types2.Signature)
    		paramTypes := sigType.Params()
    
    
    		paramType := func(i int) types2.Type {
    			if sigType.Variadic() && !expr.HasDots && i >= paramTypes.Len()-1 {
    				return paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem()
    			}
    			return paramTypes.At(i).Type()
    
    
    		w.multiExpr(expr, paramType, expr.ArgList)
    		w.Bool(expr.HasDots)
    
    func sliceElem(typ types2.Type) types2.Type {
    	return types2.CoreType(typ).(*types2.Slice).Elem()
    }
    
    
    func (w *writer) optExpr(expr syntax.Expr) {
    	if w.Bool(expr != nil) {
    		w.expr(expr)
    	}
    }
    
    
    // multiExpr writes a sequence of expressions, where the i'th value is
    // implicitly converted to dstType(i). It also handles when exprs is a
    // single, multi-valued expression (e.g., the multi-valued argument in
    // an f(g()) call, or the RHS operand in a comma-ok assignment).
    func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) {
    	w.Sync(pkgbits.SyncMultiExpr)
    
    	if len(exprs) == 1 {
    		expr := exprs[0]
    		if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok {
    			assert(tuple.Len() > 1)
    
    			w.Bool(true) // N:1 assignment
    			w.pos(pos)
    			w.expr(expr)
    
    			w.Len(tuple.Len())
    			for i := 0; i < tuple.Len(); i++ {
    				src := tuple.At(i).Type()
    				// TODO(mdempsky): Investigate not writing src here. I think
    				// the reader should be able to infer it from expr anyway.
    				w.typ(src)
    				if dst := dstType(i); w.Bool(dst != nil && !types2.Identical(src, dst)) {
    					if src == nil || dst == nil {
    						w.p.fatalf(pos, "src is %v, dst is %v", src, dst)
    					}
    					if !types2.AssignableTo(src, dst) {
    						w.p.fatalf(pos, "%v is not assignable to %v", src, dst)
    					}
    					w.typ(dst)
    
    	for i, expr := range exprs {
    		w.implicitConvExpr(pos, dstType(i), expr)
    	}
    }
    
    
    // implicitConvExpr is like expr, but if dst is non-nil and different from
    
    // expr's type, then an implicit conversion operation is inserted at
    // pos.
    
    func (w *writer) implicitConvExpr(pos poser, dst types2.Type, expr syntax.Expr) {
    
    	src := w.p.typeOf(expr)
    	if dst != nil && !types2.Identical(src, dst) {
    		if !types2.AssignableTo(src, dst) {
    			w.p.fatalf(pos, "%v is not assignable to %v", src, dst)
    		}
    		w.Code(exprConvert)
    		w.Bool(true) // implicit
    		w.typ(dst)
    		w.pos(pos)
    
    func (w *writer) compLit(lit *syntax.CompositeLit) {
    
    	w.Sync(pkgbits.SyncCompLit)
    
    	if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
    
    	var keyType, elemType types2.Type
    	var structType *types2.Struct
    
    	switch typ0 := typ; typ := types2.CoreType(typ).(type) {
    
    	default:
    		w.p.fatalf(lit, "unexpected composite literal type: %v", typ)
    	case *types2.Array:
    		elemType = typ.Elem()
    	case *types2.Map:
    
    		keyType, elemType = typ.Key(), typ.Elem()
    	case *types2.Slice:
    		elemType = typ.Elem()
    	case *types2.Struct:
    		structType = typ
    	}
    
    	for i, elem := range lit.ElemList {
    
    			if kv, ok := elem.(*syntax.KeyValueExpr); ok {
    				// use position of expr.Key rather than of elem (which has position of ':')
    				w.pos(kv.Key)
    
    				i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name))
    
    			elemType = structType.Field(i).Type()
    			w.Len(i)
    
    			if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) {
    
    				// use position of expr.Key rather than of elem (which has position of ':')
    				w.pos(kv.Key)
    
    				w.implicitConvExpr(kv.Key, keyType, kv.Key)
    
    		w.implicitConvExpr(elem, elemType, elem)
    
    	}
    }
    
    func (w *writer) funcLit(expr *syntax.FuncLit) {
    
    	sig := w.p.typeOf(expr).(*types2.Signature)
    
    	body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict)
    
    	w.Sync(pkgbits.SyncFuncLit)
    
    	for _, cv := range closureVars {
    		w.pos(cv.pos)
    
    	w.Reloc(pkgbits.RelocBody, body)
    
    type posVar struct {
    	pos  syntax.Pos
    	var_ *types2.Var
    
    }
    
    func (w *writer) exprList(expr syntax.Expr) {
    
    	w.Sync(pkgbits.SyncExprList)
    
    	w.exprs(unpackListExpr(expr))
    }
    
    func (w *writer) exprs(exprs []syntax.Expr) {
    
    	w.Sync(pkgbits.SyncExprs)
    	w.Len(len(exprs))
    
    	for _, expr := range exprs {
    		w.expr(expr)
    	}
    }
    
    
    // rtype writes information so that the reader can construct an
    // expression of type *runtime._type representing typ.
    
    func (w *writer) rtype(typ types2.Type) {
    	w.Sync(pkgbits.SyncRType)
    
    	w.typNeeded(typ)
    }
    
    // typNeeded writes a reference to typ, and records that its
    // *runtime._type is needed.
    func (w *writer) typNeeded(typ types2.Type) {
    	info := w.p.typIdx(typ, w.dict)
    	w.typInfo(info)
    
    	if info.derived {
    		w.dict.derived[info.idx].needed = true
    	}
    
    // convRTTI writes information so that the reader can construct
    // expressions for converting from src to dst.
    func (w *writer) convRTTI(src, dst types2.Type) {
    	w.Sync(pkgbits.SyncConvRTTI)
    
    func (w *writer) exprType(iface types2.Type, typ syntax.Expr) {
    
    	base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface)
    
    
    	tv, ok := w.p.info.Types[typ]
    	assert(ok)
    	assert(tv.IsType())
    
    	info := w.p.typIdx(tv.Type, w.dict)
    
    
    
    	if w.Bool(info.derived && iface != nil && !iface.Underlying().(*types2.Interface).Empty()) {
    		ifaceInfo := w.p.typIdx(iface, w.dict)
    
    		idx := -1
    		for i, itab := range w.dict.itabs {
    			if itab.typIdx == info.idx && itab.iface == ifaceInfo {
    				idx = i
    			}
    		}
    		if idx < 0 {
    			idx = len(w.dict.itabs)
    			w.dict.itabs = append(w.dict.itabs, itabInfo{typIdx: info.idx, iface: ifaceInfo})
    		}
    		w.Len(idx)
    		return
    	}
    
    	w.typInfo(info)
    
    	if info.derived {
    		w.dict.derived[info.idx].needed = true
    	}