Source file src/go/types/typestring.go

     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  

View as plain text