fix: improve interface type checks using method sets
This commit is contained in:
committed by
Traefiker Bot
parent
0d2c39d155
commit
273df8af9f
18
_test/interface18.go
Normal file
18
_test/interface18.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type T struct{}
|
||||||
|
|
||||||
|
func (t *T) Error() string { return "T: error" }
|
||||||
|
func (*T) Foo() { println("foo") }
|
||||||
|
|
||||||
|
var invalidT = &T{}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
if err != invalidT {
|
||||||
|
println("ok")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// ok
|
||||||
@@ -549,7 +549,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
|||||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||||
// Shift operator type is inherited from first parameter only
|
// Shift operator type is inherited from first parameter only
|
||||||
// All other binary operators require both parameter types to be the same
|
// All other binary operators require both parameter types to be the same
|
||||||
if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && !c0.typ.equal(c1.typ) {
|
if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && !c0.typ.equals(c1.typ) {
|
||||||
err = n.cfgErrorf("mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
err = n.cfgErrorf("mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -636,18 +636,41 @@ func (t *itype) finalize() (*itype, error) {
|
|||||||
return t, err
|
return t, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// equal returns true if the given type is identical to the receiver one
|
// Equals returns true if the given type is identical to the receiver one.
|
||||||
func (t *itype) equal(o *itype) bool {
|
func (t *itype) equals(o *itype) bool {
|
||||||
if isInterface(t) || isInterface(o) {
|
switch ti, oi := isInterface(t), isInterface(o); {
|
||||||
// Check for identical methods sets
|
case ti && oi:
|
||||||
return reflect.DeepEqual(t.methods(), o.methods())
|
return t.methods().equals(o.methods())
|
||||||
|
case ti && !oi:
|
||||||
|
return o.methods().contains(t.methods())
|
||||||
|
case oi && !ti:
|
||||||
|
return t.methods().contains(o.methods())
|
||||||
|
default:
|
||||||
|
return t.id() == o.id()
|
||||||
}
|
}
|
||||||
return t.id() == o.id()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods returns a map of method type strings, indexed by method names
|
// MethodSet defines the set of methods signatures as strings, indexed per method name.
|
||||||
func (t *itype) methods() map[string]string {
|
type methodSet map[string]string
|
||||||
res := make(map[string]string)
|
|
||||||
|
// Contains returns true if the method set m contains the method set n.
|
||||||
|
func (m methodSet) contains(n methodSet) bool {
|
||||||
|
for k, v := range n {
|
||||||
|
if m[k] != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if the method set m is equal to the method set n.
|
||||||
|
func (m methodSet) equals(n methodSet) bool {
|
||||||
|
return m.contains(n) && n.contains(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods returns a map of method type strings, indexed by method names.
|
||||||
|
func (t *itype) methods() methodSet {
|
||||||
|
res := make(methodSet)
|
||||||
switch t.cat {
|
switch t.cat {
|
||||||
case interfaceT:
|
case interfaceT:
|
||||||
// Get methods from recursive analysis of interface fields
|
// Get methods from recursive analysis of interface fields
|
||||||
@@ -879,15 +902,9 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
|
|||||||
in := make([]reflect.Type, len(t.arg))
|
in := make([]reflect.Type, len(t.arg))
|
||||||
out := make([]reflect.Type, len(t.ret))
|
out := make([]reflect.Type, len(t.ret))
|
||||||
for i, v := range t.arg {
|
for i, v := range t.arg {
|
||||||
if defined[v.name] {
|
|
||||||
v.rtype = interf
|
|
||||||
}
|
|
||||||
in[i] = v.refType(defined)
|
in[i] = v.refType(defined)
|
||||||
}
|
}
|
||||||
for i, v := range t.ret {
|
for i, v := range t.ret {
|
||||||
if defined[v.name] {
|
|
||||||
v.rtype = interf
|
|
||||||
}
|
|
||||||
out[i] = v.refType(defined)
|
out[i] = v.refType(defined)
|
||||||
}
|
}
|
||||||
t.rtype = reflect.FuncOf(in, out, false)
|
t.rtype = reflect.FuncOf(in, out, false)
|
||||||
|
|||||||
Reference in New Issue
Block a user