mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
go/types: generalize instanceHash to accept any type, rename to typeHash
This is a port of CL 345791 to go/types. Change-Id: I673c22ad8b668f07aae4117555b1c0efb273fb78 Reviewed-on: https://go-review.googlesource.com/c/go/+/346556 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
parent
3c8c9e1e44
commit
0df6df17e1
5 changed files with 79 additions and 24 deletions
|
|
@ -119,7 +119,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, posList
|
||||||
func (check *Checker) instance(pos token.Pos, typ Type, targs []Type) Type {
|
func (check *Checker) instance(pos token.Pos, typ Type, targs []Type) Type {
|
||||||
switch t := typ.(type) {
|
switch t := typ.(type) {
|
||||||
case *Named:
|
case *Named:
|
||||||
h := instantiatedHash(t, targs)
|
h := typeHash(t, targs)
|
||||||
if check != nil {
|
if check != nil {
|
||||||
// typ may already have been instantiated with identical type arguments. In
|
// typ may already have been instantiated with identical type arguments. In
|
||||||
// that case, re-use the existing instance.
|
// that case, re-use the existing instance.
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ func (n *Named) expand(typMap map[string]*Named) *Named {
|
||||||
// type-checking pass. In that case we won't have a pre-existing
|
// type-checking pass. In that case we won't have a pre-existing
|
||||||
// typMap, but don't want to create a duplicate of the current instance
|
// typMap, but don't want to create a duplicate of the current instance
|
||||||
// in the process of expansion.
|
// in the process of expansion.
|
||||||
h := instantiatedHash(n.orig, n.targs.list())
|
h := typeHash(n.orig, n.targs.list())
|
||||||
typMap = map[string]*Named{h: n}
|
typMap = map[string]*Named{h: n}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@ L:
|
||||||
// talk about "case" rather than "type" because of nil case
|
// talk about "case" rather than "type" because of nil case
|
||||||
Ts := "nil"
|
Ts := "nil"
|
||||||
if T != nil {
|
if T != nil {
|
||||||
Ts = T.String()
|
Ts = TypeString(T, check.qualifier)
|
||||||
}
|
}
|
||||||
check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts)
|
check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts)
|
||||||
check.error(other, _DuplicateCase, "\tprevious case") // secondary error, \t indented
|
check.error(other, _DuplicateCase, "\tprevious case") // secondary error, \t indented
|
||||||
|
|
@ -317,6 +317,47 @@ L:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead.
|
||||||
|
// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.)
|
||||||
|
//
|
||||||
|
// func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[string]ast.Expr) (T Type) {
|
||||||
|
// var dummy operand
|
||||||
|
// L:
|
||||||
|
// for _, e := range types {
|
||||||
|
// // The spec allows the value nil instead of a type.
|
||||||
|
// var hash string
|
||||||
|
// if check.isNil(e) {
|
||||||
|
// check.expr(&dummy, e) // run e through expr so we get the usual Info recordings
|
||||||
|
// T = nil
|
||||||
|
// hash = "<nil>" // avoid collision with a type named nil
|
||||||
|
// } else {
|
||||||
|
// T = check.varType(e)
|
||||||
|
// if T == Typ[Invalid] {
|
||||||
|
// continue L
|
||||||
|
// }
|
||||||
|
// hash = typeHash(T, nil)
|
||||||
|
// }
|
||||||
|
// // look for duplicate types
|
||||||
|
// if other := seen[hash]; other != nil {
|
||||||
|
// // talk about "case" rather than "type" because of nil case
|
||||||
|
// Ts := "nil"
|
||||||
|
// if T != nil {
|
||||||
|
// Ts = TypeString(T, check.qualifier)
|
||||||
|
// }
|
||||||
|
// var err error_
|
||||||
|
// err.errorf(e, "duplicate case %s in type switch", Ts)
|
||||||
|
// err.errorf(other, "previous case")
|
||||||
|
// check.report(&err)
|
||||||
|
// continue L
|
||||||
|
// }
|
||||||
|
// seen[hash] = e
|
||||||
|
// if T != nil {
|
||||||
|
// check.typeAssertion(e.Pos(), x, xtyp, T)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
// stmt typechecks statement s.
|
// stmt typechecks statement s.
|
||||||
func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
// statements must end with the same top scope as they started with
|
// statements must end with the same top scope as they started with
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
// before creating a new named type, check if we have this one already
|
// before creating a new named type, check if we have this one already
|
||||||
h := instantiatedHash(t, newTArgs)
|
h := typeHash(t, newTArgs)
|
||||||
dump(">>> new type hash: %s", h)
|
dump(">>> new type hash: %s", h)
|
||||||
if named, found := subst.typMap[h]; found {
|
if named, found := subst.typMap[h]; found {
|
||||||
dump(">>> found %s", named)
|
dump(">>> found %s", named)
|
||||||
|
|
@ -256,17 +256,29 @@ func (subst *subster) typ(typ Type) Type {
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
var instanceHashing = 0
|
var typeHashing = 0
|
||||||
|
|
||||||
func instantiatedHash(typ *Named, targs []Type) string {
|
// typeHash returns a string representation of typ, which can be used as an exact
|
||||||
|
// type hash: types that are identical produce identical string representations.
|
||||||
|
// If typ is a *Named type and targs is not empty, typ is printed as if it were
|
||||||
|
// instantiated with targs.
|
||||||
|
func typeHash(typ Type, targs []Type) string {
|
||||||
|
assert(typ != nil)
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
assert(instanceHashing == 0)
|
assert(typeHashing == 0)
|
||||||
instanceHashing++
|
typeHashing++
|
||||||
w := newTypeWriter(&buf, nil)
|
w := newTypeWriter(&buf, nil)
|
||||||
w.typeName(typ.obj)
|
if named, _ := typ.(*Named); named != nil && len(targs) > 0 {
|
||||||
w.typeList(targs)
|
// Don't use WriteType because we need to use the provided targs
|
||||||
instanceHashing--
|
// and not any targs that might already be with the *Named type.
|
||||||
|
w.typeName(named.obj)
|
||||||
|
w.typeList(targs)
|
||||||
|
} else {
|
||||||
|
assert(targs == nil)
|
||||||
|
w.typ(typ)
|
||||||
|
}
|
||||||
|
typeHashing--
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
// there should be no instance markers in type hashes
|
// there should be no instance markers in type hashes
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ func (w *typeWriter) typ(typ Type) {
|
||||||
// types. Write them to aid debugging, but don't write
|
// types. Write them to aid debugging, but don't write
|
||||||
// them when we need an instance hash: whether a type
|
// them when we need an instance hash: whether a type
|
||||||
// is fully expanded or not doesn't matter for identity.
|
// is fully expanded or not doesn't matter for identity.
|
||||||
if instanceHashing == 0 && t.instPos != nil {
|
if typeHashing == 0 && t.instPos != nil {
|
||||||
w.byte(instanceMarker)
|
w.byte(instanceMarker)
|
||||||
}
|
}
|
||||||
w.typeName(t.obj)
|
w.typeName(t.obj)
|
||||||
|
|
@ -292,7 +292,7 @@ func (w *typeWriter) tParamList(list []*TypeParam) {
|
||||||
|
|
||||||
func (w *typeWriter) typeName(obj *TypeName) {
|
func (w *typeWriter) typeName(obj *TypeName) {
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
assert(instanceHashing == 0) // we need an object for instance hashing
|
assert(typeHashing == 0) // we need an object for type hashing
|
||||||
w.string("<Named w/o object>")
|
w.string("<Named w/o object>")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -301,17 +301,18 @@ func (w *typeWriter) typeName(obj *TypeName) {
|
||||||
}
|
}
|
||||||
w.string(obj.name)
|
w.string(obj.name)
|
||||||
|
|
||||||
if instanceHashing != 0 {
|
if typeHashing != 0 {
|
||||||
// For local defined types, use the (original!) TypeName's scope
|
// For local defined types, use the (original!) TypeName's scope
|
||||||
// numbers to disambiguate.
|
// numbers to disambiguate.
|
||||||
typ := obj.typ.(*Named)
|
if typ, _ := obj.typ.(*Named); typ != nil {
|
||||||
// TODO(gri) Figure out why typ.orig != typ.orig.orig sometimes
|
// TODO(gri) Figure out why typ.orig != typ.orig.orig sometimes
|
||||||
// and whether the loop can iterate more than twice.
|
// and whether the loop can iterate more than twice.
|
||||||
// (It seems somehow connected to instance types.)
|
// (It seems somehow connected to instance types.)
|
||||||
for typ.orig != typ {
|
for typ.orig != typ {
|
||||||
typ = typ.orig
|
typ = typ.orig
|
||||||
|
}
|
||||||
|
w.writeScopeNumbers(typ.obj.parent)
|
||||||
}
|
}
|
||||||
w.writeScopeNumbers(typ.obj.parent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,7 +334,8 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.string(", ")
|
w.string(", ")
|
||||||
}
|
}
|
||||||
if v.name != "" {
|
// parameter names are ignored for type identity and thus type hashes
|
||||||
|
if typeHashing == 0 && v.name != "" {
|
||||||
w.string(v.name)
|
w.string(v.name)
|
||||||
w.byte(' ')
|
w.byte(' ')
|
||||||
}
|
}
|
||||||
|
|
@ -381,8 +383,8 @@ func (w *typeWriter) signature(sig *Signature) {
|
||||||
}
|
}
|
||||||
|
|
||||||
w.byte(' ')
|
w.byte(' ')
|
||||||
if n == 1 && sig.results.vars[0].name == "" {
|
if n == 1 && (typeHashing != 0 || sig.results.vars[0].name == "") {
|
||||||
// single unnamed result
|
// single unnamed result (if typeHashing, name must be ignored)
|
||||||
w.typ(sig.results.vars[0].typ)
|
w.typ(sig.results.vars[0].typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue