Skip to content
Snippets Groups Projects
writer.go 73.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • func (pw *pkgWriter) selectorIdx(obj types2.Object) selectorInfo {
    	pkgIdx := pw.pkgIdx(obj.Pkg())
    	nameIdx := pw.StringIdx(obj.Name())
    	return selectorInfo{pkgIdx: pkgIdx, nameIdx: nameIdx}
    
    }
    
    // @@@ Compiler extensions
    
    func (w *writer) funcExt(obj *types2.Func) {
    	decl, ok := w.p.funDecls[obj]
    	assert(ok)
    
    	// TODO(mdempsky): Extend these pragma validation flags to account
    	// for generics. E.g., linkname probably doesn't make sense at
    	// 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)
    
    	if len(closureVars) > 0 {
    		fmt.Fprintln(os.Stderr, "CLOSURE", closureVars)
    	}
    
    	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) declareParams(sig *types2.Signature) {
    	addLocals := func(params *types2.Tuple) {
    
    		for i := 0; i < params.Len(); i++ {
    
    			w.addLocal(params.At(i))
    
    		}
    	}
    
    	if recv := sig.Recv(); recv != nil {
    
    	addLocals(sig.Params())
    	addLocals(sig.Results())
    
    // 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 {
    
    		if dead {
    			// Any statements after a terminating statement are safe to
    			// omit, at least until the next labeled statement.
    			if _, ok := stmt.(*syntax.LabeledStmt); !ok {
    				continue
    			}
    		}
    
    		dead = w.p.terminates(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)
    
    		if stmt.Tok == syntax.Defer {
    			w.optExpr(stmt.DeferAt)
    		}
    
    
    	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, syntax.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 := syntax.UnpackListExpr(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 := syntax.UnpackListExpr(lhs0)
    	rhs := syntax.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 := syntax.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 := syntax.UnpackListExpr(rang.Lhs)
    
    			assign := func(i int, src types2.Type) {
    				if i >= len(lhs) {
    					return
    				}
    
    				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 := types2.RangeKeyVal(w.p.typeOf(rang.X))
    
    		if stmt.Cond != nil && w.p.staticBool(&stmt.Cond) < 0 { // always false
    			stmt.Post = nil
    			stmt.Body.List = nil
    		}
    
    
    		w.stmt(stmt.Post)
    	}
    
    	w.blockStmt(stmt.Body)
    
    func (w *writer) distinctVars(stmt *syntax.ForStmt) bool {
    	lv := base.Debug.LoopVar
    	v := w.p.info.FileVersions[stmt.Pos().Base()]
    	is122 := v.Major == 0 && v.Minor == 0 || v.Major == 1 && v.Minor >= 22
    
    	// Turning off loopvar for 1.22 is only possible with loopvarhash=qn
    	//
    	// Debug.LoopVar values to be preserved for 1.21 compatibility are 1 and 2,
    	// which are also set (=1) by GOEXPERIMENT=loopvar.  The knobs for turning on
    	// the new, unshared, loopvar behavior apply to versions less than 1.21 because
    	// (1) 1.21 also did that and (2) this is believed to be the likely use case;
    	// anyone checking to see if it affects their code will just run the GOEXPERIMENT
    	// but will not also update all their go.mod files to 1.21.
    	//
    	// -gcflags=-d=loopvar=3 enables logging for 1.22 but does not turn loopvar on for <= 1.21.
    
    	return is122 || lv > 0 && lv != 3
    }
    
    
    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.Int(cond)
    	if cond >= 0 {
    		w.blockStmt(stmt.Then)
    	} else {
    		w.pos(stmt.Then.Rbrace)
    	}
    	if cond <= 0 {
    		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)
    
    		var tagValue constant.Value
    
    			tv := w.p.typeAndValue(tag)
    			tagType = tv.Type
    			tagValue = tv.Value
    
    		} else {
    			tagType = types2.Typ[types2.Bool]
    
    			tagValue = constant.MakeBool(true)
    		}
    
    		if tagValue != nil {
    			// If the switch tag has a constant value, look for a case
    			// clause that we always branch to.
    			func() {
    				var target *syntax.CaseClause
    			Outer:
    				for _, clause := range stmt.Body {
    					if clause.Cases == nil {
    						target = clause
    					}
    
    					for _, cas := range syntax.UnpackListExpr(clause.Cases) {
    
    						tv := w.p.typeAndValue(cas)
    						if tv.Value == nil {
    							return // non-constant case; give up
    						}
    						if constant.Compare(tagValue, token.EQL, tv.Value) {
    							target = clause
    							break Outer
    						}
    					}
    				}
    				// We've found the target clause, if any.
    
    				if target != nil {
    					if hasFallthrough(target.Body) {
    						return // fallthrough is tricky; give up
    					}
    
    					// Rewrite as single "default" case.
    					target.Cases = nil
    					stmt.Body = []*syntax.CaseClause{target}
    				} else {
    					stmt.Body = nil
    				}
    
    				// Clear switch tag (i.e., replace with implicit "true").
    				tag = nil
    				stmt.Tag = nil
    				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 syntax.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 := syntax.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 := syntax.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 = syntax.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))
    
    
    		if _, isNil := obj.(*types2.Nil); isNil {
    
    			w.Code(exprZero)
    
    
    		// 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, syntax.Unparen(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 "Sizeof":
    				assert(len(expr.ArgList) == 1)
    				assert(!expr.HasDots)
    
    				w.Code(exprSizeof)
    				w.pos(expr)
    				w.typ(w.p.typeOf(expr.ArgList[0]))
    				return
    
    			case "Alignof":
    				assert(len(expr.ArgList) == 1)
    				assert(!expr.HasDots)
    
    				w.Code(exprAlignof)
    				w.pos(expr)
    				w.typ(w.p.typeOf(expr.ArgList[0]))
    				return
    
    			case "Offsetof":
    				assert(len(expr.ArgList) == 1)
    				assert(!expr.HasDots)
    				selector := syntax.Unparen(expr.ArgList[0]).(*syntax.SelectorExpr)
    				index := w.p.info.Selections[selector].Index()
    
    				w.Code(exprOffsetof)
    				w.pos(expr)
    				w.typ(deref2(w.p.typeOf(selector.X)))
    				w.Len(len(index) - 1)
    				for _, idx := range index {
    					w.Len(idx)
    				}
    				return
    
    
    			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))
    
    
    			if selector, ok := fun.(*syntax.SelectorExpr); ok {