Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
![Rust Stable](https://img.shields.io/badge/rustc-stable-blue.svg)
![Rust 1.89+](https://img.shields.io/badge/rustc-1.89+-blue.svg)

**A Rust language extension for reusable, swappable trait implementations — checked at compile time, with zero runtime cost.**
**A language extension for Rust, with pluggable trait implementations at compile-time.**

Context-generic programming (CGP) is a library, built on stable Rust, that lets one interface have many interchangeable implementations and lets each *context* — an application, a test, a deployment — choose which one it uses. The choice is written in one readable place and resolved entirely during compilation, so it compiles down to direct calls: there is no runtime container, no reflection, and nothing left in the binary for an implementation a context does not use.
Context-Generic Programming (CGP) is a library on stable Rust that lifts the language's one-implementation-per-type limit off your traits: it lets one interface have many interchangeable implementations and lets each *context* — an application, a test, a deployment — plug in the one it uses. The choice is written in one readable place and resolved entirely during compilation, so it compiles down to direct calls: there is no runtime container, no reflection, and nothing left in the binary for an implementation a context does not use.

> [!IMPORTANT]
> The `main` branch tracks the upcoming **v0.8.0** release (published as `0.8.0-alpha` pre-release crates). The current stable release on crates.io is **v0.7.0**, which is **not compatible** with this branch — v0.8.0 changes and removes syntax that v0.7.0 used. **All documentation in this repository describes v0.8.0 only.** For v0.7.0, refer to its published crate documentation instead.
Expand Down Expand Up @@ -72,8 +72,19 @@ impl StorageObjectFetcher { /* fetch the object from Amazon S3 */ }
impl StorageObjectFetcher { /* fetch the object from Google Cloud Storage */ }

// ...and each context picks one, resolved at compile time.
delegate_components! { App { StorageObjectFetcherComponent: FetchS3Object } }
delegate_components! { GCloudApp { StorageObjectFetcherComponent: FetchGCloudObject } }
delegate_components! {
App {
StorageObjectFetcherComponent: FetchS3Object,
// ...
}
}

delegate_components! {
GCloudApp {
StorageObjectFetcherComponent: FetchGCloudObject,
// ...
}
}
```

`App` fetches from Amazon S3, and `GCloudApp` — a context carrying a Google Cloud storage client instead — fetches from Google Cloud. Neither pays for `dyn` or runtime dispatch, the choice is a single greppable line, and code that calls `self.fetch_storage_object(..)` never changes. (Method bodies are elided here for brevity.)
Expand Down
4 changes: 0 additions & 4 deletions crates/core/cgp-component/src/traits/delegate_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@
}
```
*/
#[diagnostic::on_unimplemented(
message = "{Self} does not contain any DelegateComponent entry for {Key}",
note = "You might want to implement the provider trait for {Key} on {Self}"
)]
pub trait DelegateComponent<Key: ?Sized> {
type Delegate;
}
3 changes: 0 additions & 3 deletions crates/core/cgp-component/src/traits/is_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,4 @@
Rust would follow the implementation path and surface all unsatisfied constraints
from `GetFooValue`.
*/
#[diagnostic::on_unimplemented(
note = "You need to add `#[cgp_provider({Component})]` on the impl block for CGP provider traits"
)]
pub trait IsProviderFor<Component, Context, Params: ?Sized = ()> {}
4 changes: 0 additions & 4 deletions crates/core/cgp-field/src/traits/has_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ use cgp_component::UseContext;
}
```
*/
#[diagnostic::on_unimplemented(
message = "HasField is not implemented for {Self} with the field: {Tag}",
note = "You need to add #[derive(HasField)] to {Self} with the given field present in the struct"
)]
pub trait HasField<Tag> {
type Value;

Expand Down
4 changes: 0 additions & 4 deletions crates/core/cgp-field/src/traits/has_field_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ use core::ops::DerefMut;

use crate::traits::{FieldGetter, HasField};

#[diagnostic::on_unimplemented(
message = "HasFieldMut is not implemented for {Self} with the field: {Tag}",
note = "You need to add #[derive(HasField)] to {Self} with the given field present in the struct"
)]
pub trait HasFieldMut<Tag>: HasField<Tag> {
fn get_field_mut(&mut self, tag: PhantomData<Tag>) -> &mut Self::Value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ note: required by a bound in `GetScalar`
| ---------- required by a bound in this trait
= note: this error originates in the attribute macro `cgp_type` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: NoScalar does not contain any DelegateComponent entry for ScalarTypeProviderComponent
error[E0277]: the trait bound `NoScalar: DelegateComponent<ScalarTypeProviderComponent>` is not satisfied
--> tests/acceptable/cgp_fn/use_type_nested_unsatisfied.rs:49:5
|
49 | App: GetScalar,
Expand All @@ -93,7 +93,6 @@ help: the trait `DelegateComponent<ScalarTypeProviderComponent>` is not implemen
|
37 | pub struct NoScalar;
| ^^^^^^^^^^^^^^^^^^^
= note: You might want to implement the provider trait for ScalarTypeProviderComponent on NoScalar
= help: the following other types implement trait `DelegateComponent<Key>`:
`BuildAndMergeOutputs<Output, Handlers>` implements `DelegateComponent<ComputerComponent>`
`BuildAndMergeOutputs<Output, Handlers>` implements `DelegateComponent<ComputerRefComponent>`
Expand Down Expand Up @@ -138,7 +137,6 @@ help: the trait `IsProviderFor<ScalarTypeProviderComponent, NoScalar>` is not im
|
37 | pub struct NoScalar;
| ^^^^^^^^^^^^^^^^^^^
= note: You need to add `#[cgp_provider(ScalarTypeProviderComponent)]` on the impl block for CGP provider traits
= help: the following other types implement trait `IsProviderFor<Component, Context, Params>`:
`BindErr<M, Cont>` implements `IsProviderFor<AsyncComputerComponent, Context, (Code, Result<T1, E>)>`
`BindErr<M, Cont>` implements `IsProviderFor<ComputerComponent, Context, (Code, Result<T1, E>)>`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! Acceptable failure: a higher-order provider whose *inner* layer carries the
//! unmet dependency. `ScaledArea<Inner>` delegates to an inner `AreaCalculator`
//! and adds its own `Self: HasScaleFactor` dependency; `BaseArea` is that inner
//! provider and needs `Self: HasBaseArea`. `Rectangle` supplies `scale_factor`
//! but not `base_area`, so the *outer* layer's dependency holds and the *inner*
//! layer's fails.
//!
//! This fixture pins where the diagnostic locates the failing layer: the
//! `unsatisfied trait bound introduced here` caret lands on `BaseArea`'s
//! `Self: HasBaseArea` clause (the inner provider), and the `required for …` chain
//! runs *through* `ScaledArea<BaseArea>`'s `IsProviderFor` before reaching
//! `CanUseComponent`, so the outer wrapper appears in the chain even though its own
//! bound is satisfied. Contrast higher_order_outer_dependency.rs, whose caret lands
//! on `ScaledArea`'s own clause and whose chain never reaches the inner provider —
//! the two failures are structurally similar but point at different layers, which
//! is why `#[check_providers(...)]` exists to assert `IsProviderFor` per layer.
//! This is the check doing its job, not a macro defect.
//!
//! See docs/errors/checks/higher-order-provider-layer.md.

