Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 1 | // Copyright 2009 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 | package types |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "crypto/md5" |
| 10 | "encoding/binary" |
| 11 | "fmt" |
| 12 | "go/constant" |
| 13 | "strconv" |
| 14 | "strings" |
| 15 | "sync" |
| 16 | |
| 17 | "cmd/compile/internal/base" |
| 18 | ) |
| 19 | |
| 20 | // BuiltinPkg is a fake package that declares the universe block. |
| 21 | var BuiltinPkg *Pkg |
| 22 | |
| 23 | // LocalPkg is the package being compiled. |
| 24 | var LocalPkg *Pkg |
| 25 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 26 | // UnsafePkg is package unsafe. |
| 27 | var UnsafePkg *Pkg |
| 28 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 29 | // BlankSym is the blank (_) symbol. |
| 30 | var BlankSym *Sym |
| 31 | |
| 32 | // OrigSym returns the original symbol written by the user. |
| 33 | func OrigSym(s *Sym) *Sym { |
| 34 | if s == nil { |
| 35 | return nil |
| 36 | } |
| 37 | |
| 38 | if len(s.Name) > 1 && s.Name[0] == '~' { |
| 39 | switch s.Name[1] { |
| 40 | case 'r': // originally an unnamed result |
| 41 | return nil |
| 42 | case 'b': // originally the blank identifier _ |
| 43 | // TODO(mdempsky): Does s.Pkg matter here? |
| 44 | return BlankSym |
| 45 | } |
| 46 | return s |
| 47 | } |
| 48 | |
| 49 | if strings.HasPrefix(s.Name, ".anon") { |
| 50 | // originally an unnamed or _ name (see subr.go: NewFuncParams) |
| 51 | return nil |
| 52 | } |
| 53 | |
| 54 | return s |
| 55 | } |
| 56 | |
| 57 | // numImport tracks how often a package with a given name is imported. |
| 58 | // It is used to provide a better error message (by using the package |
| 59 | // path to disambiguate) if a package that appears multiple times with |
| 60 | // the same name appears in an error message. |
| 61 | var NumImport = make(map[string]int) |
| 62 | |
| 63 | // fmtMode represents the kind of printing being done. |
| 64 | // The default is regular Go syntax (fmtGo). |
| 65 | // fmtDebug is like fmtGo but for debugging dumps and prints the type kind too. |
| 66 | // fmtTypeID and fmtTypeIDName are for generating various unique representations |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 67 | // of types used in hashes, the linker, and function/method instantiations. |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 68 | type fmtMode int |
| 69 | |
| 70 | const ( |
| 71 | fmtGo fmtMode = iota |
| 72 | fmtDebug |
| 73 | fmtTypeID |
| 74 | fmtTypeIDName |
| 75 | ) |
| 76 | |
| 77 | // Sym |
| 78 | |
| 79 | // Format implements formatting for a Sym. |
| 80 | // The valid formats are: |
| 81 | // |
| 82 | // %v Go syntax: Name for symbols in the local package, PkgName.Name for imported symbols. |
| 83 | // %+v Debug syntax: always include PkgName. prefix even for local names. |
| 84 | // %S Short syntax: Name only, no matter what. |
| 85 | // |
| 86 | func (s *Sym) Format(f fmt.State, verb rune) { |
| 87 | mode := fmtGo |
| 88 | switch verb { |
| 89 | case 'v', 'S': |
| 90 | if verb == 'v' && f.Flag('+') { |
| 91 | mode = fmtDebug |
| 92 | } |
| 93 | fmt.Fprint(f, sconv(s, verb, mode)) |
| 94 | |
| 95 | default: |
| 96 | fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s) |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | func (s *Sym) String() string { |
| 101 | return sconv(s, 0, fmtGo) |
| 102 | } |
| 103 | |
| 104 | // See #16897 for details about performance implications |
| 105 | // before changing the implementation of sconv. |
| 106 | func sconv(s *Sym, verb rune, mode fmtMode) string { |
| 107 | if verb == 'L' { |
| 108 | panic("linksymfmt") |
| 109 | } |
| 110 | |
| 111 | if s == nil { |
| 112 | return "<S>" |
| 113 | } |
| 114 | |
| 115 | q := pkgqual(s.Pkg, verb, mode) |
| 116 | if q == "" { |
| 117 | return s.Name |
| 118 | } |
| 119 | |
| 120 | buf := fmtBufferPool.Get().(*bytes.Buffer) |
| 121 | buf.Reset() |
| 122 | defer fmtBufferPool.Put(buf) |
| 123 | |
| 124 | buf.WriteString(q) |
| 125 | buf.WriteByte('.') |
| 126 | buf.WriteString(s.Name) |
| 127 | return InternString(buf.Bytes()) |
| 128 | } |
| 129 | |
| 130 | func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) { |
| 131 | if verb == 'L' { |
| 132 | panic("linksymfmt") |
| 133 | } |
| 134 | if s == nil { |
| 135 | b.WriteString("<S>") |
| 136 | return |
| 137 | } |
| 138 | |
| 139 | symfmt(b, s, verb, mode) |
| 140 | } |
| 141 | |
| 142 | func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 143 | name := s.Name |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 144 | if q := pkgqual(s.Pkg, verb, mode); q != "" { |
| 145 | b.WriteString(q) |
| 146 | b.WriteByte('.') |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 147 | if mode == fmtTypeIDName { |
| 148 | // If name is a generic instantiation, it might have local package placeholders |
| 149 | // in it. Replace those placeholders with the package name. See issue 49547. |
| 150 | name = strings.Replace(name, LocalPkg.Prefix, q, -1) |
| 151 | } |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 152 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 153 | b.WriteString(name) |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | // pkgqual returns the qualifier that should be used for printing |
| 157 | // symbols from the given package in the given mode. |
| 158 | // If it returns the empty string, no qualification is needed. |
| 159 | func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string { |
| 160 | if verb != 'S' { |
| 161 | switch mode { |
| 162 | case fmtGo: // This is for the user |
| 163 | if pkg == BuiltinPkg || pkg == LocalPkg { |
| 164 | return "" |
| 165 | } |
| 166 | |
| 167 | // If the name was used by multiple packages, display the full path, |
| 168 | if pkg.Name != "" && NumImport[pkg.Name] > 1 { |
| 169 | return strconv.Quote(pkg.Path) |
| 170 | } |
| 171 | return pkg.Name |
| 172 | |
| 173 | case fmtDebug: |
| 174 | return pkg.Name |
| 175 | |
| 176 | case fmtTypeIDName: |
| 177 | // dcommontype, typehash |
| 178 | return pkg.Name |
| 179 | |
| 180 | case fmtTypeID: |
| 181 | // (methodsym), typesym, weaksym |
| 182 | return pkg.Prefix |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | return "" |
| 187 | } |
| 188 | |
| 189 | // Type |
| 190 | |
| 191 | var BasicTypeNames = []string{ |
| 192 | TINT: "int", |
| 193 | TUINT: "uint", |
| 194 | TINT8: "int8", |
| 195 | TUINT8: "uint8", |
| 196 | TINT16: "int16", |
| 197 | TUINT16: "uint16", |
| 198 | TINT32: "int32", |
| 199 | TUINT32: "uint32", |
| 200 | TINT64: "int64", |
| 201 | TUINT64: "uint64", |
| 202 | TUINTPTR: "uintptr", |
| 203 | TFLOAT32: "float32", |
| 204 | TFLOAT64: "float64", |
| 205 | TCOMPLEX64: "complex64", |
| 206 | TCOMPLEX128: "complex128", |
| 207 | TBOOL: "bool", |
| 208 | TANY: "any", |
| 209 | TSTRING: "string", |
| 210 | TNIL: "nil", |
| 211 | TIDEAL: "untyped number", |
| 212 | TBLANK: "blank", |
| 213 | } |
| 214 | |
| 215 | var fmtBufferPool = sync.Pool{ |
| 216 | New: func() interface{} { |
| 217 | return new(bytes.Buffer) |
| 218 | }, |
| 219 | } |
| 220 | |
| 221 | // Format implements formatting for a Type. |
| 222 | // The valid formats are: |
| 223 | // |
| 224 | // %v Go syntax |
| 225 | // %+v Debug syntax: Go syntax with a KIND- prefix for all but builtins. |
| 226 | // %L Go syntax for underlying type if t is named |
| 227 | // %S short Go syntax: drop leading "func" in function type |
| 228 | // %-S special case for method receiver symbol |
| 229 | // |
| 230 | func (t *Type) Format(s fmt.State, verb rune) { |
| 231 | mode := fmtGo |
| 232 | switch verb { |
| 233 | case 'v', 'S', 'L': |
| 234 | if verb == 'v' && s.Flag('+') { // %+v is debug format |
| 235 | mode = fmtDebug |
| 236 | } |
| 237 | if verb == 'S' && s.Flag('-') { // %-S is special case for receiver - short typeid format |
| 238 | mode = fmtTypeID |
| 239 | } |
| 240 | fmt.Fprint(s, tconv(t, verb, mode)) |
| 241 | default: |
| 242 | fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t) |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | // String returns the Go syntax for the type t. |
| 247 | func (t *Type) String() string { |
| 248 | return tconv(t, 0, fmtGo) |
| 249 | } |
| 250 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 251 | // LinkString returns an unexpanded string description of t, suitable |
| 252 | // for use in link symbols. "Unexpanded" here means that the |
| 253 | // description uses `"".` to qualify identifiers from the current |
| 254 | // package, and "expansion" refers to the renaming step performed by |
| 255 | // the linker to replace these qualifiers with proper `path/to/pkg.` |
| 256 | // qualifiers. |
| 257 | // |
| 258 | // After expansion, the description corresponds to type identity. That |
| 259 | // is, for any pair of types t1 and t2, Identical(t1, t2) and |
| 260 | // expand(t1.LinkString()) == expand(t2.LinkString()) report the same |
| 261 | // value. |
| 262 | // |
| 263 | // Within a single compilation unit, LinkString always returns the |
| 264 | // same unexpanded description for identical types. Thus it's safe to |
| 265 | // use as a map key to implement a type-identity-keyed map. However, |
| 266 | // make sure all LinkString calls used for this purpose happen within |
| 267 | // the same compile process; the string keys are not stable across |
| 268 | // multiple processes. |
| 269 | func (t *Type) LinkString() string { |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 270 | return tconv(t, 0, fmtTypeID) |
| 271 | } |
| 272 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 273 | // NameString generates a user-readable, mostly unique string |
| 274 | // description of t. NameString always returns the same description |
| 275 | // for identical types, even across compilation units. |
| 276 | // |
| 277 | // NameString qualifies identifiers by package name, so it has |
| 278 | // collisions when different packages share the same names and |
| 279 | // identifiers. It also does not distinguish function-scope defined |
| 280 | // types from package-scoped defined types or from each other. |
| 281 | func (t *Type) NameString() string { |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 282 | return tconv(t, 0, fmtTypeIDName) |
| 283 | } |
| 284 | |
| 285 | func tconv(t *Type, verb rune, mode fmtMode) string { |
| 286 | buf := fmtBufferPool.Get().(*bytes.Buffer) |
| 287 | buf.Reset() |
| 288 | defer fmtBufferPool.Put(buf) |
| 289 | |
| 290 | tconv2(buf, t, verb, mode, nil) |
| 291 | return InternString(buf.Bytes()) |
| 292 | } |
| 293 | |
| 294 | // tconv2 writes a string representation of t to b. |
| 295 | // flag and mode control exactly what is printed. |
| 296 | // Any types x that are already in the visited map get printed as @%d where %d=visited[x]. |
| 297 | // See #16897 before changing the implementation of tconv. |
| 298 | func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type]int) { |
| 299 | if off, ok := visited[t]; ok { |
| 300 | // We've seen this type before, so we're trying to print it recursively. |
| 301 | // Print a reference to it instead. |
| 302 | fmt.Fprintf(b, "@%d", off) |
| 303 | return |
| 304 | } |
| 305 | if t == nil { |
| 306 | b.WriteString("<T>") |
| 307 | return |
| 308 | } |
| 309 | if t.Kind() == TSSA { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 310 | b.WriteString(t.extra.(string)) |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 311 | return |
| 312 | } |
| 313 | if t.Kind() == TTUPLE { |
| 314 | b.WriteString(t.FieldType(0).String()) |
| 315 | b.WriteByte(',') |
| 316 | b.WriteString(t.FieldType(1).String()) |
| 317 | return |
| 318 | } |
| 319 | |
| 320 | if t.Kind() == TRESULTS { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 321 | tys := t.extra.(*Results).Types |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 322 | for i, et := range tys { |
| 323 | if i > 0 { |
| 324 | b.WriteByte(',') |
| 325 | } |
| 326 | b.WriteString(et.String()) |
| 327 | } |
| 328 | return |
| 329 | } |
| 330 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 331 | if t == AnyType || t == ByteType || t == RuneType { |
| 332 | // in %-T mode collapse predeclared aliases with their originals. |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 333 | switch mode { |
| 334 | case fmtTypeIDName, fmtTypeID: |
| 335 | t = Types[t.Kind()] |
| 336 | default: |
| 337 | sconv2(b, t.Sym(), 'S', mode) |
| 338 | return |
| 339 | } |
| 340 | } |
| 341 | if t == ErrorType { |
| 342 | b.WriteString("error") |
| 343 | return |
| 344 | } |
| 345 | |
| 346 | // Unless the 'L' flag was specified, if the type has a name, just print that name. |
| 347 | if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 348 | // Default to 'v' if verb is invalid. |
| 349 | if verb != 'S' { |
| 350 | verb = 'v' |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 351 | } |
| 352 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 353 | // In unified IR, function-scope defined types will have a ·N |
| 354 | // suffix embedded directly in their Name. Trim this off for |
| 355 | // non-fmtTypeID modes. |
| 356 | sym := t.Sym() |
| 357 | if mode != fmtTypeID { |
| 358 | i := len(sym.Name) |
| 359 | for i > 0 && sym.Name[i-1] >= '0' && sym.Name[i-1] <= '9' { |
| 360 | i-- |
| 361 | } |
| 362 | const dot = "·" |
| 363 | if i >= len(dot) && sym.Name[i-len(dot):i] == dot { |
| 364 | sym = &Sym{Pkg: sym.Pkg, Name: sym.Name[:i-len(dot)]} |
| 365 | } |
| 366 | } |
| 367 | sconv2(b, sym, verb, mode) |
| 368 | |
| 369 | // TODO(mdempsky): Investigate including Vargen in fmtTypeIDName |
| 370 | // output too. It seems like it should, but that mode is currently |
| 371 | // used in string representation used by reflection, which is |
| 372 | // user-visible and doesn't expect this. |
| 373 | if mode == fmtTypeID && t.vargen != 0 { |
| 374 | fmt.Fprintf(b, "·%d", t.vargen) |
| 375 | } |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 376 | return |
| 377 | } |
| 378 | |
| 379 | if int(t.Kind()) < len(BasicTypeNames) && BasicTypeNames[t.Kind()] != "" { |
| 380 | var name string |
| 381 | switch t { |
| 382 | case UntypedBool: |
| 383 | name = "untyped bool" |
| 384 | case UntypedString: |
| 385 | name = "untyped string" |
| 386 | case UntypedInt: |
| 387 | name = "untyped int" |
| 388 | case UntypedRune: |
| 389 | name = "untyped rune" |
| 390 | case UntypedFloat: |
| 391 | name = "untyped float" |
| 392 | case UntypedComplex: |
| 393 | name = "untyped complex" |
| 394 | default: |
| 395 | name = BasicTypeNames[t.Kind()] |
| 396 | } |
| 397 | b.WriteString(name) |
| 398 | return |
| 399 | } |
| 400 | |
| 401 | if mode == fmtDebug { |
| 402 | b.WriteString(t.Kind().String()) |
| 403 | b.WriteByte('-') |
| 404 | tconv2(b, t, 'v', fmtGo, visited) |
| 405 | return |
| 406 | } |
| 407 | |
| 408 | // At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't |
| 409 | // try to print it recursively. |
| 410 | // We record the offset in the result buffer where the type's text starts. This offset serves as a reference |
| 411 | // point for any later references to the same type. |
| 412 | // Note that we remove the type from the visited map as soon as the recursive call is done. |
| 413 | // This prevents encoding types like map[*int]*int as map[*int]@4. (That encoding would work, |
| 414 | // but I'd like to use the @ notation only when strictly necessary.) |
| 415 | if visited == nil { |
| 416 | visited = map[*Type]int{} |
| 417 | } |
| 418 | visited[t] = b.Len() |
| 419 | defer delete(visited, t) |
| 420 | |
| 421 | switch t.Kind() { |
| 422 | case TPTR: |
| 423 | b.WriteByte('*') |
| 424 | switch mode { |
| 425 | case fmtTypeID, fmtTypeIDName: |
| 426 | if verb == 'S' { |
| 427 | tconv2(b, t.Elem(), 'S', mode, visited) |
| 428 | return |
| 429 | } |
| 430 | } |
| 431 | tconv2(b, t.Elem(), 'v', mode, visited) |
| 432 | |
| 433 | case TARRAY: |
| 434 | b.WriteByte('[') |
| 435 | b.WriteString(strconv.FormatInt(t.NumElem(), 10)) |
| 436 | b.WriteByte(']') |
| 437 | tconv2(b, t.Elem(), 0, mode, visited) |
| 438 | |
| 439 | case TSLICE: |
| 440 | b.WriteString("[]") |
| 441 | tconv2(b, t.Elem(), 0, mode, visited) |
| 442 | |
| 443 | case TCHAN: |
| 444 | switch t.ChanDir() { |
| 445 | case Crecv: |
| 446 | b.WriteString("<-chan ") |
| 447 | tconv2(b, t.Elem(), 0, mode, visited) |
| 448 | case Csend: |
| 449 | b.WriteString("chan<- ") |
| 450 | tconv2(b, t.Elem(), 0, mode, visited) |
| 451 | default: |
| 452 | b.WriteString("chan ") |
| 453 | if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym() == nil && t.Elem().ChanDir() == Crecv { |
| 454 | b.WriteByte('(') |
| 455 | tconv2(b, t.Elem(), 0, mode, visited) |
| 456 | b.WriteByte(')') |
| 457 | } else { |
| 458 | tconv2(b, t.Elem(), 0, mode, visited) |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | case TMAP: |
| 463 | b.WriteString("map[") |
| 464 | tconv2(b, t.Key(), 0, mode, visited) |
| 465 | b.WriteByte(']') |
| 466 | tconv2(b, t.Elem(), 0, mode, visited) |
| 467 | |
| 468 | case TINTER: |
| 469 | if t.IsEmptyInterface() { |
| 470 | b.WriteString("interface {}") |
| 471 | break |
| 472 | } |
| 473 | b.WriteString("interface {") |
| 474 | for i, f := range t.AllMethods().Slice() { |
| 475 | if i != 0 { |
| 476 | b.WriteByte(';') |
| 477 | } |
| 478 | b.WriteByte(' ') |
| 479 | switch { |
| 480 | case f.Sym == nil: |
| 481 | // Check first that a symbol is defined for this type. |
| 482 | // Wrong interface definitions may have types lacking a symbol. |
| 483 | break |
| 484 | case IsExported(f.Sym.Name): |
| 485 | sconv2(b, f.Sym, 'S', mode) |
| 486 | default: |
| 487 | if mode != fmtTypeIDName { |
| 488 | mode = fmtTypeID |
| 489 | } |
| 490 | sconv2(b, f.Sym, 'v', mode) |
| 491 | } |
| 492 | tconv2(b, f.Type, 'S', mode, visited) |
| 493 | } |
| 494 | if t.AllMethods().Len() != 0 { |
| 495 | b.WriteByte(' ') |
| 496 | } |
| 497 | b.WriteByte('}') |
| 498 | |
| 499 | case TFUNC: |
| 500 | if verb == 'S' { |
| 501 | // no leading func |
| 502 | } else { |
| 503 | if t.Recv() != nil { |
| 504 | b.WriteString("method") |
| 505 | tconv2(b, t.Recvs(), 0, mode, visited) |
| 506 | b.WriteByte(' ') |
| 507 | } |
| 508 | b.WriteString("func") |
| 509 | } |
| 510 | if t.NumTParams() > 0 { |
| 511 | tconv2(b, t.TParams(), 0, mode, visited) |
| 512 | } |
| 513 | tconv2(b, t.Params(), 0, mode, visited) |
| 514 | |
| 515 | switch t.NumResults() { |
| 516 | case 0: |
| 517 | // nothing to do |
| 518 | |
| 519 | case 1: |
| 520 | b.WriteByte(' ') |
| 521 | tconv2(b, t.Results().Field(0).Type, 0, mode, visited) // struct->field->field's type |
| 522 | |
| 523 | default: |
| 524 | b.WriteByte(' ') |
| 525 | tconv2(b, t.Results(), 0, mode, visited) |
| 526 | } |
| 527 | |
| 528 | case TSTRUCT: |
| 529 | if m := t.StructType().Map; m != nil { |
| 530 | mt := m.MapType() |
| 531 | // Format the bucket struct for map[x]y as map.bucket[x]y. |
| 532 | // This avoids a recursive print that generates very long names. |
| 533 | switch t { |
| 534 | case mt.Bucket: |
| 535 | b.WriteString("map.bucket[") |
| 536 | case mt.Hmap: |
| 537 | b.WriteString("map.hdr[") |
| 538 | case mt.Hiter: |
| 539 | b.WriteString("map.iter[") |
| 540 | default: |
| 541 | base.Fatalf("unknown internal map type") |
| 542 | } |
| 543 | tconv2(b, m.Key(), 0, mode, visited) |
| 544 | b.WriteByte(']') |
| 545 | tconv2(b, m.Elem(), 0, mode, visited) |
| 546 | break |
| 547 | } |
| 548 | |
| 549 | if funarg := t.StructType().Funarg; funarg != FunargNone { |
| 550 | open, close := '(', ')' |
| 551 | if funarg == FunargTparams { |
| 552 | open, close = '[', ']' |
| 553 | } |
| 554 | b.WriteByte(byte(open)) |
| 555 | fieldVerb := 'v' |
| 556 | switch mode { |
| 557 | case fmtTypeID, fmtTypeIDName, fmtGo: |
| 558 | // no argument names on function signature, and no "noescape"/"nosplit" tags |
| 559 | fieldVerb = 'S' |
| 560 | } |
| 561 | for i, f := range t.Fields().Slice() { |
| 562 | if i != 0 { |
| 563 | b.WriteString(", ") |
| 564 | } |
| 565 | fldconv(b, f, fieldVerb, mode, visited, funarg) |
| 566 | } |
| 567 | b.WriteByte(byte(close)) |
| 568 | } else { |
| 569 | b.WriteString("struct {") |
| 570 | for i, f := range t.Fields().Slice() { |
| 571 | if i != 0 { |
| 572 | b.WriteByte(';') |
| 573 | } |
| 574 | b.WriteByte(' ') |
| 575 | fldconv(b, f, 'L', mode, visited, funarg) |
| 576 | } |
| 577 | if t.NumFields() != 0 { |
| 578 | b.WriteByte(' ') |
| 579 | } |
| 580 | b.WriteByte('}') |
| 581 | } |
| 582 | |
| 583 | case TFORW: |
| 584 | b.WriteString("undefined") |
| 585 | if t.Sym() != nil { |
| 586 | b.WriteByte(' ') |
| 587 | sconv2(b, t.Sym(), 'v', mode) |
| 588 | } |
| 589 | |
| 590 | case TUNSAFEPTR: |
| 591 | b.WriteString("unsafe.Pointer") |
| 592 | |
| 593 | case TTYPEPARAM: |
| 594 | if t.Sym() != nil { |
| 595 | sconv2(b, t.Sym(), 'v', mode) |
| 596 | } else { |
| 597 | b.WriteString("tp") |
| 598 | // Print out the pointer value for now to disambiguate type params |
| 599 | b.WriteString(fmt.Sprintf("%p", t)) |
| 600 | } |
| 601 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 602 | case TUNION: |
| 603 | for i := 0; i < t.NumTerms(); i++ { |
| 604 | if i > 0 { |
| 605 | b.WriteString("|") |
| 606 | } |
| 607 | elem, tilde := t.Term(i) |
| 608 | if tilde { |
| 609 | b.WriteString("~") |
| 610 | } |
| 611 | tconv2(b, elem, 0, mode, visited) |
| 612 | } |
| 613 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 614 | case Txxx: |
| 615 | b.WriteString("Txxx") |
| 616 | |
| 617 | default: |
| 618 | // Don't know how to handle - fall back to detailed prints |
| 619 | b.WriteString(t.Kind().String()) |
| 620 | b.WriteString(" <") |
| 621 | sconv2(b, t.Sym(), 'v', mode) |
| 622 | b.WriteString(">") |
| 623 | |
| 624 | } |
| 625 | } |
| 626 | |
| 627 | func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Type]int, funarg Funarg) { |
| 628 | if f == nil { |
| 629 | b.WriteString("<T>") |
| 630 | return |
| 631 | } |
| 632 | |
| 633 | var name string |
| 634 | if verb != 'S' { |
| 635 | s := f.Sym |
| 636 | |
| 637 | // Take the name from the original. |
| 638 | if mode == fmtGo { |
| 639 | s = OrigSym(s) |
| 640 | } |
| 641 | |
| 642 | if s != nil && f.Embedded == 0 { |
| 643 | if funarg != FunargNone { |
| 644 | name = fmt.Sprint(f.Nname) |
| 645 | } else if verb == 'L' { |
| 646 | name = s.Name |
| 647 | if name == ".F" { |
| 648 | name = "F" // Hack for toolstash -cmp. |
| 649 | } |
| 650 | if !IsExported(name) && mode != fmtTypeIDName { |
| 651 | name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg) |
| 652 | } |
| 653 | } else { |
| 654 | name = sconv(s, 0, mode) |
| 655 | } |
| 656 | } |
| 657 | } |
| 658 | |
| 659 | if name != "" { |
| 660 | b.WriteString(name) |
| 661 | b.WriteString(" ") |
| 662 | } |
| 663 | |
| 664 | if f.IsDDD() { |
| 665 | var et *Type |
| 666 | if f.Type != nil { |
| 667 | et = f.Type.Elem() |
| 668 | } |
| 669 | b.WriteString("...") |
| 670 | tconv2(b, et, 0, mode, visited) |
| 671 | } else { |
| 672 | tconv2(b, f.Type, 0, mode, visited) |
| 673 | } |
| 674 | |
| 675 | if verb != 'S' && funarg == FunargNone && f.Note != "" { |
| 676 | b.WriteString(" ") |
| 677 | b.WriteString(strconv.Quote(f.Note)) |
| 678 | } |
| 679 | } |
| 680 | |
| 681 | // Val |
| 682 | |
| 683 | func FmtConst(v constant.Value, sharp bool) string { |
| 684 | if !sharp && v.Kind() == constant.Complex { |
| 685 | real, imag := constant.Real(v), constant.Imag(v) |
| 686 | |
| 687 | var re string |
| 688 | sre := constant.Sign(real) |
| 689 | if sre != 0 { |
| 690 | re = real.String() |
| 691 | } |
| 692 | |
| 693 | var im string |
| 694 | sim := constant.Sign(imag) |
| 695 | if sim != 0 { |
| 696 | im = imag.String() |
| 697 | } |
| 698 | |
| 699 | switch { |
| 700 | case sre == 0 && sim == 0: |
| 701 | return "0" |
| 702 | case sre == 0: |
| 703 | return im + "i" |
| 704 | case sim == 0: |
| 705 | return re |
| 706 | case sim < 0: |
| 707 | return fmt.Sprintf("(%s%si)", re, im) |
| 708 | default: |
| 709 | return fmt.Sprintf("(%s+%si)", re, im) |
| 710 | } |
| 711 | } |
| 712 | |
| 713 | return v.String() |
| 714 | } |
| 715 | |
| 716 | // TypeHash computes a hash value for type t to use in type switch statements. |
| 717 | func TypeHash(t *Type) uint32 { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 718 | p := t.NameString() |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 719 | |
| 720 | // Using MD5 is overkill, but reduces accidental collisions. |
| 721 | h := md5.Sum([]byte(p)) |
| 722 | return binary.LittleEndian.Uint32(h[:4]) |
| 723 | } |