From 27027fb29e8f05434e632c8976d8480dc46d515c Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 10 Sep 2026 04:44:52 +0800 Subject: [PATCH 1/3] cl: compileGlobalDecls --- .gitignore | 1 + cl/blockctx.go | 33 ++++++ cl/compile.go | 271 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 cl/blockctx.go diff --git a/.gitignore b/.gitignore index 52681010..94e38187 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ *.DS_Store _temp/ +_c2go/ *.log # Test binary, built with `go test -c` diff --git a/cl/blockctx.go b/cl/blockctx.go new file mode 100644 index 00000000..2e099380 --- /dev/null +++ b/cl/blockctx.go @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cl + +import ( + "go/token" + + "github.com/goplus/gogen" +) + +// ----------------------------------------------------------------------------- + +type blockCtx struct { + pkg *gogen.Package + cb *gogen.CodeBuilder + fset *token.FileSet +} + +// ----------------------------------------------------------------------------- diff --git a/cl/compile.go b/cl/compile.go index e359794f..a050e9d5 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -17,13 +17,47 @@ package cl import ( + "go/ast" + "go/token" "go/types" + "log" "github.com/goplus/gogen" "github.com/goplus/lib/c" "github.com/goplus/llcppg/clang" + lc "github.com/goplus/llcppg/lib/clang" ) +const ( + DbgFlagCompileDecl = 1 << iota + DbgFlagLoadDeps + DbgFlagAll = DbgFlagCompileDecl | DbgFlagLoadDeps +) + +var ( + debugCompileDecl bool + debugLoadDeps bool +) + +func SetDebug(flags int) { + debugCompileDecl = (flags & DbgFlagCompileDecl) != 0 + debugLoadDeps = (flags & DbgFlagLoadDeps) != 0 +} + +// ----------------------------------------------------------------------------- + +type nodeInterp struct { + fset *token.FileSet +} + +func (p *nodeInterp) Position(start token.Pos) token.Position { + return p.fset.Position(start) +} + +func (p *nodeInterp) LoadExpr(v ast.Node) string { + panic("todo") +} + // ----------------------------------------------------------------------------- type PkgInfo struct { @@ -44,6 +78,10 @@ type Reused struct { // Config specifies the configuration for compiling C/C++ header files. type Config struct { + // Fset provides source position information for syntax trees and types. + // If Fset is nil, Load will use a new fileset, but preserve Fset's value. + Fset *token.FileSet + // An Importer resolves import paths to Packages. Importer types.Importer @@ -64,10 +102,241 @@ type Source struct { // ----------------------------------------------------------------------------- +const ( + headerGoFile = "llcppg_header.i.go" +) + // NewPackage creates a new Package instance for the specified package path and name, using // the provided Source and Config. -func NewPackage(pkgPath, pkgName string, src Source, conf *Config) (pkg Package, err error) { +func NewPackage(pkgPath, pkgName string, file Source, conf *Config) (pkg Package, err error) { + if reused := conf.Reused; reused != nil && reused.pkg.Package != nil { + pkg = reused.pkg + } else { + interp := &nodeInterp{} + confGox := &gogen.Config{ + Fset: conf.Fset, + Importer: conf.Importer, + LoadNamed: nil, + HandleErr: nil, + NewBuiltin: nil, + NodeInterpreter: interp, + CanImplicitCast: implicitCast, + DefaultGoFile: headerGoFile, + } + pkg.Package = gogen.NewPackage(pkgPath, pkgName, confGox) + interp.fset = pkg.Fset + } + pkg.SetRedeclarable(true) + pkg.pi, err = loadFile(pkg.Package, conf, file) + return +} + +func implicitCast(pkg *gogen.Package, V, T types.Type, pv *gogen.Element) bool { panic("todo") } // ----------------------------------------------------------------------------- + +func loadFile(p *gogen.Package, conf *Config, file Source) (pi *PkgInfo, err error) { + ctx := &blockCtx{ + pkg: p, cb: p.CB(), fset: p.Fset, + } + _ = conf + compileGlobalDecls(ctx, file.Cursor()) + return +} + +func compileGlobalDecls(ctx *blockCtx, node clang.Cursor) { + clang.VisitChildren(node, func(decl, parent clang.Cursor) clang.ChildVisitResult { + /* if global { + ctx.logFile(decl) + if decl.IsImplicit || ctx.inDepPkg { + continue + } + } */ + switch decl.Kind { + case lc.CursorFunctionDecl: + compileFunc(ctx, decl) + case lc.CursorVarDecl: + // compileVarDecl(ctx, decl, global) + case lc.CursorTypedefDecl: + /* origName, pub := decl.Name, false + if global { + pub = ctx.getPubName(&decl.Name) + } + compileTypedef(ctx, decl, global, pub) + if pub { + substObj(ctx.pkg.Types, scope, origName, scope.Lookup(decl.Name)) + } + case ast.RecordDecl: + pub := false + name, suKind := ctx.getSuName(decl, decl.TagUsed) + origName := name + if global { + if suKind == suAnonymous { + // pub = true if this is a public typedef + pub = i+1 < n && isPubTypedef(ctx, node.Inner[i+1]) + } else { + pub = ctx.getPubName(&name) + if decl.CompleteDefinition && ctx.checkExists(name) { + continue + } + } + } + typ, del := compileStructOrUnion(ctx, name, decl, pub) + if suKind != suAnonymous { + if pub { + substObj(ctx.pkg.Types, scope, origName, scope.Lookup(name)) + } + break + } + ctx.unnameds[decl.ID] = unnamedType{typ: typ, del: del} + for i+1 < n { + next := node.Inner[i+1] + if next.Kind == ast.VarDecl { + if ret, ok := checkAnonymous(ctx, scope, typ, next); ok { + compileVarWith(ctx, ret, next) + i++ + continue + } + } + break + } + case ast.EmptyDecl: + case ast.StaticAssertDecl: + continue + */ + case lc.CursorEnumDecl: + // compileEnum(ctx, decl, global) + default: + log.Panicln("compileDeclStmt: unknown kind =", decl.Kind) + } + return clang.Continue + }) +} + +func compileFunc(_ *blockCtx, fn clang.Cursor) { + fnName := clang.String(fn) + fnType := fn.Type() + if debugCompileDecl { + log.Println("func", fnName, "-", clang.String(fnType)) + } + /* var hasName bool + var params []*types.Var + var body *ast.Node + var results *types.Tuple + for _, item := range fn.Inner { + switch item.Kind { + case ast.ParmVarDecl: + if debugCompileDecl { + log.Println(" => param", item.Name, "-", item.Type.QualType) + } + if item.Name != "" { + hasName = true + } + params = append(params, newParam(ctx, item)) + case ast.CompoundStmt: + body = item + case ast.BuiltinAttr, ast.FormatAttr, ast.AsmLabelAttr, ast.AvailabilityAttr, ast.ColdAttr, ast.DeprecatedAttr, + ast.AlwaysInlineAttr, ast.WarnUnusedResultAttr, ast.NoThrowAttr, ast.NoInlineAttr, ast.AllocSizeAttr, + ast.NonNullAttr, ast.ConstAttr, ast.PureAttr, ast.GNUInlineAttr, ast.ReturnsTwiceAttr, ast.NoSanitizeAttr, + ast.RestrictAttr, ast.MSAllocatorAttr, ast.VisibilityAttr, ast.C11NoReturnAttr, ast.StrictFPAttr, + ast.AllocAlignAttr, ast.DisableTailCallsAttr, ast.FormatArgAttr: + default: + log.Panicln("compileFunc: unknown kind =", item.Kind) + } + } + variadic := fn.Variadic + if variadic { + params = append(params, newVariadicParam(ctx, hasName)) + } + pkg := ctx.pkg + if tyRet := toType(ctx, fnType, parser.FlagGetRetType); ctypes.NotVoid(tyRet) { + results = types.NewTuple(pkg.NewParam(token.NoPos, "", tyRet)) + } + sig := gogen.NewCSignature(types.NewTuple(params...), results, variadic) + origName, rewritten := fnName, false + if !ctx.inHeader && fn.StorageClass == ast.Static { + fnName, rewritten = ctx.autoStaticName(origName), true + } else { + rewritten = ctx.getPubName(&fnName) + } + if body != nil { + if ctx.checkExists(fnName) { + return + } + isMain := false + if fnName == "main" && (results != nil || params != nil) { + fnName, isMain = "_cgo_main", true + } + f, err := pkg.NewFuncWith(ctx.goNodePos(fn), fnName, sig, nil) + if err != nil { + log.Panicln("compileFunc:", err) + } + if rewritten { // for fnName is a recursive function + scope := pkg.Types.Scope() + substObj(pkg.Types, scope, origName, f.Obj()) + rewritten = false + } + cb := f.BodyStart(pkg) + ctx.curfn = newFuncCtx(pkg, ctx.markComplicated(fnName, body), origName) + compileSub(ctx, body) + checkNeedReturn(ctx, body) + ctx.curfn = nil + cb.End() + if isMain { + var t *types.Var + var entryParams *types.Tuple + var entry = "main" + var testMain = ctx.testMain + if testMain { + entry = "TestMain" + testing := pkg.Import("testing") + t = pkg.NewParam(token.NoPos, "t", types.NewPointer(testing.Ref("T").Type())) + entryParams = types.NewTuple(t) + } + pkg.NewFunc(nil, entry, entryParams, nil, false).BodyStart(pkg) + if results != nil { + if testMain { + // if _cgo_ret := _cgo_main(); _cgo_ret != 0 { + // t.Fatal("exit status", _cgo_ret) + // } + cb.If().DefineVarStart(token.NoPos, retName) + } else { + // os.Exit(int(_cgo_main())) + cb.Val(pkg.Import("os").Ref("Exit")).Typ(types.Typ[types.Int]) + } + } + cb.Val(f.Obj()) + if params != nil { + panic("TODO: main func with params") + } + cb.Call(len(params)) + if results != nil { + if testMain { + cb.EndInit(1) + ret := cb.Scope().Lookup(retName) + cb.Val(ret).Val(0).BinaryOp(token.NEQ).Then(). + Val(t).MemberVal("Fatal").Val("exit status").Val(ret).Call(2).EndStmt(). + End() + } else { + cb.Call(1).Call(1) + } + } + cb.EndStmt().End() + } else { + delete(ctx.extfns, fnName) + } + } else if fn.IsUsed { + f := types.NewFunc(ctx.goNodePos(fn), pkg.Types, fnName, sig) + if pkg.Types.Scope().Insert(f) == nil { + ctx.addExternFunc(fnName) + } + } + if rewritten { + scope := pkg.Types.Scope() + substObj(pkg.Types, scope, origName, scope.Lookup(fnName)) + } */ +} + +// ----------------------------------------------------------------------------- From aeca904ded6cffe78c252c4a9db10266b548d23e Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 10 Sep 2026 06:26:43 +0800 Subject: [PATCH 2/3] cl: compileFunc --- cl/blockctx.go | 22 +++++ cl/compile.go | 196 +++++++++++++++++++++++---------------------- cl/type_and_var.go | 49 ++++++++++++ lib/clang/clang.go | 90 ++++++++++----------- 4 files changed, 218 insertions(+), 139 deletions(-) create mode 100644 cl/type_and_var.go diff --git a/cl/blockctx.go b/cl/blockctx.go index 2e099380..ba3fbc8e 100644 --- a/cl/blockctx.go +++ b/cl/blockctx.go @@ -20,6 +20,7 @@ import ( "go/token" "github.com/goplus/gogen" + "github.com/goplus/llcppg/clang" ) // ----------------------------------------------------------------------------- @@ -30,4 +31,25 @@ type blockCtx struct { fset *token.FileSet } +/* +func (ctx *blockCtx) goNode(v clang.Cursor) ast.Node { + if rg := v.Range; rg != nil && ctx.file != nil { + base := ctx.file.Base() + pos := token.Pos(int(rg.Begin.Offset) + base) + end := token.Pos(int(rg.End.Offset) + rg.End.TokLen + base) + return &node{pos: pos, end: end, ctx: ctx} + } + return nil +} +*/ + +func (ctx *blockCtx) goNodePos(v clang.Cursor) token.Pos { + /* if rg := v.Range; rg != nil && ctx.file != nil { + base := ctx.file.Base() + return token.Pos(int(rg.Begin.Offset) + base) + } + return token.NoPos */ + panic("todo") +} + // ----------------------------------------------------------------------------- diff --git a/cl/compile.go b/cl/compile.go index a050e9d5..41bbc1ec 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -21,6 +21,7 @@ import ( "go/token" "go/types" "log" + "strconv" "github.com/goplus/gogen" "github.com/goplus/lib/c" @@ -142,120 +143,104 @@ func loadFile(p *gogen.Package, conf *Config, file Source) (pi *PkgInfo, err err pkg: p, cb: p.CB(), fset: p.Fset, } _ = conf - compileGlobalDecls(ctx, file.Cursor()) + clang.VisitChildren(file.Cursor(), func(decl, parent clang.Cursor) clang.ChildVisitResult { + compileDecl(ctx, decl) + return clang.Continue + }) return } -func compileGlobalDecls(ctx *blockCtx, node clang.Cursor) { - clang.VisitChildren(node, func(decl, parent clang.Cursor) clang.ChildVisitResult { - /* if global { - ctx.logFile(decl) - if decl.IsImplicit || ctx.inDepPkg { - continue - } - } */ - switch decl.Kind { - case lc.CursorFunctionDecl: - compileFunc(ctx, decl) - case lc.CursorVarDecl: - // compileVarDecl(ctx, decl, global) - case lc.CursorTypedefDecl: - /* origName, pub := decl.Name, false - if global { - pub = ctx.getPubName(&decl.Name) - } - compileTypedef(ctx, decl, global, pub) - if pub { - substObj(ctx.pkg.Types, scope, origName, scope.Lookup(decl.Name)) - } - case ast.RecordDecl: - pub := false - name, suKind := ctx.getSuName(decl, decl.TagUsed) - origName := name +func compileDecl(ctx *blockCtx, decl clang.Cursor) { + /* if global { + ctx.logFile(decl) + if decl.IsImplicit || ctx.inDepPkg { + continue + } + } */ + switch decl.Kind { + case lc.CursorFunctionDecl: + compileFunc(ctx, decl) + case lc.CursorVarDecl: + // compileVarDecl(ctx, decl, global) + case lc.CursorTypedefDecl: + /* origName, pub := decl.Name, false if global { - if suKind == suAnonymous { - // pub = true if this is a public typedef - pub = i+1 < n && isPubTypedef(ctx, node.Inner[i+1]) - } else { - pub = ctx.getPubName(&name) - if decl.CompleteDefinition && ctx.checkExists(name) { - continue - } - } + pub = ctx.getPubName(&decl.Name) + } + compileTypedef(ctx, decl, global, pub) + if pub { + substObj(ctx.pkg.Types, scope, origName, scope.Lookup(decl.Name)) } - typ, del := compileStructOrUnion(ctx, name, decl, pub) - if suKind != suAnonymous { - if pub { - substObj(ctx.pkg.Types, scope, origName, scope.Lookup(name)) + case ast.RecordDecl: + pub := false + name, suKind := ctx.getSuName(decl, decl.TagUsed) + origName := name + if global { + if suKind == suAnonymous { + // pub = true if this is a public typedef + pub = i+1 < n && isPubTypedef(ctx, node.Inner[i+1]) + } else { + pub = ctx.getPubName(&name) + if decl.CompleteDefinition && ctx.checkExists(name) { + continue } - break } - ctx.unnameds[decl.ID] = unnamedType{typ: typ, del: del} - for i+1 < n { - next := node.Inner[i+1] - if next.Kind == ast.VarDecl { - if ret, ok := checkAnonymous(ctx, scope, typ, next); ok { - compileVarWith(ctx, ret, next) - i++ - continue - } + } + typ, del := compileStructOrUnion(ctx, name, decl, pub) + if suKind != suAnonymous { + if pub { + substObj(ctx.pkg.Types, scope, origName, scope.Lookup(name)) + } + break + } + ctx.unnameds[decl.ID] = unnamedType{typ: typ, del: del} + for i+1 < n { + next := node.Inner[i+1] + if next.Kind == ast.VarDecl { + if ret, ok := checkAnonymous(ctx, scope, typ, next); ok { + compileVarWith(ctx, ret, next) + i++ + continue } - break } - case ast.EmptyDecl: - case ast.StaticAssertDecl: - continue - */ - case lc.CursorEnumDecl: - // compileEnum(ctx, decl, global) - default: - log.Panicln("compileDeclStmt: unknown kind =", decl.Kind) - } - return clang.Continue - }) + break + } + case ast.EmptyDecl: + case ast.StaticAssertDecl: + continue + */ + case lc.CursorEnumDecl: + // compileEnum(ctx, decl, global) + default: + log.Panicln("compileDecl: unknown kind =", decl.Kind) + } } -func compileFunc(_ *blockCtx, fn clang.Cursor) { +func compileFunc(ctx *blockCtx, fn clang.Cursor) { fnName := clang.String(fn) - fnType := fn.Type() if debugCompileDecl { - log.Println("func", fnName, "-", clang.String(fnType)) + log.Println("func", fnName, "-", clang.String(fn.Type())) } - /* var hasName bool + n := fn.NumArguments() var params []*types.Var - var body *ast.Node var results *types.Tuple - for _, item := range fn.Inner { - switch item.Kind { - case ast.ParmVarDecl: - if debugCompileDecl { - log.Println(" => param", item.Name, "-", item.Type.QualType) - } - if item.Name != "" { - hasName = true - } - params = append(params, newParam(ctx, item)) - case ast.CompoundStmt: - body = item - case ast.BuiltinAttr, ast.FormatAttr, ast.AsmLabelAttr, ast.AvailabilityAttr, ast.ColdAttr, ast.DeprecatedAttr, - ast.AlwaysInlineAttr, ast.WarnUnusedResultAttr, ast.NoThrowAttr, ast.NoInlineAttr, ast.AllocSizeAttr, - ast.NonNullAttr, ast.ConstAttr, ast.PureAttr, ast.GNUInlineAttr, ast.ReturnsTwiceAttr, ast.NoSanitizeAttr, - ast.RestrictAttr, ast.MSAllocatorAttr, ast.VisibilityAttr, ast.C11NoReturnAttr, ast.StrictFPAttr, - ast.AllocAlignAttr, ast.DisableTailCallsAttr, ast.FormatArgAttr: - default: - log.Panicln("compileFunc: unknown kind =", item.Kind) - } + for i := range n { + item := fn.Argument(c.Uint(i)) + param := newParam(ctx, item, i) + params = append(params, param) } - variadic := fn.Variadic + variadic := fn.IsVariadic() != 0 if variadic { - params = append(params, newVariadicParam(ctx, hasName)) + params = append(params, newVariadicParam(ctx)) } pkg := ctx.pkg - if tyRet := toType(ctx, fnType, parser.FlagGetRetType); ctypes.NotVoid(tyRet) { - results = types.NewTuple(pkg.NewParam(token.NoPos, "", tyRet)) - } - sig := gogen.NewCSignature(types.NewTuple(params...), results, variadic) - origName, rewritten := fnName, false + retType := fn.ResultType() // TODO(xsw): return void + tyRet := toType(ctx, retType, flagRetType) + results = types.NewTuple(pkg.NewParam(token.NoPos, "", tyRet, false)) + // TODO(xsw): method + sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), results, variadic) + _ = sig + /* origName, rewritten := fnName, false if !ctx.inHeader && fn.StorageClass == ast.Static { fnName, rewritten = ctx.autoStaticName(origName), true } else { @@ -339,4 +324,27 @@ func compileFunc(_ *blockCtx, fn clang.Cursor) { } */ } +var ( + tyValist types.Type = types.NewSlice(gogen.TyAny) +) + +func newVariadicParam(ctx *blockCtx) *types.Var { + return types.NewParam(token.NoPos, ctx.pkg.Types, "__llgo_va_list", tyValist) +} + +func newParam(ctx *blockCtx, decl clang.Cursor, i c.Int) *types.Var { + declName := clang.String(decl) + declTyp := decl.Type() + if debugCompileDecl { + log.Println(" => param", declName, "-", clang.String(declTyp)) + } + typ := toType(ctx, declTyp, flagIsParam) + if declName != "" { + avoidKeyword(&declName) + } else { + declName = "__llcppg_param" + strconv.Itoa(int(i)+1) + } + return types.NewParam(ctx.goNodePos(decl), ctx.pkg.Types, declName, typ) +} + // ----------------------------------------------------------------------------- diff --git a/cl/type_and_var.go b/cl/type_and_var.go new file mode 100644 index 00000000..773cf681 --- /dev/null +++ b/cl/type_and_var.go @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cl + +import ( + "go/types" + + lc "github.com/goplus/llcppg/lib/clang" +) + +// ----------------------------------------------------------------------------- + +const ( + flagIsParam = 1 << iota + flagIsStructField + flagIsExtern + flagIsTypedef + flagRetType +) + +func toType(ctx *blockCtx, typ lc.Type, flags int) types.Type { + panic("todo") +} + +// ----------------------------------------------------------------------------- + +func avoidKeyword(name *string) { + switch *name { + case "map", "type", "range", "chan", "var", "func", "go", "select", + "defer", "package", "import", "interface", "fallthrough": + *name += "_" + } +} + +// ----------------------------------------------------------------------------- diff --git a/lib/clang/clang.go b/lib/clang/clang.go index 50715d50..1f11fa03 100644 --- a/lib/clang/clang.go +++ b/lib/clang/clang.go @@ -1890,7 +1890,7 @@ func (t Type) IsConstQualifiedType() (ret c.Uint) { } /** - * Determine whether a CXCursor that is a macro, is + * Determine whether a CXCursor that is a macro, is * function like. */ // llgo:link Cursor.IsMacroFunctionLike C.clang_Cursor_isMacroFunctionLike @@ -1899,7 +1899,7 @@ func (c Cursor) IsMacroFunctionLike() (ret c.Uint) { } /** - * Determine whether a CXCursor that is a macro, is a + * Determine whether a CXCursor that is a macro, is a * builtin one. */ // llgo:link Cursor.IsMacroBuiltin C.clang_Cursor_isMacroBuiltin @@ -1908,7 +1908,7 @@ func (c Cursor) IsMacroBuiltin() (ret c.Uint) { } /** - * Determine whether a CXCursor that is a function declaration, is an + * Determine whether a CXCursor that is a function declaration, is an * inline declaration. */ // llgo:link Cursor.IsFunctionInlined C.clang_Cursor_isFunctionInlined @@ -2274,74 +2274,75 @@ func (c Cursor) Mangling() (ret String) { /** * Determine if a C++ constructor is a converting constructor. */ -// llgo:link Cursor.IsConvertingConstructor C.clang_CXXConstructor_isConvertingConstructor -func (c Cursor) IsConvertingConstructor() (ret c.Uint) { +// llgo:link Cursor.CXXConstructorIsConvertingConstructor C.clang_CXXConstructor_isConvertingConstructor +func (c Cursor) CXXConstructorIsConvertingConstructor() (ret c.Uint) { return } /** * Determine if a C++ constructor is a copy constructor. */ -// llgo:link Cursor.IsCopyConstructor C.clang_CXXConstructor_isCopyConstructor -func (c Cursor) IsCopyConstructor() (ret c.Uint) { +// llgo:link Cursor.CXXConstructorIsCopyConstructor C.clang_CXXConstructor_isCopyConstructor +func (c Cursor) CXXConstructorIsCopyConstructor() (ret c.Uint) { return } /** * Determine if a C++ constructor is the default constructor. */ -// llgo:link Cursor.IsDefaultConstructor C.clang_CXXConstructor_isDefaultConstructor -func (c Cursor) IsDefaultConstructor() (ret c.Uint) { +// llgo:link Cursor.CXXConstructorIsDefaultConstructor C.clang_CXXConstructor_isDefaultConstructor +func (c Cursor) CXXConstructorIsDefaultConstructor() (ret c.Uint) { return } /** * Determine if a C++ constructor is a move constructor. */ -// llgo:link Cursor.IsMoveConstructor C.clang_CXXConstructor_isMoveConstructor -func (c Cursor) IsMoveConstructor() (ret c.Uint) { +// llgo:link Cursor.CXXConstructorIsMoveConstructor C.clang_CXXConstructor_isMoveConstructor +func (c Cursor) CXXConstructorIsMoveConstructor() (ret c.Uint) { return } /** - * Determine if a C++ field is declared 'mutable'. + * Determine if a C++ method is declared '= default'. */ -// llgo:link Cursor.IsMutable C.clang_CXXField_isMutable -func (c Cursor) IsMutable() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsDefaulted C.clang_CXXMethod_isDefaulted +func (c Cursor) CXXMethodIsDefaulted() (ret c.Uint) { return } /** - * Determine if a C++ method is declared '= default'. + * Determine if a C++ method is declared '= delete'. */ -// llgo:link Cursor.IsDefaulted C.clang_CXXMethod_isDefaulted -func (c Cursor) IsDefaulted() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsDeleted C.clang_CXXMethod_isDeleted +func (c Cursor) CXXMethodIsDeleted() (ret c.Uint) { return } /** - * Determine if a C++ method is declared '= delete'. + * Determine if a C++ member function or member function template is + * pure virtual. */ -// llgo:link Cursor.IsDeleted C.clang_CXXMethod_isDeleted -func (c Cursor) IsDeleted() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsPureVirtual C.clang_CXXMethod_isPureVirtual +func (c Cursor) CXXMethodIsPureVirtual() (ret c.Uint) { return } /** * Determine if a C++ member function or member function template is - * pure virtual. + * declared 'static'. */ -// llgo:link Cursor.IsPureVirtual C.clang_CXXMethod_isPureVirtual -func (c Cursor) IsPureVirtual() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsStatic C.clang_CXXMethod_isStatic +func (c Cursor) CXXMethodIsStatic() (ret c.Uint) { return } /** * Determine if a C++ member function or member function template is - * declared 'static'. + * declared 'const'. */ -// llgo:link Cursor.IsStatic C.clang_CXXMethod_isStatic -func (c Cursor) IsStatic() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsConst C.clang_CXXMethod_isConst +func (c Cursor) CXXMethodIsConst() (ret c.Uint) { return } @@ -2350,8 +2351,8 @@ func (c Cursor) IsStatic() (ret c.Uint) { * explicitly declared 'virtual' or if it overrides a virtual method from * one of the base classes. */ -// llgo:link Cursor.IsVirtual C.clang_CXXMethod_isVirtual -func (c Cursor) IsVirtual() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsVirtual C.clang_CXXMethod_isVirtual +func (c Cursor) CXXMethodIsVirtual() (ret c.Uint) { return } @@ -2378,8 +2379,8 @@ func (c Cursor) IsVirtual() (ret c.Uint) { * * Is not. */ -// llgo:link Cursor.IsCopyAssignmentOperator C.clang_CXXMethod_isCopyAssignmentOperator -func (c Cursor) IsCopyAssignmentOperator() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsCopyAssignmentOperator C.clang_CXXMethod_isCopyAssignmentOperator +func (c Cursor) CXXMethodIsCopyAssignmentOperator() (ret c.Uint) { return } @@ -2406,8 +2407,8 @@ func (c Cursor) IsCopyAssignmentOperator() (ret c.Uint) { * * Is not. */ -// llgo:link Cursor.IsMoveAssignmentOperator C.clang_CXXMethod_isMoveAssignmentOperator -func (c Cursor) IsMoveAssignmentOperator() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsMoveAssignmentOperator C.clang_CXXMethod_isMoveAssignmentOperator +func (c Cursor) CXXMethodIsMoveAssignmentOperator() (ret c.Uint) { return } @@ -2454,34 +2455,33 @@ func (c Cursor) IsMoveAssignmentOperator() (ret c.Uint) { * This function will return 0 for the constructor and 1 for * the conversion function. */ -// llgo:link Cursor.IsExplicit C.clang_CXXMethod_isExplicit -func (c Cursor) IsExplicit() (ret c.Uint) { +// llgo:link Cursor.CXXMethodIsExplicit C.clang_CXXMethod_isExplicit +func (c Cursor) CXXMethodIsExplicit() (ret c.Uint) { return } /** - * Determine if a C++ record is abstract, i.e. whether a class or struct - * has a pure virtual member function. + * Determine if a C++ field is declared 'mutable'. */ -// llgo:link Cursor.IsAbstract C.clang_CXXRecord_isAbstract -func (c Cursor) IsAbstract() (ret c.Uint) { +// llgo:link Cursor.CXXFieldIsMutable C.clang_CXXField_isMutable +func (c Cursor) CXXFieldIsMutable() (ret c.Uint) { return } /** - * Determine if an enum declaration refers to a scoped enum. + * Determine if a C++ record is abstract, i.e. whether a class or struct + * has a pure virtual member function. */ -// llgo:link Cursor.IsScoped C.clang_EnumDecl_isScoped -func (c Cursor) IsScoped() (ret c.Uint) { +// llgo:link Cursor.CXXRecordIsAbstract C.clang_CXXRecord_isAbstract +func (c Cursor) CXXRecordIsAbstract() (ret c.Uint) { return } /** - * Determine if a C++ member function or member function template is - * declared 'const'. + * Determine if an enum declaration refers to a scoped enum. */ -// llgo:link Cursor.IsConst C.clang_CXXMethod_isConst -func (c Cursor) IsConst() (ret c.Uint) { +// llgo:link Cursor.EnumDeclIsScoped C.clang_EnumDecl_isScoped +func (c Cursor) EnumDeclIsScoped() (ret c.Uint) { return } From e2a7577cc1d0ce2e6130705ddd36f4c159355ae1 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 10 Sep 2026 17:05:17 +0800 Subject: [PATCH 3/3] cltest: DoTestFromDir --- cl/blockctx.go | 6 +++++ cl/compile.go | 20 ++++++++++------ cl/compile_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++ cl/type_and_var.go | 16 +++++++++++++ 4 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 cl/compile_test.go diff --git a/cl/blockctx.go b/cl/blockctx.go index ba3fbc8e..023e5ce0 100644 --- a/cl/blockctx.go +++ b/cl/blockctx.go @@ -52,4 +52,10 @@ func (ctx *blockCtx) goNodePos(v clang.Cursor) token.Pos { panic("todo") } +func (p *blockCtx) getPubName(pfnName *string) (rewritten bool) { + // TODO(xsw): + _ = pfnName + return +} + // ----------------------------------------------------------------------------- diff --git a/cl/compile.go b/cl/compile.go index 41bbc1ec..4ac5d846 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -121,7 +121,7 @@ func NewPackage(pkgPath, pkgName string, file Source, conf *Config) (pkg Package HandleErr: nil, NewBuiltin: nil, NodeInterpreter: interp, - CanImplicitCast: implicitCast, + CanImplicitCast: nil, DefaultGoFile: headerGoFile, } pkg.Package = gogen.NewPackage(pkgPath, pkgName, confGox) @@ -132,10 +132,6 @@ func NewPackage(pkgPath, pkgName string, file Source, conf *Config) (pkg Package return } -func implicitCast(pkg *gogen.Package, V, T types.Type, pv *gogen.Element) bool { - panic("todo") -} - // ----------------------------------------------------------------------------- func loadFile(p *gogen.Package, conf *Config, file Source) (pi *PkgInfo, err error) { @@ -216,11 +212,14 @@ func compileDecl(ctx *blockCtx, decl clang.Cursor) { } } +// TODO(xsw): method support func compileFunc(ctx *blockCtx, fn clang.Cursor) { fnName := clang.String(fn) if debugCompileDecl { log.Println("func", fnName, "-", clang.String(fn.Type())) } + origName := fnName + rewritten := ctx.getPubName(&fnName) n := fn.NumArguments() var params []*types.Var var results *types.Tuple @@ -237,9 +236,16 @@ func compileFunc(ctx *blockCtx, fn clang.Cursor) { retType := fn.ResultType() // TODO(xsw): return void tyRet := toType(ctx, retType, flagRetType) results = types.NewTuple(pkg.NewParam(token.NoPos, "", tyRet, false)) - // TODO(xsw): method sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), results, variadic) - _ = sig + f := types.NewFunc(ctx.goNodePos(fn), pkg.Types, fnName, sig) + if old := pkg.Types.Scope().Insert(f); old != nil { + log.Panicln("Go func", fnName, "redefined") + } + // ctx.addExternFunc(fnName) + if rewritten { + scope := pkg.Types.Scope() + substObj(pkg.Types, scope, origName, f) + } /* origName, rewritten := fnName, false if !ctx.inHeader && fn.StorageClass == ast.Static { fnName, rewritten = ctx.autoStaticName(origName), true diff --git a/cl/compile_test.go b/cl/compile_test.go new file mode 100644 index 00000000..6089f847 --- /dev/null +++ b/cl/compile_test.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cl + +import ( + "os" + "path" + "strings" + "testing" +) + +// ----------------------------------------------------------------------------- + +func DoTestFromDir(t *testing.T, sel, relDir string, testFunc func(t *testing.T, pkgDir string)) { + dir, err := os.Getwd() + if err != nil { + t.Fatal("Getwd failed:", err) + } + dir = path.Join(dir, relDir) + fis, err := os.ReadDir(dir) + if err != nil { + t.Fatal("ReadDir failed:", err) + } + for _, fi := range fis { + name := fi.Name() + if strings.HasPrefix(name, "_") { + continue + } + t.Run(name, func(t *testing.T) { + pkgDir := dir + "/" + name + if sel != "" && !strings.Contains(pkgDir, sel) { + return + } + testFunc(t, pkgDir) + }) + } +} + +// ----------------------------------------------------------------------------- +/* +func testFromDir(t *testing.T, sel, relDir string) { + DoTestFromDir(t, sel, relDir, func(t *testing.T, pkgDir string) { + }) +} +*/ +// ----------------------------------------------------------------------------- diff --git a/cl/type_and_var.go b/cl/type_and_var.go index 773cf681..48bf8cd7 100644 --- a/cl/type_and_var.go +++ b/cl/type_and_var.go @@ -17,8 +17,11 @@ package cl import ( + "go/token" "go/types" + "log" + "github.com/goplus/gogen" lc "github.com/goplus/llcppg/lib/clang" ) @@ -38,6 +41,19 @@ func toType(ctx *blockCtx, typ lc.Type, flags int) types.Type { // ----------------------------------------------------------------------------- +func substObj(pkg *types.Package, scope *types.Scope, origName string, real types.Object) { + old := scope.Insert(gogen.NewSubst(token.NoPos, pkg, origName, real)) + if old != nil { + if t, ok := old.Type().(*gogen.TySubst); ok { + t.Real = real + } else { + log.Panicln(origName, "redefined") + } + } +} + +// ----------------------------------------------------------------------------- + func avoidKeyword(name *string) { switch *name { case "map", "type", "range", "chan", "var", "func", "go", "select",