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  }