go.tools/ssa: fix bug in code emitted for ast.TypeAssertExpr.

var x I = ...
x.(E) may fail dynamically (iff x is nil).

Added a testcase.

R=gri
CC=golang-dev
https://golang.org/cl/10237045
This commit is contained in:
Alan Donovan 2013-06-13 17:31:32 -04:00
parent 3162ce0df2
commit 0f26bbae8f
4 changed files with 30 additions and 8 deletions

View File

@ -247,9 +247,8 @@ func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
// Simplify infallible assertions.
txi := x.Type().Underlying().(*types.Interface)
if ti, ok := t.Underlying().(*types.Interface); ok {
if types.IsIdentical(ti, txi) {
return x
}
// Even when ti==txi, we still need ChangeInterface
// since it performs a nil-check.
if isSuperinterface(ti, txi) {
c := &ChangeInterface{X: x}
c.setPos(pos)

View File

@ -161,7 +161,11 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args)
case *ssa.ChangeInterface:
fr.env[instr] = fr.get(instr.X) // (can't fail)
x := fr.get(instr.X)
if x.(iface).t == nil {
panic(fmt.Sprintf("interface conversion: interface is nil, not %s", instr.Type()))
}
fr.env[instr] = x
case *ssa.ChangeType:
fr.env[instr] = fr.get(instr.X) // (can't fail)

View File

@ -329,6 +329,26 @@ func init() {
}
}
// An I->I conversion always succeeds.
func init() {
var x I
if I(x) != I(nil) {
panic("I->I conversion failed")
}
}
// An I->I type-assert fails iff the value is nil.
func init() {
defer func() {
r := recover()
if r != "interface conversion: interface is nil, not main.I" {
panic("I->I type assertion succeeed for nil value")
}
}()
var x I
_ = x.(I)
}
//////////////////////////////////////////////////////////////////////
// Variadic bridge methods and interface thunks.

View File

@ -588,16 +588,15 @@ type Convert struct {
// ChangeInterface constructs a value of one interface type from a
// value of another interface type known to be assignable to it.
//
// This operation cannot fail. Use TypeAssert for interface
// conversions that may fail dynamically.
// This operation fails if the operand is nil.
// For all other operands, well-typedness ensures success.
// Use TypeAssert for interface conversions that are uncertain.
//
// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
// instruction arose from an explicit e.(T) operation; or token.NoPos
// otherwise.
//
// TODO(adonovan) what about the nil case of e = e.(E)? Test.
//
// Example printed form:
// t1 = change interface interface{} <- I (t0)
//