It looks like this was handled by the field's usage, but in Nov as part of the syntax cleanup, we stopped looking at syntax and captured things on the enum definition, so it's now defined by the enum.
</td>
</tr>
<tr>
<td>Swift
</td>
<td>Determined by the enum's definition file
<p>
Swift uses the ability for enums to have associated values, so an enum defined in a proto3 syntax file gets a value that holds all unknown values. So a proto2 syntax defined message will still end up with the enum using that to hold unknown values.
</td>
</tr>
<tr>
<td>Go
</td>
<td><strong>All</strong> enums treated as open
</td>
</tr>
<tr>
<td>Apps JSPB
</td>
<td><strong>All</strong> enums treated as open
</td>
</tr>
<tr>
<td>ImmutableJs
</td>
<td><strong>All</strong> enums treated as open
</td>
</tr>
<tr>
<td>JsProto
</td>
<td><strong>All</strong> enums treated as open
</td>
</tr>
</table>
### Impact
Approximately 2.99% of enum fields import enums across syntaxes and 1.77% of
enums are imported across syntaxes.
6.14% of fields being enum fields, meaning 0.18% of fields are affected when
used by affected languages.
## Overview
This document proposes adding an additional feature to
[Edition Zero Features](edition-zero-features.md), specified as the following
.proto fragment:
```
message Features {
// ...
optional bool legacy_treat_enum_as_closed = ??? [
retention = RUNTIME,
target = FILE,
target = FIELD
];
}
```
The name of this field captures the desired intent: this is a bad legacy
behavior that we believe is rare and want to stamp out. Edition 2023 would set
this to false by default, and `proto2` would treat it as implicitly true. It
also does not permit the converse: you cannot force a field to be open, because
that is currently not possible and we don't want to add more special cases.
Additionally, we would like to make special dispensation in migration tooling
for this field: it should not be set unconditionally when migrating from proto2
-> editions, but *only* on proto2 fields that are of proto3 enum type. We should
also want to build an allowlist for this, like we do for `required`.
This option can also help in migrating enums from closed to open, since we can
use it to migrate individual use-sites by marking the enum as open and all of
its uses as treat-as-closed in one CL, and then deleting the treat-as-closed
annotations one by one.
An open (lol) question is whether we should move `is_closed` from
`EnumDescriptor` to `FieldDescriptor`.
## Recommendation
Use the "define official behavior" alternative below. Given the wide variety of
behavior in different languages, a singular global setting will always leave
some of our languages in the lurch. As such, we will use per language features
to allow each language to control its own evolution while we define the
"correct" behavior.
For example, in C++ we will define:
```
// Determines if the given enum field is treated as closed based on legacy
// non-conformant behavior.
//
// Conformant behavior determines closedness based on the enum and
// can be queried using EnumDescriptor::is_closed().
//
// Some runtimes currently have a quirk where non-closed enums are
// treated as closed when used as the type of fields defined in a
// `syntax = proto2;` file. This quirk is not present in all runtimes; as of
// writing, we know that:
//
// - C++, Java, and C++-based Python share this quirk.
// - UPB and UPB-based Python do not.
// - PHP and Ruby treat all enums as open regardless of declaration.
//
// Care should be taken when using this function to respect the target