[dev.regabi] cmd/compile: first start towards using Ident

This CL adds Ident, which will eventually replace *Name and *PkgName
within the AST for representing uses of declared names. (Originally, I
intended to call it "IdentExpr", but neither go/ast nor
cmd/compile/internal/syntax include the "Expr" suffix for their
respective types.)

To start, this CL converts two uses of *Name to *Ident: the tag
identifier in a TypeSwitchGuard (which doesn't actually declare a
variable by itself), and the not-yet-known placeholder ONONAME
returned by oldname to stand-in for identifiers that might be declared
later in the package.

The TypeSwitchGuard's Name's Used flag was previously used for
detecting whether none of the per-clause variables were used. To avoid
bloating all Idents for this rare use, a "Used" bool is added to
TypeSwitchGuard instead. Eventually it could maybe be packed into
miniNode.bits, but for now this is good enough.

Passes buildall w/ toolstash -cmp.

Change-Id: I393284d86757cbbebd26e1320c7354e2bdcb30b0
Reviewed-on: https://go-review.googlesource.com/c/go/+/276113
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Matthew Dempsky 2020-12-07 21:56:58 -08:00
parent eae8fd519b
commit 63bc23b545
10 changed files with 74 additions and 39 deletions

View File

@ -215,7 +215,7 @@ func oldname(s *types.Sym) ir.Node {
// Maybe a top-level declaration will come along later to
// define s. resolve will check s.Def again once all input
// source has been processed.
return ir.NewDeclNameAt(base.Pos, s)
return ir.NewIdent(base.Pos, s)
}
if Curfn != nil && n.Op() == ir.ONAME && n.Name().Curfn != nil && n.Name().Curfn != Curfn {

View File

@ -833,13 +833,13 @@ func (r *importReader) node() ir.Node {
return ir.TypeNode(r.typ())
case ir.OTYPESW:
n := ir.NodAt(r.pos(), ir.OTYPESW, nil, nil)
pos := r.pos()
var tag *ir.Ident
if s := r.ident(); s != nil {
n.SetLeft(ir.NewDeclNameAt(n.Pos(), s))
tag = ir.NewIdent(pos, s)
}
right, _ := r.exprsOrNil()
n.SetRight(right)
return n
expr, _ := r.exprsOrNil()
return ir.NewTypeSwitchGuard(pos, tag, expr)
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// unreachable - should have been resolved by typechecking

View File

@ -751,14 +751,14 @@ func (p *noder) expr(expr syntax.Expr) ir.Node {
p.typeExpr(expr.Elem), p.chanDir(expr.Dir))
case *syntax.TypeSwitchGuard:
n := p.nod(expr, ir.OTYPESW, nil, p.expr(expr.X))
var tag *ir.Ident
if expr.Lhs != nil {
n.SetLeft(p.declName(expr.Lhs))
if ir.IsBlank(n.Left()) {
base.Errorf("invalid variable name %v in type switch", n.Left())
tag = ir.NewIdent(p.pos(expr.Lhs), p.name(expr.Lhs))
if ir.IsBlank(tag) {
base.Errorf("invalid variable name %v in type switch", tag)
}
}
return n
return ir.NewTypeSwitchGuard(p.pos(expr), tag, p.expr(expr.X))
}
panic("unhandled Expr")
}

View File

@ -90,12 +90,16 @@ func resolve(n ir.Node) (res ir.Node) {
defer tracePrint("resolve", n)(&res)
}
if n.Sym().Pkg != types.LocalPkg {
// Stub ir.Name left for us by iimport.
if n, ok := n.(*ir.Name); ok {
if n.Sym().Pkg == types.LocalPkg {
base.Fatalf("unexpected Name: %+v", n)
}
if inimport {
base.Fatalf("recursive inimport")
}
inimport = true
expandDecl(n.(*ir.Name))
expandDecl(n)
inimport = false
return n
}

View File

@ -44,7 +44,7 @@ func walk(fn *ir.Func) {
// Propagate the used flag for typeswitch variables up to the NONAME in its definition.
for _, ln := range fn.Dcl {
if ln.Op() == ir.ONAME && (ln.Class() == ir.PAUTO || ln.Class() == ir.PAUTOHEAP) && ln.Defn != nil && ln.Defn.Op() == ir.OTYPESW && ln.Used() {
ln.Defn.Left().Name().SetUsed(true)
ln.Defn.(*ir.TypeSwitchGuard).Used = true
}
}
@ -52,12 +52,12 @@ func walk(fn *ir.Func) {
if ln.Op() != ir.ONAME || (ln.Class() != ir.PAUTO && ln.Class() != ir.PAUTOHEAP) || ln.Sym().Name[0] == '&' || ln.Used() {
continue
}
if defn := ln.Defn; defn != nil && defn.Op() == ir.OTYPESW {
if defn.Left().Name().Used() {
if defn, ok := ln.Defn.(*ir.TypeSwitchGuard); ok {
if defn.Used {
continue
}
base.ErrorfAt(defn.Left().Pos(), "%v declared but not used", ln.Sym())
defn.Left().Name().SetUsed(true) // suppress repeats
base.ErrorfAt(defn.Tag.Pos(), "%v declared but not used", ln.Sym())
defn.Used = true // suppress repeats
} else {
base.ErrorfAt(ln.Pos(), "%v declared but not used", ln.Sym())
}

View File

@ -39,7 +39,7 @@ func main() {
nodesType := lookup("Nodes")
ptrFieldType := types.NewPointer(lookup("Field"))
slicePtrFieldType := types.NewSlice(ptrFieldType)
ptrNameType := types.NewPointer(lookup("Name"))
ptrIdentType := types.NewPointer(lookup("Ident"))
var buf bytes.Buffer
fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.")
@ -84,7 +84,7 @@ func main() {
fmt.Fprintf(&buf, "func (n *%s) doChildren(do func(Node) error) error { var err error\n", name)
forNodeFields(typName, typ, func(name string, is func(types.Type) bool) {
switch {
case is(ptrNameType):
case is(ptrIdentType):
fmt.Fprintf(&buf, "if n.%s != nil { err = maybeDo(n.%s, err, do) }\n", name, name)
case is(nodeType), is(ntypeType):
fmt.Fprintf(&buf, "err = maybeDo(n.%s, err, do)\n", name)
@ -101,8 +101,8 @@ func main() {
fmt.Fprintf(&buf, "func (n *%s) editChildren(edit func(Node) Node) {\n", name)
forNodeFields(typName, typ, func(name string, is func(types.Type) bool) {
switch {
case is(ptrNameType):
fmt.Fprintf(&buf, "if n.%s != nil { n.%s = edit(n.%s).(*Name) }\n", name, name, name)
case is(ptrIdentType):
fmt.Fprintf(&buf, "if n.%s != nil { n.%s = edit(n.%s).(*Ident) }\n", name, name, name)
case is(nodeType):
fmt.Fprintf(&buf, "n.%s = maybeEdit(n.%s, edit)\n", name, name)
case is(ntypeType):

View File

@ -13,6 +13,25 @@ import (
"go/constant"
)
// An Ident is an identifier, possibly qualified.
type Ident struct {
miniExpr
sym *types.Sym
Used bool
}
func NewIdent(pos src.XPos, sym *types.Sym) *Ident {
n := new(Ident)
n.op = ONONAME
n.pos = pos
n.sym = sym
return n
}
func (n *Ident) Sym() *types.Sym { return n.sym }
func (*Ident) CanBeNtype() {}
// Name holds Node fields used only by named nodes (ONAME, OTYPE, some OLITERAL).
type Name struct {
miniExpr

View File

@ -794,8 +794,6 @@ func NodAt(pos src.XPos, op Op, nleft, nright Node) Node {
return NewSliceHeaderExpr(pos, nil, nleft, nil, nil)
case OSWITCH:
return NewSwitchStmt(pos, nleft, nil)
case OTYPESW:
return NewTypeSwitchGuard(pos, nleft, nright)
case OINLCALL:
return NewInlinedCallExpr(pos, nil, nil)
}

View File

@ -450,6 +450,22 @@ func (n *GoDeferStmt) editChildren(edit func(Node) Node) {
n.Call = maybeEdit(n.Call, edit)
}
func (n *Ident) String() string { return fmt.Sprint(n) }
func (n *Ident) Format(s fmt.State, verb rune) { FmtNode(n, s, verb) }
func (n *Ident) copy() Node {
c := *n
c.init = c.init.Copy()
return &c
}
func (n *Ident) doChildren(do func(Node) error) error {
var err error
err = maybeDoList(n.init, err, do)
return err
}
func (n *Ident) editChildren(edit func(Node) Node) {
editList(n.init, edit)
}
func (n *IfStmt) String() string { return fmt.Sprint(n) }
func (n *IfStmt) Format(s fmt.State, verb rune) { FmtNode(n, s, verb) }
func (n *IfStmt) copy() Node {
@ -1004,15 +1020,15 @@ func (n *TypeSwitchGuard) copy() Node {
}
func (n *TypeSwitchGuard) doChildren(do func(Node) error) error {
var err error
if n.Name_ != nil {
err = maybeDo(n.Name_, err, do)
if n.Tag != nil {
err = maybeDo(n.Tag, err, do)
}
err = maybeDo(n.X, err, do)
return err
}
func (n *TypeSwitchGuard) editChildren(edit func(Node) Node) {
if n.Name_ != nil {
n.Name_ = edit(n.Name_).(*Name)
if n.Tag != nil {
n.Tag = edit(n.Tag).(*Ident)
}
n.X = maybeEdit(n.X, edit)
}

View File

@ -512,32 +512,30 @@ func (n *SwitchStmt) SetHasBreak(x bool) { n.HasBreak_ = x }
// A TypeSwitchGuard is the [Name :=] X.(type) in a type switch.
type TypeSwitchGuard struct {
miniNode
Name_ *Name
X Node
Tag *Ident
X Node
Used bool
}
func NewTypeSwitchGuard(pos src.XPos, name, x Node) *TypeSwitchGuard {
n := &TypeSwitchGuard{X: x}
if name != nil {
n.Name_ = name.(*Name)
}
func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard {
n := &TypeSwitchGuard{Tag: tag, X: x}
n.pos = pos
n.op = OTYPESW
return n
}
func (n *TypeSwitchGuard) Left() Node {
if n.Name_ == nil {
if n.Tag == nil {
return nil
}
return n.Name_
return n.Tag
}
func (n *TypeSwitchGuard) SetLeft(x Node) {
if x == nil {
n.Name_ = nil
n.Tag = nil
return
}
n.Name_ = x.(*Name)
n.Tag = x.(*Ident)
}
func (n *TypeSwitchGuard) Right() Node { return n.X }
func (n *TypeSwitchGuard) SetRight(x Node) { n.X = x }