Repository: goplus/gop
Pull request #578 (2020-08-29 00:47:31)

support starExpr in lhs
fix bug: https://github.com/goplus/gop/issues/569

**done**



codecov-commenter: # [Codecov](https://codecov.io/gh/goplus/gop/pull/578?src=pr&el=h1) Report
> Merging [#578](https://codecov.io/gh/goplus/gop/pull/578?src=pr&el=desc) into [master](https://codecov.io/gh/goplus/gop/commit/190f047fcd046bcc5ee0f755dc4ffba334b5d11c?el=desc) will **increase** coverage by `0.02%`.
> The diff coverage is `89.58%`.

[![Impacted file tree graph](https://codecov.io/gh/goplus/gop/pull/578/graphs/tree.svg?width=650&height=150&src=pr&token=4qln9Wk3Pn)](https://codecov.io/gh/goplus/gop/pull/578?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #578      +/-   ##
==========================================
+ Coverage   90.20%   90.22%   +0.02%     
==========================================
  Files          41       41              
  Lines       10488    10532      +44     
==========================================
+ Hits         9461     9503      +42     
- Misses        739      742       +3     
+ Partials      288      287       -1     
```


| [Impacted Files](https://codecov.io/gh/goplus/gop/pull/578?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [cl/context.go](https://codecov.io/gh/goplus/gop/pull/578/diff?src=pr&el=tree#diff-Y2wvY29udGV4dC5nbw==) | `87.39% <ø> (ø)` | |
| [exec/golang/builder.go](https://codecov.io/gh/goplus/gop/pull/578/diff?src=pr&el=tree#diff-ZXhlYy9nb2xhbmcvYnVpbGRlci5nbw==) | `90.96% <ø> (ø)` | |
| [cl/expr.go](https://codecov.io/gh/goplus/gop/pull/578/diff?src=pr&el=tree#diff-Y2wvZXhwci5nbw==) | `86.77% <87.17%> (-0.05%)` | :arrow_down: |
| [exec/golang/var.go](https://codecov.io/gh/goplus/gop/pull/578/diff?src=pr&el=tree#diff-ZXhlYy9nb2xhbmcvdmFyLmdv) | `86.55% <100.00%> (+1.10%)` | :arrow_up: |
| [parser/parser.go](https://codecov.io/gh/goplus/gop/pull/578/diff?src=pr&el=tree#diff-cGFyc2VyL3BhcnNlci5nbw==) | `79.80% <0.00%> (+0.06%)` | :arrow_up: |
| [exec/bytecode/goinstr\_lstcompr.go](https://codecov.io/gh/goplus/gop/pull/578/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9nb2luc3RyX2xzdGNvbXByLmdv) | `92.34% <0.00%> (+0.52%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goplus/gop/pull/578?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goplus/gop/pull/578?src=pr&el=footer). Last update [190f047...70380b4](https://codecov.io/gh/goplus/gop/pull/578?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/cl/context.go b/cl/context.go
index a24cdf88..ca0fd26c 100644
--- a/cl/context.go
+++ b/cl/context.go
@@ -193,6 +193,7 @@ type blockCtx struct {
 	syms            map[string]iSymbol
 	noExecCtx       bool
 	takeAddr        bool
+	indirect        bool
 	checkFlag       bool
 	checkLoadAddr   bool
 	fieldStructType reflect.Type
diff --git a/cl/ctx_check.go b/cl/ctx_check.go
index 0211ede5..b61c81fd 100644
--- a/cl/ctx_check.go
+++ b/cl/ctx_check.go
@@ -120,6 +120,8 @@ func isNoExecCtxExpr(ctx *blockCtx, expr ast.Expr) bool {
 		return isNoExecCtxListComprehensionExpr(ctx, v)
 	case *ast.MapComprehensionExpr:
 		return isNoExecCtxMapComprehensionExpr(ctx, v)
+	case *ast.StarExpr:
+		return isNoExecCtxStarExpr(ctx, v)
 	case *ast.ArrayType:
 		return true
 	case *ast.Ellipsis:
@@ -256,6 +258,13 @@ func isNoExecCtx2nd(ctx *blockCtx, a, b ast.Expr) bool {
 	return isNoExecCtxExpr(ctx, b)
 }
 
+func isNoExecCtxStarExpr(ctx *blockCtx, v *ast.StarExpr) bool {
+	if noExecCtx := isNoExecCtxExpr(ctx, v.X); !noExecCtx {
+		return false
+	}
+	return true
+}
+
 func isNoExecCtxCallExpr(ctx *blockCtx, v *ast.CallExpr) bool {
 	switch expr := v.Fun.(type) {
 	case *ast.Ident:
@@ -357,6 +366,8 @@ func isNoExecCtxExprLHS(ctx *blockCtx, expr ast.Expr, mode compileMode) bool {
 		return isNoExecCtxIndexExprLHS(ctx, v, mode)
 	case *ast.SelectorExpr:
 		return isNoExecCtxSelectorExprLHS(ctx, v, mode)
+	case *ast.StarExpr:
+		return isNoExecCtxStarExprLHS(ctx, v, mode)
 	default:
 		log.Panicln("isNoExecCtxExprLHS failed: unknown -", reflect.TypeOf(v))
 	}
@@ -384,4 +395,11 @@ func isNoExecCtxSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compile
 	return true
 }
 
+func isNoExecCtxStarExprLHS(ctx *blockCtx, v *ast.StarExpr, mode compileMode) bool {
+	if noExecCtx := isNoExecCtxExpr(ctx, v.X); !noExecCtx {
+		return false
+	}
+	return true
+}
+
 // -----------------------------------------------------------------------------
diff --git a/cl/expr.go b/cl/expr.go
index 3f3c8ba0..54395f88 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -48,6 +48,8 @@ func compileExprLHS(ctx *blockCtx, expr ast.Expr, mode compileMode) {
 		compileIndexExprLHS(ctx, v, mode)
 	case *ast.SelectorExpr:
 		compileSelectorExprLHS(ctx, v, mode)
+	case *ast.StarExpr:
+		compileStarExprLHS(ctx, v, mode)
 	default:
 		log.Panicln("compileExpr failed: unknown -", reflect.TypeOf(v))
 	}
@@ -89,6 +91,8 @@ func compileExpr(ctx *blockCtx, expr ast.Expr) func() {
 		return compileArrayType(ctx, v)
 	case *ast.Ellipsis:
 		return compileEllipsis(ctx, v)
+	case *ast.StarExpr:
+		return compileStarExpr(ctx, v)
 	case *ast.KeyValueExpr:
 		panic("compileExpr: ast.KeyValueExpr unexpected")
 	default:
@@ -116,7 +120,11 @@ func compileIdentLHS(ctx *blockCtx, name string, mode compileMode) {
 		typ := boundType(in.(iValue))
 		addr = ctx.insertVar(name, typ)
 	}
-	checkType(addr.getType(), in, ctx.out)
+	typ := addr.getType()
+	if ctx.indirect {
+		typ = typ.Elem()
+	}
+	checkType(typ, in, ctx.out)
 	ctx.infer.PopN(1)
 	if v, ok := addr.(*execVar); ok {
 		if mode == token.ASSIGN || mode == token.DEFINE {
@@ -128,7 +136,11 @@ func compileIdentLHS(ctx *blockCtx, name string, mode compileMode) {
 		}
 	} else {
 		if mode == token.ASSIGN || mode == token.DEFINE {
-			ctx.out.Store(addr.(*stackVar).index)
+			if ctx.indirect {
+				ctx.out.Load(addr.(*stackVar).index).AddrOp(kindOf(addr.(*stackVar).getType()), exec.OpAssign)
+			} else {
+				ctx.out.Store(addr.(*stackVar).index)
+			}
 		} else {
 			panic("compileIdentLHS: todo")
 		}
@@ -823,7 +835,9 @@ func compileIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compileMode) {
 	}
 	exprX()
 	ctx.checkLoadAddr = false
-
+	if ctx.indirect {
+		typElem = typElem.Elem()
+	}
 	if cons, ok := val.(*constVal); ok {
 		cons.bound(typElem, ctx.out)
 	} else if t := val.(iValue).Type(); t != typElem && typElem.Kind() != reflect.Interface {
@@ -837,7 +851,11 @@ func compileIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compileMode) {
 	case reflect.Slice, reflect.Array:
 		if cons, ok := i.(*constVal); ok {
 			n := boundConst(cons.v, exec.TyInt)
-			ctx.out.SetIndex(n.(int))
+			if ctx.indirect {
+				ctx.out.Index(n.(int)).AddrOp(kindOf(typElem), exec.OpAssign)
+			} else {
+				ctx.out.SetIndex(n.(int))
+			}
 			return
 		}
 		exprIdx()
@@ -848,7 +866,11 @@ func compileIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compileMode) {
 				log.Panicln("compileIndexExprLHS: index expression value type is invalid")
 			}
 		}
-		ctx.out.SetIndex(-1)
+		if ctx.indirect {
+			ctx.out.Index(-1).AddrOp(kindOf(typElem), exec.OpAssign)
+		} else {
+			ctx.out.SetIndex(-1)
+		}
 	case reflect.Map:
 		exprIdx()
 		typIdx := typ.Key()
@@ -858,7 +880,11 @@ func compileIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compileMode) {
 		if t := i.(iValue).Type(); t != typIdx {
 			logIllTypeMapIndexPanic(ctx, v, t, typIdx)
 		}
-		ctx.out.SetMapIndex()
+		if ctx.indirect {
+			ctx.out.MapIndex().AddrOp(kindOf(typElem), exec.OpAssign)
+		} else {
+			ctx.out.SetMapIndex()
+		}
 	default:
 		log.Panicln("compileIndexExprLHS: unknown -", typ)
 	}
@@ -1082,7 +1108,11 @@ func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compileMode
 		if t.PkgPath() != "" && ast.IsExported(name) || t.PkgPath() == "" {
 			if t.Kind() == reflect.Struct {
 				if sf, ok := t.FieldByName(name); ok {
-					checkType(sf.Type, in, ctx.out)
+					typ := sf.Type
+					if ctx.indirect {
+						typ = typ.Elem()
+					}
+					checkType(typ, in, ctx.out)
 					if ctx.fieldIndex == nil {
 						ctx.fieldStructType = vx.t
 					}
@@ -1095,7 +1125,11 @@ func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compileMode
 						exprX()
 					}
 					ctx.checkLoadAddr = false
-					ctx.out.StoreField(fieldStructType, fieldIndex)
+					if ctx.indirect {
+						ctx.out.LoadField(fieldStructType, fieldIndex).AddrOp(kindOf(typ), exec.OpAssign)
+					} else {
+						ctx.out.StoreField(fieldStructType, fieldIndex)
+					}
 				}
 			}
 		} else if t.PkgPath() != "" && !ast.IsExported(name) {
@@ -1107,6 +1141,12 @@ func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compileMode
 	_ = exprX
 }
 
+func compileStarExprLHS(ctx *blockCtx, v *ast.StarExpr, mode compileMode) {
+	ctx.indirect = true
+	compileExprLHS(ctx, v.X, mode)
+	ctx.indirect = false
+}
+
 func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool) func() {
 	exprX := compileExpr(ctx, v.X)
 	if v.Sel == nil {
@@ -1229,6 +1269,24 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 	return nil
 }
 
+func compileStarExpr(ctx *blockCtx, v *ast.StarExpr) func() {
+	exprX := compileExpr(ctx, v.X)
+	x := ctx.infer.Get(-1)
+	switch vx := x.(type) {
+	case *nonValue:
+	case *goValue:
+		if vx.Kind() == reflect.Ptr {
+			ctx.infer.Ret(1, &goValue{vx.t.Elem()})
+		}
+		return func() {
+			exprX()
+			ctx.out.AddrOp(kindOf(vx.t), exec.OpAddrVal)
+		}
+	}
+	log.Panicln("compileStarExpr failed: unknown -", reflect.TypeOf(x))
+	return nil
+}
+
 func isLower(name string) bool {
 	for _, c := range name {
 		return ctype.Is(ctype.LOWER, c)
diff --git a/exec/golang/var.go b/exec/golang/var.go
index 9a9410ee..dfaa2bc6 100644
--- a/exec/golang/var.go
+++ b/exec/golang/var.go
@@ -220,6 +220,16 @@ func (p *Builder) AddrOp(kind exec.Kind, op exec.AddrOperator) *Builder {
 		})
 		return p
 	}
+	if op == exec.OpAssign {
+		p.emitStmt(&ast.AssignStmt{
+			Lhs: []ast.Expr{&ast.StarExpr{
+				X: p.rhs.Pop().(ast.Expr),
+			}},
+			Tok: token.ASSIGN,
+			Rhs: []ast.Expr{p.rhs.Pop().(ast.Expr)},
+		})
+		return p
+	}
 	var stmt ast.Stmt
 	var x = p.rhs.Pop()
 	var val = p.rhs.Pop().(ast.Expr)
diff --git a/tutorial/27-Func-Set/func.gop b/tutorial/27-Func-Set/func.gop
new file mode 100644
index 00000000..2b51907c
--- /dev/null
+++ b/tutorial/27-Func-Set/func.gop
@@ -0,0 +1,34 @@
+func A(a *int, c *struct {
+	b *int
+	m map[string]*int
+	s []*int
+}) {
+	*a = 5
+	*c.b = 3
+	*c.m["foo"] = 7
+	*c.s[0] = 9
+}
+
+func Index() int {
+	return 0
+}
+
+a1 := 6
+a2 := 6
+a3 := 6
+c := struct {
+	b *int
+	m map[string]*int
+	s []*int
+}{
+	b: &a1,
+	m: map[string]*int{
+		"foo": &a2,
+	},
+	s: []*int{&a3},
+}
+A(&a1, &c)
+*c.m["foo"] = 8
+*c.s[0] = 10
+*c.s[Index()] = 11
+println(a1, *c.b, *c.m["foo"], *c.s[0])


Test patch:
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 2eb9355b..066f5b63 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -1075,6 +1075,118 @@ func TestMethodCases(t *testing.T) {
 
 // -----------------------------------------------------------------------------
 
+// -----------------------------------------------------------------------------
+var testStarExprClauses = map[string]testData{
+	"star expr": {`
+				func A(a *int, c *struct {
+					b *int
+					m map[string]*int
+					s []*int
+				}) {
+					*a = 5
+					*c.b = 3
+					*c.m["foo"] = 7
+					*c.s[0] = 9
+				}
+
+				a1 := 6
+				a2 := 6
+				a3 := 6
+				c := struct {
+					b *int
+					m map[string]*int
+					s []*int
+				}{
+					b: &a1,
+					m: map[string]*int{
+						"foo": &a2,
+					},
+					s: []*int{&a3},
+				}
+				A(&a1, &c)
+				*c.m["foo"] = 8
+				*c.s[0] = 10
+				*c.s[0+0] = 10
+				println(a1, *c.b, *c.m["foo"], *c.s[0], *c.s[0+0])
+
+					`, "3 3 8 10 10\n", false},
+	"star expr exec": {`
+					func A(a *int, c *struct {
+						b *int
+						m map[string]*int
+						s []*int
+					}) {
+						*a = 5
+						*c.b = 3
+						*c.m["foo"] = 7
+						*c.s[0] = 9
+					}
+	
+					func main() {
+						a1 := 6
+						a2 := 6
+						a3 := 6
+						c := struct {
+							b *int
+							m map[string]*int
+							s []*int
+						}{
+							b: &a1,
+							m: map[string]*int{
+								"foo": &a2,
+							},
+							s: []*int{&a3},
+						}
+						A(&a1, &c)
+						*c.m["foo"] = 8
+						*c.s[0] = 10
+						*c.s[0+0] = 10
+						println(a1, *c.b, *c.m["foo"], *c.s[0], *c.s[0+0])
+					}
+						`, "3 3 8 10 10\n", false},
+	"star expr lhs slice index func": {`
+					func A(a *int, c *struct {
+						b *int
+						m map[string]*int
+						s []*int
+					}) {
+						*a = 5
+						*c.b = 3
+						*c.m["foo"] = 7
+						*c.s[0] = 9
+					}
+					
+					func Index() int {
+						return 0
+					}
+					
+					a1 := 6
+					a2 := 6
+					a3 := 6
+					c := struct {
+						b *int
+						m map[string]*int
+						s []*int
+					}{
+						b: &a1,
+						m: map[string]*int{
+							"foo": &a2,
+						},
+						s: []*int{&a3},
+					}
+					A(&a1, &c)
+					*c.m["foo"] = 8
+					*c.s[0] = 10
+					*c.s[Index()] = 11
+					println(a1, *c.b, *c.m["foo"], *c.s[0])
+	
+						`, "3 3 8 11\n", false},
+}
+
+func TestStarExpr(t *testing.T) {
+	testScripts(t, "TestStarExpr", testStarExprClauses)
+}
+
 func testScripts(t *testing.T, testName string, scripts map[string]testData) {
 	for name, script := range scripts {
 		t.Log("Run " + testName + "---" + name)
diff --git a/exec/golang/testdata/29.func-set/func.gop b/exec/golang/testdata/29.func-set/func.gop
new file mode 100644
index 00000000..ef7cbc33
--- /dev/null
+++ b/exec/golang/testdata/29.func-set/func.gop
@@ -0,0 +1,29 @@
+func A(a *int, c *struct {
+	b *int
+	m map[string]*int
+	s []*int
+}) {
+	*a = 5
+	*c.b = 3
+	*c.m["foo"] = 7
+	*c.s[0] = 9
+}
+
+a1 := 6
+a2 := 6
+a3 := 6
+c := struct {
+	b *int
+	m map[string]*int
+	s []*int
+}{
+	b: &a1,
+	m: map[string]*int{
+		"foo": &a2,
+	},
+	s: []*int{&a3},
+}
+A(&a1, &c)
+*c.m["foo"] = 8
+*c.s[0] = 10
+println(a1, *c.b, *c.m["foo"])