Skip to content

Commit 600607f

Browse files
committed
Move search_for_adt_without_structural_match to ty/mod
1 parent 50ffa79 commit 600607f

File tree

2 files changed

+116
-123
lines changed

2 files changed

+116
-123
lines changed

src/librustc/ty/mod.rs

+115-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub use self::Variance::*;
44
pub use self::AssocItemContainer::*;
55
pub use self::BorrowKind::*;
66
pub use self::IntVarValue::*;
7-
pub use self::fold::TypeFoldable;
7+
pub use self::fold::{TypeFoldable, TypeVisitor};
88

99
use crate::hir::{map as hir_map, GlobMap, TraitMap};
1010
use crate::hir::Node;
@@ -50,7 +50,7 @@ use syntax::symbol::{kw, sym, Symbol, InternedString};
5050
use syntax_pos::Span;
5151

5252
use smallvec;
53-
use rustc_data_structures::fx::FxIndexMap;
53+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
5454
use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
5555
use rustc_index::vec::{Idx, IndexVec};
5656

@@ -3393,6 +3393,119 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
33933393
fn_like.asyncness()
33943394
}
33953395

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

33973510
pub fn provide(providers: &mut ty::query::Providers<'_>) {
33983511
context::provide(providers);

src/librustc_mir/hair/pattern/mod.rs

+1-121
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use rustc::hir::pat_util::EnumerateAndAdjustIterator;
2525
use rustc::hir::ptr::P;
2626

2727
use rustc_index::vec::Idx;
28-
use rustc_data_structures::fx::FxHashSet;
2928

3029
use std::cmp::Ordering;
3130
use std::fmt;
@@ -1000,7 +999,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
1000999
if self.include_lint_checks && !saw_error {
10011000
// If we were able to successfully convert the const to some pat, double-check
10021001
// that the type of the const obeys `#[structural_match]` constraint.
1003-
if let Some(adt_def) = search_for_adt_without_structural_match(self.tcx, cv.ty) {
1002+
if let Some(adt_def) = ty::search_for_adt_without_structural_match(self.tcx, cv.ty) {
10041003

10051004
let path = self.tcx.def_path_str(adt_def.did);
10061005
let msg = format!(
@@ -1169,125 +1168,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
11691168
}
11701169
}
11711170

1172-
/// This method traverses the structure of `ty`, trying to find an
1173-
/// instance of an ADT (i.e. struct or enum) that was declared without
1174-
/// the `#[structural_match]` attribute.
1175-
///
1176-
/// The "structure of a type" includes all components that would be
1177-
/// considered when doing a pattern match on a constant of that
1178-
/// type.
1179-
///
1180-
/// * This means this method descends into fields of structs/enums,
1181-
/// and also descends into the inner type `T` of `&T` and `&mut T`
1182-
///
1183-
/// * The traversal doesn't dereference unsafe pointers (`*const T`,
1184-
/// `*mut T`), and it does not visit the type arguments of an
1185-
/// instantiated generic like `PhantomData<T>`.
1186-
///
1187-
/// The reason we do this search is Rust currently require all ADT's
1188-
/// reachable from a constant's type to be annotated with
1189-
/// `#[structural_match]`, an attribute which essentially says that
1190-
/// the implementation of `PartialEq::eq` behaves *equivalently* to a
1191-
/// comparison against the unfolded structure.
1192-
///
1193-
/// For more background on why Rust has this requirement, and issues
1194-
/// that arose when the requirement was not enforced completely, see
1195-
/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
1196-
fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>,
1197-
ty: Ty<'tcx>)
1198-
-> Option<&'tcx AdtDef>
1199-
{
1200-
// Import here (not mod level), because `TypeFoldable::fold_with`
1201-
// conflicts with `PatternFoldable::fold_with`
1202-
use crate::rustc::ty::fold::TypeVisitor;
1203-
use crate::rustc::ty::TypeFoldable;
1204-
1205-
let mut search = Search { tcx, found: None, seen: FxHashSet::default() };
1206-
ty.visit_with(&mut search);
1207-
return search.found;
1208-
1209-
struct Search<'tcx> {
1210-
tcx: TyCtxt<'tcx>,
1211-
1212-
// records the first ADT we find without `#[structural_match`
1213-
found: Option<&'tcx AdtDef>,
1214-
1215-
// tracks ADT's previously encountered during search, so that
1216-
// we will not recur on them again.
1217-
seen: FxHashSet<hir::def_id::DefId>,
1218-
}
1219-
1220-
impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
1221-
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
1222-
debug!("Search visiting ty: {:?}", ty);
1223-
1224-
let (adt_def, substs) = match ty.kind {
1225-
ty::Adt(adt_def, substs) => (adt_def, substs),
1226-
ty::RawPtr(..) => {
1227-
// `#[structural_match]` ignores substructure of
1228-
// `*const _`/`*mut _`, so skip super_visit_with
1229-
//
1230-
// (But still tell caller to continue search.)
1231-
return false;
1232-
}
1233-
ty::FnDef(..) | ty::FnPtr(..) => {
1234-
// types of formals and return in `fn(_) -> _` are also irrelevant
1235-
//
1236-
// (But still tell caller to continue search.)
1237-
return false;
1238-
}
1239-
ty::Array(_, n) if n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0)
1240-
=> {
1241-
// rust-lang/rust#62336: ignore type of contents
1242-
// for empty array.
1243-
return false;
1244-
}
1245-
_ => {
1246-
ty.super_visit_with(self);
1247-
return false;
1248-
}
1249-
};
1250-
1251-
if !self.tcx.has_attr(adt_def.did, sym::structural_match) {
1252-
self.found = Some(&adt_def);
1253-
debug!("Search found adt_def: {:?}", adt_def);
1254-
return true // Halt visiting!
1255-
}
1256-
1257-
if !self.seen.insert(adt_def.did) {
1258-
debug!("Search already seen adt_def: {:?}", adt_def);
1259-
// let caller continue its search
1260-
return false;
1261-
}
1262-
1263-
// `#[structural_match]` does not care about the
1264-
// instantiation of the generics in an ADT (it
1265-
// instead looks directly at its fields outside
1266-
// this match), so we skip super_visit_with.
1267-
//
1268-
// (Must not recur on substs for `PhantomData<T>` cf
1269-
// rust-lang/rust#55028 and rust-lang/rust#55837; but also
1270-
// want to skip substs when only uses of generic are
1271-
// behind unsafe pointers `*const T`/`*mut T`.)
1272-
1273-
// even though we skip super_visit_with, we must recur on
1274-
// fields of ADT.
1275-
let tcx = self.tcx;
1276-
for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
1277-
if field_ty.visit_with(self) {
1278-
// found an ADT without `#[structural_match]`; halt visiting!
1279-
assert!(self.found.is_some());
1280-
return true;
1281-
}
1282-
}
1283-
1284-
// Even though we do not want to recur on substs, we do
1285-
// want our caller to continue its own search.
1286-
false
1287-
}
1288-
}
1289-
}
1290-
12911171
impl UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> {
12921172
fn tcx(&self) -> TyCtxt<'tcx> {
12931173
self.tcx

0 commit comments

Comments
 (0)