Repository: goplus/gop
Pull request #1352 (2022-12-14 14:18:58)

support typeparam caller
- support go typeparams instance
```
import "sync/atomic"

var p atomic.Pointer[int]
i := 10
p.Store(&i)
println *p.Load()
```
- check  generic type without instantiation 
- `gopfmt` / `gop fmt` use go/format for .go


Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index d2aab151..2d810abb 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -10,7 +10,7 @@ jobs:
   Test:
     strategy:
       matrix:
-        go-version: [1.16.x, 1.17.x]
+        go-version: [1.16.x, 1.17.x, 1.18.x]
         os: [ubuntu-latest, windows-latest, macos-11]
     runs-on: ${{ matrix.os }}
     steps:
diff --git a/ast/ast.go b/ast/ast.go
index 28e546bd..77a55156 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -221,6 +221,15 @@ type (
 		Rbrack token.Pos // position of "]"
 	}
 
+	// An IndexListExpr node represents an expression followed by multiple
+	// indices.
+	IndexListExpr struct {
+		X       Expr      // expression
+		Lbrack  token.Pos // position of "["
+		Indices []Expr    // index expressions
+		Rbrack  token.Pos // position of "]"
+	}
+
 	// A SliceExpr node represents an expression followed by slice indices.
 	SliceExpr struct {
 		X      Expr      // expression
@@ -316,9 +325,10 @@ type (
 
 	// A FuncType node represents a function type.
 	FuncType struct {
-		Func    token.Pos  // position of "func" keyword (token.NoPos if there is no "func")
-		Params  *FieldList // (incoming) parameters; non-nil
-		Results *FieldList // (outgoing) results; or nil
+		Func       token.Pos  // position of "func" keyword (token.NoPos if there is no "func")
+		TypeParams *FieldList // type parameters; or nil
+		Params     *FieldList // (incoming) parameters; non-nil
+		Results    *FieldList // (outgoing) results; or nil
 	}
 
 	// An InterfaceType node represents an interface type.
@@ -378,6 +388,9 @@ func (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() }
 // Pos returns position of first character belonging to the node.
 func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() }
 
+// Pos returns position of first character belonging to the node.
+func (x *IndexListExpr) Pos() token.Pos { return x.X.Pos() }
+
 // Pos returns position of first character belonging to the node.
 func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() }
 
@@ -454,6 +467,9 @@ func (x *SelectorExpr) End() token.Pos { return x.Sel.End() }
 // End returns position of first character immediately after the node.
 func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
 
+// End returns position of first character immediately after the node.
+func (x *IndexListExpr) End() token.Pos { return x.Rbrack + 1 }
+
 // End returns position of first character immediately after the node.
 func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
 
@@ -520,6 +536,7 @@ func (*CompositeLit) exprNode()   {}
 func (*ParenExpr) exprNode()      {}
 func (*SelectorExpr) exprNode()   {}
 func (*IndexExpr) exprNode()      {}
+func (*IndexListExpr) exprNode()  {}
 func (*SliceExpr) exprNode()      {}
 func (*TypeAssertExpr) exprNode() {}
 func (*CallExpr) exprNode()       {}
@@ -951,11 +968,12 @@ type (
 
 	// A TypeSpec node represents a type declaration (TypeSpec production).
 	TypeSpec struct {
-		Doc     *CommentGroup // associated documentation; or nil
-		Name    *Ident        // type name
-		Assign  token.Pos     // position of '=', if any
-		Type    Expr          // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
-		Comment *CommentGroup // line comments; or nil
+		Doc        *CommentGroup // associated documentation; or nil
+		Name       *Ident        // type name
+		TypeParams *FieldList    // type parameters; or nil
+		Assign     token.Pos     // position of '=', if any
+		Type       Expr          // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
+		Comment    *CommentGroup // line comments; or nil
 	}
 )
 
diff --git a/ast/fromgo/gopast.go b/ast/fromgo/gopast.go
index aa1618e9..f15b3b3e 100644
--- a/ast/fromgo/gopast.go
+++ b/ast/fromgo/gopast.go
@@ -24,6 +24,8 @@ import (
 
 	gopast "github.com/goplus/gop/ast"
 	goptoken "github.com/goplus/gop/token"
+
+	"github.com/goplus/gop/ast/fromgo/typeparams"
 )
 
 // ----------------------------------------------------------------------------
@@ -116,6 +118,13 @@ func gopExpr(val ast.Expr) gopast.Expr {
 			Index:  gopExpr(v.Index),
 			Rbrack: v.Rbrack,
 		}
