1
0
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:
Robert Griesemer 2024-07-31 09:40:33 -07:00 committed by Gopher Robot
parent 7dac9898c3
commit 31e692be43
4 changed files with 437 additions and 410 deletions

View 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
}
}

View 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
}
}

View 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
}
}

199
src/go/types/recording.go Normal file
View 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
}
}