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.
Этот коммит содержится в:
Ayke van Laethem 2020-03-23 14:40:38 +01:00 коммит произвёл Ron Evans
родитель 25fcf3e18e
коммит 26aba72729
3 изменённых файлов: 31 добавлений и 18 удалений

Просмотреть файл

@ -147,7 +147,7 @@ type lowerInterfacesPass struct {
// emitted by the compiler as higher-level intrinsics. They need some lowering // 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 // before LLVM can work on them. This is done so that a few cleanup passes can
// run before assigning the final type codes. // run before assigning the final type codes.
func LowerInterfaces(mod llvm.Module) { func LowerInterfaces(mod llvm.Module) error {
p := &lowerInterfacesPass{ p := &lowerInterfacesPass{
mod: mod, mod: mod,
builder: mod.Context().NewBuilder(), builder: mod.Context().NewBuilder(),
@ -157,11 +157,11 @@ func LowerInterfaces(mod llvm.Module) {
signatures: make(map[string]*signatureInfo), signatures: make(map[string]*signatureInfo),
interfaces: make(map[string]*interfaceInfo), interfaces: make(map[string]*interfaceInfo),
} }
p.run() return p.run()
} }
// run runs the pass itself. // run runs the pass itself.
func (p *lowerInterfacesPass) run() { func (p *lowerInterfacesPass) run() error {
// Collect all type codes. // Collect all type codes.
typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0) typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0)
typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0) typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0)
@ -303,19 +303,22 @@ func (p *lowerInterfacesPass) run() {
} else if len(itf.types) == 1 { } else if len(itf.types) == 1 {
// There is only one implementation of the given type. // There is only one implementation of the given type.
// Call that function directly. // 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 { } else {
// There are multiple types implementing this interface, thus there // There are multiple types implementing this interface, thus there
// are multiple possible functions to call. Delegate calling the // are multiple possible functions to call. Delegate calling the
// right function to a special wrapper function. // right function to a special wrapper function.
inttoptrs := getUses(use) inttoptrs := getUses(use)
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() { 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] inttoptr := inttoptrs[0]
calls := getUses(inttoptr) calls := getUses(inttoptr)
if len(calls) != 1 || calls[0].IsACallInst().IsNil() { 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] call := calls[0]
@ -369,11 +372,6 @@ func (p *lowerInterfacesPass) run() {
} }
sort.Sort(sort.Reverse(typeSlice)) 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. // Assign a type code for each type.
assignTypeCodes(p.mod, typeSlice) assignTypeCodes(p.mod, typeSlice)
@ -433,6 +431,7 @@ func (p *lowerInterfacesPass) run() {
typ.methodSet = llvm.Value{} typ.methodSet = llvm.Value{}
} }
} }
return nil
} }
// addTypeMethods reads the method set of the given type info struct. It // 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 // replaceInvokeWithCall replaces a runtime.interfaceMethod + inttoptr with a
// concrete method. This can be done when only one type implements the // concrete method. This can be done when only one type implements the
// interface. // 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) inttoptrs := getUses(use)
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() { 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] inttoptr := inttoptrs[0]
function := typ.getMethod(signature).function function := typ.getMethod(signature).function
@ -511,7 +510,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
// function. // function.
for _, call := range getUses(inttoptr) { for _, call := range getUses(inttoptr) {
if call.IsACallInst().IsNil() || call.CalledValue() != 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) operands := make([]llvm.Value, call.OperandsCount()-1)
for i := range operands { for i := range operands {
@ -522,7 +521,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
methodParamTypes := paramTypes[len(paramTypes)-(len(operands)-1):] methodParamTypes := paramTypes[len(paramTypes)-(len(operands)-1):]
for i, methodParamType := range methodParamTypes { for i, methodParamType := range methodParamTypes {
if methodParamType != operands[i+1].Type() { 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) p.builder.SetInsertPointBefore(call)
@ -536,6 +535,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
} }
inttoptr.EraseFromParentAsInstruction() inttoptr.EraseFromParentAsInstruction()
use.EraseFromParentAsInstruction() use.EraseFromParentAsInstruction()
return nil
} }
// getInterfaceImplementsFunc returns a function that checks whether a given // getInterfaceImplementsFunc returns a function that checks whether a given

Просмотреть файл

@ -2,9 +2,16 @@ package transform
import ( import (
"testing" "testing"
"tinygo.org/x/go-llvm"
) )
func TestInterfaceLowering(t *testing.T) { func TestInterfaceLowering(t *testing.T) {
t.Parallel() t.Parallel()
testTransform(t, "testdata/interface", LowerInterfaces) testTransform(t, "testdata/interface", func(mod llvm.Module) {
err := LowerInterfaces(mod)
if err != nil {
t.Error(err)
}
})
} }

Просмотреть файл

@ -75,7 +75,10 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
OptimizeMaps(mod) OptimizeMaps(mod)
OptimizeStringToBytes(mod) OptimizeStringToBytes(mod)
OptimizeAllocs(mod) OptimizeAllocs(mod)
LowerInterfaces(mod) err := LowerInterfaces(mod)
if err != nil {
return []error{err}
}
errs := LowerInterrupts(mod) errs := LowerInterrupts(mod)
if len(errs) > 0 { if len(errs) > 0 {
@ -115,7 +118,10 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
} else { } else {
// Must be run at any optimization level. // Must be run at any optimization level.
LowerInterfaces(mod) err := LowerInterfaces(mod)
if err != nil {
return []error{err}
}
if config.FuncImplementation() == compileopts.FuncValueSwitch { if config.FuncImplementation() == compileopts.FuncValueSwitch {
LowerFuncValues(mod) LowerFuncValues(mod)
} }