Skip to content

Commit 26aba72

Browse files
aykevldeadprogram
authored andcommitted
transform: replace panics with source locations
Panics are bad for usability: whenever something breaks, the user is shown a (not very informative) backtrace. Replace it with real error messages instead, that even try to display the Go source location.
1 parent 25fcf3e commit 26aba72

File tree

3 files changed

+31
-18
lines changed

3 files changed

+31
-18
lines changed

transform/interface-lowering.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ type lowerInterfacesPass struct {
147147
// emitted by the compiler as higher-level intrinsics. They need some lowering
148148
// before LLVM can work on them. This is done so that a few cleanup passes can
149149
// run before assigning the final type codes.
150-
func LowerInterfaces(mod llvm.Module) {
150+
func LowerInterfaces(mod llvm.Module) error {
151151
p := &lowerInterfacesPass{
152152
mod: mod,
153153
builder: mod.Context().NewBuilder(),
@@ -157,11 +157,11 @@ func LowerInterfaces(mod llvm.Module) {
157157
signatures: make(map[string]*signatureInfo),
158158
interfaces: make(map[string]*interfaceInfo),
159159
}
160-
p.run()
160+
return p.run()
161161
}
162162

163163
// run runs the pass itself.
164-
func (p *lowerInterfacesPass) run() {
164+
func (p *lowerInterfacesPass) run() error {
165165
// Collect all type codes.
166166
typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0)
167167
typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0)
@@ -303,19 +303,22 @@ func (p *lowerInterfacesPass) run() {
303303
} else if len(itf.types) == 1 {
304304
// There is only one implementation of the given type.
305305
// Call that function directly.
306-
p.replaceInvokeWithCall(use, itf.types[0], signature)
306+
err := p.replaceInvokeWithCall(use, itf.types[0], signature)
307+
if err != nil {
308+
return err
309+
}
307310
} else {
308311
// There are multiple types implementing this interface, thus there
309312
// are multiple possible functions to call. Delegate calling the
310313
// right function to a special wrapper function.
311314
inttoptrs := getUses(use)
312315
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
313-
panic("expected exactly one inttoptr use of runtime.interfaceMethod")
316+
return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod")
314317
}
315318
inttoptr := inttoptrs[0]
316319
calls := getUses(inttoptr)
317320
if len(calls) != 1 || calls[0].IsACallInst().IsNil() {
318-
panic("expected exactly one call use of runtime.interfaceMethod")
321+
return errorAt(use, "internal error: expected exactly one call use of runtime.interfaceMethod")
319322
}
320323
call := calls[0]
321324

@@ -369,11 +372,6 @@ func (p *lowerInterfacesPass) run() {
369372
}
370373
sort.Sort(sort.Reverse(typeSlice))
371374

372-
// A type code must fit in 16 bits.
373-
if len(typeSlice) >= 1<<16 {
374-
panic("typecode does not fit in a uint16: too many types in this program")
375-
}
376-
377375
// Assign a type code for each type.
378376
assignTypeCodes(p.mod, typeSlice)
379377

@@ -433,6 +431,7 @@ func (p *lowerInterfacesPass) run() {
433431
typ.methodSet = llvm.Value{}
434432
}
435433
}
434+
return nil
436435
}
437436

438437
// addTypeMethods reads the method set of the given type info struct. It
@@ -493,10 +492,10 @@ func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo {
493492
// replaceInvokeWithCall replaces a runtime.interfaceMethod + inttoptr with a
494493
// concrete method. This can be done when only one type implements the
495494
// interface.
496-
func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) {
495+
func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) error {
497496
inttoptrs := getUses(use)
498497
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
499-
panic("expected exactly one inttoptr use of runtime.interfaceMethod")
498+
return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod")
500499
}
501500
inttoptr := inttoptrs[0]
502501
function := typ.getMethod(signature).function
@@ -511,7 +510,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
511510
// function.
512511
for _, call := range getUses(inttoptr) {
513512
if call.IsACallInst().IsNil() || call.CalledValue() != inttoptr {
514-
panic("expected the inttoptr to be called as a method, this is not a method call")
513+
return errorAt(call, "internal error: expected the inttoptr to be called as a method, this is not a method call")
515514
}
516515
operands := make([]llvm.Value, call.OperandsCount()-1)
517516
for i := range operands {
@@ -522,7 +521,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
522521
methodParamTypes := paramTypes[len(paramTypes)-(len(operands)-1):]
523522
for i, methodParamType := range methodParamTypes {
524523
if methodParamType != operands[i+1].Type() {
525-
panic("expected method call param type and function param type to be the same")
524+
return errorAt(call, "internal error: expected method call param type and function param type to be the same")
526525
}
527526
}
528527
p.builder.SetInsertPointBefore(call)
@@ -536,6 +535,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
536535
}
537536
inttoptr.EraseFromParentAsInstruction()
538537
use.EraseFromParentAsInstruction()
538+
return nil
539539
}
540540

541541
// getInterfaceImplementsFunc returns a function that checks whether a given

transform/interface-lowering_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@ package transform
22

33
import (
44
"testing"
5+
6+
"tinygo.org/x/go-llvm"
57
)
68

79
func TestInterfaceLowering(t *testing.T) {
810
t.Parallel()
9-
testTransform(t, "testdata/interface", LowerInterfaces)
11+
testTransform(t, "testdata/interface", func(mod llvm.Module) {
12+
err := LowerInterfaces(mod)
13+
if err != nil {
14+
t.Error(err)
15+
}
16+
})
1017
}

transform/optimizer.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
7575
OptimizeMaps(mod)
7676
OptimizeStringToBytes(mod)
7777
OptimizeAllocs(mod)
78-
LowerInterfaces(mod)
78+
err := LowerInterfaces(mod)
79+
if err != nil {
80+
return []error{err}
81+
}
7982

8083
errs := LowerInterrupts(mod)
8184
if len(errs) > 0 {
@@ -115,7 +118,10 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
115118

116119
} else {
117120
// Must be run at any optimization level.
118-
LowerInterfaces(mod)
121+
err := LowerInterfaces(mod)
122+
if err != nil {
123+
return []error{err}
124+
}
119125
if config.FuncImplementation() == compileopts.FuncValueSwitch {
120126
LowerFuncValues(mod)
121127
}

0 commit comments

Comments
 (0)