Skip to content

Commit 2bcd5cf

Browse files
authored
Rollup merge of rust-lang#134776 - estebank:vanilla-ice, r=lcnr
Avoid ICE: Account for `for<'a>` types when checking for non-structural type in constant as pattern When we encounter a constant in a pattern, we check if it is non-structural. If so, we check if the type implements `PartialEq`, but for types with escaping bound vars the check would be incorrect as is, so we break early. This is ok because these types would be filtered anyways. Slight tweak to output to remove unnecessary context as a drive-by. Fix rust-lang#134764.
2 parents b8e230a + 857918e commit 2bcd5cf

File tree

3 files changed

+104
-51
lines changed

3 files changed

+104
-51
lines changed

compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs

+76-51
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use core::ops::ControlFlow;
2+
13
use rustc_abi::{FieldIdx, VariantIdx};
24
use rustc_apfloat::Float;
35
use rustc_data_structures::fx::FxHashSet;
@@ -8,7 +10,9 @@ use rustc_infer::infer::TyCtxtInferExt;
810
use rustc_infer::traits::Obligation;
911
use rustc_middle::mir::interpret::ErrorHandled;
1012
use rustc_middle::thir::{FieldPat, Pat, PatKind};
11-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeVisitor, ValTree};
13+
use rustc_middle::ty::{
14+
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree,
15+
};
1216
use rustc_middle::{mir, span_bug};
1317
use rustc_span::def_id::DefId;
1418
use rustc_span::{Span, sym};
@@ -185,7 +189,7 @@ impl<'tcx> ConstToPat<'tcx> {
185189

186190
if !inlined_const_as_pat.references_error() {
187191
// Always check for `PartialEq` if we had no other errors yet.
188-
if !type_has_partial_eq_impl(self.tcx, typing_env, ty).0 {
192+
if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl {
189193
let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
190194
extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
191195
return self.mk_err(err, ty);
@@ -219,12 +223,13 @@ impl<'tcx> ConstToPat<'tcx> {
219223
// Extremely important check for all ADTs! Make sure they opted-in to be used in
220224
// patterns.
221225
debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty);
222-
let (_impls_partial_eq, derived, structural, impl_def_id) =
223-
type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
226+
let PartialEqImplStatus {
227+
is_derived, structural_partial_eq, non_blanket_impl, ..
228+
} = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
224229
let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
225-
match (structural, impl_def_id) {
230+
match (structural_partial_eq, non_blanket_impl) {
226231
(true, _) => (None, false),
227-
(_, Some(def_id)) if def_id.is_local() && !derived => {
232+
(_, Some(def_id)) if def_id.is_local() && !is_derived => {
228233
(Some(tcx.def_span(def_id)), false)
229234
}
230235
_ => (None, true),
@@ -379,52 +384,63 @@ fn extend_type_not_partial_eq<'tcx>(
379384
adts_without_partialeq: FxHashSet<Span>,
380385
/// The user has written `impl PartialEq for Ty` which means it's non-structual,
381386
/// but we don't have a span to point at, so we'll just add them as a `note`.
382-
manual: Vec<Ty<'tcx>>,
387+
manual: FxHashSet<Ty<'tcx>>,
383388
/// The type has no `PartialEq` implementation, neither manual or derived, but
384389
/// we don't have a span to point at, so we'll just add them as a `note`.
385-
without: Vec<Ty<'tcx>>,
390+
without: FxHashSet<Ty<'tcx>>,
386391
}
387392

388393
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
394+
type Result = ControlFlow<()>;
389395
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
390-
if let ty::Adt(def, _args) = ty.kind() {
391-
let ty_def_id = def.did();
392-
let ty_def_span = self.tcx.def_span(ty_def_id);
393-
let (impls_partial_eq, derived, structural, impl_def_id) =
394-
type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
395-
match (impls_partial_eq, derived, structural, impl_def_id) {
396-
(_, _, true, _) => {}
397-
(true, false, _, Some(def_id)) if def_id.is_local() => {
398-
self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
399-
}
400-
(true, false, _, _) if ty_def_id.is_local() => {
401-
self.adts_with_manual_partialeq.insert(ty_def_span);
402-
}
403-
(false, _, _, _) if ty_def_id.is_local() => {
404-
self.adts_without_partialeq.insert(ty_def_span);
405-
}
406-
(true, false, _, _) => {
407-
self.manual.push(ty);
408-
}
409-
(false, _, _, _) => {
410-
self.without.push(ty);
411-
}
412-
_ => {}
413-
};
396+
match ty.kind() {
397+
ty::Dynamic(..) => return ControlFlow::Break(()),
398+
ty::FnPtr(..) => return ControlFlow::Continue(()),
399+
ty::Adt(def, _args) => {
400+
let ty_def_id = def.did();
401+
let ty_def_span = self.tcx.def_span(ty_def_id);
402+
let PartialEqImplStatus {
403+
has_impl,
404+
is_derived,
405+
structural_partial_eq,
406+
non_blanket_impl,
407+
} = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
408+
match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) {
409+
(_, _, true, _) => {}
410+
(true, false, _, Some(def_id)) if def_id.is_local() => {
411+
self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
412+
}
413+
(true, false, _, _) if ty_def_id.is_local() => {
414+
self.adts_with_manual_partialeq.insert(ty_def_span);
415+
}
416+
(false, _, _, _) if ty_def_id.is_local() => {
417+
self.adts_without_partialeq.insert(ty_def_span);
418+
}
419+
(true, false, _, _) => {
420+
self.manual.insert(ty);
421+
}
422+
(false, _, _, _) => {
423+
self.without.insert(ty);
424+
}
425+
_ => {}
426+
};
427+
ty.super_visit_with(self)
428+
}
429+
_ => ty.super_visit_with(self),
414430
}
415-
use rustc_middle::ty::TypeSuperVisitable;
416-
ty.super_visit_with(self)
417431
}
418432
}
419433
let mut v = UsedParamsNeedInstantiationVisitor {
420434
tcx,
421435
typing_env,
422436
adts_with_manual_partialeq: FxHashSet::default(),
423437
adts_without_partialeq: FxHashSet::default(),
424-
manual: vec![],
425-
without: vec![],
438+
manual: FxHashSet::default(),
439+
without: FxHashSet::default(),
426440
};
427-
v.visit_ty(ty);
441+
if v.visit_ty(ty).is_break() {
442+
return;
443+
}
428444
#[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
429445
for span in v.adts_with_manual_partialeq {
430446
err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details");
@@ -436,29 +452,38 @@ fn extend_type_not_partial_eq<'tcx>(
436452
"must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
437453
);
438454
}
439-
for ty in v.manual {
455+
#[allow(rustc::potential_query_instability)]
456+
let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect();
457+
manual.sort();
458+
for ty in manual {
440459
err.note(format!(
441460
"`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"
442461
));
443462
}
444-
for ty in v.without {
463+
#[allow(rustc::potential_query_instability)]
464+
let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect();
465+
without.sort();
466+
for ty in without {
445467
err.note(format!(
446468
"`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
447469
));
448470
}
449471
}
450472

473+
#[derive(Debug)]
474+
struct PartialEqImplStatus {
475+
has_impl: bool,
476+
is_derived: bool,
477+
structural_partial_eq: bool,
478+
non_blanket_impl: Option<DefId>,
479+
}
480+
451481
#[instrument(level = "trace", skip(tcx), ret)]
452482
fn type_has_partial_eq_impl<'tcx>(
453483
tcx: TyCtxt<'tcx>,
454484
typing_env: ty::TypingEnv<'tcx>,
455485
ty: Ty<'tcx>,
456-
) -> (
457-
/* has impl */ bool,
458-
/* is derived */ bool,
459-
/* structural partial eq */ bool,
460-
/* non-blanket impl */ Option<DefId>,
461-
) {
486+
) -> PartialEqImplStatus {
462487
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
463488
// double-check there even *is* a semantic `PartialEq` to dispatch to.
464489
//
@@ -495,10 +520,10 @@ fn type_has_partial_eq_impl<'tcx>(
495520
// that patterns can only do things that the code could also do without patterns, but it is
496521
// needed for backwards compatibility. The actual pattern matching compares primitive values,
497522
// `PartialEq::eq` never gets invoked, so there's no risk of us running non-const code.
498-
(
499-
infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
500-
automatically_derived,
501-
structural_peq,
502-
impl_def_id,
503-
)
523+
PartialEqImplStatus {
524+
has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
525+
is_derived: automatically_derived,
526+
structural_partial_eq: structural_peq,
527+
non_blanket_impl: impl_def_id,
528+
}
504529
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![feature(structural_match)]
2+
impl<T: ?Sized> std::marker::StructuralPartialEq for O<T> { }
3+
4+
enum O<T: ?Sized> {
5+
Some(*const T),
6+
None,
7+
}
8+
9+
const C: O<dyn for<'a> Fn(Box<dyn Fn(&'a u8)>)> = O::None;
10+
11+
fn main() {
12+
match O::None {
13+
C => (), //~ ERROR constant of non-structural type
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: constant of non-structural type `O<dyn for<'a> Fn(Box<dyn Fn(&'a u8)>)>` in a pattern
2+
--> $DIR/non_structural_with_escaping_bounds.rs:13:9
3+
|
4+
LL | const C: O<dyn for<'a> Fn(Box<dyn Fn(&'a u8)>)> = O::None;
5+
| ----------------------------------------------- constant defined here
6+
...
7+
LL | C => (),
8+
| ^ constant of non-structural type
9+
|
10+
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
11+
12+
error: aborting due to 1 previous error
13+

0 commit comments

Comments
 (0)