1
0
mirror of https://github.com/golang/go synced 2024-11-11 19:51:37 -07:00

[dev.regabi] cmd/compile: move gc.treecopy to ir.DeepCopy

This is a general operation on IR nodes, so it belongs in ir.
The copied implementation is adapted to support the
extension pattern, allowing nodes to implement their
own DeepCopy implementations if needed.

This is the first step toward higher-level operations instead
of Left, Right, etc. It will allow the new type syntax nodes
to be properly immutable and opt out of those fine-grained methods.

Passes buildall w/ toolstash -cmp.

Change-Id: Ibd64061e01daf14aebc6586cb2eb2b12057ca85a
Reviewed-on: https://go-review.googlesource.com/c/go/+/274102
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Russ Cox 2020-11-29 09:38:52 -05:00
parent f0001e8867
commit d40869fced
5 changed files with 133 additions and 101 deletions

View File

@ -451,7 +451,7 @@ func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
}
v := values[i]
if decl.Values == nil {
v = treecopy(v, n.Pos())
v = ir.DeepCopy(n.Pos(), v)
}
n.SetOp(ir.OLITERAL)

View File

@ -609,7 +609,10 @@ func (o *Order) stmt(n ir.Node) {
n.SetLeft(o.safeExpr(n.Left()))
l := treecopy(n.Left(), src.NoXPos)
// TODO(rsc): Why is this DeepCopy?
// We should know enough about the form here
// to do something more provably shallower.
l := ir.DeepCopy(src.NoXPos, n.Left())
if l.Op() == ir.OINDEXMAP {
l.SetIndexMapLValue(false)
}
@ -1123,8 +1126,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node {
needCopy = mapKeyReplaceStrConv(n.Right())
if instrumenting {
// Race detector needs the copy so it can
// call treecopy on the result.
// Race detector needs the copy.
needCopy = true
}
}

View File

@ -181,42 +181,6 @@ func nodstr(s string) ir.Node {
return ir.NewLiteral(constant.MakeString(s))
}
// treecopy recursively copies n, with the exception of
// ONAME, OLITERAL, OTYPE, and ONONAME leaves.
// If pos.IsKnown(), it sets the source position of newly
// allocated nodes to pos.
func treecopy(n ir.Node, pos src.XPos) ir.Node {
if n == nil {
return nil
}
switch n.Op() {
default:
m := ir.SepCopy(n)
m.SetLeft(treecopy(n.Left(), pos))
m.SetRight(treecopy(n.Right(), pos))
m.PtrList().Set(listtreecopy(n.List().Slice(), pos))
if pos.IsKnown() {
m.SetPos(pos)
}
if m.Name() != nil && n.Op() != ir.ODCLFIELD {
ir.Dump("treecopy", n)
base.Fatalf("treecopy Name")
}
return m
case ir.OPACK:
// OPACK nodes are never valid in const value declarations,
// but allow them like any other declared symbol to avoid
// crashing (golang.org/issue/11361).
fallthrough
case ir.ONAME, ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE:
return n
}
}
func isptrto(t *types.Type, et types.EType) bool {
if t == nil {
return false
@ -1375,14 +1339,6 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool
return true
}
func listtreecopy(l []ir.Node, pos src.XPos) []ir.Node {
var out []ir.Node
for _, n := range l {
out = append(out, treecopy(n, pos))
}
return out
}
func liststmt(l []ir.Node) ir.Node {
n := ir.Nod(ir.OBLOCK, nil, nil)
n.PtrList().Set(l)

View File

@ -0,0 +1,127 @@
// Copyright 2020 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.
package ir
import (
"cmd/compile/internal/base"
"cmd/internal/src"
)
// A Node may implement the Orig and SetOrig method to
// maintain a pointer to the "unrewritten" form of a Node.
// If a Node does not implement OrigNode, it is its own Orig.
//
// Note that both SepCopy and Copy have definitions compatible
// with a Node that does not implement OrigNode: such a Node
// is its own Orig, and in that case, that's what both want to return
// anyway (SepCopy unconditionally, and Copy only when the input
// is its own Orig as well, but if the output does not implement
// OrigNode, then neither does the input, making the condition true).
type OrigNode interface {
Node
Orig() Node
SetOrig(Node)
}
// Orig returns the “original” node for n.
// If n implements OrigNode, Orig returns n.Orig().
// Otherwise Orig returns n itself.
func Orig(n Node) Node {
if n, ok := n.(OrigNode); ok {
o := n.Orig()
if o == nil {
Dump("Orig nil", n)
base.Fatalf("Orig returned nil")
}
return o
}
return n
}
// SepCopy returns a separate shallow copy of n,
// breaking any Orig link to any other nodes.
func SepCopy(n Node) Node {
n = n.RawCopy()
if n, ok := n.(OrigNode); ok {
n.SetOrig(n)
}
return n
}
// Copy returns a shallow copy of n.
// If Orig(n) == n, then Orig(Copy(n)) == the copy.
// Otherwise the Orig link is preserved as well.
//
// The specific semantics surrounding Orig are subtle but right for most uses.
// See issues #26855 and #27765 for pitfalls.
func Copy(n Node) Node {
copy := n.RawCopy()
if n, ok := n.(OrigNode); ok && n.Orig() == n {
copy.(OrigNode).SetOrig(copy)
}
return copy
}
// A Node can implement DeepCopyNode to provide a custom implementation
// of DeepCopy. If the compiler only needs access to a Node's structure during
// DeepCopy, then a Node can implement DeepCopyNode instead of providing
// fine-grained mutable access with Left, SetLeft, Right, SetRight, and so on.
type DeepCopyNode interface {
Node
DeepCopy(pos src.XPos) Node
}
// DeepCopy returns a “deep” copy of n, with its entire structure copied
// (except for shared nodes like ONAME, ONONAME, OLITERAL, and OTYPE).
// If pos.IsKnown(), it sets the source position of newly allocated Nodes to pos.
//
// The default implementation is to traverse the Node graph, making
// a shallow copy of each node and then updating each field to point
// at shallow copies of children, recursively, using Left, SetLeft, and so on.
//
// If a Node wishes to provide an alternate implementation, it can
// implement a DeepCopy method: see the DeepCopyNode interface.
func DeepCopy(pos src.XPos, n Node) Node {
if n == nil {
return nil
}
if n, ok := n.(DeepCopyNode); ok {
return n.DeepCopy(pos)
}
switch n.Op() {
default:
m := SepCopy(n)
m.SetLeft(DeepCopy(pos, n.Left()))
m.SetRight(DeepCopy(pos, n.Right()))
m.PtrList().Set(deepCopyList(pos, n.List().Slice()))
if pos.IsKnown() {
m.SetPos(pos)
}
if m.Name() != nil {
Dump("DeepCopy", n)
base.Fatalf("DeepCopy Name")
}
return m
case OPACK:
// OPACK nodes are never valid in const value declarations,
// but allow them like any other declared symbol to avoid
// crashing (golang.org/issue/11361).
fallthrough
case ONAME, ONONAME, OLITERAL, ONIL, OTYPE:
return n
}
}
func deepCopyList(pos src.XPos, list []Node) []Node {
var out []Node
for _, n := range list {
out = append(out, DeepCopy(pos, n))
}
return out
}

View File

@ -1021,59 +1021,6 @@ func (n *node) RawCopy() Node {
return &copy
}
// A Node may implement the Orig and SetOrig method to
// maintain a pointer to the "unrewritten" form of a Node.
// If a Node does not implement OrigNode, it is its own Orig.
//
// Note that both SepCopy and Copy have definitions compatible
// with a Node that does not implement OrigNode: such a Node
// is its own Orig, and in that case, that's what both want to return
// anyway (SepCopy unconditionally, and Copy only when the input
// is its own Orig as well, but if the output does not implement
// OrigNode, then neither does the input, making the condition true).
type OrigNode interface {
Node
Orig() Node
SetOrig(Node)
}
func Orig(n Node) Node {
if n, ok := n.(OrigNode); ok {
o := n.Orig()
if o == nil {
Dump("Orig nil", n)
base.Fatalf("Orig returned nil")
}
return o
}
return n
}
// sepcopy returns a separate shallow copy of n, with the copy's
// Orig pointing to itself.
func SepCopy(n Node) Node {
n = n.RawCopy()
if n, ok := n.(OrigNode); ok {
n.SetOrig(n)
}
return n
}
// copy returns shallow copy of n and adjusts the copy's Orig if
// necessary: In general, if n.Orig points to itself, the copy's
// Orig should point to itself as well. Otherwise, if n is modified,
// the copy's Orig node appears modified, too, and then doesn't
// represent the original node anymore.
// (This caused the wrong complit Op to be used when printing error
// messages; see issues #26855, #27765).
func Copy(n Node) Node {
copy := n.RawCopy()
if n, ok := n.(OrigNode); ok && n.Orig() == n {
copy.(OrigNode).SetOrig(copy)
}
return copy
}
// isNil reports whether n represents the universal untyped zero value "nil".
func IsNil(n Node) bool {
// Check n.Orig because constant propagation may produce typed nil constants,