Skip to content
Snippets Groups Projects
writer.go 68.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	// least.
    
    	pragma := asPragmaFlag(decl.Pragma)
    	if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 {
    		w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined")
    	}
    
    	wi := asWasmImport(decl.Pragma)
    
    
    	if decl.Body != nil {
    		if pragma&ir.Noescape != 0 {
    			w.p.errorf(decl, "can only use //go:noescape with external func implementations")
    		}
    
    		if wi != nil {
    			w.p.errorf(decl, "can only use //go:wasmimport with external func implementations")
    		}
    
    		if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && pragma&ir.Nosplit == 0 {
    			// Stack growth can't handle uintptr arguments that may
    			// be pointers (as we don't know which are pointers
    			// when creating the stack map). Thus uintptrkeepalive
    			// functions (and all transitive callees) must be
    			// nosplit.
    			//
    			// N.B. uintptrescapes implies uintptrkeepalive but it
    			// is OK since the arguments must escape to the heap.
    			//
    			// TODO(prattmic): Add recursive nosplit check of callees.
    			// TODO(prattmic): Functions with no body (i.e.,
    			// assembly) must also be nosplit, but we can't check
    			// that here.
    			w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit")
    		}
    
    	} else {
    		if base.Flag.Complete || decl.Name.Value == "init" {
    			// Linknamed functions are allowed to have no body. Hopefully
    			// the linkname target has a body. See issue 23311.
    
    			// Wasmimport functions are also allowed to have no body.
    			if _, ok := w.p.linknames[obj]; !ok && wi == nil {
    
    				w.p.errorf(decl, "missing function body")
    			}
    		}
    	}
    
    
    	sig, block := obj.Type().(*types2.Signature), decl.Body
    
    	body, closureVars := w.p.bodyIdx(sig, block, w.dict)
    
    	w.Sync(pkgbits.SyncFuncExt)
    
    	w.pragmaFlag(pragma)
    	w.linkname(obj)
    
    
    	if buildcfg.GOARCH == "wasm" {
    		if wi != nil {
    			w.String(wi.Module)
    			w.String(wi.Name)
    		} else {
    			w.String("")
    			w.String("")
    		}
    	}
    
    
    	w.Bool(false) // stub extension
    	w.Reloc(pkgbits.RelocBody, body)
    	w.Sync(pkgbits.SyncEOF)
    
    }
    
    func (w *writer) typeExt(obj *types2.TypeName) {
    	decl, ok := w.p.typDecls[obj]
    	assert(ok)
    
    
    	w.Sync(pkgbits.SyncTypeExt)
    
    
    	w.pragmaFlag(asPragmaFlag(decl.Pragma))
    
    	// No LSym.SymIdx info yet.
    
    }
    
    func (w *writer) varExt(obj *types2.Var) {
    
    	w.Sync(pkgbits.SyncVarExt)
    
    	w.linkname(obj)
    }
    
    func (w *writer) linkname(obj types2.Object) {
    
    	w.Sync(pkgbits.SyncLinkname)
    	w.Int64(-1)
    	w.String(w.p.linknames[obj])
    
    }
    
    func (w *writer) pragmaFlag(p ir.PragmaFlag) {
    
    	w.Sync(pkgbits.SyncPragma)
    	w.Int(int(p))
    
    // bodyIdx returns the index for the given function body (specified by
    // block), adding it to the export data
    func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posVar) {
    
    	w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody)
    
    		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 {
    
    // addLocal records the declaration of a new local variable.
    
    func (w *writer) addLocal(obj *types2.Var) {
    
    	w.Sync(pkgbits.SyncAddLocal)
    	if w.p.SyncMarkers() {
    		w.Int(idx)
    
    	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(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(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.
    
    }
    
    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)
    
    // 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) {
    
    
    			// Like w.localIdent, but we don't have a types2.Object.
    			w.Sync(pkgbits.SyncLocalIdent)
    			w.pkg(w.p.curpkg)
    
    		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(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, 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(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.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, expr)
    
    	if tv, ok := w.p.maybeTypeAndValue(expr); ok {
    
    			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
    		}
    
    
    		// With shape types (and particular pointer shaping), we may have
    		// an expression of type "go.shape.*uint8", but need to reshape it
    		// to another shape-identical type to allow use in field
    		// selection, indexing, etc.
    		if typ := tv.Type; !tv.IsBuiltin() && !isTuple(typ) && !isUntyped(typ) {
    			w.Code(exprReshape)
    			w.typ(typ)
    			// fallthrough
    		}
    
    		if targs.Len() != 0 {
    			obj := obj.(*types2.Func)
    
    			w.Code(exprFuncInst)
    
    		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)
    
    
    		switch sel.Kind() {
    		default:
    			w.p.fatalf(expr, "unexpected selection kind: %v", sel.Kind())
    
    		case types2.FieldVal:
    			w.Code(exprFieldVal)
    			w.expr(expr.X)
    			w.pos(expr)
    			w.selector(sel.Obj())
    
    		case types2.MethodVal:
    			w.Code(exprMethodVal)
    
    			tv := w.p.typeAndValue(expr.X)
    
    			index := sel.Index()
    			implicits := index[:len(index)-1]
    
    			typ := tv.Type
    			w.typ(typ)
    
    			w.Len(len(implicits))
    			for _, ix := range implicits {
    				w.Len(ix)
    				typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type()
    			}
    
    			recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type()
    			if w.Bool(isPtrTo(typ, recv)) { // need deref
    				typ = recv
    			} else if w.Bool(isPtrTo(recv, typ)) { // need addr
    				typ = recv
    			}
    
    
    		_ = 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(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(commonType, expr.X)
    
    		w.implicitConvExpr(commonType, expr.Y)
    
    		tv := w.p.typeAndValue(expr.Fun)
    
    		if tv.IsType() {
    			assert(len(expr.ArgList) == 1)
    			assert(!expr.HasDots)
    
    			w.convertExpr(tv.Type, expr.ArgList[0], false)
    
    		var rtype types2.Type
    		if tv.IsBuiltin() {
    
    			switch obj, _ := lookupObj(w.p, 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))
    
    			fun := unparen(expr.Fun)
    
    			if selector, ok := fun.(*syntax.SelectorExpr); ok {
    
    				if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal {
    
    					w.Bool(true) // method call
    
    					typ := w.recvExpr(selector, sel)
    					w.methodExpr(selector, typ, sel)
    
    			w.Bool(false) // not a method call (i.e., normal function call)
    
    			if obj, inst := lookupObj(w.p, fun); w.Bool(obj != nil && inst.TypeArgs.Len() != 0) {
    
    				obj := obj.(*types2.Func)
    
    				w.pos(fun)
    				w.funcInst(obj, inst.TypeArgs)
    				return
    			}
    
    			w.expr(fun)
    
    		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)
    	}
    }
    
    
    // recvExpr writes out expr.X, but handles any implicit addressing,
    
    // dereferencing, and field selections appropriate for the method
    // selection.
    
    func (w *writer) recvExpr(expr *syntax.SelectorExpr, sel *types2.Selection) types2.Type {
    	index := sel.Index()
    	implicits := index[:len(index)-1]
    
    	w.Code(exprRecv)
    	w.expr(expr.X)
    	w.pos(expr)
    	w.Len(len(implicits))
    
    	typ := w.p.typeOf(expr.X)
    	for _, ix := range implicits {
    		typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type()
    		w.Len(ix)
    	}
    
    	recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type()
    	if w.Bool(isPtrTo(typ, recv)) { // needs deref
    		typ = recv
    	} else if w.Bool(isPtrTo(recv, typ)) { // needs addr
    		typ = recv
    	}
    
    	return typ
    }
    
    
    // funcInst writes a reference to an instantiated function.
    func (w *writer) funcInst(obj *types2.Func, targs *types2.TypeList) {
    	info := w.p.objInstIdx(obj, targs, w.dict)
    
    	// Type arguments list contains derived types; we can emit a static
    	// call to the shaped function, but need to dynamically compute the
    	// runtime dictionary pointer.
    	if w.Bool(info.anyDerived()) {
    		w.Len(w.dict.subdictIdx(info))
    		return
    	}
    
    	// Type arguments list is statically known; we can emit a static
    	// call with a statically reference to the respective runtime
    	// dictionary.
    	w.objInfo(info)
    }
    
    // methodExpr writes out a reference to the method selected by
    // expr. sel should be the corresponding types2.Selection, and recv
    // the type produced after any implicit addressing, dereferencing, and
    // field selection. (Note: recv might differ from sel.Obj()'s receiver
    // parameter in the case of interface types, and is needed for
    // handling type parameter methods.)
    func (w *writer) methodExpr(expr *syntax.SelectorExpr, recv types2.Type, sel *types2.Selection) {