Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/compile/internal/types/fmt.go

Documentation: cmd/compile/internal/types

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

View as plain text