blob: 3198a1f53c8674c6234d6d2fcaa1eb402718a36f [file] [log] [blame]
Dan Willemsencc753b72021-08-31 13:25:42 -07001// Copyright 2009 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
5package types
6
7import (
8 "bytes"
9 "crypto/md5"
10 "encoding/binary"
11 "fmt"
12 "go/constant"
13 "strconv"
14 "strings"
15 "sync"
16
17 "cmd/compile/internal/base"
18)
19
20// BuiltinPkg is a fake package that declares the universe block.
21var BuiltinPkg *Pkg
22
23// LocalPkg is the package being compiled.
24var LocalPkg *Pkg
25
Dan Willemsenbc60c3c2021-12-15 01:09:00 -080026// UnsafePkg is package unsafe.
27var UnsafePkg *Pkg
28
Dan Willemsencc753b72021-08-31 13:25:42 -070029// BlankSym is the blank (_) symbol.
30var BlankSym *Sym
31
32// OrigSym returns the original symbol written by the user.
33func OrigSym(s *Sym) *Sym {
34 if s == nil {
35 return nil
36 }
37
38 if len(s.Name) > 1 && s.Name[0] == '~' {
39 switch s.Name[1] {
40 case 'r': // originally an unnamed result
41 return nil
42 case 'b': // originally the blank identifier _
43 // TODO(mdempsky): Does s.Pkg matter here?
44 return BlankSym
45 }
46 return s
47 }
48
49 if strings.HasPrefix(s.Name, ".anon") {
50 // originally an unnamed or _ name (see subr.go: NewFuncParams)
51 return nil
52 }
53
54 return s
55}
56
57// numImport tracks how often a package with a given name is imported.
58// It is used to provide a better error message (by using the package
59// path to disambiguate) if a package that appears multiple times with
60// the same name appears in an error message.
61var NumImport = make(map[string]int)
62
63// fmtMode represents the kind of printing being done.
64// The default is regular Go syntax (fmtGo).
65// fmtDebug is like fmtGo but for debugging dumps and prints the type kind too.
66// fmtTypeID and fmtTypeIDName are for generating various unique representations
Dan Willemsenbc60c3c2021-12-15 01:09:00 -080067// of types used in hashes, the linker, and function/method instantiations.
Dan Willemsencc753b72021-08-31 13:25:42 -070068type fmtMode int
69
70const (
71 fmtGo fmtMode = iota
72 fmtDebug
73 fmtTypeID
74 fmtTypeIDName
75)
76
77// Sym
78
79// Format implements formatting for a Sym.
80// The valid formats are:
81//
82// %v Go syntax: Name for symbols in the local package, PkgName.Name for imported symbols.
83// %+v Debug syntax: always include PkgName. prefix even for local names.
84// %S Short syntax: Name only, no matter what.
85//
86func (s *Sym) Format(f fmt.State, verb rune) {
87 mode := fmtGo
88 switch verb {
89 case 'v', 'S':
90 if verb == 'v' && f.Flag('+') {
91 mode = fmtDebug
92 }
93 fmt.Fprint(f, sconv(s, verb, mode))
94
95 default:
96 fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s)
97 }
98}
99
100func (s *Sym) String() string {
101 return sconv(s, 0, fmtGo)
102}
103
104// See #16897 for details about performance implications
105// before changing the implementation of sconv.
106func sconv(s *Sym, verb rune, mode fmtMode) string {
107 if verb == 'L' {
108 panic("linksymfmt")
109 }
110
111 if s == nil {
112 return "<S>"
113 }
114
115 q := pkgqual(s.Pkg, verb, mode)
116 if q == "" {
117 return s.Name
118 }
119
120 buf := fmtBufferPool.Get().(*bytes.Buffer)
121 buf.Reset()
122 defer fmtBufferPool.Put(buf)
123
124 buf.WriteString(q)
125 buf.WriteByte('.')
126 buf.WriteString(s.Name)
127 return InternString(buf.Bytes())
128}
129
130func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
131 if verb == 'L' {
132 panic("linksymfmt")
133 }
134 if s == nil {
135 b.WriteString("<S>")
136 return
137 }
138
139 symfmt(b, s, verb, mode)
140}
141
142func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800143 name := s.Name
Dan Willemsencc753b72021-08-31 13:25:42 -0700144 if q := pkgqual(s.Pkg, verb, mode); q != "" {
145 b.WriteString(q)
146 b.WriteByte('.')
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800147 if mode == fmtTypeIDName {
148 // If name is a generic instantiation, it might have local package placeholders
149 // in it. Replace those placeholders with the package name. See issue 49547.
150 name = strings.Replace(name, LocalPkg.Prefix, q, -1)
151 }
Dan Willemsencc753b72021-08-31 13:25:42 -0700152 }
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800153 b.WriteString(name)
Dan Willemsencc753b72021-08-31 13:25:42 -0700154}
155
156// pkgqual returns the qualifier that should be used for printing
157// symbols from the given package in the given mode.
158// If it returns the empty string, no qualification is needed.
159func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string {
160 if verb != 'S' {
161 switch mode {
162 case fmtGo: // This is for the user
163 if pkg == BuiltinPkg || pkg == LocalPkg {
164 return ""
165 }
166
167 // If the name was used by multiple packages, display the full path,
168 if pkg.Name != "" && NumImport[pkg.Name] > 1 {
169 return strconv.Quote(pkg.Path)
170 }
171 return pkg.Name
172
173 case fmtDebug:
174 return pkg.Name
175
176 case fmtTypeIDName:
177 // dcommontype, typehash
178 return pkg.Name
179
180 case fmtTypeID:
181 // (methodsym), typesym, weaksym
182 return pkg.Prefix
183 }
184 }
185
186 return ""
187}
188
189// Type
190
191var BasicTypeNames = []string{
192 TINT: "int",
193 TUINT: "uint",
194 TINT8: "int8",
195 TUINT8: "uint8",
196 TINT16: "int16",
197 TUINT16: "uint16",
198 TINT32: "int32",
199 TUINT32: "uint32",
200 TINT64: "int64",
201 TUINT64: "uint64",
202 TUINTPTR: "uintptr",
203 TFLOAT32: "float32",
204 TFLOAT64: "float64",
205 TCOMPLEX64: "complex64",
206 TCOMPLEX128: "complex128",
207 TBOOL: "bool",
208 TANY: "any",
209 TSTRING: "string",
210 TNIL: "nil",
211 TIDEAL: "untyped number",
212 TBLANK: "blank",
213}
214
215var fmtBufferPool = sync.Pool{
216 New: func() interface{} {
217 return new(bytes.Buffer)
218 },
219}
220
221// Format implements formatting for a Type.
222// The valid formats are:
223//
224// %v Go syntax
225// %+v Debug syntax: Go syntax with a KIND- prefix for all but builtins.
226// %L Go syntax for underlying type if t is named
227// %S short Go syntax: drop leading "func" in function type
228// %-S special case for method receiver symbol
229//
230func (t *Type) Format(s fmt.State, verb rune) {
231 mode := fmtGo
232 switch verb {
233 case 'v', 'S', 'L':
234 if verb == 'v' && s.Flag('+') { // %+v is debug format
235 mode = fmtDebug
236 }
237 if verb == 'S' && s.Flag('-') { // %-S is special case for receiver - short typeid format
238 mode = fmtTypeID
239 }
240 fmt.Fprint(s, tconv(t, verb, mode))
241 default:
242 fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t)
243 }
244}
245
246// String returns the Go syntax for the type t.
247func (t *Type) String() string {
248 return tconv(t, 0, fmtGo)
249}
250
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800251// LinkString returns an unexpanded string description of t, suitable
252// for use in link symbols. "Unexpanded" here means that the
253// description uses `"".` to qualify identifiers from the current
254// package, and "expansion" refers to the renaming step performed by
255// the linker to replace these qualifiers with proper `path/to/pkg.`
256// qualifiers.
257//
258// After expansion, the description corresponds to type identity. That
259// is, for any pair of types t1 and t2, Identical(t1, t2) and
260// expand(t1.LinkString()) == expand(t2.LinkString()) report the same
261// value.
262//
263// Within a single compilation unit, LinkString always returns the
264// same unexpanded description for identical types. Thus it's safe to
265// use as a map key to implement a type-identity-keyed map. However,
266// make sure all LinkString calls used for this purpose happen within
267// the same compile process; the string keys are not stable across
268// multiple processes.
269func (t *Type) LinkString() string {
Dan Willemsencc753b72021-08-31 13:25:42 -0700270 return tconv(t, 0, fmtTypeID)
271}
272
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800273// NameString generates a user-readable, mostly unique string
274// description of t. NameString always returns the same description
275// for identical types, even across compilation units.
276//
277// NameString qualifies identifiers by package name, so it has
278// collisions when different packages share the same names and
279// identifiers. It also does not distinguish function-scope defined
280// types from package-scoped defined types or from each other.
281func (t *Type) NameString() string {
Dan Willemsencc753b72021-08-31 13:25:42 -0700282 return tconv(t, 0, fmtTypeIDName)
283}
284
285func tconv(t *Type, verb rune, mode fmtMode) string {
286 buf := fmtBufferPool.Get().(*bytes.Buffer)
287 buf.Reset()
288 defer fmtBufferPool.Put(buf)
289
290 tconv2(buf, t, verb, mode, nil)
291 return InternString(buf.Bytes())
292}
293
294// tconv2 writes a string representation of t to b.
295// flag and mode control exactly what is printed.
296// Any types x that are already in the visited map get printed as @%d where %d=visited[x].
297// See #16897 before changing the implementation of tconv.
298func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type]int) {
299 if off, ok := visited[t]; ok {
300 // We've seen this type before, so we're trying to print it recursively.
301 // Print a reference to it instead.
302 fmt.Fprintf(b, "@%d", off)
303 return
304 }
305 if t == nil {
306 b.WriteString("<T>")
307 return
308 }
309 if t.Kind() == TSSA {
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800310 b.WriteString(t.extra.(string))
Dan Willemsencc753b72021-08-31 13:25:42 -0700311 return
312 }
313 if t.Kind() == TTUPLE {
314 b.WriteString(t.FieldType(0).String())
315 b.WriteByte(',')
316 b.WriteString(t.FieldType(1).String())
317 return
318 }
319
320 if t.Kind() == TRESULTS {
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800321 tys := t.extra.(*Results).Types
Dan Willemsencc753b72021-08-31 13:25:42 -0700322 for i, et := range tys {
323 if i > 0 {
324 b.WriteByte(',')
325 }
326 b.WriteString(et.String())
327 }
328 return
329 }
330
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800331 if t == AnyType || t == ByteType || t == RuneType {
332 // in %-T mode collapse predeclared aliases with their originals.
Dan Willemsencc753b72021-08-31 13:25:42 -0700333 switch mode {
334 case fmtTypeIDName, fmtTypeID:
335 t = Types[t.Kind()]
336 default:
337 sconv2(b, t.Sym(), 'S', mode)
338 return
339 }
340 }
341 if t == ErrorType {
342 b.WriteString("error")
343 return
344 }
345
346 // Unless the 'L' flag was specified, if the type has a name, just print that name.
347 if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] {
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800348 // Default to 'v' if verb is invalid.
349 if verb != 'S' {
350 verb = 'v'
Dan Willemsencc753b72021-08-31 13:25:42 -0700351 }
352
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800353 // In unified IR, function-scope defined types will have a ·N
354 // suffix embedded directly in their Name. Trim this off for
355 // non-fmtTypeID modes.
356 sym := t.Sym()
357 if mode != fmtTypeID {
358 i := len(sym.Name)
359 for i > 0 && sym.Name[i-1] >= '0' && sym.Name[i-1] <= '9' {
360 i--
361 }
362 const dot = "·"
363 if i >= len(dot) && sym.Name[i-len(dot):i] == dot {
364 sym = &Sym{Pkg: sym.Pkg, Name: sym.Name[:i-len(dot)]}
365 }
366 }
367 sconv2(b, sym, verb, mode)
368
369 // TODO(mdempsky): Investigate including Vargen in fmtTypeIDName
370 // output too. It seems like it should, but that mode is currently
371 // used in string representation used by reflection, which is
372 // user-visible and doesn't expect this.
373 if mode == fmtTypeID && t.vargen != 0 {
374 fmt.Fprintf(b, "·%d", t.vargen)
375 }
Dan Willemsencc753b72021-08-31 13:25:42 -0700376 return
377 }
378
379 if int(t.Kind()) < len(BasicTypeNames) && BasicTypeNames[t.Kind()] != "" {
380 var name string
381 switch t {
382 case UntypedBool:
383 name = "untyped bool"
384 case UntypedString:
385 name = "untyped string"
386 case UntypedInt:
387 name = "untyped int"
388 case UntypedRune:
389 name = "untyped rune"
390 case UntypedFloat:
391 name = "untyped float"
392 case UntypedComplex:
393 name = "untyped complex"
394 default:
395 name = BasicTypeNames[t.Kind()]
396 }
397 b.WriteString(name)
398 return
399 }
400
401 if mode == fmtDebug {
402 b.WriteString(t.Kind().String())
403 b.WriteByte('-')
404 tconv2(b, t, 'v', fmtGo, visited)
405 return
406 }
407
408 // At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't
409 // try to print it recursively.
410 // We record the offset in the result buffer where the type's text starts. This offset serves as a reference
411 // point for any later references to the same type.
412 // Note that we remove the type from the visited map as soon as the recursive call is done.
413 // This prevents encoding types like map[*int]*int as map[*int]@4. (That encoding would work,
414 // but I'd like to use the @ notation only when strictly necessary.)
415 if visited == nil {
416 visited = map[*Type]int{}
417 }
418 visited[t] = b.Len()
419 defer delete(visited, t)
420
421 switch t.Kind() {
422 case TPTR:
423 b.WriteByte('*')
424 switch mode {
425 case fmtTypeID, fmtTypeIDName:
426 if verb == 'S' {
427 tconv2(b, t.Elem(), 'S', mode, visited)
428 return
429 }
430 }
431 tconv2(b, t.Elem(), 'v', mode, visited)
432
433 case TARRAY:
434 b.WriteByte('[')
435 b.WriteString(strconv.FormatInt(t.NumElem(), 10))
436 b.WriteByte(']')
437 tconv2(b, t.Elem(), 0, mode, visited)
438
439 case TSLICE:
440 b.WriteString("[]")
441 tconv2(b, t.Elem(), 0, mode, visited)
442
443 case TCHAN:
444 switch t.ChanDir() {
445 case Crecv:
446 b.WriteString("<-chan ")
447 tconv2(b, t.Elem(), 0, mode, visited)
448 case Csend:
449 b.WriteString("chan<- ")
450 tconv2(b, t.Elem(), 0, mode, visited)
451 default:
452 b.WriteString("chan ")
453 if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym() == nil && t.Elem().ChanDir() == Crecv {
454 b.WriteByte('(')
455 tconv2(b, t.Elem(), 0, mode, visited)
456 b.WriteByte(')')
457 } else {
458 tconv2(b, t.Elem(), 0, mode, visited)
459 }
460 }
461
462 case TMAP:
463 b.WriteString("map[")
464 tconv2(b, t.Key(), 0, mode, visited)
465 b.WriteByte(']')
466 tconv2(b, t.Elem(), 0, mode, visited)
467
468 case TINTER:
469 if t.IsEmptyInterface() {
470 b.WriteString("interface {}")
471 break
472 }
473 b.WriteString("interface {")
474 for i, f := range t.AllMethods().Slice() {
475 if i != 0 {
476 b.WriteByte(';')
477 }
478 b.WriteByte(' ')
479 switch {
480 case f.Sym == nil:
481 // Check first that a symbol is defined for this type.
482 // Wrong interface definitions may have types lacking a symbol.
483 break
484 case IsExported(f.Sym.Name):
485 sconv2(b, f.Sym, 'S', mode)
486 default:
487 if mode != fmtTypeIDName {
488 mode = fmtTypeID
489 }
490 sconv2(b, f.Sym, 'v', mode)
491 }
492 tconv2(b, f.Type, 'S', mode, visited)
493 }
494 if t.AllMethods().Len() != 0 {
495 b.WriteByte(' ')
496 }
497 b.WriteByte('}')
498
499 case TFUNC:
500 if verb == 'S' {
501 // no leading func
502 } else {
503 if t.Recv() != nil {
504 b.WriteString("method")
505 tconv2(b, t.Recvs(), 0, mode, visited)
506 b.WriteByte(' ')
507 }
508 b.WriteString("func")
509 }
510 if t.NumTParams() > 0 {
511 tconv2(b, t.TParams(), 0, mode, visited)
512 }
513 tconv2(b, t.Params(), 0, mode, visited)
514
515 switch t.NumResults() {
516 case 0:
517 // nothing to do
518
519 case 1:
520 b.WriteByte(' ')
521 tconv2(b, t.Results().Field(0).Type, 0, mode, visited) // struct->field->field's type
522
523 default:
524 b.WriteByte(' ')
525 tconv2(b, t.Results(), 0, mode, visited)
526 }
527
528 case TSTRUCT:
529 if m := t.StructType().Map; m != nil {
530 mt := m.MapType()
531 // Format the bucket struct for map[x]y as map.bucket[x]y.
532 // This avoids a recursive print that generates very long names.
533 switch t {
534 case mt.Bucket:
535 b.WriteString("map.bucket[")
536 case mt.Hmap:
537 b.WriteString("map.hdr[")
538 case mt.Hiter:
539 b.WriteString("map.iter[")
540 default:
541 base.Fatalf("unknown internal map type")
542 }
543 tconv2(b, m.Key(), 0, mode, visited)
544 b.WriteByte(']')
545 tconv2(b, m.Elem(), 0, mode, visited)
546 break
547 }
548
549 if funarg := t.StructType().Funarg; funarg != FunargNone {
550 open, close := '(', ')'
551 if funarg == FunargTparams {
552 open, close = '[', ']'
553 }
554 b.WriteByte(byte(open))
555 fieldVerb := 'v'
556 switch mode {
557 case fmtTypeID, fmtTypeIDName, fmtGo:
558 // no argument names on function signature, and no "noescape"/"nosplit" tags
559 fieldVerb = 'S'
560 }
561 for i, f := range t.Fields().Slice() {
562 if i != 0 {
563 b.WriteString(", ")
564 }
565 fldconv(b, f, fieldVerb, mode, visited, funarg)
566 }
567 b.WriteByte(byte(close))
568 } else {
569 b.WriteString("struct {")
570 for i, f := range t.Fields().Slice() {
571 if i != 0 {
572 b.WriteByte(';')
573 }
574 b.WriteByte(' ')
575 fldconv(b, f, 'L', mode, visited, funarg)
576 }
577 if t.NumFields() != 0 {
578 b.WriteByte(' ')
579 }
580 b.WriteByte('}')
581 }
582
583 case TFORW:
584 b.WriteString("undefined")
585 if t.Sym() != nil {
586 b.WriteByte(' ')
587 sconv2(b, t.Sym(), 'v', mode)
588 }
589
590 case TUNSAFEPTR:
591 b.WriteString("unsafe.Pointer")
592
593 case TTYPEPARAM:
594 if t.Sym() != nil {
595 sconv2(b, t.Sym(), 'v', mode)
596 } else {
597 b.WriteString("tp")
598 // Print out the pointer value for now to disambiguate type params
599 b.WriteString(fmt.Sprintf("%p", t))
600 }
601
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800602 case TUNION:
603 for i := 0; i < t.NumTerms(); i++ {
604 if i > 0 {
605 b.WriteString("|")
606 }
607 elem, tilde := t.Term(i)
608 if tilde {
609 b.WriteString("~")
610 }
611 tconv2(b, elem, 0, mode, visited)
612 }
613
Dan Willemsencc753b72021-08-31 13:25:42 -0700614 case Txxx:
615 b.WriteString("Txxx")
616
617 default:
618 // Don't know how to handle - fall back to detailed prints
619 b.WriteString(t.Kind().String())
620 b.WriteString(" <")
621 sconv2(b, t.Sym(), 'v', mode)
622 b.WriteString(">")
623
624 }
625}
626
627func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Type]int, funarg Funarg) {
628 if f == nil {
629 b.WriteString("<T>")
630 return
631 }
632
633 var name string
634 if verb != 'S' {
635 s := f.Sym
636
637 // Take the name from the original.
638 if mode == fmtGo {
639 s = OrigSym(s)
640 }
641
642 if s != nil && f.Embedded == 0 {
643 if funarg != FunargNone {
644 name = fmt.Sprint(f.Nname)
645 } else if verb == 'L' {
646 name = s.Name
647 if name == ".F" {
648 name = "F" // Hack for toolstash -cmp.
649 }
650 if !IsExported(name) && mode != fmtTypeIDName {
651 name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg)
652 }
653 } else {
654 name = sconv(s, 0, mode)
655 }
656 }
657 }
658
659 if name != "" {
660 b.WriteString(name)
661 b.WriteString(" ")
662 }
663
664 if f.IsDDD() {
665 var et *Type
666 if f.Type != nil {
667 et = f.Type.Elem()
668 }
669 b.WriteString("...")
670 tconv2(b, et, 0, mode, visited)
671 } else {
672 tconv2(b, f.Type, 0, mode, visited)
673 }
674
675 if verb != 'S' && funarg == FunargNone && f.Note != "" {
676 b.WriteString(" ")
677 b.WriteString(strconv.Quote(f.Note))
678 }
679}
680
681// Val
682
683func FmtConst(v constant.Value, sharp bool) string {
684 if !sharp && v.Kind() == constant.Complex {
685 real, imag := constant.Real(v), constant.Imag(v)
686
687 var re string
688 sre := constant.Sign(real)
689 if sre != 0 {
690 re = real.String()
691 }
692
693 var im string
694 sim := constant.Sign(imag)
695 if sim != 0 {
696 im = imag.String()
697 }
698
699 switch {
700 case sre == 0 && sim == 0:
701 return "0"
702 case sre == 0:
703 return im + "i"
704 case sim == 0:
705 return re
706 case sim < 0:
707 return fmt.Sprintf("(%s%si)", re, im)
708 default:
709 return fmt.Sprintf("(%s+%si)", re, im)
710 }
711 }
712
713 return v.String()
714}
715
716// TypeHash computes a hash value for type t to use in type switch statements.
717func TypeHash(t *Type) uint32 {
Dan Willemsenbc60c3c2021-12-15 01:09:00 -0800718 p := t.NameString()
Dan Willemsencc753b72021-08-31 13:25:42 -0700719
720 // Using MD5 is overkill, but reduces accidental collisions.
721 h := md5.Sum([]byte(p))
722 return binary.LittleEndian.Uint32(h[:4])
723}