Skip to main content

rustc_const_eval/const_eval/
dyn_trait.rs

1use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult, Scalar, interp_ok};
2use rustc_middle::ty::{Region, Ty};
3use rustc_middle::{span_bug, ty};
4use rustc_span::def_id::DefId;
5use rustc_span::sym;
6
7use crate::const_eval::CompileTimeMachine;
8use crate::interpret::{Immediate, InterpCx, MPlaceTy, MemoryKind, Writeable};
9impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
10    pub(crate) fn write_dyn_trait_type_info(
11        &mut self,
12        dyn_place: impl Writeable<'tcx, CtfeProvenance>,
13        data: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
14        region: Region<'tcx>,
15    ) -> InterpResult<'tcx> {
16        let tcx = self.tcx.tcx;
17
18        // Find the principal trait ref (for super trait collection), collect auto traits,
19        // and collect all projection predicates (used when computing TypeId for each supertrait).
20        let mut principal: Option<ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>> = None;
21        let mut auto_traits_def_ids: Vec<ty::Binder<'tcx, DefId>> = Vec::new();
22        let mut projections: Vec<ty::Binder<'tcx, ty::ExistentialProjection<'tcx>>> = Vec::new();
23
24        for b in data.iter() {
25            match b.skip_binder() {
26                ty::ExistentialPredicate::Trait(tr) => principal = Some(b.rebind(tr)),
27                ty::ExistentialPredicate::AutoTrait(did) => auto_traits_def_ids.push(b.rebind(did)),
28                ty::ExistentialPredicate::Projection(p) => projections.push(b.rebind(p)),
29            }
30        }
31
32        // This is to make principal dyn type include Trait and projection predicates, excluding auto traits.
33        let principal_ty: Option<Ty<'tcx>> = principal.map(|_tr| {
34            let preds = tcx
35                .mk_poly_existential_predicates_from_iter(data.iter().filter(|b| {
36                    !#[allow(non_exhaustive_omitted_patterns)] match b.skip_binder() {
    ty::ExistentialPredicate::AutoTrait(_) => true,
    _ => false,
}matches!(b.skip_binder(), ty::ExistentialPredicate::AutoTrait(_))
37                }));
38            Ty::new_dynamic(tcx, preds, region)
39        });
40
41        // DynTrait { predicates: &'static [Trait] }
42        for (field_idx, field) in
43            dyn_place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
44        {
45            let field_place = self.project_field(&dyn_place, field_idx)?;
46            match field.name {
47                sym::predicates => {
48                    self.write_dyn_trait_predicates_slice(
49                        &field_place,
50                        principal_ty,
51                        &auto_traits_def_ids,
52                        region,
53                    )?;
54                }
55                other => {
56                    ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field.did),
    format_args!("unimplemented DynTrait field {0}", other))span_bug!(self.tcx.def_span(field.did), "unimplemented DynTrait field {other}")
57                }
58            }
59        }
60
61        interp_ok(())
62    }
63
64    fn mk_dyn_principal_auto_trait_ty(
65        &self,
66        auto_trait_def_id: ty::Binder<'tcx, DefId>,
67        region: Region<'tcx>,
68    ) -> Ty<'tcx> {
69        let tcx = self.tcx.tcx;
70
71        // Preserve the binder vars from the original auto-trait predicate.
72        let pred_inner = ty::ExistentialPredicate::AutoTrait(auto_trait_def_id.skip_binder());
73        let pred = ty::Binder::bind_with_vars(pred_inner, auto_trait_def_id.bound_vars());
74
75        let preds = tcx.mk_poly_existential_predicates_from_iter([pred].into_iter());
76        Ty::new_dynamic(tcx, preds, region)
77    }
78
79    fn write_dyn_trait_predicates_slice(
80        &mut self,
81        slice_place: &impl Writeable<'tcx, CtfeProvenance>,
82        principal_ty: Option<Ty<'tcx>>,
83        auto_trait_def_ids: &[ty::Binder<'tcx, DefId>],
84        region: Region<'tcx>,
85    ) -> InterpResult<'tcx> {
86        let tcx = self.tcx.tcx;
87
88        // total entries in DynTrait predicates
89        let total_len = principal_ty.map(|_| 1).unwrap_or(0) + auto_trait_def_ids.len();
90
91        // element type = DynTraitPredicate
92        let slice_ty = slice_place.layout().ty.builtin_deref(false).unwrap(); // [DynTraitPredicate]
93        let elem_ty = slice_ty.sequence_element_type(tcx); // DynTraitPredicate
94
95        let arr_layout = self.layout_of(Ty::new_array(tcx, elem_ty, total_len as u64))?;
96        let arr_place = self.allocate(arr_layout, MemoryKind::Stack)?;
97        let mut elems = self.project_array_fields(&arr_place)?;
98
99        // principal entry (if any) - NOT an auto trait
100        if let Some(principal_ty) = principal_ty {
101            let Some((_i, elem_place)) = elems.next(self)? else {
102                ::rustc_middle::util::bug::span_bug_fmt(self.tcx.span,
    format_args!("DynTrait.predicates length computed wrong (principal)"));span_bug!(self.tcx.span, "DynTrait.predicates length computed wrong (principal)");
103            };
104            self.write_dyn_trait_predicate(elem_place, principal_ty, false)?;
105        }
106
107        // auto trait entries - these ARE auto traits
108        for auto in auto_trait_def_ids {
109            let Some((_i, elem_place)) = elems.next(self)? else {
110                ::rustc_middle::util::bug::span_bug_fmt(self.tcx.span,
    format_args!("DynTrait.predicates length computed wrong (auto)"));span_bug!(self.tcx.span, "DynTrait.predicates length computed wrong (auto)");
111            };
112            let auto_ty = self.mk_dyn_principal_auto_trait_ty(*auto, region);
113            self.write_dyn_trait_predicate(elem_place, auto_ty, true)?;
114        }
115
116        let arr_place = arr_place.map_provenance(CtfeProvenance::as_immutable);
117        let imm = Immediate::new_slice(arr_place.ptr(), total_len as u64, self);
118        self.write_immediate(imm, slice_place)
119    }
120
121    fn write_dyn_trait_predicate(
122        &mut self,
123        predicate_place: MPlaceTy<'tcx>,
124        trait_ty: Ty<'tcx>,
125        is_auto: bool,
126    ) -> InterpResult<'tcx> {
127        // DynTraitPredicate { trait_ty: Trait }
128        for (field_idx, field) in predicate_place
129            .layout
130            .ty
131            .ty_adt_def()
132            .unwrap()
133            .non_enum_variant()
134            .fields
135            .iter_enumerated()
136        {
137            let field_place = self.project_field(&predicate_place, field_idx)?;
138            match field.name {
139                sym::trait_ty => {
140                    // Now write the Trait struct
141                    self.write_trait(field_place, trait_ty, is_auto)?;
142                }
143                other => {
144                    ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field.did),
    format_args!("unimplemented DynTraitPredicate field {0}", other))span_bug!(
145                        self.tcx.def_span(field.did),
146                        "unimplemented DynTraitPredicate field {other}"
147                    )
148                }
149            }
150        }
151        interp_ok(())
152    }
153    fn write_trait(
154        &mut self,
155        trait_place: MPlaceTy<'tcx>,
156        trait_ty: Ty<'tcx>,
157        is_auto: bool,
158    ) -> InterpResult<'tcx> {
159        // Trait { ty: TypeId, is_auto: bool }
160        for (field_idx, field) in
161            trait_place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
162        {
163            let field_place = self.project_field(&trait_place, field_idx)?;
164            match field.name {
165                sym::ty => {
166                    self.write_type_id(trait_ty, &field_place)?;
167                }
168                sym::is_auto => {
169                    self.write_scalar(Scalar::from_bool(is_auto), &field_place)?;
170                }
171                other => {
172                    ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field.did),
    format_args!("unimplemented Trait field {0}", other))span_bug!(self.tcx.def_span(field.did), "unimplemented Trait field {other}")
173                }
174            }
175        }
176        interp_ok(())
177    }
178}