Repository: goplus/gop
Pull request #2305 (2025-04-25 12:36:15)

compileCallArgs: auto ConvertToClosure


Patch:
diff --git a/cl/expr.go b/cl/expr.go
index ffc1ec55..fdb180dc 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -768,6 +768,7 @@ func compileCallArgs(ctx *blockCtx, pfn *gogen.Element, fn *fnType, v *ast.CallE
 	}()
 	var needInferFunc bool
 	for i, arg := range v.Args {
+		t := fn.arg(i, ellipsis)
 		switch expr := arg.(type) {
 		case *ast.LambdaExpr:
 			if fn.typeparam {
@@ -775,7 +776,7 @@ func compileCallArgs(ctx *blockCtx, pfn *gogen.Element, fn *fnType, v *ast.CallE
 				compileIdent(ctx, ast.NewIdent("nil"), 0)
 				continue
 			}
-			sig, e := checkLambdaFuncType(ctx, expr, fn.arg(i, ellipsis), clLambaArgument, v.Fun)
+			sig, e := checkLambdaFuncType(ctx, expr, t, clLambaArgument, v.Fun)
 			if e != nil {
 				return e
 			}
@@ -788,7 +789,7 @@ func compileCallArgs(ctx *blockCtx, pfn *gogen.Element, fn *fnType, v *ast.CallE
 				compileIdent(ctx, ast.NewIdent("nil"), 0)
 				continue
 			}
-			sig, e := checkLambdaFuncType(ctx, expr, fn.arg(i, ellipsis), clLambaArgument, v.Fun)
+			sig, e := checkLambdaFuncType(ctx, expr, t, clLambaArgument, v.Fun)
 			if e != nil {
 				return e
 			}
@@ -796,11 +797,10 @@ func compileCallArgs(ctx *blockCtx, pfn *gogen.Element, fn *fnType, v *ast.CallE
 				return
 			}
 		case *ast.CompositeLit:
-			if err = compileCompositeLitEx(ctx, expr, fn.arg(i, ellipsis), true); err != nil {
+			if err = compileCompositeLitEx(ctx, expr, t, true); err != nil {
 				return
 			}
 		case *ast.SliceLit:
-			t := fn.arg(i, ellipsis)
 			switch t.(type) {
 			case *types.Slice:
 			case *types.Named:
@@ -821,9 +821,15 @@ func compileCallArgs(ctx *blockCtx, pfn *gogen.Element, fn *fnType, v *ast.CallE
 				return
 			}
 		case *ast.NumberUnitLit:
-			compileNumberUnitLit(ctx, expr, fn.arg(i, ellipsis))
+			compileNumberUnitLit(ctx, expr, t)
 		default:
 			compileExpr(ctx, arg)
+			if sigParamLen(t) == 0 {
+				cb := ctx.cb
+				if nonClosure(cb.Get(-1).Type) {
+					cb.ConvertToClosure()
+				}
+			}
 		}
 	}
 	if needInferFunc {
@@ -876,6 +882,34 @@ retry:
 	return nil, ctx.newCodeErrorf(lambda.Pos(), "cannot use lambda literal as type %v in %v%v", ftyp, flag, to)
 }
 
+func sigParamLen(typ types.Type) int {
+retry:
+	switch t := typ.(type) {
+	case *types.Signature:
+		return t.Params().Len()
+	case *types.Named:
+		typ = t.Underlying()
+		goto retry
+	}
+	return -1
+}
+
+func nonClosure(typ types.Type) bool {
+retry:
+	switch t := typ.(type) {
+	case *types.Signature:
+		return false
+	case *types.Basic:
+		if t.Kind() == types.UntypedNil {
+			return false
+		}
+	case *types.Named:
+		typ = t.Underlying()
+		goto retry
+	}
+	return true
+}
+
 func compileLambda(ctx *blockCtx, lambda ast.Expr, sig *types.Signature) {
 	switch expr := lambda.(type) {
 	case *ast.LambdaExpr2:
diff --git a/go.mod b/go.mod
index b87de220..2f683ccb 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.18
 
 require (
 	github.com/fsnotify/fsnotify v1.9.0
-	github.com/goplus/gogen v1.17.2
+	github.com/goplus/gogen v1.17.3
 	github.com/goplus/lib v0.2.0
 	github.com/goplus/mod v0.16.0
 	github.com/qiniu/x v1.13.19
diff --git a/go.sum b/go.sum
index 8bc57fe2..889c13e8 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,7 @@
 github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
 github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/goplus/gogen v1.17.2 h1:oQDWiVzZmDAdgNieJnrvrWTmxDTqYXGFMBa2rAbwjmA=
-github.com/goplus/gogen v1.17.2/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI=
+github.com/goplus/gogen v1.17.3 h1:Xhoj2KQw4feRdPEtOYjTUe9lSvNIoxBG4urhdjf+fUg=
+github.com/goplus/gogen v1.17.3/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI=
 github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
 github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=
 github.com/goplus/mod v0.16.0 h1:5CHXx3no7YaMN5HN2sgZe9MbQnc8118JLjbOeetSTfc=


Test patch:
diff --git a/cl/_testgop/repeatuntil/in.gop b/cl/_testgop/repeatuntil/in.gop
new file mode 100644
index 00000000..756f4391
--- /dev/null
+++ b/cl/_testgop/repeatuntil/in.gop
@@ -0,0 +1,12 @@
+func RepeatUntil(cond func() bool, body func()) {
+    for !cond() {
+        body()
+    }
+}
+
+x := 0
+repeatUntil x >= 3, => {
+    echo x
+    x++
+}
+repeatUntil func() bool { return false }, => {}
diff --git a/cl/_testgop/repeatuntil/out.go b/cl/_testgop/repeatuntil/out.go
new file mode 100644
index 00000000..725c50f4
--- /dev/null
+++ b/cl/_testgop/repeatuntil/out.go
@@ -0,0 +1,22 @@
+package main
+
+import "fmt"
+
+func RepeatUntil(cond func() bool, body func()) {
+	for !cond() {
+		body()
+	}
+}
+func main() {
+	x := 0
+	RepeatUntil(func() bool {
+		return x >= 3
+	}, func() {
+		fmt.Println(x)
+		x++
+	})
+	RepeatUntil(func() bool {
+		return false
+	}, func() {
+	})
+}
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index 5bd2b25c..57270975 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -39,6 +39,13 @@ func getGoxConf() *gogen.Config {
 	return &gogen.Config{Fset: fset, Importer: imp}
 }
 
+func TestNonClosure(t *testing.T) {
+	tn := types.NewTypeName(0, nil, "a", nil)
+	if !nonClosure(types.NewNamed(tn, types.Typ[types.Int], nil)) {
+		t.Fatal("nonClosure")
+	}
+}
+
 func TestLoadExpr(t *testing.T) {
 	var ni nodeInterp
 	if v := ni.LoadExpr(&ast.Ident{Name: "x"}); v != "" {