Repository: goplus/gop
Pull request #2361 (2025-05-17 08:14:03)

v1.4.6


Patch:
diff --git a/.github/codecov.yml b/.github/codecov.yml
index 3d97bb0c..671ba926 100644
--- a/.github/codecov.yml
+++ b/.github/codecov.yml
@@ -20,6 +20,7 @@ coverage:
     - "cl/internal/spx3"
     - "cl/internal/test"
     - "cl/internal/unit"
+    - "cl/internal/typesalias"
     - "parser/fsx"
     - "parser/parsertest"
     - "x/jsonrpc2"
diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml
index a90318a7..ffb83121 100644
--- a/.github/workflows/release-build.yml
+++ b/.github/workflows/release-build.yml
@@ -57,7 +57,7 @@ jobs:
     - name: Set up Go
       uses: actions/setup-go@v5
       with:
-        go-version: 1.21.x
+        go-version: 1.24.x
 
     - name: Set up QEMU
       uses: docker/setup-qemu-action@v3
diff --git a/README.md b/README.md
index 4be5516d..e1de500c 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,7 @@ Code style is just the first step. We have made many efforts to make the code mo
 | package main<br><br>import "fmt"<br><br>func main() {<br>&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println("Hi")<br>} | import "fmt"<br><br>fmt.Println("Hi")<br> | Program structure: Go+ allows omitting `package main` and `func main` |
 | fmt.Println("Hi") | echo("Hi") | More builtin functions: It simplifies the expression of the most common tasks |
 | fmt.Println("Hi") | echo "Hi" | Command-line style code: It reduces the number of parentheses in the code as much as possible, making it closer to natural language |
+| name := "Ken"<br>fmt.Printf(<br>&nbsp;&nbsp;"Hi %s\n", name) | name := "Ken"<br>echo "Hi ${name}" | [Goodbye printf](doc/goodbye-printf.md), use `${expr}` in string literals |
 | a := []int{1, 2, 3} | a := [1, 2, 3] | List literals |
 | a = append(a, 4)<br>a = append(a, 5, 6, 7) | a <- 4<br>a <- 5, 6, 7 | Append values to a list |
 | a := map[string]int{<br>&nbsp;&nbsp;&nbsp;&nbsp;"Monday": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;"Tuesday": 2,<br>} | a := {<br>&nbsp;&nbsp;&nbsp;&nbsp;"Monday": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;"Tuesday": 2,<br>} | Mapping literals |
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 959bb9d0..00000000
--- a/VERSION
+++ /dev/null
@@ -1,1 +0,0 @@
-v1.4.5
diff --git a/ast/ast.go b/ast/ast.go
index d7257687..5c6f58e8 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -427,7 +427,18 @@ func (x *ChanType) Pos() token.Pos { return x.Begin }
 func (x *BadExpr) End() token.Pos { return x.To }
 
 // End returns position of first character immediately after the node.
-func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) }
+func (x *Ident) End() token.Pos {
+	if x.Implicit() { // implicitly declared
+		return x.NamePos
+	}
+	return x.NamePos + token.Pos(len(x.Name))
+}
+
+// Implicit reports whether the identifier was implicitly declared
+func (x *Ident) Implicit() bool {
+	o := x.Obj
+	return o != nil && o.Kind >= implicitBase
+}
 
 // End returns position of first character immediately after the node.
 func (x *Ellipsis) End() token.Pos {
@@ -542,6 +553,11 @@ func (*ChanType) exprNode()      {}
 // Useful for ASTs generated by code other than the Go+ parser.
 func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }
 
+// NewIdentEx creates a new Ident with position and with the given kind.
+func NewIdentEx(pos token.Pos, name string, kind ObjKind) *Ident {
+	return &Ident{pos, name, NewObj(kind, name)}
+}
+
 // IsExported reports whether name starts with an upper-case letter.
 func IsExported(name string) bool { return token.IsExported(name) }
 
