interp: fix resolution of methods on aliased types

The type.val field was always pointing to the final underlying type
for aliased types, defeating a possible match if a method was
attached to a type in between. Now the complete chain of aliases
is always preserved.

We have added an underlying() itype method which returns the underlying
type of a defined type (aliasT), even in the presence of multiple
indirections.

We have added a definedType function which checks if type t1 is
defined from type t2 or t2 defined from t1, required when checking
assignability of aliasT types.

Fixes #1411.

PS: this is the 2nd attempt, as the first version #1412 wasn't passing
_test/issue-1408.go as well. This PR does pass and supersedes #1412.
This commit is contained in:
Marc Vertes
2022-06-14 14:42:09 +00:00
committed by GitHub
parent 996b1e33c8
commit f76db27c77
3 changed files with 36 additions and 10 deletions

View File

@@ -238,9 +238,6 @@ func namedOf(val *itype, path, name string, opts ...itypeOption) *itype {
if path != "" {
str = path + "." + name
}
for val.cat == aliasT {
val = val.val
}
t := &itype{cat: aliasT, val: val, path: path, name: name, str: str}
for _, opt := range opts {
opt(t)
@@ -1125,6 +1122,24 @@ func (t *itype) concrete() *itype {
return t
}
func (t *itype) underlying() *itype {
if t.cat == aliasT {
return t.val.underlying()
}
return t
}
// typeDefined returns true if type t1 is defined from type t2 or t2 from t1.
func typeDefined(t1, t2 *itype) bool {
if t1.cat == aliasT && t1.val == t2 {
return true
}
if t2.cat == aliasT && t2.val == t1 {
return true
}
return false
}
// isVariadic returns true if the function type is variadic.
// If the type is not a function or is not variadic, it will
// return false.
@@ -1198,8 +1213,7 @@ func (t *itype) assignableTo(o *itype) bool {
if t.equals(o) {
return true
}
if t.cat == aliasT && o.cat == aliasT && t.val.id() != o.val.id() {
// If alias types are not identical, it is not assignable.
if t.cat == aliasT && o.cat == aliasT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
return false
}
if t.isNil() && o.hasNil() || o.isNil() && t.hasNil() {