Skip to content

Commit f4d0625

Browse files
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.
2 parents 3da96ae + 760673e commit f4d0625

14 files changed

+227
-128
lines changed

compiler/rustc_hir_analysis/src/astconv/mod.rs

+2-33
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10321032
self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name)
10331033
});
10341034

1035-
let Some(mut bound) = matching_candidates.next() else {
1035+
let Some(bound) = matching_candidates.next() else {
10361036
let reported = self.complain_about_assoc_item_not_found(
10371037
all_candidates,
10381038
&ty_param_name.to_string(),
@@ -1046,38 +1046,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10461046
};
10471047
debug!(?bound);
10481048

1049-
// look for a candidate that is not the same as our first bound, disregarding
1050-
// whether the bound is const.
1051-
let mut next_cand = matching_candidates.next();
1052-
while let Some(mut bound2) = next_cand {
1053-
debug!(?bound2);
1054-
if bound2.bound_vars() != bound.bound_vars() {
1055-
break;
1056-
}
1057-
1058-
let generics = tcx.generics_of(bound.def_id());
1059-
let Some(host_index) = generics.host_effect_index else { break };
1060-
1061-
// always return the bound that contains the host param.
1062-
if let ty::ConstKind::Param(_) = bound2.skip_binder().args.const_at(host_index).kind() {
1063-
(bound, bound2) = (bound2, bound);
1064-
}
1065-
1066-
let unconsted_args = bound
1067-
.skip_binder()
1068-
.args
1069-
.iter()
1070-
.enumerate()
1071-
.map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg });
1072-
1073-
if unconsted_args.eq(bound2.skip_binder().args.iter()) {
1074-
next_cand = matching_candidates.next();
1075-
} else {
1076-
break;
1077-
}
1078-
}
1079-
1080-
if let Some(bound2) = next_cand {
1049+
if let Some(bound2) = matching_candidates.next() {
10811050
debug!(?bound2);
10821051

10831052
let assoc_kind_str = assoc_kind_str(assoc_kind);

compiler/rustc_hir_analysis/src/bounds.rs

-18
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,6 @@ impl<'tcx> Bounds<'tcx> {
4545
polarity: ty::ImplPolarity,
4646
) {
4747
self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
48-
49-
// push a non-const (`host = true`) version of the bound if it is `~const`.
50-
if tcx.features().effects
51-
&& let Some(host_effect_idx) = tcx.generics_of(trait_ref.def_id()).host_effect_index
52-
&& trait_ref.skip_binder().args.const_at(host_effect_idx) != tcx.consts.true_
53-
{
54-
let generics = tcx.generics_of(trait_ref.def_id());
55-
let Some(host_index) = generics.host_effect_index else { return };
56-
let trait_ref = trait_ref.map_bound(|mut trait_ref| {
57-
trait_ref.args =
58-
tcx.mk_args_from_iter(trait_ref.args.iter().enumerate().map(|(n, arg)| {
59-
if host_index == n { tcx.consts.true_.into() } else { arg }
60-
}));
61-
trait_ref
62-
});
63-
64-
self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
65-
}
6648
}
6749

6850
fn push_trait_bound_inner(

compiler/rustc_hir_analysis/src/collect/predicates_of.rs

+6-32
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_hir::intravisit::{self, Visitor};
1111
use rustc_middle::ty::{self, Ty, TyCtxt};
1212
use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate};
1313
use rustc_span::symbol::Ident;
14-
use rustc_span::{sym, Span, DUMMY_SP};
14+
use rustc_span::{Span, DUMMY_SP};
1515

1616
/// Returns a list of all type predicates (explicit and implicit) for the definition with
1717
/// 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
3838
// an obligation and instead be skipped. Otherwise we'd use
3939
// `tcx.def_span(def_id);`
4040
let span = rustc_span::DUMMY_SP;
41-
let non_const_bound = if tcx.features().effects && tcx.has_attr(def_id, sym::const_trait) {
42-
// when `Self` is a const trait, also add `Self: Trait<.., true>` as implied bound,
43-
// because only implementing `Self: Trait<.., false>` is currently not possible.
44-
Some((
45-
ty::TraitRef::new(
46-
tcx,
47-
def_id,
48-
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
49-
if param.is_host_effect() {
50-
tcx.consts.true_.into()
51-
} else {
52-
tcx.mk_param_from_def(param)
53-
}
54-
}),
55-
)
56-
.to_predicate(tcx),
41+
42+
result.predicates =
43+
tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
44+
ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
5745
span,
58-
))
59-
} else {
60-
None
61-
};
62-
result.predicates = tcx.arena.alloc_from_iter(
63-
result
64-
.predicates
65-
.iter()
66-
.copied()
67-
.chain(std::iter::once((
68-
ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
69-
span,
70-
)))
71-
.chain(non_const_bound),
72-
);
46+
))));
7347
}
7448
debug!("predicates_of(def_id={:?}) = {:?}", def_id, result);
7549
result

tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ trait Foo {
99
}
1010

1111
const fn foo<T: ~const Foo>() {
12-
<T as Foo>::Assoc::foo();
12+
<T as /* FIXME: ~const */ Foo>::Assoc::foo();
1313
}
1414

1515
fn main() {}

tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr

+9-7
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ LL | type Assoc: ~const Foo;
66
|
77
= note: this item cannot have `~const` trait bounds
88

9-
error[E0308]: mismatched types
10-
--> $DIR/assoc-type-const-bound-usage.rs:12:5
9+
error[E0277]: the trait bound `T: Foo` is not satisfied
10+
--> $DIR/assoc-type-const-bound-usage.rs:12:6
1111
|
12-
LL | <T as Foo>::Assoc::foo();
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected `host`, found `true`
12+
LL | <T as /* FIXME: ~const */ Foo>::Assoc::foo();
13+
| ^ the trait `Foo` is not implemented for `T`
1414
|
15-
= note: expected constant `host`
16-
found constant `true`
15+
help: consider further restricting this bound
16+
|
17+
LL | const fn foo<T: ~const Foo + Foo>() {
18+
| +++++
1719

1820
error: aborting due to 2 previous errors
1921

20-
For more information about this error, try `rustc --explain E0308`.
22+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// check-pass
2+
3+
#![crate_type = "lib"]
4+
#![allow(internal_features)]
5+
#![no_std]
6+
#![no_core]
7+
#![feature(
8+
auto_traits,
9+
const_trait_impl,
10+
effects,
11+
lang_items,
12+
no_core,
13+
staged_api,
14+
unboxed_closures
15+
)]
16+
#![stable(feature = "minicore", since = "1.0.0")]
17+
18+
fn test() {
19+
fn is_const_fn<F>(_: F)
20+
where
21+
F: const FnOnce<()>,
22+
{
23+
}
24+
25+
const fn foo() {}
26+
27+
is_const_fn(foo);
28+
}
29+
30+
/// ---------------------------------------------------------------------- ///
31+
/// Const fn trait definitions
32+
33+
#[const_trait]
34+
#[lang = "fn"]
35+
#[rustc_paren_sugar]
36+
trait Fn<Args: Tuple>: ~const FnMut<Args> {
37+
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
38+
}
39+
40+
#[const_trait]
41+
#[lang = "fn_mut"]
42+
#[rustc_paren_sugar]
43+
trait FnMut<Args: Tuple>: ~const FnOnce<Args> {
44+
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
45+
}
46+
47+
#[const_trait]
48+
#[lang = "fn_once"]
49+
#[rustc_paren_sugar]
50+
trait FnOnce<Args: Tuple> {
51+
#[lang = "fn_once_output"]
52+
type Output;
53+
54+
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
55+
}
56+
57+
/// ---------------------------------------------------------------------- ///
58+
/// All this other stuff needed for core. Unrelated to test.
59+
60+
#[lang = "destruct"]
61+
#[const_trait]
62+
trait Destruct {}
63+
64+
#[lang = "freeze"]
65+
unsafe auto trait Freeze {}
66+
67+
#[lang = "drop"]
68+
#[const_trait]
69+
trait Drop {
70+
fn drop(&mut self);
71+
}
72+
73+
#[lang = "sized"]
74+
trait Sized {}
75+
#[lang = "copy"]
76+
trait Copy {}
77+
78+
#[lang = "tuple_trait"]
79+
trait Tuple {}
80+
81+
#[lang = "receiver"]
82+
trait Receiver {}
83+
84+
impl<T: ?Sized> Receiver for &T {}
85+
86+
impl<T: ?Sized> Receiver for &mut T {}

tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// known-bug: #110395
2+
// Broken until we have `&T: const Deref` impl in stdlib
3+
24
#![allow(incomplete_features)]
35
#![feature(
46
associated_type_bounds,
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0277]: can't compare `()` with `()`
2-
--> $DIR/const-impl-trait.rs:35:17
2+
--> $DIR/const-impl-trait.rs:37:17
33
|
44
LL | assert!(cmp(&()));
55
| --- ^^^ no implementation for `() == ()`
@@ -9,23 +9,20 @@ LL | assert!(cmp(&()));
99
= help: the trait `const PartialEq` is not implemented for `()`
1010
= help: the trait `PartialEq` is implemented for `()`
1111
note: required by a bound in `cmp`
12-
--> $DIR/const-impl-trait.rs:12:23
12+
--> $DIR/const-impl-trait.rs:14:23
1313
|
1414
LL | const fn cmp(a: &impl ~const PartialEq) -> bool {
1515
| ^^^^^^^^^^^^^^^^ required by this bound in `cmp`
1616

17-
error[E0277]: can't compare `&impl ~const PartialEq` with `&impl ~const PartialEq`
18-
--> $DIR/const-impl-trait.rs:13:7
17+
error[E0369]: binary operation `==` cannot be applied to type `&impl ~const PartialEq`
18+
--> $DIR/const-impl-trait.rs:15:7
1919
|
2020
LL | a == a
21-
| ^^ no implementation for `&impl ~const PartialEq == &impl ~const PartialEq`
22-
|
23-
= help: the trait `~const PartialEq<&impl ~const PartialEq>` is not implemented for `&impl ~const PartialEq`
24-
help: consider dereferencing both sides of the expression
25-
|
26-
LL | *a == *a
27-
| + +
21+
| - ^^ - &impl ~const PartialEq
22+
| |
23+
| &impl ~const PartialEq
2824

2925
error: aborting due to 2 previous errors
3026

31-
For more information about this error, try `rustc --explain E0277`.
27+
Some errors have detailed explanations: E0277, E0369.
28+
For more information about an error, try `rustc --explain E0277`.

tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const fn bar() {
4040

4141
#[lang = "Try"]
4242
#[const_trait]
43-
trait Try: FromResidual {
43+
trait Try: FromResidual<Self::Residual> {
4444
type Output;
4545
type Residual;
4646

@@ -53,7 +53,7 @@ trait Try: FromResidual {
5353

5454
// FIXME
5555
// #[const_trait]
56-
trait FromResidual<R = <Self as Try>::Residual> {
56+
trait FromResidual<R = <Self as /* FIXME: ~const */ Try>::Residual> {
5757
#[lang = "from_residual"]
5858
fn from_residual(residual: R) -> Self;
5959
}
@@ -519,9 +519,14 @@ extern "rust-intrinsic" {
519519
called_in_const: F,
520520
called_at_rt: G,
521521
) -> RET
522-
/* where clauses enforced by built-in method confirmation:
523522
where
524-
F: const FnOnce<Arg, Output = RET>,
525-
G: FnOnce<Arg, Output = RET>,
526-
*/;
523+
F: const FnOnce<ARG, Output = RET>,
524+
G: FnOnce<ARG, Output = RET>;
525+
}
526+
527+
fn test_const_eval_select() {
528+
const fn const_fn() {}
529+
fn rt_fn() {}
530+
531+
unsafe { const_eval_select((), const_fn, rt_fn); }
527532
}

tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
// check-pass
1+
// known-bug: #110395
2+
// FIXME: effects
3+
24
#![feature(const_trait_impl, effects)]
35

4-
pub trait Owo<X = <Self as Uwu>::T> {}
6+
// This fails because `~const Uwu` doesn't imply (non-const) `Uwu`.
7+
8+
// FIXME: #[const_trait]
9+
pub trait Owo<X = <Self as /* FIXME: ~const */ Uwu>::T> {}
510

611
#[const_trait]
712
pub trait Uwu: Owo {

0 commit comments

Comments
 (0)