Files
moxa/interp/cfg.go
Marc Vertes f3dbce93a4 interp: improve handling of generic types
When generating a new type, the parameter type was not correctly duplicated in the new AST. This is fixed by making copyNode recursive if needed. The out of order processing of generic types has also been fixed.

Fixes #1488
2023-02-08 11:48:05 +01:00

3112 lines
79 KiB
Go

package interp
import (
"fmt"
"go/constant"
"log"
"math"
"path/filepath"
"reflect"
"strings"
"unicode"
)
// A cfgError represents an error during CFG build stage.
type cfgError struct {
*node
error
}
func (c *cfgError) Error() string { return c.error.Error() }
var constOp = map[action]func(*node){
aAdd: addConst,
aSub: subConst,
aMul: mulConst,
aQuo: quoConst,
aRem: remConst,
aAnd: andConst,
aOr: orConst,
aShl: shlConst,
aShr: shrConst,
aAndNot: andNotConst,
aXor: xorConst,
aNot: notConst,
aBitNot: bitNotConst,
aNeg: negConst,
aPos: posConst,
}
var constBltn = map[string]func(*node){
bltnComplex: complexConst,
bltnImag: imagConst,
bltnReal: realConst,
}
const nilIdent = "nil"
// cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
// variables. A list of nodes of init functions is returned.
// Following this pass, the CFG is ready to run.
func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string) ([]*node, error) {
if sc == nil {
sc = interp.initScopePkg(importPath, pkgName)
}
check := typecheck{scope: sc}
var initNodes []*node
var err error
baseName := filepath.Base(interp.fset.Position(root.pos).Filename)
root.Walk(func(n *node) bool {
// Pre-order processing
if err != nil {
return false
}
if n.scope == nil {
n.scope = sc
}
switch n.kind {
case binaryExpr, unaryExpr, parenExpr:
if isBoolAction(n) {
break
}
// Gather assigned type if set, to give context for type propagation at post-order.
switch n.anc.kind {
case assignStmt, defineStmt:
a := n.anc
i := childPos(n) - a.nright
if i < 0 {
break
}
if len(a.child) > a.nright+a.nleft {
i--
}
dest := a.child[i]
if dest.typ == nil {
break
}
if dest.typ.incomplete {
err = n.cfgErrorf("invalid type declaration")
return false
}
if !isInterface(dest.typ) {
// Interface type are not propagated, and will be resolved at post-order.
n.typ = dest.typ
}
case binaryExpr, unaryExpr, parenExpr:
n.typ = n.anc.typ
}
case defineStmt:
// Determine type of variables initialized at declaration, so it can be propagated.
if n.nleft+n.nright == len(n.child) {
// No type was specified on the left hand side, it will resolved at post-order.
break
}
n.typ, err = nodeType(interp, sc, n.child[n.nleft])
if err != nil {
break
}
for i := 0; i < n.nleft; i++ {
n.child[i].typ = n.typ
}
case blockStmt:
if n.anc != nil && n.anc.kind == rangeStmt {
// For range block: ensure that array or map type is propagated to iterators
// prior to process block. We cannot perform this at RangeStmt pre-order because
// type of array like value is not yet known. This could be fixed in ast structure
// by setting array/map node as 1st child of ForRangeStmt instead of 3rd child of
// RangeStmt. The following workaround is less elegant but ok.
c := n.anc.child[1]
if c != nil && c.typ != nil && isSendChan(c.typ) {
err = c.cfgErrorf("invalid operation: range %s receive from send-only channel", c.ident)
return false
}
if t := sc.rangeChanType(n.anc); t != nil {
// range over channel
e := n.anc.child[0]
index := sc.add(t.val)
sc.sym[e.ident] = &symbol{index: index, kind: varSym, typ: t.val}
e.typ = t.val
e.findex = index
n.anc.gen = rangeChan
} else {
// range over array or map
var ktyp, vtyp *itype
var k, v, o *node
if len(n.anc.child) == 4 {
k, v, o = n.anc.child[0], n.anc.child[1], n.anc.child[2]
} else {
k, o = n.anc.child[0], n.anc.child[1]
}
switch o.typ.cat {
case valueT, linkedT:
typ := o.typ.rtype
if o.typ.cat == linkedT {
typ = o.typ.val.TypeOf()
}
switch typ.Kind() {
case reflect.Map:
n.anc.gen = rangeMap
ityp := valueTOf(reflect.TypeOf((*reflect.MapIter)(nil)))
sc.add(ityp)
ktyp = valueTOf(typ.Key())
vtyp = valueTOf(typ.Elem())
case reflect.String:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
sc.add(sc.getType("int")) // Add a dummy type to store index for range
ktyp = sc.getType("int")
vtyp = sc.getType("rune")
case reflect.Array, reflect.Slice:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int")
vtyp = valueTOf(typ.Elem())
}
case mapT:
n.anc.gen = rangeMap
ityp := valueTOf(reflect.TypeOf((*reflect.MapIter)(nil)))
sc.add(ityp)
ktyp = o.typ.key
vtyp = o.typ.val
case ptrT:
ktyp = sc.getType("int")
vtyp = o.typ.val
if vtyp.cat == valueT {
vtyp = valueTOf(vtyp.rtype.Elem())
} else {
vtyp = vtyp.val
}
case stringT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
sc.add(sc.getType("int")) // Add a dummy type to store index for range
ktyp = sc.getType("int")
vtyp = sc.getType("rune")
case arrayT, sliceT, variadicT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int")
vtyp = o.typ.val
}
kindex := sc.add(ktyp)
sc.sym[k.ident] = &symbol{index: kindex, kind: varSym, typ: ktyp}
k.typ = ktyp
k.findex = kindex
if v != nil {
vindex := sc.add(vtyp)
sc.sym[v.ident] = &symbol{index: vindex, kind: varSym, typ: vtyp}
v.typ = vtyp
v.findex = vindex
}
}
}
n.findex = -1
n.val = nil
sc = sc.pushBloc()
// Pre-define symbols for labels defined in this block, so we are sure that
// they are already defined when met.
// TODO(marc): labels must be stored outside of symbols to avoid collisions.
for _, c := range n.child {
if c.kind != labeledStmt {
continue
}
label := c.child[0].ident
sym := &symbol{kind: labelSym, node: c, index: -1}
sc.sym[label] = sym
c.sym = sym
}
// If block is the body of a function, get declared variables in current scope.
// This is done in order to add the func signature symbols into sc.sym,
// as we will need them in post-processing.
if n.anc != nil && n.anc.kind == funcDecl {
for k, v := range sc.anc.sym {
sc.sym[k] = v
}
}
case breakStmt, continueStmt, gotoStmt:
if len(n.child) == 0 {
break
}
// Handle labeled statements.
label := n.child[0].ident
if sym, _, ok := sc.lookup(label); ok {
if sym.kind != labelSym {
err = n.child[0].cfgErrorf("label %s not defined", label)
break
}
n.sym = sym
} else {
n.sym = &symbol{kind: labelSym, index: -1}
sc.sym[label] = n.sym
}
if n.kind == gotoStmt {
n.sym.from = append(n.sym.from, n) // To allow forward goto statements.
}
case caseClause:
sc = sc.pushBloc()
if sn := n.anc.anc; sn.kind == typeSwitch && sn.child[1].action == aAssign {
// Type switch clause with a var defined in switch guard.
var typ *itype
if len(n.child) == 2 {
// 1 type in clause: define the var with this type in the case clause scope.
switch {
case n.child[0].ident == nilIdent:
typ = sc.getType("interface{}")
case !n.child[0].isType(sc):
err = n.cfgErrorf("%s is not a type", n.child[0].ident)
default:
typ, err = nodeType(interp, sc, n.child[0])
}
} else {
// Define the var with the type in the switch guard expression.
typ = sn.child[1].child[1].child[0].typ
}
if err != nil {
return false
}
nod := n.lastChild().child[0]
index := sc.add(typ)
sc.sym[nod.ident] = &symbol{index: index, kind: varSym, typ: typ}
nod.findex = index
nod.typ = typ
}
case commClauseDefault:
sc = sc.pushBloc()
case commClause:
sc = sc.pushBloc()
if len(n.child) > 0 && n.child[0].action == aAssign {
ch := n.child[0].child[1].child[0]
var typ *itype
if typ, err = nodeType(interp, sc, ch); err != nil {
return false
}
if !isChan(typ) {
err = n.cfgErrorf("invalid operation: receive from non-chan type")
return false
}
elem := chanElement(typ)
assigned := n.child[0].child[0]
index := sc.add(elem)
sc.sym[assigned.ident] = &symbol{index: index, kind: varSym, typ: elem}
assigned.findex = index
assigned.typ = elem
}
case compositeLitExpr:
if len(n.child) > 0 && n.child[0].isType(sc) {
// Get type from 1st child.
if n.typ, err = nodeType(interp, sc, n.child[0]); err != nil {
return false
}
// Indicate that the first child is the type.
n.nleft = 1
} else {
// Get type from ancestor (implicit type).
if n.anc.kind == keyValueExpr && n == n.anc.child[0] {
n.typ = n.anc.typ.key
} else if atyp := n.anc.typ; atyp != nil {
if atyp.cat == valueT && hasElem(atyp.rtype) {
n.typ = valueTOf(atyp.rtype.Elem())
} else {
n.typ = atyp.val
}
}
if n.typ == nil {
// A nil type indicates either an error or a generic type.
// A child indexExpr or indexListExpr is used for type parameters,
// it indicates an instanciated generic.
if n.child[0].kind != indexExpr && n.child[0].kind != indexListExpr {
err = n.cfgErrorf("undefined type")
return false
}
t0, err1 := nodeType(interp, sc, n.child[0].child[0])
if err1 != nil {
return false
}
if t0.cat != genericT {
err = n.cfgErrorf("undefined type")
return false
}
// We have a composite literal of generic type, instantiate it.
lt := []*itype{}
for _, n1 := range n.child[0].child[1:] {
t1, err1 := nodeType(interp, sc, n1)
if err1 != nil {
return false
}
lt = append(lt, t1)
}
var g *node
g, _, err = genAST(sc, t0.node.anc, lt)
if err != nil {
return false
}
n.child[0] = g.lastChild()
n.typ, err = nodeType(interp, sc, n.child[0])
if err != nil {
return false
}
// Generate methods if any.
for _, nod := range t0.method {
gm, _, err2 := genAST(nod.scope, nod, lt)
if err2 != nil {
err = err2
return false
}
gm.typ, err = nodeType(interp, nod.scope, gm.child[2])
if err != nil {
return false
}
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
return false
}
if err = genRun(gm); err != nil {
return false
}
n.typ.addMethod(gm)
}
n.nleft = 1 // Indictate the type of composite literal.
}
}
child := n.child
if n.nleft > 0 {
n.child[0].typ = n.typ
child = n.child[1:]
}
// Propagate type to children, to handle implicit types
for _, c := range child {
if isBlank(c) {
err = n.cfgErrorf("cannot use _ as value")
return false
}
switch c.kind {
case binaryExpr, unaryExpr, compositeLitExpr:
// Do not attempt to propagate composite type to operator expressions,
// it breaks constant folding.
case keyValueExpr, typeAssertExpr, indexExpr:
c.typ = n.typ
default:
if c.ident == nilIdent {
c.typ = sc.getType(nilIdent)
continue
}
if c.typ, err = nodeType(interp, sc, c); err != nil {
return false
}
}
}
case forStmt0, forStmt1, forStmt2, forStmt3, forStmt4, forStmt5, forStmt6, forStmt7, forRangeStmt:
sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.lastChild()
case funcLit:
n.typ = nil // to force nodeType to recompute the type
if n.typ, err = nodeType(interp, sc, n); err != nil {
return false
}
n.findex = sc.add(n.typ)
fallthrough
case funcDecl:
// Do not allow function declarations without body.
if len(n.child) < 4 {
err = n.cfgErrorf("function declaration without body is unsupported (linkname or assembly can not be interpreted).")
return false
}
n.val = n
// Skip substree in case of a generic function.
if len(n.child[2].child[0].child) > 0 {
return false
}
// Skip subtree if the function is a method with a generic receiver.
if len(n.child[0].child) > 0 {
recvTypeNode := n.child[0].child[0].lastChild()
typ, err := nodeType(interp, sc, recvTypeNode)
if err != nil {
return false
}
if typ.cat == genericT || (typ.val != nil && typ.val.cat == genericT) {
return false
}
if typ.cat == ptrT {
rc0 := recvTypeNode.child[0]
rt0, err := nodeType(interp, sc, rc0)
if err != nil {
return false
}
if rc0.kind == indexExpr && rt0.cat == structT {
return false
}
}
}
// Compute function type before entering local scope to avoid
// possible collisions with function argument names.
n.child[2].typ, err = nodeType(interp, sc, n.child[2])
if err != nil {
return false
}
n.typ = n.child[2].typ
// Add a frame indirection level as we enter in a func.
sc = sc.pushFunc()
sc.def = n
// Allocate frame space for return values, define output symbols.
if len(n.child[2].child) == 3 {
for _, c := range n.child[2].child[2].child {
var typ *itype
if typ, err = nodeType(interp, sc, c.lastChild()); err != nil {
return false
}
if len(c.child) > 1 {
for _, cc := range c.child[:len(c.child)-1] {
sc.sym[cc.ident] = &symbol{index: sc.add(typ), kind: varSym, typ: typ}
}
} else {
sc.add(typ)
}
}
}
// Define receiver symbol.
if len(n.child[0].child) > 0 {
var typ *itype
fr := n.child[0].child[0]
recvTypeNode := fr.lastChild()
if typ, err = nodeType(interp, sc, recvTypeNode); err != nil {
return false
}
if typ.cat == nilT {
// This may happen when instantiating generic methods.
s2, _, ok := sc.lookup(typ.id())
if !ok {
err = n.cfgErrorf("type not found: %s", typ.id())
break
}
typ = s2.typ
if typ.cat == nilT {
err = n.cfgErrorf("nil type: %s", typ.id())
break
}
}
recvTypeNode.typ = typ
n.child[2].typ.recv = typ
n.typ.recv = typ
index := sc.add(typ)
if len(fr.child) > 1 {
sc.sym[fr.child[0].ident] = &symbol{index: index, kind: varSym, typ: typ}
}
}
// Define input parameter symbols.
for _, c := range n.child[2].child[1].child {
var typ *itype
if typ, err = nodeType(interp, sc, c.lastChild()); err != nil {
return false
}
for _, cc := range c.child[:len(c.child)-1] {
sc.sym[cc.ident] = &symbol{index: sc.add(typ), kind: varSym, typ: typ}
}
}
if n.child[1].ident == "init" && len(n.child[0].child) == 0 {
initNodes = append(initNodes, n)
}
case ifStmt0, ifStmt1, ifStmt2, ifStmt3:
sc = sc.pushBloc()
case switchStmt, switchIfStmt, typeSwitch:
// Make sure default clause is in last position.
c := n.lastChild().child
if i, l := getDefault(n), len(c)-1; i >= 0 && i != l {
c[i], c[l] = c[l], c[i]
}
sc = sc.pushBloc()
sc.loop = n
case importSpec:
// Already all done in GTA.
return false
case typeSpec:
// Processing already done in GTA pass for global types, only parses inlined types.
if sc.def == nil {
return false
}
typeName := n.child[0].ident
var typ *itype
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
return false
}
if typ.incomplete {
// Type may still be incomplete in case of a local recursive struct declaration.
if typ, err = typ.finalize(); err != nil {
err = n.cfgErrorf("invalid type declaration")
return false
}
}
switch n.child[1].kind {
case identExpr, selectorExpr:
n.typ = namedOf(typ, pkgName, typeName)
default:
n.typ = typ
n.typ.name = typeName
}
sc.sym[typeName] = &symbol{kind: typeSym, typ: n.typ}
return false
case constDecl:
// Early parse of constDecl subtrees, to compute all constant
// values which may be used in further declarations.
if !sc.global {
for _, c := range n.child {
if _, err = interp.cfg(c, sc, importPath, pkgName); err != nil {
// No error processing here, to allow recovery in subtree nodes.
err = nil
}
}
}
case arrayType, basicLit, chanType, chanTypeRecv, chanTypeSend, funcType, interfaceType, mapType, structType:
n.typ, err = nodeType(interp, sc, n)
return false
}
return true
}, func(n *node) {
// Post-order processing
if err != nil {
return
}
defer func() {
if r := recover(); r != nil {
// Display the exact location in input source which triggered the panic
panic(n.cfgErrorf("CFG post-order panic: %v", r))
}
}()
switch n.kind {
case addressExpr:
if isBlank(n.child[0]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
wireChild(n)
err = check.addressExpr(n)
if err != nil {
break
}
n.typ = ptrOf(n.child[0].typ)
n.findex = sc.add(n.typ)
case assignStmt, defineStmt:
if n.anc.kind == typeSwitch && n.anc.child[1] == n {
// type switch guard assignment: assign dest to concrete value of src
n.gen = nop
break
}
var atyp *itype
if n.nleft+n.nright < len(n.child) {
if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil {
break
}
}
var sbase int
if n.nright > 0 {
sbase = len(n.child) - n.nright
}
wireChild(n)
for i := 0; i < n.nleft; i++ {
dest, src := n.child[i], n.child[sbase+i]
updateSym := false
var sym *symbol
var level int
if dest.rval.IsValid() && isConstType(dest.typ) {
err = n.cfgErrorf("cannot assign to %s (%s constant)", dest.rval, dest.typ.str)
break
}
if isBlank(src) {
err = n.cfgErrorf("cannot use _ as value")
break
}
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
if atyp != nil {
dest.typ = atyp
} else {
if src.typ, err = nodeType(interp, sc, src); err != nil {
return
}
if src.typ.isBinMethod {
dest.typ = valueTOf(src.typ.methodCallType())
} else {
// In a new definition, propagate the source type to the destination
// type. If the source is an untyped constant, make sure that the
// type matches a default type.
dest.typ = sc.fixType(src.typ)
}
}
if dest.typ.incomplete {
return
}
if sc.global {
// Do not overload existing symbols (defined in GTA) in global scope
sym, _, _ = sc.lookup(dest.ident)
}
if sym == nil {
sym = &symbol{index: sc.add(dest.typ), kind: varSym, typ: dest.typ}
sc.sym[dest.ident] = sym
}
dest.val = src.val
dest.recv = src.recv
dest.findex = sym.index
updateSym = true
} else {
sym, level, _ = sc.lookup(dest.ident)
}
err = check.assignExpr(n, dest, src)
if err != nil {
break
}
if updateSym {
sym.typ = dest.typ
sym.rval = src.rval
// As we are updating the sym type, we need to update the sc.type
// when the sym has an index.
if sym.index >= 0 {
sc.types[sym.index] = sym.typ.frameType()
}
}
n.findex = dest.findex
n.level = dest.level
// In the following, we attempt to optimize by skipping the assign
// operation and setting the source location directly to the destination
// location in the frame.
//
switch {
case n.action != aAssign:
// Do not skip assign operation if it is combined with another operator.
case src.rval.IsValid():
// Do not skip assign operation if setting from a constant value.
case isMapEntry(dest):
// Setting a map entry requires an additional step, do not optimize.
// As we only write, skip the default useless getIndexMap dest action.
dest.gen = nop
case isFuncField(dest):
// Setting a struct field of function type requires an extra step. Do not optimize.
case isCall(src) && !isInterfaceSrc(dest.typ) && n.kind != defineStmt:
// Call action may perform the assignment directly.
if dest.typ.id() != src.typ.id() {
// Skip optimitization if returned type doesn't match assigned one.
break
}
n.gen = nop
src.level = level
src.findex = dest.findex
if src.typ.untyped && !dest.typ.untyped {
src.typ = dest.typ
}
case src.action == aRecv:
// Assign by reading from a receiving channel.
n.gen = nop
src.findex = dest.findex // Set recv address to LHS.
dest.typ = src.typ
case src.action == aCompositeLit:
if dest.typ.cat == valueT && dest.typ.rtype.Kind() == reflect.Interface {
// Skip optimisation for assigned interface.
break
}
if dest.action == aGetIndex || dest.action == aStar {
// Skip optimization, as it does not work when assigning to a struct field or a dereferenced pointer.
break
}
n.gen = nop
src.findex = dest.findex
src.level = level
case len(n.child) < 4 && isArithmeticAction(src) && !isInterface(dest.typ):
// Optimize single assignments from some arithmetic operations.
src.typ = dest.typ
src.findex = dest.findex
src.level = level
n.gen = nop
case src.kind == basicLit:
// Assign to nil.
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
case n.nright == 0:
n.gen = reset
}
n.typ = dest.typ
if sym != nil {
sym.typ = n.typ
sym.recv = src.recv
}
n.level = level
if n.anc.kind == constDecl {
n.gen = nop
n.findex = notInFrame
if sym, _, ok := sc.lookup(dest.ident); ok {
sym.kind = constSym
}
if childPos(n) == len(n.anc.child)-1 {
sc.iota = 0
} else {
sc.iota++
}
}
}
case incDecStmt:
err = check.unaryExpr(n)
if err != nil {
break
}
wireChild(n)
n.findex = n.child[0].findex
n.level = n.child[0].level
n.typ = n.child[0].typ
if sym, level, ok := sc.lookup(n.child[0].ident); ok {
sym.typ = n.typ
n.level = level
}
case assignXStmt:
wireChild(n)
l := len(n.child) - 1
switch lc := n.child[l]; lc.kind {
case callExpr:
if n.child[l-1].isType(sc) {
l--
}
if r := lc.child[0].typ.numOut(); r != l {
err = n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, lc.child[0].name(), r)
}
if isBinCall(lc, sc) {
n.gen = nop
} else {
// TODO (marc): skip if no conversion or wrapping is needed.
n.gen = assignFromCall
}
case indexExpr:
lc.gen = getIndexMap2
n.gen = nop
case typeAssertExpr:
if n.child[0].ident == "_" {
lc.gen = typeAssertStatus
} else {
lc.gen = typeAssertLong
}
n.gen = nop
case unaryExpr:
if lc.action == aRecv {
lc.gen = recv2
n.gen = nop
}
}
case defineXStmt:
wireChild(n)
if sc.def == nil {
// In global scope, type definition already handled by GTA.
break
}
err = compDefineX(sc, n)
case binaryExpr:
wireChild(n)
nilSym := interp.universe.sym[nilIdent]
c0, c1 := n.child[0], n.child[1]
err = check.binaryExpr(n)
if err != nil {
break
}
switch n.action {
case aRem:
n.typ = c0.typ
case aShl, aShr:
if c0.typ.untyped {
break
}
n.typ = c0.typ
case aEqual, aNotEqual:
n.typ = sc.getType("bool")
if c0.sym == nilSym || c1.sym == nilSym {
if n.action == aEqual {
n.gen = isNil
} else {
n.gen = isNotNil
}
}
case aGreater, aGreaterEqual, aLower, aLowerEqual:
n.typ = sc.getType("bool")
}
if err != nil {
break
}
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
break
}
}
if c0.rval.IsValid() && c1.rval.IsValid() && (!isInterface(n.typ)) && constOp[n.action] != nil {
n.typ.TypeOf() // Force compute of reflection type.
constOp[n.action](n) // Compute a constant result now rather than during exec.
}
switch {
case n.rval.IsValid():
// This operation involved constants, and the result is already computed
// by constOp and available in n.rval. Nothing else to do at execution.
n.gen = nop
n.findex = notInFrame
case n.anc.kind == assignStmt && n.anc.action == aAssign && n.anc.nleft == 1:
// To avoid a copy in frame, if the result is to be assigned, store it directly
// at the frame location of destination.
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
n.level = dest.level
case n.anc.kind == returnStmt:
// To avoid a copy in frame, if the result is to be returned, store it directly
// at the frame location reserved for output arguments.
n.findex = childPos(n)
default:
// Allocate a new location in frame, and store the result here.
n.findex = sc.add(n.typ)
}
case indexExpr:
if isBlank(n.child[0]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
wireChild(n)
t := n.child[0].typ
for t.cat == linkedT {
t = t.val
}
switch t.cat {
case ptrT:
n.typ = t.val
if t.val.cat == valueT {
n.typ = valueTOf(t.val.rtype.Elem())
} else {
n.typ = t.val.val
}
case stringT:
n.typ = sc.getType("byte")
case valueT:
if t.rtype.Kind() == reflect.String {
n.typ = sc.getType("byte")
} else {
n.typ = valueTOf(t.rtype.Elem())
}
case funcT:
// A function indexed by a type means an instantiated generic function.
c1 := n.child[1]
if !c1.isType(sc) {
n.typ = t
return
}
g, found, err := genAST(sc, t.node.anc, []*itype{c1.typ})
if err != nil {
return
}
if !found {
if _, err = interp.cfg(g, t.node.anc.scope, importPath, pkgName); err != nil {
return
}
// Generate closures for function body.
if err = genRun(g.child[3]); err != nil {
return
}
}
// Replace generic func node by instantiated one.
n.anc.child[childPos(n)] = g
n.typ = g.typ
return
case genericT:
name := t.id() + "[" + n.child[1].typ.id() + "]"
sym, _, ok := sc.lookup(name)
if !ok {
err = n.cfgErrorf("type not found: %s", name)
return
}
n.gen = nop
n.typ = sym.typ
return
case structT:
// A struct indexed by a Type means an instantiated generic struct.
name := t.name + "[" + n.child[1].ident + "]"
sym, _, ok := sc.lookup(name)
if ok {
n.typ = sym.typ
n.findex = sc.add(n.typ)
n.gen = nop
return
}
default:
n.typ = t.val
}
n.findex = sc.add(n.typ)
typ := t.TypeOf()
if typ.Kind() == reflect.Map {
err = check.assignment(n.child[1], t.key, "map index")
n.gen = getIndexMap
break
}
l := -1
switch k := typ.Kind(); k {
case reflect.Array:
l = typ.Len()
fallthrough
case reflect.Slice, reflect.String:
n.gen = getIndexArray
case reflect.Ptr:
if typ2 := typ.Elem(); typ2.Kind() == reflect.Array {
l = typ2.Len()
n.gen = getIndexArray
} else {
err = n.cfgErrorf("type %v does not support indexing", typ)
}
default:
err = n.cfgErrorf("type is not an array, slice, string or map: %v", t.id())
}
err = check.index(n.child[1], l)
case blockStmt:
wireChild(n)
if len(n.child) > 0 {
l := n.lastChild()
n.findex = l.findex
n.level = l.level
n.val = l.val
n.sym = l.sym
n.typ = l.typ
n.rval = l.rval
}
sc = sc.pop()
case constDecl:
wireChild(n)
case varDecl:
// Global varDecl do not need to be wired as this
// will be handled after cfg.
if n.anc.kind == fileStmt {
break
}
wireChild(n)
case sendStmt:
if !isChan(n.child[0].typ) {
err = n.cfgErrorf("invalid operation: cannot send to non-channel %s", n.child[0].typ.id())
break
}
fallthrough
case declStmt, exprStmt:
wireChild(n)
l := n.lastChild()
n.findex = l.findex
n.level = l.level
n.val = l.val
n.sym = l.sym
n.typ = l.typ
n.rval = l.rval
case breakStmt:
if len(n.child) == 0 {
n.tnext = sc.loop
break
}
if !n.hasAnc(n.sym.node) {
err = n.cfgErrorf("invalid break label %s", n.child[0].ident)
break
}
n.tnext = n.sym.node
case continueStmt:
if len(n.child) == 0 {
n.tnext = sc.loopRestart
break
}
if !n.hasAnc(n.sym.node) {
err = n.cfgErrorf("invalid continue label %s", n.child[0].ident)
break
}
n.tnext = n.sym.node.child[1].lastChild().start
case gotoStmt:
if n.sym.node == nil {
// It can be only due to a forward goto, to be resolved at labeledStmt.
// Invalid goto labels are catched at AST parsing.
break
}
n.tnext = n.sym.node.start
case labeledStmt:
wireChild(n)
if len(n.child) > 1 {
n.start = n.child[1].start
}
for _, c := range n.sym.from {
c.tnext = n.start // Resolve forward goto.
}
case callExpr:
for _, c := range n.child {
if isBlank(c) {
err = n.cfgErrorf("cannot use _ as value")
return
}
}
wireChild(n)
switch c0 := n.child[0]; {
case c0.kind == indexListExpr:
// Instantiate a generic function then call it.
fun := c0.child[0].sym.node
lt := []*itype{}
for _, c := range c0.child[1:] {
lt = append(lt, c.typ)
}
g, found, err := genAST(sc, fun, lt)
if err != nil {
return
}
if !found {
_, err = interp.cfg(g, fun.scope, importPath, pkgName)
if err != nil {
return
}
err = genRun(g.child[3]) // Generate closures for function body.
if err != nil {
return
}
}
n.child[0] = g
c0 = n.child[0]
wireChild(n)
if typ := c0.typ; len(typ.ret) > 0 {
n.typ = typ.ret[0]
if n.anc.kind == returnStmt && n.typ.id() == sc.def.typ.ret[0].id() {
// Store the result directly to the return value area of frame.
// It can be done only if no type conversion at return is involved.
n.findex = childPos(n)
} else {
n.findex = sc.add(n.typ)
for _, t := range typ.ret[1:] {
sc.add(t)
}
}
} else {
n.findex = notInFrame
}
case isBuiltinCall(n, sc):
bname := c0.ident
err = check.builtin(bname, n, n.child[1:], n.action == aCallSlice)
if err != nil {
break
}
n.gen = c0.sym.builtin
c0.typ = &itype{cat: builtinT, name: bname}
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
switch {
case n.typ.cat == builtinT:
n.findex = notInFrame
n.val = nil
case n.anc.kind == returnStmt:
// Store result directly to frame output location, to avoid a frame copy.
n.findex = 0
case bname == "cap" && isInConstOrTypeDecl(n):
t := n.child[1].typ.TypeOf()
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
switch t.Kind() {
case reflect.Array, reflect.Chan:
capConst(n)
default:
err = n.cfgErrorf("cap argument is not an array or channel")
}
n.findex = notInFrame
n.gen = nop
case bname == "len" && isInConstOrTypeDecl(n):
t := n.child[1].typ.TypeOf()
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
switch t.Kind() {
case reflect.Array, reflect.Chan, reflect.String:
lenConst(n)
default:
err = n.cfgErrorf("len argument is not an array, channel or string")
}
n.findex = notInFrame
n.gen = nop
default:
n.findex = sc.add(n.typ)
}
if op, ok := constBltn[bname]; ok && n.anc.action != aAssign {
op(n) // pre-compute non-assigned constant :
}
case c0.isType(sc):
// Type conversion expression
c1 := n.child[1]
switch len(n.child) {
case 1:
err = n.cfgErrorf("missing argument in conversion to %s", c0.typ.id())
case 2:
err = check.conversion(c1, c0.typ)
default:
err = n.cfgErrorf("too many arguments in conversion to %s", c0.typ.id())
}
if err != nil {
break
}
n.action = aConvert
switch {
case isInterface(c0.typ) && !c1.isNil():
// Convert to interface: just check that all required methods are defined by concrete type.
if !c1.typ.implements(c0.typ) {
err = n.cfgErrorf("type %v does not implement interface %v", c1.typ.id(), c0.typ.id())
}
// Convert type to interface while keeping a reference to the original concrete type.
// besides type, the node value remains preserved.
n.gen = nop
t := *c0.typ
n.typ = &t
n.typ.val = c1.typ
n.findex = c1.findex
n.level = c1.level
n.val = c1.val
n.rval = c1.rval
case c1.rval.IsValid() && isConstType(c0.typ):
n.gen = nop
n.findex = notInFrame
n.typ = c0.typ
if c, ok := c1.rval.Interface().(constant.Value); ok {
i, _ := constant.Int64Val(constant.ToInt(c))
n.rval = reflect.ValueOf(i).Convert(c0.typ.rtype)
} else {
n.rval = c1.rval.Convert(c0.typ.rtype)
}
default:
n.gen = convert
n.typ = c0.typ
n.findex = sc.add(n.typ)
}
case isBinCall(n, sc):
err = check.arguments(n, n.child[1:], c0, n.action == aCallSlice)
if err != nil {
break
}
n.gen = callBin
typ := c0.typ.rtype
if typ.NumOut() > 0 {
if funcType := c0.typ.val; funcType != nil {
// Use the original unwrapped function type, to allow future field and
// methods resolutions, otherwise impossible on the opaque bin type.
n.typ = funcType.ret[0]
n.findex = sc.add(n.typ)
for i := 1; i < len(funcType.ret); i++ {
sc.add(funcType.ret[i])
}
} else {
n.typ = valueTOf(typ.Out(0))
if n.anc.kind == returnStmt {
n.findex = childPos(n)
} else {
n.findex = sc.add(n.typ)
for i := 1; i < typ.NumOut(); i++ {
sc.add(valueTOf(typ.Out(i)))
}
}
}
}
case isOffsetof(c0):
if len(n.child) != 2 || n.child[1].kind != selectorExpr || !isStruct(n.child[1].child[0].typ) {
err = n.cfgErrorf("Offsetof argument: invalid expression")
break
}
c1 := n.child[1]
field, ok := c1.child[0].typ.rtype.FieldByName(c1.child[1].ident)
if !ok {
err = n.cfgErrorf("struct does not contain field: %s", c1.child[1].ident)
break
}
n.typ = valueTOf(reflect.TypeOf(field.Offset))
n.rval = reflect.ValueOf(field.Offset)
n.gen = nop
default:
// The call may be on a generic function. In that case, replace the
// generic function AST by an instantiated one before going further.
if isGeneric(c0.typ) {
fun := c0.typ.node.anc
var g *node
var types []*itype
var found bool
// Infer type parameter from function call arguments.
if types, err = inferTypesFromCall(sc, fun, n.child[1:]); err != nil {
break
}
// Generate an instantiated AST from the generic function one.
if g, found, err = genAST(sc, fun, types); err != nil {
break
}
if !found {
// Compile the generated function AST, so it becomes part of the scope.
if _, err = interp.cfg(g, fun.scope, importPath, pkgName); err != nil {
break
}
// AST compilation part 2: Generate closures for function body.
if err = genRun(g.child[3]); err != nil {
break
}
}
n.child[0] = g
c0 = n.child[0]
}
err = check.arguments(n, n.child[1:], c0, n.action == aCallSlice)
if err != nil {
break
}
if c0.action == aGetFunc {
// Allocate a frame entry to store the anonymous function definition.
sc.add(c0.typ)
}
if typ := c0.typ; len(typ.ret) > 0 {
n.typ = typ.ret[0]
if n.anc.kind == returnStmt && n.typ.id() == sc.def.typ.ret[0].id() {
// Store the result directly to the return value area of frame.
// It can be done only if no type conversion at return is involved.
n.findex = childPos(n)
} else {
n.findex = sc.add(n.typ)
for _, t := range typ.ret[1:] {
sc.add(t)
}
}
} else {
n.findex = notInFrame
}
}
case caseBody:
wireChild(n)
switch {
case typeSwichAssign(n) && len(n.child) > 1:
n.start = n.child[1].start
case len(n.child) == 0:
// Empty case body: jump to switch node (exit node).
n.start = n.anc.anc.anc
default:
n.start = n.child[0].start
}
case caseClause:
sc = sc.pop()
case commClauseDefault:
wireChild(n)
sc = sc.pop()
if len(n.child) == 0 {
return
}
n.start = n.child[0].start
n.lastChild().tnext = n.anc.anc // exit node is selectStmt
case commClause:
wireChild(n)
sc = sc.pop()
if len(n.child) == 0 {
return
}
if len(n.child) > 1 {
n.start = n.child[1].start // Skip chan operation, performed by select
}
n.lastChild().tnext = n.anc.anc // exit node is selectStmt
case compositeLitExpr:
wireChild(n)
child := n.child
if n.nleft > 0 {
child = child[1:]
}
switch n.typ.cat {
case arrayT, sliceT:
err = check.arrayLitExpr(child, n.typ)
case mapT:
err = check.mapLitExpr(child, n.typ.key, n.typ.val)
case structT:
err = check.structLitExpr(child, n.typ)
case valueT:
rtype := n.typ.rtype
switch rtype.Kind() {
case reflect.Struct:
err = check.structBinLitExpr(child, rtype)
case reflect.Map:
ktyp := valueTOf(rtype.Key())
vtyp := valueTOf(rtype.Elem())
err = check.mapLitExpr(child, ktyp, vtyp)
}
}
if err != nil {
break
}
n.findex = sc.add(n.typ)
// TODO: Check that composite literal expr matches corresponding type
n.gen = compositeGenerator(n, n.typ, nil)
case fallthroughtStmt:
if n.anc.kind != caseBody {
err = n.cfgErrorf("fallthrough statement out of place")
}
case fileStmt:
wireChild(n, varDecl)
sc = sc.pop()
n.findex = notInFrame
case forStmt0: // for {}
body := n.child[0]
n.start = body.start
body.tnext = n.start
sc = sc.pop()
case forStmt1: // for init; ; {}
init, body := n.child[0], n.child[1]
n.start = init.start
init.tnext = body.start
body.tnext = n.start
sc = sc.pop()
case forStmt2: // for cond {}
cond, body := n.child[0], n.child[1]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
n.start = body.start
body.tnext = body.start
}
} else {
n.start = cond.start
cond.tnext = body.start
body.tnext = cond.start
}
setFNext(cond, n)
sc = sc.pop()
case forStmt3: // for init; cond; {}
init, cond, body := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = init.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
init.tnext = body.start
body.tnext = body.start
} else {
init.tnext = n
}
} else {
init.tnext = cond.start
body.tnext = cond.start
}
cond.tnext = body.start
setFNext(cond, n)
sc = sc.pop()
case forStmt4: // for ; ; post {}
post, body := n.child[0], n.child[1]
n.start = body.start
post.tnext = body.start
body.tnext = post.start
sc = sc.pop()
case forStmt5: // for ; cond; post {}
cond, post, body := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
n.start = body.start
post.tnext = body.start
}
} else {
n.start = cond.start
post.tnext = cond.start
}
cond.tnext = body.start
setFNext(cond, n)
body.tnext = post.start
sc = sc.pop()
case forStmt6: // for init; ; post {}
init, post, body := n.child[0], n.child[1], n.child[2]
n.start = init.start
init.tnext = body.start
body.tnext = post.start
post.tnext = body.start
sc = sc.pop()
case forStmt7: // for init; cond; post {}
init, cond, post, body := n.child[0], n.child[1], n.child[2], n.child[3]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = init.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
init.tnext = body.start
post.tnext = body.start
} else {
init.tnext = n
}
} else {
init.tnext = cond.start
post.tnext = cond.start
}
cond.tnext = body.start
setFNext(cond, n)
body.tnext = post.start
sc = sc.pop()
case forRangeStmt:
n.start = n.child[0].start
setFNext(n.child[0], n)
sc = sc.pop()
case funcDecl:
n.start = n.child[3].start
n.types, n.scope = sc.types, sc
sc = sc.pop()
funcName := n.child[1].ident
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil && !isGeneric(sym.typ) {
sym.index = -1 // to force value to n.val
sym.typ = n.typ
sym.kind = funcSym
sym.node = n
}
case funcLit:
n.types, n.scope = sc.types, sc
sc = sc.pop()
err = genRun(n)
case deferStmt, goStmt:
wireChild(n)
case identExpr:
if isKey(n) || isNewDefine(n, sc) {
break
}
if n.anc.kind == funcDecl && n.anc.child[1] == n {
// Dont process a function name identExpr.
break
}
sym, level, found := sc.lookup(n.ident)
if !found {
if n.typ != nil {
// Node is a generic instance with an already populated type.
break
}
// retry with the filename, in case ident is a package name.
sym, level, found = sc.lookup(filepath.Join(n.ident, baseName))
if !found {
err = n.cfgErrorf("undefined: %s", n.ident)
break
}
}
// Found symbol, populate node info
n.sym, n.typ, n.findex, n.level = sym, sym.typ, sym.index, level
if n.findex < 0 {
n.val = sym.node
} else {
switch {
case sym.kind == constSym && sym.rval.IsValid():
n.rval = sym.rval
n.kind = basicLit
case n.ident == "iota":
n.rval = reflect.ValueOf(constant.MakeInt64(int64(sc.iota)))
n.kind = basicLit
case n.ident == nilIdent:
n.kind = basicLit
case sym.kind == binSym:
n.typ = sym.typ
n.rval = sym.rval
case sym.kind == bltnSym:
if n.anc.kind != callExpr {
err = n.cfgErrorf("use of builtin %s not in function call", n.ident)
}
}
}
if n.sym != nil {
n.recv = n.sym.recv
}
case ifStmt0: // if cond {}
cond, tbody := n.child[0], n.child[1]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
n.start = tbody.start
}
} else {
n.start = cond.start
cond.tnext = tbody.start
}
setFNext(cond, n)
tbody.tnext = n
sc = sc.pop()
case ifStmt1: // if cond {} else {}
cond, tbody, fbody := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test and the useless branch.
if cond.rval.Bool() {
n.start = tbody.start
} else {
n.start = fbody.start
}
} else {
n.start = cond.start
cond.tnext = tbody.start
setFNext(cond, fbody.start)
}
tbody.tnext = n
fbody.tnext = n
sc = sc.pop()
case ifStmt2: // if init; cond {}
init, cond, tbody := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = init.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
init.tnext = tbody.start
} else {
init.tnext = n
}
} else {
init.tnext = cond.start
cond.tnext = tbody.start
}
tbody.tnext = n
setFNext(cond, n)
sc = sc.pop()
case ifStmt3: // if init; cond {} else {}
init, cond, tbody, fbody := n.child[0], n.child[1], n.child[2], n.child[3]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = init.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
init.tnext = tbody.start
} else {
init.tnext = fbody.start
}
} else {
init.tnext = cond.start
cond.tnext = tbody.start
setFNext(cond, fbody.start)
}
tbody.tnext = n
fbody.tnext = n
sc = sc.pop()
case keyValueExpr:
if isBlank(n.child[1]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
wireChild(n)
case landExpr:
if isBlank(n.child[0]) || isBlank(n.child[1]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
n.start = n.child[0].start
n.child[0].tnext = n.child[1].start
setFNext(n.child[0], n)
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case lorExpr:
if isBlank(n.child[0]) || isBlank(n.child[1]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
n.start = n.child[0].start
n.child[0].tnext = n
setFNext(n.child[0], n.child[1].start)
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case parenExpr:
wireChild(n)
c := n.lastChild()
n.findex = c.findex
n.level = c.level
n.typ = c.typ
n.rval = c.rval
case rangeStmt:
if sc.rangeChanType(n) != nil {
n.start = n.child[1].start // Get chan
n.child[1].tnext = n // then go to range function
n.tnext = n.child[2].start // then go to range body
n.child[2].tnext = n // then body go to range function (loop)
n.child[0].gen = empty
} else {
var k, o, body *node
if len(n.child) == 4 {
k, o, body = n.child[0], n.child[2], n.child[3]
} else {
k, o, body = n.child[0], n.child[1], n.child[2]
}
n.start = o.start // Get array or map object
o.tnext = k.start // then go to iterator init
k.tnext = n // then go to range function
n.tnext = body.start // then go to range body
body.tnext = n // then body go to range function (loop)
k.gen = empty // init filled later by generator
}
case returnStmt:
if len(n.child) > sc.def.typ.numOut() {
err = n.cfgErrorf("too many arguments to return")
break
}
for _, c := range n.child {
if isBlank(c) {
err = n.cfgErrorf("cannot use _ as value")
return
}
}
returnSig := sc.def.child[2]
if mustReturnValue(returnSig) {
nret := len(n.child)
if nret == 1 && isCall(n.child[0]) {
nret = n.child[0].child[0].typ.numOut()
}
if nret < sc.def.typ.numOut() {
err = n.cfgErrorf("not enough arguments to return")
break
}
}
wireChild(n)
n.tnext = nil
n.val = sc.def
for i, c := range n.child {
var typ *itype
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[2].fieldType(i))
if err != nil {
return
}
// TODO(mpl): move any of that code to typecheck?
c.typ.node = c
if !c.typ.assignableTo(typ) {
err = c.cfgErrorf("cannot use %v (type %v) as type %v in return argument", c.ident, c.typ.cat, typ.cat)
return
}
if c.typ.cat == nilT {
// nil: Set node value to zero of return type
c.rval = reflect.New(typ.TypeOf()).Elem()
}
}
case selectorExpr:
wireChild(n)
n.typ = n.child[0].typ
n.recv = n.child[0].recv
if n.typ == nil {
err = n.cfgErrorf("undefined type")
break
}
switch {
case n.typ.cat == binPkgT:
// Resolve binary package symbol: a type or a value
name := n.child[1].ident
pkg := n.child[0].sym.typ.path
if s, ok := interp.binPkg[pkg][name]; ok {
if isBinType(s) {
n.typ = valueTOf(s.Type().Elem())
} else {
n.typ = valueTOf(fixPossibleConstType(s.Type()), withUntyped(isValueUntyped(s)))
n.rval = s
}
n.action = aGetSym
n.gen = nop
} else {
err = n.cfgErrorf("package %s \"%s\" has no symbol %s", n.child[0].ident, pkg, name)
}
case n.typ.cat == srcPkgT:
pkg, name := n.child[0].sym.typ.path, n.child[1].ident
// Resolve source package symbol
if sym, ok := interp.srcPkg[pkg][name]; ok {
n.findex = sym.index
if sym.global {
n.level = globalFrame
}
n.val = sym.node
n.gen = nop
n.action = aGetSym
n.typ = sym.typ
n.sym = sym
n.recv = sym.recv
n.rval = sym.rval
} else {
err = n.cfgErrorf("undefined selector: %s.%s", pkg, name)
}
case isStruct(n.typ) || isInterfaceSrc(n.typ):
// Find a matching field.
if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
if isStruct(n.typ) {
// If a method of the same name exists, use it if it is shallower than the struct field.
// if method's depth is the same as field's, this is an error.
d := n.typ.methodDepth(n.child[1].ident)
if d >= 0 && d < len(ti) {
goto tryMethods
}
if d == len(ti) {
err = n.cfgErrorf("ambiguous selector: %s", n.child[1].ident)
break
}
}
n.val = ti
switch {
case isInterfaceSrc(n.typ):
n.typ = n.typ.fieldSeq(ti)
n.gen = getMethodByName
n.action = aMethod
case n.typ.cat == ptrT:
n.typ = n.typ.fieldSeq(ti)
n.gen = getPtrIndexSeq
if n.typ.cat == funcT {
// Function in a struct field is always wrapped in reflect.Value.
n.typ = wrapperValueTOf(n.typ.TypeOf(), n.typ)
}
default:
n.gen = getIndexSeq
n.typ = n.typ.fieldSeq(ti)
if n.typ.cat == funcT {
// Function in a struct field is always wrapped in reflect.Value.
n.typ = wrapperValueTOf(n.typ.TypeOf(), n.typ)
}
}
break
}
if s, lind, ok := n.typ.lookupBinField(n.child[1].ident); ok {
// Handle an embedded binary field into a struct field.
n.gen = getIndexSeqField
lind = append(lind, s.Index...)
if isStruct(n.typ) {
// If a method of the same name exists, use it if it is shallower than the struct field.
// if method's depth is the same as field's, this is an error.
d := n.typ.methodDepth(n.child[1].ident)
if d >= 0 && d < len(lind) {
goto tryMethods
}
if d == len(lind) {
err = n.cfgErrorf("ambiguous selector: %s", n.child[1].ident)
break
}
}
n.val = lind
n.typ = valueTOf(s.Type)
break
}
// No field (embedded or not) matched. Try to match a method.
tryMethods:
fallthrough
default:
// Find a matching method.
// TODO (marc): simplify the following if/elseif blocks.
if n.typ.cat == valueT || n.typ.cat == errorT {
switch method, ok := n.typ.rtype.MethodByName(n.child[1].ident); {
case ok:
hasRecvType := n.typ.TypeOf().Kind() != reflect.Interface
n.val = method.Index
n.gen = getIndexBinMethod
n.action = aGetMethod
n.recv = &receiver{node: n.child[0]}
n.typ = valueTOf(method.Type, isBinMethod())
if hasRecvType {
n.typ.recv = n.typ
}
case n.typ.TypeOf().Kind() == reflect.Ptr:
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
n.typ = valueTOf(field.Type)
n.val = field.Index
n.gen = getPtrIndexSeq
break
}
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
case n.typ.TypeOf().Kind() == reflect.Struct:
if field, ok := n.typ.rtype.FieldByName(n.child[1].ident); ok {
n.typ = valueTOf(field.Type)
n.val = field.Index
n.gen = getIndexSeq
break
}
fallthrough
default:
// method lookup failed on type, now lookup on pointer to type
pt := reflect.PtrTo(n.typ.rtype)
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
n.val = m2.Index
n.gen = getIndexBinPtrMethod
n.typ = valueTOf(m2.Type, isBinMethod(), withRecv(valueTOf(pt)))
n.recv = &receiver{node: n.child[0]}
n.action = aGetMethod
break
}
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
}
} else if n.typ.cat == ptrT && (n.typ.val.cat == valueT || n.typ.val.cat == errorT) {
// Handle pointer on object defined in runtime
if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
n.val = method.Index
n.typ = valueTOf(method.Type, isBinMethod(), withRecv(n.typ))
n.recv = &receiver{node: n.child[0]}
n.gen = getIndexBinElemMethod
n.action = aGetMethod
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
n.val = method.Index
n.gen = getIndexBinMethod
n.typ = valueTOf(method.Type, withRecv(valueTOf(reflect.PtrTo(n.typ.val.rtype), isBinMethod())))
n.recv = &receiver{node: n.child[0]}
n.action = aGetMethod
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
n.typ = valueTOf(field.Type)
n.val = field.Index
n.gen = getPtrIndexSeq
} else {
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
}
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
n.action = aGetMethod
if n.child[0].isType(sc) {
// Handle method as a function with receiver in 1st argument.
n.val = m
n.findex = notInFrame
n.gen = nop
n.typ = &itype{}
*n.typ = *m.typ
n.typ.arg = append([]*itype{n.child[0].typ}, m.typ.arg...)
} else {
// Handle method with receiver.
n.gen = getMethod
n.val = m
n.typ = m.typ
n.recv = &receiver{node: n.child[0], index: lind}
}
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
n.action = aGetMethod
switch {
case isPtr && n.typ.fieldSeq(lind).cat != ptrT:
n.gen = getIndexSeqPtrMethod
case isInterfaceSrc(n.typ):
n.gen = getMethodByName
default:
n.gen = getIndexSeqMethod
}
n.recv = &receiver{node: n.child[0], index: lind}
n.val = append([]int{m.Index}, lind...)
n.typ = valueTOf(m.Type, isBinMethod(), withRecv(n.child[0].typ))
} else {
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
}
}
if err == nil && n.findex != -1 && n.typ.cat != genericT {
n.findex = sc.add(n.typ)
}
case selectStmt:
wireChild(n)
// Move action to block statement, so select node can be an exit point.
n.child[0].gen = _select
// Chain channel init actions in commClauses prior to invoking select.
var cur *node
for _, c := range n.child[0].child {
if c.kind == commClauseDefault {
// No channel init in this case.
continue
}
var an, pn *node // channel init action nodes
if len(c.child) > 0 {
switch c0 := c.child[0]; {
case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv:
an = c0.child[0].child[0]
pn = an
case c0.action == aAssign:
an = c0.lastChild().child[0]
pn = an
case c0.kind == sendStmt:
an = c0.child[0]
pn = c0.child[1]
}
}
if an == nil {
continue
}
if cur == nil {
// First channel init action, the entry point for the select block.
n.start = an.start
} else {
// Chain channel init action to the previous one.
cur.tnext = an.start
}
if pn != nil {
// Chain channect init action to send data init action.
// (already done by wireChild, but let's be explicit).
an.tnext = pn
cur = pn
}
}
if cur == nil {
// There is no channel init action, call select directly.
n.start = n.child[0]
} else {
// Select is called after the last channel init action.
cur.tnext = n.child[0]
}
case starExpr:
if isBlank(n.child[0]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
switch {
case n.anc.kind == defineStmt && len(n.anc.child) == 3 && n.anc.child[1] == n:
// pointer type expression in a var definition
n.gen = nop
case n.anc.kind == valueSpec && n.anc.lastChild() == n:
// pointer type expression in a value spec
n.gen = nop
case n.anc.kind == fieldExpr:
// pointer type expression in a field expression (arg or struct field)
n.gen = nop
case n.child[0].isType(sc):
// pointer type expression
n.gen = nop
n.typ = ptrOf(n.child[0].typ)
default:
// dereference expression
wireChild(n)
err = check.starExpr(n.child[0])
if err != nil {
break
}
if c0 := n.child[0]; c0.typ.cat == valueT {
n.typ = valueTOf(c0.typ.rtype.Elem())
} else {
n.typ = c0.typ.val
}
n.findex = sc.add(n.typ)
}
case typeSwitch:
// Check that cases expressions are all different
usedCase := map[string]bool{}
for _, c := range n.lastChild().child {
for _, t := range c.child[:len(c.child)-1] {
tid := t.typ.id()
if usedCase[tid] {
err = c.cfgErrorf("duplicate case %s in type switch", t.ident)
return
}
usedCase[tid] = true
}
}
fallthrough
case switchStmt:
sc = sc.pop()
sbn := n.lastChild() // switch block node
clauses := sbn.child
l := len(clauses)
if l == 0 {
// Switch is empty
break
}
// Chain case clauses.
for i := l - 1; i >= 0; i-- {
c := clauses[i]
if len(c.child) == 0 {
c.tnext = n // Clause body is empty, exit.
} else {
body := c.lastChild()
c.tnext = body.start
c.child[0].tnext = c
c.start = c.child[0].start
if i < l-1 && len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
if n.kind == typeSwitch {
err = body.lastChild().cfgErrorf("cannot fallthrough in type switch")
}
if len(clauses[i+1].child) == 0 {
body.tnext = n // Fallthrough to next with empty body, just exit.
} else {
body.tnext = clauses[i+1].lastChild().start
}
} else {
body.tnext = n // Exit switch at end of clause body.
}
}
if i == l-1 {
setFNext(clauses[i], n)
continue
}
if len(clauses[i+1].child) > 1 {
setFNext(c, clauses[i+1].start)
} else {
setFNext(c, clauses[i+1])
}
}
sbn.start = clauses[0].start
n.start = n.child[0].start
if n.kind == typeSwitch {
// Handle the typeSwitch init (the type assert expression).
init := n.child[1].lastChild().child[0]
init.tnext = sbn.start
n.child[0].tnext = init.start
} else {
n.child[0].tnext = sbn.start
}
case switchIfStmt: // like an if-else chain
sc = sc.pop()
sbn := n.lastChild() // switch block node
clauses := sbn.child
l := len(clauses)
if l == 0 {
// Switch is empty
break
}
// Wire case clauses in reverse order so the next start node is already resolved when used.
for i := l - 1; i >= 0; i-- {
c := clauses[i]
c.gen = nop
if len(c.child) == 0 {
c.tnext = n
c.fnext = n
} else {
body := c.lastChild()
if len(c.child) > 1 {
cond := c.child[0]
cond.tnext = body.start
if i == l-1 {
setFNext(cond, n)
} else {
setFNext(cond, clauses[i+1].start)
}
c.start = cond.start
} else {
c.start = body.start
}
// If last case body statement is a fallthrough, then jump to next case body
if i < l-1 && len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
body.tnext = clauses[i+1].lastChild().start
} else {
body.tnext = n
}
}
}
sbn.start = clauses[0].start
n.start = n.child[0].start
n.child[0].tnext = sbn.start
case typeAssertExpr:
if len(n.child) == 1 {
// The "o.(type)" is handled by typeSwitch.
n.gen = nop
break
}
wireChild(n)
c0, c1 := n.child[0], n.child[1]
if isBlank(c0) || isBlank(c1) {
err = n.cfgErrorf("cannot use _ as value")
break
}
if c1.typ == nil {
if c1.typ, err = nodeType(interp, sc, c1); err != nil {
return
}
}
err = check.typeAssertionExpr(c0, c1.typ)
if err != nil {
break
}
if n.anc.action != aAssignX {
if c0.typ.cat == valueT && isFunc(c1.typ) {
// Avoid special wrapping of interfaces and func types.
n.typ = valueTOf(c1.typ.TypeOf())
} else {
n.typ = c1.typ
}
n.findex = sc.add(n.typ)
}
case sliceExpr:
wireChild(n)
err = check.sliceExpr(n)
if err != nil {
break
}
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
n.findex = sc.add(n.typ)
case unaryExpr:
wireChild(n)
err = check.unaryExpr(n)
if err != nil {
break
}
n.typ = n.child[0].typ
if n.action == aRecv {
// Channel receive operation: set type to the channel data type
if n.typ.cat == valueT {
n.typ = valueTOf(n.typ.rtype.Elem())
} else {
n.typ = n.typ.val
}
}
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
// TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr)
if n.child[0].rval.IsValid() && !isInterface(n.typ) && constOp[n.action] != nil {
n.typ.TypeOf() // init reflect type
constOp[n.action](n)
}
switch {
case n.rval.IsValid():
n.gen = nop
n.findex = notInFrame
case n.anc.kind == assignStmt && n.anc.action == aAssign && n.anc.nright == 1:
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
n.level = dest.level
case n.anc.kind == returnStmt:
pos := childPos(n)
n.typ = sc.def.typ.ret[pos]
n.findex = pos
default:
n.findex = sc.add(n.typ)
}
case valueSpec:
n.gen = reset
l := len(n.child) - 1
if n.typ = n.child[l].typ; n.typ == nil {
if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil {
return
}
}
for _, c := range n.child[:l] {
var index int
if sc.global {
// Global object allocation is already performed in GTA.
index = sc.sym[c.ident].index
c.level = globalFrame
} else {
index = sc.add(n.typ)
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
}
c.typ = n.typ
c.findex = index
}
}
})
if sc != interp.universe {
sc.pop()
}
return initNodes, err
}
func compDefineX(sc *scope, n *node) error {
l := len(n.child) - 1
types := []*itype{}
switch src := n.child[l]; src.kind {
case callExpr:
funtype, err := nodeType(n.interp, sc, src.child[0])
if err != nil {
return err
}
for funtype.cat == valueT && funtype.val != nil {
// Retrieve original interpreter type from a wrapped function.
// Struct fields of function types are always wrapped in valueT to ensure
// their possible use in runtime. In that case, the val field retains the
// original interpreter type, which is used now.
funtype = funtype.val
}
if funtype.cat == valueT {
// Handle functions imported from runtime.
for i := 0; i < funtype.rtype.NumOut(); i++ {
types = append(types, valueTOf(funtype.rtype.Out(i)))
}
} else {
types = funtype.ret
}
if n.anc.kind == varDecl && n.child[l-1].isType(sc) {
l--
}
if len(types) != l {
return n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, src.child[0].name(), len(types))
}
if isBinCall(src, sc) {
n.gen = nop
} else {
// TODO (marc): skip if no conversion or wrapping is needed.
n.gen = assignFromCall
}
case indexExpr:
types = append(types, src.typ, sc.getType("bool"))
n.child[l].gen = getIndexMap2
n.gen = nop
case typeAssertExpr:
if n.child[0].ident == "_" {
n.child[l].gen = typeAssertStatus
} else {
n.child[l].gen = typeAssertLong
}
types = append(types, n.child[l].child[1].typ, sc.getType("bool"))
n.gen = nop
case unaryExpr:
if n.child[l].action == aRecv {
types = append(types, src.typ, sc.getType("bool"))
n.child[l].gen = recv2
n.gen = nop
}
default:
return n.cfgErrorf("unsupported assign expression")
}
// Handle redeclarations: find out new symbols vs existing ones.
symIsNew := map[string]bool{}
hasNewSymbol := false
for i := range types {
id := n.child[i].ident
if id == "_" || id == "" {
continue
}
if _, found := symIsNew[id]; found {
return n.cfgErrorf("%s repeated on left side of :=", id)
}
// A new symbol doesn't exist in current scope. Upper scopes are not
// taken into accout here, as a new symbol can shadow an existing one.
if _, found := sc.sym[id]; found {
symIsNew[id] = false
} else {
symIsNew[id] = true
hasNewSymbol = true
}
}
for i, t := range types {
var index int
id := n.child[i].ident
// A variable can be redeclared if at least one other not blank variable is created.
// The redeclared variable must be of same type (it is reassigned, not created).
// Careful to not reuse a variable which has been shadowed (it must not be a newSym).
sym, level, ok := sc.lookup(id)
canRedeclare := hasNewSymbol && len(symIsNew) > 1 && !symIsNew[id] && ok
if canRedeclare && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
index = sym.index
} else {
index = sc.add(t)
sc.sym[id] = &symbol{index: index, kind: varSym, typ: t}
}
n.child[i].typ = t
n.child[i].findex = index
}
return nil
}
// TODO used for allocation optimization, temporarily disabled
// func isAncBranch(n *node) bool {
// switch n.anc.kind {
// case If0, If1, If2, If3:
// return true
// }
// return false
// }
func childPos(n *node) int {
for i, c := range n.anc.child {
if n == c {
return i
}
}
return -1
}
func (n *node) cfgErrorf(format string, a ...interface{}) *cfgError {
pos := n.interp.fset.Position(n.pos)
posString := n.interp.fset.Position(n.pos).String()
if pos.Filename == DefaultSourceName {
posString = strings.TrimPrefix(posString, DefaultSourceName+":")
}
a = append([]interface{}{posString}, a...)
return &cfgError{n, fmt.Errorf("%s: "+format, a...)}
}
func genRun(nod *node) error {
var err error
seen := map[*node]bool{}
nod.Walk(func(n *node) bool {
if err != nil || seen[n] {
return false
}
seen[n] = true
switch n.kind {
case funcType:
if len(n.anc.child) == 4 {
// function body entry point
setExec(n.anc.child[3].start)
}
// continue in function body as there may be inner function definitions
case constDecl, varDecl:
setExec(n.start)
return false
}
return true
}, nil)
return err
}
func genGlobalVars(roots []*node, sc *scope) (*node, error) {
var vars []*node
for _, n := range roots {
vars = append(vars, getVars(n)...)
}
if len(vars) == 0 {
return nil, nil
}
varNode, err := genGlobalVarDecl(vars, sc)
if err != nil {
return nil, err
}
setExec(varNode.start)
return varNode, nil
}
func getVars(n *node) (vars []*node) {
for _, child := range n.child {
if child.kind == varDecl {
vars = append(vars, child.child...)
}
}
return vars
}
func genGlobalVarDecl(nodes []*node, sc *scope) (*node, error) {
varNode := &node{kind: varDecl, action: aNop, gen: nop}
deps := map[*node][]*node{}
for _, n := range nodes {
deps[n] = getVarDependencies(n, sc)
}
inited := map[*node]bool{}
revisit := []*node{}
for {
for _, n := range nodes {
canInit := true
for _, d := range deps[n] {
if !inited[d] {
canInit = false
}
}
if !canInit {
revisit = append(revisit, n)
continue
}
varNode.child = append(varNode.child, n)
inited[n] = true
}
if len(revisit) == 0 || equalNodes(nodes, revisit) {
break
}
nodes = revisit
revisit = []*node{}
}
if len(revisit) > 0 {
return nil, revisit[0].cfgErrorf("variable definition loop")
}
wireChild(varNode)
return varNode, nil
}
func getVarDependencies(nod *node, sc *scope) (deps []*node) {
nod.Walk(func(n *node) bool {
if n.kind != identExpr {
return true
}
// Process ident nodes, and avoid false dependencies.
if n.anc.kind == selectorExpr && childPos(n) == 1 {
return false
}
sym, _, ok := sc.lookup(n.ident)
if !ok {
return false
}
if sym.kind != varSym || !sym.global || sym.node == nod {
return false
}
deps = append(deps, sym.node)
return false
}, nil)
return deps
}
// setFnext sets the cond fnext field to next, propagates it for parenthesis blocks
// and sets the action to branch.
func setFNext(cond, next *node) {
if cond.action == aNop {
cond.action = aBranch
cond.gen = branch
cond.fnext = next
}
if cond.kind == parenExpr {
setFNext(cond.lastChild(), next)
return
}
cond.fnext = next
}
// GetDefault return the index of default case clause in a switch statement, or -1.
func getDefault(n *node) int {
for i, c := range n.lastChild().child {
switch len(c.child) {
case 0:
return i
case 1:
if c.child[0].kind == caseBody {
return i
}
}
}
return -1
}
func isBinType(v reflect.Value) bool { return v.IsValid() && v.Kind() == reflect.Ptr && v.IsNil() }
// isType returns true if node refers to a type definition, false otherwise.
func (n *node) isType(sc *scope) bool {
switch n.kind {
case arrayType, chanType, chanTypeRecv, chanTypeSend, funcType, interfaceType, mapType, structType:
return true
case parenExpr, starExpr:
if len(n.child) == 1 {
return n.child[0].isType(sc)
}
case selectorExpr:
pkg, name := n.child[0].ident, n.child[1].ident
baseName := filepath.Base(n.interp.fset.Position(n.pos).Filename)
suffixedPkg := filepath.Join(pkg, baseName)
sym, _, ok := sc.lookup(suffixedPkg)
if !ok {
sym, _, ok = sc.lookup(pkg)
if !ok {
return false
}
}
if sym.kind != pkgSym {
return false
}
path := sym.typ.path
if p, ok := n.interp.binPkg[path]; ok && isBinType(p[name]) {
return true // Imported binary type
}
if p, ok := n.interp.srcPkg[path]; ok && p[name] != nil && p[name].kind == typeSym {
return true // Imported source type
}
case identExpr:
return sc.getType(n.ident) != nil
case indexExpr:
// Maybe a generic type.
sym, _, ok := sc.lookup(n.child[0].ident)
return ok && sym.kind == typeSym
}
return false
}
// wireChild wires AST nodes for CFG in subtree.
func wireChild(n *node, exclude ...nkind) {
child := excludeNodeKind(n.child, exclude)
// Set start node, in subtree (propagated to ancestors by post-order processing)
for _, c := range child {
switch c.kind {
case arrayType, chanType, chanTypeRecv, chanTypeSend, funcDecl, importDecl, mapType, basicLit, identExpr, typeDecl:
continue
default:
n.start = c.start
}
break
}
// Chain sequential operations inside a block (next is right sibling)
for i := 1; i < len(child); i++ {
switch child[i].kind {
case funcDecl:
child[i-1].tnext = child[i]
default:
switch child[i-1].kind {
case breakStmt, continueStmt, gotoStmt, returnStmt:
// tnext is already computed, no change
default:
child[i-1].tnext = child[i].start
}
}
}
// Chain subtree next to self
for i := len(child) - 1; i >= 0; i-- {
switch child[i].kind {
case arrayType, chanType, chanTypeRecv, chanTypeSend, importDecl, mapType, funcDecl, basicLit, identExpr, typeDecl:
continue
case breakStmt, continueStmt, gotoStmt, returnStmt:
// tnext is already computed, no change
default:
child[i].tnext = n
}
break
}
}
func excludeNodeKind(child []*node, kinds []nkind) []*node {
if len(kinds) == 0 {
return child
}
var res []*node
for _, c := range child {
exclude := false
for _, k := range kinds {
if c.kind == k {
exclude = true
}
}
if !exclude {
res = append(res, c)
}
}
return res
}
func (n *node) name() (s string) {
switch {
case n.ident != "":
s = n.ident
case n.action == aGetSym:
s = n.child[0].ident + "." + n.child[1].ident
}
return s
}
// isNatural returns true if node type is natural, false otherwise.
func (n *node) isNatural() bool {
if isUint(n.typ.TypeOf()) {
return true
}
if n.rval.IsValid() {
t := n.rval.Type()
if isUint(t) {
return true
}
if isInt(t) && n.rval.Int() >= 0 {
// positive untyped integer constant is ok
return true
}
if isFloat(t) {
// positive untyped float constant with null decimal part is ok
f := n.rval.Float()
if f == math.Trunc(f) && f >= 0 {
n.rval = reflect.ValueOf(uint(f))
n.typ.rtype = n.rval.Type()
return true
}
}
if isConstantValue(t) {
c := n.rval.Interface().(constant.Value)
switch c.Kind() {
case constant.Int:
i, _ := constant.Int64Val(c)
if i >= 0 {
return true
}
case constant.Float:
f, _ := constant.Float64Val(c)
if f == math.Trunc(f) {
n.rval = reflect.ValueOf(constant.ToInt(c))
n.typ.rtype = n.rval.Type()
return true
}
}
}
}
return false
}
// isNil returns true if node is a literal nil value, false otherwise.
func (n *node) isNil() bool { return n.kind == basicLit && !n.rval.IsValid() }
// fieldType returns the nth parameter field node (type) of a fieldList node.
func (n *node) fieldType(m int) *node {
k := 0
l := len(n.child)
for i := 0; i < l; i++ {
cl := len(n.child[i].child)
if cl < 2 {
if k == m {
return n.child[i].lastChild()
}
k++
continue
}
for j := 0; j < cl-1; j++ {
if k == m {
return n.child[i].lastChild()
}
k++
}
}
return nil
}
// lastChild returns the last child of a node.
func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
func (n *node) hasAnc(nod *node) bool {
for a := n.anc; a != nil; a = a.anc {
if a == nod {
return true
}
}
return false
}
func isKey(n *node) bool {
return n.anc.kind == fileStmt ||
(n.anc.kind == selectorExpr && n.anc.child[0] != n) ||
(n.anc.kind == funcDecl && isMethod(n.anc)) ||
(n.anc.kind == keyValueExpr && isStruct(n.anc.typ) && n.anc.child[0] == n) ||
(n.anc.kind == fieldExpr && len(n.anc.child) > 1 && n.anc.child[0] == n)
}
func isField(n *node) bool {
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
}
func isInInterfaceType(n *node) bool {
anc := n.anc
for anc != nil {
if anc.kind == interfaceType {
return true
}
anc = anc.anc
}
return false
}
func isInConstOrTypeDecl(n *node) bool {
anc := n.anc
for anc != nil {
switch anc.kind {
case constDecl, typeDecl, arrayType, chanType:
return true
case varDecl, funcDecl:
return false
}
anc = anc.anc
}
return false
}
// isNewDefine returns true if node refers to a new definition.
func isNewDefine(n *node, sc *scope) bool {
if n.ident == "_" {
return true
}
if (n.anc.kind == defineXStmt || n.anc.kind == defineStmt || n.anc.kind == valueSpec) && childPos(n) < n.anc.nleft {
return true
}
if n.anc.kind == rangeStmt {
if n.anc.child[0] == n {
return true // array or map key, or chan element
}
if sc.rangeChanType(n.anc) == nil && n.anc.child[1] == n && len(n.anc.child) == 4 {
return true // array or map value
}
return false // array, map or channel are always pre-defined in range expression
}
return false
}
func isMethod(n *node) bool {
return len(n.child[0].child) > 0 // receiver defined
}
func isFuncField(n *node) bool {
return isField(n) && isFunc(n.typ)
}
func isMapEntry(n *node) bool {
return n.action == aGetIndex && isMap(n.child[0].typ)
}
func isCall(n *node) bool {
return n.action == aCall || n.action == aCallSlice
}
func isBinCall(n *node, sc *scope) bool {
if !isCall(n) || len(n.child) == 0 {
return false
}
c0 := n.child[0]
if c0.typ == nil {
// If called early in parsing, child type may not be known yet.
c0.typ, _ = nodeType(n.interp, sc, c0)
if c0.typ == nil {
return false
}
}
return c0.typ.cat == valueT && c0.typ.rtype.Kind() == reflect.Func
}
func isOffsetof(n *node) bool {
return n.typ != nil && n.typ.cat == valueT && n.rval.String() == "Offsetof"
}
func mustReturnValue(n *node) bool {
if len(n.child) < 3 {
return false
}
for _, f := range n.child[2].child {
if len(f.child) > 1 {
return false
}
}
return true
}
func isRegularCall(n *node) bool {
return isCall(n) && n.child[0].typ.cat == funcT
}
func variadicPos(n *node) int {
if len(n.child[0].typ.arg) == 0 {
return -1
}
last := len(n.child[0].typ.arg) - 1
if n.child[0].typ.arg[last].cat == variadicT {
return last
}
return -1
}
func canExport(name string) bool {
if r := []rune(name); len(r) > 0 && unicode.IsUpper(r[0]) {
return true
}
return false
}
func getExec(n *node) bltn {
if n == nil {
return nil
}
if n.exec == nil {
setExec(n)
}
return n.exec
}
// setExec recursively sets the node exec builtin function by walking the CFG
// from the entry point (first node to exec).
func setExec(n *node) {
if n.exec != nil {
return
}
seen := map[*node]bool{}
var set func(n *node)
set = func(n *node) {
if n == nil || n.exec != nil {
return
}
seen[n] = true
if n.tnext != nil && n.tnext.exec == nil {
if seen[n.tnext] {
m := n.tnext
n.tnext.exec = func(f *frame) bltn { return m.exec(f) }
} else {
set(n.tnext)
}
}
if n.fnext != nil && n.fnext.exec == nil {
if seen[n.fnext] {
m := n.fnext
n.fnext.exec = func(f *frame) bltn { return m.exec(f) }
} else {
set(n.fnext)
}
}
n.gen(n)
}
set(n)
}
func typeSwichAssign(n *node) bool {
ts := n.anc.anc.anc
return ts.kind == typeSwitch && ts.child[1].action == aAssign
}
func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
switch typ.cat {
case linkedT, ptrT:
gen = compositeGenerator(n, typ.val, rtyp)
case arrayT, sliceT:
gen = arrayLit
case mapT:
gen = mapLit
case structT:
switch {
case len(n.child) == 0:
gen = compositeLitNotype
case n.lastChild().kind == keyValueExpr:
if n.nleft == 1 {
gen = compositeLitKeyed
} else {
gen = compositeLitKeyedNotype
}
default:
if n.nleft == 1 {
gen = compositeLit
} else {
gen = compositeLitNotype
}
}
case valueT:
if rtyp == nil {
rtyp = n.typ.TypeOf()
}
switch k := rtyp.Kind(); k {
case reflect.Struct:
if n.nleft == 1 {
gen = compositeBinStruct
} else {
gen = compositeBinStructNotype
}
case reflect.Map:
// TODO(mpl): maybe needs a NoType version too
gen = compositeBinMap
case reflect.Ptr:
gen = compositeGenerator(n, typ, n.typ.val.rtype)
case reflect.Slice, reflect.Array:
gen = compositeBinSlice
default:
log.Panic(n.cfgErrorf("compositeGenerator not implemented for type kind: %s", k))
}
}
return gen
}
// arrayTypeLen returns the node's array length. If the expression is an
// array variable it is determined from the value's type, otherwise it is
// computed from the source definition.
func arrayTypeLen(n *node, sc *scope) (int, error) {
if n.typ != nil && n.typ.cat == arrayT {
return n.typ.length, nil
}
max := -1
for _, c := range n.child[1:] {
var r int
if c.kind != keyValueExpr {
r = max + 1
max = r
continue
}
c0 := c.child[0]
v := c0.rval
if v.IsValid() {
r = int(v.Int())
} else {
// Resolve array key value as a constant.
if c0.kind == identExpr {
// Key is defined by a symbol which must be a constant integer.
sym, _, ok := sc.lookup(c0.ident)
if !ok {
return 0, c0.cfgErrorf("undefined: %s", c0.ident)
}
if sym.kind != constSym {
return 0, c0.cfgErrorf("non-constant array bound %q", c0.ident)
}
r = int(vInt(sym.rval))
} else {
// Key is defined by a numeric constant expression.
if _, err := c0.interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
return 0, err
}
cv, ok := c0.rval.Interface().(constant.Value)
if !ok {
return 0, c0.cfgErrorf("non-constant expression")
}
r = constToInt(cv)
}
}
if r > max {
max = r
}
}
return max + 1, nil
}
// isValueUntyped returns true if value is untyped.
func isValueUntyped(v reflect.Value) bool {
// Consider only constant values.
if v.CanSet() {
return false
}
return v.Type().Implements(constVal)
}
// isArithmeticAction returns true if the node action is an arithmetic operator.
func isArithmeticAction(n *node) bool {
switch n.action {
case aAdd, aAnd, aAndNot, aBitNot, aMul, aNeg, aOr, aPos, aQuo, aRem, aShl, aShr, aSub, aXor:
return true
}
return false
}
func isBoolAction(n *node) bool {
switch n.action {
case aEqual, aGreater, aGreaterEqual, aLand, aLor, aLower, aLowerEqual, aNot, aNotEqual:
return true
}
return false
}
func isBlank(n *node) bool {
if n.kind == parenExpr && len(n.child) > 0 {
return isBlank(n.child[0])
}
return n.ident == "_"
}