Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: spec: add clear(x) builtin, to clear map, zero content of slice, ptr-to-array #56351

Open
rsc opened this issue Oct 20, 2022 · 83 comments

Comments

@rsc
Copy link
Contributor

rsc commented Oct 20, 2022

There is no way to clear a map in Go. You can write

for k := range m {
    delete(m, k)
}

but that only works if m does not contain any key values that contain NaNs.

Based on the discussion in #55002, we suggest adding delete(m) to the language to clear the map, always (even if it contains NaNs).

This is somewhat a reopening of #45328, which we closed because the loop was good enough, but we missed the earlier discussion in that issue of NaNs.

Adding delete(m) would then let us also add a similar mechanism in reflect.
We wouldn't need to add maps.Clear(m) since delete(m) is just as good.

@rsc
Copy link
Contributor Author

rsc commented Oct 20, 2022

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

@atdiar
Copy link

atdiar commented Oct 20, 2022

Personally, I'm ok with it but can't determine if it is good enough.

Having a way to disallow NaN containing values in maps would be best. Then map clearing via iteration just works and the change in this proposal is unnecessary (if not for perf reasons).

Otherwise, comparing two maps may still have edge cases. (the crux of the issue is that comparison is actually underspecified it seems).
Notably relevant for Set types that would be implemented by using maps.

With this proposal, map clearing gets solved but not inadvertent comparison of potential NaN composites.
If deemed a serious issue, I'm a bit concerned that it could actually hide a programming mistake instead.

Other than that, this proposal should be semantically sound at least.

@robpike
Copy link
Contributor

robpike commented Oct 20, 2022

I tend to agree that this is really only about NaNs, and is a peculiar response to their presence. At least, if you want to justify a new predefined function with a new capability, making NaNs the selling point is off-key (see what I did there?).

@carlmjohnson
Copy link
Contributor

carlmjohnson commented Oct 20, 2022

I worry that this could lead to programmer error when clearing map[string]map[string]T or map[string]any.

@timothy-king
Copy link
Contributor

timothy-king commented Oct 20, 2022

With delete(m), will it be clear whether this is releasing memory of the underlying map?

  • If it does release the memory, it is equivalent to if m != nil { m = make(m[K]V) }. (But maybe I am not thinking through aliasing enough?) [edit: I was not thinking through aliasing enough.]
  • If it does not release the memory, will users assume that it does and misuse it? My guess is yes. So if this goes forward with this semantics, maybe document that in the spec?

@bcmills
Copy link
Member

bcmills commented Oct 20, 2022

@atdiar

If deemed a serious issue, I'm a bit concerned that it could actually hide a programming mistake instead.

I think this proposal strictly improves the situation for irreflexive keys: if you use it it does the right thing for clearing the map, and if you don't use it you're no worse off than if it never existed.

That said, this still wouldn't help use-cases that need to add and remove individual keys: delete(m) would only give an all-or-nothing approach, where in order to delete an irreflexive key you must burn the rest of the map to the ground along with it.


The programming mistake I'm more worried about is typos. If both delete(m, k) and delete(m) are defined, then I could inadvertently write delete(m) when I meant delete(m, k), and they're visually similar enough that the bug could be difficult to spot. 😅

@randall77
Copy link
Contributor

randall77 commented Oct 20, 2022

With delete(m), will it be clear whether this is releasing memory of the underlying map?

I would assume this does not release the memory, just as the delete-with-a-for-loop doesn't.

If we ever implement #20135 the map will shrink eventually if it is never refilled.

@Merovius
Copy link
Contributor

Merovius commented Oct 20, 2022

@carlmjohnson To be clear, what's the programming error? Both of these would work fine ISTM. For the first I can see that perhaps a programmer might've thought it deletes the inner maps (though that seems a bit far-fetched to me). But the second?

@timothy-king

If it does release the memory, it is equivalent to if m != nil { m = make(m[K]V) }. (But maybe I am not thinking through aliasing enough?)

You are not. func Clear[K , V](m map[K, V]) { m = make(m[K]V) } is a NOP, but func Clear[K, V](m map[K, V]) { delete(m) } is not.

If it does not release the memory, will users assume that it does and misuse it? My guess is yes.

What's the misuse? Semantics don't change. I am opposed to specify allocation behavior. That's an optimization that's up to implementers.

@carlmjohnson
Copy link
Contributor

carlmjohnson commented Oct 20, 2022

@carlmjohnson To be clear, what's the programming error?

What @bcmills said, a typo of delete(m, k) to delete(m) that is easy to overlook. You mean delete(m, "key") but write delete(m["key"]) by mistake, so m["key"] still exists but is blank.

@randall77
Copy link
Contributor

randall77 commented Oct 20, 2022

a typo of delete(m, k) to delete(m) that is easy to overlook.

I'm not too worried about this. For one, any reasonable test would catch this mistake. Second, you're not actually using k, which the compiler might just catch for you (e.g. if the previous line was k := ... and no other use of k appears later). Third, this is not a problem unique to this case. Forgetting an argument can happen in all manner of ...-arg'ed functions: fmt.Printf("%d"), append(s), path.Join(p), and so on.

@dheerajdlalwani

This comment has been hidden.

@ianlancetaylor

This comment has been hidden.

@ulikunitz
Copy link
Contributor

ulikunitz commented Oct 21, 2022

While I understand the motivation to reuse a predeclared function name here, I'm concerned because delete(m) doesn't actually delete the map but removes all entries. I understand that clear(m) would create backward compatibility issues, but a maps.Clear(m), reflect.ClearMap(m) or a runtime.ClearMap(m) would express the intention explicitly.

@Merovius
Copy link
Contributor

Merovius commented Oct 21, 2022

I understand that clear(m) would create backward compatibility issues

It wouldn't. All the builtin functions are predeclared identifiers. Adding new predeclared identifiers is backwards compatible - cf. comparable.

That's not saying we should (I have no strong feelings), but this, at least, is not a reason not to do it.

@blackgreen100
Copy link

blackgreen100 commented Oct 21, 2022

@bcmills

I think this proposal strictly improves the situation for irreflexive keys

at the cost of having an overloaded built-in function that behaves differently based on the number of arguments.

It's less user-friendly and more cognitively demanding to think about language features in terms of exceptions and/or gotchas, instead of just relying on different functions doing different things.

Yes, the language already has overloaded built-ins e.g. make but I feel the use case in this proposal is subtle enough to warrant a new built-in with a different name, e.g. clear(m) or reset(m).

Moreover, this would improve clarity if the new built-in went a step further and do additional things that delete(m, k) might or might not do, like shrinking the map (not sure if this is in scope for this proposal, though)

@DmitriyMV
Copy link

DmitriyMV commented Oct 21, 2022

If #20135 is implemented what would be the actual difference between

delete(m)

and

m = nil // this is not actually needed
m = make(map[type1]type2)

?

@thepudds
Copy link

thepudds commented Oct 21, 2022

If this proposal is adopted as delete(m), and if #20135 is implemented without any new API, then it would depend on the heuristic(s) adopted in #20135, but I would guess delete(m) would clear the elements but leave the capacity.

There is some related conversation in a different issue #54454, including this comment from Keith in #54454 (comment)

We probably don't want to shrink if people do the map clear idiom + reinsert a new set of stuff. Perhaps we only start shrinking after 2N operations, so that N delete + N add involves no shrinking. There's definitely a tradeoff here between responsiveness when you use fewer entries and extra work that needs to be done to regrow.

Perhaps #20135 and this proposal could be solved together (resize? remake?) or at least briefly considered together. (FWIW, I think some solution to #20135 probably has broader impact than this issue, though that probably is a better discussion for elsewhere).

@randall77
Copy link
Contributor

randall77 commented Oct 21, 2022

If #20135 is implemented what would be the actual difference between

delete(m)
and

m = nil // this is not actually needed
m = make(map[type1]type2)
?

The difference comes down to aliasing. What happens if other variables have a reference to the same map? See #56351 (comment)

@natefinch
Copy link
Contributor

natefinch commented Oct 21, 2022

I really don't like that delete(m) does not, in fact, delete m. Please don't take this shortcut. It makes the code lie, and that is a grievous offense for something that would be built into the language.

Personally, I think that it's a bug that you can have values that aren't equatable as map keys, and changing the language to better handle problems caused by that bug is not a good idea. I understand we can't change the fact that NaN is a valid map key, but we don't have to perpetuate the error by adding features to make it more usable.

My first test for most languages features is "is this just trying to avoid writing a loop or if statement?" and if it is, I put it loooow on the list. If we ignore the NaN edge case, that's all this is.

I'm not sure I've ever cleared a map in production code (though I do understand the uses). I am pretty sure I've never used floats as keys in a map. Adding a language feature for a tiny edge case based on a bug seems like an easy "no".

That being said, if people reeeealllly want this, maybe make it delete(m, _) instead? At least then it's kinda clear you're not deleting m itself, and you're not having one method that changes behavior based on how many arguments you give it.

@Merovius
Copy link
Contributor

Merovius commented Oct 21, 2022

My first test for most languages features is "is this just trying to avoid writing a loop or if statement?" and if it is, I put it loooow on the list. If we ignore the NaN edge case, that's all this is.

FWIW one of the reasons this is being asked for now is that I wished to have a method in reflect which efficiently (!) clears a map. One of the main reasons for not adding that method to reflect is that reflect should not allow doing something the language can't do either.

So, for me, this isn't really about the syntactical overhead and effort of writing a loop. It's about being able to clear a map from reflect at all, without having to pay the allocation to iterate it first and then pay the allocations for all of its storage when growing it again.

I agree that it would be better if we could've done something about irreflexive keys before Go 1. But to me, the reason for this isn't really to be able to clear them out. That's such an edge-case, it doesn't even factor into the calculation for me. As I said here, I'd even want something like this if delete(m) didn't remove irreflexive keys. Though, TBF, maps.Clear couldn't use the fallback I could do with reflect.

@ianlancetaylor
Copy link
Contributor

ianlancetaylor commented Oct 21, 2022

I want to note that the proposed delete(m) is precisely analogous to the existing os/signal.Notify. Notify(ch, sig) notifies for the signal sig, while Notify(ch) notifies for all signals. Similarly, delete(m, k) deletes k from m, while delete(m) deletes all k values from m.

That analogy suggests that perhaps we should permit delete(m, k1, k2) to delete both k1 and k2 from the map.

@natefinch
Copy link
Contributor

natefinch commented Oct 21, 2022

I'd vastly prefer clear(m) to delete(m) if we need it for optimization's sake. Then it's not a misleading name, it matches the other Clear methods, there's no confusion about how many arguments it takes, and it's much more clear what it does. Heck, we could even allow it to also take in a slice and zero out the memory of its backing array. There's no easy way to do that right now, either.

@atdiar
Copy link

atdiar commented Oct 21, 2022

@ianlancetaylor

I think I can understand why people would like to dissociate map clearing and delete.

In one case, everything is deleted regardless of the key.

In the other, NaN containing keys would not be deleted.

Is this an issue?

@bcmills
Copy link
Member

bcmills commented Oct 21, 2022

It seems to me that:

  1. We would not care about this form of delete if it were possible to delete irreflexive keys otherwise.
  2. Deleting irreflexive keys from a map is a very niche use-case, because putting irreflexive keys in a map in the first place is a very niche use-case.

Given those, I wonder if it would suffice to define delete as the inverse of m[k] = v. That is, instead of having delete(m, k) remove all keys equal to k, we could have it delete all map entries that could have been created by assigning to m[k].

That would change the behavior of a tiny number of existing programs that both add irreflexive keys to maps and use delete with those keys. My hypothesis is that essentially every such program is already broken to begin with, and redefining delete in this way would strictly reduce their degree of brokenness.

@jimmyfrasche
Copy link
Member

jimmyfrasche commented Oct 21, 2022

@ianlancetaylor if you could delete multiple keys delete(m, keys...) would be useful but it would also be dangerous if len(keys) == 0 cleared the map.

@timothy-king
Copy link
Contributor

timothy-king commented Oct 21, 2022

If it does not release the memory, will users assume that it does and misuse it? My guess is yes.

What's the misuse? Semantics don't change. I am opposed to specify allocation behavior. That's an optimization that's up to implementers.

