Compare commits

..

15 Commits

Author SHA1 Message Date
Marc Vertes
14bc3b56b8 interp: add support of Go generics in interpreter
Status:
* [x] parsing code with generics
* [x] instantiate generics from concrete types
* [x] automatic type inference
* [x] support of generic recursive types 
* [x] support of generic methods
* [x] support of generic receivers in methods
* [x] support of multiple type parameters
* [x] support of generic constraints
* [x] tests (see _test/gen*.go)

Fixes #1363.
2022-08-03 15:18:08 +02:00
Marc Vertes
255b1cf1de interp: do not allow function declaration without body
Such function declaration denotes either a linkname (an access to
an arbitrary, typically unexported symbol, solved by go compiler),
or a foreign C or assembly implementation of the body.

Those cases are not supported (or planned to be) by the interpreter.

Fixes #1431.
2022-08-03 10:06:06 +02:00
Marc Vertes
d3fc5e990e chore: upgrade to go1.19
* chore: upgrade to go1.19

* review

Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2022-08-03 09:44:07 +02:00
Marc Vertes
dc082b5ded stdlib: support of go1.18 and go1.19, remove go1.16 and go1.17
In addition:
- extract commmand now skips exported generics in runtime wrappers
- interp_consistent_test.go fixed for go1.18 and go1.19
- move minimal version of go compiler to go1.18

Note that this version is incompatible with go1.17 and before due
to the major changes in the stdlib go parser.

To be merged once go1.19 is officially released (not before).
2022-07-20 17:10:08 +02:00
Marc Vertes
d9c402e20d interp: fix unit testing for go1.18
Some tests are not passing when using go1.18, due to a change of
content in error messages compared to go1.17. We simply skip them
while we support go1.17. It concerns a small number of tests
regarding error detection.
2022-07-20 11:04:09 +02:00
ttoad
09a1617640 interp: improve support of alias types
I expect the following code to be supported.
```go
type TT http.Header

func (t TT) Set(key, val string) {

}

func (t TT) Get(key string) string {

}
```
So, I pushed this PR. 
Do I need to add some test cases?  I don't see the relevant test files ....
2022-07-14 19:38:07 +02:00
Luo Peng
cb642c44ba interp: improve type checking when comparing aliased types
Fixes #1421.
2022-06-30 10:22:12 +02:00
Marc Vertes
f76db27c77 interp: fix resolution of methods on aliased types
The type.val field was always pointing to the final underlying type
for aliased types, defeating a possible match if a method was
attached to a type in between. Now the complete chain of aliases
is always preserved.

We have added an underlying() itype method which returns the underlying
type of a defined type (aliasT), even in the presence of multiple
indirections.

We have added a definedType function which checks if type t1 is
defined from type t2 or t2 defined from t1, required when checking
assignability of aliasT types.

Fixes #1411.

PS: this is the 2nd attempt, as the first version #1412 wasn't passing
_test/issue-1408.go as well. This PR does pass and supersedes #1412.
2022-06-14 16:42:09 +02:00
mpl
996b1e33c8 interp: catch mismatched types for other comparisons
The check for mismatched types was already added recently for ==  and != comparisons.
This PR now adds it for other comparisons ( < , <=, > , >=).
2022-06-14 10:52:08 +02:00
Marc Vertes
236a0effaf interp: improve the behaviour of interface{} function parameters
We finally address a long standing limitation of the interpreter:
the capacity to generate the correct interface wrapper for an
anonymous interface{} function parameter of a binary function.

It allows for example fmt.Printf to invoke the String method
of an object defined within the interpreter, or json.Marshal
to invoke a textMarshaler method if it exists and if there is
no Marshaler method already defined for the passed interpreter
object.

To achieve that, we add a new mapType part of the "Used" symbols
to describe what not empty interfaces are expected and in which
priority order. This information can not be guessed and is found
in the related package documentation, then captured in stdlib/maptypes.go.

Then, at compile time and/or during execution, a lookup on mapTypes
is performed to allow the correct wrapper to be generated.

This change adds a new MapType type to the stdlib package.

Fixes #435.
2022-06-14 10:18:08 +02:00
Marc Vertes
eaeb445e17 interp: create interpreter interface value with new
In that case, the interface must be wrapped in an valueInterface
at creation.

With that fix, it is now possible to import
github.com/google/go-querystring/query. Not tested beyond that.

Fixes #1123.
2022-06-13 11:36:09 +02:00
Marc Vertes
6933ba2b4e interp: improve type checking for defined types
Fixes #1408.
2022-06-13 11:24:09 +02:00
Marc Vertes
a61a7d5bcd interp: avoid panic when defining a label in incremental parsing mode
In REPL mode, a panic (stack overflow) could be triggered by:

	$ yaegi
	> a:
	runtime: goroutine stack exceeds 1000000000-byte limit
	runtime: sp=0x14020760330 stack=[0x14020760000, 0x14040760000]
	fatal error: stack overflow
	[...]

This issue occurs in incremental parsing mode only, and not when the parser
is in file mode. We avoid it by being more defensive when generating
values.

Fixes #982.
2022-06-13 11:10:09 +02:00
Marc Vertes
259f64cfd4 interp: fix redeclaration of an interface variable
Fixes #1404.
2022-06-13 10:56:09 +02:00
Marc Vertes
6c74ab7bec interp: allow conversions of untyped complex
For untyped numerical types, conversions to different numerical
types can be allowed if there is no overflow (not checked here).

Fixes #1402.
2022-06-13 10:42:08 +02:00
580 changed files with 12205 additions and 6605 deletions

View File

@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
go-version: [ 1.16, 1.17 ]
go-version: [ 1.18, 1.19 ]
os: [ubuntu-latest, macos-latest, windows-latest]
include:
@@ -34,6 +34,7 @@ jobs:
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
stable: true
# https://github.com/marketplace/actions/checkout
- name: Checkout code

View File

@@ -7,8 +7,8 @@ on:
pull_request:
env:
GO_VERSION: 1.17
GOLANGCI_LINT_VERSION: v1.42.1
GO_VERSION: 1.18
GOLANGCI_LINT_VERSION: v1.47.1
jobs:
@@ -45,12 +45,13 @@ jobs:
needs: linting
strategy:
matrix:
go-version: [ 1.16, 1.17 ]
go-version: [ 1.18, 1.19 ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
stable: true
- name: Check out code
uses: actions/checkout@v2
@@ -75,13 +76,14 @@ jobs:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/yaegi
strategy:
matrix:
go-version: [ 1.16, 1.17 ]
go-version: [ 1.18, 1.19 ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
stable: true
- name: Check out code
uses: actions/checkout@v2

View File

@@ -6,7 +6,7 @@ on:
- v[0-9]+.[0-9]+*
env:
GO_VERSION: 1.17
GO_VERSION: 1.19
jobs:

View File

@@ -27,6 +27,7 @@
"scopelint", # deprecated
"interfacer", # deprecated
"maligned", # deprecated
"exhaustivestruct", # deprecated
"lll",
"gas",
"dupl",
@@ -49,10 +50,15 @@
"wrapcheck",
"nestif",
"exhaustive",
"exhaustivestruct",
"exhaustruct",
"forbidigo",
"ifshort",
"forcetypeassert",
"varnamelen",
"nosnakecase",
"nonamedreturns",
"nilnil",
"maintidx",
"errorlint", # TODO: must be reactivate before fixes
]
@@ -81,3 +87,6 @@
[[issues.exclude-rules]]
path = "interp/interp_eval_test.go"
linters = ["thelper"]
[[issues.exclude-rules]]
path = "interp/debugger.go"
linters = ["containedctx"]

View File

@@ -18,7 +18,7 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
* Works everywhere Go works
* All Go & runtime resources accessible from script (with control)
* Security: `unsafe` and `syscall` packages neither used nor exported by default
* Support Go 1.16 and Go 1.17 (the latest 2 major releases)
* Support Go 1.18 and Go 1.19 (the latest 2 major releases)
## Install

View File

@@ -1,5 +1,10 @@
package alias3
import (
"fmt"
"net/http"
)
type T struct {
A string
}
@@ -7,3 +12,20 @@ type T struct {
func (t *T) Print() {
println(t.A)
}
type A http.Header
func (a A) ForeachKey() error {
for k, vals := range a {
for _, v := range vals {
fmt.Println(k, v)
}
}
return nil
}
func (a A) Set(k string, v []string) {
a[k] = v
}

View File

@@ -17,11 +17,23 @@ func (b B) Test2() {
fmt.Println("test2")
}
func (b B) Test3() {
for k, vals := range b {
for _, v := range vals {
fmt.Println(k, v)
}
}
}
func main() {
b := B{}
b.Test2()
b["test"] = []string{"a", "b"}
b.Test3()
}
// Output:
// test2
// test a
// test b

39
_test/gen1.go Normal file
View File

@@ -0,0 +1,39 @@
package main
import "fmt"
// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
func main() {
// Initialize a map for the integer values
ints := map[string]int64{
"first": 34,
"second": 12,
}
// Initialize a map for the float values
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Non-Generic Sums: %v and %v\n",
SumInts(ints),
SumFloats(floats))
}

34
_test/gen2.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import "fmt"
// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
func main() {
// Initialize a map for the integer values
ints := map[string]int64{
"first": 34,
"second": 12,
}
// Initialize a map for the float values
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Generic Sums: %v and %v\n",
SumIntsOrFloats[string, int64](ints),
SumIntsOrFloats[string, float64](floats))
}
// Output:
// Generic Sums: 46 and 62.97

22
_test/gen3.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type Number interface {
int | int64 | ~float64
}
func Sum[T Number](numbers []T) T {
var total T
for _, x := range numbers {
total += x
}
return total
}
func main() {
xs := []int{3, 5, 10}
total := Sum(xs)
println(total)
}
// Output:
// 18

42
_test/gen4.go Normal file
View File

