From 98eed077b65efe08e65dee3eb1c744e872555185 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 2 Oct 2026 06:20:01 +0800 Subject: [PATCH] cl: conf.TypeAlias; loadTypedef: support alias type --- cl/_testcpp/type_alias/in.cfg | 9 ++++++ cl/_testcpp/type_alias/in.h | 3 ++ cl/_testcpp/type_alias/out.go | 7 +++++ cl/class.go | 2 +- cl/cltest/cltest.go | 14 ++++++---- cl/compile.go | 5 +++- cl/compile_test.go | 35 ++++++++++++++++++------ cl/ctx.go | 7 +++-- cl/type.go | 2 +- cl/typedef.go | 27 +++++++++++++----- cl/var.go | 20 -------------- cmd/llcppg/llcppg.go | 2 ++ go.mod | 2 +- go.sum | 4 +-- tool/_testc/clang-c-22.1.8/go.mod | 2 +- tool/_testc/clang-c-22.1.8/go.sum | 4 +-- tool/_testcpp/llvm-22.1.8-support/go.mod | 2 +- tool/_testcpp/llvm-22.1.8-support/go.sum | 4 +-- 18 files changed, 95 insertions(+), 56 deletions(-) create mode 100644 cl/_testcpp/type_alias/in.cfg create mode 100644 cl/_testcpp/type_alias/in.h create mode 100644 cl/_testcpp/type_alias/out.go diff --git a/cl/_testcpp/type_alias/in.cfg b/cl/_testcpp/type_alias/in.cfg new file mode 100644 index 00000000..6f8c2dfd --- /dev/null +++ b/cl/_testcpp/type_alias/in.cfg @@ -0,0 +1,9 @@ +{ + "LLGoPackage": "link: -L/path/foo -lfoo", + "CFlags": "-I/path/foo/include", + "WrapFileHeader": "#include \n", + "TypeAlias": { + "std::ptrdiff_t": "ptrdiff_t" + }, + "LoadLibcPubFile": true +} diff --git a/cl/_testcpp/type_alias/in.h b/cl/_testcpp/type_alias/in.h new file mode 100644 index 00000000..c102e702 --- /dev/null +++ b/cl/_testcpp/type_alias/in.h @@ -0,0 +1,3 @@ +namespace std { + using ptrdiff_t = decltype(static_cast(nullptr) - static_cast(nullptr)); +} diff --git a/cl/_testcpp/type_alias/out.go b/cl/_testcpp/type_alias/out.go new file mode 100644 index 00000000..a5cd4e09 --- /dev/null +++ b/cl/_testcpp/type_alias/out.go @@ -0,0 +1,7 @@ +package foo + +import "github.com/goplus/lib/c" + +const LLGoPackage = "link: -L/path/foo -lfoo" + +type StdPtrdiffT = c.PtrdiffT diff --git a/cl/class.go b/cl/class.go index 3764cb1b..b6fffc1d 100644 --- a/cl/class.go +++ b/cl/class.go @@ -475,7 +475,7 @@ func baseClass(ctx *pkgCtx, decl clang.Cursor, feats *int) *types.TypeName { } switch t.Kind { case lc.Type_Record: - cName := clang.String(t.Declaration().Type()) + cName := cTypeName(t) if t, ok := ctx.typeObj(cName); ok { return t } diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 65d74c15..29f8744d 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -30,12 +30,14 @@ import ( // Config represents the lltest configuration. type Config struct { - LLGoPackage string `json:"LLGoPackage"` - WrapFileHeader string `json:"WrapFileHeader"` - CFlags string `json:"CFlags"` - Files []string `json:"Files"` - MacroIgnore []string `json:"MacroIgnore"` - KeepDoc bool `json:"KeepDoc"` + LLGoPackage string `json:"LLGoPackage"` + WrapFileHeader string `json:"WrapFileHeader"` + CFlags string `json:"CFlags"` + Files []string `json:"Files"` + TypeAlias map[string]string `json:"TypeAlias"` + MacroIgnore []string `json:"MacroIgnore"` + LoadLibcPubFile bool `json:"LoadLibcPubFile"` + KeepDoc bool `json:"KeepDoc"` } // LoadConf loads the lltest configuration. diff --git a/cl/compile.go b/cl/compile.go index 7ed29c29..c38a94e1 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -120,6 +120,9 @@ type Config struct { // TypeIgnore specifies a list of C/C++ type names to be ignored (optional). TypeIgnore []string + // TypeAlias specifies a mapping of C/C++ type names to another C/C++ name (optional). + TypeAlias map[string]string + // TypeAbbr specifies a mapping of Go type name to its abbreviated name(s). The abbreviated // name(s) can be a name or name list. They will be used in function names (optional). TypeAbbr map[string]any @@ -234,7 +237,7 @@ func NewPackage(pkgPath, pkgName string, files []Source, conf *Config) (ret Pack keepDoc: !conf.DontKeepDoc, stdRecvName: conf.UseStdRecvName, cflags: conf.CFlags, wrapFileHeader: conf.WrapFileHeader, typeAbbr: conf.TypeAbbr, typeAbbrSuffix: conf.TypeAbbrSuffix, - typePrefix: conf.TypePrefix, typeSuffix: conf.TypeSuffix, + typePrefix: conf.TypePrefix, typeSuffix: conf.TypeSuffix, typeAlias: conf.TypeAlias, fnPrefix: conf.FuncPrefix, enumPrefix: conf.EnumPrefix, rename: rename, nsPrefix: conf.NSPrefix, macroPrefix: conf.MacroPrefix, varPrefix: conf.VarPrefix, nsIgnore: conf.NSIgnore, macroIgnore: conf.MacroIgnore, typeIgnores: conf.TypeIgnore, diff --git a/cl/compile_test.go b/cl/compile_test.go index fd8753ce..90c280b5 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -18,6 +18,7 @@ package cl_test import ( "bytes" + "log" "os" "path/filepath" "strings" @@ -28,10 +29,12 @@ import ( "github.com/goplus/llcppg/cl" "github.com/goplus/llcppg/cl/cltest" "github.com/goplus/llcppg/clang" + "github.com/goplus/llcppg/tool" "github.com/qiniu/x/test" ) func init() { + log.SetFlags(0) cl.SetDebug(cl.DbgFlagAll) } @@ -60,14 +63,26 @@ func testGenGo(t *testing.T, pkg *gogen.Package, dir string, exp any) { func testFromDir(t *testing.T, sel, relDir string, lang cl.Language) { idx := clang.CreateIndex(1, 1) defer idx.Dispose() + cltest.TestFromDir(t, sel, relDir, func(t *testing.T, pkgDir string) { pkgDir, _ = filepath.Abs(pkgDir) + conf, _ := cltest.LoadConf(pkgDir + "/in.cfg") srcFiles := conf.Files if len(srcFiles) == 0 { srcFiles = []string{"in.h"} } + var mod tool.Module + if conf.LoadLibcPubFile { + m, err := tool.LoadModuleFrom(pkgDir) + if err != nil { + t.Errorf("failed to load module: %v", err) + return + } + mod = m + } + files := make([]cl.Source, len(srcFiles)) for i, srcFile := range srcFiles { presumedFile := filepath.Join(pkgDir, srcFile) @@ -84,17 +99,21 @@ func testFromDir(t *testing.T, sel, relDir string, lang cl.Language) { rootDir, myPkgName := filepath.Split(pkgDir) imp := packages.NewImporter(nil, rootDir) pkg, err := cl.NewPackage(pkgPrefix+myPkgName, "foo", files, &cl.Config{ - Importer: imp, - LLGoPackage: conf.LLGoPackage, - Language: lang, - WrapFileHeader: conf.WrapFileHeader, - MacroIgnore: conf.MacroIgnore, - CFlags: conf.CFlags, - NameLookup: nil, - DontKeepDoc: !conf.KeepDoc, + Importer: imp, + LLGoPackage: conf.LLGoPackage, + Language: lang, + WrapFileHeader: conf.WrapFileHeader, + TypeAlias: conf.TypeAlias, + MacroIgnore: conf.MacroIgnore, + CFlags: conf.CFlags, + LoadLibcPubFile: conf.LoadLibcPubFile, + DontKeepDoc: !conf.KeepDoc, + NameLookup: nil, PubFileLookup: func(pkgPath string) (pubFile string, ok bool) { if name, ok := strings.CutPrefix(pkgPath, pkgPrefix); ok { return filepath.Join(rootDir, name, "llcppg.pub"), true + } else if conf.LoadLibcPubFile { + return mod.PubFileLookup(pkgPath) } return }, diff --git a/cl/ctx.go b/cl/ctx.go index da2815e7..15c9a645 100644 --- a/cl/ctx.go +++ b/cl/ctx.go @@ -130,6 +130,7 @@ type pkgCtx struct { nonClasses []string // typedef names to be treated as non-classes typeAbbr map[string]any // Go type name => abbreviated name(s), used in function names rename map[string]string // C/C++ name => Go name + typeAlias map[string]string // C/C++ type name => another C/C++ type name typeIgnores []string // C/C++ type names to be ignored macroIgnore []string // C/C++ macro names to be ignored nsIgnore []string // C/C++ namespace names to be ignored @@ -258,18 +259,18 @@ func (p *pkgCtx) ignoreType(cName string, feats int) { } func (p *pkgCtx) isTypeIgnored(cName string) bool { - return contains(trimTypeTag(cName), p.typeIgnores) + return contains(cName, p.typeIgnores) } func (p *pkgCtx) typeObj(cName string) (*types.TypeName, bool) { - if o, ok := p.types[cName]; ok { + if o, ok := p.types[cName]; ok && o.feats&featQuietIgnore == 0 { return o.TypeName, true } return nil, false } func (p *pkgCtx) typeOf(cName string) (types.Type, bool) { - if o, ok := p.types[cName]; ok { + if o, ok := p.types[cName]; ok && o.feats&featQuietIgnore == 0 { return o.Type(), true } return nil, false diff --git a/cl/type.go b/cl/type.go index 5aacf1e5..518410d9 100644 --- a/cl/type.go +++ b/cl/type.go @@ -182,7 +182,7 @@ func toTypeEx(ctx *pkgCtx, pkg *types.Package, typ lc.Type, flags int, feats *in default: ctx.logtf(typ, "toType: unknown kind - %v", typ.Kind) } - ctx.logtf(typ, "unsupported type - %s", clang.String(typ)) + ctx.logtf(typ, "toType: unsupported type - %s", clang.String(typ)) *feats |= featExplicitIgnore return types.Typ[types.Invalid] } diff --git a/cl/typedef.go b/cl/typedef.go index 8ca6bf10..03d0a5d1 100644 --- a/cl/typedef.go +++ b/cl/typedef.go @@ -26,7 +26,23 @@ import ( // ----------------------------------------------------------------------------- func loadTypedef(ctx *pkgCtx, decl clang.Cursor, scope *scopeCtx) { - cName := cNameOf(decl) + pkg := ctx.pkg + pkgTypes := pkg.Types + + var feats int + var underlying lc.Type + var tunder types.Type + var cName = cNameOf(decl) + var goName = ctx.typeName(cName, true) + if cNewName, ok := ctx.typeAlias[cName]; ok { + if t, ok := ctx.typeOf(cNewName); ok { + tunder = t + goto lzFind + } + ctx.logf(decl, "typedef %s: alias to %s but not found, skipped", cName, cNewName) + return + } + if ctx.isTypeIgnored(cName) { ctx.ignoref(featQuietIgnore, decl, "typedef %s: ignored by config", cName) ctx.ignoreType(cName, featQuietIgnore) @@ -38,15 +54,12 @@ func loadTypedef(ctx *pkgCtx, decl clang.Cursor, scope *scopeCtx) { return } - pkg := ctx.pkg - pkgTypes := pkg.Types - underlying := decl.TypedefDeclUnderlyingType() + underlying = decl.TypedefDeclUnderlyingType() if debugCompileDecl { ctx.logf(decl, "typedef %ss: %s", cName, clang.String(underlying)) } - feats := 0 - tunder := toTypeEx(ctx, pkgTypes, underlying, flagIsTypeDef, &feats, scope) + tunder = toTypeEx(ctx, pkgTypes, underlying, flagIsTypeDef, &feats, scope) if feats&featQuietIgnore != 0 || isTypedefUnsupported(tunder) { ctx.ignoref(featQuietIgnore, decl, "typedef %s: unsupported underlying type, ignored", cName) ctx.ignoreType(cName, featQuietIgnore) @@ -58,13 +71,13 @@ func loadTypedef(ctx *pkgCtx, decl clang.Cursor, scope *scopeCtx) { return } - goName := ctx.typeName(cName, true) if tn, ok := tunder.(*types.Named); ok { if o := tn.Obj(); o.Pkg() == pkgTypes && o.Name() == goName { return // already defined } } +lzFind: var obj *types.TypeName var typDefs = pkg.NewTypeDefs() if doc := ctx.directiveTypeC(decl, feats&featHasCallback != 0); doc != nil { diff --git a/cl/var.go b/cl/var.go index 794b6eba..0ac8eb81 100644 --- a/cl/var.go +++ b/cl/var.go @@ -23,32 +23,12 @@ import ( // ----------------------------------------------------------------------------- -// loadVar loads a C/C++ variable declaration (a global variable, a variable -// within a namespace, or a static variable within a class) and schedules it to -// be compiled into a Go variable. -// -// The Go name is prefixed by ns, which encodes the enclosing namespaces and/or -// class (the class name acts like a namespace, mirroring how static methods are -// handled). Non-static class member variables are fields, not VarDecls, and are -// handled separately in loadClassMember. func loadVar(ctx *pkgCtx, decl clang.Cursor) { ctx.addCompileUnit(func(ctx *pkgCtx) { compileVar(ctx, decl) }) } -// compileVar compiles a C/C++ variable into a Go variable declared with a -// //go:linkname directive that binds it to the external C/C++ symbol, similar -// to how a global function is compiled in compileFuncOrMethod. -// -// A const-qualified variable (e.g. const int x;) is handled the same way: in -// terms of C/C++ semantics a const is just an immutable var, and there is no -// difference in the underlying binding logic. It maps to the same Go var linked -// to the same C/C++ symbol; Go has no way to express a linkname-bound immutable -// value, so the const-ness is not reflected in the generated declaration. The -// const qualifier is a property of the type, not a distinct type kind, so -// toType (which switches on the type kind) already yields the unqualified Go -// type without any special casing here. func compileVar(ctx *pkgCtx, decl clang.Cursor) { cName := cNameOf(decl) if varHasInitExpr(ctx, decl) { diff --git a/cmd/llcppg/llcppg.go b/cmd/llcppg/llcppg.go index 844e0616..1fb5004d 100644 --- a/cmd/llcppg/llcppg.go +++ b/cmd/llcppg/llcppg.go @@ -19,6 +19,7 @@ package main import ( "flag" "fmt" + "log" "os" "github.com/goplus/gogen" @@ -45,6 +46,7 @@ func main() { srcDir = args[1] } + log.SetFlags(0) if *debug { cl.SetDebug(cl.DbgFlagAll) tool.SetDebug(tool.DbgFlagAll) diff --git a/go.mod b/go.mod index 777d5399..d45ab8b5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.27.0 // llgo 1.0 require ( github.com/goplus/gogen v1.25.3 - github.com/goplus/lib v0.5.5 + github.com/goplus/lib v0.5.6 github.com/goplus/mod v0.22.1 github.com/llarhub/clang-c v0.7.0 github.com/qiniu/x v1.18.3 diff --git a/go.sum b/go.sum index 1e1a7a2b..5a175b18 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/goplus/gogen v1.25.3 h1:g8VWgNcCwTG9DFs08umVNtxl/BSLHH1cbvqWzyxrPVk= github.com/goplus/gogen v1.25.3/go.mod h1:Y7ulYW3wonQ3d9er00b0uGFEV/IUZa6okWJZh892ACQ= -github.com/goplus/lib v0.5.5 h1:COcqsafjaXBI6ikcyJgnc7l9Hu03K8kymUSIZtXNTz8= -github.com/goplus/lib v0.5.5/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= +github.com/goplus/lib v0.5.6 h1:lSr4QKgTtIZtD/9aAjwhQW2fzHyyWLNT8FviNSBZEgs= +github.com/goplus/lib v0.5.6/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= 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= diff --git a/tool/_testc/clang-c-22.1.8/go.mod b/tool/_testc/clang-c-22.1.8/go.mod index 50bf17e7..2336c41c 100644 --- a/tool/_testc/clang-c-22.1.8/go.mod +++ b/tool/_testc/clang-c-22.1.8/go.mod @@ -2,4 +2,4 @@ module clang go 1.20 -require github.com/goplus/lib v0.5.4 +require github.com/goplus/lib v0.5.6 diff --git a/tool/_testc/clang-c-22.1.8/go.sum b/tool/_testc/clang-c-22.1.8/go.sum index d5a751ae..f425c4f8 100644 --- a/tool/_testc/clang-c-22.1.8/go.sum +++ b/tool/_testc/clang-c-22.1.8/go.sum @@ -1,2 +1,2 @@ -github.com/goplus/lib v0.5.4 h1:Ltrj+lY844/jx0nJqCRQEq3YzBkHb3jBjCQZWFUdlTc= -github.com/goplus/lib v0.5.4/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= +github.com/goplus/lib v0.5.6 h1:lSr4QKgTtIZtD/9aAjwhQW2fzHyyWLNT8FviNSBZEgs= +github.com/goplus/lib v0.5.6/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= diff --git a/tool/_testcpp/llvm-22.1.8-support/go.mod b/tool/_testcpp/llvm-22.1.8-support/go.mod index b9c5da05..e2871f0f 100644 --- a/tool/_testcpp/llvm-22.1.8-support/go.mod +++ b/tool/_testcpp/llvm-22.1.8-support/go.mod @@ -3,7 +3,7 @@ module llvm go 1.23 require ( - github.com/goplus/lib v0.5.4 + github.com/goplus/lib v0.5.6 github.com/llarhub/libcxx v0.1.1 github.com/llarhub/llvm-c v0.2.0 ) diff --git a/tool/_testcpp/llvm-22.1.8-support/go.sum b/tool/_testcpp/llvm-22.1.8-support/go.sum index d2154dd6..b3ee7024 100644 --- a/tool/_testcpp/llvm-22.1.8-support/go.sum +++ b/tool/_testcpp/llvm-22.1.8-support/go.sum @@ -1,5 +1,5 @@ -github.com/goplus/lib v0.5.4 h1:Ltrj+lY844/jx0nJqCRQEq3YzBkHb3jBjCQZWFUdlTc= -github.com/goplus/lib v0.5.4/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= +github.com/goplus/lib v0.5.6 h1:lSr4QKgTtIZtD/9aAjwhQW2fzHyyWLNT8FviNSBZEgs= +github.com/goplus/lib v0.5.6/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= github.com/llarhub/libcxx v0.1.1 h1:pq/qd3sgtDsima+rrgwOEHYGd/YnyePe6p86rHFD+Xs= github.com/llarhub/libcxx v0.1.1/go.mod h1:5ANUOJMknbqPXhwxbXnLLA8NbHHyfYWUVQNCPiz04f8= github.com/llarhub/llvm-c v0.2.0 h1:KXSm+1A9AjyxOcVTtZnlgvWDaWwBySMR27xSouaOaGg=