func buildCast(b *builder, expr *ast.CallExpr, t types.T) tast.Expr { pos := expr.Lparen.Pos args := buildExprList(b, expr.Args) if args == nil { return nil } ref := args.R() if !ref.IsSingle() { b.Errorf(pos, "cannot convert %s to %s", ref, t) return nil } srcType := ref.T if c, ok := srcType.(*types.Const); ok { if v, ok := types.NumConst(srcType); ok && types.IsInteger(t) { return constCast(b, pos, v, args, t) } srcType = c.Type // using the underlying type } if types.IsInteger(t) && types.IsInteger(srcType) { return tast.NewCast(args, t) } if regSizeCastable(t, srcType) { return tast.NewCast(args, t) } b.Errorf(pos, "cannot convert from %s to %s", srcType, t) return nil }
func canShift(b *builder, atyp, btyp types.T, pos *lex8.Pos, op string) bool { if !types.IsInteger(atyp) { b.Errorf(pos, "%q on %s", op, atyp) return false } else if !types.IsInteger(btyp) { b.Errorf(pos, "%q with %s", op, btyp) return false } else if !types.IsUnsigned(btyp) { b.Errorf(pos, "%q with %s; must be unsigned", op, btyp) return false } return true }
func buildUnaryOpExpr(b *builder, expr *ast.OpExpr) tast.Expr { opTok := expr.Op op := opTok.Lit opPos := opTok.Pos B := b.buildExpr(expr.B) if B == nil { return nil } bref := B.R() if !bref.IsSingle() { b.Errorf(opPos, "%q on %s", op, bref) return nil } btyp := bref.T if op == "&" { return refAddress(b, opTok, B) } else if types.IsConst(btyp) { return unaryOpConst(b, opTok, B) } else if types.IsInteger(btyp) { return unaryOpInt(b, opTok, B) } else if types.IsBasic(btyp, types.Bool) { return unaryOpBool(b, opTok, B) } b.Errorf(opPos, "invalid unary operator %q", op) return nil }
func buildIncStmt(b *builder, stmt *ast.IncStmt) tast.Stmt { op := stmt.Op.Lit expr := b.buildExpr(stmt.Expr) if expr == nil { return nil } ref := expr.R() if !ref.IsSingle() { b.Errorf(stmt.Op.Pos, "%s on expression list", op) return nil } t := ref.Type() if !types.IsInteger(t) { b.Errorf(stmt.Op.Pos, "%s on %s", op, t) return nil } if !ref.Addressable { b.Errorf(stmt.Op.Pos, "%s on non-addressable", op) return nil } switch stmt.Op.Lit { case "++", "--": return &tast.IncStmt{expr, stmt.Op} default: b.Errorf(stmt.Op.Pos, "invalid inc op %s", op) return nil } }
func constCast( b *builder, pos *lex8.Pos, v int64, from tast.Expr, to types.T, ) tast.Expr { if types.IsInteger(to) && types.InRange(v, to) { return tast.NewCast(from, to) } b.Errorf(pos, "cannot cast %d to %s", v, to) return nil }
func checkArrayIndex(b *builder, index tast.Expr, pos *lex8.Pos) tast.Expr { t := index.R().T if v, ok := types.NumConst(t); ok { if v < 0 { b.Errorf(pos, "array index is negative: %d", v) return nil } return constCastInt(b, pos, v, index) } if !types.IsInteger(t) { b.Errorf(pos, "index must be an integer") return nil } return index }
func buildCast(b *builder, from *ref, t types.T) *ref { srcType := from.Type() ret := b.newTemp(t) if types.IsNil(srcType) { size := t.Size() if size == arch8.RegSize { return newRef(t, ir.Num(0)) } if _, ok := t.(*types.Slice); !ok { panic("bug") } ret := b.newTemp(t) b.b.Zero(ret.IR()) return ret } if c, ok := srcType.(*types.Const); ok { if v, ok := types.NumConst(srcType); ok && types.IsInteger(t) { return newRef(t, constNumIr(v, t)) } // TODO: we do not support typed const right? // so why need this line? srcType = c.Type // using the underlying type } if types.IsInteger(t) && types.IsInteger(srcType) { b.b.Arith(ret.IR(), nil, "cast", from.IR()) return ret } if regSizeCastable(t, srcType) { b.b.Arith(ret.IR(), nil, "", from.IR()) return ret } panic("bug") }
func buildUnaryOpExpr(b *builder, expr *tast.OpExpr) *ref { op := expr.Op.Lit B := b.buildExpr(expr.B) btyp := B.Type() if op == "&" { ret := b.newTemp(&types.Pointer{btyp}) b.b.Arith(ret.IR(), nil, op, B.IR()) return ret } else if types.IsConst(btyp) { panic("bug") } else if types.IsInteger(btyp) { return unaryOpInt(b, op, B) } else if types.IsBasic(btyp, types.Bool) { return unaryOpBool(b, op, B) } panic("bug") }
func buildBinaryOpExpr(b *builder, expr *ast.OpExpr) tast.Expr { opTok := expr.Op op := opTok.Lit opPos := opTok.Pos A := b.buildExpr(expr.A) if A == nil { return nil } aref := A.R() if !aref.IsSingle() { b.Errorf(opPos, "%q on %s", op, aref) return nil } atyp := aref.T B := b.buildExpr(expr.B) if B == nil { return nil } bref := B.R() if !bref.IsSingle() { b.Errorf(opPos, "%q on %s", op, bref) return nil } btyp := bref.T if types.IsConst(atyp) && types.IsConst(btyp) { return binaryOpConst(b, opTok, A, B) } if op == ">>" || op == "<<" { if v, ok := types.NumConst(btyp); ok { B = constCast(b, opPos, v, B, types.Uint) if B == nil { return nil } btyp = types.Uint } if v, ok := types.NumConst(atyp); ok { A = constCast(b, opPos, v, A, types.Int) if A == nil { return nil } atyp = types.Int } if !canShift(b, atyp, btyp, opPos, op) { return nil } r := tast.NewRef(atyp) return &tast.OpExpr{A, opTok, B, r} } if v, ok := types.NumConst(atyp); ok { A = constCast(b, opPos, v, A, btyp) if A == nil { return nil } atyp = btyp } else if c, ok := atyp.(*types.Const); ok { atyp = c.Type } if v, ok := types.NumConst(btyp); ok { B = constCast(b, opPos, v, B, atyp) if B == nil { return nil } btyp = atyp } else if c, ok := btyp.(*types.Const); ok { btyp = c.Type } if ok, t := types.SameBasic(atyp, btyp); ok { switch t { case types.Int, types.Int8, types.Uint, types.Uint8: return binaryOpInt(b, opTok, A, B, t) case types.Bool: return binaryOpBool(b, opTok, A, B) case types.Float32: b.Errorf(opPos, "floating point operations not implemented") return nil } } if types.IsNil(atyp) && types.IsNil(btyp) { return binaryOpNil(b, opTok, A, B) } else if types.BothPointer(atyp, btyp) { return binaryOpPtr(b, opTok, A, B) } else if types.BothFuncPointer(atyp, btyp) { return binaryOpPtr(b, opTok, A, B) } else if types.BothSlice(atyp, btyp) { return binaryOpSlice(b, opTok, A, B) } b.Errorf(opPos, "invalid operation of %s %s %s", atyp, op, btyp) if types.IsInteger(atyp) && types.IsInteger(btyp) { switch op { case "+", "-", "*", "&", "|", "^", "%", "/", "==", "!=", ">", "<", ">=", "<=": b.Errorf( opPos, "operation %s needs the same type on both sides", op, ) } } return nil }
func buildArrayLit(b *builder, lit *ast.ArrayLiteral) tast.Expr { hold := b.lhsSwap(false) defer b.lhsRestore(hold) if lit.Type.Len != nil { b.Errorf( ast.ExprPos(lit), "array literal with length not supported yet", ) return nil } buf := new(bytes.Buffer) t := buildType(b, lit.Type.Type) if t == nil { return nil } if !types.IsInteger(t) { pos := ast.ExprPos(lit.Type.Type) b.Errorf(pos, "array literal must be integer type") return nil } bt := t.(types.Basic) if lit.Exprs != nil { for _, expr := range lit.Exprs.Exprs { n := b.buildConstExpr(expr) if n == nil { return nil } ntype := n.R().T if _, ok := ntype.(*types.Const); !ok { b.Errorf(ast.ExprPos(expr), "array literal not a constant") return nil } if v, ok := types.NumConst(ntype); ok { if !types.InRange(v, t) { b.Errorf( ast.ExprPos(expr), "constant out of range of %s", t, ) return nil } switch bt { case types.Int, types.Uint: var bs [4]byte binary.LittleEndian.PutUint32(bs[:], uint32(v)) buf.Write(bs[:]) case types.Int8, types.Uint8: buf.Write([]byte{byte(v)}) default: panic("not integer") } } } } ref := tast.NewConstRef(&types.Slice{bt}, buf.Bytes()) return &tast.Const{ref} }