Repository: goplus/gop
Pull request #1826 (2024-03-25 02:55:19)

generic func infer lambda expr
fix https://github.com/goplus/gop/issues/1765



codecov[bot]: ## [Codecov](https://app.codecov.io/gh/goplus/gop/pull/1826?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 93.23%. Comparing base [(`0a7dbfe`)](https://app.codecov.io/gh/goplus/gop/commit/0a7dbfe124b1fea3022aea6e92c2fd83439100a6?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) to head [(`564f50f`)](https://app.codecov.io/gh/goplus/gop/pull/1826?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1826      +/-   ##
==========================================
+ Coverage   92.57%   93.23%   +0.66%     
==========================================
  Files          42       41       -1     
  Lines       11418     9416    -2002     
==========================================
- Hits        10570     8779    -1791     
+ Misses        686      475     -211     
  Partials      162      162              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/goplus/gop/pull/1826?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).


Patch:
diff --git a/cl/expr.go b/cl/expr.go
index b261dcf7..527f4709 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -18,6 +18,7 @@ package cl
 
 import (
 	"bytes"
+	"errors"
 	goast "go/ast"
 	gotoken "go/token"
 	"go/types"
@@ -566,12 +567,14 @@ func identVal(ctx *blockCtx, x *ast.Ident, flags int, v types.Object, alias bool
 }
 
 type fnType struct {
-	next     *fnType
-	params   *types.Tuple
-	base     int
-	size     int
-	variadic bool
-	typetype bool
+	next      *fnType
+	params    *types.Tuple
+	sig       *types.Signature
+	base      int
+	size      int
+	variadic  bool
+	typetype  bool
+	typeparam bool
 }
 
 func (p *fnType) arg(i int, ellipsis bool) types.Type {
@@ -590,7 +593,8 @@ func (p *fnType) arg(i int, ellipsis bool) types.Type {
 
 func (p *fnType) init(base int, t *types.Signature) {
 	p.base = base
-	p.params, p.variadic = t.Params(), t.Variadic()
+	p.sig = t
+	p.params, p.variadic, p.typeparam = t.Params(), t.Variadic(), t.TypeParams() != nil
 	p.size = p.params.Len()
 	if p.variadic {
 		p.size--
@@ -669,7 +673,6 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, inFlags int) {
 	var err error
 	var stk = ctx.cb.InternalStack()
 	var base = stk.Len()
-	var fnt = stk.Get(-1).Type
 	var flags gogen.InstrFlags
 	var ellipsis = v.Ellipsis != gotoken.NoPos
 	if ellipsis {
@@ -678,10 +681,12 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, inFlags int) {
 	if (inFlags & clCallWithTwoValue) != 0 {
 		flags |= gogen.InstrFlagTwoValue
 	}
+	pfn := stk.Get(-1)
+	fnt := pfn.Type
 	fn := &fnType{}
 	fn.load(fnt)
 	for fn != nil {
-		if err = compileCallArgs(fn, ctx, v, ellipsis, flags); err == nil {
+		if err = compileCallArgs(ctx, pfn, fn, v, ellipsis, flags); err == nil {
 			if rec := ctx.recorder(); rec != nil {
 				rec.recordCallExpr(ctx, v, fnt)
 			}
@@ -736,10 +741,16 @@ func fnCall(ctx *blockCtx, v *ast.CallExpr, flags gogen.InstrFlags, extra int) e
 	return ctx.cb.CallWithEx(len(v.Args)+extra, flags, v)
 }
 
-func compileCallArgs(fn *fnType, ctx *blockCtx, v *ast.CallExpr, ellipsis bool, flags gogen.InstrFlags) (err error) {
+func compileCallArgs(ctx *blockCtx, pfn *gogen.Element, fn *fnType, v *ast.CallExpr, ellipsis bool, flags gogen.InstrFlags) (err error) {
+	var needInferFunc bool
 	for i, arg := range v.Args {
 		switch expr := arg.(type) {
 		case *ast.LambdaExpr:
+			if fn.typeparam {
+				needInferFunc = true
+				compileIdent(ctx, ast.NewIdent("nil"), 0)
+				continue
+			}
 			sig, e := checkLambdaFuncType(ctx, expr, fn.arg(i, ellipsis), clLambaArgument, v.Fun)
 			if e != nil {
 				return e
@@ -748,6 +759,11 @@ func compileCallArgs(fn *fnType, ctx *blockCtx, v *ast.CallExpr, ellipsis bool,
 				return
 			}
 		case *ast.LambdaExpr2:
+			if fn.typeparam {
+				needInferFunc = true
+				compileIdent(ctx, ast.NewIdent("nil"), 0)
+				continue
+			}
 			sig, e := checkLambdaFuncType(ctx, expr, fn.arg(i, ellipsis), clLambaArgument, v.Fun)
 			if e != nil {
 				return e
@@ -784,9 +800,24 @@ func compileCallArgs(fn *fnType, ctx *blockCtx, v *ast.CallExpr, ellipsis bool,
 			compileExpr(ctx, arg)
 		}
 	}
+	if needInferFunc {
+		typ, err := gogen.InferFunc(ctx.pkg, pfn, fn.sig, nil, ctx.cb.InternalStack().GetArgs(len(v.Args)), flags)
+		if err != nil {
+			return err
+		}
+		next := &fnType{}
+		next.init(fn.base, typ.(*types.Signature))
+		next.next = fn.next
+		fn.next = next
+		return errCallNext
+	}
 	return ctx.cb.CallWithEx(len(v.Args), flags, v)
 }
 
+var (
+	errCallNext = errors.New("call next")
+)
+
 type clLambaFlag string
 
 const (
diff --git a/go.mod b/go.mod
index 4da48795..97ab1bea 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.18
 require (
 	github.com/fsnotify/fsnotify v1.7.0
 	github.com/goplus/c2go v0.7.25
-	github.com/goplus/gogen v1.15.1
+	github.com/goplus/gogen v1.15.2-0.20240325030304-38a18ebdfb1f
 	github.com/goplus/mod v0.13.9
 	github.com/qiniu/x v1.13.9
 	golang.org/x/tools v0.19.0
diff --git a/go.sum b/go.sum
index 2f87c7a3..7f54aabb 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,8 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/goplus/c2go v0.7.25 h1:QvQfOwGVGKFYOTLry8i4p9U55jnIOQWeLsnSyFg0hhc=
 github.com/goplus/c2go v0.7.25/go.mod h1:e9oe4jDVhGFMJLEGmPSrVkLuXbLZAEmAu0/uD6fSz5E=
-github.com/goplus/gogen v1.15.1 h1:iz/fFpOeldjwmnjLzEdNsZF2mCf+sOHJavbAvV3o7sY=
-github.com/goplus/gogen v1.15.1/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
+github.com/goplus/gogen v1.15.2-0.20240325030304-38a18ebdfb1f h1:ScHZQ/KkVjVyea/1ivGUaBh24KnqG3PpQXn0LffjwUc=
+github.com/goplus/gogen v1.15.2-0.20240325030304-38a18ebdfb1f/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
 github.com/goplus/mod v0.13.9 h1:B9zZoHi2AzMltTSOFqZNVjqGlSMlhhNTWwEzVqhTQzg=
 github.com/goplus/mod v0.13.9/go.mod h1:MibsLSftGmxaQq78YzUzNviyFwB9RtpMaoscufvEKH4=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=


Test patch:
diff --git a/cl/typeparams_test.go b/cl/typeparams_test.go
index c4b53b2d..4d35387f 100644
--- a/cl/typeparams_test.go
+++ b/cl/typeparams_test.go
@@ -622,3 +622,74 @@ func main() {
 }
 `)
 }
+
+func TestInferFuncLambda(t *testing.T) {
+	gopMixedClTest(t, "main", `package main
+func ListMap[T any](ar []T, fn func(v T) T)[]T {
+	for i, v := range ar {
+		ar[i] = fn(v)
+	}
+	return ar
+}
+`, `
+println ListMap([1,2,3,4], x => x*x)
+ListMap [1,2,3,4], x => {
+	println x
+	return x
+}
+`, `package main
+
+import "fmt"
+
+func main() {
+	fmt.Println(ListMap([]int{1, 2, 3, 4}, func(x int) int {
+		return x * x
+	}))
+	ListMap([]int{1, 2, 3, 4}, func(x int) int {
+		fmt.Println(x)
+		return x
+	})
+}
+`)
+}
+
+func TestInferOverloadFuncLambda(t *testing.T) {
+	gopMixedClTest(t, "main", `package main
+func ListMap__0[T any](ar []T, fn func(v T) T)[]T {
+	for i, v := range ar {
+		ar[i] = fn(v)
+	}
+	return ar
+}
+func ListMap__1(a string, fn func(s string)) {
+	for _, c := range a {
+		fn(string(c))
+	}
+}
+`, `
+println ListMap([1,2,3,4], x => x*x)
+ListMap [1,2,3,4], x => {
+	println x
+	return x
+}
+ListMap "hello", x => {
+	println x
+}
+`, `package main
+
+import "fmt"
+
+func main() {
+	fmt.Println(ListMap__0([]int{1, 2, 3, 4}, func(x int) int {
+		return x * x
+	}))
+	ListMap__0([]int{1, 2, 3, 4}, func(x int) int {
+		fmt.Println(x)
+		return x
+	})
+	ListMap__1("hello", func(x string) {
+		fmt.Println(x)
+	})
+}
+`)
+}