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 }
97