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 10package types 11 12import ( 13 "bytes" 14 "fmt" 15 "sort" 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". 31type Qualifier func(*Package) string 32 33// RelativeTo returns a [Qualifier] that fully qualifies members of 34// all packages other than pkg. 35func 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. 50func 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. 59func 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. 66func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { 67 newTypeWriter(buf, qf).signature(sig) 68} 69 70type 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 81func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter { 82 return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false} 83} 84 85func 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 90func (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 104func (w *typeWriter) string(s string) { 105 w.buf.WriteString(s) 106} 107 108func (w *typeWriter) error(msg string) { 109 if w.ctxt != nil { 110 panic(msg) 111 } 112 w.buf.WriteString("<" + msg + ">") 113} 114 115func (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 := tparamIndex(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 } 345 if w.ctxt != nil { 346 // TODO(gri) do we need to print the alias type name, too? 347 w.typ(Unalias(t.obj.typ)) 348 } 349 350 default: 351 // For externally defined implementations of Type. 352 // Note: In this case cycles won't be caught. 353 w.string(t.String()) 354 } 355} 356 357// typeSet writes a canonical hash for an interface type set. 358func (w *typeWriter) typeSet(s *_TypeSet) { 359 assert(w.ctxt != nil) 360 first := true 361 for _, m := range s.methods { 362 if !first { 363 w.byte(';') 364 } 365 first = false 366 w.string(m.name) 367 w.signature(m.typ.(*Signature)) 368 } 369 switch { 370 case s.terms.isAll(): 371 // nothing to do 372 case s.terms.isEmpty(): 373 w.string(s.terms.String()) 374 default: 375 var termHashes []string 376 for _, term := range s.terms { 377 // terms are not canonically sorted, so we sort their hashes instead. 378 var buf bytes.Buffer 379 if term.tilde { 380 buf.WriteByte('~') 381 } 382 newTypeHasher(&buf, w.ctxt).typ(term.typ) 383 termHashes = append(termHashes, buf.String()) 384 } 385 sort.Strings(termHashes) 386 if !first { 387 w.byte(';') 388 } 389 w.string(strings.Join(termHashes, "|")) 390 } 391} 392 393func (w *typeWriter) typeList(list []Type) { 394 w.byte('[') 395 for i, typ := range list { 396 if i > 0 { 397 w.byte(',') 398 } 399 w.typ(typ) 400 } 401 w.byte(']') 402} 403 404func (w *typeWriter) tParamList(list []*TypeParam) { 405 w.byte('[') 406 var prev Type 407 for i, tpar := range list { 408 // Determine the type parameter and its constraint. 409 // list is expected to hold type parameter names, 410 // but don't crash if that's not the case. 411 if tpar == nil { 412 w.error("nil type parameter") 413 continue 414 } 415 if i > 0 { 416 if tpar.bound != prev { 417 // bound changed - write previous one before advancing 418 w.byte(' ') 419 w.typ(prev) 420 } 421 w.byte(',') 422 } 423 prev = tpar.bound 424 w.typ(tpar) 425 } 426 if prev != nil { 427 w.byte(' ') 428 w.typ(prev) 429 } 430 w.byte(']') 431} 432 433func (w *typeWriter) typeName(obj *TypeName) { 434 w.string(packagePrefix(obj.pkg, w.qf)) 435 w.string(obj.name) 436} 437 438func (w *typeWriter) tuple(tup *Tuple, variadic bool) { 439 w.byte('(') 440 if tup != nil { 441 for i, v := range tup.vars { 442 if i > 0 { 443 w.byte(',') 444 } 445 // parameter names are ignored for type identity and thus type hashes 446 if w.ctxt == nil && v.name != "" && w.paramNames { 447 w.string(v.name) 448 w.byte(' ') 449 } 450 typ := v.typ 451 if variadic && i == len(tup.vars)-1 { 452 if s, ok := typ.(*Slice); ok { 453 w.string("...") 454 typ = s.elem 455 } else { 456 // special case: 457 // append(s, "foo"...) leads to signature func([]byte, string...) 458 if t, _ := under(typ).(*Basic); t == nil || t.kind != String { 459 w.error("expected string type") 460 continue 461 } 462 w.typ(typ) 463 w.string("...") 464 continue 465 } 466 } 467 w.typ(typ) 468 } 469 } 470 w.byte(')') 471} 472 473func (w *typeWriter) signature(sig *Signature) { 474 if sig.TypeParams().Len() != 0 { 475 if w.ctxt != nil { 476 assert(w.tparams == nil) 477 w.tparams = sig.TypeParams() 478 defer func() { 479 w.tparams = nil 480 }() 481 } 482 w.tParamList(sig.TypeParams().list()) 483 } 484 485 w.tuple(sig.params, sig.variadic) 486 487 n := sig.results.Len() 488 if n == 0 { 489 // no result 490 return 491 } 492 493 w.byte(' ') 494 if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") { 495 // single unnamed result (if type hashing, name must be ignored) 496 w.typ(sig.results.vars[0].typ) 497 return 498 } 499 500 // multiple or named result(s) 501 w.tuple(sig.results, false) 502} 503 504// subscript returns the decimal (utf8) representation of x using subscript digits. 505func subscript(x uint64) string { 506 const w = len("₀") // all digits 0...9 have the same utf8 width 507 var buf [32 * w]byte 508 i := len(buf) 509 for { 510 i -= w 511 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080 512 x /= 10 513 if x == 0 { 514 break 515 } 516 } 517 return string(buf[i:]) 518} 519