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.
Этот коммит содержится в:
родитель
25fcf3e18e
коммит
26aba72729
3 изменённых файлов: 31 добавлений и 18 удалений
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче