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>
func (check *Checker) instance(pos token.Pos, typ Type, targs []Type) Type {
switch t := typ.(type) {
case *Named:
- h := instantiatedHash(t, targs)
+ h := typeHash(t, targs)
if check != nil {
// typ may already have been instantiated with identical type arguments. In
// that case, re-use the existing instance.
// 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
// 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}
}
}
// talk about "case" rather than "type" because of nil case
Ts := "nil"
if T != nil {
- Ts = T.String()
+ Ts = TypeString(T, check.qualifier)
}
check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts)
check.error(other, _DuplicateCase, "\tprevious case") // secondary error, \t indented
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.
func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// statements must end with the same top scope as they started with
}
// 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)
if named, found := subst.typMap[h]; found {
dump(">>> found %s", named)
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
- assert(instanceHashing == 0)
- instanceHashing++
+ assert(typeHashing == 0)
+ typeHashing++
w := newTypeWriter(&buf, nil)
- w.typeName(typ.obj)
- w.typeList(targs)
- instanceHashing--
+ if named, _ := typ.(*Named); named != nil && len(targs) > 0 {
+ // Don't use WriteType because we need to use the provided targs
+ // 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 {
// there should be no instance markers in type hashes
// types. Write them to aid debugging, but don't write
// them when we need an instance hash: whether a type
// 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.typeName(t.obj)
func (w *typeWriter) typeName(obj *TypeName) {
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>")
return
}
}
w.string(obj.name)
- if instanceHashing != 0 {
+ if typeHashing != 0 {
// For local defined types, use the (original!) TypeName's scope
// numbers to disambiguate.
- typ := obj.typ.(*Named)
- // TODO(gri) Figure out why typ.orig != typ.orig.orig sometimes
- // and whether the loop can iterate more than twice.
- // (It seems somehow connected to instance types.)
- for typ.orig != typ {
- typ = typ.orig
+ if typ, _ := obj.typ.(*Named); typ != nil {
+ // TODO(gri) Figure out why typ.orig != typ.orig.orig sometimes
+ // and whether the loop can iterate more than twice.
+ // (It seems somehow connected to instance types.)
+ for typ.orig != typ {
+ typ = typ.orig
+ }
+ w.writeScopeNumbers(typ.obj.parent)
}
- w.writeScopeNumbers(typ.obj.parent)
}
}
if i > 0 {
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.byte(' ')
}
}
w.byte(' ')
- if n == 1 && sig.results.vars[0].name == "" {
- // single unnamed result
+ if n == 1 && (typeHashing != 0 || sig.results.vars[0].name == "") {
+ // single unnamed result (if typeHashing, name must be ignored)
w.typ(sig.results.vars[0].typ)
return
}