mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
cmd/compile: support inlining of type switches
This CL adds support for inlining type switches, including exporting and importing them. Type switches are represented mostly the same as expression switches. However, if the type switch guard includes a short variable declaration, then there are two differences: (1) there's an ONONAME (in the OTYPESW's Left) to represent the overall pseudo declaration; and (2) there's an ONAME (in each OCASE's Rlist) to represent the per-case variables. For simplicity, this CL simply writes out each variable separately using iimport/iiexport's normal Vargen mechanism for disambiguating identically named variables within a function. This could be improved somewhat, but inlinable type switches are probably too uncommon to merit the complexity. While here, remove "case OCASE" from typecheck1. We only type check "case" clauses as part of a "select" or "switch" statement, never as standalone statements. Fixes #37837 Change-Id: I8f42f6c9afdd821d6202af4a6bf1dbcbba0ef424 Reviewed-on: https://go-review.googlesource.com/c/go/+/266203 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org> Trust: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
362d25f2c8
commit
5736eb0013
@ -1138,13 +1138,10 @@ func (w *exportWriter) stmt(n *Node) {
|
|||||||
w.pos(n.Pos)
|
w.pos(n.Pos)
|
||||||
w.stmtList(n.Ninit)
|
w.stmtList(n.Ninit)
|
||||||
w.exprsOrNil(n.Left, nil)
|
w.exprsOrNil(n.Left, nil)
|
||||||
w.stmtList(n.List)
|
w.caseList(n)
|
||||||
|
|
||||||
case OCASE:
|
// case OCASE:
|
||||||
w.op(OCASE)
|
// handled by caseList
|
||||||
w.pos(n.Pos)
|
|
||||||
w.stmtList(n.List)
|
|
||||||
w.stmtList(n.Nbody)
|
|
||||||
|
|
||||||
case OFALL:
|
case OFALL:
|
||||||
w.op(OFALL)
|
w.op(OFALL)
|
||||||
@ -1168,6 +1165,24 @@ func (w *exportWriter) stmt(n *Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *exportWriter) caseList(sw *Node) {
|
||||||
|
namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil
|
||||||
|
|
||||||
|
cases := sw.List.Slice()
|
||||||
|
w.uint64(uint64(len(cases)))
|
||||||
|
for _, cas := range cases {
|
||||||
|
if cas.Op != OCASE {
|
||||||
|
Fatalf("expected OCASE, got %v", cas)
|
||||||
|
}
|
||||||
|
w.pos(cas.Pos)
|
||||||
|
w.stmtList(cas.List)
|
||||||
|
if namedTypeSwitch {
|
||||||
|
w.localName(cas.Rlist.First())
|
||||||
|
}
|
||||||
|
w.stmtList(cas.Nbody)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (w *exportWriter) exprList(list Nodes) {
|
func (w *exportWriter) exprList(list Nodes) {
|
||||||
for _, n := range list.Slice() {
|
for _, n := range list.Slice() {
|
||||||
w.expr(n)
|
w.expr(n)
|
||||||
@ -1232,6 +1247,19 @@ func (w *exportWriter) expr(n *Node) {
|
|||||||
w.op(OTYPE)
|
w.op(OTYPE)
|
||||||
w.typ(n.Type)
|
w.typ(n.Type)
|
||||||
|
|
||||||
|
case OTYPESW:
|
||||||
|
w.op(OTYPESW)
|
||||||
|
w.pos(n.Pos)
|
||||||
|
var s *types.Sym
|
||||||
|
if n.Left != nil {
|
||||||
|
if n.Left.Op != ONONAME {
|
||||||
|
Fatalf("expected ONONAME, got %v", n.Left)
|
||||||
|
}
|
||||||
|
s = n.Left.Sym
|
||||||
|
}
|
||||||
|
w.localIdent(s, 0) // declared pseudo-variable, if any
|
||||||
|
w.exprsOrNil(n.Right, nil)
|
||||||
|
|
||||||
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
|
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
|
||||||
// should have been resolved by typechecking - handled by default case
|
// should have been resolved by typechecking - handled by default case
|
||||||
|
|
||||||
|
@ -784,6 +784,28 @@ func (r *importReader) stmtList() []*Node {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *importReader) caseList(sw *Node) []*Node {
|
||||||
|
namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil
|
||||||
|
|
||||||
|
cases := make([]*Node, r.uint64())
|
||||||
|
for i := range cases {
|
||||||
|
cas := nodl(r.pos(), OCASE, nil, nil)
|
||||||
|
cas.List.Set(r.stmtList())
|
||||||
|
if namedTypeSwitch {
|
||||||
|
// Note: per-case variables will have distinct, dotted
|
||||||
|
// names after import. That's okay: swt.go only needs
|
||||||
|
// Sym for diagnostics anyway.
|
||||||
|
caseVar := newnamel(cas.Pos, r.ident())
|
||||||
|
declare(caseVar, dclcontext)
|
||||||
|
cas.Rlist.Set1(caseVar)
|
||||||
|
caseVar.Name.Defn = sw.Left
|
||||||
|
}
|
||||||
|
cas.Nbody.Set(r.stmtList())
|
||||||
|
cases[i] = cas
|
||||||
|
}
|
||||||
|
return cases
|
||||||
|
}
|
||||||
|
|
||||||
func (r *importReader) exprList() []*Node {
|
func (r *importReader) exprList() []*Node {
|
||||||
var list []*Node
|
var list []*Node
|
||||||
for {
|
for {
|
||||||
@ -831,6 +853,14 @@ func (r *importReader) node() *Node {
|
|||||||
case OTYPE:
|
case OTYPE:
|
||||||
return typenod(r.typ())
|
return typenod(r.typ())
|
||||||
|
|
||||||
|
case OTYPESW:
|
||||||
|
n := nodl(r.pos(), OTYPESW, nil, nil)
|
||||||
|
if s := r.ident(); s != nil {
|
||||||
|
n.Left = npos(n.Pos, newnoname(s))
|
||||||
|
}
|
||||||
|
n.Right, _ = r.exprsOrNil()
|
||||||
|
return n
|
||||||
|
|
||||||
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
|
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
|
||||||
// unreachable - should have been resolved by typechecking
|
// unreachable - should have been resolved by typechecking
|
||||||
|
|
||||||
@ -1025,16 +1055,11 @@ func (r *importReader) node() *Node {
|
|||||||
n := nodl(r.pos(), op, nil, nil)
|
n := nodl(r.pos(), op, nil, nil)
|
||||||
n.Ninit.Set(r.stmtList())
|
n.Ninit.Set(r.stmtList())
|
||||||
n.Left, _ = r.exprsOrNil()
|
n.Left, _ = r.exprsOrNil()
|
||||||
n.List.Set(r.stmtList())
|
n.List.Set(r.caseList(n))
|
||||||
return n
|
return n
|
||||||
|
|
||||||
case OCASE:
|
// case OCASE:
|
||||||
n := nodl(r.pos(), OCASE, nil, nil)
|
// handled by caseList
|
||||||
n.List.Set(r.exprList())
|
|
||||||
// TODO(gri) eventually we must declare variables for type switch
|
|
||||||
// statements (type switch statements are not yet exported)
|
|
||||||
n.Nbody.Set(r.stmtList())
|
|
||||||
return n
|
|
||||||
|
|
||||||
case OFALL:
|
case OFALL:
|
||||||
n := nodl(r.pos(), OFALL, nil, nil)
|
n := nodl(r.pos(), OFALL, nil, nil)
|
||||||
|
@ -392,13 +392,9 @@ func (v *hairyVisitor) visit(n *Node) bool {
|
|||||||
v.reason = "call to recover"
|
v.reason = "call to recover"
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case OCALLPART:
|
|
||||||
// OCALLPART is inlineable, but no extra cost to the budget
|
|
||||||
|
|
||||||
case OCLOSURE,
|
case OCLOSURE,
|
||||||
ORANGE,
|
ORANGE,
|
||||||
OSELECT,
|
OSELECT,
|
||||||
OTYPESW,
|
|
||||||
OGO,
|
OGO,
|
||||||
ODEFER,
|
ODEFER,
|
||||||
ODCLTYPE, // can't print yet
|
ODCLTYPE, // can't print yet
|
||||||
|
@ -2065,11 +2065,6 @@ func typecheck1(n *Node, top int) (res *Node) {
|
|||||||
n.Type = nil
|
n.Type = nil
|
||||||
return n
|
return n
|
||||||
|
|
||||||
case OCASE:
|
|
||||||
ok |= ctxStmt
|
|
||||||
typecheckslice(n.List.Slice(), ctxExpr)
|
|
||||||
typecheckslice(n.Nbody.Slice(), ctxStmt)
|
|
||||||
|
|
||||||
case ODCLFUNC:
|
case ODCLFUNC:
|
||||||
ok |= ctxStmt
|
ok |= ctxStmt
|
||||||
typecheckfunc(n)
|
typecheckfunc(n)
|
||||||
|
33
test/fixedbugs/issue37837.dir/a.go
Normal file
33
test/fixedbugs/issue37837.dir/a.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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 a
|
||||||
|
|
||||||
|
func F(i interface{}) int { // ERROR "can inline F" "i does not escape"
|
||||||
|
switch i.(type) {
|
||||||
|
case nil:
|
||||||
|
return 0
|
||||||
|
case int:
|
||||||
|
return 1
|
||||||
|
case float64:
|
||||||
|
return 2
|
||||||
|
default:
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func G(i interface{}) interface{} { // ERROR "can inline G" "leaking param: i"
|
||||||
|
switch i := i.(type) {
|
||||||
|
case nil: // ERROR "moved to heap: i"
|
||||||
|
return &i
|
||||||
|
case int: // ERROR "moved to heap: i"
|
||||||
|
return &i
|
||||||
|
case float64: // ERROR "moved to heap: i"
|
||||||
|
return &i
|
||||||
|
case string, []byte: // ERROR "moved to heap: i"
|
||||||
|
return &i
|
||||||
|
default: // ERROR "moved to heap: i"
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
}
|
32
test/fixedbugs/issue37837.dir/b.go
Normal file
32
test/fixedbugs/issue37837.dir/b.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 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 main
|
||||||
|
|
||||||
|
import "./a"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Test that inlined type switches without short variable
|
||||||
|
// declarations work correctly.
|
||||||
|
check(0, a.F(nil)) // ERROR "inlining call to a.F"
|
||||||
|
check(1, a.F(0)) // ERROR "inlining call to a.F" "does not escape"
|
||||||
|
check(2, a.F(0.0)) // ERROR "inlining call to a.F" "does not escape"
|
||||||
|
check(3, a.F("")) // ERROR "inlining call to a.F" "does not escape"
|
||||||
|
|
||||||
|
// Test that inlined type switches with short variable
|
||||||
|
// declarations work correctly.
|
||||||
|
_ = a.G(nil).(*interface{}) // ERROR "inlining call to a.G"
|
||||||
|
_ = a.G(1).(*int) // ERROR "inlining call to a.G" "does not escape"
|
||||||
|
_ = a.G(2.0).(*float64) // ERROR "inlining call to a.G" "does not escape"
|
||||||
|
_ = (*a.G("").(*interface{})).(string) // ERROR "inlining call to a.G" "does not escape"
|
||||||
|
_ = (*a.G(([]byte)(nil)).(*interface{})).([]byte) // ERROR "inlining call to a.G" "does not escape"
|
||||||
|
_ = (*a.G(true).(*interface{})).(bool) // ERROR "inlining call to a.G" "does not escape"
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func check(want, got int) {
|
||||||
|
if want != got {
|
||||||
|
println("want", want, "but got", got)
|
||||||
|
}
|
||||||
|
}
|
7
test/fixedbugs/issue37837.go
Normal file
7
test/fixedbugs/issue37837.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// errorcheckandrundir -0 -m
|
||||||
|
|
||||||
|
// 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 ignored
|
@ -152,8 +152,7 @@ func switchBreak(x, y int) int {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// can't currently inline functions with a type switch
|
func switchType(x interface{}) int { // ERROR "can inline switchType" "x does not escape"
|
||||||
func switchType(x interface{}) int { // ERROR "x does not escape"
|
|
||||||
switch x.(type) {
|
switch x.(type) {
|
||||||
case int:
|
case int:
|
||||||
return x.(int)
|
return x.(int)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user