]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: move type recording functionality in its own files
authorRobert Griesemer <gri@golang.org>
Wed, 31 Jul 2024 16:40:33 +0000 (09:40 -0700)
committerGopher Robot <gobot@golang.org>
Wed, 31 Jul 2024 21:23:48 +0000 (21:23 +0000)
This CL simply moves recording functions into recording.go and
adjust the imports as needed. There are no other code changes.
Preparation for generating go/types/recording.go from types2
sources.

Change-Id: Idc5850462a78afb1bfce78ba216722d07c8bca1e
Reviewed-on: https://go-review.googlesource.com/c/go/+/602116
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Tim King <taking@google.com>
src/cmd/compile/internal/types2/check.go
src/cmd/compile/internal/types2/recording.go [new file with mode: 0644]
src/go/types/check.go
src/go/types/recording.go [new file with mode: 0644]

index 44274b194bc462d1e9e810a59bc4c83b9a82142a..f344142011686f187ca91b164a38ea0cc6d0c1a1 100644 (file)
@@ -514,228 +514,3 @@ func (check *Checker) cleanup() {
        }
        check.cleaners = nil
 }
-
-func (check *Checker) record(x *operand) {
-       // convert x into a user-friendly set of values
-       // TODO(gri) this code can be simplified
-       var typ Type
-       var val constant.Value
-       switch x.mode {
-       case invalid:
-               typ = Typ[Invalid]
-       case novalue:
-               typ = (*Tuple)(nil)
-       case constant_:
-               typ = x.typ
-               val = x.val
-       default:
-               typ = x.typ
-       }
-       assert(x.expr != nil && typ != nil)
-
-       if isUntyped(typ) {
-               // delay type and value recording until we know the type
-               // or until the end of type checking
-               check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
-       } else {
-               check.recordTypeAndValue(x.expr, x.mode, typ, val)
-       }
-}
-
-func (check *Checker) recordUntyped() {
-       if !debug && !check.recordTypes() {
-               return // nothing to do
-       }
-
-       for x, info := range check.untyped {
-               if debug && isTyped(info.typ) {
-                       check.dump("%v: %s (type %s) is typed", atPos(x), x, info.typ)
-                       panic("unreachable")
-               }
-               check.recordTypeAndValue(x, info.mode, info.typ, info.val)
-       }
-}
-
-func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) {
-       assert(x != nil)
-       assert(typ != nil)
-       if mode == invalid {
-               return // omit
-       }
-       if mode == constant_ {
-               assert(val != nil)
-               // We check allBasic(typ, IsConstType) here as constant expressions may be
-               // recorded as type parameters.
-               assert(!isValid(typ) || allBasic(typ, IsConstType))
-       }
-       if m := check.Types; m != nil {
-               m[x] = TypeAndValue{mode, typ, val}
-       }
-       if check.StoreTypesInSyntax {
-               tv := TypeAndValue{mode, typ, val}
-               stv := syntax.TypeAndValue{Type: typ, Value: val}
-               if tv.IsVoid() {
-                       stv.SetIsVoid()
-               }
-               if tv.IsType() {
-                       stv.SetIsType()
-               }
-               if tv.IsBuiltin() {
-                       stv.SetIsBuiltin()
-               }
-               if tv.IsValue() {
-                       stv.SetIsValue()
-               }
-               if tv.IsNil() {
-                       stv.SetIsNil()
-               }
-               if tv.Addressable() {
-                       stv.SetAddressable()
-               }
-               if tv.Assignable() {
-                       stv.SetAssignable()
-               }
-               if tv.HasOk() {
-                       stv.SetHasOk()
-               }
-               x.SetTypeInfo(stv)
-       }
-}
-
-func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
-       // f must be a (possibly parenthesized, possibly qualified)
-       // identifier denoting a built-in (including unsafe's non-constant
-       // functions Add and Slice): record the signature for f and possible
-       // children.
-       for {
-               check.recordTypeAndValue(f, builtin, sig, nil)
-               switch p := f.(type) {
-               case *syntax.Name, *syntax.SelectorExpr:
-                       return // we're done
-               case *syntax.ParenExpr:
-                       f = p.X
-               default:
-                       panic("unreachable")
-               }
-       }
-}
-
-// recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context
-// (and therefore has tuple type).
-func (check *Checker) recordCommaOkTypes(x syntax.Expr, a []*operand) {
-       assert(x != nil)
-       assert(len(a) == 2)
-       if a[0].mode == invalid {
-               return
-       }
-       t0, t1 := a[0].typ, a[1].typ
-       assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError))
-       if m := check.Types; m != nil {
-               for {
-                       tv := m[x]
-                       assert(tv.Type != nil) // should have been recorded already
-                       pos := x.Pos()
-                       tv.Type = NewTuple(
-                               NewVar(pos, check.pkg, "", t0),
-                               NewVar(pos, check.pkg, "", t1),
-                       )
-                       m[x] = tv
-                       // if x is a parenthesized expression (p.X), update p.X
-                       p, _ := x.(*syntax.ParenExpr)
-                       if p == nil {
-                               break
-                       }
-                       x = p.X
-               }
-       }
-       if check.StoreTypesInSyntax {
-               // Note: this loop is duplicated because the type of tv is different.
-               // Above it is types2.TypeAndValue, here it is syntax.TypeAndValue.
-               for {
-                       tv := x.GetTypeInfo()
-                       assert(tv.Type != nil) // should have been recorded already
-                       pos := x.Pos()
-                       tv.Type = NewTuple(
-                               NewVar(pos, check.pkg, "", t0),
-                               NewVar(pos, check.pkg, "", t1),
-                       )
-                       x.SetTypeInfo(tv)
-                       p, _ := x.(*syntax.ParenExpr)
-                       if p == nil {
-                               break
-                       }
-                       x = p.X
-               }
-       }
-}
-
-// recordInstance records instantiation information into check.Info, if the
-// Instances map is non-nil. The given expr must be an ident, selector, or
-// index (list) expr with ident or selector operand.
-//
-// TODO(rfindley): the expr parameter is fragile. See if we can access the
-// instantiated identifier in some other way.
-func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) {
-       ident := instantiatedIdent(expr)
-       assert(ident != nil)
-       assert(typ != nil)
-       if m := check.Instances; m != nil {
-               m[ident] = Instance{newTypeList(targs), typ}
-       }
-}
-
-func instantiatedIdent(expr syntax.Expr) *syntax.Name {
-       var selOrIdent syntax.Expr
-       switch e := expr.(type) {
-       case *syntax.IndexExpr:
-               selOrIdent = e.X
-       case *syntax.SelectorExpr, *syntax.Name:
-               selOrIdent = e
-       }
-       switch x := selOrIdent.(type) {
-       case *syntax.Name:
-               return x
-       case *syntax.SelectorExpr:
-               return x.Sel
-       }
-       panic("instantiated ident not found")
-}
-
-func (check *Checker) recordDef(id *syntax.Name, obj Object) {
-       assert(id != nil)
-       if m := check.Defs; m != nil {
-               m[id] = obj
-       }
-}
-
-func (check *Checker) recordUse(id *syntax.Name, obj Object) {
-       assert(id != nil)
-       assert(obj != nil)
-       if m := check.Uses; m != nil {
-               m[id] = obj
-       }
-}
-
-func (check *Checker) recordImplicit(node syntax.Node, obj Object) {
-       assert(node != nil)
-       assert(obj != nil)
-       if m := check.Implicits; m != nil {
-               m[node] = obj
-       }
-}
-
-func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
-       assert(obj != nil && (recv == nil || len(index) > 0))
-       check.recordUse(x.Sel, obj)
-       if m := check.Selections; m != nil {
-               m[x] = &Selection{kind, recv, obj, index, indirect}
-       }
-}
-
-func (check *Checker) recordScope(node syntax.Node, scope *Scope) {
-       assert(node != nil)
-       assert(scope != nil)
-       if m := check.Scopes; m != nil {
-               m[node] = scope
-       }
-}
diff --git a/src/cmd/compile/internal/types2/recording.go b/src/cmd/compile/internal/types2/recording.go
new file mode 100644 (file)
index 0000000..cdd38dd
--- /dev/null
@@ -0,0 +1,238 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements recording of type information
+// in the types2.Info maps.
+
+package types2
+
+import (
+       "cmd/compile/internal/syntax"
+       "go/constant"
+)
+
+func (check *Checker) record(x *operand) {
+       // convert x into a user-friendly set of values
+       // TODO(gri) this code can be simplified
+       var typ Type
+       var val constant.Value
+       switch x.mode {
+       case invalid:
+               typ = Typ[Invalid]
+       case novalue:
+               typ = (*Tuple)(nil)
+       case constant_:
+               typ = x.typ
+               val = x.val
+       default:
+               typ = x.typ
+       }
+       assert(x.expr != nil && typ != nil)
+
+       if isUntyped(typ) {
+               // delay type and value recording until we know the type
+               // or until the end of type checking
+               check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
+       } else {
+               check.recordTypeAndValue(x.expr, x.mode, typ, val)
+       }
+}
+
+func (check *Checker) recordUntyped() {
+       if !debug && !check.recordTypes() {
+               return // nothing to do
+       }
+
+       for x, info := range check.untyped {
+               if debug && isTyped(info.typ) {
+                       check.dump("%v: %s (type %s) is typed", atPos(x), x, info.typ)
+                       panic("unreachable")
+               }
+               check.recordTypeAndValue(x, info.mode, info.typ, info.val)
+       }
+}
+
+func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) {
+       assert(x != nil)
+       assert(typ != nil)
+       if mode == invalid {
+               return // omit
+       }
+       if mode == constant_ {
+               assert(val != nil)
+               // We check allBasic(typ, IsConstType) here as constant expressions may be
+               // recorded as type parameters.
+               assert(!isValid(typ) || allBasic(typ, IsConstType))
+       }
+       if m := check.Types; m != nil {
+               m[x] = TypeAndValue{mode, typ, val}
+       }
+       if check.StoreTypesInSyntax {
+               tv := TypeAndValue{mode, typ, val}
+               stv := syntax.TypeAndValue{Type: typ, Value: val}
+               if tv.IsVoid() {
+                       stv.SetIsVoid()
+               }
+               if tv.IsType() {
+                       stv.SetIsType()
+               }
+               if tv.IsBuiltin() {
+                       stv.SetIsBuiltin()
+               }
+               if tv.IsValue() {
+                       stv.SetIsValue()
+               }
+               if tv.IsNil() {
+                       stv.SetIsNil()
+               }
+               if tv.Addressable() {
+                       stv.SetAddressable()
+               }
+               if tv.Assignable() {
+                       stv.SetAssignable()
+               }
+               if tv.HasOk() {
+                       stv.SetHasOk()
+               }
+               x.SetTypeInfo(stv)
+       }
+}
+
+func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
+       // f must be a (possibly parenthesized, possibly qualified)
+       // identifier denoting a built-in (including unsafe's non-constant
+       // functions Add and Slice): record the signature for f and possible
+       // children.
+       for {
+               check.recordTypeAndValue(f, builtin, sig, nil)
+               switch p := f.(type) {
+               case *syntax.Name, *syntax.SelectorExpr:
+                       return // we're done
+               case *syntax.ParenExpr:
+                       f = p.X
+               default:
+                       panic("unreachable")
+               }
+       }
+}
+
+// recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context
+// (and therefore has tuple type).
+func (check *Checker) recordCommaOkTypes(x syntax.Expr, a []*operand) {
+       assert(x != nil)
+       assert(len(a) == 2)
+       if a[0].mode == invalid {
+               return
+       }
+       t0, t1 := a[0].typ, a[1].typ
+       assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError))
+       if m := check.Types; m != nil {
+               for {
+                       tv := m[x]
+                       assert(tv.Type != nil) // should have been recorded already
+                       pos := x.Pos()
+                       tv.Type = NewTuple(
+                               NewVar(pos, check.pkg, "", t0),
+                               NewVar(pos, check.pkg, "", t1),
+                       )
+                       m[x] = tv
+                       // if x is a parenthesized expression (p.X), update p.X
+                       p, _ := x.(*syntax.ParenExpr)
+                       if p == nil {
+                               break
+                       }
+                       x = p.X
+               }
+       }
+       if check.StoreTypesInSyntax {
+               // Note: this loop is duplicated because the type of tv is different.
+               // Above it is types2.TypeAndValue, here it is syntax.TypeAndValue.
+               for {
+                       tv := x.GetTypeInfo()
+                       assert(tv.Type != nil) // should have been recorded already
+                       pos := x.Pos()
+                       tv.Type = NewTuple(
+                               NewVar(pos, check.pkg, "", t0),
+                               NewVar(pos, check.pkg, "", t1),
+                       )
+                       x.SetTypeInfo(tv)
+                       p, _ := x.(*syntax.ParenExpr)
+                       if p == nil {
+                               break
+                       }
+                       x = p.X
+               }
+       }
+}
+
+// recordInstance records instantiation information into check.Info, if the
+// Instances map is non-nil. The given expr must be an ident, selector, or
+// index (list) expr with ident or selector operand.
+//
+// TODO(rfindley): the expr parameter is fragile. See if we can access the
+// instantiated identifier in some other way.
+func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) {
+       ident := instantiatedIdent(expr)
+       assert(ident != nil)
+       assert(typ != nil)
+       if m := check.Instances; m != nil {
+               m[ident] = Instance{newTypeList(targs), typ}
+       }
+}
+
+func instantiatedIdent(expr syntax.Expr) *syntax.Name {
+       var selOrIdent syntax.Expr
+       switch e := expr.(type) {
+       case *syntax.IndexExpr:
+               selOrIdent = e.X
+       case *syntax.SelectorExpr, *syntax.Name:
+               selOrIdent = e
+       }
+       switch x := selOrIdent.(type) {
+       case *syntax.Name:
+               return x
+       case *syntax.SelectorExpr:
+               return x.Sel
+       }
+       panic("instantiated ident not found")
+}
+
+func (check *Checker) recordDef(id *syntax.Name, obj Object) {
+       assert(id != nil)
+       if m := check.Defs; m != nil {
+               m[id] = obj
+       }
+}
+
+func (check *Checker) recordUse(id *syntax.Name, obj Object) {
+       assert(id != nil)
+       assert(obj != nil)
+       if m := check.Uses; m != nil {
+               m[id] = obj
+       }
+}
+
+func (check *Checker) recordImplicit(node syntax.Node, obj Object) {
+       assert(node != nil)
+       assert(obj != nil)
+       if m := check.Implicits; m != nil {
+               m[node] = obj
+       }
+}
+
+func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
+       assert(obj != nil && (recv == nil || len(index) > 0))
+       check.recordUse(x.Sel, obj)
+       if m := check.Selections; m != nil {
+               m[x] = &Selection{kind, recv, obj, index, indirect}
+       }
+}
+
+func (check *Checker) recordScope(node syntax.Node, scope *Scope) {
+       assert(node != nil)
+       assert(scope != nil)
+       if m := check.Scopes; m != nil {
+               m[node] = scope
+       }
+}
index a4d0ff97d5b0414f7aeda0e10597aac358e33155..a31e049c716e472efddced15a69a68503a948940 100644 (file)
@@ -13,7 +13,6 @@ import (
        "go/token"
        "internal/godebug"
        . "internal/types/errors"
-       "strings"
        "sync/atomic"
 )
 
