diff options
author | Ayke van Laethem <[email protected]> | 2020-03-23 14:40:38 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2020-03-24 15:07:55 +0100 |
commit | 26aba727296c7302cf369d2b6e32b4fe010e1434 (patch) | |
tree | a7060c4a27f7d96ea8429fc43b6a59fcd791ec22 /transform | |
parent | 25fcf3e18e3baf6496dddba0a3f8811b2ca90609 (diff) | |
download | tinygo-26aba727296c7302cf369d2b6e32b4fe010e1434.tar.gz tinygo-26aba727296c7302cf369d2b6e32b4fe010e1434.zip |
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.
Diffstat (limited to 'transform')
-rw-r--r-- | transform/interface-lowering.go | 30 | ||||
-rw-r--r-- | transform/interface-lowering_test.go | 9 | ||||
-rw-r--r-- | transform/optimizer.go | 10 |
3 files changed, 31 insertions, 18 deletions
diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index 5c017dd91..2f85e79c2 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -147,7 +147,7 @@ type lowerInterfacesPass struct { // emitted by the compiler as higher-level intrinsics. They need some lowering // before LLVM can work on them. This is done so that a few cleanup passes can // run before assigning the final type codes. -func LowerInterfaces(mod llvm.Module) { +func LowerInterfaces(mod llvm.Module) error { p := &lowerInterfacesPass{ mod: mod, builder: mod.Context().NewBuilder(), @@ -157,11 +157,11 @@ func LowerInterfaces(mod llvm.Module) { signatures: make(map[string]*signatureInfo), interfaces: make(map[string]*interfaceInfo), } - p.run() + return p.run() } // run runs the pass itself. -func (p *lowerInterfacesPass) run() { +func (p *lowerInterfacesPass) run() error { // Collect all type codes. typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0) typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0) @@ -303,19 +303,22 @@ func (p *lowerInterfacesPass) run() { } else if len(itf.types) == 1 { // There is only one implementation of the given type. // Call that function directly. - p.replaceInvokeWithCall(use, itf.types[0], signature) + err := p.replaceInvokeWithCall(use, itf.types[0], signature) + if err != nil { + return err + } } else { // There are multiple types implementing this interface, thus there // are multiple possible functions to call. Delegate calling the // right function to a special wrapper function. inttoptrs := getUses(use) if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() { - panic("expected exactly one inttoptr use of runtime.interfaceMethod") + return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod") } inttoptr := inttoptrs[0] calls := getUses(inttoptr) if len(calls) != 1 || calls[0].IsACallInst().IsNil() { - panic("expected exactly one call use of runtime.interfaceMethod") + return errorAt(use, "internal error: expected exactly one call use of runtime.interfaceMethod") } call := calls[0] @@ -369,11 +372,6 @@ func (p *lowerInterfacesPass) run() { } sort.Sort(sort.Reverse(typeSlice)) - // A type code must fit in 16 bits. - if len(typeSlice) >= 1<<16 { - panic("typecode does not fit in a uint16: too many types in this program") - } - // Assign a type code for each type. assignTypeCodes(p.mod, typeSlice) @@ -433,6 +431,7 @@ func (p *lowerInterfacesPass) run() { typ.methodSet = llvm.Value{} } } + return nil } // addTypeMethods reads the method set of the given type info struct. It @@ -493,10 +492,10 @@ func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo { // replaceInvokeWithCall replaces a runtime.interfaceMethod + inttoptr with a // concrete method. This can be done when only one type implements the // interface. -func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) { +func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) error { inttoptrs := getUses(use) if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() { - panic("expected exactly one inttoptr use of runtime.interfaceMethod") + return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod") } inttoptr := inttoptrs[0] function := typ.getMethod(signature).function @@ -511,7 +510,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf // function. for _, call := range getUses(inttoptr) { if call.IsACallInst().IsNil() || call.CalledValue() != inttoptr { - panic("expected the inttoptr to be called as a method, this is not a method call") + return errorAt(call, "internal error: expected the inttoptr to be called as a method, this is not a method call") } operands := make([]llvm.Value, call.OperandsCount()-1) for i := range operands { @@ -522,7 +521,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf methodParamTypes := paramTypes[len(paramTypes)-(len(operands)-1):] for i, methodParamType := range methodParamTypes { if methodParamType != operands[i+1].Type() { - panic("expected method call param type and function param type to be the same") + return errorAt(call, "internal error: expected method call param type and function param type to be the same") } } p.builder.SetInsertPointBefore(call) @@ -536,6 +535,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf } inttoptr.EraseFromParentAsInstruction() use.EraseFromParentAsInstruction() + return nil } // getInterfaceImplementsFunc returns a function that checks whether a given diff --git a/transform/interface-lowering_test.go b/transform/interface-lowering_test.go index 2bc8ed643..f3652cc90 100644 --- a/transform/interface-lowering_test.go +++ b/transform/interface-lowering_test.go @@ -2,9 +2,16 @@ package transform import ( "testing" + + "tinygo.org/x/go-llvm" ) func TestInterfaceLowering(t *testing.T) { t.Parallel() - testTransform(t, "testdata/interface", LowerInterfaces) + testTransform(t, "testdata/interface", func(mod llvm.Module) { + err := LowerInterfaces(mod) + if err != nil { + t.Error(err) + } + }) } diff --git a/transform/optimizer.go b/transform/optimizer.go index 71bf4d617..2677e6902 100644 --- a/transform/optimizer.go +++ b/transform/optimizer.go @@ -75,7 +75,10 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i OptimizeMaps(mod) OptimizeStringToBytes(mod) OptimizeAllocs(mod) - LowerInterfaces(mod) + err := LowerInterfaces(mod) + if err != nil { + return []error{err} + } errs := LowerInterrupts(mod) if len(errs) > 0 { @@ -115,7 +118,10 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i } else { // Must be run at any optimization level. - LowerInterfaces(mod) + err := LowerInterfaces(mod) + if err != nil { + return []error{err} + } if config.FuncImplementation() == compileopts.FuncValueSwitch { LowerFuncValues(mod) } |