Skip to content

Latest commit

 

History

History
97 lines (69 loc) · 3.22 KB

File metadata and controls

97 lines (69 loc) · 3.22 KB

Limitations

Redline is a syntactic lint over one function body. This page is the list of ways it is wrong, not a roadmap.

What it can catch

  • Constructs in its allocation table (Vec::new, String::from, .collect(), vec!, format!, …) appearing in the annotated body
  • Constructs in its syscall table (std::fs::*, File::open, TcpStream::connect, println!, …) appearing in the annotated body
  • Loop multiplication when the range is a literal
  • The more expensive arm of an if/match
  • Closure bodies written inline

What it cannot catch

Anything behind a call

There is no cross-function analysis and there cannot be one in a proc-macro: no MIR, no type information, no cross-crate data. Redline therefore treats an unrecognized call as an error, not as free:

#[redline(latency = "< 1ms")]
fn looks_fine() {
    expensive(); // error: cannot analyze `expensive`
}

assume_calls_free suppresses that error and restores the old, unsound behaviour, at your explicit request.

Indirect calls and trait objects

Function pointers, closures held in variables, and dyn Trait method calls are opaque. They are reported as un-analyzable rather than silently skipped.

#[redline(allocs = 0)]
fn indirect() -> String {
    let f: fn() -> String = String::new;
    f() // error: the callee is a runtime value
}

Type aliases and generics

Matching is on written path segments. use std::collections::HashMap as Map; followed by Map::new() is not in the table, so it is reported as an un-analyzable call rather than as an allocation. Generic code, operator overloads and Deref are invisible: a + b costs 1 ALU op even if Add is implemented by allocating.

Drop

Deallocation and Drop impls are not counted at all.

Macros

Macro bodies are not expanded — expansion order makes their contents unavailable. A macro whose name is not in the recognized list is counted as one call and its contents are ignored. This is a real hole: a macro that allocates passes an allocs = 0 check.

unsafe and FFI

unsafe blocks are walked, but an extern "C" call is just an unrecognized call. It errors rather than passing, which is the best available answer.

Dynamic loop bounds

Non-literal bounds are assumed to be 1000 iterations. Arbitrary.

async

.await is reported as un-analyzable: suspension time is not a property of the syntax. An async fn under a latency bound is not meaningful.

Stack size

max_stack counts bindings × 8 bytes. There is no type information.

The right mental model

A lint with a hard failure mode, over one function body, matching a fixed list of names. It is useful for keeping String::from and println! out of a hot path that someone might edit later. It is not a verifier, and a passing latency bound is not evidence about runtime.

For actual numbers use iai-callgrind (instruction counts on compiled code) or criterion. For actual compile-time verification of program properties use Kani, Prusti, MIRAI or Creusot.