Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func NewPackage(pkgPath, pkgName string, files []Source, conf *Config) (ret Pack
pkgOf: conf.PackageOf, nameLookup: nameLookup, pubLookup: conf.PubFileLookup,
fileBases: make(map[clang.File]int), ovobjs: make(map[string]*overloadObj),
macroVals: make(map[string]any), types: make(map[string]typeObj),
logicals: make(map[string]*logicalClass),
logicals: make(map[string]*types.Named),
lastSeen: make(map[string]none), impPkgs: make(map[string]none),
}

Expand Down
4 changes: 2 additions & 2 deletions cl/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func goNode(ctx *pkgCtx, v clang.Cursor) ast.Node {
var file clang.File
var pos, end c.Uint
rg := v.Extent()
rg.Start().Spelling(&file, nil, nil, &pos)
rg.Start().Expansion(&file, nil, nil, &pos)
if file == clang.InvalidFile {
return nil
}
Expand Down Expand Up @@ -155,7 +155,7 @@ type pkgCtx struct {
lastSeen map[string]none // last seen include file set (loaded include files)
thisSeen map[string]none // include file set seen in this translation unit

logicals map[string]*logicalClass // logical Go class name => logical class info
logicals map[string]*types.Named // logical Go class name => logical class type
Comment thread
xushiwei marked this conversation as resolved.

loads []compileUnit
compiles []compileUnit
Expand Down
33 changes: 20 additions & 13 deletions cl/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,25 +133,32 @@ func compileFuncOrMethod(ctx *pkgCtx, obj *overloadObj, this *classCtx) {
if this == nil {
if ctx.lang == LanguageC {
// try to method for C global functions
if cls, mayClass := ctx.methodCheck(cName); mayClass {
params, recv, typRecv, typName = tryToMethod(ctx, pkgTypes, params)
if typRecv != nil {
recvCType := fn.Argument(0).Type()
if recvCType.Kind == lc.Type_Pointer {
recvCType = recvCType.Pointee()
}
typCName = trimTypeTag(clang.String(recvCType.Unqualified()))
oldParams := params
params, recv, typRecv, typName = tryToMethod(ctx, pkgTypes, params)
if typRecv != nil {
recvCType := fn.Argument(0).Type()
isPtr := recvCType.Kind == lc.Type_Pointer
if isPtr {
recvCType = recvCType.Pointee()
}
typCName = trimTypeTag(clang.String(recvCType.Unqualified()))
if isPtr {
// When the function resolves to a logical class distinct from
// the physical receiver type (for example PyList_GetItem
// resolves to List while its receiver is the base class
// Object), emit the method on the logical class - which embeds
// the base - and generate the As<Class> conversion method on
// the base class. See logical.go.
if cls != "" && cls != typName {
logical := ctx.logicalClassOf(fn, cls, typRecv)
recv = types.NewParam(recv.Pos(), pkgTypes, recv.Name(), types.NewPointer(logical))
typRecv = logical
typName = cls
if cls, mayClass := ctx.methodCheck(cName); mayClass {
if cls != "" && cls != typName {
if contains(cls, ctx.nonClasses) {
params, recv, typName, typCName = oldParams, nil, "", ""
} else if logical, ok := ctx.logicalClassOf(fn, cls, typRecv, recv); ok {
recv = types.NewParam(recv.Pos(), pkgTypes, recv.Name(), types.NewPointer(logical))
typRecv = logical
typName = cls
}
}
}
}
}
Expand Down
57 changes: 23 additions & 34 deletions cl/logical.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ import (

const asMethodPrefix = "As"

type logicalClass struct {
named *types.Named // the logical class type (List)
base *types.Named // the physical base class it embeds (Object)
}

// logicalClassOf returns the logical class named goName whose base is the
// physical type base, creating it (and its As<Class> conversion method on base)
// on first use. It is lazy and deterministic: the type and the conversion method
Expand All @@ -65,42 +60,38 @@ type logicalClass struct {
// If a package-level type with the same name already exists (generated from the
// headers or provided through TypeAlias), it is reused instead of generating a
// second one, and the As<Class> conversion is still added when it is missing.
func (p *pkgCtx) logicalClassOf(decl clang.Cursor, goName string, base *types.Named) *types.Named {
func (p *pkgCtx) logicalClassOf(decl clang.Cursor, goName string, baseTy *types.Named, baseRecv *types.Var) (*types.Named, bool) {
if lc, ok := p.logicals[goName]; ok {
return lc.named
return lc, lc != nil
}

named := p.newLogicalType(decl, goName, base)
lc := &logicalClass{named: named, base: base}
p.logicals[goName] = lc
named, ok := p.newLogicalType(decl, goName, baseTy)
p.logicals[goName] = named
if !ok {
return nil, false
}

// genAsMethod runs once per logical class (subsequent resolutions hit the
// cache above). It is attempted whether the type was freshly emitted or an
// existing package-level type was reused, so a reused type still gets its
// As<Class> conversion; genAsMethod itself keeps any pre-existing method of
// that name and reports a diagnostic.
p.genAsMethod(decl, lc)
return named
p.genAsMethod(decl, named, baseTy, baseRecv)
return named, true
}

// newLogicalType returns the named type for the logical class, reusing an
// existing package-level type when one is present and otherwise emitting a fresh
// "type <goName> struct { <base> }".
func (p *pkgCtx) newLogicalType(decl clang.Cursor, goName string, base *types.Named) *types.Named {
if o := p.pkg.Types.Scope().Lookup(goName); o != nil {
if tn, ok := o.(*types.TypeName); ok {
if n, ok := tn.Type().(*types.Named); ok {
return n
}
}
}

func (p *pkgCtx) newLogicalType(decl clang.Cursor, goName string, base *types.Named) (*types.Named, bool) {
pkg := p.pkg
pkgTypes := pkg.Types
if pkgTypes.Scope().Lookup(goName) != nil {
// can't create a new logical type with the same name as an existing type
// p.ignoref(featExplicitIgnore, decl, "logical class %s: name exists, ignored", goName)
Comment thread
xushiwei marked this conversation as resolved.
return nil, false
}
typDecl := newType(p, decl, "", goName)
embed := types.NewField(goNodePos(p, decl), pkgTypes, base.Obj().Name(), base, true)
typDecl.InitType(pkg, types.NewStruct([]*types.Var{embed}, nil))
return typDecl.Type()
return typDecl.Type(), true
}

// genAsMethod emits the conversion method on the base class, for example
Expand All @@ -110,29 +101,27 @@ func (p *pkgCtx) newLogicalType(decl clang.Cursor, goName string, base *types.Na
// The conversion only reinterprets the pointer; it does not call into C and does
// not check the object's real type. If a method of the same name already exists
// on the base class, the existing method is kept and a diagnostic is reported.
func (p *pkgCtx) genAsMethod(decl clang.Cursor, lc *logicalClass) {
name := asMethodPrefix + lc.named.Obj().Name()
if pos, _, exists := findMember(lc.base, name); exists {
func (p *pkgCtx) genAsMethod(decl clang.Cursor, named, baseTy *types.Named, baseRecv *types.Var) {
name := asMethodPrefix + named.Obj().Name()
if pos, _, exists := findMember(baseTy, name); exists {
p.errorf(decl, "%s redeclared in this block\n\t%v: other declaration of %s", name, p.position(pos), name)
return
}

pkg := p.pkg
pkgTypes := pkg.Types
recvType := types.NewPointer(lc.base)
retType := types.NewPointer(lc.named)
recv := types.NewParam(goNodePos(p, decl), pkgTypes, c2goMethodRecvName, recvType)
retType := types.NewPointer(named)
results := types.NewTuple(types.NewParam(token.NoPos, pkgTypes, "", retType))
sig := types.NewSignatureType(recv, nil, nil, nil, results, false)
sig := types.NewSignatureType(baseRecv, nil, nil, nil, results, false)

f, err := pkg.NewFuncWith(goNodePos(p, decl), name, sig, nil)
if err != nil {
p.panicf(decl, "logical class %s: genAsMethod failed - %v", lc.named.Obj().Name(), err)
p.panicf(decl, "logical class %s: genAsMethod failed - %v", named.Obj().Name(), err)
}
cb := f.BodyStart(pkg)
// return (*List)(unsafe.Pointer(self))
cb.Typ(retType).
Typ(p.unsafePointer()).Val(recv).
Typ(p.unsafePointer()).Val(baseRecv).
Call(1).
Call(1).
Return(1).End()
Expand Down
27 changes: 15 additions & 12 deletions cl/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func (p *pkgCtx) typeName(cName string, _ bool) string {
}
name := rmPrefix(cName, p.nsPrefix)
name, underscoreStart := rmPrefixAndUnderscoreStart(name, p.typePrefix)
name = rmSuffix(name, p.typeSuffix)
name = cutSuffixes(name, p.typeSuffix)
return p.cstyleToGo(name, underscoreStart, true)
}

Expand Down Expand Up @@ -303,10 +303,10 @@ func (p *pkgCtx) funcName(cName string, order int, typName, typCName string, glo
panic(fmt.Errorf("invalid TypeAbbr for %q: %v", typName, v))
}
} else {
typName = rmSuffix(typName, p.typeAbbrSuffix)
typName = cutSuffixes(typName, p.typeAbbrSuffix)
typSuffix = []string{typName}
}
name = rmSuffix(name, typSuffix)
name = cutSuffixes(name, typSuffix)
name = cutMethodPrefix(name, typName)
}
}
Expand Down Expand Up @@ -408,6 +408,18 @@ func cutPrefix(name, prefix string) string {
return name
}

func cutSuffixes(name string, suffix []string) string {
for _, sfx := range suffix {
if strings.HasSuffix(name, sfx) {
if name == sfx {
return name
}
return name[:len(name)-len(sfx)]
}
}
return name
}

func rmPrefixAndUnderscoreStart(cName string, prefix []string) (name string, underscoreStart bool) {
name, underscoreStart = checkUnderscoreStart(cName)
name = rmPrefix(name, prefix)
Expand All @@ -432,15 +444,6 @@ func rmPrefix(name string, prefix []string) string {
return name
}

func rmSuffix(name string, suffix []string) string {
for _, sfx := range suffix {
if strings.HasSuffix(name, sfx) {
return name[:len(name)-len(sfx)]
}
}
return name
}

func contains(v string, names []string) bool {
for _, name := range names {
if name == v {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ go 1.27.0 // llgo 1.0

require (
github.com/goplus/gogen v1.26.0
github.com/goplus/lib v0.5.11
github.com/goplus/lib v0.5.12
github.com/goplus/mod v0.22.1
github.com/llarhub/clang-c v0.7.0
github.com/llarhub/clang-c v0.8.0
github.com/qiniu/x v1.19.1
)

Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
github.com/goplus/gogen v1.26.0 h1:A0s5CyFrOKuBy1NaN6apnxNmOoxciYbI81eBzyfYdjg=
github.com/goplus/gogen v1.26.0/go.mod h1:Y7ulYW3wonQ3d9er00b0uGFEV/IUZa6okWJZh892ACQ=
github.com/goplus/lib v0.5.11 h1:1KZ6pLcc4Ef6EZVk4A/LtKIi1/zQ4g9f+TEdHkdpfhg=
github.com/goplus/lib v0.5.11/go.mod h1:t4Cgs4QYbPzpI1RnYRA0iXWTgUFUuE09TrE2PvuU0VM=
github.com/goplus/lib v0.5.12 h1:LZ31xPTvuchVI1lqsKlCRHJIzqSFhXDjWTA1pLoaWb0=
github.com/goplus/lib v0.5.12/go.mod h1:Ucw7fZlTqrcuHgUvZHP8Oce6IwxULgASlzJxLt8E4p8=
github.com/goplus/mod v0.22.1 h1:3GQZqKlcQAiMpRUDAHvFryGYie+mT0JMjguEqOtIjkE=
github.com/goplus/mod v0.22.1/go.mod h1:APrczG2FtFcQelU4vTq9xw+GrVs4sPPKnRfGidWBlXY=
github.com/llarhub/clang-c v0.7.0 h1:YkO1FR1R1ScKJijF1PUVIpHnIC4uNgsSg/7CLzEBpFE=
github.com/llarhub/clang-c v0.7.0/go.mod h1:sZhzXACY6REPetdPrGWcSvRmVkl9RgMHbGWHGeOTchI=
github.com/llarhub/clang-c v0.8.0 h1:2JPW4ZiaYPwdI9G7LIiBAaFfI1fLzsJlmJe/3eWtdtQ=
github.com/llarhub/clang-c v0.8.0/go.mod h1:os8efa2pPUOT8LmMprrndHWqlaFffYhxYwtxpH5SBR4=
github.com/qiniu/x v1.19.1 h1:Z6ZALlHMrZw/AImYjYnYKMXemasVdUF2o7riwm9TZTs=
github.com/qiniu/x v1.19.1/go.mod h1:Sx3Wy+0GI9OsX4a53mYj6A0o7mHJ94PUvraqGYb4EIs=
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
Expand Down
38 changes: 37 additions & 1 deletion tool/_testc/python-3.14.8/include/pythread.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ typedef enum Py_Lock_Status {

typedef struct _Py_tss_t Py_tss_t; /* opaque */

typedef struct PyObject {} PyObject;
struct _object {};

typedef struct _object PyObject;

// Test if an object is the True singleton, the same as "x is True" in Python.
PyAPI_FUNC(int) Py_IsTrue(PyObject *x);
Expand All @@ -29,6 +31,40 @@ PyAPI_FUNC(void) PyList_SetItem(PyObject *x, PyObject *index, PyObject *value);

PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *x, PyObject *index);

PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name);

typedef enum {
/* PyMem_RawMalloc(), PyMem_RawRealloc() and PyMem_RawFree() */
PYMEM_DOMAIN_RAW,

/* PyMem_Malloc(), PyMem_Realloc() and PyMem_Free() */
PYMEM_DOMAIN_MEM,

/* PyObject_Malloc(), PyObject_Realloc() and PyObject_Free() */
PYMEM_DOMAIN_OBJ
} PyMemAllocatorDomain;

typedef struct {
/* user context passed as the first argument to the 4 functions */
void *ctx;

/* allocate a memory block */
void* (*malloc) (void *ctx, size_t size);

/* allocate a memory block initialized by zeros */
void* (*calloc) (void *ctx, size_t nelem, size_t elsize);

/* allocate or resize a memory block */
void* (*realloc) (void *ctx, void *ptr, size_t new_size);

/* release a memory block */
void (*free) (void *ctx, void *ptr);
} PyMemAllocatorEx;

/* Get the memory block allocator of the specified domain. */
PyAPI_FUNC(void) PyMem_GetAllocator(PyMemAllocatorDomain domain,
PyMemAllocatorEx *allocator);

#ifndef Py_LIMITED_API
# define Py_CPYTHON_PYTHREAD_H
# include "cpython/pythread.h"
Expand Down
3 changes: 2 additions & 1 deletion tool/_testc/python-3.14.8/llcppg.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"Files": ["Python.h"],
"Deps": [],
"Class": [],
"NonClass": [],
"NonClass": ["Import"],
"TypePrefix": ["Py_", "Py"],
"MethodCheck": ["Py*_: $1"],
"FuncPrefix": ["Py_", "Py"],
Expand All @@ -16,6 +16,7 @@
"TypeAbbr": {
},
"Rename": {
"PyImport_Import": "Import"
},
"MacroIgnore": [],
"TypeIgnore": [],
Expand Down
Loading
Loading