Repository: goplus/gop
Pull request #556 (2020-08-17 10:45:02)

cl: take address value and field
support take address value&field

```
Go+ -> generate Golang
&array[0] -> &(&array)[0]
&slice[0] -> &slice[0]

&map[0] : panic cannot take the address
```

Go+ code
```
import "image"

type Point struct {
	X int
	Y int
}

pt := Point{10,20}
println(&pt)
println(&pt.X)

m := {1:"hello",2:"world"}
println(&m)
//println(&m[1]) // panic cannot take the address of m[1]

ar := [2]Point{Point{10,20},Point{100,200}}
println(&ar)
println(&ar[0])
println(&ar[0].X)

rcs := [image.Rect(1,1,10,10),image.Rect(2,2,20,20)]
println(&rcs)
println(&rcs[0])
println(&rcs[0].Min)
println(&rcs[0].Min.X)

rc := image.Rect(10,20,100,200)
println(&rc)
println(&rc.Min)
println(&rc.Min.X)

println(&image.ZR)
println(&image.ZR.Min)
println(&image.ZR.Min.X)
```
generate Golang code
```
package main

import (
	fmt "fmt"
	image "image"
)

type Point struct {
	X int
	Y int
}

var (
	pt  Point
	m   map[int]string
	ar  [2]Point
	rcs []image.Rectangle
	rc  image.Rectangle
)

func main() {
//line ./main.gop:9
	pt = Point{X: 10, Y: 20}
//line ./main.gop:10
	fmt.Println(&pt)
//line ./main.gop:11
	fmt.Println(&pt.X)
//line ./main.gop:13
	m = map[int]string{1: "hello", 2: "world"}
//line ./main.gop:14
	fmt.Println(&m)
//line ./main.gop:17
	ar = [2]Point{Point{X: 10, Y: 20}, Point{X: 100, Y: 200}}
//line ./main.gop:18
	fmt.Println(&ar)
//line ./main.gop:19
	fmt.Println(&(&ar)[0])
//line ./main.gop:20
	fmt.Println(&(&ar)[0].X)
//line ./main.gop:22
	rcs = []image.Rectangle{image.Rect(1, 1, 10, 10), image.Rect(2, 2, 20, 20)}
//line ./main.gop:23
	fmt.Println(&rcs)
//line ./main.gop:24
	fmt.Println(&rcs[0])
//line ./main.gop:25
	fmt.Println(&rcs[0].Min)
//line ./main.gop:26
	fmt.Println(&rcs[0].Min.X)
//line ./main.gop:28
	rc = image.Rect(10, 20, 100, 200)
//line ./main.gop:29
	fmt.Println(&rc)
//line ./main.gop:30
	fmt.Println(&rc.Min)
//line ./main.gop:31
	fmt.Println(&rc.Min.X)
//line ./main.gop:33
	fmt.Println(&image.ZR)
//line ./main.gop:34
	fmt.Println(&image.ZR.Min)
//line ./main.gop:35
	fmt.Println(&image.ZR.Min.X)
}
```

