Skip to content

Commit ce03d25

Browse files
committed
Disallow specializing on const impls with non-const impls.
1 parent 5c25d30 commit ce03d25

6 files changed

+51
-80
lines changed

compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ use crate::constrained_generic_params as cgp;
6969
use crate::errors::SubstsOnOverriddenImpl;
7070

7171
use rustc_data_structures::fx::FxHashSet;
72+
use rustc_hir as hir;
7273
use rustc_hir::def_id::{DefId, LocalDefId};
7374
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
7475
use rustc_infer::infer::TyCtxtInferExt;
@@ -117,12 +118,33 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
117118
};
118119

119120
let span = tcx.def_span(impl1_def_id);
121+
check_constness(tcx, impl1_def_id, impl2_node, span);
120122
check_static_lifetimes(tcx, &parent_substs, span);
121123
check_duplicate_params(tcx, impl1_substs, &parent_substs, span);
122124
check_predicates(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span);
123125
}
124126
}
125127

128+
/// Check that the specializing impl `impl1` is at least as const as the base
129+
/// impl `impl2`
130+
fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) {
131+
if impl2_node.is_from_trait() {
132+
// This isn't a specialization
133+
return;
134+
}
135+
136+
let impl1_constness = tcx.constness(impl1_def_id.to_def_id());
137+
let impl2_constness = tcx.constness(impl2_node.def_id());
138+
139+
if let hir::Constness::Const = impl2_constness {
140+
if let hir::Constness::NotConst = impl1_constness {
141+
tcx.sess
142+
.struct_span_err(span, "cannot specialize on const impl with non-const impl")
143+
.emit();
144+
}
145+
}
146+
}
147+
126148
/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
127149
/// substitutions `(S1, S2)` that equate their trait references. The returned
128150
/// types are expressed in terms of the generics of `impl1`.
@@ -277,7 +299,7 @@ fn check_static_lifetimes<'tcx>(
277299

278300
/// Check whether predicates on the specializing impl (`impl1`) are allowed.
279301
///
280-
/// Each predicate `P` must be:
302+
/// Each predicate `P` must be one of:
281303
///
282304
/// * Global (not reference any parameters).
283305
/// * A `T: Tr` predicate where `Tr` is an always-applicable trait.
@@ -375,16 +397,19 @@ fn check_predicates<'tcx>(
375397
}
376398
}
377399

378-
/// Checks whether two predicates are the same for the purposes of specialization.
400+
/// Checks if some predicate on the specializing impl (`predicate1`) is the same
401+
/// as some predicate on the base impl (`predicate2`).
379402
///
380403
/// This is slightly more complicated than simple syntactic equivalence, since
381404
/// we want to equate `T: Tr` with `T: ~const Tr` so this can work:
382405
///
406+
/// ```ignore (illustrative)
383407
/// #[rustc_specialization_trait]
384408
/// trait Specialize { }
385409
///
386-
/// impl<T: ~const Bound> const Tr for T { }
387-
/// impl<T: Bound + Specialize> Tr for T { }
410+
/// impl<T: Bound> Tr for T { }
411+
/// impl<T: ~const Bound + Specialize> const Tr for T { }
412+
/// ```
388413
fn trait_predicates_eq<'tcx>(
389414
predicate1: ty::Predicate<'tcx>,
390415
predicate2: ty::Predicate<'tcx>,
@@ -400,6 +425,8 @@ fn trait_predicates_eq<'tcx>(
400425
_ => kind,
401426
};
402427

428+
// We rely on `check_constness` above to ensure that pred1 is const if pred2
429+
// is const.
403430
let pred1_kind_not_const = predicate1.kind().map_bound(predicate_kind_without_constness);
404431
let pred2_kind_not_const = predicate2.kind().map_bound(predicate_kind_without_constness);
405432

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Tests that a const default trait impl can be specialized by a non-const trait
2-
// impl, but that the specializing impl cannot be used in a const context.
1+
// Tests that a const default trait impl cannot be specialized by a non-const
2+
// trait impl.
33

44
#![feature(const_trait_impl)]
55
#![feature(min_specialization)]
@@ -8,12 +8,6 @@ trait Value {
88
fn value() -> u32;
99
}
1010

