interp: fix processing of aliased types

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.
This commit is contained in:
Marc Vertes
2022-10-26 17:00:07 +02:00
committed by GitHub
parent 9f43170708
commit 7bb8b4631f
14 changed files with 81 additions and 62 deletions

View File

@@ -17,7 +17,6 @@ type tcat uint
// Types for go language.
const (
nilT tcat = iota
aliasT
arrayT
binT
binPkgT
@@ -41,6 +40,7 @@ const (
int16T
int32T
int64T
linkedT
mapT
ptrT
sliceT
@@ -60,7 +60,6 @@ const (
var cats = [...]string{
nilT: "nilT",
aliasT: "aliasT",
arrayT: "arrayT",
binT: "binT",
binPkgT: "binPkgT",
@@ -75,14 +74,15 @@ var cats = [...]string{
float32T: "float32",
float64T: "float64T",
funcT: "funcT",
genericT: "genericT",
interfaceT: "interfaceT",
intT: "intT",
int8T: "int8T",
int16T: "int16T",
int32T: "int32T",
int64T: "int64T",
linkedT: "linkedT",
mapT: "mapT",
genericT: "genericT",
ptrT: "ptrT",
sliceT: "sliceT",
srcPkgT: "srcPkgT",
@@ -118,7 +118,7 @@ type itype struct {
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT, sliceT, variadicT or genericT
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, linkedT, arrayT, sliceT, variadicT or genericT
recv *itype // Receiver type for funcT or nil
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
@@ -248,7 +248,7 @@ func namedOf(val *itype, path, name string, opts ...itypeOption) *itype {
if path != "" {
str = path + "." + name
}
t := &itype{cat: aliasT, val: val, path: path, name: name, str: str}
t := &itype{cat: linkedT, val: val, path: path, name: name, str: str}
for _, opt := range opts {
opt(t)
}
@@ -373,8 +373,8 @@ func structOf(t *itype, fields []structField, opts ...itypeOption) *itype {
}
// genericOf returns a generic type.
func genericOf(val *itype, name string, opts ...itypeOption) *itype {
t := &itype{cat: genericT, name: name, str: name, val: val}
func genericOf(val *itype, name, path string, opts ...itypeOption) *itype {
t := &itype{cat: genericT, name: name, path: path, str: name, val: val}
for _, opt := range opts {
opt(t)
}
@@ -782,11 +782,11 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
}
if sym.kind == varTypeSym {
t = genericOf(sym.typ, n.ident, withNode(n), withScope(sc))
t = genericOf(sym.typ, n.ident, sc.pkgName, withNode(n), withScope(sc))
} else {
t = sym.typ
}
if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT {
if t.incomplete && t.cat == linkedT && t.val != nil && t.val.cat != nilT {
t.incomplete = false
}
if t.incomplete && t.node != n {
@@ -1332,7 +1332,7 @@ func (t *itype) concrete() *itype {
}
func (t *itype) underlying() *itype {
if t.cat == aliasT {
if t.cat == linkedT {
return t.val.underlying()
}
return t
@@ -1340,10 +1340,10 @@ func (t *itype) underlying() *itype {
// 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 {
if t1.cat == linkedT && t1.val == t2 {
return true
}
if t2.cat == aliasT && t2.val == t1 {
if t2.cat == linkedT && t2.val == t1 {
return true
}
return false
@@ -1379,7 +1379,7 @@ func isComplete(t *itype, visited map[string]bool) bool {
visited[name] = true
}
switch t.cat {
case aliasT:
case linkedT:
if t.val != nil && t.val.cat != nilT {
// A type aliased to a partially defined type is considered complete, to allow recursivity.
return true
@@ -1423,7 +1423,7 @@ func (t *itype) assignableTo(o *itype) bool {
return true
}
if t.cat == aliasT && o.cat == aliasT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
if t.cat == linkedT && o.cat == linkedT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
return false
}
@@ -1538,7 +1538,7 @@ func (t *itype) methods() methodSet {
seen[typ] = true
switch typ.cat {
case aliasT:
case linkedT:
for k, v := range getMethods(typ.val) {
res[k] = v
}
@@ -1629,7 +1629,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
return v, err
}
switch t.cat {
case aliasT:
case linkedT:
v, err = t.val.zero()
case arrayT, ptrT, structT, sliceT:
@@ -1647,7 +1647,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
// fieldIndex returns the field index from name in a struct, or -1 if not found.
func (t *itype) fieldIndex(name string) int {
switch t.cat {
case aliasT, ptrT:
case linkedT, ptrT:
return t.val.fieldIndex(name)
}
for i, field := range t.field {
@@ -1682,7 +1682,7 @@ func (t *itype) lookupField(name string) []int {
seen[typ] = true
switch typ.cat {
case aliasT, ptrT:
case linkedT, ptrT:
return lookup(typ.val)
}
if fi := typ.fieldIndex(name); fi >= 0 {
@@ -1691,7 +1691,7 @@ func (t *itype) lookupField(name string) []int {
for i, f := range typ.field {
switch f.typ.cat {
case ptrT, structT, interfaceT, aliasT:
case ptrT, structT, interfaceT, linkedT:
if index2 := lookup(f.typ); len(index2) > 0 {
return append([]int{i}, index2...)
}
@@ -1750,7 +1750,7 @@ func (t *itype) methodCallType() reflect.Type {
}
func (t *itype) resolveAlias() *itype {
for t.cat == aliasT {
for t.cat == linkedT {
t = t.val
}
return t
@@ -1794,7 +1794,7 @@ func (t *itype) lookupMethod2(name string, seen map[*itype]bool) (*node, []int)
}
}
}
if t.cat == aliasT || isInterfaceSrc(t) && t.val != nil {
if t.cat == linkedT || isInterfaceSrc(t) && t.val != nil {
return t.val.lookupMethod2(name, seen)
}
}
@@ -2012,7 +2012,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
return reflect.TypeOf((*generic)(nil)).Elem()
}
switch t.cat {
case aliasT:
case linkedT:
t.rtype = t.val.refType(ctx)
case arrayT:
t.rtype = reflect.ArrayOf(t.length, t.val.refType(ctx))
@@ -2127,7 +2127,7 @@ func (t *itype) frameType() (r reflect.Type) {
panic(err)
}
switch t.cat {
case aliasT:
case linkedT:
r = t.val.frameType()
case arrayT:
r = reflect.ArrayOf(t.length, t.val.frameType())
@@ -2274,7 +2274,7 @@ func isShiftNode(n *node) bool {
// chanElement returns the channel element type.
func chanElement(t *itype) *itype {
switch t.cat {
case aliasT:
case linkedT:
return chanElement(t.val)
case chanT, chanSendT, chanRecvT:
return t.val
@@ -2303,11 +2303,11 @@ func isNamedFuncSrc(t *itype) bool {
}
func isFuncSrc(t *itype) bool {
return t.cat == funcT || (t.cat == aliasT && isFuncSrc(t.val))
return t.cat == funcT || (t.cat == linkedT && isFuncSrc(t.val))
}
func isPtrSrc(t *itype) bool {
return t.cat == ptrT || (t.cat == aliasT && isPtrSrc(t.val))
return t.cat == ptrT || (t.cat == linkedT && isPtrSrc(t.val))
}
func isSendChan(t *itype) bool {
@@ -2324,7 +2324,7 @@ func isArray(t *itype) bool {
}
func isInterfaceSrc(t *itype) bool {
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
return t.cat == interfaceT || (t.cat == linkedT && isInterfaceSrc(t.val))
}
func isInterfaceBin(t *itype) bool {
@@ -2339,7 +2339,7 @@ func isBin(t *itype) bool {
switch t.cat {
case valueT:
return true
case aliasT, ptrT:
case linkedT, ptrT:
return isBin(t.val)
default:
return false
@@ -2352,7 +2352,7 @@ func isStruct(t *itype) bool {
switch t.cat {
case structT:
return true
case aliasT, ptrT:
case linkedT, ptrT:
return isStruct(t.val)
case valueT:
k := t.rtype.Kind()