Repository: goplus/gop
Pull request #1756 (2024-02-19 01:32:56)

compileCompositeLit: support type-inter for map


Patch:
diff --git a/.gitignore b/.gitignore
index 4619940e..e4d9000f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,3 +51,4 @@ go-num/
 _todo*.go
 
 dist/
+gsh-exec/
diff --git a/cl/expr.go b/cl/expr.go
index 7c007366..27e4f21d 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -732,7 +732,9 @@ func compileCallArgs(fn *fnType, ctx *blockCtx, v *ast.CallExpr, ellipsis bool,
 				return
 			}
 		case *ast.CompositeLit:
-			compileCompositeLit(ctx, expr, fn.arg(i, ellipsis), true)
+			if err = compileCompositeLitEx(ctx, expr, fn.arg(i, ellipsis), true); err != nil {
+				return
+			}
 		case *ast.SliceLit:
 			t := fn.arg(i, ellipsis)
 			switch t.(type) {
@@ -963,15 +965,12 @@ const (
 	compositeLitKeyVal = 1
 )
 
-func checkCompositeLitElts(ctx *blockCtx, elts []ast.Expr, onlyStruct bool) (kind int) {
+func checkCompositeLitElts(ctx *blockCtx, elts []ast.Expr) (kind int) {
 	for _, elt := range elts {
 		if _, ok := elt.(*ast.KeyValueExpr); ok {
 			return compositeLitKeyVal
 		}
 	}
-	if len(elts) == 0 && onlyStruct {
-		return compositeLitKeyVal
-	}
 	return compositeLitVal
 }
 
@@ -1001,7 +1000,7 @@ func compileCompositeLitElts(ctx *blockCtx, elts []ast.Expr, kind int, expected
 	}
 }
 
-func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, typ types.Type, src *ast.CompositeLit) {
+func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, typ types.Type, src *ast.CompositeLit) error {
 	for _, elt := range elts {
 		kv := elt.(*ast.KeyValueExpr)
 		name := kv.Key.(*ast.Ident)
@@ -1010,8 +1009,7 @@ func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, t
 			ctx.cb.Val(idx)
 		} else {
 			src := ctx.LoadExpr(name)
-			err := ctx.newCodeErrorf(name.Pos(), "%s undefined (type %v has no field or method %s)", src, typ, name.Name)
-			panic(err)
+			return ctx.newCodeErrorf(name.Pos(), "%s undefined (type %v has no field or method %s)", src, typ, name.Name)
 		}
 		if rec := ctx.recorder(); rec != nil {
 			rec.Use(name, t.Field(idx))
@@ -1020,7 +1018,7 @@ func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, t
 		case *ast.LambdaExpr, *ast.LambdaExpr2:
 			sig, err := checkLambdaFuncType(ctx, expr, t.Field(idx).Type(), clLambaField, kv.Key)
 			if err != nil {
-				panic(err)
+				return err
 			}
 			compileLambda(ctx, expr, sig)
 		default:
@@ -1028,6 +1026,7 @@ func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, t
 		}
 	}
 	ctx.cb.StructLit(typ, len(elts)<<1, true, src)
