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 != Bool { old := copyExpr(x) if targ.Kind == 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 == Bool { old := copyExpr(x) left := fixGoTypesExpr(fn, old, nil) if left != nil && left.Kind == 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) fixSpecialCompare(fn, x) return targ } } fixArray(fn, x) switch x.Op { default: panic(fmt.Sprintf("unexpected construct %v in fixGoTypesExpr - %v - %v", GoString(x), x.Op, x.Span)) case ExprType: // inserted by a rewrite return nil case 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 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 == 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 = AndNot x.Right = x.Right.Left } if x.Op == cc.Add && isSliceStringOrArray(left) { fixGoTypesExpr(fn, x.Right, nil) x.Op = 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: 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 = 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: 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) && GoString(x.Left.Right) == "0" && 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) if x.Op == cc.Eq && x.Left != nil && x.Right != nil && x.Right.XType != nil && isCall(x.Right, "make") && x.Left.XDecl != nil && x.Left.XDecl.Type != nil && x.Left.XDecl.Type.Kind == cc.Ptr && sameType(x.Left.XDecl.Type.Base, x.Right.XType.Base) { x.Left.XDecl.Type = x.Right.XType x.Left.XType = x.Right.XType left = x.Right.XType } return left case 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 == Slice && sameType(targ.Base, left) { l := x.Left l.Op = 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) if left != nil && left.Kind == cc.Ptr && left.Base.Kind == cc.Func { left = left.Base } 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) if isEmptyInterface(x.Left.XType) { x.Op = TypeAssert } 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" || x.Right.Op == cc.Name && x.Right.Text == "nil" { if isSliceOrPtr(left) { x.Right.Op = cc.Name x.Right.Text = "nil" return boolType } if left != nil && left.Kind == 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, Slice, cc.Array: if x.Op == cc.Indir && left.Kind == cc.Ptr && left.Base.Kind == cc.Func { *x = *x.Left } return left.Base case String: return byteType } return nil case cc.Lsh, cc.Rsh: left := fixGoTypesExpr(fn, x.Left, targ) if left != nil && targ != nil && Int8 <= left.Kind && left.Kind <= 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", "C": 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 { if x.Text == "true" || x.Text == "false" { return x.XType } 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: 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 == Slice) && left.Base.Def().Is(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: String} case cc.VaArg: // TODO return nil } }
func forceConvert(fn *cc.Decl, x *cc.Expr, actual, targ *cc.Type) { if isEmptyInterface(targ) { return } if isEmptyInterface(actual) { old := copyExpr(x) x.Op = TypeAssert x.Left = old x.Right = nil x.List = nil x.Type = targ x.XType = targ return } if isNumericConst(x) && targ != nil { switch targ.Kind { case cc.Ptr, Slice: if x.Op == cc.Number && x.Text == "0" { x.Op = cc.Name x.Text = "nil" x.XType = targ } case String: if x.Op == cc.Number && x.Text == "0" { x.Op = cc.Name x.Text = `""` x.XType = targ } } return } if x.Op == cc.Name && x.Text == "nil" && targ != nil { switch targ.Kind { case cc.Func, cc.Ptr, Slice: return case String: x.Text = `""` x.XType = targ x.XDecl = nil return } } if actual == nil || targ == nil { return } if actual.Kind == Ideal && Int8 <= targ.Kind && targ.Kind <= Float64 { return } if x != nil && x.Op == cc.Name && x.Text == "nil" { if targ.Kind == cc.Func || targ.Kind == cc.Ptr || targ.Kind == 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 == Bool && Int8 <= targ.Kind && targ.Kind <= Float64 { old := copyExpr(x) if targ.Kind == 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 == Slice && sameType(actual.Base, targ.Base) { old := copyExpr(x) x.Op = ExprSlice x.List = []*cc.Expr{old, nil, nil} x.Left = nil x.Right = nil return } if actual.Kind == Slice && targ.Kind == cc.Ptr && sameType(actual.Base, targ.Base) { if isCall(x, "make") { return } 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) { if x.Op == cc.Twid { forceConvert(fn, x.Left, actual, targ) x.XType = targ return } 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 == Slice { x.Op = ExprSlice x.List = []*cc.Expr{old, nil, nil} x.Left = nil x.Type = nil } } }
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: 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 } } }