Skip to content
Snippets Groups Projects
reader.go 56.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	for _, param := range sig.Params().FieldSlice() {
    		r.funcarg(param, param.Sym, ir.PPARAM)
    	}
    
    	for i, param := range sig.Results().FieldSlice() {
    		sym := types.OrigSym(param.Sym)
    
    		if sym == nil || sym.IsBlank() {
    			prefix := "~r"
    			if r.inlCall != nil {
    				prefix = "~R"
    			} else if sym != nil {
    				prefix = "~b"
    			}
    			sym = typecheck.LookupNum(prefix, i)
    		}
    
    		r.funcarg(param, sym, ir.PPARAMOUT)
    	}
    }
    
    func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) {
    	if sym == nil {
    		assert(ctxt == ir.PPARAM)
    		if r.inlCall != nil {
    			r.inlvars.Append(ir.BlankNode)
    		}
    		return
    	}
    
    	name := ir.NewNameAt(r.updatePos(param.Pos), sym)
    
    	setType(name, param.Type)
    
    	r.addLocal(name, ctxt)
    
    	if r.inlCall == nil {
    		if !r.funarghack {
    			param.Sym = sym
    			param.Nname = name
    		}
    	} else {
    		if ctxt == ir.PPARAMOUT {
    			r.retvars.Append(name)
    		} else {
    			r.inlvars.Append(name)
    		}
    	}
    }
    
    func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) {
    	assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT)
    
    
    	r.Sync(pkgbits.SyncAddLocal)
    	if pkgbits.EnableSync {
    		want := r.Int()
    
    		if have := len(r.locals); have != want {
    			base.FatalfAt(name.Pos(), "locals table has desynced")
    		}
    	}
    
    	name.SetUsed(true)
    	r.locals = append(r.locals, name)
    
    	// TODO(mdempsky): Move earlier.
    	if ir.IsBlank(name) {
    		return
    	}
    
    	if r.inlCall != nil {
    		if ctxt == ir.PAUTO {
    			name.SetInlLocal(true)
    		} else {
    			name.SetInlFormal(true)
    			ctxt = ir.PAUTO
    		}
    
    		// TODO(mdempsky): Rethink this hack.
    		if strings.HasPrefix(name.Sym().Name, "~") || base.Flag.GenDwarfInl == 0 {
    			name.SetPos(r.inlCall.Pos())
    			name.SetInlFormal(false)
    			name.SetInlLocal(false)
    		}
    	}
    
    	name.Class = ctxt
    	name.Curfn = r.curfn
    
    	r.curfn.Dcl = append(r.curfn.Dcl, name)
    
    	if ctxt == ir.PAUTO {
    		name.SetFrameOffset(0)
    	}
    }
    
    func (r *reader) useLocal() *ir.Name {
    
    	r.Sync(pkgbits.SyncUseObjLocal)
    	if r.Bool() {
    		return r.locals[r.Len()]
    
    	return r.closureVars[r.Len()]
    
    	r.Sync(pkgbits.SyncOpenScope)
    
    	pos := r.pos()
    
    	if base.Flag.Dwarf {
    		r.scopeVars = append(r.scopeVars, len(r.curfn.Dcl))
    		r.marker.Push(pos)
    	}
    }
    
    func (r *reader) closeScope() {
    
    	r.Sync(pkgbits.SyncCloseScope)
    
    	r.lastCloseScopePos = r.pos()
    
    	r.closeAnotherScope()
    }
    
    // closeAnotherScope is like closeScope, but it reuses the same mark
    // position as the last closeScope call. This is useful for "for" and
    // "if" statements, as their implicit blocks always end at the same
    // position as an explicit block.
    func (r *reader) closeAnotherScope() {
    
    	r.Sync(pkgbits.SyncCloseAnotherScope)
    
    
    	if base.Flag.Dwarf {
    		scopeVars := r.scopeVars[len(r.scopeVars)-1]
    		r.scopeVars = r.scopeVars[:len(r.scopeVars)-1]
    
    
    		// Quirkish: noder decides which scopes to keep before
    		// typechecking, whereas incremental typechecking during IR
    		// construction can result in new autotemps being allocated. To
    		// produce identical output, we ignore autotemps here for the
    		// purpose of deciding whether to retract the scope.
    		//
    		// This is important for net/http/fcgi, because it contains:
    		//
    		//	var body io.ReadCloser
    		//	if len(content) > 0 {
    		//		body, req.pw = io.Pipe()
    		//	} else { … }
    		//
    		// Notably, io.Pipe is inlinable, and inlining it introduces a ~R0
    		// variable at the call site.
    		//
    		// Noder does not preserve the scope where the io.Pipe() call
    		// resides, because it doesn't contain any declared variables in
    		// source. So the ~R0 variable ends up being assigned to the
    		// enclosing scope instead.
    		//
    		// However, typechecking this assignment also introduces
    		// autotemps, because io.Pipe's results need conversion before
    		// they can be assigned to their respective destination variables.
    		//
    		// TODO(mdempsky): We should probably just keep all scopes, and
    		// let dwarfgen take care of pruning them instead.
    		retract := true
    		for _, n := range r.curfn.Dcl[scopeVars:] {
    			if !n.AutoTemp() {
    				retract = false
    				break
    			}
    		}
    
    		if retract {
    
    			// no variables were declared in this scope, so we can retract it.
    			r.marker.Unpush()
    		} else {
    			r.marker.Pop(r.lastCloseScopePos)
    		}
    	}
    }
    
    // @@@ Statements
    
    func (r *reader) stmt() ir.Node {
    	switch stmts := r.stmts(); len(stmts) {
    	case 0:
    		return nil
    	case 1:
    		return stmts[0]
    	default:
    		return ir.NewBlockStmt(stmts[0].Pos(), stmts)
    	}
    }
    
    func (r *reader) stmts() []ir.Node {
    
    		tag := codeStmt(r.Code(pkgbits.SyncStmt1))
    
    			r.Sync(pkgbits.SyncStmtsEnd)
    
    			return res
    		}
    
    		if n := r.stmt1(tag, &res); n != nil {
    
    		}
    	}
    }
    
    func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
    	var label *types.Sym
    	if n := len(*out); n > 0 {
    		if ls, ok := (*out)[n-1].(*ir.LabelStmt); ok {
    			label = ls.Label
    		}
    	}
    
    	switch tag {
    	default:
    		panic("unexpected statement")
    
    	case stmtAssign:
    		pos := r.pos()
    
    
    		// TODO(mdempsky): After quirks mode is gone, swap these
    		// statements so we visit LHS before RHS again.
    
    
    		if len(rhs) == 0 {
    			for _, name := range names {
    				as := ir.NewAssignStmt(pos, name, nil)
    				as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name))
    
    			}
    			return nil
    		}
    
    		if len(lhs) == 1 && len(rhs) == 1 {
    			n := ir.NewAssignStmt(pos, lhs[0], rhs[0])
    			n.Def = r.initDefn(n, names)
    			return n
    		}
    
    		n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs)
    		n.Def = r.initDefn(n, names)
    		return n
    
    	case stmtAssignOp:
    		op := r.op()
    		lhs := r.expr()
    		pos := r.pos()
    		rhs := r.expr()
    		return ir.NewAssignOpStmt(pos, op, lhs, rhs)
    
    	case stmtIncDec:
    		op := r.op()
    		lhs := r.expr()
    		pos := r.pos()
    		n := ir.NewAssignOpStmt(pos, op, lhs, ir.NewBasicLit(pos, one))
    		n.IncDec = true
    		return n
    
    	case stmtBlock:
    		out.Append(r.blockStmt()...)
    		return nil
    
    	case stmtBranch:
    		pos := r.pos()
    		op := r.op()
    		sym := r.optLabel()
    		return ir.NewBranchStmt(pos, op, sym)
    
    	case stmtCall:
    		pos := r.pos()
    		op := r.op()
    		call := r.expr()
    		return ir.NewGoDeferStmt(pos, op, call)
    
    	case stmtExpr:
    		return r.expr()
    
    	case stmtFor:
    		return r.forStmt(label)
    
    	case stmtIf:
    		return r.ifStmt()
    
    	case stmtLabel:
    		pos := r.pos()
    		sym := r.label()
    		return ir.NewLabelStmt(pos, sym)
    
    	case stmtReturn:
    		pos := r.pos()
    		results := r.exprList()
    		return ir.NewReturnStmt(pos, results)
    
    	case stmtSelect:
    		return r.selectStmt(label)
    
    	case stmtSend:
    		pos := r.pos()
    		ch := r.expr()
    		value := r.expr()
    		return ir.NewSendStmt(pos, ch, value)
    
    	case stmtSwitch:
    		return r.switchStmt(label)
    	}
    }
    
    func (r *reader) assignList() ([]*ir.Name, []ir.Node) {
    
    	lhs := make([]ir.Node, r.Len())
    
    	var names []*ir.Name
    
    	for i := range lhs {
    
    			pos := r.pos()
    			_, sym := r.localIdent()
    			typ := r.typ()
    
    			name := ir.NewNameAt(pos, sym)
    			lhs[i] = name
    			names = append(names, name)
    
    	}
    
    	return names, lhs
    }
    
    func (r *reader) blockStmt() []ir.Node {
    
    	r.Sync(pkgbits.SyncBlockStmt)
    
    	r.openScope()
    	stmts := r.stmts()
    	r.closeScope()
    	return stmts
    }
    
    func (r *reader) forStmt(label *types.Sym) ir.Node {
    
    	r.Sync(pkgbits.SyncForStmt)
    
    
    		// TODO(mdempsky): After quirks mode is gone, swap these
    		// statements so we read LHS before X again.
    
    		body := r.blockStmt()
    		r.closeAnotherScope()
    
    		rang := ir.NewRangeStmt(pos, nil, nil, x, body)
    		if len(lhs) >= 1 {
    			rang.Key = lhs[0]
    			if len(lhs) >= 2 {
    				rang.Value = lhs[1]
    			}
    		}
    		rang.Def = r.initDefn(rang, names)
    		rang.Label = label
    		return rang
    	}
    
    	pos := r.pos()
    	init := r.stmt()
    	cond := r.expr()
    	post := r.stmt()
    	body := r.blockStmt()
    	r.closeAnotherScope()
    
    	stmt := ir.NewForStmt(pos, init, cond, post, body)
    	stmt.Label = label
    	return stmt
    }
    
    func (r *reader) ifStmt() ir.Node {
    
    	r.Sync(pkgbits.SyncIfStmt)
    
    	r.openScope()
    	pos := r.pos()
    	init := r.stmts()
    	cond := r.expr()
    	then := r.blockStmt()
    	els := r.stmts()
    	n := ir.NewIfStmt(pos, cond, then, els)
    	n.SetInit(init)
    	r.closeAnotherScope()
    	return n
    }
    
    func (r *reader) selectStmt(label *types.Sym) ir.Node {
    
    	r.Sync(pkgbits.SyncSelectStmt)
    
    	clauses := make([]*ir.CommClause, r.Len())
    
    	for i := range clauses {
    		if i > 0 {
    			r.closeScope()
    		}
    		r.openScope()
    
    		pos := r.pos()
    		comm := r.stmt()
    		body := r.stmts()
    
    		clauses[i] = ir.NewCommStmt(pos, comm, body)
    	}
    	if len(clauses) > 0 {
    		r.closeScope()
    	}
    	n := ir.NewSelectStmt(pos, clauses)
    	n.Label = label
    	return n
    }
    
    func (r *reader) switchStmt(label *types.Sym) ir.Node {
    
    	r.Sync(pkgbits.SyncSwitchStmt)
    
    
    	r.openScope()
    	pos := r.pos()
    	init := r.stmt()
    
    	var ident *ir.Ident
    	var iface *types.Type
    
    			sym := typecheck.Lookup(r.String())
    
    		tag = ir.NewTypeSwitchGuard(pos, ident, x)
    	} else {
    		tag = r.expr()
    	}
    
    	clauses := make([]*ir.CaseClause, r.Len())
    
    	for i := range clauses {
    		if i > 0 {
    			r.closeScope()
    		}
    		r.openScope()
    
    		pos := r.pos()
    
    		var cases []ir.Node
    		if iface != nil {
    			cases = make([]ir.Node, r.Len())
    			if len(cases) == 0 {
    				cases = nil // TODO(mdempsky): Unclear if this matters.
    			}
    			for i := range cases {
    
    				cases[i] = r.exprType(true)
    
    
    		clause := ir.NewCaseStmt(pos, cases, nil)
    
    			name := ir.NewNameAt(pos, ident.Sym())
    
    			r.addLocal(name, ir.PAUTO)
    			clause.Var = name
    
    		}
    
    		clause.Body = r.stmts()
    		clauses[i] = clause
    	}
    	if len(clauses) > 0 {
    		r.closeScope()
    	}
    	r.closeScope()
    
    	n := ir.NewSwitchStmt(pos, tag, clauses)
    	n.Label = label
    	if init != nil {
    		n.SetInit([]ir.Node{init})
    	}
    	return n
    }
    
    func (r *reader) label() *types.Sym {
    
    	r.Sync(pkgbits.SyncLabel)
    	name := r.String()
    
    	if r.inlCall != nil {
    		name = fmt.Sprintf("~%s·%d", name, inlgen)
    	}
    	return typecheck.Lookup(name)
    }
    
    func (r *reader) optLabel() *types.Sym {
    
    	r.Sync(pkgbits.SyncOptLabel)
    	if r.Bool() {
    
    		return r.label()
    	}
    	return nil
    }
    
    // initDefn marks the given names as declared by defn and populates
    // its Init field with ODCL nodes. It then reports whether any names
    // were so declared, which can be used to initialize defn.Def.
    func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool {
    	if len(names) == 0 {
    		return false
    	}
    
    	init := make([]ir.Node, len(names))
    	for i, name := range names {
    		name.Defn = defn
    		init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name)
    	}
    	defn.SetInit(init)
    	return true
    }
    
    // @@@ Expressions
    
    
    // expr reads and returns a typechecked expression.
    
    func (r *reader) expr() (res ir.Node) {
    	defer func() {
    		if res != nil && res.Typecheck() == 0 {
    			base.FatalfAt(res.Pos(), "%v missed typecheck", res)
    		}
    	}()
    
    	switch tag := codeExpr(r.Code(pkgbits.SyncExpr)); tag {
    
    	default:
    		panic("unhandled expression")
    
    	case exprNone:
    		return nil
    
    	case exprBlank:
    
    		// blank only allowed in LHS of assignments
    		// TODO(mdempsky): Handle directly in assignList instead?
    		return typecheck.AssignExpr(ir.BlankNode)
    
    		// Callee instead of Expr allows builtins
    		// TODO(mdempsky): Handle builtins directly in exprCall, like method calls?
    		return typecheck.Callee(r.obj())
    
    		return r.exprType(false)
    
    		val := FixValue(typ, r.Value())
    
    		return typecheck.Expr(OrigConst(pos, typ, val, op, orig))
    
    
    	case exprCompLit:
    		return r.compLit()
    
    	case exprFuncLit:
    		return r.funcLit()
    
    	case exprSelector:
    		x := r.expr()
    		pos := r.pos()
    		_, sym := r.selector()
    
    
    		// Method expression with derived receiver type.
    		if x.Op() == ir.ODYNAMICTYPE {
    			// TODO(mdempsky): Handle with runtime dictionary lookup.
    			n := ir.TypeNode(x.Type())
    			n.SetTypecheck(1)
    			x = n
    		}
    
    
    		n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
    		if n.Op() == ir.OMETHVALUE {
    			wrapper := methodValueWrapper{
    				rcvr:   n.X.Type(),
    				method: n.Selection,
    			}
    			if r.importedDef() {
    				haveMethodValueWrappers = append(haveMethodValueWrappers, wrapper)
    			} else {
    				needMethodValueWrappers = append(needMethodValueWrappers, wrapper)
    			}
    		}
    		return n
    
    
    	case exprIndex:
    		x := r.expr()
    		pos := r.pos()
    		index := r.expr()
    
    		return typecheck.Expr(ir.NewIndexExpr(pos, x, index))
    
    
    	case exprSlice:
    		x := r.expr()
    		pos := r.pos()
    		var index [3]ir.Node
    		for i := range index {
    			index[i] = r.expr()
    		}
    		op := ir.OSLICE
    		if index[2] != nil {
    			op = ir.OSLICE3
    		}
    
    		return typecheck.Expr(ir.NewSliceExpr(pos, op, x, index[0], index[1], index[2]))
    
    
    	case exprAssert:
    		x := r.expr()
    		pos := r.pos()
    
    		typ := r.exprType(false)
    
    
    		if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE {
    			return typed(typ.Type(), ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.X))
    		}
    		return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ.(ir.Ntype)))
    
    
    	case exprUnaryOp:
    		op := r.op()
    		pos := r.pos()
    		x := r.expr()
    
    		switch op {
    		case ir.OADDR:
    
    			return typecheck.Expr(typecheck.NodAddrAt(pos, x))
    
    			return typecheck.Expr(ir.NewStarExpr(pos, x))
    
    		return typecheck.Expr(ir.NewUnaryExpr(pos, op, x))
    
    
    	case exprBinaryOp:
    		op := r.op()
    		x := r.expr()
    		pos := r.pos()
    		y := r.expr()
    
    		switch op {
    		case ir.OANDAND, ir.OOROR:
    
    			return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y))
    
    		return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
    
    		if r.Bool() { // method call
    
    			pos := r.pos()
    			_, sym := r.selector()
    			fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym))
    		}
    
    		pos := r.pos()
    		args := r.exprs()
    
    		return typecheck.Call(pos, fun, args, dots)
    
    		return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x))
    
    	}
    }
    
    func (r *reader) compLit() ir.Node {
    
    	r.Sync(pkgbits.SyncCompLit)
    
    		typ = typ.Elem()
    	}
    	if typ.Kind() == types.TFORW {
    		base.FatalfAt(pos, "unresolved composite literal type: %v", typ)
    	}
    	isStruct := typ.Kind() == types.TSTRUCT
    
    
    	elems := make([]ir.Node, r.Len())
    
    	for i := range elems {
    		elemp := &elems[i]
    
    		if isStruct {
    
    			sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.Len()), nil)
    
    			*elemp, elemp = sk, &sk.Value
    
    			kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
    			*elemp, elemp = kv, &kv.Value
    		}
    
    		*elemp = wrapName(r.pos(), r.expr())
    	}
    
    
    	lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(typ), elems))
    	if typ0.IsPtr() {
    		lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
    		lit.SetType(typ0)
    
    	}
    	return lit
    }
    
    func wrapName(pos src.XPos, x ir.Node) ir.Node {
    	// These nodes do not carry line numbers.
    	// Introduce a wrapper node to give them the correct line.
    	switch ir.Orig(x).Op() {
    	case ir.OTYPE, ir.OLITERAL:
    		if x.Sym() == nil {
    			break
    		}
    		fallthrough
    
    	case ir.ONAME, ir.ONONAME, ir.ONIL:
    
    		p := ir.NewParenExpr(pos, x)
    		p.SetImplicit(true)
    		return p
    	}
    	return x
    }
    
    func (r *reader) funcLit() ir.Node {
    
    	r.Sync(pkgbits.SyncFuncLit)
    
    
    	pos := r.pos()
    	xtype2 := r.signature(types.LocalPkg, nil)
    
    	opos := pos
    
    	fn := ir.NewClosureFunc(opos, r.curfn != nil)
    
    	clo := fn.OClosure
    	ir.NameClosure(clo, r.curfn)
    
    	setType(fn.Nname, xtype2)
    
    	fn.ClosureVars = make([]*ir.Name, 0, r.Len())
    
    	for len(fn.ClosureVars) < cap(fn.ClosureVars) {
    
    		ir.NewClosureVar(r.pos(), fn, r.useLocal())
    
    	// TODO(mdempsky): Remove hard-coding of typecheck.Target.
    	return ir.UseClosure(clo, typecheck.Target)
    
    }
    
    func (r *reader) exprList() []ir.Node {
    
    	r.Sync(pkgbits.SyncExprList)
    
    	return r.exprs()
    }
    
    func (r *reader) exprs() []ir.Node {
    
    	r.Sync(pkgbits.SyncExprs)
    	nodes := make([]ir.Node, r.Len())
    
    	if len(nodes) == 0 {
    		return nil // TODO(mdempsky): Unclear if this matters.
    	}
    	for i := range nodes {
    		nodes[i] = r.expr()
    	}
    	return nodes
    }
    
    
    func (r *reader) exprType(nilOK bool) ir.Node {
    
    	r.Sync(pkgbits.SyncExprType)
    
    	if nilOK && r.Bool() {
    		return typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr))
    	}
    
    	pos := r.pos()
    
    
    	var typ *types.Type
    	var lsym *obj.LSym
    
    	if r.Bool() {
    		itab := r.dict.itabs[r.Len()]
    		typ, lsym = itab.typ, itab.lsym
    	} else {
    		info := r.typInfo()
    		typ = r.p.typIdx(info, r.dict, true)
    
    		if !info.derived {
    			// TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node.
    			n := ir.TypeNode(typ)
    			n.SetTypecheck(1)
    			return n
    
    		lsym = reflectdata.TypeLinksym(typ)
    
    	ptr := typecheck.Expr(typecheck.NodAddr(ir.NewLinksymExpr(pos, lsym, types.Types[types.TUINT8])))
    	return typed(typ, ir.NewDynamicType(pos, ptr))
    
    func (r *reader) op() ir.Op {
    
    	r.Sync(pkgbits.SyncOp)
    	return ir.Op(r.Len())
    
    }
    
    // @@@ Package initialization
    
    func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) {
    
    	cgoPragmas := make([][]string, r.Len())
    
    	for i := range cgoPragmas {
    
    		cgoPragmas[i] = r.Strings()
    
    	}
    	target.CgoPragmas = cgoPragmas
    
    	r.pkgDecls(target)
    
    
    }
    
    func (r *reader) pkgDecls(target *ir.Package) {
    
    		switch code := codeDecl(r.Code(pkgbits.SyncDecl)); code {
    
    		default:
    			panic(fmt.Sprintf("unhandled decl: %v", code))
    
    		case declEnd:
    			return
    
    		case declFunc:
    			names := r.pkgObjs(target)
    			assert(len(names) == 1)
    			target.Decls = append(target.Decls, names[0].Func)
    
    		case declMethod:
    			typ := r.typ()
    			_, sym := r.selector()
    
    			method := typecheck.Lookdot1(nil, sym, typ, typ.Methods(), 0)
    			target.Decls = append(target.Decls, method.Nname.(*ir.Name).Func)
    
    		case declVar:
    			pos := r.pos()
    			names := r.pkgObjs(target)
    			values := r.exprList()
    
    			if len(names) > 1 && len(values) == 1 {
    				as := ir.NewAssignListStmt(pos, ir.OAS2, nil, values)
    				for _, name := range names {
    					as.Lhs.Append(name)
    					name.Defn = as
    				}
    				target.Decls = append(target.Decls, as)
    			} else {
    				for i, name := range names {
    					as := ir.NewAssignStmt(pos, name, nil)
    					if i < len(values) {
    						as.Y = values[i]
    					}
    					name.Defn = as
    					target.Decls = append(target.Decls, as)
    				}
    			}
    
    
    				assert(len(names) == 1)
    				embeds := make([]ir.Embed, n)
    				for i := range embeds {
    
    					embeds[i] = ir.Embed{Pos: r.pos(), Patterns: r.Strings()}
    
    				}
    				names[0].Embed = &embeds
    				target.Embeds = append(target.Embeds, names[0])
    			}
    
    		case declOther:
    			r.pkgObjs(target)
    		}
    	}
    }
    
    func (r *reader) pkgObjs(target *ir.Package) []*ir.Name {
    
    	r.Sync(pkgbits.SyncDeclNames)
    	nodes := make([]*ir.Name, r.Len())
    
    		r.Sync(pkgbits.SyncDeclName)
    
    
    		name := r.obj().(*ir.Name)
    		nodes[i] = name
    
    		sym := name.Sym()
    		if sym.IsBlank() {
    			continue
    		}
    
    		switch name.Class {
    		default:
    			base.FatalfAt(name.Pos(), "unexpected class: %v", name.Class)
    
    		case ir.PEXTERN:
    			target.Externs = append(target.Externs, name)
    
    		case ir.PFUNC:
    			assert(name.Type().Recv() == nil)
    
    			// TODO(mdempsky): Cleaner way to recognize init?
    			if strings.HasPrefix(sym.Name, "init.") {
    				target.Inits = append(target.Inits, name.Func)
    			}
    		}
    
    		if types.IsExported(sym.Name) {
    			assert(!sym.OnExportList())
    			target.Exports = append(target.Exports, name)
    			sym.SetOnExportList(true)
    		}
    
    		if base.Flag.AsmHdr != "" {
    			assert(!sym.Asm())
    			target.Asms = append(target.Asms, name)
    			sym.SetAsm(true)
    		}
    	}
    
    	return nodes
    }
    
    // @@@ Inlining
    
    var inlgen = 0
    
    func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
    	// TODO(mdempsky): Turn callerfn into an explicit parameter.
    	callerfn := ir.CurFunc
    
    	pri, ok := bodyReader[fn]
    	if !ok {
    		// Assume it's an imported function or something that we don't
    		// have access to in quirks mode.
    		if haveLegacyImports {
    			return nil
    		}
    
    		base.FatalfAt(call.Pos(), "missing function body for call to %v", fn)
    	}
    
    	if fn.Inl.Body == nil {
    		expandInline(fn, pri)
    	}
    
    
    	r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
    
    
    	// TODO(mdempsky): This still feels clumsy. Can we do better?
    	tmpfn := ir.NewFunc(fn.Pos())
    	tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym())
    	tmpfn.Closgen = callerfn.Closgen
    	defer func() { callerfn.Closgen = tmpfn.Closgen }()
    
    
    	setType(tmpfn.Nname, fn.Type())
    
    	r.inlCall = call
    	r.inlFunc = fn
    	r.inlTreeIndex = inlIndex
    	r.inlPosBases = make(map[*src.PosBase]*src.PosBase)
    
    
    	r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
    	for i, cv := range r.inlFunc.ClosureVars {
    		r.closureVars[i] = cv.Outer
    
    	assert(r.Bool()) // have body
    
    	r.delayResults = fn.Inl.CanDelayResults
    
    	r.retlabel = typecheck.AutoLabel(".i")
    	inlgen++
    
    	init := ir.TakeInit(call)
    
    	// For normal function calls, the function callee expression
    
    	// may contain side effects. Make sure to preserve these,
    
    	// if necessary (#42703).
    	if call.Op() == ir.OCALLFUNC {
    
    		inline.CalleeEffects(&init, call.X)
    
    	}
    
    	var args ir.Nodes
    	if call.Op() == ir.OCALLMETH {
    
    		base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
    
    	}
    	args.Append(call.Args...)
    
    	// Create assignment to declare and initialize inlvars.