github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/interp/errors.go (about) 1 package interp 2 3 // This file provides useful types for errors encountered during IR evaluation. 4 5 import ( 6 "errors" 7 "go/scanner" 8 "go/token" 9 "path/filepath" 10 11 "tinygo.org/x/go-llvm" 12 ) 13 14 // These errors are expected during normal execution and can be recovered from 15 // by running the affected function at runtime instead of compile time. 16 var ( 17 errIntegerAsPointer = errors.New("interp: trying to use an integer as a pointer (memory-mapped I/O?)") 18 errUnsupportedInst = errors.New("interp: unsupported instruction") 19 errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)") 20 errMapAlreadyCreated = errors.New("interp: map already created") 21 errLoopUnrolled = errors.New("interp: loop unrolled") 22 ) 23 24 // This is one of the errors that can be returned from toLLVMValue when the 25 // passed type does not fit the data to serialize. It is recoverable by 26 // serializing without a type (using rawValue.rawLLVMValue). 27 var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size") 28 29 func isRecoverableError(err error) bool { 30 return err == errIntegerAsPointer || err == errUnsupportedInst || 31 err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated || 32 err == errLoopUnrolled 33 } 34 35 // ErrorLine is one line in a traceback. The position may be missing. 36 type ErrorLine struct { 37 Pos token.Position 38 Inst string 39 } 40 41 // Error encapsulates compile-time interpretation errors with an associated 42 // import path. The errors may not have a precise location attached. 43 type Error struct { 44 ImportPath string 45 Inst string 46 Pos token.Position 47 Err error 48 Traceback []ErrorLine 49 } 50 51 // Error returns the string of the first error in the list of errors. 52 func (e *Error) Error() string { 53 return e.Pos.String() + ": " + e.Err.Error() 54 } 55 56 // errorAt returns an error value for the currently interpreted package at the 57 // location of the instruction. The location information may not be complete as 58 // it depends on debug information in the IR. 59 func (r *runner) errorAt(inst instruction, err error) *Error { 60 pos := getPosition(inst.llvmInst) 61 return &Error{ 62 ImportPath: r.pkgName, 63 Inst: inst.llvmInst.String(), 64 Pos: pos, 65 Err: err, 66 Traceback: []ErrorLine{{pos, inst.llvmInst.String()}}, 67 } 68 } 69 70 // errorAt returns an error value at the location of the instruction. 71 // The location information may not be complete as it depends on debug 72 // information in the IR. 73 func errorAt(inst llvm.Value, msg string) scanner.Error { 74 return scanner.Error{ 75 Pos: getPosition(inst), 76 Msg: msg, 77 } 78 } 79 80 // getPosition returns the position information for the given instruction, as 81 // far as it is available. 82 func getPosition(inst llvm.Value) token.Position { 83 if inst.IsAInstruction().IsNil() { 84 return token.Position{} 85 } 86 loc := inst.InstructionDebugLoc() 87 if loc.IsNil() { 88 return token.Position{} 89 } 90 file := loc.LocationScope().ScopeFile() 91 return token.Position{ 92 Filename: filepath.Join(file.FileDirectory(), file.FileFilename()), 93 Line: int(loc.LocationLine()), 94 Column: int(loc.LocationColumn()), 95 } 96 }