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 } } }
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 } }