]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ty_utils/src/abi.rs
Rollup merge of #105484 - nbdd0121:upcast, r=compiler-errors
[rust.git] / compiler / rustc_ty_utils / src / abi.rs
1 use rustc_hir as hir;
2 use rustc_hir::lang_items::LangItem;
3 use rustc_middle::ty::layout::{
4     fn_can_unwind, FnAbiError, HasParamEnv, HasTyCtxt, LayoutCx, LayoutOf, TyAndLayout,
5 };
6 use rustc_middle::ty::{self, Ty, TyCtxt};
7 use rustc_session::config::OptLevel;
8 use rustc_span::def_id::DefId;
9 use rustc_target::abi::call::{
10     ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind,
11 };
12 use rustc_target::abi::*;
13 use rustc_target::spec::abi::Abi as SpecAbi;
14
15 use std::iter;
16
17 pub fn provide(providers: &mut ty::query::Providers) {
18     *providers = ty::query::Providers { fn_abi_of_fn_ptr, fn_abi_of_instance, ..*providers };
19 }
20
21 // NOTE(eddyb) this is private to avoid using it from outside of
22 // `fn_abi_of_instance` - any other uses are either too high-level
23 // for `Instance` (e.g. typeck would use `Ty::fn_sig` instead),
24 // or should go through `FnAbi` instead, to avoid losing any
25 // adjustments `fn_abi_of_instance` might be performing.
26 #[tracing::instrument(level = "debug", skip(tcx, param_env))]
27 fn fn_sig_for_fn_abi<'tcx>(
28     tcx: TyCtxt<'tcx>,
29     instance: ty::Instance<'tcx>,
30     param_env: ty::ParamEnv<'tcx>,
31 ) -> ty::PolyFnSig<'tcx> {
32     let ty = instance.ty(tcx, param_env);
33     match *ty.kind() {
34         ty::FnDef(..) => {
35             // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering
36             // parameters unused if they show up in the signature, but not in the `mir::Body`
37             // (i.e. due to being inside a projection that got normalized, see
38             // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping
39             // track of a polymorphization `ParamEnv` to allow normalizing later.
40             //
41             // We normalize the `fn_sig` again after substituting at a later point.
42             let mut sig = match *ty.kind() {
43                 ty::FnDef(def_id, substs) => tcx
44                     .bound_fn_sig(def_id)
45                     .map_bound(|fn_sig| {
46                         tcx.normalize_erasing_regions(tcx.param_env(def_id), fn_sig)
47                     })
48                     .subst(tcx, substs),
49                 _ => unreachable!(),
50             };
51
52             if let ty::InstanceDef::VTableShim(..) = instance.def {
53                 // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
54                 sig = sig.map_bound(|mut sig| {
55                     let mut inputs_and_output = sig.inputs_and_output.to_vec();
56                     inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]);
57                     sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output);
58                     sig
59                 });
60             }
61             sig
62         }
63         ty::Closure(def_id, substs) => {
64             let sig = substs.as_closure().sig();
65
66             let bound_vars = tcx.mk_bound_variable_kinds(
67                 sig.bound_vars().iter().chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))),
68             );
69             let br = ty::BoundRegion {
70                 var: ty::BoundVar::from_usize(bound_vars.len() - 1),
71                 kind: ty::BoundRegionKind::BrEnv,
72             };
73             let env_region = ty::ReLateBound(ty::INNERMOST, br);
74             let env_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap();
75
76             let sig = sig.skip_binder();
77             ty::Binder::bind_with_vars(
78                 tcx.mk_fn_sig(
79                     iter::once(env_ty).chain(sig.inputs().iter().cloned()),
80                     sig.output(),
81                     sig.c_variadic,
82                     sig.unsafety,
83                     sig.abi,
84                 ),
85                 bound_vars,
86             )
87         }
88         ty::Generator(did, substs, _) => {
89             let sig = substs.as_generator().poly_sig();
90
91             let bound_vars = tcx.mk_bound_variable_kinds(
92                 sig.bound_vars().iter().chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))),
93             );
94             let br = ty::BoundRegion {
95                 var: ty::BoundVar::from_usize(bound_vars.len() - 1),
96                 kind: ty::BoundRegionKind::BrEnv,
97             };
98             let env_region = ty::ReLateBound(ty::INNERMOST, br);
99             let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
100
101             let pin_did = tcx.require_lang_item(LangItem::Pin, None);
102             let pin_adt_ref = tcx.adt_def(pin_did);
103             let pin_substs = tcx.intern_substs(&[env_ty.into()]);
104             let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
105
106             let sig = sig.skip_binder();
107             // The `FnSig` and the `ret_ty` here is for a generators main
108             // `Generator::resume(...) -> GeneratorState` function in case we
109             // have an ordinary generator, or the `Future::poll(...) -> Poll`
110             // function in case this is a special generator backing an async construct.
111             let ret_ty = if tcx.generator_is_async(did) {
112                 let state_did = tcx.require_lang_item(LangItem::Poll, None);
113                 let state_adt_ref = tcx.adt_def(state_did);
114                 let state_substs = tcx.intern_substs(&[sig.return_ty.into()]);
115                 tcx.mk_adt(state_adt_ref, state_substs)
116             } else {
117                 let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
118                 let state_adt_ref = tcx.adt_def(state_did);
119                 let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
120                 tcx.mk_adt(state_adt_ref, state_substs)
121             };
122
123             ty::Binder::bind_with_vars(
124                 tcx.mk_fn_sig(
125                     [env_ty, sig.resume_ty].iter(),
126                     &ret_ty,
127                     false,
128                     hir::Unsafety::Normal,
129                     rustc_target::spec::abi::Abi::Rust,
130                 ),
131                 bound_vars,
132             )
133         }
134         _ => bug!("unexpected type {:?} in Instance::fn_sig", ty),
135     }
136 }
137
138 #[inline]
139 fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
140     use rustc_target::spec::abi::Abi::*;
141     match tcx.sess.target.adjust_abi(abi) {
142         RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,
143         RustCold => Conv::RustCold,
144
145         // It's the ABI's job to select this, not ours.
146         System { .. } => bug!("system abi should be selected elsewhere"),
147         EfiApi => bug!("eficall abi should be selected elsewhere"),
148
149         Stdcall { .. } => Conv::X86Stdcall,
150         Fastcall { .. } => Conv::X86Fastcall,
151         Vectorcall { .. } => Conv::X86VectorCall,
152         Thiscall { .. } => Conv::X86ThisCall,
153         C { .. } => Conv::C,
154         Unadjusted => Conv::C,
155         Win64 { .. } => Conv::X86_64Win64,
156         SysV64 { .. } => Conv::X86_64SysV,
157         Aapcs { .. } => Conv::ArmAapcs,
158         CCmseNonSecureCall => Conv::CCmseNonSecureCall,
159         PtxKernel => Conv::PtxKernel,
160         Msp430Interrupt => Conv::Msp430Intr,
161         X86Interrupt => Conv::X86Intr,
162         AmdGpuKernel => Conv::AmdGpuKernel,
163         AvrInterrupt => Conv::AvrInterrupt,
164         AvrNonBlockingInterrupt => Conv::AvrNonBlockingInterrupt,
165         Wasm => Conv::C,
166
167         // These API constants ought to be more specific...
168         Cdecl { .. } => Conv::C,
169     }
170 }
171
172 fn fn_abi_of_fn_ptr<'tcx>(
173     tcx: TyCtxt<'tcx>,
174     query: ty::ParamEnvAnd<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>)>,
175 ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
176     let (param_env, (sig, extra_args)) = query.into_parts();
177
178     let cx = LayoutCx { tcx, param_env };
179     fn_abi_new_uncached(&cx, sig, extra_args, None, None, false)
180 }
181
182 fn fn_abi_of_instance<'tcx>(
183     tcx: TyCtxt<'tcx>,
184     query: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>)>,
185 ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
186     let (param_env, (instance, extra_args)) = query.into_parts();
187
188     let sig = fn_sig_for_fn_abi(tcx, instance, param_env);
189
190     let caller_location = if instance.def.requires_caller_location(tcx) {
191         Some(tcx.caller_location_ty())
192     } else {
193         None
194     };
195
196     fn_abi_new_uncached(
197         &LayoutCx { tcx, param_env },
198         sig,
199         extra_args,
200         caller_location,
201         Some(instance.def_id()),
202         matches!(instance.def, ty::InstanceDef::Virtual(..)),
203     )
204 }
205
206 // Handle safe Rust thin and fat pointers.
207 fn adjust_for_rust_scalar<'tcx>(
208     cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
209     attrs: &mut ArgAttributes,
210     scalar: Scalar,
211     layout: TyAndLayout<'tcx>,
212     offset: Size,
213     is_return: bool,
214 ) {
215     // Booleans are always a noundef i1 that needs to be zero-extended.
216     if scalar.is_bool() {
217         attrs.ext(ArgExtension::Zext);
218         attrs.set(ArgAttribute::NoUndef);
219         return;
220     }
221
222     // Scalars which have invalid values cannot be undef.
223     if !scalar.is_always_valid(&cx) {
224         attrs.set(ArgAttribute::NoUndef);
225     }
226
227     // Only pointer types handled below.
228     let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
229
230     if !valid_range.contains(0) {
231         attrs.set(ArgAttribute::NonNull);
232     }
233
234     if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
235         if let Some(kind) = pointee.safe {
236             attrs.pointee_align = Some(pointee.align);
237
238             // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
239             // for the entire duration of the function as they can be deallocated
240             // at any time. Same for shared mutable references. If LLVM had a
241             // way to say "dereferenceable on entry" we could use it here.
242             attrs.pointee_size = match kind {
243                 PointerKind::UniqueBorrowed
244                 | PointerKind::UniqueBorrowedPinned
245                 | PointerKind::Frozen => pointee.size,
246                 PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
247             };
248
249             // `Box`, `&T`, and `&mut T` cannot be undef.
250             // Note that this only applies to the value of the pointer itself;
251             // this attribute doesn't make it UB for the pointed-to data to be undef.
252             attrs.set(ArgAttribute::NoUndef);
253
254             // The aliasing rules for `Box<T>` are still not decided, but currently we emit
255             // `noalias` for it. This can be turned off using an unstable flag.
256             // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
257             let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias.unwrap_or(true);
258
259             // `&mut` pointer parameters never alias other parameters,
260             // or mutable global data
261             //
262             // `&T` where `T` contains no `UnsafeCell<U>` is immutable,
263             // and can be marked as both `readonly` and `noalias`, as
264             // LLVM's definition of `noalias` is based solely on memory
265             // dependencies rather than pointer equality
266             //
267             // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
268             // for UniqueBorrowed arguments, so that the codegen backend can decide whether
269             // or not to actually emit the attribute. It can also be controlled with the
270             // `-Zmutable-noalias` debugging option.
271             let no_alias = match kind {
272                 PointerKind::SharedMutable
273                 | PointerKind::UniqueBorrowed
274                 | PointerKind::UniqueBorrowedPinned => false,
275                 PointerKind::UniqueOwned => noalias_for_box,
276                 PointerKind::Frozen => !is_return,
277             };
278             if no_alias {
279                 attrs.set(ArgAttribute::NoAlias);
280             }
281
282             if kind == PointerKind::Frozen && !is_return {
283                 attrs.set(ArgAttribute::ReadOnly);
284             }
285
286             if kind == PointerKind::UniqueBorrowed && !is_return {
287                 attrs.set(ArgAttribute::NoAliasMutRef);
288             }
289         }
290     }
291 }
292
293 // FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
294 // arguments of this method, into a separate `struct`.
295 #[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))]
296 fn fn_abi_new_uncached<'tcx>(
297     cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
298     sig: ty::PolyFnSig<'tcx>,
299     extra_args: &[Ty<'tcx>],
300     caller_location: Option<Ty<'tcx>>,
301     fn_def_id: Option<DefId>,
302     // FIXME(eddyb) replace this with something typed, like an `enum`.
303     force_thin_self_ptr: bool,
304 ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
305     let sig = cx.tcx.normalize_erasing_late_bound_regions(cx.param_env, sig);
306
307     let conv = conv_from_spec_abi(cx.tcx(), sig.abi);
308
309     let mut inputs = sig.inputs();
310     let extra_args = if sig.abi == RustCall {
311         assert!(!sig.c_variadic && extra_args.is_empty());
312
313         if let Some(input) = sig.inputs().last() {
314             if let ty::Tuple(tupled_arguments) = input.kind() {
315                 inputs = &sig.inputs()[0..sig.inputs().len() - 1];
316                 tupled_arguments
317             } else {
318                 bug!(
319                     "argument to function with \"rust-call\" ABI \
320                         is not a tuple"
321                 );
322             }
323         } else {
324             bug!(
325                 "argument to function with \"rust-call\" ABI \
326                     is not a tuple"
327             );
328         }
329     } else {
330         assert!(sig.c_variadic || extra_args.is_empty());
331         extra_args
332     };
333
334     let target = &cx.tcx.sess.target;
335     let target_env_gnu_like = matches!(&target.env[..], "gnu" | "musl" | "uclibc");
336     let win_x64_gnu = target.os == "windows" && target.arch == "x86_64" && target.env == "gnu";
337     let linux_s390x_gnu_like =
338         target.os == "linux" && target.arch == "s390x" && target_env_gnu_like;
339     let linux_sparc64_gnu_like =
340         target.os == "linux" && target.arch == "sparc64" && target_env_gnu_like;
341     let linux_powerpc_gnu_like =
342         target.os == "linux" && target.arch == "powerpc" && target_env_gnu_like;
343     use SpecAbi::*;
344     let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
345
346     let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
347         let span = tracing::debug_span!("arg_of");
348         let _entered = span.enter();
349         let is_return = arg_idx.is_none();
350
351         let layout = cx.layout_of(ty)?;
352         let layout = if force_thin_self_ptr && arg_idx == Some(0) {
353             // Don't pass the vtable, it's not an argument of the virtual fn.
354             // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
355             // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen
356             make_thin_self_ptr(cx, layout)
357         } else {
358             layout
359         };
360
361         let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| {
362             let mut attrs = ArgAttributes::new();
363             adjust_for_rust_scalar(*cx, &mut attrs, scalar, *layout, offset, is_return);
364             attrs
365         });
366
367         if arg.layout.is_zst() {
368             // For some forsaken reason, x86_64-pc-windows-gnu
369             // doesn't ignore zero-sized struct arguments.
370             // The same is true for {s390x,sparc64,powerpc}-unknown-linux-{gnu,musl,uclibc}.
371             if is_return
372                 || rust_abi
373                 || (!win_x64_gnu
374                     && !linux_s390x_gnu_like
375                     && !linux_sparc64_gnu_like
376                     && !linux_powerpc_gnu_like)
377             {
378                 arg.mode = PassMode::Ignore;
379             }
380         }
381
382         Ok(arg)
383     };
384
385     let mut fn_abi = FnAbi {
386         ret: arg_of(sig.output(), None)?,
387         args: inputs
388             .iter()
389             .copied()
390             .chain(extra_args.iter().copied())
391             .chain(caller_location)
392             .enumerate()
393             .map(|(i, ty)| arg_of(ty, Some(i)))
394             .collect::<Result<_, _>>()?,
395         c_variadic: sig.c_variadic,
396         fixed_count: inputs.len() as u32,
397         conv,
398         can_unwind: fn_can_unwind(cx.tcx(), fn_def_id, sig.abi),
399     };
400     fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi, fn_def_id)?;
401     debug!("fn_abi_new_uncached = {:?}", fn_abi);
402     Ok(cx.tcx.arena.alloc(fn_abi))
403 }
404
405 #[tracing::instrument(level = "trace", skip(cx))]
406 fn fn_abi_adjust_for_abi<'tcx>(
407     cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
408     fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>,
409     abi: SpecAbi,
410     fn_def_id: Option<DefId>,
411 ) -> Result<(), FnAbiError<'tcx>> {
412     if abi == SpecAbi::Unadjusted {
413         return Ok(());
414     }
415
416     if abi == SpecAbi::Rust
417         || abi == SpecAbi::RustCall
418         || abi == SpecAbi::RustIntrinsic
419         || abi == SpecAbi::PlatformIntrinsic
420     {
421         // Look up the deduced parameter attributes for this function, if we have its def ID and
422         // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes
423         // as appropriate.
424         let deduced_param_attrs = if cx.tcx.sess.opts.optimize != OptLevel::No
425             && cx.tcx.sess.opts.incremental.is_none()
426         {
427             fn_def_id.map(|fn_def_id| cx.tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default()
428         } else {
429             &[]
430         };
431
432         let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, arg_idx: Option<usize>| {
433             if arg.is_ignore() {
434                 return;
435             }
436
437             match arg.layout.abi {
438                 Abi::Aggregate { .. } => {}
439
440                 // This is a fun case! The gist of what this is doing is
441                 // that we want callers and callees to always agree on the
442                 // ABI of how they pass SIMD arguments. If we were to *not*
443                 // make these arguments indirect then they'd be immediates
444                 // in LLVM, which means that they'd used whatever the
445                 // appropriate ABI is for the callee and the caller. That
446                 // means, for example, if the caller doesn't have AVX
447                 // enabled but the callee does, then passing an AVX argument
448                 // across this boundary would cause corrupt data to show up.
449                 //
450                 // This problem is fixed by unconditionally passing SIMD
451                 // arguments through memory between callers and callees
452                 // which should get them all to agree on ABI regardless of
453                 // target feature sets. Some more information about this
454                 // issue can be found in #44367.
455                 //
456                 // Note that the platform intrinsic ABI is exempt here as
457                 // that's how we connect up to LLVM and it's unstable
458                 // anyway, we control all calls to it in libstd.
459                 Abi::Vector { .. }
460                     if abi != SpecAbi::PlatformIntrinsic
461                         && cx.tcx.sess.target.simd_types_indirect =>
462                 {
463                     arg.make_indirect();
464                     return;
465                 }
466
467                 _ => return,
468             }
469
470             let size = arg.layout.size;
471             if arg.layout.is_unsized() || size > Pointer.size(cx) {
472                 arg.make_indirect();
473             } else {
474                 // We want to pass small aggregates as immediates, but using
475                 // a LLVM aggregate type for this leads to bad optimizations,
476                 // so we pick an appropriately sized integer type instead.
477                 arg.cast_to(Reg { kind: RegKind::Integer, size });
478             }
479
480             // If we deduced that this parameter was read-only, add that to the attribute list now.
481             //
482             // The `readonly` parameter only applies to pointers, so we can only do this if the
483             // argument was passed indirectly. (If the argument is passed directly, it's an SSA
484             // value, so it's implicitly immutable.)
485             if let (Some(arg_idx), &mut PassMode::Indirect { ref mut attrs, .. }) =
486                 (arg_idx, &mut arg.mode)
487             {
488                 // The `deduced_param_attrs` list could be empty if this is a type of function
489                 // we can't deduce any parameters for, so make sure the argument index is in
490                 // bounds.
491                 if let Some(deduced_param_attrs) = deduced_param_attrs.get(arg_idx) {
492                     if deduced_param_attrs.read_only {
493                         attrs.regular.insert(ArgAttribute::ReadOnly);
494                         debug!("added deduced read-only attribute");
495                     }
496                 }
497             }
498         };
499
500         fixup(&mut fn_abi.ret, None);
501         for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() {
502             fixup(arg, Some(arg_idx));
503         }
504     } else {
505         fn_abi.adjust_for_foreign_abi(cx, abi)?;
506     }
507
508     Ok(())
509 }
510
511 #[tracing::instrument(level = "debug", skip(cx))]
512 fn make_thin_self_ptr<'tcx>(
513     cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>),
514     layout: TyAndLayout<'tcx>,
515 ) -> TyAndLayout<'tcx> {
516     let tcx = cx.tcx();
517     let fat_pointer_ty = if layout.is_unsized() {
518         // unsized `self` is passed as a pointer to `self`
519         // FIXME (mikeyhew) change this to use &own if it is ever added to the language
520         tcx.mk_mut_ptr(layout.ty)
521     } else {
522         match layout.abi {
523             Abi::ScalarPair(..) | Abi::Scalar(..) => (),
524             _ => bug!("receiver type has unsupported layout: {:?}", layout),
525         }
526
527         // In the case of Rc<Self>, we need to explicitly pass a *mut RcBox<Self>
528         // with a Scalar (not ScalarPair) ABI. This is a hack that is understood
529         // elsewhere in the compiler as a method on a `dyn Trait`.
530         // To get the type `*mut RcBox<Self>`, we just keep unwrapping newtypes until we
531         // get a built-in pointer type
532         let mut fat_pointer_layout = layout;
533         'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr()
534             && !fat_pointer_layout.ty.is_region_ptr()
535         {
536             for i in 0..fat_pointer_layout.fields.count() {
537                 let field_layout = fat_pointer_layout.field(cx, i);
538
539                 if !field_layout.is_zst() {
540                     fat_pointer_layout = field_layout;
541                     continue 'descend_newtypes;
542                 }
543             }
544
545             bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout);
546         }
547
548         fat_pointer_layout.ty
549     };
550
551     // we now have a type like `*mut RcBox<dyn Trait>`
552     // change its layout to that of `*mut ()`, a thin pointer, but keep the same type
553     // this is understood as a special case elsewhere in the compiler
554     let unit_ptr_ty = tcx.mk_mut_ptr(tcx.mk_unit());
555
556     TyAndLayout {
557         ty: fat_pointer_ty,
558
559         // NOTE(eddyb) using an empty `ParamEnv`, and `unwrap`-ing the `Result`
560         // should always work because the type is always `*mut ()`.
561         ..tcx.layout_of(ty::ParamEnv::reveal_all().and(unit_ptr_ty)).unwrap()
562     }
563 }