interp: fix handling generic types with multiple type parameters

Those declarations involve the indexListExpr AST token, which was not handled in type.go. The same processing as for a single type parameter is applied.

Fixes #1460.
This commit is contained in:
Marc Vertes
2022-09-22 13:50:09 +02:00
committed by GitHub
parent 021824930d
commit dfeddbe823
3 changed files with 164 additions and 37 deletions

79
_test/issue-1460.go Normal file
View File

@@ -0,0 +1,79 @@
package main
import (
"bytes"
"encoding/json"
"errors"
"reflect"
)
func unmarshalJSON[T any](b []byte, x *[]T) error {
if *x != nil {
return errors.New("already initialized")
}
if len(b) == 0 {
return nil
}
return json.Unmarshal(b, x)
}
type StructView[T any] interface {
Valid() bool
AsStruct() T
}
type SliceView[T ViewCloner[T, V], V StructView[T]] struct {
ж []T
}
type ViewCloner[T any, V StructView[T]] interface {
View() V
Clone() T
}
func SliceOfViews[T ViewCloner[T, V], V StructView[T]](x []T) SliceView[T, V] {
return SliceView[T, V]{x}
}
func (v SliceView[T, V]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
func (v *SliceView[T, V]) UnmarshalJSON(b []byte) error { return unmarshalJSON(b, &v.ж) }
type Slice[T any] struct {
ж []T
}
func (v Slice[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
func (v *Slice[T]) UnmarshalJSON(b []byte) error { return unmarshalJSON(b, &v.ж) }
func SliceOf[T any](x []T) Slice[T] {
return Slice[T]{x}
}
type viewStruct struct {
Int int
Strings Slice[string]
StringsPtr *Slice[string] `json:",omitempty"`
}
func main() {
ss := SliceOf([]string{"bar"})
in := viewStruct{
Int: 1234,
Strings: ss,
StringsPtr: &ss,
}
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
encoder.SetIndent("", "")
err1 := encoder.Encode(&in)
b := buf.Bytes()
var got viewStruct
err2 := json.Unmarshal(b, &got)
println(err1 == nil, err2 == nil, reflect.DeepEqual(got, in))
}
// Output:
// true true true

View File

@@ -161,8 +161,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
switch rtn.kind {
case starExpr:
typPtr = true
if rtn.child[0].kind == indexExpr {
typName = rtn.child[0].child[0].ident
switch c := rtn.child[0]; c.kind {
case indexExpr, indexListExpr:
typName = c.child[0].ident
genericMethod = true
}
case indexExpr:

View File

@@ -821,46 +821,50 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
break
}
// A generic type is being instantiated. Generate it.
g, err := genAST(sc, lt.node.anc, []*node{t1.node})
t, err = genType(interp, sc, name, lt, []*node{t1.node}, seen)
if err != nil {
return nil, err
}
t, err = nodeType2(interp, sc, g.lastChild(), seen)
if err != nil {
return nil, err
}
sc.sym[name] = &symbol{index: -1, kind: typeSym, typ: t, node: g}
// Instantiate type methods (if any).
var pt *itype
if len(lt.method) > 0 {
pt = ptrOf(t, withNode(g), withScope(sc))
}
for _, nod := range lt.method {
gm, err := genAST(sc, nod, []*node{t1.node})
if err != nil {
return nil, err
}
if gm.typ, err = nodeType(interp, sc, gm.child[2]); err != nil {
return nil, err
}
t.addMethod(gm)
if rtn := gm.child[0].child[0].lastChild(); rtn.kind == starExpr {
// The receiver is a pointer on a generic type.
pt.addMethod(gm)
rtn.typ = pt
}
// Compile method CFG.
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
return nil, err
}
// Generate closures for function body.
if err = genRun(gm); err != nil {
return nil, err
}
}
}
case indexListExpr:
// Similar to above indexExpr for generic types, but handle multiple type parameters.
var lt *itype
if lt, err = nodeType2(interp, sc, n.child[0], seen); err != nil {
return nil, err
}
// Index list expressions can be used only in context of generic types.
if lt.cat != genericT {
err = n.cfgErrorf("not a generic type: %s", lt.id())
return nil, err
}
name := lt.id() + "["
out := false
tnodes := []*node{}
for _, c := range n.child[1:] {
t1, err := nodeType2(interp, sc, c, seen)
if err != nil {
return nil, err
}
if t1.cat == genericT || t1.incomplete {
t = lt
out = true
break
}
tnodes = append(tnodes, t1.node)
name += t1.id() + ","
}
if out {
break
}
name += "]"
if sym, _, found := sc.lookup(name); found {
t = sym.typ
break
}
// A generic type is being instantiated. Generate it.
t, err = genType(interp, sc, name, lt, tnodes, seen)
case interfaceType:
if sname := typeName(n); sname != "" {
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
@@ -1083,6 +1087,49 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
return t, err
}
func genType(interp *Interpreter, sc *scope, name string, lt *itype, tnodes, seen []*node) (t *itype, err error) {
// A generic type is being instantiated. Generate it.
g, err := genAST(sc, lt.node.anc, tnodes)
if err != nil {
return nil, err
}
t, err = nodeType2(interp, sc, g.lastChild(), seen)
if err != nil {
return nil, err
}
sc.sym[name] = &symbol{index: -1, kind: typeSym, typ: t, node: g}
// Instantiate type methods (if any).
var pt *itype
if len(lt.method) > 0 {
pt = ptrOf(t, withNode(g), withScope(sc))
}
for _, nod := range lt.method {
gm, err := genAST(sc, nod, tnodes)
if err != nil {
return nil, err
}
if gm.typ, err = nodeType(interp, sc, gm.child[2]); err != nil {
return nil, err
}
t.addMethod(gm)
if rtn := gm.child[0].child[0].lastChild(); rtn.kind == starExpr {
// The receiver is a pointer on a generic type.
pt.addMethod(gm)
rtn.typ = pt
}
// Compile method CFG.
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
return nil, err
}
// Generate closures for function body.
if err = genRun(gm); err != nil {
return nil, err
}
}
return t, err
}
// findPackageType searches the top level scope for a package type.
func findPackageType(interp *Interpreter, sc *scope, n *node) *itype {
// Find the root scope, the package symbols will exist there.