From: Robert Findley Date: Tue, 31 Aug 2021 22:12:15 +0000 (-0400) Subject: go/types: generalize instanceHash to accept any type, rename to typeHash X-Git-Tag: go1.18beta1~1543 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0df6df17e12d4cf8c2ed7c68ce8841a18f739f63;p=gostls13.git 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 Run-TryBot: Robert Findley Reviewed-by: Robert Griesemer TryBot-Result: Go Bot --- diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index fe4904f63a..e89f645c8f 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -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 { 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. diff --git a/src/go/types/named.go b/src/go/types/named.go index 4ee76eb835..4540956658 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -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 // 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} } } diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index 5ba57041bd..e74862afef 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -302,7 +302,7 @@ L: // 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 @@ -317,6 +317,47 @@ L: 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 = "" // 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 diff --git a/src/go/types/subst.go b/src/go/types/subst.go index f1bdbc34bd..c811f8a4df 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -217,7 +217,7 @@ func (subst *subster) typ(typ Type) Type { } // 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) @@ -256,17 +256,29 @@ func (subst *subster) typ(typ Type) Type { 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 diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index 46e749c84a..3841227044 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -208,7 +208,7 @@ func (w *typeWriter) typ(typ Type) { // 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) @@ -292,7 +292,7 @@ func (w *typeWriter) tParamList(list []*TypeParam) { 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("") return } @@ -301,17 +301,18 @@ func (w *typeWriter) typeName(obj *TypeName) { } 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) } } @@ -333,7 +334,8 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) { 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(' ') } @@ -381,8 +383,8 @@ func (w *typeWriter) signature(sig *Signature) { } 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 }