11-
const fn get_value<T: ~const Value>() -> u32 {
12-
T::value()
13-
//~^ ERROR any use of this value will cause an error [const_err]
14-
//~| WARNING this was previously accepted
15-
}
16-
1711
impl<T> const Value for T {
1812
default fn value() -> u32 {
1913
0
@@ -22,16 +16,11 @@ impl<T> const Value for T {
2216

2317
struct FortyTwo;
2418

25-
impl Value for FortyTwo {
19+
impl Value for FortyTwo { //~ ERROR cannot specialize on const impl with non-const impl
2620
fn value() -> u32 {
2721
println!("You can't do that (constly)");
2822
42
2923
}
3024
}
3125

32-
const ZERO: u32 = get_value::<()>();
33-
34-
const FORTY_TWO: u32 =
35-
get_value::<FortyTwo>(); // This is the line that causes the error, but it gets reported above
36-
3726
fn main() {}
Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,8 @@
1-
error: any use of this value will cause an error
2-
--> $DIR/const-default-non-const-specialized.rs:12:5
1+
error: cannot specialize on const impl with non-const impl
2+
--> $DIR/const-default-non-const-specialized.rs:19:1
33
|
4-
LL | T::value()
5-
| ^^^^^^^^^^
6-
| |
7-
| calling non-const function `<FortyTwo as Value>::value`
8-
| inside `get_value::<FortyTwo>` at $DIR/const-default-non-const-specialized.rs:12:5
9-
| inside `FORTY_TWO` at $DIR/const-default-non-const-specialized.rs:35:5
10-
...
11-
LL | const FORTY_TWO: u32 =
12-
| --------------------
13-
|
14-
= note: `#[deny(const_err)]` on by default
15-
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
16-
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
4+
LL | impl Value for FortyTwo {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
176

187
error: aborting due to previous error
198

20-
Future incompatibility report: Future breakage diagnostic:
21-
error: any use of this value will cause an error
22-
--> $DIR/const-default-non-const-specialized.rs:12:5
23-
|
24-
LL | T::value()
25-
| ^^^^^^^^^^
26-
| |
27-
| calling non-const function `<FortyTwo as Value>::value`
28-
| inside `get_value::<FortyTwo>` at $DIR/const-default-non-const-specialized.rs:12:5
29-
| inside `FORTY_TWO` at $DIR/const-default-non-const-specialized.rs:35:5
30-
...
31-
LL | const FORTY_TWO: u32 =
32-
| --------------------
33-
|
34-
= note: `#[deny(const_err)]` on by default
35-
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
36-
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
37-

src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Tests that `T: ~const Foo` and `T: Foo` are treated as equivalent for the
1+
// Tests that `T: Foo` and `T: ~const Foo` are treated as equivalent for the
22
// purposes of min_specialization.
33

44
// check-pass
@@ -14,14 +14,14 @@ trait Foo {}
1414

1515
trait Bar {}
1616

17-
impl<T> const Bar for T
17+
impl<T> Bar for T
1818
where
19-
T: ~const Foo,
19+
T: Foo,
2020
{}
2121

22-
impl<T> Bar for T
22+
impl<T> const Bar for T
2323
where
24-
T: Foo,
24+
T: ~const Foo,
2525
T: Specialize,
2626
{}
2727

src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Tests that a non-const default impl can be specialized by a const trait impl,
22
// but that the default impl cannot be used in a const context.
33

4+
// run-pass
5+
46
#![feature(const_trait_impl)]
57
#![feature(min_specialization)]
68

@@ -27,8 +29,10 @@ impl const Value for FortyTwo {
2729
}
2830
}
2931

30-
const ZERO: u32 = get_value::<()>(); //~ ERROR the trait bound `(): ~const Value` is not satisfied
31-
32-
const FORTY_TWO: u32 = get_value::<FortyTwo>();
32+
fn main() {
33+
let zero = get_value::<()>();
34+
assert_eq!(zero, 0);
3335

34-
fn main() {}
36+
const FORTY_TWO: u32 = get_value::<FortyTwo>();
37+
assert_eq!(FORTY_TWO, 42);
38+
}

src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)