interp: fix use of interfaces in composite types
The representation of non empty interfaces defined in the interpreter is now identical between refType() and frameType() functions, which are used to generate interpreter objects. Fixes #1447 and #1426.
This commit is contained in:
@@ -5,8 +5,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defined an interface of stringBuilder that compatible with
|
// Define an interface of stringBuilder that is compatible with
|
||||||
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10)
|
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10).
|
||||||
type stringBuilder interface {
|
type stringBuilder interface {
|
||||||
WriteRune(r rune) (n int, err error)
|
WriteRune(r rune) (n int, err error)
|
||||||
WriteString(s string) (int, error)
|
WriteString(s string) (int, error)
|
||||||
|
|||||||
20
_test/issue-1447.go
Normal file
20
_test/issue-1447.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type I interface {
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type S struct {
|
||||||
|
iMap map[string]I
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
s := S{}
|
||||||
|
s.iMap = map[string]I{}
|
||||||
|
fmt.Println(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {map[]}
|
||||||
@@ -553,7 +553,7 @@ func convert(n *node) {
|
|||||||
if c.isNil() { // convert nil to type
|
if c.isNil() { // convert nil to type
|
||||||
// TODO(mpl): Try to completely remove, as maybe frameType already does the job for interfaces.
|
// TODO(mpl): Try to completely remove, as maybe frameType already does the job for interfaces.
|
||||||
if isInterfaceSrc(n.child[0].typ) && !isEmptyInterface(n.child[0].typ) {
|
if isInterfaceSrc(n.child[0].typ) && !isEmptyInterface(n.child[0].typ) {
|
||||||
typ = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
typ = valueInterfaceType
|
||||||
}
|
}
|
||||||
n.exec = func(f *frame) bltn {
|
n.exec = func(f *frame) bltn {
|
||||||
dest(f).Set(reflect.New(typ).Elem())
|
dest(f).Set(reflect.New(typ).Elem())
|
||||||
@@ -713,7 +713,7 @@ func assign(n *node) {
|
|||||||
case isFuncSrc(typ):
|
case isFuncSrc(typ):
|
||||||
t = reflect.TypeOf((*node)(nil))
|
t = reflect.TypeOf((*node)(nil))
|
||||||
case isInterfaceSrc(typ):
|
case isInterfaceSrc(typ):
|
||||||
t = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
t = valueInterfaceType
|
||||||
default:
|
default:
|
||||||
t = typ.TypeOf()
|
t = typ.TypeOf()
|
||||||
}
|
}
|
||||||
@@ -1007,7 +1007,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
|||||||
}
|
}
|
||||||
typ := def.typ.arg[i]
|
typ := def.typ.arg[i]
|
||||||
switch {
|
switch {
|
||||||
case isEmptyInterface(typ):
|
case isEmptyInterface(typ) || typ.TypeOf() == valueInterfaceType:
|
||||||
d[i].Set(arg)
|
d[i].Set(arg)
|
||||||
case isInterfaceSrc(typ):
|
case isInterfaceSrc(typ):
|
||||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||||
@@ -1560,12 +1560,9 @@ func callBin(n *node) {
|
|||||||
case isInterfaceSrc(c.typ):
|
case isInterfaceSrc(c.typ):
|
||||||
values = append(values, genValueInterfaceValue(c))
|
values = append(values, genValueInterfaceValue(c))
|
||||||
case c.typ.cat == arrayT || c.typ.cat == variadicT:
|
case c.typ.cat == arrayT || c.typ.cat == variadicT:
|
||||||
switch {
|
if isEmptyInterface(c.typ.val) {
|
||||||
case isEmptyInterface(c.typ.val):
|
|
||||||
values = append(values, genValueArray(c))
|
values = append(values, genValueArray(c))
|
||||||
case isInterfaceSrc(c.typ.val):
|
} else {
|
||||||
values = append(values, genValueInterfaceArray(c))
|
|
||||||
default:
|
|
||||||
values = append(values, genInterfaceWrapper(c, defType))
|
values = append(values, genInterfaceWrapper(c, defType))
|
||||||
}
|
}
|
||||||
case isPtrSrc(c.typ):
|
case isPtrSrc(c.typ):
|
||||||
@@ -2703,8 +2700,6 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
|||||||
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
|
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
|
||||||
case isFuncSrc(val.typ):
|
case isFuncSrc(val.typ):
|
||||||
values[fieldIndex] = genValueAsFunctionWrapper(val)
|
values[fieldIndex] = genValueAsFunctionWrapper(val)
|
||||||
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val) && !isEmptyInterface(val.typ.val):
|
|
||||||
values[fieldIndex] = genValueInterfaceArray(val)
|
|
||||||
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
|
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
|
||||||
values[fieldIndex] = genValueInterface(val)
|
values[fieldIndex] = genValueInterface(val)
|
||||||
case isInterface(ft):
|
case isInterface(ft):
|
||||||
@@ -3585,7 +3580,7 @@ func convertLiteralValue(n *node, t reflect.Type) {
|
|||||||
case n.typ.cat == nilT:
|
case n.typ.cat == nilT:
|
||||||
// Create a zero value of target type.
|
// Create a zero value of target type.
|
||||||
n.rval = reflect.New(t).Elem()
|
n.rval = reflect.New(t).Elem()
|
||||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t == valueInterfaceType || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
||||||
// Skip non-constant values, undefined target type or interface target type.
|
// Skip non-constant values, undefined target type or interface target type.
|
||||||
case n.rval.IsValid():
|
case n.rval.IsValid():
|
||||||
// Convert constant value to target type.
|
// Convert constant value to target type.
|
||||||
@@ -3946,7 +3941,7 @@ func isNotNil(n *node) {
|
|||||||
dest := genValue(n)
|
dest := genValue(n)
|
||||||
|
|
||||||
if n.fnext == nil {
|
if n.fnext == nil {
|
||||||
if isInterfaceSrc(c0.typ) {
|
if isInterfaceSrc(c0.typ) && c0.typ.TypeOf() != valueInterfaceType {
|
||||||
if isInterface {
|
if isInterface {
|
||||||
n.exec = func(f *frame) bltn {
|
n.exec = func(f *frame) bltn {
|
||||||
dest(f).Set(reflect.ValueOf(!value(f).IsNil()).Convert(typ))
|
dest(f).Set(reflect.ValueOf(!value(f).IsNil()).Convert(typ))
|
||||||
@@ -3991,7 +3986,7 @@ func isNotNil(n *node) {
|
|||||||
|
|
||||||
fnext := getExec(n.fnext)
|
fnext := getExec(n.fnext)
|
||||||
|
|
||||||
if isInterfaceSrc(c0.typ) {
|
if isInterfaceSrc(c0.typ) && c0.typ.TypeOf() != valueInterfaceType {
|
||||||
n.exec = func(f *frame) bltn {
|
n.exec = func(f *frame) bltn {
|
||||||
if value(f).IsNil() {
|
if value(f).IsNil() {
|
||||||
dest(f).SetBool(false)
|
dest(f).SetBool(false)
|
||||||
|
|||||||
@@ -1817,8 +1817,9 @@ func exportName(s string) string {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// TODO(mpl): generators.
|
// TODO(mpl): generators.
|
||||||
interf = reflect.TypeOf((*interface{})(nil)).Elem()
|
emptyInterfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
valueInterfaceType = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||||
|
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||||
)
|
)
|
||||||
|
|
||||||
type fieldRebuild struct {
|
type fieldRebuild struct {
|
||||||
@@ -1971,7 +1972,12 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
|||||||
}
|
}
|
||||||
t.rtype = reflect.FuncOf(in, out, variadic)
|
t.rtype = reflect.FuncOf(in, out, variadic)
|
||||||
case interfaceT:
|
case interfaceT:
|
||||||
t.rtype = interf
|
if len(t.field) == 0 {
|
||||||
|
// empty interface, do not wrap it
|
||||||
|
t.rtype = emptyInterfaceType
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.rtype = valueInterfaceType
|
||||||
case mapT:
|
case mapT:
|
||||||
t.rtype = reflect.MapOf(t.key.refType(ctx), t.val.refType(ctx))
|
t.rtype = reflect.MapOf(t.key.refType(ctx), t.val.refType(ctx))
|
||||||
case ptrT:
|
case ptrT:
|
||||||
@@ -2056,10 +2062,10 @@ func (t *itype) frameType() (r reflect.Type) {
|
|||||||
case interfaceT:
|
case interfaceT:
|
||||||
if len(t.field) == 0 {
|
if len(t.field) == 0 {
|
||||||
// empty interface, do not wrap it
|
// empty interface, do not wrap it
|
||||||
r = reflect.TypeOf((*interface{})(nil)).Elem()
|
r = emptyInterfaceType
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
r = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
r = valueInterfaceType
|
||||||
case mapT:
|
case mapT:
|
||||||
r = reflect.MapOf(t.key.frameType(), t.val.frameType())
|
r = reflect.MapOf(t.key.frameType(), t.val.frameType())
|
||||||
case ptrT:
|
case ptrT:
|
||||||
@@ -2072,6 +2078,14 @@ func (t *itype) frameType() (r reflect.Type) {
|
|||||||
|
|
||||||
func (t *itype) implements(it *itype) bool {
|
func (t *itype) implements(it *itype) bool {
|
||||||
if isBin(t) {
|
if isBin(t) {
|
||||||
|
// Note: in case of a valueInterfaceType, we
|
||||||
|
// miss required data which will be available
|
||||||
|
// later, so we optimistically return true to progress,
|
||||||
|
// and additional checks will be hopefully performed at
|
||||||
|
// runtime.
|
||||||
|
if rt := it.TypeOf(); rt == valueInterfaceType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return t.TypeOf().Implements(it.TypeOf())
|
return t.TypeOf().Implements(it.TypeOf())
|
||||||
}
|
}
|
||||||
return t.methods().contains(it.methods())
|
return t.methods().contains(it.methods())
|
||||||
@@ -2127,11 +2141,15 @@ func (t *itype) defaultType(v reflect.Value, sc *scope) *itype {
|
|||||||
func (t *itype) isNil() bool { return t.cat == nilT }
|
func (t *itype) isNil() bool { return t.cat == nilT }
|
||||||
|
|
||||||
func (t *itype) hasNil() bool {
|
func (t *itype) hasNil() bool {
|
||||||
switch t.TypeOf().Kind() {
|
switch rt := t.TypeOf(); rt.Kind() {
|
||||||
case reflect.UnsafePointer:
|
case reflect.UnsafePointer:
|
||||||
return true
|
return true
|
||||||
case reflect.Slice, reflect.Ptr, reflect.Func, reflect.Interface, reflect.Map, reflect.Chan:
|
case reflect.Slice, reflect.Ptr, reflect.Func, reflect.Interface, reflect.Map, reflect.Chan:
|
||||||
return true
|
return true
|
||||||
|
case reflect.Struct:
|
||||||
|
if rt == valueInterfaceType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -2246,7 +2264,7 @@ func isInterfaceBin(t *itype) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isInterface(t *itype) bool {
|
func isInterface(t *itype) bool {
|
||||||
return isInterfaceSrc(t) || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
return isInterfaceSrc(t) || t.TypeOf() == valueInterfaceType || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBin(t *itype) bool {
|
func isBin(t *itype) bool {
|
||||||
|
|||||||
@@ -590,7 +590,7 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
|||||||
// https://github.com/golang/go/issues/39717 lands. It is currently impractical to
|
// https://github.com/golang/go/issues/39717 lands. It is currently impractical to
|
||||||
// type check Named types as they cannot be asserted.
|
// type check Named types as they cannot be asserted.
|
||||||
|
|
||||||
if n.typ.TypeOf().Kind() != reflect.Interface {
|
if rt := n.typ.TypeOf(); rt.Kind() != reflect.Interface && rt != valueInterfaceType {
|
||||||
return n.cfgErrorf("invalid type assertion: non-interface type %s on left", n.typ.id())
|
return n.cfgErrorf("invalid type assertion: non-interface type %s on left", n.typ.id())
|
||||||
}
|
}
|
||||||
ims := n.typ.methods()
|
ims := n.typ.methods()
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ func genValue(n *node) func(*frame) reflect.Value {
|
|||||||
convertConstantValue(n)
|
convertConstantValue(n)
|
||||||
v := n.rval
|
v := n.rval
|
||||||
if !v.IsValid() {
|
if !v.IsValid() {
|
||||||
v = reflect.New(interf).Elem()
|
v = reflect.New(emptyInterfaceType).Elem()
|
||||||
}
|
}
|
||||||
return func(f *frame) reflect.Value { return v }
|
return func(f *frame) reflect.Value { return v }
|
||||||
case funcDecl:
|
case funcDecl:
|
||||||
@@ -287,19 +287,6 @@ func genValueRangeArray(n *node) func(*frame) reflect.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func genValueInterfaceArray(n *node) func(*frame) reflect.Value {
|
|
||||||
value := genValue(n)
|
|
||||||
return func(f *frame) reflect.Value {
|
|
||||||
vi := value(f).Interface().([]valueInterface)
|
|
||||||
v := reflect.MakeSlice(reflect.TypeOf([]interface{}{}), len(vi), len(vi))
|
|
||||||
for i, vv := range vi {
|
|
||||||
v.Index(i).Set(vv.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func genValueInterface(n *node) func(*frame) reflect.Value {
|
func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||||
value := genValue(n)
|
value := genValue(n)
|
||||||
|
|
||||||
@@ -356,7 +343,7 @@ func getConcreteValue(val reflect.Value) reflect.Value {
|
|||||||
|
|
||||||
func zeroInterfaceValue() reflect.Value {
|
func zeroInterfaceValue() reflect.Value {
|
||||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true, str: "nil"}}
|
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true, str: "nil"}}
|
||||||
v := reflect.New(interf).Elem()
|
v := reflect.New(emptyInterfaceType).Elem()
|
||||||
return reflect.ValueOf(valueInterface{n, v})
|
return reflect.ValueOf(valueInterface{n, v})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user