github.com/bir3/gocompiler@v0.9.2202/src/go/types/typestring.go (about) 1 // Copyright 2013 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 // This file implements printing of types. 6 7 package types 8 9 import ( 10 "bytes" 11 "fmt" 12 "github.com/bir3/gocompiler/src/go/token" 13 "sort" 14 "strconv" 15 "strings" 16 "unicode/utf8" 17 ) 18 19 // A Qualifier controls how named package-level objects are printed in 20 // calls to [TypeString], [ObjectString], and [SelectionString]. 21 // 22 // These three formatting routines call the Qualifier for each 23 // package-level object O, and if the Qualifier returns a non-empty 24 // string p, the object is printed in the form p.O. 25 // If it returns an empty string, only the object name O is printed. 26 // 27 // Using a nil Qualifier is equivalent to using (*[Package]).Path: the 28 // object is qualified by the import path, e.g., "encoding/json.Marshal". 29 type Qualifier func(*Package) string 30 31 // RelativeTo returns a [Qualifier] that fully qualifies members of 32 // all packages other than pkg. 33 func RelativeTo(pkg *Package) Qualifier { 34 if pkg == nil { 35 return nil 36 } 37 return func(other *Package) string { 38 if pkg == other { 39 return "" // same package; unqualified 40 } 41 return other.Path() 42 } 43 } 44 45 // TypeString returns the string representation of typ. 46 // The [Qualifier] controls the printing of 47 // package-level objects, and may be nil. 48 func TypeString(typ Type, qf Qualifier) string { 49 var buf bytes.Buffer 50 WriteType(&buf, typ, qf) 51 return buf.String() 52 } 53 54 // WriteType writes the string representation of typ to buf. 55 // The [Qualifier] controls the printing of 56 // package-level objects, and may be nil. 57 func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) { 58 newTypeWriter(buf, qf).typ(typ) 59 } 60 61 // WriteSignature writes the representation of the signature sig to buf, 62 // without a leading "func" keyword. The [Qualifier] controls the printing 63 // of package-level objects, and may be nil. 64 func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { 65 newTypeWriter(buf, qf).signature(sig) 66 } 67 68 type typeWriter struct { 69 buf *bytes.Buffer 70 seen map[Type]bool 71 qf Qualifier 72 ctxt *Context // if non-nil, we are type hashing 73 tparams *TypeParamList // local type parameters 74 paramNames bool // if set, write function parameter names, otherwise, write types only 75 tpSubscripts bool // if set, write type parameter indices as subscripts 76 pkgInfo bool // package-annotate first unexported-type field to avoid confusing type description 77 } 78 79 func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter { 80 return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false} 81 } 82 83 func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter { 84 assert(ctxt != nil) 85 return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false, false, false} 86 } 87 88 func (w *typeWriter) byte(b byte) { 89 if w.ctxt != nil { 90 if b == ' ' { 91 b = '#' 92 } 93 w.buf.WriteByte(b) 94 return 95 } 96 w.buf.WriteByte(b) 97 if b == ',' || b == ';' { 98 w.buf.WriteByte(' ') 99 } 100 } 101 102 func (w *typeWriter) string(s string) { 103 w.buf.WriteString(s) 104 } 105 106 func (w *typeWriter) error(msg string) { 107 if w.ctxt != nil { 108 panic(msg) 109 } 110 w.buf.WriteString("<" + msg + ">") 111 } 112 113 func (w *typeWriter) typ(typ Type) { 114 if w.seen[typ] { 115 w.error("cycle to " + goTypeName(typ)) 116 return 117 } 118 w.seen[typ] = true 119 defer delete(w.seen, typ) 120 121 switch t := typ.(type) { 122 case nil: 123 w.error("nil") 124 125 case *Basic: 126 // exported basic types go into package unsafe 127 // (currently this is just unsafe.Pointer) 128 if token.IsExported(t.name) { 129 if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil { 130 w.typeName(obj) 131 break 132 } 133 } 134 w.string(t.name) 135 136 case *Array: 137 w.byte('[') 138 w.string(strconv.FormatInt(t.len, 10)) 139 w.byte(']') 140 w.typ(t.elem) 141 142 case *Slice: 143 w.string("[]") 144 w.typ(t.elem) 145 146 case *Struct: 147 w.string("struct{") 148 for i, f := range t.fields { 149 if i > 0 { 150 w.byte(';') 151 } 152 153 // If disambiguating one struct for another, look for the first unexported field. 154 // Do this first in case of nested structs; tag the first-outermost field. 155 pkgAnnotate := false 156 if w.qf == nil && w.pkgInfo && !token.IsExported(f.name) { 157 // note for embedded types, type name is field name, and "string" etc are lower case hence unexported. 158 pkgAnnotate = true 159 w.pkgInfo = false // only tag once 160 } 161 162 // This doesn't do the right thing for embedded type 163 // aliases where we should print the alias name, not 164 // the aliased type (see go.dev/issue/44410). 165 if !f.embedded { 166 w.string(f.name) 167 w.byte(' ') 168 } 169 w.typ(f.typ) 170 if pkgAnnotate { 171 w.string(" /* package ") 172 w.string(f.pkg.Path()) 173 w.string(" */ ") 174 } 175 if tag := t.Tag(i); tag != "" { 176 w.byte(' ') 177 // TODO(rfindley) If tag contains blanks, replacing them with '#' 178 // in Context.TypeHash may produce another tag 179 // accidentally. 180 w.string(strconv.Quote(tag)) 181 } 182 } 183 w.byte('}') 184 185 case *Pointer: 186 w.byte('*') 187 w.typ(t.base) 188 189 case *Tuple: 190 w.tuple(t, false) 191 192 case *Signature: 193 w.string("func") 194 w.signature(t) 195 196 case *Union: 197 // Unions only appear as (syntactic) embedded elements 198 // in interfaces and syntactically cannot be empty. 199 if t.Len() == 0 { 200 w.error("empty union") 201 break 202 } 203 for i, t := range t.terms { 204 if i > 0 { 205 w.string(termSep) 206 } 207 if t.tilde { 208 w.byte('~') 209 } 210 w.typ(t.typ) 211 } 212 213 case *Interface: 214 if w.ctxt == nil { 215 if t == universeAny.Type() { 216 // When not hashing, we can try to improve type strings by writing "any" 217 // for a type that is pointer-identical to universeAny. This logic should 218 // be deprecated by more robust handling for aliases. 219 w.string("any") 220 break 221 } 222 if t == asNamed(universeComparable.Type()).underlying { 223 w.string("interface{comparable}") 224 break 225 } 226 } 227 if t.implicit { 228 if len(t.methods) == 0 && len(t.embeddeds) == 1 { 229 w.typ(t.embeddeds[0]) 230 break 231 } 232 // Something's wrong with the implicit interface. 233 // Print it as such and continue. 234 w.string("/* implicit */ ") 235 } 236 w.string("interface{") 237 first := true 238 if w.ctxt != nil { 239 w.typeSet(t.typeSet()) 240 } else { 241 for _, m := range t.methods { 242 if !first { 243 w.byte(';') 244 } 245 first = false 246 w.string(m.name) 247 w.signature(m.typ.(*Signature)) 248 } 249 for _, typ := range t.embeddeds { 250 if !first { 251 w.byte(';') 252 } 253 first = false 254 w.typ(typ) 255 } 256 } 257 w.byte('}') 258 259 case *Map: 260 w.string("map[") 261 w.typ(t.key) 262 w.byte(']') 263 w.typ(t.elem) 264 265 case *Chan: 266 var s string 267 var parens bool 268 switch t.dir { 269 case SendRecv: 270 s = "chan " 271 // chan (<-chan T) requires parentheses 272 if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly { 273 parens = true 274 } 275 case SendOnly: 276 s = "chan<- " 277 case RecvOnly: 278 s = "<-chan " 279 default: 280 w.error("unknown channel direction") 281 } 282 w.string(s) 283 if parens { 284 w.byte('(') 285 } 286 w.typ(t.elem) 287 if parens { 288 w.byte(')') 289 } 290 291 case *Named: 292 // If hashing, write a unique prefix for t to represent its identity, since 293 // named type identity is pointer identity. 294 if w.ctxt != nil { 295 w.string(strconv.Itoa(w.ctxt.getID(t))) 296 } 297 w.typeName(t.obj) // when hashing written for readability of the hash only 298 if t.inst != nil { 299 // instantiated type 300 w.typeList(t.inst.targs.list()) 301 } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams 302 // parameterized type 303 w.tParamList(t.TypeParams().list()) 304 } 305 306 case *TypeParam: 307 if t.obj == nil { 308 w.error("unnamed type parameter") 309 break 310 } 311 if i := tparamIndex(w.tparams.list(), t); i >= 0 { 312 // The names of type parameters that are declared by the type being 313 // hashed are not part of the type identity. Replace them with a 314 // placeholder indicating their index. 315 w.string(fmt.Sprintf("$%d", i)) 316 } else { 317 w.string(t.obj.name) 318 if w.tpSubscripts || w.ctxt != nil { 319 w.string(subscript(t.id)) 320 } 321 // If the type parameter name is the same as a predeclared object 322 // (say int), point out where it is declared to avoid confusing 323 // error messages. This doesn't need to be super-elegant; we just 324 // need a clear indication that this is not a predeclared name. 325 // Note: types2 prints position information here - we can't do 326 // that because we don't have a token.FileSet accessible. 327 if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil { 328 w.string("/* type parameter */") 329 } 330 } 331 332 case *Alias: 333 w.typeName(t.obj) 334 if w.ctxt != nil { 335 // TODO(gri) do we need to print the alias type name, too? 336 w.typ(Unalias(t.obj.typ)) 337 } 338 339 default: 340 // For externally defined implementations of Type. 341 // Note: In this case cycles won't be caught. 342 w.string(t.String()) 343 } 344 } 345 346 // typeSet writes a canonical hash for an interface type set. 347 func (w *typeWriter) typeSet(s *_TypeSet) { 348 assert(w.ctxt != nil) 349 first := true 350 for _, m := range s.methods { 351 if !first { 352 w.byte(';') 353 } 354 first = false 355 w.string(m.name) 356 w.signature(m.typ.(*Signature)) 357 } 358 switch { 359 case s.terms.isAll(): 360 // nothing to do 361 case s.terms.isEmpty(): 362 w.string(s.terms.String()) 363 default: 364 var termHashes []string 365 for _, term := range s.terms { 366 // terms are not canonically sorted, so we sort their hashes instead. 367 var buf bytes.Buffer 368 if term.tilde { 369 buf.WriteByte('~') 370 } 371 newTypeHasher(&buf, w.ctxt).typ(term.typ) 372 termHashes = append(termHashes, buf.String()) 373 } 374 sort.Strings(termHashes) 375 if !first { 376 w.byte(';') 377 } 378 w.string(strings.Join(termHashes, "|")) 379 } 380 } 381 382 func (w *typeWriter) typeList(list []Type) { 383 w.byte('[') 384 for i, typ := range list { 385 if i > 0 { 386 w.byte(',') 387 } 388 w.typ(typ) 389 } 390 w.byte(']') 391 } 392 393 func (w *typeWriter) tParamList(list []*TypeParam) { 394 w.byte('[') 395 var prev Type 396 for i, tpar := range list { 397 // Determine the type parameter and its constraint. 398 // list is expected to hold type parameter names, 399 // but don't crash if that's not the case. 400 if tpar == nil { 401 w.error("nil type parameter") 402 continue 403 } 404 if i > 0 { 405 if tpar.bound != prev { 406 // bound changed - write previous one before advancing 407 w.byte(' ') 408 w.typ(prev) 409 } 410 w.byte(',') 411 } 412 prev = tpar.bound 413 w.typ(tpar) 414 } 415 if prev != nil { 416 w.byte(' ') 417 w.typ(prev) 418 } 419 w.byte(']') 420 } 421 422 func (w *typeWriter) typeName(obj *TypeName) { 423 w.string(packagePrefix(obj.pkg, w.qf)) 424 w.string(obj.name) 425 } 426 427 func (w *typeWriter) tuple(tup *Tuple, variadic bool) { 428 w.byte('(') 429 if tup != nil { 430 for i, v := range tup.vars { 431 if i > 0 { 432 w.byte(',') 433 } 434 // parameter names are ignored for type identity and thus type hashes 435 if w.ctxt == nil && v.name != "" && w.paramNames { 436 w.string(v.name) 437 w.byte(' ') 438 } 439 typ := v.typ 440 if variadic && i == len(tup.vars)-1 { 441 if s, ok := typ.(*Slice); ok { 442 w.string("...") 443 typ = s.elem 444 } else { 445 // special case: 446 // append(s, "foo"...) leads to signature func([]byte, string...) 447 if t, _ := under(typ).(*Basic); t == nil || t.kind != String { 448 w.error("expected string type") 449 continue 450 } 451 w.typ(typ) 452 w.string("...") 453 continue 454 } 455 } 456 w.typ(typ) 457 } 458 } 459 w.byte(')') 460 } 461 462 func (w *typeWriter) signature(sig *Signature) { 463 if sig.TypeParams().Len() != 0 { 464 if w.ctxt != nil { 465 assert(w.tparams == nil) 466 w.tparams = sig.TypeParams() 467 defer func() { 468 w.tparams = nil 469 }() 470 } 471 w.tParamList(sig.TypeParams().list()) 472 } 473 474 w.tuple(sig.params, sig.variadic) 475 476 n := sig.results.Len() 477 if n == 0 { 478 // no result 479 return 480 } 481 482 w.byte(' ') 483 if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") { 484 // single unnamed result (if type hashing, name must be ignored) 485 w.typ(sig.results.vars[0].typ) 486 return 487 } 488 489 // multiple or named result(s) 490 w.tuple(sig.results, false) 491 } 492 493 // subscript returns the decimal (utf8) representation of x using subscript digits. 494 func subscript(x uint64) string { 495 const w = len("₀") // all digits 0...9 have the same utf8 width 496 var buf [32 * w]byte 497 i := len(buf) 498 for { 499 i -= w 500 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080 501 x /= 10 502 if x == 0 { 503 break 504 } 505 } 506 return string(buf[i:]) 507 }