go.tools/ssa: repair treatment of typeswitch after recent go/types changes.
Now, in a "switch y := x.(type)", there is no object for the outer y, only implicit objects, one per case (including default). Also: don't set obj=nil for blank idents (workaround suggested by gri). R=gri CC=golang-dev https://golang.org/cl/11564046
This commit is contained in:
parent
c5b1cc4227
commit
d4f2f2d7fa
|
|
@ -27,7 +27,6 @@ func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) {
|
||||||
if obj.Name() == "_" {
|
if obj.Name() == "_" {
|
||||||
// blank identifiers are not declared
|
// blank identifiers are not declared
|
||||||
obj.setParent(scope)
|
obj.setParent(scope)
|
||||||
obj = nil
|
|
||||||
} else if alt := scope.Insert(obj); alt != nil {
|
} else if alt := scope.Insert(obj); alt != nil {
|
||||||
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
|
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
|
||||||
check.reportAltDecl(alt)
|
check.reportAltDecl(alt)
|
||||||
|
|
@ -41,7 +40,6 @@ func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) {
|
||||||
func (check *checker) declareFld(oset *objset, id *ast.Ident, obj Object) {
|
func (check *checker) declareFld(oset *objset, id *ast.Ident, obj Object) {
|
||||||
if obj.Name() == "_" {
|
if obj.Name() == "_" {
|
||||||
// blank identifiers are not declared
|
// blank identifiers are not declared
|
||||||
obj = nil
|
|
||||||
} else if alt := oset.insert(obj); alt != nil {
|
} else if alt := oset.insert(obj); alt != nil {
|
||||||
check.errorf(obj.Pos(), "%s redeclared", obj.Name())
|
check.errorf(obj.Pos(), "%s redeclared", obj.Name())
|
||||||
check.reportAltDecl(alt)
|
check.reportAltDecl(alt)
|
||||||
|
|
|
||||||
|
|
@ -1382,30 +1382,40 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
||||||
// var x X
|
// var x X
|
||||||
// switch y := x.(type) {
|
// switch y := x.(type) {
|
||||||
// case T1, T2: S1 // >1 (y := x)
|
// case T1, T2: S1 // >1 (y := x)
|
||||||
|
// case nil: SN // nil (y := x)
|
||||||
// default: SD // 0 types (y := x)
|
// default: SD // 0 types (y := x)
|
||||||
// case T3: S3 // 1 type (y := x.(T3))
|
// case T3: S3 // 1 type (y := x.(T3))
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// ...s.Init...
|
// ...s.Init...
|
||||||
// x := eval x
|
// x := eval x
|
||||||
// y := x
|
|
||||||
// .caseT1:
|
// .caseT1:
|
||||||
// t1, ok1 := typeswitch,ok x <T1>
|
// t1, ok1 := typeswitch,ok x <T1>
|
||||||
// if ok1 then goto S1 else goto .caseT2
|
// if ok1 then goto S1 else goto .caseT2
|
||||||
// .caseT2:
|
// .caseT2:
|
||||||
// t2, ok2 := typeswitch,ok x <T2>
|
// t2, ok2 := typeswitch,ok x <T2>
|
||||||
// if ok2 then goto S1 else goto .caseT3
|
// if ok2 then goto S1 else goto .caseNil
|
||||||
// .S1:
|
// .S1:
|
||||||
|
// y := x
|
||||||
// ...S1...
|
// ...S1...
|
||||||
// goto done
|
// goto done
|
||||||
|
// .caseNil:
|
||||||
|
// if t2, ok2 := typeswitch,ok x <T2>
|
||||||
|
// if x == nil then goto SN else goto .caseT3
|
||||||
|
// .SN:
|
||||||
|
// y := x
|
||||||
|
// ...SN...
|
||||||
|
// goto done
|
||||||
// .caseT3:
|
// .caseT3:
|
||||||
// t3, ok3 := typeswitch,ok x <T3>
|
// t3, ok3 := typeswitch,ok x <T3>
|
||||||
// if ok3 then goto S3 else goto default
|
// if ok3 then goto S3 else goto default
|
||||||
// .S3:
|
// .S3:
|
||||||
// y' := t3 // Kludge: within scope of S3, y resolves here
|
// y := t3
|
||||||
// ...S3...
|
// ...S3...
|
||||||
// goto done
|
// goto done
|
||||||
// .default:
|
// .default:
|
||||||
|
// y := x
|
||||||
|
// ...SD...
|
||||||
// goto done
|
// goto done
|
||||||
// .done:
|
// .done:
|
||||||
|
|
||||||
|
|
@ -1419,33 +1429,30 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
||||||
x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X)
|
x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X)
|
||||||
case *ast.AssignStmt: // y := x.(type)
|
case *ast.AssignStmt: // y := x.(type)
|
||||||
x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
|
x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
|
||||||
id := ass.Lhs[0].(*ast.Ident)
|
|
||||||
fn.addLocalForIdent(id)
|
|
||||||
lval := b.addr(fn, id, false) // non-escaping
|
|
||||||
lval.store(fn, x)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
done := fn.newBasicBlock("typeswitch.done")
|
done := fn.newBasicBlock("typeswitch.done")
|
||||||
if label != nil {
|
if label != nil {
|
||||||
label._break = done
|
label._break = done
|
||||||
}
|
}
|
||||||
var dfltBody []ast.Stmt
|
var default_ *ast.CaseClause
|
||||||
for _, clause := range s.Body.List {
|
for _, clause := range s.Body.List {
|
||||||
cc := clause.(*ast.CaseClause)
|
cc := clause.(*ast.CaseClause)
|
||||||
if cc.List == nil {
|
if cc.List == nil {
|
||||||
dfltBody = cc.Body
|
default_ = cc
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
body := fn.newBasicBlock("typeswitch.body")
|
body := fn.newBasicBlock("typeswitch.body")
|
||||||
var next *BasicBlock
|
var next *BasicBlock
|
||||||
var casetype types.Type
|
var casetype types.Type
|
||||||
var ti Value // t_i, ok := typeassert,ok x <T_i>
|
var ti Value // ti, ok := typeassert,ok x <Ti>
|
||||||
for _, cond := range cc.List {
|
for _, cond := range cc.List {
|
||||||
next = fn.newBasicBlock("typeswitch.next")
|
next = fn.newBasicBlock("typeswitch.next")
|
||||||
casetype = fn.Pkg.typeOf(cond)
|
casetype = fn.Pkg.typeOf(cond)
|
||||||
var condv Value
|
var condv Value
|
||||||
if casetype == tUntypedNil {
|
if casetype == tUntypedNil {
|
||||||
condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos)
|
condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos)
|
||||||
|
ti = x
|
||||||
} else {
|
} else {
|
||||||
yok := emitTypeTest(fn, x, casetype, token.NoPos)
|
yok := emitTypeTest(fn, x, casetype, token.NoPos)
|
||||||
ti = emitExtract(fn, yok, 0, casetype)
|
ti = emitExtract(fn, yok, 0, casetype)
|
||||||
|
|
@ -1454,32 +1461,37 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
||||||
emitIf(fn, condv, body, next)
|
emitIf(fn, condv, body, next)
|
||||||
fn.currentBlock = next
|
fn.currentBlock = next
|
||||||
}
|
}
|
||||||
|
if len(cc.List) != 1 {
|
||||||
|
ti = x
|
||||||
|
}
|
||||||
fn.currentBlock = body
|
fn.currentBlock = body
|
||||||
if obj := fn.Pkg.info.TypeCaseVar(cc); obj != nil {
|
b.typeCase(fn, cc, ti, done)
|
||||||
// Declare a new shadow local variable of the
|
|
||||||
// same name but a more specific type.
|
|
||||||
y2 := fn.addNamedLocal(obj)
|
|
||||||
y2.name += "'" // debugging aid
|
|
||||||
y2.typ = types.NewPointer(casetype)
|
|
||||||
emitStore(fn, y2, ti)
|
|
||||||
}
|
|
||||||
fn.targets = &targets{
|
|
||||||
tail: fn.targets,
|
|
||||||
_break: done,
|
|
||||||
}
|
|
||||||
b.stmtList(fn, cc.Body)
|
|
||||||
fn.targets = fn.targets.tail
|
|
||||||
emitJump(fn, done)
|
|
||||||
fn.currentBlock = next
|
fn.currentBlock = next
|
||||||
}
|
}
|
||||||
|
if default_ != nil {
|
||||||
|
b.typeCase(fn, default_, x, done)
|
||||||
|
} else {
|
||||||
|
emitJump(fn, done)
|
||||||
|
}
|
||||||
|
fn.currentBlock = done
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) typeCase(fn *Function, cc *ast.CaseClause, x Value, done *BasicBlock) {
|
||||||
|
if obj := fn.Pkg.info.TypeCaseVar(cc); obj != nil {
|
||||||
|
// In a switch y := x.(type), each case clause
|
||||||
|
// implicitly declares a distinct object y.
|
||||||
|
// In a single-type case, y has that type.
|
||||||
|
// In multi-type cases, 'case nil' and default,
|
||||||
|
// y has the same type as the interface operand.
|
||||||
|
emitStore(fn, fn.addNamedLocal(obj), x)
|
||||||
|
}
|
||||||
fn.targets = &targets{
|
fn.targets = &targets{
|
||||||
tail: fn.targets,
|
tail: fn.targets,
|
||||||
_break: done,
|
_break: done,
|
||||||
}
|
}
|
||||||
b.stmtList(fn, dfltBody)
|
b.stmtList(fn, cc.Body)
|
||||||
fn.targets = fn.targets.tail
|
fn.targets = fn.targets.tail
|
||||||
emitJump(fn, done)
|
emitJump(fn, done)
|
||||||
fn.currentBlock = done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectStmt emits to fn code for the select statement s, optionally
|
// selectStmt emits to fn code for the select statement s, optionally
|
||||||
|
|
@ -1576,13 +1588,13 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
|
||||||
label._break = done
|
label._break = done
|
||||||
}
|
}
|
||||||
|
|
||||||
var dfltBody *[]ast.Stmt
|
var defaultBody *[]ast.Stmt
|
||||||
state := 0
|
state := 0
|
||||||
r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV
|
r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV
|
||||||
for _, cc := range s.Body.List {
|
for _, cc := range s.Body.List {
|
||||||
clause := cc.(*ast.CommClause)
|
clause := cc.(*ast.CommClause)
|
||||||
if clause.Comm == nil {
|
if clause.Comm == nil {
|
||||||
dfltBody = &clause.Body
|
defaultBody = &clause.Body
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
body := fn.newBasicBlock("select.body")
|
body := fn.newBasicBlock("select.body")
|
||||||
|
|
@ -1619,12 +1631,12 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
|
||||||
fn.currentBlock = next
|
fn.currentBlock = next
|
||||||
state++
|
state++
|
||||||
}
|
}
|
||||||
if dfltBody != nil {
|
if defaultBody != nil {
|
||||||
fn.targets = &targets{
|
fn.targets = &targets{
|
||||||
tail: fn.targets,
|
tail: fn.targets,
|
||||||
_break: done,
|
_break: done,
|
||||||
}
|
}
|
||||||
b.stmtList(fn, *dfltBody)
|
b.stmtList(fn, *defaultBody)
|
||||||
fn.targets = fn.targets.tail
|
fn.targets = fn.targets.tail
|
||||||
}
|
}
|
||||||
emitJump(fn, done)
|
emitJump(fn, done)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue