Skip to content

Revise #[use_type] and expand the error catalog#255

Merged
soareschen merged 7 commits into
mainfrom
ai-updates-6
Jul 6, 2026
Merged

Revise #[use_type] and expand the error catalog#255
soareschen merged 7 commits into
mainfrom
ai-updates-6

Conversation

@soareschen

Copy link
Copy Markdown
Collaborator

AI Overview

This PR reworks the #[use_type] attribute — CGP's tool for importing an abstract associated type under a bare alias — and builds out the documentation around post-codegen errors and the attribute internals. The code changes are concentrated in one macro (cgp-macro-core), and the bulk of the diff is tests and docs that make the new behavior correct, covered, and explained. In numbers, the change touches 100 files with roughly 1,500 lines added under crates/ (almost all of it tests) and 1,200 under docs/, against a much smaller net change to the macro logic itself. This tree is the pre-release of v0.8.0, so the syntax change lands before any stable release depends on it.

What changed, in concept

The headline change is a new syntax for importing an abstract type from a foreign context. Previously a #[use_type] spec named a non-Self rewrite target with a leading @ prefix — #[use_type(@Types.HasScalarType.Scalar)] meant "rewrite Scalar to <Types as HasScalarType>::Scalar." The prefix now moves to a trailing in Context clause, so the same import reads #[use_type(HasScalarType.Scalar in Types)]. The motivation is readability and consistency: in is a reserved Rust keyword, so it can never be mistaken for a trait, type, or associated-type name and needs no sigil to disambiguate, and it matches the in already used elsewhere in CGP wiring such as #[prefix(@path in Namespace)]. The clause scopes over the whole spec, so a braced group like #[use_type(HasFooType.{Foo, Bar} in Ctx)] projects every imported type in the group against Ctx.

Alongside the syntax, the foreign-import semantics became more ergonomic. A foreign in Context import now adds the required Context: Trait bound to the generated trait's own where clause, not only to the impl. Before this change, a component that imported a type from one of its own generic parameters had to declare that parameter's bound by hand — pub trait CanCalculateArea<Types: HasScalarType> — because the macro rewrote the signatures to name <Types as HasScalarType>::Scalar without ensuring Types: HasScalarType held. The macro now supplies that bound itself, so writing the plain, unbounded pub trait CanCalculateArea<Types> is enough. This closes a real soundness gap as much as a convenience one: the old behavior silently dropped the constraint, leaving a signature that compiled only when the bound happened to be supplied elsewhere. The type-equality (= T) pin remains impl-side and is deliberately never added to the trait.

The revision also adds a genuinely new capability: nested foreign imports that chain through one another. One import's in Context may now name another import's alias, as in #[use_type(HasTypes.Types, HasScalarType.Scalar in Types)], where Types is itself imported rather than a real generic parameter. The macro resolves these to a fixpoint, so the order in which the imports are written does not matter — #[use_type(HasC.C in B, HasB.B in A, HasA.A)] grounds identically to its front-to-back rewrite. The one arrangement with no valid order is a cycle, and the macro stops rather than looping on one, letting the compiler report an ordinary "cannot find type" error at the offending alias instead of hanging.

Structural changes

The macro internals were refactored to support the above rather than merely patched. #[use_type] transformation grew from a two-phase pass (substitute, then add bounds) into a three-step one: a new grounding step resolves every spec's context against all the other specs and iterates to a fixpoint, then a single substitution pass rewrites the bare aliases, then the bounds are appended. The substitution visitor was generalized to match: the per-spec SubstituteAbstractType became SubstituteAbstractTypes, which holds every grounded spec at once and rewrites all imports in one traversal, carrying an is_changed flag the grounding fixpoint reads to know when a further pass would be a no-op. This let the old cross-spec alias resolver (find_type_alias in type_predicates.rs) be removed entirely, since grounding now does that work up front and the predicate derivation reads the already-grounded context directly.