use cgp::prelude::*;

#[cgp_component(AreaCalculator)]
pub trait CanCalculateArea {
fn area(&self) -> f64;
}

#[cgp_auto_getter]
pub trait HasBaseArea {
fn base_area(&self) -> f64;
}

#[cgp_auto_getter]
pub trait HasScaleFactor {
fn scale_factor(&self) -> f64;
}

#[cgp_impl(new BaseArea)]
impl AreaCalculator
where
Self: HasBaseArea,
{
fn area(&self) -> f64 {
self.base_area()
}
}

#[cgp_impl(new ScaledArea<Inner>)]
#[use_provider(Inner: AreaCalculator)]
impl<Inner> AreaCalculator
where
Self: HasScaleFactor,
{
fn area(&self) -> f64 {
self.scale_factor() * Inner::area(self)
}
}

#[derive(HasField)]
pub struct Rectangle {
pub scale_factor: f64,
// missing `base_area`, so the inner `BaseArea` layer fails
}

delegate_components! {
Rectangle {
AreaCalculatorComponent: ScaledArea<BaseArea>,
}
}

// Fails in the inner `BaseArea` layer: `Rectangle` has `scale_factor` but not
// `base_area`, so `ScaledArea`'s own dependency holds and `BaseArea`'s does not.
check_components! {
Rectangle {
AreaCalculatorComponent,
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
error[E0277]: the trait bound `BaseArea: AreaCalculator<Rectangle>` is not satisfied
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:75:9
|
75 | AreaCalculatorComponent,
| ^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `AreaCalculator<Rectangle>` is not implemented for `BaseArea`
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:38:16
|
38 | #[cgp_impl(new BaseArea)]
| ^^^^^^^^
help: the trait `AreaCalculator<__Context__>` is implemented for `BaseArea`
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:39:1
|
39 | / impl AreaCalculator
40 | | where
41 | | Self: HasBaseArea,
| |______________________^
note: required for `ScaledArea<BaseArea>` to implement `IsProviderFor<AreaCalculatorComponent, Rectangle>`
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:48:1
|
48 | #[cgp_impl(new ScaledArea<Inner>)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: required for `Rectangle` to implement `CanUseComponent<AreaCalculatorComponent>`
note: required by a bound in `__CheckRectangle`
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:73:1
|
73 | / check_components! {
74 | | Rectangle {
75 | | AreaCalculatorComponent,
76 | | }
77 | | }
| |_^ required by this bound in `__CheckRectangle`
= note: this error originates in the attribute macro `cgp_impl` which comes from the expansion of the macro `check_components` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `Rectangle: CanUseComponent<AreaCalculatorComponent>` is not satisfied
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:75:9
|
75 | AreaCalculatorComponent,
| ^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `HasField<Symbol<9, cgp::prelude::Chars<'b', cgp::prelude::Chars<'a', cgp::prelude::Chars<'s', cgp::prelude::Chars<'e', cgp::prelude::Chars<'_', cgp::prelude::Chars<'a', cgp::prelude::Chars<'r', cgp::prelude::Chars<'e', cgp::prelude::Chars<'a', Nil>>>>>>>>>>>` is not implemented for `Rectangle`
but trait `HasField<Symbol<12, cgp::prelude::Chars<'s', cgp::prelude::Chars<'c', cgp::prelude::Chars<'a', cgp::prelude::Chars<'l', cgp::prelude::Chars<'e', cgp::prelude::Chars<'_', cgp::prelude::Chars<'f', cgp::prelude::Chars<'a', cgp::prelude::Chars<'c', cgp::prelude::Chars<'t', cgp::prelude::Chars<'o', cgp::prelude::Chars<'r', Nil>>>>>>>>>>>>>>` is implemented for it
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:59:10
|
59 | #[derive(HasField)]
| ^^^^^^^^
note: required for `Rectangle` to implement `HasBaseArea`
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:28:1
|
28 | #[cgp_auto_getter]
| ^^^^^^^^^^^^^^^^^^
29 | pub trait HasBaseArea {
| ^^^^^^^^^^^
note: required for `BaseArea` to implement `IsProviderFor<AreaCalculatorComponent, Rectangle>`
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:38:1
|
38 | #[cgp_impl(new BaseArea)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
...
41 | Self: HasBaseArea,
| ----------- unsatisfied trait bound introduced here
= note: 1 redundant requirement hidden
= note: required for `ScaledArea<BaseArea>` to implement `IsProviderFor<AreaCalculatorComponent, Rectangle>`
= note: required for `Rectangle` to implement `CanUseComponent<AreaCalculatorComponent>`
note: required by a bound in `__CheckRectangle`
--> tests/acceptable/check_components/higher_order_inner_dependency.rs:73:1
|
73 | / check_components! {
74 | | Rectangle {
75 | | AreaCalculatorComponent,
76 | | }
77 | | }
| |_^ required by this bound in `__CheckRectangle`
= note: this error originates in the derive macro `HasField` which comes from the expansion of the macro `check_components` (in Nightly builds, run with -Z macro-backtrace for more info)
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! Acceptable failure: the mirror of higher_order_inner_dependency.rs, where the
//! *outer* layer of the same higher-order provider carries the unmet dependency.
//! `ScaledArea<BaseArea>` is wired again, but now `Rectangle` supplies `base_area`
//! and not `scale_factor`, so the *inner* `BaseArea` layer would succeed and the
//! *outer* `ScaledArea` layer fails on its own `Self: HasScaleFactor`.
//!
//! This fixture pins the contrast with the inner-failure case: the `unsatisfied
//! trait bound introduced here` caret lands on `ScaledArea`'s own
//! `Self: HasScaleFactor` clause, and the `required for …` chain is *shorter* — it
//! reaches `ScaledArea<BaseArea>`'s `IsProviderFor` and stops, never descending
//! into `BaseArea`, because the outer layer fails before it ever delegates inward.
//! Read alongside higher_order_inner_dependency.rs, the pair shows that the layer
//! at fault is identified by which provider's `where` clause the caret sits on and
//! how deep the chain runs. This is the check doing its job, not a macro defect.
//!
//! See docs/errors/checks/higher-order-provider-layer.md.

use cgp::prelude::*;

#[cgp_component(AreaCalculator)]
pub trait CanCalculateArea {
fn area(&self) -> f64;
}

#[cgp_auto_getter]
pub trait HasBaseArea {
fn base_area(&self) -> f64;
}

#[cgp_auto_getter]
pub trait HasScaleFactor {
fn scale_factor(&self) -> f64;
}

#[cgp_impl(new BaseArea)]
impl AreaCalculator
where
Self: HasBaseArea,
{
fn area(&self) -> f64 {
self.base_area()
}
}

#[cgp_impl(new ScaledArea<Inner>)]
#[use_provider(Inner: AreaCalculator)]
impl<Inner> AreaCalculator
where
Self: HasScaleFactor,
{
fn area(&self) -> f64 {
self.scale_factor() * Inner::area(self)
}
}

#[derive(HasField)]
pub struct Rectangle {
pub base_area: f64,
// missing `scale_factor`, so the outer `ScaledArea` layer fails
}

delegate_components! {
Rectangle {
AreaCalculatorComponent: ScaledArea<BaseArea>,
}
}

// Fails in the outer `ScaledArea` layer: `Rectangle` has `base_area` but not
// `scale_factor`, so the inner `BaseArea` dependency holds and `ScaledArea`'s does
// not.
check_components! {
Rectangle {
AreaCalculatorComponent,
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
error[E0277]: the trait bound `Rectangle: CanUseComponent<AreaCalculatorComponent>` is not satisfied
--> tests/acceptable/check_components/higher_order_outer_dependency.rs:73:9
|
73 | AreaCalculatorComponent,
| ^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `HasField<Symbol<12, cgp::prelude::Chars<'s', cgp::prelude::Chars<'c', cgp::prelude::Chars<'a', cgp::prelude::Chars<'l', cgp::prelude::Chars<'e', cgp::prelude::Chars<'_', cgp::prelude::Chars<'f', cgp::prelude::Chars<'a', cgp::prelude::Chars<'c', cgp::prelude::Chars<'t', cgp::prelude::Chars<'o', cgp::prelude::Chars<'r', Nil>>>>>>>>>>>>>>` is not implemented for `Rectangle`
but trait `HasField<Symbol<9, cgp::prelude::Chars<'b', cgp::prelude::Chars<'a', cgp::prelude::Chars<'s', cgp::prelude::Chars<'e', cgp::prelude::Chars<'_', cgp::prelude::Chars<'a', cgp::prelude::Chars<'r', cgp::prelude::Chars<'e', cgp::prelude::Chars<'a', Nil>>>>>>>>>>>` is implemented for it
--> tests/acceptable/check_components/higher_order_outer_dependency.rs:56:10
|
56 | #[derive(HasField)]
| ^^^^^^^^
note: required for `Rectangle` to implement `HasScaleFactor`
--> tests/acceptable/check_components/higher_order_outer_dependency.rs:30:1
|
30 | #[cgp_auto_getter]
| ^^^^^^^^^^^^^^^^^^
31 | pub trait HasScaleFactor {
| ^^^^^^^^^^^^^^
note: required for `ScaledArea<BaseArea>` to implement `IsProviderFor<AreaCalculatorComponent, Rectangle>`
--> tests/acceptable/check_components/higher_order_outer_dependency.rs:45:1
|
45 | #[cgp_impl(new ScaledArea<Inner>)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
49 | Self: HasScaleFactor,
| -------------- unsatisfied trait bound introduced here
= note: required for `Rectangle` to implement `CanUseComponent<AreaCalculatorComponent>`
note: required by a bound in `__CheckRectangle`
--> tests/acceptable/check_components/higher_order_outer_dependency.rs:71:1
|
71 | / check_components! {
72 | | Rectangle {
73 | | AreaCalculatorComponent,
74 | | }
75 | | }
| |_^ required by this bound in `__CheckRectangle`
= note: this error originates in the derive macro `HasField` which comes from the expansion of the macro `check_components` (in Nightly builds, run with -Z macro-backtrace for more info)
Loading
Loading