Files
moxa/interp/cfg.go
Marc Vertes 021824930d interp: improve type assertions
In type assertion at compile time, compare signatures between function types only.

Make `itype.numOut()` return the correct value for Go builtins (this was not strictly necessary due to above fix, but it is correct and improves maintainability).

Fixes #1454.
2022-09-12 22:30:08 +02:00

3026 lines
77 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, aliasT:
typ := o.typ.rtype
if o.typ.cat == aliasT {
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 {
err = n.cfgErrorf("undefined type")
return false
}
}
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
}
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 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):
// 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.
pos := childPos(n)
n.typ = sc.def.typ.ret[pos]
n.findex = pos
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 == aliasT {
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, err := genAST(sc, t.node.anc, []*node{c1})
if err != nil {
return
}
if _, err = interp.cfg(g, nil, 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 declStmt, exprStmt, sendStmt:
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
g, err := genAST(sc, fun, c0.child[1:])
if err != nil {
return
}
_, err = interp.cfg(g, nil, 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 []*node
// 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, err = genAST(sc, fun, types); err != nil {
break
}
// Compile the generated function AST, so it becomes part of the scope.
if _, err = interp.cfg(g, nil, 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 {
// 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
if typ.cat == funcT {
// Wrap the typed nil value in a node, as per other interpreter functions
c.rval = reflect.ValueOf(&node{kind: basicLit, rval: reflect.New(typ.TypeOf()).Elem()})
} else {
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.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
nod.Walk(func(n *node) bool {
if err != nil {
return false
}
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 aliasT, 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 == "_"
}