in go/ssa/builder.go [538:791]
func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
switch e := e.(type) {
case *ast.BasicLit:
panic("non-constant BasicLit") // unreachable
case *ast.FuncLit:
fn2 := &Function{
name: fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)),
Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),
pos: e.Type.Func,
parent: fn,
Pkg: fn.Pkg,
Prog: fn.Prog,
syntax: e,
}
fn.AnonFuncs = append(fn.AnonFuncs, fn2)
b.buildFunction(fn2)
if fn2.FreeVars == nil {
return fn2
}
v := &MakeClosure{Fn: fn2}
v.setType(tv.Type)
for _, fv := range fn2.FreeVars {
v.Bindings = append(v.Bindings, fv.outer)
fv.outer = nil
}
return fn.emit(v)
case *ast.TypeAssertExpr: // single-result form only
return emitTypeAssert(fn, b.expr(fn, e.X), tv.Type, e.Lparen)
case *ast.CallExpr:
if fn.Pkg.info.Types[e.Fun].IsType() {
// Explicit type conversion, e.g. string(x) or big.Int(x)
x := b.expr(fn, e.Args[0])
y := emitConv(fn, x, tv.Type)
if y != x {
switch y := y.(type) {
case *Convert:
y.pos = e.Lparen
case *ChangeType:
y.pos = e.Lparen
case *MakeInterface:
y.pos = e.Lparen
case *SliceToArrayPointer:
y.pos = e.Lparen
}
}
return y
}
// Call to "intrinsic" built-ins, e.g. new, make, panic.
if id, ok := unparen(e.Fun).(*ast.Ident); ok {
if obj, ok := fn.Pkg.info.Uses[id].(*types.Builtin); ok {
if v := b.builtin(fn, obj, e.Args, tv.Type, e.Lparen); v != nil {
return v
}
}
}
// Regular function call.
var v Call
b.setCall(fn, e, &v.Call)
v.setType(tv.Type)
return fn.emit(&v)
case *ast.UnaryExpr:
switch e.Op {
case token.AND: // &X --- potentially escaping.
addr := b.addr(fn, e.X, true)
if _, ok := unparen(e.X).(*ast.StarExpr); ok {
// &*p must panic if p is nil (http://golang.org/s/go12nil).
// For simplicity, we'll just (suboptimally) rely
// on the side effects of a load.
// TODO(adonovan): emit dedicated nilcheck.
addr.load(fn)
}
return addr.address(fn)
case token.ADD:
return b.expr(fn, e.X)
case token.NOT, token.ARROW, token.SUB, token.XOR: // ! <- - ^
v := &UnOp{
Op: e.Op,
X: b.expr(fn, e.X),
}
v.setPos(e.OpPos)
v.setType(tv.Type)
return fn.emit(v)
default:
panic(e.Op)
}
case *ast.BinaryExpr:
switch e.Op {
case token.LAND, token.LOR:
return b.logicalBinop(fn, e)
case token.SHL, token.SHR:
fallthrough
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), tv.Type, e.OpPos)
case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:
cmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos)
// The type of x==y may be UntypedBool.
return emitConv(fn, cmp, types.Default(tv.Type))
default:
panic("illegal op in BinaryExpr: " + e.Op.String())
}
case *ast.SliceExpr:
var low, high, max Value
var x Value
switch fn.Pkg.typeOf(e.X).Underlying().(type) {
case *types.Array:
// Potentially escaping.
x = b.addr(fn, e.X, true).address(fn)
case *types.Basic, *types.Slice, *types.Pointer: // *array
x = b.expr(fn, e.X)
default:
panic("unreachable")
}
if e.High != nil {
high = b.expr(fn, e.High)
}
if e.Low != nil {
low = b.expr(fn, e.Low)
}
if e.Slice3 {
max = b.expr(fn, e.Max)
}
v := &Slice{
X: x,
Low: low,
High: high,
Max: max,
}
v.setPos(e.Lbrack)
v.setType(tv.Type)
return fn.emit(v)
case *ast.Ident:
obj := fn.Pkg.info.Uses[e]
// Universal built-in or nil?
switch obj := obj.(type) {
case *types.Builtin:
return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)}
case *types.Nil:
return nilConst(tv.Type)
}
// Package-level func or var?
if v := fn.Prog.packageLevelValue(obj); v != nil {
if _, ok := obj.(*types.Var); ok {
return emitLoad(fn, v) // var (address)
}
return v // (func)
}
// Local var.
return emitLoad(fn, fn.lookup(obj, false)) // var (address)
case *ast.SelectorExpr:
sel, ok := fn.Pkg.info.Selections[e]
if !ok {
// builtin unsafe.{Add,Slice}
if obj, ok := fn.Pkg.info.Uses[e.Sel].(*types.Builtin); ok {
return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)}
}
// qualified identifier
return b.expr(fn, e.Sel)
}
switch sel.Kind() {
case types.MethodExpr:
// (*T).f or T.f, the method f from the method-set of type T.
// The result is a "thunk".
return emitConv(fn, makeThunk(fn.Prog, sel), tv.Type)
case types.MethodVal:
// e.f where e is an expression and f is a method.
// The result is a "bound".
obj := sel.Obj().(*types.Func)
rt := recvType(obj)
wantAddr := isPointer(rt)
escaping := true
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
if isInterface(rt) {
// If v has interface type I,
// we must emit a check that v is non-nil.
// We use: typeassert v.(I).
emitTypeAssert(fn, v, rt, token.NoPos)
}
c := &MakeClosure{
Fn: makeBound(fn.Prog, obj),
Bindings: []Value{v},
}
c.setPos(e.Sel.Pos())
c.setType(tv.Type)
return fn.emit(c)
case types.FieldVal:
indices := sel.Index()
last := len(indices) - 1
v := b.expr(fn, e.X)
v = emitImplicitSelections(fn, v, indices[:last])
v = emitFieldSelection(fn, v, indices[last], false, e.Sel)
return v
}
panic("unexpected expression-relative selector")
case *ast.IndexExpr:
switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
case *types.Array:
// Non-addressable array (in a register).
v := &Index{
X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), tInt),
}
v.setPos(e.Lbrack)
v.setType(t.Elem())
return fn.emit(v)
case *types.Map:
// Maps are not addressable.
mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
v := &Lookup{
X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
}
v.setPos(e.Lbrack)
v.setType(mapt.Elem())
return fn.emit(v)
case *types.Basic: // => string
// Strings are not addressable.
v := &Lookup{
X: b.expr(fn, e.X),
Index: b.expr(fn, e.Index),
}
v.setPos(e.Lbrack)
v.setType(tByte)
return fn.emit(v)
case *types.Slice, *types.Pointer: // *array
// Addressable slice/array; use IndexAddr and Load.
return b.addr(fn, e, false).load(fn)
default:
panic("unexpected container type in IndexExpr: " + t.String())
}
case *ast.CompositeLit, *ast.StarExpr:
// Addressable types (lvalues)
return b.addr(fn, e, false).load(fn)
}
panic(fmt.Sprintf("unexpected expr: %T", e))
}