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