]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/cmd/compile/internal/types2/typestring.go
go/types, types2: introduce _Alias type node
[gostls13.git] / src / cmd / compile / internal / types2 / typestring.go
index 0c93a7e6e483e7af6eb8ea37469d91735c20ce4d..3c2150273ee85c54276cea8d1af37c9b710afb35 100644 (file)
@@ -8,7 +8,10 @@ package types2
 
 import (
        "bytes"
+       "fmt"
+       "sort"
        "strconv"
+       "strings"
        "unicode/utf8"
 )
 
@@ -22,7 +25,6 @@ import (
 //
 // Using a nil Qualifier is equivalent to using (*Package).Path: the
 // object is qualified by the import path, e.g., "encoding/json.Marshal".
-//
 type Qualifier func(*Package) string
 
 // RelativeTo returns a Qualifier that fully qualifies members of
@@ -43,14 +45,8 @@ func RelativeTo(pkg *Package) Qualifier {
 // The Qualifier controls the printing of
 // package-level objects, and may be nil.
 func TypeString(typ Type, qf Qualifier) string {
-       return typeString(typ, qf, false)
-}
-
-func typeString(typ Type, qf Qualifier, debug bool) string {
        var buf bytes.Buffer
-       w := newTypeWriter(&buf, qf)
-       w.debug = debug
-       w.typ(typ)
+       WriteType(&buf, typ, qf)
        return buf.String()
 }
 
@@ -62,28 +58,30 @@ func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
 }
 
 // WriteSignature writes the representation of the signature sig to buf,
-// without a leading "func" keyword.
-// The Qualifier controls the printing of
-// package-level objects, and may be nil.
+// without a leading "func" keyword. The Qualifier controls the printing
+// of package-level objects, and may be nil.
 func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
        newTypeWriter(buf, qf).signature(sig)
 }
 
 type typeWriter struct {
-       buf   *bytes.Buffer
-       seen  map[Type]bool
-       qf    Qualifier
-       ctxt  *Context // if non-nil, we are type hashing
-       debug bool     // if true, write debug annotations
+       buf          *bytes.Buffer
+       seen         map[Type]bool
+       qf           Qualifier
+       ctxt         *Context       // if non-nil, we are type hashing
+       tparams      *TypeParamList // local type parameters
+       paramNames   bool           // if set, write function parameter names, otherwise, write types only
+       tpSubscripts bool           // if set, write type parameter indices as subscripts
+       pkgInfo      bool           // package-annotate first unexported-type field to avoid confusing type description
 }
 
 func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
-       return &typeWriter{buf, make(map[Type]bool), qf, nil, false}
+       return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false}
 }
 
 func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
        assert(ctxt != nil)
