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