]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile/internal/types2: generalize instanceHash to accept any type, rename to...
authorRobert Griesemer <gri@golang.org>
Sat, 28 Aug 2021 03:50:31 +0000 (20:50 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 30 Aug 2021 22:07:43 +0000 (22:07 +0000)
Rename instanceHashing accordingly.

Eventually, this will make it possible to use typeHash to detect
multiple identical types in type switch cases and other places.

Also fix some bugs: When creating a type hash, the name of function
parameters must be ignored because they don't matter for type
identity. And when printing a type name, don't assume its type
is a *Named type; it could be a *Basic type as well.

Finally, use a correctly qualified type string when reporting
a duplicate type error in a type switch case rather than the
(debugging) type string.

Change-Id: Ida3873f6259b51847843b0e2d7e3aa2fcdc3a0c3
Reviewed-on: https://go-review.googlesource.com/c/go/+/345791
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/named.go
src/cmd/compile/internal/types2/stmt.go
src/cmd/compile/internal/types2/subst.go
src/cmd/compile/internal/types2/typestring.go

index 4113d248b8f0fb5a41195ae00c803bf2a177bb95..c882699d1d5c30064996648fdf9b8409b1b1b916 100644 (file)
@@ -125,7 +125,7 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis
 func (check *Checker) instance(pos syntax.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.
index b4074aa3dc2877d387b7a9eb9425b8c7a0645240..a76e69fcf17ef166cfd9fe9e956352f99f133fb3 100644 (file)
@@ -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}
                                }
                        }
index 3e2ac2e29e911b4353433b09c7660f22b4ee6bf8..2673e98c57aeb72f0d1c00464e3941cecf0869b5 100644 (file)
@@ -295,7 +295,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)
                                }
                                var err error_
                                err.errorf(e, "duplicate case %s in type switch", Ts)
@@ -312,6 +312,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 []syntax.Expr, seen map[string]syntax.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 syntax.Stmt) {
        // statements must end with the same top scope as they started with
index 9a4db6fddb64e408db2bf3a9118ced3e591763d9..18a9e393007b1c752dc98c3bd4e018b1d3ae4b95 100644 (file)
@@ -214,7 +214,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)
@@ -253,17 +253,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
index 3b9981089e178f48cce1f29cf4c6228e706c74f2..2110b46498fb159e755e2aa7c3149080b4b47119 100644 (file)
@@ -207,7 +207,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)
@@ -291,7 +291,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("<Named w/o object>")
                return
        }
@@ -300,17 +300,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)
        }
 }
 
@@ -332,7 +333,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(' ')
                        }
@@ -380,8 +382,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
        }