Skip to content

Macros ​

Implementation status: M28 + Service DI v19 (SYX 116.0) — see ADR-130 · Architecture Decisions · documentation fingerprint macro-docs-v20:m28-service-v19-honesty

SYX 116.0: Macro M28 + Service DI v19 (GA vs partial) ​

CapabilityTierDescription
Declaration-position #macro multi-token decl rewrite MVPpartial GAA declaration-position Ident+Punct+Group token stream can be rewritten into a rename/wrap function decl (fingerprint macro-token-v9:token-rewrite-mvp); not a complete token-stream / user SYX expansion logic → 139+
@Pointcut("within(*Service)") / within(Foo*) type-name wildcard MVPpartial GA@Pointcut("within(*Service)") suffix / within(Foo*) prefix narrow wildcards (fingerprint macro-service-v19:pointcut-within-wildcard-mvp); not full AspectJ expressions / aspect classes → 139+

SYX 115.0: Macro M27 + Service DI v18 (GA vs partial) ​

CapabilityTierDescription
Declaration-position #macro decl-expand MVPpartial GAA single declaration-position Ident token can expand into a rename/wrap function decl (fingerprint macro-token-v8:decl-expand-mvp); not a complete token-stream / user SYX expansion logic → 138+
@Pointcut("!within(Type)") negation MVPpartial GA@Pointcut("!within(Foo)") narrow negated type-name pattern (fingerprint macro-service-v18:pointcut-not-within-mvp); not full AspectJ expressions / aspect classes → 138+

SYX 114.0: Macro M26 + Service DI v17 (GA vs partial) ​

