Skip to main content

annotation

A named piece of metadata with optional parameters and optional scope restrictions. It is a global declaration, importable like a struct or a const, and it leaves the element it decorates unchanged.

annotation Classification(level: string)
annotation Deprecated

@Deprecated
@Classification("unclassified")
struct TrackReport {
    track_id: uint32;
    bearing_deg: float32;
}

Parameters​

Parameters follow the name in parentheses, each a name, a colon, and a type.

  • Parameters are optional. A parameterless annotation takes no parentheses, and an empty () is a parse error.
  • Parameter names are unique within the declaration.
  • A parameter's type may be any type.
annotation Note(id: int32, msg: string)
annotation Critical
annotation Offset(x: int32, y: int32)

Scopes​

A scope list between | bars restricts where the annotation may be applied. With no list, it applies to any target below.

KeywordTarget
Struct / Fielda struct declaration / one of its fields
Variant / VariantConstructora variant declaration / one of its constructors
Enum / EnumValuean enum declaration / one of its members
Newtypea newtype declaration (NewType is accepted as well)
TypeAliasa type alias
Consta const declaration
Function / Transform / Contractthe corresponding declaration
Component / Connection / Systemthe corresponding declaration
LocalProtocol / GlobalProtocola local protocol / global protocol declaration
DeclAnnotationan annotation declaration itself
ProtocolStmt / ProtocolStatean annotatable protocol statement: send, recv, exch, a listen arm, or a standalone use
Unique / Requirementnot a target — see below
  • Each name in the list is one of the keywords above.
  • Several keywords may be listed, separated by whitespace, and a use matching any one of them is accepted.
  • Unique and Requirement name no target. Listed alone, they leave the annotation applicable anywhere; listed beside a target scope, that scope still restricts.
annotation Deprecated | Struct Variant VariantConstructor |
annotation FieldTag(tag: uint8) | Field |
annotation Topic(name: string) | ProtocolStmt |

Unique​

Unique limits an annotation to one use per set of argument values per package.

  • Two uses carrying the same argument values within one package are an error, and every use after the first is flagged.
  • Uses whose arguments differ are unrelated.
  • The rule is package-scoped, so the same values may repeat in a different package, and two colliding uses may sit in different modules of one package.
  • An argument-less annotation carries an empty argument tuple, which equals itself, so a bare | Unique | marker appears at most once per package.
  • Arguments compare as values, not as spellings. @TrackId(42), @TrackId(0x2A), and @TrackId(4_2) are one value, as are 1.0, 1.00, and 1e0.
  • A constant reference is not evaluated. @TrackId(max_tracks) is compared by the declaration max_tracks resolves to, so it never collides with @TrackId(42) even when max_tracks is 42. Two references to the same constant do collide.
annotation TrackId(id: int32) | Unique |

const max_tracks: int32 = 42;

@TrackId(42)
struct TrackReport { track_id: uint32; }

@TrackId(max_tracks)
struct TrackUpdate { track_id: uint32; }

Requirement​

Requirement marks the annotation as carrying requirement identity. The language records the mark and reads nothing from it; requirement tracing outside the language is what reads it. A use is checked exactly as any other use is.

Like Unique, it names no target, so a target scope listed beside it is what decides where the annotation may be applied.

enum GpsRequirement int32 {
    SysReq1234 = 1234;
    SysReq1235 = 1235;
}

annotation GpsClaim(req: GpsRequirement) | Requirement LocalProtocol |

Uses​

A use is @, the annotation's path, and arguments in parentheses when the declaration takes parameters. It stands immediately before the element it decorates, on its own line or on that element's line, and several uses stack in order.

  • The Target column above is the whole set of positions a use may occupy. An import, a module header, a let, and a newtype's single field take none.
  • The path must resolve to an annotation declaration. An annotation imported under a module alias is used as @alias.Deprecated.
annotation Deprecated | Struct Variant VariantConstructor Field EnumValue |
annotation FieldTag(tag: uint8) | Field |

@Deprecated
struct Contact {
    @FieldTag(1) contact_id: uint32;
    @Deprecated @FieldTag(2) bearing_deg: float32;
}

variant TrackKind {
    Air(uint32);
    @Deprecated Surface(uint32);
}

enum SensorMode string {
    Search = "search";
    @Deprecated Sweep = "sweep";
}

Arguments​

An argument is a literal or a path naming a const or an enum member, either one optionally preceded by a unary -. No other expression is an argument.

  • Arguments match parameters positionally, and the count must match exactly.
  • Each argument's type must be assignable to its parameter's type. A numeric literal pins to the parameter's width.
  • A path argument carries the type of the declaration it resolves to.
  • Unary - applies to a numeric literal or a numeric constant, and never to an unsigned-integer parameter.
annotation Offset(x: int32, y: int32)
annotation Label(name: string, active: bit)
annotation Mode(mode: SensorMode)

enum SensorMode string {
    Search = "search";
    Track = "track";
}

const default_offset: int32 = 25;

@Offset(-10, default_offset)
@Label("header", true)
@Mode(SensorMode.Search)
struct DisplayHint { hint_id: uint32; }

Protocol statements​

Annotations on a protocol carry transport metadata — a topic, a URL, a timeout — and do not affect what the protocol does. They appear in two positions.

  • Attached: a trailing run on a send, recv, or exch statement, or on a listen arm, ahead of the terminator. These belong to that statement.
  • Standalone: a bare use as a statement of its own, anywhere a protocol statement is allowed. Each use is one statement, so two uses written ahead of another statement are two statements rather than a run attached to it.

A standalone use is scope-checked as a protocol statement, so ProtocolStmt and ProtocolState both admit it.

message struct TrackReport { track_id: uint32; }

component Radar;
component CommandPost;

annotation Topic(name: string) | ProtocolStmt |
annotation RecvWithinMs(ms: int32) | ProtocolStmt |

global protocol ReportTrack {
    @Topic("tracks")
    exch any TrackReport from Radar to CommandPost
        @RecvWithinMs(1000);
}

Errors​

Two parameters with one name​

annotation Note(id: int32, id: string)   // error: duplicate-annotation-param

A scope the language does not define​

The scope list is a fixed vocabulary.

annotation Deprecated | Gadget |   // error: invalid-annotation-scope

The same Unique values twice in one package​

annotation TrackId(id: int32) | Unique |

@TrackId(42)
struct TrackReport { track_id: uint32; }

@TrackId(42)   // error: duplicate-unique-annotation
struct TrackUpdate { track_id: uint32; }

Using something that is not an annotation​

struct Contact { contact_id: uint32; }

@Contact                       // error: not-an-annotation
struct Radar { scan_period_ms: uint32; }

The wrong number of arguments​

annotation Note(id: int32, msg: string)

@Note(42)                      // error: annotation-arg-count
struct TrackReport { track_id: uint32; }

An argument the parameter's type does not accept​

annotation Note(id: int32, msg: string)
annotation FieldTag(tag: uint8) | Field |

@Note("42", "late")            // error: annotation-arg-type
struct TrackReport {
    @FieldTag(-1) track_id: uint32;   // error: annotation-arg-type
}

Applying an annotation outside its scopes​

annotation Deprecated | Struct |

@Deprecated                    // error: annotation-scope-mismatch
variant TrackKind { Air(uint32); Surface(uint32); }
  • const — a constant an argument may name.
  • enum — a member an argument may name.