Skip to content

Commit 8aa5853

Browse files
authored
Rollup merge of rust-lang#133325 - compiler-errors:const-spec, r=lcnr,fee1-dead
Reimplement `~const` trait specialization Reimplement const specialization. We need this for `PartialEq` constification :) r? lcnr
2 parents 3ec2107 + 9bda88b commit 8aa5853

14 files changed

+274
-74
lines changed

compiler/rustc_hir_analysis/messages.ftl

-2
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@ hir_analysis_const_param_ty_impl_on_unsized =
113113
the trait `ConstParamTy` may not be implemented for this type
114114
.label = type is not `Sized`
115115
116-
hir_analysis_const_specialize = cannot specialize on const impl with non-const impl
117-
118116
hir_analysis_copy_impl_on_non_adt =
119117
the trait `Copy` cannot be implemented for this type
120118
.label = type is not a structure or enumeration

compiler/rustc_hir_analysis/src/errors.rs

-7
Original file line numberDiff line numberDiff line change
@@ -1079,13 +1079,6 @@ pub(crate) struct EmptySpecialization {
10791079
pub base_impl_span: Span,
10801080
}
10811081

1082-
#[derive(Diagnostic)]
1083-
#[diag(hir_analysis_const_specialize)]
1084-
pub(crate) struct ConstSpecialize {
1085-
#[primary_span]
1086-
pub span: Span,
1087-
}
1088-
10891082
#[derive(Diagnostic)]
10901083
#[diag(hir_analysis_static_specialize)]
10911084
pub(crate) struct StaticSpecialize {

compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs

-26
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
//! on traits with methods can.
6767
6868
use rustc_data_structures::fx::FxHashSet;
69-
use rustc_hir as hir;
7069
use rustc_hir::def_id::{DefId, LocalDefId};
7170
use rustc_infer::infer::TyCtxtInferExt;
7271
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
@@ -134,7 +133,6 @@ fn check_always_applicable(
134133
unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args)
135134
};
136135

137-
res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span));
138136
res = res.and(check_static_lifetimes(tcx, &parent_args, span));
139137
res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span));
140138
res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
@@ -157,30 +155,6 @@ fn check_has_items(
157155
Ok(())
158156
}
159157

160-
/// Check that the specializing impl `impl1` is at least as const as the base
161-
/// impl `impl2`
162-
fn check_constness(
163-
tcx: TyCtxt<'_>,
164-
impl1_def_id: LocalDefId,
165-
impl2_node: Node,
166-
span: Span,
167-
) -> Result<(), ErrorGuaranteed> {
168-
if impl2_node.is_from_trait() {
169-
// This isn't a specialization
170-
return Ok(());
171-
}
172-
173-
let impl1_constness = tcx.constness(impl1_def_id.to_def_id());
174-
let impl2_constness = tcx.constness(impl2_node.def_id());
175-
176-
if let hir::Constness::Const = impl2_constness {
177-
if let hir::Constness::NotConst = impl1_constness {
178-
return Err(tcx.dcx().emit_err(errors::ConstSpecialize { span }));
179-
}
180-
}
181-
Ok(())
182-
}
183-
184158
/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
185159
/// generic parameters `(S1, S2)` that equate their trait references.
186160
/// The returned types are expressed in terms of the generics of `impl1`.

compiler/rustc_trait_selection/src/traits/specialize/mod.rs

+128-22
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxIndexSet;
1515
use rustc_errors::codes::*;
1616
use rustc_errors::{Diag, EmissionGuarantee};
1717
use rustc_hir::def_id::{DefId, LocalDefId};
18+
use rustc_infer::traits::Obligation;
1819
use rustc_middle::bug;
1920
use rustc_middle::query::LocalCrate;
2021
use rustc_middle::ty::print::PrintTraitRefExt as _;
@@ -224,29 +225,38 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool
224225
tcx.features().specialization() || tcx.features().min_specialization()
225226
}
226227

