Repository: goplus/gop
Pull request #935 (2021-11-24 14:11:30)

cl: support lambda in struct literal 
fix https://github.com/goplus/gop/issues/933

key:value only
```
type Foo struct {
    Plot func(x float64) (float64, float64)
}

foo := &Foo{
    Plot: x => (x * 2, x * x),
}

bar := &Foo{
    Plot: x => { 
        return x * 2, x * x
    },
}
```



Patch:
diff --git a/cl/expr.go b/cl/expr.go
index f6c205f2..3ae52203 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -418,22 +418,6 @@ 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:
@@ -450,11 +434,11 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, flags int) {
 		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)
+			sig := checkLambdaArgumentType(ctx, expr, v.Fun, fn.arg(i, true), len(expr.Rhs))
+			compileLambdaExpr(ctx, expr, sig)
 		case *ast.LambdaExpr2:
 			fn.initWith(fnt, i, len(expr.Lhs))
-			sig := checkLambdaArgumentType(ctx, expr, v.Fun, fn.arg(i, true))
+			sig := checkLambdaArgumentType(ctx, expr, v.Fun, fn.arg(i, true), -1)
 			compileLambdaExpr2(ctx, expr, sig)
 		case *ast.CompositeLit:
 			fn.initWith(fnt, i, -1)
@@ -466,6 +450,43 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, flags int) {
 	ctx.cb.CallWith(len(v.Args), ellipsis, v)
 }
 