-       return &typeWriter{buf, make(map[Type]bool), nil, ctxt, false}
+       return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false, false, false}
 }
 
 func (w *typeWriter) byte(b byte) {
@@ -150,14 +148,29 @@ func (w *typeWriter) typ(typ Type) {
                        if i > 0 {
                                w.byte(';')
                        }
+
+                       // If disambiguating one struct for another, look for the first unexported field.
+                       // Do this first in case of nested structs; tag the first-outermost field.
+                       pkgAnnotate := false
+                       if w.qf == nil && w.pkgInfo && !isExported(f.name) {
+                               // note for embedded types, type name is field name, and "string" etc are lower case hence unexported.
+                               pkgAnnotate = true
+                               w.pkgInfo = false // only tag once
+                       }
+
                        // This doesn't do the right thing for embedded type
                        // aliases where we should print the alias name, not
-                       // the aliased type (see issue #44410).
+                       // the aliased type (see go.dev/issue/44410).
                        if !f.embedded {
                                w.string(f.name)
                                w.byte(' ')
                        }
                        w.typ(f.typ)
+                       if pkgAnnotate {
+                               w.string(" /* package ")
+                               w.string(f.pkg.Path())
+                               w.string(" */ ")
+                       }
                        if tag := t.Tag(i); tag != "" {
                                w.byte(' ')
                                // TODO(gri) If tag contains blanks, replacing them with '#'
@@ -188,7 +201,7 @@ func (w *typeWriter) typ(typ Type) {
                }
                for i, t := range t.terms {
                        if i > 0 {
-                               w.byte('|')
+                               w.string(termSep)
                        }
                        if t.tilde {
                                w.byte('~')
@@ -197,12 +210,18 @@ func (w *typeWriter) typ(typ Type) {
                }
 
        case *Interface:
-               if t == universeAny.Type() && w.ctxt == nil {
-                       // When not hashing, we can try to improve type strings by writing "any"
-                       // for a type that is pointer-identical to universeAny. This logic should
-                       // be deprecated by more robust handling for aliases.
-                       w.string("any")
-                       break
+               if w.ctxt == nil {
+                       if t == universeAny.Type() {
+                               // When not hashing, we can try to improve type strings by writing "any"
+                               // for a type that is pointer-identical to universeAny. This logic should
+                               // be deprecated by more robust handling for aliases.
+                               w.string("any")
+                               break
+                       }
+                       if t == asNamed(universeComparable.Type()).underlying {
+                               w.string("interface{comparable}")
+                               break
+                       }
                }
                if t.implicit {
                        if len(t.methods) == 0 && len(t.embeddeds) == 1 {
@@ -215,20 +234,24 @@ func (w *typeWriter) typ(typ Type) {
                }
                w.string("interface{")
                first := true
-               for _, m := range t.methods {
-                       if !first {
-                               w.byte(';')
+               if w.ctxt != nil {
+                       w.typeSet(t.typeSet())
+               } else {
+                       for _, m := range t.methods {
+                               if !first {
+                                       w.byte(';')
+                               }
+                               first = false
+                               w.string(m.name)
+                               w.signature(m.typ.(*Signature))
                        }
-                       first = false
-                       w.string(m.name)
-                       w.signature(m.typ.(*Signature))
-               }
-               for _, typ := range t.embeddeds {
-                       if !first {
-                               w.byte(';')
+                       for _, typ := range t.embeddeds {
+                               if !first {
+                                       w.byte(';')
+                               }
+                               first = false
+                               w.typ(typ)
                        }
-                       first = false
-                       w.typ(typ)
                }
                w.byte('}')
 
@@ -265,12 +288,16 @@ func (w *typeWriter) typ(typ Type) {
                }
 
        case *Named:
-               w.typePrefix(t)
-               w.typeName(t.obj)
-               if t.targs != nil {
+               // If hashing, write a unique prefix for t to represent its identity, since
+               // named type identity is pointer identity.
+               if w.ctxt != nil {
+                       w.string(strconv.Itoa(w.ctxt.getID(t)))
+               }
+               w.typeName(t.obj) // when hashing written for readability of the hash only
+               if t.inst != nil {
                        // instantiated type
-                       w.typeList(t.targs.list())
-               } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TParams
+                       w.typeList(t.inst.targs.list())
+               } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
                        // parameterized type
                        w.tParamList(t.TypeParams().list())
                }
@@ -280,9 +307,32 @@ func (w *typeWriter) typ(typ Type) {
                        w.error("unnamed type parameter")
                        break
                }
-               w.string(t.obj.name)
-               if w.debug || w.ctxt != nil {
-                       w.string(subscript(t.id))
+               if i := tparamIndex(w.tparams.list(), t); i >= 0 {
+                       // The names of type parameters that are declared by the type being
+                       // hashed are not part of the type identity. Replace them with a
+                       // placeholder indicating their index.
+                       w.string(fmt.Sprintf("$%d", i))
+               } else {
+                       w.string(t.obj.name)
+                       if w.tpSubscripts || w.ctxt != nil {
+                               w.string(subscript(t.id))
+                       }
+                       // If the type parameter name is the same as a predeclared object
+                       // (say int), point out where it is declared to avoid confusing
+                       // error messages. This doesn't need to be super-elegant; we just
+                       // need a clear indication that this is not a predeclared name.
+                       if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil {
+                               w.string(fmt.Sprintf(" /* with %s declared at %s */", t.obj.name, t.obj.Pos()))
+                       }
+               }
+
+       case *_Alias:
+               w.typeName(t.obj)
+               if w.ctxt != nil {
+                       // TODO(gri) do we need to print the alias type name, too?
+                       w.typ(_Unalias(t.obj.typ))
+               } else {
+                       w.string(fmt.Sprintf(" /* = %s */", _Unalias(t.obj.typ)))
                }
 
        default:
@@ -292,12 +342,39 @@ func (w *typeWriter) typ(typ Type) {
        }
 }
 
-// If w.ctxt is non-nil, typePrefix writes a unique prefix for the named type t
-// based on the types already observed by w.ctxt. If w.ctxt is nil, it does
-// nothing.
-func (w *typeWriter) typePrefix(t *Named) {
-       if w.ctxt != nil {
-               w.string(strconv.Itoa(w.ctxt.idForType(t)))
+// typeSet writes a canonical hash for an interface type set.
+func (w *typeWriter) typeSet(s *_TypeSet) {
+       assert(w.ctxt != nil)
+       first := true
+       for _, m := range s.methods {
+               if !first {
+                       w.byte(';')
+               }
+               first = false
+               w.string(m.name)
+               w.signature(m.typ.(*Signature))
+       }
+       switch {
+       case s.terms.isAll():
+               // nothing to do
+       case s.terms.isEmpty():
+               w.string(s.terms.String())
+       default:
+               var termHashes []string
+               for _, term := range s.terms {
+                       // terms are not canonically sorted, so we sort their hashes instead.
+                       var buf bytes.Buffer
+                       if term.tilde {
+                               buf.WriteByte('~')
+                       }
+                       newTypeHasher(&buf, w.ctxt).typ(term.typ)
+                       termHashes = append(termHashes, buf.String())
+               }
+               sort.Strings(termHashes)
+               if !first {
+                       w.byte(';')
+               }
+               w.string(strings.Join(termHashes, "|"))
        }
 }
 
@@ -342,9 +419,7 @@ func (w *typeWriter) tParamList(list []*TypeParam) {
 }
 
 func (w *typeWriter) typeName(obj *TypeName) {
-       if obj.pkg != nil {
-               writePackage(w.buf, obj.pkg, w.qf)
-       }
+       w.string(packagePrefix(obj.pkg, w.qf))
        w.string(obj.name)
 }
 
@@ -356,7 +431,7 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
                                w.byte(',')
                        }
                        // parameter names are ignored for type identity and thus type hashes
-                       if w.ctxt == nil && v.name != "" {
+                       if w.ctxt == nil && v.name != "" && w.paramNames {
                                w.string(v.name)
                                w.byte(' ')
                        }
@@ -385,6 +460,13 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
 
 func (w *typeWriter) signature(sig *Signature) {
        if sig.TypeParams().Len() != 0 {
+               if w.ctxt != nil {
+                       assert(w.tparams == nil)
+                       w.tparams = sig.TypeParams()
+                       defer func() {
+                               w.tparams = nil
+                       }()
+               }
                w.tParamList(sig.TypeParams().list())
        }