1. Abstract
This RFC defines Aura’s attributes (annotations) and metadata retention model for compile-time tooling, derives, and optional runtime reflection. v1 favors pay-as-you-go metadata (not full Java-style always-on reflection) to keep single binaries lean while enabling serializers, test discovery, and DI-like libraries later—without putting frameworks in core.
2. Motivation
2.1 Problem statement
Derives, tests, and serializers need a uniform attribute system. Full runtime reflection on every type bloats binaries and hurts optimization; zero reflection blocks ecosystem patterns.
2.2 Why now
Macros (RFC-010), testing (RFC-011), and compiler emission depend on attribute grammar and retention.
2.3 Success metrics
| Metric | Target |
|---|---|
| Attribute syntax | Stable, documented |
| Binary bloat | No metadata for types without retention |
| Test discovery | Works via attributes |
| Safe reflection | Visibility respected |
3. Goals
- Unified attribute syntax on declarations and parameters.
- Retention policies: source / binary / runtime.
- Opt-in runtime type info (
TypeId, members) for annotated types. - Hooks for derive macros.
4. Non-goals
- Full dynamic
invokeon every method of every class by default. - Java classpath scanning of the world.
- Runtime code generation / JIT of new classes in v1.
5. Prior art & alternatives
| System | Notes | Take |
|---|---|---|
| Java annotations + reflection | Powerful, heavy | Opt-in retention |
| C# attributes + reflection | Similar | Inspiration |
| Rust attributes + limited reflect | Pay-as-you-go | Closer spirit |
| Go tags + reflect | Struct tags | Contrast |
6. Design
6.1 Overview
@attr(...) on decl
→ compiler front records Attribute
→ expand derives ([RFC-010](/rfc/010))
→ emit metadata per retention
→ optional runtime APIs in std.reflect6.2 Attribute syntax
@test
@derive(Equals, Hash)
@deprecated("use NewApi", since = "0.2")
@json(name = "user_id")
fun foo(@notNull x: String) { }- Attributes are
@Nameor@Name(args). - Args: positional and/or named literals / simple consts.
- Repeatable attributes only if declared repeatable.
- Unknown attributes are a hard compile error (builtins + declared attribute types only).
6.3 Attribute declaration
@Attr(retention = Runtime, targets = [Class, Fun])
class JsonName(val name: String)Or simpler v1: built-in set + user attributes as classes marked @attribute.
6.4 Retention
| Level | Kept in | Use |
|---|---|---|
Source | Compiler session only | Lints, local tools |
Binary | Package metadata / rlib side table | Link-time, tools |
Runtime | Embedded in binary | std.reflect |
Default for user attrs: Binary (available to tools/dependents without runtime bloat). Runtime requires explicit opt-in.
6.5 Built-in attributes (alpha vocabulary)
The current names and implementation status are tracked in
docs/api/compiler-alpha.md. Reserved names are
accepted only after their parser/sema phase is enabled; until then they must
produce an explicit unsupported diagnostic rather than being silently ignored.
| Attribute | Role |
|---|---|
@test, @bench | Test discovery (RFC-011) |
@derive(...) | Macro derives (RFC-010) |
@deprecated | Warnings |
@inline, @noinline, @cold | Optimization hints |
@throws | Doc/tooling only (unchecked exceptions) |
@unsafe | Marks unsafe APIs |
@repr(...) | Layout hints for structs/FFI |
@json(name = "...") | JSON wire-name mapping on fields |
@retention / meta-attributes | On attribute types |
6.6 Runtime reflection API (sketch)
package std.reflect
class Type {
fun name(): String
fun methods(): Array<Method>
fun fields(): Array<Field>
// ...
}
fun typeOf<T>(): Type
fun typeIdOf<T>(): TypeIdConstraints:
- Only types with runtime metadata; others → error or limited info.
- Visibility: public members only by default; non-
pubrequires same-module or privileged API (if ever). - Generics: may show raw class + type args if reified/recorded; mono instances may collapse—document honestly.
6.7 Interaction with monomorphization
- Concrete mono functions need not all appear in reflection.
- Class metadata describes the class template; type arguments recorded when retention requests it.
6.8 Examples
@derive(Debug)
@reflect // opt into runtime metadata
class User(
@json(name = "id") val id: Long,
val email: String,
)6.9 Error model / edge cases
| Case | Behavior |
|---|---|
| Unknown attribute | Error or warn (feature-gated unknown attrs for tools) |
| Wrong target | Error |
| Reflect without metadata | Clear runtime/compile error |
| Malformed attr args | Compile error |
6.10 Compatibility & migration
- Adding
@deprecatedis non-breaking. - Changing retention of a public attribute type may be breaking for consumers.
- Metadata format versioned in binary.
7. Open questions
| # | Question | Options | Owner | Status |
|---|---|---|---|---|
| 1 | Unknown attributes hard error? | error / warn | Lang | Resolved — hard error on unknown attributes |
| 2 | Reflect private members? | no by default | Lang | Resolved |
| 3 | Builtin @reflect vs retention on class | @reflect opt-in attribute | Lang | Resolved (direction) |
8. Rationale & trade-offs
Pay-as-you-go metadata protects single-binary size and optimization. Attributes unify derives, tests, and future serializers. Limited reflection avoids building a second dynamic language on top of Aura. Cost: less “magic” than Java enterprise stacks—acceptable for core scope.
9. Unresolved / future work
- Runtime invocation of reflected methods remains outside v1; type identity, opt-in type kinds, and public member metadata are compiler-backed
- Annotation processors beyond macros
- Source generators pipeline
10. Security & safety considerations
- Reflection must not silently pierce
privateacross trust boundaries. - Runtime metadata can leak class structure—document for security-sensitive apps; provide strip flags.
- Untrusted attributes from dependencies run only as compile-time macros under sandbox (RFC-010), not arbitrary runtime code.
11. Implementation plan (optional)
| Phase | Scope | Exit criteria |
|---|---|---|
| A0 | Parse + store attributes | Round-trip |
| A1 | @test discovery | aura test |
| A2 | Opt-in runtime Type | Demo inspect |
11.1 Implemented compiler boundary
The current compiler implements the normalized attribute metadata boundary.
CheckedFile.attribute_metadata carries declaration, target, name, normalized
arguments, retention, and source span. Built-in and registered host
macro/derive expansion is recorded in CheckedFile.expansions with its phase,
invocation span, and generated span.
Generated C exposes Binary/Runtime entries through a versioned read-only table:
AURA_METADATA_ABI_VERSION is 1, with accessors
aura_generated_attribute_metadata and aura_generated_expansion_metadata.
Source-retained attributes remain compiler/tooling metadata and are deliberately
not copied into the binary table.
12. References
Changelog
| Date | Author | Change |
|---|---|---|
| 2026-07-16 | Lock unknown attributes = error; Status → Accepted | |
| 2026-07-16 | Status → In Review — Review: retention + opt-in reflect locked; unknown-attr still open | |
| 2026-07-15 | Initial skeleton | |
| 2026-07-15 | Solid draft: retention, opt-in reflect | |
| 2026-07-15 | Lock Binary default retention; public-only reflect | |
| 2026-07-31 | Implement typed retention/expansion metadata and versioned generated C table |