traefik / yaegi

Yaegi is Another Elegant Go Interpreter
https://pkg.go.dev/github.com/traefik/yaegi
Apache License 2.0
7.02k stars 348 forks source link

Generic usage #1599

Open donnol opened 10 months ago

donnol commented 10 months ago

The following program sample.go triggers an unexpected result

package main

import (
    "fmt"
    "testing"

    "github.com/traefik/yaegi/interp"
)

func TestYaegi(t *testing.T) {
    const src = `package main

func Sum(a, b int) int {
    return a + b
}

func SumG[T ~int](a, b T) T {
    return a + b
}
`

    {
        i := interp.New(interp.Options{})

        _, err := i.Eval(src)
        if err != nil {
            t.Fatal(err)
        }

        // normal function
        v, err := i.Eval("main.Sum")
        if err != nil {
            t.Fatal(err)
        }

        bar := v.Interface().(func(int, int) int)

        r := bar(1, 1)
        println(r)

        // generic function
        {
            v, err := i.Eval("main.SumG[int]")
            if err != nil {
                t.Fatal(err)
            }
            fmt.Printf("v: %+v\n", v) // v: <invalid reflect.Value>

            bar := v.Interface().(func(int, int) int)

            r := bar(1, 1)
            println(r)
        }
    }
}

Expected result

go test -v .

Got

--- FAIL: TestYaegi (0.00s)
panic: reflect: call of reflect.Value.Interface on zero Value [recovered]
    panic: reflect: call of reflect.Value.Interface on zero Value

goroutine 21 [running]:
testing.tRunner.func1.2({0xdabfa0, 0xc00012cd08})
    /home/jd/.go/current/src/testing/testing.go:1526 +0x24e
testing.tRunner.func1()
    /home/jd/.go/current/src/testing/testing.go:1529 +0x39f
panic({0xdabfa0, 0xc00012cd08})
    /home/jd/.go/current/src/runtime/panic.go:884 +0x213
reflect.valueInterface({0x0?, 0x0?, 0xea7241?}, 0x7?)
    /home/jd/.go/current/src/reflect/value.go:1485 +0x10e
reflect.Value.Interface(...)
    /home/jd/.go/current/src/reflect/value.go:1480
github.com/donnol/jdmgr/data/script/yaegi.TestYaegi(0xc000208b60)
    /home/jd/Project/jd/jdmgr/data/script/yaegi/script_test.go:48 +0x2b3
testing.tRunner(0xc000208b60, 0xf17708)
    /home/jd/.go/current/src/testing/testing.go:1576 +0x10b
created by testing.(*T).Run
    /home/jd/.go/current/src/testing/testing.go:1629 +0x3ea
FAIL    github.com/donnol/jdmgr/data/script/yaegi   0.007s

Yaegi Version

v0.15.1

Additional Notes

package main

import (
    "fmt"
    "testing"

    "github.com/traefik/yaegi/interp"
)

func TestYaegi(t *testing.T) {
    const src = `package main

func Sum(a, b int) int {
    return a + b
}

func SumG[T ~int](a, b T) T {
    return a + b
}
`

    {
        i := interp.New(interp.Options{})

        _, err := i.Eval(src)
        if err != nil {
            t.Fatal(err)
        }

        // normal function
        v, err := i.Eval("main.Sum")
        if err != nil {
            t.Fatal(err)
        }

        bar := v.Interface().(func(int, int) int)

        r := bar(1, 1)
        println(r)

        // generic function
        {
            v, err := i.Eval("main.SumG[int]")
            if err != nil {
                t.Fatal(err)
            }
            fmt.Printf("v: %+v\n", v) // v: <invalid reflect.Value>

            bar := v.Interface().(func(int, int) int)

            r := bar(1, 1)
            println(r)
        }
    }
}

failed with:

--- FAIL: TestYaegi (0.00s)
panic: reflect: call of reflect.Value.Interface on zero Value [recovered]
    panic: reflect: call of reflect.Value.Interface on zero Value

goroutine 21 [running]:
testing.tRunner.func1.2({0xdabfa0, 0xc00012cd08})
    /home/jd/.go/current/src/testing/testing.go:1526 +0x24e
testing.tRunner.func1()
    /home/jd/.go/current/src/testing/testing.go:1529 +0x39f
panic({0xdabfa0, 0xc00012cd08})
    /home/jd/.go/current/src/runtime/panic.go:884 +0x213
reflect.valueInterface({0x0?, 0x0?, 0xea7241?}, 0x7?)
    /home/jd/.go/current/src/reflect/value.go:1485 +0x10e
reflect.Value.Interface(...)
    /home/jd/.go/current/src/reflect/value.go:1480
github.com/donnol/jdmgr/data/script/yaegi.TestYaegi(0xc000208b60)
    /home/jd/Project/jd/jdmgr/data/script/yaegi/script_test.go:48 +0x2b3
testing.tRunner(0xc000208b60, 0xf17708)
    /home/jd/.go/current/src/testing/testing.go:1576 +0x10b
created by testing.(*T).Run
    /home/jd/.go/current/src/testing/testing.go:1629 +0x3ea
FAIL    github.com/donnol/jdmgr/data/script/yaegi   0.007s
donnol commented 10 months ago

After some try, I found it works like below:

...
        {
            v, err := i.Eval("main.SumG(1, 2)")
            if err != nil {
                t.Fatal(err)
            }

            r := v.Interface().(int)
            println(r)
        }
...

But, how can I do to work like the normal function?

sixhuan commented 9 months ago

I also have the same problem, and I really hope to get the answer from the master.