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