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