aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-11-26 14:49:44 +0100
committerRon Evans <[email protected]>2019-11-26 15:45:39 +0100
commit24ff2d1ee228a6dc78ea61a228743c04e37e972e (patch)
tree08fea7b9b2adf082153bbfe4bd4765e9bf22a527
parent0db26b06624f566790eb799f0252d6a50817e7fd (diff)
downloadtinygo-24ff2d1ee228a6dc78ea61a228743c04e37e972e.tar.gz
tinygo-24ff2d1ee228a6dc78ea61a228743c04e37e972e.zip
interp: replace many panics with error messages
This commit replaces most panics in interp/frame.go and interp/scan.go with real error messages. The remaining ones are panics that should not happen when working with valid IR.
-rw-r--r--interp/errors.go3
-rw-r--r--interp/frame.go26
-rw-r--r--interp/interp.go1
-rw-r--r--interp/scan.go31
-rw-r--r--interp/scan_test.go6
5 files changed, 40 insertions, 27 deletions
diff --git a/interp/errors.go b/interp/errors.go
index c3cf0a1ad..26b3ea377 100644
--- a/interp/errors.go
+++ b/interp/errors.go
@@ -78,6 +78,9 @@ func errorAt(inst llvm.Value, msg string) scanner.Error {
// getPosition returns the position information for the given instruction, as
// far as it is available.
func getPosition(inst llvm.Value) token.Position {
+ if inst.IsAInstruction().IsNil() {
+ return token.Position{}
+ }
loc := inst.InstructionDebugLoc()
if loc.IsNil() {
return token.Position{}
diff --git a/interp/frame.go b/interp/frame.go
index c42dcf253..04a9fa0cd 100644
--- a/interp/frame.go
+++ b/interp/frame.go
@@ -97,7 +97,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
value = operand.Load()
}
if value.Type() != inst.Type() {
- panic("interp: load: type does not match")
+ return nil, nil, fr.errorAt(inst, "interp: load: type does not match")
}
fr.locals[inst] = fr.getValue(value)
case !inst.IsAStoreInst().IsNil():
@@ -121,15 +121,13 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
// Not a constant operation.
// This should be detected by the scanner, but isn't at the
// moment.
- panic("todo: non-const gep")
+ return nil, nil, fr.errorAt(inst, "todo: non-const gep")
}
indices[i] = uint32(operand.Value().ZExtValue())
}
result := value.GetElementPtr(indices)
if result.Type() != inst.Type() {
- println(" expected:", inst.Type().String())
- println(" actual: ", result.Type().String())
- panic("interp: gep: type does not match")
+ return nil, nil, fr.errorAt(inst, "interp: gep: type does not match")
}
fr.locals[inst] = result
@@ -184,7 +182,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
}
}
// It is not possible in Go to bitcast a map value to a pointer.
- panic("unimplemented: bitcast of map")
+ return nil, nil, fr.errorAt(inst, "unimplemented: bitcast of map")
}
value := fr.getLocal(operand).(*LocalValue)
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
@@ -397,16 +395,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
if typecode.IsAConstantExpr().IsNil() || typecode.Opcode() != llvm.PtrToInt {
- panic("interp: expected typecode to be a ptrtoint")
+ return nil, nil, fr.errorAt(inst, "interp: expected typecode to be a ptrtoint")
}
typecode = typecode.Operand(0)
if interfaceMethodSet.IsAConstantExpr().IsNil() || interfaceMethodSet.Opcode() != llvm.GetElementPtr {
- panic("interp: expected method set in runtime.interfaceImplements to be a constant gep")
+ return nil, nil, fr.errorAt(inst, "interp: expected method set in runtime.interfaceImplements to be a constant gep")
}
interfaceMethodSet = interfaceMethodSet.Operand(0).Initializer()
methodSet := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1})
if methodSet.IsAConstantExpr().IsNil() || methodSet.Opcode() != llvm.GetElementPtr {
- panic("interp: expected method set to be a constant gep")
+ return nil, nil, fr.errorAt(inst, "interp: expected method set to be a constant gep")
}
methodSet = methodSet.Operand(0).Initializer()
@@ -476,7 +474,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
params = append(params, local)
}
var ret Value
- scanResult := fr.Eval.hasSideEffects(callee)
+ scanResult, err := fr.hasSideEffects(callee)
+ if err != nil {
+ return nil, nil, err
+ }
if scanResult.severity == sideEffectLimited || dirtyParams && scanResult.severity != sideEffectAll {
// Side effect is bounded. This means the operation invokes
// side effects (like calling an external function) but it
@@ -545,7 +546,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
// conditional branch (if/then/else)
cond := fr.getLocal(inst.Operand(0)).Value()
if cond.Type() != fr.Mod.Context().Int1Type() {
- panic("expected an i1 in a branch instruction")
+ return nil, nil, fr.errorAt(inst, "expected an i1 in a branch instruction")
}
thenBB := inst.Operand(1)
elseBB := inst.Operand(2)
@@ -563,7 +564,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false): // true
return nil, []llvm.Value{elseBB}, nil // else
default:
- panic("branch was not true or false")
+ return nil, nil, fr.errorAt(inst, "branch was not true or false")
}
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
// unconditional branch (goto)
@@ -589,6 +590,7 @@ func (fr *frame) getLocal(v llvm.Value) Value {
} else if value := fr.getValue(v); value != nil {
return value
} else {
+ // This should not happen under normal circumstances.
panic("cannot find value")
}
}
diff --git a/interp/interp.go b/interp/interp.go
index f91c31f3e..cea2316f5 100644
--- a/interp/interp.go
+++ b/interp/interp.go
@@ -27,7 +27,6 @@ type Eval struct {
type evalPackage struct {
*Eval
packagePath string
- errors []error
}
// Run evaluates the function with the given name and then eliminates all
diff --git a/interp/scan.go b/interp/scan.go
index b8c68117f..7a256c27c 100644
--- a/interp/scan.go
+++ b/interp/scan.go
@@ -38,33 +38,33 @@ type sideEffectResult struct {
// hasSideEffects scans this function and all descendants, recursively. It
// returns whether this function has side effects and if it does, which globals
// it mentions anywhere in this function or any called functions.
-func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
+func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
switch fn.Name() {
case "runtime.alloc":
// Cannot be scanned but can be interpreted.
- return &sideEffectResult{severity: sideEffectNone}
+ return &sideEffectResult{severity: sideEffectNone}, nil
case "runtime.nanotime":
// Fixed value at compile time.
- return &sideEffectResult{severity: sideEffectNone}
+ return &sideEffectResult{severity: sideEffectNone}, nil
case "runtime._panic":
- return &sideEffectResult{severity: sideEffectLimited}
+ return &sideEffectResult{severity: sideEffectLimited}, nil
case "runtime.interfaceImplements":
- return &sideEffectResult{severity: sideEffectNone}
+ return &sideEffectResult{severity: sideEffectNone}, nil
case "runtime.sliceCopy":
- return &sideEffectResult{severity: sideEffectNone}
+ return &sideEffectResult{severity: sideEffectNone}, nil
case "runtime.trackPointer":
- return &sideEffectResult{severity: sideEffectNone}
+ return &sideEffectResult{severity: sideEffectNone}, nil
case "llvm.dbg.value":
- return &sideEffectResult{severity: sideEffectNone}
+ return &sideEffectResult{severity: sideEffectNone}, nil
}
if fn.IsDeclaration() {
- return &sideEffectResult{severity: sideEffectLimited}
+ return &sideEffectResult{severity: sideEffectLimited}, nil
}
if e.sideEffectFuncs == nil {
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
}
if se, ok := e.sideEffectFuncs[fn]; ok {
- return se
+ return se, nil
}
result := &sideEffectResult{
severity: sideEffectInProgress,
@@ -75,6 +75,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
for bb := fn.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst.IsAInstruction().IsNil() {
+ // Should not happen in valid IR.
panic("not an instruction")
}
@@ -91,7 +92,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
switch inst.InstructionOpcode() {
case llvm.IndirectBr, llvm.Invoke:
// Not emitted by the compiler.
- panic("unknown instructions")
+ return nil, e.errorAt(inst, "unknown instructions")
case llvm.Call:
child := inst.CalledValue()
if !child.IsAInlineAsm().IsNil() {
@@ -117,7 +118,10 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
}
continue
}
- childSideEffects := e.hasSideEffects(child)
+ childSideEffects, err := e.hasSideEffects(child)
+ if err != nil {
+ return nil, err
+ }
switch childSideEffects.severity {
case sideEffectInProgress, sideEffectNone:
// no side effects or recursive function - continue scanning
@@ -159,7 +163,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
// No side effect was reported for this function.
result.severity = sideEffectNone
}
- return result
+ return result, nil
}
// hasLocalSideEffects checks whether the given instruction flows into a branch
@@ -174,6 +178,7 @@ func (e *Eval) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct{}, inst llv
for use := inst.FirstUse(); !use.IsNil(); use = use.NextUse() {
user := use.User()
if user.IsAInstruction().IsNil() {
+ // Should not happen in valid IR.
panic("user not an instruction")
}
switch user.InstructionOpcode() {
diff --git a/interp/scan_test.go b/interp/scan_test.go
index 2150f108e..9d3e15dbe 100644
--- a/interp/scan_test.go
+++ b/interp/scan_test.go
@@ -59,7 +59,11 @@ func TestScan(t *testing.T) {
t.Errorf("scan test: could not find tested function %s in the IR", tc.name)
continue
}
- result := e.hasSideEffects(fn)
+ evalPkg := &evalPackage{e, "testdata"}
+ result, err := evalPkg.hasSideEffects(fn)
+ if err != nil {
+ t.Errorf("scan test: failed to scan %s for side effects: %v", fn.Name(), err)
+ }
// Check whether the result is what we expect.
if result.severity != tc.severity {