Newer
Older
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")
}
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)
}
assert(len(closureVars) == 0)
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.
w.Int64(-1)
w.Int64(-1)
}
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))
}
// @@@ Function bodies
// 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.sig = sig
w.dict = dict
if w.Bool(block != nil) {
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) {
idx := len(w.localsIdx)
w.Sync(pkgbits.SyncAddLocal)
if w.p.SyncMarkers() {
w.Int(idx)
w.varDictIndex(obj)
if w.localsIdx == nil {
w.localsIdx = make(map[*types2.Var]int)
}
w.localsIdx[obj] = idx
}
// 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})
w.closureVarsIdx[obj] = idx
}
}
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)
}
// @@@ Statements
// 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) {
w.Sync(pkgbits.SyncStmts)
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.Code(stmtIncDec)
w.op(binOps[stmt.Op])
w.expr(stmt.Lhs)
w.pos(stmt)
case stmt.Op != 0 && stmt.Op != syntax.Def:
w.Code(stmtAssignOp)
w.op(binOps[stmt.Op])
w.expr(stmt.Lhs)
w.pos(stmt)
Matthew Dempsky
committed
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)
}
case *syntax.BlockStmt:
w.Code(stmtBlock)
w.blockStmt(stmt)
case *syntax.BranchStmt:
w.Code(stmtBranch)
w.pos(stmt)
w.op(branchOps[stmt.Tok])
w.optLabel(stmt.Label)
case *syntax.CallStmt:
w.Code(stmtCall)
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.Code(stmtExpr)
w.expr(stmt.X)
case *syntax.ForStmt:
w.Code(stmtFor)
w.forStmt(stmt)
case *syntax.IfStmt:
w.Code(stmtIf)
w.ifStmt(stmt)
case *syntax.LabeledStmt:
w.Code(stmtLabel)
w.pos(stmt)
w.label(stmt.Label)
w.stmt1(stmt.Stmt)
case *syntax.ReturnStmt:
w.Code(stmtReturn)
resultTypes := w.sig.Results()
Matthew Dempsky
committed
dstType := func(i int) types2.Type {
return resultTypes.At(i).Type()
}
w.multiExpr(stmt, dstType, syntax.UnpackListExpr(stmt.Results))
case *syntax.SelectStmt:
w.Code(stmtSelect)
w.selectStmt(stmt)
case *syntax.SendStmt:
Matthew Dempsky
committed
chanType := types2.CoreType(w.p.typeOf(stmt.Chan)).(*types2.Chan)
w.Code(stmtSend)
w.pos(stmt)
w.expr(stmt.Chan)
w.implicitConvExpr(chanType.Elem(), stmt.Value)
case *syntax.SwitchStmt:
w.Code(stmtSwitch)
w.switchStmt(stmt)
}
}
func (w *writer) assignList(expr syntax.Expr) {
exprs := syntax.UnpackListExpr(expr)
w.Len(len(exprs))
for _, expr := range exprs {
Matthew Dempsky
committed
w.assign(expr)
}
}
func (w *writer) assign(expr syntax.Expr) {
expr = syntax.Unparen(expr)
Matthew Dempsky
committed
if name, ok := expr.(*syntax.Name); ok {
if name.Value == "_" {
w.Code(assignBlank)
return
Matthew Dempsky
committed
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)
Matthew Dempsky
committed
return
}
Matthew Dempsky
committed
w.Code(assignExpr)
w.expr(expr)
}
func (w *writer) declStmt(decl syntax.Decl) {
switch decl := decl.(type) {
default:
w.p.unexpected("declaration", decl)
case *syntax.ConstDecl, *syntax.TypeDecl:
case *syntax.VarDecl:
w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values)
// assignStmt writes out an assignment for "lhs = rhs".
Matthew Dempsky
committed
func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) {
lhs := syntax.UnpackListExpr(lhs0)
rhs := syntax.UnpackListExpr(rhs0)
Matthew Dempsky
committed
w.Code(stmtAssign)
w.pos(pos)
Matthew Dempsky
committed
// As if w.assignList(lhs0).
w.Len(len(lhs))
for _, expr := range lhs {
w.assign(expr)
}
Matthew Dempsky
committed
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 {
Matthew Dempsky
committed
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()
Matthew Dempsky
committed
} else {
Matthew Dempsky
committed
w.p.fatalf(dst, "cannot find type of destination object: %v", dst)
Matthew Dempsky
committed
}
}
Matthew Dempsky
committed
return w.p.typeOf(dst)
Matthew Dempsky
committed
}
Matthew Dempsky
committed
w.multiExpr(pos, dstType, rhs)
}
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)
w.openScope(stmt.Pos())
if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) {
w.assignList(rang.Lhs)
w.expr(rang.X)
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
}
dst := syntax.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 := types2.RangeKeyVal(w.p.typeOf(rang.X))
assign(0, keyType)
assign(1, valueType)
}
if stmt.Cond != nil && w.p.staticBool(&stmt.Cond) < 0 { // always false
stmt.Post = nil
stmt.Body.List = nil
}
w.pos(stmt)
w.stmt(stmt.Init)
Matthew Dempsky
committed
w.optExpr(stmt.Cond)
w.stmt(stmt.Post)
}
w.blockStmt(stmt.Body)
w.Bool(w.distinctVars(stmt))
w.closeAnotherScope()
}
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) {
cond := w.p.staticBool(&stmt.Cond)
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)
w.pos(stmt)
w.Len(len(stmt.Body))
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)
var iface, tagType types2.Type
if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) {
iface = w.p.typeOf(guard.X)
w.pos(guard)
if tag := guard.Lhs; w.Bool(tag != nil) {
w.pos(tag)
// Like w.localIdent, but we don't have a types2.Object.
w.Sync(pkgbits.SyncLocalIdent)
w.pkg(w.p.curpkg)
w.String(tag.Value)
}
w.expr(guard.X)
} else {
tag := stmt.Tag
var tagValue constant.Value
if tag != nil {
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) {
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
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)
}
w.Len(len(stmt.Body))
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)) {
continue
}
w.exprType(iface, cas)
}
} else {
// 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.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) {
w.Sync(pkgbits.SyncLabel)
// TODO(mdempsky): Replace label strings with dense indices.
w.String(label.Value)
}
func (w *writer) optLabel(label *syntax.Name) {
w.Sync(pkgbits.SyncOptLabel)
if w.Bool(label != nil) {
w.label(label)
}
}
// @@@ Expressions
// expr writes the given expression into the function body bitstream.
func (w *writer) expr(expr syntax.Expr) {
Matthew Dempsky
committed
base.Assertf(expr != nil, "missing expression")
expr = syntax.Unparen(expr) // skip parens; unneeded after typecheck
obj, inst := lookupObj(w.p, expr)
Robert Griesemer
committed
targs := inst.TypeArgs
if tv, ok := w.p.maybeTypeAndValue(expr); ok {
if tv.IsType() {
Matthew Dempsky
committed
w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr))
}
if tv.Value != nil {
w.Code(exprConst)
w.pos(expr)
Cuong Manh Le
committed
typ := idealType(tv)
assert(typ != nil)
w.typ(typ)
w.Value(tv.Value)
if _, isNil := obj.(*types2.Nil); isNil {
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 obj != nil {
if targs.Len() != 0 {
obj := obj.(*types2.Func)
w.Code(exprFuncInst)
w.pos(expr)
w.funcInst(obj, targs)
return
}
if isGlobal(obj) {
w.Code(exprGlobal)
w.obj(obj, nil)
return
}
obj := obj.(*types2.Var)
assert(!obj.IsField())
w.Code(exprLocal)
w.useLocal(expr.Pos(), obj)
return
}
switch expr := expr.(type) {
default:
w.p.unexpected("expression", expr)
case *syntax.CompositeLit:
w.Code(exprCompLit)
w.compLit(expr)
case *syntax.FuncLit:
w.Code(exprFuncLit)
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)
typ := w.recvExpr(expr, sel)
w.pos(expr)
w.methodExpr(expr, typ, sel)
case types2.MethodExpr:
w.Code(exprMethodExpr)
tv := w.p.typeAndValue(expr.X)
assert(tv.IsType())
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.pos(expr)
w.methodExpr(expr, typ, sel)
Matthew Dempsky
committed
}
case *syntax.IndexExpr:
_ = w.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation
xtyp := w.p.typeOf(expr.X)
Matthew Dempsky
committed
var keyType types2.Type
if mapType, ok := types2.CoreType(xtyp).(*types2.Map); ok {
Matthew Dempsky
committed
keyType = mapType.Key()
}
w.Code(exprIndex)
w.expr(expr.X)
w.pos(expr)
w.implicitConvExpr(keyType, expr.Index)
if keyType != nil {
w.rtype(xtyp)
}
case *syntax.SliceExpr:
w.Code(exprSlice)
w.expr(expr.X)
w.pos(expr)
for _, n := range &expr.Index {
Matthew Dempsky
committed
w.optExpr(n)
}
case *syntax.AssertExpr:
iface := w.p.typeOf(expr.X)
w.Code(exprAssert)
w.expr(expr.X)
w.pos(expr)
w.exprType(iface, expr.Type)
w.rtype(iface)
case *syntax.Operation:
if expr.Y == nil {
w.Code(exprUnaryOp)
w.op(unOps[expr.Op])
w.pos(expr)
w.expr(expr.X)
break
}
Matthew Dempsky
committed
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.Code(exprBinaryOp)
w.op(binOps[expr.Op])
w.implicitConvExpr(commonType, expr.X)
w.implicitConvExpr(commonType, expr.Y)
case *syntax.CallExpr:
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)
break
}
var rtype types2.Type
if tv.IsBuiltin() {
switch obj, _ := lookupObj(w.p, syntax.Unparen(expr.Fun)); obj.Name() {
Matthew Dempsky
committed
case "make":
assert(len(expr.ArgList) >= 1)
assert(!expr.HasDots)
w.Code(exprMake)
w.pos(expr)
w.exprType(nil, expr.ArgList[0])
Matthew Dempsky
committed
w.exprs(expr.ArgList[1:])
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))
}
Matthew Dempsky
committed
return
case "new":
assert(len(expr.ArgList) == 1)
assert(!expr.HasDots)
w.Code(exprNew)
w.pos(expr)
w.exprType(nil, expr.ArgList[0])
Matthew Dempsky
committed
return
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
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))
Matthew Dempsky
committed
}
}
writeFunExpr := func() {
fun := syntax.Unparen(expr.Fun)
if selector, ok := fun.(*syntax.SelectorExpr); ok {