+	case *typeparams.IndexListExpr:
+		return &gopast.IndexListExpr{
+			X:       gopExpr(v.X),
+			Lbrack:  v.Lbrack,
+			Indices: gopExprs(v.Indices),
+			Rbrack:  v.Rbrack,
+		}
 	case *ast.ParenExpr:
 		return &gopast.ParenExpr{
 			Lparen: v.Lparen,
@@ -173,9 +182,10 @@ func gopExprs(vals []ast.Expr) []gopast.Expr {
 
 func gopFuncType(v *ast.FuncType) *gopast.FuncType {
 	return &gopast.FuncType{
-		Func:    v.Func,
-		Params:  gopFieldList(v.Params),
-		Results: gopFieldList(v.Results),
+		Func:       v.Func,
+		TypeParams: gopFieldList(typeparams.ForFuncType(v)),
+		Params:     gopFieldList(v.Params),
+		Results:    gopFieldList(v.Results),
 	}
 }
 
@@ -254,9 +264,10 @@ func gopImportSpec(spec *ast.ImportSpec) *gopast.ImportSpec {
 
 func gopTypeSpec(spec *ast.TypeSpec) *gopast.TypeSpec {
 	return &gopast.TypeSpec{
-		Name:   gopIdent(spec.Name),
-		Assign: spec.Assign,
-		Type:   gopType(spec.Type),
+		Name:       gopIdent(spec.Name),
+		TypeParams: gopFieldList(typeparams.ForTypeSpec(spec)),
+		Assign:     spec.Assign,
+		Type:       gopType(spec.Type),
 	}
 }
 
diff --git a/ast/fromgo/typeparams/typeparams_go117.go b/ast/fromgo/typeparams/typeparams_go117.go
new file mode 100644
index 00000000..d3377028
--- /dev/null
+++ b/ast/fromgo/typeparams/typeparams_go117.go
@@ -0,0 +1,38 @@
+//go:build !go1.18
+// +build !go1.18
+
+package typeparams
+
+import (
+	"go/ast"
+	"go/token"
+)
+
+func unsupported() {
+	panic("type parameters are unsupported at this go version")
+}
+
+// IndexListExpr is a placeholder type, as type parameters are not supported at
+// this Go version. Its methods panic on use.
+type IndexListExpr struct {
+	ast.Expr
+	X       ast.Expr   // expression
+	Lbrack  token.Pos  // position of "["
+	Indices []ast.Expr // index expressions
+	Rbrack  token.Pos  // position of "]"
+}
+
+func (*IndexListExpr) Pos() token.Pos { unsupported(); return token.NoPos }
+func (*IndexListExpr) End() token.Pos { unsupported(); return token.NoPos }
+
+// ForFuncType returns an empty field list, as type parameters are not
+// supported at this Go version.
+func ForFuncType(*ast.FuncType) *ast.FieldList {
+	return nil
+}
+
+// ForTypeSpec returns an empty field list, as type parameters are not
+// supported at this Go version.
+func ForTypeSpec(*ast.TypeSpec) *ast.FieldList {
+	return nil
+}
diff --git a/ast/fromgo/typeparams/typeparams_go118.go b/ast/fromgo/typeparams/typeparams_go118.go
new file mode 100644
index 00000000..1e5d9876
--- /dev/null
+++ b/ast/fromgo/typeparams/typeparams_go118.go
@@ -0,0 +1,25 @@
+//go:build go1.18
+// +build go1.18
+
+package typeparams
+
+import "go/ast"
+
+// IndexListExpr is an alias for ast.IndexListExpr.
+type IndexListExpr = ast.IndexListExpr
+
+// ForFuncType returns n.TypeParams.
+func ForFuncType(n *ast.FuncType) *ast.FieldList {
+	if n == nil {
+		return nil
+	}
+	return n.TypeParams
+}
+
+// ForTypeSpec returns n.TypeParams.
+func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList {
+	if n == nil {
+		return nil
+	}
+	return n.TypeParams
+}
diff --git a/cl/compile.go b/cl/compile.go
index d0d070e5..29475e31 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -128,6 +128,9 @@ func (p *nodeInterp) LoadExpr(node ast.Node) (src string, pos token.Position) {
 	start := node.Pos()
 	pos = p.fset.Position(start)
 	f := p.files[pos.Filename]
+	if f == nil { // not found
+		return
+	}
 	n := int(node.End() - start)
 	pos.Filename = relFile(p.workingDir, pos.Filename)
 	if pos.Offset+n < 0 {
@@ -238,11 +241,14 @@ func doInitMethods(ld *typeLoader) {
 type pkgCtx struct {
 	*nodeInterp
 	*gmxSettings
-	cpkgs *cpackages.Importer
-	syms  map[string]loader
-	inits []func()
-	tylds []*typeLoader
-	errs  errors.List
+	cpkgs    *cpackages.Importer
+	syms     map[string]loader
+	generics map[string]bool // generic type record
+	inits    []func()
+	tylds    []*typeLoader
+	idents   []*ast.Ident // toType ident recored
+	errs     errors.List
+	inInst   int // toType in generic instance
 }
 
 type blockCtx struct {
@@ -253,6 +259,7 @@ type blockCtx struct {
 	imports      map[string]*gox.PkgRef
 	lookups      []*gox.PkgRef
 	clookups     []*cpackages.PkgRef
+	tlookup      *typeParamLookup
 	c2goBase     string // default is `github.com/goplus/`
 	targetDir    string
 	classRecv    *ast.FieldList // available when gmxSettings != nil
@@ -365,7 +372,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		fset: fset, files: files, workingDir: workingDir,
 	}
 	ctx := &pkgCtx{
-		syms: make(map[string]loader), nodeInterp: interp,
+		syms: make(map[string]loader), nodeInterp: interp, generics: make(map[string]bool),
 	}
 	confGox := &gox.Config{
 		Fset:            fset,
@@ -724,6 +731,7 @@ func preloadFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, genCod
 							}
 						}
 					} else {
+						ctx.generics[name] = true
 						ld.typ = func() {
 							pkg := ctx.pkg.Types
 							if t.Assign != token.NoPos { // alias type
@@ -737,11 +745,12 @@ func preloadFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, genCod
 								log.Println("==> Load > NewType", name)
 							}
 							named := newType(pkg, t.Pos(), name)
+
 							ld.typInit = func() { // decycle
 								if debugLoad {
 									log.Println("==> Load > InitType", name)
 								}
-								initType(ctx, named, toType(ctx, t.Type))
+								initType(ctx, named, t)
 							}
 						}
 					}
@@ -798,13 +807,6 @@ func newType(pkg *types.Package, pos token.Pos, name string) *types.Named {
 	return types.NewNamed(typName, nil, nil)
 }
 
-func initType(ctx *blockCtx, named *types.Named, typ types.Type) {
-	if named, ok := typ.(*types.Named); ok {
-		typ = getUnderlying(ctx, named)
-	}
-	named.SetUnderlying(typ)
-}
-
 func aliasType(pkg *types.Package, pos token.Pos, name string, typ types.Type) {
 	o := types.NewTypeName(pos, pkg, name, typ)
 	pkg.Scope().Insert(o)
@@ -823,7 +825,7 @@ func declFunc(ctx *blockCtx, recv *types.Var, d *ast.FuncDecl) {
 		return
 	}
 	pkg := ctx.pkg.Types
-	sig := toFuncType(ctx, d.Type, recv)
+	sig := toFuncType(ctx, d.Type, recv, d)
 	fn := types.NewFunc(d.Pos(), pkg, name, sig)
 	if recv != nil {
 		typ := recv.Type()
@@ -868,7 +870,7 @@ func loadFunc(ctx *blockCtx, recv *types.Var, d *ast.FuncDecl) {
 			}
 		}
 	}
-	sig := toFuncType(ctx, d.Type, recv)
+	sig := toFuncType(ctx, d.Type, recv, d)
 	fn, err := ctx.pkg.NewFuncWith(d.Pos(), name, sig, func() token.Pos {
 		return d.Recv.List[0].Type.Pos()
 	})
diff --git a/cl/expr.go b/cl/expr.go
index 6fcad90f..99e7624a 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -18,7 +18,6 @@ package cl
 
 import (
 	"bytes"
-	"github.com/goplus/gop/printer"
 	goast "go/ast"
 	gotoken "go/token"
 	"go/types"
@@ -29,6 +28,7 @@ import (
 	"strings"
 
 	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/printer"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 	"github.com/goplus/gox/cpackages"
@@ -230,6 +230,8 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 		compileRangeExpr(ctx, v)
 	case *ast.IndexExpr:
 		compileIndexExpr(ctx, v, twoValue(inFlags))
+	case *ast.IndexListExpr:
+		compileIndexListExpr(ctx, v, twoValue(inFlags))
 	case *ast.SliceExpr:
 		compileSliceExpr(ctx, v)
 	case *ast.StarExpr:
@@ -253,7 +255,7 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 	case *ast.ErrWrapExpr:
 		compileErrWrapExpr(ctx, v, 0)
 	case *ast.FuncType:
-		ctx.cb.Typ(toFuncType(ctx, v, nil), v)
+		ctx.cb.Typ(toFuncType(ctx, v, nil, nil), v)
 	case *ast.Ellipsis:
 		panic("compileEllipsis: ast.Ellipsis unexpected")
 	case *ast.KeyValueExpr:
@@ -313,6 +315,15 @@ func compileIndexExpr(ctx *blockCtx, v *ast.IndexExpr, twoValue bool) { // x[i]
 	ctx.cb.Index(1, twoValue, v)
 }
 
+func compileIndexListExpr(ctx *blockCtx, v *ast.IndexListExpr, twoValue bool) { // fn[t1,t2]
+	compileExpr(ctx, v.X)
+	n := len(v.Indices)
+	for i := 0; i < n; i++ {
+		compileExpr(ctx, v.Indices[i])
+	}
+	ctx.cb.Index(n, twoValue, v)
+}
+
 func compileSliceExpr(ctx *blockCtx, v *ast.SliceExpr) { // x[i:j:k]
 	compileExpr(ctx, v.X)
 	compileExprOrNone(ctx, v.Low)
@@ -634,7 +645,7 @@ func compileLambdaExpr2(ctx *blockCtx, v *ast.LambdaExpr2, sig *types.Signature)
 func compileFuncLit(ctx *blockCtx, v *ast.FuncLit) {
 	cb := ctx.cb
 	comments, once := cb.BackupComments()
-	sig := toFuncType(ctx, v.Type, nil)
+	sig := toFuncType(ctx, v.Type, nil, nil)
 	fn := cb.NewClosureWith(sig)
 	if body := v.Body; body != nil {
 		loadFuncBody(ctx, fn, body)
@@ -839,7 +850,11 @@ func compileSliceLit(ctx *blockCtx, v *ast.SliceLit, typ types.Type) {
 	for _, elt := range v.Elts {
 		compileExpr(ctx, elt)
 	}
-	ctx.cb.SliceLit(typ, n)
+	if sliceHasTypeParam(ctx, typ) {
+		ctx.cb.SliceLit(nil, n)
+	} else {
+		ctx.cb.SliceLit(typ, n)
+	}
 }
 
 func compileRangeExpr(ctx *blockCtx, v *ast.RangeExpr) {
@@ -1064,5 +1079,4 @@ func sprintAst(fset *token.FileSet, x ast.Node) string {
 	return buf.String()
 }
 
-
 // -----------------------------------------------------------------------------
diff --git a/cl/func_type_and_var.go b/cl/func_type_and_var.go
index 277c163e..89b5965a 100644
--- a/cl/func_type_and_var.go
+++ b/cl/func_type_and_var.go
@@ -31,26 +31,22 @@ import (
 
 // -----------------------------------------------------------------------------
 
-func toFuncType(ctx *blockCtx, typ *ast.FuncType, recv *types.Var) *types.Signature {
-	params, variadic := toParams(ctx, typ.Params.List)
-	results := toResults(ctx, typ.Results)
-	return types.NewSignature(recv, params, results, variadic)
-}
-
 func toRecv(ctx *blockCtx, recv *ast.FieldList) *types.Var {
 	v := recv.List[0]
 	var name string
 	if len(v.Names) > 0 {
 		name = v.Names[0].Name
 	}
-	return ctx.pkg.NewParam(v.Pos(), name, toType(ctx, v.Type))
+	typ, star := getRecvType(v.Type)
+	t := toType(ctx, typ)
+	if star {
+		t = types.NewPointer(t)
+	}
+	return ctx.pkg.NewParam(v.Pos(), name, t)
 }
 
 func getRecvTypeName(ctx *pkgCtx, recv *ast.FieldList, handleErr bool) (string, bool) {
-	typ := recv.List[0].Type
-	if t, ok := typ.(*ast.StarExpr); ok {
-		typ = t.X
-	}
+	typ, _ := getRecvType(recv.List[0].Type)
 	if t, ok := typ.(*ast.Ident); ok {
 		return t.Name, true
 	}
@@ -104,7 +100,28 @@ func toParam(ctx *blockCtx, fld *ast.Field, args []*gox.Param) []*gox.Param {
 func toType(ctx *blockCtx, typ ast.Expr) types.Type {
 	switch v := typ.(type) {
 	case *ast.Ident:
-		return toIdentType(ctx, v)
+		if enableTypeParams {
+			ctx.idents = append(ctx.idents, v)
+			defer func() {
+				ctx.idents = ctx.idents[:len(ctx.idents)-1]
+			}()
+		}
+		typ := toIdentType(ctx, v)
+		if enableTypeParams && ctx.inInst == 0 {
+			if t, ok := typ.(*types.Named); ok {
+				if namedIsTypeParams(ctx, t) {
+					pos := ctx.idents[0].Pos()
+					for _, i := range ctx.idents {
+						if i.Name == v.Name {
+							pos = i.Pos()
+							break
+						}
+					}
+					panic(ctx.newCodeErrorf(pos, "cannot use generic type %v without instantiation", t.Obj().Type()))
+				}
+			}
+		}
+		return typ
 	case *ast.StarExpr:
 		elem := toType(ctx, v.X)
 		return types.NewPointer(elem)
@@ -122,11 +139,27 @@ func toType(ctx *blockCtx, typ ast.Expr) types.Type {
 	case *ast.ChanType:
 		return toChanType(ctx, v)
 	case *ast.FuncType:
-		return toFuncType(ctx, v, nil)
+		return toFuncType(ctx, v, nil, nil)
 	case *ast.SelectorExpr:
-		return toExternalType(ctx, v)
+		typ := toExternalType(ctx, v)
+		if enableTypeParams && ctx.inInst == 0 {
+			if t, ok := typ.(*types.Named); ok {
+				if namedIsTypeParams(ctx, t) {
+					panic(ctx.newCodeErrorf(v.Pos(), "cannot use generic type %v without instantiation", t.Obj().Type()))
+				}
+			}
+		}
+		return typ
 	case *ast.ParenExpr:
 		return toType(ctx, v.X)
+	case *ast.BinaryExpr:
+		return toBinaryExprType(ctx, v)
+	case *ast.UnaryExpr:
+		return toUnaryExprType(ctx, v)
+	case *ast.IndexExpr:
+		return toIndexType(ctx, v)
+	case *ast.IndexListExpr:
+		return toIndexListType(ctx, v)
 	}
 	log.Panicln("toType: unknown -", reflect.TypeOf(typ))
 	return nil
@@ -166,6 +199,11 @@ Name context:
 // ---------------------------------------------------------------------------*/
 
 func toIdentType(ctx *blockCtx, ident *ast.Ident) types.Type {
+	if ctx.tlookup != nil {
+		if typ := ctx.tlookup.Lookup(ident.Name); typ != nil {
+			return typ
+		}
+	}
 	v, builtin := lookupType(ctx, ident.Name)
 	if isBuiltin(builtin) {
 		panic(ctx.newCodeErrorf(ident.Pos(), "use of builtin %s not in function call", ident.Name))
@@ -225,7 +263,7 @@ func toStructType(ctx *blockCtx, v *ast.StructType) *types.Struct {
 	}
 	for _, field := range fieldList {
 		typ := toType(ctx, field.Type)
-		if field.Names == nil { // embedded
+		if len(field.Names) == 0 { // embedded
 			name := getTypeName(typ)
 			if chkRedecl(name, field.Type.Pos()) {
 				continue
@@ -318,7 +356,7 @@ func toInterfaceType(ctx *blockCtx, v *ast.InterfaceType) types.Type {
 	var methods []*types.Func
 	var embeddeds []types.Type
 	for _, m := range methodsList {
-		if m.Names == nil { // embedded
+		if len(m.Names) == 0 { // embedded
 			typ := toType(ctx, m.Type)
 			if t, ok := typ.(*types.Named); ok { // #1198: embedded type should ensure loaded
 				ctx.loadNamed(ctx.pkg, t)
@@ -327,13 +365,37 @@ func toInterfaceType(ctx *blockCtx, v *ast.InterfaceType) types.Type {
 			continue
 		}
 		name := m.Names[0].Name
-		sig := toFuncType(ctx, m.Type.(*ast.FuncType), nil)
+		sig := toFuncType(ctx, m.Type.(*ast.FuncType), nil, nil)
 		methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
 	}
 	intf := types.NewInterfaceType(methods, embeddeds).Complete()
 	return intf
 }
 
+func toIndexType(ctx *blockCtx, v *ast.IndexExpr) types.Type {
+	ctx.inInst++
+	defer func() {
+		ctx.inInst--
+	}()
+	ctx.cb.Typ(toType(ctx, v.X), v.X)
+	ctx.cb.Typ(toType(ctx, v.Index), v.Index)
+	ctx.cb.Index(1, false, v)
+	return ctx.cb.InternalStack().Pop().Type.(*gox.TypeType).Type()
+}
+
+func toIndexListType(ctx *blockCtx, v *ast.IndexListExpr) types.Type {
+	ctx.inInst++
+	defer func() {
+		ctx.inInst--
+	}()
+	ctx.cb.Typ(toType(ctx, v.X), v.X)
+	for _, index := range v.Indices {
+		ctx.cb.Typ(toType(ctx, index), index)
+	}
+	ctx.cb.Index(len(v.Indices), false, v)
+	return ctx.cb.InternalStack().Pop().Type.(*gox.TypeType).Type()
+}
+
 // -----------------------------------------------------------------------------
 
 func toString(l *ast.BasicLit) string {
diff --git a/cl/types_go117.go b/cl/types_go117.go
new file mode 100644
index 00000000..2f32f916
--- /dev/null
+++ b/cl/types_go117.go
@@ -0,0 +1,81 @@
+//go:build !go1.18
+// +build !go1.18
+
+/*
+ * 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 cl
+
+import (
+	"go/types"
+
+	"github.com/goplus/gop/ast"
+)
+
+const enableTypeParams = false
+
+func toBinaryExprType(ctx *blockCtx, v *ast.BinaryExpr) types.Type {
+	panic("type parameters are unsupported at this go version")
+}
+
+func toUnaryExprType(ctx *blockCtx, v *ast.UnaryExpr) types.Type {
+	panic("type parameters are unsupported at this go version")
+}
+
+type typeParamLookup struct {
+}
+
+func (p *typeParamLookup) Lookup(name string) types.Type {
+	return nil
+}
+
+func toFuncType(ctx *blockCtx, typ *ast.FuncType, recv *types.Var, d *ast.FuncDecl) *types.Signature {
+	params, variadic := toParams(ctx, typ.Params.List)
+	results := toResults(ctx, typ.Results)
+	return types.NewSignature(recv, params, results, variadic)
+}
+
+func initType(ctx *blockCtx, named *types.Named, spec *ast.TypeSpec) {
+	typ := toType(ctx, spec.Type)
+	if named, ok := typ.(*types.Named); ok {
+		typ = getUnderlying(ctx, named)
+	}
+	named.SetUnderlying(typ)
+}
+
+func getRecvType(typ ast.Expr) (ast.Expr, bool) {
+	var ptr bool
+L:
+	for {
+		switch t := typ.(type) {
+		case *ast.ParenExpr:
+			typ = t.X
+		case *ast.StarExpr:
+			ptr = true
+			typ = t.X
+		default:
+			break L
+		}
+	}
+	return typ, ptr
+}
+
+func sliceHasTypeParam(ctx *blockCtx, typ types.Type) bool {
+	return false
+}
+
+func namedIsTypeParams(ctx *blockCtx, t *types.Named) bool {
+	return false
+}
diff --git a/cl/types_go118.go b/cl/types_go118.go
new file mode 100644
index 00000000..4c723913
--- /dev/null
+++ b/cl/types_go118.go
@@ -0,0 +1,305 @@
+//go:build go1.18
+// +build go1.18
+
+/*
+ * Copyright (c) 2022 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 cl
+
+import (
+	"go/types"
+
+	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/token"
+)
+
+const enableTypeParams = true
+
+func toTermList(ctx *blockCtx, expr ast.Expr) []*types.Term {
+retry:
+	switch v := expr.(type) {
+	case *ast.UnaryExpr:
+		if v.Op != token.TILDE {
+			panic(ctx.newCodeErrorf(v.Pos(), "invalid op %v must ~", v.Op))
+		}
+		return []*types.Term{types.NewTerm(true, toType(ctx, v.X))}
+	case *ast.BinaryExpr:
+		if v.Op != token.OR {
+			panic(ctx.newCodeErrorf(v.Pos(), "invalid op %v must |", v.Op))
+		}
+		return append(toTermList(ctx, v.X), toTermList(ctx, v.Y)...)
+	case *ast.ParenExpr:
+		expr = v.X
+		goto retry
+	}
+	return []*types.Term{types.NewTerm(false, toType(ctx, expr))}
+}
+
+func toBinaryExprType(ctx *blockCtx, v *ast.BinaryExpr) types.Type {
+	return types.NewInterfaceType(nil, []types.Type{types.NewUnion(toTermList(ctx, v))})
+}
+
+func toUnaryExprType(ctx *blockCtx, v *ast.UnaryExpr) types.Type {
+	return types.NewInterfaceType(nil, []types.Type{types.NewUnion(toTermList(ctx, v))})
+}
+
+func toTypeParams(ctx *blockCtx, params *ast.FieldList) []*types.TypeParam {
+	if params == nil {
+		return nil
+	}
+	return collectTypeParams(ctx, params)
+}
+
+func recvTypeParams(ctx *blockCtx, typ ast.Expr, named *types.Named) (tparams []*types.TypeParam) {
+	orgTypeParams := named.TypeParams()
+	if orgTypeParams == nil {
+		return nil
+	}
+L:
+	for {
+		switch t := typ.(type) {
+		case *ast.ParenExpr:
+			typ = t.X
+		case *ast.StarExpr:
+			typ = t.X
+		default:
+			break L
+		}
+	}
+	switch t := typ.(type) {
+	case *ast.IndexExpr:
+		if 1 != orgTypeParams.Len() {
+			panic(ctx.newCodeErrorf(typ.Pos(), "got 1 type parameter, but receiver base type declares %v", orgTypeParams.Len()))
+		}
+		v := t.Index.(*ast.Ident)
+		tp := orgTypeParams.At(0)
+		obj := types.NewTypeName(v.Pos(), tp.Obj().Pkg(), v.Name, nil)
+		tparams = []*types.TypeParam{types.NewTypeParam(obj, tp.Constraint())}
+	case *ast.IndexListExpr:
+		n := len(t.Indices)
+		if n != orgTypeParams.Len() {
+			panic(ctx.newCodeErrorf(typ.Pos(), "got %v arguments but %v type parameters", n, orgTypeParams.Len()))
+		}
+		tparams = make([]*types.TypeParam, n)
+		for i := 0; i < n; i++ {
+			v := t.Indices[i].(*ast.Ident)
+			tp := orgTypeParams.At(i)
+			obj := types.NewTypeName(v.Pos(), tp.Obj().Pkg(), v.Name, nil)
+			tparams[i] = types.NewTypeParam(obj, tp.Constraint())
+		}
+	default:
+		panic(ctx.newCodeErrorf(typ.Pos(), "cannot use generic type %v without instantiation", named))
+	}
+	return
+}
+
+func toFuncType(ctx *blockCtx, typ *ast.FuncType, recv *types.Var, d *ast.FuncDecl) *types.Signature {
+	var typeParams []*types.TypeParam
+	if recv != nil && d.Recv != nil {
+		typ := recv.Type()
+		if pt, ok := typ.(*types.Pointer); ok {
+			typ = pt.Elem()
+		}
+		typeParams = recvTypeParams(ctx, d.Recv.List[0].Type, typ.(*types.Named))
+	} else {
+		typeParams = toTypeParams(ctx, typ.TypeParams)
+	}
+	if len(typeParams) > 0 {
+		ctx.tlookup = &typeParamLookup{typeParams}
+		defer func() {
+			ctx.tlookup = nil
+		}()
+	}
+	params, variadic := toParams(ctx, typ.Params.List)
+	results := toResults(ctx, typ.Results)
+	if recv != nil {
+		return types.NewSignatureType(recv, typeParams, nil, params, results, variadic)
+	}
+	return types.NewSignatureType(recv, nil, typeParams, params, results, variadic)
+}
+
+type typeParamLookup struct {
+	typeParams []*types.TypeParam
+}
+
+func (p *typeParamLookup) Lookup(name string) types.Type {
+	for _, t := range p.typeParams {
+		tname := t.Obj().Name()
+		if tname != "_" && name == tname {
+			return t
+		}
+	}
+	return nil
+}
+
+func initType(ctx *blockCtx, named *types.Named, spec *ast.TypeSpec) {
+	typeParams := toTypeParams(ctx, spec.TypeParams)
+	if len(typeParams) > 0 {
+		named.SetTypeParams(typeParams)
+		ctx.tlookup = &typeParamLookup{typeParams}
+		defer func() {
+			ctx.tlookup = nil
+		}()
+	}
+	org := ctx.inInst
+	ctx.inInst = 0
+	defer func() {
+		ctx.inInst = org
+	}()
+	typ := toType(ctx, spec.Type)
+	if named, ok := typ.(*types.Named); ok {
+		typ = getUnderlying(ctx, named)
+	}
+	named.SetUnderlying(typ)
+}
+
+func getRecvType(typ ast.Expr) (ast.Expr, bool) {
+	var ptr bool
+L:
+	for {
+		switch t := typ.(type) {
+		case *ast.ParenExpr:
+			typ = t.X
+		case *ast.StarExpr:
+			ptr = true
+			typ = t.X
+		default:
+			break L
+		}
+	}
+	switch t := typ.(type) {
+	case *ast.IndexExpr:
+		typ = t.X
+	case *ast.IndexListExpr:
+		typ = t.X
+	}
+	return typ, ptr
+}
+
+func collectTypeParams(ctx *blockCtx, list *ast.FieldList) []*types.TypeParam {
+	var tparams []*types.TypeParam
+	// Declare type parameters up-front, with empty interface as type bound.
+	// The scope of type parameters starts at the beginning of the type parameter
+	// list (so we can have mutually recursive parameterized interfaces).
+	for _, f := range list.List {
+		tparams = declareTypeParams(ctx, tparams, f.Names)
+	}
+
+	ctx.tlookup = &typeParamLookup{tparams}
+	defer func() {
+		ctx.tlookup = nil
+	}()
+
+	index := 0
+	for _, f := range list.List {
+		var bound types.Type
+		// NOTE: we may be able to assert that f.Type != nil here, but this is not
+		// an invariant of the AST, so we are cautious.
+		if f.Type != nil {
+			bound = boundTypeParam(ctx, f.Type)
+			if isTypeParam(bound) {
+				// We may be able to allow this since it is now well-defined what
+				// the underlying type and thus type set of a type parameter is.
+				// But we may need some additional form of cycle detection within
+				// type parameter lists.
+				//check.error(f.Type, MisplacedTypeParam, "cannot use a type parameter as constraint")
+				bound = types.Typ[types.Invalid]
+			} else if t, ok := bound.(*types.Named); ok {
+				if t.Underlying() == nil { // check named underlying is nil
+					ctx.loadNamed(ctx.pkg, t)
+				}
+			}
+		} else {
+			bound = types.Typ[types.Invalid]
+		}
+		for i := range f.Names {
+			tparams[index+i].SetConstraint(bound)
+		}
+		index += len(f.Names)
+	}
+	return tparams
+}
+
+func declareTypeParams(ctx *blockCtx, tparams []*types.TypeParam, names []*ast.Ident) []*types.TypeParam {
+	// Use Typ[Invalid] for the type constraint to ensure that a type
+	// is present even if the actual constraint has not been assigned
+	// yet.
+	// TODO(gri) Need to systematically review all uses of type parameter
+	//           constraints to make sure we don't rely on them if they
+	//           are not properly set yet.
+	for _, name := range names {
+		tname := types.NewTypeName(name.Pos(), ctx.pkg.Types, name.Name, nil)
+		tpar := types.NewTypeParam(tname, types.Typ[types.Invalid]) // assigns type to tpar as a side-effect
+		// check.declare(check.scope, name, tname, check.scope.pos)    // TODO(gri) check scope position
+		tparams = append(tparams, tpar)
+	}
+
+	return tparams
+}
+
+func isTypeParam(t types.Type) bool {
+	_, ok := t.(*types.TypeParam)
+	return ok
+}
+
+func sliceHasTypeParam(ctx *blockCtx, typ types.Type) bool {
+	if typ == nil {
+		return false
+	}
+	var t *types.Slice
+	switch tt := typ.(type) {
+	case *types.Named:
+		t = getUnderlying(ctx, tt).(*types.Slice)
+	case *types.Slice:
+		t = tt
+	}
+	_, ok := t.Elem().(*types.TypeParam)
+	return ok
+}
+
+func boundTypeParam(ctx *blockCtx, x ast.Expr) types.Type {
+	// A type set literal of the form ~T and A|B may only appear as constraint;
+	// embed it in an implicit interface so that only interface type-checking
+	// needs to take care of such type expressions.
+	wrap := false
+	switch op := x.(type) {
+	case *ast.UnaryExpr:
+		wrap = op.Op == token.TILDE
+	case *ast.BinaryExpr:
+		wrap = op.Op == token.OR
+	}
+	if wrap {
+		x = &ast.InterfaceType{Methods: &ast.FieldList{List: []*ast.Field{{Type: x}}}}
+		t := toType(ctx, x)
+		// mark t as implicit interface if all went well
+		if t, _ := t.(*types.Interface); t != nil {
+			t.MarkImplicit()
+		}
+		return t
+	}
+	return toType(ctx, x)
+}
+
+func namedIsTypeParams(ctx *blockCtx, t *types.Named) bool {
+	o := t.Obj()
+	if o.Pkg() == ctx.pkg.Types {
+		if _, ok := ctx.generics[o.Name()]; !ok {
+			return false
+		}
+		ctx.loadType(o.Name())
+	}
+	return t.Obj() != nil && t.TypeArgs() == nil && t.TypeParams() != nil
+}
diff --git a/cmd/gopfmt/gopfmt.go b/cmd/gopfmt/gopfmt.go
index e29bcb13..81bd41b2 100644
--- a/cmd/gopfmt/gopfmt.go
+++ b/cmd/gopfmt/gopfmt.go
@@ -17,8 +17,11 @@
 package main
 
 import (
+	"bytes"
 	"flag"
 	"fmt"
+	"go/parser"
+	"go/token"
 	"io"
 	"io/fs"
 	"io/ioutil"
@@ -26,6 +29,8 @@ import (
 	"path/filepath"
 	"strings"
 
+	goformat "go/format"
+
 	"github.com/goplus/gop/format"
 )
 
@@ -68,8 +73,22 @@ func processFile(filename string, in io.Reader, out io.Writer) error {
 	if err != nil {
 		return err
 	}
-
-	res, err := format.Source(src, filename)
+	var res []byte
+	if filepath.Ext(filename) == ".go" {
+		fset := token.NewFileSet()
+		f, err := parser.ParseFile(fset, filename, src, parser.ParseComments)
+		if err != nil {
+			return err
+		}
+		var buf bytes.Buffer
+		err = goformat.Node(&buf, fset, f)
+		if err != nil {
+			return err
+		}
+		res = buf.Bytes()
+	} else {
+		res, err = format.Source(src, filename)
+	}
 	if err != nil {
 		return err
 	}
diff --git a/cmd/internal/gopfmt/fmt.go b/cmd/internal/gopfmt/fmt.go
index e721b740..071028b2 100644
--- a/cmd/internal/gopfmt/fmt.go
+++ b/cmd/internal/gopfmt/fmt.go
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// Package gopfmt implements the ``gop fmt'' command.
+// Package gopfmt implements the “gop fmt” command.
 package gopfmt
 
 import (
@@ -30,6 +30,10 @@ import (
 	"github.com/goplus/gop/cmd/internal/base"
 	"github.com/goplus/gop/format"
 
+	goformat "go/format"
+	"go/parser"
+	"go/token"
+
 	xformat "github.com/goplus/gop/x/format"
 )
 
@@ -71,7 +75,21 @@ func gopfmt(path string, smart, mvgo bool) (err error) {
 	if smart {
 		target, err = xformat.GopstyleSource(src, path)
 	} else {
-		target, err = format.Source(src, path)
+		if !mvgo && filepath.Ext(path) == ".go" {
+			fset := token.NewFileSet()
+			f, err := parser.ParseFile(fset, path, src, parser.ParseComments)
+			if err != nil {
+				return err
+			}
+			var buf bytes.Buffer
+			err = goformat.Node(&buf, fset, f)
+			if err != nil {
+				return err
+			}
+			target = buf.Bytes()
+		} else {
+			target, err = format.Source(src, path)
+		}
 	}
 	if err != nil {
 		return
diff --git a/parser/_instance/instance1/cmd.gop b/parser/_instance/instance1/cmd.gop
new file mode 100644
index 00000000..32596eb3
--- /dev/null
+++ b/parser/_instance/instance1/cmd.gop
@@ -0,0 +1,22 @@
+type T1 P1[int]
+
+type T2 P2[int, string]
+
+type T3 *P1[P2[int, P1[int]]]
+
+type S1 struct {
+	v1 P1[int]
+	v2 P2[int, string]
+}
+
+type S2 struct {
+	P1[int]
+}
+
+type A1 []P1[int]
+
+type A2 [2]P2[int, string]
+
+type M1 map[P1[int]]P2[int, string]
+
+type C1 chan P1[int]
diff --git a/parser/_instance/instance1/parser.expect b/parser/_instance/instance1/parser.expect
new file mode 100644
index 00000000..8293981f
--- /dev/null
+++ b/parser/_instance/instance1/parser.expect
@@ -0,0 +1,207 @@
+package main
+
+file cmd.gop
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: T1
+      Type:
+        ast.IndexExpr:
+          X:
+            ast.Ident:
+              Name: P1
+          Index:
+            ast.Ident:
+              Name: int
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: T2
+      Type:
+        ast.IndexListExpr:
+          X:
+            ast.Ident:
+              Name: P2
+          Indices:
+            ast.Ident:
+              Name: int
+            ast.Ident:
+              Name: string
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: T3
+      Type:
+        ast.StarExpr:
+          X:
+            ast.IndexExpr:
+              X:
+                ast.Ident:
+                  Name: P1
+              Index:
+                ast.IndexListExpr:
+                  X:
+                    ast.Ident:
+                      Name: P2
+                  Indices:
+                    ast.Ident:
+                      Name: int
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: S1
+      Type:
+        ast.StructType:
+          Fields:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Names:
+                    ast.Ident:
+                      Name: v1
+                  Type:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+                ast.Field:
+                  Names:
+                    ast.Ident:
+                      Name: v2
+                  Type:
+                    ast.IndexListExpr:
+                      X:
+                        ast.Ident:
+                          Name: P2
+                      Indices:
+                        ast.Ident:
+                          Name: int
+                        ast.Ident:
+                          Name: string
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: S2
+      Type:
+        ast.StructType:
+          Fields:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Type:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: A1
+      Type:
+        ast.ArrayType:
+          Elt:
+            ast.IndexExpr:
+              X:
+                ast.Ident:
+                  Name: P1
+              Index:
+                ast.Ident:
+                  Name: int
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: A2
+      Type:
+        ast.ArrayType:
+          Len:
+            ast.BasicLit:
+              Kind: INT
+              Value: 2
+          Elt:
+            ast.IndexListExpr:
+              X:
+                ast.Ident:
+                  Name: P2
+              Indices:
+                ast.Ident:
+                  Name: int
+                ast.Ident:
+                  Name: string
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: M1
+      Type:
+        ast.MapType:
+          Key:
+            ast.IndexExpr:
+              X:
+                ast.Ident:
+                  Name: P1
+              Index:
+                ast.Ident:
+                  Name: int
+          Value:
+            ast.IndexListExpr:
+              X:
+                ast.Ident:
+                  Name: P2
+              Indices:
+                ast.Ident:
+                  Name: int
+                ast.Ident:
+                  Name: string
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: C1
+      Type:
+        ast.ChanType:
+          Value:
+            ast.IndexExpr:
+              X:
+                ast.Ident:
+                  Name: P1
+              Index:
+                ast.Ident:
+                  Name: int
diff --git a/parser/_instance/instance2/cmd.gop b/parser/_instance/instance2/cmd.gop
new file mode 100644
index 00000000..0da552ae
--- /dev/null
+++ b/parser/_instance/instance2/cmd.gop
@@ -0,0 +1,16 @@
+func fn1(P1[int], P2[int, string])
+
+func fn2(p1 P1[int], p2 P2[int, string])
+
+func fn3(p1 P1[int], p2 ...P2[int, string])
+
+func fn4(p1 []P1[int], p3 []P2[int, string]) *P1[P2[int, P1[int]]]
+
+func fn5(p1 []P1[int], p2 [2]P2[int, string])
+
+func fn6(p1 chan P1[int], p2 map[P1[int]]P2[int, string])
+
+func fn7(p1 struct {
+	v1 P1[int]
+	v2 P2[int, string]
+})
diff --git a/parser/_instance/instance2/parser.expect b/parser/_instance/instance2/parser.expect
new file mode 100644
index 00000000..681c44e9
--- /dev/null
+++ b/parser/_instance/instance2/parser.expect
@@ -0,0 +1,303 @@
+package main
+
+file cmd.gop
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: fn1
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Type:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: P1
+                  Index:
+                    ast.Ident:
+                      Name: int
+            ast.Field:
+              Type:
+                ast.IndexListExpr:
+                  X:
+                    ast.Ident:
+                      Name: P2
+                  Indices:
+                    ast.Ident:
+                      Name: int
+                    ast.Ident:
+                      Name: string
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: fn2
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p1
+              Type:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: P1
+                  Index:
+                    ast.Ident:
+                      Name: int
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p2
+              Type:
+                ast.IndexListExpr:
+                  X:
+                    ast.Ident:
+                      Name: P2
+                  Indices:
+                    ast.Ident:
+                      Name: int
+                    ast.Ident:
+                      Name: string
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: fn3
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p1
+              Type:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: P1
+                  Index:
+                    ast.Ident:
+                      Name: int
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p2
+              Type:
+                ast.Ellipsis:
+                  Elt:
+                    ast.IndexListExpr:
+                      X:
+                        ast.Ident:
+                          Name: P2
+                      Indices:
+                        ast.Ident:
+                          Name: int
+                        ast.Ident:
+                          Name: string
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: fn4
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p1
+              Type:
+                ast.ArrayType:
+                  Elt:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p3
+              Type:
+                ast.ArrayType:
+                  Elt:
+                    ast.IndexListExpr:
+                      X:
+                        ast.Ident:
+                          Name: P2
+                      Indices:
+                        ast.Ident:
+                          Name: int
+                        ast.Ident:
+                          Name: string
+      Results:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Type:
+                ast.StarExpr:
+                  X:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.IndexListExpr:
+                          X:
+                            ast.Ident:
+                              Name: P2
+                          Indices:
+                            ast.Ident:
+                              Name: int
+                            ast.IndexExpr:
+                              X:
+                                ast.Ident:
+                                  Name: P1
+                              Index:
+                                ast.Ident:
+                                  Name: int
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: fn5
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p1
+              Type:
+                ast.ArrayType:
+                  Elt:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p2
+              Type:
+                ast.ArrayType:
+                  Len:
+                    ast.BasicLit:
+                      Kind: INT
+                      Value: 2
+                  Elt:
+                    ast.IndexListExpr:
+                      X:
+                        ast.Ident:
+                          Name: P2
+                      Indices:
+                        ast.Ident:
+                          Name: int
+                        ast.Ident:
+                          Name: string
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: fn6
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p1
+              Type:
+                ast.ChanType:
+                  Value:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p2
+              Type:
+                ast.MapType:
+                  Key:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+                  Value:
+                    ast.IndexListExpr:
+                      X:
+                        ast.Ident:
+                          Name: P2
+                      Indices:
+                        ast.Ident:
+                          Name: int
+                        ast.Ident:
+                          Name: string
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: fn7
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: p1
+              Type:
+                ast.StructType:
+                  Fields:
+                    ast.FieldList:
+                      List:
+                        ast.Field:
+                          Names:
+                            ast.Ident:
+                              Name: v1
+                          Type:
+                            ast.IndexExpr:
+                              X:
+                                ast.Ident:
+                                  Name: P1
+                              Index:
+                                ast.Ident:
+                                  Name: int
+                        ast.Field:
+                          Names:
+                            ast.Ident:
+                              Name: v2
+                          Type:
+                            ast.IndexListExpr:
+                              X:
+                                ast.Ident:
+                                  Name: P2
+                              Indices:
+                                ast.Ident:
+                                  Name: int
+                                ast.Ident:
+                                  Name: string
diff --git a/parser/_instance/instance3/cmd.gop b/parser/_instance/instance3/cmd.gop
new file mode 100644
index 00000000..923ecb18
--- /dev/null
+++ b/parser/_instance/instance3/cmd.gop
@@ -0,0 +1,22 @@
+println(P1[int]{}, P2[int, string]{})
+println((*P1[int])(nil), (*P2[int, string])(nil))
+
+println P1[int]{}, P2[int, string]{}
+println (*P1[int])(nil), (*P2[int, string])(nil)
+
+func(x P1[int], y *P2[int, string]) *int {
+	return nil
+}(P1[int]{1}, &P2[int, string]{})
+
+foo(=> P1[int]{1})
+foo(=> {
+	println(&P2[int, string]{})
+})
+
+fn1[int]([]P1[int]{P1[int]{0}, P1[int]{1}}, &P2[int, string]{})
+fn2[int, string](&P1[int]{}, &P2[int, string]{})
+
+foo1[int](=> P1[int]{1})
+foo2[int, string](=> {
+	println(&P2[int, string]{})
+})
diff --git a/parser/_instance/instance3/parser.expect b/parser/_instance/instance3/parser.expect
new file mode 100644
index 00000000..53e872cb
--- /dev/null
+++ b/parser/_instance/instance3/parser.expect
@@ -0,0 +1,457 @@
+package main
+
+file cmd.gop
+noEntrypoint
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: main
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+  Body:
+    ast.BlockStmt:
+      List:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: println
+              Args:
+                ast.CompositeLit:
+                  Type:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+                ast.CompositeLit:
+                  Type:
+                    ast.IndexListExpr:
+                      X:
+                        ast.Ident:
+                          Name: P2
+                      Indices:
+                        ast.Ident:
+                          Name: int
+                        ast.Ident:
+                          Name: string
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: println
+              Args:
+                ast.CallExpr:
+                  Fun:
+                    ast.ParenExpr:
+                      X:
+                        ast.StarExpr:
+                          X:
+                            ast.IndexExpr:
+                              X:
+                                ast.Ident:
+                                  Name: P1
+                              Index:
+                                ast.Ident:
+                                  Name: int
+                  Args:
+                    ast.Ident:
+                      Name: nil
+                ast.CallExpr:
+                  Fun:
+                    ast.ParenExpr:
+                      X:
+                        ast.StarExpr:
+                          X:
+                            ast.IndexListExpr:
+                              X:
+                                ast.Ident:
+                                  Name: P2
+                              Indices:
+                                ast.Ident:
+                                  Name: int
+                                ast.Ident:
+                                  Name: string
+                  Args:
+                    ast.Ident:
+                      Name: nil
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: println
+              Args:
+                ast.CompositeLit:
+                  Type:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+                ast.CompositeLit:
+                  Type:
+                    ast.IndexListExpr:
+                      X:
+                        ast.Ident:
+                          Name: P2
+                      Indices:
+                        ast.Ident:
+                          Name: int
+                        ast.Ident:
+                          Name: string
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: println
+              Args:
+                ast.CallExpr:
+                  Fun:
+                    ast.ParenExpr:
+                      X:
+                        ast.StarExpr:
+                          X:
+                            ast.IndexExpr:
+                              X:
+                                ast.Ident:
+                                  Name: P1
+                              Index:
+                                ast.Ident:
+                                  Name: int
+                  Args:
+                    ast.Ident:
+                      Name: nil
+                ast.CallExpr:
+                  Fun:
+                    ast.ParenExpr:
+                      X:
+                        ast.StarExpr:
+                          X:
+                            ast.IndexListExpr:
+                              X:
+                                ast.Ident:
+                                  Name: P2
+                              Indices:
+                                ast.Ident:
+                                  Name: int
+                                ast.Ident:
+                                  Name: string
+                  Args:
+                    ast.Ident:
+                      Name: nil
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.FuncLit:
+                  Type:
+                    ast.FuncType:
+                      Params:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: x
+                              Type:
+                                ast.IndexExpr:
+                                  X:
+                                    ast.Ident:
+                                      Name: P1
+                                  Index:
+                                    ast.Ident:
+                                      Name: int
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: y
+                              Type:
+                                ast.StarExpr:
+                                  X:
+                                    ast.IndexListExpr:
+                                      X:
+                                        ast.Ident:
+                                          Name: P2
+                                      Indices:
+                                        ast.Ident:
+                                          Name: int
+                                        ast.Ident:
+                                          Name: string
+                      Results:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Type:
+                                ast.StarExpr:
+                                  X:
+                                    ast.Ident:
+                                      Name: int
+                  Body:
+                    ast.BlockStmt:
+                      List:
+                        ast.ReturnStmt:
+                          Results:
+                            ast.Ident:
+                              Name: nil
+              Args:
+                ast.CompositeLit:
+                  Type:
+                    ast.IndexExpr:
+                      X:
+                        ast.Ident:
+                          Name: P1
+                      Index:
+                        ast.Ident:
+                          Name: int
+                  Elts:
+                    ast.BasicLit:
+                      Kind: INT
+                      Value: 1
+                ast.UnaryExpr:
+                  Op: &
+                  X:
+                    ast.CompositeLit:
+                      Type:
+                        ast.IndexListExpr:
+                          X:
+                            ast.Ident:
+                              Name: P2
+                          Indices:
+                            ast.Ident:
+                              Name: int
+                            ast.Ident:
+                              Name: string
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: foo
+              Args:
+                ast.LambdaExpr:
+                  Rhs:
+                    ast.CompositeLit:
+                      Type:
+                        ast.IndexExpr:
+                          X:
+                            ast.Ident:
+                              Name: P1
+                          Index:
+                            ast.Ident:
+                              Name: int
+                      Elts:
+                        ast.BasicLit:
+                          Kind: INT
+                          Value: 1
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: foo
+              Args:
+                ast.LambdaExpr2:
+                  Body:
+                    ast.BlockStmt:
+                      List:
+                        ast.ExprStmt:
+                          X:
+                            ast.CallExpr:
+                              Fun:
+                                ast.Ident:
+                                  Name: println
+                              Args:
+                                ast.UnaryExpr:
+                                  Op: &
+                                  X:
+                                    ast.CompositeLit:
+                                      Type:
+                                        ast.IndexListExpr:
+                                          X:
+                                            ast.Ident:
+                                              Name: P2
+                                          Indices:
+                                            ast.Ident:
+                                              Name: int
+                                            ast.Ident:
+                                              Name: string
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn1
+                  Index:
+                    ast.Ident:
+                      Name: int
+              Args:
+                ast.CompositeLit:
+                  Type:
+                    ast.ArrayType:
+                      Elt:
+                        ast.IndexExpr:
+                          X:
+                            ast.Ident:
+                              Name: P1
+                          Index:
+                            ast.Ident:
+                              Name: int
+                  Elts:
+                    ast.CompositeLit:
+                      Type:
+                        ast.IndexExpr:
+                          X:
+                            ast.Ident:
+                              Name: P1
+                          Index:
+                            ast.Ident:
+                              Name: int
+                      Elts:
+                        ast.BasicLit:
+                          Kind: INT
+                          Value: 0
+                    ast.CompositeLit:
+                      Type:
+                        ast.IndexExpr:
+                          X:
+                            ast.Ident:
+                              Name: P1
+                          Index:
+                            ast.Ident:
+                              Name: int
+                      Elts:
+                        ast.BasicLit:
+                          Kind: INT
+                          Value: 1
+                ast.UnaryExpr:
+                  Op: &
+                  X:
+                    ast.CompositeLit:
+                      Type:
+                        ast.IndexListExpr:
+                          X:
+                            ast.Ident:
+                              Name: P2
+                          Indices:
+                            ast.Ident:
+                              Name: int
+                            ast.Ident:
+                              Name: string
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexListExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn2
+                  Indices:
+                    ast.Ident:
+                      Name: int
+                    ast.Ident:
+                      Name: string
+              Args:
+                ast.UnaryExpr:
+                  Op: &
+                  X:
+                    ast.CompositeLit:
+                      Type:
+                        ast.IndexExpr:
+                          X:
+                            ast.Ident:
+                              Name: P1
+                          Index:
+                            ast.Ident:
+                              Name: int
+                ast.UnaryExpr:
+                  Op: &
+                  X:
+                    ast.CompositeLit:
+                      Type:
+                        ast.IndexListExpr:
+                          X:
+                            ast.Ident:
+                              Name: P2
+                          Indices:
+                            ast.Ident:
+                              Name: int
+                            ast.Ident:
+                              Name: string
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: foo1
+                  Index:
+                    ast.Ident:
+                      Name: int
+              Args:
+                ast.LambdaExpr:
+                  Rhs:
+                    ast.CompositeLit:
+                      Type:
+                        ast.IndexExpr:
+                          X:
+                            ast.Ident:
+                              Name: P1
+                          Index:
+                            ast.Ident:
+                              Name: int
+                      Elts:
+                        ast.BasicLit:
+                          Kind: INT
+                          Value: 1
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexListExpr:
+                  X:
+                    ast.Ident:
+                      Name: foo2
+                  Indices:
+                    ast.Ident:
+                      Name: int
+                    ast.Ident:
+                      Name: string
+              Args:
+                ast.LambdaExpr2:
+                  Body:
+                    ast.BlockStmt:
+                      List:
+                        ast.ExprStmt:
+                          X:
+                            ast.CallExpr:
+                              Fun:
+                                ast.Ident:
+                                  Name: println
+                              Args:
+                                ast.UnaryExpr:
+                                  Op: &
+                                  X:
+                                    ast.CompositeLit:
+                                      Type:
+                                        ast.IndexListExpr:
+                                          X:
+                                            ast.Ident:
+                                              Name: P2
+                                          Indices:
+                                            ast.Ident:
+                                              Name: int
+                                            ast.Ident:
+                                              Name: string
diff --git a/parser/_instance/instance4/cmd.gop b/parser/_instance/instance4/cmd.gop
new file mode 100644
index 00000000..fb8c6688
--- /dev/null
+++ b/parser/_instance/instance4/cmd.gop
@@ -0,0 +1,15 @@
+fn[int]()
+fn[[]int]()
+fn[struct {
+	X int
+	Y int
+}]()
+fn[map[int]string]()
+fn[chan int]()
+fn[chan struct{}]()
+fn[interface{}]()
+fn[interface{ Method() }]()
+fn[[]struct {
+	X int
+	Y int
+}, map[int]string]()
diff --git a/parser/_instance/instance4/parser.expect b/parser/_instance/instance4/parser.expect
new file mode 100644
index 00000000..7e01f3fa
--- /dev/null
+++ b/parser/_instance/instance4/parser.expect
@@ -0,0 +1,178 @@
+package main
+
+file cmd.gop
+noEntrypoint
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: main
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+  Body:
+    ast.BlockStmt:
+      List:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Index:
+                    ast.Ident:
+                      Name: int
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Index:
+                    ast.ArrayType:
+                      Elt:
+                        ast.Ident:
+                          Name: int
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Index:
+                    ast.StructType:
+                      Fields:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: X
+                              Type:
+                                ast.Ident:
+                                  Name: int
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: Y
+                              Type:
+                                ast.Ident:
+                                  Name: int
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Index:
+                    ast.MapType:
+                      Key:
+                        ast.Ident:
+                          Name: int
+                      Value:
+                        ast.Ident:
+                          Name: string
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Index:
+                    ast.ChanType:
+                      Value:
+                        ast.Ident:
+                          Name: int
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Index:
+                    ast.ChanType:
+                      Value:
+                        ast.StructType:
+                          Fields:
+                            ast.FieldList:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Index:
+                    ast.InterfaceType:
+                      Methods:
+                        ast.FieldList:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Index:
+                    ast.InterfaceType:
+                      Methods:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: Method
+                              Type:
+                                ast.FuncType:
+                                  Params:
+                                    ast.FieldList:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.IndexListExpr:
+                  X:
+                    ast.Ident:
+                      Name: fn
+                  Indices:
+                    ast.ArrayType:
+                      Elt:
+                        ast.StructType:
+                          Fields:
+                            ast.FieldList:
+                              List:
+                                ast.Field:
+                                  Names:
+                                    ast.Ident:
+                                      Name: X
+                                  Type:
+                                    ast.Ident:
+                                      Name: int
+                                ast.Field:
+                                  Names:
+                                    ast.Ident:
+                                      Name: Y
+                                  Type:
+                                    ast.Ident:
+                                      Name: int
+                    ast.MapType:
+                      Key:
+                        ast.Ident:
+                          Name: int
+                      Value:
+                        ast.Ident:
+                          Name: string
diff --git a/parser/parser.go b/parser/parser.go
index 711904fc..c217b70a 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -756,12 +756,14 @@ func (p *parser) parseType() ast.Expr {
 }
 
 // If the result is an identifier, it is not resolved.
-func (p *parser) parseTypeName() ast.Expr {
+func (p *parser) parseTypeName(ident *ast.Ident) ast.Expr {
 	if p.trace {
 		defer un(trace(p, "TypeName"))
 	}
 
-	ident := p.parseIdent()
+	if ident == nil {
+		ident = p.parseIdent()
+	}
 	// don't resolve ident yet - it may be a parameter or field name
 
 	if p.tok == token.PERIOD {
@@ -902,22 +904,6 @@ func newSliceLit(lbrack, rbrack token.Pos, len ast.Expr) *ast.SliceLit {
 	return &ast.SliceLit{Lbrack: lbrack, Elts: elts, Rbrack: rbrack}
 }
 
-func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
-	idents := make([]*ast.Ident, len(list))
-	for i, x := range list {
-		ident, isIdent := x.(*ast.Ident)
-		if !isIdent {
-			if _, isBad := x.(*ast.BadExpr); !isBad {
-				// only report error if it's a new one
-				p.errorExpected(x.Pos(), "identifier", 2)
-			}
-			ident = &ast.Ident{NamePos: x.Pos(), Name: "_"}
-		}
-		idents[i] = ident
-	}
-	return idents
-}
-
 func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
 	if p.trace {
 		defer un(trace(p, "FieldDecl"))
@@ -925,48 +911,88 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
 
 	doc := p.leadComment
 
-	// 1st FieldDecl
-	// A type name used as an anonymous field looks like a field identifier.
-	var list []ast.Expr
-	for {
-		list = append(list, p.parseVarType(false))
-		if p.tok != token.COMMA {
-			break
+	var names []*ast.Ident
+	var typ ast.Expr
+	switch p.tok {
+	case token.IDENT:
+		name := p.parseIdent()
+		if p.tok == token.PERIOD || p.tok == token.STRING || p.tok == token.SEMICOLON || p.tok == token.RBRACE {
+			// embedded type
+			typ = name
+			if p.tok == token.PERIOD {
+				typ = p.parseQualifiedIdent(name)
+			}
+		} else {
+			// name1, name2, ... T
+			names = []*ast.Ident{name}
+			for p.tok == token.COMMA {
+				p.next()
+				names = append(names, p.parseIdent())
+			}
+			// Careful dance: We don't know if we have an embedded instantiated
+			// type T[P1, P2, ...] or a field T of array type []E or [P]E.
+			if len(names) == 1 && p.tok == token.LBRACK {
+				name, typ = p.parseArrayFieldOrTypeInstance(name, stateType)
+				if name == nil {
+					names = nil
+				}
+			} else {
+				// T P
+				typ = p.parseType()
+			}
 		}
+	case token.MUL:
+		star := p.pos
 		p.next()
-	}
-
-	typ := p.tryVarType(false)
+		if p.tok == token.LPAREN {
+			// *(T)
+			p.error(p.pos, "cannot parenthesize embedded type")
+			p.next()
+			typ = p.parseQualifiedIdent(nil)
+			// expect closing ')' but no need to complain if missing
+			if p.tok == token.RPAREN {
+				p.next()
+			}
+		} else {
+			// *T
+			typ = p.parseQualifiedIdent(nil)
+		}
+		typ = &ast.StarExpr{Star: star, X: typ}
 
-	// analyze case
-	var idents []*ast.Ident
-	if typ != nil {
-		// IdentifierList Type
-		idents = p.makeIdentList(list)
-	} else {
-		// ["*"] TypeName (AnonymousField)
-		typ = list[0] // we always have at least one element
-		if n := len(list); n > 1 {
-			p.errorExpected(p.pos, "type", 2)
-			typ = &ast.BadExpr{From: p.pos, To: p.pos}
-		} else if !isTypeName(deref(typ)) {
-			p.errorExpected(typ.Pos(), "anonymous field", 2)
-			typ = &ast.BadExpr{From: typ.Pos(), To: p.safePos(typ.End())}
+	case token.LPAREN:
+		p.error(p.pos, "cannot parenthesize embedded type")
+		p.next()
+		if p.tok == token.MUL {
+			// (*T)
+			star := p.pos
+			p.next()
+			typ = &ast.StarExpr{Star: star, X: p.parseQualifiedIdent(nil)}
+		} else {
+			// (T)
+			typ = p.parseQualifiedIdent(nil)
+		}
+		// expect closing ')' but no need to complain if missing
+		if p.tok == token.RPAREN {
+			p.next()
 		}
+
+	default:
+		pos := p.pos
+		p.errorExpected(pos, "field name or embedded type", 2)
+		p.advance(exprEnd)
+		typ = &ast.BadExpr{From: pos, To: p.pos}
 	}
 
-	// Tag
 	var tag *ast.BasicLit
 	if p.tok == token.STRING {
 		tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
 		p.next()
 	}
 
-	p.expectSemi() // call before accessing p.linecomment
+	p.expectSemi()
 
-	field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment}
-	p.declare(field, nil, scope, ast.Var, idents...)
-	p.resolve(typ)
+	field := &ast.Field{Doc: doc, Names: names, Type: typ, Tag: tag, Comment: p.lineComment}
+	p.declare(field, nil, scope, ast.Var, names...)
 
 	return field
 }
@@ -1009,95 +1035,224 @@ func (p *parser) parsePointerType() *ast.StarExpr {
 	return &ast.StarExpr{Star: star, X: base}
 }
 
-// If the result is an identifier, it is not resolved.
-func (p *parser) tryVarType(isParam bool) ast.Expr {
-	if isParam && p.tok == token.ELLIPSIS {
-		pos := p.pos
-		p.next()
-		typ, _ := p.tryIdentOrType(stateType, nil) // don't use parseType so we can provide better error message
-		if typ != nil {
-			p.resolve(typ)
-		} else {
-			p.error(pos, "'...' parameter is missing type")
-			typ = &ast.BadExpr{From: pos, To: p.pos}
-		}
-		return &ast.Ellipsis{Ellipsis: pos, Elt: typ}
-	}
-	typ, _ := p.tryIdentOrType(stateType, nil)
-	return typ
-}
-
-// If the result is an identifier, it is not resolved.
-func (p *parser) parseVarType(isParam bool) ast.Expr {
-	typ := p.tryVarType(isParam)
-	if typ == nil {
-		pos := p.pos
-		p.errorExpected(pos, "type", 2)
-		p.next() // make progress
-		typ = &ast.BadExpr{From: pos, To: p.pos}
-	}
-	return typ
+type field struct {
+	name *ast.Ident
+	typ  ast.Expr
 }
 
-func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) {
+func (p *parser) parseParameterList(scope *ast.Scope, name0 *ast.Ident, typ0 ast.Expr, closing token.Token) (params []*ast.Field) {
 	if p.trace {
 		defer un(trace(p, "ParameterList"))
 	}
 
-	// 1st ParameterDecl
-	// A list of identifiers looks like a list of type names.
-	var list []ast.Expr
-	for {
-		list = append(list, p.parseVarType(ellipsisOk))
-		if p.tok != token.COMMA {
+	// Type parameters are the only parameter list closed by ']'.
+	tparams := closing == token.RBRACK
+	// Type set notation is ok in type parameter lists.
+
+	pos := p.pos
+	if name0 != nil {
+		pos = name0.Pos()
+	}
+
+	var list []field
+	var named int // number of parameters that have an explicit name and type
+
+	for name0 != nil || p.tok != closing && p.tok != token.EOF {
+		var par field
+		if typ0 != nil {
+			par = field{name0, typ0}
+		} else {
+			par = p.parseParamDecl(name0)
+		}
+		name0 = nil // 1st name was consumed if present
+		typ0 = nil  // 1st typ was consumed if present
+		if par.name != nil || par.typ != nil {
+			list = append(list, par)
+			if par.name != nil && par.typ != nil {
+				named++
+			}
+		}
+		if !p.atComma("parameter list", closing) {
 			break
 		}
 		p.next()
-		if p.tok == token.RPAREN {
-			break
+	}
+
+	if len(list) == 0 {
+		return // not uncommon
+	}
+
+	// TODO(gri) parameter distribution and conversion to []*ast.Field
+	//           can be combined and made more efficient
+
+	// distribute parameter types
+	if named == 0 {
+		// all unnamed => found names are type names
+		for i := 0; i < len(list); i++ {
+			par := &list[i]
+			if typ := par.name; typ != nil {
+				par.typ = typ
+				par.name = nil
+			}
+		}
+		if tparams {
+			p.error(pos, "type parameters must be named")
+		}
+	} else if named != len(list) {
+		// some named => all must be named
+		ok := true
+		var typ ast.Expr
+		missingName := pos
+		for i := len(list) - 1; i >= 0; i-- {
+			if par := &list[i]; par.typ != nil {
+				typ = par.typ
+				if par.name == nil {
+					ok = false
+					missingName = par.typ.Pos()
+					n := ast.NewIdent("_")
+					n.NamePos = typ.Pos() // correct position
+					par.name = n
+				}
+			} else if typ != nil {
+				par.typ = typ
+			} else {
+				// par.typ == nil && typ == nil => we only have a par.name
+				ok = false
+				missingName = par.name.Pos()
+				par.typ = &ast.BadExpr{From: par.name.Pos(), To: p.pos}
+			}
+		}
+		if !ok {
+			if tparams {
+				p.error(missingName, "type parameters must be named")
+			} else {
+				p.error(pos, "mixed named and unnamed parameters")
+			}
 		}
 	}
 
-	// analyze case
-	if typ := p.tryVarType(ellipsisOk); typ != nil {
-		// IdentifierList Type
-		idents := p.makeIdentList(list)
-		field := &ast.Field{Names: idents, Type: typ}
-		params = append(params, field)
+	// convert list []*ast.Field
+	if named == 0 {
+		// parameter list consists of types only
+		for _, par := range list {
+			assert(par.typ != nil, "nil type in unnamed parameter list")
+			params = append(params, &ast.Field{Type: par.typ})
+		}
+		return
+	}
+
+	// parameter list consists of named parameters with types
+	var names []*ast.Ident
+	var typ ast.Expr
+	addParams := func() {
+		assert(typ != nil, "nil type in named parameter list")
+		field := &ast.Field{Names: names, Type: typ}
 		// Go spec: The scope of an identifier denoting a function
 		// parameter or result variable is the function body.
-		p.declare(field, nil, scope, ast.Var, idents...)
-		p.resolve(typ)
-		if !p.atComma("parameter list", token.RPAREN) {
-			return
-		}
-		p.next()
-		for p.tok != token.RPAREN && p.tok != token.EOF {
-			idents := p.parseIdentList()
-			typ := p.parseVarType(ellipsisOk)
-			field := &ast.Field{Names: idents, Type: typ}
-			params = append(params, field)
-			// Go spec: The scope of an identifier denoting a function
-			// parameter or result variable is the function body.
-			p.declare(field, nil, scope, ast.Var, idents...)
-			p.resolve(typ)
-			if !p.atComma("parameter list", token.RPAREN) {
-				break
+		p.declare(field, nil, scope, ast.Var, names...)
+		params = append(params, field)
+		names = nil
+	}
+	for _, par := range list {
+		if par.typ != typ {
+			if len(names) > 0 {
+				addParams()
 			}
-			p.next()
+			typ = par.typ
 		}
-		return
+		names = append(names, par.name)
+	}
+	if len(names) > 0 {
+		addParams()
 	}
+	return
+}
 
-	// Type { "," Type } (anonymous parameters)
-	params = make([]*ast.Field, len(list))
-	for i, typ := range list {
-		p.resolve(typ)
-		params[i] = &ast.Field{Type: typ}
+func (p *parser) parseParamDecl(name *ast.Ident) (f field) {
+	// TODO(rFindley) refactor to be more similar to paramDeclOrNil in the syntax
+	// package
+	if p.trace {
+		defer un(trace(p, "ParamDeclOrNil"))
+	}
+
+	ptok := p.tok
+	if name != nil {
+		p.tok = token.IDENT // force token.IDENT case in switch below
+	}
+
+	switch p.tok {
+	case token.IDENT:
+		// name
+		if name != nil {
+			f.name = name
+			p.tok = ptok
+		} else {
+			f.name = p.parseIdent()
+		}
+		switch p.tok {
+		case token.IDENT, token.MUL, token.ARROW, token.FUNC, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:
+			// name type
+			f.typ = p.parseType()
+
+		case token.LBRACK:
+			// name "[" type1, ..., typeN "]" or name "[" n "]" type
+			f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name, stateType)
+
+		case token.ELLIPSIS:
+			// name "..." type
+			f.typ = p.parseDotsType()
+			return // don't allow ...type "|" ...
+
+		case token.PERIOD:
+			// name "." ...
+			f.typ = p.parseQualifiedIdent(f.name)
+			f.name = nil
+		}
+
+	case token.MUL, token.ARROW, token.FUNC, token.LBRACK, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:
+		// type
+		f.typ = p.parseType()
+
+	case token.ELLIPSIS:
+		// "..." type
+		// (always accepted)
+		f.typ = p.parseDotsType()
+		return // don't allow ...type "|" ...
+
+	default:
+		// TODO(rfindley): this is incorrect in the case of type parameter lists
+		//                 (should be "']'" in that case)
+		p.errorExpected(p.pos, "')'", 2)
+		p.advance(exprEnd)
 	}
+
 	return
 }
 
+func (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr {
+	if p.trace {
+		defer un(trace(p, "QualifiedIdent"))
+	}
+
+	typ := p.parseTypeName(ident)
+	if p.tok == token.LBRACK {
+		typ = p.parseTypeInstance(typ)
+	}
+
+	return typ
+}
+
+func (p *parser) parseDotsType() *ast.Ellipsis {
+	if p.trace {
+		defer un(trace(p, "DotsType"))
+	}
+
+	pos := p.expect(token.ELLIPSIS)
+	elt := p.parseType()
+
+	return &ast.Ellipsis{Ellipsis: pos, Elt: elt}
+}
+
 func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList {
 	if p.trace {
 		defer un(trace(p, "Parameters"))
@@ -1105,8 +1260,9 @@ func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldLi
 
 	var params []*ast.Field
 	lparen := p.expect(token.LPAREN)
+
 	if p.tok != token.RPAREN {
-		params = p.parseParameterList(scope, ellipsisOk)
+		params = p.parseParameterList(scope, nil, nil, token.RPAREN)
 	}
 	rparen := p.expect(token.RPAREN)
 
@@ -1163,7 +1319,7 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
 	doc := p.leadComment
 	var idents []*ast.Ident
 	var typ ast.Expr
-	x := p.parseTypeName()
+	x := p.parseTypeName(nil)
 	if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
 		// method
 		idents = []*ast.Ident{ident}
@@ -1248,12 +1404,95 @@ func (p *parser) parseChanType() *ast.ChanType {
 	return &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value}
 }
 
+func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
+	if p.trace {
+		defer un(trace(p, "TypeInstance"))
+	}
+
+	opening := p.expect(token.LBRACK)
+	p.exprLev++
+	var list []ast.Expr
+	for p.tok != token.RBRACK && p.tok != token.EOF {
+		list = append(list, p.parseType())
+		if !p.atComma("type argument list", token.RBRACK) {
+			break
+		}
+		p.next()
+	}
+	p.exprLev--
+
+	closing := p.expectClosing(token.RBRACK, "type argument list")
+
+	if len(list) == 0 {
+		p.errorExpected(closing, "type argument list", 2)
+		return &ast.IndexExpr{
+			X:      typ,
+			Lbrack: opening,
+			Index:  &ast.BadExpr{From: opening + 1, To: closing},
+			Rbrack: closing,
+		}
+	}
+
+	return packIndexExpr(typ, opening, list, closing)
+}
+
+func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident, state int) (*ast.Ident, ast.Expr) {
+	if p.trace {
+		defer un(trace(p, "ArrayFieldOrTypeInstance"))
+	}
+
+	lbrack := p.expect(token.LBRACK)
+	trailingComma := token.NoPos // if valid, the position of a trailing comma preceding the ']'
+	var args []ast.Expr
+	if p.tok != token.RBRACK {
+		p.exprLev++
+		args = append(args, p.parseRHS())
+		for p.tok == token.COMMA {
+			comma := p.pos
+			p.next()
+			if p.tok == token.RBRACK {
+				trailingComma = comma
+				break
+			}
+			args = append(args, p.parseRHS())
+		}
+		p.exprLev--
+	}
+	rbrack := p.expect(token.RBRACK)
+
+	if len(args) == 0 {
+		// x []E
+		elt := p.parseType()
+		return x, &ast.ArrayType{Lbrack: lbrack, Elt: elt}
+	}
+
+	// x [P]E or x[P]
+	if len(args) == 1 {
+		elt, _ := p.tryIdentOrType(state, nil)
+		if elt != nil {
+			// x [P]E
+			if trailingComma.IsValid() {
+				// Trailing commas are invalid in array type fields.
+				p.error(trailingComma, "unexpected comma; expecting ]")
+			}
+			return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
+		}
+	}
+
+	// x[P], x[P1, P2], ...
+	return nil, packIndexExpr(x, lbrack, args, rbrack)
+}
+
 // state = stateArrayTypeOrSliceLit | stateTypeOrSliceOp | stateType | ...
 // If the result is an identifier, it is not resolved.
 func (p *parser) tryIdentOrType(state int, len ast.Expr) (ast.Expr, int) {
 	switch p.tok {
 	case token.IDENT:
-		return p.parseTypeName(), resultIdent
+		typ := p.parseTypeName(nil)
+		if p.tok == token.LBRACK {
+			typ = p.parseTypeInstance(typ)
+		}
+		return typ, resultIdent
 	case token.LBRACK:
 		return p.parseArrayTypeOrSliceLit(state, len)
 	case token.STRUCT:
@@ -1503,20 +1742,35 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
 
 func (p *parser) parseIndexOrSliceContinue(x ast.Expr, lbrack token.Pos, idx ast.Expr) ast.Expr {
 	const N = 3 // change the 3 to 2 to disable 3-index slices
+	var args []ast.Expr
 	var index [N]ast.Expr
 	var colons [N - 1]token.Pos
 	if idx != nil {
 		index[0] = idx
 	}
 	ncolons := 0
-	for p.tok == token.COLON && ncolons < len(colons) {
-		colons[ncolons] = p.pos
-		ncolons++
-		p.next()
-		if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
-			index[ncolons] = p.parseRHS()
+	switch p.tok {
+	case token.COLON:
+		// slice expression
+		for p.tok == token.COLON && ncolons < len(colons) {
+			colons[ncolons] = p.pos
+			ncolons++
+			p.next()
+			if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
+				index[ncolons] = p.parseRHS()
+			}
+		}
+	case token.COMMA:
+		// instance expression
+		args = append(args, index[0])
+		for p.tok == token.COMMA {
+			p.next()
+			if p.tok != token.RBRACK && p.tok != token.EOF {
+				args = append(args, p.parseType())
+			}
 		}
 	}
+
 	p.exprLev--
 	rbrack := p.expect(token.RBRACK)
 
@@ -1525,27 +1779,57 @@ func (p *parser) parseIndexOrSliceContinue(x ast.Expr, lbrack token.Pos, idx ast
 		slice3 := false
 		if ncolons == 2 {
 			slice3 = true
-			// Check presence of 2nd and 3rd index here rather than during type-checking
+			// Check presence of middle and final index here rather than during type-checking
 			// to prevent erroneous programs from passing through gofmt (was issue 7305).
 			if index[1] == nil {
-				p.error(colons[0], "2nd index required in 3-index slice")
+				p.error(colons[0], "middle index required in 3-index slice")
 				index[1] = &ast.BadExpr{From: colons[0] + 1, To: colons[1]}
 			}
 			if index[2] == nil {
-				p.error(colons[1], "3rd index required in 3-index slice")
+				p.error(colons[1], "final index required in 3-index slice")
 				index[2] = &ast.BadExpr{From: colons[1] + 1, To: rbrack}
 			}
 		}
+		return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: slice3, Rbrack: rbrack}
+	}
+
+	if len(args) == 0 {
 		if debugParseOutput {
-			log.Printf("ast.SliceExpr{X: %v, Low: %v, High: %v, Max: %v, Slice3: %v}\n", x, index[0], index[1], index[2], slice3)
+			log.Printf("ast.IndexExpr{X: %v, Index: %v}\n", x, index[0])
 		}
-		return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: slice3, Rbrack: rbrack}
+		// index expression
+		return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
 	}
 
-	if debugParseOutput {
-		log.Printf("ast.IndexExpr{X: %v, Index: %v}\n", x, index[0])
+	// instance expression
+	return packIndexExpr(x, lbrack, args, rbrack)
+}
+
+func packIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token.Pos) ast.Expr {
+	switch len(exprs) {
+	case 0:
+		panic("internal error: packIndexExpr with empty expr slice")
+	case 1:
+		if debugParseOutput {
+			log.Printf("ast.IndexExpr{X: %v, Index: %v}\n", x, exprs[0])
+		}
+		return &ast.IndexExpr{
+			X:      x,
+			Lbrack: lbrack,
+			Index:  exprs[0],
+			Rbrack: rbrack,
+		}
+	default:
+		if debugParseOutput {
+			log.Printf("ast.IndexListExpr{X: %v, Index: %v}\n", x, exprs)
+		}
+		return &ast.IndexListExpr{
+			X:       x,
+			Lbrack:  lbrack,
+			Indices: exprs,
+			Rbrack:  rbrack,
+		}
 	}
-	return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
 }
 
 func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
@@ -1756,6 +2040,13 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
 	case *ast.ComprehensionExpr:
 	case *ast.SelectorExpr:
 	case *ast.IndexExpr:
+	case *ast.IndexListExpr:
+	case *ast.ArrayType:
+	case *ast.StructType:
+	case *ast.InterfaceType:
+	case *ast.FuncType:
+	case *ast.MapType:
+	case *ast.ChanType:
 	case *ast.SliceExpr:
 	case *ast.TypeAssertExpr:
 		// If t.Type == nil we have a type assertion of the form
@@ -1801,6 +2092,12 @@ func isLiteralType(x ast.Expr) bool {
 	switch t := x.(type) {
 	case *ast.BadExpr:
 	case *ast.Ident:
+	case *ast.IndexExpr:
+		_, isIdent := t.X.(*ast.Ident)
+		return isIdent
+	case *ast.IndexListExpr:
+		_, isIdent := t.X.(*ast.Ident)
+		return isIdent
 	case *ast.SelectorExpr:
 		_, isIdent := t.X.(*ast.Ident)
 		return isIdent
diff --git a/printer/nodes.go b/printer/nodes.go
index 0cdd255d..c575c291 100644
--- a/printer/nodes.go
+++ b/printer/nodes.go
@@ -862,6 +862,13 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
 		p.expr0(x.Index, depth+1)
 		p.print(x.Rbrack, token.RBRACK)
 
+	case *ast.IndexListExpr:
+		// TODO(gri): should treat[] like parentheses and undo one level of depth
+		p.expr1(x.X, token.HighestPrec, 1)
+		p.print(x.Lbrack, token.LBRACK)
+		p.exprList(x.Lbrack, x.Indices, depth+1, commaTerm, x.Rbrack, false)
+		p.print(x.Rbrack, token.RBRACK)
+
 	case *ast.SliceExpr:
 		// TODO(gri): should treat[] like parentheses and undo one level of depth
 		p.expr1(x.X, token.HighestPrec, 1)


Test patch:
diff --git a/ast/fromgo/typeparams_test.go b/ast/fromgo/typeparams_test.go
new file mode 100644
index 00000000..3d5e3dc2
--- /dev/null
+++ b/ast/fromgo/typeparams_test.go
@@ -0,0 +1,17 @@
+//go:build go1.18
+// +build go1.18
+
+package fromgo
+
+import "testing"
+
+func TestIndexListExpr(t *testing.T) {
+	test(t, `package main
+
+type N1 = T1[int]
+
+type N2 = T2[string, int]
+
+var f = test[string, int](1, 2)
+`)
+}
diff --git a/cl/typeparams_test.go b/cl/typeparams_test.go
new file mode 100644
index 00000000..c3d56150
--- /dev/null
+++ b/cl/typeparams_test.go
@@ -0,0 +1,559 @@
+//go:build go1.18
+// +build go1.18
+
+package cl_test
+
+import (
+	"go/scanner"
+	"os"
+	"runtime"
+	"testing"
+
+	"github.com/goplus/gop/cl"
+	"github.com/goplus/gop/parser"
+	"github.com/goplus/gop/parser/parsertest"
+)
+
+func TestTypeParamsFunc(t *testing.T) {
+	gopMixedClTest(t, "main", `package main
+
+type Number interface {
+	~int | ~uint | (float64)
+}
+
+func Sum[T Number](vec []T) T {
+	var sum T
+	for _, elt := range vec {
+		sum = sum + elt
+	}
+	return sum
+}
+
+func At[T interface{ ~[]E }, E any](x T, i int) E {
+	return x[i]
+}
+
+func Loader[T1 any, T2 any](p1 T1, p2 T2) T1 {
+	return p1
+}
+
+func Add[T1 any, T2 ~int|~uint](v1 T1, v2 ...T2) (sum T2) {
+	println(v1)
+	for _, v := range v2 {
+		sum += v
+	}
+	return sum
+}
+
+type Int []int
+var MyInts = Int{1,2,3,4}
+`, `
+_ = At[[]int]
+_ = At[[]int,int]
+_ = Sum[int]
+_ = Loader[*int,int]
+_ = Add[string,int]
+var s1 int = Sum([1, 2, 3])
+var s2 int = Sum[int]([1, 2, 3])
+var v1 int = At([1, 2, 3], 1)
+var v2 int = At[[]int]([1, 2, 3], 1)
+var v3 int = At[[]int, int]([1, 2, 3], 1)
+var n1 int = Add("hello", 1, 2, 3)
+var n2 int = Add[string]("hello", 1, 2, 3)
+var n3 int = Add[string, int]("hello", 1, 2, 3)
+var n4 int = Add("hello", [1, 2, 3]...)
+var n5 int = Add("hello", [1, 2, 3]...)
+var n6 int = Add[string]("hello", MyInts...)
+var n7 int = Add[string, int]("hello", [1, 2, 3]...)
+var p1 *int = Loader[*int](nil, 1)
+var p2 *int = Loader[*int, int](nil, 1)
+var fn1 func(p1 *int, p2 int) *int
+fn1 = Loader[*int, int]
+fn1(nil, 1)
+`, `package main
+
+func main() {
+	_ = At[[]int]
+	_ = At[[]int, int]
+	_ = Sum[int]
+	_ = Loader[*int, int]
+	_ = Add[string, int]
+	var s1 int = Sum([]int{1, 2, 3})
+	var s2 int = Sum[int]([]int{1, 2, 3})
+	var v1 int = At([]int{1, 2, 3}, 1)
+	var v2 int = At[[]int]([]int{1, 2, 3}, 1)
+	var v3 int = At[[]int, int]([]int{1, 2, 3}, 1)
+	var n1 int = Add("hello", 1, 2, 3)
+	var n2 int = Add[string]("hello", 1, 2, 3)
+	var n3 int = Add[string, int]("hello", 1, 2, 3)
+	var n4 int = Add("hello", []int{1, 2, 3}...)
+	var n5 int = Add("hello", []int{1, 2, 3}...)
+	var n6 int = Add[string]("hello", MyInts...)
+	var n7 int = Add[string, int]("hello", []int{1, 2, 3}...)
+	var p1 *int = Loader[*int](nil, 1)
+	var p2 *int = Loader[*int, int](nil, 1)
+	var fn1 func(p1 *int, p2 int) *int
+	fn1 = Loader[*int, int]
+	fn1(nil, 1)
+}
+`)
+}
+
+func TestTypeParamsType(t *testing.T) {
+	gopMixedClTest(t, "main", `package main
+type Data[T any] struct {
+	v T
+}
+func(p *Data[T]) Set(v T) {
+	p.v = v
+}
+func(p *(Data[T1])) Set2(v T1) {
+	p.v = v
+}
+type sliceOf[E any] interface {
+	~[]E
+}
+type Slice[S sliceOf[T], T any] struct {
+	Data S
+}
+func (p *Slice[S, T]) Append(t ...T) S {
+	p.Data = append(p.Data, t...)
+	return p.Data
+}
+func (p *Slice[S1, T1]) Append2(t ...T1) S1 {
+	p.Data = append(p.Data, t...)
+	return p.Data
+}
+type (
+	DataInt = Data[int]
+	SliceInt = Slice[[]int,int]
+)
+`, `
+type DataString = Data[string]
+type SliceString = Slice[[]string,string]
+println(DataInt{1}.v)
+println(DataString{"hello"}.v)
+println(Data[int]{100}.v)
+println(Data[string]{"hello"}.v)
+println(Data[struct{X int;Y int}]{}.v.X)
+
+v1 := SliceInt{}
+v2 := SliceString{}
+v3 := Slice[[]int,int]{}
+v3.Append([1,2,3,4]...)
+v3.Append2([1,2,3,4]...)
+`, `package main
+
+import fmt "fmt"
+
+type DataString = Data[string]
+type SliceString = Slice[[]string, string]
+
+func main() {
+	fmt.Println(Data[int]{1}.v)
+	fmt.Println(Data[string]{"hello"}.v)
+	fmt.Println(Data[int]{100}.v)
+	fmt.Println(Data[string]{"hello"}.v)
+	fmt.Println(Data[struct {
+		X int
+		Y int
+	}]{}.v.X)
+	v1 := Slice[[]int, int]{}
+	v2 := Slice[[]string, string]{}
+	v3 := Slice[[]int, int]{}
+	v3.Append([]int{1, 2, 3, 4}...)
+	v3.Append2([]int{1, 2, 3, 4}...)
+}
+`)
+}
+
+func TestTypeParamsComparable(t *testing.T) {
+	gopMixedClTest(t, "main", `package main
+// Index returns the index of x in s, or -1 if not found.
+func Index[T comparable](s []T, x T) int {
+	for i, v := range s {
+		// v and x are type T, which has the comparable
+		// constraint, so we can use == here.
+		if v == x {
+			return i
+		}
+	}
+	return -1
+}
+
+var IndexInt = Index[int]
+`, `
+v1 := IndexInt([1,2,3,4],1)
+v2 := Index([1,2,3,4],1)
+v3 := Index[int]([1,2,3,4],1)
+v4 := Index(["a","b","c","d"],"b")
+`, `package main
+
+func main() {
+	v1 := IndexInt([]int{1, 2, 3, 4}, 1)
+	v2 := Index([]int{1, 2, 3, 4}, 1)
+	v3 := Index[int]([]int{1, 2, 3, 4}, 1)
+	v4 := Index([]string{"a", "b", "c", "d"}, "b")
+}
+`)
+}
+
+func mixedErrorTest(t *testing.T, msg, gocode, gopcode string) {
+	mixedErrorTestEx(t, "main", msg, gocode, gopcode)
+}
+
+func mixedErrorTestEx(t *testing.T, pkgname, msg, gocode, gopcode string) {
+	fs := parsertest.NewTwoFilesFS("/foo", "a.go", gocode, "b.gop", gopcode)
+	pkgs, err := parser.ParseFSDir(gblFset, fs, "/foo", parser.Config{})
+	if err != nil {
+		scanner.PrintError(os.Stderr, err)
+		t.Fatal("parser.ParseFSDir failed")
+	}
+	conf := *gblConf
+	conf.NoFileLine = false
+	conf.WorkingDir = "/foo"
+	conf.TargetDir = "/foo"
+	bar := pkgs[pkgname]
+	_, 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 TestTypeParamsErrorInstantiate(t *testing.T) {
+	var msg string
+	if runtime.Version()[:6] == "go1.18" {
+		msg = `./b.gop:2:1: uint does not implement Number`
+	} else {
+		msg = `./b.gop:2:1: uint does not implement Number (uint missing in ~int | float64)`
+	}
+
+	mixedErrorTest(t, msg, `
+package main
+
+type Number interface {
+	~int | float64
+}
+
+func Sum[T Number](vec []T) T {
+	var sum T
+	for _, elt := range vec {
+		sum = sum + elt
+	}
+	return sum
+}
+
+var	SumInt = Sum[int]
+`, `
+Sum[uint]
+`)
+}
+
+func TestTypeParamsErrorMatch(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:5: T does not match ~[]E`, `
+package main
+
+func At[T interface{ ~[]E }, E any](x T, i int) E {
+	return x[i]
+}
+
+var	AtInt = At[[]int]
+`, `
+_ = At[int]
+`)
+}
+
+func TestTypeParamsErrInferFunc(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:5: cannot infer T2 (/foo/a.go:4:21)`, `
+package main
+
+func Loader[T1 any, T2 any](p1 T1, p2 T2) T1 {
+	return p1
+}
+`, `
+_ = Loader[int]
+`)
+}
+
+func TestTypeParamsErrArgumentsParameters1(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:7: got 1 type arguments but Data[T1, T2 interface{}] has 2 type parameters`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+`, `
+var v Data[int]
+`)
+}
+
+func TestTypeParamsErrArgumentsParameters2(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:7: got 3 type arguments but Data[T1, T2 interface{}] has 2 type parameters`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+`, `
+var v Data[int,int,int]
+`)
+}
+
+func TestTypeParamsErrArgumentsParameters3(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:1: got 3 type arguments but func[T1, T2 interface{}](t1 T1, t2 T2) has 2 type parameters`, `
+package main
+
+func Test[T1 any, T2 any](t1 T1, t2 T2) {
+	println(t1,t2)
+}
+`, `
+Test[int,int,int](1,2)
+`)
+}
+
+func TestTypeParamsErrCallArguments1(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:1: not enough arguments in call to Test
+	have (untyped int)
+	want (T1, T2)`, `
+package main
+
+func Test[T1 any, T2 any](t1 T1, t2 T2) {
+	println(t1,t2)
+}
+`, `
+Test(1)
+`)
+}
+
+func TestTypeParamsErrCallArguments2(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:1: too many arguments in call to Test
+	have (untyped int, untyped int, untyped int)
+	want (T1, T2)`, `
+package main
+
+func Test[T1 any, T2 any](t1 T1, t2 T2) {
+	println(t1,t2)
+}
+`, `
+Test(1,2,3)
+`)
+}
+
+func TestTypeParamsErrCallArguments3(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:1: too many arguments in call to Test
+	have (untyped int, untyped int)
+	want ()`, `
+package main
+
+func Test[T1 any, T2 any]() {
+	var t1 T1
+	var t2 T2
+	println(t1,t2)
+}
+`, `
+Test(1,2)
+`)
+}
+
+func TestTypeParamsErrCallVariadicArguments1(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:1: not enough arguments in call to Add
+	have ()
+	want (T1, ...T2)`, `
+package main
+
+func Add[T1 any, T2 ~int|~uint](v1 T1, v2 ...T2) (sum T2) {
+	println(v1)
+	for _, v := range v2 {
+		sum += v
+	}
+	return sum
+}
+`, `
+Add()
+`)
+}
+
+func TestTypeParamsErrCallVariadicArguments2(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:1: cannot infer T2 (./a.go:4:18)`, `
+package main
+
+func Add[T1 any, T2 ~int|~uint](v1 T1, v2 ...T2) (sum T2) {
+	println(v1)
+	for _, v := range v2 {
+		sum += v
+	}
+	return sum
+}
+`, `
+Add(1)
+`)
+}
+
+func TestTypeParamsRecvTypeError1(t *testing.T) {
+	mixedErrorTest(t, `./a.go:7:9: cannot use generic type Data[T interface{}] without instantiation`, `
+package main
+
+type Data[T any] struct {
+	v T
+}
+func(p *Data) Test() {
+}
+`, `
+Data[int]{}.Test()
+`)
+}
+
+func TestTypeParamsRecvTypeError2(t *testing.T) {
+	mixedErrorTest(t, `./a.go:7:9: got 2 arguments but 1 type parameters`, `
+package main
+
+type Data[T any] struct {
+	v T
+}
+func(p *Data[T1,T2]) Test() {
+}
+`, `
+Data[int]{}.Test()
+`)
+}
+
+func TestTypeParamsRecvTypeError3(t *testing.T) {
+	mixedErrorTest(t, `./a.go:8:9: got 1 type parameter, but receiver base type declares 2`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+func(p *Data[T1]) Test() {
+}
+`, `
+Data[int,int]{}.Test()
+`)
+}
+
+func TestGenericTypeWithoutInst1(t *testing.T) {
+	mixedErrorTest(t, `./a.go:8:9: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+func(p *Data) Test() {
+}
+`, `
+var v Data[int,int]
+`)
+}
+
+func TestGenericTypeWithoutInst2(t *testing.T) {
+	mixedErrorTest(t, `./a.go:10:2: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+
+type My[T any] struct {
+	Data
+}
+`, `
+var v My[int]
+`)
+}
+
+func TestGenericTypeWithoutInst3(t *testing.T) {
+	mixedErrorTest(t, `./a.go:10:2: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+
+type My struct {
+	Data
+}
+`, `
+var v My
+`)
+}
+
+func TestGenericTypeWithoutInst4(t *testing.T) {
+	mixedErrorTest(t, `./a.go:10:15: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+
+type My struct {
+	v map[string]Data
+}
+`, `
+var v My
+`)
+}
+
+func TestGenericTypeWithoutInst5(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:7: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+`, `
+var v Data
+`)
+}
+
+func TestGenericTypeWithoutInst6(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:8: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+`, `
+type T Data
+`)
+}
+
+func TestGenericTypeWithoutInst7(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:3:2: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+`, `
+type My struct {
+	Data
+}
+`)
+}
+
+func TestGenericTypeWithoutInst8(t *testing.T) {
+	mixedErrorTest(t, `./b.gop:2:23: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `
+package main
+
+type Data[T1 any, T2 any] struct {
+	v1 T1
+	v2 T2
+}
+`, `
+func test(v1 int, v2 *Data) {
+}
+`)
+}
diff --git a/parser/parserdir_go118_test.go b/parser/parserdir_go118_test.go
new file mode 100644
index 00000000..d4e0bf05
--- /dev/null
+++ b/parser/parserdir_go118_test.go
@@ -0,0 +1,30 @@
+//go:build go1.18
+// +build go1.18
+
+/*
+ * Copyright (c) 2022 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 parser
+
+import (
+	"testing"
+)
+
+func TestFromInstance(t *testing.T) {
+	testFromDir(t, "", "./_instance")
+}
+
+// -----------------------------------------------------------------------------