From f28373978ee1f4d6c8c9e43a48a693d69ec63547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 12 Jan 2024 16:37:25 +0100 Subject: [PATCH] Allow `~const` on assoc ty bounds again --- compiler/rustc_ast_passes/messages.ftl | 3 ++ .../rustc_ast_passes/src/ast_validation.rs | 38 ++++++++++++++++--- compiler/rustc_ast_passes/src/errors.rs | 17 +++++++++ ...-type-const-bound-usage-0.qualified.stderr | 14 +++++++ .../assoc-type-const-bound-usage-0.rs | 24 ++++++++++++ ...-type-const-bound-usage-1.qualified.stderr | 14 +++++++ .../assoc-type-const-bound-usage-1.rs | 27 +++++++++++++ .../assoc-type-const-bound-usage.rs | 15 -------- .../assoc-type-const-bound-usage.stderr | 22 ----------- .../rfc-2632-const-trait-impl/assoc-type.rs | 28 ++++++++++---- .../assoc-type.stderr | 22 +++++------ .../tilde-const-invalid-places.stderr | 24 ++++++++++-- .../tilde-const-trait-assoc-tys.rs | 18 +++++++++ 13 files changed, 202 insertions(+), 64 deletions(-) create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.qualified.stderr create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.rs create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.qualified.stderr create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.rs delete mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs delete mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-trait-assoc-tys.rs diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index a10797626f1b2..6586ca5d36f88 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -232,6 +232,9 @@ ast_passes_tilde_const_disallowed = `~const` is not allowed here .trait = this trait is not a `#[const_trait]`, so it cannot have `~const` trait bounds .trait_impl = this impl is not `const`, so it cannot have `~const` trait bounds .impl = inherent impls cannot have `~const` trait bounds + .trait_assoc_ty = associated types in non-`#[const_trait]` traits cannot have `~const` trait bounds + .trait_impl_assoc_ty = associated types in non-const impls cannot have `~const` trait bounds + .inherent_assoc_ty = inherent associated types cannot have `~const` trait bounds .object = trait objects cannot have `~const` trait bounds .item = this item cannot have `~const` trait bounds diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 7f78f6870552f..9ea5d1ed5fa27 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -37,12 +37,17 @@ enum SelfSemantic { } /// What is the context that prevents using `~const`? +// FIXME(effects): Consider getting rid of this in favor of `errors::TildeConstReason`, they're +// almost identical. This gets rid of an abstraction layer which might be considered bad. enum DisallowTildeConstContext<'a> { TraitObject, Fn(FnKind<'a>), Trait(Span), TraitImpl(Span), Impl(Span), + TraitAssocTy(Span), + TraitImplAssocTy(Span), + InherentAssocTy(Span), Item, } @@ -316,6 +321,7 @@ impl<'a> AstValidator<'a> { constness: Const::No, polarity: ImplPolarity::Positive, trait_ref, + .. } = parent { Some(trait_ref.path.span.shrink_to_lo()) @@ -1286,6 +1292,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { // suggestion for moving such bounds to the assoc const fns if available. errors::TildeConstReason::Impl { span } } + &DisallowTildeConstContext::TraitAssocTy(span) => { + errors::TildeConstReason::TraitAssocTy { span } + } + &DisallowTildeConstContext::TraitImplAssocTy(span) => { + errors::TildeConstReason::TraitImplAssocTy { span } + } + &DisallowTildeConstContext::InherentAssocTy(span) => { + errors::TildeConstReason::InherentAssocTy { span } + } DisallowTildeConstContext::TraitObject => { errors::TildeConstReason::TraitObject } @@ -1483,13 +1498,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_item_named(item.ident, "const"); } + let parent_is_const = + self.outer_trait_or_trait_impl.as_ref().and_then(TraitOrTraitImpl::constness).is_some(); + match &item.kind { AssocItemKind::Fn(box Fn { sig, generics, body, .. }) - if self - .outer_trait_or_trait_impl - .as_ref() - .and_then(TraitOrTraitImpl::constness) - .is_some() + if parent_is_const || ctxt == AssocCtxt::Trait || matches!(sig.header.constness, Const::Yes(_)) => { @@ -1505,6 +1519,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ); self.visit_fn(kind, item.span, item.id); } + AssocItemKind::Type(_) => { + let disallowed = (!parent_is_const).then(|| match self.outer_trait_or_trait_impl { + Some(TraitOrTraitImpl::Trait { .. }) => { + DisallowTildeConstContext::TraitAssocTy(item.span) + } + Some(TraitOrTraitImpl::TraitImpl { .. }) => { + DisallowTildeConstContext::TraitImplAssocTy(item.span) + } + None => DisallowTildeConstContext::InherentAssocTy(item.span), + }); + self.with_tilde_const(disallowed, |this| { + this.with_in_trait_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)) + }) + } _ => self.with_in_trait_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)), } } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index fcf19ce52ec62..e2b8e64b115e5 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -565,6 +565,8 @@ pub struct ConstBoundTraitObject { pub span: Span, } +// FIXME(effects): Consider making the note/reason the message of the diagnostic. +// FIXME(effects): Provide structured suggestions (e.g., add `const` / `#[const_trait]` here). #[derive(Diagnostic)] #[diag(ast_passes_tilde_const_disallowed)] pub struct TildeConstDisallowed { @@ -598,6 +600,21 @@ pub enum TildeConstReason { #[primary_span] span: Span, }, + #[note(ast_passes_trait_assoc_ty)] + TraitAssocTy { + #[primary_span] + span: Span, + }, + #[note(ast_passes_trait_impl_assoc_ty)] + TraitImplAssocTy { + #[primary_span] + span: Span, + }, + #[note(ast_passes_inherent_assoc_ty)] + InherentAssocTy { + #[primary_span] + span: Span, + }, #[note(ast_passes_object)] TraitObject, #[note(ast_passes_item)] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.qualified.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.qualified.stderr new file mode 100644 index 0000000000000..62c8a442ab9bd --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.qualified.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `T: Trait` is not satisfied + --> $DIR/assoc-type-const-bound-usage-0.rs:21:6 + | +LL | ::Assoc::func() + | ^ the trait `Trait` is not implemented for `T` + | +help: consider further restricting this bound + | +LL | const fn qualified() -> i32 { + | +++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.rs new file mode 100644 index 0000000000000..d8573d3af014c --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.rs @@ -0,0 +1,24 @@ +// FIXME(effects): Collapse the revisions into one once we support `::Proj`. +// revisions: unqualified qualified +//[unqualified] check-pass +//[qualified] known-bug: unknown + +#![feature(const_trait_impl, effects)] + +#[const_trait] +trait Trait { + type Assoc: ~const Trait; + fn func() -> i32; +} + +#[cfg(unqualified)] +const fn unqualified() -> i32 { + T::Assoc::func() +} + +#[cfg(qualified)] +const fn qualified() -> i32 { + ::Assoc::func() +} + +fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.qualified.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.qualified.stderr new file mode 100644 index 0000000000000..10e467da9521a --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.qualified.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `T: Trait` is not satisfied + --> $DIR/assoc-type-const-bound-usage-1.rs:23:43 + | +LL | fn qualified() -> Type<{ ::Assoc::func() }> { + | ^ the trait `Trait` is not implemented for `T` + | +help: consider further restricting this bound + | +LL | fn qualified() -> Type<{ ::Assoc::func() }> { + | +++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.rs new file mode 100644 index 0000000000000..2190fa337b49f --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.rs @@ -0,0 +1,27 @@ +// FIXME(effects): Collapse the revisions into one once we support `::Proj`. +// revisions: unqualified qualified +//[unqualified] check-pass +//[qualified] known-bug: unknown + +#![feature(const_trait_impl, effects, generic_const_exprs)] +#![allow(incomplete_features)] + +#[const_trait] +trait Trait { + type Assoc: ~const Trait; + fn func() -> i32; +} + +struct Type; + +#[cfg(unqualified)] +fn unqualified() -> Type<{ T::Assoc::func() }> { + Type +} + +#[cfg(qualified)] +fn qualified() -> Type<{ ::Assoc::func() }> { + Type +} + +fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs deleted file mode 100644 index 16b717bc18131..0000000000000 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs +++ /dev/null @@ -1,15 +0,0 @@ -// known-bug: #110395 -// FIXME check-pass -#![feature(const_trait_impl, effects)] - -#[const_trait] -trait Foo { - type Assoc: ~const Foo; - fn foo() {} -} - -const fn foo() { - ::Assoc::foo(); -} - -fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr deleted file mode 100644 index 268e337ee93e6..0000000000000 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: `~const` is not allowed here - --> $DIR/assoc-type-const-bound-usage.rs:7:17 - | -LL | type Assoc: ~const Foo; - | ^^^^^^ - | - = note: this item cannot have `~const` trait bounds - -error[E0277]: the trait bound `T: Foo` is not satisfied - --> $DIR/assoc-type-const-bound-usage.rs:12:6 - | -LL | ::Assoc::foo(); - | ^ the trait `Foo` is not implemented for `T` - | -help: consider further restricting this bound - | -LL | const fn foo() { - | +++++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs index 96790a87311dd..886fa6577d76c 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs @@ -1,29 +1,43 @@ -// known-bug: #110395 +// FIXME(effects): Replace `Add` with `std::ops::Add` once the latter a `#[const_trait]` again. +#![feature(const_trait_impl, effects)] -#![feature(const_trait_impl)] +#[const_trait] +trait Add { + type Output; + + fn add(self, other: Rhs) -> Self::Output; +} + +impl const Add for i32 { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + self + other + } +} struct NonConstAdd(i32); -impl std::ops::Add for NonConstAdd { +impl Add for NonConstAdd { type Output = Self; fn add(self, rhs: Self) -> Self { - NonConstAdd(self.0 + rhs.0) + NonConstAdd(self.0.add(rhs.0)) } } #[const_trait] trait Foo { - type Bar: ~const std::ops::Add; + type Bar: ~const Add; } impl const Foo for NonConstAdd { - type Bar = NonConstAdd; + type Bar = NonConstAdd; //~ ERROR the trait bound `NonConstAdd: ~const Add` is not satisfied } #[const_trait] trait Baz { - type Qux: std::ops::Add; + type Qux: Add; } impl const Baz for NonConstAdd { diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr index 58ad1849d4fab..a9cae2a70be90 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr @@ -1,16 +1,16 @@ -error: `~const` is not allowed here - --> $DIR/assoc-type.rs:17:15 +error[E0277]: the trait bound `NonConstAdd: ~const Add` is not satisfied + --> $DIR/assoc-type.rs:35:16 | -LL | type Bar: ~const std::ops::Add; - | ^^^^^^ +LL | type Bar = NonConstAdd; + | ^^^^^^^^^^^ the trait `~const Add` is not implemented for `NonConstAdd` | - = note: this item cannot have `~const` trait bounds - -error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/assoc-type.rs:17:22 + = help: the trait `Add` is implemented for `NonConstAdd` +note: required by a bound in `Foo::Bar` + --> $DIR/assoc-type.rs:31:15 | -LL | type Bar: ~const std::ops::Add; - | ^^^^^^^^^^^^^ +LL | type Bar: ~const Add; + | ^^^^^^^^^^ required by this bound in `Foo::Bar` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr index a54ba7a94b4aa..6b886e8851104 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr @@ -72,7 +72,11 @@ error: `~const` is not allowed here LL | type Type: ~const Trait; | ^^^^^^ | - = note: this item cannot have `~const` trait bounds +note: associated types in non-`#[const_trait]` traits cannot have `~const` trait bounds + --> $DIR/tilde-const-invalid-places.rs:25:5 + | +LL | type Type: ~const Trait; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here --> $DIR/tilde-const-invalid-places.rs:25:33 @@ -80,7 +84,11 @@ error: `~const` is not allowed here LL | type Type: ~const Trait; | ^^^^^^ | - = note: this item cannot have `~const` trait bounds +note: associated types in non-`#[const_trait]` traits cannot have `~const` trait bounds + --> $DIR/tilde-const-invalid-places.rs:25:5 + | +LL | type Type: ~const Trait; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here --> $DIR/tilde-const-invalid-places.rs:28:30 @@ -108,7 +116,11 @@ error: `~const` is not allowed here LL | type Type = (); | ^^^^^^ | - = note: this item cannot have `~const` trait bounds +note: associated types in non-const impls cannot have `~const` trait bounds + --> $DIR/tilde-const-invalid-places.rs:34:5 + | +LL | type Type = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here --> $DIR/tilde-const-invalid-places.rs:36:30 @@ -136,7 +148,11 @@ error: `~const` is not allowed here LL | type Type = (); | ^^^^^^ | - = note: this item cannot have `~const` trait bounds +note: inherent associated types cannot have `~const` trait bounds + --> $DIR/tilde-const-invalid-places.rs:44:5 + | +LL | type Type = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here --> $DIR/tilde-const-invalid-places.rs:46:30 diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-trait-assoc-tys.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-trait-assoc-tys.rs new file mode 100644 index 0000000000000..4c383fe150674 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-trait-assoc-tys.rs @@ -0,0 +1,18 @@ +// check-pass +#![feature(const_trait_impl, effects)] + +#[const_trait] +trait Trait { + // FIXME(effects): `~const` bounds in trait associated types (excluding associated type bounds) + // don't look super useful. Should we forbid them again? + type Assoc; +} + +impl const Trait for () { + type Assoc = T; +} + +#[const_trait] +trait Bound {} + +fn main() {}