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:
Alan Donovan 2013-07-24 14:03:53 -04:00
parent c5b1cc4227
commit d4f2f2d7fa
2 changed files with 43 additions and 33 deletions

View File

@ -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)

View File

@ -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)