Skip to content

Commit

Permalink
Merge pull request #1756 from xushiwei/q
Browse files Browse the repository at this point in the history
compileCompositeLit: support type-inter for map
  • Loading branch information
xushiwei authored Feb 19, 2024
2 parents 0a458e5 + a0bf538 commit 271d98e
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 54 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ go-num/
_todo*.go

dist/
gsh-exec/
15 changes: 15 additions & 0 deletions cl/compile_gop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down
11 changes: 11 additions & 0 deletions cl/error_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
`
Expand Down
114 changes: 66 additions & 48 deletions cl/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
Expand All @@ -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))
Expand All @@ -1020,14 +1018,15 @@ 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:
compileExpr(ctx, kv.Value)
}
}
ctx.cb.StructLit(typ, len(elts)<<1, true, src)
return nil
}

func lookupField(t *types.Struct, name string) int {
Expand Down Expand Up @@ -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) {
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
9 changes: 6 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand All @@ -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=
Expand Down

0 comments on commit 271d98e

Please sign in to comment.