Which attribute components should be serialized?

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
  • 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.

†: 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):

  1. 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.
  2. Serialize only the raw parsed components: enough for full-fidelity printing after deserialization.
  3. 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 *.