github.com/bir3/gocompiler@v0.3.205/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 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 == universeComparable.Type().(*Named).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 } 322 323 default: 324 // For externally defined implementations of Type. 325 // Note: In this case cycles won't be caught. 326 w.string(t.String()) 327 } 328 } 329 330 // typeSet writes a canonical hash for an interface type set. 331 func (w *typeWriter) typeSet(s *_TypeSet) { 332 assert(w.ctxt != nil) 333 first := true 334 for _, m := range s.methods { 335 if !first { 336 w.byte(';') 337 } 338 first = false 339 w.string(m.name) 340 w.signature(m.typ.(*Signature)) 341 } 342 switch { 343 case s.terms.isAll(): 344 // nothing to do 345 case s.terms.isEmpty(): 346 w.string(s.terms.String()) 347 default: 348 var termHashes []string 349 for _, term := range s.terms { 350 // terms are not canonically sorted, so we sort their hashes instead. 351 var buf bytes.Buffer 352 if term.tilde { 353 buf.WriteByte('~') 354 } 355 newTypeHasher(&buf, w.ctxt).typ(term.typ) 356 termHashes = append(termHashes, buf.String()) 357 } 358 sort.Strings(termHashes) 359 if !first { 360 w.byte(';') 361 } 362 w.string(strings.Join(termHashes, "|")) 363 } 364 } 365 366 func (w *typeWriter) typeList(list []Type) { 367 w.byte('[') 368 for i, typ := range list { 369 if i > 0 { 370 w.byte(',') 371 } 372 w.typ(typ) 373 } 374 w.byte(']') 375 } 376 377 func (w *typeWriter) tParamList(list []*TypeParam) { 378 w.byte('[') 379 var prev Type 380 for i, tpar := range list { 381 // Determine the type parameter and its constraint. 382 // list is expected to hold type parameter names, 383 // but don't crash if that's not the case. 384 if tpar == nil { 385 w.error("nil type parameter") 386 continue 387 } 388 if i > 0 { 389 if tpar.bound != prev { 390 // bound changed - write previous one before advancing 391 w.byte(' ') 392 w.typ(prev) 393 } 394 w.byte(',') 395 } 396 prev = tpar.bound 397 w.typ(tpar) 398 } 399 if prev != nil { 400 w.byte(' ') 401 w.typ(prev) 402 } 403 w.byte(']') 404 } 405 406 func (w *typeWriter) typeName(obj *TypeName) { 407 w.string(packagePrefix(obj.pkg, w.qf)) 408 w.string(obj.name) 409 } 410 411 func (w *typeWriter) tuple(tup *Tuple, variadic bool) { 412 w.byte('(') 413 if tup != nil { 414 for i, v := range tup.vars { 415 if i > 0 { 416 w.byte(',') 417 } 418 // parameter names are ignored for type identity and thus type hashes 419 if w.ctxt == nil && v.name != "" && w.paramNames { 420 w.string(v.name) 421 w.byte(' ') 422 } 423 typ := v.typ 424 if variadic && i == len(tup.vars)-1 { 425 if s, ok := typ.(*Slice); ok { 426 w.string("...") 427 typ = s.elem 428 } else { 429 // special case: 430 // append(s, "foo"...) leads to signature func([]byte, string...) 431 if t, _ := under(typ).(*Basic); t == nil || t.kind != String { 432 w.error("expected string type") 433 continue 434 } 435 w.typ(typ) 436 w.string("...") 437 continue 438 } 439 } 440 w.typ(typ) 441 } 442 } 443 w.byte(')') 444 } 445 446 func (w *typeWriter) signature(sig *Signature) { 447 if sig.TypeParams().Len() != 0 { 448 if w.ctxt != nil { 449 assert(w.tparams == nil) 450 w.tparams = sig.TypeParams() 451 defer func() { 452 w.tparams = nil 453 }() 454 } 455 w.tParamList(sig.TypeParams().list()) 456 } 457 458 w.tuple(sig.params, sig.variadic) 459 460 n := sig.results.Len() 461 if n == 0 { 462 // no result 463 return 464 } 465 466 w.byte(' ') 467 if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") { 468 // single unnamed result (if type hashing, name must be ignored) 469 w.typ(sig.results.vars[0].typ) 470 return 471 } 472 473 // multiple or named result(s) 474 w.tuple(sig.results, false) 475 } 476 477 // subscript returns the decimal (utf8) representation of x using subscript digits. 478 func subscript(x uint64) string { 479 const w = len("₀") // all digits 0...9 have the same utf8 width 480 var buf [32 * w]byte 481 i := len(buf) 482 for { 483 i -= w 484 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080 485 x /= 10 486 if x == 0 { 487 break 488 } 489 } 490 return string(buf[i:]) 491 }