227-
/// Is `impl1` a specialization of `impl2`?
228+
/// Is `specializing_impl_def_id` a specialization of `parent_impl_def_id`?
228229
///
229-
/// Specialization is determined by the sets of types to which the impls apply;
230-
/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
231-
/// to.
230+
/// For every type that could apply to `specializing_impl_def_id`, we prove that
231+
/// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and
232+
/// its where-clauses hold).
233+
///
234+
/// For the purposes of const traits, we also check that the specializing
235+
/// impl is not more restrictive than the parent impl. That is, if the
236+
/// `parent_impl_def_id` is a const impl (conditionally based off of some `~const`
237+
/// bounds), then `specializing_impl_def_id` must also be const for the same
238+
/// set of types.
232239
#[instrument(skip(tcx), level = "debug")]
233-
pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool {
240+
pub(super) fn specializes(
241+
tcx: TyCtxt<'_>,
242+
(specializing_impl_def_id, parent_impl_def_id): (DefId, DefId),
243+
) -> bool {
234244
// We check that the specializing impl comes from a crate that has specialization enabled,
235245
// or if the specializing impl is marked with `allow_internal_unstable`.
236246
//
237247
// We don't really care if the specialized impl (the parent) is in a crate that has
238248
// specialization enabled, since it's not being specialized, and it's already been checked
239249
// for coherence.
240-
if !tcx.specialization_enabled_in(impl1_def_id.krate) {
241-
let span = tcx.def_span(impl1_def_id);
250+
if !tcx.specialization_enabled_in(specializing_impl_def_id.krate) {
251+
let span = tcx.def_span(specializing_impl_def_id);
242252
if !span.allows_unstable(sym::specialization)
243253
&& !span.allows_unstable(sym::min_specialization)
244254
{
245255
return false;
246256
}
247257
}
248258

249-
let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap();
259+
let specializing_impl_trait_header = tcx.impl_trait_header(specializing_impl_def_id).unwrap();
250260

251261
// We determine whether there's a subset relationship by:
252262
//
@@ -261,27 +271,123 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
261271
// See RFC 1210 for more details and justification.
262272

263273
// Currently we do not allow e.g., a negative impl to specialize a positive one
264-
if impl1_trait_header.polarity != tcx.impl_polarity(impl2_def_id) {
274+
if specializing_impl_trait_header.polarity != tcx.impl_polarity(parent_impl_def_id) {
265275
return false;
266276
}
267277

268-
// create a parameter environment corresponding to an identity instantiation of impl1,
269-
// i.e. the most generic instantiation of impl1.
270-
let param_env = tcx.param_env(impl1_def_id);
278+
// create a parameter environment corresponding to an identity instantiation of the specializing impl,
279+
// i.e. the most generic instantiation of the specializing impl.
280+
let param_env = tcx.param_env(specializing_impl_def_id);
271281

272-
// Create an infcx, taking the predicates of impl1 as assumptions:
282+
// Create an infcx, taking the predicates of the specializing impl as assumptions:
273283
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
274284

275-
// Attempt to prove that impl2 applies, given all of the above.
276-
fulfill_implication(
277-
&infcx,
285+
let specializing_impl_trait_ref =
286+
specializing_impl_trait_header.trait_ref.instantiate_identity();
287+
let cause = &ObligationCause::dummy();
288+
debug!(
289+
"fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
290+
param_env, specializing_impl_trait_ref, parent_impl_def_id
291+
);
292+
293+
// Attempt to prove that the parent impl applies, given all of the above.
294+
295+
let ocx = ObligationCtxt::new(&infcx);
296+
let specializing_impl_trait_ref = ocx.normalize(cause, param_env, specializing_impl_trait_ref);
297+
298+
if !ocx.select_all_or_error().is_empty() {
299+
infcx.dcx().span_delayed_bug(
300+
infcx.tcx.def_span(specializing_impl_def_id),
301+
format!("failed to fully normalize {specializing_impl_trait_ref}"),
302+
);
303+
return false;
304+
}
305+
306+
let parent_args = infcx.fresh_args_for_item(DUMMY_SP, parent_impl_def_id);
307+
let parent_impl_trait_ref = ocx.normalize(
308+
cause,
278309
param_env,
279-
impl1_trait_header.trait_ref.instantiate_identity(),
280-
impl1_def_id,
281-
impl2_def_id,
282-
&ObligationCause::dummy(),
283-
)
284-
.is_ok()
310+
infcx
311+
.tcx
312+
.impl_trait_ref(parent_impl_def_id)
313+
.expect("expected source impl to be a trait impl")
314+
.instantiate(infcx.tcx, parent_args),
315+
);
316+
317+
// do the impls unify? If not, no specialization.
318+
let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref)
319+
else {
320+
return false;
321+
};
322+
323+
// Now check that the source trait ref satisfies all the where clauses of the target impl.
324+
// This is not just for correctness; we also need this to constrain any params that may
325+
// only be referenced via projection predicates.
326+
let predicates = ocx.normalize(
327+
cause,
328+
param_env,
329+
infcx.tcx.predicates_of(parent_impl_def_id).instantiate(infcx.tcx, parent_args),
330+
);
331+
let obligations = predicates_for_generics(|_, _| cause.clone(), param_env, predicates);
332+
ocx.register_obligations(obligations);
333+
334+
let errors = ocx.select_all_or_error();
335+
if !errors.is_empty() {
336+
// no dice!
337+
debug!(
338+
"fulfill_implication: for impls on {:?} and {:?}, \
339+
could not fulfill: {:?} given {:?}",
340+
specializing_impl_trait_ref,
341+
parent_impl_trait_ref,
342+
errors,
343+
param_env.caller_bounds()
344+
);
345+
return false;
346+
}
347+
348+
// If the parent impl is const, then the specializing impl must be const,
349+
// and it must not be *more restrictive* than the parent impl (that is,
350+
// it cannot be const in fewer cases than the parent impl).
351+
if tcx.is_conditionally_const(parent_impl_def_id) {
352+
if !tcx.is_conditionally_const(specializing_impl_def_id) {
353+
return false;
354+
}
355+
356+
let const_conditions = ocx.normalize(
357+
cause,
358+
param_env,
359+
infcx.tcx.const_conditions(parent_impl_def_id).instantiate(infcx.tcx, parent_args),
360+
);
361+
ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, _)| {
362+
Obligation::new(
363+
infcx.tcx,
364+
cause.clone(),
365+
param_env,
366+
trait_ref.to_host_effect_clause(infcx.tcx, ty::BoundConstness::Maybe),
367+
)
368+
}));
369+
370+
let errors = ocx.select_all_or_error();
371+
if !errors.is_empty() {
372+
// no dice!
373+
debug!(
374+
"fulfill_implication: for impls on {:?} and {:?}, \
375+
could not fulfill: {:?} given {:?}",
376+
specializing_impl_trait_ref,
377+
parent_impl_trait_ref,
378+
errors,
379+
param_env.caller_bounds()
380+
);
381+
return false;
382+
}
383+
}
384+
385+
debug!(
386+
"fulfill_implication: an impl for {:?} specializes {:?}",
387+
specializing_impl_trait_ref, parent_impl_trait_ref
388+
);
389+
390+
true
285391
}
286392

