Adds "Protobuf Editions Design: Features" to the GitHub code repository.
PiperOrigin-RevId: 563109674
diff --git a/docs/design/editions/README.md b/docs/design/editions/README.md
index 66671a4..5ecf5eb 100644
--- a/docs/design/editions/README.md
+++ b/docs/design/editions/README.md
@@ -21,5 +21,6 @@
* [What are Protobuf Editions?](what-are-protobuf-editions.md)
* [Life of an Edition](life-of-an-edition.md)
-* [Editions: Life of a FeatureSet](editions-life-of-a-featureset.md)
-* [Protobuf Design: Options Attributes](protobuf-design-options-attributes.md)
+* [Protobuf Editions Design: Features](protobuf-editions-design-features.md)
+* [Editions: Life of a Featureset](editions-life-of-a-featureset.md)
+* [Edition Naming](edition-naming.md)
diff --git a/docs/design/editions/protobuf-editions-design-features.md b/docs/design/editions/protobuf-editions-design-features.md
new file mode 100644
index 0000000..f2a98f5
--- /dev/null
+++ b/docs/design/editions/protobuf-editions-design-features.md
@@ -0,0 +1,317 @@
+# Protobuf Editions Design: Features
+
+**Author:** [@haberman](https://github.com/haberman),
+[@fowles](https://github.com/fowles)
+
+**Approved:** 2022-10-13
+
+A proposal to use custom options as our way of defining and representing
+features.
+
+## Background
+
+The [Protobuf Editions](what-are-protobuf-editions.md) project uses "editions"
+to allow Protobuf to safely evolve over time. An edition is formally a set of
+"features" with a default value per feature. The set of features or a default
+value for a feature can only change with the introduction of a new edition.
+Features define the specific points of change and evolution on a per entity
+basis within a .proto file (entities being files, messages, fields, or any other
+lexical element in the file). The design in this doc supplants an earlier design
+which used strings for feature definition.
+
+Protobuf already supports
+[custom options](https://protobuf.dev/programming-guides/proto2#customoptions)
+and we will leverage these to provide a rich syntax without introducing new
+syntactic forms into Protobuf.
+
+## Sample Usage
+
+Here is a small sample usage of features to give a flavor for how it looks
+
+```
+edition = "2023";
+
+package experimental.users.kfm.editions;
+
+import "net/proto2/proto/features_cpp.proto";
+
+option features.repeated_field_encoding = EXPANDED;
+option features.enum = OPEN;
+option features.(pb.cpp).string_field_type = STRING;
+option features.(pb.cpp).namespace = "kfm::proto_experiments";
+
+message Lab {
+ // `Mouse` is open as it inherits the file's value.
+ enum Mouse {
+ UNKNOWN_MOUSE = 0;
+ PINKY = 1;
+ THE_BRAIN = 2;
+ }
+ repeated Mouse mice = 1 [features.repeated_field_encoding = PACKED];
+
+ string name = 2;
+ string address = 3 [features.(pb.cpp).string_field_type = CORD];
+ string function = 4 [features.(pb.cpp).string_field_type = STRING_VIEW];
+}
+
+enum ColorChannel {
+ // Turn off the option from the surrounding file
+ option features.enum = CLOSED;
+
+ UNKNOWN_COLOR_CHANNEL = 0;
+ RED = 1;
+ BLUE = 2;
+ GREEN = 3;
+ ALPHA = 4;
+}
+```
+
+## Language-Specific Features
+
+We will use extensions to manage features specific to individual code
+generators.
+
+```
+// In net/proto2/proto/descriptor.proto:
+syntax = "proto2";
+package proto2;
+
+message Features {
+ ...
+ extensions 1000; // for features_cpp.proto
+ extensions 1001; // for features_java.proto
+}
+
+```
+
+This will allow third-party code generators to use editions for their own
+evolution as long as they reserve a single extension number in
+`descriptor.proto`. Using this from a .proto file would look like this:
+
+```
+edition = "2023";
+
+import "third_party/protobuf/compiler/cpp/features_cpp.proto"
+
+message Bar {
+ optional string str = 1 [features.(pb.cpp).string_field_type = true];
+}
+```
+
+## Inheritance
+
+To support inheritance, we will specify a single `Features` message that extends
+every kind of option:
+
+```
+// In net/proto2/proto/descriptor.proto:
+syntax = "proto2";
+package proto2;
+
+message Features {
+ ...
+}
+
+message FileOptions {
+ optional Features features = ..;
+}
+
+message MessageOptions {
+ optional Features features = ..;
+}
+// All the other `*Options` protos.
+```
+
+At the implementation level, feature inheritance is exactly the behavior of
+`MergeFrom`
+
+```
+void InheritFrom(const Features& parent, Features* child) {
+ Features tmp(parent);
+ tmp.MergeFrom(child);
+ child->Swap(&tmp);
+}
+```
+
+which means that custom backends will be able to faithfully implement
+inheritance without difficulty.
+
+## Target Attributes
+
+While inheritance can be useful for minimizing changes or pushing defaults
+broadly, it can be overused in ways that would make simple refactoring of
+`.proto` files harder. Additionally, not all features are meaningful on all
+entities (for example `features.enum = OPEN` is meaningless on a field).
+
+To avoid these issues, we will introduce "target" attributes on features
+(similar in concept to the "target" attribute on Java annotations).
+
+```
+enum FeatureTargetType {
+ FILE = 0;
+ MESSAGE = 1;
+ ENUM = 2;
+ FIELD = 3;
+ ...
+};
+```
+
+These will restrict the set of entities to which a feature may be attached.
+
+```
+message Features {
+ ...
+
+ enum EnumType {
+ OPEN = 0;
+ CLOSED = 1;
+ }
+ optional EnumType enum = 2 [
+ target = ENUM
+ ];
+}
+```
+
+## Retention
+
+To reduce the size of descriptors in protobuf runtimes, features will be
+permitted to specify retention rules (again similar in concept to "retention"
+attributes on Java annotations).
+
+```
+enum FeatureRetention {
+ SOURCE = 0;
+ RUNTIME = 1;
+}
+```
+
+## Specification of an Edition
+
+An edition is, effectively, an instance of the `Feature` proto which forms the
+base for performing inheritance using `MergeFrom`. This allows `protoc` and
+specific language generators to leverage existing formats (like text-format) for
+specifying the value of features at a given edition.
+
+Although naively we would think that field defaults are the right approach, this
+does not quite work, because the default is editions-dependent. Instead, we
+propose adding the following to the protoc-provided `features.proto`:
+
+```
+message Features {
+ // ...
+ message EditionDefault {
+ optional string edition = 1;
+ optional string default = 2; // Textproto value.
+ }
+
+ extend FieldOptions {
+ // Ideally this is a map, but map extensions are not permitted...
+ repeated EditionDefault edition_defaults = 9001;
+ }
+}
+```
+
+To build the edition defaults for a particular edition `current` in the context
+of a particular file `foo.proto`, we execute the following algorithm:
+
+1. Construct a new `Features feats;`.
+2. For each field in `Features`, take the value of the
+ `Features.edition_defaults` option (call it `defaults`), and sort it by the
+ value of `edition` (per the total order for edition names,
+ [Life of an Edition](life-of-an-edition.md)).
+3. Binsearch for the latest edition in `defaults` that is earlier or equal to
+ `current`.
+ 1. If the field is of singular, scalar type, use that value as the value of
+ the field in `feats`.
+ 2. Otherwise, the value of the field in `feats` is given by merging all of
+ the values less than `current`, starting from the oldest edition.
+4. For the purposes of this algorithm, `Features`'s fields all behave as if
+ they were `required`; failure to find a default explicitly via the editions
+ default search mechanism should result in a compilation error, because it
+ means the file's edition is too old.
+5. For each extension of `Features` that is visible from `foo.proto` via
+ imports, perform the same algorithm as above to construct the editions
+ default for that extension message, and add it to `feat`.
+
+This algorithm has the following properties:
+
+* Language-scoped features are discovered via imports, which is how they need
+ to be imported for use in a file in the first place.
+* Every value is set explicitly, so we correctly reject too-old files.
+* Files from "the future" will not be rejected out of hand by the algorithm,
+ allowing us to provide a flag like `--allow-experimental-editions` for ease
+ of allowing backends to implement a new edition.
+
+## Edition Zero Features
+
+Putting the parts together, we can offer a potential `Feature` message for
+edition zero: [Edition Zero Features](edition-zero-features.md).
+
+```
+message Features {
+ enum FieldPresence {
+ EXPLICIT = 0;
+ IMPLICIT = 1;
+ LEGACY_REQUIRED = 2;
+ }
+ optional FieldPresence field_presence = 1 [
+ retention = RUNTIME,
+ target = FIELD,
+ (edition_defaults) = {
+ edition: "2023", default: "EXPLICIT"
+ }
+ ];
+
+ enum EnumType {
+ OPEN = 0;
+ CLOSED = 1;
+ }
+ optional EnumType enum = 2 [
+ retention = RUNTIME,
+ target = ENUM,
+ (edition_defaults) = {
+ edition: "2023", default: "OPEN"
+ }
+ ];
+
+ enum RepeatedFieldEncoding {
+ PACKED = 0;
+ EXPANDED = 1;
+ }
+ optional RepeatedFieldEncoding repeated_field_encoding = 3 [
+ retention = RUNTIME,
+ target = FIELD,
+ (edition_defaults) = {
+ edition: "2023", default: "PACKED"
+ }
+ ];
+
+ enum StringFieldValidation {
+ REQUIRED = 0;
+ HINT = 1;
+ SKIP = 2;
+ }
+ optional StringFieldValidation string_field_validation = 4 [
+ retention = RUNTIME,
+ target = FIELD,
+ (edition_defaults) = {
+ edition: "2023", default: "REQUIRED"
+ }
+ ];
+
+ enum MessageEncoding {
+ LENGTH_PREFIXED = 0;
+ DELIMITED = 1;
+ }
+ optional MessageEncoding message_encoding = 5 [
+ retention = RUNTIME,
+ target = FIELD,
+ (edition_defaults) = {
+ edition: "2023", default: "LENGTH_PREFIXED"
+ }
+ ];
+
+ extensions 1000; // for features_cpp.proto
+ extensions 1001; // for features_java.proto
+}
+```