@@ -536,187 +535,3 @@ func (check *Checker) cleanup() {
        }
        check.cleaners = nil
 }
-
-func (check *Checker) record(x *operand) {
-       // convert x into a user-friendly set of values
-       // TODO(gri) this code can be simplified
-       var typ Type
-       var val constant.Value
-       switch x.mode {
-       case invalid:
-               typ = Typ[Invalid]
-       case novalue:
-               typ = (*Tuple)(nil)
-       case constant_:
-               typ = x.typ
-               val = x.val
-       default:
-               typ = x.typ
-       }
-       assert(x.expr != nil && typ != nil)
-
-       if isUntyped(typ) {
-               // delay type and value recording until we know the type
-               // or until the end of type checking
-               check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
-       } else {
-               check.recordTypeAndValue(x.expr, x.mode, typ, val)
-       }
-}
-
-func (check *Checker) recordUntyped() {
-       if !debug && check.Types == nil {
-               return // nothing to do
-       }
-
-       for x, info := range check.untyped {
-               if debug && isTyped(info.typ) {
-                       check.dump("%v: %s (type %s) is typed", x.Pos(), x, info.typ)
-                       panic("unreachable")
-               }
-               check.recordTypeAndValue(x, info.mode, info.typ, info.val)
-       }
-}
-
-func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val constant.Value) {
-       assert(x != nil)
-       assert(typ != nil)
-       if mode == invalid {
-               return // omit
-       }
-       if mode == constant_ {
-               assert(val != nil)
-               // We check allBasic(typ, IsConstType) here as constant expressions may be
-               // recorded as type parameters.
-               assert(!isValid(typ) || allBasic(typ, IsConstType))
-       }
-       if m := check.Types; m != nil {
-               m[x] = TypeAndValue{mode, typ, val}
-       }
-}
-
-func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
-       // f must be a (possibly parenthesized, possibly qualified)
-       // identifier denoting a built-in (including unsafe's non-constant
-       // functions Add and Slice): record the signature for f and possible
-       // children.
-       for {
-               check.recordTypeAndValue(f, builtin, sig, nil)
-               switch p := f.(type) {
-               case *ast.Ident, *ast.SelectorExpr:
-                       return // we're done
-               case *ast.ParenExpr:
-                       f = p.X
-               default:
-                       panic("unreachable")
-               }
-       }
-}
-
-// recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context
-// (and therefore has tuple type).
-func (check *Checker) recordCommaOkTypes(x ast.Expr, a []*operand) {
-       assert(x != nil)
-       assert(len(a) == 2)
-       if a[0].mode == invalid {
-               return
-       }
-       t0, t1 := a[0].typ, a[1].typ
-       assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError))
-       if m := check.Types; m != nil {
-               for {
-                       tv := m[x]
-                       assert(tv.Type != nil) // should have been recorded already
-                       pos := x.Pos()
-                       tv.Type = NewTuple(
-                               NewVar(pos, check.pkg, "", t0),
-                               NewVar(pos, check.pkg, "", t1),
-                       )
-                       m[x] = tv
-                       // if x is a parenthesized expression (p.X), update p.X
-                       p, _ := x.(*ast.ParenExpr)
-                       if p == nil {
-                               break
-                       }
-                       x = p.X
-               }
-       }
-}
-
-// recordInstance records instantiation information into check.Info, if the
-// Instances map is non-nil. The given expr must be an ident, selector, or
-// index (list) expr with ident or selector operand.
-//
-// TODO(rfindley): the expr parameter is fragile. See if we can access the
-// instantiated identifier in some other way.
-func (check *Checker) recordInstance(expr ast.Expr, targs []Type, typ Type) {
-       ident := instantiatedIdent(expr)
-       assert(ident != nil)
-       assert(typ != nil)
-       if m := check.Instances; m != nil {
-               m[ident] = Instance{newTypeList(targs), typ}
-       }
-}
-
-func instantiatedIdent(expr ast.Expr) *ast.Ident {
-       var selOrIdent ast.Expr
-       switch e := expr.(type) {
-       case *ast.IndexExpr:
-               selOrIdent = e.X
-       case *ast.IndexListExpr:
-               selOrIdent = e.X
-       case *ast.SelectorExpr, *ast.Ident:
-               selOrIdent = e
-       }
-       switch x := selOrIdent.(type) {
-       case *ast.Ident:
-               return x
-       case *ast.SelectorExpr:
-               return x.Sel
-       }
-
-       // extra debugging of #63933
-       var buf strings.Builder
-       buf.WriteString("instantiated ident not found; please report: ")
-       ast.Fprint(&buf, token.NewFileSet(), expr, ast.NotNilFilter)
-       panic(buf.String())
-}
-
-func (check *Checker) recordDef(id *ast.Ident, obj Object) {
-       assert(id != nil)
-       if m := check.Defs; m != nil {
-               m[id] = obj
-       }
-}
-
-func (check *Checker) recordUse(id *ast.Ident, obj Object) {
-       assert(id != nil)
-       assert(obj != nil)
-       if m := check.Uses; m != nil {
-               m[id] = obj
-       }
-}
-
-func (check *Checker) recordImplicit(node ast.Node, obj Object) {
-       assert(node != nil)
-       assert(obj != nil)
-       if m := check.Implicits; m != nil {
-               m[node] = obj
-       }
-}
-
-func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
-       assert(obj != nil && (recv == nil || len(index) > 0))
-       check.recordUse(x.Sel, obj)
-       if m := check.Selections; m != nil {
-               m[x] = &Selection{kind, recv, obj, index, indirect}
-       }
-}
-
-func (check *Checker) recordScope(node ast.Node, scope *Scope) {
-       assert(node != nil)
-       assert(scope != nil)
-       if m := check.Scopes; m != nil {
-               m[node] = scope
-       }
-}
diff --git a/src/go/types/recording.go b/src/go/types/recording.go
new file mode 100644 (file)
index 0000000..aae2b20
--- /dev/null
@@ -0,0 +1,199 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements recording of type information
+// in the types.Info maps.
+
+package types
+
+import (
+       "go/ast"
+       "go/constant"
+       "go/token"
+       "strings"
+)
+
+func (check *Checker) record(x *operand) {
+       // convert x into a user-friendly set of values
+       // TODO(gri) this code can be simplified
+       var typ Type
+       var val constant.Value
+       switch x.mode {
+       case invalid:
+               typ = Typ[Invalid]
+       case novalue:
+               typ = (*Tuple)(nil)
+       case constant_:
+               typ = x.typ
+               val = x.val
+       default:
+               typ = x.typ
+       }
+       assert(x.expr != nil && typ != nil)
+
+       if isUntyped(typ) {
+               // delay type and value recording until we know the type
+               // or until the end of type checking
+               check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
+       } else {
+               check.recordTypeAndValue(x.expr, x.mode, typ, val)
+       }
+}
+
+func (check *Checker) recordUntyped() {
+       if !debug && check.Types == nil {
+               return // nothing to do
+       }
+
+       for x, info := range check.untyped {
+               if debug && isTyped(info.typ) {
+                       check.dump("%v: %s (type %s) is typed", x.Pos(), x, info.typ)
+                       panic("unreachable")
+               }
+               check.recordTypeAndValue(x, info.mode, info.typ, info.val)
+       }
+}
+
+func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val constant.Value) {
+       assert(x != nil)
+       assert(typ != nil)
+       if mode == invalid {
+               return // omit
+       }
+       if mode == constant_ {
+               assert(val != nil)
+               // We check allBasic(typ, IsConstType) here as constant expressions may be
+               // recorded as type parameters.
+               assert(!isValid(typ) || allBasic(typ, IsConstType))
+       }
+       if m := check.Types; m != nil {
+               m[x] = TypeAndValue{mode, typ, val}
+       }
+}
+
+func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
+       // f must be a (possibly parenthesized, possibly qualified)
+       // identifier denoting a built-in (including unsafe's non-constant
+       // functions Add and Slice): record the signature for f and possible
+       // children.
+       for {
+               check.recordTypeAndValue(f, builtin, sig, nil)
+               switch p := f.(type) {
+               case *ast.Ident, *ast.SelectorExpr:
+                       return // we're done
+               case *ast.ParenExpr:
+                       f = p.X
+               default:
+                       panic("unreachable")
+               }
+       }
+}
+
+// recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context
+// (and therefore has tuple type).
+func (check *Checker) recordCommaOkTypes(x ast.Expr, a []*operand) {
+       assert(x != nil)
+       assert(len(a) == 2)
+       if a[0].mode == invalid {
+               return
+       }
+       t0, t1 := a[0].typ, a[1].typ
+       assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError))
+       if m := check.Types; m != nil {
+               for {
+                       tv := m[x]
+                       assert(tv.Type != nil) // should have been recorded already
+                       pos := x.Pos()
+                       tv.Type = NewTuple(
+                               NewVar(pos, check.pkg, "", t0),
+                               NewVar(pos, check.pkg, "", t1),
+                       )
+                       m[x] = tv
+                       // if x is a parenthesized expression (p.X), update p.X
+                       p, _ := x.(*ast.ParenExpr)
+                       if p == nil {
+                               break
+                       }
+                       x = p.X
+               }
+       }
+}
+
+// recordInstance records instantiation information into check.Info, if the
+// Instances map is non-nil. The given expr must be an ident, selector, or
+// index (list) expr with ident or selector operand.
+//
+// TODO(rfindley): the expr parameter is fragile. See if we can access the
+// instantiated identifier in some other way.
+func (check *Checker) recordInstance(expr ast.Expr, targs []Type, typ Type) {
+       ident := instantiatedIdent(expr)
+       assert(ident != nil)
+       assert(typ != nil)
+       if m := check.Instances; m != nil {
+               m[ident] = Instance{newTypeList(targs), typ}
+       }
+}
+
+func instantiatedIdent(expr ast.Expr) *ast.Ident {
+       var selOrIdent ast.Expr
+       switch e := expr.(type) {
+       case *ast.IndexExpr:
+               selOrIdent = e.X
+       case *ast.IndexListExpr:
+               selOrIdent = e.X
+       case *ast.SelectorExpr, *ast.Ident:
+               selOrIdent = e
+       }
+       switch x := selOrIdent.(type) {
+       case *ast.Ident:
+               return x
+       case *ast.SelectorExpr:
+               return x.Sel
+       }
+
+       // extra debugging of #63933
+       var buf strings.Builder
+       buf.WriteString("instantiated ident not found; please report: ")
+       ast.Fprint(&buf, token.NewFileSet(), expr, ast.NotNilFilter)
+       panic(buf.String())
+}
+
+func (check *Checker) recordDef(id *ast.Ident, obj Object) {
+       assert(id != nil)
+       if m := check.Defs; m != nil {
+               m[id] = obj
+       }
+}
+
+func (check *Checker) recordUse(id *ast.Ident, obj Object) {
+       assert(id != nil)
+       assert(obj != nil)
+       if m := check.Uses; m != nil {
+               m[id] = obj
+       }
+}
+
+func (check *Checker) recordImplicit(node ast.Node, obj Object) {
+       assert(node != nil)
+       assert(obj != nil)
+       if m := check.Implicits; m != nil {
+               m[node] = obj
+       }
+}
+
+func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
+       assert(obj != nil && (recv == nil || len(index) > 0))
+       check.recordUse(x.Sel, obj)
+       if m := check.Selections; m != nil {
+               m[x] = &Selection{kind, recv, obj, index, indirect}
+       }
+}
+
+func (check *Checker) recordScope(node ast.Node, scope *Scope) {
+       assert(node != nil)
+       assert(scope != nil)
+       if m := check.Scopes; m != nil {
+               m[node] = scope
+       }
+}