@@ -0,0 +1,42 @@
package main
import "fmt"
type List[T any] struct {
head, tail *element[T]
}
// A recursive generic type.
type element[T any] struct {
next *element[T]
val T
}
func (lst *List[T]) Push(v T) {
if lst.tail == nil {
lst.head = &element[T]{val: v}
lst.tail = lst.head
} else {
lst.tail.next = &element[T]{val: v}
lst.tail = lst.tail.next
}
}
func (lst *List[T]) GetAll() []T {
var elems []T
for e := lst.head; e != nil; e = e.next {
elems = append(elems, e.val)
}
return elems
}
func main() {
lst := List[int]{}
lst.Push(10)
lst.Push(13)
lst.Push(23)
fmt.Println("list:", lst.GetAll())
}
// Output:
// list: [10 13 23]

24
_test/gen5.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import "fmt"
type Set[Elem comparable] struct {
m map[Elem]struct{}
}
func Make[Elem comparable]() Set[Elem] {
return Set[Elem]{m: make(map[Elem]struct{})}
}
func (s Set[Elem]) Add(v Elem) {
s.m[v] = struct{}{}
}
func main() {
s := Make[int]()
s.Add(1)
fmt.Println(s)
}
// Output:
// {map[1:{}]}

19
_test/gen6.go Normal file
View File

@@ -0,0 +1,19 @@
package main
func MapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
func main() {
var m = map[int]string{1: "2", 2: "4", 4: "8"}
// Test type inference
println(len(MapKeys(m)))
}
// Output:
// 3

19
_test/gen7.go Normal file
View File

@@ -0,0 +1,19 @@
package main
func MapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
func main() {
var m = map[int]string{1: "2", 2: "4", 4: "8"}
// Test type inference
println(len(MapKeys))
}
// Error:
// invalid argument for len

15
_test/gen8.go Normal file
View File

@@ -0,0 +1,15 @@
package main
type Float interface {
~float32 | ~float64
}
func add[T Float](a, b T) float64 { return float64(a) + float64(b) }
func main() {
var x, y int = 1, 2
println(add(x, y))
}
// Error:
// int does not implement main.Float

14
_test/gen9.go Normal file
View File

@@ -0,0 +1,14 @@
package main
type Float interface {
~float32 | ~float64
}
func add[T Float](a, b T) float64 { return float64(a) + float64(b) }
func main() {
println(add(1, 2))
}
// Error:
// untyped int does not implement main.Float

22
_test/issue-1404.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type I interface {
inI()
}
type T struct {
name string
}
func (t *T) inI() {}
func main() {
var i I = &T{name: "foo"}
if i, ok := i.(*T); ok {
println(i.name)
}
}
// Output:
// foo

16
_test/issue-1408.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type (
Number = int32
Number2 = Number
)
func f(n Number2) { println(n) }
func main() {
var n Number = 5
f(n)
}
// Output:
// 5

16
_test/issue-1411.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type Number int32
func (n Number) IsValid() bool { return true }
type Number1 = Number
type Number2 = Number1
func main() {
a := Number2(5)
println(a.IsValid())
}
// Output: true

9
_test/issue-1421.go Normal file
View File

@@ -0,0 +1,9 @@
package main
type Number = int
func main() {
println(Number(1) < int(2))
}
// Output: true

25
_test/issue-435.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import (
"fmt"
"strconv"
)
type Foo int
func (f Foo) String() string {
return "foo-" + strconv.Itoa(int(f))
}
func print1(arg interface{}) {
fmt.Println(arg)
}
func main() {
var arg Foo = 3
var f = print1
f(arg)
}
// Output:
// foo-3

View File

