diff --git a/.gitignore b/.gitignore index 4619940eb..e4d9000fa 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,4 @@ go-num/ _todo*.go dist/ +gsh-exec/ diff --git a/cl/compile_gop_test.go b/cl/compile_gop_test.go index 095bba2ba..09563ccee 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 c97acab39..ff7dd02b6 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`, ` diff --git a/cl/expr.go b/cl/expr.go index 7c0073669..27e4f21d6 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< 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<