@Merovius Say they have a long lived struct and don't need a map field anymore so they want to free the underlying memory. The concern is a user writes delete(x.m) instead of x.m = nil here and is consuming more memory than they intended. My point was that the documentation for delete just needs to be written in a way that that users should not expect to get the memory back. If this can be done without specifying the allocation behavior, I agree that that would be preferable. I am just not sure how.

@leaxoy
Copy link

leaxoy commented Oct 26, 2022

Instead of introduce new global function, is the introduction method more appropriate?
Then this becomes to slice.Clear() or map.Clear().

Or more aggressive, add interface to builtin or std library? User can define their's custom clear action for arbitrary type.

type Clearable interface {
    Clear()
}
// or
type Resetable interface {
    Reset()
}

@rsc
Copy link
Contributor Author

rsc commented Oct 26, 2022

@leaxoy, it can't work for an array: if you have

var x [10]byte
clear(x)

The call clear(x) should be passing a copy of x to clear. If clear can modify the original x, it's not a real function.

@rsc
Copy link
Contributor Author

rsc commented Oct 26, 2022

@leaxoy We talked about defining maps.Clear in #55002, but how? It would not be possible to write in pure Go code. Instead it would need some kind of hidden entry point into the runtime.

In the past I've referred to that kind of library change as a "back-door language change", meaning it's a language change - it adds something not possible in pure Go - but pretends not to be one. We try hard to avoid doing that.

@leaxoy
Copy link

leaxoy commented Oct 26, 2022

Ok, I will look at it and understand the context.

@rsc
Copy link
Contributor Author

rsc commented Nov 2, 2022

Discussion seems to have trailed off, so it sounds like people are in favor of calling the builtin clear(x), and it would apply to maps (deleting all entries, even NaN-containing ones), slices (zeroing all elements up to len, not up to cap), and pointers to arrays (zeroing all elements).

It's true that clear(x) changes len of a map but not len of a slice/array ptr, but that's true of other operations too (like assigning to x[i]).

@griesemer
Copy link
Contributor

griesemer commented Nov 4, 2022

We also need to say what happens for arguments of type parameter type. It seems straight-forward to accept all type sets with underlying types that are one of map, slice, or pointer to array.

Tentative spec wording:

The built-in function clear takes an argument of map, slice, pointer-to-array, or type parameter type. For maps, clear deletes all entries, resulting in an empty map. For slices and arrays, clear sets all elements up to the length of the slice or array to the zero value of the respective element type. If the argument type is a type parameter, the type parameter's type set must contain only maps, slices, or pointer-to-array types, and clear performs the operation implied by the actual type argument.

A final observation: As is, clear([]int{1, 2, 3}) will be valid but meaningless. One could require the argument to be addressable. I am not suggesting we should (we currently also allow delete(map[int]int{1: 1}, 1)), just bringing it up for completeness.

@gopherbot
Copy link

gopherbot commented Nov 4, 2022

Change https://go.dev/cl/448076 mentions this issue: go/types, types2: implement type checking of "clear" built-in

@blackgreen100
Copy link

blackgreen100 commented Nov 5, 2022

@griesemer

If the argument type is a type parameter, the type parameter's type set must contain only maps, slices, or pointer-to-array types

This wording seems to imply that a value of type parameter whose constraint includes a union type element with diverse type terms can also be supplied to clear, i.e. the following should compile:

func Foo[T ~map[string]int | ~[]string](v T) {
    clear(v)
}

Is this the intent?

If it's not the intent, perhaps it's better to refer to the type parameter's core type instead, like make:

The built-in function make takes a type T, optionally followed by a type-specific list of expressions. The core type of T must be a slice, map or channel.

For this case, please let me propose:

If the argument type is a type parameter, the type parameter's core type must be a map, slice, or pointer to array, and clear performs the operation implied by the actual type argument.

@griesemer
Copy link
Contributor

griesemer commented Nov 5, 2022

The (proposed) intent is to allow the former (no need to restrict to core type). But depending on how that influences the implementation, we may start with the restriction to core types.

@griesemer
Copy link
Contributor

griesemer commented Nov 10, 2022

