diff --git a/builtin_test.go b/builtin_test.go index 127b442b..84eb9727 100644 --- a/builtin_test.go +++ b/builtin_test.go @@ -44,6 +44,35 @@ func getConf() *Config { return &Config{Fset: fset, Importer: imp} } +func TestCheckOverloads(t *testing.T) { + defer func() { + if e := recover(); e != "checkOverloads: should be string constant - foo" { + t.Fatal("TestCheckOverloads:", e) + } + }() + scope := types.NewScope(nil, 0, 0, "") + scope.Insert(types.NewLabel(0, nil, "foo")) + checkOverloads(scope, "bar") + checkOverloads(scope, "foo") +} + +func TestCheckGopPkgNoop(t *testing.T) { + pkg := NewPackage("", "foo", nil) + pkg.Types.Scope().Insert(types.NewConst( + token.NoPos, pkg.Types, "GopPackage", types.Typ[types.UntypedBool], constant.MakeBool(true), + )) + if _, ok := checkGopPkg(pkg); ok { + t.Fatal("checkGopPkg: ok?") + } + defer func() { + if recover() == nil { + t.Fatal("expDeps.typ: no panic?") + } + }() + var ed expDeps + ed.typ(&unboundFuncParam{}) +} + func TestDenoted(t *testing.T) { if denoteRecv(&ast.SelectorExpr{Sel: ast.NewIdent("foo")}) != nil { t.Fatal("denoteRecv: not nil?") diff --git a/func.go b/func.go index 47beb982..6545c0d2 100644 --- a/func.go +++ b/func.go @@ -213,6 +213,9 @@ func (p *Package) NewFuncWith( if isGopFunc(name) { p.isGopPkg = true } + if token.IsExported(name) { + p.expObjTypes = append(p.expObjTypes, sig) + } fn.decl = &ast.FuncDecl{} p.file.decls = append(p.file.decls, fn.decl) diff --git a/func_ext.go b/func_ext.go index 39edd536..4684d89a 100644 --- a/func_ext.go +++ b/func_ext.go @@ -88,8 +88,8 @@ const ( func CheckSigFuncEx(sig *types.Signature) (types.Type, bool) { if sig.Params().Len() == 1 { if param := sig.Params().At(0); param.Name() == overloadArgs { - if typ, ok := param.Type().(*types.Interface); ok && typ.NumMethods() == 1 { - if sig, ok := typ.Method(0).Type().(*types.Signature); ok { + if typ, ok := param.Type().(*types.Interface); ok && typ.NumExplicitMethods() == 1 { + if sig, ok := typ.ExplicitMethod(0).Type().(*types.Signature); ok { if recv := sig.Recv(); recv != nil { return recv.Type(), true } @@ -100,6 +100,15 @@ func CheckSigFuncEx(sig *types.Signature) (types.Type, bool) { return nil, false } +func isSigFuncEx(sig *types.Signature) bool { + if sig.Params().Len() == 1 { + if param := sig.Params().At(0); param.Name() == overloadArgs { + return true + } + } + return false +} + // sigFuncEx return func type ($overloadArgs ...interface{$overloadMethod()}) func sigFuncEx(pkg *types.Package, recv *types.Var, t types.Type) *types.Signature { sig := types.NewSignatureType(types.NewVar(token.NoPos, nil, "", t), nil, nil, nil, nil, false) diff --git a/import.go b/import.go index 11eca3b0..75b28df9 100644 --- a/import.go +++ b/import.go @@ -76,10 +76,6 @@ func (p PkgRef) MarkForceUsed(pkg *Package) { func (p PkgRef) EnsureImported() { } -func shouldAddGopPkg(pkg *Package) bool { - return pkg.isGopPkg && pkg.Types.Scope().Lookup(gopPackage) == nil -} - func isGopoConst(name string) bool { return strings.HasPrefix(name, gopoPrefix) } @@ -94,12 +90,25 @@ func isOverload(name string) bool { } // InitThisGopPkg initializes a Go+ package. -func InitThisGopPkg(pkg *types.Package) { +func InitThisGopPkg(pkg *types.Package) (deps []string) { scope := pkg.Scope() - if scope.Lookup(gopPackage) == nil { // not is a Go+ package + if scope.Lookup(gopPkgInit) != nil { // initialized + return + } + scope.Insert(types.NewConst( + token.NoPos, pkg, gopPkgInit, types.Typ[types.UntypedBool], constant.MakeBool(true), + )) + + pkgDeps := scope.Lookup(gopPackage) + if pkgDeps == nil { // not is a Go+ package return } - if debugImport { + valDeps := pkgDeps.(*types.Const).Val() + if valDeps.Kind() == constant.String { + deps = strings.Split(constant.StringVal(valDeps), ",") + } + + if debugImport && pkg.Path() != "" { log.Println("==> Import", pkg.Path()) } gopos := make([]string, 0, 4) @@ -175,6 +184,7 @@ func InitThisGopPkg(pkg *types.Package) { on := NewOverloadNamed(token.NoPos, pkg, name, nameds...) scope.Insert(on) } + return } // name @@ -271,8 +281,9 @@ func checkGoptGopx(pkg *types.Package, scope *types.Scope, name string, o types. const ( goptPrefix = "Gopt_" // template method gopoPrefix = "Gopo_" // overload function/method - gopxPrefix = "Gopx_" + gopxPrefix = "Gopx_" // type as parameters function/method gopPackage = "GopPackage" + gopPkgInit = "__gop_inited" ) /* @@ -290,7 +301,7 @@ func checkOverloads(scope *types.Scope, gopoName string) (ret []string, exists b return strings.Split(constant.StringVal(v), ","), true } } - panic("checkOverloads TODO: should be string constant - " + gopoName) + panic("checkOverloads: should be string constant - " + gopoName) } return } @@ -358,40 +369,129 @@ const ( // ---------------------------------------------------------------------------- -// Context represents all things between packages. -type Context struct { - chkGopImports map[string]bool - stdPkg func(pkgPath string) bool +type expDeps struct { + this *types.Package + ret map[*types.Package]none + exists map[types.Type]none } -func NewContext(isPkgtStandard func(pkgPath string) bool) *Context { - if isPkgtStandard == nil { - isPkgtStandard = isStdPkg +func checkGopPkg(pkg *Package) (val ast.Expr, ok bool) { + if pkg.Types.Scope().Lookup(gopPackage) != nil { + return + } + ed := expDeps{pkg.Types, make(map[*types.Package]none), make(map[types.Type]none)} + for _, t := range pkg.expObjTypes { + ed.typ(t) } - return &Context{ - chkGopImports: make(map[string]bool), - stdPkg: isPkgtStandard, + var deps []string + for depPkg := range ed.ret { + if depPkg.Scope().Lookup(gopPackage) != nil { + deps = append(deps, depPkg.Path()) + } + } + if len(deps) > 0 { + return stringLit(strings.Join(deps, ",")), true + } + if ok = pkg.isGopPkg; ok { + return identTrue, true } + return } -// initGopPkg initializes a Go+ packages. -func (p *Context) initGopPkg(importer types.Importer, pkgImp *types.Package) { - pkgPath := pkgImp.Path() - if p.stdPkg(pkgPath) || p.chkGopImports[pkgPath] { - return +func (p expDeps) typ(typ types.Type) { +retry: + switch t := typ.(type) { + case *types.Basic: // bool, int, etc + case *types.Pointer: + typ = t.Elem() + goto retry + case *types.Slice: + typ = t.Elem() + goto retry + case *types.Map: + p.typ(t.Key()) + typ = t.Elem() + goto retry + case *types.Named: + p.named(t) + case *types.Signature: + p.sig(t) + case *types.Struct: + p.struc(t) + case *types.Interface: + p.interf(t) + case *types.Chan: + typ = t.Elem() + goto retry + case *types.Array: + typ = t.Elem() + goto retry + default: + log.Panicf("expDeps: unknown type - %T\n", typ) + } +} + +func (p expDeps) sig(sig *types.Signature) { + p.tuple(sig.Params()) + p.tuple(sig.Results()) +} + +func (p expDeps) tuple(v *types.Tuple) { + for i, n := 0, v.Len(); i < n; i++ { + p.typ(v.At(i).Type()) + } +} + +func (p expDeps) named(t *types.Named) { + o := t.Obj() + if at := o.Pkg(); at != nil && at != p.this { + if _, ok := p.exists[t]; ok { + return + } + p.exists[t] = none{} + p.ret[at] = none{} + for i, n := 0, t.NumMethods(); i < n; i++ { + m := t.Method(i) + p.method(m) + } + p.typ(t.Underlying()) } - if !pkgImp.Complete() { - importer.Import(pkgPath) +} + +func (p expDeps) interf(t *types.Interface) { + for i, n := 0, t.NumEmbeddeds(); i < n; i++ { + p.typ(t.EmbeddedType(i)) } - InitThisGopPkg(pkgImp) - p.chkGopImports[pkgPath] = true - for _, imp := range pkgImp.Imports() { - p.initGopPkg(importer, imp) + for i, n := 0, t.NumExplicitMethods(); i < n; i++ { + m := t.ExplicitMethod(i) + p.method(m) + } +} + +func (p expDeps) method(m *types.Func) { + if m.Exported() { + if sig := m.Type().(*types.Signature); !isSigFuncEx(sig) { + p.sig(sig) + } + } +} + +func (p expDeps) struc(t *types.Struct) { + for i, n := 0, t.NumFields(); i < n; i++ { + fld := t.Field(i) + if fld.Embedded() || fld.Exported() { + p.typ(fld.Type()) + } } } -func isStdPkg(pkgPath string) bool { - return strings.IndexByte(pkgPath, '.') < 0 +// initGopPkg initializes a Go+ packages. +func (p *Package) initGopPkg(importer types.Importer, pkgImp *types.Package) { + gopDeps := InitThisGopPkg(pkgImp) + for _, depPath := range gopDeps { + imp, _ := importer.Import(depPath) + p.initGopPkg(importer, imp) + } } // ---------------------------------------------------------------------------- @@ -409,7 +509,7 @@ func importPkg(this *Package, pkgPath string, src ast.Node) (PkgRef, error) { } return PkgRef{}, e } else { - this.ctx.initGopPkg(this.imp, pkgImp) + this.initGopPkg(this.imp, pkgImp) } return PkgRef{Types: pkgImp}, nil } diff --git a/package.go b/package.go index 2f3af2e9..de55abb0 100644 --- a/package.go +++ b/package.go @@ -95,12 +95,6 @@ type Config struct { // If Fset is nil, Load will use a new fileset, but preserve Fset's value. Fset *token.FileSet - // Context represents all things between packages (optional). - Context *Context - - // IsPkgtStandard checks a pkgPath is a Go standard package or not. - IsPkgtStandard func(pkgPath string) bool - // HandleErr is called to handle errors (optional). HandleErr func(err error) @@ -249,7 +243,11 @@ func (p *File) getDecls(this *Package) (decls []ast.Decl) { return specs[i].(*ast.ImportSpec).Path.Value < specs[j].(*ast.ImportSpec).Path.Value }) - addGopPkg := p.fname == this.conf.DefaultGoFile && shouldAddGopPkg(this) + var valGopPkg ast.Expr + var addGopPkg bool + if p.fname == this.conf.DefaultGoFile { + valGopPkg, addGopPkg = checkGopPkg(this) + } if len(specs) == 0 && !addGopPkg { return p.decls } @@ -261,7 +259,7 @@ func (p *File) getDecls(this *Package) (decls []ast.Decl) { &ast.ValueSpec{ Names: []*ast.Ident{{Name: gopPackage}}, Values: []ast.Expr{ - &ast.Ident{Name: "true"}, + valGopPkg, }, }, }}) @@ -286,7 +284,6 @@ type Package struct { files map[string]*File file *File conf *Config - ctx *Context builtin PkgRef pkgBig PkgRef utBigInt *types.Named @@ -294,8 +291,10 @@ type Package struct { utBigFlt *types.Named commentedStmts map[ast.Stmt]*ast.CommentGroup implicitCast func(pkg *Package, V, T types.Type, pv *Element) bool - allowRedecl bool // for c2go - isGopPkg bool + + expObjTypes []types.Type // types of export objects + isGopPkg bool + allowRedecl bool // for c2go } const ( @@ -311,10 +310,6 @@ func NewPackage(pkgPath, name string, conf *Config) *Package { if fset == nil { fset = token.NewFileSet() } - ctx := conf.Context - if ctx == nil { - ctx = NewContext(conf.IsPkgtStandard) - } imp := conf.Importer if imp == nil { imp = packages.NewImporter(fset) @@ -331,7 +326,6 @@ func NewPackage(pkgPath, name string, conf *Config) *Package { file: file, files: files, conf: conf, - ctx: ctx, } pkg.initAutoNames() pkg.imp = imp diff --git a/package_test.go b/package_test.go index 568c4221..552b2b2c 100644 --- a/package_test.go +++ b/package_test.go @@ -22,7 +22,6 @@ import ( "go/types" "log" "os" - "strings" "syscall" "testing" "unsafe" @@ -56,11 +55,6 @@ func (p eventRecorder) Member(id ast.Node, obj types.Object) { func (p eventRecorder) Call(fn ast.Node, obj types.Object) { } -func isStdPkg(pkgPath string) bool { - is := pkgPath != "foo" && strings.IndexByte(pkgPath, '.') < 0 - return is -} - func newMainPackage( implicitCast ...func(pkg *gox.Package, V, T types.Type, pv *gox.Element) bool) *gox.Package { conf := &gox.Config{ @@ -69,7 +63,6 @@ func newMainPackage( Recorder: eventRecorder{}, NodeInterpreter: nodeInterp{}, DbgPositioner: nodeInterp{}, - IsPkgtStandard: isStdPkg, } if len(implicitCast) > 0 { conf.CanImplicitCast = implicitCast[0] @@ -150,7 +143,6 @@ func (p *goxTest) NewPackage(pkgPath string, name string) *gox.Package { Importer: p.imp, NodeInterpreter: nodeInterp{}, DbgPositioner: nodeInterp{}, - IsPkgtStandard: isStdPkg, } return gox.NewPackage(pkgPath, name, conf) } diff --git a/typeparams_test.go b/typeparams_test.go index ec4e6299..e9cee2fb 100644 --- a/typeparams_test.go +++ b/typeparams_test.go @@ -134,13 +134,13 @@ func Gopt_Table_Gopx_Col__1[Array any](p *Table, v int) { if err != nil { t.Fatal(err) } - pkg := gt.NewPackage("", "main") + pkg := gt.NewPackage("", "test") foo := pkg.Import("foo") objTable := foo.Ref("Table") typ := objTable.Type().(*types.Named) tyInt := types.Typ[types.Int] - cb := pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg). + cb := pkg.NewFunc(nil, "Example", nil, nil, false).BodyStart(pkg). NewVar(types.NewPointer(typ), "tbl") _, err = cb.VarVal("tbl").Member("col", gox.MemberFlagMethodAlias) if err != nil { @@ -151,11 +151,11 @@ func Gopt_Table_Gopx_Col__1[Array any](p *Table, v int) { Val(foo.Ref("Row")).Typ(tyInt).Val(1, source("1")).Call(2).EndStmt(). End() - domTest(t, pkg, `package main + domTest(t, pkg, `package test import "foo" -func main() { +func Example() { var tbl *foo.Table foo.Gopt_Table_Gopx_Col__0[int](tbl, "bar") foo.Gopx_Bar[int]("1") @@ -164,6 +164,89 @@ func main() { `) } +func TestCheckGopPkg(t *testing.T) { + const src = `package foo + +import "io" + +const GopPackage = "io" + +type basetype interface { + int | string +} + +func Gopx_Bar[T basetype](name string) { +} + +func Gopx_Row__0[T basetype](name string) { +} + +func Gopx_Row__1[Array any](v int) { +} + +type EmbIntf interface { + io.Reader + Close() +} + +type Table struct { + EmbIntf + N int + b string +} + +func Gopt_Table_Gopx_Col__0[T basetype](p *Table, name string) { +} + +func Gopt_Table_Gopx_Col__1[Array any](p *Table, v int) { +} +` + gt := newGoxTest() + _, err := gt.LoadGoPackage("foo", "foo.go", src) + if err != nil { + t.Fatal(err) + } + pkg := gt.NewPackage("", "test") + foo := pkg.Import("foo") + objTable := foo.Ref("Table") + typ := objTable.Type().(*types.Named) + tyInt := types.Typ[types.Int] + + typSlice := types.NewSlice(types.NewPointer(typ)) + typMap := types.NewMap(types.Typ[types.String], typSlice) + + args := types.NewTuple(types.NewParam(0, pkg.Types, "tbls", typMap)) + cb := pkg.NewFunc(nil, "Example", args, nil, false).BodyStart(pkg) + _, err = cb.VarVal("tbls").Val("Hi").Index(1, false).Val(0).Index(1, false).Member("col", gox.MemberFlagMethodAlias) + if err != nil { + t.Fatal("tbl.Member(col):", err) + } + cb.Typ(tyInt).Val("bar").Call(2).EndStmt(). + Val(foo.Ref("Bar")).Typ(tyInt).Val("1").Call(2).EndStmt(). + Val(foo.Ref("Row")).Typ(tyInt).Val(1, source("1")).Call(2).EndStmt(). + End() + + typChan := types.NewChan(types.SendRecv, typSlice) + typArray := types.NewArray(typChan, 2) + args = types.NewTuple(types.NewParam(0, pkg.Types, "", typArray)) + pkg.NewFunc(nil, "Create", args, nil, false).BodyStart(pkg).End() + + domTest(t, pkg, `package test + +import "foo" + +const GopPackage = "foo" + +func Example(tbls map[string][]*foo.Table) { + foo.Gopt_Table_Gopx_Col__0[int](tbls["Hi"][0], "bar") + foo.Gopx_Bar[int]("1") + foo.Gopx_Row__1[int](1) +} +func Create([2]chan []*foo.Table) { +} +`) +} + func TestOverloadNamed(t *testing.T) { const src = `package foo