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