From a26330993732e1725bb9ac84aebfc199a870ac7f Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 17 Sep 2026 00:46:23 +0800 Subject: [PATCH 1/5] cl pkgCtx.types: c/c++ fullName => types.Type --- cl/_testcpp/ctor_dtor/out.go | 5 +-- cl/_testcpp/namespace/out.go | 9 ++--- cl/class.go | 28 +++++++------- cl/compile.go | 71 +++++++++++++++++------------------- cl/ctx.go | 2 + cl/func.go | 2 +- cl/type_and_var.go | 2 + cmd/llcppdump/cppdump.go | 11 +++++- 8 files changed, 70 insertions(+), 60 deletions(-) diff --git a/cl/_testcpp/ctor_dtor/out.go b/cl/_testcpp/ctor_dtor/out.go index 2c095389..271bd5f2 100644 --- a/cl/_testcpp/ctor_dtor/out.go +++ b/cl/_testcpp/ctor_dtor/out.go @@ -10,14 +10,13 @@ const ( type Base struct { } +type Bar struct { +} // llgo:link (*Base).XGo_Dtor C._ZN4baseD1Ev func (this *Base) XGo_Dtor() { } -type Bar struct { -} - // llgo:link (*Bar).XGo_Ctor__1 C._ZN3barC1Ei func (this *Bar) XGo_Ctor__1(a c.Int) { } diff --git a/cl/_testcpp/namespace/out.go b/cl/_testcpp/namespace/out.go index 02ecf355..9f59a3ac 100644 --- a/cl/_testcpp/namespace/out.go +++ b/cl/_testcpp/namespace/out.go @@ -7,17 +7,16 @@ import ( const XGoPackage = true +type Bar_base struct { +} +type Bar_boolean = c.Char + //go:linkname Bar_detail_f__1 C._ZN3bar6detail1fEi func Bar_detail_f__1(a c.Int) c.Uint //go:linkname Bar_detail_f__0 C._ZN3bar6detail1fEv func Bar_detail_f__0() -type Bar_base struct { -} - // llgo:link (*Bar_base).XGo_Dtor C._ZN3bar4baseD1Ev func (this *Bar_base) XGo_Dtor() { } - -type Bar_boolean = c.Char diff --git a/cl/class.go b/cl/class.go index 5d718f9c..9a18d801 100644 --- a/cl/class.go +++ b/cl/class.go @@ -20,6 +20,7 @@ import ( "go/types" "log" + "github.com/goplus/gogen" "github.com/goplus/llcppg/clang" lc "github.com/goplus/llcppg/lib/clang" ) @@ -36,28 +37,17 @@ type classMethod struct { type classCtx struct { scopeCtx decl clang.Cursor - typNamed *types.Named + typDecl *gogen.TypeDecl fields []*types.Var publicMethods []*classMethod inPublic bool } func compileClass(ctx *pkgCtx, scope *classCtx, cls clang.Cursor, ns string) { - origName := ns + clang.String(cls) - if debugCompileDecl { - log.Println("class", origName) - } pkg := ctx.pkg - pkgTypes := pkg.Types - clsName, rewritten := ctx.getPubName(origName, -1) - typDecl := pkg.NewTypeDefs().NewType(clsName, goNode(ctx, cls)) typStruc := types.NewStruct(scope.fields, nil) - typNamed := typDecl.InitType(pkg, typStruc) - if rewritten { - substObj(pkgTypes, pkgTypes.Scope(), origName, typNamed.Obj()) - } + scope.typDecl.InitType(pkg, typStruc) scope.reorder() - scope.typNamed = typNamed for _, method := range scope.publicMethods { obj := method.obj if decl := method.outsideDecl; decl.Kind != 0 { @@ -71,8 +61,20 @@ func compileClass(ctx *pkgCtx, scope *classCtx, cls clang.Cursor, ns string) { func loadClass(ctx *pkgCtx, cls clang.Cursor, ns string, defaultInPublic bool) { pkg := ctx.pkg pkgTypes := pkg.Types + origName := ns + clang.String(cls) + if debugCompileDecl { + log.Println("class", origName) + } + clsName, rewritten := ctx.getPubName(origName, -1) + typDecl := pkg.NewTypeDefs().NewType(clsName, goNode(ctx, cls)) + typNamed := typDecl.Type() + if rewritten { + substObj(pkgTypes, pkgTypes.Scope(), origName, typNamed.Obj()) + } + ctx.types[clang.String(cls.Type())] = typNamed scope := &classCtx{ decl: cls, + typDecl: typDecl, overloads: make(map[string]*overloads), inPublic: defaultInPublic, } diff --git a/cl/compile.go b/cl/compile.go index f9d836e4..48d62d4f 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -130,10 +130,10 @@ func NewPackage(pkgPath, pkgName string, conf *Config, tu clang.TranslationUnit, nameLookup = defaultNameLookup } ctx := &pkgCtx{ - pkg: pkg, cb: pkg.CB(), llgo: llgo, fset: pkg.Fset, tu: tu, - c: c, lang: conf.Language, cflags: conf.CFlags, - wrapFileHeader: conf.WrapFileHeader, nameLookup: nameLookup, + pkg: pkg, cb: pkg.CB(), llgo: llgo, fset: pkg.Fset, tu: tu, c: c, lang: conf.Language, + cflags: conf.CFlags, wrapFileHeader: conf.WrapFileHeader, nameLookup: nameLookup, methods: make(map[string]*classMethod), macroVals: make(map[string]any), + types: make(map[string]types.Type), } ctx.initFiles(files) loadFiles(ctx) @@ -209,44 +209,41 @@ func loadMacro(ctx *pkgCtx, decl clang.Cursor) { if decl.IsMacroFunctionLike() != 0 { return } - ctx.compiles = append(ctx.compiles, func(ctx *pkgCtx) { - origName := clang.String(decl) - tokens, dispose := ctx.tu.Tokenize(decl.Extent()) - defer dispose() - if debugCompileDecl { - log.Println("macro", origName, "-", len(tokens), "tokens") - } - if len(tokens) > 1 { - if v, ok := evalConstExpr(ctx, tokens[1:]); ok { - pkg := ctx.pkg - pkgTypes := pkg.Types - ctx.macroVals[origName] = v - name, _ := ctx.getPubName(origName, -1) - pkg.NewConstDefs(pkgTypes.Scope()).New(func(cb *gogen.CodeBuilder) int { - cb.Val(v) - return 1 - }, 0, token.NoPos, nil, name) - } + origName := clang.String(decl) + tokens, dispose := ctx.tu.Tokenize(decl.Extent()) + defer dispose() + if debugCompileDecl { + log.Println("macro", origName, "-", len(tokens), "tokens") + } + if len(tokens) > 1 { + if v, ok := evalConstExpr(ctx, tokens[1:]); ok { + pkg := ctx.pkg + pkgTypes := pkg.Types + ctx.macroVals[origName] = v + name, _ := ctx.getPubName(origName, -1) + pkg.NewConstDefs(pkgTypes.Scope()).New(func(cb *gogen.CodeBuilder) int { + cb.Val(v) + return 1 + }, 0, token.NoPos, nil, name) } - }) + } } func loadTypedef(ctx *pkgCtx, decl clang.Cursor, ns string) { - ctx.compiles = append(ctx.compiles, func(ctx *pkgCtx) { - origName := ns + clang.String(decl) - pkg := ctx.pkg - pkgTypes := pkg.Types - underlying := decl.TypedefDeclUnderlyingType() - if debugCompileDecl { - log.Println("typedef", origName, "-", clang.String(underlying)) - } - tunder := toType(ctx, pkgTypes, underlying, flagIsTypedef) - name, rewritten := ctx.getPubName(origName, -1) - t := pkg.NewTypeDefs().AliasType(name, tunder) - if rewritten { - pkgTypes.Scope().Insert(types.NewTypeName(token.NoPos, pkgTypes, origName, t)) - } - }) + pkg := ctx.pkg + pkgTypes := pkg.Types + origName := ns + clang.String(decl) + underlying := decl.TypedefDeclUnderlyingType() + if debugCompileDecl { + log.Println("typedef", origName, "-", clang.String(underlying)) + } + tunder := toType(ctx, pkgTypes, underlying, flagIsTypedef) + name, rewritten := ctx.getPubName(origName, -1) + t := pkg.NewTypeDefs().AliasType(name, tunder) + if rewritten { + pkgTypes.Scope().Insert(types.NewTypeName(token.NoPos, pkgTypes, origName, t)) + } + ctx.types[clang.String(decl.Type())] = t } // ----------------------------------------------------------------------------- diff --git a/cl/ctx.go b/cl/ctx.go index 99b9c78d..c8ae699a 100644 --- a/cl/ctx.go +++ b/cl/ctx.go @@ -19,6 +19,7 @@ package cl import ( "go/ast" "go/token" + "go/types" "log" "sort" "strconv" @@ -109,6 +110,7 @@ type pkgCtx struct { macroVals map[string]any // macroName => value methods map[string]*classMethod // manglingName => class + types map[string]types.Type // c/c++ fullName => types.Type compiles []compileFunc diff --git a/cl/func.go b/cl/func.go index 97a05c27..221aad2d 100644 --- a/cl/func.go +++ b/cl/func.go @@ -68,7 +68,7 @@ func compileFuncOrMethod(ctx *pkgCtx, fn clang.Cursor, obj *object, cls *classCt if cls == nil { nameInPkg = fnName } else { - typNamed := cls.typNamed + typNamed := cls.typDecl.Type() nameInPkg = "(*" + typNamed.Obj().Name() + ")." + fnName recv = types.NewParam(token.NoPos, pkgTypes, "this", types.NewPointer(typNamed)) } diff --git a/cl/type_and_var.go b/cl/type_and_var.go index 69189fc9..33f25929 100644 --- a/cl/type_and_var.go +++ b/cl/type_and_var.go @@ -74,6 +74,8 @@ func toType(ctx *pkgCtx, pkg *types.Package, typ lc.Type, flags int) types.Type return newPointer(pointee) case lc.TypeVoid: return tyVoid + case lc.TypeElaborated: + typ.NamedType() case lc.TypeFunctionProto: return toFuncType(ctx, pkg, typ) default: diff --git a/cmd/llcppdump/cppdump.go b/cmd/llcppdump/cppdump.go index be44812c..a2ae1d1c 100644 --- a/cmd/llcppdump/cppdump.go +++ b/cmd/llcppdump/cppdump.go @@ -40,7 +40,7 @@ func dump(node clang.Cursor, ns, presumedFile string) { return clang.Continue } name := ns + clang.String(cur) - log.Println("==>", kind, clang.String(kind), name) + log.Println("==>", kind, clang.String(kind), name, typeOf(cur.Type())) switch kind { case lc.CursorFunctionDecl, lc.CursorCXXMethod, lc.CursorConstructor, lc.CursorDestructor: case lc.CursorClassDecl, lc.CursorNamespace: @@ -50,6 +50,15 @@ func dump(node clang.Cursor, ns, presumedFile string) { }) } +func typeOf(t lc.Type) string { + switch t.Kind { + case lc.TypeElaborated: + return typeOf(t.NamedType()) + default: + return clang.String(t) + } +} + func main() { if len(os.Args) < 2 { fmt.Println("usage: llcppdump []") From 1a0871ed2ef7be829e7c4086c8dbf7ad708ba9ac Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 17 Sep 2026 00:49:30 +0800 Subject: [PATCH 2/5] cl toType: support TypeElaborated --- cl/_testcpp/class_with_ns/in.h | 13 +++++++++++++ cl/_testcpp/class_with_ns/out.go | 11 +++++++++++ cl/type_and_var.go | 5 ++++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 cl/_testcpp/class_with_ns/in.h create mode 100644 cl/_testcpp/class_with_ns/out.go diff --git a/cl/_testcpp/class_with_ns/in.h b/cl/_testcpp/class_with_ns/in.h new file mode 100644 index 00000000..a0876b6a --- /dev/null +++ b/cl/_testcpp/class_with_ns/in.h @@ -0,0 +1,13 @@ +namespace bar { + class base + { + public: + ~base(); + }; + + class derived + { + private: + base b; + } +} diff --git a/cl/_testcpp/class_with_ns/out.go b/cl/_testcpp/class_with_ns/out.go new file mode 100644 index 00000000..4613680d --- /dev/null +++ b/cl/_testcpp/class_with_ns/out.go @@ -0,0 +1,11 @@ +package foo + +type Bar_base struct { +} +type Bar_derived struct { + b Bar_base +} + +// llgo:link (*Bar_base).XGo_Dtor C._ZN3bar4baseD1Ev +func (this *Bar_base) XGo_Dtor() { +} diff --git a/cl/type_and_var.go b/cl/type_and_var.go index 33f25929..718bb171 100644 --- a/cl/type_and_var.go +++ b/cl/type_and_var.go @@ -75,7 +75,10 @@ func toType(ctx *pkgCtx, pkg *types.Package, typ lc.Type, flags int) types.Type case lc.TypeVoid: return tyVoid case lc.TypeElaborated: - typ.NamedType() + cName := clang.String(typ.NamedType()) + if t, ok := ctx.types[cName]; ok { + return t + } case lc.TypeFunctionProto: return toFuncType(ctx, pkg, typ) default: From 3949609ebe98ded1bce8dacfd85a9ce15e87fc0e Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 17 Sep 2026 01:04:41 +0800 Subject: [PATCH 3/5] cl loadClass: init fields before compileClass --- cl/_testcpp/class_with_ns/in.h | 2 +- cl/class.go | 14 ++++++-------- cl/func.go | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cl/_testcpp/class_with_ns/in.h b/cl/_testcpp/class_with_ns/in.h index a0876b6a..d2265a45 100644 --- a/cl/_testcpp/class_with_ns/in.h +++ b/cl/_testcpp/class_with_ns/in.h @@ -9,5 +9,5 @@ namespace bar { { private: base b; - } + }; } diff --git a/cl/class.go b/cl/class.go index 9a18d801..f8f75a29 100644 --- a/cl/class.go +++ b/cl/class.go @@ -20,7 +20,6 @@ import ( "go/types" "log" - "github.com/goplus/gogen" "github.com/goplus/llcppg/clang" lc "github.com/goplus/llcppg/lib/clang" ) @@ -37,16 +36,13 @@ type classMethod struct { type classCtx struct { scopeCtx decl clang.Cursor - typDecl *gogen.TypeDecl + typNamed *types.Named fields []*types.Var publicMethods []*classMethod inPublic bool } -func compileClass(ctx *pkgCtx, scope *classCtx, cls clang.Cursor, ns string) { - pkg := ctx.pkg - typStruc := types.NewStruct(scope.fields, nil) - scope.typDecl.InitType(pkg, typStruc) +func compileClass(ctx *pkgCtx, scope *classCtx) { scope.reorder() for _, method := range scope.publicMethods { obj := method.obj @@ -74,7 +70,7 @@ func loadClass(ctx *pkgCtx, cls clang.Cursor, ns string, defaultInPublic bool) { ctx.types[clang.String(cls.Type())] = typNamed scope := &classCtx{ decl: cls, - typDecl: typDecl, + typNamed: typNamed, overloads: make(map[string]*overloads), inPublic: defaultInPublic, } @@ -82,8 +78,10 @@ func loadClass(ctx *pkgCtx, cls clang.Cursor, ns string, defaultInPublic bool) { loadClassMember(ctx, pkgTypes, scope, decl) return clang.Continue }) + typStruc := types.NewStruct(scope.fields, nil) + typDecl.InitType(pkg, typStruc) ctx.compiles = append(ctx.compiles, func(ctx *pkgCtx) { - compileClass(ctx, scope, cls, ns) + compileClass(ctx, scope) }) } diff --git a/cl/func.go b/cl/func.go index 221aad2d..97a05c27 100644 --- a/cl/func.go +++ b/cl/func.go @@ -68,7 +68,7 @@ func compileFuncOrMethod(ctx *pkgCtx, fn clang.Cursor, obj *object, cls *classCt if cls == nil { nameInPkg = fnName } else { - typNamed := cls.typDecl.Type() + typNamed := cls.typNamed nameInPkg = "(*" + typNamed.Obj().Name() + ")." + fnName recv = types.NewParam(token.NoPos, pkgTypes, "this", types.NewPointer(typNamed)) } From 1d58939acb79d917b744276a9ef1fcded2f6280e Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 17 Sep 2026 18:52:44 +0800 Subject: [PATCH 4/5] ci: llgo v1.0.3 --- .github/workflows/llgo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/llgo.yml b/.github/workflows/llgo.yml index 926cb25e..fb4aaccd 100644 --- a/.github/workflows/llgo.yml +++ b/.github/workflows/llgo.yml @@ -24,7 +24,7 @@ jobs: - macos-latest - ubuntu-latest llvm: [22] - llgo: [v1.0.2] + llgo: [v1.0.3] go: [1.27] fail-fast: false runs-on: ${{matrix.os}} From c69bc6f6f00d9c20dc6d87660a8d702f3c3e16d6 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 17 Sep 2026 19:17:51 +0800 Subject: [PATCH 5/5] cl toType: support TypeRecord --- cl/type_and_var.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cl/type_and_var.go b/cl/type_and_var.go index 718bb171..1065fd92 100644 --- a/cl/type_and_var.go +++ b/cl/type_and_var.go @@ -74,6 +74,12 @@ func toType(ctx *pkgCtx, pkg *types.Package, typ lc.Type, flags int) types.Type return newPointer(pointee) case lc.TypeVoid: return tyVoid + case lc.TypeRecord: + cName := fullTypeName(typ) + log.Println("==> fullTypeName:", cName) + if t, ok := ctx.types[cName]; ok { + return t + } case lc.TypeElaborated: cName := clang.String(typ.NamedType()) if t, ok := ctx.types[cName]; ok { @@ -87,6 +93,20 @@ func toType(ctx *pkgCtx, pkg *types.Package, typ lc.Type, flags int) types.Type panic("todo: toType " + clang.String(typ)) } +func fullTypeName(typ lc.Type) string { + decl := typ.TypeDeclaration() + name := clang.String(decl) + for { + parent := decl.SemanticParent() + if kind := parent.Kind; kind != lc.CursorNamespace && kind != lc.CursorLastDecl { + break + } + name = clang.String(parent) + "::" + name + decl = parent + } + return name +} + func toFuncType(ctx *pkgCtx, pkg *types.Package, fn lc.Type) *types.Signature { params, variadic := toFuncParams(ctx, pkg, fn) results := toFuncResults(ctx, pkg, fn.ResultType())