Repository: goplus/xgo (Go)

Issue:
user-defined struct not assignable array or map
Go+ code
```
type Point struct {
	X int
	Y int
}
var pt Point
var ar []Point
ar = append(ar,pt)
```
------ build output -----
```
panic: reflect.Set: value of type struct { QX int; QY int } is not assignable to type struct { X int; Y int }
```


Discussion:

Patch:
diff --git a/exec/bytecode/gopkg.go b/exec/bytecode/gopkg.go
index 81086e59d..d297c83e2 100644
--- a/exec/bytecode/gopkg.go
+++ b/exec/bytecode/gopkg.go
@@ -67,14 +67,14 @@ func execLoadField(i Instr, p *Context) {
 	index := p.Pop()
 	v := reflect.ValueOf(p.Pop())
 	v = reflect.Indirect(v)
-	p.Push(v.FieldByIndex(index.([]int)).Interface())
+	p.Push(FieldByIndex(v, index.([]int)).Interface())
 }
 
 func execAddrField(i Instr, p *Context) {
 	index := p.Pop()
 	v := reflect.ValueOf(p.Pop())
 	v = toElem(v)
-	p.Push(v.FieldByIndex(index.([]int)).Addr().Interface())
+	p.Push(FieldByIndex(v, index.([]int)).Addr().Interface())
 }
 
 func toElem(v reflect.Value) reflect.Value {
@@ -97,7 +97,7 @@ func storeField(v reflect.Value, index []int, value interface{}) {
 	if !v.CanSet() {
 		log.Panicf("cannot assign to %v\n", v)
 	}
-	setValue(v.FieldByIndex(index), value)
+	setValue(FieldByIndex(v, index), value)
 }
 
 // -----------------------------------------------------------------------------
diff --git a/exec/bytecode/reflect.go b/exec/bytecode/reflect.go
new file mode 100644
index 000000000..83c9c8e11
--- /dev/null
+++ b/exec/bytecode/reflect.go
@@ -0,0 +1,64 @@
+/*
+ Copyright 2020 The GoPlus Authors (goplus.org)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package bytecode
+
+import (
+	"reflect"
+	"unsafe"
+
+	"github.com/goplus/gop/ast"
+)
+
+type Value struct {
+	typ *unsafe.Pointer
+	ptr unsafe.Pointer
+	flag
+}
+
+type flag uintptr
+
+const (
+	flagKindWidth        = 5 // there are 27 kinds
+	flagKindMask    flag = 1<<flagKindWidth - 1
+	flagStickyRO    flag = 1 << 5
+	flagEmbedRO     flag = 1 << 6
+	flagIndir       flag = 1 << 7
+	flagAddr        flag = 1 << 8
+	flagMethod      flag = 1 << 9
+	flagMethodShift      = 10
+	flagRO          flag = flagStickyRO | flagEmbedRO
+)
+
+func Field(v reflect.Value, i int) reflect.Value {
+	sf := v.Type().Field(i)
+	if !ast.IsExported(sf.Name) {
+		vf := v.Field(i)
+		(*Value)(unsafe.Pointer(&vf)).flag &= ^flagRO
+		return vf
+	}
+	return v.Field(i)
+}
+
+func FieldByIndex(v reflect.Value, index []int) reflect.Value {
+	sf := v.Type().FieldByIndex(index)
+	if !ast.IsExported(sf.Name) {
+		vf := v.FieldByIndex(index)
+		(*Value)(unsafe.Pointer(&vf)).flag &= ^flagRO
+		return vf
+	}
+	return v.FieldByIndex(index)
+}
diff --git a/exec/bytecode/struct.go b/exec/bytecode/struct.go
index 8adc86ba7..17cb97ff9 100644
--- a/exec/bytecode/struct.go
+++ b/exec/bytecode/struct.go
@@ -162,8 +162,7 @@ func (p *Builder) Struct(typ reflect.Type, arity int) *Builder {
 }
 
 func execStruct(i Instr, p *Context) {
-	typ := getType(i&bitsOpCallFuncvOperand, p)
-	typStruct := toType(typ)
+	typStruct := getType(i&bitsOpCallFuncvOperand, p)
 	arity := int((i >> bitsOpCallFuncvShift) & bitsFuncvArityOperand)
 	if arity == bitsFuncvArityMax {
 		arity = p.Pop().(int) + bitsFuncvArityMax
@@ -183,7 +182,7 @@ func makeStruct(typStruct reflect.Type, arity int, p *Context) {
 	v := reflect.New(typStruct).Elem()
 	for i := 0; i < n; i += 2 {
 		index := args[i].(int)
-		v.Field(index).Set(reflect.ValueOf(args[i+1]))
+		Field(v, index).Set(reflect.ValueOf(args[i+1]))
 	}
 	if ptr {
 		p.Ret(n, v.Addr().Interface())
diff --git a/exec/bytecode/var.go b/exec/bytecode/var.go
index 300d505ac..2155be391 100644
--- a/exec/bytecode/var.go
+++ b/exec/bytecode/var.go
@@ -237,7 +237,6 @@ func (p *Builder) storeVar(addr tAddress) *Builder {
 // Var represents a variable.
 type Var struct {
 	typ       reflect.Type
-	actualTyp reflect.Type
 	name      string
 	nestDepth uint32
 	idx       uint32
@@ -245,8 +244,7 @@ type Var struct {
 
 // NewVar creates a variable instance.
 func NewVar(typ reflect.Type, name string) *Var {
-	totyp := toType(typ)
-	return &Var{typ: totyp, name: "Q" + name, idx: 0xffffffff, actualTyp: typ}
+	return &Var{typ: typ, name: "Q" + name, idx: 0xffffffff}
 }
 
 func (p *Var) isGlobal() bool {
@@ -255,7 +253,7 @@ func (p *Var) isGlobal() bool {
 
 // Type returns variable's type.
 func (p *Var) Type() reflect.Type {
-	return p.actualTyp
+	return p.typ
 }
 
 // Name returns variable's name.
@@ -310,28 +308,6 @@ func (p *varManager) addVars(vars ...exec.Var) {
 	}
 }
 
-func toType(typ reflect.Type) reflect.Type {
-	if typ.Kind() == reflect.Ptr {
-		temp := typ.Elem()
-		structType := toType(temp)
-		return reflect.PtrTo(structType)
-	}
-
-	if typ.Kind() == reflect.Struct && typ.Name() == "" {
-		var fields = make([]StructField, 0, typ.NumField())
-		for i := 0; i < typ.NumField(); i++ {
-			field := typ.Field(i)
-			fields = append(fields, StructField{
-				Type: toType(field.Type),
-				Name: "Q" + field.Name,
-			})
-		}
-
-		typ = Struct(fields).Type()
-	}
-	return typ
-}
-
 type blockCtx struct {
 	varManager
 	parent *varManager


Test patch:
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 95334783b..21816a0f3 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -1106,6 +1106,24 @@ var testStructClauses = map[string]testData{
 				
 				mu.noCopy = struct{}{}
 					`, "", true},
+	"struct_array": {`
+	type Point struct {
+		X int
+		Y int
+	}
+	ar := []Point{}
+	ar = append(ar,Point{10,20})
+	println(ar)
+	`, "[{10 20}]\n", false},
+	"struct_ptr_array": {`
+	type Point struct {
+		X int
+		Y int
+	}
+	ar := []*Point{}
+	ar = append(ar,&Point{10,20})
+	println(ar[0])
+	`, "&{10 20}\n", false},
 }
 
 func TestStruct2(t *testing.T) {
diff --git a/exec/bytecode/struct_test.go b/exec/bytecode/struct_test.go
index 8a90073fa..14ad2fc39 100644
--- a/exec/bytecode/struct_test.go
+++ b/exec/bytecode/struct_test.go
@@ -90,3 +90,13 @@ func TestStruct2(t *testing.T) {
 		"&{bar 30}\n",
 	)
 }
+
+func TestValueOf(t *testing.T) {
+	typ := reflect.TypeOf(100)
+	if v := getValueOf(100, typ); v.Int() != 100 {
+		t.Fatal("getValueOf", v)
+	}
+	if v := getValueOf(nil, typ); v.Int() != 0 {
+		t.Fatal("getValueOf", v)
+	}
+}