typestring.go raw

   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