compiler,runtime: panic in case of Scriggo type as map key when calling builtin 'delete'
Closed this issue · 5 comments
cannot delete from a map when the key is a complex type.
Consider the following snippet:
package main
type key struct {
X int
Y int
}
func main() {
println("starting population")
mpbasic := map[int]int{}
mpcomplex := map[key]int{}
for i := 0; i < 256; i++ {
mpbasic[i] = i * 2
mpcomplex[key{X: i, Y: i + 1}] = i * 2
}
println("starting delete simple")
for k := range mpbasic {
delete(mpbasic, k)
}
println("starting delete complex")
for k := range mpcomplex {
if mpcomplex[k] > 2 {
delete(mpcomplex, k)
}
}
println("done")
}
When run using the GC we get the following output (runs to completion):
starting population
starting delete simple
starting delete complex
done
When run using scriggo we get the following output with a panic:
panic: reflect.Value.SetMapIndex: value of type types.emptyInterfaceProxy is not assignable to type struct { X int; Y int }
goroutine 1 [running]:
github.com/open2b/scriggo/internal/runtime.(*VM).Run(0xc0000e4240, 0xc0000a5520, 0xc00009e970, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/kris/mygo/pkg/mod/github.com/open2b/scriggo@v0.53.2-0.20211019155432-8c493a4aa1f8/internal/runtime/vm.go:161 +0x169
github.com/open2b/scriggo.(*Program).Run(0xc0000a1380, 0x0, 0x0, 0xc0000a1380)
/home/kris/mygo/pkg/mod/github.com/open2b/scriggo@v0.53.2-0.20211019155432-8c493a4aa1f8/programs.go:143 +0xd6
main.main()
/tmp/scriggo_test/main.go:19 +0x13f
I am building off of the tip of master (commit 8c493a4)
It appears that the range iterator on a map hands back some sort of pointer when using complex map key types, but the delete function cannot handle that pointer. Using native types for the key this completes fine.
Complete test program
package main
import (
"fmt"
"github.com/open2b/scriggo"
)
func main() {
// Create a file system with the file of the program to run.
fsys := scriggo.Files{"main.go": []byte(src)}
// Build the program.
program, err := scriggo.Build(fsys, nil)
if err != nil {
panic(err)
}
// Run the program.
err = program.Run(nil)
if err != nil {
fmt.Println("RUN ERROR", err)
}
fmt.Println("DONE")
}
const src = `
package main
type key struct {
X int
Y int
}
func main() {
println("starting population")
mpbasic := map[int]int{}
mpcomplex := map[key]int{}
for i := 0; i < 256; i++ {
mpbasic[i] = i*2
mpcomplex[key{X: i, Y: i+1}] = i*2
}
println("starting delete simple")
for k := range mpbasic {
delete(mpbasic, k)
}
println("starting delete complex")
for k := range mpcomplex {
if mpcomplex[k] > 2 {
delete(mpcomplex, k)
}
}
println("done")
}
`
This is the minimal program that reproduces the issue
package main
type T struct {}
func main() {
delete(map[T]int{}, T{})
}
This is the minimal program that reproduces the issue
package main type T struct {} func main() { delete(map[T]int{}, T{}) }
This code seems to fail for another reason (still not sure, i'm investigating) related to this code:
scriggo/internal/compiler/emitter.go
Line 811 in 1d45ea7
I opened a new issue for that.
After investigating this issue, it turned out that it is strictly related to #913, which will be closed in favour of this.
Consider this code:
package main
type T int
func main() {
delete(map[T]int{}, T(0))
}
the runtime reads the key from a general registers:
scriggo/internal/runtime/run.go
Lines 550 to 552 in 92001f4
So:
- if the key is not typified, it remains in the int registers and thus it's not found
- if the key is typified, it is moved to the general register but is is converted to a proxy, so it cannot be passed to the
SetMapIndex
method of the reflect.
wow, great work guys! Thank you very much.