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

fix(gnovm): annotate specific reason a value does not implement an interface #2492

Merged
merged 2 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions gnovm/pkg/gnolang/op_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,14 @@ func (m *Machine) doOpTypeAssert1() {

// t is Gno interface.
// assert that x implements type.
var impl bool
impl = it.IsImplementedBy(xt)
if !impl {
err := it.VerifyImplementedBy(xt)
if err != nil {
// TODO: default panic type?
ex := fmt.Sprintf(
"%s doesn't implement %s",
"%s doesn't implement %s (%s)",
xt.String(),
it.String())
it.String(),
err.Error())
m.Panic(typedString(ex))
return
}
Expand Down
7 changes: 4 additions & 3 deletions gnovm/pkg/gnolang/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,15 @@ func checkAssignableTo(xt, dt Type, autoNative bool) error {
if idt.IsEmptyInterface() { // XXX, can this be merged with IsImplementedBy?
// if dt is an empty Gno interface, any x ok.
return nil // ok
} else if idt.IsImplementedBy(xt) {
} else if err := idt.VerifyImplementedBy(xt); err == nil {
// if dt implements idt, ok.
return nil // ok
} else {
return errors.New(
"%s does not implement %s",
"%s does not implement %s (%s)",
xt.String(),
dt.String())
dt.String(),
err.Error())
}
} else if ndt, ok := baseOf(dt).(*NativeType); ok {
nidt := ndt.Type
Expand Down
20 changes: 12 additions & 8 deletions gnovm/pkg/gnolang/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1009,21 +1009,21 @@

// For run-time type assertion.
// TODO: optimize somehow.
func (it *InterfaceType) IsImplementedBy(ot Type) (result bool) {
func (it *InterfaceType) VerifyImplementedBy(ot Type) error {
for _, im := range it.Methods {
if im.Type.Kind() == InterfaceKind {
// field is embedded interface...
im2 := baseOf(im.Type).(*InterfaceType)
if !im2.IsImplementedBy(ot) {
return false
if err := im2.VerifyImplementedBy(ot); err != nil {
return err

Check warning on line 1018 in gnovm/pkg/gnolang/types.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/types.go#L1018

Added line #L1018 was not covered by tests
} else {
continue
}
}
// find method in field.
tr, hp, rt, ft, _ := findEmbeddedFieldType(it.PkgPath, ot, im.Name, nil)
if tr == nil { // not found.
return false
return fmt.Errorf("missing method %s", im.Name)
}
if nft, ok := ft.(*NativeType); ok {
// Treat native function types as autoNative calls.
Expand All @@ -1033,22 +1033,26 @@
// ie, if each of ft's arg types can match
// against the desired arg types in im.Types.
if !gno2GoTypeMatches(im.Type, nft.Type) {
return false
return fmt.Errorf("wrong type for method %s", im.Name)

Check warning on line 1036 in gnovm/pkg/gnolang/types.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/types.go#L1036

Added line #L1036 was not covered by tests
}
} else if mt, ok := ft.(*FuncType); ok {
// if method is pointer receiver, check addressability:
if _, ptrRcvr := rt.(*PointerType); ptrRcvr && !hp {
return false // not addressable.
return fmt.Errorf("method %s has pointer receiver", im.Name) // not addressable.
}
// check for func type equality.
dmtid := mt.TypeID()
imtid := im.Type.TypeID()
if dmtid != imtid {
return false
return fmt.Errorf("wrong type for method %s", im.Name)
}
}
}
return true
return nil
}

func (it *InterfaceType) IsImplementedBy(ot Type) bool {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
return it.VerifyImplementedBy(ot) == nil
}

func (it *InterfaceType) GetPathForName(n Name) ValuePath {
Expand Down
2 changes: 1 addition & 1 deletion gnovm/tests/files/access6_stdlibs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ func main() {
}

// Error:
// main/files/access6_stdlibs.gno:15:2: main.mystruct does not implement gno.land/p/demo/testutils.PrivateInterface
// main/files/access6_stdlibs.gno:15:2: main.mystruct does not implement gno.land/p/demo/testutils.PrivateInterface (missing method privateMethod)
2 changes: 1 addition & 1 deletion gnovm/tests/files/access7_stdlibs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ func main() {
}

// Error:
// main/files/access7_stdlibs.gno:19:2: main.PrivateInterface2 does not implement gno.land/p/demo/testutils.PrivateInterface
// main/files/access7_stdlibs.gno:19:2: main.PrivateInterface2 does not implement gno.land/p/demo/testutils.PrivateInterface (missing method privateMethod)
2 changes: 1 addition & 1 deletion gnovm/tests/files/fun27.gno
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ func main() {
}

// Error:
// main/files/fun27.gno:8:2: <untyped> bigint does not implement main.Foo
// main/files/fun27.gno:8:2: <untyped> bigint does not implement main.Foo (missing method foo)
2 changes: 1 addition & 1 deletion gnovm/tests/files/type24b.gno
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ func assertValue() {
// Output:
// int is not of type string
// interface{} is not of type string
// *github.com/gnolang/gno/_test/net/http/httptest.ResponseRecorder doesn't implement interface{Push func(string;*github.com/gnolang/gno/_test/net/http.PushOptions)(.uverse.error)}
// *github.com/gnolang/gno/_test/net/http/httptest.ResponseRecorder doesn't implement interface{Push func(string;*github.com/gnolang/gno/_test/net/http.PushOptions)(.uverse.error)} (missing method Push)
2 changes: 1 addition & 1 deletion gnovm/tests/files/typeassert4a.gno
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,5 @@ func main() {
// interface conversion: interface is nil, not main.Setter
// interface conversion: interface is nil, not main.Setter
// ok
// main.ValueSetter doesn't implement interface{Set func(string)()}
// main.ValueSetter doesn't implement interface{Set func(string)()} (method Set has pointer receiver)
// ok
4 changes: 2 additions & 2 deletions gnovm/tests/files/typeassert6a.gno
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ func main() {
}

// Output:
// int doesn't implement interface{Do func(string)()}
// *int doesn't implement interface{Do func(string)()}
// int doesn't implement interface{Do func(string)()} (missing method Do)
// *int doesn't implement interface{Do func(string)()} (missing method Do)
19 changes: 19 additions & 0 deletions gnovm/tests/files/typeassert9a.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

// First interface
type Reader interface {
Read(int) string
}

type csvReader struct{}
func (r*csvReader) Read(string) string{
return ""
}


func main() {
var csvReader Reader = &csvReader{}
}

// Error:
// main/files/typeassert9a.gno:15:6: *main.csvReader does not implement main.Reader (wrong type for method Read)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/assign_type_assertion_c.gno
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ func main() {
}

// Error:
// main/files/types/assign_type_assertion_c.gno:23:2: interface{IsSet func()(bool)} does not implement interface{IsNotSet func()(bool)}
// main/files/types/assign_type_assertion_c.gno:23:2: interface{IsSet func()(bool)} does not implement interface{IsNotSet func()(bool)} (missing method IsNotSet)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0b4_stdlibs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ func main() {
}

// Error:
// main/files/types/eql_0b4_stdlibs.gno:9:10: <untyped> bigint does not implement .uverse.error
// main/files/types/eql_0b4_stdlibs.gno:9:10: <untyped> bigint does not implement .uverse.error (missing method Error)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0f0_stdlibs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ func main() {
}

// Error:
// main/files/types/eql_0f0_stdlibs.gno:19:5: <untyped> bigint does not implement .uverse.error
// main/files/types/eql_0f0_stdlibs.gno:19:5: <untyped> bigint does not implement .uverse.error (missing method Error)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0f1_stdlibs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ func main() {
}

// Error:
// main/files/types/eql_0f1_stdlibs.gno:19:5: int64 does not implement .uverse.error
// main/files/types/eql_0f1_stdlibs.gno:19:5: int64 does not implement .uverse.error (missing method Error)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0f41_stdlibs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ func main() {
}

// Error:
// main/files/types/eql_0f41_stdlibs.gno:27:5: main.animal does not implement .uverse.error
// main/files/types/eql_0f41_stdlibs.gno:27:5: main.animal does not implement .uverse.error (missing method Error)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0f8_stdlibs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ func main() {
}

// Error:
// main/files/types/eql_0f8_stdlibs.gno:19:5: int64 does not implement .uverse.error
// main/files/types/eql_0f8_stdlibs.gno:19:5: int64 does not implement .uverse.error (missing method Error)
Loading