Skip to content

Commit

Permalink
[dev.unified] cmd/compile: plumb rtype through OSWITCH/OCASE clauses
Browse files Browse the repository at this point in the history
For (value) switch statements, we may generate OEQ comparisons between
values of interface and concrete type, which in turn may require
access to the concrete type's RType.

To plumb this through, this CL adds CaseClause.RTypes to hold the
rtype values, updates the GOEXPERIMENT=unified frontend to set it, and
updates walk to plumb rtypes through into generated OEQ nodes.

Change-Id: I6f1de2a1167ce54f5770147498a0a591efb3f012
Reviewed-on: https://go-review.googlesource.com/c/go/+/413361
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
  • Loading branch information
mdempsky committed Jun 23, 2022
1 parent 3d432b6 commit 61ae2b7
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 10 deletions.
11 changes: 11 additions & 0 deletions src/cmd/compile/internal/ir/stmt.go
Expand Up @@ -170,6 +170,17 @@ type CaseClause struct {
miniStmt
Var *Name // declared variable for this case in type switch
List Nodes // list of expressions for switch, early select

// RTypes is a list of RType expressions, which are copied to the
// corresponding OEQ nodes that are emitted when switch statements
// are desugared. RTypes[i] must be non-nil if the emitted
// comparison for List[i] will be a mixed interface/concrete
// comparison; see reflectdata.CompareRType for details.
//
// Because mixed interface/concrete switch cases are rare, we allow
// len(RTypes) < len(List). Missing entries are implicitly nil.
RTypes Nodes

Body Nodes
}

Expand Down
23 changes: 22 additions & 1 deletion src/cmd/compile/internal/noder/reader.go
Expand Up @@ -1487,7 +1487,7 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node {
r.openScope()

pos := r.pos()
var cases []ir.Node
var cases, rtypes []ir.Node
if iface != nil {
cases = make([]ir.Node, r.Len())
if len(cases) == 0 {
Expand All @@ -1498,9 +1498,30 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node {
}
} else {
cases = r.exprList()

tagType := types.Types[types.TBOOL]
if tag != nil {
tagType = tag.Type()
}
for i, cas := range cases {
if cas.Op() == ir.ONIL {
continue // never needs rtype
}
if tagType.IsInterface() != cas.Type().IsInterface() {
typ := tagType
if typ.IsInterface() {
typ = cas.Type()
}
for len(rtypes) < i {
rtypes = append(rtypes, nil)
}
rtypes = append(rtypes, reflectdata.TypePtr(typ))
}
}
}

clause := ir.NewCaseStmt(pos, cases, nil)
clause.RTypes = rtypes

if ident != nil {
pos := r.pos()
Expand Down
4 changes: 1 addition & 3 deletions src/cmd/compile/internal/reflectdata/helpers.go
Expand Up @@ -84,9 +84,7 @@ func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node {
func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node {
assertOp2(n, ir.OEQ, ir.ONE)
base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y)
// TODO(mdempsky): Need to propagate RType from OSWITCH/OCASE
// clauses to emitted OEQ nodes.
if haveRType(n, n.RType, "RType", false) {
if haveRType(n, n.RType, "RType", true) {
return n.RType
}
typ := n.X.Type()
Expand Down
19 changes: 13 additions & 6 deletions src/cmd/compile/internal/walk/switch.go
Expand Up @@ -85,8 +85,12 @@ func walkSwitchExpr(sw *ir.SwitchStmt) {
defaultGoto = jmp
}

for _, n1 := range ncase.List {
s.Add(ncase.Pos(), n1, jmp)
for i, n1 := range ncase.List {
var rtype ir.Node
if i < len(ncase.RTypes) {
rtype = ncase.RTypes[i]
}
s.Add(ncase.Pos(), n1, rtype, jmp)
}

// Process body.
Expand Down Expand Up @@ -124,11 +128,12 @@ type exprSwitch struct {
type exprClause struct {
pos src.XPos
lo, hi ir.Node
rtype ir.Node // *runtime._type for OEQ node
jmp ir.Node
}

func (s *exprSwitch) Add(pos src.XPos, expr, jmp ir.Node) {
c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp}
func (s *exprSwitch) Add(pos src.XPos, expr, rtype, jmp ir.Node) {
c := exprClause{pos: pos, lo: expr, hi: expr, rtype: rtype, jmp: jmp}
if types.IsOrdered[s.exprname.Type().Kind()] && expr.Op() == ir.OLITERAL {
s.clauses = append(s.clauses, c)
return
Expand Down Expand Up @@ -233,7 +238,7 @@ func (s *exprSwitch) flush() {
// Add length case to outer switch.
cas := ir.NewBasicLit(pos, constant.MakeInt64(runLen(run)))
jmp := ir.NewBranchStmt(pos, ir.OGOTO, label)
outer.Add(pos, cas, jmp)
outer.Add(pos, cas, nil, jmp)
}
s.done.Append(ir.NewLabelStmt(s.pos, outerLabel))
outer.Emit(&s.done)
Expand Down Expand Up @@ -342,7 +347,9 @@ func (c *exprClause) test(exprname ir.Node) ir.Node {
}
}

return ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo)
n := ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo)
n.RType = c.rtype
return n
}

func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool {
Expand Down

0 comments on commit 61ae2b7

Please sign in to comment.