Example #1
0
func fixGoTypesExpr(fn *cc.Decl, x *cc.Expr, targ *cc.Type) (ret *cc.Type) {
	if x == nil {
		return nil
	}

	defer func() {
		x.XType = ret
	}()

	if x.Op == cc.Paren {
		return fixGoTypesExpr(fn, x.Left, targ)
	}

	// Make explicit C's implicit conversions from boolean to non-boolean and vice versa.
	switch x.Op {
	case cc.AndAnd, cc.OrOr, cc.Not, cc.EqEq, cc.Lt, cc.LtEq, cc.Gt, cc.GtEq, cc.NotEq:
		if targ != nil && targ.Kind != c2go.Bool {
			old := copyExpr(x)
			if targ.Kind == c2go.Int {
				x.Op = cc.Call
				x.Left = &cc.Expr{Op: cc.Name, Text: "bool2int"}
				x.List = []*cc.Expr{old}
				x.Right = nil
			} else {
				x.Op = cc.Cast
				x.Left = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "bool2int"}, List: []*cc.Expr{old}}
				x.Type = targ
			}
			fixGoTypesExpr(fn, old, boolType)
			return targ
		}
	default:
		if targ != nil && targ.Kind == c2go.Bool {
			old := copyExpr(x)
			left := fixGoTypesExpr(fn, old, nil)
			if left != nil && left.Kind == c2go.Bool {
				return targ
			}
			if old.Op == cc.Number {
				switch old.Text {
				case "1":
					x.Op = cc.Name
					x.Text = "true"
					return targ
				case "0":
					x.Op = cc.Name
					x.Text = "false"
					return targ
				}
			}
			x.Op = cc.NotEq
			x.Left = old
			x.Right = zeroFor(left)
			return targ
		}
	}

	switch x.Op {
	default:
		panic(fmt.Sprintf("unexpected construct %v in fixGoTypesExpr - %v - %v", c2go.GoString(x), x.Op, x.Span))

	case c2go.ExprSlice:
		// inserted by rewriteLen
		left := fixGoTypesExpr(fn, x.List[0], targ)
		fixGoTypesExpr(fn, x.List[1], nil)
		fixGoTypesExpr(fn, x.List[2], nil)
		return left

	case cc.Comma:
		for i, y := range x.List {
			t := targ
			if i+1 < len(x.List) {
				t = nil
			}
			fixGoTypesExpr(fn, y, t)
		}
		return nil

	case c2go.ExprBlock:
		for _, stmt := range x.Block {
			fixGoTypesStmt(nil, fn, stmt)
		}
		return nil

	case cc.Add, cc.And, cc.Div, cc.Mod, cc.Mul, cc.Or, cc.Sub, cc.Xor:
		if x.Op == cc.Sub && isPtrSliceOrArray(x.Left.XType) && isPtrSliceOrArray(x.Right.XType) {
			left := fixGoTypesExpr(fn, x.Left, nil)
			right := fixGoTypesExpr(fn, x.Right, nil)
			if left != nil && right != nil && left.Kind != right.Kind {
				if left.Kind == c2go.Slice {
					forceConvert(fn, x.Right, right, left)
				} else {
					forceConvert(fn, x.Left, left, right)
				}
			}
			x.Left = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Left}}}
			x.Right = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Right}}
			x.Op = cc.Add
			return intType
		}

		left := fixGoTypesExpr(fn, x.Left, targ)

		if x.Op == cc.And && x.Right.Op == cc.Twid {
			x.Op = c2go.AndNot
			x.Right = x.Right.Left
		}

		if x.Op == cc.Add && isSliceStringOrArray(left) {
			fixGoTypesExpr(fn, x.Right, nil)
			x.Op = c2go.ExprSlice
			x.List = []*cc.Expr{x.Left, x.Right, nil}
			x.Left = nil
			x.Right = nil
			if left.Kind == cc.Array {
				left = &cc.Type{Kind: c2go.Slice, Base: left.Base}
			}
			return left
		}

		right := fixGoTypesExpr(fn, x.Right, targ)
		return fixBinary(fn, x, left, right, targ)

	case cc.AddEq, cc.AndEq, cc.DivEq, cc.Eq, cc.ModEq, cc.MulEq, cc.OrEq, cc.SubEq, cc.XorEq:
		left := fixGoTypesExpr(fn, x.Left, nil)

		if x.Op == cc.AndEq && x.Right.Op == cc.Twid {
			x.Op = c2go.AndNotEq
			x.Right = x.Right.Left
		}

		if x.Op == cc.AddEq && isSliceOrString(left) {
			fixGoTypesExpr(fn, x.Right, nil)
			old := copyExpr(x.Left)
			x.Op = cc.Eq
			x.Right = &cc.Expr{Op: c2go.ExprSlice, List: []*cc.Expr{old, x.Right, nil}}
			return left
		}

		if x.Op == cc.Eq && x.Left.Op == cc.Index && sameType(x.Left.Left.XType, stringType) && c2go.GoString(x.Left.Right) == "0" && c2go.GoString(x.Right) == "0" {
			x.Left = x.Left.Left
			x.Right = &cc.Expr{Op: cc.Name, Text: `""`}
			return x.Left.XType
		}

		forceGoType(fn, x.Right, left)

		return left

	case c2go.ColonEq:
		left := fixGoTypesExpr(fn, x.Right, nil)
		x.Left.XType = left
		x.Left.XDecl.Type = left
		return left

	case cc.Addr:
		left := fixGoTypesExpr(fn, x.Left, nil)
		if left == nil {
			return nil
		}

		if targ != nil && targ.Kind == c2go.Slice && sameType(targ.Base, left) {
			l := x.Left
			l.Op = c2go.ExprSlice
			l.List = []*cc.Expr{l.Left, l.Right, nil}
			l.Left = nil
			l.Right = nil
			fixMerge(x, l)
			return targ
		}

		return &cc.Type{Kind: cc.Ptr, Base: left}

	case cc.AndAnd, cc.OrOr, cc.Not:
		fixGoTypesExpr(fn, x.Left, boolType)
		if x.Right != nil {
			fixGoTypesExpr(fn, x.Right, boolType)
		}
		return boolType

	case cc.Arrow, cc.Dot:
		left := fixGoTypesExpr(fn, x.Left, nil)

		if x.Op == cc.Arrow && isSliceOrString(left) {
			x.Left = &cc.Expr{Op: cc.Index, Left: x.Left, Right: &cc.Expr{Op: cc.Number, Text: "0"}}
		}

		return x.XDecl.Type

	case cc.Call:
		if fixPrintf(fn, x) {
			return x.XType
		}
		if fixSpecialCall(fn, x, targ) {
			return x.XType
		}
		left := fixGoTypesExpr(fn, x.Left, nil)
		for i, y := range x.List {
			if left != nil && left.Kind == cc.Func && i < len(left.Decls) {
				forceGoType(fn, y, left.Decls[i].Type)
			} else {
				fixGoTypesExpr(fn, y, nil)
			}
		}
		if left != nil && left.Kind == cc.Func {
			return left.Base
		}
		return nil

	case cc.Cast:
		fixGoTypesExpr(fn, x.Left, nil)
		return x.Type

	case cc.CastInit:
		fixGoTypesInit(nil, x.Init)
		return x.Type

	case cc.EqEq, cc.Gt, cc.GtEq, cc.Lt, cc.LtEq, cc.NotEq:
		if fixSpecialCompare(fn, x) {
			return boolType
		}
		left := fixGoTypesExpr(fn, x.Left, nil)
		if x.Right.Op == cc.Number && x.Right.Text == "0" {
			if isSliceOrPtr(left) {
				x.Right.Op = cc.Name
				x.Right.Text = "nil"
				return boolType
			}
			if left != nil && left.Kind == c2go.String {
				x.Right.Op = cc.String
				x.Right.Texts = []string{`""`}
				return boolType
			}
		}
		right := fixGoTypesExpr(fn, x.Right, nil)

		if isSliceOrArray(x.Left.XType) && isSliceOrArray(x.Right.XType) {
			x.Left = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Left}}}
			x.Right = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Right}}}
			return boolType
		}

		fixBinary(fn, x, left, right, nil)
		return boolType

	case cc.Index, cc.Indir:
		left := fixGoTypesExpr(fn, x.Left, nil)
		if x.Right != nil {
			fixGoTypesExpr(fn, x.Right, nil)
		}
		if left == nil {
			return nil
		}

		if isSliceOrString(left) && x.Op == cc.Indir {
			x.Op = cc.Index
			x.Right = &cc.Expr{Op: cc.Number, Text: "0"}
		}

		switch left.Kind {
		case cc.Ptr, c2go.Slice, cc.Array:
			return left.Base

		case c2go.String:
			return byteType
		}
		return nil

	case cc.Lsh, cc.Rsh:
		left := fixGoTypesExpr(fn, x.Left, targ)
		if left != nil && targ != nil && c2go.Int8 <= left.Kind && left.Kind <= c2go.Float64 && targ.Kind > left.Kind {
			forceConvert(fn, x.Left, left, targ)
			left = targ
		}
		fixShiftCount(fn, x.Right)
		return left

	case cc.LshEq, cc.RshEq:
		left := fixGoTypesExpr(fn, x.Left, nil)
		fixShiftCount(fn, x.Right)
		return left

	case cc.Name:
		if x.Text == "nelem" {
		}
		switch x.Text {
		case "T", "S", "N", "L", "P":
			x.Text = "nil"
			x.XDecl = nil
			return nil
		case "nelem":
			x.Text = "len"
			x.XDecl = nil
			fallthrough
		case "len":
			return &cc.Type{Kind: cc.Func, Base: intType}
		}
		if x.XDecl == nil {
			return nil
		}
		return x.XDecl.Type

	case cc.Number:
		return idealType

	case cc.Minus, cc.Plus, cc.Twid:
		return fixGoTypesExpr(fn, x.Left, targ)

	case cc.Offsetof:
		// TODO
		return nil

	case cc.Paren:
		return fixGoTypesExpr(fn, x.Left, targ)

	case cc.PostDec, cc.PostInc:
		left := fixGoTypesExpr(fn, x.Left, nil)

		if x.Op == cc.PostInc && isSliceOrString(left) {
			old := copyExpr(x.Left)
			x.Op = cc.Eq
			x.Right = &cc.Expr{Op: c2go.ExprSlice, List: []*cc.Expr{old, &cc.Expr{Op: cc.Number, Text: "1"}, nil}}
		}

		return nil

	case cc.SizeofExpr:
		left := fixGoTypesExpr(fn, x.Left, nil)
		if left != nil && (left.Kind == cc.Array || left.Kind == c2go.Slice) && left.Base.Def().Is(c2go.Uint8) {
			x.Op = cc.Call
			x.List = []*cc.Expr{x.Left}
			x.Left = &cc.Expr{Op: cc.Name, Text: "len"}
			return intType
		}
		return nil

	case cc.SizeofType:
		return nil

	case cc.String:
		return &cc.Type{Kind: c2go.String}

	case cc.VaArg:
		// TODO
		return nil
	}
}
Example #2
0
func forceConvert(fn *cc.Decl, x *cc.Expr, actual, targ *cc.Type) {
	if isNumericConst(x) && targ != nil {
		switch targ.Kind {
		case cc.Ptr, c2go.Slice:
			if x.Op == cc.Number && x.Text == "0" {
				x.Op = cc.Name
				x.Text = "nil"
				x.XType = targ
			}
		case c2go.String:
			if x.Op == cc.Number && x.Text == "0" {
				x.Op = cc.Name
				x.Text = `""`
				x.XType = targ
			}

		}
		return
	}

	if actual == nil || targ == nil {
		return
	}

	if actual.Kind == c2go.Ideal && c2go.Int8 <= targ.Kind && targ.Kind <= c2go.Float64 {
		return
	}

	if x != nil && x.Op == cc.Name && x.Text == "nil" {
		if targ.Kind == cc.Func || targ.Kind == cc.Ptr || targ.Kind == c2go.Slice {
			return
		}
	}

	// Func conversions are never useful.
	// If the func types are different, the conversion will fail;
	// if not, the conversion is unnecessary.
	// Either way the conversion is an eyesore.
	if targ.Kind == cc.Func || targ.Kind == cc.Ptr && targ.Base.Kind == cc.Func {
		return
	}

	if actual.Kind == c2go.Bool && c2go.Int8 <= targ.Kind && targ.Kind <= c2go.Float64 {
		old := copyExpr(x)
		if targ.Kind == c2go.Int {
			x.Op = cc.Call
			x.Left = &cc.Expr{Op: cc.Name, Text: "bool2int"}
			x.List = []*cc.Expr{old}
			x.Right = nil
		} else {
			x.Op = cc.Cast
			x.Left = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "bool2int"}, List: []*cc.Expr{old}}
			x.Type = targ
		}
		return
	}

	if actual.Kind == cc.Array && targ.Kind == c2go.Slice && sameType(actual.Base, targ.Base) {
		old := copyExpr(x)
		x.Op = c2go.ExprSlice
		x.List = []*cc.Expr{old, nil, nil}
		x.Left = nil
		x.Right = nil
		return
	}

	if actual.Kind == c2go.Slice && targ.Kind == cc.Ptr && sameType(actual.Base, targ.Base) {
		old := copyExpr(x)
		x.Op = cc.Addr
		x.Left = &cc.Expr{Op: cc.Index, Left: old, Right: &cc.Expr{Op: cc.Number, Text: "0"}}
		return
	}

	if !sameType(actual, targ) {
		old := copyExpr(x)
		// for debugging:
		// old = &cc.Expr{Op: cc.Cast, Left: old, Type: actual, XType: actual}
		x.Op = cc.Cast
		x.Left = old
		x.Right = nil
		x.List = nil
		x.Type = targ
		x.XType = targ
		if actual.Kind == cc.Array && targ.Kind == c2go.Slice {
			x.Op = c2go.ExprSlice
			x.List = []*cc.Expr{old, nil, nil}
			x.Left = nil
			x.Type = nil
		}
	}
}
Example #3
0
func doSideEffects(x *cc.Expr, before, after *[]*cc.Stmt, mode int) {
	if x == nil {
		return
	}

	// Cannot hoist side effects from conditionally evaluated expressions
	// into unconditionally evaluated statement lists.
	// For now, detect but do not handle.
	switch x.Op {
	case cc.Cond:
		doSideEffects(x.List[0], before, after, mode&^sideStmt|sideNoAfter)
		checkNoSideEffects(x.List[1], 0)
		checkNoSideEffects(x.List[2], 0)

	case cc.AndAnd, cc.OrOr:
		doSideEffects(x.Left, before, after, mode&^sideStmt|sideNoAfter)
		checkNoSideEffects(x.Right, 0)

	case cc.Comma:
		var leftover []*cc.Expr
		for i, y := range x.List {
			m := mode | sideNoAfter
			if i+1 < len(x.List) {
				m |= sideStmt
			}
			doSideEffects(y, before, after, m)
			switch y.Op {
			case cc.PostInc, cc.PostDec, cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq:
				*before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: y})
			default:
				leftover = append(leftover, y)
			}
		}
		x.List = leftover

	default:
		doSideEffects(x.Left, before, after, mode&^sideStmt)
		doSideEffects(x.Right, before, after, mode&^sideStmt)
		for _, y := range x.List {
			doSideEffects(y, before, after, mode&^sideStmt)
		}
	}

	if mode&sideStmt != 0 {
		// Expression as statement.
		// Can leave x++ alone, can rewrite ++x to x++, can leave x [op]= y alone.
		switch x.Op {
		case cc.PreInc:
			x.Op = cc.PostInc
			return
		case cc.PreDec:
			x.Op = cc.PostDec
			return
		case cc.PostInc, cc.PostDec:
			return
		case cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq:
			return
		case cc.Call:
			return
		}
	}

	switch x.Op {
	case cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq:
		x.Left = forceCheap(before, x.Left)
		old := copyExpr(x)
		*before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: old})
		fixMerge(x, x.Left)

	case cc.PreInc, cc.PreDec:
		x.Left = forceCheap(before, x.Left)
		old := copyExpr(x)
		old.SyntaxInfo = cc.SyntaxInfo{}
		if old.Op == cc.PreInc {
			old.Op = cc.PostInc
		} else {
			old.Op = cc.PostDec
		}
		*before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: old})
		fixMerge(x, x.Left)

	case cc.PostInc, cc.PostDec:
		x.Left = forceCheap(before, x.Left)
		if mode&sideNoAfter != 0 {
			// Not allowed to generate fixups afterward.
			d := &cc.Decl{
				Name: fmt.Sprintf("tmp%d", <-tmpGen),
				Type: x.Left.XType,
			}
			eq := &cc.Expr{
				Op:    c2go.ColonEq,
				Left:  &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d},
				Right: x.Left,
			}
			old := copyExpr(x.Left)
			old.SyntaxInfo = cc.SyntaxInfo{}
			*before = append(*before,
				&cc.Stmt{Op: cc.StmtExpr, Expr: eq},
				&cc.Stmt{Op: cc.StmtExpr, Expr: &cc.Expr{Op: x.Op, Left: old}},
			)
			x.Op = cc.Name
			x.Text = d.Name
			x.XDecl = d
			x.Left = nil
			break
		}
		old := copyExpr(x)
		old.SyntaxInfo = cc.SyntaxInfo{}
		*after = append(*after, &cc.Stmt{Op: cc.StmtExpr, Expr: old})
		fixMerge(x, x.Left)

	case cc.Cond:
		// Rewrite c ? y : z into tmp with initialization:
		//	var tmp typeof(c?y:z)
		//	if c {
		//		tmp = y
		//	} else {
		//		tmp = z
		//	}
		d := &cc.Decl{
			Name: "tmp",
			Type: x.XType,
		}
		*before = append(*before,
			&cc.Stmt{Op: cc.StmtDecl, Decl: d},
			&cc.Stmt{Op: cc.If, Expr: x.List[0],
				Body: &cc.Stmt{
					Op: cc.StmtExpr,
					Expr: &cc.Expr{
						Op:    cc.Eq,
						Left:  &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d},
						Right: x.List[1],
					},
				},
				Else: &cc.Stmt{
					Op: cc.StmtExpr,
					Expr: &cc.Expr{
						Op:    cc.Eq,
						Left:  &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d},
						Right: x.List[2],
					},
				},
			},
		)
		x.Op = cc.Name
		x.Text = d.Name
		x.XDecl = d
		x.List = nil

	case cc.Call:
		if x.Left.Text == "fmtstrcpy" || x.Left.Text == "fmtprint" {
			old := copyExpr(x)
			old.SyntaxInfo = cc.SyntaxInfo{}
			*before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: old})
			x.Op = cc.Number
			x.Text = "0"
			x.XDecl = nil
			x.Left = nil
			x.List = nil
		}
	}
}