@@ -2,9 +2,7 @@ package fs1
import (
"testing"
// only available from 1.16.
"testing/fstest"
"testing/fstest" // only available from 1.16.
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"

View File

@@ -194,6 +194,11 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
case *types.Var:
val[name] = Val{pname, true}
case *types.TypeName:
// Skip type if it is generic.
if t, ok := o.Type().(*types.Named); ok && t.TypeParams().Len() > 0 {
continue
}
typ[name] = pname
if t, ok := o.Type().Underlying().(*types.Interface); ok {
var methods []Method
@@ -463,7 +468,7 @@ func GetMinor(part string) string {
return minor
}
const defaultMinorVersion = 17
const defaultMinorVersion = 19
func genBuildTags() (string, error) {
version := runtime.Version()

2
go.mod
View File

@@ -1,3 +1,3 @@
module github.com/traefik/yaegi
go 1.16
go 1.18

View File

@@ -72,6 +72,7 @@ const (
importSpec
incDecStmt
indexExpr
indexListExpr
interfaceType
keyValueExpr
labeledStmt
@@ -155,6 +156,7 @@ var kinds = [...]string{
importSpec: "importSpec",
incDecStmt: "incDecStmt",
indexExpr: "indexExpr",
indexListExpr: "indexListExpr",
interfaceType: "interfaceType",
keyValueExpr: "keyValueExpr",
labeledStmt: "labeledStmt",
@@ -694,7 +696,7 @@ func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
n := addChild(&root, anc, pos, funcDecl, aNop)
n.val = n
if a.Recv == nil {
// function is not a method, create an empty receiver list
// Function is not a method, create an empty receiver list.
addChild(&root, astNode{n, nod}, pos, fieldList, aNop)
}
st.push(n, nod)
@@ -706,7 +708,13 @@ func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
st.push(n, nod)
case *ast.FuncType:
st.push(addChild(&root, anc, pos, funcType, aNop), nod)
n := addChild(&root, anc, pos, funcType, aNop)
n.val = n
if a.TypeParams == nil {
// Function has no type parameters, create an empty fied list.
addChild(&root, astNode{n, nod}, pos, fieldList, aNop)
}
st.push(n, nod)
case *ast.GenDecl:
var kind nkind
@@ -776,6 +784,9 @@ func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
case *ast.IndexExpr:
st.push(addChild(&root, anc, pos, indexExpr, aGetIndex), nod)
case *ast.IndexListExpr:
st.push(addChild(&root, anc, pos, indexListExpr, aNop), nod)
case *ast.InterfaceType:
st.push(addChild(&root, anc, pos, interfaceType, aNop), nod)

View File

@@ -145,8 +145,11 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
switch o.typ.cat {
case valueT:
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
@@ -300,7 +303,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
// Indicate that the first child is the type.
n.nleft = 1
} else {
// Get type from ancestor (implicit type)
// 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 {
@@ -357,16 +360,55 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
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])
// Add a frame indirection level as we enter in a func
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
if len(n.child[2].child) == 2 {
// Allocate frame space for return values, define output symbols
for _, c := range n.child[2].child[1].child {
// 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
@@ -380,8 +422,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
}
}
// Define receiver symbol.
if len(n.child[0].child) > 0 {
// define receiver symbol
var typ *itype
fr := n.child[0].child[0]
recvTypeNode := fr.lastChild()
@@ -396,8 +439,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
sc.sym[fr.child[0].ident] = &symbol{index: index, kind: varSym, typ: typ}
}
}
for _, c := range n.child[2].child[0].child {
// define input parameter symbols
// 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
@@ -406,6 +450,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
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)
}
@@ -790,13 +835,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
wireChild(n)
t := n.child[0].typ
for t.cat == aliasT {
t = t.val
}
switch t.cat {
case aliasT:
if isString(t.val.TypeOf()) {
n.typ = sc.getType("byte")
break
}
fallthrough
case ptrT:
n.typ = t.val
if t.val.cat == valueT {
@@ -812,6 +854,49 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
} 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
}
@@ -924,9 +1009,42 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
}
wireChild(n)
switch {
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):
c0 := n.child[0]
bname := c0.ident
err = check.builtin(bname, n, n.child[1:], n.action == aCallSlice)
if err != nil {
@@ -977,9 +1095,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
if op, ok := constBltn[bname]; ok && n.anc.action != aAssign {
op(n) // pre-compute non-assigned constant :
}
case n.child[0].isType(sc):
case c0.isType(sc):
// Type conversion expression
c0, c1 := n.child[0], n.child[1]
c1 := n.child[1]
switch len(n.child) {
case 1:
err = n.cfgErrorf("missing argument in conversion to %s", c0.typ.id())
@@ -1024,16 +1143,17 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.typ = c0.typ
n.findex = sc.add(n.typ)
}
case isBinCall(n, sc):
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
err = check.arguments(n, n.child[1:], c0, n.action == aCallSlice)
if err != nil {
break
}
n.gen = callBin
typ := n.child[0].typ.rtype
typ := c0.typ.rtype
if typ.NumOut() > 0 {
if funcType := n.child[0].typ.val; funcType != nil {
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]
@@ -1053,7 +1173,8 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
}
}
case isOffsetof(n):
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
@@ -1067,17 +1188,45 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.typ = valueTOf(reflect.TypeOf(field.Offset))
n.rval = reflect.ValueOf(field.Offset)
n.gen = nop
default:
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
// 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 n.child[0].action == aGetFunc {
if c0.action == aGetFunc {
// Allocate a frame entry to store the anonymous function definition.
sc.add(n.child[0].typ)
sc.add(c0.typ)
}
if typ := n.child[0].typ; len(typ.ret) > 0 {
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.
@@ -1295,7 +1444,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.types, n.scope = sc.types, sc
sc = sc.pop()
funcName := n.child[1].ident
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil {
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
@@ -1324,7 +1473,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
// 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 %d", n.ident, n.index)
err = n.cfgErrorf("undefined: %s", n.ident)
break
}
}
@@ -1531,7 +1680,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.val = sc.def
for i, c := range n.child {
var typ *itype
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[1].fieldType(i))
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[2].fieldType(i))
if err != nil {
return
}
@@ -1662,7 +1811,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
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.rtype.Kind() != reflect.Interface
hasRecvType := n.typ.TypeOf().Kind() != reflect.Interface
n.val = method.Index
n.gen = getIndexBinMethod
n.action = aGetMethod
@@ -1671,7 +1820,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
if hasRecvType {
n.typ.recv = n.typ
}
case n.typ.rtype.Kind() == reflect.Ptr:
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
@@ -1679,7 +1828,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
break
}
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
case n.typ.rtype.Kind() == reflect.Struct:
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
@@ -1724,7 +1873,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
} 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
// Handle method as a function with receiver in 1st argument.
n.val = m
n.findex = notInFrame
n.gen = nop
@@ -1732,7 +1881,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
*n.typ = *m.typ
n.typ.arg = append([]*itype{n.child[0].typ}, m.typ.arg...)
} else {
// Handle method with receiver
// Handle method with receiver.
n.gen = getMethod
n.val = m
n.typ = m.typ
@@ -2135,7 +2284,7 @@ func compDefineX(sc *scope, n *node) error {
for i, t := range types {
var index int
id := n.child[i].ident
if sym, level, ok := sc.lookup(id); ok && level == n.child[i].level && sym.kind == varSym && sym.typ.equals(t) {
if sym, level, ok := sc.lookup(id); ok && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
// Reuse symbol in case of a variable redeclaration with the same type.
index = sym.index
} else {
@@ -2348,6 +2497,10 @@ func (n *node) isType(sc *scope) bool {
}
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
}
@@ -2519,6 +2672,17 @@ 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 {
@@ -2585,14 +2749,14 @@ func isBinCall(n *node, sc *scope) bool {
}
func isOffsetof(n *node) bool {
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].rval.String() == "Offsetof"
return n.typ != nil && n.typ.cat == valueT && n.rval.String() == "Offsetof"
}
func mustReturnValue(n *node) bool {
if len(n.child) < 2 {
if len(n.child) < 3 {
return false
}
for _, f := range n.child[1].child {
for _, f := range n.child[2].child {
if len(f.child) > 1 {
return false
}
@@ -2700,11 +2864,7 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
}
case valueT:
if rtyp == nil {
rtyp = n.typ.rtype
}
// TODO(mpl): I do not understand where this side-effect is coming from, and why it happens. quickfix for now.
if rtyp == nil {
rtyp = n.typ.val.rtype
rtyp = n.typ.TypeOf()
}
switch k := rtyp.Kind(); k {
case reflect.Struct:

View File

@@ -52,7 +52,7 @@ func TestCompileAST(t *testing.T) {
node ast.Node
skip string
}{
{desc: "file", node: file},
{desc: "file", node: file, skip: "temporary ignore"},
{desc: "import", node: file.Imports[0]},
{desc: "type", node: dType},
{desc: "var", node: dVar, skip: "not supported"},

281
interp/generic.go Normal file
View File

@@ -0,0 +1,281 @@
package interp
import (
"strings"
"sync/atomic"
)
// genAST returns a new AST where generic types are replaced by instantiated types.
func genAST(sc *scope, root *node, types []*node) (*node, error) {
typeParam := map[string]*node{}
pindex := 0
tname := ""
rtname := ""
recvrPtr := false
fixNodes := []*node{}
var gtree func(*node, *node) (*node, error)
gtree = func(n, anc *node) (*node, error) {
nod := copyNode(n, anc)
switch n.kind {
case funcDecl, funcType:
nod.val = nod
case identExpr:
// Replace generic type by instantiated one.
nt, ok := typeParam[n.ident]
if !ok {
break
}
nod = copyNode(nt, anc)
case indexExpr:
// Catch a possible recursive generic type definition
if root.kind != typeSpec {
break
}
if root.child[0].ident != n.child[0].ident {
break
}
nod := copyNode(n.child[0], anc)
fixNodes = append(fixNodes, nod)
return nod, nil
case fieldList:
// Node is the type parameters list of a generic function.
if root.kind == funcDecl && n.anc == root.child[2] && childPos(n) == 0 {
// Fill the types lookup table used for type substitution.
for _, c := range n.child {
l := len(c.child) - 1
for _, cc := range c.child[:l] {
if pindex >= len(types) {
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
}
if err := checkConstraint(sc, types[pindex], c.child[l]); err != nil {
return nil, err
}
typeParam[cc.ident] = types[pindex]
pindex++
}
}
// Skip type parameters specification, so generated func doesn't look generic.
return nod, nil
}
// Node is the receiver of a generic method.
if root.kind == funcDecl && n.anc == root && childPos(n) == 0 && len(n.child) > 0 {
rtn := n.child[0].child[1]
if rtn.kind == indexExpr || (rtn.kind == starExpr && rtn.child[0].kind == indexExpr) {
// Method receiver is a generic type.
if rtn.kind == starExpr && rtn.child[0].kind == indexExpr {
// Method receiver is a pointer on a generic type.
rtn = rtn.child[0]
recvrPtr = true
}
rtname = rtn.child[0].ident + "["
for _, cc := range rtn.child[1:] {
if pindex >= len(types) {
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
}
it, err := nodeType(n.interp, sc, types[pindex])
if err != nil {
return nil, err
}
typeParam[cc.ident] = types[pindex]
rtname += it.id() + ","
pindex++
}
rtname = strings.TrimSuffix(rtname, ",") + "]"
}
}
// Node is the type parameters list of a generic type.
if root.kind == typeSpec && n.anc == root && childPos(n) == 1 {
// Fill the types lookup table used for type substitution.
tname = n.anc.child[0].ident + "["
for _, c := range n.child {
l := len(c.child) - 1
for _, cc := range c.child[:l] {
if pindex >= len(types) {
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
}
it, err := nodeType(n.interp, sc, types[pindex])
if err != nil {
return nil, err
}
if err := checkConstraint(sc, types[pindex], c.child[l]); err != nil {
return nil, err
}
typeParam[cc.ident] = types[pindex]
tname += it.id() + ","
pindex++
}
}
tname = strings.TrimSuffix(tname, ",") + "]"
return nod, nil
}
}
for _, c := range n.child {
gn, err := gtree(c, nod)
if err != nil {
return nil, err
}
nod.child = append(nod.child, gn)
}
return nod, nil
}
r, err := gtree(root, root.anc)
if err != nil {
return nil, err
}
if tname != "" {
for _, nod := range fixNodes {
nod.ident = tname
}
r.child[0].ident = tname
}
if rtname != "" {
// Replace method receiver type by synthetized ident.
nod := r.child[0].child[0].child[1]
if recvrPtr {
nod = nod.child[0]
}
nod.kind = identExpr
nod.ident = rtname
nod.child = nil
}
// r.astDot(dotWriter(root.interp.dotCmd), root.child[1].ident) // Used for debugging only.
return r, nil
}
func copyNode(n, anc *node) *node {
var i interface{}
nindex := atomic.AddInt64(&n.interp.nindex, 1)
nod := &node{
debug: n.debug,
anc: anc,
interp: n.interp,
index: nindex,
level: n.level,
nleft: n.nleft,
nright: n.nright,
kind: n.kind,
pos: n.pos,
action: n.action,
gen: n.gen,
val: &i,
rval: n.rval,
ident: n.ident,
meta: n.meta,
}
nod.start = nod
return nod
}
func inferTypesFromCall(sc *scope, fun *node, args []*node) ([]*node, error) {
ftn := fun.typ.node
// Fill the map of parameter types, indexed by type param ident.
types := map[string]*itype{}
for _, c := range ftn.child[0].child {
typ, err := nodeType(fun.interp, sc, c.lastChild())
if err != nil {
return nil, err
}
for _, cc := range c.child[:len(c.child)-1] {
types[cc.ident] = typ
}
}
var inferTypes func(*itype, *itype) ([]*node, error)
inferTypes = func(param, input *itype) ([]*node, error) {
switch param.cat {
case chanT, ptrT, sliceT:
return inferTypes(param.val, input.val)
case mapT:
k, err := inferTypes(param.key, input.key)
if err != nil {
return nil, err
}
v, err := inferTypes(param.val, input.val)
if err != nil {
return nil, err
}
return append(k, v...), nil
case structT:
nods := []*node{}
for i, f := range param.field {
nl, err := inferTypes(f.typ, input.field[i].typ)
if err != nil {
return nil, err
}
nods = append(nods, nl...)
}
return nods, nil
case funcT:
nods := []*node{}
for i, t := range param.arg {
nl, err := inferTypes(t, input.arg[i])
if err != nil {
return nil, err
}
nods = append(nods, nl...)
}
for i, t := range param.ret {
nl, err := inferTypes(t, input.ret[i])
if err != nil {
return nil, err
}
nods = append(nods, nl...)
}
return nods, nil
case genericT:
return []*node{input.node}, nil
}
return nil, nil
}
nodes := []*node{}
for i, c := range ftn.child[1].child {
typ, err := nodeType(fun.interp, sc, c.lastChild())
if err != nil {
return nil, err
}
nods, err := inferTypes(typ, args[i].typ)
if err != nil {
return nil, err
}
nodes = append(nodes, nods...)
}
return nodes, nil
}
func checkConstraint(sc *scope, input, constraint *node) error {
ct, err := nodeType(constraint.interp, sc, constraint)
if err != nil {
return err
}
it, err := nodeType(input.interp, sc, input)
if err != nil {
return err
}
if len(ct.constraint) == 0 && len(ct.ulconstraint) == 0 {
return nil
}
for _, c := range ct.constraint {
if it.equals(c) {
return nil
}
}
for _, c := range ct.ulconstraint {
if it.underlying().equals(c) {
return nil
}
}
return input.cfgErrorf("%s does not implement %s", input.typ.id(), ct.id())
}

View File

@@ -144,6 +144,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
return false
}
genericMethod := false
ident := n.child[1].ident
switch {
case isMethod(n):
@@ -153,8 +154,20 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
rcvr := n.child[0].child[0]
rtn := rcvr.lastChild()
typName, typPtr := rtn.ident, false
// Identifies the receiver type name. It could be an ident, a
// generic type (indexExpr), or a pointer on either lasts.
if typName == "" {
typName, typPtr = rtn.child[0].ident, true
typName = rtn.child[0].ident
switch rtn.kind {
case starExpr:
typPtr = true
if rtn.child[0].kind == indexExpr {
typName = rtn.child[0].child[0].ident
genericMethod = true
}
case indexExpr:
genericMethod = true
}
}
sym, _, found := sc.lookup(typName)
if !found {
@@ -174,7 +187,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
elementType.addMethod(n)
}
rcvrtype.addMethod(n)
n.child[0].child[0].lastChild().typ = rcvrtype
rtn.typ = rcvrtype
case ident == "init":
// init functions do not get declared as per the Go spec.
default:
@@ -185,9 +198,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
return false
}
// Add a function symbol in the package name space except for init
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
sc.sym[ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
}
if !n.typ.isComplete() {
if !n.typ.isComplete() && !genericMethod {
revisit = append(revisit, n)
}
return false
@@ -282,6 +295,15 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
return false
}
typeName := n.child[0].ident
if len(n.child) > 2 {
// Handle a generic type: skip definition as parameter is not instantiated yet.
n.typ = genericOf(nil, typeName, withNode(n.child[0]), withScope(sc))
if _, exists := sc.sym[typeName]; !exists {
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
}
sc.sym[typeName].typ = n.typ
return false
}
var typ *itype
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
err = nil

View File

@@ -204,11 +204,12 @@ type Interpreter struct {
name string // name of the input source file (or main)
opt // user settable options
cancelChan bool // enables cancellable chan operations
fset *token.FileSet // fileset to locate node in source code
binPkg Exports // binary packages used in interpreter, indexed by path
rdir map[string]bool // for src import cycle detection
opt // user settable options
cancelChan bool // enables cancellable chan operations
fset *token.FileSet // fileset to locate node in source code
binPkg Exports // binary packages used in interpreter, indexed by path
rdir map[string]bool // for src import cycle detection
mapTypes map[reflect.Value][]reflect.Type // special interfaces mapping for wrappers
mutex sync.RWMutex
frame *frame // program data storage during execution
@@ -333,6 +334,7 @@ func New(options Options) *Interpreter {
universe: initUniverse(),
scopes: map[string]*scope{},
binPkg: Exports{"": map[string]reflect.Value{"_error": reflect.ValueOf((*_error)(nil))}},
mapTypes: map[reflect.Value][]reflect.Type{},
srcPkg: imports{},
pkgNames: map[string]string{},
rdir: map[string]bool{},
@@ -423,8 +425,10 @@ const (
func initUniverse() *scope {
sc := &scope{global: true, sym: map[string]*symbol{
// predefined Go types
"any": {kind: typeSym, typ: &itype{cat: interfaceT, str: "any"}},
"bool": {kind: typeSym, typ: &itype{cat: boolT, name: "bool", str: "bool"}},
"byte": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
"comparable": {kind: typeSym, typ: &itype{cat: comparableT, name: "comparable", str: "comparable"}},
"complex64": {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64", str: "complex64"}},
"complex128": {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128", str: "complex128"}},
"error": {kind: typeSym, typ: &itype{cat: errorT, name: "error", str: "error"}},
@@ -446,9 +450,9 @@ func initUniverse() *scope {
"uintptr": {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr", str: "uintptr"}},
// predefined Go constants
"false": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(false)},
"true": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(true)},
"iota": {kind: constSym, typ: untypedInt()},
"false": {kind: constSym, typ: untypedBool(nil), rval: reflect.ValueOf(false)},
"true": {kind: constSym, typ: untypedBool(nil), rval: reflect.ValueOf(true)},
"iota": {kind: constSym, typ: untypedInt(nil)},
// predefined Go zero value
"nil": {typ: &itype{cat: nilT, untyped: true, str: "nil"}},
@@ -675,6 +679,14 @@ func (interp *Interpreter) Use(values Exports) error {
importPath := path.Dir(k)
packageName := path.Base(k)
if k == "." && v["MapTypes"].IsValid() {
// Use mapping for special interface wrappers.
for kk, vv := range v["MapTypes"].Interface().(map[reflect.Value][]reflect.Type) {
interp.mapTypes[kk] = vv
}
continue
}
if importPath == "." {
return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k)
}
@@ -726,6 +738,14 @@ func fixStdlib(interp *Interpreter) {
p["Scanf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fscanf(stdin, f, a...) })
p["Scanln"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscanln(stdin, a...) })
// Update mapTypes to virtualized symbols as well.
interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(fmt.Print)]
interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(fmt.Printf)]
interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(fmt.Println)]
interp.mapTypes[p["Scan"]] = interp.mapTypes[reflect.ValueOf(fmt.Scan)]
interp.mapTypes[p["Scanf"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanf)]
interp.mapTypes[p["Scanln"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanln)]
if p = interp.binPkg["flag"]; p != nil {
c := flag.NewFlagSet(os.Args[0], flag.PanicOnError)
c.SetOutput(stderr)
@@ -752,6 +772,14 @@ func fixStdlib(interp *Interpreter) {
p["SetOutput"] = reflect.ValueOf(l.SetOutput)
p["SetPrefix"] = reflect.ValueOf(l.SetPrefix)
p["Writer"] = reflect.ValueOf(l.Writer)
// Update mapTypes to virtualized symbols as well.
interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(log.Print)]
interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(log.Printf)]
interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(log.Println)]
interp.mapTypes[p["Panic"]] = interp.mapTypes[reflect.ValueOf(log.Panic)]
interp.mapTypes[p["Panicf"]] = interp.mapTypes[reflect.ValueOf(log.Panicf)]
interp.mapTypes[p["Panicln"]] = interp.mapTypes[reflect.ValueOf(log.Panicln)]
}
if p = interp.binPkg["os"]; p != nil {

View File

@@ -48,6 +48,9 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "fun23.go" || // expect error
file.Name() == "fun24.go" || // expect error
file.Name() == "fun25.go" || // expect error
file.Name() == "gen7.go" || // expect error
file.Name() == "gen8.go" || // expect error
file.Name() == "gen9.go" || // expect error
file.Name() == "if2.go" || // expect error
file.Name() == "import6.go" || // expect error
file.Name() == "init1.go" || // expect error
@@ -188,12 +191,12 @@ func TestInterpErrorConsistency(t *testing.T) {
{
fileName: "assign11.go",
expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values",
expectedExec: "6:10: assignment mismatch: 3 variables but fmt.Println returns 2 values",
expectedExec: "6:12: assignment mismatch: 3 variables but fmt.Println returns 2 values",
},
{
fileName: "assign12.go",
expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values",
expectedExec: "6:10: assignment mismatch: 3 variables but fmt.Println returns 2 values",
expectedExec: "6:13: assignment mismatch: 3 variables but fmt.Println returns 2 values",
},
{
fileName: "bad0.go",
@@ -213,46 +216,47 @@ func TestInterpErrorConsistency(t *testing.T) {
{
fileName: "const9.go",
expectedInterp: "5:2: constant definition loop",
expectedExec: "5:2: constant definition loop",
expectedExec: "5:2: initialization loop for b",
},
{
fileName: "if2.go",
expectedInterp: "7:5: non-bool used as if condition",
expectedExec: "7:2: non-bool i % 1000000 (type int) used as if condition",
expectedExec: "7:5: non-boolean condition in if statement",
},
{
fileName: "for7.go",
expectedInterp: "4:14: non-bool used as for condition",
expectedExec: "4:2: non-bool i (type int) used as for condition",
expectedExec: "4:14: non-boolean condition in for statement",
},
{
fileName: "fun21.go",
expectedInterp: "4:2: not enough arguments to return",
expectedExec: "4:2: not enough arguments to return",
expectedExec: "4:2: not enough return values",
},
{
fileName: "fun22.go",
expectedInterp: "6:2: not enough arguments in call to time.Date",
expectedExec: "6:11: not enough arguments in call to time.Date",
expectedExec: "6:2: not enough arguments in call to time.Date",
},
{
fileName: "fun23.go",
expectedInterp: "3:17: too many arguments to return",
expectedExec: "3:17: too many arguments to return",
expectedExec: "3:24: too many return values",
},
{
fileName: "issue-1093.go",
expectedInterp: "9:6: cannot use type untyped string as type int in assignment",
expectedExec: `9:4: cannot use "a" + b() (type string) as type int in assignment`,
expectedExec: `9:6: cannot use "a" + b() (value of type string) as type int in assignment`,
},
{
fileName: "op1.go",
expectedInterp: "5:2: invalid operation: mismatched types int and untyped float",
expectedExec: "5:4: constant 1.3 truncated to integer",
expectedExec: "5:7: 1.3 (untyped float constant) truncated to int",
},
{
fileName: "bltn0.go",
expectedInterp: "4:7: use of builtin println not in function call",
expectedExec: "4:7: println (built-in) must be called",
},
{
fileName: "import6.go",
@@ -267,17 +271,17 @@ func TestInterpErrorConsistency(t *testing.T) {
{
fileName: "switch9.go",
expectedInterp: "9:3: cannot fallthrough in type switch",
expectedExec: "9:3: cannot fallthrough in type switch",
expectedExec: "fallthrough",
},
{
fileName: "switch13.go",
expectedInterp: "9:2: i is not a type",
expectedExec: "9:2: i (type interface {}) is not a type",
expectedExec: "9:7: i (variable of type interface{}) is not a type",
},
{
fileName: "switch19.go",
expectedInterp: "37:2: duplicate case Bir in type switch",
expectedExec: "37:2: duplicate case Bir in type switch",
expectedExec: "37:7: duplicate case Bir in type switch",
},
}

View File

@@ -176,7 +176,7 @@ func TestEvalBuiltin(t *testing.T) {
{src: `m := complex(3, 2); real(m)`, res: "3"},
{src: `m := complex(3, 2); imag(m)`, res: "2"},
{src: `m := complex("test", 2)`, err: "1:33: invalid types string and int"},
{src: `imag("test")`, err: "1:33: cannot convert \"test\" to complex128"},
{src: `imag("test")`, err: "1:33: cannot convert untyped string to untyped complex"},
{src: `imag(a)`, err: "1:33: invalid argument type []int for imag"},
{src: `real(a)`, err: "1:33: invalid argument type []int for real"},
{src: `t := map[int]int{}; t[123]++; t`, res: "map[123:1]"},
@@ -184,6 +184,9 @@ func TestEvalBuiltin(t *testing.T) {
{src: `t := map[int]int{}; t[123] += 1; t`, res: "map[123:1]"},
{src: `t := map[int]int{}; t[123] -= 1; t`, res: "map[123:-1]"},
{src: `println("hello", _)`, err: "1:28: cannot use _ as value"},
{src: `f := func() complex64 { return complex(0, 0) }()`, res: "(0+0i)"},
{src: `f := func() float32 { return real(complex(2, 1)) }()`, res: "2"},
{src: `f := func() int8 { return imag(complex(2, 1)) }()`, res: "1"},
})
}
@@ -437,7 +440,7 @@ func TestEvalComparison(t *testing.T) {
{src: `a, b, c := 1, 1, false; if a == b { c = true }; c`, res: "true"},
{src: `a, b, c := 1, 2, false; if a != b { c = true }; c`, res: "true"},
{
desc: "mismatched types",
desc: "mismatched types equality",
src: `
type Foo string
type Bar string
@@ -448,6 +451,18 @@ func TestEvalComparison(t *testing.T) {
`,
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
},
{
desc: "mismatched types less than",
src: `
type Foo string
type Bar string
var a = Foo("test")
var b = Bar("test")
var c = a < b
`,
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
},
{src: `1 > _`, err: "1:28: cannot use _ as value"},
{src: `(_) > 1`, err: "1:28: cannot use _ as value"},
{src: `v := interface{}(2); v == 2`, res: "true"},
@@ -686,6 +701,7 @@ func TestEvalCall(t *testing.T) {
{src: ` test := func(a, b int) int { return a }
blah := func() (int, float64) { return 1, 1.1 }
a := test(blah())`, err: "3:15: cannot use func() (int,float64) as type (int,int)"},
{src: "func f()", err: "function declaration without body is unsupported"},
})
}
@@ -706,6 +722,29 @@ func TestEvalBinCall(t *testing.T) {
})
}
func TestEvalReflect(t *testing.T) {
i := interp.New(interp.Options{})
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if _, err := i.Eval(`
import (
"net/url"
"reflect"
)
type Encoder interface {
EncodeValues(key string, v *url.Values) error
}
`); err != nil {
t.Fatal(err)
}
runTests(t, i, []testCase{
{src: "reflect.TypeOf(new(Encoder)).Elem()", res: "interp.valueInterface"},
})
}
func TestEvalMissingSymbol(t *testing.T) {
defer func() {
r := recover()
@@ -972,7 +1011,7 @@ const goMinorVersionTest = 16
func TestHasIOFS(t *testing.T) {
code := `
// +build go1.16
// +build go1.18
package main
@@ -1010,6 +1049,8 @@ func main() {
var minor int
var err error
version := runtime.Version()
version = strings.Replace(version, "beta", ".", 1)
version = strings.Replace(version, "rc", ".", 1)
fields := strings.Fields(version)
// Go stable
if len(fields) == 1 {
@@ -1372,7 +1413,7 @@ func testConcurrentComposite(t *testing.T, filePath string) {
}
}
func TestEvalScanner(t *testing.T) {
func TestEvalREPL(t *testing.T) {
if testing.Short() {
return
}
@@ -1460,6 +1501,13 @@ func TestEvalScanner(t *testing.T) {
},
errorLine: -1,
},
{
desc: "define a label",
src: []string{
`a:`,
},
errorLine: -1,
},
}
runREPL := func(t *testing.T, test testCase) {
@@ -1552,10 +1600,8 @@ func TestREPLCommands(t *testing.T) {
if testing.Short() {
return
}
_ = os.Setenv("YAEGI_PROMPT", "1") // To force prompts over non-tty streams
defer func() {
_ = os.Setenv("YAEGI_PROMPT", "0")
}()
t.Setenv("YAEGI_PROMPT", "1") // To force prompts over non-tty streams
allDone := make(chan bool)
runREPL := func() {
done := make(chan error)

View File

@@ -19,10 +19,7 @@ func TestFile(t *testing.T) {
filePath := "../_test/str.go"
runCheck(t, filePath)
defer func() {
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "0")
}()
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "1")
t.Setenv("YAEGI_SPECIAL_STDIO", "1")
baseDir := filepath.Join("..", "_test")
files, err := os.ReadDir(baseDir)

View File

@@ -1479,6 +1479,20 @@ func callBin(n *node) {
}
}
// getMapType returns a reflect type suitable for interface wrapper for functions
// with some special processing in case of interface{} argument, i.e. fmt.Printf.
var getMapType func(*itype) reflect.Type
if lr, ok := n.interp.mapTypes[c0.rval]; ok {
getMapType = func(typ *itype) reflect.Type {
for _, rt := range lr {
if typ.implements(&itype{cat: valueT, rtype: rt}) {
return rt
}
}
return nil
}
}
// Determine if we should use `Call` or `CallSlice` on the function Value.
callFn := func(v reflect.Value, in []reflect.Value) []reflect.Value { return v.Call(in) }
if n.action == aCallSlice {
@@ -1486,13 +1500,6 @@ func callBin(n *node) {
}
for i, c := range child {
var defType reflect.Type
if variadic >= 0 && i+rcvrOffset >= variadic {
defType = funcType.In(variadic)
} else {
defType = funcType.In(rcvrOffset + i)
}
switch {
case isBinCall(c, c.scope):
// Handle nested function calls: pass returned values as arguments
@@ -1527,6 +1534,19 @@ func callBin(n *node) {
break
}
// defType is the target type for a potential interface wrapper.
var defType reflect.Type
if variadic >= 0 && i+rcvrOffset >= variadic {
defType = funcType.In(variadic)
} else {
defType = funcType.In(rcvrOffset + i)
}
if getMapType != nil {
if rt := getMapType(c.typ); rt != nil {
defType = rt
}
}
switch {
case isFuncSrc(c.typ):
values = append(values, genFunctionWrapper(c))
@@ -1565,7 +1585,7 @@ func callBin(n *node) {
val := make([]reflect.Value, l+1)
val[0] = value(f)
for i, v := range values {
val[i+1] = v(f)
val[i+1] = getBinValue(getMapType, v, f)
}
f.deferred = append([][]reflect.Value{val}, f.deferred...)
return tnext
@@ -1575,7 +1595,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
go callFn(value(f), in)
return tnext
@@ -1587,7 +1607,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
res := callFn(value(f), in)
b := res[0].Bool()
@@ -1619,7 +1639,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
out := callFn(value(f), in)
for i, v := range rvalues {
@@ -1636,7 +1656,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
out := callFn(value(f), in)
for i, v := range out {
@@ -1652,7 +1672,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
out := callFn(value(f), in)
for i := 0; i < len(out); i++ {
@@ -3303,11 +3323,20 @@ func _len(n *node) {
func _new(n *node) {
next := getExec(n.tnext)
typ := n.child[1].typ.TypeOf()
t1 := n.child[1].typ
typ := t1.TypeOf()
dest := genValueOutput(n, reflect.PtrTo(typ))
if isInterfaceSrc(t1) && (!isEmptyInterface(t1) || len(t1.method) > 0) {
typ = zeroInterfaceValue().Type()
}
n.exec = func(f *frame) bltn {
dest(f).Set(reflect.New(typ))
v := reflect.New(typ)
if vi, ok := v.Interface().(*valueInterface); ok {
vi.node = n
}
dest(f).Set(v)
return next
}
}

View File

@@ -11,27 +11,29 @@ type sKind uint
// Symbol kinds for the Go interpreter.
const (
undefSym sKind = iota
binSym // Binary from runtime
bltnSym // Builtin
constSym // Constant
funcSym // Function
labelSym // Label
pkgSym // Package
typeSym // Type
varSym // Variable
undefSym sKind = iota
binSym // Binary from runtime
bltnSym // Builtin
constSym // Constant
funcSym // Function
labelSym // Label
pkgSym // Package
typeSym // Type
varTypeSym // Variable type (generic)
varSym // Variable
)
var symKinds = [...]string{
undefSym: "undefSym",
binSym: "binSym",
bltnSym: "bltnSym",
constSym: "constSym",
funcSym: "funcSym",
labelSym: "labelSym",
pkgSym: "pkgSym",
typeSym: "typeSym",
varSym: "varSym",
undefSym: "undefSym",
binSym: "binSym",
bltnSym: "bltnSym",
constSym: "constSym",
funcSym: "funcSym",
labelSym: "labelSym",
pkgSym: "pkgSym",
typeSym: "typeSym",
varTypeSym: "varTypeSym",
varSym: "varSym",
}
func (k sKind) String() string {

View File

@@ -26,12 +26,15 @@ const (
chanT
chanSendT
chanRecvT
comparableT
complex64T
complex128T
constraintT
errorT
float32T
float64T
funcT
genericT
interfaceT
intT
int8T
@@ -64,8 +67,10 @@ var cats = [...]string{
boolT: "boolT",
builtinT: "builtinT",
chanT: "chanT",
comparableT: "comparableT",
complex64T: "complex64T",
complex128T: "complex128T",
constraintT: "constraintT",
errorT: "errorT",
float32T: "float32",
float64T: "float64T",
@@ -77,6 +82,7 @@ var cats = [...]string{
int32T: "int32T",
int64T: "int64T",
mapT: "mapT",
genericT: "genericT",
ptrT: "ptrT",
sliceT: "sliceT",
srcPkgT: "srcPkgT",
@@ -109,49 +115,53 @@ type structField struct {
// itype defines the internal representation of types in the interpreter.
type itype struct {
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT, sliceT or variadicT
recv *itype // Receiver type for funcT or nil
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
ptr *itype // Pointer to this type. Might be nil
method []*node // Associated methods or nil
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
length int // length of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
str string // String representation of the type
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
isBinMethod bool // true if the type refers to a bin method function
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT, sliceT, variadicT or genericT
recv *itype // Receiver type for funcT or nil
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
ptr *itype // Pointer to this type. Might be nil
method []*node // Associated methods or nil
constraint []*itype // For interfaceT: list of types part of interface set
ulconstraint []*itype // For interfaceT: list of underlying types part of interface set
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
length int // length of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
str string // String representation of the type
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
isBinMethod bool // true if the type refers to a bin method function
}
func untypedBool() *itype {
return &itype{cat: boolT, name: "bool", untyped: true, str: "untyped bool"}
type generic struct{}
func untypedBool(n *node) *itype {
return &itype{cat: boolT, name: "bool", untyped: true, str: "untyped bool", node: n}
}
func untypedString() *itype {
return &itype{cat: stringT, name: "string", untyped: true, str: "untyped string"}
func untypedString(n *node) *itype {
return &itype{cat: stringT, name: "string", untyped: true, str: "untyped string", node: n}
}
func untypedRune() *itype {
return &itype{cat: int32T, name: "int32", untyped: true, str: "untyped rune"}
func untypedRune(n *node) *itype {
return &itype{cat: int32T, name: "int32", untyped: true, str: "untyped rune", node: n}
}
func untypedInt() *itype {
return &itype{cat: intT, name: "int", untyped: true, str: "untyped int"}
func untypedInt(n *node) *itype {
return &itype{cat: intT, name: "int", untyped: true, str: "untyped int", node: n}
}
func untypedFloat() *itype {
return &itype{cat: float64T, name: "float64", untyped: true, str: "untyped float"}
func untypedFloat(n *node) *itype {
return &itype{cat: float64T, name: "float64", untyped: true, str: "untyped float", node: n}
}
func untypedComplex() *itype {
return &itype{cat: complex128T, name: "complex128", untyped: true, str: "untyped complex"}
func untypedComplex(n *node) *itype {
return &itype{cat: complex128T, name: "complex128", untyped: true, str: "untyped complex", node: n}
}
func errorMethodType(sc *scope) *itype {
@@ -238,9 +248,6 @@ func namedOf(val *itype, path, name string, opts ...itypeOption) *itype {
if path != "" {
str = path + "." + name
}
for val.cat == aliasT {
val = val.val
}
t := &itype{cat: aliasT, val: val, path: path, name: name, str: str}
for _, opt := range opts {
opt(t)
@@ -328,7 +335,7 @@ func mapOf(key, val *itype, opts ...itypeOption) *itype {
}
// interfaceOf returns an interface type with the given fields.
func interfaceOf(t *itype, fields []structField, opts ...itypeOption) *itype {
func interfaceOf(t *itype, fields []structField, constraint, ulconstraint []*itype, opts ...itypeOption) *itype {
str := "interface{}"
if len(fields) > 0 {
str = "interface { " + methodsTypeString(fields) + "}"
@@ -338,6 +345,8 @@ func interfaceOf(t *itype, fields []structField, opts ...itypeOption) *itype {
}
t.cat = interfaceT
t.field = fields
t.constraint = constraint
t.ulconstraint = ulconstraint
t.str = str
for _, opt := range opts {
opt(t)
@@ -363,6 +372,15 @@ func structOf(t *itype, fields []structField, opts ...itypeOption) *itype {
return t
}
// genericOf returns a generic type.
func genericOf(val *itype, name string, opts ...itypeOption) *itype {
t := &itype{cat: genericT, name: name, str: name, val: val}
for _, opt := range opts {
opt(t)
}
return t
}
// seenNode determines if a node has been seen.
//
// seenNode treats the slice of nodes as the path traveled down a node
@@ -479,24 +497,24 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
switch v := n.rval.Interface().(type) {
case bool:
n.rval = reflect.ValueOf(constant.MakeBool(v))
t = untypedBool()
t = untypedBool(n)
case rune:
// It is impossible to work out rune const literals in AST
// with the correct type so we must make the const type here.
n.rval = reflect.ValueOf(constant.MakeInt64(int64(v)))
t = untypedRune()
t = untypedRune(n)
case constant.Value:
switch v.Kind() {
case constant.Bool:
t = untypedBool()
t = untypedBool(n)
case constant.String:
t = untypedString()
t = untypedString(n)
case constant.Int:
t = untypedInt()
t = untypedInt(n)
case constant.Float:
t = untypedFloat()
t = untypedFloat(n)
case constant.Complex:
t = untypedComplex()
t = untypedComplex(n)
default:
err = n.cfgErrorf("missing support for type %v", n.rval)
}
@@ -505,9 +523,36 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
case unaryExpr:
// In interfaceType, we process an underlying type constraint definition.
if isInInterfaceType(n) {
t1, err := nodeType2(interp, sc, n.child[0], seen)
if err != nil {
return nil, err
}
t = &itype{cat: constraintT, ulconstraint: []*itype{t1}}
break
}
t, err = nodeType2(interp, sc, n.child[0], seen)
case binaryExpr:
// In interfaceType, we process a type constraint union definition.
if isInInterfaceType(n) {
t = &itype{cat: constraintT, constraint: []*itype{}, ulconstraint: []*itype{}}
for _, c := range n.child {
t1, err := nodeType2(interp, sc, c, seen)
if err != nil {
return nil, err
}
switch t1.cat {
case constraintT:
t.constraint = append(t.constraint, t1.constraint...)
t.ulconstraint = append(t.ulconstraint, t1.ulconstraint...)
default:
t.constraint = append(t.constraint, t1)
}
}
break
}
// Get type of first operand.
if t, err = nodeType2(interp, sc, n.child[0], seen); err != nil {
return nil, err
@@ -567,7 +612,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
case isFloat64(t0) && isFloat64(t1):
t = sc.getType("complex128")
case nt0.untyped && isNumber(t0) && nt1.untyped && isNumber(t1):
t = untypedComplex()
t = untypedComplex(n)
case nt0.untyped && isFloat32(t1) || nt1.untyped && isFloat32(t0):
t = sc.getType("complex64")
case nt0.untyped && isFloat64(t1) || nt1.untyped && isFloat64(t0):
@@ -576,7 +621,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
err = n.cfgErrorf("invalid types %s and %s", t0.Kind(), t1.Kind())
}
if nt0.untyped && nt1.untyped {
t = untypedComplex()
t = untypedComplex(n)
}
}
case bltnReal, bltnImag:
@@ -585,12 +630,12 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
if !t.incomplete {
switch k := t.TypeOf().Kind(); {
case t.untyped && isNumber(t.TypeOf()):
t = untypedFloat(n)
case k == reflect.Complex64:
t = sc.getType("float32")
case k == reflect.Complex128:
t = sc.getType("float64")
case t.untyped && isNumber(t.TypeOf()):
t = valueTOf(floatType, withUntyped(true), withScope(sc))
default:
err = n.cfgErrorf("invalid complex type %s", k)
}
@@ -659,34 +704,48 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
case funcType:
var incomplete bool
// Handle input parameters
args := make([]*itype, 0, len(n.child[0].child))
// Handle type parameters.
for _, arg := range n.child[0].child {
cl := len(arg.child) - 1
typ, err := nodeType2(interp, sc, arg.child[cl], seen)
if err != nil {
return nil, err
}
for _, c := range arg.child[:cl] {
sc.sym[c.ident] = &symbol{index: -1, kind: varTypeSym, typ: typ}
}
incomplete = incomplete || typ.incomplete
}
// Handle input parameters.
args := make([]*itype, 0, len(n.child[1].child))
for _, arg := range n.child[1].child {
cl := len(arg.child) - 1
typ, err := nodeType2(interp, sc, arg.child[cl], seen)
if err != nil {
return nil, err
}
args = append(args, typ)
// Several arguments may be factorized on the same field type.
for i := 1; i < cl; i++ {
// Several arguments may be factorized on the same field type
args = append(args, typ)
}
incomplete = incomplete || typ.incomplete
}
// Handle returned values.
var rets []*itype
if len(n.child) == 2 {
// Handle returned values
for _, ret := range n.child[1].child {
if len(n.child) == 3 {
for _, ret := range n.child[2].child {
cl := len(ret.child) - 1
typ, err := nodeType2(interp, sc, ret.child[cl], seen)
if err != nil {
return nil, err
}
rets = append(rets, typ)
// Several arguments may be factorized on the same field type.
for i := 1; i < cl; i++ {
// Several arguments may be factorized on the same field type
rets = append(rets, typ)
}
incomplete = incomplete || typ.incomplete
@@ -708,7 +767,11 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
break
}
}
t = sym.typ
if sym.kind == varTypeSym {
t = genericOf(sym.typ, n.ident, withNode(n), withScope(sc))
} else {
t = sym.typ
}
if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT {
t.incomplete = false
}
@@ -736,42 +799,102 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
switch lt.cat {
case arrayT, mapT, sliceT, variadicT:
t = lt.val
case genericT:
t1, err := nodeType2(interp, sc, n.child[1], seen)
if err != nil {
return nil, err
}
if t1.cat == genericT || t1.incomplete {
t = lt
break
}
name := lt.id() + "[" + t1.id() + "]"
if sym, _, found := sc.lookup(name); found {
t = sym.typ
break
}
// A generic type is being instantiated. Generate it.
g, err := genAST(sc, lt.node.anc, []*node{t1.node})
if err != nil {
return nil, err
}
t, err = nodeType2(interp, sc, g.lastChild(), seen)
if err != nil {
return nil, err
}
sc.sym[name] = &symbol{index: -1, kind: typeSym, typ: t, node: g}
// Instantiate type methods (if any).
var pt *itype
if len(lt.method) > 0 {
pt = ptrOf(t, withNode(g), withScope(sc))
}
for _, nod := range lt.method {
gm, err := genAST(sc, nod, []*node{t1.node})
if err != nil {
return nil, err
}
if gm.typ, err = nodeType(interp, sc, gm.child[2]); err != nil {
return nil, err
}
t.addMethod(gm)
if rtn := gm.child[0].child[0].lastChild(); rtn.kind == starExpr {
// The receiver is a pointer on a generic type.
pt.addMethod(gm)
rtn.typ = pt
}
// Compile method CFG.
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
return nil, err
}
// Generate closures for function body.
if err = genRun(gm); err != nil {
return nil, err
}
}
}
case interfaceType:
if sname := typeName(n); sname != "" {
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
t = interfaceOf(sym.typ, sym.typ.field, withNode(n), withScope(sc))
t = interfaceOf(sym.typ, sym.typ.field, sym.typ.constraint, sym.typ.ulconstraint, withNode(n), withScope(sc))
}
}
var incomplete bool
fields := make([]structField, 0, len(n.child[0].child))
for _, field := range n.child[0].child {
f0 := field.child[0]
if len(field.child) == 1 {
if f0.ident == "error" {
fields := []structField{}
constraint := []*itype{}
ulconstraint := []*itype{}
for _, c := range n.child[0].child {
c0 := c.child[0]
if len(c.child) == 1 {
if c0.ident == "error" {
// Unwrap error interface inplace rather than embedding it, because
// "error" is lower case which may cause problems with reflect for method lookup.
typ := errorMethodType(sc)
fields = append(fields, structField{name: "Error", typ: typ})
continue
}
typ, err := nodeType2(interp, sc, f0, seen)
typ, err := nodeType2(interp, sc, c0, seen)
if err != nil {
return nil, err
}
fields = append(fields, structField{name: fieldName(f0), embed: true, typ: typ})
incomplete = incomplete || typ.incomplete
if typ.cat == constraintT {
constraint = append(constraint, typ.constraint...)
ulconstraint = append(ulconstraint, typ.ulconstraint...)
continue
}
fields = append(fields, structField{name: fieldName(c0), embed: true, typ: typ})
continue
}
typ, err := nodeType2(interp, sc, field.child[1], seen)
typ, err := nodeType2(interp, sc, c.child[1], seen)
if err != nil {
return nil, err
}
fields = append(fields, structField{name: f0.ident, typ: typ})
fields = append(fields, structField{name: c0.ident, typ: typ})
incomplete = incomplete || typ.incomplete
}
t = interfaceOf(t, fields, withNode(n), withScope(sc))
t = interfaceOf(t, fields, constraint, ulconstraint, withNode(n), withScope(sc))
t.incomplete = incomplete
case landExpr, lorExpr:
@@ -870,9 +993,16 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
case structType:
if sname := typeName(n); sname != "" {
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
var sym *symbol
var found bool
sname := structName(n)
if sname != "" {
sym, _, found = sc.lookup(sname)
if found && sym.kind == typeSym {
t = structOf(sym.typ, sym.typ.field, withNode(n), withScope(sc))
} else {
t = structOf(nil, nil, withNode(n), withScope(sc))
sc.sym[sname] = &symbol{index: -1, kind: typeSym, typ: t, node: n}
}
}
var incomplete bool
@@ -913,6 +1043,9 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
t = structOf(t, fields, withNode(n), withScope(sc))
t.incomplete = incomplete
if sname != "" {
sc.sym[sname].typ = t
}
default:
err = n.cfgErrorf("type definition not implemented: %s", n.kind)
@@ -976,6 +1109,13 @@ func isBuiltinCall(n *node, sc *scope) bool {
// struct name returns the name of a struct type.
func typeName(n *node) string {
if n.anc.kind == typeSpec && len(n.anc.child) == 2 {
return n.anc.child[0].ident
}
return ""
}
func structName(n *node) string {
if n.anc.kind == typeSpec {
return n.anc.child[0].ident
}
@@ -1125,6 +1265,24 @@ func (t *itype) concrete() *itype {
return t
}
func (t *itype) underlying() *itype {
if t.cat == aliasT {
return t.val.underlying()
}
return t
}
// typeDefined returns true if type t1 is defined from type t2 or t2 from t1.
func typeDefined(t1, t2 *itype) bool {
if t1.cat == aliasT && t1.val == t2 {
return true
}
if t2.cat == aliasT && t2.val == t1 {
return true
}
return false
}
// isVariadic returns true if the function type is variadic.
// If the type is not a function or is not variadic, it will
// return false.
@@ -1198,10 +1356,11 @@ func (t *itype) assignableTo(o *itype) bool {
if t.equals(o) {
return true
}
if t.cat == aliasT && o.cat == aliasT {
// If alias types are not identical, it is not assignable.
if t.cat == aliasT && o.cat == aliasT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
return false
}
if t.isNil() && o.hasNil() || o.isNil() && t.hasNil() {
return true
}
@@ -1214,11 +1373,20 @@ func (t *itype) assignableTo(o *itype) bool {
return true
}
if t.cat == sliceT && o.cat == sliceT {
return t.val.assignableTo(o.val)
}
if t.isBinMethod && isFunc(o) {
// TODO (marc): check that t without receiver as first parameter is equivalent to o.
return true
}
if t.untyped && isNumber(t.TypeOf()) && isNumber(o.TypeOf()) {
// Assignability depends on constant numeric value (overflow check), to be tested elsewhere.
return true
}
n := t.node
if n == nil || !n.rval.IsValid() {
return false
@@ -1772,6 +1940,9 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
ctx.refs[name] = append(flds, fieldRebuild{})
return unsafe2.DummyType
}
if isGeneric(t) {
return reflect.TypeOf((*generic)(nil)).Elem()
}
switch t.cat {
case aliasT:
t.rtype = t.val.refType(ctx)
@@ -2041,6 +2212,10 @@ func isEmptyInterface(t *itype) bool {
return t.cat == interfaceT && len(t.field) == 0
}
func isGeneric(t *itype) bool {
return t.cat == funcT && t.node != nil && len(t.node.child[0].child) > 0
}
func isFuncSrc(t *itype) bool {
return t.cat == funcT || (t.cat == aliasT && isFuncSrc(t.val))
}

View File

@@ -184,23 +184,29 @@ func (check typecheck) shift(n *node) error {
// comparison type checks a comparison binary expression.
func (check typecheck) comparison(n *node) error {
c0, c1 := n.child[0], n.child[1]
t0, t1 := n.child[0].typ, n.child[1].typ
if !c0.typ.assignableTo(c1.typ) && !c1.typ.assignableTo(c0.typ) {
return n.cfgErrorf("invalid operation: mismatched types %s and %s", c0.typ.id(), c1.typ.id())
if !t0.assignableTo(t1) && !t1.assignableTo(t0) {
return n.cfgErrorf("invalid operation: mismatched types %s and %s", t0.id(), t1.id())
}
ok := false
if !isInterface(t0) && !isInterface(t1) && !t0.isNil() && !t1.isNil() && t0.untyped == t1.untyped && t0.id() != t1.id() && !typeDefined(t0, t1) {
// Non interface types must be really equals.
return n.cfgErrorf("invalid operation: mismatched types %s and %s", t0.id(), t1.id())
}
switch n.action {
case aEqual, aNotEqual:
ok = c0.typ.comparable() && c1.typ.comparable() || c0.typ.isNil() && c1.typ.hasNil() || c1.typ.isNil() && c0.typ.hasNil()
ok = t0.comparable() && t1.comparable() || t0.isNil() && t1.hasNil() || t1.isNil() && t0.hasNil()
case aLower, aLowerEqual, aGreater, aGreaterEqual:
ok = c0.typ.ordered() && c1.typ.ordered()
ok = t0.ordered() && t1.ordered()
}
if !ok {
typ := c0.typ
typ := t0
if typ.isNil() {
typ = c1.typ
typ = t1
}
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id())
}
@@ -815,7 +821,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
case !typ0.untyped && typ1.untyped:
err = check.convertUntyped(p1.nod, typ0)
case typ0.untyped && typ1.untyped:
fltType := check.scope.getType("float64")
fltType := untypedFloat(nil)
err = check.convertUntyped(p0.nod, fltType)
if err != nil {
break
@@ -838,7 +844,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
p := params[0]
typ := p.Type()
if typ.untyped {
if err := check.convertUntyped(p.nod, check.scope.getType("complex128")); err != nil {
if err := check.convertUntyped(p.nod, untypedComplex(nil)); err != nil {
return err
}
}

View File

@@ -34,10 +34,10 @@ func valueGenerator(n *node, i int) func(*frame) reflect.Value {
// because a cancellation prior to any evaluation result may leave
// the frame's data empty.
func valueOf(data []reflect.Value, i int) reflect.Value {
if i < len(data) {
return data[i]
if i < 0 || i >= len(data) {
return reflect.Value{}
}
return reflect.Value{}
return data[i]
}
func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value) func(*frame) reflect.Value {
@@ -195,7 +195,7 @@ func genValue(n *node) func(*frame) reflect.Value {
}
if n.sym != nil {
i := n.sym.index
if i < 0 {
if i < 0 && n != n.sym.node {
return genValue(n.sym.node)
}
if n.sym.global {
@@ -392,6 +392,21 @@ func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value {
return value
}
func getBinValue(getMapType func(*itype) reflect.Type, value func(*frame) reflect.Value, f *frame) reflect.Value {
v := value(f)
if getMapType == nil {
return v
}
val, ok := v.Interface().(valueInterface)
if !ok || val.node == nil {
return v
}
if rt := getMapType(val.node.typ); rt != nil {
return genInterfaceWrapper(val.node, rt)(f)
}
return v
}
func valueInterfaceValue(v reflect.Value) reflect.Value {
for {
vv, ok := v.Interface().(valueInterface)

View File

@@ -1,30 +0,0 @@
// Code generated by 'yaegi extract go/parser'. DO NOT EDIT.
// +build go1.16,!go1.17
package stdlib
import (
"go/parser"
"reflect"
)
func init() {
Symbols["go/parser/parser"] = map[string]reflect.Value{
// function, constant and variable definitions
"AllErrors": reflect.ValueOf(parser.AllErrors),
"DeclarationErrors": reflect.ValueOf(parser.DeclarationErrors),
"ImportsOnly": reflect.ValueOf(parser.ImportsOnly),
"PackageClauseOnly": reflect.ValueOf(parser.PackageClauseOnly),
"ParseComments": reflect.ValueOf(parser.ParseComments),
"ParseDir": reflect.ValueOf(parser.ParseDir),
"ParseExpr": reflect.ValueOf(parser.ParseExpr),
"ParseExprFrom": reflect.ValueOf(parser.ParseExprFrom),
"ParseFile": reflect.ValueOf(parser.ParseFile),
"SpuriousErrors": reflect.ValueOf(parser.SpuriousErrors),
"Trace": reflect.ValueOf(parser.Trace),
// type definitions
"Mode": reflect.ValueOf((*parser.Mode)(nil)),
}
}

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract archive/tar'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract archive/zip'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract bufio'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract bytes'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -19,6 +20,7 @@ func init() {
"ContainsAny": reflect.ValueOf(bytes.ContainsAny),
"ContainsRune": reflect.ValueOf(bytes.ContainsRune),
"Count": reflect.ValueOf(bytes.Count),
"Cut": reflect.ValueOf(bytes.Cut),
"Equal": reflect.ValueOf(bytes.Equal),
"EqualFold": reflect.ValueOf(bytes.EqualFold),
"ErrTooLarge": reflect.ValueOf(&bytes.ErrTooLarge).Elem(),

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract compress/bzip2'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract compress/flate'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract compress/gzip'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract compress/lzw'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -18,6 +19,8 @@ func init() {
"NewWriter": reflect.ValueOf(lzw.NewWriter),
// type definitions
"Order": reflect.ValueOf((*lzw.Order)(nil)),
"Order": reflect.ValueOf((*lzw.Order)(nil)),
"Reader": reflect.ValueOf((*lzw.Reader)(nil)),
"Writer": reflect.ValueOf((*lzw.Writer)(nil)),
}
}

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract compress/zlib'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract container/heap'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -31,8 +32,8 @@ type _container_heap_Interface struct {
IValue interface{}
WLen func() int
WLess func(i int, j int) bool
WPop func() interface{}
WPush func(x interface{})
WPop func() any
WPush func(x any)
WSwap func(i int, j int)
}
@@ -42,10 +43,10 @@ func (W _container_heap_Interface) Len() int {
func (W _container_heap_Interface) Less(i int, j int) bool {
return W.WLess(i, j)
}
func (W _container_heap_Interface) Pop() interface{} {
func (W _container_heap_Interface) Pop() any {
return W.WPop()
}
func (W _container_heap_Interface) Push(x interface{}) {
func (W _container_heap_Interface) Push(x any) {
W.WPush(x)
}
func (W _container_heap_Interface) Swap(i int, j int) {

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract container/list'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract container/ring'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract context'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -37,7 +38,7 @@ type _context_Context struct {
WDeadline func() (deadline time.Time, ok bool)
WDone func() <-chan struct{}
WErr func() error
WValue func(key interface{}) interface{}
WValue func(key any) any
}
func (W _context_Context) Deadline() (deadline time.Time, ok bool) {
@@ -49,6 +50,6 @@ func (W _context_Context) Done() <-chan struct{} {
func (W _context_Context) Err() error {
return W.WErr()
}
func (W _context_Context) Value(key interface{}) interface{} {
func (W _context_Context) Value(key any) any {
return W.WValue(key)
}

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/aes'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/cipher'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/des'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/dsa'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/ecdsa'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/ed25519'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/elliptic'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/hmac'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/md5'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/rand'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/rc4'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/rsa'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/sha1'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/sha256'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/sha512'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/subtle'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/tls'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/x509'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/x509/pkix'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract database/sql'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -37,7 +38,9 @@ func init() {
"IsolationLevel": reflect.ValueOf((*sql.IsolationLevel)(nil)),
"NamedArg": reflect.ValueOf((*sql.NamedArg)(nil)),
"NullBool": reflect.ValueOf((*sql.NullBool)(nil)),
"NullByte": reflect.ValueOf((*sql.NullByte)(nil)),
"NullFloat64": reflect.ValueOf((*sql.NullFloat64)(nil)),
"NullInt16": reflect.ValueOf((*sql.NullInt16)(nil)),
"NullInt32": reflect.ValueOf((*sql.NullInt32)(nil)),
"NullInt64": reflect.ValueOf((*sql.NullInt64)(nil)),
"NullString": reflect.ValueOf((*sql.NullString)(nil)),
@@ -75,9 +78,9 @@ func (W _database_sql_Result) RowsAffected() (int64, error) {
// _database_sql_Scanner is an interface wrapper for Scanner type
type _database_sql_Scanner struct {
IValue interface{}
WScan func(src interface{}) error
WScan func(src any) error
}
func (W _database_sql_Scanner) Scan(src interface{}) error {
func (W _database_sql_Scanner) Scan(src any) error {
return W.WScan(src)
}

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract database/sql/driver'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -491,10 +491,10 @@ type _database_sql_driver_Value struct {
// _database_sql_driver_ValueConverter is an interface wrapper for ValueConverter type
type _database_sql_driver_ValueConverter struct {
IValue interface{}
WConvertValue func(v interface{}) (driver.Value, error)
WConvertValue func(v any) (driver.Value, error)
}
func (W _database_sql_driver_ValueConverter) ConvertValue(v interface{}) (driver.Value, error) {
func (W _database_sql_driver_ValueConverter) ConvertValue(v any) (driver.Value, error) {
return W.WConvertValue(v)
}

View File

@@ -0,0 +1,22 @@
// Code generated by 'yaegi extract debug/buildinfo'. DO NOT EDIT.
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
import (
"debug/buildinfo"
"reflect"
)
func init() {
Symbols["debug/buildinfo/buildinfo"] = map[string]reflect.Value{
// function, constant and variable definitions
"Read": reflect.ValueOf(buildinfo.Read),
"ReadFile": reflect.ValueOf(buildinfo.ReadFile),
// type definitions
"BuildInfo": reflect.ValueOf((*buildinfo.BuildInfo)(nil)),
}
}

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract debug/dwarf'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract debug/elf'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -964,6 +964,7 @@ func init() {
"R_PPC64_REL24_NOTOC": reflect.ValueOf(elf.R_PPC64_REL24_NOTOC),
"R_PPC64_REL32": reflect.ValueOf(elf.R_PPC64_REL32),
"R_PPC64_REL64": reflect.ValueOf(elf.R_PPC64_REL64),
"R_PPC64_RELATIVE": reflect.ValueOf(elf.R_PPC64_RELATIVE),
"R_PPC64_SECTOFF_DS": reflect.ValueOf(elf.R_PPC64_SECTOFF_DS),
"R_PPC64_SECTOFF_LO_DS": reflect.ValueOf(elf.R_PPC64_SECTOFF_LO_DS),
"R_PPC64_TLS": reflect.ValueOf(elf.R_PPC64_TLS),

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract debug/gosym'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract debug/macho'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract debug/pe'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract debug/plan9obj'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -15,12 +15,13 @@ import (
func init() {
Symbols["debug/plan9obj/plan9obj"] = map[string]reflect.Value{
// function, constant and variable definitions
"Magic386": reflect.ValueOf(constant.MakeFromLiteral("491", token.INT, 0)),
"Magic64": reflect.ValueOf(constant.MakeFromLiteral("32768", token.INT, 0)),
"MagicAMD64": reflect.ValueOf(constant.MakeFromLiteral("35479", token.INT, 0)),
"MagicARM": reflect.ValueOf(constant.MakeFromLiteral("1607", token.INT, 0)),
"NewFile": reflect.ValueOf(plan9obj.NewFile),
"Open": reflect.ValueOf(plan9obj.Open),
"ErrNoSymbols": reflect.ValueOf(&plan9obj.ErrNoSymbols).Elem(),
"Magic386": reflect.ValueOf(constant.MakeFromLiteral("491", token.INT, 0)),
"Magic64": reflect.ValueOf(constant.MakeFromLiteral("32768", token.INT, 0)),
"MagicAMD64": reflect.ValueOf(constant.MakeFromLiteral("35479", token.INT, 0)),
"MagicARM": reflect.ValueOf(constant.MakeFromLiteral("1607", token.INT, 0)),
"NewFile": reflect.ValueOf(plan9obj.NewFile),
"Open": reflect.ValueOf(plan9obj.Open),
// type definitions
"File": reflect.ValueOf((*plan9obj.File)(nil)),

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract embed'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract encoding'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract encoding/ascii85'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract encoding/asn1'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract encoding/base32'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract encoding/base64'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract encoding/binary'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract encoding/csv'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract encoding/gob'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract encoding/hex'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract encoding/json'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract encoding/pem'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract encoding/xml'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract errors'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract expvar'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract flag'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -67,12 +67,12 @@ func init() {
// _flag_Getter is an interface wrapper for Getter type
type _flag_Getter struct {
IValue interface{}
WGet func() interface{}
WGet func() any
WSet func(a0 string) error
WString func() string
}
func (W _flag_Getter) Get() interface{} {
func (W _flag_Getter) Get() any {
return W.WGet()
}
func (W _flag_Getter) Set(a0 string) error {

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract fmt'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract go/ast'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -87,6 +88,7 @@ func init() {
"Importer": reflect.ValueOf((*ast.Importer)(nil)),
"IncDecStmt": reflect.ValueOf((*ast.IncDecStmt)(nil)),
"IndexExpr": reflect.ValueOf((*ast.IndexExpr)(nil)),
"IndexListExpr": reflect.ValueOf((*ast.IndexListExpr)(nil)),
"InterfaceType": reflect.ValueOf((*ast.InterfaceType)(nil)),
"KeyValueExpr": reflect.ValueOf((*ast.KeyValueExpr)(nil)),
"LabeledStmt": reflect.ValueOf((*ast.LabeledStmt)(nil)),

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract go/build'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

Some files were not shown because too many files have changed in this diff Show More