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