+	return nil
 }
 
 func lookupField(t *types.Struct, name string) int {
@@ -1079,65 +1078,84 @@ func getUnderlying(ctx *blockCtx, typ types.Type) types.Type {
 	return u
 }
 
-func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit, expected types.Type, onlyStruct bool) {
+func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit, expected types.Type, mapOrStructOnly bool) {
+	if err := compileCompositeLitEx(ctx, v, expected, mapOrStructOnly); err != nil {
+		panic(err)
+	}
+}
+
+// mapOrStructOnly means only map/struct can omit type
+func compileCompositeLitEx(ctx *blockCtx, v *ast.CompositeLit, expected types.Type, mapOrStructOnly bool) error {
 	var hasPtr bool
 	var typ, underlying types.Type
-	var kind = checkCompositeLitElts(ctx, v.Elts, onlyStruct)
+	var kind = checkCompositeLitElts(ctx, v.Elts)
 	if v.Type != nil {
 		typ = toType(ctx, v.Type)
 		underlying = getUnderlying(ctx, typ)
 	} else if expected != nil {
 		if t, ok := expected.(*types.Pointer); ok {
-			expected, hasPtr = t.Elem(), true
-		}
-		if onlyStruct {
-			if kind == compositeLitKeyVal {
-				t := getUnderlying(ctx, expected)
-				if _, ok := t.(*types.Struct); ok { // can't omit non-struct type
-					typ, underlying = expected, t
-				}
+			telem := t.Elem()
+			tu := getUnderlying(ctx, telem)
+			if _, ok := tu.(*types.Struct); ok { // struct pointer
+				typ, underlying, hasPtr = telem, tu, true
 			}
-		} else {
-			typ, underlying = expected, getUnderlying(ctx, expected)
+		} else if tu := getUnderlying(ctx, expected); !mapOrStructOnly || isMapOrStruct(tu) {
+			typ, underlying = expected, tu
 		}
 	}
 	if t, ok := underlying.(*types.Struct); ok && kind == compositeLitKeyVal {
-		compileStructLitInKeyVal(ctx, v.Elts, t, typ, v)
-		if rec := ctx.recorder(); rec != nil {
-			rec.recordCompositeLit(ctx, v, typ)
+		if err := compileStructLitInKeyVal(ctx, v.Elts, t, typ, v); err != nil {
+			return err
 		}
-		if hasPtr {
-			ctx.cb.UnaryOp(gotoken.AND)
+	} else {
+		compileCompositeLitElts(ctx, v.Elts, kind, &kvType{underlying: underlying})
+		n := len(v.Elts)
+		if isMap(underlying) {
+			if kind == compositeLitVal && n > 0 {
+				return ctx.newCodeError(v.Pos(), "missing key in map literal")
+			}
+			if err := ctx.cb.MapLitEx(typ, n<<1, v); err != nil {
+				return err
+			}
+		} else {
+			switch underlying.(type) {
+			case *types.Slice:
+				ctx.cb.SliceLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)
+			case *types.Array:
+				ctx.cb.ArrayLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)
+			case *types.Struct:
+				ctx.cb.StructLit(typ, n, false, v) // key-val mode handled by compileStructLitInKeyVal
+			default:
+				return ctx.newCodeErrorf(v.Pos(), "invalid composite literal type %v", typ)
+			}
 		}
-		return
 	}
-	compileCompositeLitElts(ctx, v.Elts, kind, &kvType{underlying: underlying})
-	n := len(v.Elts)
-	if typ == nil {
-		if kind == compositeLitVal && n > 0 {
-			panic("TODO: mapLit should be in {key: val, ...} form")
-		}
-		ctx.cb.MapLit(nil, n<<1)
-		return
+	if hasPtr {
+		ctx.cb.UnaryOp(gotoken.AND)
+		typ = expected
 	}
 	if rec := ctx.recorder(); rec != nil {
 		rec.recordCompositeLit(ctx, v, typ)
 	}
-	switch underlying.(type) {
-	case *types.Slice:
-		ctx.cb.SliceLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)
-	case *types.Array:
-		ctx.cb.ArrayLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)
-	case *types.Map:
-		ctx.cb.MapLit(typ, n<<1, v)
-	case *types.Struct:
-		ctx.cb.StructLit(typ, n, false, v)
-	default:
-		log.Panicln("compileCompositeLit: unknown type -", reflect.TypeOf(underlying))
+	return nil
+}
+
+func isMap(tu types.Type) bool {
+	if tu == nil { // map can omit type
+		return true
 	}
-	if hasPtr {
-		ctx.cb.UnaryOp(gotoken.AND)
+	_, ok := tu.(*types.Map)
+	return ok
+}
+
+func isMapOrStruct(tu types.Type) bool {
+	switch tu.(type) {
+	case *types.Struct:
+		return true
+	case *types.Map:
+		return true
 	}
+	return false
 }
 
 func compileSliceLit(ctx *blockCtx, v *ast.SliceLit, typ types.Type, noPanic ...bool) (err error) {
diff --git a/doc/code-coverage.md b/doc/code-coverage.md
deleted file mode 100644
index e31270e4..00000000
--- a/doc/code-coverage.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Go+ Unit Test: Code Coverage
-=====
-
-TODO
diff --git a/go.mod b/go.mod
index c0a02ec9..b88b069d 100644
--- a/go.mod
+++ b/go.mod
@@ -5,9 +5,9 @@ go 1.18
 require (
 	github.com/fsnotify/fsnotify v1.7.0
 	github.com/goplus/c2go v0.7.23
-	github.com/goplus/gox v1.14.12
-	github.com/goplus/mod v0.13.7
-	github.com/qiniu/x v1.13.8
+	github.com/goplus/gox v1.14.13-0.20240218225042-4ebf0a5dcde8
+	github.com/goplus/mod v0.13.8-0.20240218230953-c1aeebf6e4f7
+	github.com/qiniu/x v1.13.9-0.20240218231431-55e88daed284
 	golang.org/x/tools v0.18.0
 )
 
diff --git a/go.sum b/go.sum
index a2b811ca..a157a284 100644
--- a/go.sum
+++ b/go.sum
@@ -6,10 +6,12 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/goplus/c2go v0.7.23 h1:MccDB9xBwDg9Bb9w+S9Lzbhg47ZtRFZHzqkr2OvxarM=
 github.com/goplus/c2go v0.7.23/go.mod h1:xBHI3uJ7hzQEYhw8Ql//1DHfJeuEgO5119QyoAs6JfY=
-github.com/goplus/gox v1.14.12 h1:VIrkU4T/MQ48olkTGgNk5ybF4U7YqkzIAv7By10gf9Y=
 github.com/goplus/gox v1.14.12/go.mod h1:6b6XYHmyiCevhwuEHcV/jzm7Z2FXLDBhuxgvkjceA+o=
-github.com/goplus/mod v0.13.7 h1:2tIe3cT/fmV+rOoPf896OH+AYcRu94Q0IeJU1LHVU4o=
+github.com/goplus/gox v1.14.13-0.20240218225042-4ebf0a5dcde8 h1:40Hb+m3t0B0upH3Det4GfhoSMK2TKazsEcOW1ZwETfo=
+github.com/goplus/gox v1.14.13-0.20240218225042-4ebf0a5dcde8/go.mod h1:6b6XYHmyiCevhwuEHcV/jzm7Z2FXLDBhuxgvkjceA+o=
 github.com/goplus/mod v0.13.7/go.mod h1:edZE5Qs+9mRnIjvWhyMNUkm5ayJ+dMlJ3J3YeXODNwA=
+github.com/goplus/mod v0.13.8-0.20240218230953-c1aeebf6e4f7 h1:3r+SsB6gkVApZgxG6WaFk41nIKHWaSDx7T/C4aTo2S8=
+github.com/goplus/mod v0.13.8-0.20240218230953-c1aeebf6e4f7/go.mod h1:edZE5Qs+9mRnIjvWhyMNUkm5ayJ+dMlJ3J3YeXODNwA=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
@@ -18,8 +20,9 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/qiniu/x v1.13.8 h1:xXXD1LuTw4VaqTWxFKonm0F6XlhQNE59+vgPYP3ij8Q=
 github.com/qiniu/x v1.13.8/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E=
+github.com/qiniu/x v1.13.9-0.20240218231431-55e88daed284 h1:0dcHGdsq5U0vLqJ7iX8xWkRIGAna9DidyptYJZWpTxo=
+github.com/qiniu/x v1.13.9-0.20240218231431-55e88daed284/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=


Test patch:
diff --git a/cl/compile_gop_test.go b/cl/compile_gop_test.go
index 095bba2b..09563cce 100644
--- a/cl/compile_gop_test.go
+++ b/cl/compile_gop_test.go
@@ -20,6 +20,21 @@ import (
 	"testing"
 )
 
+func TestMapLit(t *testing.T) {
+	gopClTest(t, `
+func foo(map[string]string) {}
+
+foo {}
+`, `package main
+
+func foo(map[string]string) {
+}
+func main() {
+	foo(map[string]string{})
+}
+`)
+}
+
 func TestMayBuiltinDelete(t *testing.T) {
 	gopClTest(t, `
 func Delete(a int) {}
diff --git a/cl/error_msg_test.go b/cl/error_msg_test.go
index c97acab3..ff7dd02b 100644
--- a/cl/error_msg_test.go
+++ b/cl/error_msg_test.go
@@ -600,6 +600,17 @@ b := []int{2: a}
 }
 
 func TestErrMapLit(t *testing.T) {
+	codeErrorTest(t, `bar.gop:4:6: cannot use 1 (type untyped int) as type string in map key`, `
+func foo(map[string]string) {}
+
+foo {1: 2}
+`)
+	codeErrorTest(t, `bar.gop:2:1: invalid composite literal type int`, `
+int{2}
+`)
+	codeErrorTest(t, `bar.gop:2:1: missing key in map literal`, `
+map[string]int{2}
+`)
 	codeErrorTest(t, `bar.gop:2:21: cannot use 1+2 (type untyped int) as type string in map key
 bar.gop:3:27: cannot use "Go" + "+" (type untyped string) as type int in map value`,
 		`