Language Subsetting
Sage intentionally supports a subset of Rust. This is a pragmatic choice — each restriction eliminates significant implementation complexity while affecting little real-world code. Restrictions are documented here with rationale and may be lifted as sage matures.
crates.sage_ir.crate.explicit_restrictions
Verification scenarios: Every listed restriction has focused acceptance and rejection fixtures at the phase which encounters it, and the observable diagnostic, incomplete result, or documented approximation agrees with the stated impact.
crates.sage_ir.crate.restrictions_and_gaps
Verification scenarios: Documentation review traces every unsupported fixture either to a deliberate restriction here or to a capability gap and roadmap owner, and rejects restrictions justified only by the implementation not having been written yet.
Restrictions
No proc-macro crates defined in the workspace
What: Sage does not support workspace crates with proc-macro = true in
their [lib] target. Proc-macro crates from external dependencies (e.g.,
serde_derive, tokio_macros, clap_derive) can be loaded by the rustc bridge,
but their name resolution and output are not yet fully integrated into Sage's
module expansion pipeline.
Why: A workspace-defined proc-macro requires compiling it to a host-side dylib before it can be expanded. This means sage would need to invoke rustc (or itself) to produce a working dylib, manage host vs target compilation, and handle the case where the proc-macro crate is being edited (invalidating the dylib). This is a significant amount of machinery for a feature that most application-level workspaces don't use — proc-macros are typically published as separate crates.
Impact: Low. Application workspaces (web services, CLI tools, libraries) almost never define proc-macros inline. Projects that do (e.g., a proc-macro crate + its consumer in one workspace) can still use sage for the consumer crate — just not for the proc-macro crate itself.
No glob imports targeting inline modules
What: use some_inline_module::* (where the target is mod foo { ... },
not mod foo;) is not yet supported. Glob imports from file-based workspace
modules and from external dependencies work fine. Inline modules can still
be navigated as path targets (mod foo { ... }; foo::Bar resolves), and the
items inside an inline module participate in normal name resolution; only
glob-importing into a parent's scope from an inline module is broken.
Why: Glob target resolution feeds back into the construction-time path walker. The walker takes a fast items-based path that doesn't traverse inline-module bodies; until it does, globs that resolve through an inline intermediate produce no entries.
Impact: Low. Inline modules with glob imports are uncommon in application code, and the cross-module glob case (the primary fixpoint trigger) goes through file-based modules in practice.
No #[path = "..."] on modules
What: #[path = "custom.rs"] mod foo; is not supported. Module file
resolution assumes conventional paths (foo.rs or foo/mod.rs).
Why: Supporting #[path] requires parsing attributes on mod items during
module resolution, before the module's contents are known. The current
resolve_mod function only looks at the module name.
Impact: Very low. #[path] is rare in practice — almost all Rust code uses
conventional module paths.
No derive helper attributes
What: Derive helper attributes introduced by proc-macro derives are not
resolved. For example, #[derive(Serialize)] introduces #[serde(...)]
helper attributes — sage does not recognize these.
Why: Derive helper attributes require knowing which attributes a proc-macro derive registers, which means reading the proc-macro's registration metadata. This is a separate mechanism from derive expansion itself.
Impact: Low for type checking — helper attributes affect the derive expansion output but don't change the type structure. Sage will report unknown attribute warnings on helper attributes.
macro_rules! scoping is module-scoped
What: macro_rules! definitions are visible throughout their containing
module, not just after the definition point. In real Rust, macro_rules! uses
textual scoping — a macro is only visible to code that appears after it in the
source file.
Why: Textual scoping requires tracking source position during name resolution, which adds complexity to the resolution algorithm. Module-scoped visibility is simpler and correct for the vast majority of code.
Impact: Very low. Code that depends on textual scoping (e.g., defining a macro between two items where only the second should see it) is extremely rare in practice.
Lifetime semantics and borrow checking are deferred
What: Sage preserves lifetime syntax in the CST but maps every lifetime to
the dedicated checked value Lifetime::Dummy. All lifetime relations hold
trivially, and Sage does not perform borrow checking.
Why: The intended lifetime and type inference design is more unified than
rustc's separate region subsystem. Introducing a temporary region solver would
prematurely constrain that design. Dummy keeps reference structure available
to ordinary type elaboration without pretending to be 'static or a solved
region.
Impact: Sage currently accepts some programs which rustc rejects for lifetime or borrow errors. This is a documented temporary soundness hole, not an ambiguous type-checking result.
Outside that explicit lifetime exception, unsupported or unrepresented source is governed by Incompleteness is an explicit terminal outcome.
crates.sage_ir.crate.represented_completeness
Verification scenarios: Malformed, unresolved, unsupported-attribute, and resource-limited fixtures retain usable represented symbols while each potentially affected resolution or solver-negative query remains explicitly incomplete or ambiguous.
Supported features
The destination language includes the following. This list is not a statement that every path is implemented today; current implementation limitations are recorded below and in the build-out roadmap.
- async/await
- Trait definitions and implementations
- Generics, lifetime syntax, where clauses (with lifetime semantics deferred)
- Pattern matching
- Closures and
impl Fn/dyn Fn - Derive macros (external integration in progress)
- Proc-macro attributes, e.g.
#[tokio::test](external integration in progress) macro_rules!definitions and invocations within the workspace (module-scoped — see restriction above)- Module tree (
mod,pub use,pub(crate)) - Type aliases, constants, statics
cfgattributes
Implementation and test evidence
The generated anchor pages report implementation entries, annotated tests, explicit TODO/N/A gaps, and saved claim/test reviews. These associations do not themselves establish that a test ran.
The earlier coverage snapshot preserves the former capability descriptions, edit experiments, and limitations as historical evidence. Current coverage comes from the generated entries; implementation plans and decision rationale belong in the relevant RFDs.