Something to be aware of: clear applied to slices and arrays permits the creation of zero values (elements) of possibly unexported types (in a package that doesn't have name access to the respective type and thus doesn't have a direct way to create a zero value of such a type). Per discussion in #56669, this is not adding a new capability to the language (but it does make it easier to create such zero values).

@rsc
Copy link
Contributor Author

rsc commented Nov 16, 2022

Based on the discussion above, this proposal seems like a likely accept.
— rsc for the proposal review group

gopherbot pushed a commit that referenced this issue Nov 17, 2022
Will become available with Go 1.21.

Recognizing the `clear` built-in early is not causing any problems:
if existing code defines a `clear`, that will be used as before. If
code doesn't define `clear` the error message will make it clear
that with 1.21 the function will be available. It's still possible
to define a local `clear` and get rid of the error; but more likely
the name choice should be avoided going forward, so this provides a
useful early "heads-up".

For #56351.

Change-Id: I3d0fb1eb3508fbc78d7514b6238eac89610158c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/448076
Run-TryBot: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
@Sajmani
Copy link
Contributor

Sajmani commented Nov 18, 2022

Clearing maps and slices makes sense to me. I'm not sure why clear needs to support clearing a pointer to an array; can't this be done by slicing the array?

var a [3]int
clear(a[:])

@cespare
Copy link
Contributor

cespare commented Nov 18, 2022

We can look at the existing special provisions made for pointers-to-arrays in the language.

Pointers-to-arrays are treated specially (like a kind of fixed-length slice) in a few places:

  • slice expressions
  • type conversions (as of Go 1.17 at least, where you can do (*[3]int)(aSlice))
  • for...range statements
  • The len and cap (pseudo)functions

However, they are not treated specially by append and copy today:

s := []int{1, 2}
a := [2]int{3, 4}
_ = append(s, (&a)...) // doesn't work
copy(s, &a)            // doesn't work

(There is a (very) special case for these where if the first argument is a []byte, the second argument can be a string.)

If we think of clear as more akin to append and copy, then perhaps it makes sense to not include pointer-to-array support. (Or maybe we should go the other direction and make these builtins, or at least copy, support pointers-to-arrays. Has this been discussed before? Surely yes, but I can't locate the issue.)

OTOH, append and copy do not work on maps, so maybe clear is more like len/cap, which operate on a variety of types. But this analogy doesn't go very far because len/cap also work on strings and chans which clear will not accept.

I think my slightly preferred option is for clear to only support maps and slices initially, and we can separately discuss having it support pointers-to-arrays (perhaps in conjunction with copy).

@Sajmani
Copy link
Contributor

Sajmani commented Nov 18, 2022

@thomasf
Copy link

thomasf commented Nov 19, 2022

Have you considered panicking when trying to assign a NaN float as a key to a map?

It could be one of those features that only activates if go.mod is at 1.21? It is of course impossible to know if a map created by an module with a pre go1.21 version contains NaN keys so it might become a bit confusing.

Is there a real use case for when a NaN map key isn't a bug?

I am btw. personally in favour of simpler way to clear a map regardless if NaN keys are bugs or not but that doesn't mean it's the appropriate solution to the problem.

@Merovius
Copy link
Contributor

Merovius commented Nov 19, 2022

@thomasf See #20660. And to reiterate: The motivation here is not to be able to remove NaN keys from maps. It's to be able to efficiently clear them.

@maykinayki
Copy link

maykinayki commented Nov 20, 2022

My first test for most languages features is "is this just trying to avoid writing a loop or if statement?" and if it is, I put it loooow on the list. If we ignore the NaN edge case, that's all this is.

Hey @natefinch, how about ignoring the NaN edge case here and returning the fixed hash value just like any other floating number?
hashCode implementation of Double in the Kotlin/Java returns a predefined constant value (see hashCode and doubleToLongBits below). Maybe something similar can be implemented in go, though I have to mention that the memhash function returns a fixed hash value for NaN anyway. So maybe there is no need to handle it at all.

I tried to remove case f != f: case from f64hash and recompiled go but it didn't work even though f64hash indeed gets called when I created a map and inserted some key-values. It was as if the return value of f64hash function was irrelevant. I presume some underlying implementation of f64hash needs to be changed somewhere else(???). Any help here is appreciated.

@Merovius
Copy link
Contributor

Merovius commented Nov 20, 2022

@maykinayki See this comment regarding that suggestion.

@maykinayki
Copy link

maykinayki commented Nov 20, 2022

@maykinayki See this comment regarding that suggestion.

The research shared in this comment is quite misleading. Iteration condition i < 20 is chosen wisely in the equivalent Go program as the program exists in the 19th iteration even if you set the condition to, for example, i < 26 (see: https://go.dev/play/p/gpj4X9TCNbP). Try it locally, you will see that it does not run in linear time at all. So I don't see any optimization here in the hash function for NaN cases.

@Merovius
Copy link
Contributor

Merovius commented Nov 20, 2022

@maykinayki I don't understand what you are arguing. This is what your program prints on my laptop:

0 1 823ns
1 2 425ns
2 4 454ns
3 8 9.456µs
4 16 5.241µs
5 32 18.713µs
6 64 15.894µs
7 128 37.662µs
8 256 63.915µs
9 512 94.165µs
10 1024 173.128µs
11 2048 322.786µs
12 4096 681.056µs
13 8192 1.319906ms
14 16384 2.481955ms
15 32768 5.360443ms
16 65536 12.040117ms
17 131072 23.937675ms
18 262144 50.91205ms
19 524288 113.154386ms
20 1048576 235.864912ms
21 2097152 465.504537ms
22 4194304 1.043067046s
23 8388608 2.232165622s
24 16777216 5.043666817s
25 33554432 9.841919012s
26 67108864 26.609594195s

That looks pretty linear to me (ignoring the noise around the ends). But that's off-topic anyways. Whether or not Go's builtin hash function randomizes NaN keys is immaterial. The interesting part of that post is the "the alternatives are worse" section. We are not going to change the behavior of irreflexive keys for maps.

@maykinayki
Copy link

maykinayki commented Nov 20, 2022

@Merovius, the post says that returning a random hash for NaN improves the performance, I argue that it has a bad performance anyway. Try to run the same program with a float value other than NaN, it finishes in a second. So where is the performance improvement for NaN? Whereas the same program in Kotlin returns similar execution times for NaN and any other float value. Why should it differ anyway?!

My point is if there is no performance improvement on returning a random hash for NaN then let's return a fixed hash for it. So a map can have only a single NaN key which is readable and removable thus no need to introduce a new built-in such as clear.
But I understand that at the same time changing the way map behaves at this point is not feasible.

@Merovius
Copy link
Contributor

Merovius commented Nov 20, 2022

@maykinayki The performance improvement comes from an asymptotically linear behavior vs. an asymptotically quadratic behavior. That matters a lot, when you talk about attacker-chosen map keys. Even if there is an overhead to using NaN, as long as that overhead is proportional to the length of the input data, there is no amplification effect.

My point is if there is no performance improvement on returning a random hash for NaN then let's return a fixed hash for it. So a map can have only a single NaN key which is readable

The post mentions this possibility and explains why we are not going to do it:

If you define that NaN is equal to itself during hash key comparisons, now you have a second parallel definition of equality, to handle NaNs inside structs and so on, only used for map lookups. Languages typically have too many equality operators anyway; introducing a new one for this special case seems unwise.

To be clear, this is not up to discussion right now. Unless someone has radically new data, we are not going to reverse this decision and break compatibility.

and removable thus no need to introduce a new built-in such as clear.

Again, the point of clear is not to be able to remove irreflexive keys. The point is to have an efficient way to clear maps.

@thomasf
Copy link

thomasf commented Nov 21, 2022

@thomasf See #20660. And to reiterate: The motivation here is not to be able to remove NaN keys from maps. It's to be able to efficiently clear them.

Oh, I didn't think about the reflect package since I almost never use it directly myself.

In all other scenarios Go could just start using the same efficient compiler replacement that it already uses for all keys that does not contain floats ( https://go.dev/doc/go1.11#performance-compiler ) if NaNs were disallowed as keys.

Maybe that's not enough...

@gopherbot
Copy link

gopherbot commented Nov 25, 2022

Change https://go.dev/cl/453395 mentions this issue: cmd/compile: add clear(x) builtin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Likely Accept
Development

No branches or pull requests