]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types2/typestring.go
go/types, types2: implement Alias proposal (export API)
[gostls13.git] / src / cmd / compile / internal / types2 / typestring.go
1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // This file implements printing of types.
6
7 package types2
8
9 import (
10         "bytes"
11         "fmt"
12         "sort"
13         "strconv"
14         "strings"
15         "unicode/utf8"
16 )
17
18 // A Qualifier controls how named package-level objects are printed in
19 // calls to TypeString, ObjectString, and SelectionString.
20 //
21 // These three formatting routines call the Qualifier for each
22 // package-level object O, and if the Qualifier returns a non-empty
23 // string p, the object is printed in the form p.O.
24 // If it returns an empty string, only the object name O is printed.
25 //
26 // Using a nil Qualifier is equivalent to using (*Package).Path: the
27 // object is qualified by the import path, e.g., "encoding/json.Marshal".
28 type Qualifier func(*Package) string
29
30 // RelativeTo returns a Qualifier that fully qualifies members of
31 // all packages other than pkg.
32 func RelativeTo(pkg *Package) Qualifier {
33         if pkg == nil {
34                 return nil
35         }
36         return func(other *Package) string {
37                 if pkg == other {
38                         return "" // same package; unqualified
39                 }
40                 return other.Path()
41         }
42 }
43
44 // TypeString returns the string representation of typ.
45 // The Qualifier controls the printing of
46 // package-level objects, and may be nil.
47 func TypeString(typ Type, qf Qualifier) string {
48         var buf bytes.Buffer
49         WriteType(&buf, typ, qf)
50         return buf.String()
51 }
52
53 // WriteType writes the string representation of typ to buf.
54 // The Qualifier controls the printing of
55 // package-level objects, and may be nil.
56 func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
57         newTypeWriter(buf, qf).typ(typ)
58 }
59
60 // WriteSignature writes the representation of the signature sig to buf,
61 // without a leading "func" keyword. The Qualifier controls the printing
62 // of package-level objects, and may be nil.
63 func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
64         newTypeWriter(buf, qf).signature(sig)
65 }
66
67 type typeWriter struct {
68         buf          *bytes.Buffer
69         seen         map[Type]bool
70         qf           Qualifier
71         ctxt         *Context       // if non-nil, we are type hashing
72         tparams      *TypeParamList // local type parameters
73         paramNames   bool           // if set, write function parameter names, otherwise, write types only
74         tpSubscripts bool           // if set, write type parameter indices as subscripts
75         pkgInfo      bool           // package-annotate first unexported-type field to avoid confusing type description
76 }
77
78 func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
79         return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false}
80 }
81
82 func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
83         assert(ctxt != nil)
84         return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false, false, false}
85 }
86
87 func (w *typeWriter) byte(b byte) {
88         if w.ctxt != nil {
89                 if b == ' ' {
90                         b = '#'
91                 }
92                 w.buf.WriteByte(b)
93                 return
94         }
95         w.buf.WriteByte(b)
96         if b == ',' || b == ';' {
97                 w.buf.WriteByte(' ')
98         }
99 }
100
101 func (w *typeWriter) string(s string) {
102         w.buf.WriteString(s)
103 }
104
105 func (w *typeWriter) error(msg string) {
106         if w.ctxt != nil {
107                 panic(msg)
108         }
109         w.buf.WriteString("<" + msg + ">")
110 }
111
112 func (w *typeWriter) typ(typ Type) {
113         if w.seen[typ] {
114                 w.error("cycle to " + goTypeName(typ))
115                 return
116         }
117         w.seen[typ] = true
118         defer delete(w.seen, typ)
119
120         switch t := typ.(type) {
121         case nil:
122                 w.error("nil")
123
124         case *Basic:
125                 // exported basic types go into package unsafe
126                 // (currently this is just unsafe.Pointer)
127                 if isExported(t.name) {
128                         if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
129                                 w.typeName(obj)
130                                 break
131                         }
132                 }
133                 w.string(t.name)
134
135         case *Array:
136                 w.byte('[')
137                 w.string(strconv.FormatInt(t.len, 10))
138                 w.byte(']')
139                 w.typ(t.elem)
140
141         case *Slice:
142                 w.string("[]")
143                 w.typ(t.elem)
144
145         case *Struct:
146                 w.string("struct{")
147                 for i, f := range t.fields {
148                         if i > 0 {
149                                 w.byte(';')
150                         }
151
152                         // If disambiguating one struct for another, look for the first unexported field.
153                         // Do this first in case of nested structs; tag the first-outermost field.
154                         pkgAnnotate := false
155                         if w.qf == nil && w.pkgInfo && !isExported(f.name) {
156                                 // note for embedded types, type name is field name, and "string" etc are lower case hence unexported.
157                                 pkgAnnotate = true
158                                 w.pkgInfo = false // only tag once
159                         }
160
161                         // This doesn't do the right thing for embedded type
162                         // aliases where we should print the alias name, not
163                         // the aliased type (see go.dev/issue/44410).
164                         if !f.embedded {
165                                 w.string(f.name)
166                                 w.byte(' ')
167                         }
168                         w.typ(f.typ)
169                         if pkgAnnotate {
170                                 w.string(" /* package ")
171                                 w.string(f.pkg.Path())
172                                 w.string(" */ ")
173                         }
174                         if tag := t.Tag(i); tag != "" {
175                                 w.byte(' ')
176                                 // TODO(gri) If tag contains blanks, replacing them with '#'
177                                 //           in Context.TypeHash may produce another tag
178                                 //           accidentally.
179                                 w.string(strconv.Quote(tag))
180                         }
181                 }
182                 w.byte('}')
183
184         case *Pointer:
185                 w.byte('*')
186                 w.typ(t.base)
187
188         case *Tuple:
189                 w.tuple(t, false)
190
191         case *Signature:
192                 w.string("func")
193                 w.signature(t)
194
195         case *Union:
196                 // Unions only appear as (syntactic) embedded elements
197                 // in interfaces and syntactically cannot be empty.
198                 if t.Len() == 0 {
199                         w.error("empty union")
200                         break
201                 }
202                 for i, t := range t.terms {
203                         if i > 0 {
204                                 w.string(termSep)
205                         }
206                         if t.tilde {
207                                 w.byte('~')
208                         }
209                         w.typ(t.typ)
210                 }
211
212         case *Interface:
213                 if w.ctxt == nil {
214                         if t == universeAny.Type() {
215                                 // When not hashing, we can try to improve type strings by writing "any"
216                                 // for a type that is pointer-identical to universeAny. This logic should
217                                 // be deprecated by more robust handling for aliases.
218                                 w.string("any")
219                                 break
220                         }
221                         if t == asNamed(universeComparable.Type()).underlying {
222                                 w.string("interface{comparable}")
223                                 break
224                         }
225                 }
226                 if t.implicit {
227                         if len(t.methods) == 0 && len(t.embeddeds) == 1 {
228                                 w.typ(t.embeddeds[0])
229                                 break
230                         }
231                         // Something's wrong with the implicit interface.
232                         // Print it as such and continue.
233                         w.string("/* implicit */ ")
234                 }
235                 w.string("interface{")
236                 first := true
237                 if w.ctxt != nil {
238                         w.typeSet(t.typeSet())
239                 } else {
240                         for _, m := range t.methods {
241                                 if !first {
242                                         w.byte(';')
243                                 }
244                                 first = false
245                                 w.string(m.name)
246                                 w.signature(m.typ.(*Signature))
247                         }
248                         for _, typ := range t.embeddeds {
249                                 if !first {
250                                         w.byte(';')
251                                 }
252                                 first = false
253                                 w.typ(typ)
254                         }
255                 }
256                 w.byte('}')
257
258         case *Map:
259                 w.string("map[")
260                 w.typ(t.key)
261                 w.byte(']')
262                 w.typ(t.elem)
263
264         case *Chan:
265                 var s string
266                 var parens bool
267                 switch t.dir {
268                 case SendRecv:
269                         s = "chan "
270                         // chan (<-chan T) requires parentheses
271                         if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
272                                 parens = true
273                         }
274                 case SendOnly:
275                         s = "chan<- "
276                 case RecvOnly:
277                         s = "<-chan "
278                 default:
279                         w.error("unknown channel direction")
280                 }
281                 w.string(s)
282                 if parens {
283                         w.byte('(')
284                 }
285                 w.typ(t.elem)
286                 if parens {
287                         w.byte(')')
288                 }
289
290         case *Named:
291                 // If hashing, write a unique prefix for t to represent its identity, since
292                 // named type identity is pointer identity.
293                 if w.ctxt != nil {
294                         w.string(strconv.Itoa(w.ctxt.getID(t)))
295                 }
296                 w.typeName(t.obj) // when hashing written for readability of the hash only
297                 if t.inst != nil {
298                         // instantiated type
299                         w.typeList(t.inst.targs.list())
300                 } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
301                         // parameterized type
302                         w.tParamList(t.TypeParams().list())
303                 }
304
305         case *TypeParam:
306                 if t.obj == nil {
307                         w.error("unnamed type parameter")
308                         break
309                 }
310                 if i := tparamIndex(w.tparams.list(), t); i >= 0 {
311                         // The names of type parameters that are declared by the type being
312                         // hashed are not part of the type identity. Replace them with a
313                         // placeholder indicating their index.
314                         w.string(fmt.Sprintf("$%d", i))
315                 } else {
316                         w.string(t.obj.name)
317                         if w.tpSubscripts || w.ctxt != nil {
318                                 w.string(subscript(t.id))
319                         }
320                         // If the type parameter name is the same as a predeclared object
321                         // (say int), point out where it is declared to avoid confusing
322                         // error messages. This doesn't need to be super-elegant; we just
323                         // need a clear indication that this is not a predeclared name.
324                         if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil {
325                                 w.string(fmt.Sprintf(" /* with %s declared at %s */", t.obj.name, t.obj.Pos()))
326                         }
327                 }
328
329         case *Alias:
330                 w.typeName(t.obj)
331                 if w.ctxt != nil {
332                         // TODO(gri) do we need to print the alias type name, too?
333                         w.typ(Unalias(t.obj.typ))
334                 } else {
335                         w.string(fmt.Sprintf(" /* = %s */", Unalias(t.obj.typ)))
336                 }
337
338         default:
339                 // For externally defined implementations of Type.
340                 // Note: In this case cycles won't be caught.
341                 w.string(t.String())
342         }
343 }
344
345 // typeSet writes a canonical hash for an interface type set.
346 func (w *typeWriter) typeSet(s *_TypeSet) {
347         assert(w.ctxt != nil)
348         first := true
349         for _, m := range s.methods {
350                 if !first {
351                         w.byte(';')
352                 }
353                 first = false
354                 w.string(m.name)
355                 w.signature(m.typ.(*Signature))
356         }
357         switch {
358         case s.terms.isAll():
359                 // nothing to do
360         case s.terms.isEmpty():
361                 w.string(s.terms.String())
362         default:
363                 var termHashes []string
364                 for _, term := range s.terms {
365                         // terms are not canonically sorted, so we sort their hashes instead.
366                         var buf bytes.Buffer
367                         if term.tilde {
368                                 buf.WriteByte('~')
369                         }
370                         newTypeHasher(&buf, w.ctxt).typ(term.typ)
371                         termHashes = append(termHashes, buf.String())
372                 }
373                 sort.Strings(termHashes)
374                 if !first {
375                         w.byte(';')
376                 }
377                 w.string(strings.Join(termHashes, "|"))
378         }
379 }
380
381 func (w *typeWriter) typeList(list []Type) {
382         w.byte('[')
383         for i, typ := range list {
384                 if i > 0 {
385                         w.byte(',')
386                 }
387                 w.typ(typ)
388         }
389         w.byte(']')
390 }
391
392 func (w *typeWriter) tParamList(list []*TypeParam) {
393         w.byte('[')
394         var prev Type
395         for i, tpar := range list {
396                 // Determine the type parameter and its constraint.
397                 // list is expected to hold type parameter names,
398                 // but don't crash if that's not the case.
399                 if tpar == nil {
400                         w.error("nil type parameter")
401                         continue
402                 }
403                 if i > 0 {
404                         if tpar.bound != prev {
405                                 // bound changed - write previous one before advancing
406                                 w.byte(' ')
407                                 w.typ(prev)
408                         }
409                         w.byte(',')
410                 }
411                 prev = tpar.bound
412                 w.typ(tpar)
413         }
414         if prev != nil {
415                 w.byte(' ')
416                 w.typ(prev)
417         }
418         w.byte(']')
419 }
420
421 func (w *typeWriter) typeName(obj *TypeName) {
422         w.string(packagePrefix(obj.pkg, w.qf))
423         w.string(obj.name)
424 }
425
426 func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
427         w.byte('(')
428         if tup != nil {
429                 for i, v := range tup.vars {
430                         if i > 0 {
431                                 w.byte(',')
432                         }
433                         // parameter names are ignored for type identity and thus type hashes
434                         if w.ctxt == nil && v.name != "" && w.paramNames {
435                                 w.string(v.name)
436                                 w.byte(' ')
437                         }
438                         typ := v.typ
439                         if variadic && i == len(tup.vars)-1 {
440                                 if s, ok := typ.(*Slice); ok {
441                                         w.string("...")
442                                         typ = s.elem
443                                 } else {
444                                         // special case:
445                                         // append(s, "foo"...) leads to signature func([]byte, string...)
446                                         if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
447                                                 w.error("expected string type")
448                                                 continue
449                                         }
450                                         w.typ(typ)
451                                         w.string("...")
452                                         continue
453                                 }
454                         }
455                         w.typ(typ)
456                 }
457         }
458         w.byte(')')
459 }
460
461 func (w *typeWriter) signature(sig *Signature) {
462         if sig.TypeParams().Len() != 0 {
463                 if w.ctxt != nil {
464                         assert(w.tparams == nil)
465                         w.tparams = sig.TypeParams()
466                         defer func() {
467                                 w.tparams = nil
468                         }()
469                 }
470                 w.tParamList(sig.TypeParams().list())
471         }
472
473         w.tuple(sig.params, sig.variadic)
474
475         n := sig.results.Len()
476         if n == 0 {
477                 // no result
478                 return
479         }
480
481         w.byte(' ')
482         if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
483                 // single unnamed result (if type hashing, name must be ignored)
484                 w.typ(sig.results.vars[0].typ)
485                 return
486         }
487
488         // multiple or named result(s)
489         w.tuple(sig.results, false)
490 }
491
492 // subscript returns the decimal (utf8) representation of x using subscript digits.
493 func subscript(x uint64) string {
494         const w = len("₀") // all digits 0...9 have the same utf8 width
495         var buf [32 * w]byte
496         i := len(buf)
497         for {
498                 i -= w
499                 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
500                 x /= 10
501                 if x == 0 {
502                         break
503                 }
504         }
505         return string(buf[i:])
506 }