1
Fork 0

Rollup merge of #119721 - compiler-errors:constness-implication, r=fee1-dead

`~const` trait and projection bounds do not imply their non-const counterparts

This PR removes the hack where we install a non-const trait and projection bound for every `const_trait` and `~const` projection bound we have in the AST. It ends up messing up more things than it fixes, see words below.

Fixes #119718

cc `@fmease` `@fee1-dead` `@oli-obk`
r? fee1-dead or one of y'all i don't care

---

My understanding is that this hack was added to support the following code:

```rust
pub trait Owo<X = <Self as Uwu>::T> {}

#[const_trait]
pub trait Uwu: Owo {}
```

Which is concretely lifted from in the `FromResidual` and `Try` traits. Since within the param-env of `trait Uwu`, we only know that `Self: ~const Uwu` and not `Self: Uwu`, the projection `<Self as Uwu>::T` is not satsifyable.

This causes problems such as #119718, since instantiations of `FnDef` types coming from `const fn` really do **only** implement one of `FnOnce` or `const FnOnce`!

---

In the long-term, I believe that such code should really look something more like:

```rust
#[const_trait]
pub trait Owo<X = <Self as ~const Uwu>::T> {}

#[const_trait]
pub trait Uwu: Owo {}
```

... and that we should introduce some sort of `<T as ~const Foo>::Bar` bound syntax, since due to the fact that `~const` bounds can be present in item bounds, e.g.

```rust
#[const_trait] trait Foo { type Bar: ~const Destruct; }
```

It's easy to see that `<T as Foo>::Bar` and `<T as ~const Foo>::Bar` (or `<T as const Foo>::Bar`) can be distinct types with distinct item bounds!

**Admission**: I know I've said before that I don't like `~const` projection syntax, I do at this point believe they're necessary to fully express bounds and types in a maybe-const world.
This commit is contained in:
Guillaume Gomez 2024-01-09 17:52:21 +01:00 committed by GitHub
commit f4d06256d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 227 additions and 128 deletions

View file

@ -1032,7 +1032,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name) self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name)
}); });
let Some(mut bound) = matching_candidates.next() else { let Some(bound) = matching_candidates.next() else {
let reported = self.complain_about_assoc_item_not_found( let reported = self.complain_about_assoc_item_not_found(
all_candidates, all_candidates,
&ty_param_name.to_string(), &ty_param_name.to_string(),
@ -1046,38 +1046,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}; };
debug!(?bound); debug!(?bound);
// look for a candidate that is not the same as our first bound, disregarding if let Some(bound2) = matching_candidates.next() {
// whether the bound is const.
let mut next_cand = matching_candidates.next();
while let Some(mut bound2) = next_cand {
debug!(?bound2);
if bound2.bound_vars() != bound.bound_vars() {
break;
}
let generics = tcx.generics_of(bound.def_id());
let Some(host_index) = generics.host_effect_index else { break };
// always return the bound that contains the host param.
if let ty::ConstKind::Param(_) = bound2.skip_binder().args.const_at(host_index).kind() {
(bound, bound2) = (bound2, bound);
}
let unconsted_args = bound
.skip_binder()
.args
.iter()
.enumerate()
.map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg });
if unconsted_args.eq(bound2.skip_binder().args.iter()) {
next_cand = matching_candidates.next();
} else {
break;
}
}
if let Some(bound2) = next_cand {
debug!(?bound2); debug!(?bound2);
let assoc_kind_str = assoc_kind_str(assoc_kind); let assoc_kind_str = assoc_kind_str(assoc_kind);

View file

@ -45,24 +45,6 @@ impl<'tcx> Bounds<'tcx> {
polarity: ty::ImplPolarity, polarity: ty::ImplPolarity,
) { ) {
self.push_trait_bound_inner(tcx, trait_ref, span, polarity); self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
// push a non-const (`host = true`) version of the bound if it is `~const`.
if tcx.features().effects
&& let Some(host_effect_idx) = tcx.generics_of(trait_ref.def_id()).host_effect_index
&& trait_ref.skip_binder().args.const_at(host_effect_idx) != tcx.consts.true_
{
let generics = tcx.generics_of(trait_ref.def_id());
let Some(host_index) = generics.host_effect_index else { return };
let trait_ref = trait_ref.map_bound(|mut trait_ref| {
trait_ref.args =
tcx.mk_args_from_iter(trait_ref.args.iter().enumerate().map(|(n, arg)| {
if host_index == n { tcx.consts.true_.into() } else { arg }
}));
trait_ref
});
self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
}
} }
fn push_trait_bound_inner( fn push_trait_bound_inner(

View file

@ -11,7 +11,7 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate}; use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{sym, Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
/// Returns a list of all type predicates (explicit and implicit) for the definition with /// Returns a list of all type predicates (explicit and implicit) for the definition with
/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus /// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
@ -38,38 +38,12 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
// an obligation and instead be skipped. Otherwise we'd use // an obligation and instead be skipped. Otherwise we'd use
// `tcx.def_span(def_id);` // `tcx.def_span(def_id);`
let span = rustc_span::DUMMY_SP; let span = rustc_span::DUMMY_SP;
let non_const_bound = if tcx.features().effects && tcx.has_attr(def_id, sym::const_trait) {
// when `Self` is a const trait, also add `Self: Trait<.., true>` as implied bound, result.predicates =
// because only implementing `Self: Trait<.., false>` is currently not possible. tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
Some((
ty::TraitRef::new(
tcx,
def_id,
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
if param.is_host_effect() {
tcx.consts.true_.into()
} else {
tcx.mk_param_from_def(param)
}
}),
)
.to_predicate(tcx),
span,
))
} else {
None
};
result.predicates = tcx.arena.alloc_from_iter(
result
.predicates
.iter()
.copied()
.chain(std::iter::once((
ty::TraitRef::identity(tcx, def_id).to_predicate(tcx), ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
span, span,
))) ))));
.chain(non_const_bound),
);
} }
debug!("predicates_of(def_id={:?}) = {:?}", def_id, result); debug!("predicates_of(def_id={:?}) = {:?}", def_id, result);
result result

