Skip to content

Commit 4f7b922

Browse files
committed
refactor: move structural_match analysis into its own module.
1 parent f0e370f commit 4f7b922

File tree

2 files changed

+135
-125
lines changed

2 files changed

+135
-125
lines changed

src/librustc/ty/mod.rs

Lines changed: 4 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use syntax::symbol::{kw, sym, Symbol};
5151
use syntax_pos::Span;
5252

5353
use smallvec;
54-
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
54+
use rustc_data_structures::fx::{FxIndexMap};
5555
use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
5656
use rustc_index::vec::{Idx, IndexVec};
5757

@@ -84,6 +84,8 @@ pub use self::context::{
8484

8585
pub use self::instance::{Instance, InstanceDef};
8686

87+
pub use self::structural_match::{search_for_structural_match_violation, NonStructuralMatchTy};
88+
8789
pub use self::trait_def::TraitDef;
8890

8991
pub use self::query::queries;
@@ -116,6 +118,7 @@ pub mod util;
116118
mod context;
117119
mod instance;
118120
mod structural_impls;
121+
mod structural_match;
119122
mod sty;
120123

121124
// Data types
@@ -3395,130 +3398,6 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
33953398
fn_like.asyncness()
33963399
}
33973400

3398-
pub enum NonStructuralMatchTy<'tcx> {
3399-
Adt(&'tcx AdtDef),
3400-
Param,
3401-
}
3402-
3403-
/// This method traverses the structure of `ty`, trying to find an
3404-
/// instance of an ADT (i.e. struct or enum) that was declared without
3405-
/// the `#[structural_match]` attribute, or a generic type parameter
3406-
/// (which cannot be determined to be `structural_match`).
3407-
///
3408-
/// The "structure of a type" includes all components that would be
3409-
/// considered when doing a pattern match on a constant of that
3410-
/// type.
3411-
///
3412-
/// * This means this method descends into fields of structs/enums,
3413-
/// and also descends into the inner type `T` of `&T` and `&mut T`
3414-
///
3415-
/// * The traversal doesn't dereference unsafe pointers (`*const T`,
3416-
/// `*mut T`), and it does not visit the type arguments of an
3417-
/// instantiated generic like `PhantomData<T>`.
3418-
///
3419-
/// The reason we do this search is Rust currently require all ADTs
3420-
/// reachable from a constant's type to be annotated with
3421-
/// `#[structural_match]`, an attribute which essentially says that
3422-
/// the implementation of `PartialEq::eq` behaves *equivalently* to a
3423-
/// comparison against the unfolded structure.
3424-
///
3425-
/// For more background on why Rust has this requirement, and issues
3426-
/// that arose when the requirement was not enforced completely, see
3427-
/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
3428-
pub fn search_for_structural_match_violation<'tcx>(
3429-
tcx: TyCtxt<'tcx>,
3430-
ty: Ty<'tcx>,
3431-
) -> Option<NonStructuralMatchTy<'tcx>> {
3432-
let mut search = Search { tcx, found: None, seen: FxHashSet::default() };
3433-
ty.visit_with(&mut search);
3434-
return search.found;
3435-
3436-
struct Search<'tcx> {
3437-
tcx: TyCtxt<'tcx>,
3438-
3439-
// Records the first ADT or type parameter we find without `#[structural_match`.
3440-
found: Option<NonStructuralMatchTy<'tcx>>,
3441-
3442-
// Tracks ADTs previously encountered during search, so that
3443-
// we will not recurse on them again.
3444-
seen: FxHashSet<hir::def_id::DefId>,
3445-
}
3446-
3447-
impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
3448-
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
3449-
debug!("Search visiting ty: {:?}", ty);
3450-
3451-
let (adt_def, substs) = match ty.kind {
3452-
ty::Adt(adt_def, substs) => (adt_def, substs),
3453-
ty::Param(_) => {
3454-
self.found = Some(NonStructuralMatchTy::Param);
3455-
return true; // Stop visiting.
3456-
}
3457-
ty::RawPtr(..) => {
3458-
// `#[structural_match]` ignores substructure of
3459-
// `*const _`/`*mut _`, so skip super_visit_with
3460-
//
3461-
// (But still tell caller to continue search.)
3462-
return false;
3463-
}
3464-
ty::FnDef(..) | ty::FnPtr(..) => {
3465-
// types of formals and return in `fn(_) -> _` are also irrelevant
3466-
//
3467-
// (But still tell caller to continue search.)
3468-
return false;
3469-
}
3470-
ty::Array(_, n) if n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0)
3471-
=> {
3472-
// rust-lang/rust#62336: ignore type of contents
3473-
// for empty array.
3474-
return false;
3475-
}
3476-
_ => {
3477-
ty.super_visit_with(self);
3478-
return false;
3479-
}
3480-
};
3481-
3482-
if !self.tcx.has_attr(adt_def.did, sym::structural_match) {
3483-
self.found = Some(NonStructuralMatchTy::Adt(&adt_def));
3484-
debug!("Search found adt_def: {:?}", adt_def);
3485-
return true; // Stop visiting.
3486-
}
3487-
3488-
if !self.seen.insert(adt_def.did) {
3489-
debug!("Search already seen adt_def: {:?}", adt_def);
3490-
// let caller continue its search
3491-
return false;
3492-
}
3493-
3494-
// `#[structural_match]` does not care about the
3495-
// instantiation of the generics in an ADT (it
3496-
// instead looks directly at its fields outside
3497-
// this match), so we skip super_visit_with.
3498-
//
3499-
// (Must not recur on substs for `PhantomData<T>` cf
3500-
// rust-lang/rust#55028 and rust-lang/rust#55837; but also
3501-
// want to skip substs when only uses of generic are
3502-
// behind unsafe pointers `*const T`/`*mut T`.)
3503-
3504-
// even though we skip super_visit_with, we must recur on
3505-
// fields of ADT.
3506-
let tcx = self.tcx;
3507-
for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
3508-
if field_ty.visit_with(self) {
3509-
// found an ADT without `#[structural_match]`; halt visiting!
3510-
assert!(self.found.is_some());
3511-
return true;
3512-
}
3513-
}
3514-
3515-
// Even though we do not want to recur on substs, we do
3516-
// want our caller to continue its own search.
3517-
false
3518-
}
3519-
}
3520-
}
3521-
35223401
pub fn provide(providers: &mut ty::query::Providers<'_>) {
35233402
context::provide(providers);
35243403
erase_regions::provide(providers);

src/librustc/ty/structural_match.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use crate::hir;
2+
use rustc_data_structures::fx::{FxHashSet};
3+
4+
use syntax::symbol::{sym};
5+
6+
use crate::ty::{self, AdtDef, Ty, TyCtxt};
7+
use crate::ty::fold::{TypeFoldable, TypeVisitor};
8+
9+
pub enum NonStructuralMatchTy<'tcx> {
10+
Adt(&'tcx AdtDef),
11+
Param,
12+
}
13+
14+
/// This method traverses the structure of `ty`, trying to find an
15+
/// instance of an ADT (i.e. struct or enum) that was declared without
16+
/// the `#[structural_match]` attribute, or a generic type parameter
17+
/// (which cannot be determined to be `structural_match`).
18+
///
19+
/// The "structure of a type" includes all components that would be
20+
/// considered when doing a pattern match on a constant of that
21+
/// type.
22+
///
23+
/// * This means this method descends into fields of structs/enums,
24+
/// and also descends into the inner type `T` of `&T` and `&mut T`
25+
///
26+
/// * The traversal doesn't dereference unsafe pointers (`*const T`,
27+
/// `*mut T`), and it does not visit the type arguments of an
28+
/// instantiated generic like `PhantomData<T>`.
29+
///
30+
/// The reason we do this search is Rust currently require all ADTs
31+
/// reachable from a constant's type to be annotated with
32+
/// `#[structural_match]`, an attribute which essentially says that
33+
/// the implementation of `PartialEq::eq` behaves *equivalently* to a
34+
/// comparison against the unfolded structure.
35+
///
36+
/// For more background on why Rust has this requirement, and issues
37+
/// that arose when the requirement was not enforced completely, see
38+
/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
39+
pub fn search_for_structural_match_violation<'tcx>(
40+
tcx: TyCtxt<'tcx>,
41+
ty: Ty<'tcx>,
42+
) -> Option<NonStructuralMatchTy<'tcx>> {
43+
let mut search = Search { tcx, found: None, seen: FxHashSet::default() };
44+
ty.visit_with(&mut search);
45+
return search.found;
46+
47+
struct Search<'tcx> {
48+
tcx: TyCtxt<'tcx>,
49+
50+
// Records the first ADT or type parameter we find without `#[structural_match`.
51+
found: Option<NonStructuralMatchTy<'tcx>>,
52+
53+
// Tracks ADTs previously encountered during search, so that
54+
// we will not recurse on them again.
55+
seen: FxHashSet<hir::def_id::DefId>,
56+
}
57+
58+
impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
59+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
60+
debug!("Search visiting ty: {:?}", ty);
61+
62+
let (adt_def, substs) = match ty.kind {
63+
ty::Adt(adt_def, substs) => (adt_def, substs),
64+
ty::Param(_) => {
65+
self.found = Some(NonStructuralMatchTy::Param);
66+
return true; // Stop visiting.
67+
}
68+
ty::RawPtr(..) => {
69+
// `#[structural_match]` ignores substructure of
70+
// `*const _`/`*mut _`, so skip super_visit_with
71+
//
72+
// (But still tell caller to continue search.)
73+
return false;
74+
}
75+
ty::FnDef(..) | ty::FnPtr(..) => {
76+
// types of formals and return in `fn(_) -> _` are also irrelevant
77+
//
78+
// (But still tell caller to continue search.)
79+
return false;
80+
}
81+
ty::Array(_, n) if n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0)
82+
=> {
83+
// rust-lang/rust#62336: ignore type of contents
84+
// for empty array.
85+
return false;
86+
}
87+
_ => {
88+
ty.super_visit_with(self);
89+
return false;
90+
}
91+
};
92+
93+
if !self.tcx.has_attr(adt_def.did, sym::structural_match) {
94+
self.found = Some(NonStructuralMatchTy::Adt(&adt_def));
95+
debug!("Search found adt_def: {:?}", adt_def);
96+
return true; // Stop visiting.
97+
}
98+
99+
if !self.seen.insert(adt_def.did) {
100+
debug!("Search already seen adt_def: {:?}", adt_def);
101+
// let caller continue its search
102+
return false;
103+
}
104+
105+
// `#[structural_match]` does not care about the
106+
// instantiation of the generics in an ADT (it
107+
// instead looks directly at its fields outside
108+
// this match), so we skip super_visit_with.
109+
//
110+
// (Must not recur on substs for `PhantomData<T>` cf
111+
// rust-lang/rust#55028 and rust-lang/rust#55837; but also
112+
// want to skip substs when only uses of generic are
113+
// behind unsafe pointers `*const T`/`*mut T`.)
114+
115+
// even though we skip super_visit_with, we must recur on
116+
// fields of ADT.
117+
let tcx = self.tcx;
118+
for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
119+
if field_ty.visit_with(self) {
120+
// found an ADT without `#[structural_match]`; halt visiting!
121+
assert!(self.found.is_some());
122+
return true;
123+
}
124+
}
125+
126+
// Even though we do not want to recur on substs, we do
127+
// want our caller to continue its own search.
128+
false
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)