Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 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 | "cmd/compile/internal/syntax" |
| 11 | "errors" |
| 12 | "fmt" |
| 13 | "go/constant" |
| 14 | ) |
| 15 | |
| 16 | var nopos syntax.Pos |
| 17 | |
| 18 | // debugging/development support |
| 19 | const debug = false // leave on during development |
| 20 | |
| 21 | // If forceStrict is set, the type-checker enforces additional |
| 22 | // rules not specified by the Go 1 spec, but which will |
| 23 | // catch guaranteed run-time errors if the respective |
| 24 | // code is executed. In other words, programs passing in |
| 25 | // strict mode are Go 1 compliant, but not all Go 1 programs |
| 26 | // will pass in strict mode. The additional rules are: |
| 27 | // |
| 28 | // - A type assertion x.(T) where T is an interface type |
| 29 | // is invalid if any (statically known) method that exists |
| 30 | // for both x and T have different signatures. |
| 31 | // |
| 32 | const forceStrict = false |
| 33 | |
| 34 | // exprInfo stores information about an untyped expression. |
| 35 | type exprInfo struct { |
| 36 | isLhs bool // expression is lhs operand of a shift with delayed type-check |
| 37 | mode operandMode |
| 38 | typ *Basic |
| 39 | val constant.Value // constant value; or nil (if not a constant) |
| 40 | } |
| 41 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 42 | // An environment represents the environment within which an object is |
| 43 | // type-checked. |
| 44 | type environment struct { |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 45 | decl *declInfo // package-level declaration whose init expression/function body is checked |
| 46 | scope *Scope // top-most scope for lookups |
| 47 | pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval) |
| 48 | iota constant.Value // value of iota in a constant declaration; nil otherwise |
| 49 | errpos syntax.Pos // if valid, identifier position of a constant with inherited initializer |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 50 | inTParamList bool // set if inside a type parameter list |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 51 | sig *Signature // function signature if inside a function; nil otherwise |
| 52 | isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check) |
| 53 | hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions |
| 54 | hasCallOrRecv bool // set if an expression contains a function call or channel receive operation |
| 55 | } |
| 56 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 57 | // lookup looks up name in the current environment and returns the matching object, or nil. |
| 58 | func (env *environment) lookup(name string) Object { |
| 59 | _, obj := env.scope.LookupParent(name, env.pos) |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 60 | return obj |
| 61 | } |
| 62 | |
| 63 | // An importKey identifies an imported package by import path and source directory |
| 64 | // (directory containing the file containing the import). In practice, the directory |
| 65 | // may always be the same, or may not matter. Given an (import path, directory), an |
| 66 | // importer must always return the same package (but given two different import paths, |
| 67 | // an importer may still return the same package by mapping them to the same package |
| 68 | // paths). |
| 69 | type importKey struct { |
| 70 | path, dir string |
| 71 | } |
| 72 | |
| 73 | // A dotImportKey describes a dot-imported object in the given scope. |
| 74 | type dotImportKey struct { |
| 75 | scope *Scope |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 76 | name string |
| 77 | } |
| 78 | |
| 79 | // An action describes a (delayed) action. |
| 80 | type action struct { |
| 81 | f func() // action to be executed |
| 82 | desc *actionDesc // action description; may be nil, requires debug to be set |
| 83 | } |
| 84 | |
| 85 | // If debug is set, describef sets a printf-formatted description for action a. |
| 86 | // Otherwise, it is a no-op. |
| 87 | func (a *action) describef(pos poser, format string, args ...interface{}) { |
| 88 | if debug { |
| 89 | a.desc = &actionDesc{pos, format, args} |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | // An actionDesc provides information on an action. |
| 94 | // For debugging only. |
| 95 | type actionDesc struct { |
| 96 | pos poser |
| 97 | format string |
| 98 | args []interface{} |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 99 | } |
| 100 | |
| 101 | // A Checker maintains the state of the type checker. |
| 102 | // It must be created with NewChecker. |
| 103 | type Checker struct { |
| 104 | // package information |
| 105 | // (initialized by NewChecker, valid for the life-time of checker) |
| 106 | conf *Config |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 107 | ctxt *Context // context for de-duplicating instances |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 108 | pkg *Package |
| 109 | *Info |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 110 | version version // 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 |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 114 | |
| 115 | // pkgPathMap maps package names to the set of distinct import paths we've |
| 116 | // seen for that name, anywhere in the import graph. It is used for |
| 117 | // disambiguating package names in error messages. |
| 118 | // |
| 119 | // pkgPathMap is allocated lazily, so that we don't pay the price of building |
| 120 | // it on the happy path. seenPkgMap tracks the packages that we've already |
| 121 | // walked. |
| 122 | pkgPathMap map[string]map[string]bool |
| 123 | seenPkgMap map[*Package]bool |
| 124 | |
| 125 | // information collected during type-checking of a set of package files |
| 126 | // (initialized by Files, valid only for the duration of check.Files; |
| 127 | // maps and lists are allocated on demand) |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 128 | files []*syntax.File // list of package files |
| 129 | imports []*PkgName // list of imported packages |
| 130 | dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through |
| 131 | recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type |
| 132 | mono monoGraph // graph for detecting non-monomorphizable instantiation loops |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 133 | |
| 134 | firstErr error // first error encountered |
| 135 | methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods |
| 136 | untyped map[syntax.Expr]exprInfo // map of expressions without final type |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 137 | delayed []action // stack of delayed action segments; segments are processed in FIFO order |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 138 | objPath []Object // path of object dependencies during type inference (for cycle reporting) |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 139 | defTypes []*Named // defined types created during type checking, for final validation. |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 140 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 141 | // environment within which the current object is type-checked (valid only |
| 142 | // for the duration of type-checking a specific object) |
| 143 | environment |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 144 | |
| 145 | // debugging |
| 146 | indent int // indentation for tracing |
| 147 | } |
| 148 | |
| 149 | // addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists |
| 150 | func (check *Checker) addDeclDep(to Object) { |
| 151 | from := check.decl |
| 152 | if from == nil { |
| 153 | return // not in a package-level init expression |
| 154 | } |
| 155 | if _, found := check.objMap[to]; !found { |
| 156 | return // to is not a package-level object |
| 157 | } |
| 158 | from.addDep(to) |
| 159 | } |
| 160 | |
| 161 | func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) { |
| 162 | m := check.untyped |
| 163 | if m == nil { |
| 164 | m = make(map[syntax.Expr]exprInfo) |
| 165 | check.untyped = m |
| 166 | } |
| 167 | m[e] = exprInfo{lhs, mode, typ, val} |
| 168 | } |
| 169 | |
| 170 | // later pushes f on to the stack of actions that will be processed later; |
| 171 | // either at the end of the current statement, or in case of a local constant |
| 172 | // or variable declaration, before the constant or variable is in scope |
| 173 | // (so that f still sees the scope before any new declarations). |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 174 | // later returns the pushed action so one can provide a description |
| 175 | // via action.describef for debugging, if desired. |
| 176 | func (check *Checker) later(f func()) *action { |
| 177 | i := len(check.delayed) |
| 178 | check.delayed = append(check.delayed, action{f: f}) |
| 179 | return &check.delayed[i] |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | // push pushes obj onto the object path and returns its index in the path. |
| 183 | func (check *Checker) push(obj Object) int { |
| 184 | check.objPath = append(check.objPath, obj) |
| 185 | return len(check.objPath) - 1 |
| 186 | } |
| 187 | |
| 188 | // pop pops and returns the topmost object from the object path. |
| 189 | func (check *Checker) pop() Object { |
| 190 | i := len(check.objPath) - 1 |
| 191 | obj := check.objPath[i] |
| 192 | check.objPath[i] = nil |
| 193 | check.objPath = check.objPath[:i] |
| 194 | return obj |
| 195 | } |
| 196 | |
| 197 | // NewChecker returns a new Checker instance for a given package. |
| 198 | // Package files may be added incrementally via checker.Files. |
| 199 | func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { |
| 200 | // make sure we have a configuration |
| 201 | if conf == nil { |
| 202 | conf = new(Config) |
| 203 | } |
| 204 | |
| 205 | // make sure we have an info struct |
| 206 | if info == nil { |
| 207 | info = new(Info) |
| 208 | } |
| 209 | |
| 210 | version, err := parseGoVersion(conf.GoVersion) |
| 211 | if err != nil { |
| 212 | panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err)) |
| 213 | } |
| 214 | |
| 215 | return &Checker{ |
| 216 | conf: conf, |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 217 | ctxt: conf.Context, |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 218 | pkg: pkg, |
| 219 | Info: info, |
| 220 | version: version, |
| 221 | objMap: make(map[Object]*declInfo), |
| 222 | impMap: make(map[importKey]*Package), |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 223 | } |
| 224 | } |
| 225 | |
| 226 | // initFiles initializes the files-specific portion of checker. |
| 227 | // The provided files must all belong to the same package. |
| 228 | func (check *Checker) initFiles(files []*syntax.File) { |
| 229 | // start with a clean slate (check.Files may be called multiple times) |
| 230 | check.files = nil |
| 231 | check.imports = nil |
| 232 | check.dotImportMap = nil |
| 233 | |
| 234 | check.firstErr = nil |
| 235 | check.methods = nil |
| 236 | check.untyped = nil |
| 237 | check.delayed = nil |
| 238 | |
| 239 | // determine package name and collect valid files |
| 240 | pkg := check.pkg |
| 241 | for _, file := range files { |
| 242 | switch name := file.PkgName.Value; pkg.name { |
| 243 | case "": |
| 244 | if name != "_" { |
| 245 | pkg.name = name |
| 246 | } else { |
| 247 | check.error(file.PkgName, "invalid package name _") |
| 248 | } |
| 249 | fallthrough |
| 250 | |
| 251 | case name: |
| 252 | check.files = append(check.files, file) |
| 253 | |
| 254 | default: |
| 255 | check.errorf(file, "package %s; expected %s", name, pkg.name) |
| 256 | // ignore this file |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | // A bailout panic is used for early termination. |
| 262 | type bailout struct{} |
| 263 | |
| 264 | func (check *Checker) handleBailout(err *error) { |
| 265 | switch p := recover().(type) { |
| 266 | case nil, bailout: |
| 267 | // normal return or early exit |
| 268 | *err = check.firstErr |
| 269 | default: |
| 270 | // re-panic |
| 271 | panic(p) |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | // Files checks the provided files as part of the checker's package. |
| 276 | func (check *Checker) Files(files []*syntax.File) error { return check.checkFiles(files) } |
| 277 | |
| 278 | var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together") |
| 279 | |
| 280 | func (check *Checker) checkFiles(files []*syntax.File) (err error) { |
| 281 | if check.conf.FakeImportC && check.conf.go115UsesCgo { |
| 282 | return errBadCgo |
| 283 | } |
| 284 | |
| 285 | defer check.handleBailout(&err) |
| 286 | |
| 287 | print := func(msg string) { |
| 288 | if check.conf.Trace { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 289 | fmt.Println() |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 290 | fmt.Println(msg) |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | print("== initFiles ==") |
| 295 | check.initFiles(files) |
| 296 | |
| 297 | print("== collectObjects ==") |
| 298 | check.collectObjects() |
| 299 | |
| 300 | print("== packageObjects ==") |
| 301 | check.packageObjects() |
| 302 | |
| 303 | print("== processDelayed ==") |
| 304 | check.processDelayed(0) // incl. all functions |
| 305 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 306 | print("== expandDefTypes ==") |
| 307 | check.expandDefTypes() |
| 308 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 309 | print("== initOrder ==") |
| 310 | check.initOrder() |
| 311 | |
| 312 | if !check.conf.DisableUnusedImportCheck { |
| 313 | print("== unusedImports ==") |
| 314 | check.unusedImports() |
| 315 | } |
| 316 | |
| 317 | print("== recordUntyped ==") |
| 318 | check.recordUntyped() |
| 319 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 320 | if check.firstErr == nil { |
| 321 | // TODO(mdempsky): Ensure monomorph is safe when errors exist. |
| 322 | check.monomorph() |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 323 | } |
| 324 | |
| 325 | check.pkg.complete = true |
| 326 | |
| 327 | // no longer needed - release memory |
| 328 | check.imports = nil |
| 329 | check.dotImportMap = nil |
| 330 | check.pkgPathMap = nil |
| 331 | check.seenPkgMap = nil |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 332 | check.recvTParamMap = nil |
| 333 | check.defTypes = nil |
| 334 | check.ctxt = nil |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 335 | |
| 336 | // TODO(gri) There's more memory we should release at this point. |
| 337 | |
| 338 | return |
| 339 | } |
| 340 | |
| 341 | // processDelayed processes all delayed actions pushed after top. |
| 342 | func (check *Checker) processDelayed(top int) { |
| 343 | // If each delayed action pushes a new action, the |
| 344 | // stack will continue to grow during this loop. |
| 345 | // However, it is only processing functions (which |
| 346 | // are processed in a delayed fashion) that may |
| 347 | // add more actions (such as nested functions), so |
| 348 | // this is a sufficiently bounded process. |
| 349 | for i := top; i < len(check.delayed); i++ { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 350 | a := &check.delayed[i] |
| 351 | if check.conf.Trace && a.desc != nil { |
| 352 | fmt.Println() |
| 353 | check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...) |
| 354 | } |
| 355 | a.f() // may append to check.delayed |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 356 | } |
| 357 | assert(top <= len(check.delayed)) // stack must not have shrunk |
| 358 | check.delayed = check.delayed[:top] |
| 359 | } |
| 360 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 361 | func (check *Checker) expandDefTypes() { |
| 362 | // Ensure that every defined type created in the course of type-checking has |
| 363 | // either non-*Named underlying, or is unresolved. |
| 364 | // |
| 365 | // This guarantees that we don't leak any types whose underlying is *Named, |
| 366 | // because any unresolved instances will lazily compute their underlying by |
| 367 | // substituting in the underlying of their origin. The origin must have |
| 368 | // either been imported or type-checked and expanded here, and in either case |
| 369 | // its underlying will be fully expanded. |
| 370 | for i := 0; i < len(check.defTypes); i++ { |
| 371 | n := check.defTypes[i] |
| 372 | switch n.underlying.(type) { |
| 373 | case nil: |
| 374 | if n.resolver == nil { |
| 375 | panic("nil underlying") |
| 376 | } |
| 377 | case *Named: |
| 378 | n.under() // n.under may add entries to check.defTypes |
| 379 | } |
| 380 | n.check = nil |
| 381 | } |
| 382 | } |
| 383 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 384 | func (check *Checker) record(x *operand) { |
| 385 | // convert x into a user-friendly set of values |
| 386 | // TODO(gri) this code can be simplified |
| 387 | var typ Type |
| 388 | var val constant.Value |
| 389 | switch x.mode { |
| 390 | case invalid: |
| 391 | typ = Typ[Invalid] |
| 392 | case novalue: |
| 393 | typ = (*Tuple)(nil) |
| 394 | case constant_: |
| 395 | typ = x.typ |
| 396 | val = x.val |
| 397 | default: |
| 398 | typ = x.typ |
| 399 | } |
| 400 | assert(x.expr != nil && typ != nil) |
| 401 | |
| 402 | if isUntyped(typ) { |
| 403 | // delay type and value recording until we know the type |
| 404 | // or until the end of type checking |
| 405 | check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val) |
| 406 | } else { |
| 407 | check.recordTypeAndValue(x.expr, x.mode, typ, val) |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | func (check *Checker) recordUntyped() { |
| 412 | if !debug && check.Types == nil { |
| 413 | return // nothing to do |
| 414 | } |
| 415 | |
| 416 | for x, info := range check.untyped { |
| 417 | if debug && isTyped(info.typ) { |
| 418 | check.dump("%v: %s (type %s) is typed", posFor(x), x, info.typ) |
| 419 | unreachable() |
| 420 | } |
| 421 | check.recordTypeAndValue(x, info.mode, info.typ, info.val) |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) { |
| 426 | assert(x != nil) |
| 427 | assert(typ != nil) |
| 428 | if mode == invalid { |
| 429 | return // omit |
| 430 | } |
| 431 | if mode == constant_ { |
| 432 | assert(val != nil) |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 433 | // We check allBasic(typ, IsConstType) here as constant expressions may be |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 434 | // recorded as type parameters. |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 435 | assert(typ == Typ[Invalid] || allBasic(typ, IsConstType)) |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 436 | } |
| 437 | if m := check.Types; m != nil { |
| 438 | m[x] = TypeAndValue{mode, typ, val} |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) { |
| 443 | // f must be a (possibly parenthesized, possibly qualified) |
| 444 | // identifier denoting a built-in (including unsafe's non-constant |
| 445 | // functions Add and Slice): record the signature for f and possible |
| 446 | // children. |
| 447 | for { |
| 448 | check.recordTypeAndValue(f, builtin, sig, nil) |
| 449 | switch p := f.(type) { |
| 450 | case *syntax.Name, *syntax.SelectorExpr: |
| 451 | return // we're done |
| 452 | case *syntax.ParenExpr: |
| 453 | f = p.X |
| 454 | default: |
| 455 | unreachable() |
| 456 | } |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) { |
| 461 | assert(x != nil) |
| 462 | if a[0] == nil || a[1] == nil { |
| 463 | return |
| 464 | } |
| 465 | assert(isTyped(a[0]) && isTyped(a[1]) && (isBoolean(a[1]) || a[1] == universeError)) |
| 466 | if m := check.Types; m != nil { |
| 467 | for { |
| 468 | tv := m[x] |
| 469 | assert(tv.Type != nil) // should have been recorded already |
| 470 | pos := x.Pos() |
| 471 | tv.Type = NewTuple( |
| 472 | NewVar(pos, check.pkg, "", a[0]), |
| 473 | NewVar(pos, check.pkg, "", a[1]), |
| 474 | ) |
| 475 | m[x] = tv |
| 476 | // if x is a parenthesized expression (p.X), update p.X |
| 477 | p, _ := x.(*syntax.ParenExpr) |
| 478 | if p == nil { |
| 479 | break |
| 480 | } |
| 481 | x = p.X |
| 482 | } |
| 483 | } |
| 484 | } |
| 485 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 486 | // recordInstance records instantiation information into check.Info, if the |
| 487 | // Instances map is non-nil. The given expr must be an ident, selector, or |
| 488 | // index (list) expr with ident or selector operand. |
| 489 | // |
| 490 | // TODO(rfindley): the expr parameter is fragile. See if we can access the |
| 491 | // instantiated identifier in some other way. |
| 492 | func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) { |
| 493 | ident := instantiatedIdent(expr) |
| 494 | assert(ident != nil) |
| 495 | assert(typ != nil) |
| 496 | if m := check.Instances; m != nil { |
| 497 | m[ident] = Instance{newTypeList(targs), typ} |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 498 | } |
| 499 | } |
| 500 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 501 | func instantiatedIdent(expr syntax.Expr) *syntax.Name { |
| 502 | var selOrIdent syntax.Expr |
| 503 | switch e := expr.(type) { |
| 504 | case *syntax.IndexExpr: |
| 505 | selOrIdent = e.X |
| 506 | case *syntax.SelectorExpr, *syntax.Name: |
| 507 | selOrIdent = e |
| 508 | } |
| 509 | switch x := selOrIdent.(type) { |
| 510 | case *syntax.Name: |
| 511 | return x |
| 512 | case *syntax.SelectorExpr: |
| 513 | return x.Sel |
| 514 | } |
| 515 | panic("instantiated ident not found") |
| 516 | } |
| 517 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 518 | func (check *Checker) recordDef(id *syntax.Name, obj Object) { |
| 519 | assert(id != nil) |
| 520 | if m := check.Defs; m != nil { |
| 521 | m[id] = obj |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | func (check *Checker) recordUse(id *syntax.Name, obj Object) { |
| 526 | assert(id != nil) |
| 527 | assert(obj != nil) |
| 528 | if m := check.Uses; m != nil { |
| 529 | m[id] = obj |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | func (check *Checker) recordImplicit(node syntax.Node, obj Object) { |
| 534 | assert(node != nil) |
| 535 | assert(obj != nil) |
| 536 | if m := check.Implicits; m != nil { |
| 537 | m[node] = obj |
| 538 | } |
| 539 | } |
| 540 | |
| 541 | func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { |
| 542 | assert(obj != nil && (recv == nil || len(index) > 0)) |
| 543 | check.recordUse(x.Sel, obj) |
| 544 | if m := check.Selections; m != nil { |
| 545 | m[x] = &Selection{kind, recv, obj, index, indirect} |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | func (check *Checker) recordScope(node syntax.Node, scope *Scope) { |
| 550 | assert(node != nil) |
| 551 | assert(scope != nil) |
| 552 | if m := check.Scopes; m != nil { |
| 553 | m[node] = scope |
| 554 | } |
| 555 | } |