-
Notifications
You must be signed in to change notification settings - Fork 374
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: achieve type assertion parity with go (#1689)
<!-- please provide a detailed description of the changes made in this pull request. --> Fixes #1650 This PR was originally intended to fix the case described in issue #1650, but it soon became apparent that there were other subtle inconsistencies between type assertions in go vs gno. The changes outlined here attempt to fix the issues and the new file tests ensure correctness. The summary below provides details as to the cases that were fixed for type assertions, both 1 and 2 -- 1 and 2 referring to the type assertion op codes, `OpTypeAssert1` and `OpTypeAssert2`. The former handles type assertions with one LHS argument that panic on assertion failure and the latter handles those with two LHS arguments that assigns a boolean value dependent on success or failure. ### Summary of type assertion changes - fail when the value being asserted has a nil type (i.e. the result of `recover()` when no panic has occurred). This applies for both cases where the type being asserted to is an interface or concrete type. - fail when asserting to an interface type and the underlying type of the value being asserted is also an interface type (non-concrete type) Also, is this an accurate assumption? https://github.com/gnolang/gno/blob/7d4e8e645ca1d2e89f5a8f2e85470e66b3253b33/gnovm/pkg/gnolang/op_expressions.go#L261 <details><summary>Contributors' checklist...</summary> - [x] Added new tests, or not needed, or not feasible - [x] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [x] Added references to related issues and PRs - [x] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md). </details>
- Loading branch information
Showing
18 changed files
with
579 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package main | ||
|
||
type A interface { | ||
Do(s string) | ||
} | ||
|
||
func main() { | ||
var a A | ||
_ = a.(A) | ||
} | ||
|
||
// Error: | ||
// interface conversion: interface is nil, not main.A |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package main | ||
|
||
type ex int | ||
|
||
func (ex) Error() string { return "" } | ||
|
||
type A interface { | ||
Do(s string) | ||
} | ||
|
||
func main() { | ||
defer func() { | ||
e := recover() | ||
if _, ok := e.(ex); ok { | ||
println("wat") | ||
} else { | ||
println("ok") | ||
} | ||
}() | ||
defer func() { | ||
e := recover() | ||
if _, ok := e.(A); ok { | ||
println("wat") | ||
} else { | ||
println("ok") | ||
} | ||
}() | ||
} | ||
|
||
// Output: | ||
// ok | ||
// ok |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package main | ||
|
||
type A interface { | ||
Do(s string) | ||
} | ||
|
||
func main() { | ||
defer func() { | ||
e := recover() | ||
_ = e.(A) | ||
}() | ||
} | ||
|
||
// Error: | ||
// interface conversion: interface is nil, not main.A |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package main | ||
|
||
type ex int | ||
|
||
func (ex) Error() string { return "" } | ||
|
||
func main() { | ||
defer func() { | ||
r := _.(ex) | ||
println(r) | ||
}() | ||
} | ||
|
||
// Error: | ||
// main/files/typeassert3.gno:9: cannot use _ as value or type |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package main | ||
|
||
type Setter interface { | ||
Set(string) | ||
} | ||
|
||
type SetterClone interface { | ||
Set(string) | ||
} | ||
|
||
type ValueSetter struct { | ||
value string | ||
} | ||
|
||
func (s *ValueSetter) Set(value string) { | ||
s.value = value | ||
} | ||
|
||
func cmpSetter(i interface{}) { | ||
if _, ok := i.(Setter); ok { | ||
println("ok") | ||
} else { | ||
println("not ok") | ||
} | ||
} | ||
|
||
func main() { | ||
var ( | ||
i interface{} | ||
setter Setter | ||
setterClone SetterClone | ||
valueSetter ValueSetter | ||
valueSetterPtr *ValueSetter | ||
) | ||
|
||
cmpSetter(i) | ||
|
||
i = setter | ||
cmpSetter(i) | ||
|
||
setterClone = valueSetterPtr | ||
setter = setterClone | ||
i = setter | ||
cmpSetter(i) | ||
|
||
i = valueSetter | ||
cmpSetter(i) | ||
|
||
i = valueSetterPtr | ||
cmpSetter(i) | ||
} | ||
|
||
// Output: | ||
// not ok | ||
// not ok | ||
// ok | ||
// not ok | ||
// ok |
Oops, something went wrong.