errors.go raw

   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  	errTimeout                = errors.New("interp: timeout")
  23  )
  24  
  25  // This is one of the errors that can be returned from toLLVMValue when the
  26  // passed type does not fit the data to serialize. It is recoverable by
  27  // serializing without a type (using rawValue.rawLLVMValue).
  28  var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
  29  
  30  func isRecoverableError(err error) bool {
  31  	return err == errIntegerAsPointer || err == errUnsupportedInst ||
  32  		err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
  33  		err == errLoopUnrolled || err == errTimeout
  34  }
  35  
  36  // ErrorLine is one line in a traceback. The position may be missing.
  37  type ErrorLine struct {
  38  	Pos  token.Position
  39  	Inst string
  40  }
  41  
  42  // Error encapsulates compile-time interpretation errors with an associated
  43  // import path. The errors may not have a precise location attached.
  44  type Error struct {
  45  	ImportPath string
  46  	Inst       string
  47  	Pos        token.Position
  48  	Err        error
  49  	Traceback  []ErrorLine
  50  }
  51  
  52  // Error returns the string of the first error in the list of errors.
  53  func (e *Error) Error() string {
  54  	return e.Pos.String() + ": " + e.Err.Error()
  55  }
  56  
  57  // errorAt returns an error value for the currently interpreted package at the
  58  // location of the instruction. The location information may not be complete as
  59  // it depends on debug information in the IR.
  60  func (r *runner) errorAt(inst instruction, err error) *Error {
  61  	pos := getPosition(inst.llvmInst)
  62  	return &Error{
  63  		ImportPath: r.pkgName,
  64  		Inst:       inst.llvmInst.String(),
  65  		Pos:        pos,
  66  		Err:        err,
  67  		Traceback:  []ErrorLine{{pos, inst.llvmInst.String()}},
  68  	}
  69  }
  70  
  71  // errorAt returns an error value at the location of the instruction.
  72  // The location information may not be complete as it depends on debug
  73  // information in the IR.
  74  func errorAt(inst llvm.Value, msg string) scanner.Error {
  75  	return scanner.Error{
  76  		Pos: getPosition(inst),
  77  		Msg: msg,
  78  	}
  79  }
  80  
  81  // getPosition returns the position information for the given instruction, as
  82  // far as it is available.
  83  func getPosition(inst llvm.Value) token.Position {
  84  	if inst.IsAInstruction().IsNil() {
  85  		return token.Position{}
  86  	}
  87  	loc := inst.InstructionDebugLoc()
  88  	if loc.IsNil() {
  89  		return token.Position{}
  90  	}
  91  	file := loc.LocationScope().ScopeFile()
  92  	return token.Position{
  93  		Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
  94  		Line:     int(loc.LocationLine()),
  95  		Column:   int(loc.LocationColumn()),
  96  	}
  97  }
  98