Repository: goplus/gop
Pull request #936 (2021-11-25 00:22:36)

cl: fix lambda for named func call, fix  lambda result untyped check.
1.  fix lambda for named func call
```
type Fn func() (int, error)
func Do(fn Fn) {
	v, err := fn()
	println(v, err)
}

Do => {
	return 100, nil
}
```
2. fix  lambda result type for untyped
```
- func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, in *types.Tuple)
+ func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, in *types.Tuple, out *types.Tuple)
```

3. check lambda and panic
```
func foo(int) {
}
foo(=> {})
```
```
main.gop:3:5: cannot use lambda literal as type int in argument to foo
```


Patch:
diff --git a/README.md b/README.md
index 3df6b800..95a6e3a2 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ Of course, we don't only do less-typing things.
 
 For example, we support [list comprehension](https://en.wikipedia.org/wiki/List_comprehension), which makes data processing easier.
 
-```go
+```coffee
 println [x*x for x <- 1:6:2] // output: [1 9 25]
 
 mapData := {"Hi": 1, "Hello": 2, "Go+": 3}
diff --git a/cl/expr.go b/cl/expr.go
index 5a021edf..f6c205f2 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -418,6 +418,22 @@ func (p *fnType) initWith(fnt types.Type, idx, nin int) {
 	}
 }
 
