Skip to content

Latest commit

 

History

History
178 lines (120 loc) · 4.9 KB

File metadata and controls

178 lines (120 loc) · 4.9 KB

#pragma pagurus Annotations Reference

Overview

pagurus uses pragma annotations to specify lifetime constraints, drop functions, and borrow management. These annotations guide the borrow checker's analysis and enable move semantics for resource-owning types.

1. Borrow release

Explicitly release a borrow before its natural scope end (mirrors Rust drop(borrow)):

#pragma pagurus borrow_end(p)

This tells the checker that the borrow through pointer p ends at this point, even though p may still be in scope. Useful for manually resolving borrow conflicts.

2. Function lifetime annotations

Concise syntax (preferred)

Place immediately before a function declaration. Space-separated tokens represent pointer parameter lifetimes; comma-grouped tokens represent struct parameter lifetimes.

Simple pointer parameters

Both params constrain the return (lifetime 'a for all):

#pragma pagurus 'a 'a -> 'a
char *func1(char *a, char *b);

Only the first param constrains the return (lifetime 'a):

#pragma pagurus 'a 'b -> 'a
int *get_x(int *x, int *y);

Struct parameters with multiple lifetimes

Comma-grouped syntax 'b,'a indicates a struct parameter instantiated with lifetimes 'b (first) and 'a (second) from the struct's declaration:

#pragma pagurus 'b,'a -> 'b
char *get_data(struct Buf buf);

The checker resolves which field is returned by matching lifetime 'b to the struct's field annotations (see section 4).

Legacy syntax

Backward compatible with earlier versions:

#pragma pagurus lifetime(get_ptr, 0)

This annotation indicates that the return value's lifetime is tied to parameter 0.

3. Struct and field lifetime annotations

Declare the struct's lifetime parameters and each field's lifetime:

#pragma pagurus 'a 'b
struct Buf {
    #pragma pagurus 'a
    const char *data;   /* lifetime 'a */
    #pragma pagurus 'b
    char *scratch;      /* lifetime 'b */
};

Validation rules:

  • Field with undeclared lifetime → error: "unknown lifetime 'e' in field ..."
  • Declared lifetime parameter with no field → error: "declared but not bound 'c' ..."

All declared lifetimes must be used, and all field lifetimes must be declared.

4. Drop function annotation

Designates the next function declaration as the required destructor for a C struct or typedef type:

#pragma pagurus drop(TypeName)
void destructor_fn(TypeName value);

Requirements:

  • TypeName may be a struct tag name OR a typedef name
  • The drop handler must:
    • Return void
    • Take exactly one parameter of type T
  • Invalid signatures are reported as errors and not registered

Examples

Named struct (tag name):

#pragma pagurus drop(StrStruct)
void str_free(struct StrStruct s);

Using a typedef name:

typedef struct StrStruct { ... } StrType;
#pragma pagurus drop(StrType)
void str_type_free(StrType s);

Effect on error checking

Any local variable of a drop-annotated type that goes out of scope without the drop function being called triggers E020[missing-drop]:

Plugin flags E020 severity Effect
-fplugin= only error No automatic injection; the drop call must be written explicitly in source
-fplugin= -fpass-plugin= warning IR pass (PagurusDropPass) auto-injects the drop call into the binary

Move semantics

Drop-annotated types have move semantics — assignment transfers ownership:

#pragma pagurus drop(StrStruct)
void str_free(struct StrStruct s);

void example(void) {
    struct StrStruct a = str_with_cap(64);
    struct StrStruct b = a;  // ownership moves to b — a is now Moved
    str_free(a);             // error: E019[use-after-move]
    str_free(b);             // OK
}

Structs without drop annotations are copy-able and can be freely assigned without transferring ownership.

5. Lifetime elision (rule 1 — extended)

For function declarations with exactly one "significant" parameter and a pointer return type, the checker automatically infers the return constraint without requiring an explicit annotation.

A parameter is "significant" when it is:

  • A pointer parameter (T *), or
  • A struct parameter passed by value where the struct has exactly one declared lifetime parameter

Examples

Automatically inferred (no annotation needed):

char *first_char(char *s);

Redundant annotation (emits warning: "can be elided"):

#pragma pagurus 'a -> 'a
char *dup(char *s);

Single-lifetime struct (annotation can be elided):

#pragma pagurus 'b
struct Buf { #pragma pagurus 'b char *scratch; };

#pragma pagurus 'a -> 'a    // Warning: can be elided
char *get_data(struct Buf buf);

The single-lifetime struct annotation itself also warns: "explicit lifetime declaration for struct 'Buf' can be elided".

Error code reference

For detailed information about specific error codes (E001–E021), see the main README.md.