diff --git a/GNUmakefile b/GNUmakefile index 28031ffec..2b85d610e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -362,6 +362,7 @@ TEST_PACKAGES_FAST = \ # debug/plan9obj requires os.ReadAt, which is not yet supported on windows # image requires recover(), which is not yet supported on wasi # io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi +# math/bits: needs panic()/recover() # mime: fail on wasi; neds panic()/recover() # mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK # mime/quotedprintable requires syscall.Faccessat @@ -382,8 +383,10 @@ TEST_PACKAGES_LINUX := \ crypto/hmac \ debug/dwarf \ debug/plan9obj \ + go/constant \ image \ io/ioutil \ + math/bits \ mime \ mime/multipart \ mime/quotedprintable \ @@ -403,6 +406,8 @@ TEST_PACKAGES_WINDOWS := \ compress/flate \ crypto/des \ crypto/hmac \ + go/constant \ + math/bits \ strconv \ text/template/parse \ $(nil) diff --git a/compiler/asserts.go b/compiler/asserts.go index f07b73bc2..a3d060ce5 100644 --- a/compiler/asserts.go +++ b/compiler/asserts.go @@ -31,7 +31,7 @@ func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) { // Now do the bounds check: index >= arrayLen outOfBounds := b.CreateICmp(llvm.IntUGE, index, arrayLen, "") - b.createRuntimeAssert(outOfBounds, "lookup", "lookupPanic") + b.createRuntimeAssert(outOfBounds, "lookup", "lookupPanic", false) } // createSliceBoundsCheck emits a bounds check before a slicing operation to make @@ -74,7 +74,7 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo outOfBounds3 := b.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap") outOfBounds := b.CreateOr(outOfBounds1, outOfBounds2, "slice.lowmax") outOfBounds = b.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap") - b.createRuntimeAssert(outOfBounds, "slice", "slicePanic") + b.createRuntimeAssert(outOfBounds, "slice", "slicePanic", false) } // createSliceToArrayPointerCheck adds a check for slice-to-array pointer @@ -86,7 +86,7 @@ func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen i // > run-time panic occurs. arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false) isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "") - b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic") + b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic", false) } // createUnsafeSliceStringCheck inserts a runtime check used for unsafe.Slice @@ -118,7 +118,7 @@ func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value, lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "") assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "") assert = b.CreateOr(assert, lenOutOfBounds, "") - b.createRuntimeAssert(assert, name, "unsafeSlicePanic") + b.createRuntimeAssert(assert, name, "unsafeSlicePanic", false) } // createChanBoundsCheck creates a bounds check before creating a new channel to @@ -155,7 +155,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, // Do the check for a too large (or negative) buffer size. bufSizeTooBig := b.CreateICmp(llvm.IntUGE, bufSize, maxBufSize, "") - b.createRuntimeAssert(bufSizeTooBig, "chan", "chanMakePanic") + b.createRuntimeAssert(bufSizeTooBig, "chan", "chanMakePanic", false) } // createNilCheck checks whether the given pointer is nil, and panics if it is. @@ -199,7 +199,7 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str isnil := b.CreateICmp(llvm.IntEQ, ptr, nilptr, "") // Emit the nil check in IR. - b.createRuntimeAssert(isnil, blockPrefix, "nilPanic") + b.createRuntimeAssert(isnil, blockPrefix, "nilPanic", false) } // createNegativeShiftCheck creates an assertion that panics if the given shift value is negative. @@ -212,7 +212,7 @@ func (b *builder) createNegativeShiftCheck(shift llvm.Value) { // isNegative = shift < 0 isNegative := b.CreateICmp(llvm.IntSLT, shift, llvm.ConstInt(shift.Type(), 0, false), "") - b.createRuntimeAssert(isNegative, "shift", "negativeShiftPanic") + b.createRuntimeAssert(isNegative, "shift", "negativeShiftPanic", false) } // createDivideByZeroCheck asserts that y is not zero. If it is, a runtime panic @@ -225,12 +225,12 @@ func (b *builder) createDivideByZeroCheck(y llvm.Value) { // isZero = y == 0 isZero := b.CreateICmp(llvm.IntEQ, y, llvm.ConstInt(y.Type(), 0, false), "") - b.createRuntimeAssert(isZero, "divbyzero", "divideByZeroPanic") + b.createRuntimeAssert(isZero, "divbyzero", "divideByZeroPanic", true) } // createRuntimeAssert is a common function to create a new branch on an assert // bool, calling an assert func if the assert value is true (1). -func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string) { +func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string, invoke bool) { // Check whether we can resolve this check at compile time. if !assert.IsAConstantInt().IsNil() { val := assert.ZExtValue() @@ -252,7 +252,13 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc // Fail: the assert triggered so panic. b.SetInsertPointAtEnd(faultBlock) - b.createRuntimeCall(assertFunc, nil, "") + if invoke { + // This runtime panic is recoverable. + b.createRuntimeInvoke(assertFunc, nil, "") + } else { + // This runtime panic is not recoverable. + b.createRuntimeCall(assertFunc, nil, "") + } b.CreateUnreachable() // Ok: assert didn't trigger so continue normally. diff --git a/src/runtime/error.go b/src/runtime/error.go index 3ae5ea3aa..0bca20c0b 100644 --- a/src/runtime/error.go +++ b/src/runtime/error.go @@ -4,5 +4,22 @@ package runtime type Error interface { error + // Method to indicate this is indeed a runtime error. RuntimeError() } + +type runtimeError struct { + msg string +} + +func (r runtimeError) Error() string { + return r.msg +} + +// Purely here to satisfy the Error interface. +func (r runtimeError) RuntimeError() {} + +var ( + divideError error = runtimeError{"runtime error: integer divide by zero"} + overflowError error = runtimeError{"runtime error: integer overflow"} +) diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 9ae1f982b..1cabe8a51 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -220,7 +220,7 @@ func negativeShiftPanic() { // Panic when there is a divide by zero. func divideByZeroPanic() { - runtimePanicAt(returnAddress(0), "divide by zero") + _panic(divideError) } func blockingPanic() { diff --git a/testdata/recover.go b/testdata/recover.go index 6fdf282e7..0a6a93dd7 100644 --- a/testdata/recover.go +++ b/testdata/recover.go @@ -30,6 +30,9 @@ func main() { println("\n# defer panic") deferPanic() + println("\n# runtime panics") + runtimePanicDivByZero(1, 0) + println("\n# runtime.Goexit") runtimeGoexit() } @@ -114,6 +117,16 @@ func deferPanic() { println("defer panic") } +func runtimePanicDivByZero(a, b int) int { + defer func() { + if err := recover(); err != nil { + println("recovered:", err) + } + }() + + return a / b +} + func runtimeGoexit() { wg.Add(1) go func() {