Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interface assertion not work #1558

Open
laushunyu opened this issue Jun 14, 2023 · 5 comments · May be fixed by #1562
Open

interface assertion not work #1558

laushunyu opened this issue Jun 14, 2023 · 5 comments · May be fixed by #1562

Comments

@laushunyu
Copy link
Contributor

The following program sample.go triggers an unexpected result

package main

import (
	"fmt"
	"github.com/traefik/yaegi/interp"
	"github.com/traefik/yaegi/stdlib"
)

func main() {
	interpreter := interp.New(interp.Options{})
	interpreter.Use(stdlib.Symbols)
	expected := ""
	fmt.Printf(expected)

	_, err := interpreter.Compile(`package main

import "fmt"

type A struct {
}

func (a A) String() string {
	return "String"
}

func (a A) GoString() string {
	return "GoString"
}

func GetGoString() (fmt.GoStringer, bool) {
	var a fmt.Stringer = A{}
	goStr, ok := a.(fmt.GoStringer)
	return goStr, ok
}
`)
	if err != nil {
		panic(err)
	}

	mainSymbols := interpreter.Symbols("main")["main"]
	SelfSrc := mainSymbols["GetGoString"].Interface().(func() (fmt.GoStringer, bool))
	_, ok := SelfSrc()
	fmt.Println(ok)
}

Expected result

# go run .
true

Got

# go run .
false

Yaegi Version

v0.15.1

Additional Notes

hi,
assertion from a interface to another interface is not working.

I haven't found the exact location in the source code where this bug occurs yet, but I can suggest some possible causes and solutions for the bug:

package main

import "fmt"

type A struct {
}

func (a A) String() string {
	return "String"
}

func (a A) GoString() string {
	return "GoString"
}

func main() {
	var a fmt.Stringer = A{}
	v := reflect.ValueOf(&a).Elem()
	target := reflect.TypeOf((*fmt.GoStringer)(nil)).Elem()

	fmt.Println(v.Type(), target)
	fmt.Println(v.CanConvert(target))

	// implementation of interface v
	fmt.Println(v.Elem().Type(), target)
	fmt.Println(v.Elem().CanConvert(target))
}

output:

fmt.Stringer fmt.GoStringer
false
main.A fmt.GoStringer
true

I think when trying to know whether v.CanConvert(), we should Elem the v if v.Kind == reflect.Interface.

@laushunyu
Copy link
Contributor Author

it seem like that the assignee to a interface does not save it's impletation's value. so when converting to another interface, it not work?

in yaegi/interp/run.go:410
v = v.Elem()
image

@laushunyu
Copy link
Contributor Author

laushunyu commented Jun 14, 2023

it seem like that the assignee to a interface does not save it's impletation's value. so when converting to another interface, it not work?

in yaegi/interp/run.go:410 v = v.Elem() image

the address of empty struct value is 0, so we cannot get the implementation. another field IType to store Type is nesserary. or the type of IValue should be reflect.Value

@laushunyu
Copy link
Contributor Author

laushunyu commented Jun 14, 2023

another case:

package main

import "fmt"

type A struct {
	Fuck string
	Shit int
}

func (a A) String() string {
	return "String"
}

func (a A) GoString() string {
	return "GoString"
}

type Stringer interface {
	String() string
}

func GetGoString() (fmt.GoStringer, bool) {
	var a Stringer = &A{}
	goStr, ok := a.(fmt.GoStringer)
	return goStr, ok
}

if define the interface in src package, it can compare every method (in interp/run.go:379) then return true; but interface in bin package(fmt.Stringer) is not work.

@laushunyu
Copy link
Contributor Author

and

switch a.(type) {
case fmt.GoStringer:
}

is not working too, even if interface is in the same src package, i found that yaegi/interp/run.go:2997:

// match against 1 type: assign var to concrete value

dest value can also be a interface too, so we should compare all its method.

mvertes added a commit that referenced this issue Jun 14, 2023
Embedding files using `//go:embed` and `embed` packages can not be
supported in yaegi, so it is better to not generate the wrapper to
embed and have a graceful error in case of usage of `embed.FS` in
the interpreter.
Also update README about unsuported directives.

Fixes #1558.
@laushunyu
Copy link
Contributor Author

The commit 25f44d6 seem like had a wrong issue referenced.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant