github.com/gotranspile/cxgo@v0.3.7/casts.go (about) 1 package cxgo 2 3 import ( 4 "fmt" 5 "math" 6 7 "github.com/gotranspile/cxgo/types" 8 ) 9 10 func (g *translator) cCast(typ types.Type, x Expr) Expr { 11 if typ == nil { 12 panic("no type") 13 } 14 tk := typ.Kind() 15 xt := x.CType(typ) 16 xk := xt.Kind() 17 if xt == g.env.Go().Any() { 18 return &CCastExpr{Assert: true, Type: typ, Expr: x} 19 } 20 if typ == g.env.Go().Any() { 21 return x 22 } 23 if at, ok := typ.(types.ArrayType); ok && at.IsSlice() { 24 switch x := x.(type) { 25 case Nil: 26 return g.Nil() 27 case IntLit: 28 if x.IsZero() { 29 return g.Nil() 30 } 31 case *TakeAddr: 32 if ind, ok := x.X.(*CIndexExpr); ok && types.Same(ind.Expr.CType(nil), typ) { 33 if ind.IndexZero() { 34 // special case: unwrap unnecessary cast to slice 35 return ind.Expr 36 } 37 } 38 } 39 if fc, ok := x.(*CallExpr); ok && len(fc.Args) >= 1 { 40 if f, ok := fc.Fun.(Ident); ok { 41 gg := g.env.Go() 42 switch f.Identifier() { 43 case gg.SliceFunc(), 44 gg.AppendFunc(): 45 if types.Same(typ, fc.Args[0].CType(nil)) { 46 return x 47 } 48 } 49 } 50 } 51 } 52 if xk.Is(types.Array) && !tk.Is(types.Array) { 53 x = g.cAddr(x) 54 return g.cCast(typ, x) 55 } 56 // equal or same type: no conversion 57 if types.Same(typ, xt) { 58 return x 59 } 60 // unknown types: bypass 61 if tk.Is(types.Unknown) { 62 // special cases for well-known types 63 switch typ { 64 case g.env.Go().String(): 65 var fnc *types.Ident 66 if types.Same(xt, g.env.C().String()) { 67 fnc = g.env.StringC2Go() 68 } else if types.Same(xt, g.env.C().WString()) { 69 fnc = g.env.WStringC2Go() 70 } else { 71 return g.cCast(g.env.C().String(), x) 72 } 73 return g.NewCCallExpr(FuncIdent{fnc}, []Expr{x}) 74 } 75 return x 76 } 77 if c1, ok := cUnwrap(x).(*CCastExpr); ok { 78 // casts A(A(x)) -> A(x) 79 if types.Same(c1.Type, typ) { 80 return c1 81 } 82 } 83 // conversions to bool - we have a specialized function for that 84 if tk.IsBool() { 85 return g.ToBool(x) 86 } 87 // nil should be first, because it's an "untyped ptr" 88 if xk.Is(types.Nil) { 89 if tk.IsPtr() || tk.IsFunc() { 90 return cUnwrap(x) 91 } 92 } 93 // strings are immutable, so call a specialized function for conversion 94 if types.Same(xt, g.env.Go().String()) { 95 // string -> []byte 96 if at, ok := types.Unwrap(typ).(types.ArrayType); ok && at.IsSlice() && at.Elem() == g.env.Go().Byte() { 97 return &CCastExpr{Type: at, Expr: x} 98 } 99 // [N]byte = "xyz" 100 if at, ok := types.Unwrap(typ).(types.ArrayType); ok && (types.Same(at.Elem(), g.env.Go().Byte()) || xk == types.Unknown) { 101 if !at.IsSlice() { 102 tmp := types.NewIdent("t", at) 103 copyF := FuncIdent{g.env.Go().CopyFunc()} 104 // declare a function literal returning an array of the same size 105 var body []CStmt 106 // declare temp variable with an array type 107 body = append(body, g.NewCDeclStmt(&CVarDecl{CVarSpec: CVarSpec{ 108 g: g, 109 Type: at, 110 Names: []*types.Ident{tmp}, 111 }})...) 112 body = append(body, 113 // copy string into it 114 &CExprStmt{Expr: &CallExpr{ 115 Fun: copyF, Args: []Expr{ 116 &SliceExpr{Expr: IdentExpr{tmp}}, // tmp[:] 117 &CCastExpr{Type: types.SliceT(at.Elem()), Expr: x}, // ([]TYPE)("xyz") 118 }, 119 }}, 120 ) 121 // return temp variable 122 body = append(body, g.NewReturnStmt(IdentExpr{tmp}, at)...) 123 lit := g.NewFuncLit(g.env.FuncTT(at), body...) 124 return g.NewCCallExpr(lit, nil) 125 } 126 } 127 if types.Same(typ, g.env.C().WString()) { 128 return g.cCast(typ, g.NewCCallExpr(FuncIdent{g.env.WStringGo2C()}, []Expr{x})) 129 } 130 return g.cCast(typ, g.NewCCallExpr(FuncIdent{g.env.StringGo2C()}, []Expr{x})) 131 } 132 if xt == g.env.Go().String() { 133 var conv *types.Ident 134 if types.Same(typ, g.env.C().String()) { 135 conv = g.env.StringGo2C() 136 } else if types.Same(typ, g.env.C().WString()) { 137 conv = g.env.WStringGo2C() 138 } 139 if conv != nil { 140 return g.cCast(typ, g.NewCCallExpr(FuncIdent{conv}, []Expr{x})) 141 } 142 } 143 // any casts from array to other types should go through pointer to an array 144 if xk.Is(types.Unknown) { 145 return &CCastExpr{ 146 Type: typ, 147 Expr: x, 148 } 149 } 150 switch { 151 case tk.IsPtr(): 152 return g.cPtrToPtr(typ, g.ToPointer(x)) 153 case tk.IsInt(): 154 if l, ok := cUnwrap(x).(IntLit); ok { 155 ti, ok := types.Unwrap(typ).(types.IntType) 156 if l.IsUint() && ok && ti.Signed() && !litCanStore(ti, l) { 157 // try overflowing it 158 return l.OverflowInt(ti.Sizeof()) 159 } 160 if l.IsUint() || (ok && ti.Signed()) { 161 return &CCastExpr{ 162 Type: typ, 163 Expr: x, 164 } 165 } 166 sz := typ.Sizeof() 167 var uv uint64 168 switch sz { 169 case 1: 170 uv = math.MaxUint8 171 case 2: 172 uv = math.MaxUint16 173 case 4: 174 uv = math.MaxUint32 175 case 8: 176 uv = math.MaxUint64 177 default: 178 return &CCastExpr{ 179 Type: typ, 180 Expr: x, 181 } 182 } 183 uv -= uint64(-l.Int()) - 1 184 return cUintLit(uv) 185 } 186 if xk.IsFunc() { 187 // func() -> int 188 return &FuncToInt{ 189 X: g.ToFunc(x, nil), 190 To: types.Unwrap(typ).(types.IntType), 191 } 192 } 193 if xk.IsPtr() { 194 // *some -> int 195 return g.cPtrToInt(typ, g.ToPointer(x)) 196 } 197 if x.IsConst() && xk.IsUntyped() { 198 return x 199 } 200 if xk.IsBool() { 201 return g.cCast(typ, &BoolToInt{X: g.ToBool(x)}) 202 } 203 xi, ok1 := types.Unwrap(xt).(types.IntType) 204 ti, ok2 := types.Unwrap(typ).(types.IntType) 205 if ok1 && ok2 && xi.Signed() != ti.Signed() { 206 if ti.Sizeof() > xi.Sizeof() { 207 return &CCastExpr{ 208 Type: typ, 209 Expr: x, 210 } 211 } else if ti.Sizeof() < xi.Sizeof() { 212 var t2 types.Type 213 if !ti.Signed() { 214 t2 = types.IntT(ti.Sizeof()) 215 } else { 216 t2 = types.UintT(ti.Sizeof()) 217 } 218 return &CCastExpr{ 219 Type: typ, 220 Expr: g.cCast(t2, x), 221 } 222 } 223 } 224 return &CCastExpr{ 225 Type: typ, 226 Expr: x, 227 } 228 case tk.IsFunc(): 229 switch x := cUnwrap(x).(type) { 230 case Nil: 231 return x 232 case IntLit: 233 if x.IsZero() { 234 return g.Nil() 235 } 236 } 237 if !xk.IsFunc() { 238 x = g.ToFunc(x, types.Unwrap(typ).(*types.FuncType)) 239 return g.cCast(typ, x) 240 } 241 ft, fx := types.Unwrap(typ).(*types.FuncType), types.Unwrap(xt).(*types.FuncType) 242 if (ft.Variadic() == fx.Variadic() || !ft.Variadic()) && ft.ArgN() >= fx.ArgN() && ((ft.Return() != nil) == (fx.Return() != nil) || (ft.Return() == nil && fx.Return() != nil)) { 243 // cannot cast directly, but can return lambda instead 244 callArgs := make([]Expr, 0, ft.ArgN()) 245 funcArgs := make([]*types.Field, 0, ft.ArgN()) 246 for i, a := range ft.Args() { 247 at := a.Type() 248 name := types.NewIdent(fmt.Sprintf("arg%d", i+1), at) 249 if a.Name != nil && !a.Name.IsUnnamed() { 250 name = types.NewIdent(a.Name.Name, at) 251 } 252 funcArgs = append(funcArgs, &types.Field{ 253 Name: name, 254 }) 255 callArgs = append(callArgs, IdentExpr{name}) 256 } 257 argn := fx.ArgN() 258 if ft.Variadic() { 259 callArgs = append(callArgs, &ExpandExpr{X: IdentExpr{types.NewIdent("_rest", types.UnkT(1))}}) 260 argn++ 261 } 262 e := g.NewCCallExpr(g.ToFunc(x, nil), callArgs[:argn]) 263 var stmts []CStmt 264 if ft.Return() != nil { 265 stmts = g.NewReturnStmt(e, ft.Return()) 266 } else { 267 stmts = NewCExprStmt(e) 268 } 269 var litT *types.FuncType 270 if ft.Variadic() { 271 litT = g.env.VarFuncT( 272 ft.Return(), 273 funcArgs..., 274 ) 275 } else { 276 litT = g.env.FuncT( 277 ft.Return(), 278 funcArgs..., 279 ) 280 } 281 return g.NewFuncLit(litT, stmts...) 282 } 283 // incompatible function types - force error 284 return x 285 case tk.IsFloat(): 286 if xk.IsUntyped() { 287 return x 288 } 289 case tk.Is(types.Array): 290 ta := types.Unwrap(typ).(types.ArrayType) 291 if xa, ok := types.Unwrap(xt).(types.ArrayType); ok && ta.Len() == 0 && xa.Len() != 0 { 292 return &SliceExpr{Expr: x} 293 } 294 case tk.Is(types.Struct): 295 isZero := false 296 switch x := cUnwrap(x).(type) { 297 case Nil: 298 isZero = true 299 case IntLit: 300 if x.IsZero() { 301 isZero = true 302 } 303 } 304 if isZero { 305 return g.NewCCompLitExpr(typ, nil) 306 } 307 } 308 return &CCastExpr{ 309 Type: typ, 310 Expr: x, 311 } 312 }