Skip to content

Commit

Permalink
interp: fix resolution of methods on aliased types
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mvertes committed Jun 14, 2022
1 parent 996b1e3 commit f76db27
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 10 deletions.
16 changes: 16 additions & 0 deletions _test/issue-1411.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

type Number int32

func (n Number) IsValid() bool { return true }

type Number1 = Number

type Number2 = Number1

func main() {
a := Number2(5)
println(a.IsValid())
}

// Output: true
6 changes: 1 addition & 5 deletions interp/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -2700,11 +2700,7 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
}
case valueT:
if rtyp == nil {
rtyp = n.typ.rtype
}
// TODO(mpl): I do not understand where this side-effect is coming from, and why it happens. quickfix for now.
if rtyp == nil {
rtyp = n.typ.val.rtype
rtyp = n.typ.TypeOf()
}
switch k := rtyp.Kind(); k {
case reflect.Struct:
Expand Down
24 changes: 19 additions & 5 deletions interp/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit f76db27

Please sign in to comment.