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