From da0a02d128f70f8fa811ff496f0aa02fe4241200 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 1 Dec 2018 17:19:40 +0100 Subject: [PATCH] compiler: return error messages with source location Replace most errors returned by the compiler (using errors.New) with an error type that includes the source location. --- compiler/compiler.go | 118 +++++++++++++++++++++--------------------- compiler/errors.go | 14 +++++ compiler/interface.go | 6 +-- compiler/map.go | 14 ++--- main.go | 3 ++ 5 files changed, 86 insertions(+), 69 deletions(-) create mode 100644 compiler/errors.go diff --git a/compiler/compiler.go b/compiler/compiler.go index 1dfcde07..e2e6f3b1 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -817,7 +817,7 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) { var retType llvm.Type if frame.blocking { if f.Signature.Results() != nil { - return nil, errors.New("todo: return values in blocking function") + return nil, c.makeError(f.Function.Pos(), "todo: return values in blocking function") } retType = c.i8ptrType } else if f.Signature.Results() == nil { @@ -1518,7 +1518,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { c.ctxDeferFuncs = append(c.ctxDeferFuncs, thunk) } else { - return errors.New("todo: defer on uncommon function call type") + return c.makeError(instr.Pos(), "todo: defer on uncommon function call type") } // Make a struct out of the collected values to put in the defer frame. @@ -1542,7 +1542,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { case *ssa.Go: if instr.Common().Method != nil { - return errors.New("todo: go on method receiver") + return c.makeError(instr.Pos(), "todo: go on method receiver") } // Execute non-blocking calls (including builtins) directly. @@ -1589,7 +1589,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { return err } mapType := instr.Map.Type().Underlying().(*types.Map) - return c.emitMapUpdate(mapType.Key(), m, key, value) + return c.emitMapUpdate(mapType.Key(), m, key, value, instr.Pos()) case *ssa.Panic: value, err := c.parseExpr(frame, instr.X) if err != nil { @@ -1601,7 +1601,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { case *ssa.Return: if frame.blocking { if len(instr.Results) != 0 { - return errors.New("todo: return values from blocking function") + return c.makeError(instr.Pos(), "todo: return values from blocking function") } // Final suspend. continuePoint := c.builder.CreateCall(c.coroSuspendFunc, []llvm.Value{ @@ -1668,11 +1668,11 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { } return nil default: - return errors.New("unknown instruction: " + instr.String()) + return c.makeError(instr.Pos(), "unknown instruction: "+instr.String()) } } -func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) (llvm.Value, error) { +func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string, pos token.Pos) (llvm.Value, error) { switch callName { case "append": src, err := c.parseExpr(frame, args[0]) @@ -1711,7 +1711,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) case *types.Slice: return c.builder.CreateExtractValue(value, 2, "cap"), nil default: - return llvm.Value{}, errors.New("todo: cap: unknown type") + return llvm.Value{}, c.makeError(pos, "todo: cap: unknown type") } case "complex": r, err := c.parseExpr(frame, args[0]) @@ -1730,7 +1730,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) case types.Float64: cplx = llvm.Undef(llvm.VectorType(c.ctx.DoubleType(), 2)) default: - return llvm.Value{}, errors.New("unsupported type in complex builtin: " + t.String()) + return llvm.Value{}, c.makeError(pos, "unsupported type in complex builtin: "+t.String()) } cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "") cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "") @@ -1762,7 +1762,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) if err != nil { return llvm.Value{}, err } - return llvm.Value{}, c.emitMapDelete(args[1].Type(), m, key) + return llvm.Value{}, c.emitMapDelete(args[1].Type(), m, key, pos) case "imag": cplx, err := c.parseExpr(frame, args[0]) if err != nil { @@ -1783,7 +1783,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) case *types.Map: llvmLen = c.createRuntimeCall("hashmapLen", []llvm.Value{value}, "len") default: - return llvm.Value{}, errors.New("todo: len: unknown type") + return llvm.Value{}, c.makeError(pos, "todo: len: unknown type") } if c.targetData.TypeAllocSize(llvmLen.Type()) < c.targetData.TypeAllocSize(c.intType) { llvmLen = c.builder.CreateZExt(llvmLen, c.intType, "len.int") @@ -1831,7 +1831,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) } else if typ.Kind() == types.Complex128 { c.createRuntimeCall("printcomplex128", []llvm.Value{value}, "") } else { - return llvm.Value{}, errors.New("unknown basic arg type: " + typ.String()) + return llvm.Value{}, c.makeError(pos, "unknown basic arg type: "+typ.String()) } } case *types.Interface: @@ -1842,7 +1842,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) ptrValue := c.builder.CreatePtrToInt(value, c.uintptrType, "") c.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "") default: - return llvm.Value{}, errors.New("unknown arg type: " + typ.String()) + return llvm.Value{}, c.makeError(pos, "unknown arg type: "+typ.String()) } } if callName == "println" { @@ -1862,7 +1862,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) // TODO: do an actual nil check? return c.parseExpr(frame, args[0]) default: - return llvm.Value{}, errors.New("todo: builtin: " + callName) + return llvm.Value{}, c.makeError(pos, "todo: builtin: "+callName) } } @@ -2004,7 +2004,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l // ignore case *ssa.MapUpdate: if r.Block() != registerMap.Block() { - return llvm.Value{}, errors.New("register value map must be created in the same basic block") + return llvm.Value{}, c.makeError(instr.Pos(), "register value map must be created in the same basic block") } key := constant.StringVal(r.Key.(*ssa.Const).Value) //println("value:", r.Value.(*ssa.MakeInterface).X.String()) @@ -2018,7 +2018,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l break } default: - return llvm.Value{}, errors.New("don't know how to handle argument to inline assembly: " + r.String()) + return llvm.Value{}, c.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String()) } } // TODO: handle dollar signs in asm string @@ -2033,7 +2033,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l name := s[1 : len(s)-1] if _, ok := registers[name]; !ok { if err == nil { - err = errors.New("unknown register name: " + name) + err = c.makeError(instr.Pos(), "unknown register name: "+name) } return s } @@ -2047,7 +2047,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l case llvm.PointerTypeKind: constraints = append(constraints, "*m") default: - err = errors.New("unknown type in inline assembly for value: " + name) + err = c.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name) return s } } @@ -2063,7 +2063,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l targetFunc := c.ir.GetFunction(fn) if targetFunc.LLVMFn.IsNil() { - return llvm.Value{}, errors.New("undefined function: " + targetFunc.LinkName()) + return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName()) } var context llvm.Value if c.ir.FunctionNeedsContext(targetFunc) { @@ -2091,7 +2091,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l // Builtin or function pointer. switch call := instr.Value.(type) { case *ssa.Builtin: - return c.parseBuiltin(frame, instr.Args, call.Name()) + return c.parseBuiltin(frame, instr.Args, call.Name(), instr.Pos()) default: // function pointer value, err := c.parseExpr(frame, instr.Value) if err != nil { @@ -2180,7 +2180,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if value, ok := frame.locals[expr]; ok { // Value is a local variable that has already been computed. if value.IsNil() { - return llvm.Value{}, errors.New("undefined local var (from cgo?)") + return llvm.Value{}, c.makeError(expr.Pos(), "undefined local var (from cgo?)") } return value, nil } @@ -2217,7 +2217,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if err != nil { return llvm.Value{}, err } - return c.parseBinOp(expr.Op, expr.X.Type(), x, y) + return c.parseBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos()) case *ssa.Call: // Passing the current task here to the subroutine. It is only used when // the subroutine is blocking. @@ -2253,7 +2253,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if err != nil { return llvm.Value{}, err } - return c.parseConvert(expr.X.Type(), expr.Type(), x) + return c.parseConvert(expr.X.Type(), expr.Type(), x, expr.Pos()) case *ssa.Extract: value, err := c.parseExpr(frame, expr.Tuple) if err != nil { @@ -2297,7 +2297,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { } value := c.ir.GetGlobal(expr).LLVMGlobal if value.IsNil() { - return llvm.Value{}, errors.New("global not found: " + c.ir.GetGlobal(expr).LinkName()) + return llvm.Value{}, c.makeError(expr.Pos(), "global not found: "+c.ir.GetGlobal(expr).LinkName()) } return value, nil case *ssa.Index: @@ -2342,13 +2342,13 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { bufptr = val buflen = llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false) default: - return llvm.Value{}, errors.New("todo: indexaddr: " + typ.String()) + return llvm.Value{}, c.makeError(expr.Pos(), "todo: indexaddr: "+typ.String()) } case *types.Slice: bufptr = c.builder.CreateExtractValue(val, 0, "indexaddr.ptr") buflen = c.builder.CreateExtractValue(val, 1, "indexaddr.len") default: - return llvm.Value{}, errors.New("todo: indexaddr: " + ptrTyp.String()) + return llvm.Value{}, c.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String()) } // Bounds check. @@ -2385,7 +2385,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // Bounds check. // LLVM optimizes this away in most cases. - length, err := c.parseBuiltin(frame, []ssa.Value{expr.X}, "len") + length, err := c.parseBuiltin(frame, []ssa.Value{expr.X}, "len", expr.Pos()) if err != nil { return llvm.Value{}, err // shouldn't happen } @@ -2400,7 +2400,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if expr.CommaOk { valueType = valueType.(*types.Tuple).At(0).Type() } - return c.emitMapLookup(xType.Key(), valueType, value, index, expr.CommaOk) + return c.emitMapLookup(xType.Key(), valueType, value, index, expr.CommaOk, expr.Pos()) default: panic("unknown lookup type: " + expr.String()) } @@ -2415,7 +2415,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if err != nil { return llvm.Value{}, err } - return c.parseMakeInterface(val, expr.X.Type(), "") + return c.parseMakeInterface(val, expr.X.Type(), "", expr.Pos()) case *ssa.MakeMap: mapType := expr.Type().Underlying().(*types.Map) llvmKeyType, err := c.getLLVMType(mapType.Key().Underlying()) @@ -2470,7 +2470,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // Allocate the backing array. // TODO: escape analysis elemSizeValue := llvm.ConstInt(c.uintptrType, elemSize, false) - sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap) + sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos()) if err != nil { return llvm.Value{}, err } @@ -2554,7 +2554,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { return it, nil case *ssa.Slice: if expr.Max != nil { - return llvm.Value{}, errors.New("todo: full slice expressions (with max): " + expr.Type().String()) + return llvm.Value{}, c.makeError(expr.Pos(), "todo: full slice expressions (with max): "+expr.Type().String()) } value, err := c.parseExpr(frame, expr.X) if err != nil { @@ -2669,7 +2669,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { case *types.Basic: if typ.Info()&types.IsString == 0 { - return llvm.Value{}, errors.New("unknown slice type: " + typ.String()) + return llvm.Value{}, c.makeError(expr.Pos(), "unknown slice type: "+typ.String()) } // slice a string oldPtr := c.builder.CreateExtractValue(value, 0, "") @@ -2691,18 +2691,18 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { return str, nil default: - return llvm.Value{}, errors.New("unknown slice type: " + typ.String()) + return llvm.Value{}, c.makeError(expr.Pos(), "unknown slice type: "+typ.String()) } case *ssa.TypeAssert: return c.parseTypeAssert(frame, expr) case *ssa.UnOp: return c.parseUnOp(frame, expr) default: - return llvm.Value{}, errors.New("todo: unknown expression: " + expr.String()) + return llvm.Value{}, c.makeError(expr.Pos(), "todo: unknown expression: "+expr.String()) } } -func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) (llvm.Value, error) { +func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, pos token.Pos) (llvm.Value, error) { switch typ := typ.Underlying().(type) { case *types.Basic: if typ.Info()&types.IsInteger != 0 { @@ -2793,7 +2793,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( return c.builder.CreateICmp(llvm.IntUGE, x, y, ""), nil } default: - return llvm.Value{}, errors.New("todo: binop on integer: " + op.String()) + return llvm.Value{}, c.makeError(pos, "todo: binop on integer: "+op.String()) } } else if typ.Info()&types.IsFloat != 0 { // Operations on floats @@ -2821,7 +2821,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.GEQ: // >= return c.builder.CreateFCmp(llvm.FloatOGE, x, y, ""), nil default: - return llvm.Value{}, errors.New("todo: binop on float: " + op.String()) + return llvm.Value{}, c.makeError(pos, "todo: binop on float: "+op.String()) } } else if typ.Info()&types.IsBoolean != 0 { // Operations on booleans @@ -2831,7 +2831,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.NEQ: // != return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil default: - return llvm.Value{}, errors.New("todo: binop on boolean: " + op.String()) + return llvm.Value{}, c.makeError(pos, "todo: binop on boolean: "+op.String()) } } else if typ.Kind() == types.UnsafePointer { // Operations on pointers @@ -2841,7 +2841,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.NEQ: // != return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil default: - return llvm.Value{}, errors.New("todo: binop on pointer: " + op.String()) + return llvm.Value{}, c.makeError(pos, "todo: binop on pointer: "+op.String()) } } else if typ.Info()&types.IsString != 0 { // Operations on strings @@ -2864,10 +2864,10 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.GEQ: // >= return c.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil default: - return llvm.Value{}, errors.New("todo: binop on string: " + op.String()) + return llvm.Value{}, c.makeError(pos, "todo: binop on string: "+op.String()) } } else { - return llvm.Value{}, errors.New("todo: unknown basic type in binop: " + typ.String()) + return llvm.Value{}, c.makeError(pos, "todo: unknown basic type in binop: "+typ.String()) } case *types.Signature: if c.ir.SignatureNeedsContext(typ) { @@ -2885,7 +2885,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.NEQ: // != return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil default: - return llvm.Value{}, errors.New("binop on signature: " + op.String()) + return llvm.Value{}, c.makeError(pos, "binop on signature: "+op.String()) } case *types.Interface: switch op { @@ -2896,7 +2896,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( } return result, nil default: - return llvm.Value{}, errors.New("binop on interface: " + op.String()) + return llvm.Value{}, c.makeError(pos, "binop on interface: "+op.String()) } case *types.Map, *types.Pointer: // Maps are in general not comparable, but can be compared against nil @@ -2908,7 +2908,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.NEQ: // != return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil default: - return llvm.Value{}, errors.New("todo: binop on pointer: " + op.String()) + return llvm.Value{}, c.makeError(pos, "todo: binop on pointer: "+op.String()) } case *types.Slice: // Slices are in general not comparable, but can be compared against @@ -2921,7 +2921,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.NEQ: // != return c.builder.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil default: - return llvm.Value{}, errors.New("todo: binop on slice: " + op.String()) + return llvm.Value{}, c.makeError(pos, "todo: binop on slice: "+op.String()) } case *types.Array: // Compare each array element and combine the result. From the spec: @@ -2932,7 +2932,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( for i := 0; i < int(typ.Len()); i++ { xField := c.builder.CreateExtractValue(x, i, "") yField := c.builder.CreateExtractValue(y, i, "") - fieldEqual, err := c.parseBinOp(token.EQL, typ.Elem(), xField, yField) + fieldEqual, err := c.parseBinOp(token.EQL, typ.Elem(), xField, yField, pos) if err != nil { return llvm.Value{}, err } @@ -2944,7 +2944,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.NEQ: // != return c.builder.CreateNot(result, ""), nil default: - return llvm.Value{}, errors.New("unknown: binop on struct: " + op.String()) + return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String()) } return result, nil case *types.Struct: @@ -2961,7 +2961,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( fieldType := typ.Field(i).Type() xField := c.builder.CreateExtractValue(x, i, "") yField := c.builder.CreateExtractValue(y, i, "") - fieldEqual, err := c.parseBinOp(token.EQL, fieldType, xField, yField) + fieldEqual, err := c.parseBinOp(token.EQL, fieldType, xField, yField, pos) if err != nil { return llvm.Value{}, err } @@ -2973,11 +2973,11 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) ( case token.NEQ: // != return c.builder.CreateNot(result, ""), nil default: - return llvm.Value{}, errors.New("unknown: binop on struct: " + op.String()) + return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String()) } return result, nil default: - return llvm.Value{}, errors.New("todo: binop type: " + typ.String()) + return llvm.Value{}, c.makeError(pos, "todo: binop type: "+typ.String()) } } @@ -3099,7 +3099,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error } } -func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) (llvm.Value, error) { +func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, pos token.Pos) (llvm.Value, error) { llvmTypeFrom := value.Type() llvmTypeTo, err := c.getLLVMType(typeTo) if err != nil { @@ -3144,10 +3144,10 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) ( case types.Byte: return c.createRuntimeCall("stringFromBytes", []llvm.Value{value}, ""), nil default: - return llvm.Value{}, errors.New("todo: convert to string: " + typeFrom.String()) + return llvm.Value{}, c.makeError(pos, "todo: convert to string: "+typeFrom.String()) } default: - return llvm.Value{}, errors.New("todo: convert to string: " + typeFrom.String()) + return llvm.Value{}, c.makeError(pos, "todo: convert to string: "+typeFrom.String()) } } @@ -3218,7 +3218,7 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) ( return cplx, nil } - return llvm.Value{}, errors.New("todo: convert: basic non-integer type: " + typeFrom.String() + " -> " + typeTo.String()) + return llvm.Value{}, c.makeError(pos, "todo: convert: basic non-integer type: "+typeFrom.String()+" -> "+typeTo.String()) case *types.Slice: if basic, ok := typeFrom.(*types.Basic); !ok || basic.Info()&types.IsString == 0 { @@ -3230,11 +3230,11 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) ( case types.Byte: return c.createRuntimeCall("stringToBytes", []llvm.Value{value}, ""), nil default: - return llvm.Value{}, errors.New("todo: convert from string: " + elemType.String()) + return llvm.Value{}, c.makeError(pos, "todo: convert from string: "+elemType.String()) } default: - return llvm.Value{}, errors.New("todo: convert " + typeTo.String() + " <- " + typeFrom.String()) + return llvm.Value{}, c.makeError(pos, "todo: convert "+typeTo.String()+" <- "+typeFrom.String()) } } @@ -3332,10 +3332,10 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { } else if typ.Info()&types.IsFloat != 0 { return c.builder.CreateFSub(llvm.ConstFloat(x.Type(), 0.0), x, ""), nil } else { - return llvm.Value{}, errors.New("todo: unknown basic type for negate: " + typ.String()) + return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown basic type for negate: "+typ.String()) } } else { - return llvm.Value{}, errors.New("todo: unknown type for negate: " + unop.X.Type().Underlying().String()) + return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String()) } case token.MUL: // *x, dereference pointer valType := unop.X.Type().(*types.Pointer).Elem() @@ -3353,7 +3353,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { case token.XOR: // ^x, toggle all bits in integer return c.builder.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil default: - return llvm.Value{}, errors.New("todo: unknown unop") + return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown unop") } } diff --git a/compiler/errors.go b/compiler/errors.go new file mode 100644 index 00000000..1d76b423 --- /dev/null +++ b/compiler/errors.go @@ -0,0 +1,14 @@ +package compiler + +import ( + "go/token" + "go/types" +) + +func (c *Compiler) makeError(pos token.Pos, msg string) types.Error { + return types.Error{ + Fset: c.ir.Program.Fset, + Pos: pos, + Msg: msg, + } +} diff --git a/compiler/interface.go b/compiler/interface.go index 6517f2ed..f6c0374d 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -6,7 +6,7 @@ package compiler // interface-lowering.go for more details. import ( - "errors" + "go/token" "go/types" "github.com/aykevl/go-llvm" @@ -20,7 +20,7 @@ import ( // value field. // // An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact. -func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global string) (llvm.Value, error) { +func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global string, pos token.Pos) (llvm.Value, error) { var itfValue llvm.Value size := c.targetData.TypeAllocSize(val.Type()) if size > c.targetData.TypeAllocSize(c.i8ptrType) { @@ -60,7 +60,7 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global str c.builder.CreateStore(val, memStructPtr) itfValue = c.builder.CreateLoad(mem, "makeinterface.cast.load") default: - return llvm.Value{}, errors.New("todo: makeinterface: cast small type to i8*") + return llvm.Value{}, c.makeError(pos, "todo: makeinterface: cast small type to i8*") } } itfTypeCodeGlobal := c.getTypeCode(typ) diff --git a/compiler/map.go b/compiler/map.go index d4d318bc..ceaa1588 100644 --- a/compiler/map.go +++ b/compiler/map.go @@ -3,13 +3,13 @@ package compiler // This file emits the correct map intrinsics for map operations. import ( - "errors" + "go/token" "go/types" "github.com/aykevl/go-llvm" ) -func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool) (llvm.Value, error) { +func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) { llvmValueType, err := c.getLLVMType(valueType) if err != nil { return llvm.Value{}, err @@ -29,7 +29,7 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu params := []llvm.Value{m, keyPtr, mapValuePtr} commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "") } else { - return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String()) + return llvm.Value{}, c.makeError(pos, "todo: map lookup key type: "+keyType.String()) } mapValue := c.builder.CreateLoad(mapValueAlloca, "") if commaOk { @@ -42,7 +42,7 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu } } -func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value) error { +func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) error { valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value") c.builder.CreateStore(value, valueAlloca) valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr") @@ -61,11 +61,11 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value) e c.createRuntimeCall("hashmapBinarySet", params, "") return nil } else { - return errors.New("todo: map update key type: " + keyType.String()) + return c.makeError(pos, "todo: map update key type: "+keyType.String()) } } -func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value) error { +func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error { keyType = keyType.Underlying() if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { // key is a string @@ -80,7 +80,7 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value) error { c.createRuntimeCall("hashmapBinaryDelete", params, "") return nil } else { - return errors.New("todo: map delete key type: " + keyType.String()) + return c.makeError(pos, "todo: map delete key type: "+keyType.String()) } } diff --git a/main.go b/main.go index d85554ac..8e8fc43e 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "errors" "flag" "fmt" + "go/types" "io" "io/ioutil" "os" @@ -451,6 +452,8 @@ func handleCompilerError(err error) { fmt.Fprintln(os.Stderr, "unsupported instruction during init evaluation:") errUnsupported.Inst.Dump() fmt.Fprintln(os.Stderr) + } else if errCompiler, ok := err.(types.Error); ok { + fmt.Fprintln(os.Stderr, errCompiler) } else { fmt.Fprintln(os.Stderr, "error:", err) }