]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_transmute/src/maybe_transmutable/mod.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_transmute / src / maybe_transmutable / mod.rs
CommitLineData
064997fb
FG
1use crate::Map;
2use crate::{Answer, Reason};
3
4#[cfg(test)]
5mod tests;
6
7mod query_context;
8use query_context::QueryContext;
9
10use crate::layout::{self, dfa, Byte, Dfa, Nfa, Tree, Uninhabited};
11pub(crate) struct MaybeTransmutableQuery<L, C>
12where
13 C: QueryContext,
14{
15 src: L,
16 dst: L,
17 scope: <C as QueryContext>::Scope,
18 assume: crate::Assume,
19 context: C,
20}
21
22impl<L, C> MaybeTransmutableQuery<L, C>
23where
24 C: QueryContext,
25{
26 pub(crate) fn new(
27 src: L,
28 dst: L,
29 scope: <C as QueryContext>::Scope,
30 assume: crate::Assume,
31 context: C,
32 ) -> Self {
33 Self { src, dst, scope, assume, context }
34 }
35
36 pub(crate) fn map_layouts<F, M>(
37 self,
38 f: F,
39 ) -> Result<MaybeTransmutableQuery<M, C>, Answer<<C as QueryContext>::Ref>>
40 where
41 F: FnOnce(
42 L,
43 L,
44 <C as QueryContext>::Scope,
45 &C,
46 ) -> Result<(M, M), Answer<<C as QueryContext>::Ref>>,
47 {
48 let Self { src, dst, scope, assume, context } = self;
49
50 let (src, dst) = f(src, dst, scope, &context)?;
51
52 Ok(MaybeTransmutableQuery { src, dst, scope, assume, context })
53 }
54}
55
56#[cfg(feature = "rustc")]
57mod rustc {
58 use super::*;
353b0b11 59 use crate::layout::tree::rustc::Err;
064997fb
FG
60
61 use rustc_middle::ty::Ty;
62 use rustc_middle::ty::TyCtxt;
63
64 impl<'tcx> MaybeTransmutableQuery<Ty<'tcx>, TyCtxt<'tcx>> {
65 /// This method begins by converting `src` and `dst` from `Ty`s to `Tree`s,
66 /// then computes an answer using those trees.
67 #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
68 pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
69 let query_or_answer = self.map_layouts(|src, dst, scope, &context| {
70 // Convert `src` and `dst` from their rustc representations, to `Tree`-based
71 // representations. If these conversions fail, conclude that the transmutation is
72 // unacceptable; the layouts of both the source and destination types must be
73 // well-defined.
353b0b11
FG
74 let src = Tree::from_ty(src, context);
75 let dst = Tree::from_ty(dst, context);
064997fb 76
353b0b11
FG
77 match (src, dst) {
78 // Answer `Yes` here, because 'unknown layout' and type errors will already
79 // be reported by rustc. No need to spam the user with more errors.
80 (Err(Err::TypeError(_)), _) => Err(Answer::Yes),
81 (_, Err(Err::TypeError(_))) => Err(Answer::Yes),
82 (Err(Err::Unknown), _) => Err(Answer::Yes),
83 (_, Err(Err::Unknown)) => Err(Answer::Yes),
84 (Err(Err::Unspecified), _) => Err(Answer::No(Reason::SrcIsUnspecified)),
85 (_, Err(Err::Unspecified)) => Err(Answer::No(Reason::DstIsUnspecified)),
86 (Ok(src), Ok(dst)) => Ok((src, dst)),
87 }
064997fb
FG
88 });
89
90 match query_or_answer {
91 Ok(query) => query.answer(),
92 Err(answer) => answer,
93 }
94 }
95 }
96}
97
98impl<C> MaybeTransmutableQuery<Tree<<C as QueryContext>::Def, <C as QueryContext>::Ref>, C>
99where
100 C: QueryContext,
101{
102 /// Answers whether a `Tree` is transmutable into another `Tree`.
103 ///
104 /// This method begins by de-def'ing `src` and `dst`, and prunes private paths from `dst`,
105 /// then converts `src` and `dst` to `Nfa`s, and computes an answer using those NFAs.
106 #[inline(always)]
107 #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
108 pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
f2b60f7d 109 let assume_visibility = self.assume.safety;
064997fb
FG
110 let query_or_answer = self.map_layouts(|src, dst, scope, context| {
111 // Remove all `Def` nodes from `src`, without checking their visibility.
112 let src = src.prune(&|def| true);
113
f2b60f7d 114 trace!(?src, "pruned src");
064997fb
FG
115
116 // Remove all `Def` nodes from `dst`, additionally...
117 let dst = if assume_visibility {
118 // ...if visibility is assumed, don't check their visibility.
119 dst.prune(&|def| true)
120 } else {
121 // ...otherwise, prune away all unreachable paths through the `Dst` layout.
122 dst.prune(&|def| context.is_accessible_from(def, scope))
123 };
124
f2b60f7d 125 trace!(?dst, "pruned dst");
064997fb
FG
126
127 // Convert `src` from a tree-based representation to an NFA-based representation.
128 // If the conversion fails because `src` is uninhabited, conclude that the transmutation
129 // is acceptable, because instances of the `src` type do not exist.
130 let src = Nfa::from_tree(src).map_err(|Uninhabited| Answer::Yes)?;
131
132 // Convert `dst` from a tree-based representation to an NFA-based representation.
133 // If the conversion fails because `src` is uninhabited, conclude that the transmutation
134 // is unacceptable, because instances of the `dst` type do not exist.
135 let dst =
136 Nfa::from_tree(dst).map_err(|Uninhabited| Answer::No(Reason::DstIsPrivate))?;
137
138 Ok((src, dst))
139 });
140
141 match query_or_answer {
142 Ok(query) => query.answer(),
143 Err(answer) => answer,
144 }
145 }
146}
147
148impl<C> MaybeTransmutableQuery<Nfa<<C as QueryContext>::Ref>, C>
149where
150 C: QueryContext,
151{
152 /// Answers whether a `Nfa` is transmutable into another `Nfa`.
153 ///
154 /// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs.
155 #[inline(always)]
156 #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
157 pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
158 let query_or_answer = self
159 .map_layouts(|src, dst, scope, context| Ok((Dfa::from_nfa(src), Dfa::from_nfa(dst))));
160
161 match query_or_answer {
162 Ok(query) => query.answer(),
163 Err(answer) => answer,
164 }
165 }
166}
167
168impl<C> MaybeTransmutableQuery<Dfa<<C as QueryContext>::Ref>, C>
169where
170 C: QueryContext,
171{
172 /// Answers whether a `Nfa` is transmutable into another `Nfa`.
173 ///
174 /// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs.
175 pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
176 MaybeTransmutableQuery {
177 src: &self.src,
178 dst: &self.dst,
179 scope: self.scope,
180 assume: self.assume,
181 context: self.context,
182 }
183 .answer()
184 }
185}
186
187impl<'l, C> MaybeTransmutableQuery<&'l Dfa<<C as QueryContext>::Ref>, C>
188where
189 C: QueryContext,
190{
191 pub(crate) fn answer(&mut self) -> Answer<<C as QueryContext>::Ref> {
192 self.answer_memo(&mut Map::default(), self.src.start, self.dst.start)
193 }
194
195 #[inline(always)]
196 #[instrument(level = "debug", skip(self))]
197 fn answer_memo(
198 &self,
199 cache: &mut Map<(dfa::State, dfa::State), Answer<<C as QueryContext>::Ref>>,
200 src_state: dfa::State,
201 dst_state: dfa::State,
202 ) -> Answer<<C as QueryContext>::Ref> {
203 if let Some(answer) = cache.get(&(src_state, dst_state)) {
204 answer.clone()
205 } else {
206 let answer = if dst_state == self.dst.accepting {
207 // truncation: `size_of(Src) >= size_of(Dst)`
208 Answer::Yes
209 } else if src_state == self.src.accepting {
210 // extension: `size_of(Src) >= size_of(Dst)`
211 if let Some(dst_state_prime) = self.dst.byte_from(dst_state, Byte::Uninit) {
212 self.answer_memo(cache, src_state, dst_state_prime)
213 } else {
214 Answer::No(Reason::DstIsTooBig)
215 }
216 } else {
217 let src_quantification = if self.assume.validity {
218 // if the compiler may assume that the programmer is doing additional validity checks,
219 // (e.g.: that `src != 3u8` when the destination type is `bool`)
220 // then there must exist at least one transition out of `src_state` such that the transmute is viable...
221 there_exists
222 } else {
223 // if the compiler cannot assume that the programmer is doing additional validity checks,
224 // then for all transitions out of `src_state`, such that the transmute is viable...
225 // then there must exist at least one transition out of `src_state` such that the transmute is viable...
226 for_all
227 };
228
229 src_quantification(
230 self.src.bytes_from(src_state).unwrap_or(&Map::default()),
231 |(&src_validity, &src_state_prime)| {
232 if let Some(dst_state_prime) = self.dst.byte_from(dst_state, src_validity) {
233 self.answer_memo(cache, src_state_prime, dst_state_prime)
234 } else if let Some(dst_state_prime) =
235 self.dst.byte_from(dst_state, Byte::Uninit)
236 {
237 self.answer_memo(cache, src_state_prime, dst_state_prime)
238 } else {
239 Answer::No(Reason::DstIsBitIncompatible)
240 }
241 },
242 )
243 };
244 cache.insert((src_state, dst_state), answer.clone());
245 answer
246 }
247 }
248}
249
250impl<R> Answer<R>
251where
252 R: layout::Ref,
253{
254 pub(crate) fn and(self, rhs: Self) -> Self {
255 match (self, rhs) {
256 (Self::No(reason), _) | (_, Self::No(reason)) => Self::No(reason),
257 (Self::Yes, Self::Yes) => Self::Yes,
258 (Self::IfAll(mut lhs), Self::IfAll(ref mut rhs)) => {
259 lhs.append(rhs);
260 Self::IfAll(lhs)
261 }
262 (constraint, Self::IfAll(mut constraints))
263 | (Self::IfAll(mut constraints), constraint) => {
264 constraints.push(constraint);
265 Self::IfAll(constraints)
266 }
267 (lhs, rhs) => Self::IfAll(vec![lhs, rhs]),
268 }
269 }
270
271 pub(crate) fn or(self, rhs: Self) -> Self {
272 match (self, rhs) {
273 (Self::Yes, _) | (_, Self::Yes) => Self::Yes,
274 (Self::No(lhr), Self::No(rhr)) => Self::No(lhr),
275 (Self::IfAny(mut lhs), Self::IfAny(ref mut rhs)) => {
276 lhs.append(rhs);
277 Self::IfAny(lhs)
278 }
279 (constraint, Self::IfAny(mut constraints))
280 | (Self::IfAny(mut constraints), constraint) => {
281 constraints.push(constraint);
282 Self::IfAny(constraints)
283 }
284 (lhs, rhs) => Self::IfAny(vec![lhs, rhs]),
285 }
286 }
287}
288
289pub fn for_all<R, I, F>(iter: I, f: F) -> Answer<R>
290where
291 R: layout::Ref,
292 I: IntoIterator,
293 F: FnMut(<I as IntoIterator>::Item) -> Answer<R>,
294{
295 use std::ops::ControlFlow::{Break, Continue};
296 let (Continue(result) | Break(result)) =
297 iter.into_iter().map(f).try_fold(Answer::Yes, |constraints, constraint| {
298 match constraint.and(constraints) {
299 Answer::No(reason) => Break(Answer::No(reason)),
300 maybe => Continue(maybe),
301 }
302 });
303 result
304}
305
306pub fn there_exists<R, I, F>(iter: I, f: F) -> Answer<R>
307where
308 R: layout::Ref,
309 I: IntoIterator,
310 F: FnMut(<I as IntoIterator>::Item) -> Answer<R>,
311{
312 use std::ops::ControlFlow::{Break, Continue};
313 let (Continue(result) | Break(result)) = iter.into_iter().map(f).try_fold(
314 Answer::No(Reason::DstIsBitIncompatible),
315 |constraints, constraint| match constraint.or(constraints) {
316 Answer::Yes => Break(Answer::Yes),
317 maybe => Continue(maybe),
318 },
319 );
320 result
321}