From b89a840d6572c97a80ac78462b03122b83bc84e9 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 25 Nov 2022 16:11:45 +0700 Subject: [PATCH] cmd/compile: add clear(x) builtin To clear map, and zero content of slice. Updates #56351 Change-Id: I5f81dfbc465500f5acadaf2c6beb9b5f0d2c4045 Reviewed-on: https://go-review.googlesource.com/c/go/+/453395 Reviewed-by: Cherry Mui Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Keith Randall Run-TryBot: Cuong Manh Le Auto-Submit: Cuong Manh Le --- src/cmd/compile/internal/escape/call.go | 2 +- src/cmd/compile/internal/escape/stmt.go | 2 +- src/cmd/compile/internal/ir/expr.go | 2 +- src/cmd/compile/internal/ir/fmt.go | 3 + src/cmd/compile/internal/ir/node.go | 1 + src/cmd/compile/internal/ir/op_string.go | 247 +++++++++--------- src/cmd/compile/internal/typecheck/const.go | 1 + src/cmd/compile/internal/typecheck/func.go | 24 +- src/cmd/compile/internal/typecheck/stmt.go | 1 + .../compile/internal/typecheck/typecheck.go | 6 +- .../compile/internal/typecheck/universe.go | 1 + src/cmd/compile/internal/walk/builtin.go | 12 + src/cmd/compile/internal/walk/expr.go | 4 + src/cmd/compile/internal/walk/order.go | 2 +- src/cmd/compile/internal/walk/range.go | 42 ++- src/cmd/compile/internal/walk/stmt.go | 1 + test/clear.go | 47 ++++ 17 files changed, 256 insertions(+), 142 deletions(-) create mode 100644 test/clear.go diff --git a/src/cmd/compile/internal/escape/call.go b/src/cmd/compile/internal/escape/call.go index f1c2c306a2..f9eced7dc0 100644 --- a/src/cmd/compile/internal/escape/call.go +++ b/src/cmd/compile/internal/escape/call.go @@ -179,7 +179,7 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir argument(e.discardHole(), &call.Args[i]) } - case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE: + case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE, ir.OCLEAR: call := call.(*ir.UnaryExpr) argument(e.discardHole(), &call.X) diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go index 90d4f2dedc..1ce04d98f3 100644 --- a/src/cmd/compile/internal/escape/stmt.go +++ b/src/cmd/compile/internal/escape/stmt.go @@ -180,7 +180,7 @@ func (e *escape) stmt(n ir.Node) { dsts[i] = res.Nname.(*ir.Name) } e.assignList(dsts, n.Results, "return", n) - case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, ir.OCLEAR, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: e.call(nil, n) case ir.OGO, ir.ODEFER: n := n.(*ir.GoDeferStmt) diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index a481b14f8b..95c142b93d 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -747,7 +747,7 @@ func (n *UnaryExpr) SetOp(op Op) { default: panic(n.no("SetOp " + op.String())) case OBITNOT, ONEG, ONOT, OPLUS, ORECV, - OALIGNOF, OCAP, OCLOSE, OIMAG, OLEN, ONEW, + OALIGNOF, OCAP, OCLEAR, OCLOSE, OIMAG, OLEN, ONEW, OOFFSETOF, OPANIC, OREAL, OSIZEOF, OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR, OUNSAFESTRINGDATA, OUNSAFESLICEDATA: diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go index bac172dbbd..ccd295d7e1 100644 --- a/src/cmd/compile/internal/ir/fmt.go +++ b/src/cmd/compile/internal/ir/fmt.go @@ -39,6 +39,7 @@ var OpNames = []string{ OCALL: "function call", // not actual syntax OCAP: "cap", OCASE: "case", + OCLEAR: "clear", OCLOSE: "close", OCOMPLEX: "complex", OBITNOT: "^", @@ -182,6 +183,7 @@ var OpPrec = []int{ OCALLMETH: 8, OCALL: 8, OCAP: 8, + OCLEAR: 8, OCLOSE: 8, OCOMPLIT: 8, OCONVIFACE: 8, @@ -767,6 +769,7 @@ func exprFmt(n Node, s fmt.State, prec int) { case OREAL, OIMAG, OCAP, + OCLEAR, OCLOSE, OLEN, ONEW, diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go index b42f914aad..ad25b9ff32 100644 --- a/src/cmd/compile/internal/ir/node.go +++ b/src/cmd/compile/internal/ir/node.go @@ -159,6 +159,7 @@ const ( OCALLMETH // X(Args) (direct method call x.Method(args)) OCALLINTER // X(Args) (interface method call x.Method(args)) OCAP // cap(X) + OCLEAR // clear(X) OCLOSE // close(X) OCLOSURE // func Type { Func.Closure.Body } (func literal) OCOMPLIT // Type{List} (composite literal, not yet lowered to specific form) diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go index d84a08e2a1..e0861457cb 100644 --- a/src/cmd/compile/internal/ir/op_string.go +++ b/src/cmd/compile/internal/ir/op_string.go @@ -42,132 +42,133 @@ func _() { _ = x[OCALLMETH-31] _ = x[OCALLINTER-32] _ = x[OCAP-33] - _ = x[OCLOSE-34] - _ = x[OCLOSURE-35] - _ = x[OCOMPLIT-36] - _ = x[OMAPLIT-37] - _ = x[OSTRUCTLIT-38] - _ = x[OARRAYLIT-39] - _ = x[OSLICELIT-40] - _ = x[OPTRLIT-41] - _ = x[OCONV-42] - _ = x[OCONVIFACE-43] - _ = x[OCONVIDATA-44] - _ = x[OCONVNOP-45] - _ = x[OCOPY-46] - _ = x[ODCL-47] - _ = x[ODCLFUNC-48] - _ = x[ODCLCONST-49] - _ = x[ODCLTYPE-50] - _ = x[ODELETE-51] - _ = x[ODOT-52] - _ = x[ODOTPTR-53] - _ = x[ODOTMETH-54] - _ = x[ODOTINTER-55] - _ = x[OXDOT-56] - _ = x[ODOTTYPE-57] - _ = x[ODOTTYPE2-58] - _ = x[OEQ-59] - _ = x[ONE-60] - _ = x[OLT-61] - _ = x[OLE-62] - _ = x[OGE-63] - _ = x[OGT-64] - _ = x[ODEREF-65] - _ = x[OINDEX-66] - _ = x[OINDEXMAP-67] - _ = x[OKEY-68] - _ = x[OSTRUCTKEY-69] - _ = x[OLEN-70] - _ = x[OMAKE-71] - _ = x[OMAKECHAN-72] - _ = x[OMAKEMAP-73] - _ = x[OMAKESLICE-74] - _ = x[OMAKESLICECOPY-75] - _ = x[OMUL-76] - _ = x[ODIV-77] - _ = x[OMOD-78] - _ = x[OLSH-79] - _ = x[ORSH-80] - _ = x[OAND-81] - _ = x[OANDNOT-82] - _ = x[ONEW-83] - _ = x[ONOT-84] - _ = x[OBITNOT-85] - _ = x[OPLUS-86] - _ = x[ONEG-87] - _ = x[OOROR-88] - _ = x[OPANIC-89] - _ = x[OPRINT-90] - _ = x[OPRINTN-91] - _ = x[OPAREN-92] - _ = x[OSEND-93] - _ = x[OSLICE-94] - _ = x[OSLICEARR-95] - _ = x[OSLICESTR-96] - _ = x[OSLICE3-97] - _ = x[OSLICE3ARR-98] - _ = x[OSLICEHEADER-99] - _ = x[OSTRINGHEADER-100] - _ = x[ORECOVER-101] - _ = x[ORECOVERFP-102] - _ = x[ORECV-103] - _ = x[ORUNESTR-104] - _ = x[OSELRECV2-105] - _ = x[OREAL-106] - _ = x[OIMAG-107] - _ = x[OCOMPLEX-108] - _ = x[OALIGNOF-109] - _ = x[OOFFSETOF-110] - _ = x[OSIZEOF-111] - _ = x[OUNSAFEADD-112] - _ = x[OUNSAFESLICE-113] - _ = x[OUNSAFESLICEDATA-114] - _ = x[OUNSAFESTRING-115] - _ = x[OUNSAFESTRINGDATA-116] - _ = x[OMETHEXPR-117] - _ = x[OMETHVALUE-118] - _ = x[OBLOCK-119] - _ = x[OBREAK-120] - _ = x[OCASE-121] - _ = x[OCONTINUE-122] - _ = x[ODEFER-123] - _ = x[OFALL-124] - _ = x[OFOR-125] - _ = x[OGOTO-126] - _ = x[OIF-127] - _ = x[OLABEL-128] - _ = x[OGO-129] - _ = x[ORANGE-130] - _ = x[ORETURN-131] - _ = x[OSELECT-132] - _ = x[OSWITCH-133] - _ = x[OTYPESW-134] - _ = x[OFUNCINST-135] - _ = x[OINLCALL-136] - _ = x[OEFACE-137] - _ = x[OITAB-138] - _ = x[OIDATA-139] - _ = x[OSPTR-140] - _ = x[OCFUNC-141] - _ = x[OCHECKNIL-142] - _ = x[ORESULT-143] - _ = x[OINLMARK-144] - _ = x[OLINKSYMOFFSET-145] - _ = x[OJUMPTABLE-146] - _ = x[ODYNAMICDOTTYPE-147] - _ = x[ODYNAMICDOTTYPE2-148] - _ = x[ODYNAMICTYPE-149] - _ = x[OTAILCALL-150] - _ = x[OGETG-151] - _ = x[OGETCALLERPC-152] - _ = x[OGETCALLERSP-153] - _ = x[OEND-154] + _ = x[OCLEAR-34] + _ = x[OCLOSE-35] + _ = x[OCLOSURE-36] + _ = x[OCOMPLIT-37] + _ = x[OMAPLIT-38] + _ = x[OSTRUCTLIT-39] + _ = x[OARRAYLIT-40] + _ = x[OSLICELIT-41] + _ = x[OPTRLIT-42] + _ = x[OCONV-43] + _ = x[OCONVIFACE-44] + _ = x[OCONVIDATA-45] + _ = x[OCONVNOP-46] + _ = x[OCOPY-47] + _ = x[ODCL-48] + _ = x[ODCLFUNC-49] + _ = x[ODCLCONST-50] + _ = x[ODCLTYPE-51] + _ = x[ODELETE-52] + _ = x[ODOT-53] + _ = x[ODOTPTR-54] + _ = x[ODOTMETH-55] + _ = x[ODOTINTER-56] + _ = x[OXDOT-57] + _ = x[ODOTTYPE-58] + _ = x[ODOTTYPE2-59] + _ = x[OEQ-60] + _ = x[ONE-61] + _ = x[OLT-62] + _ = x[OLE-63] + _ = x[OGE-64] + _ = x[OGT-65] + _ = x[ODEREF-66] + _ = x[OINDEX-67] + _ = x[OINDEXMAP-68] + _ = x[OKEY-69] + _ = x[OSTRUCTKEY-70] + _ = x[OLEN-71] + _ = x[OMAKE-72] + _ = x[OMAKECHAN-73] + _ = x[OMAKEMAP-74] + _ = x[OMAKESLICE-75] + _ = x[OMAKESLICECOPY-76] + _ = x[OMUL-77] + _ = x[ODIV-78] + _ = x[OMOD-79] + _ = x[OLSH-80] + _ = x[ORSH-81] + _ = x[OAND-82] + _ = x[OANDNOT-83] + _ = x[ONEW-84] + _ = x[ONOT-85] + _ = x[OBITNOT-86] + _ = x[OPLUS-87] + _ = x[ONEG-88] + _ = x[OOROR-89] + _ = x[OPANIC-90] + _ = x[OPRINT-91] + _ = x[OPRINTN-92] + _ = x[OPAREN-93] + _ = x[OSEND-94] + _ = x[OSLICE-95] + _ = x[OSLICEARR-96] + _ = x[OSLICESTR-97] + _ = x[OSLICE3-98] + _ = x[OSLICE3ARR-99] + _ = x[OSLICEHEADER-100] + _ = x[OSTRINGHEADER-101] + _ = x[ORECOVER-102] + _ = x[ORECOVERFP-103] + _ = x[ORECV-104] + _ = x[ORUNESTR-105] + _ = x[OSELRECV2-106] + _ = x[OREAL-107] + _ = x[OIMAG-108] + _ = x[OCOMPLEX-109] + _ = x[OALIGNOF-110] + _ = x[OOFFSETOF-111] + _ = x[OSIZEOF-112] + _ = x[OUNSAFEADD-113] + _ = x[OUNSAFESLICE-114] + _ = x[OUNSAFESLICEDATA-115] + _ = x[OUNSAFESTRING-116] + _ = x[OUNSAFESTRINGDATA-117] + _ = x[OMETHEXPR-118] + _ = x[OMETHVALUE-119] + _ = x[OBLOCK-120] + _ = x[OBREAK-121] + _ = x[OCASE-122] + _ = x[OCONTINUE-123] + _ = x[ODEFER-124] + _ = x[OFALL-125] + _ = x[OFOR-126] + _ = x[OGOTO-127] + _ = x[OIF-128] + _ = x[OLABEL-129] + _ = x[OGO-130] + _ = x[ORANGE-131] + _ = x[ORETURN-132] + _ = x[OSELECT-133] + _ = x[OSWITCH-134] + _ = x[OTYPESW-135] + _ = x[OFUNCINST-136] + _ = x[OINLCALL-137] + _ = x[OEFACE-138] + _ = x[OITAB-139] + _ = x[OIDATA-140] + _ = x[OSPTR-141] + _ = x[OCFUNC-142] + _ = x[OCHECKNIL-143] + _ = x[ORESULT-144] + _ = x[OINLMARK-145] + _ = x[OLINKSYMOFFSET-146] + _ = x[OJUMPTABLE-147] + _ = x[ODYNAMICDOTTYPE-148] + _ = x[ODYNAMICDOTTYPE2-149] + _ = x[ODYNAMICTYPE-150] + _ = x[OTAILCALL-151] + _ = x[OGETG-152] + _ = x[OGETCALLERPC-153] + _ = x[OGETCALLERSP-154] + _ = x[OEND-155] } -const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2REALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTINLCALLEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND" +const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLEARCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2REALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTINLCALLEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND" -var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 225, 232, 238, 247, 255, 263, 269, 273, 282, 291, 298, 302, 305, 312, 320, 327, 333, 336, 342, 349, 357, 361, 368, 376, 378, 380, 382, 384, 386, 388, 393, 398, 406, 409, 418, 421, 425, 433, 440, 449, 462, 465, 468, 471, 474, 477, 480, 486, 489, 492, 498, 502, 505, 509, 514, 519, 525, 530, 534, 539, 547, 555, 561, 570, 581, 593, 600, 609, 613, 620, 628, 632, 636, 643, 650, 658, 664, 673, 684, 699, 711, 727, 735, 744, 749, 754, 758, 766, 771, 775, 778, 782, 784, 789, 791, 796, 802, 808, 814, 820, 828, 835, 840, 844, 849, 853, 858, 866, 872, 879, 892, 901, 915, 930, 941, 949, 953, 964, 975, 978} +var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 223, 230, 237, 243, 252, 260, 268, 274, 278, 287, 296, 303, 307, 310, 317, 325, 332, 338, 341, 347, 354, 362, 366, 373, 381, 383, 385, 387, 389, 391, 393, 398, 403, 411, 414, 423, 426, 430, 438, 445, 454, 467, 470, 473, 476, 479, 482, 485, 491, 494, 497, 503, 507, 510, 514, 519, 524, 530, 535, 539, 544, 552, 560, 566, 575, 586, 598, 605, 614, 618, 625, 633, 637, 641, 648, 655, 663, 669, 678, 689, 704, 716, 732, 740, 749, 754, 759, 763, 771, 776, 780, 783, 787, 789, 794, 796, 801, 807, 813, 819, 825, 833, 840, 845, 849, 854, 858, 863, 871, 877, 884, 897, 906, 920, 935, 946, 954, 958, 969, 980, 983} func (i Op) String() string { if i >= Op(len(_Op_index)-1) { diff --git a/src/cmd/compile/internal/typecheck/const.go b/src/cmd/compile/internal/typecheck/const.go index edc399ffd7..26a3753c5f 100644 --- a/src/cmd/compile/internal/typecheck/const.go +++ b/src/cmd/compile/internal/typecheck/const.go @@ -743,6 +743,7 @@ func callOrChan(n ir.Node) bool { ir.OCALLINTER, ir.OCALLMETH, ir.OCAP, + ir.OCLEAR, ir.OCLOSE, ir.OCOMPLEX, ir.OCOPY, diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go index 065007b04e..f64523c9a0 100644 --- a/src/cmd/compile/internal/typecheck/func.go +++ b/src/cmd/compile/internal/typecheck/func.go @@ -260,7 +260,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node { n.SetTypecheck(0) // re-typechecking new op is OK, not a loop return typecheck(n, top) - case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: + case ir.OCAP, ir.OCLEAR, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: typecheckargs(n) fallthrough case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: @@ -441,6 +441,28 @@ func tcAppend(n *ir.CallExpr) ir.Node { return n } +// tcClear typechecks an OCLEAR node. +func tcClear(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + + switch { + case t.IsMap(), t.IsSlice(): + default: + base.Errorf("invalid operation: %v (argument must be a map or slice)", n) + n.SetType(nil) + return n + } + + return n +} + // tcClose typechecks an OCLOSE node. func tcClose(n *ir.UnaryExpr) ir.Node { n.X = Expr(n.X) diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go index 5eeab4115e..2af6c26a16 100644 --- a/src/cmd/compile/internal/typecheck/stmt.go +++ b/src/cmd/compile/internal/typecheck/stmt.go @@ -273,6 +273,7 @@ func tcGoDefer(n *ir.GoDeferStmt) { case ir.OCALLINTER, ir.OCALLMETH, ir.OCALLFUNC, + ir.OCLEAR, ir.OCLOSE, ir.OCOPY, ir.ODELETE, diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go index ea49e76a3e..b06b9d9753 100644 --- a/src/cmd/compile/internal/typecheck/typecheck.go +++ b/src/cmd/compile/internal/typecheck/typecheck.go @@ -336,7 +336,7 @@ func typecheck(n ir.Node, top int) (res ir.Node) { case ir.OAPPEND: // Must be used (and not BinaryExpr/UnaryExpr). isStmt = false - case ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN: + case ir.OCLEAR, ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN: // Must not be used. isExpr = false isStmt = true @@ -621,6 +621,10 @@ func typecheck1(n ir.Node, top int) ir.Node { n := n.(*ir.BinaryExpr) return tcComplex(n) + case ir.OCLEAR: + n := n.(*ir.UnaryExpr) + return tcClear(n) + case ir.OCLOSE: n := n.(*ir.UnaryExpr) return tcClose(n) diff --git a/src/cmd/compile/internal/typecheck/universe.go b/src/cmd/compile/internal/typecheck/universe.go index 828a8db3e7..6c6a504606 100644 --- a/src/cmd/compile/internal/typecheck/universe.go +++ b/src/cmd/compile/internal/typecheck/universe.go @@ -34,6 +34,7 @@ var builtinFuncs = [...]struct { }{ {"append", ir.OAPPEND}, {"cap", ir.OCAP}, + {"clear", ir.OCLEAR}, {"close", ir.OCLOSE}, {"complex", ir.OCOMPLEX}, {"copy", ir.OCOPY}, diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go index 4c5ee9baec..3c85b19a36 100644 --- a/src/cmd/compile/internal/walk/builtin.go +++ b/src/cmd/compile/internal/walk/builtin.go @@ -130,6 +130,18 @@ func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node { return s } +// walkClear walks an OCLEAR node. +func walkClear(n *ir.UnaryExpr) ir.Node { + typ := n.X.Type() + switch { + case typ.IsSlice(): + return arrayClear(n.X.Pos(), n.X, nil) + case typ.IsMap(): + return mapClear(n.X, reflectdata.TypePtrAt(n.X.Pos(), n.X.Type())) + } + panic("unreachable") +} + // walkClose walks an OCLOSE node. func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { // cannot use chanfn - closechan takes any, not chan any diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go index 24fe0d0b9f..6f4a5339ce 100644 --- a/src/cmd/compile/internal/walk/expr.go +++ b/src/cmd/compile/internal/walk/expr.go @@ -279,6 +279,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { case ir.OCOPY: return walkCopy(n.(*ir.BinaryExpr), init, base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime) + case ir.OCLEAR: + n := n.(*ir.UnaryExpr) + return walkClear(n) + case ir.OCLOSE: n := n.(*ir.UnaryExpr) return walkClose(n, init) diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index d6712ae0fc..b6b277c9a5 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -740,7 +740,7 @@ func (o *orderState) stmt(n ir.Node) { } } - case ir.OCHECKNIL, ir.OCLOSE, ir.OPANIC, ir.ORECV: + case ir.OCHECKNIL, ir.OCLEAR, ir.OCLOSE, ir.OPANIC, ir.ORECV: n := n.(*ir.UnaryExpr) t := o.markTemp() n.X = o.expr(n.X, nil) diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index 64af26bf29..67c13a847c 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -13,6 +13,7 @@ import ( "cmd/compile/internal/ssagen" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/internal/src" "cmd/internal/sys" ) @@ -38,7 +39,7 @@ func cheapComputableIndex(width int64) bool { // the returned node. func walkRange(nrange *ir.RangeStmt) ir.Node { if isMapClear(nrange) { - return mapClear(nrange) + return mapRangeClear(nrange) } nfor := ir.NewForStmt(nrange.Pos(), nil, nil, nil, nil) @@ -77,7 +78,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { base.Fatalf("walkRange") case types.TARRAY, types.TSLICE, types.TPTR: // TPTR is pointer-to-array - if nn := arrayClear(nrange, v1, v2, a); nn != nil { + if nn := arrayRangeClear(nrange, v1, v2, a); nn != nil { base.Pos = lno return nn } @@ -437,18 +438,23 @@ func isMapClear(n *ir.RangeStmt) bool { return true } -// mapClear constructs a call to runtime.mapclear for the map m. -func mapClear(nrange *ir.RangeStmt) ir.Node { +// mapRangeClear constructs a call to runtime.mapclear for the map range idiom. +func mapRangeClear(nrange *ir.RangeStmt) ir.Node { m := nrange.X origPos := ir.SetPos(m) defer func() { base.Pos = origPos }() + return mapClear(m, reflectdata.RangeMapRType(base.Pos, nrange)) +} + +// mapClear constructs a call to runtime.mapclear for the map m. +func mapClear(m, rtyp ir.Node) ir.Node { t := m.Type() // instantiate mapclear(typ *type, hmap map[any]any) fn := typecheck.LookupRuntime("mapclear") fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem()) - n := mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), m) + n := mkcallstmt1(fn, rtyp, m) return walkStmt(typecheck.Stmt(n)) } @@ -463,7 +469,7 @@ func mapClear(nrange *ir.RangeStmt) ir.Node { // in which the evaluation of a is side-effect-free. // // Parameters are as in walkRange: "for v1, v2 = range a". -func arrayClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node { +func arrayRangeClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node { if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting { return nil } @@ -496,8 +502,17 @@ func arrayClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node { return nil } - elemsize := typecheck.RangeExprType(loop.X.Type()).Elem().Size() - if elemsize <= 0 || !ir.IsZero(stmt.Y) { + if !ir.IsZero(stmt.Y) { + return nil + } + + return arrayClear(stmt.Pos(), a, loop) +} + +// arrayClear constructs a call to runtime.memclr for fast zeroing of slices and arrays. +func arrayClear(wbPos src.XPos, a ir.Node, nrange *ir.RangeStmt) ir.Node { + elemsize := typecheck.RangeExprType(a.Type()).Elem().Size() + if elemsize <= 0 { return nil } @@ -527,7 +542,7 @@ func arrayClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node { var fn ir.Node if a.Type().Elem().HasPointers() { // memclrHasPointers(hp, hn) - ir.CurFunc.SetWBPos(stmt.Pos()) + ir.CurFunc.SetWBPos(wbPos) fn = mkcallstmt("memclrHasPointers", hp, hn) } else { // memclrNoHeapPointers(hp, hn) @@ -536,10 +551,11 @@ func arrayClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node { n.Body.Append(fn) - // i = len(a) - 1 - v1 = ir.NewAssignStmt(base.Pos, v1, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(1))) - - n.Body.Append(v1) + // For array range clear, also set "i = len(a) - 1" + if nrange != nil { + idx := ir.NewAssignStmt(base.Pos, nrange.Key, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(1))) + n.Body.Append(idx) + } n.Cond = typecheck.Expr(n.Cond) n.Cond = typecheck.DefaultLit(n.Cond, nil) diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go index ceee1b1b75..c6a03d2bd8 100644 --- a/src/cmd/compile/internal/walk/stmt.go +++ b/src/cmd/compile/internal/walk/stmt.go @@ -39,6 +39,7 @@ func walkStmt(n ir.Node) ir.Node { ir.OAS2RECV, ir.OAS2FUNC, ir.OAS2MAPR, + ir.OCLEAR, ir.OCLOSE, ir.OCOPY, ir.OCALLINTER, diff --git a/test/clear.go b/test/clear.go new file mode 100644 index 0000000000..60ee4ecf85 --- /dev/null +++ b/test/clear.go @@ -0,0 +1,47 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "math" + +func checkClearSlice() { + s := []int{1, 2, 3} + clear(s) + for i := range s { + if s[i] != 0 { + panic("clear not zeroing slice elem") + } + } + + clear([]int{}) +} + +func checkClearMap() { + m1 := make(map[int]int) + m1[0] = 0 + m1[1] = 1 + clear(m1) + if len(m1) != 0 { + panic("m1 is not cleared") + } + + // map contains NaN keys is also cleared. + m2 := make(map[float64]int) + m2[math.NaN()] = 1 + m2[math.NaN()] = 1 + clear(m2) + if len(m2) != 0 { + panic("m2 is not cleared") + } + + clear(map[int]int{}) +} + +func main() { + checkClearSlice() + checkClearMap() +}