Hi folks,
The differentiable programming project adds a few declaration attributes: @differentiable
, @derivative
, and @transpose
.
These attributes have a bunch of components and not all of them are currently serialized.
@derivative(of: foo, wrt: (x, y))
// ^~~ ^~~~~~
// Parsed: `DeclNameRef` `ArrayRef<ParsedAutoDiffParameter>`
// Resolved: `Decl *` `IndexSubset *`
func fooDerivative(x: Float, y: Float) -> (value: Float, differential: (Float, Float) -> Float) { ... }
- Not serialized
- Parsed parameter indices (
ArrayRef<ParsedAutoDiffParameter>
)†. -
DeclNameRef
(s) for referenced declaration(s)†. - Parsed
TypeRepr *
for the base type of the referenced declaration.† (currently@transpose
attribute only)
- Parsed parameter indices (
- Serialized
- Resolved parameter indices (
IndexSubset *
). Resolving parameter indices (during attribute type-checking) is not particularly cheap, so serializing them seemed sensible. - Referenced declarations (
Decl *
), resolved during attribute type-checking.
- Resolved parameter indices (
†: Serializing this component is necessary for full fidelity printing: printing deserialized attributes exactly like how they were written in source code. This may be nice for diagnostics.
Are there guidelines for which components (for attributes, or anything) should be serialized and which shouldn't be?
Here are some possible guidelines (some are in conflict with others in the list):
- Serialize all components that are important for use cases (e.g. serialize enough components to determine "identity" for equality).
- This may include data that is computed from raw parsed components, esp. if computing the data is expensive.
- Serialize only the raw parsed components: enough for full-fidelity printing after deserialization.
- Do not serialize data that can be computed from raw parsed components: such data can be recomputed lazily after deserialization.
Currently, we've followed (1) for @differentiable
, @derivative
, and @transpose
. Serializing raw parsed components for full-fidelity printing may make sense if there's no concern about serialized data bloat.
Some precedent: DynamicReplacementAttr
is similar to DifferentiableAttr
/DerivativeAttr
/TransposeAttr
in that it also stores a DeclNameRef
.
For DynamicReplacementAttr
, all DeclNameRef
components are serialized. The resolved Decl *
is not serialized: [NFC] Refactor @_dynamicReplacement Checking by CodaFi · Pull Request #28434 · apple/swift · GitHub added DynamicallyReplacedDeclRequest
so DynamicReplacementAttr
no longer stores its resolved Decl *
.