287393
/// Query provider for `specialization_graph_of`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0119]: conflicting implementations of trait `Foo` for type `(_,)`
2+
--> $DIR/overlap-const-with-nonconst.rs:23:1
3+
|
4+
LL | / impl<T> const Foo for T
5+
LL | | where
6+
LL | | T: ~const Bar,
7+
| |__________________- first implementation here
8+
...
9+
LL | impl<T> Foo for (T,) {
10+
| ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(_,)`
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0119`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//@ revisions: spec min_spec
2+
3+
#![feature(const_trait_impl)]
4+
#![cfg_attr(spec, feature(specialization))]
5+
//[spec]~^ WARN the feature `specialization` is incomplete
6+
#![cfg_attr(min_spec, feature(min_specialization))]
7+
8+
#[const_trait]
9+
trait Bar {}
10+
impl<T> const Bar for T {}
11+
12+
#[const_trait]
13+
trait Foo {
14+
fn method(&self);
15+
}
16+
impl<T> const Foo for T
17+
where
18+
T: ~const Bar,
19+
{
20+
default fn method(&self) {}
21+
}
22+
// specializing impl:
23+
impl<T> Foo for (T,) {
24+
//~^ ERROR conflicting implementations
25+
fn method(&self) {
26+
println!("hi");
27+
}
28+
}
29+
30+
const fn dispatch<T: ~const Bar + Copy>(t: T) {
31+
t.method();
32+
}
33+
34+
fn main() {
35+
const {
36+
dispatch(((),));
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/overlap-const-with-nonconst.rs:4:27
3+
|
4+
LL | #![cfg_attr(spec, feature(specialization))]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
8+
= help: consider using `min_specialization` instead, which is more stable and complete
9+
= note: `#[warn(incomplete_features)]` on by default
10+
11+
error[E0119]: conflicting implementations of trait `Foo` for type `(_,)`
12+
--> $DIR/overlap-const-with-nonconst.rs:23:1
13+
|
14+
LL | / impl<T> const Foo for T
15+
LL | | where
16+
LL | | T: ~const Bar,
17+
| |__________________- first implementation here
18+
...
19+
LL | impl<T> Foo for (T,) {
20+
| ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(_,)`
21+
22+
error: aborting due to 1 previous error; 1 warning emitted
23+
24+
For more information about this error, try `rustc --explain E0119`.
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
1-
error: cannot specialize on const impl with non-const impl
1+
error[E0119]: conflicting implementations of trait `Bar`
22
--> $DIR/const-default-bound-non-const-specialized-bound.rs:28:1
33
|
4+
LL | / impl<T> const Bar for T
5+
LL | | where
6+
LL | | T: ~const Foo,
7+
| |__________________- first implementation here
8+
...
49
LL | / impl<T> Bar for T
510
LL | | where
611
LL | | T: Foo, //FIXME ~ ERROR missing `~const` qualifier
712
LL | | T: Specialize,
8-
| |__________________^
13+
| |__________________^ conflicting implementation
14+
15+
error[E0119]: conflicting implementations of trait `Baz`
16+
--> $DIR/const-default-bound-non-const-specialized-bound.rs:48:1
17+
|
18+
LL | / impl<T> const Baz for T
19+
LL | | where
20+
LL | | T: ~const Foo,
21+
| |__________________- first implementation here
22+
...
23+
LL | / impl<T> const Baz for T //FIXME ~ ERROR conflicting implementations of trait `Baz`
24+
LL | | where
25+
LL | | T: Foo,
26+
LL | | T: Specialize,
27+
| |__________________^ conflicting implementation
928

10-
error: aborting due to 1 previous error
29+
error: aborting due to 2 previous errors
1130

31+
For more information about this error, try `rustc --explain E0119`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0119]: conflicting implementations of trait `Value` for type `FortyTwo`
2+
--> $DIR/const-default-impl-non-const-specialized-impl.rs:22:1
3+
|
4+
LL | impl<T> const Value for T {
5+
| ------------------------- first implementation here
6+
...
7+
LL | impl Value for FortyTwo {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `FortyTwo`
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0119`.

0 commit comments

Comments
 (0)