Skip to content

Commit

Permalink
Unrolled build for rust-lang#119894
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#119894 - fmease:tilde-const-assoc-ty-bounds, r=compiler-errors

Allow `~const` on associated type bounds again

This follows from [this Zulip discussion](https://rust-lang.zulipchat.com/#narrow/stream/419616-t-compiler.2Fproject-const-traits/topic/projections.20on.20.28~.29const.20Trait.20.26.20.28~.29const.20assoc.20ty.20bounds).

Basically in my opinion, it makes sense to allow `~const` on associated type bounds again since they're quite useful even though we haven't implemented the proposed syntax `<Ty as ~const Trait>::Proj`/`<Ty as const Trait>::Proj` yet; that can happen as a follow-up.

This already allows more code to compile since `T::Assoc` where `T` is a type parameter and where the predicate `<T as ~const Trait>` is in the environment gets elaborated to (pseudo) `<T as ~const Trait>::Assoc`.

```rs
#[const_trait]
trait Trait {
    type Assoc: ~const Trait;
    fn func() -> i32;
}

const fn function<T: ~const Trait>() -> i32 {
    T::Assoc::func()
}
```

`~const` associated type bounds also work together with `const` bounds:

```rs
struct Type<const N: i32>;

fn procedure<T: const Trait>() -> Type<{ T::Assoc::func() }> { // `Trait` comes from above
    Type
}
```

NB: This PR also starts allowing `~const` bounds in the generics and the where-clause of trait associated types since it's trivial to support them. However, I don't know if those bounds are actually useful. Maybe we should continue to reject them?
For reference, it wouldn't make any sense to allow `~const Trait` in GACs (generic associated constants, `generic_const_items`) because they'd be absolutely useless (contrary to `const Trait`).

~~[``@]rustbot`` ping project-const-traits~~
r? project-const-traits
  • Loading branch information
rust-timer committed Jan 13, 2024
2 parents 1d8d7b1 + f283739 commit 93b391e
Show file tree
Hide file tree
Showing 13 changed files with 202 additions and 64 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 33 additions & 5 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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(_)) =>
{
Expand All @@ -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)),
}
}
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)]
Expand Down
Original file line number Diff line number Diff line change
@@ -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 | <T as /* FIXME: ~const */ Trait>::Assoc::func()
| ^ the trait `Trait` is not implemented for `T`
|
help: consider further restricting this bound
|
LL | const fn qualified<T: ~const Trait + Trait>() -> i32 {
| +++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// FIXME(effects): Collapse the revisions into one once we support `<Ty as ~const Trait>::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<T: ~const Trait>() -> i32 {
T::Assoc::func()
}

#[cfg(qualified)]
const fn qualified<T: ~const Trait>() -> i32 {
<T as /* FIXME: ~const */ Trait>::Assoc::func()
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -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<T: const Trait>() -> Type<{ <T as /* FIXME: const */ Trait>::Assoc::func() }> {
| ^ the trait `Trait` is not implemented for `T`
|
help: consider further restricting this bound
|
LL | fn qualified<T: const Trait + Trait>() -> Type<{ <T as /* FIXME: const */ Trait>::Assoc::func() }> {
| +++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// FIXME(effects): Collapse the revisions into one once we support `<Ty as const Trait>::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<const N: i32>;

#[cfg(unqualified)]
fn unqualified<T: const Trait>() -> Type<{ T::Assoc::func() }> {
Type
}

#[cfg(qualified)]
fn qualified<T: const Trait>() -> Type<{ <T as /* FIXME: const */ Trait>::Assoc::func() }> {
Type
}

fn main() {}

This file was deleted.

This file was deleted.

28 changes: 21 additions & 7 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs
Original file line number Diff line number Diff line change
@@ -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<Rhs = Self> {
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 {
Expand Down
22 changes: 11 additions & 11 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,23 @@ error: `~const` is not allowed here
LL | type Type<T: ~const Trait>: ~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<T: ~const Trait>: ~const Trait;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `~const` is not allowed here
--> $DIR/tilde-const-invalid-places.rs:25:33
|
LL | type Type<T: ~const Trait>: ~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<T: ~const Trait>: ~const Trait;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `~const` is not allowed here
--> $DIR/tilde-const-invalid-places.rs:28:30
Expand Down Expand Up @@ -108,7 +116,11 @@ error: `~const` is not allowed here
LL | type Type<T: ~const Trait> = ();
| ^^^^^^
|
= 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<T: ~const Trait> = ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `~const` is not allowed here
--> $DIR/tilde-const-invalid-places.rs:36:30
Expand Down Expand Up @@ -136,7 +148,11 @@ error: `~const` is not allowed here
LL | type Type<T: ~const Trait> = ();
| ^^^^^^
|
= 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<T: ~const Trait> = ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `~const` is not allowed here
--> $DIR/tilde-const-invalid-places.rs:46:30
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T: ~const Bound>;
}

impl const Trait for () {
type Assoc<T: ~const Bound> = T;
}

#[const_trait]
trait Bound {}

fn main() {}

0 comments on commit 93b391e

Please sign in to comment.