+func checkLambdaArgumentType(ctx *blockCtx, lambda ast.Expr, fun ast.Expr, argTyp types.Type) *types.Signature {
+	typ := argTyp
+retry:
+	switch t := typ.(type) {
+	case *types.Signature:
+		return t
+	case *types.Named:
+		typ = t.Underlying()
+		goto retry
+	}
+	pos := ctx.Position(lambda.Pos())
+	src, _ := ctx.LoadExpr(fun)
+	err := newCodeErrorf(&pos, "cannot use lambda literal as type %v in argument to %v", argTyp, src)
+	panic(err)
+}
+
 func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, flags int) {
 	switch fn := v.Fun.(type) {
 	case *ast.Ident:
@@ -431,24 +447,19 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, flags int) {
 	fnt := ctx.cb.Get(-1).Type
 	ellipsis := (v.Ellipsis != gotoken.NoPos)
 	for i, arg := range v.Args {
-		if l, ok := arg.(*ast.LambdaExpr); ok {
-			fn.initWith(fnt, i, len(l.Lhs))
-			if sig, ok := fn.arg(i, true).(*types.Signature); ok {
-				compileLambdaExpr(ctx, l, sig.Params())
-				continue
-			}
-		}
-		if l, ok := arg.(*ast.LambdaExpr2); ok {
-			fn.initWith(fnt, i, len(l.Lhs))
-			if sig, ok := fn.arg(i, true).(*types.Signature); ok {
-				compileLambdaExpr2(ctx, l, sig.Params(), sig.Results())
-				continue
-			}
-		}
-		if c, ok := arg.(*ast.CompositeLit); ok && c.Type == nil {
+		switch expr := arg.(type) {
+		case *ast.LambdaExpr:
+			fn.initWith(fnt, i, len(expr.Lhs))
+			sig := checkLambdaArgumentType(ctx, expr, v.Fun, fn.arg(i, true))
+			compileLambdaExpr(ctx, expr, v.Fun, sig)
+		case *ast.LambdaExpr2:
+			fn.initWith(fnt, i, len(expr.Lhs))
+			sig := checkLambdaArgumentType(ctx, expr, v.Fun, fn.arg(i, true))
+			compileLambdaExpr2(ctx, expr, sig)
+		case *ast.CompositeLit:
 			fn.initWith(fnt, i, -1)
-			compileCompositeLit(ctx, c, fn.arg(i, ellipsis), true)
-		} else {
+			compileCompositeLit(ctx, expr, fn.arg(i, ellipsis), true)
+		default:
 			compileExpr(ctx, arg)
 		}
 	}
@@ -477,13 +488,20 @@ func compileLambdaParams(ctx *blockCtx, pos token.Pos, lhs []*ast.Ident, in *typ
 	return params
 }
 
-func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, in *types.Tuple) {
+func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, fun ast.Expr, sig *types.Signature) {
 	pkg := ctx.pkg
-	params := compileLambdaParams(ctx, v.Pos(), v.Lhs, in)
-	nout := len(v.Rhs)
+	params := compileLambdaParams(ctx, v.Pos(), v.Lhs, sig.Params())
+	out := sig.Results()
+	nout := out.Len()
+	if nout != len(v.Rhs) {
+		pos := ctx.Position(v.Pos())
+		src, _ := ctx.LoadExpr(fun)
+		err := newCodeErrorf(&pos, "cannot use lambda literal as type %v in argument to %v", sig, src)
+		panic(err)
+	}
 	results := make([]*types.Var, nout)
 	for i := 0; i < nout; i++ {
-		results[i] = pkg.NewAutoParam("")
+		results[i] = pkg.NewParam(token.NoPos, "", out.At(i).Type())
 	}
 	ctx.cb.NewClosure(types.NewTuple(params...), types.NewTuple(results...), false).BodyStart(pkg)
 	for _, v := range v.Rhs {
@@ -492,11 +510,12 @@ func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, in *types.Tuple) {
 	ctx.cb.Return(nout).End()
 }
 
-func compileLambdaExpr2(ctx *blockCtx, v *ast.LambdaExpr2, in *types.Tuple, out *types.Tuple) {
+func compileLambdaExpr2(ctx *blockCtx, v *ast.LambdaExpr2, sig *types.Signature) {
 	pkg := ctx.pkg
-	params := compileLambdaParams(ctx, v.Pos(), v.Lhs, in)
+	params := compileLambdaParams(ctx, v.Pos(), v.Lhs, sig.Params())
 	cb := ctx.cb
 	comments := cb.Comments()
+	out := sig.Results()
 	nout := out.Len()
 	results := make([]*types.Var, nout)
 	for i := 0; i < nout; i++ {


Test patch:
diff --git a/cl/compile_test.go b/cl/compile_test.go
index 0ea78d12..da4fcf67 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -2751,6 +2751,30 @@ func main() {
 		return x * x, x + x
 	})
 }
+`)
+	gopClTest(t, `
+type Fn func() (int, error)
+func Do(fn Fn) {
+	v, err := fn()
+	println(v, err)
+}
+
+Do => (100, nil)
+`, `package main
+
+import fmt "fmt"
+
+type Fn func() (int, error)
+
+func Do(fn Fn) {
+	v, err := fn()
+	fmt.Println(v, err)
+}
+func main() {
+	Do(func() (int, error) {
+		return 100, nil
+	})
+}
 `)
 }
 
@@ -2797,6 +2821,32 @@ func main() {
 		return 100, nil
 	})
 }
+`)
+	gopClTest(t, `
+type Fn func() (int, error)
+func Do(fn Fn) {
+	v, err := fn()
+	println(v, err)
+}
+
+Do => {
+	return 100, nil
+}
+`, `package main
+
+import fmt "fmt"
+
+type Fn func() (int, error)
+
+func Do(fn Fn) {
+	v, err := fn()
+	fmt.Println(v, err)
+}
+func main() {
+	Do(func() (int, error) {
+		return 100, nil
+	})
+}
 `)
 }
 
diff --git a/cl/error_msg_test.go b/cl/error_msg_test.go
index b4ee9bcb..3af9bb7d 100644
--- a/cl/error_msg_test.go
+++ b/cl/error_msg_test.go
@@ -71,6 +71,19 @@ func foo(func(int, int)) {
 func main() {
 	foo((x, y, z) => {})
 }
+`)
+
+	codeErrorTest(t,
+		"./bar.gop:4:5: cannot use lambda literal as type int in argument to foo", `
+func foo(int) {
+}
+foo(=> {})
+`)
+	codeErrorTest(t,
+		"./bar.gop:4:5: cannot use lambda literal as type func() in argument to foo", `
+func foo(func()) {
+}
+foo => (100)
 `)
 }