• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Protobuf Design: Options Attributes
2
3A proposal to create target and retention attributes to support.
4
5**Author:** [@kfm](https://github.com/fowles)
6
7**Approved:** 2022-08-26
8
9## Background
10
11The [Protobuf Editions](what-are-protobuf-editions.md) project plans to use
12[custom options](protobuf-editions-design-features.md) to model features and
13encourage language bindings to build custom features off options as well.
14
15This design proposed the specific addition of `target` and `retention`
16attributes for options as well as their suggested meaning.
17
18Both `target` and `retention` attributes are no-ops when applied to fields that
19are not options (either from descriptor.proto or custom options).
20
21## Target Attributes
22
23Historically, options have only applied to specific entities, but features will
24be available on most entities. To allow language specific extensions to restrict
25the places where options can bind, we will allow features to explicitly specify
26the targets they apply to (similar in concept to the "target" attribute on Java
27annotations). `TARGET_TYPE_UNKNOWN` will be treated as absent.
28
29```
30message FieldOptions {
31  ...
32  optional OptionTargetType target = 17;
33
34  enum OptionTargetType {
35    TARGET_TYPE_UNKNOWN = 0;
36    TARGET_TYPE_FILE = 1;
37    TARGET_TYPE_EXTENSION_RANGE = 2;
38    TARGET_TYPE_MESSAGE = 3;
39    TARGET_TYPE_FIELD = 4;
40    TARGET_TYPE_ONEOF = 5;
41    TARGET_TYPE_ENUM = 6;
42    TARGET_TYPE_ENUM_VALUE = 7;
43    TARGET_TYPE_SERVICE = 8;
44    TARGET_TYPE_METHOD = 9;
45  };
46}
47```
48
49If no target is provided, `protoc` will permit the target to apply to any
50entity. Otherwise, `protoc` will allow an option to be applied at either the
51file level or to its target entity (and will produce a compile error for any
52other placement). For example
53
54```
55message Features {
56  ...
57
58  enum EnumType {
59    OPEN = 0;
60    CLOSED = 1;
61  }
62  optional EnumType enum = 2 [
63      target = TARGET_TYPE_ENUM
64  ];
65}
66```
67
68would allow usage of
69
70```
71// foo.proto
72edition = "tbd"
73
74option features.enum = OPEN;  // allowed at FILE scope
75
76enum Foo {
77  option features.enum = CLOSED;  // allowed at ENUM scope
78  A = 2;
79  B = 4;
80}
81
82message Bar {
83  option features.enum = CLOSED;  // disallowed at Message scope
84
85  enum Baz {
86    C = 8;
87  }
88}
89```
90
91## Retention
92
93To reduce the size of descriptors in protobuf runtimes, features will be
94permitted to specify retention rules (again similar in concept to "retention"
95attributes on Java annotations).
96
97```
98enum FeatureRetention {
99  RETENTION_UNKNOWN = 0;
100  RETENTION_RUNTIME = 1;
101  RETENTION_SOURCE = 2;
102}
103```
104
105Options intended to inform code generators or `protoc` itself can be annotated
106with `SOURCE` retention. The default retention will be `RUNTIME` as that is the
107current behavior for all options. **Code generators that emit generated
108descriptors will be required to omit/strip options with `SOURCE` retention from
109their generated descriptors.** For example:
110
111```
112message Cpp {
113  enum StringType {
114    STRING = 1;
115    STRING_VIEW = 0;
116    CORD = 2;
117  }
118
119  optional string namespace = 2 [
120      retention = RETENTION_SOURCE,
121      target = TARGET_TYPE_FILE
122  ];
123}
124```
125
126## Motivation
127
128While the proximal motivation for these options is for use with "features" in
129"editions", I believe they provide sufficient general utility that adding them
130directly to `FieldDescriptorOptions` is warranted. For example, significant
131savings in binary sizes could be realized if `ExtensionRangeOptions::Metadata`
132had only `SOURCE` retention. Previously, we have specifically special-cased this
133behavior on a per-field basis, which does work but does not provide good
134extensibility.
135
136## Discussion
137
138In the initial design `target` was serving the dual purpose of identifying the
139semantic entity, and also the granularity of inheritance for features. After
140discussion about concerns around over use of inheritance, we decided for a
141slightly refined definition that decouples these concerns. `target` **only**
142specifies the semantic entity to which an option can apply. Features will be
143able to be set on both the `FILE` level and their semantic entity. Everything in
144between will be refused in the initial release. This allows us a clean
145forward-compatible way to allow arbitrary feature inheritance, but doesn't
146commit us to doing that until we need it.
147
148Similarly, we will start with `optional` target, because we can safely move to
149`repeated` later should the need arise.
150
151The naming for `target` and `retention` are directly modeled after Java
152annotations. Other names were considered, but no better name was found and the
153similarity to an existing thing won the day.
154
155## Alternatives
156
157### Use a repeated `target` proposed
158
159This is the proposed alternative.
160
161#### Pros
162
163*   Allows fine-grained control of target applicability.
164
165#### Cons
166
167*   Harder to generalize for users (every feature's specification is potentially
168    unique).
169
170### Allow hierarchy based on `target` semantic location.
171
172Rather than having a repeated `target` that specifies all locations, we allow
173only the level at which it semantically applies to be specified. The protoc
174compiler will implicitly allow the field to be used on entities that can
175lexically group that type of entry. For this `target` can be either singular or
176repeated.
177
178#### Pros
179
180*   Enables tooling that understands when a feature is used for grouping vs when
181    it has semantic value (helpful for minimizing churn in large-scale changes).
182*   Easier to generalize for users (any `FIELD` feature can apply to a message
183    as opposed to only the `FIELD` features that explicitly specified an
184    additional `target`).
185
186#### Cons
187
188*   Forces all `target` applications to be permitted on scoping entities.
189
190### Use Custom Options (aka "We Must Go Deeper")
191
192Rather than building `retention` and `target` directly as fields of
193`FieldOptions`, we could use custom options to define an equivalent thing. This
194option was rejected because it pushes extra syntax onto users for a fundamental
195feature.
196
197#### Pros
198
199*   Doesn't require modifying `descriptor.proto`.
200
201#### Cons
202
203*   Requires a less-intuitive spelling in user code.
204*   Requires an additional import for users.
205*   Language-level features would have to have a magic syntax or a side table
206    instead of using the same consistent option as user per code gen features.
207
208### Hard Code Behaviors in `protoc`
209
210Rather than building a generic mechanism we could simply hard code the behavior
211of protoc and document it.
212
213#### Pros
214
215*   Can't be misused.
216
217#### Cons
218
219*   Not extensible for users.
220*   Requires more special cases users need to learn.
221
222### Original Approved Proposal
223
224The proposal as originally approved had some slight differences from what was
225ultimately implemented:
226
227*   The retention enum did not have an `UNKNOWN` type.
228*   The enums were defined at the top level instead of nested inside
229    `FieldOptions`.
230*   The enum values did not have a scoping prefix.
231*   The target enum had a `STREAM` entry, but this turned out to be unnecessary
232    since the syntax that it applied to was removed.
233
234### Do Nothing
235
236We could omit this entirely and get ice cream instead. This was rejected because
237the proliferation of features on entities they do not apply to is considered too
238high a cost.
239
240#### Pros
241
242*   Ice cream is awesome.
243
244#### Cons
245
246*   Doesn't address any of the problems that caused this to come up.
247*   Some people are lactose intolerant.
248