1// 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
26// UnsafePkg is package unsafe.
27var UnsafePkg *Pkg
28
29// 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
67// of types used in hashes, the linker, and function/method instantiations.
68type 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) {
143	name := s.Name
144	if q := pkgqual(s.Pkg, verb, mode); q != "" {
145		b.WriteString(q)
146		b.WriteByte('.')
147		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		}
152	}
153	b.WriteString(name)
154}
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
251// 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 {
270	return tconv(t, 0, fmtTypeID)
271}
272
273// 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 {
282	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 {
310		b.WriteString(t.extra.(string))
311		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 {
321		tys := t.extra.(*Results).Types
322		for i, et := range tys {
323			if i > 0 {
324				b.WriteByte(',')
325			}
326			b.WriteString(et.String())
327		}
328		return
329	}
330
331	if t == AnyType || t == ByteType || t == RuneType {
332		// in %-T mode collapse predeclared aliases with their originals.
333		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()] {
348		// Default to 'v' if verb is invalid.
349		if verb != 'S' {
350			verb = 'v'
351		}
352
353		// 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		}
376		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
602	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
614	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 {
718	p := t.NameString()
719
720	// Using MD5 is overkill, but reduces accidental collisions.
721	h := md5.Sum([]byte(p))
722	return binary.LittleEndian.Uint32(h[:4])
723}
724