From d4f2f2d7fa932351635515cbd0496684e6da6fd2 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 24 Jul 2013 14:03:53 -0400 Subject: [PATCH] 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 --- go/types/resolver.go | 2 -- ssa/builder.go | 74 +++++++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/go/types/resolver.go b/go/types/resolver.go index 55733510..360959c1 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -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) diff --git a/ssa/builder.go b/ssa/builder.go index 50e1cbc3..fd4e57f2 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -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 // if ok1 then goto S1 else goto .caseT2 // .caseT2: // t2, ok2 := typeswitch,ok x - // 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 + // if x == nil then goto SN else goto .caseT3 + // .SN: + // y := x + // ...SN... + // goto done // .caseT3: // t3, ok3 := typeswitch,ok x // 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 + var ti Value // ti, ok := typeassert,ok x 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)