tInvalid = types.Typ[types.Invalid]
tUntypedNil = types.Typ[types.UntypedNil]
tRangeIter = &types.Basic{Name: "iter"} // the type of all "range" iterators
+ tEface = new(types.Interface)
// The result type of a "select".
tSelect = &types.Result{Values: []*types.Var{
return intLiteral(at.Len)
}
// Otherwise treat as normal.
+
+ case "panic":
+ fn.emit(&Panic{X: emitConv(fn, b.expr(fn, args[0]), tEface)})
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+ return vFalse // any non-nil Value will do
}
return nil // treat all others as a regular function call
}
// Type conversion, e.g. string(x) or big.Int(x)
return emitConv(fn, b.expr(fn, e.Args[0]), typ)
}
- // Call to "intrinsic" built-ins, e.g. new, make.
- wasPanic := false
+ // Call to "intrinsic" built-ins, e.g. new, make, panic.
if id, ok := e.Fun.(*ast.Ident); ok {
obj := b.obj(id)
if _, ok := fn.Prog.Builtins[obj]; ok {
if v := b.builtin(fn, id.Name, e.Args, typ); v != nil {
return v
}
- wasPanic = id.Name == "panic"
}
}
// Regular function call.
var v Call
b.setCall(fn, e, &v.CallCommon)
v.setType(typ)
- fn.emit(&v)
-
- // Compile panic as if followed by for{} so that its
- // successor is unreachable.
- // TODO(adonovan): consider a dedicated Panic instruction
- // (in which case, don't forget Go and Defer).
- if wasPanic {
- emitSelfLoop(fn)
- fn.currentBlock = fn.newBasicBlock("unreachable")
- }
- return &v
+ return fn.emit(&v)
case *ast.UnaryExpr:
switch e.Op {
bptypes = append(bptypes, nil) // map
bptypes = append(bptypes, nil) // key
case "print", "println": // print{,ln}(any, ...any)
- vt = new(types.Interface) // variadic
+ vt = tEface // variadic
if !c.HasEllipsis {
args, varargs = args[:1], args[1:]
}
}
bptypes = append(bptypes, argType, argType)
case "panic":
- bptypes = append(bptypes, new(types.Interface))
+ bptypes = append(bptypes, tEface)
case "recover":
// no-op
default:
case *ast.GoStmt:
// The "intrinsics" new/make/len/cap are forbidden here.
- // panic() is not forbidden, but is not (yet) an intrinsic.
+ // panic is treated like an ordinary function call.
var v Go
b.setCall(fn, s.Call, &v.CallCommon)
fn.emit(&v)
case *ast.DeferStmt:
// The "intrinsics" new/make/len/cap are forbidden here.
- // panic() is not forbidden, but is not (yet) an intrinsic.
+ // panic is treated like an ordinary function call.
var v Defer
b.setCall(fn, s.Call, &v.CallCommon)
fn.emit(&v)
f.emit(&ret)
f.currentBlock = nil
}
-
-// emitSelfLoop emits to f a self-loop.
-// This is a defensive measure to ensure control-flow integrity.
-// It should never be reachable.
-// Postcondition: f.currentBlock is nil.
-//
-func emitSelfLoop(f *Function) {
- loop := f.newBasicBlock("selfloop")
- emitJump(f, loop)
- f.currentBlock = loop
- emitJump(f, loop)
-}
}
return kReturn
+ case *ssa.Panic:
+ panic(targetPanic{fr.get(instr.X)})
+
case *ssa.Send:
fr.get(instr.Chan).(chan value) <- copyVal(fr.get(instr.X))
for {
if i.mode&EnableTracing != 0 {
- fmt.Fprintf(os.Stderr, ".%s:\n", fr.block.Name)
+ fmt.Fprintf(os.Stderr, ".%s:\n", fr.block)
}
block:
for _, instr = range fr.block.Instrs {
v value
}
+// If the target program calls exit, the interpreter panics with this type.
+type exitPanic int
+
// literalValue returns the value of the literal with the
// dynamic type tag appropriate for l.Type().
func literalValue(l *ssa.Literal) value {
}
case "panic":
+ // ssa.Panic handles most cases; this is only for "go
+ // panic" or "defer panic".
panic(targetPanic{args[0]})
case "recover":
return printCall(&s.CallCommon, "go ", s)
}
+func (s *Panic) String() string {
+ return "panic " + relName(s.X, s)
+}
+
func (s *Ret) String() string {
var b bytes.Buffer
b.WriteString("ret")
func (s *sanity) checkInstr(idx int, instr Instruction) {
switch instr := instr.(type) {
- case *If, *Jump, *Ret:
+ case *If, *Jump, *Ret, *Panic:
s.errorf("control flow instruction not at end of block")
case *Phi:
if idx == 0 {
}
// TODO(adonovan): check number and types of results
+ case *Panic:
+ if nsuccs := len(s.block.Succs); nsuccs != 0 {
+ s.errorf("Panic-terminated block has %d successors; expected none", nsuccs)
+ return
+ }
+
default:
s.errorf("non-control flow instruction at end of block")
}
// An SSA basic block.
//
// The final element of Instrs is always an explicit transfer of
-// control (If, Jump or Ret).
+// control (If, Jump, Ret or Panic).
//
// A block may contain no Instructions only if it is unreachable,
// i.e. Preds is nil. Empty blocks are typically pruned.
Results []Value
}
+// Panic initiates a panic with value X.
+//
+// A Panic instruction must be the last instruction of its containing
+// BasicBlock, which must have no successors.
+//
+// NB: 'go panic(x)' and 'defer panic(x)' do not use this instruction;
+// they are treated as calls to a built-in function.
+//
+// Example printed form:
+// panic t0
+//
+type Panic struct {
+ anInstruction
+ X Value // an interface{}
+}
+
// Go creates a new goroutine and calls the specified function
// within it.
//
func (*MakeSlice) ImplementsInstruction() {}
func (*MapUpdate) ImplementsInstruction() {}
func (*Next) ImplementsInstruction() {}
+func (*Panic) ImplementsInstruction() {}
func (*Phi) ImplementsInstruction() {}
func (*Range) ImplementsInstruction() {}
func (*Ret) ImplementsInstruction() {}
return append(rands, &v.Iter)
}
+func (s *Panic) Operands(rands []*Value) []*Value {
+ return append(rands, &s.X)
+}
+
func (v *Phi) Operands(rands []*Value) []*Value {
for i := range v.Edges {
rands = append(rands, &v.Edges[i])