Skip to content
UNDER THE HOOD

weavec::Analysis — the bridge

lib/Analysis is the only library allowed to include both weavec/Core/* and clang/*. It:

  • recognises WeaveC annotations on declarations, statements and function-pointer types (Annotations.h; collectFunctionTypeAnnotations walks through typedefs, fields and parameters to the prototype);
  • converts source locations in both directions (ClangLocation.h);
  • resolves the summary of any callee (Summaries.h, SummaryStore), in order: the callee’s own annotations, the summary inferred from its body in this TU, the program database (a definition in another unit of the program), the shipped libc/POSIX table (Builtins.cpp), and finally a documented default that also records the callee as an unknown boundary. For a call through a function pointer (lookupCall), annotations on the pointer’s type remain authoritative; otherwise the current function-value targets select the summaries. Unknown alternatives retain the boundary behavior (RFC 0014). lookupIndirect supplies only an explicit type contract;
  • holds what other units export (ProgramDatabase.h): UnitExports (the functions a unit defines with their summaries, linkage, canonical type key and address-taken flag; the names it imports; the indirect-call type keys it has no signature for; the boundaries it deferred) and ProgramDatabase, which joins exports by name and by type key and remaps summaries that mention globals into the importing unit’s GlobalTable;
  • classifies calls by their ownership effect on top of that (Allocators.h, classifyCallCallEffects);
  • maps expressions onto structured places, classifies pointer-typed values as allocation, copy, borrow, null, raw or opaque, and translates summary paths into the caller’s places and back (lib/Analysis/PlaceBuilder.h). Resolved dereferences retain each pointer, expression and element witness together; short paths and mirror-query results use inline storage that grows for larger results. Object-view validation may reuse an individual comparison between an immutable AST type and a view in the same live callee summary. Every path step and expected view still participates; erased-pointer recovery reads the current flow state (RFC 0027). Pointer arithmetic and pointer-to-pointer casts preserve identity; only integer-to-pointer casts produce raw values;
  • runs a forward dataflow over clang::CFG for each function body (lib/Analysis/Dataflow.h, FunctionDataflow): a worklist to a fixpoint with core::AnalysisState as the lattice, then one reporting pass that emits each diagnostic once. A backward liveness pass over the same CFG runs first; before each element the loans held by dead locals expire (RFC 0006). On each edge out of a conditional, applyEdge refines the state with what the condition says: pointer equality unites or separates aliases, and a test on a call result selects outcome classes and reinstates what the callee consumed only in the other classes. While running it applies callee summaries at every call (element-aware, deepest consumed path first, with written effects forgetting the facts below the written place), records its own effects, stores, returns and per-class consumption, checks them against the function’s annotations, tracks raw pointers and reports raw operations, keeps the books of owned resources (acquired at allocations and WEAVEC_OWNED declarations; released, moved, escaped or lost — the leak and release-family checks of RFC 0007 run where a holder dies, on each CFG edge, at overwrites and at container frees), tracks what is known about each pointer’s nullness and reports dereferences and calls that need more (RFC 0008; the null test idioms are the RFC 0006 condition facts), marks uninitialised locals and checks what a releaser is handed, tracks what is known about each integer’s value (constants assigned, ==/!=/</… against a constant, truthiness, switch cases) and attaches the facts of the current path as a guard to every move, held resource and null record it creates so that a later test can refute them, translates a callee’s when guards to the arguments and prunes them against its own facts, ends the block at a call to a callee inferred never-returns (RFC 0009), keeps what is known of the string each object holds and checks the string copies and terminator-seeking reads of the shipped table against it (DataflowStrings.cpp, RFC 0012), gives a WEAVEC_SIZED_BY or inferred sized field the extent its count says and records what every store into a field says about the pair (DataflowSizedFields.cpp, RFC 0012), applies WEAVEC_ASSUME as a condition edge, and produces the function’s FunctionSummary at exit (with neverReturns when the exit was never reached). WEAVEC_UNSAFE regions are analysed like any other code; the pass only suppresses what it would report inside them. FunctionAnalysis.h is the per-function entry point; AnalysisOptions::exclusiveBorrows switches RFC 0001’s exclusivity rules back on;
  • drives a whole translation unit (TranslationUnitAnalysis.h, TranslationUnitAnalyzer): collects definitions and address-taken functions, builds the call graph (with an edge from every indirect call to each candidate of its type), and analyses strongly connected components in reverse topological order (callees first), iterating recursive components to a fixpoint on their summaries before the final reporting pass. When the unit’s own stores confirm a sized-field pair (RFC 0012) the functions that read the field are analysed once more with it in force and only their new reports are shown. With a ProgramDatabase attached, callers see callees from other units; discover() returns the unit’s exports without analysing it (what it defines and imports, for ordering units) and exports() returns them with summaries, sized-field witnesses and refutations, and the fields it looked up, after run().

The model is specified by RFC 0001, the dataflow by RFC 0002, summaries by RFC 0003, raw pointers, unsafe regions and indirect calls by RFC 0004, cross-unit analysis by RFC 0005, non-lexical loans, condition facts, element witnesses and outcome-conditional summaries by RFC 0006, leaks, release families and owned fields by RFC 0007, nullness, uninitialised pointers, invalid releases and replaced values by RFC 0008, and scalar facts, guards, argument-conditional summaries and inferred noreturn by RFC 0009; each RFC’s Implementation notes record where the code refines the design.