The implementation docs were reorganized to match the code's structure. The single monolithic asts/attributes.md was split into an asts/attributes/ directory with one page per modifier — #[uses], #[use_type], #[use_provider], #[extend], #[extend_where], #[derive_delegate], and #[default_impl] — plus a README that covers what the modifiers share, namely how a host macro collects them and which host accepts which. Links from the reference and implementation READMEs were repointed to the new layout.

The errors catalog saw the largest documentation expansion. It grew from three class subdirectories to five, and gained a supporting reference. A new lowering/ class directory covers failures that arise in what a macro lowered the input into rather than in wiring — an ill-formed generated (unsized) type, and an unresolved imported abstract type from a misspelled #[use_type]. A new error_codes/ directory is a forward reference of the nine rustc codes the catalog surfaces (E0117, E0119, E0207, E0210, E0275, E0277, E0428, E0576, E0599), each stating what the code means in plain Rust and the rule behind it, so the class documents can cite one verified entry instead of restating the language rule. The wiring section gained two namespace-specific coherence documents (overlapping forwarding, and override conflict) that had previously been folded into the generic conflict doc, and the checks section gained a document for an unsatisfied ordinary trait bound. The debugging guide gained a new section on grep-based error triage — classifying a failure from its ^error headlines and confirming the cause with one class-specific pattern before reading the whole cascade.

Test coverage was extended broadly. The cgp-tests suite gained behavioral tests for the new and revised #[use_type] forms — foreign imports, nested and reverse-order imports, imports feeding a getter, and imports combined with a supertrait — and the cgp-macro-tests suite gained trybuild compile-fail fixtures (with pinned .stderr) for the new failure classes: cyclic and unresolved foreign imports, unsatisfied ordinary bounds surfaced through a check, foreign-component #[default_impl] orphan violations, and namespace override and forwarding conflicts.

Impacts

The changes ripple out in a handful of distinct ways, listed here because each affects a different reader or workflow.

  • Breaking syntax change (in-tree only). Every existing #[use_type(@Context.Trait.Type)] must be rewritten to #[use_type(Trait.Type in Context)]; the @-prefix form no longer parses. Because the old foreign-import form existed only in this pre-release tree and not in the stable v0.7.0 line, the migration is confined to this repository's own sources, which are updated in the PR.
  • Changed generated output for foreign imports. A component or function that imports a type via in Context now emits an extra Context: Trait predicate on the generated trait's where clause. This is why the hand-written parameter bound can be dropped, but it also means anyone relying on the previous (unsound) expansion — a signature that compiled without the bound — will now see the bound enforced.
  • New expressive power. Nested, chained, and out-of-order foreign imports are now legal and order-independent, and a cyclic import fails with a clear compiler error rather than a macro hang.
  • Macro-internals churn. The rename SubstituteAbstractTypeSubstituteAbstractTypes, the new grounded_specs step, and the removal of find_type_alias change the internal API surface that the implementation docs describe; anyone reading or extending the use_type transform meets a different, single-pass shape.
  • Documentation link changes. The split of asts/attributes.md into asts/attributes/ moves several stable anchor targets, so external links into the old single page will break and were repointed within the tree.
  • Richer debugging surface. The catalog now documents failure classes it did not before (ordinary trait bounds, the two namespace coherence collisions, and the lowering errors), adds a plain-Rust reference for every surfaced error code, and gives the debugging guide a grep-first triage workflow — together making a wider range of CGP compile errors diagnosable from the docs alone.
  • Skill updates for agents. The bundled /cgp skill references were updated to the new syntax and behavior, so an agent following the skill now writes in Context imports and unbounded component parameters rather than the retired forms.
  • No public-facing API break beyond the attribute. The change is entirely within the macro layer and its documentation; the facade crates, core traits, and prelude are untouched, so a downstream user who does not write foreign #[use_type] imports sees no difference.

@soareschen soareschen merged commit 2c558fb into main Jul 6, 2026
3 checks passed
@soareschen soareschen deleted the ai-updates-6 branch July 6, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant