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