@@ -1084,7 +1100,6 @@ func (*FuncDecl) declNode() {}
 // collectively building a Go+ package.
 type Package struct {
 	Name    string               // package name
-	Scope   *Scope               // package scope across all files
 	Imports map[string]*Object   // map of package id -> package object
 	Files   map[string]*File     // Go+ source files by filename
 	GoFiles map[string]*ast.File // Go source files by filename
diff --git a/ast/ast_gop.go b/ast/ast_gop.go
index afa29c47..400ed927 100644
--- a/ast/ast_gop.go
+++ b/ast/ast_gop.go
@@ -471,9 +471,7 @@ type File struct {
 	Name    *Ident        // package name
 	Decls   []Decl        // top-level declarations; or nil
 
-	Scope       *Scope          // package scope (this file only)
 	Imports     []*ImportSpec   // imports in this file
-	Unresolved  []*Ident        // unresolved identifiers in this file
 	Comments    []*CommentGroup // list of all comments in the source file
 	Code        []byte
 	ShadowEntry *FuncDecl // indicate the module entry point.
diff --git a/ast/filter.go b/ast/filter.go
index fa76d19c..714100a7 100644
--- a/ast/filter.go
+++ b/ast/filter.go
@@ -499,7 +499,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
 
 	// TODO(gri) need to compute unresolved identifiers!
 	return &File{
-		doc, pos, NewIdent(pkg.Name), decls, pkg.Scope,
-		imports, nil, comments, nil, nil, false, false, false, false,
+		doc, pos, NewIdent(pkg.Name), decls,
+		imports, comments, nil, nil, false, false, false, false,
 	}
 }
diff --git a/ast/resolve.go b/ast/resolve.go
index 770177e5..d94c08e4 100644
--- a/ast/resolve.go
+++ b/ast/resolve.go
@@ -18,6 +18,7 @@
 
 package ast
 
+/* TODO(xsw): remove
 import (
 	"fmt"
 	"strconv"
@@ -184,3 +185,4 @@ func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer,
 	p.errors.Sort()
 	return &Package{pkgName, pkgScope, imports, files, nil}, p.errors.Err()
 }
+*/
diff --git a/ast/scope.go b/ast/scope.go
index 992d3802..15f25a86 100644
--- a/ast/scope.go
+++ b/ast/scope.go
@@ -154,6 +154,10 @@ const (
 	Var                // variable
 	Fun                // function or method
 	Lbl                // label
+	implicitBase
+
+	ImplicitPkg = ObjKind(implicitBase)
+	ImplicitFun = ObjKind(implicitBase + 1)
 )
 
 var objKindStrings = [...]string{
@@ -164,6 +168,9 @@ var objKindStrings = [...]string{
 	Var: "var",
 	Fun: "func",
 	Lbl: "label",
+
+	ImplicitPkg: "package",
+	ImplicitFun: "func",
 }
 
 func (kind ObjKind) String() string { return objKindStrings[kind] }
diff --git a/cl/builtin.go b/cl/builtin.go
index 176f76d5..4e97b9d1 100644
--- a/cl/builtin.go
+++ b/cl/builtin.go
@@ -21,6 +21,7 @@ import (
 	"go/types"
 
 	"github.com/goplus/gogen"
+	"github.com/goplus/gop/cl/internal/typesalias"
 )
 
 // -----------------------------------------------------------------------------
@@ -81,7 +82,7 @@ const (
 	osxPkgPath = "github.com/qiniu/x/gop/osx"
 )
 
-func newBuiltinDefault(pkg *gogen.Package, conf *gogen.Config) *types.Package {
+func (ctx *pkgCtx) newBuiltinDefault(pkg *gogen.Package, conf *gogen.Config) *types.Package {
 	builtin := types.NewPackage("", "")
 	fmt := pkg.TryImport("fmt")
 	os := pkg.TryImport("os")
@@ -95,9 +96,12 @@ func newBuiltinDefault(pkg *gogen.Package, conf *gogen.Config) *types.Package {
 	pkg.TryImport("strings")
 	if ng.Types != nil {
 		initMathBig(pkg, conf, ng)
-		if obj := ng.Types.Scope().Lookup("Gop_ninteger"); obj != nil {
-			if _, ok := obj.Type().(*types.Basic); !ok {
-				conf.EnableTypesalias = true
+		if typesalias.Support {
+			if obj := ng.Types.Scope().Lookup("Gop_ninteger"); obj != nil {
+				if _, ok := obj.Type().(*types.Basic); !ok {
+					conf.EnableTypesalias = true
+					ctx.featTypesAlias = true
+				}
 			}
 		}
 	}
diff --git a/cl/classfile.go b/cl/classfile.go
index 0b70acc1..bd210a65 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -516,11 +516,12 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 			mainFn := stk.Pop()
 			sigParams := mainFn.Type.(*types.Signature).Params()
 			callMain := func() {
+				src := parent.lookupClassNode(proj.gameClass_)
 				stk.Push(mainFn)
 				if _, isPtr := sigParams.At(0).Type().(*types.Pointer); isPtr {
-					cb.Val(recv).MemberRef(base.Name()).UnaryOp(gotoken.AND)
+					cb.Val(recv, src).MemberRef(base.Name()).UnaryOp(gotoken.AND)
 				} else {
-					cb.Val(recv) // template recv method
+					cb.Val(recv, src) // template recv method
 				}
 			}
 
@@ -531,7 +532,7 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 				if len(sprites) == 1 && sprites[0].proto == "" { // no work class prototype
 					sp := sprites[0]
 					narg = 1 + len(sp.types)
-					genWorkClasses(pkg, cb, recv, sp, iobj, -1, callMain)
+					genWorkClasses(pkg, parent, cb, recv, sp, iobj, -1, callMain)
 				} else {
 					lstNames := make([]string, narg)
 					for i := 1; i < narg; i++ {
@@ -539,7 +540,7 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 						tn := tslice.(*types.Slice).Elem().(*types.Named)
 						sp := spriteByProto(sprites, tn.Obj().Name()) // work class
 						if n := len(sp.types); n > 0 {
-							lstNames[i] = genWorkClasses(pkg, cb, recv, sp, iobj, i, nil)
+							lstNames[i] = genWorkClasses(pkg, parent, cb, recv, sp, iobj, i, nil)
 							cb.SliceLitEx(tslice, n, false).EndInit(1)
 							iobj += n
 						}
@@ -563,7 +564,7 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 }
 
 func genWorkClasses(
-	pkg *gogen.Package, cb *gogen.CodeBuilder, recv *types.Var,
+	pkg *gogen.Package, parent *pkgCtx, cb *gogen.CodeBuilder, recv *types.Var,
 	sp *spxObj, iobj, ilst int, callMain func()) (lstName string) {
 	const (
 		indexGame     = 1
@@ -588,9 +589,10 @@ func genWorkClasses(
 	} else {
 		callMain()
 	}
-	for i := range sptypes {
+	for i, spt := range sptypes {
+		src := parent.lookupClassNode(spt)
 		objName := objNamePrefix + strconv.Itoa(iobj+i)
-		cb.VarVal(objName)
+		cb.VarVal(objName, src)
 	}
 	return
 }
diff --git a/cl/compile.go b/cl/compile.go
index e2c05495..a3eab709 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -29,6 +29,7 @@ import (
 	"github.com/goplus/gogen"
 	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/ast/fromgo"
+	"github.com/goplus/gop/cl/internal/typesalias"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/mod/modfile"
 	"github.com/qiniu/x/errors"
@@ -347,8 +348,10 @@ type pkgCtx struct {
 	idents   []*ast.Ident    // toType ident recored
 	inInst   int             // toType in generic instance
 
-	goxMain      int // normal gox files with main func
 	goxMainClass string
+	goxMain      int32 // normal gox files with main func
+
+	featTypesAlias bool // support types alias
 }
 
 type pkgImp struct {
@@ -376,9 +379,10 @@ type blockCtx struct {
 	fileScope *types.Scope // available when isGopFile
 	rec       *goxRecorder
 
-	fileLine  bool
-	isClass   bool
-	isGopFile bool // is Go+ file or not
+	fileLine   bool
+	isClass    bool
+	isGopFile  bool // is Go+ file or not
+	typesAlias bool // support types alias
 }
 
 func (p *blockCtx) cstr() gogen.Ref {
@@ -483,6 +487,16 @@ func (p *pkgCtx) recoverErr(e any, src ast.Node) error {
 	return err
 }
 
+// lookupClassFile looks up the class file by name.
+func (p *pkgCtx) lookupClassNode(name string) ast.Node {
+	for f, cls := range p.classes {
+		if name == cls.clsfile {
+			return f.Name
+		}
+	}
+	return nil
+}
+
 const (
 	defaultGoFile  = ""
 	skippingGoFile = "_skip"
@@ -513,7 +527,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Packag
 		LoadNamed:       ctx.loadNamed,
 		HandleErr:       ctx.handleErr,
 		NodeInterpreter: interp,
-		NewBuiltin:      newBuiltinDefault,
+		NewBuiltin:      ctx.newBuiltinDefault,
 		DefaultGoFile:   defaultGoFile,
 		NoSkipConstant:  conf.NoSkipConstant,
 		PkgPathIox:      osxPkgPath,
@@ -569,7 +583,8 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Packag
 		fileScope := types.NewScope(p.Types.Scope(), f.Pos(), f.End(), f.path)
 		ctx := &blockCtx{
 			pkg: p, pkgCtx: ctx, cb: p.CB(), relBaseDir: relBaseDir, fileScope: fileScope,
-			fileLine: fileLine, isClass: f.IsClass, rec: rec, imports: make(map[string]pkgImp), isGopFile: true,
+			fileLine: fileLine, isClass: f.IsClass, rec: rec, imports: make(map[string]pkgImp),
+			isGopFile: true, typesAlias: ctx.featTypesAlias,
 		}
 		if rec := ctx.rec; rec != nil {
 			rec.Scope(f.File, fileScope)
@@ -590,7 +605,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Packag
 		gofiles = append(gofiles, f)
 		ctx := &blockCtx{
 			pkg: p, pkgCtx: ctx, cb: p.CB(), relBaseDir: relBaseDir,
-			imports: make(map[string]pkgImp),
+			imports: make(map[string]pkgImp), typesAlias: ctx.featTypesAlias,
 		}
 		preloadFile(p, ctx, f, skippingGoFile, false)
 	}
@@ -1068,7 +1083,7 @@ func preloadFile(p *gogen.Package, ctx *blockCtx, f *ast.File, goFile string, ge
 								if debugLoad {
 									log.Println("==> Load > AliasType", name)
 								}
-								aliasType(pkg, t.Pos(), name, toType(ctx, t.Type))
+								aliasType(ctx, pkg, t.Pos(), name, t)
 								return
 							}
 							if debugLoad {
@@ -1300,9 +1315,30 @@ func newType(pkg *types.Package, pos token.Pos, name string) *types.Named {
 	return types.NewNamed(typName, nil, nil)
 }
 
-func aliasType(pkg *types.Package, pos token.Pos, name string, typ types.Type) {
-	o := types.NewTypeName(pos, pkg, name, typ)
-	pkg.Scope().Insert(o)
+func aliasType(ctx *blockCtx, pkg *types.Package, pos token.Pos, name string, t *ast.TypeSpec) {
+	if ctx.typesAlias {
+		var typeParams []*types.TypeParam
+		if t.TypeParams != nil {
+			typeParams = toTypeParams(ctx, t.TypeParams)
+			ctx.tlookup = &typeParamLookup{typeParams}
+			defer func() {
+				ctx.tlookup = nil
+			}()
+			org := ctx.inInst
+			ctx.inInst = 0
+			defer func() {
+				ctx.inInst = org
+			}()
+		}
+		o := typesalias.NewAlias(types.NewTypeName(pos, pkg, name, nil), toType(ctx, t.Type))
+		if typeParams != nil {
+			typesalias.SetTypeParams(o, typeParams)
+		}
+		pkg.Scope().Insert(o.Obj())
+	} else {
+		o := types.NewTypeName(pos, pkg, name, toType(ctx, t.Type))
+		pkg.Scope().Insert(o)
+	}
 }
 
 func loadFunc(ctx *blockCtx, recv *types.Var, name string, d *ast.FuncDecl, genBody bool) {
diff --git a/cl/internal/spx4/game.go b/cl/internal/spx4/game.go
new file mode 100644
index 00000000..2e18253b
--- /dev/null
+++ b/cl/internal/spx4/game.go
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2021 The GoPlus Authors (goplus.org). 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 spx4
+
+const (
+	GopPackage = "github.com/goplus/gop/cl/internal/spx/pkg"
+	Gop_sched  = "Sched,SchedNow"
+)
+
+type Sound string
+
+type MyGame struct {
+}
+
+func Gopt_MyGame_Main(game any, sprites ...Sprite) {
+}
+
+func (p *MyGame) Ls(n int) {}
+
+func (p *MyGame) Capout(doSth func()) (string, error) {
+	return "", nil
+}
+
+func (p *MyGame) Gop_Env(name string) int {
+	return 0
+}
+
+func (p *MyGame) Gop_Exec(name string, args ...any) {
+}
+
+func (p *MyGame) InitGameApp(args ...string) {
+}
+
+func (p *MyGame) Broadcast__0(msg string) {
+}
+
+func (p *MyGame) Broadcast__1(msg string, wait bool) {
+}
+
+func (p *MyGame) Broadcast__2(msg string, data any, wait bool) {
+}
+
+func (p *MyGame) Play(media string, wait ...bool) {
+}
+
+func (p *MyGame) sendMessage(data any) {
+}
+
+func (p *MyGame) SendMessage(data any) {
+	p.sendMessage(data)
+}
+
+func Gopt_MyGame_Run(game any, resource string) error {
+	return nil
+}
+
+func Sched() {
+}
+
+func SchedNow() {
+}
+
+func Rand__0(int) int {
+	return 0
+}
+
+func Rand__1(float64) float64 {
+	return 0
+}
+
+var (
+	TestIntValue int
+)
diff --git a/cl/internal/spx4/pkg/pkg.go b/cl/internal/spx4/pkg/pkg.go
new file mode 100644
index 00000000..bd2ff494
--- /dev/null
+++ b/cl/internal/spx4/pkg/pkg.go
@@ -0,0 +1,27 @@
+package pkg
+
+const (
+	GopPackage = true
+)
+
+type Vector struct {
+	X int
+	Y int
+}
+
+func NewVector(x, y int) *Vector {
+	return &Vector{x, y}
+}
+
+func (v *Vector) Add__0(x int, y int) {
+	v.X += x
+	v.Y += y
+}
+
+func (v *Vector) Add__1(o *Vector) {
+	v.Add__0(o.X, o.Y)
+}
+
+func (v *Vector) Self() *Vector {
+	return v
+}
diff --git a/cl/internal/spx4/sprite.go b/cl/internal/spx4/sprite.go
new file mode 100644
index 00000000..97ca23da
--- /dev/null
+++ b/cl/internal/spx4/sprite.go
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021 The GoPlus Authors (goplus.org). 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 spx4
+
+import (
+	"github.com/goplus/gop/cl/internal/spx/pkg"
+)
+
+type Sprite struct {
+	pos pkg.Vector
+	Entry
+}
+
+type Entry struct {
+	vec pkg.Vector
+}
+
+func (p *Entry) Vector() *pkg.Vector {
+	return &p.vec
+}
+
+func (p *Sprite) SetCostume(costume any) {
+}
+
+func (p *Sprite) Say(msg string, secs ...float64) {
+}
+
+func (p *Sprite) Position() *pkg.Vector {
+	return &p.pos
+}
+
+type Mesher interface {
+	Name() string
+}
+
+func Gopt_Sprite_Clone__0(sprite any) {
+}
+
+func Gopt_Sprite_Clone__1(sprite any, data any) {
+}
+
+func Gopt_Sprite_OnKey__0(sprite any, a string, fn func()) {
+}
+
+func Gopt_Sprite_OnKey__1(sprite any, a string, fn func(key string)) {
+}
+
+func Gopt_Sprite_OnKey__2(sprite any, a []string, fn func()) {
+}
+
+func Gopt_Sprite_OnKey__3(sprite any, a []string, fn func(key string)) {
+}
+
+func Gopt_Sprite_OnKey__4(sprite any, a []Mesher, fn func()) {
+}
+
+func Gopt_Sprite_OnKey__5(sprite any, a []Mesher, fn func(key Mesher)) {
+}
+
+func Gopt_Sprite_OnKey__6(sprite any, a []string, b []string, fn func(key string)) {
+}
+
+func Gopt_Sprite_OnKey__7(sprite any, a []string, b []Mesher, fn func(key string)) {
+}
+
+func Gopt_Sprite_OnKey__8(sprite any, x int, y int) {
+}
+
+func Gopt_Sprite_OnKey2(sprite any, a string, fn func(key string)) {
+}
diff --git a/cl/internal/typesalias/alias_go121.go b/cl/internal/typesalias/alias_go121.go
new file mode 100644
index 00000000..77d50e5d
--- /dev/null
+++ b/cl/internal/typesalias/alias_go121.go
@@ -0,0 +1,33 @@
+//go:build !go1.22
+// +build !go1.22
+
+package typesalias
+
+import "go/types"
+
+const Support = false
+
+type Alias struct {
+}
+
+const unsupported = "typesAlias are unsupported at this go version"
+
+func (t *Alias) Underlying() types.Type {
+	panic(unsupported)
+}
+
+func (t *Alias) String() string {
+	panic(unsupported)
+}
+
+func (t *Alias) Obj() *types.TypeName {
+	panic(unsupported)
+}
+
+func NewAlias(obj *types.TypeName, rhs types.Type) *Alias {
+	return &Alias{}
+}
+
+func Unalias(t types.Type) types.Type {
+	return t
+}
diff --git a/cl/internal/typesalias/alias_go122.go b/cl/internal/typesalias/alias_go122.go
new file mode 100644
index 00000000..d5f84735
--- /dev/null
+++ b/cl/internal/typesalias/alias_go122.go
@@ -0,0 +1,18 @@
+//go:build go1.22
+// +build go1.22
+
+package typesalias
+
+import "go/types"
+
+const Support = true
+
+type Alias = types.Alias
+
+func NewAlias(obj *types.TypeName, rhs types.Type) *Alias {
+	return types.NewAlias(obj, rhs)
+}
+
+func Unalias(t types.Type) types.Type {
+	return types.Unalias(t)
+}
diff --git a/cl/internal/typesalias/typeparams_go122.go b/cl/internal/typesalias/typeparams_go122.go
new file mode 100644
index 00000000..fc9ea9e8
--- /dev/null
+++ b/cl/internal/typesalias/typeparams_go122.go
@@ -0,0 +1,18 @@
+//go:build !go1.23
+// +build !go1.23
+
+package typesalias
+
+import "go/types"
+
+func TypeArgs(t *Alias) *types.TypeList {
+	return nil
+}
+
+func TypeParams(t *Alias) *types.TypeParamList {
+	return nil
+}
+
+func SetTypeParams(t *Alias, params []*types.TypeParam) {
+	panic("unsupport Alias.SetTypeParams")
+}
diff --git a/cl/internal/typesalias/typeparams_go123.go b/cl/internal/typesalias/typeparams_go123.go
new file mode 100644
index 00000000..58c63c77
--- /dev/null
+++ b/cl/internal/typesalias/typeparams_go123.go
@@ -0,0 +1,18 @@
+//go:build go1.23
+// +build go1.23
+
+package typesalias
+
+import "go/types"
+
+func TypeArgs(t *Alias) *types.TypeList {
+	return t.TypeArgs()
+}
+
+func TypeParams(t *Alias) *types.TypeParamList {
+	return t.TypeParams()
+}
+
+func SetTypeParams(t *Alias, params []*types.TypeParam) {
+	t.SetTypeParams(params)
+}
diff --git a/cl/typeparams.go b/cl/typeparams.go
index ede7ff34..54c3bfbd 100644
--- a/cl/typeparams.go
+++ b/cl/typeparams.go
@@ -20,6 +20,7 @@ import (
 	"go/types"
 
 	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/cl/internal/typesalias"
 	"github.com/goplus/gop/token"
 )
 
@@ -155,8 +156,13 @@ func initType(ctx *blockCtx, named *types.Named, spec *ast.TypeSpec) {
 		ctx.inInst = org
 	}()
 	typ := toType(ctx, spec.Type)
-	if named, ok := typ.(*types.Named); ok {
-		typ = getUnderlying(ctx, named)
+retry:
+	switch t := typ.(type) {
+	case *types.Named:
+		typ = getUnderlying(ctx, t)
+	case *typesalias.Alias:
+		typ = typesalias.Unalias(t)
+		goto retry
 	}
 	named.SetUnderlying(typ)
 }
diff --git a/cmd/xgo/bug_cmd.gox b/cmd/xgo/bug_cmd.gox
index e670a5eb..5beb7c7a 100644
--- a/cmd/xgo/bug_cmd.gox
+++ b/cmd/xgo/bug_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/bug"
 )
 
+use "bug"
+
 short "Start a bug report"
 
 flagOff
diff --git a/cmd/xgo/build_cmd.gox b/cmd/xgo/build_cmd.gox
index abc99b3b..83e615bd 100644
--- a/cmd/xgo/build_cmd.gox
+++ b/cmd/xgo/build_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/build"
 )
 
+use "build [flags] [packages]"
+
 short "Build Go+ files"
 
 flagOff
diff --git a/cmd/xgo/clean_cmd.gox b/cmd/xgo/clean_cmd.gox
index 308bfed3..cf9a9189 100644
--- a/cmd/xgo/clean_cmd.gox
+++ b/cmd/xgo/clean_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/clean"
 )
 
+use "clean [flags] <gopSrcDir>"
+
 short "Clean all Go+ auto generated files"
 
 flagOff
diff --git a/cmd/xgo/doc_cmd.gox b/cmd/xgo/doc_cmd.gox
index 5ec0c871..fd28b87d 100644
--- a/cmd/xgo/doc_cmd.gox
+++ b/cmd/xgo/doc_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/doc"
 )
 
+use "doc [flags] [pkgPath]"
+
 short "Show documentation for package or symbol"
 
 flagOff
diff --git a/cmd/xgo/env_cmd.gox b/cmd/xgo/env_cmd.gox
index 9131b3e6..3b41200b 100644
--- a/cmd/xgo/env_cmd.gox
+++ b/cmd/xgo/env_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/env"
 )
 
+use "env [flags] [var ...]"
+
 short "Prints Go+ environment information"
 
 flagOff
diff --git a/cmd/xgo/fmt_cmd.gox b/cmd/xgo/fmt_cmd.gox
index 7d6fc49e..e4475c0b 100644
--- a/cmd/xgo/fmt_cmd.gox
+++ b/cmd/xgo/fmt_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/gopfmt"
 )
 
+use "fmt [flags] path ..."
+
 short "Format Go+ packages"
 
 flagOff
diff --git a/cmd/xgo/get_cmd.gox b/cmd/xgo/get_cmd.gox
index cd7ed6e8..37a74482 100644
--- a/cmd/xgo/get_cmd.gox
+++ b/cmd/xgo/get_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/gopget"
 )
 
+use "get [flags] [packages]"
+
 short "Add dependencies to current module and install them"
 
 flagOff
diff --git a/cmd/xgo/go_cmd.gox b/cmd/xgo/go_cmd.gox
index 0767d7ed..124dff96 100644
--- a/cmd/xgo/go_cmd.gox
+++ b/cmd/xgo/go_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/gengo"
 )
 
+use "go [flags] [packages|files]"
+
 short "Convert Go+ code into Go code"
 
 flagOff
diff --git a/cmd/xgo/gop_autogen.go b/cmd/xgo/gop_autogen.go
index 2bfc9675..02de7325 100644
--- a/cmd/xgo/gop_autogen.go
+++ b/cmd/xgo/gop_autogen.go
@@ -3,422 +3,468 @@
 package main
 
 import (
-	fmt1 "fmt"
+	"fmt"
 	"github.com/goplus/cobra/xcmd"
-	bug1 "github.com/goplus/gop/cmd/internal/bug"
-	build1 "github.com/goplus/gop/cmd/internal/build"
-	clean1 "github.com/goplus/gop/cmd/internal/clean"
-	doc1 "github.com/goplus/gop/cmd/internal/doc"
-	env1 "github.com/goplus/gop/cmd/internal/env"
+	"github.com/goplus/gogen"
+	"github.com/goplus/gop/cmd/internal/bug"
+	"github.com/goplus/gop/cmd/internal/build"
+	"github.com/goplus/gop/cmd/internal/clean"
+	"github.com/goplus/gop/cmd/internal/doc"
+	"github.com/goplus/gop/cmd/internal/env"
 	"github.com/goplus/gop/cmd/internal/gengo"
 	"github.com/goplus/gop/cmd/internal/gopfmt"
 	"github.com/goplus/gop/cmd/internal/gopget"
-	install1 "github.com/goplus/gop/cmd/internal/install"
-	mod1 "github.com/goplus/gop/cmd/internal/mod"
-	run1 "github.com/goplus/gop/cmd/internal/run"
-	serve1 "github.com/goplus/gop/cmd/internal/serve"
-	test1 "github.com/goplus/gop/cmd/internal/test"
-	watch1 "github.com/goplus/gop/cmd/internal/watch"
-	env2 "github.com/goplus/gop/env"
+	"github.com/goplus/gop/cmd/internal/install"
+	"github.com/goplus/gop/cmd/internal/mod"
+	"github.com/goplus/gop/cmd/internal/run"
+	"github.com/goplus/gop/cmd/internal/serve"
+	"github.com/goplus/gop/cmd/internal/test"
+	"github.com/goplus/gop/cmd/internal/watch"
+	env1 "github.com/goplus/gop/env"
+	"github.com/qiniu/x/log"
 	"github.com/qiniu/x/stringutil"
 	"runtime"
 )
 
 const _ = true
 
-type bug struct {
+type Cmd_bug struct {
 	xcmd.Command
 	*App
 }
-type build struct {
+type Cmd_build struct {
 	xcmd.Command
 	*App
 }
-type clean struct {
+type Cmd_clean struct {
 	xcmd.Command
 	*App
 }
-type doc struct {
+type Cmd_doc struct {
 	xcmd.Command
 	*App
 }
-type env struct {
+type Cmd_env struct {
 	xcmd.Command
 	*App
 }
-type fmt struct {
+type Cmd_fmt struct {
 	xcmd.Command
 	*App
 }
-type get struct {
+type Cmd_get struct {
 	xcmd.Command
 	*App
 }
-type _go struct {
+type Cmd_go struct {
 	xcmd.Command
 	*App
 }
-type install struct {
+type Cmd_install struct {
 	xcmd.Command
 	*App
 }
-type mod struct {
+type App struct {
+	xcmd.App
+}
+type Cmd_mod struct {
 	xcmd.Command
 	*App
 }
-type mod_download struct {
+type Cmd_mod_download struct {
 	xcmd.Command
 	*App
 }
-type mod_init struct {
+type Cmd_mod_init struct {
 	xcmd.Command
 	*App
 }
-type mod_tidy struct {
+type Cmd_mod_tidy struct {
 	xcmd.Command
 	*App
 }
-type run struct {
+type Cmd_run struct {
 	xcmd.Command
 	*App
 }
-type serve struct {
+type Cmd_serve struct {
 	xcmd.Command
 	*App
 }
-type test struct {
+type Cmd_test struct {
 	xcmd.Command
 	*App
 }
-type version struct {
+type Cmd_version struct {
 	xcmd.Command
 	*App
 }
-type watch struct {
+type Cmd_watch struct {
 	xcmd.Command
 	*App
 }
-type App struct {
-	xcmd.App
+//line cmd/xgo/main_app.gox:6
+func (this *App) MainEntry() {
+//line cmd/xgo/main_app.gox:6:1
+	this.Short("xgo is a tool for managing xGo source code.")
+//line cmd/xgo/main_app.gox:7:1
+	log.SetFlags(log.Ldefault &^ log.LstdFlags)
+//line cmd/xgo/main_app.gox:9:1
+	gogen.GeneratedHeader = "// Code generated by xgo (xGo); DO NOT EDIT.\n\n"
 }
-
 func (this *App) Main() {
-	_gop_obj0 := &bug{App: this}
-	_gop_obj1 := &build{App: this}
-	_gop_obj2 := &clean{App: this}
-	_gop_obj3 := &doc{App: this}
-	_gop_obj4 := &env{App: this}
-	_gop_obj5 := &fmt{App: this}
-	_gop_obj6 := &get{App: this}
-	_gop_obj7 := &_go{App: this}
-	_gop_obj8 := &install{App: this}
-	_gop_obj9 := &mod{App: this}
-	_gop_obj10 := &mod_download{App: this}
-	_gop_obj11 := &mod_init{App: this}
-	_gop_obj12 := &mod_tidy{App: this}
-	_gop_obj13 := &run{App: this}
-	_gop_obj14 := &serve{App: this}
-	_gop_obj15 := &test{App: this}
-	_gop_obj16 := &version{App: this}
-	_gop_obj17 := &watch{App: this}
+	_gop_obj0 := &Cmd_bug{App: this}
+	_gop_obj1 := &Cmd_build{App: this}
+	_gop_obj2 := &Cmd_clean{App: this}
+	_gop_obj3 := &Cmd_doc{App: this}
+	_gop_obj4 := &Cmd_env{App: this}
+	_gop_obj5 := &Cmd_fmt{App: this}
+	_gop_obj6 := &Cmd_get{App: this}
+	_gop_obj7 := &Cmd_go{App: this}
+	_gop_obj8 := &Cmd_install{App: this}
+	_gop_obj9 := &Cmd_mod{App: this}
+	_gop_obj10 := &Cmd_mod_download{App: this}
+	_gop_obj11 := &Cmd_mod_init{App: this}
+	_gop_obj12 := &Cmd_mod_tidy{App: this}
+	_gop_obj13 := &Cmd_run{App: this}
+	_gop_obj14 := &Cmd_serve{App: this}
+	_gop_obj15 := &Cmd_test{App: this}
+	_gop_obj16 := &Cmd_version{App: this}
+	_gop_obj17 := &Cmd_watch{App: this}
 	xcmd.Gopt_App_Main(this, _gop_obj0, _gop_obj1, _gop_obj2, _gop_obj3, _gop_obj4, _gop_obj5, _gop_obj6, _gop_obj7, _gop_obj8, _gop_obj9, _gop_obj10, _gop_obj11, _gop_obj12, _gop_obj13, _gop_obj14, _gop_obj15, _gop_obj16, _gop_obj17)
 }
 //line cmd/xgo/bug_cmd.gox:20
-func (this *bug) Main(_gop_arg0 string) {
+func (this *Cmd_bug) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/bug_cmd.gox:20:1
-	this.Short("Start a bug report")
+	this.Use("bug")
 //line cmd/xgo/bug_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Start a bug report")
 //line cmd/xgo/bug_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/bug_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/bug_cmd.gox:25:1
-		bug1.Cmd.Run(bug1.Cmd, args)
+//line cmd/xgo/bug_cmd.gox:27:1
+		bug.Cmd.Run(bug.Cmd, args)
 	})
 }
-func (this *bug) Classfname() string {
+func (this *Cmd_bug) Classfname() string {
 	return "bug"
 }
 //line cmd/xgo/build_cmd.gox:20
-func (this *build) Main(_gop_arg0 string) {
+func (this *Cmd_build) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/build_cmd.gox:20:1
-	this.Short("Build Go+ files")
+	this.Use("build [flags] [packages]")
 //line cmd/xgo/build_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Build Go+ files")
 //line cmd/xgo/build_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/build_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/build_cmd.gox:25:1
-		build1.Cmd.Run(build1.Cmd, args)
+//line cmd/xgo/build_cmd.gox:27:1
+		build.Cmd.Run(build.Cmd, args)
 	})
 }
-func (this *build) Classfname() string {
+func (this *Cmd_build) Classfname() string {
 	return "build"
 }
 //line cmd/xgo/clean_cmd.gox:20
-func (this *clean) Main(_gop_arg0 string) {
+func (this *Cmd_clean) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/clean_cmd.gox:20:1
-	this.Short("Clean all Go+ auto generated files")
+	this.Use("clean [flags] <gopSrcDir>")
 //line cmd/xgo/clean_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Clean all Go+ auto generated files")
 //line cmd/xgo/clean_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/clean_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/clean_cmd.gox:25:1
-		clean1.Cmd.Run(clean1.Cmd, args)
+//line cmd/xgo/clean_cmd.gox:27:1
+		clean.Cmd.Run(clean.Cmd, args)
 	})
 }
-func (this *clean) Classfname() string {
+func (this *Cmd_clean) Classfname() string {
 	return "clean"
 }
 //line cmd/xgo/doc_cmd.gox:20
-func (this *doc) Main(_gop_arg0 string) {
+func (this *Cmd_doc) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/doc_cmd.gox:20:1
-	this.Short("Show documentation for package or symbol")
+	this.Use("doc [flags] [pkgPath]")
 //line cmd/xgo/doc_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Show documentation for package or symbol")
 //line cmd/xgo/doc_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/doc_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/doc_cmd.gox:25:1
-		doc1.Cmd.Run(doc1.Cmd, args)
+//line cmd/xgo/doc_cmd.gox:27:1
+		doc.Cmd.Run(doc.Cmd, args)
 	})
 }
-func (this *doc) Classfname() string {
+func (this *Cmd_doc) Classfname() string {
 	return "doc"
 }
 //line cmd/xgo/env_cmd.gox:20
-func (this *env) Main(_gop_arg0 string) {
+func (this *Cmd_env) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/env_cmd.gox:20:1
-	this.Short("Prints Go+ environment information")
+	this.Use("env [flags] [var ...]")
 //line cmd/xgo/env_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Prints Go+ environment information")
 //line cmd/xgo/env_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/env_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/env_cmd.gox:25:1
-		env1.Cmd.Run(env1.Cmd, args)
+//line cmd/xgo/env_cmd.gox:27:1
+		env.Cmd.Run(env.Cmd, args)
 	})
 }
-func (this *env) Classfname() string {
+func (this *Cmd_env) Classfname() string {
 	return "env"
 }
 //line cmd/xgo/fmt_cmd.gox:20
-func (this *fmt) Main(_gop_arg0 string) {
+func (this *Cmd_fmt) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/fmt_cmd.gox:20:1
-	this.Short("Format Go+ packages")
+	this.Use("fmt [flags] path ...")
 //line cmd/xgo/fmt_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Format Go+ packages")
 //line cmd/xgo/fmt_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/fmt_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/fmt_cmd.gox:25:1
+//line cmd/xgo/fmt_cmd.gox:27:1
 		gopfmt.Cmd.Run(gopfmt.Cmd, args)
 	})
 }
-func (this *fmt) Classfname() string {
+func (this *Cmd_fmt) Classfname() string {
 	return "fmt"
 }
 //line cmd/xgo/get_cmd.gox:20
-func (this *get) Main(_gop_arg0 string) {
+func (this *Cmd_get) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/get_cmd.gox:20:1
-	this.Short("Add dependencies to current module and install them")
+	this.Use("get [flags] [packages]")
 //line cmd/xgo/get_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Add dependencies to current module and install them")
 //line cmd/xgo/get_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/get_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/get_cmd.gox:25:1
+//line cmd/xgo/get_cmd.gox:27:1
 		gopget.Cmd.Run(gopget.Cmd, args)
 	})
 }
-func (this *get) Classfname() string {
+func (this *Cmd_get) Classfname() string {
 	return "get"
 }
 //line cmd/xgo/go_cmd.gox:20
-func (this *_go) Main(_gop_arg0 string) {
+func (this *Cmd_go) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/go_cmd.gox:20:1
-	this.Short("Convert Go+ code into Go code")
+	this.Use("go [flags] [packages|files]")
 //line cmd/xgo/go_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Convert Go+ code into Go code")
 //line cmd/xgo/go_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/go_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/go_cmd.gox:25:1
+//line cmd/xgo/go_cmd.gox:27:1
 		gengo.Cmd.Run(gengo.Cmd, args)
 	})
 }
-func (this *_go) Classfname() string {
+func (this *Cmd_go) Classfname() string {
 	return "go"
 }
 //line cmd/xgo/install_cmd.gox:20
-func (this *install) Main(_gop_arg0 string) {
+func (this *Cmd_install) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/install_cmd.gox:20:1
-	this.Short("Build Go+ files and install target to GOBIN")
+	this.Use("install [flags] [packages]")
 //line cmd/xgo/install_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Build Go+ files and install target to GOBIN")
 //line cmd/xgo/install_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/install_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/install_cmd.gox:25:1
-		install1.Cmd.Run(install1.Cmd, args)
+//line cmd/xgo/install_cmd.gox:27:1
+		install.Cmd.Run(install.Cmd, args)
 	})
 }
-func (this *install) Classfname() string {
+func (this *Cmd_install) Classfname() string {
 	return "install"
 }
 //line cmd/xgo/mod_cmd.gox:20
-func (this *mod) Main(_gop_arg0 string) {
+func (this *Cmd_mod) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/mod_cmd.gox:20:1
-	this.Short("Module maintenance")
+	this.Use("mod")
 //line cmd/xgo/mod_cmd.gox:22:1
+	this.Short("Module maintenance")
+//line cmd/xgo/mod_cmd.gox:24:1
 	this.Run__0(func() {
-//line cmd/xgo/mod_cmd.gox:23:1
+//line cmd/xgo/mod_cmd.gox:25:1
 		this.Help()
 	})
 }
-func (this *mod) Classfname() string {
+func (this *Cmd_mod) Classfname() string {
 	return "mod"
 }
 //line cmd/xgo/mod_download_cmd.gox:20
-func (this *mod_download) Main(_gop_arg0 string) {
+func (this *Cmd_mod_download) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/mod_download_cmd.gox:20:1
-	this.Short("download modules to local cache")
+	this.Use("download [flags] [modules]")
 //line cmd/xgo/mod_download_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("download modules to local cache")
 //line cmd/xgo/mod_download_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/mod_download_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/mod_download_cmd.gox:25:1
+//line cmd/xgo/mod_download_cmd.gox:27:1
 		if len(args) < 1 {
-//line cmd/xgo/mod_download_cmd.gox:26:1
+//line cmd/xgo/mod_download_cmd.gox:28:1
 			this.Help()
-//line cmd/xgo/mod_download_cmd.gox:27:1
+//line cmd/xgo/mod_download_cmd.gox:29:1
 			return
 		}
-//line cmd/xgo/mod_download_cmd.gox:29:1
-		mod1.CmdDownload.Run(mod1.CmdDownload, args)
+//line cmd/xgo/mod_download_cmd.gox:31:1
+		mod.CmdDownload.Run(mod.CmdDownload, args)
 	})
 }
-func (this *mod_download) Classfname() string {
+func (this *Cmd_mod_download) Classfname() string {
 	return "mod_download"
 }
 //line cmd/xgo/mod_init_cmd.gox:20
-func (this *mod_init) Main(_gop_arg0 string) {
+func (this *Cmd_mod_init) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/mod_init_cmd.gox:20:1
-	this.Short("initialize new module in current directory")
+	this.Use("init [flags] module-path")
 //line cmd/xgo/mod_init_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("initialize new module in current directory")
 //line cmd/xgo/mod_init_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/mod_init_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/mod_init_cmd.gox:25:1
+//line cmd/xgo/mod_init_cmd.gox:27:1
 		if len(args) < 1 {
-//line cmd/xgo/mod_init_cmd.gox:26:1
+//line cmd/xgo/mod_init_cmd.gox:28:1
 			this.Help()
-//line cmd/xgo/mod_init_cmd.gox:27:1
+//line cmd/xgo/mod_init_cmd.gox:29:1
 			return
 		}
-//line cmd/xgo/mod_init_cmd.gox:29:1
-		mod1.CmdInit.Run(mod1.CmdInit, args)
+//line cmd/xgo/mod_init_cmd.gox:31:1
+		mod.CmdInit.Run(mod.CmdInit, args)
 	})
 }
-func (this *mod_init) Classfname() string {
+func (this *Cmd_mod_init) Classfname() string {
 	return "mod_init"
 }
 //line cmd/xgo/mod_tidy_cmd.gox:20
-func (this *mod_tidy) Main(_gop_arg0 string) {
+func (this *Cmd_mod_tidy) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/mod_tidy_cmd.gox:20:1
-	this.Short("add missing and remove unused modules")
+	this.Use("tidy [flags]")
 //line cmd/xgo/mod_tidy_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("add missing and remove unused modules")
 //line cmd/xgo/mod_tidy_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/mod_tidy_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/mod_tidy_cmd.gox:25:1
-		mod1.CmdTidy.Run(mod1.CmdTidy, args)
+//line cmd/xgo/mod_tidy_cmd.gox:27:1
+		mod.CmdTidy.Run(mod.CmdTidy, args)
 	})
 }
-func (this *mod_tidy) Classfname() string {
+func (this *Cmd_mod_tidy) Classfname() string {
 	return "mod_tidy"
 }
 //line cmd/xgo/run_cmd.gox:20
-func (this *run) Main(_gop_arg0 string) {
+func (this *Cmd_run) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/run_cmd.gox:20:1
-	this.Short("Compile and run a Go+ program")
+	this.Use("run [flags] package [arguments...]")
 //line cmd/xgo/run_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Compile and run a Go+ program")
 //line cmd/xgo/run_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/run_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/run_cmd.gox:25:1
-		run1.Cmd.Run(run1.Cmd, args)
+//line cmd/xgo/run_cmd.gox:27:1
+		run.Cmd.Run(run.Cmd, args)
 	})
 }
-func (this *run) Classfname() string {
+func (this *Cmd_run) Classfname() string {
 	return "run"
 }
 //line cmd/xgo/serve_cmd.gox:20
-func (this *serve) Main(_gop_arg0 string) {
+func (this *Cmd_serve) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/serve_cmd.gox:20:1
-	this.Short("Serve as a Go+ LangServer")
+	this.Use("serve [flags]")
 //line cmd/xgo/serve_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Serve as a Go+ LangServer")
 //line cmd/xgo/serve_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/serve_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/serve_cmd.gox:25:1
-		serve1.Cmd.Run(serve1.Cmd, args)
+//line cmd/xgo/serve_cmd.gox:27:1
+		serve.Cmd.Run(serve.Cmd, args)
 	})
 }
-func (this *serve) Classfname() string {
+func (this *Cmd_serve) Classfname() string {
 	return "serve"
 }
 //line cmd/xgo/test_cmd.gox:20
-func (this *test) Main(_gop_arg0 string) {
+func (this *Cmd_test) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/test_cmd.gox:20:1
-	this.Short("Test Go+ packages")
+	this.Use("test [flags] [packages]")
 //line cmd/xgo/test_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Test Go+ packages")
 //line cmd/xgo/test_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/test_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/test_cmd.gox:25:1
-		test1.Cmd.Run(test1.Cmd, args)
+//line cmd/xgo/test_cmd.gox:27:1
+		test.Cmd.Run(test.Cmd, args)
 	})
 }
-func (this *test) Classfname() string {
+func (this *Cmd_test) Classfname() string {
 	return "test"
 }
 //line cmd/xgo/version_cmd.gox:21
-func (this *version) Main(_gop_arg0 string) {
+func (this *Cmd_version) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/version_cmd.gox:21:1
-	this.Short("Print Go+ version")
+	this.Use("version [flags]")
 //line cmd/xgo/version_cmd.gox:23:1
+	this.Short("Print Go+ version")
+//line cmd/xgo/version_cmd.gox:25:1
 	this.Run__0(func() {
-//line cmd/xgo/version_cmd.gox:24:1
-		fmt1.Println(stringutil.Concat("xgo ", env2.Version(), " ", runtime.GOOS, "/", runtime.GOARCH))
+//line cmd/xgo/version_cmd.gox:26:1
+		fmt.Println(stringutil.Concat("xgo ", env1.Version(), " ", runtime.GOOS, "/", runtime.GOARCH))
 	})
 }
-func (this *version) Classfname() string {
+func (this *Cmd_version) Classfname() string {
 	return "version"
 }
 //line cmd/xgo/watch_cmd.gox:20
-func (this *watch) Main(_gop_arg0 string) {
+func (this *Cmd_watch) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
 //line cmd/xgo/watch_cmd.gox:20:1
-	this.Short("Monitor code changes in a Go+ workspace to generate Go files")
+	this.Use("watch [flags] [dir]")
 //line cmd/xgo/watch_cmd.gox:22:1
-	this.FlagOff()
+	this.Short("Monitor code changes in a Go+ workspace to generate Go files")
 //line cmd/xgo/watch_cmd.gox:24:1
+	this.FlagOff()
+//line cmd/xgo/watch_cmd.gox:26:1
 	this.Run__1(func(args []string) {
-//line cmd/xgo/watch_cmd.gox:25:1
-		watch1.Cmd.Run(watch1.Cmd, args)
+//line cmd/xgo/watch_cmd.gox:27:1
+		watch.Cmd.Run(watch.Cmd, args)
 	})
 }
-func (this *watch) Classfname() string {
+func (this *Cmd_watch) Classfname() string {
 	return "watch"
 }
 func main() {
diff --git a/cmd/xgo/install_cmd.gox b/cmd/xgo/install_cmd.gox
index 3c0541ef..e86e03a7 100644
--- a/cmd/xgo/install_cmd.gox
+++ b/cmd/xgo/install_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/install"
 )
 
+use "install [flags] [packages]"
+
 short "Build Go+ files and install target to GOBIN"
 
 flagOff
diff --git a/cmd/xgo/main_app.gox b/cmd/xgo/main_app.gox
new file mode 100644
index 00000000..78af825a
--- /dev/null
+++ b/cmd/xgo/main_app.gox
@@ -0,0 +1,9 @@
+import (
+	"github.com/goplus/gogen"
+	"github.com/qiniu/x/log"
+)
+
+short "xgo is a tool for managing xGo source code."
+log.setFlags log.Ldefault&^log.LstdFlags
+
+gogen.GeneratedHeader = "// Code generated by xgo (xGo); DO NOT EDIT.\n\n"
diff --git a/cmd/xgo/mod_cmd.gox b/cmd/xgo/mod_cmd.gox
index 40005530..fde9ae57 100644
--- a/cmd/xgo/mod_cmd.gox
+++ b/cmd/xgo/mod_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/mod"
 )
 
+use "mod"
+
 short "Module maintenance"
 
 run => {
diff --git a/cmd/xgo/mod_download_cmd.gox b/cmd/xgo/mod_download_cmd.gox
index 6ccd1805..b0cedd99 100644
--- a/cmd/xgo/mod_download_cmd.gox
+++ b/cmd/xgo/mod_download_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/mod"
 )
 
+use "download [flags] [modules]"
+
 short "download modules to local cache"
 
 flagOff
diff --git a/cmd/xgo/mod_init_cmd.gox b/cmd/xgo/mod_init_cmd.gox
index ef41795c..ce8aff76 100644
--- a/cmd/xgo/mod_init_cmd.gox
+++ b/cmd/xgo/mod_init_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/mod"
 )
 
+use "init [flags] module-path"
+
 short "initialize new module in current directory"
 
 flagOff
diff --git a/cmd/xgo/mod_tidy_cmd.gox b/cmd/xgo/mod_tidy_cmd.gox
index 497cb45e..5e845fb5 100644
--- a/cmd/xgo/mod_tidy_cmd.gox
+++ b/cmd/xgo/mod_tidy_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/mod"
 )
 
+use "tidy [flags]"
+
 short "add missing and remove unused modules"
 
 flagOff
diff --git a/cmd/xgo/run_cmd.gox b/cmd/xgo/run_cmd.gox
index c50bb82c..b816126c 100644
--- a/cmd/xgo/run_cmd.gox
+++ b/cmd/xgo/run_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/run"
 )
 
+use "run [flags] package [arguments...]"
+
 short "Compile and run a Go+ program"
 
 flagOff
diff --git a/cmd/xgo/serve_cmd.gox b/cmd/xgo/serve_cmd.gox
index 2e3c2be1..2bb6815a 100644
--- a/cmd/xgo/serve_cmd.gox
+++ b/cmd/xgo/serve_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/serve"
 )
 
+use "serve [flags]"
+
 short "Serve as a Go+ LangServer"
 
 flagOff
diff --git a/cmd/xgo/version_cmd.gox b/cmd/xgo/version_cmd.gox
index 0e7d61ff..e63db27d 100644
--- a/cmd/xgo/version_cmd.gox
+++ b/cmd/xgo/version_cmd.gox
@@ -18,6 +18,8 @@ import (
 	"runtime"
 )
 
+use "version [flags]"
+
 short "Print Go+ version"
 
 run => {
diff --git a/cmd/xgo/watch_cmd.gox b/cmd/xgo/watch_cmd.gox
index 532107f1..5eb84692 100644
--- a/cmd/xgo/watch_cmd.gox
+++ b/cmd/xgo/watch_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/watch"
 )
 
+use "watch [flags] [dir]"
+
 short "Monitor code changes in a Go+ workspace to generate Go files"
 
 flagOff
diff --git a/go.mod b/go.mod
index e93a2864..699fd1f1 100644
--- a/go.mod
+++ b/go.mod
@@ -4,11 +4,11 @@ go 1.18
 
 require (
 	github.com/fsnotify/fsnotify v1.9.0
-	github.com/goplus/cobra v1.9.9 //gop:class
-	github.com/goplus/gogen v1.18.0
+	github.com/goplus/cobra v1.9.12 //gop:class
+	github.com/goplus/gogen v1.18.1
 	github.com/goplus/lib v0.2.0
 	github.com/goplus/mod v0.16.1
-	github.com/qiniu/x v1.14.0
+	github.com/qiniu/x v1.14.6
 )
 
 require (
diff --git a/go.sum b/go.sum
index 2be26ab7..bddfe69d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,15 +1,15 @@
 github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
 github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/goplus/cobra v1.9.9 h1:jUculMMZ5zLSc5tt+8OvJVLzOwd4gaZ73OAnqXAQvf0=
-github.com/goplus/cobra v1.9.9/go.mod h1:p4LhfNJDKEpiGjGiNn0crUXL5dUPA5DX2ztYpEJR34E=
-github.com/goplus/gogen v1.18.0 h1:B0PJE/4e8lfRDmrGJbElEB1weokD1vyOyyXb5eZdjfk=
-github.com/goplus/gogen v1.18.0/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI=
+github.com/goplus/cobra v1.9.12 h1:0F9EdEbeGyITGz+mqoHoJ5KpUw97p1CkxV74IexHw5s=
+github.com/goplus/cobra v1.9.12/go.mod h1:p4LhfNJDKEpiGjGiNn0crUXL5dUPA5DX2ztYpEJR34E=
+github.com/goplus/gogen v1.18.1 h1:ys18IiQwQ4QeQ3Mmk4vzql0Yfr6r+hkkQdjaL/M0Isc=
+github.com/goplus/gogen v1.18.1/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI=
 github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
 github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=
 github.com/goplus/mod v0.16.1 h1:2O+rKubJTn+Fighz3kckwpW87STSZ6s3m/YECfXuNhE=
 github.com/goplus/mod v0.16.1/go.mod h1:8d1P+pBavZfNQtJo4A742DgsLTtSf26BQn51owhmNqI=
-github.com/qiniu/x v1.14.0 h1:Dv30khHR5b1tA4ewilu8lsa5t/NTtGU435FApM4Q/SQ=
-github.com/qiniu/x v1.14.0/go.mod h1:AiovSOCaRijaf3fj+0CBOpR1457pn24b0Vdb1JpwhII=
+github.com/qiniu/x v1.14.6 h1:JY8jOumYFshuqNAjVkF6zsYhbcwM8A199ALkUOvJPks=
+github.com/qiniu/x v1.14.6/go.mod h1:AiovSOCaRijaf3fj+0CBOpR1457pn24b0Vdb1JpwhII=
 golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
 golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
diff --git a/parser/interface.go b/parser/interface.go
index e8f5b456..06e978fd 100644
--- a/parser/interface.go
+++ b/parser/interface.go
@@ -45,8 +45,6 @@ const (
 	DeclarationErrors = Mode(goparser.DeclarationErrors)
 	// AllErrors - report all errors (not just the first 10 on different lines)
 	AllErrors = Mode(goparser.AllErrors)
-	// SkipObjectResolution - don't resolve identifiers to objects - see ParseFile
-	SkipObjectResolution = Mode(goparser.SkipObjectResolution)
 
 	// ParseGoAsGoPlus - parse Go files by gop/parser
 	ParseGoAsGoPlus Mode = 1 << 16
@@ -103,8 +101,7 @@ func parseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast
 			// source is not a valid Go source file - satisfy
 			// ParseFile API and return a valid (but) empty *ast.File
 			f = &ast.File{
-				Name:  new(ast.Ident),
-				Scope: ast.NewScope(nil),
+				Name: new(ast.Ident),
 			}
 		}
 		f.Code = text
diff --git a/parser/parser.go b/parser/parser.go
index 918c00f9..192802f8 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -4062,14 +4062,27 @@ func (p *parser) parseGlobalStmts(sync map[token.Token]bool, pos token.Pos, stmt
 	if p.tok != token.EOF {
 		p.errorExpected(p.pos, "statement", 2)
 	}
+
+	// Determine appropriate positions for the block statement
+	// Use file beginning position as fallback when no statements are available
+	startPos := pos
+	endPos := p.pos
+	if len(list) > 0 {
+		startPos = list[0].Pos()
+		endPos = list[len(list)-1].End()
+	}
 	return &ast.FuncDecl{
-		Name: &ast.Ident{NamePos: pos, Name: "main"},
+		Name: ast.NewIdentEx(pos, "main", ast.ImplicitFun),
 		Doc:  doc,
 		Type: &ast.FuncType{
 			Func:   pos,
 			Params: &ast.FieldList{},
 		},
-		Body:   &ast.BlockStmt{List: list},
+		Body: &ast.BlockStmt{
+			Lbrace: startPos,
+			List:   list,
+			Rbrace: endPos,
+		},
 		Shadow: true,
 	}
 }
@@ -4111,7 +4124,7 @@ func (p *parser) parseFile() *ast.File {
 	} else {
 		noPkgDecl = true
 		pos = token.NoPos
-		ident = &ast.Ident{NamePos: p.file.Pos(0), Name: "main"}
+		ident = ast.NewIdentEx(p.file.Pos(0), "main", ast.ImplicitPkg)
 	}
 
 	p.openScope()
@@ -4140,26 +4153,12 @@ func (p *parser) parseFile() *ast.File {
 	assert(p.topScope == nil, "unbalanced scopes")
 	assert(p.labelScope == nil, "unbalanced label scopes")
 
-	// resolve global identifiers within the same file
-	i := 0
-	for _, ident := range p.unresolved {
-		// i <= index for current ident
-		assert(ident.Obj == unresolved, "object already resolved")
-		ident.Obj = p.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
-		if ident.Obj == nil {
-			p.unresolved[i] = ident
-			i++
-		}
-	}
-
 	return &ast.File{
 		Doc:         doc,
 		Package:     pos,
 		Name:        ident,
 		Decls:       decls,
-		Scope:       p.pkgScope,
 		Imports:     p.imports,
-		Unresolved:  p.unresolved[0:i],
 		Comments:    p.comments,
 		ShadowEntry: shadowEntry,
 		NoPkgDecl:   noPkgDecl,


Test patch:
diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go
index 44000f38..5343d19b 100644
--- a/cl/cltest/cltest.go
+++ b/cl/cltest/cltest.go
@@ -85,6 +85,11 @@ func LookupClass(ext string) (c *modfile.Project, ok bool) {
 				{Ext: ".t2spx", Class: "Sprite"},
 			},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
+	case ".t4gmx", ".t4spx":
+		return &modfile.Project{
+			Ext: ".t4gmx", Class: "*MyGame",
+			Works:    []*modfile.Class{{Ext: ".t4spx", Class: "Sprite"}},
+			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx4", "math"}}, true
 	case "_spx.gox":
 		return &modfile.Project{
 			Ext: "_spx.gox", Class: "Game",
diff --git a/cl/cltest/spx.go b/cl/cltest/spx.go
index 60034f86..56c26709 100644
--- a/cl/cltest/spx.go
+++ b/cl/cltest/spx.go
@@ -98,6 +98,26 @@ func SpxTest(t *testing.T, fs fsx.FileSystem, dir string, parseConf parser.Confi
 	}
 }
 
+func SpxErrorFS(t *testing.T, msg string, fs parser.FileSystem) {
+	pkgs, err := parser.ParseFSDir(Conf.Fset, fs, "/foo", spxParserConf())
+	if err != nil {
+		scanner.PrintError(os.Stderr, err)
+		t.Fatal("ParseFSDir:", err)
+	}
+	conf := *Conf
+	conf.RelativeBase = "/foo"
+	conf.Recorder = nil
+	conf.NoFileLine = false
+	bar := pkgs["main"]
+	_, err = cl.NewPackage("", bar, &conf)
+	if err == nil {
+		t.Fatal("no error?")
+	}
+	if ret := err.Error(); ret != msg {
+		t.Fatalf("\nError: \"%s\"\nExpected: \"%s\"\n", ret, msg)
+	}
+}
+
 func SpxErrorEx(t *testing.T, msg, gmx, spxcode, gmxfile, spxfile string) {
 	fs := memfs.TwoFiles("/foo", spxfile, spxcode, gmxfile, gmx)
 	pkgs, err := parser.ParseFSDir(Conf.Fset, fs, "/foo", spxParserConf())
diff --git a/cl/compile_gop_test.go b/cl/compile_gop_test.go
index fa1c1b7d..73543bda 100644
--- a/cl/compile_gop_test.go
+++ b/cl/compile_gop_test.go
@@ -972,6 +972,30 @@ d := bar.Var(bar.M)
 }
 
 func TestMixedOverloadNamed(t *testing.T) {
+	var expect string
+	if gotypesalias {
+		expect = `package main
+
+var a Var__0[int]
+var b Var__1[M]
+
+func main() {
+	c := Gopx_Var_Cast__0[string]()
+	d := Gopx_Var_Cast__1[M]()
+}
+`
+	} else {
+		expect = `package main
+
+var a Var__0[int]
+var b Var__1[map[string]interface{}]
+
+func main() {
+	c := Gopx_Var_Cast__0[string]()
+	d := Gopx_Var_Cast__1[map[string]interface{}]()
+}
+`
+	}
 	gopMixedClTest(t, "main", `package main
 
 type M = map[string]any
@@ -1000,16 +1024,7 @@ var a Var[int]
 var b Var[M]
 c := Var(string)
 d := Var(M)
-`, `package main
-
-var a Var__0[int]
-var b Var__1[map[string]interface{}]
-
-func main() {
-	c := Gopx_Var_Cast__0[string]()
-	d := Gopx_Var_Cast__1[map[string]interface{}]()
-}
-`)
+`, expect)
 }
 
 func TestStringLitBasic(t *testing.T) {
@@ -1077,6 +1092,28 @@ func main() {
 }
 
 func TestMixedGo(t *testing.T) {
+	var expect string
+	if gotypesalias {
+		expect = `package main
+
+var a [10]int
+var b string = f(n)
+var c foo2
+var d int = c.v
+var e = foo3{}
+var x string = c.Str()
+`
+	} else {
+		expect = `package main
+
+var a [10]int
+var b string = f(n)
+var c foo
+var d int = c.v
+var e = foo3{}
+var x string = c.Str()
+`
+	}
 	gopMixedClTest(t, "main", `package main
 
 import "strconv"
@@ -1111,15 +1148,7 @@ var c foo2
 var d int = c.v
 var e = foo3{}
 var x string = c.str
-`, `package main
-
-var a [10]int
-var b string = f(n)
-var c foo
-var d int = c.v
-var e = foo3{}
-var x string = c.Str()
-`, true)
+`, expect, true)
 	gopMixedClTest(t, "main", `package main
 type Point struct {
 	X int
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index fffc0c88..39aecf5d 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -21,6 +21,7 @@ import (
 
 	"github.com/goplus/gop/cl"
 	"github.com/goplus/gop/cl/cltest"
+	"github.com/goplus/gop/parser/fsx/memfs"
 )
 
 func gopSpxTest(t *testing.T, gmx, spxcode, expected string) {
@@ -43,6 +44,10 @@ func gopSpxErrorTestEx(t *testing.T, msg, gmx, spxcode, gmxfile, spxfile string)
 	cltest.SpxErrorEx(t, msg, gmx, spxcode, gmxfile, spxfile)
 }
 
+func gopSpxErrorTestMap(t *testing.T, msg string, dirs map[string][]string, files map[string]string) {
+	cltest.SpxErrorFS(t, msg, memfs.New(dirs, files))
+}
+
 func TestSpxError(t *testing.T) {
 	gopSpxErrorTestEx(t, `Game.tgmx:6:2: userScore redeclared
 	Game.tgmx:5:2 other declaration of userScore`, `
@@ -69,6 +74,33 @@ var (
 )
 println "hi"
 `, "Game.tgmx", "Kai.tspx")
+
+	gopSpxErrorTestEx(t, `Game.t4gmx:6:2: userScore redeclared
+	Game.t4gmx:5:2 other declaration of userScore
+Kai.t4spx:1:1: cannot use  (type *Kai) as type github.com/goplus/gop/cl/internal/spx4.Sprite in argument to `, `
+import "bytes"
+var (
+	Kai Kai
+	userScore int
+	userScore string
+)
+`, `
+println "hi"
+`, "Game.t4gmx", "Kai.t4spx")
+
+	gopSpxErrorTestMap(t, `Kai.t4spx:4:2: userScore redeclared
+	Kai.t4spx:3:2 other declaration of userScore
+Greem.t4spx:1:1: cannot use  (type *Greem) as type github.com/goplus/gop/cl/internal/spx4.Sprite in argument to `, map[string][]string{
+		"/foo": {"Game.t4gmx", "Kai.t4spx", "Greem.t4spx"},
+	}, map[string]string{
+		"/foo/Game.t4gmx": `println "hi"`,
+		"/foo/Kai.t4spx": `var (
+	Kai Kai
+	userScore int
+	userScore string
+)`,
+		"/foo/Greem.t4spx": ``,
+	})
 }
 
 func TestSpxBasic(t *testing.T) {
diff --git a/cl/compile_test.go b/cl/compile_test.go
index 323ff405..95ec8cc9 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -33,6 +33,7 @@ const (
 var (
 	gblConfLine  *cl.Config
 	gotypesalias bool
+	go1point     int
 )
 
 func init() {
@@ -47,6 +48,7 @@ func init() {
 		NoAutoGenMain: true,
 	}
 	gotypesalias = cltest.EnableTypesalias()
+	go1point = cltest.Go1Point()
 }
 
 func gopClNamedTest(t *testing.T, name string, gopcode, expected string) {
diff --git a/cl/typeparams_test.go b/cl/typeparams_test.go
index b55ba4fc..f94d8d43 100644
--- a/cl/typeparams_test.go
+++ b/cl/typeparams_test.go
@@ -146,7 +146,7 @@ type DataString = Data[string]
 type SliceString = Slice[[]string, string]
 
 func main() {
-	fmt.Println(Data[int]{1}.v)
+	fmt.Println(DataInt{1}.v)
 	fmt.Println(DataString{"hello"}.v)
 	fmt.Println(Data[int]{100}.v)
 	fmt.Println(Data[string]{"hello"}.v)
@@ -154,7 +154,7 @@ func main() {
 		X int
 		Y int
 	}]{}.v.X)
-	v1 := Slice[[]int, int]{}
+	v1 := SliceInt{}
 	v2 := SliceString{}
 	v3 := Slice[[]int, int]{}
 	v3.Append([]int{1, 2, 3, 4}...)
@@ -343,7 +343,7 @@ _ = At[int]
 
 func TestTypeParamsErrInferFunc(t *testing.T) {
 	var expect string
-	if cltest.Go1Point() == 24 {
+	if go1point == 24 {
 		expect = `b.gop:2:5: cannot infer T2 (declared at /foo/a.go:4:21)`
 	} else {
 		expect = `b.gop:2:5: cannot infer T2 (/foo/a.go:4:21)`
@@ -844,3 +844,51 @@ p.onCmd string, Message,"hello",(n, msg) => {
 }
 `)
 }
+
+func TestAliasTypeparams(t *testing.T) {
+	if !gotypesalias || go1point < 23 {
+		t.Skip()
+	}
+	t.Setenv("GOEXPERIMENT", "aliastypeparams")
+	gopMixedClTest(t, "main", `package main
+
+type Set[T comparable] = map[T]struct{}
+`, `
+set := Set[string]{
+	"go":  {},
+	"gop": {},
+	"gox": {},
+}
+echo set
+`, `package main
+
+import "fmt"
+
+func main() {
+	set := Set[string]{"go": struct {
+	}{}, "gop": struct {
+	}{}, "gox": struct {
+	}{}}
+	fmt.Println(set)
+}
+`)
+
+	gopMixedClTest(t, "main", `package main
+
+type Pair[T, U any] = struct {
+	First  T
+	Second U
+}
+`, `
+c := &Pair[string, bool]{"hello", true}
+echo c
+`, `package main
+
+import "fmt"
+
+func main() {
+	c := &Pair[string, bool]{"hello", true}
+	fmt.Println(c)
+}
+`)
+}
diff --git a/cmd/xgo/test_cmd.gox b/cmd/xgo/test_cmd.gox
index 6e980462..348ba666 100644
--- a/cmd/xgo/test_cmd.gox
+++ b/cmd/xgo/test_cmd.gox
@@ -17,6 +17,8 @@ import (
 	self "github.com/goplus/gop/cmd/internal/test"
 )
 
+use "test [flags] [packages]"
+
 short "Test Go+ packages"
 
 flagOff
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 46e082b1..f28f8df8 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -90,6 +90,27 @@ func testErrCode(t *testing.T, code string, errExp, panicExp string) {
 	}
 }
 
+func testShadowEntry(t *testing.T, code string, errExp string, decl *ast.FuncDecl) {
+	t.Helper()
+	fset := token.NewFileSet()
+	f, err := ParseEntry(fset, "/foo/bar.gop", code, Config{
+		Mode: ParseGoPlusClass | ParseComments | AllErrors,
+	})
+	if err != nil && err.Error() != errExp {
+		t.Fatal("testShadowEntry error:", err)
+	}
+	if err == nil && errExp != "" {
+		t.Fatal("testShadowEntry: nil")
+	}
+
+	if f.ShadowEntry == nil {
+		t.Fatal("testShadowEntry: nil")
+	}
+	if f.ShadowEntry.Body.Lbrace != decl.Body.Lbrace || f.ShadowEntry.Body.Rbrace != decl.Body.Rbrace {
+		t.Fatal("testShadowEntry: brace mismatch", f.ShadowEntry.Body.Lbrace, decl.Body.Lbrace, f.ShadowEntry.Body.Rbrace, decl.Body.Rbrace)
+	}
+}
+
 func testErrCodeParseExpr(t *testing.T, code string, errExp, panicExp string) {
 	defer func() {
 		if e := recover(); e != nil {
@@ -358,4 +379,58 @@ func TestNumberUnitLit(t *testing.T) {
 	p.toIdent(&ast.NumberUnitLit{})
 }
 
+func TestImplicitIdent(t *testing.T) {
+	if ast.NewIdentEx(100, "foo", ast.ImplicitPkg).End() != 100 {
+		t.Fatal("TestImplicitPkg: not 100")
+	}
+	if ast.NewIdentEx(100, "foo", ast.ImplicitFun).End() != 100 {
+		t.Fatal("TestImplicitFun: not 100")
+	}
+	if ast.NewIdentEx(100, "foo", ast.Fun).End() != 103 {
+		t.Fatal("TestFun: not 103")
+	}
+}
+
+func TestErrGlobalVarWithSyntaxError(t *testing.T) {
+	// Parse the code
+	testShadowEntry(t, `var (
+	foo int.=2
+	sprites []Sprite
+)
+
+func reset() {
+	foo = 10
+	sprites = make([]Sprite, 0)
+}
+
+onStart => {
+	reset()
+}
+`, `/foo/bar.gop:2:10: expected 'IDENT', found '=' (and 21 more errors)`, &ast.FuncDecl{
+		Body: &ast.BlockStmt{
+			Lbrace: 119,
+			Rbrace: 121,
+		},
+	})
+
+	testShadowEntry(t, `var (
+	foo int
+	sprites []Sprite
+)
+
+func reset() {
+	foo = 10
+	sprites = make([]Sprite, 0)
+}
+
+onStart => {
+	reset()
+}`, "", &ast.FuncDecl{
+		Body: &ast.BlockStmt{
+			Lbrace: 94,
+			Rbrace: 117,
+		},
+	})
+}
+
 // -----------------------------------------------------------------------------
diff --git a/x/typesutil/info_test.go b/x/typesutil/info_test.go
index 15942644..19a1b743 100644
--- a/x/typesutil/info_test.go
+++ b/x/typesutil/info_test.go
@@ -1580,36 +1580,43 @@ d := bar.Var(bar.M)
 }
 
 func TestMixedOverloadNamed(t *testing.T) {
-	testGopInfo(t, `
-var a Var[int]
-var b Var[M]
-c := Var(string)
-d := Var(M)
-`, `
-package main
-
-type M = map[string]any
-
-type basetype interface {
-	string | int | bool | float64
-}
-
-type Var__0[T basetype] struct {
-	val T
-}
-
-type Var__1[T map[string]any] struct {
-	val T
-}
-
-func Gopx_Var_Cast__0[T basetype]() *Var__0[T] {
-	return new(Var__0[T])
-}
-
-func Gopx_Var_Cast__1[T map[string]any]() *Var__1[T] {
-	return new(Var__1[T])
-}
-`, `== types ==
+	var expect string
+	if gotypesalias {
+		expect = `== types ==
+000:  2: 7 | Var                 *ast.Ident                     | type    : main.Var__0[int] | type
+001:  2: 7 | Var[int]            *ast.IndexExpr                 | type    : main.Var__0[int] | type
+002:  2:11 | int                 *ast.Ident                     | type    : int | type
+003:  3: 7 | Var                 *ast.Ident                     | type    : main.Var__1[main.M] | type
+004:  3: 7 | Var[M]              *ast.IndexExpr                 | type    : main.Var__1[main.M] | type
+005:  3:11 | M                   *ast.Ident                     | type    : main.M | type
+006:  4: 6 | Var                 *ast.Ident                     | value   : func[T main.basetype]() *main.Var__0[T] | value
+007:  4: 6 | Var(string)         *ast.CallExpr                  | value   : *main.Var__0[string] | value
+008:  4:10 | string              *ast.Ident                     | type    : string | type
+009:  5: 6 | Var                 *ast.Ident                     | value   : func[T map[string]interface{}]() *main.Var__1[T] | value
+010:  5: 6 | Var(M)              *ast.CallExpr                  | value   : *main.Var__1[main.M] | value
+011:  5:10 | M                   *ast.Ident                     | type    : main.M | type
+== defs ==
+000:  2: 5 | a                   | var main.a main.Var__0[int]
+001:  3: 5 | b                   | var main.b main.Var__1[main.M]
+002:  4: 1 | c                   | var c *main.Var__0[string]
+003:  4: 1 | main                | func main.main()
+004:  5: 1 | d                   | var d *main.Var__1[main.M]
+== uses ==
+000:  2: 7 | Var                 | type main.Var__0[T main.basetype] struct{val T}
+001:  2:11 | int                 | type int
+002:  3: 7 | Var                 | type main.Var__1[T map[string]any] struct{val T}
+003:  3:11 | M                   | type main.M = map[string]any
+004:  4: 6 | Var                 | func main.Gopx_Var_Cast__0[T main.basetype]() *main.Var__0[T]
+005:  4:10 | string              | type string
+006:  5: 6 | Var                 | func main.Gopx_Var_Cast__1[T map[string]any]() *main.Var__1[T]
+007:  5:10 | M                   | type main.M = map[string]any
+== overloads ==
+000:  2: 7 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})
+001:  3: 7 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})
+002:  4: 6 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})
+003:  5: 6 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})`
+	} else {
+		expect = `== types ==
 000:  2: 7 | Var                 *ast.Ident                     | type    : main.Var__0[int] | type
 001:  2: 7 | Var[int]            *ast.IndexExpr                 | type    : main.Var__0[int] | type
 002:  2:11 | int                 *ast.Ident                     | type    : int | type
@@ -1641,15 +1648,14 @@ func Gopx_Var_Cast__1[T map[string]any]() *Var__1[T] {
 000:  2: 7 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})
 001:  3: 7 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})
 002:  4: 6 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})
-003:  5: 6 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})`)
-}
+003:  5: 6 | Var                 | type main.Var = func(__gop_overload_args__ interface{_()})`
+	}
 
-func TestMixedRawNamed(t *testing.T) {
 	testGopInfo(t, `
-var a Var__0[int]
-var b Var__1[M]
-c := Gopx_Var_Cast__0[string]
-d := Gopx_Var_Cast__1[M]
+var a Var[int]
+var b Var[M]
+c := Var(string)
+d := Var(M)
 `, `
 package main
 
@@ -1674,7 +1680,42 @@ func Gopx_Var_Cast__0[T basetype]() *Var__0[T] {
 func Gopx_Var_Cast__1[T map[string]any]() *Var__1[T] {
 	return new(Var__1[T])
 }
-`, `== types ==
+`, expect)
+}
+
+func TestMixedRawNamed(t *testing.T) {
+	var expect string
+	if gotypesalias {
+		expect = `== types ==
+000:  2: 7 | Var__0              *ast.Ident                     | type    : main.Var__0[int] | type
+001:  2: 7 | Var__0[int]         *ast.IndexExpr                 | type    : main.Var__0[int] | type
+002:  2:14 | int                 *ast.Ident                     | type    : int | type
+003:  3: 7 | Var__1              *ast.Ident                     | type    : main.Var__1[main.M] | type
+004:  3: 7 | Var__1[M]           *ast.IndexExpr                 | type    : main.Var__1[main.M] | type
+005:  3:14 | M                   *ast.Ident                     | type    : main.M | type
+006:  4: 6 | Gopx_Var_Cast__0    *ast.Ident                     | value   : func[T main.basetype]() *main.Var__0[T] | value
+007:  4: 6 | Gopx_Var_Cast__0[string] *ast.IndexExpr                 | var     : func() *main.Var__0[string] | variable
+008:  4:23 | string              *ast.Ident                     | type    : string | type
+009:  5: 6 | Gopx_Var_Cast__1    *ast.Ident                     | value   : func[T map[string]interface{}]() *main.Var__1[T] | value
+010:  5: 6 | Gopx_Var_Cast__1[M] *ast.IndexExpr                 | var     : func() *main.Var__1[main.M] | variable
+011:  5:23 | M                   *ast.Ident                     | type    : main.M | type
+== defs ==
+000:  2: 5 | a                   | var main.a main.Var__0[int]
+001:  3: 5 | b                   | var main.b main.Var__1[main.M]
+002:  4: 1 | c                   | var c func() *main.Var__0[string]
+003:  4: 1 | main                | func main.main()
+004:  5: 1 | d                   | var d func() *main.Var__1[main.M]
+== uses ==
+000:  2: 7 | Var__0              | type main.Var__0[T main.basetype] struct{val T}
+001:  2:14 | int                 | type int
+002:  3: 7 | Var__1              | type main.Var__1[T map[string]any] struct{val T}
+003:  3:14 | M                   | type main.M = map[string]any
+004:  4: 6 | Gopx_Var_Cast__0    | func main.Gopx_Var_Cast__0[T main.basetype]() *main.Var__0[T]
+005:  4:23 | string              | type string
+006:  5: 6 | Gopx_Var_Cast__1    | func main.Gopx_Var_Cast__1[T map[string]any]() *main.Var__1[T]
+007:  5:23 | M                   | type main.M = map[string]any`
+	} else {
+		expect = `== types ==
 000:  2: 7 | Var__0              *ast.Ident                     | type    : main.Var__0[int] | type
 001:  2: 7 | Var__0[int]         *ast.IndexExpr                 | type    : main.Var__0[int] | type
 002:  2:14 | int                 *ast.Ident                     | type    : int | type
@@ -1701,7 +1742,38 @@ func Gopx_Var_Cast__1[T map[string]any]() *Var__1[T] {
 004:  4: 6 | Gopx_Var_Cast__0    | func main.Gopx_Var_Cast__0[T main.basetype]() *main.Var__0[T]
 005:  4:23 | string              | type string
 006:  5: 6 | Gopx_Var_Cast__1    | func main.Gopx_Var_Cast__1[T map[string]any]() *main.Var__1[T]
-007:  5:23 | M                   | type main.M = map[string]any`)
+007:  5:23 | M                   | type main.M = map[string]any`
+	}
+	testGopInfo(t, `
+var a Var__0[int]
+var b Var__1[M]
+c := Gopx_Var_Cast__0[string]
+d := Gopx_Var_Cast__1[M]
+`, `
+package main
+
+type M = map[string]any
+
+type basetype interface {
+	string | int | bool | float64
+}
+
+type Var__0[T basetype] struct {
+	val T
+}
+
+type Var__1[T map[string]any] struct {
+	val T
+}
+
+func Gopx_Var_Cast__0[T basetype]() *Var__0[T] {
+	return new(Var__0[T])
+}
+
+func Gopx_Var_Cast__1[T map[string]any]() *Var__1[T] {
+	return new(Var__1[T])
+}
+`, expect)
 }
 
 func TestSpxInfo(t *testing.T) {