flux_rustc_bridge/
lowering.rs

1use flux_arc_interner::List;
2use flux_common::result::ResultExt;
3use flux_errors::FluxSession;
4use itertools::Itertools;
5use rustc_borrowck::consumers::BodyWithBorrowckFacts;
6use rustc_errors::ErrorGuaranteed;
7use rustc_hir::def_id::{DefId, LocalDefId};
8use rustc_infer::{
9    infer::{InferCtxt, TyCtxtInferExt},
10    traits::Obligation,
11};
12use rustc_macros::{Decodable, Encodable};
13use rustc_middle::{
14    mir::{self as rustc_mir},
15    traits::{ImplSource, ObligationCause},
16    ty::{
17        self as rustc_ty, GenericArgKind, ParamConst, ParamEnv, TyCtxt, TypingMode,
18        adjustment as rustc_adjustment,
19    },
20};
21use rustc_span::Span;
22use rustc_trait_selection::traits::SelectionContext;
23
24use super::{
25    mir::{
26        AggregateKind, AssertKind, BasicBlockData, BinOp, Body, CallArgs, CastKind, LocalDecl,
27        NonDivergingIntrinsic, NullOp, Operand, Place, PlaceElem, PointerCast, Rvalue, Statement,
28        StatementKind, Terminator, TerminatorKind,
29    },
30    ty::{
31        AdtDef, AdtDefData, AliasKind, Binder, BoundRegion, BoundVariableKind, Clause, ClauseKind,
32        Const, ConstKind, ExistentialPredicate, ExistentialProjection, FieldDef, FnSig, GenericArg,
33        GenericParamDef, GenericParamDefKind, GenericPredicates, Generics, OutlivesPredicate,
34        TraitPredicate, TraitRef, Ty, TypeOutlivesPredicate, UnevaluatedConst, VariantDef,
35    },
36};
37use crate::{
38    mir::{BodyRoot, CallKind, ConstOperand},
39    ty::{
40        AliasTy, ExistentialTraitRef, GenericArgs, ProjectionPredicate, Region,
41        RegionOutlivesPredicate,
42    },
43};
44
45pub trait Lower<'tcx> {
46    type R;
47
48    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R;
49}
50
51pub struct MirLoweringCtxt<'a, 'sess, 'tcx> {
52    tcx: TyCtxt<'tcx>,
53    param_env: ParamEnv<'tcx>,
54    selcx: SelectionContext<'a, 'tcx>,
55    sess: &'sess FluxSession,
56    rustc_mir: &'a rustc_mir::Body<'tcx>,
57}
58
59#[derive(Debug, Clone)]
60pub struct UnsupportedReason {
61    pub(crate) descr: String,
62}
63
64impl UnsupportedReason {
65    fn new(reason: impl ToString) -> Self {
66        UnsupportedReason { descr: reason.to_string() }
67    }
68
69    pub fn into_err(self) -> UnsupportedErr {
70        UnsupportedErr { descr: self.descr, span: None }
71    }
72}
73
74#[derive(Debug, Clone, Encodable, Decodable)]
75pub struct UnsupportedErr {
76    pub descr: String,
77    pub span: Option<Span>,
78}
79
80impl UnsupportedErr {
81    pub fn new(reason: UnsupportedReason) -> Self {
82        UnsupportedErr { descr: reason.descr, span: None }
83    }
84
85    pub fn with_span(mut self, span: Span) -> Self {
86        self.span = Some(span);
87        self
88    }
89}
90
91fn trait_ref_impl_id<'tcx>(
92    tcx: TyCtxt<'tcx>,
93    selcx: &mut SelectionContext<'_, 'tcx>,
94    param_env: ParamEnv<'tcx>,
95    trait_ref: rustc_ty::TraitRef<'tcx>,
96) -> Option<(DefId, rustc_middle::ty::GenericArgsRef<'tcx>)> {
97    let trait_ref = tcx.erase_and_anonymize_regions(trait_ref);
98    let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
99    let impl_source = selcx.select(&obligation).ok()??;
100    let impl_source = selcx.infcx.resolve_vars_if_possible(impl_source);
101    // let impl_source = selcx.infcx.fully_resolve(impl_source).ok()?;
102    let ImplSource::UserDefined(impl_data) = impl_source else { return None };
103    Some((impl_data.impl_def_id, impl_data.args))
104}
105
106pub fn resolve_trait_ref_impl_id<'tcx>(
107    tcx: TyCtxt<'tcx>,
108    def_id: DefId,
109    trait_ref: rustc_ty::TraitRef<'tcx>,
110) -> Option<(DefId, rustc_middle::ty::GenericArgsRef<'tcx>)> {
111    let param_env = tcx.param_env(def_id);
112    let infcx = tcx
113        .infer_ctxt()
114        .with_next_trait_solver(true)
115        .build(TypingMode::non_body_analysis());
116    trait_ref_impl_id(tcx, &mut SelectionContext::new(&infcx), param_env, trait_ref)
117}
118
119fn resolve_call_query<'tcx>(
120    tcx: TyCtxt<'tcx>,
121    selcx: &mut SelectionContext<'_, 'tcx>,
122    param_env: ParamEnv<'tcx>,
123    callee_id: DefId,
124    args: rustc_middle::ty::GenericArgsRef<'tcx>,
125) -> Option<(DefId, rustc_middle::ty::GenericArgsRef<'tcx>)> {
126    let trait_id = tcx.trait_of_assoc(callee_id)?;
127    let trait_ref = rustc_ty::TraitRef::from_assoc(tcx, trait_id, args);
128    let (impl_def_id, impl_args) = trait_ref_impl_id(tcx, selcx, param_env, trait_ref)?;
129    let impl_args = args.rebase_onto(tcx, trait_id, impl_args);
130    let assoc_id = tcx.impl_item_implementor_ids(impl_def_id).get(&callee_id)?;
131    let assoc_item = tcx.associated_item(assoc_id);
132    Some((assoc_item.def_id, impl_args))
133}
134
135impl<'sess, 'tcx> MirLoweringCtxt<'_, 'sess, 'tcx> {
136    // We used to call `replicate_infer_ctxt` to compute the `infcx`
137    // which replicated the [`InferCtxt`] used for mir typeck
138    // by generating region variables for every region the body.
139    // HOWEVER,the rustc folks hid access to the region-variables
140    // so instead we have to take care to call `tcx.erase_regions(..)`
141    // before using the `infcx` or `SelectionContext` to do any queries
142    // (eg. see `get_impl_id_of_alias_reft` in projections.rs)
143    pub fn lower_mir_body(
144        tcx: TyCtxt<'tcx>,
145        sess: &'sess FluxSession,
146        def_id: LocalDefId,
147        body_with_facts: BodyWithBorrowckFacts<'tcx>,
148    ) -> Result<BodyRoot<'tcx>, ErrorGuaranteed> {
149        let infcx = tcx
150            .infer_ctxt()
151            .with_next_trait_solver(true)
152            .build(TypingMode::analysis_in_body(tcx, def_id));
153        let rustc_body = body_with_facts.body;
154        let def_id = rustc_body.source.def_id();
155
156        let body = Self::lower_rustc_body(tcx, &infcx, sess, def_id, rustc_body)?;
157        let promoted = body_with_facts
158            .promoted
159            .into_iter()
160            .map(|rustc_promoted| Self::lower_rustc_body(tcx, &infcx, sess, def_id, rustc_promoted))
161            .try_collect()?;
162
163        let body_root = BodyRoot::new(
164            body_with_facts.borrow_set,
165            body_with_facts.region_inference_context,
166            infcx,
167            body,
168            promoted,
169        );
170        Ok(body_root)
171    }
172
173    fn lower_rustc_body(
174        tcx: TyCtxt<'tcx>,
175        infcx: &InferCtxt<'tcx>,
176        sess: &'sess FluxSession,
177        def_id: DefId,
178        body: rustc_mir::Body<'tcx>,
179    ) -> Result<Body<'tcx>, ErrorGuaranteed> {
180        let selcx = SelectionContext::new(infcx);
181        let param_env = tcx.param_env(def_id);
182        let mut lower = MirLoweringCtxt { tcx, selcx, param_env, sess, rustc_mir: &body };
183
184        let basic_blocks = body
185            .basic_blocks
186            .iter()
187            .map(|bb_data| lower.lower_basic_block_data(bb_data))
188            .try_collect()?;
189
190        let local_decls = body
191            .local_decls
192            .iter()
193            .map(|local_decl| lower.lower_local_decl(local_decl))
194            .try_collect()?;
195
196        Ok(Body::new(basic_blocks, local_decls, body))
197    }
198
199    fn lower_basic_block_data(
200        &mut self,
201        data: &rustc_mir::BasicBlockData<'tcx>,
202    ) -> Result<BasicBlockData<'tcx>, ErrorGuaranteed> {
203        let data = BasicBlockData {
204            statements: data
205                .statements
206                .iter()
207                .map(|stmt| self.lower_statement(stmt))
208                .try_collect()?,
209            terminator: data
210                .terminator
211                .as_ref()
212                .map(|terminator| self.lower_terminator(terminator))
213                .transpose()?,
214            is_cleanup: data.is_cleanup,
215        };
216        Ok(data)
217    }
218
219    fn lower_local_decl(
220        &self,
221        local_decl: &rustc_mir::LocalDecl<'tcx>,
222    ) -> Result<LocalDecl, ErrorGuaranteed> {
223        Ok(LocalDecl {
224            ty: local_decl
225                .ty
226                .lower(self.tcx)
227                .map_err(|err| errors::UnsupportedLocalDecl::new(local_decl, err))
228                .emit(self.sess)?,
229            source_info: local_decl.source_info,
230        })
231    }
232
233    fn lower_statement(
234        &self,
235        stmt: &rustc_mir::Statement<'tcx>,
236    ) -> Result<Statement<'tcx>, ErrorGuaranteed> {
237        let span = stmt.source_info.span;
238        let kind = match &stmt.kind {
239            rustc_mir::StatementKind::Assign(box (place, rvalue)) => {
240                StatementKind::Assign(
241                    lower_place(self.tcx, place)
242                        .map_err(|reason| errors::UnsupportedMir::statement(span, reason))
243                        .emit(self.sess)?,
244                    self.lower_rvalue(rvalue)
245                        .map_err(|reason| errors::UnsupportedMir::statement(span, reason))
246                        .emit(self.sess)?,
247                )
248            }
249            rustc_mir::StatementKind::SetDiscriminant { place, variant_index } => {
250                StatementKind::SetDiscriminant(
251                    lower_place(self.tcx, place)
252                        .map_err(|reason| errors::UnsupportedMir::statement(span, reason))
253                        .emit(self.sess)?,
254                    *variant_index,
255                )
256            }
257            rustc_mir::StatementKind::FakeRead(box (cause, place)) => {
258                StatementKind::FakeRead(Box::new((
259                    *cause,
260                    lower_place(self.tcx, place)
261                        .map_err(|reason| errors::UnsupportedMir::statement(span, reason))
262                        .emit(self.sess)?,
263                )))
264            }
265            rustc_mir::StatementKind::PlaceMention(place) => {
266                StatementKind::PlaceMention(
267                    lower_place(self.tcx, place)
268                        .map_err(|reason| errors::UnsupportedMir::statement(span, reason))
269                        .emit(self.sess)?,
270                )
271            }
272            rustc_mir::StatementKind::Nop
273            | rustc_mir::StatementKind::StorageLive(_)
274            | rustc_mir::StatementKind::StorageDead(_) => StatementKind::Nop,
275            rustc_mir::StatementKind::AscribeUserType(
276                box (place, rustc_mir::UserTypeProjection { projs, .. }),
277                variance,
278            ) if projs.is_empty() => {
279                StatementKind::AscribeUserType(
280                    lower_place(self.tcx, place)
281                        .map_err(|reason| errors::UnsupportedMir::statement(span, reason))
282                        .emit(self.sess)?,
283                    *variance,
284                )
285            }
286            rustc_mir::StatementKind::Intrinsic(ndi) => {
287                match ndi.as_ref() {
288                    rustc_mir::NonDivergingIntrinsic::Assume(op) => {
289                        let op = self
290                            .lower_operand(op)
291                            .map_err(|reason| errors::UnsupportedMir::statement(span, reason))
292                            .emit(self.sess)?;
293                        StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(op))
294                    }
295                    rustc_mir::NonDivergingIntrinsic::CopyNonOverlapping(_) => {
296                        return Err(errors::UnsupportedMir::from(stmt)).emit(self.sess);
297                    }
298                }
299            }
300
301            rustc_mir::StatementKind::Retag(_, _)
302            | rustc_mir::StatementKind::Deinit(_)
303            | rustc_mir::StatementKind::AscribeUserType(..)
304            | rustc_mir::StatementKind::Coverage(_)
305            | rustc_mir::StatementKind::ConstEvalCounter
306            | rustc_mir::StatementKind::BackwardIncompatibleDropHint { .. } => {
307                return Err(errors::UnsupportedMir::from(stmt)).emit(self.sess);
308            }
309        };
310        Ok(Statement { kind, source_info: stmt.source_info })
311    }
312
313    fn lower_terminator(
314        &mut self,
315        terminator: &rustc_mir::Terminator<'tcx>,
316    ) -> Result<Terminator<'tcx>, ErrorGuaranteed> {
317        let span = terminator.source_info.span;
318        let kind = match &terminator.kind {
319            rustc_mir::TerminatorKind::Return => TerminatorKind::Return,
320            rustc_mir::TerminatorKind::Call { func, args, destination, target, unwind, .. } => {
321                let kind = {
322                    let func_ty = func.ty(self.rustc_mir, self.tcx);
323                    match func_ty.kind() {
324                        rustc_middle::ty::TyKind::FnDef(fn_def, args) => {
325                            let lowered = args
326                                .lower(self.tcx)
327                                .map_err(|reason| errors::UnsupportedMir::terminator(span, reason))
328                                .emit(self.sess)?;
329                            let def_id = *fn_def;
330                            let generic_args = CallArgs { orig: args, lowered };
331                            let (resolved_id, resolved_args) = self
332                                .resolve_call(def_id, generic_args.orig)
333                                .map_err(|reason| {
334                                    errors::UnsupportedMir::new(span, "terminator call", reason)
335                                })
336                                .emit(self.sess)?;
337                            CallKind::FnDef { def_id, generic_args, resolved_id, resolved_args }
338                        }
339                        rustc_middle::ty::TyKind::FnPtr(fn_sig_tys, header) => {
340                            let fn_sig = fnptr_as_fnsig(fn_sig_tys, header)
341                                .lower(self.tcx)
342                                .map_err(|reason| errors::UnsupportedMir::terminator(span, reason))
343                                .emit(self.sess)?;
344                            let operand = self
345                                .lower_operand(func)
346                                .map_err(|reason| {
347                                    errors::UnsupportedMir::new(
348                                        span,
349                                        "function pointer target",
350                                        reason,
351                                    )
352                                })
353                                .emit(self.sess)?;
354                            CallKind::FnPtr { fn_sig, operand }
355                        }
356                        _ => {
357                            Err(errors::UnsupportedMir::terminator(
358                                span,
359                                UnsupportedReason::new(format!(
360                                    "unsupported callee type `{func_ty:?}`"
361                                )),
362                            ))
363                            .emit(self.sess)?
364                        }
365                    }
366                };
367
368                let destination = lower_place(self.tcx, destination)
369                    .map_err(|reason| {
370                        errors::UnsupportedMir::new(span, "terminator destination", reason)
371                    })
372                    .emit(self.sess)?;
373
374                TerminatorKind::Call {
375                    kind,
376                    destination,
377                    target: *target,
378                    args: args
379                        .iter()
380                        .map(|arg| {
381                            self.lower_operand(&arg.node).map_err(|reason| {
382                                errors::UnsupportedMir::new(span, "terminator args", reason)
383                            })
384                        })
385                        .try_collect()
386                        .emit(self.sess)?,
387                    unwind: *unwind,
388                }
389            }
390            rustc_mir::TerminatorKind::SwitchInt { discr, targets, .. } => {
391                TerminatorKind::SwitchInt {
392                    discr: self
393                        .lower_operand(discr)
394                        .map_err(|reason| errors::UnsupportedMir::terminator(span, reason))
395                        .emit(self.sess)?,
396                    targets: targets.clone(),
397                }
398            }
399            rustc_mir::TerminatorKind::Goto { target } => TerminatorKind::Goto { target: *target },
400            rustc_mir::TerminatorKind::Drop { place, target, unwind, .. } => {
401                TerminatorKind::Drop {
402                    place: lower_place(self.tcx, place)
403                        .map_err(|reason| errors::UnsupportedMir::terminator(span, reason))
404                        .emit(self.sess)?,
405                    target: *target,
406                    unwind: *unwind,
407                }
408            }
409            rustc_mir::TerminatorKind::Assert { cond, target, expected, msg, .. } => {
410                TerminatorKind::Assert {
411                    cond: self
412                        .lower_operand(cond)
413                        .map_err(|reason| errors::UnsupportedMir::terminator(span, reason))
414                        .emit(self.sess)?,
415                    expected: *expected,
416                    target: *target,
417                    msg: self
418                        .lower_assert_msg(msg)
419                        .ok_or_else(|| errors::UnsupportedMir::from(terminator))
420                        .emit(self.sess)?,
421                }
422            }
423            rustc_mir::TerminatorKind::Unreachable => TerminatorKind::Unreachable,
424            rustc_mir::TerminatorKind::FalseEdge { real_target, imaginary_target } => {
425                TerminatorKind::FalseEdge {
426                    real_target: *real_target,
427                    imaginary_target: *imaginary_target,
428                }
429            }
430            rustc_mir::TerminatorKind::FalseUnwind { real_target, unwind } => {
431                TerminatorKind::FalseUnwind { real_target: *real_target, unwind: *unwind }
432            }
433            rustc_mir::TerminatorKind::Yield { value, resume, resume_arg, drop } => {
434                TerminatorKind::Yield {
435                    value: self
436                        .lower_operand(value)
437                        .map_err(|reason| errors::UnsupportedMir::terminator(span, reason))
438                        .emit(self.sess)?,
439                    resume: *resume,
440                    resume_arg: lower_place(self.tcx, resume_arg)
441                        .map_err(|reason| errors::UnsupportedMir::terminator(span, reason))
442                        .emit(self.sess)?,
443                    drop: *drop,
444                }
445            }
446            rustc_mir::TerminatorKind::CoroutineDrop => TerminatorKind::CoroutineDrop,
447            rustc_mir::TerminatorKind::UnwindResume => TerminatorKind::UnwindResume,
448            rustc_mir::TerminatorKind::UnwindTerminate(..)
449            | rustc_mir::TerminatorKind::TailCall { .. }
450            | rustc_mir::TerminatorKind::InlineAsm { .. } => {
451                return Err(errors::UnsupportedMir::from(terminator)).emit(self.sess);
452            }
453        };
454        Ok(Terminator { kind, source_info: terminator.source_info })
455    }
456
457    fn resolve_call(
458        &mut self,
459        callee_id: DefId,
460        args: rustc_middle::ty::GenericArgsRef<'tcx>,
461    ) -> Result<(DefId, CallArgs<'tcx>), UnsupportedReason> {
462        let (resolved_id, resolved_args) =
463            resolve_call_query(self.tcx, &mut self.selcx, self.param_env, callee_id, args)
464                .unwrap_or((callee_id, args));
465        let call_args = CallArgs { lowered: resolved_args.lower(self.tcx)?, orig: resolved_args };
466        Ok((resolved_id, call_args))
467    }
468
469    fn lower_rvalue(
470        &self,
471        rvalue: &rustc_mir::Rvalue<'tcx>,
472    ) -> Result<Rvalue<'tcx>, UnsupportedReason> {
473        match rvalue {
474            rustc_mir::Rvalue::Use(op) => Ok(Rvalue::Use(self.lower_operand(op)?)),
475            rustc_mir::Rvalue::Repeat(op, c) => {
476                let op = self.lower_operand(op)?;
477                let c = c.lower(self.tcx)?;
478                Ok(Rvalue::Repeat(op, c))
479            }
480            rustc_mir::Rvalue::Ref(region, bk, p) => {
481                Ok(Rvalue::Ref(region.lower(self.tcx)?, *bk, lower_place(self.tcx, p)?))
482            }
483            rustc_mir::Rvalue::RawPtr(kind, place) => {
484                Ok(Rvalue::RawPtr(*kind, lower_place(self.tcx, place)?))
485            }
486            rustc_mir::Rvalue::Cast(kind, op, ty) => {
487                let kind = self.lower_cast_kind(*kind).ok_or_else(|| {
488                    UnsupportedReason::new(format!("unsupported cast `{kind:?}`"))
489                })?;
490                let op = self.lower_operand(op)?;
491                let ty = ty.lower(self.tcx)?;
492                Ok(Rvalue::Cast(kind, op, ty))
493            }
494            rustc_mir::Rvalue::BinaryOp(bin_op, box (op1, op2)) => {
495                Ok(Rvalue::BinaryOp(
496                    self.lower_bin_op(*bin_op)?,
497                    self.lower_operand(op1)?,
498                    self.lower_operand(op2)?,
499                ))
500            }
501            rustc_mir::Rvalue::NullaryOp(null_op, ty) => {
502                Ok(Rvalue::NullaryOp(self.lower_null_op(*null_op)?, ty.lower(self.tcx)?))
503            }
504            rustc_mir::Rvalue::UnaryOp(un_op, op) => {
505                Ok(Rvalue::UnaryOp(*un_op, self.lower_operand(op)?))
506            }
507            rustc_mir::Rvalue::Discriminant(p) => {
508                Ok(Rvalue::Discriminant(lower_place(self.tcx, p)?))
509            }
510            rustc_mir::Rvalue::Aggregate(aggregate_kind, args) => {
511                let aggregate_kind = self.lower_aggregate_kind(aggregate_kind)?;
512                let args = args.iter().map(|op| self.lower_operand(op)).try_collect()?;
513                Ok(Rvalue::Aggregate(aggregate_kind, args))
514            }
515            rustc_mir::Rvalue::ShallowInitBox(op, ty) => {
516                Ok(Rvalue::ShallowInitBox(self.lower_operand(op)?, ty.lower(self.tcx)?))
517            }
518            rustc_mir::Rvalue::ThreadLocalRef(_)
519            | rustc_mir::Rvalue::CopyForDeref(_)
520            | rustc_mir::Rvalue::WrapUnsafeBinder(..) => {
521                Err(UnsupportedReason::new(format!("unsupported rvalue `{rvalue:?}`")))
522            }
523        }
524    }
525
526    fn lower_pointer_coercion(
527        &self,
528        coercion: rustc_adjustment::PointerCoercion,
529    ) -> Option<PointerCast> {
530        match coercion {
531            rustc_adjustment::PointerCoercion::MutToConstPointer => {
532                Some(crate::mir::PointerCast::MutToConstPointer)
533            }
534            rustc_adjustment::PointerCoercion::Unsize => Some(crate::mir::PointerCast::Unsize),
535            rustc_adjustment::PointerCoercion::ClosureFnPointer(_) => {
536                Some(crate::mir::PointerCast::ClosureFnPointer)
537            }
538            rustc_adjustment::PointerCoercion::ReifyFnPointer => {
539                Some(crate::mir::PointerCast::ReifyFnPointer)
540            }
541            rustc_adjustment::PointerCoercion::UnsafeFnPointer
542            | rustc_adjustment::PointerCoercion::ArrayToPointer => None,
543        }
544    }
545    fn lower_cast_kind(&self, kind: rustc_mir::CastKind) -> Option<CastKind> {
546        match kind {
547            rustc_mir::CastKind::IntToInt => Some(CastKind::IntToInt),
548            rustc_mir::CastKind::IntToFloat => Some(CastKind::IntToFloat),
549            rustc_mir::CastKind::FloatToInt => Some(CastKind::FloatToInt),
550            rustc_mir::CastKind::PtrToPtr => Some(CastKind::PtrToPtr),
551            rustc_mir::CastKind::PointerCoercion(ptr_coercion, _) => {
552                Some(CastKind::PointerCoercion(self.lower_pointer_coercion(ptr_coercion)?))
553            }
554            rustc_mir::CastKind::PointerExposeProvenance => Some(CastKind::PointerExposeProvenance),
555            rustc_mir::CastKind::PointerWithExposedProvenance => {
556                Some(CastKind::PointerWithExposedProvenance)
557            }
558            _ => None,
559        }
560    }
561
562    fn lower_aggregate_kind(
563        &self,
564        aggregate_kind: &rustc_mir::AggregateKind<'tcx>,
565    ) -> Result<AggregateKind, UnsupportedReason> {
566        match aggregate_kind {
567            rustc_mir::AggregateKind::Adt(
568                def_id,
569                variant_idx,
570                args,
571                user_type_annot_idx,
572                field_idx,
573            ) => {
574                Ok(AggregateKind::Adt(
575                    *def_id,
576                    *variant_idx,
577                    args.lower(self.tcx)?,
578                    *user_type_annot_idx,
579                    *field_idx,
580                ))
581            }
582            rustc_mir::AggregateKind::Array(ty) => Ok(AggregateKind::Array(ty.lower(self.tcx)?)),
583            rustc_mir::AggregateKind::Tuple => Ok(AggregateKind::Tuple),
584            rustc_mir::AggregateKind::Closure(did, args) => {
585                let args = args.lower(self.tcx)?;
586                Ok(AggregateKind::Closure(*did, args))
587            }
588            rustc_mir::AggregateKind::Coroutine(did, args) => {
589                let args = args.lower(self.tcx)?;
590                Ok(AggregateKind::Coroutine(*did, args))
591            }
592            rustc_mir::AggregateKind::RawPtr(_, _)
593            | rustc_mir::AggregateKind::CoroutineClosure(..) => {
594                Err(UnsupportedReason::new(format!(
595                    "unsupported aggregate kind `{aggregate_kind:?}`"
596                )))
597            }
598        }
599    }
600
601    fn lower_bin_op(&self, bin_op: rustc_mir::BinOp) -> Result<BinOp, UnsupportedReason> {
602        match bin_op {
603            rustc_mir::BinOp::Add => Ok(BinOp::Add),
604            rustc_mir::BinOp::Sub => Ok(BinOp::Sub),
605            rustc_mir::BinOp::Gt => Ok(BinOp::Gt),
606            rustc_mir::BinOp::Ge => Ok(BinOp::Ge),
607            rustc_mir::BinOp::Lt => Ok(BinOp::Lt),
608            rustc_mir::BinOp::Le => Ok(BinOp::Le),
609            rustc_mir::BinOp::Eq => Ok(BinOp::Eq),
610            rustc_mir::BinOp::Ne => Ok(BinOp::Ne),
611            rustc_mir::BinOp::Mul => Ok(BinOp::Mul),
612            rustc_mir::BinOp::Div => Ok(BinOp::Div),
613            rustc_mir::BinOp::Rem => Ok(BinOp::Rem),
614            rustc_mir::BinOp::BitAnd => Ok(BinOp::BitAnd),
615            rustc_mir::BinOp::BitOr => Ok(BinOp::BitOr),
616            rustc_mir::BinOp::BitXor => Ok(BinOp::BitXor),
617            rustc_mir::BinOp::Shl => Ok(BinOp::Shl),
618            rustc_mir::BinOp::Shr => Ok(BinOp::Shr),
619            rustc_mir::BinOp::AddUnchecked
620            | rustc_mir::BinOp::SubUnchecked
621            | rustc_mir::BinOp::MulUnchecked
622            | rustc_mir::BinOp::ShlUnchecked
623            | rustc_mir::BinOp::ShrUnchecked
624            | rustc_mir::BinOp::AddWithOverflow
625            | rustc_mir::BinOp::SubWithOverflow
626            | rustc_mir::BinOp::MulWithOverflow
627            | rustc_mir::BinOp::Cmp
628            | rustc_mir::BinOp::Offset => {
629                Err(UnsupportedReason::new(format!("unsupported binary op `{bin_op:?}`")))
630            }
631        }
632    }
633
634    fn lower_null_op(&self, null_op: rustc_mir::NullOp) -> Result<NullOp, UnsupportedReason> {
635        match null_op {
636            rustc_mir::NullOp::SizeOf => Ok(NullOp::SizeOf),
637            rustc_mir::NullOp::AlignOf => Ok(NullOp::AlignOf),
638            rustc_mir::NullOp::OffsetOf(_)
639            | rustc_mir::NullOp::UbChecks
640            | rustc_mir::NullOp::ContractChecks => {
641                Err(UnsupportedReason::new(format!("unsupported nullary op `{null_op:?}`")))
642            }
643        }
644    }
645
646    fn lower_operand(
647        &self,
648        op: &rustc_mir::Operand<'tcx>,
649    ) -> Result<Operand<'tcx>, UnsupportedReason> {
650        match op {
651            rustc_mir::Operand::Copy(place) => Ok(Operand::Copy(lower_place(self.tcx, place)?)),
652            rustc_mir::Operand::Move(place) => Ok(Operand::Move(lower_place(self.tcx, place)?)),
653            rustc_mir::Operand::Constant(c) => Ok(Operand::Constant(self.lower_constant(c)?)),
654        }
655    }
656
657    fn lower_constant(
658        &self,
659        constant: &rustc_mir::ConstOperand<'tcx>,
660    ) -> Result<ConstOperand<'tcx>, UnsupportedReason> {
661        Ok(ConstOperand {
662            span: constant.span,
663            ty: constant.const_.ty().lower(self.tcx)?,
664            const_: constant.const_,
665        })
666    }
667
668    fn lower_assert_msg(&self, msg: &rustc_mir::AssertMessage) -> Option<AssertKind> {
669        use rustc_mir::AssertKind::*;
670        match msg {
671            BoundsCheck { .. } => Some(AssertKind::BoundsCheck),
672            DivisionByZero(_) => Some(AssertKind::DivisionByZero),
673            RemainderByZero(_) => Some(AssertKind::RemainderByZero),
674            Overflow(bin_op, ..) => Some(AssertKind::Overflow(self.lower_bin_op(*bin_op).ok()?)),
675            _ => None,
676        }
677    }
678}
679
680pub fn lower_place<'tcx>(
681    _tcx: TyCtxt<'tcx>,
682    place: &rustc_mir::Place<'tcx>,
683) -> Result<Place, UnsupportedReason> {
684    let mut projection = vec![];
685    for elem in place.projection {
686        match elem {
687            rustc_mir::PlaceElem::Deref => projection.push(PlaceElem::Deref),
688            rustc_mir::PlaceElem::Field(field, _) => projection.push(PlaceElem::Field(field)),
689            rustc_mir::PlaceElem::Downcast(name, idx) => {
690                projection.push(PlaceElem::Downcast(name, idx));
691            }
692            rustc_mir::PlaceElem::Index(v) => projection.push(PlaceElem::Index(v)),
693            rustc_mir::ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
694                projection.push(PlaceElem::ConstantIndex { offset, min_length, from_end });
695            }
696            _ => {
697                return Err(UnsupportedReason::new(format!("unsupported place `{place:?}`")));
698            }
699        }
700    }
701    Ok(Place { local: place.local, projection })
702}
703
704impl<'tcx> Lower<'tcx> for rustc_ty::FnSig<'tcx> {
705    type R = Result<FnSig, UnsupportedReason>;
706
707    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
708        let inputs_and_output = List::from_vec(
709            self.inputs_and_output
710                .iter()
711                .map(|ty| ty.lower(tcx))
712                .try_collect()?,
713        );
714        Ok(FnSig { safety: self.safety, abi: self.abi, inputs_and_output })
715    }
716}
717
718impl<'tcx> Lower<'tcx> for &'tcx rustc_ty::List<rustc_ty::BoundVariableKind> {
719    type R = Result<List<BoundVariableKind>, UnsupportedReason>;
720
721    fn lower(self, _tcx: TyCtxt<'tcx>) -> Self::R {
722        let mut vars = vec![];
723        for var in self {
724            match var {
725                rustc_ty::BoundVariableKind::Region(kind) => {
726                    vars.push(BoundVariableKind::Region(kind));
727                }
728                _ => {
729                    return Err(UnsupportedReason {
730                        descr: format!("unsupported bound variable {var:?}"),
731                    });
732                }
733            }
734        }
735        Ok(List::from_vec(vars))
736    }
737}
738
739impl<'tcx> Lower<'tcx> for rustc_ty::ValTree<'tcx> {
740    type R = crate::ty::ValTree;
741
742    fn lower(self, _tcx: TyCtxt<'tcx>) -> Self::R {
743        match &*self {
744            rustc_ty::ValTreeKind::Leaf(scalar_int) => crate::ty::ValTree::Leaf(*scalar_int),
745            rustc_ty::ValTreeKind::Branch(trees) => {
746                let trees = trees.iter().map(|tree| tree.lower(_tcx)).collect();
747                crate::ty::ValTree::Branch(trees)
748            }
749        }
750    }
751}
752
753impl<'tcx> Lower<'tcx> for rustc_ty::Const<'tcx> {
754    type R = Result<Const, UnsupportedReason>;
755
756    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
757        let kind = match self.kind() {
758            rustc_type_ir::ConstKind::Param(param_const) => {
759                ConstKind::Param(ParamConst { name: param_const.name, index: param_const.index })
760            }
761            rustc_type_ir::ConstKind::Value(value) => {
762                ConstKind::Value(value.ty.lower(tcx)?, value.valtree.lower(tcx))
763            }
764            rustc_type_ir::ConstKind::Unevaluated(c) => {
765                // TODO: raise unsupported if c.args is not empty?
766                let args = c.args.lower(tcx)?;
767                ConstKind::Unevaluated(UnevaluatedConst { def: c.def, args, promoted: None })
768            }
769            _ => return Err(UnsupportedReason::new(format!("unsupported const {self:?}"))),
770        };
771        Ok(Const { kind })
772    }
773}
774
775impl<'tcx, T, S> Lower<'tcx> for rustc_ty::Binder<'tcx, T>
776where
777    T: Lower<'tcx, R = Result<S, UnsupportedReason>>,
778{
779    type R = Result<Binder<S>, UnsupportedReason>;
780
781    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
782        let vars = self.bound_vars().lower(tcx)?;
783        Ok(Binder::bind_with_vars(self.skip_binder().lower(tcx)?, vars))
784    }
785}
786
787impl<'tcx> Lower<'tcx> for rustc_ty::Ty<'tcx> {
788    type R = Result<Ty, UnsupportedReason>;
789
790    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
791        match self.kind() {
792            rustc_ty::Ref(region, ty, mutability) => {
793                Ok(Ty::mk_ref(region.lower(tcx)?, ty.lower(tcx)?, *mutability))
794            }
795            rustc_ty::Bool => Ok(Ty::mk_bool()),
796            rustc_ty::Int(int_ty) => Ok(Ty::mk_int(*int_ty)),
797            rustc_ty::Uint(uint_ty) => Ok(Ty::mk_uint(*uint_ty)),
798            rustc_ty::Float(float_ty) => Ok(Ty::mk_float(*float_ty)),
799            rustc_ty::Param(param_ty) => Ok(Ty::mk_param(*param_ty)),
800            rustc_ty::Adt(adt_def, args) => {
801                let args = args.lower(tcx)?;
802                Ok(Ty::mk_adt(adt_def.lower(tcx), args))
803            }
804            rustc_ty::FnDef(def_id, args) => {
805                let args = args.lower(tcx)?;
806                Ok(Ty::mk_fn_def(*def_id, args))
807            }
808            rustc_ty::Never => Ok(Ty::mk_never()),
809            rustc_ty::Str => Ok(Ty::mk_str()),
810            rustc_ty::Char => Ok(Ty::mk_char()),
811            rustc_ty::Tuple(tys) => {
812                let tys = List::from_vec(tys.iter().map(|ty| ty.lower(tcx)).try_collect()?);
813                Ok(Ty::mk_tuple(tys))
814            }
815            rustc_ty::Array(ty, len) => Ok(Ty::mk_array(ty.lower(tcx)?, len.lower(tcx)?)),
816            rustc_ty::Slice(ty) => Ok(Ty::mk_slice(ty.lower(tcx)?)),
817            rustc_ty::RawPtr(ty, mutbl) => {
818                let ty = ty.lower(tcx)?;
819                Ok(Ty::mk_raw_ptr(ty, *mutbl))
820            }
821            rustc_ty::FnPtr(fn_sig_tys, header) => {
822                let fn_sig = fnptr_as_fnsig(fn_sig_tys, header).lower(tcx)?;
823                Ok(Ty::mk_fn_ptr(fn_sig))
824            }
825            rustc_ty::Closure(did, args) => {
826                let args = args.lower(tcx)?;
827                Ok(Ty::mk_closure(*did, args))
828            }
829
830            rustc_ty::Alias(kind, alias_ty) => {
831                let kind = kind.lower(tcx)?;
832                let args = alias_ty.args.lower(tcx)?;
833                Ok(Ty::mk_alias(kind, alias_ty.def_id, args))
834            }
835            rustc_ty::Coroutine(did, args) => {
836                let args = args.lower(tcx)?;
837                Ok(Ty::mk_coroutine(*did, args))
838            }
839            rustc_ty::CoroutineWitness(did, args) => {
840                let args = args.lower(tcx)?;
841                Ok(Ty::mk_generator_witness(*did, args))
842            }
843            rustc_ty::Dynamic(predicates, region) => {
844                let region = region.lower(tcx)?;
845
846                let exi_preds = List::from_vec(
847                    predicates
848                        .iter()
849                        .map(|pred| pred.lower(tcx))
850                        .try_collect()?,
851                );
852
853                Ok(Ty::mk_dynamic(exi_preds, region))
854            }
855            rustc_ty::Foreign(def_id) => Ok(Ty::mk_foreign(*def_id)),
856            _ => Err(UnsupportedReason::new(format!("unsupported type `{self:?}`"))),
857        }
858    }
859}
860
861fn fnptr_as_fnsig<'tcx>(
862    fn_sig_tys: &'tcx rustc_ty::Binder<'tcx, rustc_ty::FnSigTys<TyCtxt<'tcx>>>,
863    header: &'tcx rustc_ty::FnHeader<TyCtxt<'tcx>>,
864) -> rustc_ty::Binder<'tcx, rustc_ty::FnSig<'tcx>> {
865    fn_sig_tys.map_bound(|fn_sig_tys| {
866        rustc_ty::FnSig {
867            inputs_and_output: fn_sig_tys.inputs_and_output,
868            c_variadic: header.c_variadic,
869            safety: header.safety,
870            abi: header.abi,
871        }
872    })
873}
874
875impl<'tcx> Lower<'tcx> for rustc_ty::AliasTyKind {
876    type R = Result<AliasKind, UnsupportedReason>;
877
878    fn lower(self, _tcx: TyCtxt<'tcx>) -> Self::R {
879        match self {
880            rustc_type_ir::AliasTyKind::Projection => Ok(AliasKind::Projection),
881            rustc_type_ir::AliasTyKind::Opaque => Ok(AliasKind::Opaque),
882            _ => Err(UnsupportedReason::new(format!("unsupported alias kind `{self:?}`"))),
883        }
884    }
885}
886
887impl<'tcx> Lower<'tcx> for rustc_ty::AdtDef<'tcx> {
888    type R = AdtDef;
889
890    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
891        AdtDef::new(AdtDefData::new(
892            tcx,
893            self,
894            self.variants()
895                .iter()
896                .map(|variant| {
897                    VariantDef {
898                        def_id: variant.def_id,
899                        name: variant.name,
900                        fields: variant
901                            .fields
902                            .iter()
903                            .map(|f| FieldDef { did: f.did, name: f.name })
904                            .collect(),
905                    }
906                })
907                .collect(),
908        ))
909    }
910}
911
912impl<'tcx> Lower<'tcx> for rustc_ty::ExistentialPredicate<'tcx> {
913    type R = Result<ExistentialPredicate, UnsupportedReason>;
914
915    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
916        match self {
917            rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => {
918                Ok(ExistentialPredicate::Trait(ExistentialTraitRef {
919                    def_id: trait_ref.def_id,
920                    args: trait_ref.args.lower(tcx)?,
921                }))
922            }
923            rustc_type_ir::ExistentialPredicate::Projection(proj) => {
924                let Some(term) = proj.term.as_type() else {
925                    return Err(UnsupportedReason::new(format!(
926                        "unsupported existential predicate `{self:?}`"
927                    )));
928                };
929                Ok(ExistentialPredicate::Projection(ExistentialProjection {
930                    def_id: proj.def_id,
931                    args: proj.args.lower(tcx)?,
932                    term: term.lower(tcx)?,
933                }))
934            }
935            rustc_type_ir::ExistentialPredicate::AutoTrait(def_id) => {
936                Ok(ExistentialPredicate::AutoTrait(def_id))
937            }
938        }
939    }
940}
941
942impl<'tcx> Lower<'tcx> for rustc_middle::ty::GenericArgsRef<'tcx> {
943    type R = Result<GenericArgs, UnsupportedReason>;
944
945    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
946        Ok(List::from_vec(self.iter().map(|arg| arg.lower(tcx)).try_collect()?))
947    }
948}
949
950impl<'tcx> Lower<'tcx> for rustc_middle::ty::GenericArg<'tcx> {
951    type R = Result<GenericArg, UnsupportedReason>;
952
953    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
954        match self.kind() {
955            GenericArgKind::Type(ty) => Ok(GenericArg::Ty(ty.lower(tcx)?)),
956            GenericArgKind::Lifetime(region) => Ok(GenericArg::Lifetime(region.lower(tcx)?)),
957            GenericArgKind::Const(c) => Ok(GenericArg::Const(c.lower(tcx)?)),
958        }
959    }
960}
961
962impl<'tcx> Lower<'tcx> for rustc_middle::ty::Region<'tcx> {
963    type R = Result<Region, UnsupportedReason>;
964
965    fn lower(self, _tcx: TyCtxt<'tcx>) -> Self::R {
966        use rustc_middle::ty;
967        match self.kind() {
968            ty::ReVar(rvid) => Ok(Region::ReVar(rvid)),
969            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), bregion) => {
970                Ok(Region::ReBound(
971                    debruijn,
972                    Ok(BoundRegion { kind: bregion.kind, var: bregion.var })?,
973                ))
974            }
975            ty::ReEarlyParam(bregion) => Ok(Region::ReEarlyParam(bregion)),
976            ty::ReStatic => Ok(Region::ReStatic),
977            ty::ReErased => Ok(Region::ReErased),
978            ty::ReBound(ty::BoundVarIndexKind::Canonical, _)
979            | ty::ReLateParam(_)
980            | ty::RePlaceholder(_)
981            | ty::ReError(_) => {
982                Err(UnsupportedReason::new(format!("unsupported region `{self:?}`")))
983            }
984        }
985    }
986}
987
988impl<'tcx> Lower<'tcx> for &'tcx rustc_middle::ty::Generics {
989    type R = Generics<'tcx>;
990
991    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
992        let params = List::from_vec(
993            self.own_params
994                .iter()
995                .map(|param| param.lower(tcx))
996                .collect(),
997        );
998        Generics { params, orig: self }
999    }
1000}
1001
1002impl<'tcx> Lower<'tcx> for &rustc_middle::ty::GenericParamDef {
1003    type R = GenericParamDef;
1004
1005    fn lower(self, _tcx: TyCtxt<'tcx>) -> Self::R {
1006        let kind = match self.kind {
1007            rustc_ty::GenericParamDefKind::Type { has_default, .. } => {
1008                GenericParamDefKind::Type { has_default }
1009            }
1010            rustc_ty::GenericParamDefKind::Lifetime => GenericParamDefKind::Lifetime,
1011            rustc_ty::GenericParamDefKind::Const { has_default, .. } => {
1012                GenericParamDefKind::Const { has_default }
1013            }
1014        };
1015        GenericParamDef { def_id: self.def_id, index: self.index, name: self.name, kind }
1016    }
1017}
1018
1019impl<'tcx> Lower<'tcx> for rustc_ty::GenericPredicates<'tcx> {
1020    type R = Result<GenericPredicates, UnsupportedErr>;
1021
1022    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
1023        let predicates = self
1024            .predicates
1025            .iter()
1026            .map(|(clause, span)| {
1027                clause
1028                    .lower(tcx)
1029                    .map_err(|reason| UnsupportedErr::new(reason).with_span(*span))
1030            })
1031            .try_collect()?;
1032        Ok(GenericPredicates { parent: self.parent, predicates })
1033    }
1034}
1035
1036impl<'tcx> Lower<'tcx> for rustc_ty::Clauses<'tcx> {
1037    type R = Result<List<Clause>, UnsupportedErr>;
1038
1039    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
1040        self.iter()
1041            .map(|clause| clause.lower(tcx).map_err(UnsupportedErr::new))
1042            .try_collect()
1043    }
1044}
1045
1046impl<'tcx> Lower<'tcx> for rustc_ty::ClauseKind<'tcx> {
1047    type R = Result<ClauseKind, UnsupportedReason>;
1048
1049    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
1050        let kind = match self {
1051            rustc_ty::ClauseKind::Trait(trait_pred) => {
1052                ClauseKind::Trait(TraitPredicate { trait_ref: trait_pred.trait_ref.lower(tcx)? })
1053            }
1054            rustc_ty::ClauseKind::Projection(proj_pred) => {
1055                let Some(term) = proj_pred.term.as_type() else {
1056                    return Err(UnsupportedReason::new(format!(
1057                        "unsupported projection predicate `{proj_pred:?}`"
1058                    )));
1059                };
1060                let proj_ty = proj_pred.projection_term;
1061                let args = proj_ty.args.lower(tcx)?;
1062
1063                let projection_ty = AliasTy { args, def_id: proj_ty.def_id };
1064                let term = term.lower(tcx)?;
1065                ClauseKind::Projection(ProjectionPredicate { projection_ty, term })
1066            }
1067            rustc_ty::ClauseKind::RegionOutlives(outlives) => {
1068                ClauseKind::RegionOutlives(outlives.lower(tcx)?)
1069            }
1070            rustc_ty::ClauseKind::TypeOutlives(outlives) => {
1071                ClauseKind::TypeOutlives(outlives.lower(tcx)?)
1072            }
1073            rustc_ty::ClauseKind::ConstArgHasType(const_, ty) => {
1074                ClauseKind::ConstArgHasType(const_.lower(tcx)?, ty.lower(tcx)?)
1075            }
1076            _ => {
1077                return Err(UnsupportedReason::new(format!("unsupported clause kind `{self:?}`")));
1078            }
1079        };
1080        Ok(kind)
1081    }
1082}
1083
1084impl<'tcx> Lower<'tcx> for rustc_ty::Clause<'tcx> {
1085    type R = Result<Clause, UnsupportedReason>;
1086
1087    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
1088        Ok(Clause::new(self.kind().lower(tcx)?))
1089    }
1090}
1091
1092impl<'tcx> Lower<'tcx> for rustc_ty::TraitRef<'tcx> {
1093    type R = Result<TraitRef, UnsupportedReason>;
1094
1095    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
1096        Ok(TraitRef { def_id: self.def_id, args: self.args.lower(tcx)? })
1097    }
1098}
1099
1100impl<'tcx> Lower<'tcx> for rustc_ty::TypeOutlivesPredicate<'tcx> {
1101    type R = Result<TypeOutlivesPredicate, UnsupportedReason>;
1102
1103    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
1104        Ok(OutlivesPredicate(self.0.lower(tcx)?, self.1.lower(tcx)?))
1105    }
1106}
1107
1108impl<'tcx> Lower<'tcx> for rustc_ty::RegionOutlivesPredicate<'tcx> {
1109    type R = Result<RegionOutlivesPredicate, UnsupportedReason>;
1110
1111    fn lower(self, tcx: TyCtxt<'tcx>) -> Self::R {
1112        Ok(OutlivesPredicate(self.0.lower(tcx)?, self.1.lower(tcx)?))
1113    }
1114}
1115
1116mod errors {
1117    use std::path::PathBuf;
1118
1119    use flux_errors::E0999;
1120    use flux_macros::Diagnostic;
1121    use rustc_middle::mir as rustc_mir;
1122    use rustc_span::Span;
1123
1124    use super::UnsupportedReason;
1125
1126    #[derive(Diagnostic)]
1127    #[diag(rustc_bridge_unsupported_local_decl, code = E0999)]
1128    pub(super) struct UnsupportedLocalDecl<'tcx> {
1129        #[primary_span]
1130        #[label]
1131        span: Span,
1132        ty: rustc_middle::ty::Ty<'tcx>,
1133    }
1134
1135    impl<'tcx> UnsupportedLocalDecl<'tcx> {
1136        pub(super) fn new(
1137            local_decl: &rustc_mir::LocalDecl<'tcx>,
1138            _err: UnsupportedReason,
1139        ) -> Self {
1140            Self { span: local_decl.source_info.span, ty: local_decl.ty }
1141        }
1142    }
1143
1144    #[derive(Diagnostic)]
1145    #[diag(rustc_bridge_unsupported_mir, code = E0999)]
1146    #[note]
1147    pub(super) struct UnsupportedMir {
1148        #[primary_span]
1149        span: Span,
1150        kind: &'static str,
1151        reason: UnsupportedReason,
1152    }
1153
1154    impl rustc_errors::IntoDiagArg for UnsupportedReason {
1155        fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> rustc_errors::DiagArgValue {
1156            rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(self.descr))
1157        }
1158    }
1159
1160    impl UnsupportedMir {
1161        pub(super) fn new(span: Span, kind: &'static str, reason: UnsupportedReason) -> Self {
1162            Self { span, kind, reason }
1163        }
1164
1165        pub(super) fn terminator(span: Span, reason: UnsupportedReason) -> Self {
1166            Self { span, kind: "terminator", reason }
1167        }
1168
1169        pub(super) fn statement(span: Span, reason: UnsupportedReason) -> Self {
1170            Self { span, kind: "statement", reason }
1171        }
1172    }
1173
1174    impl<'a, 'tcx> From<&'a rustc_mir::Terminator<'tcx>> for UnsupportedMir {
1175        fn from(terminator: &'a rustc_mir::Terminator<'tcx>) -> Self {
1176            Self::terminator(
1177                terminator.source_info.span,
1178                UnsupportedReason::new(format!("{terminator:?}",)),
1179            )
1180        }
1181    }
1182
1183    impl<'a, 'tcx> From<&'a rustc_mir::Statement<'tcx>> for UnsupportedMir {
1184        fn from(statement: &'a rustc_mir::Statement<'tcx>) -> Self {
1185            Self::statement(
1186                statement.source_info.span,
1187                UnsupportedReason::new(format!("{statement:?}")),
1188            )
1189        }
1190    }
1191}