View file

@ -9,7 +9,7 @@ trait Foo {
} }
const fn foo<T: ~const Foo>() { const fn foo<T: ~const Foo>() {
<T as Foo>::Assoc::foo(); <T as /* FIXME: ~const */ Foo>::Assoc::foo();
} }
fn main() {} fn main() {}

View file

@ -6,15 +6,17 @@ LL | type Assoc: ~const Foo;
| |
= note: this item cannot have `~const` trait bounds = note: this item cannot have `~const` trait bounds
error[E0308]: mismatched types error[E0277]: the trait bound `T: Foo` is not satisfied
--> $DIR/assoc-type-const-bound-usage.rs:12:5 --> $DIR/assoc-type-const-bound-usage.rs:12:6
| |
LL | <T as Foo>::Assoc::foo(); LL | <T as /* FIXME: ~const */ Foo>::Assoc::foo();
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected `host`, found `true` | ^ the trait `Foo` is not implemented for `T`
| |
= note: expected constant `host` help: consider further restricting this bound
found constant `true` |
LL | const fn foo<T: ~const Foo + Foo>() {
| +++++
error: aborting due to 2 previous errors error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`. For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,86 @@
// check-pass
#![crate_type = "lib"]
#![allow(internal_features)]
#![no_std]
#![no_core]
#![feature(
auto_traits,
const_trait_impl,
effects,
lang_items,
no_core,
staged_api,
unboxed_closures
)]
#![stable(feature = "minicore", since = "1.0.0")]
fn test() {
fn is_const_fn<F>(_: F)
where
F: const FnOnce<()>,
{
}
const fn foo() {}
is_const_fn(foo);
}
/// ---------------------------------------------------------------------- ///
/// Const fn trait definitions
#[const_trait]
#[lang = "fn"]
#[rustc_paren_sugar]
trait Fn<Args: Tuple>: ~const FnMut<Args> {
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
#[const_trait]
#[lang = "fn_mut"]
#[rustc_paren_sugar]
trait FnMut<Args: Tuple>: ~const FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
#[const_trait]
#[lang = "fn_once"]
#[rustc_paren_sugar]
trait FnOnce<Args: Tuple> {
#[lang = "fn_once_output"]
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
/// ---------------------------------------------------------------------- ///
/// All this other stuff needed for core. Unrelated to test.
#[lang = "destruct"]
#[const_trait]
trait Destruct {}
#[lang = "freeze"]
unsafe auto trait Freeze {}
#[lang = "drop"]
#[const_trait]
trait Drop {
fn drop(&mut self);
}
#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}
#[lang = "tuple_trait"]
trait Tuple {}
#[lang = "receiver"]
trait Receiver {}
impl<T: ?Sized> Receiver for &T {}
impl<T: ?Sized> Receiver for &mut T {}

View file

@ -1,4 +1,6 @@
// known-bug: #110395 // known-bug: #110395
// Broken until we have `&T: const Deref` impl in stdlib
#![allow(incomplete_features)] #![allow(incomplete_features)]
#![feature( #![feature(
associated_type_bounds, associated_type_bounds,

View file

@ -1,5 +1,5 @@
error[E0277]: can't compare `()` with `()` error[E0277]: can't compare `()` with `()`
--> $DIR/const-impl-trait.rs:35:17 --> $DIR/const-impl-trait.rs:37:17
| |
LL | assert!(cmp(&())); LL | assert!(cmp(&()));
| --- ^^^ no implementation for `() == ()` | --- ^^^ no implementation for `() == ()`
@ -9,23 +9,20 @@ LL | assert!(cmp(&()));
= help: the trait `const PartialEq` is not implemented for `()` = help: the trait `const PartialEq` is not implemented for `()`
= help: the trait `PartialEq` is implemented for `()` = help: the trait `PartialEq` is implemented for `()`
note: required by a bound in `cmp` note: required by a bound in `cmp`
--> $DIR/const-impl-trait.rs:12:23 --> $DIR/const-impl-trait.rs:14:23
| |
LL | const fn cmp(a: &impl ~const PartialEq) -> bool { LL | const fn cmp(a: &impl ~const PartialEq) -> bool {
| ^^^^^^^^^^^^^^^^ required by this bound in `cmp` | ^^^^^^^^^^^^^^^^ required by this bound in `cmp`
error[E0277]: can't compare `&impl ~const PartialEq` with `&impl ~const PartialEq` error[E0369]: binary operation `==` cannot be applied to type `&impl ~const PartialEq`
--> $DIR/const-impl-trait.rs:13:7 --> $DIR/const-impl-trait.rs:15:7
| |
LL | a == a LL | a == a
| ^^ no implementation for `&impl ~const PartialEq == &impl ~const PartialEq` | - ^^ - &impl ~const PartialEq
| | |
= help: the trait `~const PartialEq<&impl ~const PartialEq>` is not implemented for `&impl ~const PartialEq` | &impl ~const PartialEq
help: consider dereferencing both sides of the expression
|
LL | *a == *a
| + +
error: aborting due to 2 previous errors error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`. Some errors have detailed explanations: E0277, E0369.
For more information about an error, try `rustc --explain E0277`.

View file

@ -40,7 +40,7 @@ const fn bar() {
#[lang = "Try"] #[lang = "Try"]
#[const_trait] #[const_trait]
trait Try: FromResidual { trait Try: FromResidual<Self::Residual> {
type Output; type Output;
type Residual; type Residual;
@ -53,7 +53,7 @@ trait Try: FromResidual {
// FIXME // FIXME
// #[const_trait] // #[const_trait]
trait FromResidual<R = <Self as Try>::Residual> { trait FromResidual<R = <Self as /* FIXME: ~const */ Try>::Residual> {
#[lang = "from_residual"] #[lang = "from_residual"]
fn from_residual(residual: R) -> Self; fn from_residual(residual: R) -> Self;
} }
@ -519,9 +519,14 @@ extern "rust-intrinsic" {
called_in_const: F, called_in_const: F,
called_at_rt: G, called_at_rt: G,
) -> RET ) -> RET
/* where clauses enforced by built-in method confirmation:
where where
F: const FnOnce<Arg, Output = RET>, F: const FnOnce<ARG, Output = RET>,
G: FnOnce<Arg, Output = RET>, G: FnOnce<ARG, Output = RET>;
*/; }
fn test_const_eval_select() {
const fn const_fn() {}
fn rt_fn() {}
unsafe { const_eval_select((), const_fn, rt_fn); }
} }

View file

@ -1,7 +1,12 @@
// check-pass // known-bug: #110395
// FIXME: effects
#![feature(const_trait_impl, effects)] #![feature(const_trait_impl, effects)]
pub trait Owo<X = <Self as Uwu>::T> {} // This fails because `~const Uwu` doesn't imply (non-const) `Uwu`.
// FIXME: #[const_trait]
pub trait Owo<X = <Self as /* FIXME: ~const */ Uwu>::T> {}
#[const_trait] #[const_trait]
pub trait Uwu: Owo { pub trait Uwu: Owo {

View file

@ -0,0 +1,65 @@
error[E0277]: the trait bound `Self: Uwu` is not satisfied
--> $DIR/project.rs:12:1
|
LL | pub trait Uwu: Owo {
| ^^^^^^^^^^^^^^^^^^ the trait `Uwu` is not implemented for `Self`
|
help: consider further restricting `Self`
|
LL | pub trait Uwu: Owo + Uwu {
| +++++
error[E0277]: the trait bound `Self: Uwu` is not satisfied
--> $DIR/project.rs:12:1
|
LL | / pub trait Uwu: Owo {
LL | | type T;
LL | | }
| |_^ the trait `Uwu` is not implemented for `Self`
|
help: consider further restricting `Self`
|
LL | pub trait Uwu: Owo + Uwu {
| +++++
error[E0277]: the trait bound `Self: Uwu` is not satisfied
--> $DIR/project.rs:12:16
|
LL | pub trait Uwu: Owo {
| ^^^ the trait `Uwu` is not implemented for `Self`
|
note: required by a bound in `Owo`
--> $DIR/project.rs:9:15
|
LL | pub trait Owo<X = <Self as /* FIXME: ~const */ Uwu>::T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Owo`
help: consider further restricting `Self`
|
LL | pub trait Uwu: Owo + Uwu {
| +++++
error[E0277]: the trait bound `Self: Uwu` is not satisfied
--> $DIR/project.rs:13:5
|
LL | type T;
| ^^^^^^ the trait `Uwu` is not implemented for `Self`
|
help: consider further restricting `Self`
|
LL | pub trait Uwu: Owo + Uwu {
| +++++
error[E0277]: the trait bound `Self: Uwu` is not satisfied
--> $DIR/project.rs:13:5
|
LL | type T;
| ^^^^^^^ the trait `Uwu` is not implemented for `Self`
|
help: consider further restricting `Self`
|
LL | pub trait Uwu: Owo + Uwu {
| +++++
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -1,21 +1,35 @@
error[E0308]: mismatched types error[E0277]: the trait bound `T: ~const Bar` is not satisfied
--> $DIR/trait-where-clause-const.rs:21:5 --> $DIR/trait-where-clause-const.rs:21:5
| |
LL | T::b(); LL | T::b();
| ^^^^^^ expected `host`, found `true` | ^ the trait `~const Bar` is not implemented for `T`
| |
= note: expected constant `host` note: required by a bound in `Foo::b`
found constant `true` --> $DIR/trait-where-clause-const.rs:15:24
|
LL | fn b() where Self: ~const Bar;
| ^^^^^^^^^^ required by this bound in `Foo::b`
help: consider further restricting this bound
|
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
| ++++++++++++
error[E0308]: mismatched types error[E0277]: the trait bound `T: ~const Bar` is not satisfied
--> $DIR/trait-where-clause-const.rs:23:5 --> $DIR/trait-where-clause-const.rs:23:12
| |
LL | T::c::<T>(); LL | T::c::<T>();
| ^^^^^^^^^^^ expected `host`, found `true` | ^ the trait `~const Bar` is not implemented for `T`
| |
= note: expected constant `host` note: required by a bound in `Foo::c`
found constant `true` --> $DIR/trait-where-clause-const.rs:16:13
|
LL | fn c<T: ~const Bar>();
| ^^^^^^^^^^ required by this bound in `Foo::c`
help: consider further restricting this bound
|
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
| ++++++++++++
error: aborting due to 2 previous errors error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`. For more information about this error, try `rustc --explain E0277`.

View file

@ -30,4 +30,4 @@ fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
// FIXME(effects): Instead of suggesting `+ const Trait`, suggest // FIXME(effects): Instead of suggesting `+ const Trait`, suggest
// changing `~const Trait` to `const Trait`. // changing `~const Trait` to `const Trait`.
const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {} const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
//~^ ERROR the trait bound `T: const Trait` is not satisfied //~^ ERROR mismatched types

View file

@ -7,16 +7,14 @@ LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
= note: expected constant `false` = note: expected constant `false`
found constant `true` found constant `true`
error[E0277]: the trait bound `T: const Trait` is not satisfied error[E0308]: mismatched types
--> $DIR/unsatisfied-const-trait-bound.rs:32:50 --> $DIR/unsatisfied-const-trait-bound.rs:32:50
| |
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {} LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^ the trait `const Trait` is not implemented for `T` | ^^^^^^^^^ expected `false`, found `host`
| |
help: consider further restricting this bound = note: expected constant `false`
| found constant `host`
LL | const fn accept1<T: ~const Trait + const Trait>(_: Container<{ T::make() }>) {}
| +++++++++++++
error[E0277]: the trait bound `Ty: const Trait` is not satisfied error[E0277]: the trait bound `Ty: const Trait` is not satisfied
--> $DIR/unsatisfied-const-trait-bound.rs:20:15 --> $DIR/unsatisfied-const-trait-bound.rs:20:15