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() == "_" {
|
||||
// blank identifiers are not declared
|
||||
obj.setParent(scope)
|
||||
obj = nil
|
||||
} else if alt := scope.Insert(obj); alt != nil {
|
||||
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
|
||||
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) {
|
||||
if obj.Name() == "_" {
|
||||
// blank identifiers are not declared
|
||||
obj = nil
|
||||
} else if alt := oset.insert(obj); alt != nil {
|
||||
check.errorf(obj.Pos(), "%s redeclared", obj.Name())
|
||||
check.reportAltDecl(alt)
|
||||
|
|
|
|||
|
|
@ -1382,30 +1382,40 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
|||
// var x X
|
||||
// switch y := x.(type) {
|
||||
// case T1, T2: S1 // >1 (y := x)
|
||||
// case nil: SN // nil (y := x)
|
||||
// default: SD // 0 types (y := x)
|
||||
// case T3: S3 // 1 type (y := x.(T3))
|
||||
// }
|
||||
//
|
||||
// ...s.Init...
|
||||
// x := eval x
|
||||
// y := x
|
||||
// .caseT1:
|
||||
// t1, ok1 := typeswitch,ok x <T1>
|
||||
// if ok1 then goto S1 else goto .caseT2
|
||||
// .caseT2:
|
||||
// t2, ok2 := typeswitch,ok x <T2>
|
||||
// if ok2 then goto S1 else goto .caseT3
|
||||
// if ok2 then goto S1 else goto .caseNil
|
||||
// .S1:
|
||||
// y := x
|
||||
// ...S1...
|
||||
// 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:
|
||||
// t3, ok3 := typeswitch,ok x <T3>
|
||||
// if ok3 then goto S3 else goto default
|
||||
// .S3:
|
||||
// y' := t3 // Kludge: within scope of S3, y resolves here
|
||||
// y := t3
|
||||
// ...S3...
|
||||
// goto done
|
||||
// .default:
|
||||
// y := x
|
||||
// ...SD...
|
||||
// goto 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)
|
||||
case *ast.AssignStmt: // y := x.(type)
|
||||
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")
|
||||
if label != nil {
|
||||
label._break = done
|
||||
}
|
||||
var dfltBody []ast.Stmt
|
||||
var default_ *ast.CaseClause
|
||||
for _, clause := range s.Body.List {
|
||||
cc := clause.(*ast.CaseClause)
|
||||
if cc.List == nil {
|
||||
dfltBody = cc.Body
|
||||
default_ = cc
|
||||
continue
|
||||
}
|
||||
body := fn.newBasicBlock("typeswitch.body")
|
||||
var next *BasicBlock
|
||||
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 {
|
||||
next = fn.newBasicBlock("typeswitch.next")
|
||||
casetype = fn.Pkg.typeOf(cond)
|
||||
var condv Value
|
||||
if casetype == tUntypedNil {
|
||||
condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos)
|
||||
ti = x
|
||||
} else {
|
||||
yok := emitTypeTest(fn, x, casetype, token.NoPos)
|
||||
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)
|
||||
fn.currentBlock = next
|
||||
}
|
||||
if len(cc.List) != 1 {
|
||||
ti = x
|
||||
}
|
||||
fn.currentBlock = body
|
||||
if obj := fn.Pkg.info.TypeCaseVar(cc); obj != nil {
|
||||
// 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)
|
||||
b.typeCase(fn, cc, ti, done)
|
||||
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{
|
||||
tail: fn.targets,
|
||||
_break: done,
|
||||
}
|
||||
b.stmtList(fn, dfltBody)
|
||||
b.stmtList(fn, cc.Body)
|
||||
fn.targets = fn.targets.tail
|
||||
emitJump(fn, done)
|
||||
fn.currentBlock = done
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
var dfltBody *[]ast.Stmt
|
||||
var defaultBody *[]ast.Stmt
|
||||
state := 0
|
||||
r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV
|
||||
for _, cc := range s.Body.List {
|
||||
clause := cc.(*ast.CommClause)
|
||||
if clause.Comm == nil {
|
||||
dfltBody = &clause.Body
|
||||
defaultBody = &clause.Body
|
||||
continue
|
||||
}
|
||||
body := fn.newBasicBlock("select.body")
|
||||
|
|
@ -1619,12 +1631,12 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
|
|||
fn.currentBlock = next
|
||||
state++
|
||||
}
|
||||
if dfltBody != nil {
|
||||
if defaultBody != nil {
|
||||
fn.targets = &targets{
|
||||
tail: fn.targets,
|
||||
_break: done,
|
||||
}
|
||||
b.stmtList(fn, *dfltBody)
|
||||
b.stmtList(fn, *defaultBody)
|
||||
fn.targets = fn.targets.tail
|
||||
}
|
||||
emitJump(fn, done)
|
||||
|
|
|
|||
Loading…
Reference in New Issue