Skip to content

Commit

Permalink
interp: fix processing of aliased types
Browse files Browse the repository at this point in the history
For a long time, there was a confusion between aliased types and named types (due to my misunderstanding of alias types in Go at that time). The type category `aliasT` has been renamed into `linkedT`, which is correct semantically. 

Aliased types are only declared using `typeSpecAssign`, and its processing is now distinct from `typeSpec` statement, used for named types.

A `linkedT` type is obtained by a statement like `type A uint32`, where `A` type category is therefore `linkedT`.

An aliased type is obtained by a statement like `type A = uint32` (notice the `=` sign, translating into `typeSpecAssign`).

The semantic difference is that in the first linkedT form, `A` and `uint32` are 2 distinct types, instead of being strictly equivalent in the `typeSpecAssign` form (the 2 names lead to one type definition).


Fixes #1416.
  • Loading branch information
mvertes authored Oct 26, 2022
1 parent 9f43170 commit 7bb8b46
Show file tree
Hide file tree
Showing 14 changed files with 81 additions and 62 deletions.
17 changes: 17 additions & 0 deletions _test/issue-1416.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

type Number int32

type Number1 = Number

type Number2 = Number1

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

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

// Output:
// true
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions _test/alias3.go → _test/named3.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package main

import "github.com/traefik/yaegi/_test/alias3"
import "github.com/traefik/yaegi/_test/named3"

var globalT *T

func init() {
globalT = &T{A: "test"}
}

type T alias3.T
type T named3.T

func (t *T) PrintT() {
(*alias3.T)(t).Print()
(*named3.T)(t).Print()
}

func main() {
Expand Down
2 changes: 1 addition & 1 deletion _test/alias3/alias3.go → _test/named3/named3.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package alias3
package named3

import (
"fmt"
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion internal/cmd/genop/genop.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions interp/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}

switch o.typ.cat {
case valueT, aliasT:
case valueT, linkedT:
typ := o.typ.rtype
if o.typ.cat == aliasT {
if o.typ.cat == linkedT {
typ = o.typ.val.TypeOf()
}
switch typ.Kind() {
Expand Down Expand Up @@ -845,7 +845,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
wireChild(n)
t := n.child[0].typ
for t.cat == aliasT {
for t.cat == linkedT {
t = t.val
}
switch t.cat {
Expand Down Expand Up @@ -2894,7 +2894,7 @@ func typeSwichAssign(n *node) bool {

func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
switch typ.cat {
case aliasT, ptrT:
case linkedT, ptrT:
gen = compositeGenerator(n, typ.val, rtyp)
case arrayT, sliceT:
gen = arrayLit
Expand Down
40 changes: 20 additions & 20 deletions interp/gta.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
revisit = append(revisit, n)
return false
}
if sym.kind != typeSym || (sym.node != nil && sym.node.kind == typeSpecAssign) {
if sym.typ.path != pkgName {
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(sym.typ).id())
return false
}
Expand Down Expand Up @@ -298,7 +298,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
typeName := n.child[0].ident
if len(n.child) > 2 {
// Handle a generic type: skip definition as parameter is not instantiated yet.
n.typ = genericOf(nil, typeName, withNode(n.child[0]), withScope(sc))
n.typ = genericOf(nil, typeName, pkgName, withNode(n.child[0]), withScope(sc))
if _, exists := sc.sym[typeName]; !exists {
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
}
Expand All @@ -312,6 +312,15 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
return false
}

if n.kind == typeSpecAssign {
// Create an aliased type in the current scope
sc.sym[typeName] = &symbol{kind: typeSym, node: n, typ: typ}
n.typ = typ
break
}

// else we are not an alias (typeSpec)

switch n.child[1].kind {
case identExpr, selectorExpr:
n.typ = namedOf(typ, pkgName, typeName, withNode(n.child[0]), withScope(sc))
Expand All @@ -333,24 +342,15 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
}
sym, exists := sc.sym[typeName]
if !exists {
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
} else {
if sym.typ != nil && (len(sym.typ.method) > 0) {
if n.kind == typeSpecAssign {
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(typ).id())
return false
}
// Type has already been seen as a receiver in a method function
for _, m := range sym.typ.method {
n.typ.addMethod(m)
}
} else {
// TODO(mpl): figure out how to detect redeclarations without breaking type aliases.
// Allow redeclarations for now.
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
sym = &symbol{kind: typeSym, node: n}
sc.sym[typeName] = sym
} else if sym.typ != nil && (len(sym.typ.method) > 0) {
// Type has already been seen as a receiver in a method function
for _, m := range sym.typ.method {
n.typ.addMethod(m)
}
}
sc.sym[typeName].typ = n.typ
sym.typ = n.typ
if !n.typ.isComplete() {
revisit = append(revisit, n)
}
Expand All @@ -368,7 +368,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
func baseType(t *itype) *itype {
for {
switch t.cat {
case ptrT, aliasT:
case ptrT, linkedT:
t = t.val
default:
return t
Expand Down Expand Up @@ -440,7 +440,7 @@ func definedType(typ *itype) error {
return err
}
fallthrough
case aliasT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
case linkedT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
if err := definedType(typ.val); err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions interp/interp_eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ func TestEvalTypeSpec(t *testing.T) {
runTests(t, i, []testCase{
{src: `type _ struct{}`, err: "1:19: cannot use _ as value"},
{src: `a := struct{a, _ int}{32, 0}`, res: "{32 0}"},
{src: "type A int; type A = string", err: "1:31: A redeclared in this block"},
{src: "type B int; type B string", err: "1:31: B redeclared in this block"},
})
}

Expand Down
4 changes: 2 additions & 2 deletions interp/op.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions interp/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2616,7 +2616,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
next := getExec(n.tnext)
value := valueGenerator(n, n.findex)
typ := n.typ.rtype
if n.typ.cat == ptrT || n.typ.cat == aliasT {
if n.typ.cat == ptrT || n.typ.cat == linkedT {
typ = n.typ.val.rtype
}
child := n.child
Expand Down Expand Up @@ -2683,7 +2683,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
value := valueGenerator(n, n.findex)
next := getExec(n.tnext)
typ := n.typ
if typ.cat == ptrT || typ.cat == aliasT {
if typ.cat == ptrT || typ.cat == linkedT {
typ = typ.val
}
child := n.child
Expand Down
Loading

0 comments on commit 7bb8b46

Please sign in to comment.