github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/types2/check.go (about) 1 // Copyright 2011 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 the Check function, which drives type-checking. 6 7 package types2 8 9 import ( 10 "errors" 11 "fmt" 12 "go/constant" 13 14 "github.com/go-asm/go/cmd/compile/syntax" 15 "github.com/go-asm/go/godebug" 16 . "github.com/go-asm/go/types/errors" 17 ) 18 19 // nopos indicates an unknown position 20 var nopos syntax.Pos 21 22 // debugging/development support 23 const debug = false // leave on during development 24 25 // gotypesalias controls the use of Alias types. 26 var gotypesalias = godebug.New("#gotypesalias") 27 28 // exprInfo stores information about an untyped expression. 29 type exprInfo struct { 30 isLhs bool // expression is lhs operand of a shift with delayed type-check 31 mode operandMode 32 typ *Basic 33 val constant.Value // constant value; or nil (if not a constant) 34 } 35 36 // An environment represents the environment within which an object is 37 // type-checked. 38 type environment struct { 39 decl *declInfo // package-level declaration whose init expression/function body is checked 40 scope *Scope // top-most scope for lookups 41 pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval) 42 iota constant.Value // value of iota in a constant declaration; nil otherwise 43 errpos syntax.Pos // if valid, identifier position of a constant with inherited initializer 44 inTParamList bool // set if inside a type parameter list 45 sig *Signature // function signature if inside a function; nil otherwise 46 isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check) 47 hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions 48 hasCallOrRecv bool // set if an expression contains a function call or channel receive operation 49 } 50 51 // lookup looks up name in the current environment and returns the matching object, or nil. 52 func (env *environment) lookup(name string) Object { 53 _, obj := env.scope.LookupParent(name, env.pos) 54 return obj 55 } 56 57 // An importKey identifies an imported package by import path and source directory 58 // (directory containing the file containing the import). In practice, the directory 59 // may always be the same, or may not matter. Given an (import path, directory), an 60 // importer must always return the same package (but given two different import paths, 61 // an importer may still return the same package by mapping them to the same package 62 // paths). 63 type importKey struct { 64 path, dir string 65 } 66 67 // A dotImportKey describes a dot-imported object in the given scope. 68 type dotImportKey struct { 69 scope *Scope 70 name string 71 } 72 73 // An action describes a (delayed) action. 74 type action struct { 75 f func() // action to be executed 76 desc *actionDesc // action description; may be nil, requires debug to be set 77 } 78 79 // If debug is set, describef sets a printf-formatted description for action a. 80 // Otherwise, it is a no-op. 81 func (a *action) describef(pos poser, format string, args ...interface{}) { 82 if debug { 83 a.desc = &actionDesc{pos, format, args} 84 } 85 } 86 87 // An actionDesc provides information on an action. 88 // For debugging only. 89 type actionDesc struct { 90 pos poser 91 format string 92 args []interface{} 93 } 94 95 // A Checker maintains the state of the type checker. 96 // It must be created with NewChecker. 97 type Checker struct { 98 // package information 99 // (initialized by NewChecker, valid for the life-time of checker) 100 101 // If enableAlias is set, alias declarations produce an Alias type. 102 // Otherwise the alias information is only in the type name, which 103 // points directly to the actual (aliased) type. 104 enableAlias bool 105 106 conf *Config 107 ctxt *Context // context for de-duplicating instances 108 pkg *Package 109 *Info 110 version goVersion // accepted language version 111 nextID uint64 // unique Id for type parameters (first valid Id is 1) 112 objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info 113 impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package 114 valids instanceLookup // valid *Named (incl. instantiated) types per the validType check 115 116 // pkgPathMap maps package names to the set of distinct import paths we've 117 // seen for that name, anywhere in the import graph. It is used for 118 // disambiguating package names in error messages. 119 // 120 // pkgPathMap is allocated lazily, so that we don't pay the price of building 121 // it on the happy path. seenPkgMap tracks the packages that we've already 122 // walked. 123 pkgPathMap map[string]map[string]bool 124 seenPkgMap map[*Package]bool 125 126 // information collected during type-checking of a set of package files 127 // (initialized by Files, valid only for the duration of check.Files; 128 // maps and lists are allocated on demand) 129 files []*syntax.File // list of package files 130 versions map[*syntax.PosBase]string // maps file bases to version strings (each file has an entry) 131 imports []*PkgName // list of imported packages 132 dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through 133 recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type 134 brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types 135 unionTypeSets map[*Union]*_TypeSet // computed type sets for union types 136 mono monoGraph // graph for detecting non-monomorphizable instantiation loops 137 138 firstErr error // first error encountered 139 methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods 140 untyped map[syntax.Expr]exprInfo // map of expressions without final type 141 delayed []action // stack of delayed action segments; segments are processed in FIFO order 142 objPath []Object // path of object dependencies during type inference (for cycle reporting) 143 cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking 144 145 // environment within which the current object is type-checked (valid only 146 // for the duration of type-checking a specific object) 147 environment 148 149 // debugging 150 indent int // indentation for tracing 151 } 152 153 // addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists 154 func (check *Checker) addDeclDep(to Object) { 155 from := check.decl 156 if from == nil { 157 return // not in a package-level init expression 158 } 159 if _, found := check.objMap[to]; !found { 160 return // to is not a package-level object 161 } 162 from.addDep(to) 163 } 164 165 // Note: The following three alias-related functions are only used 166 // when Alias types are not enabled. 167 168 // brokenAlias records that alias doesn't have a determined type yet. 169 // It also sets alias.typ to Typ[Invalid]. 170 // Not used if check.enableAlias is set. 171 func (check *Checker) brokenAlias(alias *TypeName) { 172 assert(!check.enableAlias) 173 if check.brokenAliases == nil { 174 check.brokenAliases = make(map[*TypeName]bool) 175 } 176 check.brokenAliases[alias] = true 177 alias.typ = Typ[Invalid] 178 } 179 180 // validAlias records that alias has the valid type typ (possibly Typ[Invalid]). 181 func (check *Checker) validAlias(alias *TypeName, typ Type) { 182 assert(!check.enableAlias) 183 delete(check.brokenAliases, alias) 184 alias.typ = typ 185 } 186 187 // isBrokenAlias reports whether alias doesn't have a determined type yet. 188 func (check *Checker) isBrokenAlias(alias *TypeName) bool { 189 assert(!check.enableAlias) 190 return check.brokenAliases[alias] 191 } 192 193 func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) { 194 m := check.untyped 195 if m == nil { 196 m = make(map[syntax.Expr]exprInfo) 197 check.untyped = m 198 } 199 m[e] = exprInfo{lhs, mode, typ, val} 200 } 201 202 // later pushes f on to the stack of actions that will be processed later; 203 // either at the end of the current statement, or in case of a local constant 204 // or variable declaration, before the constant or variable is in scope 205 // (so that f still sees the scope before any new declarations). 206 // later returns the pushed action so one can provide a description 207 // via action.describef for debugging, if desired. 208 func (check *Checker) later(f func()) *action { 209 i := len(check.delayed) 210 check.delayed = append(check.delayed, action{f: f}) 211 return &check.delayed[i] 212 } 213 214 // push pushes obj onto the object path and returns its index in the path. 215 func (check *Checker) push(obj Object) int { 216 check.objPath = append(check.objPath, obj) 217 return len(check.objPath) - 1 218 } 219 220 // pop pops and returns the topmost object from the object path. 221 func (check *Checker) pop() Object { 222 i := len(check.objPath) - 1 223 obj := check.objPath[i] 224 check.objPath[i] = nil 225 check.objPath = check.objPath[:i] 226 return obj 227 } 228 229 type cleaner interface { 230 cleanup() 231 } 232 233 // needsCleanup records objects/types that implement the cleanup method 234 // which will be called at the end of type-checking. 235 func (check *Checker) needsCleanup(c cleaner) { 236 check.cleaners = append(check.cleaners, c) 237 } 238 239 // NewChecker returns a new Checker instance for a given package. 240 // Package files may be added incrementally via checker.Files. 241 func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { 242 // make sure we have a configuration 243 if conf == nil { 244 conf = new(Config) 245 } 246 247 // make sure we have an info struct 248 if info == nil { 249 info = new(Info) 250 } 251 252 // Note: clients may call NewChecker with the Unsafe package, which is 253 // globally shared and must not be mutated. Therefore NewChecker must not 254 // mutate *pkg. 255 // 256 // (previously, pkg.goVersion was mutated here: go.dev/issue/61212) 257 258 return &Checker{ 259 enableAlias: gotypesalias.Value() == "1", 260 conf: conf, 261 ctxt: conf.Context, 262 pkg: pkg, 263 Info: info, 264 version: asGoVersion(conf.GoVersion), 265 objMap: make(map[Object]*declInfo), 266 impMap: make(map[importKey]*Package), 267 } 268 } 269 270 // initFiles initializes the files-specific portion of checker. 271 // The provided files must all belong to the same package. 272 func (check *Checker) initFiles(files []*syntax.File) { 273 // start with a clean slate (check.Files may be called multiple times) 274 check.files = nil 275 check.imports = nil 276 check.dotImportMap = nil 277 278 check.firstErr = nil 279 check.methods = nil 280 check.untyped = nil 281 check.delayed = nil 282 check.objPath = nil 283 check.cleaners = nil 284 285 // determine package name and collect valid files 286 pkg := check.pkg 287 for _, file := range files { 288 switch name := file.PkgName.Value; pkg.name { 289 case "": 290 if name != "_" { 291 pkg.name = name 292 } else { 293 check.error(file.PkgName, BlankPkgName, "invalid package name _") 294 } 295 fallthrough 296 297 case name: 298 check.files = append(check.files, file) 299 300 default: 301 check.errorf(file, MismatchedPkgName, "package %s; expected %s", name, pkg.name) 302 // ignore this file 303 } 304 } 305 306 // reuse Info.FileVersions if provided 307 versions := check.Info.FileVersions 308 if versions == nil { 309 versions = make(map[*syntax.PosBase]string) 310 } 311 check.versions = versions 312 313 pkgVersionOk := check.version.isValid() 314 downgradeOk := check.version.cmp(go1_21) >= 0 315 316 // determine Go version for each file 317 for _, file := range check.files { 318 // use unaltered Config.GoVersion by default 319 // (This version string may contain dot-release numbers as in go1.20.1, 320 // unlike file versions which are Go language versions only, if valid.) 321 v := check.conf.GoVersion 322 // use the file version, if applicable 323 // (file versions are either the empty string or of the form go1.dd) 324 if pkgVersionOk { 325 fileVersion := asGoVersion(file.GoVersion) 326 if fileVersion.isValid() { 327 cmp := fileVersion.cmp(check.version) 328 // Go 1.21 introduced the feature of setting the go.mod 329 // go line to an early version of Go and allowing //go:build lines 330 // to “upgrade” (cmp > 0) the Go version in a given file. 331 // We can do that backwards compatibly. 332 // 333 // Go 1.21 also introduced the feature of allowing //go:build lines 334 // to “downgrade” (cmp < 0) the Go version in a given file. 335 // That can't be done compatibly in general, since before the 336 // build lines were ignored and code got the module's Go version. 337 // To work around this, downgrades are only allowed when the 338 // module's Go version is Go 1.21 or later. 339 // 340 // If there is no valid check.version, then we don't really know what 341 // Go version to apply. 342 // Legacy tools may do this, and they historically have accepted everything. 343 // Preserve that behavior by ignoring //go:build constraints entirely in that 344 // case (!pkgVersionOk). 345 if cmp > 0 || cmp < 0 && downgradeOk { 346 v = file.GoVersion 347 } 348 } 349 } 350 versions[base(file.Pos())] = v // base(file.Pos()) may be nil for tests 351 } 352 } 353 354 // A bailout panic is used for early termination. 355 type bailout struct{} 356 357 func (check *Checker) handleBailout(err *error) { 358 switch p := recover().(type) { 359 case nil, bailout: 360 // normal return or early exit 361 *err = check.firstErr 362 default: 363 // re-panic 364 panic(p) 365 } 366 } 367 368 // Files checks the provided files as part of the checker's package. 369 func (check *Checker) Files(files []*syntax.File) error { return check.checkFiles(files) } 370 371 var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together") 372 373 func (check *Checker) checkFiles(files []*syntax.File) (err error) { 374 if check.pkg == Unsafe { 375 // Defensive handling for Unsafe, which cannot be type checked, and must 376 // not be mutated. See https://go.dev/issue/61212 for an example of where 377 // Unsafe is passed to NewChecker. 378 return nil 379 } 380 381 // Note: NewChecker doesn't return an error, so we need to check the version here. 382 if check.version.cmp(go_current) > 0 { 383 return fmt.Errorf("package requires newer Go version %v", check.version) 384 } 385 if check.conf.FakeImportC && check.conf.go115UsesCgo { 386 return errBadCgo 387 } 388 389 defer check.handleBailout(&err) 390 391 print := func(msg string) { 392 if check.conf.Trace { 393 fmt.Println() 394 fmt.Println(msg) 395 } 396 } 397 398 print("== initFiles ==") 399 check.initFiles(files) 400 401 print("== collectObjects ==") 402 check.collectObjects() 403 404 print("== packageObjects ==") 405 check.packageObjects() 406 407 print("== processDelayed ==") 408 check.processDelayed(0) // incl. all functions 409 410 print("== cleanup ==") 411 check.cleanup() 412 413 print("== initOrder ==") 414 check.initOrder() 415 416 if !check.conf.DisableUnusedImportCheck { 417 print("== unusedImports ==") 418 check.unusedImports() 419 } 420 421 print("== recordUntyped ==") 422 check.recordUntyped() 423 424 if check.firstErr == nil { 425 // TODO(mdempsky): Ensure monomorph is safe when errors exist. 426 check.monomorph() 427 } 428 429 check.pkg.goVersion = check.conf.GoVersion 430 check.pkg.complete = true 431 432 // no longer needed - release memory 433 check.imports = nil 434 check.dotImportMap = nil 435 check.pkgPathMap = nil 436 check.seenPkgMap = nil 437 check.recvTParamMap = nil 438 check.brokenAliases = nil 439 check.unionTypeSets = nil 440 check.ctxt = nil 441 442 // TODO(gri) There's more memory we should release at this point. 443 444 return 445 } 446 447 // processDelayed processes all delayed actions pushed after top. 448 func (check *Checker) processDelayed(top int) { 449 // If each delayed action pushes a new action, the 450 // stack will continue to grow during this loop. 451 // However, it is only processing functions (which 452 // are processed in a delayed fashion) that may 453 // add more actions (such as nested functions), so 454 // this is a sufficiently bounded process. 455 for i := top; i < len(check.delayed); i++ { 456 a := &check.delayed[i] 457 if check.conf.Trace { 458 if a.desc != nil { 459 check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...) 460 } else { 461 check.trace(nopos, "-- delayed %p", a.f) 462 } 463 } 464 a.f() // may append to check.delayed 465 if check.conf.Trace { 466 fmt.Println() 467 } 468 } 469 assert(top <= len(check.delayed)) // stack must not have shrunk 470 check.delayed = check.delayed[:top] 471 } 472 473 // cleanup runs cleanup for all collected cleaners. 474 func (check *Checker) cleanup() { 475 // Don't use a range clause since Named.cleanup may add more cleaners. 476 for i := 0; i < len(check.cleaners); i++ { 477 check.cleaners[i].cleanup() 478 } 479 check.cleaners = nil 480 } 481 482 func (check *Checker) record(x *operand) { 483 // convert x into a user-friendly set of values 484 // TODO(gri) this code can be simplified 485 var typ Type 486 var val constant.Value 487 switch x.mode { 488 case invalid: 489 typ = Typ[Invalid] 490 case novalue: 491 typ = (*Tuple)(nil) 492 case constant_: 493 typ = x.typ 494 val = x.val 495 default: 496 typ = x.typ 497 } 498 assert(x.expr != nil && typ != nil) 499 500 if isUntyped(typ) { 501 // delay type and value recording until we know the type 502 // or until the end of type checking 503 check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val) 504 } else { 505 check.recordTypeAndValue(x.expr, x.mode, typ, val) 506 } 507 } 508 509 func (check *Checker) recordUntyped() { 510 if !debug && !check.recordTypes() { 511 return // nothing to do 512 } 513 514 for x, info := range check.untyped { 515 if debug && isTyped(info.typ) { 516 check.dump("%v: %s (type %s) is typed", atPos(x), x, info.typ) 517 unreachable() 518 } 519 check.recordTypeAndValue(x, info.mode, info.typ, info.val) 520 } 521 } 522 523 func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) { 524 assert(x != nil) 525 assert(typ != nil) 526 if mode == invalid { 527 return // omit 528 } 529 if mode == constant_ { 530 assert(val != nil) 531 // We check allBasic(typ, IsConstType) here as constant expressions may be 532 // recorded as type parameters. 533 assert(!isValid(typ) || allBasic(typ, IsConstType)) 534 } 535 if m := check.Types; m != nil { 536 m[x] = TypeAndValue{mode, typ, val} 537 } 538 if check.StoreTypesInSyntax { 539 tv := TypeAndValue{mode, typ, val} 540 stv := syntax.TypeAndValue{Type: typ, Value: val} 541 if tv.IsVoid() { 542 stv.SetIsVoid() 543 } 544 if tv.IsType() { 545 stv.SetIsType() 546 } 547 if tv.IsBuiltin() { 548 stv.SetIsBuiltin() 549 } 550 if tv.IsValue() { 551 stv.SetIsValue() 552 } 553 if tv.IsNil() { 554 stv.SetIsNil() 555 } 556 if tv.Addressable() { 557 stv.SetAddressable() 558 } 559 if tv.Assignable() { 560 stv.SetAssignable() 561 } 562 if tv.HasOk() { 563 stv.SetHasOk() 564 } 565 x.SetTypeInfo(stv) 566 } 567 } 568 569 func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) { 570 // f must be a (possibly parenthesized, possibly qualified) 571 // identifier denoting a built-in (including unsafe's non-constant 572 // functions Add and Slice): record the signature for f and possible 573 // children. 574 for { 575 check.recordTypeAndValue(f, builtin, sig, nil) 576 switch p := f.(type) { 577 case *syntax.Name, *syntax.SelectorExpr: 578 return // we're done 579 case *syntax.ParenExpr: 580 f = p.X 581 default: 582 unreachable() 583 } 584 } 585 } 586 587 // recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context 588 // (and therefore has tuple type). 589 func (check *Checker) recordCommaOkTypes(x syntax.Expr, a []*operand) { 590 assert(x != nil) 591 assert(len(a) == 2) 592 if a[0].mode == invalid { 593 return 594 } 595 t0, t1 := a[0].typ, a[1].typ 596 assert(isTyped(t0) && isTyped(t1) && (isBoolean(t1) || t1 == universeError)) 597 if m := check.Types; m != nil { 598 for { 599 tv := m[x] 600 assert(tv.Type != nil) // should have been recorded already 601 pos := x.Pos() 602 tv.Type = NewTuple( 603 NewVar(pos, check.pkg, "", t0), 604 NewVar(pos, check.pkg, "", t1), 605 ) 606 m[x] = tv 607 // if x is a parenthesized expression (p.X), update p.X 608 p, _ := x.(*syntax.ParenExpr) 609 if p == nil { 610 break 611 } 612 x = p.X 613 } 614 } 615 if check.StoreTypesInSyntax { 616 // Note: this loop is duplicated because the type of tv is different. 617 // Above it is types2.TypeAndValue, here it is syntax.TypeAndValue. 618 for { 619 tv := x.GetTypeInfo() 620 assert(tv.Type != nil) // should have been recorded already 621 pos := x.Pos() 622 tv.Type = NewTuple( 623 NewVar(pos, check.pkg, "", t0), 624 NewVar(pos, check.pkg, "", t1), 625 ) 626 x.SetTypeInfo(tv) 627 p, _ := x.(*syntax.ParenExpr) 628 if p == nil { 629 break 630 } 631 x = p.X 632 } 633 } 634 } 635 636 // recordInstance records instantiation information into check.Info, if the 637 // Instances map is non-nil. The given expr must be an ident, selector, or 638 // index (list) expr with ident or selector operand. 639 // 640 // TODO(rfindley): the expr parameter is fragile. See if we can access the 641 // instantiated identifier in some other way. 642 func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) { 643 ident := instantiatedIdent(expr) 644 assert(ident != nil) 645 assert(typ != nil) 646 if m := check.Instances; m != nil { 647 m[ident] = Instance{newTypeList(targs), typ} 648 } 649 } 650 651 func instantiatedIdent(expr syntax.Expr) *syntax.Name { 652 var selOrIdent syntax.Expr 653 switch e := expr.(type) { 654 case *syntax.IndexExpr: 655 selOrIdent = e.X 656 case *syntax.SelectorExpr, *syntax.Name: 657 selOrIdent = e 658 } 659 switch x := selOrIdent.(type) { 660 case *syntax.Name: 661 return x 662 case *syntax.SelectorExpr: 663 return x.Sel 664 } 665 panic("instantiated ident not found") 666 } 667 668 func (check *Checker) recordDef(id *syntax.Name, obj Object) { 669 assert(id != nil) 670 if m := check.Defs; m != nil { 671 m[id] = obj 672 } 673 } 674 675 func (check *Checker) recordUse(id *syntax.Name, obj Object) { 676 assert(id != nil) 677 assert(obj != nil) 678 if m := check.Uses; m != nil { 679 m[id] = obj 680 } 681 } 682 683 func (check *Checker) recordImplicit(node syntax.Node, obj Object) { 684 assert(node != nil) 685 assert(obj != nil) 686 if m := check.Implicits; m != nil { 687 m[node] = obj 688 } 689 } 690 691 func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { 692 assert(obj != nil && (recv == nil || len(index) > 0)) 693 check.recordUse(x.Sel, obj) 694 if m := check.Selections; m != nil { 695 m[x] = &Selection{kind, recv, obj, index, indirect} 696 } 697 } 698 699 func (check *Checker) recordScope(node syntax.Node, scope *Scope) { 700 assert(node != nil) 701 assert(scope != nil) 702 if m := check.Scopes; m != nil { 703 m[node] = scope 704 } 705 }