github.com/gotranspile/cxgo@v0.3.8-0.20240118201721-29871598a6a2/c_type.go (about)

     1  package cxgo
     2  
     3  import (
     4  	"fmt"
     5  	"unicode"
     6  	"unicode/utf8"
     7  
     8  	"modernc.org/cc/v3"
     9  	"modernc.org/token"
    10  
    11  	"github.com/gotranspile/cxgo/types"
    12  )
    13  
    14  func (g *translator) convertTypeOper(p cc.Operand, where token.Position) types.Type {
    15  	defer func() {
    16  		switch r := recover().(type) {
    17  		case nil:
    18  		case error:
    19  			panic(ErrorWithPos(r, where))
    20  		default:
    21  			panic(ErrorWithPos(fmt.Errorf("%v", r), where))
    22  		}
    23  	}()
    24  	if d := p.Declarator(); d != nil {
    25  		where = d.Position()
    26  	}
    27  	var conf IdentConfig
    28  	if d := p.Declarator(); d != nil {
    29  		conf = g.idents[d.Name().String()]
    30  	}
    31  	return g.convertTypeRoot(conf, p.Type(), where)
    32  }
    33  
    34  // convertType is similar to newTypeCC but it will first consult the type cache.
    35  func (g *translator) convertType(conf IdentConfig, t cc.Type, where token.Position) types.Type {
    36  	defer func() {
    37  		if r := recover(); r != nil {
    38  			panic(fmt.Errorf("type conversion failed at %v: %v", where, r))
    39  		}
    40  	}()
    41  	// custom type overrides coming from the config
    42  	// note that we don't save them since they might depend
    43  	// not only on the input type, but also on a field name
    44  	switch conf.Type {
    45  	case HintBool:
    46  		return g.env.Go().Bool()
    47  	case HintIface:
    48  		return g.env.Go().Any()
    49  	case HintString:
    50  		return g.env.Go().String()
    51  	case HintSlice:
    52  		ct := g.newTypeCC(IdentConfig{}, t, where)
    53  		var elem types.Type
    54  		switch ct := ct.(type) {
    55  		case types.PtrType:
    56  			elem = ct.Elem()
    57  		case types.ArrayType:
    58  			elem = ct.Elem()
    59  		default:
    60  			panic(fmt.Errorf("expected an array or a pointer, got: %v, %#v; defined at: %v", ct, ct, where))
    61  		}
    62  		if elem == types.UintT(1) {
    63  			elem = g.env.Go().Byte()
    64  		}
    65  		return types.SliceT(elem)
    66  	}
    67  	// allow invalid types, they might still be useful
    68  	// since one may define them in a separate Go file
    69  	// and make the code valid
    70  	if t.Kind() == cc.Invalid {
    71  		return types.UnkT(g.env.PtrSize())
    72  	}
    73  	if ct, ok := g.ctypes[t]; ok {
    74  		return ct
    75  	}
    76  	ct := g.newTypeCC(conf, t, where)
    77  	g.ctypes[t] = ct
    78  	return ct
    79  }
    80  
    81  // convertTypeRoot is the same as convertType, but it applies a workaround for
    82  // C function pointers.
    83  func (g *translator) convertTypeRoot(conf IdentConfig, t cc.Type, where token.Position) types.Type {
    84  	ft := g.convertType(conf, t, where)
    85  	if p, ok := ft.(types.PtrType); ok && p.ElemKind().IsFunc() {
    86  		ft = p.Elem()
    87  	}
    88  	if p, ok := ft.(types.ArrayType); ok && p.Len() == 0 {
    89  		ft = types.SliceT(p.Elem())
    90  	}
    91  	return ft
    92  }
    93  
    94  // convertTypeOpt is similar to convertType, but it also allows void type by returning nil.
    95  func (g *translator) convertTypeOpt(conf IdentConfig, t cc.Type, where token.Position) types.Type {
    96  	if t == nil || t.Kind() == cc.Void || t.Kind() == cc.Invalid {
    97  		return nil
    98  	}
    99  	return g.convertType(conf, t, where)
   100  }
   101  
   102  // convertTypeRootOpt is similar to convertTypeRoot, but it also allows void type by returning nil.
   103  func (g *translator) convertTypeRootOpt(conf IdentConfig, t cc.Type, where token.Position) types.Type {
   104  	if t == nil || t.Kind() == cc.Void || t.Kind() == cc.Invalid {
   105  		return nil
   106  	}
   107  	return g.convertTypeRoot(conf, t, where)
   108  }
   109  
   110  // replaceType checks if the type needs to be replaced. It usually happens for builtin types.
   111  func (g *translator) replaceType(name string) (types.Type, bool) {
   112  	if t, ok := g.env.TypeByName(name); ok {
   113  		return t, true
   114  	}
   115  	if t := g.env.C().Type(name); t != nil {
   116  		return t, true
   117  	}
   118  	return nil, false
   119  }
   120  
   121  // newNamedTypeAt finds or creates a named type defined by specified CC types and tokens.
   122  func (g *translator) newNamedTypeAt(name string, typ, elem cc.Type, where token.Position) types.Type {
   123  	if typ == elem {
   124  		switch typ.Kind() {
   125  		case cc.Struct, cc.Union:
   126  		default:
   127  			panic(fmt.Errorf("name: %s, elem: (%T) %v", name, elem, elem))
   128  		}
   129  	}
   130  	conf := g.idents[name]
   131  	if typ, ok := g.replaceType(name); ok {
   132  		return typ
   133  	}
   134  	if c, ok := g.idents[name]; ok && c.Alias {
   135  		sub := g.convertTypeRoot(conf, elem, where)
   136  		g.ctypes[typ] = sub
   137  		g.aliases[name] = sub
   138  		return sub
   139  	}
   140  	return g.newOrFindNamedType(name, func() types.Type {
   141  		return g.convertTypeRoot(conf, elem, where)
   142  	})
   143  }
   144  
   145  func (g *translator) newOrFindNamedTypedef(name string, underlying func() types.Type) types.Named {
   146  	if c, ok := g.idents[name]; ok && c.Alias {
   147  		if _, ok := g.aliases[name]; ok {
   148  			return nil
   149  		}
   150  		// we should register the underlying type with the current name,
   151  		// so all the accesses will use underlying type
   152  		t := underlying()
   153  		g.aliases[name] = t
   154  		// and we suppress the definition of this type
   155  		return nil
   156  	}
   157  	return g.newOrFindNamedType(name, underlying)
   158  }
   159  
   160  // newOrFindNamedType finds or creates a new named type with a given underlying type.
   161  // The function is given because types may be recursive.
   162  func (g *translator) newOrFindNamedType(name string, underlying func() types.Type) types.Named {
   163  	if _, ok := g.aliases[name]; ok {
   164  		panic("alias")
   165  	}
   166  	if typ, ok := g.named[name]; ok {
   167  		return typ
   168  	}
   169  	und := underlying()
   170  	if typ, ok := g.named[name]; ok {
   171  		return typ
   172  	}
   173  	return g.newNamedType(name, und)
   174  }
   175  
   176  // newNamedType creates a new named type based on the underlying type.
   177  func (g *translator) newNamedType(name string, underlying types.Type) types.Named {
   178  	if _, ok := g.named[name]; ok {
   179  		panic("type with a same name already exists: " + name)
   180  	}
   181  	goname := ""
   182  	if c, ok := g.idents[name]; ok && c.Rename != "" {
   183  		goname = c.Rename
   184  	}
   185  	nt := types.NamedTGo(name, goname, underlying)
   186  	g.named[name] = nt
   187  	return nt
   188  }
   189  
   190  // newNamedTypeFrom creates a new named type based on a given CC type.
   191  // It is similar to newNamedType, but accepts a CC type that should be bound to a new type.
   192  func (g *translator) newNamedTypeFrom(name string, underlying types.Type, from cc.Type) types.Named {
   193  	if _, ok := g.ctypes[from]; ok {
   194  		panic("same C type already exists")
   195  	}
   196  	nt := g.newNamedType(name, underlying)
   197  	g.ctypes[from] = nt
   198  	return nt
   199  }
   200  
   201  // newOrFindNamedTypeFrom finds or creates a type with a given name, underlying type and source C type.
   202  // It cannot return the NamedType because the type may have an override.
   203  func (g *translator) newOrFindNamedTypeFrom(name string, elem func() types.Type, from cc.Type) types.Type {
   204  	if t, ok := g.ctypes[from]; ok {
   205  		return t
   206  	}
   207  	nt := g.newOrFindNamedType(name, elem)
   208  	g.ctypes[from] = nt
   209  	return nt
   210  }
   211  
   212  // newOrFindIncompleteNamedTypeFrom finds or creates a incomplete type with a given name and source C type.
   213  // It cannot return the NamedType because the type may have an override, or the type may have been resolved to something else.
   214  func (g *translator) newOrFindIncompleteNamedTypeFrom(name string, from cc.Type) types.Type {
   215  	return g.newOrFindNamedTypeFrom(name, nil, from)
   216  }
   217  
   218  func asExportedName(s string) string {
   219  	if len(s) == 0 {
   220  		return ""
   221  	}
   222  	r, n := utf8.DecodeRuneInString(s)
   223  	return string(unicode.ToUpper(r)) + s[n:]
   224  }
   225  
   226  // newTypeCC creates a new type based on a specified C type. This function will not consult the cache for a given type.
   227  // It will recursively convert all the underlying and sub-types using convertType.
   228  func (g *translator) newTypeCC(conf IdentConfig, t cc.Type, where token.Position) types.Type {
   229  	sname := t.Name().String()
   230  	if nt, ok := g.replaceType(sname); ok {
   231  		g.ctypes[t] = nt
   232  		return nt
   233  	}
   234  	// it's handled separately because it's the only type that is allowed to be incomplete
   235  	if t != t.Alias() {
   236  		if t.IsIncomplete() {
   237  			if nt, ok := g.named[sname]; ok {
   238  				return nt
   239  			}
   240  			return g.newOrFindNamedType(sname, func() types.Type {
   241  				return types.StructT(nil)
   242  			})
   243  		}
   244  		if t, ok := g.aliases[sname]; ok {
   245  			return t
   246  		}
   247  		conf := g.idents[sname]
   248  		u := t.Alias()
   249  		sub := g.convertType(conf, u, where)
   250  		if sub, ok := sub.(types.Named); ok {
   251  			if name := t.Name(); name == u.Name() {
   252  				g.named[name.String()] = sub
   253  				g.ctypes[t] = sub
   254  				return sub
   255  			}
   256  		}
   257  		return g.newNamedTypeAt(sname, t, u, where)
   258  	}
   259  	switch t.Kind() {
   260  	case cc.Struct, cc.Union:
   261  		return g.convertStructType(conf, t, where)
   262  	}
   263  	if u := t.Alias(); t != u {
   264  		panic(fmt.Errorf("unhandled alias type: %T", t))
   265  	}
   266  	switch kind := t.Kind(); kind {
   267  	case cc.UInt64, cc.UInt32, cc.UInt16, cc.UInt8:
   268  		return types.UintT(int(t.Size()))
   269  	case cc.Int64, cc.Int32, cc.Int16, cc.Int8:
   270  		return types.IntT(int(t.Size()))
   271  	case cc.SChar:
   272  		return g.env.C().SignedChar()
   273  	case cc.UChar:
   274  		return g.env.C().UnsignedChar()
   275  	case cc.Short:
   276  		return g.env.C().Short()
   277  	case cc.UShort:
   278  		return g.env.C().UnsignedShort()
   279  	case cc.Int:
   280  		return g.env.C().Int()
   281  	case cc.UInt:
   282  		return g.env.C().UnsignedInt()
   283  	case cc.Long:
   284  		return g.env.C().Long()
   285  	case cc.ULong:
   286  		return g.env.C().UnsignedLong()
   287  	case cc.LongLong:
   288  		return g.env.C().LongLong()
   289  	case cc.ULongLong:
   290  		return g.env.C().UnsignedLongLong()
   291  	case cc.Float:
   292  		return g.env.C().Float()
   293  	case cc.Double:
   294  		return g.env.C().Double()
   295  	case cc.LongDouble:
   296  		return types.FloatT(int(t.Size()))
   297  	case cc.Char:
   298  		return g.env.C().Char()
   299  	case cc.Bool:
   300  		return g.env.C().Bool()
   301  	case cc.Function:
   302  		return g.convertFuncType(conf, nil, t, where)
   303  	case cc.Ptr:
   304  		if t.Elem().Kind() == cc.Char {
   305  			return g.env.C().String()
   306  		}
   307  		if e := t.Elem(); e.Kind() == cc.Struct && e.NumField() == 1 {
   308  			// Go slices defined via cxgo builtins
   309  			f := e.FieldByIndex([]int{0})
   310  			if f.Name().String() == types.GoPrefix+"slice_data" {
   311  				elem := g.convertType(IdentConfig{}, f.Type(), where)
   312  				return types.SliceT(elem)
   313  			}
   314  		}
   315  		var ptr types.PtrType
   316  		if name := t.Elem().Name(); name != 0 {
   317  			if pt, ok := g.namedPtrs[name.String()]; ok {
   318  				return pt
   319  			}
   320  			ptr = g.env.PtrT(nil) // incomplete
   321  			g.namedPtrs[name.String()] = ptr
   322  		}
   323  		// use Opt because of the void*
   324  		elem := g.convertTypeOpt(IdentConfig{}, t.Elem(), where)
   325  		if ptr != nil {
   326  			ptr.SetElem(elem)
   327  			return ptr
   328  		}
   329  		return g.env.PtrT(elem)
   330  	case cc.Array:
   331  		if t.Elem().Kind() == cc.Char {
   332  			return types.ArrayT(g.env.Go().Byte(), int(t.Len()))
   333  		}
   334  		elem := g.convertType(IdentConfig{}, t.Elem(), where)
   335  		return types.ArrayT(
   336  			elem,
   337  			int(t.Len()),
   338  		)
   339  	case cc.Union:
   340  		if name := t.Name(); name != 0 {
   341  			u := t.Alias()
   342  			if name == u.Name() {
   343  				u = u.Alias()
   344  			}
   345  			return g.newNamedTypeAt(name.String(), t, u, where)
   346  		}
   347  		fconf := make(map[string]IdentConfig)
   348  		for _, f := range conf.Fields {
   349  			fconf[f.Name] = f
   350  		}
   351  		var fields []*types.Field
   352  		for i := 0; i < t.NumField(); i++ {
   353  			f := t.FieldByIndex([]int{i})
   354  			name := f.Name().String()
   355  			fc := fconf[name]
   356  			ft := g.convertTypeRoot(fc, f.Type(), where)
   357  			fields = append(fields, &types.Field{
   358  				Name: g.newIdent(name, ft),
   359  			})
   360  		}
   361  		return types.UnionT(fields)
   362  	case cc.Enum:
   363  		return g.newTypeCC(IdentConfig{}, t.EnumType(), where)
   364  	default:
   365  		panic(fmt.Errorf("%T, %s (%s)", t, kind, t.String()))
   366  	}
   367  }
   368  
   369  func (g *translator) convertStructType(conf IdentConfig, t cc.Type, where token.Position) types.Type {
   370  	sname := t.Name().String()
   371  	if c, ok := g.idents[sname]; ok {
   372  		conf = c
   373  	}
   374  	fconf := make(map[string]IdentConfig)
   375  	for _, f := range conf.Fields {
   376  		fconf[f.Name] = f
   377  	}
   378  	buildType := func() types.Type {
   379  		var fields []*types.Field
   380  		for i := 0; i < t.NumField(); i++ {
   381  			f := t.FieldByIndex([]int{i})
   382  			fc := fconf[f.Name().String()]
   383  			ft := g.convertTypeRoot(fc, f.Type(), where)
   384  			if f.Name() == 0 {
   385  				st := types.Unwrap(ft).(*types.StructType)
   386  				fields = append(fields, st.Fields()...)
   387  				continue
   388  			}
   389  			fname := g.newIdent(f.Name().String(), ft)
   390  			if fc.Rename != "" {
   391  				fname.GoName = fc.Rename
   392  			} else if !g.conf.UnexportedFields {
   393  				fname.GoName = asExportedName(fname.Name)
   394  			}
   395  			fields = append(fields, &types.Field{
   396  				Name: fname,
   397  			})
   398  		}
   399  		if !where.IsValid() {
   400  			panic(where)
   401  		}
   402  		var s *types.StructType
   403  		if t.Kind() == cc.Union {
   404  			s = types.UnionT(fields)
   405  		} else {
   406  			s = types.StructT(fields)
   407  		}
   408  		s.Where = where.String()
   409  		if t.Name() == 0 {
   410  			return s
   411  		}
   412  		return s
   413  	}
   414  	if t.Name() == 0 {
   415  		return buildType()
   416  	}
   417  	return g.newOrFindNamedType(sname, buildType)
   418  }
   419  
   420  func (g *translator) convertFuncType(conf IdentConfig, d *cc.Declarator, t cc.Type, where token.Position) *types.FuncType {
   421  	if kind := t.Kind(); kind != cc.Function {
   422  		panic(kind)
   423  	}
   424  	if d != nil {
   425  		where = d.Position()
   426  	}
   427  	var rconf IdentConfig
   428  	aconf := make(map[string]IdentConfig)
   429  	iconf := make(map[int]IdentConfig)
   430  	for _, f := range conf.Fields {
   431  		if f.Name != "" {
   432  			if f.Name == "return" {
   433  				rconf = f
   434  			} else {
   435  				aconf[f.Name] = f
   436  			}
   437  		} else {
   438  			iconf[f.Index] = f
   439  		}
   440  	}
   441  	var (
   442  		args  []*types.Field
   443  		named int
   444  	)
   445  	for i, p := range t.Parameters() {
   446  		pt := p.Type()
   447  		if pt.Kind() == cc.Void {
   448  			continue
   449  		}
   450  		var fc IdentConfig
   451  		if ac, ok := aconf[p.Name().String()]; ok {
   452  			fc = ac
   453  		} else if ac, ok = iconf[i]; ok {
   454  			fc = ac
   455  		}
   456  		at := g.convertTypeRoot(fc, pt, where)
   457  		var name *types.Ident
   458  		if d != nil && p.Name() != 0 {
   459  			name = g.convertIdent(d.ParamScope(), p.Declarator().NameTok(), at).Ident
   460  			named++
   461  		} else if p.Name() != 0 {
   462  			name = g.convertIdentWith(p.Declarator().NameTok().String(), at, p.Declarator()).Ident
   463  			named++
   464  		} else {
   465  			name = types.NewUnnamed(at)
   466  		}
   467  		args = append(args, &types.Field{
   468  			Name: name,
   469  		})
   470  	}
   471  	if named != 0 && len(args) != named {
   472  		for i, a := range args {
   473  			if a.Name.Name == "" && a.Name.GoName == "" {
   474  				a.Name.GoName = fmt.Sprintf("a%d", i+1)
   475  			}
   476  		}
   477  	}
   478  	ret := g.convertTypeRootOpt(rconf, t.Result(), where)
   479  	if t.IsVariadic() {
   480  		return g.env.VarFuncT(ret, args...)
   481  	}
   482  	return g.env.FuncT(ret, args...)
   483  }
   484  
   485  func propagateConst(t types.Type) bool {
   486  	switch t := t.(type) {
   487  	case types.PtrType:
   488  		if !propagateConst(t.Elem()) {
   489  			//t.Const = true // TODO
   490  		}
   491  		return true
   492  	case types.ArrayType:
   493  		return propagateConst(t.Elem())
   494  	case types.IntType:
   495  		//t.Const = true
   496  		return true
   497  	}
   498  	return false
   499  }
   500  
   501  func (g *translator) ZeroValue(t types.Type) Expr {
   502  	if t == nil {
   503  		panic("nil type")
   504  	}
   505  	switch t.Kind().Major() {
   506  	case types.Ptr, types.Func:
   507  		return g.Nil()
   508  	case types.Int, types.Float:
   509  		return cUintLit(0, 10)
   510  	case types.Struct, types.Array:
   511  		return &CCompLitExpr{Type: t}
   512  	default:
   513  		panic(t)
   514  	}
   515  }