CapabilityTierDescription
Freestanding builtin (#assert #if #for #stringify #compileTime)GAsyx-macro builtin pass; role misplacement → E003 (inherited from M9)
User #macro + Hygiene v2partialStatement-position text substitution; expression/declaration-position parameter substitution + declaration-position hygiene MVP below
Token expression-position #macro MVPpartial GAExpression-position #macro ident/literal parameter substitution; Phase 134 deepened the expression tree (fingerprint macro-token-v5:expr-token-subst-mvp); Phase 130 initial version (macro-token-v1:expr-token-mvp); not a complete token syntax macro
Declaration-position #macro token-stream enumeration MVPpartial GADeclaration-position #macro parameters can be enumerated as an Ident / Punct / Group tri-state token list (fingerprint macro-token-v7:token-stream-mvp); not a complete token-stream / user SYX expansion logic → 137+
Declaration-position #macro token subst deepening MVPpartial GADeepened ident/literal substitution of declaration-position freestanding #macro function names / parameter defaults / enum payloads (fingerprint macro-token-v6:decl-token-subst-mvp); Phase 131 initial version (macro-token-v2:decl-param-subst-mvp); not a complete token syntax macro
Declaration-position #macro parameter substitution MVPpartial GADeclaration-position freestanding #macro parameter text substitution (fingerprint macro-token-v2:decl-param-subst-mvp); not a complete token syntax macro
Declaration-position #macro hygiene + linked/run_file closeout MVPpartial GADeclaration-position hygiene (macro-token-v3:decl-hygiene-mvp); compile_linked / run_file_printed are reproducible; not a complete token syntax macro
Statement-position #macro token subst MVPpartial GAStatement-position freestanding #macro ident/literal parameter text substitution (fingerprint macro-token-v4:stmt-token-subst-mvp); not a complete token syntax macro / token-stream API
@attached member/peer/accessor/extension/deinitpartialReal AST parse_fragment; not the full Swift role surface
@Equatable / @Hashable / @Codable L1GAstored data class
@Codable L2 associated-value enum fully automaticpartial GAAssociated-value enum encode/decode round-tripping (fingerprint macro-codable-l2:associated-enum-auto); not the full set of generic associated values / arbitrary nesting depth / a custom coding keys DSL
#externalMacro plugin-prodpartialcdylib JSON production path; Linux seccomp hard gate / Darwin reexec-only honest label
@resultBuilderGASixth pass of the engine fixed-point pipeline; literal+for-in mixed with HostCall+Closure compiles and runs (macro-pipeline-v6:rb-hostcall-closure-fixed)
Macro package [macros].exportsGAPhase 120 schema aligned with SyxManifest
@Service / @Component + @Inject ctor + field @AutowiredGAMulti-package workspace scanning (only resolved_packages) + real ctor resolve; singleton by default (inherited from M14)
@Scope("prototype") / @Scope("request")GAprototype creates a new instance each time; request is a singleton within a frame (inherited from M13)
@Qualifier / @PrimaryGADisambiguating multiple bindings (fingerprint macro-service-v5:conditional-qualifier)
@ProfileGAConditional registration; Di.setProfile / SYX_DI_PROFILE (fingerprint macro-service-v5:conditional-profile)
@PostConstruct / @PreDestroy hook MVPGAAfter construction + field injection / at container destroy·reset·request end
Typed resolve: "Type" / Type identGAA string literal or a registered type identifier; a dynamic variable → E010
DI ceremony sugar + Di package GAGAimport DI → resolve / withRequest / setProfile / …; short sugar resolve(...) (inherited from M14)
Base-type polymorphic injectionGAConstructor/field @Inject resolves the concrete implementation by base/parent type; registry binds a superclass alias (fingerprint macro-service-v6:polymorphic-base-inject); must not rely on init side effects to fake dispatch
Protocol-typed injection / witness tableGAconformance witness registers the protocol ← @Service implementation (fingerprint runtime-service-v7:protocol-witness); M15 alias binding inherited
Field @Qualifier metadataGA@Autowired @Qualifier("…") preserves disambiguating metadata across packages (fingerprint macro-service-v6:field-qualifier-metadata)
@Around advice proceed + @Before/@Afterpartial GAOnly instance methods of @Service/@Component; ordered multi-@Around proceed delegation (__syx_proceed_N) + Before/After; may bind to @Pointcut / execution name patterns (fingerprint macro-service-v8:advice-proceed); not AspectJ / aspect classes
@Pointcut method name MVPpartial GA@Pointcut("Type.method") registers a narrow string pattern; @Around("…") binds the proceed chain (fingerprint macro-service-v11:pointcut-name-mvp); not AspectJ expressions / aspect classes / wildcard join points
execution(* T.m(..)) method-name pattern MVPpartial GA@Pointcut("execution(* Type.method(..))") / execution(* Type.*(..)) name/* patterns (fingerprint macro-service-v12:pointcut-execution-name-mvp); not complete AspectJ
@Before/@After pointcut string binding MVPpartial GA@Before("…") / @After("…") bind a registered @Pointcut / execution name pattern (fingerprint macro-service-v13:before-after-pointcut-bind-mvp); bare Before/After still self-wrap in declaration order; not complete AspectJ
@Pointcut("within(Type)") type-name pattern MVPpartial GA@Pointcut("within(Foo)") registers a narrow type-name pattern; @Around/@Before/@After may bind any method on that type (fingerprint macro-service-v14:pointcut-within-name-mvp); not AspectJ expressions / aspect classes
@Pointcut("args(Type)") first-parameter type-name pattern MVPpartial GA@Pointcut("args(Int)") registers a narrow first-parameter type-name pattern; @Around/@Before/@After may bind methods whose first parameter type matches (fingerprint macro-service-v15:pointcut-args-name-mvp); not full AspectJ expressions / aspect classes
@Pointcut("within(T) && args(U)") narrow composition MVPpartial GA@Pointcut("within(Foo) && args(Int)") only within + args name patterns composed with &&; it matches only when both hit (fingerprint macro-service-v16:pointcut-and-compose-mvp); not negation / full AspectJ expressions / aspect classes
@Pointcut("within(T) || args(U)") narrow composition MVPpartial GA@Pointcut("within(Foo) || args(Int)") only within + args name patterns composed with ||; it matches when either side hits (fingerprint macro-service-v17:pointcut-or-compose-mvp); not negation / full AspectJ expressions / aspect classes / Spring → 137+
@Pointcut("!within(Type)") negation MVPpartial GA@Pointcut("!within(Foo)") narrow negated type-name pattern (fingerprint macro-service-v18:pointcut-not-within-mvp); not full AspectJ expressions / aspect classes
Declaration-position #macro decl-expand MVPpartial GAA single declaration-position Ident token can expand into a rename/wrap function decl (fingerprint macro-token-v8:decl-expand-mvp); not a complete token-stream / user SYX expansion → 138+
@Configuration / @Bean MVPpartial GAConfiguration-class factory methods register with DI; Di.resolve can resolve them (fingerprint macro-service-v8:configuration-bean-mvp)
@Lazy + cyclic-dependency lazy proxy MVPpartial GADefers the factory until the first resolve/use; a cycle containing a @Lazy edge gets a lazy proxy inserted; a cycle without @Lazy is still E007 (fingerprint macro-service-v9:lazy-cycle-proxy-mvp); not arbitrary type proxies / CGLIB / the full set of scoped proxies
@Bean parameter @Autowired / type resolution MVPpartial GA@Bean method parameters inside a @Configuration are injected by type from the registered @Service/@Bean graph (fingerprint macro-service-v10:bean-param-autowire-mvp); not the full set of conditional assembly / CGLIB / the full set of scoped proxies
Complete token syntax macros / complete AspectJ / complete SpringNot implemented137+

Tests: crates/syx-driver/tests/fixtures/macros/ · examples/service_di/ · examples/stmt_token_macro/ · examples/decl_param_macro/ · examples/token_expr_macro/ · within_wildcard_pointcut.sy · not_within_pointcut.sy · or_pointcut.sy · and_pointcut.sy · decl_token.sy · expr_token.sy · within_pointcut.sy · stmt_token.sy · macro_di_within_wildcard.rs · macro_di_not_within.rs · macro_di_or_pointcut.rs · service_di_and_pointcut.rs · token_decl_deepen.rs · task_queue_caller_runs.rs · bounded_task_queue.rs

SYX macros come in two kinds: freestanding (#) and attached (@). Driven by engine::expand_to_fixed_point (builtin → attached → wrapper → user → external → resultBuilder → service DI).

@Service / @Component / @Inject / @Autowired (Phase 136 · Service DI v17) ​

A Spring-style demonstrable subset: singleton (default) + prototype + request scope + Qualifier/Primary/Profile + base-type polymorphic injection + protocol witness table + field @Qualifier metadata + @Around proceed delegation / @Before/@After (including string pointcut binding) + @Pointcut("Type.method") method-name MVP + execution(* T.m(..)) name-pattern MVP + @Pointcut("within(Type)") type-name-pattern MVP + @Pointcut("args(Type)") first-parameter type-name-pattern MVP + @Pointcut("within(T) && args(U)") / within(T) || args(U) narrow composition MVP + @Configuration/@Bean MVP + @Bean parameter injection MVP + @Lazy + cyclic-dependency lazy proxy MVP + constructor/field injection + multi-package workspace scanning + Di.withRequest. Do not claim Spring compatibility / complete AspectJ AOP / complete token syntax macros / fully automatic wiring.

swift
import DI

@Qualifier("fast")
@Service
class FastLogger {
  init() {}
  func tag() -> String { return "fast" }
}

@Primary
@Service
class SlowLogger {
  init() {}
  func tag() -> String { return "slow" }
}

@Service
class Worker {
  let logger: FastLogger
  init(@Inject @Qualifier("fast") logger: FastLogger) {
    self.logger = logger
  }
}

@Profile("prod")
@Service
class ProdOnly {
  init() {}
}

@Scope("request")
@Service
class RequestCtx {
  init() {}
}

func handleReq() {
  let _ctx = Di.resolve("RequestCtx")
}

func main() {
  Di.setProfile("prod")
  let w = Di.resolve("Worker") as Worker
  print(w.logger.tag())
  Di.withRequest("handleReq")
  Di.destroy()
}
  • @Service / @Component — attached attributes marking a registrable type; used on a non-type → E006
  • Multi-package workspace scanning — the driver aggregates @Services inside resolved_packages; skips sibling nested syx.toml directories
  • @Scope("prototype") — creates a new instance on each resolve
  • @Scope("request") — the same instance within a request frame, a new instance across frames; prefer Di.withRequest(handler), or Di.beginRequest / Di.endRequest
  • @Qualifier("name") / @Primary — disambiguates multiple implementations; ambiguity without Primary/Qualifier → fail-closed
  • @Profile("name") — registered only when Di.setProfile / SYX_DI_PROFILE matches
  • @Inject — constructor parameter injection; a missing dependency fails closed (E008); a dependency cycle without @Lazy is still E007
  • Field @Autowired MVP — stored property; assigned on the instance after resolve (no full reflection set)
  • @PostConstruct / @PreDestroy MVP — lifecycle hooks
  • User-facing container — import DI: Di.resolve / Di.withRequest / Di.setProfile / Di.destroy; short sugar resolve(...); application code must not call __syx_service_* directly
  • Base-type polymorphic injection — @Inject logger: Logger resolves to the concrete implementation when FastLogger: Logger is registered (registry superclass alias); init side effects no longer stand in for dispatch
  • Field @Qualifier — @Autowired @Qualifier("fast") let logger: Logger preserves disambiguating metadata across packages
  • @Around proceed — only instance methods of @Service/@Component; expands into ordered proceed delegation (__syx_proceed_N) wrapping the original method body + print("__around__:method"); the return value is observable; may stack @Around("Type.method") bound to a registered @Pointcut
  • @Pointcut method-name MVP — @Pointcut("Type.method") registers a narrow string pattern; @Around("…") must match a registered pointcut, otherwise fail-closed (fingerprint macro-service-v11:pointcut-name-mvp); not AspectJ expressions / aspect classes / wildcard join points
  • Token expression-position #macro MVP — expression-position #double(21) / #apply(f,x) and other ident/literal parameter substitutions; Phase 134 deepened the expression-tree walk (fingerprint macro-token-v5:expr-token-subst-mvp); Phase 130 initial version (macro-token-v1:expr-token-mvp); not a complete token syntax macro / token-stream API
  • Declaration-position #macro token subst deepening MVP — declaration-position function names / parameter defaults / enum payloads and other ident/literal substitutions (fingerprint macro-token-v6:decl-token-subst-mvp); Phase 131 initial version (macro-token-v2:decl-param-subst-mvp); not a complete token syntax macro / token-stream API
  • Declaration-position #macro token-stream enumeration MVP — declaration-position parameters can be enumerated as Ident / Punct / Group (fingerprint macro-token-v7:token-stream-mvp); not a complete token-stream / user SYX expansion → 137+
  • Statement-position #macro token subst MVP — statement-position #say("x") and other ident/literal parameter text substitutions (fingerprint macro-token-v4:stmt-token-subst-mvp); not a complete token syntax macro / token-stream API
  • @Pointcut("within(Type)") type-name MVP — @Pointcut("within(Foo)") registers a narrow type-name pattern; @Around/@Before/@After may bind any method on that type (fingerprint macro-service-v14:pointcut-within-name-mvp); not AspectJ expressions / aspect classes
  • @Pointcut("args(Type)") first-parameter type-name MVP — @Pointcut("args(Int)") registers a narrow first-parameter type-name pattern; @Around/@Before/@After may bind methods whose first parameter type matches (fingerprint macro-service-v15:pointcut-args-name-mvp); not full AspectJ expressions / aspect classes
  • @Pointcut("within(T) && args(U)") narrow composition MVP — @Pointcut("within(Foo) && args(Int)") only within + args name patterns composed with &&; it matches only when both hit (fingerprint macro-service-v16:pointcut-and-compose-mvp); not negation / full AspectJ expressions / aspect classes
  • @Pointcut("within(T) || args(U)") narrow composition MVP — @Pointcut("within(Foo) || args(Int)") only within + args name patterns composed with ||; it matches when either side hits (fingerprint macro-service-v17:pointcut-or-compose-mvp); not negation / full AspectJ expressions / aspect classes / Spring → 137+
  • @Configuration / @Bean MVP — factory methods inside a configuration class register bindings; Di.resolve resolves the return type
  • @Bean parameter injection MVP — @Bean method parameters are injected by type / @Autowired (optionally @Qualifier) from the registered @Service/@Bean graph (fingerprint macro-service-v10:bean-param-autowire-mvp); not the full set of conditional assembly
  • @Lazy + cyclic-dependency lazy proxy — defers the factory until the first Di.resolve/use; an A↔B cycle containing a @Lazy edge gets a lazy proxy handle inserted (materialized on the first method call); not arbitrary type proxies / CGLIB / the full set of scoped proxies
  • 137+ debt — complete token syntax macros / token-stream API, complete AspectJ-style AOP (negation / aspect classes / wildcard join points / the full expression and precedence set), a complete Spring container, the full ThreadPoolExecutor set (DiscardOldest / Actor shared pool, …)
swift
import DI

@Service
class DepLogger {
  init() {}
  func tag() -> String { return "dep-logger" }
}

class BeanGreeter {
  var logger: DepLogger
  init(logger: DepLogger) { self.logger = logger }
}

@Service
class Audited {
  init() {}
  @Pointcut("Audited.work")
  @Around("Audited.work")
  func work() -> Int {
    return 42
  }
}

@Configuration
class AppConfig {
  init() {}
  @Bean
  func greeter(@Autowired logger: DepLogger) -> BeanGreeter {
    return BeanGreeter(logger: logger)
  }
}

@Lazy
@Service
class Expensive {
  init() { print("factory") }
}

@Service
class CycleA {
  let b: CycleB
  init(@Inject b: CycleB) { self.b = b }
}

@Lazy
@Service
class CycleB {
  let a: CycleA
  init(@Inject a: CycleA) { self.a = a }
}

Tests: platform/service_di_and_pointcut.rs · fixtures/macros/ (token_expr.sy · pointcut_around.sy, …) · examples/service_di/ · examples/token_expr_macro/ · packages/di/mod.sy

@freestanding Role Enforcement (Phase 119 · inherited from M9) ​

expression / statement / declaration attachment-point misplacement → compile error E003 (driver + LSP E2E).

Test: fixtures/lsp/freestanding_decl_role_a.sy

Attached wrapper + Hygiene DX ​

swift
@attached(wrapper)
class Clamped {
  var wrappedValue: Int
  init(wrappedValue: Int) { self.wrappedValue = wrappedValue }
}

class Point {
  @Clamped var x: Int
}
  • @propertyWrapper is deprecated (W001); migrate to @attached(wrapper)
  • Hygiene escape: \name inside a macro body preserves the outer binding (SYX_MACRO_HYGIENE=0 turns it off globally for one build)
  • Import macros: register_imported_macros registers User + External

Tests: fixtures/macros/attached_wrapper.sy, fixtures/macros/hygiene_escape_ok.sy, fixtures/macros/

External Production Path (Phase 119 plugin-prod) ​

swift
macro ExternalSmoke = #externalMacro(module: "SyxMacros", type: "SmokeMemberMacro")

func main() {
    #ExternalSmoke()   // freestanding statement expansion
}
  • Cross-module import: a dependency crate's MacroDecl is registered through register_imported_external_macros
  • WASM: with SYX_TARGET_WASM=1, external macros raise compile-time E004
  • Production: cdylib JSON protocol with real types; the subprocess sandbox is on by default; without seccomp you must not claim hard-fail GA (Linux has a hard gate when available, Darwin is honestly sandbox-reexec-only)

Tests: fixtures/lsp/freestanding_decl.sy, fixtures/macros/json_plugin_prod.sy, fixtures/macros/json_plugin_prod.sy

Freestanding Builtin Macros ​

MacroDescription
#assert(expr)Terminates when the assertion fails
#if conditionConditional compilation
#assert_eq / #assert_neEquality assertions
#while / #forLoop unrolling
#warning / #errorW001 / E001
#compileTimeConstant-evaluation subset (M9 hardened boundary)
#stringify(expr)Expression position → string literal (M9 builtin pass)
swift
func main() {
  #assert(1 + 1 == 2)
  #for(3, print("x"))
  let s = #stringify(1 + 1)   // "1 + 1"
  print(s)
}

Tests: fixtures/macros/macro_engine.sy, fixtures/macros/macro_engine.sy, fixtures/lsp/freestanding_decl.sy

User #macro (Phase 40+, hygiene v2 Phase 50) ​

swift
#macro twice(x) {
    print(x)
    print(x)
}

func main() {
    #twice("hello")
}
  • Freestanding only (statement position); parameters are identifier/expression text substitution
  • Expansion depth ≤16; cyclic or overly deep expansion → E002
  • Phase 50 hygiene v2: parameter bindings use MacroParamId, avoiding collisions with same-named identifiers at the call site (SYX_MACRO_HYGIENE=0 disables it for one build)

Tests: fixtures/macros/macro_engine.sy, fixtures/macros/hygiene_escape_ok.sy

@propertyWrapper (Phase 40 GA) ​

swift
@propertyWrapper
class Twice {
    var wrappedValue: Int
    init(wrappedValue: Int) { self.wrappedValue = wrappedValue }
}

class User {
    @Twice var count: Int
}

CLI: --no-property-wrapper disables it. Test: fixtures/macros/attached_wrapper.sy

@attached (Phase 50 Real AST Expansion) ​

Since Phase 50, body: is parsed by parse_fragment into real statements/declarations, no longer just generating a print stub.

swift
@attached(member, named, greet, "print(\"member:{name}\")")
data class Greeter(name: String)

@attached(peer, named, _describe)
data class Point(x: Int, y: Int)
  • Roles: member, peer, custom
  • An in-source body: takes precedence over the SYX_ATTACHED_*_BODY environment variables (for tests/templates)

Tests: fixtures/macros/attached_wrapper.sy, fixtures/macros/hygiene_escape_ok.sy, fixtures/macros/attached_conformance_dispatch.sy

Proof Macros (L1 GA · L2 partial, Phase 50–52 / 119) ​

Automatically generate protocol witnesses on a data class:

MacroGenerated contentTier
@Equatablefunc ==(other:) -> BoolL1 GA
@Hashablefunc hash(into:) → __syx_hasher_combineL1 GA
@Codableencode() + static decode(text:) (JSON Host)L1 GA; associated-value enum L2 partial GA (fingerprint macro-codable-l2:associated-enum-auto); generic witness partial
swift
@Codable
data class User(n: Int, name: String)

@Codable
enum WireStatus {
    ok(code: Int)
    err(message: String)
}

func main() {
    let u = User(n: 42, name: "syx")
    let text = u.encode()
    let back = User.decode(text: text)
    let w = WireStatus.ok(code: 7)
    let wj = w.encode()
    let w2 = WireStatus.decode(text: wj)
}

Limits: L1 applies only to a data class with stored properties; L2 associated-value enum full automatic round-tripping is GA (narrow); the full set of generic associated values / arbitrary nesting / custom coding keys → 131+. Tests: fixtures/parity/dataclass_synth.sy, fixtures/parity/dataclass_synth.sy, fixtures/macros/codable_associated_enum.sy, codable_associated_enum.sy

attached(conformance) Honesty (Phase 119) ​

An unknown protocol must not be silently ignored (E00x or an explicit non-GA diagnostic); the Equatable/Hashable/Codable paths remain.

#externalMacro (Phase 51–52 → 119 plugin-prod) ​

Expansion happens in-process or in a plugin process through the JSON protocol MacroExpandRequest / MacroExpandResponse:

swift
macro ExternalSmoke = #externalMacro(module: "SyxMacros", type: "SmokeMemberMacro")

@ExternalSmoke
data class Point(x: Int, y: Int)

A package manifest may declare [macros].exports to register external macros; LSP v44 supports jump-to-definition and expand preview. See ADR-112

Test: fixtures/macros/json_plugin_prod.sy

@resultBuilder (Phase 119 · engine pipeline · Phase 123 HostCall+Closure stack fix) ​

@resultBuilder expands in the sixth pass of the syx-macro engine fixed-point pipeline. It supports multi-stmt / nested if/for, buildBlock / buildEither / buildArray, and buildOptional.

Phase 123: literal + for-in + HostCall inside for-in (such as __syx_str_length) via closure inlining compiles and runs (fingerprint macro-pipeline-v6:rb-hostcall-closure-fixed); illegal patterns still fail-closed.

See Architecture Decisions · Macro System.

@attached Roles (Phase 53+) ​

RoleAttachment pointExample
member / peer / customType@attached(member, named, greet, "...")
accessorProperty (extension method)@attached(extension, named, readValue, "func readValue() ...")
deinitclass@attached(deinit, named, cleanup, "print(...)")
extensionType@attached(extension, named, label, "func label() ...")

JSON protocol role field: member | accessor | deinit | extension.

Not Implemented / Deferred (136+) ​

  • Complete token syntax macros (currently expression/declaration/statement-position ident/literal MVP + Phase 134 expression tree / Phase 135 declaration-position deepening; not a token-stream API / arbitrary syntax rewriting)
  • Complete AspectJ-style AOP / aspect classes / wildcard join points (currently @Pointcut("Type.method") + narrow execution(* T.m(..)) + within(Type) + args(Type) + within && args name-pattern MVP)
  • A complete Spring container
  • The complete commercial OT license table-level attached roles
  • Writing expansion logic in user SYX source
  • @Codable L2 full set of generic associated values / arbitrary nesting enums / a custom coding keys DSL

Differences from Swift ​

Built-in + limited user macros + L1 proof macros + opt-in external macros + M25 Service DI v16 + decl-token deepen / expr-token / stmt-token / decl-hygiene / decl-param MVP + @Before/@After pointcut binding + @Pointcut / execution / within(Type) / args(Type) / within && args name-pattern MVP + @Around proceed + @Bean parameter injection MVP + Codable L2 associated-enum + honest partial annotations — not Swift's complete extensible hygienic macro system.


Next chapter → Type Casting

See also → Language Reference: Attributes · ADR-127

SYX · Apache-2.0