codecov-commenter: # [Codecov](https://codecov.io/gh/goplus/gop/pull/556?src=pr&el=h1) Report
> Merging [#556](https://codecov.io/gh/goplus/gop/pull/556?src=pr&el=desc) into [master](https://codecov.io/gh/goplus/gop/commit/57fa3c6f480a461b817e6397ee47ac76b5b5ee5f&el=desc) will **decrease** coverage by `0.06%`.
> The diff coverage is `50.00%`.

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

```diff
@@            Coverage Diff             @@
##           master     #556      +/-   ##
==========================================
- Coverage   90.22%   90.15%   -0.07%     
==========================================
  Files          40       40              
  Lines       10329    10337       +8     
==========================================
  Hits         9319     9319              
- Misses        722      727       +5     
- Partials      288      291       +3     
```


| [Impacted Files](https://codecov.io/gh/goplus/gop/pull/556?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [cl/expr.go](https://codecov.io/gh/goplus/gop/pull/556/diff?src=pr&el=tree#diff-Y2wvZXhwci5nbw==) | `85.50% <50.00%> (-0.79%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goplus/gop/pull/556?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/556?src=pr&el=footer). Last update [57fa3c6...40f2a87](https://codecov.io/gh/goplus/gop/pull/556?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/cl/expr.go b/cl/expr.go
index 41fcfbc3..77d805eb 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -161,9 +161,7 @@ func compileIdent(ctx *blockCtx, name string) func() {
 			ctx.infer.Push(&goValue{t: typ})
 			ctx.resetFieldIndex()
 			return func() {
-				if ctx.checkLoadAddr && kind != reflect.Slice && kind != reflect.Map {
-					ctx.out.AddrVar(v.v)
-				} else if ctx.takeAddr {
+				if ctx.takeAddr || (ctx.checkLoadAddr && kind != reflect.Slice && kind != reflect.Map) {
 					ctx.out.AddrVar(v.v)
 				} else {
 					ctx.out.LoadVar(v.v)
@@ -937,17 +935,29 @@ func compileIndexExpr(ctx *blockCtx, v *ast.IndexExpr) func() { // x[i]
 	ctx.infer.Ret(1, &goValue{typElem})
 	ctx.resetFieldIndex()
 	return func() {
+		if ctx.takeAddr {
+			if kind == reflect.String || kind == reflect.Map {
+				_, info := ctx.getCodeInfo(v)
+				log.Panicf("cannot take the address of %v\n", info)
+			} else if kind == reflect.Slice {
+				ctx.takeAddr = false
+				exprX()
+				ctx.takeAddr = true
+			} else {
+				exprX()
+			}
+		} else {
+			exprX()
+		}
 		switch kind {
 		case reflect.String, reflect.Slice, reflect.Array:
-			exprX()
 			n := compileIdx(ctx, v.Index, 1<<30, kind)
-			if ctx.checkLoadAddr {
+			if ctx.takeAddr || ctx.checkLoadAddr {
 				ctx.out.AddrIndex(n)
 			} else {
 				ctx.out.Index(n)
 			}
 		case reflect.Map:
-			exprX()
 			typIdx := typ.Key()
 			compileExpr(ctx, v.Index)()
 			i := ctx.infer.Pop()
@@ -1122,7 +1132,7 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 				ctx.infer.Ret(1, &goValue{t: typ})
 				ctx.resetFieldIndex()
 				return func() {
-					if ctx.checkLoadAddr && kind != reflect.Slice && kind != reflect.Map {
+					if ctx.takeAddr || (ctx.checkLoadAddr && kind != reflect.Slice && kind != reflect.Map) {
 						ctx.out.AddrGoVar(exec.GoVarAddr(addr))
 					} else {
 						ctx.out.LoadGoVar(exec.GoVarAddr(addr))
@@ -1152,7 +1162,7 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 				if fieldExprX != nil {
 					fieldExprX()
 				}
-				if ctx.checkLoadAddr {
+				if ctx.takeAddr || ctx.checkLoadAddr {
 					ctx.out.AddrField(fieldStructType, fieldIndex)
 				} else {
 					ctx.out.LoadField(fieldStructType, fieldIndex)


Test patch:
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 10e8f7b8..a8f19caf 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -366,6 +366,33 @@ func TestPanic(t *testing.T) {
 	)
 }
 
+func TestTakeAddrMap(t *testing.T) {
+	cltest.Expect(t, `
+		m := {1:"hello",2:"ok"}
+		println(m)
+		println(&m)
+		`,
+		"map[1:hello 2:ok]\n&map[1:hello 2:ok]\n")
+}
+
+func TestTakeAddrMapIndexBad(t *testing.T) {
+	cltest.Expect(t, `
+		m := {1:"hello",2:"ok"}
+		println(&m[1])
+		`,
+		"",
+		"cannot take the address of m[1]\n")
+}
+
+func TestTakeAddrStringBad(t *testing.T) {
+	cltest.Expect(t, `
+		m := "hello"
+		println(&m[1])
+		`,
+		"",
+		"cannot take the address of m[1]\n")
+}
+
 func TestTypeCast(t *testing.T) {
 	cltest.Call(t, `
 	x := []byte("hello")
diff --git a/cl/gopkg_test.go b/cl/gopkg_test.go
index 31151c35..320ec3a8 100644
--- a/cl/gopkg_test.go
+++ b/cl/gopkg_test.go
@@ -698,6 +698,95 @@ import (
 	}
 }
 
+func TestPkgTakeAddr(t *testing.T) {
+	var I = exec.NewGoPackage("pkg_test_takeaddr")
+	rc := tMakeRect(10, 20, 100, 200)
+	ar := [2]trect{tMakeRect(1, 2, 10, 20), tMakeRect(10, 20, 100, 200)}
+	slice := []trect{tMakeRect(1, 2, 10, 20), tMakeRect(10, 20, 100, 200)}
+	m := make(map[int]trect)
+	m[1] = tMakeRect(1, 2, 10, 20)
+	m[2] = tMakeRect(10, 20, 100, 200)
+	I.RegisterVars(
+		I.Var("RC", &rc),
+		I.Var("Ar", &ar),
+		I.Var("Slice", &slice),
+		I.Var("M", &m),
+	)
+	var testSource = `
+	import pkg "pkg_test_takeaddr"
+
+	&pkg.M
+	&pkg.Ar
+	&pkg.Ar[0]
+	&pkg.Ar[0].Min
+	&pkg.Ar[1].Max.Y
+	&pkg.Slice
+	&pkg.Slice[0]
+	&pkg.Slice[0].Min
+	&pkg.Slice[1].Max.Y
+	&pkg.RC
+	&pkg.RC.Min
+	&pkg.RC.Max.Y
+	`
+
+	fsTestPkgVar := asttest.NewSingleFileFS("/foo", "bar.gop", testSource)
+	t.Log(testSource)
+
+	fset := token.NewFileSet()
+	pkgs, err := parser.ParseFSDir(fset, fsTestPkgVar, "/foo", nil, 0)
+	if err != nil || len(pkgs) != 1 {
+		t.Fatal("ParseFSDir failed:", err, len(pkgs))
+	}
+
+	bar := pkgs["main"]
+	b := exec.NewBuilder(nil)
+	_, _, err = newPackage(b, bar, fset)
+	if err != nil {
+		t.Fatal("Compile failed:", err)
+	}
+	code := b.Resolve()
+	code.Dump(os.Stdout)
+
+	ctx := exec.NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := ctx.Get(-12); v != &m {
+		t.Fatal("takeAddr &m", v)
+	}
+	if v := ctx.Get(-11); v != &ar {
+		t.Fatal("takeAddr &ar", v)
+	}
+	if v := ctx.Get(-10); v != &ar[0] {
+		t.Fatal("takeAddr &ar[0]", v)
+	}
+	if v := ctx.Get(-9); v != &ar[0].Min {
+		t.Fatal("takeAddr &ar[0].Min", v)
+	}
+	if v := ctx.Get(-8); v != &ar[1].Max.Y {
+		t.Fatal("takeAddr &ar[1].Max.Y", v)
+	}
+	if v := ctx.Get(-7); v != &slice {
+		t.Fatal("takeAddr &slice", v)
+	}
+	if v := ctx.Get(-6); v != &slice[0] {
+		t.Fatal("takeAddr &slice[0]", v)
+	}
+	if v := ctx.Get(-5); v != &slice[0].Min {
+		t.Fatal("takeAddr &slice[0].Min", v)
+	}
+	if v := ctx.Get(-4); v != &slice[1].Max.Y {
+		t.Fatal("takeAddr &slice[1].Max.Y", v)
+	}
+	if v := ctx.Get(-3); v != &rc {
+		t.Fatal("takeAddr &rc", v)
+	}
+	if v := ctx.Get(-2); v != &rc.Min {
+		t.Fatal("takeAddr &rc.Min", v)
+	}
+	if v := ctx.Get(-1); v != &rc.Max.Y {
+		t.Fatal("takeAddr &rc.Max.Y", v)
+	}
+}
+
 func TestPkgFieldBadSet(t *testing.T) {
 	defer func() {
 		r := recover()