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  }