+// check lambda func argument type, check results len if chkResults >= 0.
+func checkLambdaArgumentType(ctx *blockCtx, lambda ast.Expr, fun ast.Expr, argTyp types.Type, chkResults int) *types.Signature {
+	typ := argTyp
+retry:
+	switch t := typ.(type) {
+	case *types.Signature:
+		if chkResults < 0 || chkResults == t.Results().Len() {
+			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)
+}
+
+// check lambda field type, check results len if chkResults >= 0.
+func checkLambdaFieldType(ctx *blockCtx, lambda ast.Expr, fieldType types.Type, chkResults int) *types.Signature {
+	typ := fieldType
+retry:
+	switch t := typ.(type) {
+	case *types.Signature:
+		if chkResults < 0 || chkResults == t.Results().Len() {
+			return t
+		}
+	case *types.Named:
+		typ = t.Underlying()
+		goto retry
+	}
+	pos := ctx.Position(lambda.Pos())
+	err := newCodeErrorf(&pos, "cannot use lambda literal as type %v in field value", fieldType)
+	panic(err)
+}
+
 func compileLambdaParams(ctx *blockCtx, pos token.Pos, lhs []*ast.Ident, in *types.Tuple) []*types.Var {
 	pkg := ctx.pkg
 	n := len(lhs)
@@ -488,17 +509,11 @@ func compileLambdaParams(ctx *blockCtx, pos token.Pos, lhs []*ast.Ident, in *typ
 	return params
 }
 
-func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, fun ast.Expr, sig *types.Signature) {
+func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, sig *types.Signature) {
 	pkg := ctx.pkg
 	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.NewParam(token.NoPos, "", out.At(i).Type())
@@ -591,12 +606,22 @@ func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, t
 	for _, elt := range elts {
 		kv := elt.(*ast.KeyValueExpr)
 		name := kv.Key.(*ast.Ident).Name
-		if idx := lookupField(t, name); idx >= 0 {
+		idx := lookupField(t, name)
+		if idx >= 0 {
 			ctx.cb.Val(idx)
 		} else {
 			log.Panicln("TODO: struct member not found -", name)
 		}
-		compileExpr(ctx, kv.Value)
+		switch expr := kv.Value.(type) {
+		case *ast.LambdaExpr:
+			sig := checkLambdaFieldType(ctx, expr, t.Field(idx).Type(), len(expr.Rhs))
+			compileLambdaExpr(ctx, expr, sig)
+		case *ast.LambdaExpr2:
+			sig := checkLambdaFieldType(ctx, expr, t.Field(idx).Type(), -1)
+			compileLambdaExpr2(ctx, expr, sig)
+		default:
+			compileExpr(ctx, kv.Value)
+		}
 	}
 	ctx.cb.StructLit(typ, len(elts)<<1, true)
 }
diff --git a/cl/stmt.go b/cl/stmt.go
index 2161cb7b..d79f5c7c 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -60,8 +60,8 @@ func commentStmt(ctx *blockCtx, stmt ast.Stmt) {
 func compileStmts(ctx *blockCtx, body []ast.Stmt) {
 	for _, stmt := range body {
 		if v, ok := stmt.(*ast.LabeledStmt); ok {
-			l := v.Label
-			ctx.cb.NewLabel(l.Pos(), l.Name)
+			expr := v.Label
+			ctx.cb.NewLabel(expr.Pos(), expr.Name)
 		}
 	}
 	for _, stmt := range body {
diff --git a/parser/parser.go b/parser/parser.go
index 8412ab22..0d8beba5 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -1763,6 +1763,8 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
 	case *ast.BinaryExpr:
 	case *ast.RangeExpr:
 	case *ast.ErrWrapExpr:
+	case *ast.LambdaExpr:
+	case *ast.LambdaExpr2:
 	default:
 		// all other nodes are not proper expressions
 		p.errorExpected(x.Pos(), "expression", 3)


Test patch:
diff --git a/cl/compile_test.go b/cl/compile_test.go
index da4fcf67..78413739 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -2751,6 +2751,43 @@ func main() {
 		return x * x, x + x
 	})
 }
+`)
+	gopClTest(t, `type Foo struct {
+	Plot func(x float64) (float64, float64)
+}
+foo := &Foo{
+	Plot: x => (x * 2, x * x),
+}`, `package main
+
+type Foo struct {
+	Plot func(x float64) (float64, float64)
+}
+
+func main() {
+	foo := &Foo{Plot: func(x float64) (float64, float64) {
+		return x * 2, x * x
+	}}
+}
+`)
+	gopClTest(t, `
+type Fn func(x float64) (float64, float64)
+type Foo struct {
+	Plot Fn
+}
+foo := &Foo{
+	Plot: x => (x * 2, x * x),
+}`, `package main
+
+type Fn func(x float64) (float64, float64)
+type Foo struct {
+	Plot Fn
+}
+
+func main() {
+	foo := &Foo{Plot: func(x float64) (float64, float64) {
+		return x * 2, x * x
+	}}
+}
 `)
 	gopClTest(t, `
 type Fn func() (int, error)
@@ -2822,6 +2859,48 @@ func main() {
 	})
 }
 `)
+	gopClTest(t, `type Foo struct {
+	Plot func(x float64) (float64, float64)
+}
+foo := &Foo{
+	Plot: x => {
+		return x * 2, x * x
+	},
+}`, `package main
+
+type Foo struct {
+	Plot func(x float64) (float64, float64)
+}
+
+func main() {
+	foo := &Foo{Plot: func(x float64) (float64, float64) {
+		return x * 2, x * x
+	}}
+}
+`)
+	gopClTest(t, `
+type Fn func(x float64) (float64, float64)
+type Foo struct {
+	Plot Fn
+}
+foo := &Foo{
+	Plot: x => {
+		return x * 2, x * x
+	},
+}`, `package main
+
+type Fn func(x float64) (float64, float64)
+type Foo struct {
+	Plot Fn
+}
+
+func main() {
+	foo := &Foo{Plot: func(x float64) (float64, float64) {
+		return x * 2, x * x
+	}}
+}
+`)
+
 	gopClTest(t, `
 type Fn func() (int, error)
 func Do(fn Fn) {
diff --git a/cl/error_msg_test.go b/cl/error_msg_test.go
index 3af9bb7d..6f408652 100644
--- a/cl/error_msg_test.go
+++ b/cl/error_msg_test.go
@@ -72,7 +72,24 @@ func main() {
 	foo((x, y, z) => {})
 }
 `)
-
+	codeErrorTest(t, "./bar.gop:6:8: cannot use lambda literal as type int in field value", `
+type Foo struct {
+	Plot int
+}
+foo := &Foo{
+	Plot: x => (x * 2, x * x),
+}
+`)
+	codeErrorTest(t, "./bar.gop:6:8: cannot use lambda literal as type int in field value", `
+type Foo struct {
+	Plot int
+}
+foo := &Foo{
+	Plot: x => {
+		return x * 2, x * x
+	},
+}
+`)
 	codeErrorTest(t,
 		"./bar.gop:4:5: cannot use lambda literal as type int in argument to foo", `
 func foo(int) {
@@ -84,6 +101,15 @@ foo(=> {})
 func foo(func()) {
 }
 foo => (100)
+`)
+	codeErrorTest(t,
+		"./bar.gop:6:8: cannot use lambda literal as type func() int in field value", `
+type Foo struct {
+	Plot func() int
+}
+foo := &Foo{
+	Plot: x => (x * 2, x * x),
+}
 `)
 }
 
diff --git a/parser/_testdata/lambda4/lambda4.gop b/parser/_testdata/lambda4/lambda4.gop
new file mode 100644
index 00000000..b9c9fe5a
--- /dev/null
+++ b/parser/_testdata/lambda4/lambda4.gop
@@ -0,0 +1,13 @@
+type Foo struct {
+	Plot func(x float64) (float64, float64)
+}
+
+&Foo{
+	Plot: x => (x * 2, x * x),
+}
+
+&Foo{
+	Plot: x => {
+		return x * 2, x * x
+	},
+}
diff --git a/parser/_testdata/lambda4/parser.expect b/parser/_testdata/lambda4/parser.expect
new file mode 100644
index 00000000..190127ff
--- /dev/null
+++ b/parser/_testdata/lambda4/parser.expect
@@ -0,0 +1,132 @@
+package main
+
+file lambda4.gop
+noEntrypoint
+ast.GenDecl:
+  Tok: type
+  Specs:
+    ast.TypeSpec:
+      Name:
+        ast.Ident:
+          Name: Foo
+      Type:
+        ast.StructType:
+          Fields:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Names:
+                    ast.Ident:
+                      Name: Plot
+                  Type:
+                    ast.FuncType:
+                      Params:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: x
+                              Type:
+                                ast.Ident:
+                                  Name: float64
+                      Results:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Type:
+                                ast.Ident:
+                                  Name: float64
+                            ast.Field:
+                              Type:
+                                ast.Ident:
+                                  Name: float64
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: main
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+  Body:
+    ast.BlockStmt:
+      List:
+        ast.ExprStmt:
+          X:
+            ast.UnaryExpr:
+              Op: &
+              X:
+                ast.CompositeLit:
+                  Type:
+                    ast.Ident:
+                      Name: Foo
+                  Elts:
+                    ast.KeyValueExpr:
+                      Key:
+                        ast.Ident:
+                          Name: Plot
+                      Value:
+                        ast.LambdaExpr:
+                          Lhs:
+                            ast.Ident:
+                              Name: x
+                          Rhs:
+                            ast.BinaryExpr:
+                              X:
+                                ast.Ident:
+                                  Name: x
+                              Op: *
+                              Y:
+                                ast.BasicLit:
+                                  Kind: INT
+                                  Value: 2
+                            ast.BinaryExpr:
+                              X:
+                                ast.Ident:
+                                  Name: x
+                              Op: *
+                              Y:
+                                ast.Ident:
+                                  Name: x
+        ast.ExprStmt:
+          X:
+            ast.UnaryExpr:
+              Op: &
+              X:
+                ast.CompositeLit:
+                  Type:
+                    ast.Ident:
+                      Name: Foo
+                  Elts:
+                    ast.KeyValueExpr:
+                      Key:
+                        ast.Ident:
+                          Name: Plot
+                      Value:
+                        ast.LambdaExpr2:
+                          Lhs:
+                            ast.Ident:
+                              Name: x
+                          Body:
+                            ast.BlockStmt:
+                              List:
+                                ast.ReturnStmt:
+                                  Results:
+                                    ast.BinaryExpr:
+                                      X:
+                                        ast.Ident:
+                                          Name: x
+                                      Op: *
+                                      Y:
+                                        ast.BasicLit:
+                                          Kind: INT
+                                          Value: 2
+                                    ast.BinaryExpr:
+                                      X:
+                                        ast.Ident:
+                                          Name: x
+                                      Op: *
+                                      Y:
+                                        ast.Ident:
+                                          Name: x