Compare commits

...

6 Commits

Author SHA1 Message Date
Marc Vertes
d494f9e420 interp: support calling goto from sub-scope
As opposed to other symbols, goto labels must be searched in included
scopes, not upper ones. Implement scope.lookdown to perform this,
to allow calls to goto to be embedded in included scopes where label
is defined.

Fixes #953.
2020-11-19 12:48:03 +01:00
Marc Vertes
6da1107c39 fix: do not confuse function call with type conversion
Use node action to better discriminate between function call and type
conversion which have the same pattern at AST level.

Fixes #960.
2020-11-18 14:56:05 +01:00
Marc Vertes
38a7331bf9 interp: fix type check on function signature
Perform function declaration type check from the upper level scope (the scope where the
function is declared), to avoid possible collisions of local variables with package names.

Fixes #957.
2020-11-13 18:02:04 +01:00
Marc Vertes
13783889cb interp: avoid useless interface wrapping
in `callBin`, call arguments are converted to the corresponding
parameter type. In a case of an interface, the original concrete type
should be preserved instead, and only wrapped to an interface type for
internal interpreter types, as runtime values should already implement the
interface.

This change removes the interface wrapping when parameter is a runtime
value (valueT or ptrT to valueT).

This removes some overhead when handling runtime values, and keep a
similar behavior between interpreted and pre-compiled code. For
example, `io.Copy` preserves its internal optimisations when passed a
`bytes.Buffer`.
2020-11-12 10:48:04 +01:00
mpl
ed626f3fb9 interp: support conversion of runtime func into interp func
Conversion of interp func into runtime func already worked, but added a
test anyway to watch out for regressions.

Fixes #941
2020-11-10 00:40:04 +01:00
Marc Vertes
d0a34d467b interp: fix getting unsigned constant value
The function vUint, used to get the unsigned integer value of a value,
variable (frame) or constant, was broken for constant.Value expression.

Fixes #948.
2020-11-09 17:40:04 +01:00
10 changed files with 164 additions and 25 deletions

17
_test/convert1.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "strconv"
type atoidef func(s string) (int, error)
func main() {
stdatoi := atoidef(strconv.Atoi)
n, err := stdatoi("7")
if err != nil {
panic(err)
}
println(n)
}
// Output:
// 7

19
_test/convert2.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import "bufio"
func fakeSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
return 7, nil, nil
}
func main() {
splitfunc := bufio.SplitFunc(fakeSplitFunc)
n, _, err := splitfunc(nil, true)
if err != nil {
panic(err)
}
println(n)
}
// Output:
// 7

17
_test/fun26.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type F func() (int, error)
func f1() (int, error) { return 3, nil }
func f2(a string, f F) {
c, _ := f()
println(a, c)
}
func main() {
f2("hello", F(f1))
}
// Output:
// hello 3

12
_test/goto1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
if true {
goto here
}
here:
println("ok")
}
// Output:
// ok

20
_test/time14.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
"time"
)
var t time.Time
func f() time.Time {
time := t
return time
}
func main() {
fmt.Println(f())
}
// Output:
// 0001-01-01 00:00:00 +0000 UTC

View File

@@ -194,7 +194,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case breakStmt, continueStmt, gotoStmt:
if len(n.child) > 0 {
// Handle labeled statements
// Handle labeled statements.
label := n.child[0].ident
if sym, _, ok := sc.lookup(label); ok {
if sym.kind != labelSym {
@@ -211,25 +211,23 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case labeledStmt:
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
}
// TODO(marc): labels must be stored outside of symbols to avoid collisions
// Used labels are searched in current and sub scopes, not upper ones.
if sym, ok := sc.lookdown(label); ok {
sym.node = n
n.sym = sym
} else {
n.sym = &symbol{kind: labelSym, node: n, index: -1}
sc.sym[label] = n.sym
}
sc.sym[label] = n.sym
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
// 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
// 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{}")
@@ -239,7 +237,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
typ, err = nodeType(interp, sc, n.child[0])
}
} else {
// define the var with the type in the switch guard expression
// Define the var with the type in the switch guard expression.
typ = sn.child[1].child[1].child[0].typ
}
if err != nil {
@@ -1381,7 +1379,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.val = sc.def
for i, c := range n.child {
var typ *itype
typ, err = nodeType(interp, sc, returnSig.child[1].fieldType(i))
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[1].fieldType(i))
if err != nil {
return
}
@@ -2333,7 +2331,7 @@ func isCall(n *node) bool {
}
func isBinCall(n *node) bool {
return n.kind == callExpr && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
}
func mustReturnValue(n *node) bool {
@@ -2349,7 +2347,7 @@ func mustReturnValue(n *node) bool {
}
func isRegularCall(n *node) bool {
return n.kind == callExpr && n.child[0].typ.cat == funcT
return isCall(n) && n.child[0].typ.cat == funcT
}
func variadicPos(n *node) int {

View File

@@ -82,6 +82,16 @@ func TestEvalShift(t *testing.T) {
})
}
func TestOpVarConst(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
{pre: func() { eval(t, i, "const a uint = 8 + 2") }, src: "a", res: "10"},
{src: "b := uint(5); a+b", res: "15"},
{src: "b := uint(5); b+a", res: "15"},
{src: "b := uint(5); b>a", res: "false"},
})
}
func TestEvalStar(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{

View File

@@ -405,10 +405,15 @@ func convert(n *node) {
return
}
doConvert := true
var value func(*frame) reflect.Value
if c.typ.cat == funcT {
switch {
case c.typ.cat == funcT:
value = genFunctionWrapper(c)
} else {
case n.child[0].typ.cat == funcT && c.typ.cat == valueT:
doConvert = false
value = genValueNode(c)
default:
value = genValue(c)
}
@@ -429,7 +434,11 @@ func convert(n *node) {
}
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Convert(typ))
if doConvert {
dest(f).Set(value(f).Convert(typ))
} else {
dest(f).Set(value(f))
}
return next
}
}
@@ -1178,6 +1187,7 @@ func callBin(n *node) {
c.val = reflect.Zero(argType)
}
}
switch c.typ.cat {
case funcT:
values = append(values, genFunctionWrapper(c))
@@ -1190,6 +1200,14 @@ func callBin(n *node) {
default:
values = append(values, genInterfaceWrapper(c, defType))
}
case ptrT:
if c.typ.val.cat == valueT {
values = append(values, genValue(c))
} else {
values = append(values, genInterfaceWrapper(c, defType))
}
case valueT:
values = append(values, genValue(c))
default:
values = append(values, genInterfaceWrapper(c, defType))
}

