mirror of
https://github.com/golang/go
synced 2024-11-13 13:50:26 -07:00
go/types, types2: move type recording functionality in its own files
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>
This commit is contained in:
parent
7dac9898c3
commit
31e692be43
@ -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
|
||||
}
|
||||
}
|
||||
|
238
src/cmd/compile/internal/types2/recording.go
Normal file
238
src/cmd/compile/internal/types2/recording.go
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
199
src/go/types/recording.go
Normal file
199
src/go/types/recording.go
Normal file
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user