mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.regabi] cmd/compile: cleanup for concrete types - range, select, swt
An automated rewrite will add concrete type assertions after a test of n.Op(), when n can be safely type-asserted (meaning, n is not reassigned a different type, n is not reassigned and then used outside the scope of the type assertion, and so on). This sequence of CLs handles the code that the automated rewrite does not: adding specific types to function arguments, adjusting code not to call n.Left() etc when n may have multiple representations, and so on. This CL focuses on range.go, select.go, and swt.go: the big control structures. Passes buildall w/ toolstash -cmp. Change-Id: I033fe056a7b815edb6e8a06f45c12ffd990f4d45 Reviewed-on: https://go-review.googlesource.com/c/go/+/277929 Trust: Russ Cox <rsc@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
42fec2ded4
commit
dd67b13d07
4 changed files with 102 additions and 82 deletions
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// range
|
// range
|
||||||
func typecheckrange(n ir.Node) {
|
func typecheckrange(n *ir.RangeStmt) {
|
||||||
// Typechecking order is important here:
|
// Typechecking order is important here:
|
||||||
// 0. first typecheck range expression (slice/map/chan),
|
// 0. first typecheck range expression (slice/map/chan),
|
||||||
// it is evaluated only once and so logically it is not part of the loop.
|
// it is evaluated only once and so logically it is not part of the loop.
|
||||||
|
|
@ -39,7 +39,7 @@ func typecheckrange(n ir.Node) {
|
||||||
decldepth--
|
decldepth--
|
||||||
}
|
}
|
||||||
|
|
||||||
func typecheckrangeExpr(n ir.Node) {
|
func typecheckrangeExpr(n *ir.RangeStmt) {
|
||||||
n.SetRight(typecheck(n.Right(), ctxExpr))
|
n.SetRight(typecheck(n.Right(), ctxExpr))
|
||||||
|
|
||||||
t := n.Right().Type()
|
t := n.Right().Type()
|
||||||
|
|
@ -157,7 +157,7 @@ func cheapComputableIndex(width int64) bool {
|
||||||
// simpler forms. The result must be assigned back to n.
|
// simpler forms. The result must be assigned back to n.
|
||||||
// Node n may also be modified in place, and may also be
|
// Node n may also be modified in place, and may also be
|
||||||
// the returned node.
|
// the returned node.
|
||||||
func walkrange(nrange ir.Node) ir.Node {
|
func walkrange(nrange *ir.RangeStmt) ir.Node {
|
||||||
if isMapClear(nrange) {
|
if isMapClear(nrange) {
|
||||||
m := nrange.Right()
|
m := nrange.Right()
|
||||||
lno := setlineno(m)
|
lno := setlineno(m)
|
||||||
|
|
@ -204,7 +204,7 @@ func walkrange(nrange ir.Node) ir.Node {
|
||||||
base.Fatalf("walkrange: v2 != nil while v1 == nil")
|
base.Fatalf("walkrange: v2 != nil while v1 == nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
var ifGuard ir.Node
|
var ifGuard *ir.IfStmt
|
||||||
|
|
||||||
var body []ir.Node
|
var body []ir.Node
|
||||||
var init []ir.Node
|
var init []ir.Node
|
||||||
|
|
@ -267,7 +267,7 @@ func walkrange(nrange ir.Node) ir.Node {
|
||||||
// TODO(austin): OFORUNTIL inhibits bounds-check
|
// TODO(austin): OFORUNTIL inhibits bounds-check
|
||||||
// elimination on the index variable (see #20711).
|
// elimination on the index variable (see #20711).
|
||||||
// Enhance the prove pass to understand this.
|
// Enhance the prove pass to understand this.
|
||||||
ifGuard = ir.Nod(ir.OIF, nil, nil)
|
ifGuard = ir.NewIfStmt(base.Pos, nil, nil, nil)
|
||||||
ifGuard.SetLeft(ir.Nod(ir.OLT, hv1, hn))
|
ifGuard.SetLeft(ir.Nod(ir.OLT, hv1, hn))
|
||||||
nfor.SetOp(ir.OFORUNTIL)
|
nfor.SetOp(ir.OFORUNTIL)
|
||||||
|
|
||||||
|
|
@ -426,7 +426,7 @@ func walkrange(nrange ir.Node) ir.Node {
|
||||||
|
|
||||||
if ifGuard != nil {
|
if ifGuard != nil {
|
||||||
ifGuard.PtrInit().Append(init...)
|
ifGuard.PtrInit().Append(init...)
|
||||||
ifGuard = typecheck(ifGuard, ctxStmt)
|
ifGuard = typecheck(ifGuard, ctxStmt).(*ir.IfStmt)
|
||||||
} else {
|
} else {
|
||||||
nfor.PtrInit().Append(init...)
|
nfor.PtrInit().Append(init...)
|
||||||
}
|
}
|
||||||
|
|
@ -459,7 +459,7 @@ func walkrange(nrange ir.Node) ir.Node {
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// where == for keys of map m is reflexive.
|
// where == for keys of map m is reflexive.
|
||||||
func isMapClear(n ir.Node) bool {
|
func isMapClear(n *ir.RangeStmt) bool {
|
||||||
if base.Flag.N != 0 || instrumenting {
|
if base.Flag.N != 0 || instrumenting {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -488,7 +488,7 @@ func isMapClear(n ir.Node) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
m := n.Right()
|
m := n.Right()
|
||||||
if !samesafeexpr(stmt.List().First(), m) || !samesafeexpr(stmt.List().Second(), k) {
|
if delete := stmt.(*ir.CallExpr); !samesafeexpr(delete.List().First(), m) || !samesafeexpr(delete.List().Second(), k) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -508,11 +508,7 @@ func mapClear(m ir.Node) ir.Node {
|
||||||
fn := syslook("mapclear")
|
fn := syslook("mapclear")
|
||||||
fn = substArgTypes(fn, t.Key(), t.Elem())
|
fn = substArgTypes(fn, t.Key(), t.Elem())
|
||||||
n := mkcall1(fn, nil, nil, typename(t), m)
|
n := mkcall1(fn, nil, nil, typename(t), m)
|
||||||
|
return walkstmt(typecheck(n, ctxStmt))
|
||||||
n = typecheck(n, ctxStmt)
|
|
||||||
n = walkstmt(n)
|
|
||||||
|
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lower n into runtime·memclr if possible, for
|
// Lower n into runtime·memclr if possible, for
|
||||||
|
|
@ -526,7 +522,7 @@ func mapClear(m ir.Node) ir.Node {
|
||||||
// in which the evaluation of a is side-effect-free.
|
// in which the evaluation of a is side-effect-free.
|
||||||
//
|
//
|
||||||
// Parameters are as in walkrange: "for v1, v2 = range a".
|
// Parameters are as in walkrange: "for v1, v2 = range a".
|
||||||
func arrayClear(loop, v1, v2, a ir.Node) ir.Node {
|
func arrayClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node {
|
||||||
if base.Flag.N != 0 || instrumenting {
|
if base.Flag.N != 0 || instrumenting {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -539,12 +535,17 @@ func arrayClear(loop, v1, v2, a ir.Node) ir.Node {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt := loop.Body().First() // only stmt in body
|
stmt1 := loop.Body().First() // only stmt in body
|
||||||
if stmt.Op() != ir.OAS || stmt.Left().Op() != ir.OINDEX {
|
if stmt1.Op() != ir.OAS {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
stmt := stmt1.(*ir.AssignStmt)
|
||||||
|
if stmt.Left().Op() != ir.OINDEX {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
lhs := stmt.Left().(*ir.IndexExpr)
|
||||||
|
|
||||||
if !samesafeexpr(stmt.Left().Left(), a) || !samesafeexpr(stmt.Left().Right(), v1) {
|
if !samesafeexpr(lhs.Left(), a) || !samesafeexpr(lhs.Right(), v1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// select
|
// select
|
||||||
func typecheckselect(sel ir.Node) {
|
func typecheckselect(sel *ir.SelectStmt) {
|
||||||
var def ir.Node
|
var def ir.Node
|
||||||
lno := setlineno(sel)
|
lno := setlineno(sel)
|
||||||
typecheckslice(sel.Init().Slice(), ctxStmt)
|
typecheckslice(sel.Init().Slice(), ctxStmt)
|
||||||
for _, ncase := range sel.List().Slice() {
|
for _, ncase := range sel.List().Slice() {
|
||||||
if ncase.Op() != ir.OCASE {
|
ncase := ncase.(*ir.CaseStmt)
|
||||||
setlineno(ncase)
|
|
||||||
base.Fatalf("typecheckselect %v", ncase.Op())
|
|
||||||
}
|
|
||||||
|
|
||||||
if ncase.List().Len() == 0 {
|
if ncase.List().Len() == 0 {
|
||||||
// default
|
// default
|
||||||
|
|
@ -51,8 +48,10 @@ func typecheckselect(sel ir.Node) {
|
||||||
// convert x = <-c into OSELRECV(x, <-c).
|
// convert x = <-c into OSELRECV(x, <-c).
|
||||||
// remove implicit conversions; the eventual assignment
|
// remove implicit conversions; the eventual assignment
|
||||||
// will reintroduce them.
|
// will reintroduce them.
|
||||||
if (n.Right().Op() == ir.OCONVNOP || n.Right().Op() == ir.OCONVIFACE) && n.Right().Implicit() {
|
if r := n.Right(); r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE {
|
||||||
n.SetRight(n.Right().Left())
|
if r.Implicit() {
|
||||||
|
n.SetRight(r.Left())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if n.Right().Op() != ir.ORECV {
|
if n.Right().Op() != ir.ORECV {
|
||||||
base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side")
|
base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side")
|
||||||
|
|
@ -70,9 +69,10 @@ func typecheckselect(sel ir.Node) {
|
||||||
|
|
||||||
case ir.ORECV:
|
case ir.ORECV:
|
||||||
// convert <-c into OSELRECV(_, <-c)
|
// convert <-c into OSELRECV(_, <-c)
|
||||||
n = ir.NodAt(n.Pos(), ir.OAS, ir.BlankNode, n)
|
as := ir.NewAssignStmt(n.Pos(), ir.BlankNode, n)
|
||||||
n.SetOp(ir.OSELRECV)
|
as.SetOp(ir.OSELRECV)
|
||||||
n.SetTypecheck(1)
|
as.SetTypecheck(1)
|
||||||
|
n = as
|
||||||
ncase.SetLeft(n)
|
ncase.SetLeft(n)
|
||||||
|
|
||||||
case ir.OSEND:
|
case ir.OSEND:
|
||||||
|
|
@ -86,7 +86,7 @@ func typecheckselect(sel ir.Node) {
|
||||||
base.Pos = lno
|
base.Pos = lno
|
||||||
}
|
}
|
||||||
|
|
||||||
func walkselect(sel ir.Node) {
|
func walkselect(sel *ir.SelectStmt) {
|
||||||
lno := setlineno(sel)
|
lno := setlineno(sel)
|
||||||
if sel.Body().Len() != 0 {
|
if sel.Body().Len() != 0 {
|
||||||
base.Fatalf("double walkselect")
|
base.Fatalf("double walkselect")
|
||||||
|
|
@ -95,8 +95,8 @@ func walkselect(sel ir.Node) {
|
||||||
init := sel.Init().Slice()
|
init := sel.Init().Slice()
|
||||||
sel.PtrInit().Set(nil)
|
sel.PtrInit().Set(nil)
|
||||||
|
|
||||||
init = append(init, walkselectcases(sel.PtrList())...)
|
init = append(init, walkselectcases(sel.List())...)
|
||||||
sel.PtrList().Set(nil)
|
sel.SetList(ir.Nodes{})
|
||||||
|
|
||||||
sel.PtrBody().Set(init)
|
sel.PtrBody().Set(init)
|
||||||
walkstmtlist(sel.Body().Slice())
|
walkstmtlist(sel.Body().Slice())
|
||||||
|
|
@ -104,7 +104,7 @@ func walkselect(sel ir.Node) {
|
||||||
base.Pos = lno
|
base.Pos = lno
|
||||||
}
|
}
|
||||||
|
|
||||||
func walkselectcases(cases *ir.Nodes) []ir.Node {
|
func walkselectcases(cases ir.Nodes) []ir.Node {
|
||||||
ncas := cases.Len()
|
ncas := cases.Len()
|
||||||
sellineno := base.Pos
|
sellineno := base.Pos
|
||||||
|
|
||||||
|
|
@ -115,7 +115,7 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
|
|
||||||
// optimization: one-case select: single op.
|
// optimization: one-case select: single op.
|
||||||
if ncas == 1 {
|
if ncas == 1 {
|
||||||
cas := cases.First()
|
cas := cases.First().(*ir.CaseStmt)
|
||||||
setlineno(cas)
|
setlineno(cas)
|
||||||
l := cas.Init().Slice()
|
l := cas.Init().Slice()
|
||||||
if cas.Left() != nil { // not default:
|
if cas.Left() != nil { // not default:
|
||||||
|
|
@ -130,18 +130,20 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
// already ok
|
// already ok
|
||||||
|
|
||||||
case ir.OSELRECV:
|
case ir.OSELRECV:
|
||||||
if ir.IsBlank(n.Left()) {
|
r := n.(*ir.AssignStmt)
|
||||||
n = n.Right()
|
if ir.IsBlank(r.Left()) {
|
||||||
|
n = r.Right()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
n.SetOp(ir.OAS)
|
r.SetOp(ir.OAS)
|
||||||
|
|
||||||
case ir.OSELRECV2:
|
case ir.OSELRECV2:
|
||||||
if ir.IsBlank(n.List().First()) && ir.IsBlank(n.List().Second()) {
|
r := n.(*ir.AssignListStmt)
|
||||||
n = n.Rlist().First()
|
if ir.IsBlank(r.List().First()) && ir.IsBlank(r.List().Second()) {
|
||||||
|
n = r.Rlist().First()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
n.SetOp(ir.OAS2RECV)
|
r.SetOp(ir.OAS2RECV)
|
||||||
}
|
}
|
||||||
|
|
||||||
l = append(l, n)
|
l = append(l, n)
|
||||||
|
|
@ -154,8 +156,9 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
|
|
||||||
// convert case value arguments to addresses.
|
// convert case value arguments to addresses.
|
||||||
// this rewrite is used by both the general code and the next optimization.
|
// this rewrite is used by both the general code and the next optimization.
|
||||||
var dflt ir.Node
|
var dflt *ir.CaseStmt
|
||||||
for _, cas := range cases.Slice() {
|
for _, cas := range cases.Slice() {
|
||||||
|
cas := cas.(*ir.CaseStmt)
|
||||||
setlineno(cas)
|
setlineno(cas)
|
||||||
n := cas.Left()
|
n := cas.Left()
|
||||||
if n == nil {
|
if n == nil {
|
||||||
|
|
@ -164,11 +167,14 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lower x, _ = <-c to x = <-c.
|
// Lower x, _ = <-c to x = <-c.
|
||||||
if n.Op() == ir.OSELRECV2 && ir.IsBlank(n.List().Second()) {
|
if sel := n; sel.Op() == ir.OSELRECV2 {
|
||||||
n = ir.NodAt(n.Pos(), ir.OAS, n.List().First(), n.Rlist().First())
|
if ir.IsBlank(sel.List().Second()) {
|
||||||
n.SetOp(ir.OSELRECV)
|
as := ir.NewAssignStmt(sel.Pos(), sel.List().First(), sel.Rlist().First())
|
||||||
n.SetTypecheck(1)
|
as.SetOp(ir.OSELRECV)
|
||||||
cas.SetLeft(n)
|
as.SetTypecheck(1)
|
||||||
|
n = as
|
||||||
|
cas.SetLeft(n)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
|
|
@ -192,9 +198,9 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
|
|
||||||
// optimization: two-case select but one is default: single non-blocking op.
|
// optimization: two-case select but one is default: single non-blocking op.
|
||||||
if ncas == 2 && dflt != nil {
|
if ncas == 2 && dflt != nil {
|
||||||
cas := cases.First()
|
cas := cases.First().(*ir.CaseStmt)
|
||||||
if cas == dflt {
|
if cas == dflt {
|
||||||
cas = cases.Second()
|
cas = cases.Second().(*ir.CaseStmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
n := cas.Left()
|
n := cas.Left()
|
||||||
|
|
@ -213,7 +219,8 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
|
|
||||||
case ir.OSELRECV:
|
case ir.OSELRECV:
|
||||||
// if selectnbrecv(&v, c) { body } else { default body }
|
// if selectnbrecv(&v, c) { body } else { default body }
|
||||||
ch := n.Right().Left()
|
recv := n.Right().(*ir.UnaryExpr)
|
||||||
|
ch := recv.Left()
|
||||||
elem := n.Left()
|
elem := n.Left()
|
||||||
if ir.IsBlank(elem) {
|
if ir.IsBlank(elem) {
|
||||||
elem = nodnil()
|
elem = nodnil()
|
||||||
|
|
@ -222,7 +229,8 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
|
|
||||||
case ir.OSELRECV2:
|
case ir.OSELRECV2:
|
||||||
// if selectnbrecv2(&v, &received, c) { body } else { default body }
|
// if selectnbrecv2(&v, &received, c) { body } else { default body }
|
||||||
ch := n.Rlist().First().Left()
|
recv := n.Rlist().First().(*ir.UnaryExpr)
|
||||||
|
ch := recv.Left()
|
||||||
elem := n.List().First()
|
elem := n.List().First()
|
||||||
if ir.IsBlank(elem) {
|
if ir.IsBlank(elem) {
|
||||||
elem = nodnil()
|
elem = nodnil()
|
||||||
|
|
@ -240,7 +248,7 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
if dflt != nil {
|
if dflt != nil {
|
||||||
ncas--
|
ncas--
|
||||||
}
|
}
|
||||||
casorder := make([]ir.Node, ncas)
|
casorder := make([]*ir.CaseStmt, ncas)
|
||||||
nsends, nrecvs := 0, 0
|
nsends, nrecvs := 0, 0
|
||||||
|
|
||||||
var init []ir.Node
|
var init []ir.Node
|
||||||
|
|
@ -263,6 +271,7 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
|
|
||||||
// register cases
|
// register cases
|
||||||
for _, cas := range cases.Slice() {
|
for _, cas := range cases.Slice() {
|
||||||
|
cas := cas.(*ir.CaseStmt)
|
||||||
setlineno(cas)
|
setlineno(cas)
|
||||||
|
|
||||||
init = append(init, cas.Init().Slice()...)
|
init = append(init, cas.Init().Slice()...)
|
||||||
|
|
@ -286,12 +295,14 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
case ir.OSELRECV:
|
case ir.OSELRECV:
|
||||||
nrecvs++
|
nrecvs++
|
||||||
i = ncas - nrecvs
|
i = ncas - nrecvs
|
||||||
c = n.Right().Left()
|
recv := n.Right().(*ir.UnaryExpr)
|
||||||
|
c = recv.Left()
|
||||||
elem = n.Left()
|
elem = n.Left()
|
||||||
case ir.OSELRECV2:
|
case ir.OSELRECV2:
|
||||||
nrecvs++
|
nrecvs++
|
||||||
i = ncas - nrecvs
|
i = ncas - nrecvs
|
||||||
c = n.Rlist().First().Left()
|
recv := n.Rlist().First().(*ir.UnaryExpr)
|
||||||
|
c = recv.Left()
|
||||||
elem = n.List().First()
|
elem = n.List().First()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,7 +349,7 @@ func walkselectcases(cases *ir.Nodes) []ir.Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch cases
|
// dispatch cases
|
||||||
dispatch := func(cond, cas ir.Node) {
|
dispatch := func(cond ir.Node, cas *ir.CaseStmt) {
|
||||||
cond = typecheck(cond, ctxExpr)
|
cond = typecheck(cond, ctxExpr)
|
||||||
cond = defaultlit(cond, nil)
|
cond = defaultlit(cond, nil)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// typecheckswitch typechecks a switch statement.
|
// typecheckswitch typechecks a switch statement.
|
||||||
func typecheckswitch(n ir.Node) {
|
func typecheckswitch(n *ir.SwitchStmt) {
|
||||||
typecheckslice(n.Init().Slice(), ctxStmt)
|
typecheckslice(n.Init().Slice(), ctxStmt)
|
||||||
if n.Left() != nil && n.Left().Op() == ir.OTYPESW {
|
if n.Left() != nil && n.Left().Op() == ir.OTYPESW {
|
||||||
typecheckTypeSwitch(n)
|
typecheckTypeSwitch(n)
|
||||||
|
|
@ -24,24 +24,26 @@ func typecheckswitch(n ir.Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func typecheckTypeSwitch(n ir.Node) {
|
func typecheckTypeSwitch(n *ir.SwitchStmt) {
|
||||||
n.Left().SetRight(typecheck(n.Left().Right(), ctxExpr))
|
guard := n.Left().(*ir.TypeSwitchGuard)
|
||||||
t := n.Left().Right().Type()
|
guard.SetRight(typecheck(guard.Right(), ctxExpr))
|
||||||
|
t := guard.Right().Type()
|
||||||
if t != nil && !t.IsInterface() {
|
if t != nil && !t.IsInterface() {
|
||||||
base.ErrorfAt(n.Pos(), "cannot type switch on non-interface value %L", n.Left().Right())
|
base.ErrorfAt(n.Pos(), "cannot type switch on non-interface value %L", guard.Right())
|
||||||
t = nil
|
t = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't actually declare the type switch's guarded
|
// We don't actually declare the type switch's guarded
|
||||||
// declaration itself. So if there are no cases, we won't
|
// declaration itself. So if there are no cases, we won't
|
||||||
// notice that it went unused.
|
// notice that it went unused.
|
||||||
if v := n.Left().Left(); v != nil && !ir.IsBlank(v) && n.List().Len() == 0 {
|
if v := guard.Left(); v != nil && !ir.IsBlank(v) && n.List().Len() == 0 {
|
||||||
base.ErrorfAt(v.Pos(), "%v declared but not used", v.Sym())
|
base.ErrorfAt(v.Pos(), "%v declared but not used", v.Sym())
|
||||||
}
|
}
|
||||||
|
|
||||||
var defCase, nilCase ir.Node
|
var defCase, nilCase ir.Node
|
||||||
var ts typeSet
|
var ts typeSet
|
||||||
for _, ncase := range n.List().Slice() {
|
for _, ncase := range n.List().Slice() {
|
||||||
|
ncase := ncase.(*ir.CaseStmt)
|
||||||
ls := ncase.List().Slice()
|
ls := ncase.List().Slice()
|
||||||
if len(ls) == 0 { // default:
|
if len(ls) == 0 { // default:
|
||||||
if defCase != nil {
|
if defCase != nil {
|
||||||
|
|
@ -60,31 +62,33 @@ func typecheckTypeSwitch(n ir.Node) {
|
||||||
|
|
||||||
var missing, have *types.Field
|
var missing, have *types.Field
|
||||||
var ptr int
|
var ptr int
|
||||||
switch {
|
if ir.IsNil(n1) { // case nil:
|
||||||
case ir.IsNil(n1): // case nil:
|
|
||||||
if nilCase != nil {
|
if nilCase != nil {
|
||||||
base.ErrorfAt(ncase.Pos(), "multiple nil cases in type switch (first at %v)", ir.Line(nilCase))
|
base.ErrorfAt(ncase.Pos(), "multiple nil cases in type switch (first at %v)", ir.Line(nilCase))
|
||||||
} else {
|
} else {
|
||||||
nilCase = ncase
|
nilCase = ncase
|
||||||
}
|
}
|
||||||
case n1.Op() != ir.OTYPE:
|
continue
|
||||||
|
}
|
||||||
|
if n1.Op() != ir.OTYPE {
|
||||||
base.ErrorfAt(ncase.Pos(), "%L is not a type", n1)
|
base.ErrorfAt(ncase.Pos(), "%L is not a type", n1)
|
||||||
case !n1.Type().IsInterface() && !implements(n1.Type(), t, &missing, &have, &ptr) && !missing.Broke():
|
continue
|
||||||
|
}
|
||||||
|
if !n1.Type().IsInterface() && !implements(n1.Type(), t, &missing, &have, &ptr) && !missing.Broke() {
|
||||||
if have != nil && !have.Broke() {
|
if have != nil && !have.Broke() {
|
||||||
base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
|
base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
|
||||||
" (wrong type for %v method)\n\thave %v%S\n\twant %v%S", n.Left().Right(), n1.Type(), missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
|
" (wrong type for %v method)\n\thave %v%S\n\twant %v%S", guard.Right(), n1.Type(), missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
|
||||||
} else if ptr != 0 {
|
} else if ptr != 0 {
|
||||||
base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
|
base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
|
||||||
" (%v method has pointer receiver)", n.Left().Right(), n1.Type(), missing.Sym)
|
" (%v method has pointer receiver)", guard.Right(), n1.Type(), missing.Sym)
|
||||||
} else {
|
} else {
|
||||||
base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
|
base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
|
||||||
" (missing %v method)", n.Left().Right(), n1.Type(), missing.Sym)
|
" (missing %v method)", guard.Right(), n1.Type(), missing.Sym)
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if n1.Op() == ir.OTYPE {
|
ts.add(ncase.Pos(), n1.Type())
|
||||||
ts.add(ncase.Pos(), n1.Type())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ncase.Rlist().Len() != 0 {
|
if ncase.Rlist().Len() != 0 {
|
||||||
|
|
@ -144,7 +148,7 @@ func (s *typeSet) add(pos src.XPos, typ *types.Type) {
|
||||||
s.m[ls] = append(prevs, typeSetEntry{pos, typ})
|
s.m[ls] = append(prevs, typeSetEntry{pos, typ})
|
||||||
}
|
}
|
||||||
|
|
||||||
func typecheckExprSwitch(n ir.Node) {
|
func typecheckExprSwitch(n *ir.SwitchStmt) {
|
||||||
t := types.Types[types.TBOOL]
|
t := types.Types[types.TBOOL]
|
||||||
if n.Left() != nil {
|
if n.Left() != nil {
|
||||||
n.SetLeft(typecheck(n.Left(), ctxExpr))
|
n.SetLeft(typecheck(n.Left(), ctxExpr))
|
||||||
|
|
@ -175,6 +179,7 @@ func typecheckExprSwitch(n ir.Node) {
|
||||||
var defCase ir.Node
|
var defCase ir.Node
|
||||||
var cs constSet
|
var cs constSet
|
||||||
for _, ncase := range n.List().Slice() {
|
for _, ncase := range n.List().Slice() {
|
||||||
|
ncase := ncase.(*ir.CaseStmt)
|
||||||
ls := ncase.List().Slice()
|
ls := ncase.List().Slice()
|
||||||
if len(ls) == 0 { // default:
|
if len(ls) == 0 { // default:
|
||||||
if defCase != nil {
|
if defCase != nil {
|
||||||
|
|
@ -225,7 +230,7 @@ func typecheckExprSwitch(n ir.Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// walkswitch walks a switch statement.
|
// walkswitch walks a switch statement.
|
||||||
func walkswitch(sw ir.Node) {
|
func walkswitch(sw *ir.SwitchStmt) {
|
||||||
// Guard against double walk, see #25776.
|
// Guard against double walk, see #25776.
|
||||||
if sw.List().Len() == 0 && sw.Body().Len() > 0 {
|
if sw.List().Len() == 0 && sw.Body().Len() > 0 {
|
||||||
return // Was fatal, but eliminating every possible source of double-walking is hard
|
return // Was fatal, but eliminating every possible source of double-walking is hard
|
||||||
|
|
@ -240,7 +245,7 @@ func walkswitch(sw ir.Node) {
|
||||||
|
|
||||||
// walkExprSwitch generates an AST implementing sw. sw is an
|
// walkExprSwitch generates an AST implementing sw. sw is an
|
||||||
// expression switch.
|
// expression switch.
|
||||||
func walkExprSwitch(sw ir.Node) {
|
func walkExprSwitch(sw *ir.SwitchStmt) {
|
||||||
lno := setlineno(sw)
|
lno := setlineno(sw)
|
||||||
|
|
||||||
cond := sw.Left()
|
cond := sw.Left()
|
||||||
|
|
@ -278,6 +283,7 @@ func walkExprSwitch(sw ir.Node) {
|
||||||
var defaultGoto ir.Node
|
var defaultGoto ir.Node
|
||||||
var body ir.Nodes
|
var body ir.Nodes
|
||||||
for _, ncase := range sw.List().Slice() {
|
for _, ncase := range sw.List().Slice() {
|
||||||
|
ncase := ncase.(*ir.CaseStmt)
|
||||||
label := autolabel(".s")
|
label := autolabel(".s")
|
||||||
jmp := npos(ncase.Pos(), nodSym(ir.OGOTO, nil, label))
|
jmp := npos(ncase.Pos(), nodSym(ir.OGOTO, nil, label))
|
||||||
|
|
||||||
|
|
@ -393,7 +399,7 @@ func (s *exprSwitch) flush() {
|
||||||
func(i int) ir.Node {
|
func(i int) ir.Node {
|
||||||
return ir.Nod(ir.OLE, ir.Nod(ir.OLEN, s.exprname, nil), nodintconst(runLen(runs[i-1])))
|
return ir.Nod(ir.OLE, ir.Nod(ir.OLEN, s.exprname, nil), nodintconst(runLen(runs[i-1])))
|
||||||
},
|
},
|
||||||
func(i int, nif ir.Node) {
|
func(i int, nif *ir.IfStmt) {
|
||||||
run := runs[i]
|
run := runs[i]
|
||||||
nif.SetLeft(ir.Nod(ir.OEQ, ir.Nod(ir.OLEN, s.exprname, nil), nodintconst(runLen(run))))
|
nif.SetLeft(ir.Nod(ir.OEQ, ir.Nod(ir.OLEN, s.exprname, nil), nodintconst(runLen(run))))
|
||||||
s.search(run, nif.PtrBody())
|
s.search(run, nif.PtrBody())
|
||||||
|
|
@ -428,7 +434,7 @@ func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) {
|
||||||
func(i int) ir.Node {
|
func(i int) ir.Node {
|
||||||
return ir.Nod(ir.OLE, s.exprname, cc[i-1].hi)
|
return ir.Nod(ir.OLE, s.exprname, cc[i-1].hi)
|
||||||
},
|
},
|
||||||
func(i int, nif ir.Node) {
|
func(i int, nif *ir.IfStmt) {
|
||||||
c := &cc[i]
|
c := &cc[i]
|
||||||
nif.SetLeft(c.test(s.exprname))
|
nif.SetLeft(c.test(s.exprname))
|
||||||
nif.PtrBody().Set1(c.jmp)
|
nif.PtrBody().Set1(c.jmp)
|
||||||
|
|
@ -456,7 +462,7 @@ func (c *exprClause) test(exprname ir.Node) ir.Node {
|
||||||
return ir.NodAt(c.pos, ir.OEQ, exprname, c.lo)
|
return ir.NodAt(c.pos, ir.OEQ, exprname, c.lo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func allCaseExprsAreSideEffectFree(sw ir.Node) bool {
|
func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool {
|
||||||
// In theory, we could be more aggressive, allowing any
|
// In theory, we could be more aggressive, allowing any
|
||||||
// side-effect-free expressions in cases, but it's a bit
|
// side-effect-free expressions in cases, but it's a bit
|
||||||
// tricky because some of that information is unavailable due
|
// tricky because some of that information is unavailable due
|
||||||
|
|
@ -465,9 +471,7 @@ func allCaseExprsAreSideEffectFree(sw ir.Node) bool {
|
||||||
// enough.
|
// enough.
|
||||||
|
|
||||||
for _, ncase := range sw.List().Slice() {
|
for _, ncase := range sw.List().Slice() {
|
||||||
if ncase.Op() != ir.OCASE {
|
ncase := ncase.(*ir.CaseStmt)
|
||||||
base.Fatalf("switch string(byteslice) bad op: %v", ncase.Op())
|
|
||||||
}
|
|
||||||
for _, v := range ncase.List().Slice() {
|
for _, v := range ncase.List().Slice() {
|
||||||
if v.Op() != ir.OLITERAL {
|
if v.Op() != ir.OLITERAL {
|
||||||
return false
|
return false
|
||||||
|
|
@ -497,9 +501,9 @@ func hasFall(stmts []ir.Node) (bool, src.XPos) {
|
||||||
|
|
||||||
// walkTypeSwitch generates an AST that implements sw, where sw is a
|
// walkTypeSwitch generates an AST that implements sw, where sw is a
|
||||||
// type switch.
|
// type switch.
|
||||||
func walkTypeSwitch(sw ir.Node) {
|
func walkTypeSwitch(sw *ir.SwitchStmt) {
|
||||||
var s typeSwitch
|
var s typeSwitch
|
||||||
s.facename = sw.Left().Right()
|
s.facename = sw.Left().(*ir.TypeSwitchGuard).Right()
|
||||||
sw.SetLeft(nil)
|
sw.SetLeft(nil)
|
||||||
|
|
||||||
s.facename = walkexpr(s.facename, sw.PtrInit())
|
s.facename = walkexpr(s.facename, sw.PtrInit())
|
||||||
|
|
@ -541,6 +545,7 @@ func walkTypeSwitch(sw ir.Node) {
|
||||||
var defaultGoto, nilGoto ir.Node
|
var defaultGoto, nilGoto ir.Node
|
||||||
var body ir.Nodes
|
var body ir.Nodes
|
||||||
for _, ncase := range sw.List().Slice() {
|
for _, ncase := range sw.List().Slice() {
|
||||||
|
ncase := ncase.(*ir.CaseStmt)
|
||||||
var caseVar ir.Node
|
var caseVar ir.Node
|
||||||
if ncase.Rlist().Len() != 0 {
|
if ncase.Rlist().Len() != 0 {
|
||||||
caseVar = ncase.Rlist().First()
|
caseVar = ncase.Rlist().First()
|
||||||
|
|
@ -704,7 +709,7 @@ func (s *typeSwitch) flush() {
|
||||||
func(i int) ir.Node {
|
func(i int) ir.Node {
|
||||||
return ir.Nod(ir.OLE, s.hashname, nodintconst(int64(cc[i-1].hash)))
|
return ir.Nod(ir.OLE, s.hashname, nodintconst(int64(cc[i-1].hash)))
|
||||||
},
|
},
|
||||||
func(i int, nif ir.Node) {
|
func(i int, nif *ir.IfStmt) {
|
||||||
// TODO(mdempsky): Omit hash equality check if
|
// TODO(mdempsky): Omit hash equality check if
|
||||||
// there's only one type.
|
// there's only one type.
|
||||||
c := cc[i]
|
c := cc[i]
|
||||||
|
|
@ -723,7 +728,7 @@ func (s *typeSwitch) flush() {
|
||||||
//
|
//
|
||||||
// leaf(i, nif) should setup nif (an OIF node) to test case i. In
|
// leaf(i, nif) should setup nif (an OIF node) to test case i. In
|
||||||
// particular, it should set nif.Left and nif.Nbody.
|
// particular, it should set nif.Left and nif.Nbody.
|
||||||
func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i int, nif ir.Node)) {
|
func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i int, nif *ir.IfStmt)) {
|
||||||
const binarySearchMin = 4 // minimum number of cases for binary search
|
const binarySearchMin = 4 // minimum number of cases for binary search
|
||||||
|
|
||||||
var do func(lo, hi int, out *ir.Nodes)
|
var do func(lo, hi int, out *ir.Nodes)
|
||||||
|
|
@ -731,7 +736,7 @@ func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i in
|
||||||
n := hi - lo
|
n := hi - lo
|
||||||
if n < binarySearchMin {
|
if n < binarySearchMin {
|
||||||
for i := lo; i < hi; i++ {
|
for i := lo; i < hi; i++ {
|
||||||
nif := ir.Nod(ir.OIF, nil, nil)
|
nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
|
||||||
leaf(i, nif)
|
leaf(i, nif)
|
||||||
base.Pos = base.Pos.WithNotStmt()
|
base.Pos = base.Pos.WithNotStmt()
|
||||||
nif.SetLeft(typecheck(nif.Left(), ctxExpr))
|
nif.SetLeft(typecheck(nif.Left(), ctxExpr))
|
||||||
|
|
|
||||||
|
|
@ -349,14 +349,17 @@ func walkstmt(n ir.Node) ir.Node {
|
||||||
return n
|
return n
|
||||||
|
|
||||||
case ir.OSELECT:
|
case ir.OSELECT:
|
||||||
|
n := n.(*ir.SelectStmt)
|
||||||
walkselect(n)
|
walkselect(n)
|
||||||
return n
|
return n
|
||||||
|
|
||||||
case ir.OSWITCH:
|
case ir.OSWITCH:
|
||||||
|
n := n.(*ir.SwitchStmt)
|
||||||
walkswitch(n)
|
walkswitch(n)
|
||||||
return n
|
return n
|
||||||
|
|
||||||
case ir.ORANGE:
|
case ir.ORANGE:
|
||||||
|
n := n.(*ir.RangeStmt)
|
||||||
return walkrange(n)
|
return walkrange(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue