Repository: goplus/gop
Pull request #596 (2020-09-13 15:50:46)

add reference type check nil operator 
Support reference type check nil, fix:  https://github.com/goplus/gop/issues/540
Use the following code to test
```
func foo() []int {
	return nil
}

func foo1() map[int]int {
	return make(map[int]int, 10)
}

func foo2() chan int {
	return make(chan int, 10)
}

func foo3() *int {
	return nil
}

println(foo() == nil)
println(nil == foo())
println(foo() != nil)
println(nil != foo())

println(foo1() == nil)
println(nil == foo1())
println(foo1() != nil)
println(nil != foo1())

println(foo2() == nil)
println(nil == foo2())
println(foo2() != nil)
println(nil != foo2())

println(foo3() == nil)
println(nil == foo3())
println(foo3() != nil)
println(nil != foo3())
```

result:

```
true
true
false
false
false
false
true
true
false
false
true
true
true
true
false
false
```





Patch:
diff --git a/ast/spec/lang_spec.go b/ast/spec/lang_spec.go
index 9276436b..0fc6efc5 100644
--- a/ast/spec/lang_spec.go
+++ b/ast/spec/lang_spec.go
@@ -45,6 +45,14 @@ const (
 	ConstUnboundComplex = ConstKind(reflect.UnsafePointer + 6)
 	// ConstUnboundPtr - nil: unbound ptr
 	ConstUnboundPtr = ConstKind(reflect.UnsafePointer + 7)
+	// Slice - bound type: slice
+	Slice = reflect.Slice
+	// Map - bound type: map
+	Map = reflect.Map
+	// Chan - bound type: chan
+	Chan = reflect.Chan
+	// Ptr - bound type: ptr
+	Ptr = reflect.Ptr
 )
 
 // IsConstBound checks a const is bound or not.
diff --git a/cl/check_type.go b/cl/check_type.go
index e18216a3..45bac360 100644
--- a/cl/check_type.go
+++ b/cl/check_type.go
@@ -117,8 +117,12 @@ func checkUnaryOp(kind exec.Kind, op exec.Operator, x interface{}, b exec.Builde
 func checkBinaryOp(kind exec.Kind, op exec.Operator, x, y interface{}, b exec.Builder) {
 	if xcons, xok := x.(*constVal); xok {
 		if xcons.reserve != -1 {
-			xv := boundConst(xcons.v, exec.TypeFromKind(kind))
-			xcons.reserve.Push(b, xv)
+			if xcons.kind == exec.ConstUnboundPtr {
+				xcons.reserve.Push(b, nil)
+			} else {
+				xv := boundConst(xcons.v, exec.TypeFromKind(kind))
+				xcons.reserve.Push(b, xv)
+			}
 		}
 	}
 	if ycons, yok := y.(*constVal); yok {
@@ -130,8 +134,12 @@ func checkBinaryOp(kind exec.Kind, op exec.Operator, x, y interface{}, b exec.Bu
 			kind = ycons.boundKind()
 		}
 		if ycons.reserve != -1 {
-			yv := boundConst(ycons.v, exec.TypeFromKind(kind))
-			ycons.reserve.Push(b, yv)
+			if ycons.kind == exec.ConstUnboundPtr {
+				ycons.reserve.Push(b, nil)
+			} else {
+				yv := boundConst(ycons.v, exec.TypeFromKind(kind))
+				ycons.reserve.Push(b, yv)
+			}
 		}
 	}
 }
diff --git a/exec.spec/kind.go b/exec.spec/kind.go
index 4646b9e0..9c568a70 100644
--- a/exec.spec/kind.go
+++ b/exec.spec/kind.go
@@ -72,6 +72,14 @@ const (
 	BigRat = spec.BigRat
 	// BigFloat type
 	BigFloat = spec.BigFloat
+	// Slice type
+	Slice = spec.Slice
+	// Map type
+	Map = spec.Map
+	// Chan type
+	Chan = spec.Chan
+	// Ptr type
+	Ptr = spec.Ptr
 )
 
 var (
@@ -131,6 +139,14 @@ var (
 
 	// TyEmptyInterfaceSlice type
 	TyEmptyInterfaceSlice = reflect.SliceOf(TyEmptyInterface)
+	// TySlice type
+	TySlice = reflect.SliceOf(TyEmptyInterface)
+	// TyMap type
+	TyMap = reflect.MapOf(TyEmptyInterface, TyEmptyInterface)
+	// Chan type
+	TyChan = reflect.ChanOf(reflect.BothDir, TyEmptyInterface)
+	// TyPtr type
+	TyPtr = TyUnsafePointer
 )
 
 type bTI struct { // builtin type info
diff --git a/exec/bytecode/exec_op_autogen.go b/exec/bytecode/exec_op_autogen.go
index f201cad3..e654ccd4 100644
--- a/exec/bytecode/exec_op_autogen.go
+++ b/exec/bytecode/exec_op_autogen.go
@@ -301,6 +301,14 @@ var builtinOps = [...]func(i Instr, p *Context){
 	(int(Uint64) << bitsOperator) | int(OpBitNot):  execBitNotUint64,
 	(int(Uintptr) << bitsOperator) | int(OpBitNot): execBitNotUintptr,
 	(int(BigInt) << bitsOperator) | int(OpBitNot):  execBitNotBigInt,
+	(int(Slice) << bitsOperator) | int(OpEQ):       execRefTypeEQ,
+	(int(Map) << bitsOperator) | int(OpEQ):         execRefTypeEQ,
+	(int(Chan) << bitsOperator) | int(OpEQ):        execRefTypeEQ,
+	(int(Ptr) << bitsOperator) | int(OpEQ):         execRefTypeEQ,
+	(int(Slice) << bitsOperator) | int(OpNE):       execRefTypeNEQ,
+	(int(Map) << bitsOperator) | int(OpNE):         execRefTypeNEQ,
+	(int(Chan) << bitsOperator) | int(OpNE):        execRefTypeNEQ,
+	(int(Ptr) << bitsOperator) | int(OpNE):         execRefTypeNEQ,
 }
 
 func execAddInt(i Instr, p *Context) {
diff --git a/exec/bytecode/operator.go b/exec/bytecode/operator.go
index d7adc12d..a7f82e2e 100644
--- a/exec/bytecode/operator.go
+++ b/exec/bytecode/operator.go
@@ -141,9 +141,54 @@ const (
 	BigRat = exec.BigRat
 	// BigFloat type
 	BigFloat = exec.BigFloat
+	// Slice type
+	Slice = exec.Slice
+	// Map type
+	Map = exec.Map
+	// Chan type
+	Chan = exec.Chan
+	// Ptr type
+	Ptr = exec.Ptr
 )
 
-// -----------------------------------------------------------------------------
+// -------------------------------------------------------------------------
+func execRefTypeEQ(i Instr, p *Context) {
+	n := len(p.data)
+	arg1 := reflect.ValueOf(p.data[n-2])
+	arg2 := reflect.ValueOf(p.data[n-1])
+	if p.data[n-1] == nil && (arg1.Type().Kind() == reflect.Slice ||
+		arg1.Type().Kind() == reflect.Map ||
+		arg1.Type().Kind() == reflect.Chan ||
+		arg1.Type().Kind() == reflect.Ptr) {
+		p.Ret(2, arg1.IsNil())
+	}
+	if p.data[n-2] == nil && (arg2.Type().Kind() == reflect.Slice ||
+		arg2.Type().Kind() == reflect.Map ||
+		arg2.Type().Kind() == reflect.Chan ||
+		arg2.Type().Kind() == reflect.Ptr) {
+		p.Ret(2, arg2.IsNil())
+	}
+}
+
+func execRefTypeNEQ(i Instr, p *Context) {
+	n := len(p.data)
+	arg1 := reflect.ValueOf(p.data[n-2])
+	arg2 := reflect.ValueOf(p.data[n-1])
+	if p.data[n-1] == nil && (arg1.Type().Kind() == reflect.Slice ||
+		arg1.Type().Kind() == reflect.Map ||
+		arg1.Type().Kind() == reflect.Chan ||
+		arg1.Type().Kind() == reflect.Ptr) {
+		p.Ret(2, !arg1.IsNil())
+	}
+	if p.data[n-2] == nil && (arg2.Type().Kind() == reflect.Slice ||
+		arg2.Type().Kind() == reflect.Map ||
+		arg2.Type().Kind() == reflect.Chan ||
+		arg2.Type().Kind() == reflect.Ptr) {
+		p.Ret(2, !arg2.IsNil())
+	}
+}
+
+// -------------------------------------------------------------------------
 
 func toUint(v interface{}) uint {
 	switch n := v.(type) {
diff --git a/tutorial/29-ref-type-op/ref.gop b/tutorial/29-ref-type-op/ref.gop
new file mode 100644
index 00000000..30fa914e
--- /dev/null
+++ b/tutorial/29-ref-type-op/ref.gop
@@ -0,0 +1,35 @@
+func foo() []int {
+	return nil
+}
+
+func foo1() map[int]int {
+	return make(map[int]int, 10)
+}
+
+func foo2() chan int {
+	return make(chan int, 10)
+}
+
+func foo3() *int {
+	return nil
+}
+
+println(foo() == nil)
+println(nil == foo())
+println(foo() != nil)
+println(nil != foo())
+
+println(foo1() == nil)
+println(nil == foo1())
+println(foo1() != nil)
+println(nil != foo1())
+
+println(foo2() == nil)
+println(nil == foo2())
+println(foo2() != nil)
+println(nil != foo2())
+
+println(foo3() == nil)
+println(nil == foo3())
+println(foo3() != nil)
+println(nil != foo3())


Test patch:
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 2b12a27a..39052a25 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -1485,6 +1485,77 @@ func TestStarExpr(t *testing.T) {
 	testScripts(t, "TestStarExpr", testStarExprClauses)
 }
 
+// -----------------------------------------------------------------------------
+var testRefTypeClauses = map[string]testData{
+	"ref type": {`
+	func foo() []int {
+		return make([]int, 10)
+	}
+	
+	func foo1() map[int]int {
+		return make(map[int]int, 10)
+	}
+	
+	func foo2() chan int {
+		return make(chan int, 10)
+	}
+	a := foo()
+	if a != nil {
+		println("foo")
+	}
+	
+	a1 := foo1()
+	if a1 != nil {
+		println("foo1")
+	}
+	a2 := foo2()
+	if a2 != nil {
+		println("foo2")
+	}
+						`, "foo\nfoo1\nfoo2\n", false},
+	"ref type 2": {`
+	func foo() []int {
+		return nil
+	}
+	
+	func foo1() map[int]int {
+		return make(map[int]int, 10)
+	}
+	
+	func foo2() chan int {
+		return make(chan int, 10)
+	}
+
+	func foo3() *int {
+		return nil
+	}
+	
+	println(foo() == nil)
+	println(nil == foo())
+	println(foo() != nil)
+	println(nil != foo())
+	
+	println(foo1() == nil)
+	println(nil == foo1())
+	println(foo1() != nil)
+	println(nil != foo1())
+	
+	println(foo2() == nil)
+	println(nil == foo2())
+	println(foo2() != nil)
+	println(nil != foo2())
+	
+	println(foo3() == nil)
+	println(nil == foo3())
+	println(foo3() != nil)
+	println(nil != foo3())
+						`, "true\ntrue\nfalse\nfalse\nfalse\nfalse\ntrue\ntrue\nfalse\nfalse\ntrue\ntrue\ntrue\ntrue\nfalse\nfalse\n", false},
+}
+
+func TestRefType(t *testing.T) {
+	testScripts(t, "TestRefType", testRefTypeClauses)
+}
+
 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/bytecode/exec_autogen_test.go b/exec/bytecode/exec_autogen_test.go
index b06fe737..b17dd08c 100644
--- a/exec/bytecode/exec_autogen_test.go
+++ b/exec/bytecode/exec_autogen_test.go
@@ -231,6 +231,17 @@ func _TestOpAutogen(t *testing.T) {
 
 func newKindValue(kind Kind) reflect.Value {
 	t := exec.TypeFromKind(kind)
+	if t == nil {
+		if kind == reflect.Slice {
+			t = reflect.SliceOf(TyEmptyInterface)
+		} else if kind == reflect.Map {
+			t = reflect.MapOf(TyEmptyInterface, TyEmptyInterface)
+		} else if kind == reflect.Chan {
+			t = reflect.ChanOf(reflect.BothDir, TyEmptyInterface)
+		} else if kind == reflect.Ptr {
+			t = TyUnsafePointer
+		}
+	}
 	if t.Kind() == reflect.Ptr {
 		t = t.Elem()
 	}
diff --git a/exec/bytecode/operator_test.go b/exec/bytecode/operator_test.go
index eb8124df..d2bf4170 100644
--- a/exec/bytecode/operator_test.go
+++ b/exec/bytecode/operator_test.go
@@ -17,6 +17,7 @@
 package bytecode
 
 import (
+	"reflect"
 	"testing"
 )
 
@@ -378,3 +379,78 @@ func TestCallBuiltinOp(t *testing.T) {
 }
 
 // -----------------------------------------------------------------------------
+
+func TestRefTypeOp(t *testing.T) {
+	code := newBuilder().
+		Push("Hello").
+		Push(3.2).
+		Push("wyvern").
+		Push(1.0).
+		MakeMap(reflect.MapOf(TyString, TyFloat64), 2).
+		Push(nil).
+		BuiltinOp(reflect.Map, OpEQ).
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != false {
+		t.Fatal("ref type of map is not false", v)
+	}
+}
+
+func TestRefTypeOp1(t *testing.T) {
+	code := newBuilder().
+		Push("Hello").
+		Push(3.2).
+		Push("wyvern").
+		Push(1.0).
+		MakeMap(reflect.MapOf(TyString, TyFloat64), 2).
+		Push(nil).
+		BuiltinOp(reflect.Map, OpNE).
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != true {
+		t.Fatal("ref type of map is not false", v)
+	}
+}
+func TestRefTypeOp2(t *testing.T) {
+	typData := reflect.SliceOf(TyInt)
+	code := newBuilder().
+		Push(nil).
+		Push(1).
+		Push(2).
+		Push(3).
+		Push(4).
+		MakeArray(typData, 4).
+		BuiltinOp(reflect.Slice, OpEQ).
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != false {
+		t.Fatal("ref type of map is not false", v)
+	}
+}
+
+func TestRefTypeOp3(t *testing.T) {
+	typData := reflect.SliceOf(TyInt)
+	code := newBuilder().
+		Push(nil).
+		Push(1).
+		Push(2).
+		Push(3).
+		Push(4).
+		MakeArray(typData, 4).
+		BuiltinOp(reflect.Slice, OpNE).
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != true {
+		t.Fatal("ref type of map is not false", v)
+	}
+}
+
+// -----------------------------------------------------------------------------
diff --git a/exec/golang/testdata/31.ref-type-op/ref_type_op.gop b/exec/golang/testdata/31.ref-type-op/ref_type_op.gop
new file mode 100644
index 00000000..a820e7e1
--- /dev/null
+++ b/exec/golang/testdata/31.ref-type-op/ref_type_op.gop
@@ -0,0 +1,7 @@
+func foo() []int {
+	return make([]int, 10)
+}
+a := foo()
+if a != nil {
+	println("foo")
+}