Skip to content

Instantly share code, notes, and snippets.

@mlugg
Last active September 28, 2023 02:34
Show Gist options
  • Save mlugg/cef75e023be295376924aa7e3da69708 to your computer and use it in GitHub Desktop.
Save mlugg/cef75e023be295376924aa7e3da69708 to your computer and use it in GitHub Desktop.
Incremental Compilation and Dependency Tracking

Definitions

A Decl, like today, is either:

  • A struct, union, enum, or opaque (i.e. a type with a namespace)
  • A container-scope const, var, or fn

Types are immutable. Whenever a type declaration is analyzed, it gets it from the InternPool, keyed on the current generation (so re-analysis of a type declaration across generations always creates a distinct type). The existing owner decl is used, but its value updated to refer to the new type.

(TODO: the above system will need some changes if we want structural equivalence based on captures)

Rationale: if types are not immutable, we need to recursively mark dependencies on the type from every entity which references it, which is very complex. Immutable types give us this for free, because everything which uses the type automatically becomes outdated.

Across incremental updates, we attempt to correlate ZIR instructions so as to decide the new ZIR indices of existing Decls. Notably, if a named decl is mapped, then it is guaranteed to have the same name as before and to live in the same namespace.

Dependencies

During semantic analysis, dependencies may be marked to trigger re-analysis of Decls and function bodies in future generations. Dependencies are marked on Sema's owner decl or function.

The "depender" of a dependency edge may be either of the following:

  • a Decl
    • arises when semantically analyzing the comptime-known tv of a Decl
  • a runtime function (either from a non-generic function decl, or a generic instantiation)
    • arises when semantically analyzing a runtime function body

The "dependee" of a dependency edge may be any of the following:

  • a Decl's value
    • arises from using a Decl's tv
      • note that this is not a dependency on the Decl's namespace!
    • outdated whenever a Decl's InternPool.Index changes, or when the Decl is removed
  • a Decl's source code
    • arises from a comptime or inline call
    • outdated whenever the source code corresponding to a Decl changes, or when the Decl is removed
  • a function Decl's type signature source code
    • arises from a runtime generic call
    • outdated whenever the source code corresponding to a generic function's type signature changes, or when the Decl is removed
    • this exists so that modifying the body of a runtime generic function does not force re-analysis of call sites
  • the full set of names in a Decl's namespace
    • arises from @typeInfo
    • outdated whenever the namespace of this Decl gains or loses any name, or when the Decl is removed
  • non-existence of a name foo in a Decl's namespace
    • arises from a failed namespace lookup (either directly or through @hasDecl)
    • outdated whenever the namespace of this Decl gains the name foo, or when the Decl is removed
  • a runtime function's IES
    • arises from resolution of an IES into a concrete error set
    • outdated whenever re-analysis of a function body changes its resolved IES

Every Decl has an implicit dependency on its own source code.

Every runtime function has an explicit dependency on the source code of its owner decl. (This is made explicit because otherwise we need to be able to iterate all runtime funtions corresponding to a single Decl!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment