mirror of
https://github.com/golang/go
synced 2024-11-23 01:40:03 -07:00
cmd/compile/internal/typecheck: add selector helpers
This CL refactors common patterns for constructing field and method selector expressions. Notably, XDotField and XDotMethod are now the only two functions where a SelecterExpr with OXDOT is constructed. Change-Id: I4c087225d8b295c4a6a92281ffcbcabafe2dc94d Reviewed-on: https://go-review.googlesource.com/c/go/+/520979 Auto-Submit: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
3761e3fbfd
commit
9ac6b00e79
@ -198,15 +198,15 @@ func EqStruct(t *types.Type, np, nq ir.Node) ([]ir.Node, bool) {
|
|||||||
// Enforce ordering by starting a new set of reorderable conditions.
|
// Enforce ordering by starting a new set of reorderable conditions.
|
||||||
conds = append(conds, []ir.Node{})
|
conds = append(conds, []ir.Node{})
|
||||||
}
|
}
|
||||||
p := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym)
|
|
||||||
q := ir.NewSelectorExpr(base.Pos, ir.OXDOT, nq, f.Sym)
|
|
||||||
switch {
|
switch {
|
||||||
case f.Type.IsString():
|
case f.Type.IsString():
|
||||||
|
p := typecheck.DotField(base.Pos, typecheck.Expr(np), i)
|
||||||
|
q := typecheck.DotField(base.Pos, typecheck.Expr(nq), i)
|
||||||
eqlen, eqmem := EqString(p, q)
|
eqlen, eqmem := EqString(p, q)
|
||||||
and(eqlen)
|
and(eqlen)
|
||||||
and(eqmem)
|
and(eqmem)
|
||||||
default:
|
default:
|
||||||
and(ir.NewBinaryExpr(base.Pos, ir.OEQ, p, q))
|
and(eqfield(np, nq, i))
|
||||||
}
|
}
|
||||||
if typeCanPanic {
|
if typeCanPanic {
|
||||||
// Also enforce ordering after something that can panic.
|
// Also enforce ordering after something that can panic.
|
||||||
@ -219,13 +219,12 @@ func EqStruct(t *types.Type, np, nq ir.Node) ([]ir.Node, bool) {
|
|||||||
cost, size, next := eqStructFieldCost(t, i)
|
cost, size, next := eqStructFieldCost(t, i)
|
||||||
if cost <= 4 {
|
if cost <= 4 {
|
||||||
// Cost of 4 or less: use plain field equality.
|
// Cost of 4 or less: use plain field equality.
|
||||||
s := fields[i:next]
|
for j := i; j < next; j++ {
|
||||||
for _, f := range s {
|
and(eqfield(np, nq, j))
|
||||||
and(eqfield(np, nq, ir.OEQ, f.Sym))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Higher cost: use memequal.
|
// Higher cost: use memequal.
|
||||||
cc := eqmem(np, nq, f.Sym, size)
|
cc := eqmem(np, nq, i, size)
|
||||||
and(cc)
|
and(cc)
|
||||||
}
|
}
|
||||||
i = next
|
i = next
|
||||||
@ -348,19 +347,18 @@ func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
|
|||||||
// eqfield returns the node
|
// eqfield returns the node
|
||||||
//
|
//
|
||||||
// p.field == q.field
|
// p.field == q.field
|
||||||
func eqfield(p ir.Node, q ir.Node, op ir.Op, field *types.Sym) ir.Node {
|
func eqfield(p, q ir.Node, field int) ir.Node {
|
||||||
nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)
|
nx := typecheck.DotField(base.Pos, typecheck.Expr(p), field)
|
||||||
ny := ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)
|
ny := typecheck.DotField(base.Pos, typecheck.Expr(q), field)
|
||||||
ne := ir.NewBinaryExpr(base.Pos, op, nx, ny)
|
return typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.OEQ, nx, ny))
|
||||||
return ne
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eqmem returns the node
|
// eqmem returns the node
|
||||||
//
|
//
|
||||||
// memequal(&p.field, &q.field, size)
|
// memequal(&p.field, &q.field, size)
|
||||||
func eqmem(p ir.Node, q ir.Node, field *types.Sym, size int64) ir.Node {
|
func eqmem(p, q ir.Node, field int, size int64) ir.Node {
|
||||||
nx := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)))
|
nx := typecheck.Expr(typecheck.NodAddr(typecheck.DotField(base.Pos, p, field)))
|
||||||
ny := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)))
|
ny := typecheck.Expr(typecheck.NodAddr(typecheck.DotField(base.Pos, q, field)))
|
||||||
|
|
||||||
fn, needsize := eqmemfunc(size, nx.Type().Elem())
|
fn, needsize := eqmemfunc(size, nx.Type().Elem())
|
||||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
|
call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
|
||||||
|
@ -113,10 +113,9 @@ func staticCall(call *ir.CallExpr) {
|
|||||||
|
|
||||||
dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil)
|
dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil)
|
||||||
dt.SetType(typ)
|
dt.SetType(typ)
|
||||||
x := typecheck.Callee(ir.NewSelectorExpr(sel.Pos(), ir.OXDOT, dt, sel.Sel))
|
x := typecheck.XDotMethod(sel.Pos(), dt, sel.Sel, true)
|
||||||
switch x.Op() {
|
switch x.Op() {
|
||||||
case ir.ODOTMETH:
|
case ir.ODOTMETH:
|
||||||
x := x.(*ir.SelectorExpr)
|
|
||||||
if base.Flag.LowerM != 0 {
|
if base.Flag.LowerM != 0 {
|
||||||
base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ)
|
base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ)
|
||||||
}
|
}
|
||||||
@ -124,7 +123,6 @@ func staticCall(call *ir.CallExpr) {
|
|||||||
call.X = x
|
call.X = x
|
||||||
case ir.ODOTINTER:
|
case ir.ODOTINTER:
|
||||||
// Promoted method from embedded interface-typed field (#42279).
|
// Promoted method from embedded interface-typed field (#42279).
|
||||||
x := x.(*ir.SelectorExpr)
|
|
||||||
if base.Flag.LowerM != 0 {
|
if base.Flag.LowerM != 0 {
|
||||||
base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ)
|
base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ)
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,7 @@ func rewriteCondCall(call *ir.CallExpr, curfn, callee *ir.Func, concretetyp *typ
|
|||||||
assertAsList := ir.NewAssignListStmt(pos, ir.OAS2, []ir.Node{tmpnode, tmpok}, []ir.Node{typecheck.Expr(assert)})
|
assertAsList := ir.NewAssignListStmt(pos, ir.OAS2, []ir.Node{tmpnode, tmpok}, []ir.Node{typecheck.Expr(assert)})
|
||||||
init.Append(typecheck.Stmt(assertAsList))
|
init.Append(typecheck.Stmt(assertAsList))
|
||||||
|
|
||||||
concreteCallee := typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, tmpnode, method))
|
concreteCallee := typecheck.XDotMethod(pos, tmpnode, method, true)
|
||||||
// Copy slice so edits in one location don't affect another.
|
// Copy slice so edits in one location don't affect another.
|
||||||
argvars = append([]ir.Node(nil), argvars...)
|
argvars = append([]ir.Node(nil), argvars...)
|
||||||
concreteCall := typecheck.Call(pos, concreteCallee, argvars, call.IsDDD)
|
concreteCall := typecheck.Call(pos, concreteCallee, argvars, call.IsDDD)
|
||||||
|
@ -7,7 +7,6 @@ package noder
|
|||||||
import (
|
import (
|
||||||
"go/constant"
|
"go/constant"
|
||||||
|
|
||||||
"cmd/compile/internal/base"
|
|
||||||
"cmd/compile/internal/ir"
|
"cmd/compile/internal/ir"
|
||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"cmd/compile/internal/typecheck"
|
"cmd/compile/internal/typecheck"
|
||||||
@ -83,29 +82,6 @@ func Deref(pos src.XPos, typ *types.Type, x ir.Node) *ir.StarExpr {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func DotField(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
|
|
||||||
op, typ := ir.ODOT, x.Type()
|
|
||||||
if typ.IsPtr() {
|
|
||||||
op, typ = ir.ODOTPTR, typ.Elem()
|
|
||||||
}
|
|
||||||
if !typ.IsStruct() {
|
|
||||||
base.FatalfAt(pos, "DotField of non-struct: %L", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(mdempsky): This is the backend's responsibility.
|
|
||||||
types.CalcSize(typ)
|
|
||||||
|
|
||||||
field := typ.Field(index)
|
|
||||||
return dot(pos, field.Type, op, x, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
func dot(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node, selection *types.Field) *ir.SelectorExpr {
|
|
||||||
n := ir.NewSelectorExpr(pos, op, x, selection.Sym)
|
|
||||||
n.Selection = selection
|
|
||||||
typed(typ, n)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// Statements
|
// Statements
|
||||||
|
|
||||||
var one = constant.MakeInt64(1)
|
var one = constant.MakeInt64(1)
|
||||||
|
@ -2173,7 +2173,7 @@ func (r *reader) expr() (res ir.Node) {
|
|||||||
pos := r.pos()
|
pos := r.pos()
|
||||||
_, sym := r.selector()
|
_, sym := r.selector()
|
||||||
|
|
||||||
return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
|
return typecheck.XDotField(pos, x, sym)
|
||||||
|
|
||||||
case exprMethodVal:
|
case exprMethodVal:
|
||||||
recv := r.expr()
|
recv := r.expr()
|
||||||
@ -2208,7 +2208,7 @@ func (r *reader) expr() (res ir.Node) {
|
|||||||
recv = typecheck.Expr(ir.NewConvExpr(recv.Pos(), ir.OCONVNOP, typ, recv))
|
recv = typecheck.Expr(ir.NewConvExpr(recv.Pos(), ir.OCONVNOP, typ, recv))
|
||||||
}
|
}
|
||||||
|
|
||||||
n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, recv, wrapperFn.Sel)).(*ir.SelectorExpr)
|
n := typecheck.XDotMethod(pos, recv, wrapperFn.Sel, false)
|
||||||
|
|
||||||
// As a consistency check here, we make sure "n" selected the
|
// As a consistency check here, we make sure "n" selected the
|
||||||
// same method (represented by a types.Field) that wrapperFn
|
// same method (represented by a types.Field) that wrapperFn
|
||||||
@ -2347,7 +2347,7 @@ func (r *reader) expr() (res ir.Node) {
|
|||||||
x := r.expr()
|
x := r.expr()
|
||||||
pos := r.pos()
|
pos := r.pos()
|
||||||
for i, n := 0, r.Len(); i < n; i++ {
|
for i, n := 0, r.Len(); i < n; i++ {
|
||||||
x = Implicit(DotField(pos, x, r.Len()))
|
x = Implicit(typecheck.DotField(pos, x, r.Len()))
|
||||||
}
|
}
|
||||||
if r.Bool() { // needs deref
|
if r.Bool() { // needs deref
|
||||||
x = Implicit(Deref(pos, x.Type().Elem(), x))
|
x = Implicit(Deref(pos, x.Type().Elem(), x))
|
||||||
@ -2374,7 +2374,7 @@ func (r *reader) expr() (res ir.Node) {
|
|||||||
// There are also corner cases where semantically it's perhaps
|
// There are also corner cases where semantically it's perhaps
|
||||||
// significant; e.g., fixedbugs/issue15975.go, #38634, #52025.
|
// significant; e.g., fixedbugs/issue15975.go, #38634, #52025.
|
||||||
|
|
||||||
fun = typecheck.Callee(ir.NewSelectorExpr(method.Pos(), ir.OXDOT, recv, method.Sel))
|
fun = typecheck.XDotMethod(method.Pos(), recv, method.Sel, true)
|
||||||
} else {
|
} else {
|
||||||
if recv.Type().IsInterface() {
|
if recv.Type().IsInterface() {
|
||||||
// N.B., this happens currently for typeparam/issue51521.go
|
// N.B., this happens currently for typeparam/issue51521.go
|
||||||
@ -2665,7 +2665,7 @@ func (r *reader) methodExprWrap(origPos src.XPos, recv *types.Type, implicits []
|
|||||||
{
|
{
|
||||||
arg := args[0]
|
arg := args[0]
|
||||||
for _, ix := range implicits {
|
for _, ix := range implicits {
|
||||||
arg = Implicit(DotField(pos, arg, ix))
|
arg = Implicit(typecheck.DotField(pos, arg, ix))
|
||||||
}
|
}
|
||||||
if deref {
|
if deref {
|
||||||
arg = Implicit(Deref(pos, arg.Type().Elem(), arg))
|
arg = Implicit(Deref(pos, arg.Type().Elem(), arg))
|
||||||
@ -3947,7 +3947,7 @@ func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
|
|||||||
|
|
||||||
fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
|
fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
|
||||||
|
|
||||||
dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym)
|
dot := typecheck.XDotMethod(pos, recv, method.Sym, true)
|
||||||
call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr)
|
call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr)
|
||||||
|
|
||||||
if method.Type.NumResults() == 0 {
|
if method.Type.NumResults() == 0 {
|
||||||
|
@ -203,8 +203,7 @@ func hashFunc(t *types.Type) *ir.Func {
|
|||||||
if !compare.IsRegularMemory(f.Type) {
|
if !compare.IsRegularMemory(f.Type) {
|
||||||
hashel := hashfor(f.Type)
|
hashel := hashfor(f.Type)
|
||||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
|
call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
|
||||||
nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO: fields from other packages?
|
na := typecheck.NodAddr(typecheck.DotField(base.Pos, np, i))
|
||||||
na := typecheck.NodAddr(nx)
|
|
||||||
call.Args.Append(na)
|
call.Args.Append(na)
|
||||||
call.Args.Append(nh)
|
call.Args.Append(nh)
|
||||||
fn.Body.Append(ir.NewAssignStmt(base.Pos, nh, call))
|
fn.Body.Append(ir.NewAssignStmt(base.Pos, nh, call))
|
||||||
@ -218,8 +217,7 @@ func hashFunc(t *types.Type) *ir.Func {
|
|||||||
// h = hashel(&p.first, size, h)
|
// h = hashel(&p.first, size, h)
|
||||||
hashel := hashmem(f.Type)
|
hashel := hashmem(f.Type)
|
||||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
|
call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
|
||||||
nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO: fields from other packages?
|
na := typecheck.NodAddr(typecheck.DotField(base.Pos, np, i))
|
||||||
na := typecheck.NodAddr(nx)
|
|
||||||
call.Args.Append(na)
|
call.Args.Append(na)
|
||||||
call.Args.Append(nh)
|
call.Args.Append(nh)
|
||||||
call.Args.Append(ir.NewInt(base.Pos, size))
|
call.Args.Append(ir.NewInt(base.Pos, size))
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"cmd/compile/internal/base"
|
"cmd/compile/internal/base"
|
||||||
"cmd/compile/internal/ir"
|
"cmd/compile/internal/ir"
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/internal/src"
|
||||||
)
|
)
|
||||||
|
|
||||||
// tcAddr typechecks an OADDR node.
|
// tcAddr typechecks an OADDR node.
|
||||||
@ -436,6 +437,60 @@ func tcConv(n *ir.ConvExpr) ir.Node {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DotField returns a field selector expression that selects the
|
||||||
|
// index'th field of the given expression, which must be of struct or
|
||||||
|
// pointer-to-struct type.
|
||||||
|
func DotField(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
|
||||||
|
op, typ := ir.ODOT, x.Type()
|
||||||
|
if typ.IsPtr() {
|
||||||
|
op, typ = ir.ODOTPTR, typ.Elem()
|
||||||
|
}
|
||||||
|
if !typ.IsStruct() {
|
||||||
|
base.FatalfAt(pos, "DotField of non-struct: %L", x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mdempsky): This is the backend's responsibility.
|
||||||
|
types.CalcSize(typ)
|
||||||
|
|
||||||
|
field := typ.Field(index)
|
||||||
|
return dot(pos, field.Type, op, x, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dot(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node, selection *types.Field) *ir.SelectorExpr {
|
||||||
|
n := ir.NewSelectorExpr(pos, op, x, selection.Sym)
|
||||||
|
n.Selection = selection
|
||||||
|
n.SetType(typ)
|
||||||
|
n.SetTypecheck(1)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// XDotMethod returns an expression representing the field selection
|
||||||
|
// x.sym. If any implicit field selection are necessary, those are
|
||||||
|
// inserted too.
|
||||||
|
func XDotField(pos src.XPos, x ir.Node, sym *types.Sym) *ir.SelectorExpr {
|
||||||
|
n := Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
|
||||||
|
// TODO(mdempsky): Assert n is ODOT/ODOTPTR.
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// XDotMethod returns an expression representing the method value
|
||||||
|
// x.sym (i.e., x is a value, not a type). If any implicit field
|
||||||
|
// selection are necessary, those are inserted too.
|
||||||
|
//
|
||||||
|
// If callee is true, the result is an ODOTMETH/ODOTINTER, otherwise
|
||||||
|
// an OMETHVALUE.
|
||||||
|
func XDotMethod(pos src.XPos, x ir.Node, sym *types.Sym, callee bool) *ir.SelectorExpr {
|
||||||
|
n := ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)
|
||||||
|
if callee {
|
||||||
|
n = Callee(n).(*ir.SelectorExpr)
|
||||||
|
// TODO(mdempsky): Assert n is ODOTMETH/ODOTINTER.
|
||||||
|
} else {
|
||||||
|
n = Expr(n).(*ir.SelectorExpr)
|
||||||
|
// TODO(mdempsky): Assert n is OMETHVALUE.
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
// tcDot typechecks an OXDOT or ODOT node.
|
// tcDot typechecks an OXDOT or ODOT node.
|
||||||
func tcDot(n *ir.SelectorExpr, top int) ir.Node {
|
func tcDot(n *ir.SelectorExpr, top int) ir.Node {
|
||||||
if n.Op() == ir.OXDOT {
|
if n.Op() == ir.OXDOT {
|
||||||
|
@ -418,7 +418,7 @@ func soleComponent(init *ir.Nodes, n ir.Node) ir.Node {
|
|||||||
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n, nil))
|
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n, nil))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n = typecheck.Expr(ir.NewSelectorExpr(n.Pos(), ir.OXDOT, n, n.Type().Field(0).Sym))
|
n = typecheck.DotField(n.Pos(), n, 0)
|
||||||
case n.Type().IsArray():
|
case n.Type().IsArray():
|
||||||
n = typecheck.Expr(ir.NewIndexExpr(n.Pos(), n, ir.NewInt(base.Pos, 0)))
|
n = typecheck.Expr(ir.NewIndexExpr(n.Pos(), n, ir.NewInt(base.Pos, 0)))
|
||||||
default:
|
default:
|
||||||
|
Loading…
Reference in New Issue
Block a user