# Architecture

## Module Structure

```
src/
  lib.rs       Entry point: #[redline(...)] proc-macro attribute
  parse.rs     Attribute parser: latency, throughput, allocs, syscalls, max_stack
  cost.rs      Cost model constants and recognized function/method signatures
  analyze.rs   AST walker: counts allocs, syscalls, estimates latency per path
```

## Data Flow

```
#[redline(latency = "< 1ms", allocs = 0)]
fn foo() { ... }
                |
                v
        [parse.rs] Parse attribute tokens
                |  -> RedlineAttr { constraints: [Latency(1_000_000ns), Allocs(0)] }
                v
        [analyze.rs] Walk function AST
                |  -> FnCost { latency_ns: 500, allocs: 0, syscalls: 0 }
                v
        [analyze.rs] Check constraints
                |  latency: 500 < 1_000_000  OK
                |  allocs:  0 <= 0           OK
                v
        Emit original function (no changes)
```

If any constraint fails:

```
        [analyze.rs] Check constraints
                |  allocs: 1 > 0  VIOLATION
                v
        Emit compile_error!("redline: function `foo` has 1 allocation(s), limit is 0")
        + emit original function (so other errors are still useful)
```

## Cost Accumulation

```
analyze_function(attr, func)
  -> analyze_block(func.body.stmts)
       -> for each stmt:
            analyze_stmt(stmt)
              -> analyze_expr(expr)
                   -> Expr::Call     -> check ALLOC_PATHS, SYSCALL_PATHS, NET_PATHS
                   -> Expr::MethodCall -> check method name patterns
                   -> Expr::Macro    -> check macro name (println, vec, format)
                   -> Expr::ForLoop  -> estimate_range_size * analyze_block(body)
                   -> Expr::While    -> 1000 * analyze_block(body)
                   -> Expr::If       -> max(then_cost, else_cost)
                   -> Expr::Match    -> max(all arm costs)
                   -> Expr::Closure  -> analyze_expr(closure.body)
                   -> Expr::Binary   -> ALU_NS
                   -> _              -> 0
```

## Key Design Decisions

1. **Pessimistic by default.** Unknown loop bounds = 1000 iterations. Unknown function calls = 5ns. The model overestimates, never underestimates.

2. **Name-based matching.** Without type information, we match on function/method names. This means aliases and indirect calls are missed. The tradeoff: proc-macros run before type checking, so type info is not available.

3. **Worst-case branching.** if/else and match take the cost of the most expensive arm. This is the correct model for latency bounds.

4. **Compile errors, not warnings.** The function does not compile if any constraint is violated. This is the entire point: a guarantee, not a suggestion.
