← Catalog

RFC-009AcceptedLanguage

Reflection & Metadata

Depends: RFC-001RFC-002

Blocks: RFC-004RFC-010RFC-007

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

MetricTarget
Attribute syntaxStable, documented
Binary bloatNo metadata for types without retention
Test discoveryWorks via attributes
Safe reflectionVisibility 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 invoke on 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

SystemNotesTake
Java annotations + reflectionPowerful, heavyOpt-in retention
C# attributes + reflectionSimilarInspiration
Rust attributes + limited reflectPay-as-you-goCloser spirit
Go tags + reflectStruct tagsContrast

6. Design

6.1 Overview

code
@attr(...)  on decl
    → compiler front records Attribute
    → expand derives ([RFC-010](/rfc/010))
    → emit metadata per retention
    → optional runtime APIs in std.reflect

6.2 Attribute syntax

aura
@test
@derive(Equals, Hash)
@deprecated("use NewApi", since = "0.2")
@json(name = "user_id")
fun foo(@notNull x: String) { }
  • Attributes are @Name or @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

aura
@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

LevelKept inUse
SourceCompiler session onlyLints, local tools
BinaryPackage metadata / rlib side tableLink-time, tools
RuntimeEmbedded in binarystd.reflect

Default for user attrs: Binary (available to tools/dependents without runtime bloat). Runtime requires explicit opt-in.

6.5 Built-in attributes (non-exhaustive)

AttributeRole
@test, @benchTest discovery (RFC-011)
@derive(...)Macro derives (RFC-010)
@deprecatedWarnings
@inline, @noinline, @coldOptimization hints
@throwsDoc/tooling only (unchecked exceptions)
@unsafeMarks unsafe APIs
@repr(...)Layout hints for structs/FFI
@retention / meta-attributesOn attribute types

6.6 Runtime reflection API (sketch)

aura
package std.reflect

class Type {
  fun name(): String
  fun methods(): Array<Method>
  fun fields(): Array<Field>
  // ...
}

fun typeOf<T>(): Type
fun typeIdOf<T>(): TypeId

Constraints:

  • Only types with runtime metadata; others → error or limited info.
  • Visibility: public members only by default; non-pub requires 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

aura
@derive(Debug)
@reflect  // opt into runtime metadata
class User(
  @json(name = "id") val id: Long,
  val email: String,
)

6.9 Error model / edge cases

CaseBehavior
Unknown attributeError or warn (feature-gated unknown attrs for tools)
Wrong targetError
Reflect without metadataClear runtime/compile error
Malformed attr argsCompile error

6.10 Compatibility & migration

  • Adding @deprecated is non-breaking.
  • Changing retention of a public attribute type may be breaking for consumers.
  • Metadata format versioned in binary.

7. Open questions

#QuestionOptionsOwnerStatus
1Unknown attributes hard error?error / warnLangResolved — hard error on unknown attributes
2Reflect private members?no by defaultLangResolved
3Builtin @reflect vs retention on class@reflect opt-in attributeLangResolved (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

  • Full std.reflect surface
  • Annotation processors beyond macros
  • Source generators pipeline

10. Security & safety considerations

  • Reflection must not silently pierce private across 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)

PhaseScopeExit criteria
A0Parse + store attributesRound-trip
A1@test discoveryaura test
A2Opt-in runtime TypeDemo inspect

12. References


Changelog

DateAuthorChange
2026-07-16Lock unknown attributes = error; Status → Accepted
2026-07-16Status → In Review — Review: retention + opt-in reflect locked; unknown-attr still open
2026-07-15Initial skeleton
2026-07-15Solid draft: retention, opt-in reflect
2026-07-15Lock Binary default retention; public-only reflect