@@ -4,6 +4,7 @@ package interp
44// functions.
55
66import (
7+ "errors"
78 "strings"
89
910 "tinygo.org/x/go-llvm"
@@ -21,7 +22,7 @@ type frame struct {
2122// Most of it works at compile time. Some calls get translated into calls to be
2223// executed at runtime: calls to functions with side effects, external calls,
2324// and operations on the result of such instructions.
24- func (fr * frame ) evalBasicBlock (bb , incoming llvm.BasicBlock , indent string ) (retval Value , outgoing []llvm.Value , err error ) {
25+ func (fr * frame ) evalBasicBlock (bb , incoming llvm.BasicBlock , indent string ) (retval Value , outgoing []llvm.Value , err * Error ) {
2526 for inst := bb .FirstInstruction (); ! inst .IsNil (); inst = llvm .NextInstruction (inst ) {
2627 if fr .Debug {
2728 print (indent )
@@ -97,7 +98,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
9798 value = operand .Load ()
9899 }
99100 if value .Type () != inst .Type () {
100- return nil , nil , fr .errorAt (inst , "interp: load: type does not match" )
101+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: load: type does not match" ) )
101102 }
102103 fr .locals [inst ] = fr .getValue (value )
103104 case ! inst .IsAStoreInst ().IsNil ():
@@ -121,16 +122,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
121122 // Not a constant operation.
122123 // This should be detected by the scanner, but isn't at the
123124 // moment.
124- return nil , nil , fr .errorAt (inst , "todo: non-const gep" )
125+ return nil , nil , fr .errorAt (inst , errors . New ( "todo: non-const gep" ) )
125126 }
126127 indices [i ] = uint32 (operand .Value ().ZExtValue ())
127128 }
128129 result , err := value .GetElementPtr (indices )
129130 if err != nil {
130- return nil , nil , fr .errorAt (inst , err . Error () )
131+ return nil , nil , fr .errorAt (inst , err )
131132 }
132133 if result .Type () != inst .Type () {
133- return nil , nil , fr .errorAt (inst , "interp: gep: type does not match" )
134+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: gep: type does not match" ) )
134135 }
135136 fr .locals [inst ] = result
136137
@@ -185,7 +186,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
185186 }
186187 }
187188 // It is not possible in Go to bitcast a map value to a pointer.
188- return nil , nil , fr .errorAt (inst , "unimplemented: bitcast of map" )
189+ return nil , nil , fr .errorAt (inst , errors . New ( "unimplemented: bitcast of map" ) )
189190 }
190191 value := fr .getLocal (operand ).(* LocalValue )
191192 fr .locals [inst ] = & LocalValue {fr .Eval , fr .builder .CreateBitCast (value .Value (), inst .Type (), "" )}
@@ -269,7 +270,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
269270 } else {
270271 result , err := result .GetElementPtr ([]uint32 {0 , 0 })
271272 if err != nil {
272- return nil , nil , errorAt (inst , err . Error () )
273+ return nil , nil , fr . errorAt (inst , err )
273274 }
274275 fr .locals [resultInst ] = result
275276 }
@@ -374,30 +375,30 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
374375 // Re-add this GEP, in the hope that it it is then of the correct type...
375376 dstArrayValue , err := dstArray .GetElementPtr ([]uint32 {0 , 0 })
376377 if err != nil {
377- return nil , nil , errorAt (inst , err . Error () )
378+ return nil , nil , fr . errorAt (inst , err )
378379 }
379380 dstArray = dstArrayValue .(* LocalValue )
380381 srcArrayValue , err := srcArray .GetElementPtr ([]uint32 {0 , 0 })
381382 if err != nil {
382- return nil , nil , errorAt (inst , err . Error () )
383+ return nil , nil , fr . errorAt (inst , err )
383384 }
384385 srcArray = srcArrayValue .(* LocalValue )
385386 }
386387 if fr .Eval .TargetData .TypeAllocSize (dstArray .Type ().ElementType ()) != elementSize {
387- return nil , nil , fr .errorAt (inst , "interp: slice dst element size does not match pointer type" )
388+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: slice dst element size does not match pointer type" ) )
388389 }
389390 if fr .Eval .TargetData .TypeAllocSize (srcArray .Type ().ElementType ()) != elementSize {
390- return nil , nil , fr .errorAt (inst , "interp: slice src element size does not match pointer type" )
391+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: slice src element size does not match pointer type" ) )
391392 }
392393 if dstArray .Type () != srcArray .Type () {
393- return nil , nil , fr .errorAt (inst , "interp: slice element types don't match" )
394+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: slice element types don't match" ) )
394395 }
395396 length := dstLen .Value ().SExtValue ()
396397 if srcLength := srcLen .Value ().SExtValue (); srcLength < length {
397398 length = srcLength
398399 }
399400 if length < 0 {
400- return nil , nil , fr .errorAt (inst , "interp: trying to copy a slice with negative length?" )
401+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: trying to copy a slice with negative length?" ) )
401402 }
402403 for i := int64 (0 ); i < length ; i ++ {
403404 var err error
@@ -406,13 +407,13 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
406407 // dst++
407408 dstArrayValue , err := dstArray .GetElementPtr ([]uint32 {1 })
408409 if err != nil {
409- return nil , nil , errorAt (inst , err . Error () )
410+ return nil , nil , fr . errorAt (inst , err )
410411 }
411412 dstArray = dstArrayValue .(* LocalValue )
412413 // src++
413414 srcArrayValue , err := srcArray .GetElementPtr ([]uint32 {1 })
414415 if err != nil {
415- return nil , nil , errorAt (inst , err . Error () )
416+ return nil , nil , fr . errorAt (inst , err )
416417 }
417418 srcArray = srcArrayValue .(* LocalValue )
418419 }
@@ -444,11 +445,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
444445 actualTypeInt := fr .getLocal (inst .Operand (0 )).(* LocalValue ).Underlying
445446 assertedType := fr .getLocal (inst .Operand (1 )).(* LocalValue ).Underlying
446447 if actualTypeInt .IsAConstantExpr ().IsNil () || actualTypeInt .Opcode () != llvm .PtrToInt {
447- return nil , nil , fr .errorAt (inst , "interp: expected typecode in runtime.typeAssert to be a ptrtoint" )
448+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: expected typecode in runtime.typeAssert to be a ptrtoint" ) )
448449 }
449450 actualType := actualTypeInt .Operand (0 )
450451 if actualType .IsAConstant ().IsNil () || assertedType .IsAConstant ().IsNil () {
451- return nil , nil , fr .errorAt (inst , "interp: unimplemented: type assert with non-constant interface value" )
452+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: unimplemented: type assert with non-constant interface value" ) )
452453 }
453454 assertOk := uint64 (0 )
454455 if llvm .ConstExtractValue (actualType .Initializer (), []uint32 {0 }) == assertedType {
@@ -459,16 +460,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
459460 typecode := fr .getLocal (inst .Operand (0 )).(* LocalValue ).Underlying
460461 interfaceMethodSet := fr .getLocal (inst .Operand (1 )).(* LocalValue ).Underlying
461462 if typecode .IsAConstantExpr ().IsNil () || typecode .Opcode () != llvm .PtrToInt {
462- return nil , nil , fr .errorAt (inst , "interp: expected typecode to be a ptrtoint" )
463+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: expected typecode to be a ptrtoint" ) )
463464 }
464465 typecode = typecode .Operand (0 )
465466 if interfaceMethodSet .IsAConstantExpr ().IsNil () || interfaceMethodSet .Opcode () != llvm .GetElementPtr {
466- return nil , nil , fr .errorAt (inst , "interp: expected method set in runtime.interfaceImplements to be a constant gep" )
467+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: expected method set in runtime.interfaceImplements to be a constant gep" ) )
467468 }
468469 interfaceMethodSet = interfaceMethodSet .Operand (0 ).Initializer ()
469470 methodSet := llvm .ConstExtractValue (typecode .Initializer (), []uint32 {1 })
470471 if methodSet .IsAConstantExpr ().IsNil () || methodSet .Opcode () != llvm .GetElementPtr {
471- return nil , nil , fr .errorAt (inst , "interp: expected method set to be a constant gep" )
472+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: expected method set to be a constant gep" ) )
472473 }
473474 methodSet = methodSet .Operand (0 ).Initializer ()
474475
@@ -568,6 +569,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
568569 // interpret anyway and hope for the best.
569570 ret , err = fr .function (callee , params , indent + " " )
570571 if err != nil {
572+ // Record this function call in the backtrace.
573+ err .Traceback = append (err .Traceback , ErrorLine {
574+ Pos : getPosition (inst ),
575+ Inst : inst ,
576+ })
571577 return nil , nil , err
572578 }
573579 }
@@ -586,7 +592,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
586592 fr .locals [inst ] = fr .getValue (newValue )
587593 } else {
588594 if len (indices ) != 1 {
589- return nil , nil , fr .errorAt (inst , "interp: cannot handle extractvalue with not exactly 1 index" )
595+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: cannot handle extractvalue with not exactly 1 index" ) )
590596 }
591597 fr .locals [inst ] = & LocalValue {fr .Eval , fr .builder .CreateExtractValue (agg .Underlying , int (indices [0 ]), inst .Name ())}
592598 }
@@ -599,7 +605,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
599605 fr .locals [inst ] = & LocalValue {fr .Eval , newValue }
600606 } else {
601607 if len (indices ) != 1 {
602- return nil , nil , fr .errorAt (inst , "interp: cannot handle insertvalue with not exactly 1 index" )
608+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: cannot handle insertvalue with not exactly 1 index" ) )
603609 }
604610 fr .locals [inst ] = & LocalValue {fr .Eval , fr .builder .CreateInsertValue (agg .Underlying , val .Value (), int (indices [0 ]), inst .Name ())}
605611 }
@@ -624,25 +630,25 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
624630 // conditional branch (if/then/else)
625631 cond := fr .getLocal (inst .Operand (0 )).Value ()
626632 if cond .Type () != fr .Mod .Context ().Int1Type () {
627- return nil , nil , fr .errorAt (inst , "expected an i1 in a branch instruction" )
633+ return nil , nil , fr .errorAt (inst , errors . New ( "expected an i1 in a branch instruction" ) )
628634 }
629635 thenBB := inst .Operand (1 )
630636 elseBB := inst .Operand (2 )
631637 if ! cond .IsAInstruction ().IsNil () {
632- return nil , nil , fr .errorAt (inst , "interp: branch on a non-constant" )
638+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: branch on a non-constant" ) )
633639 }
634640 if ! cond .IsAConstantExpr ().IsNil () {
635641 // This may happen when the instruction builder could not
636642 // const-fold some instructions.
637- return nil , nil , fr .errorAt (inst , "interp: branch on a non-const-propagated constant expression" )
643+ return nil , nil , fr .errorAt (inst , errors . New ( "interp: branch on a non-const-propagated constant expression" ) )
638644 }
639645 switch cond {
640646 case llvm .ConstInt (fr .Mod .Context ().Int1Type (), 0 , false ): // false
641647 return nil , []llvm.Value {thenBB }, nil // then
642648 case llvm .ConstInt (fr .Mod .Context ().Int1Type (), 1 , false ): // true
643649 return nil , []llvm.Value {elseBB }, nil // else
644650 default :
645- return nil , nil , fr .errorAt (inst , "branch was not true or false" )
651+ return nil , nil , fr .errorAt (inst , errors . New ( "branch was not true or false" ) )
646652 }
647653 case ! inst .IsABranchInst ().IsNil () && inst .OperandsCount () == 1 :
648654 // unconditional branch (goto)
0 commit comments