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:
79
_test/issue-1460.go
Normal file
79
_test/issue-1460.go
Normal 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
|
||||
@@ -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:
|
||||
|
||||
117
interp/type.go
117
interp/type.go
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user