View File

@@ -72,26 +72,28 @@ type symbol struct {
// execution to the index in frame, created exactly from the types layout.
//
type scope struct {
anc *scope // Ancestor upper scope
anc *scope // ancestor upper scope
child []*scope // included scopes
def *node // function definition node this scope belongs to, or nil
loop *node // loop exit node for break statement
loopRestart *node // loop restart node for continue statement
pkgID string // unique id of package in which scope is defined
types []reflect.Type // Frame layout, may be shared by same level scopes
level int // Frame level: number of frame indirections to access var during execution
sym map[string]*symbol // Map of symbols defined in this current scope
types []reflect.Type // frame layout, may be shared by same level scopes
level int // frame level: number of frame indirections to access var during execution
sym map[string]*symbol // map of symbols defined in this current scope
global bool // true if scope refers to global space (single frame for universe and package level scopes)
iota int // iota value in this scope
}
// push creates a new scope and chain it to the current one.
// push creates a new child scope and chain it to the current one.
func (s *scope) push(indirect bool) *scope {
sc := scope{anc: s, level: s.level, sym: map[string]*symbol{}}
sc := &scope{anc: s, level: s.level, sym: map[string]*symbol{}}
s.child = append(s.child, sc)
if indirect {
sc.types = []reflect.Type{}
sc.level = s.level + 1
} else {
// propagate size, types, def and global as scopes at same level share the same frame
// Propagate size, types, def and global as scopes at same level share the same frame.
sc.types = s.types
sc.def = s.def
sc.global = s.global
@@ -99,7 +101,7 @@ func (s *scope) push(indirect bool) *scope {
}
// inherit loop state and pkgID from ancestor
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
return &sc
return sc
}
func (s *scope) pushBloc() *scope { return s.push(false) }
@@ -107,12 +109,20 @@ func (s *scope) pushFunc() *scope { return s.push(true) }
func (s *scope) pop() *scope {
if s.level == s.anc.level {
// propagate size and types, as scopes at same level share the same frame
// Propagate size and types, as scopes at same level share the same frame.
s.anc.types = s.types
}
return s.anc
}
func (s *scope) upperLevel() *scope {
level := s.level
for s != nil && s.level == level {
s = s.anc
}
return s
}
// lookup searches for a symbol in the current scope, and upper ones if not found
// it returns the symbol, the number of indirections level from the current scope
// and status (false if no result).
@@ -130,6 +140,20 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
return nil, 0, false
}
// lookdown searches for a symbol in the current scope and included ones, recursively.
// It returns the first found symbol and true, or nil and false.
func (s *scope) lookdown(ident string) (*symbol, bool) {
if sym, ok := s.sym[ident]; ok {
return sym, true
}
for _, c := range s.child {
if sym, ok := c.lookdown(ident); ok {
return sym, true
}
}
return nil, false
}
func (s *scope) rangeChanType(n *node) *itype {
if sym, _, found := s.lookup(n.child[1].ident); found {
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {

View File

@@ -342,6 +342,10 @@ func vInt(v reflect.Value) (i int64) {
}
func vUint(v reflect.Value) (i uint64) {
if c := vConstantValue(v); c != nil {
i, _ = constant.Uint64Val(constant.ToInt(c))
return i
}
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i = uint64(v.Int())