]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | use rustc_data_structures::fx::FxHashSet; |
2 | use rustc_hir as hir; | |
064997fb | 3 | use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; |
dfeec247 | 4 | use rustc_span::Span; |
29967ef6 | 5 | use std::ops::ControlFlow; |
e74abb32 | 6 | |
e74abb32 | 7 | /// This method traverses the structure of `ty`, trying to find an |
ba9703b0 XL |
8 | /// instance of an ADT (i.e. struct or enum) that doesn't implement |
9 | /// the structural-match traits, or a generic type parameter | |
10 | /// (which cannot be determined to be structural-match). | |
e74abb32 XL |
11 | /// |
12 | /// The "structure of a type" includes all components that would be | |
13 | /// considered when doing a pattern match on a constant of that | |
14 | /// type. | |
15 | /// | |
16 | /// * This means this method descends into fields of structs/enums, | |
17 | /// and also descends into the inner type `T` of `&T` and `&mut T` | |
18 | /// | |
19 | /// * The traversal doesn't dereference unsafe pointers (`*const T`, | |
20 | /// `*mut T`), and it does not visit the type arguments of an | |
21 | /// instantiated generic like `PhantomData<T>`. | |
22 | /// | |
23 | /// The reason we do this search is Rust currently require all ADTs | |
ba9703b0 XL |
24 | /// reachable from a constant's type to implement the |
25 | /// structural-match traits, which essentially say that | |
e74abb32 XL |
26 | /// the implementation of `PartialEq::eq` behaves *equivalently* to a |
27 | /// comparison against the unfolded structure. | |
28 | /// | |
29 | /// For more background on why Rust has this requirement, and issues | |
30 | /// that arose when the requirement was not enforced completely, see | |
31 | /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. | |
32 | pub fn search_for_structural_match_violation<'tcx>( | |
e74abb32 XL |
33 | span: Span, |
34 | tcx: TyCtxt<'tcx>, | |
35 | ty: Ty<'tcx>, | |
064997fb FG |
36 | ) -> Option<Ty<'tcx>> { |
37 | ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false }) | |
38 | .break_value() | |
39 | } | |
40 | ||
41 | /// This method traverses the structure of `ty`, trying to find any | |
42 | /// types that are not allowed to be used in a const generic. | |
43 | /// | |
44 | /// This is either because the type does not implement `StructuralEq` | |
45 | /// and `StructuralPartialEq`, or because the type is intentionally | |
46 | /// not supported in const generics (such as floats and raw pointers, | |
47 | /// which are allowed in match blocks). | |
48 | pub fn search_for_adt_const_param_violation<'tcx>( | |
49 | span: Span, | |
50 | tcx: TyCtxt<'tcx>, | |
51 | ty: Ty<'tcx>, | |
52 | ) -> Option<Ty<'tcx>> { | |
53 | ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true }) | |
54 | .break_value() | |
e74abb32 XL |
55 | } |
56 | ||
e74abb32 XL |
57 | /// This implements the traversal over the structure of a given type to try to |
58 | /// find instances of ADTs (specifically structs or enums) that do not implement | |
59 | /// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). | |
064997fb | 60 | struct Search<'tcx> { |
e74abb32 XL |
61 | span: Span, |
62 | ||
064997fb | 63 | tcx: TyCtxt<'tcx>, |
e74abb32 | 64 | |
e74abb32 XL |
65 | /// Tracks ADTs previously encountered during search, so that |
66 | /// we will not recur on them again. | |
67 | seen: FxHashSet<hir::def_id::DefId>, | |
e74abb32 | 68 | |
064997fb FG |
69 | // Additionally deny things that have been allowed in patterns, |
70 | // but are not allowed in adt const params, such as floats and | |
71 | // fn ptrs. | |
72 | adt_const_param: bool, | |
73 | } | |
e74abb32 | 74 | |
064997fb | 75 | impl<'tcx> Search<'tcx> { |
e74abb32 | 76 | fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { |
064997fb | 77 | adt_ty.is_structural_eq_shallow(self.tcx) |
e74abb32 XL |
78 | } |
79 | } | |
80 | ||
9ffffee4 | 81 | impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> { |
064997fb | 82 | type BreakTy = Ty<'tcx>; |
fc512014 XL |
83 | |
84 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { | |
e74abb32 XL |
85 | debug!("Search visiting ty: {:?}", ty); |
86 | ||
1b1a35ee | 87 | let (adt_def, substs) = match *ty.kind() { |
e74abb32 XL |
88 | ty::Adt(adt_def, substs) => (adt_def, substs), |
89 | ty::Param(_) => { | |
064997fb | 90 | return ControlFlow::Break(ty); |
e74abb32 | 91 | } |
f9f354fc | 92 | ty::Dynamic(..) => { |
064997fb | 93 | return ControlFlow::Break(ty); |
f9f354fc XL |
94 | } |
95 | ty::Foreign(_) => { | |
064997fb | 96 | return ControlFlow::Break(ty); |
f9f354fc | 97 | } |
9c376795 | 98 | ty::Alias(..) => { |
064997fb | 99 | return ControlFlow::Break(ty); |
f9f354fc | 100 | } |
3c0e092e | 101 | ty::Closure(..) => { |
064997fb | 102 | return ControlFlow::Break(ty); |
3c0e092e | 103 | } |
9ffffee4 | 104 | ty::Generator(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => { |
064997fb | 105 | return ControlFlow::Break(ty); |
f9f354fc | 106 | } |
064997fb | 107 | ty::FnDef(..) => { |
f9f354fc | 108 | // Types of formals and return in `fn(_) -> _` are also irrelevant; |
e74abb32 | 109 | // so we do not recur into them via `super_visit_with` |
9c376795 | 110 | return ControlFlow::Continue(()); |
e74abb32 | 111 | } |
dfeec247 | 112 | ty::Array(_, n) |
9ffffee4 | 113 | if { n.try_eval_target_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } => |
dfeec247 | 114 | { |
e74abb32 XL |
115 | // rust-lang/rust#62336: ignore type of contents |
116 | // for empty array. | |
9c376795 | 117 | return ControlFlow::Continue(()); |
e74abb32 | 118 | } |
064997fb | 119 | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => { |
f9f354fc XL |
120 | // These primitive types are always structural match. |
121 | // | |
122 | // `Never` is kind of special here, but as it is not inhabitable, this should be fine. | |
9c376795 | 123 | return ControlFlow::Continue(()); |
f9f354fc XL |
124 | } |
125 | ||
064997fb FG |
126 | ty::FnPtr(..) => { |
127 | if !self.adt_const_param { | |
9c376795 | 128 | return ControlFlow::Continue(()); |
064997fb FG |
129 | } else { |
130 | return ControlFlow::Break(ty); | |
131 | } | |
132 | } | |
133 | ||
134 | ty::RawPtr(..) => { | |
135 | if !self.adt_const_param { | |
136 | // structural-match ignores substructure of | |
137 | // `*const _`/`*mut _`, so skip `super_visit_with`. | |
138 | // | |
139 | // For example, if you have: | |
140 | // ``` | |
141 | // struct NonStructural; | |
142 | // #[derive(PartialEq, Eq)] | |
143 | // struct T(*const NonStructural); | |
144 | // const C: T = T(std::ptr::null()); | |
145 | // ``` | |
146 | // | |
147 | // Even though `NonStructural` does not implement `PartialEq`, | |
148 | // structural equality on `T` does not recur into the raw | |
149 | // pointer. Therefore, one can still use `C` in a pattern. | |
9c376795 | 150 | return ControlFlow::Continue(()); |
064997fb FG |
151 | } else { |
152 | return ControlFlow::Break(ty); | |
153 | } | |
154 | } | |
155 | ||
156 | ty::Float(_) => { | |
157 | if !self.adt_const_param { | |
9c376795 | 158 | return ControlFlow::Continue(()); |
064997fb FG |
159 | } else { |
160 | return ControlFlow::Break(ty); | |
161 | } | |
162 | } | |
163 | ||
f9f354fc XL |
164 | ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { |
165 | // First check all contained types and then tell the caller to continue searching. | |
fc512014 | 166 | return ty.super_visit_with(self); |
e74abb32 | 167 | } |
3c0e092e | 168 | ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { |
f9f354fc XL |
169 | bug!("unexpected type during structural-match checking: {:?}", ty); |
170 | } | |
f035d41b | 171 | ty::Error(_) => { |
064997fb | 172 | self.tcx.sess.delay_span_bug(self.span, "ty::Error in structural-match check"); |
f9f354fc XL |
173 | // We still want to check other types after encountering an error, |
174 | // as this may still emit relevant errors. | |
9c376795 | 175 | return ControlFlow::Continue(()); |
f9f354fc | 176 | } |
e74abb32 XL |
177 | }; |
178 | ||
5e7ed085 | 179 | if !self.seen.insert(adt_def.did()) { |
e74abb32 | 180 | debug!("Search already seen adt_def: {:?}", adt_def); |
9c376795 | 181 | return ControlFlow::Continue(()); |
e74abb32 XL |
182 | } |
183 | ||
184 | if !self.type_marked_structural(ty) { | |
185 | debug!("Search found ty: {:?}", ty); | |
064997fb | 186 | return ControlFlow::Break(ty); |
e74abb32 XL |
187 | } |
188 | ||
189 | // structural-match does not care about the | |
190 | // instantiation of the generics in an ADT (it | |
191 | // instead looks directly at its fields outside | |
192 | // this match), so we skip super_visit_with. | |
193 | // | |
194 | // (Must not recur on substs for `PhantomData<T>` cf | |
195 | // rust-lang/rust#55028 and rust-lang/rust#55837; but also | |
196 | // want to skip substs when only uses of generic are | |
197 | // behind unsafe pointers `*const T`/`*mut T`.) | |
198 | ||
199 | // even though we skip super_visit_with, we must recur on | |
200 | // fields of ADT. | |
064997fb | 201 | let tcx = self.tcx; |
fc512014 | 202 | adt_def.all_fields().map(|field| field.ty(tcx, substs)).try_for_each(|field_ty| { |
064997fb | 203 | let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty); |
f9f354fc | 204 | debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty); |
fc512014 XL |
205 | ty.visit_with(self) |
206 | }) | |
e74abb32 XL |
207 | } |
208 | } |