diff options
| author | Lukas Markeffsky <@> | 2024-09-15 22:16:21 +0200 |
|---|---|---|
| committer | Lukas Markeffsky <@> | 2024-09-16 15:53:21 +0200 |
| commit | 697450151c0b674eae406a4d1e0854e9386ac7ea (patch) | |
| tree | f5df62671bfc0e817080d1e4643cb880ec2d10f2 /compiler/rustc_ty_utils | |
| parent | 16be6666d4502e0e2255b9c4c1afab87db0ac50f (diff) | |
| download | rust-697450151c0b674eae406a4d1e0854e9386ac7ea.tar.gz rust-697450151c0b674eae406a4d1e0854e9386ac7ea.zip | |
layout computation: eagerly error for unexpected unsized fields
Diffstat (limited to 'compiler/rustc_ty_utils')
| -rw-r--r-- | compiler/rustc_ty_utils/src/abi.rs | 48 | ||||
| -rw-r--r-- | compiler/rustc_ty_utils/src/layout.rs | 98 | ||||
| -rw-r--r-- | compiler/rustc_ty_utils/src/layout_sanity_check.rs | 8 |
3 files changed, 95 insertions, 59 deletions
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 0d433da3aea..2d0c2e83690 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -331,7 +331,7 @@ fn fn_abi_of_fn_ptr<'tcx>( ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let (param_env, (sig, extra_args)) = query.into_parts(); - let cx = LayoutCx { tcx, param_env }; + let cx = LayoutCx::new(tcx, param_env); fn_abi_new_uncached(&cx, sig, extra_args, None, None, false) } @@ -347,7 +347,7 @@ fn fn_abi_of_instance<'tcx>( instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()); fn_abi_new_uncached( - &LayoutCx { tcx, param_env }, + &LayoutCx::new(tcx, param_env), sig, extra_args, caller_location, @@ -386,12 +386,14 @@ fn adjust_for_rust_scalar<'tcx>( attrs.set(ArgAttribute::NonNull); } + let tcx = cx.tcx(); + if let Some(pointee) = layout.pointee_info_at(&cx, offset) { let kind = if let Some(kind) = pointee.safe { Some(kind) } else if let Some(pointee) = drop_target_pointee { // The argument to `drop_in_place` is semantically equivalent to a mutable reference. - Some(PointerKind::MutableRef { unpin: pointee.is_unpin(cx.tcx, cx.param_env()) }) + Some(PointerKind::MutableRef { unpin: pointee.is_unpin(tcx, cx.param_env()) }) } else { None }; @@ -415,12 +417,12 @@ fn adjust_for_rust_scalar<'tcx>( // The aliasing rules for `Box<T>` are still not decided, but currently we emit // `noalias` for it. This can be turned off using an unstable flag. // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 - let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias; + let noalias_for_box = tcx.sess.opts.unstable_opts.box_noalias; // LLVM prior to version 12 had known miscompiles in the presence of noalias attributes // (see #54878), so it was conditionally disabled, but we don't support earlier // versions at all anymore. We still support turning it off using -Zmutable-noalias. - let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias; + let noalias_mut_ref = tcx.sess.opts.unstable_opts.mutable_noalias; // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on memory @@ -458,6 +460,7 @@ fn fn_abi_sanity_check<'tcx>( spec_abi: SpecAbi, arg: &ArgAbi<'tcx, Ty<'tcx>>, ) { + let tcx = cx.tcx(); match &arg.mode { PassMode::Ignore => {} PassMode::Direct(_) => { @@ -484,7 +487,7 @@ fn fn_abi_sanity_check<'tcx>( // It needs to switch to something else before stabilization can happen. // (See issue: https://github.com/rust-lang/rust/issues/117271) assert!( - matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64") + matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") || matches!(spec_abi, SpecAbi::PtxKernel | SpecAbi::Unadjusted), "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ Problematic type: {:#?}", @@ -516,7 +519,7 @@ fn fn_abi_sanity_check<'tcx>( // With metadata. Must be unsized and not on the stack. assert!(arg.layout.is_unsized() && !on_stack); // Also, must not be `extern` type. - let tail = cx.tcx.struct_tail_for_codegen(arg.layout.ty, cx.param_env()); + let tail = tcx.struct_tail_for_codegen(arg.layout.ty, cx.param_env()); if matches!(tail.kind(), ty::Foreign(..)) { // These types do not have metadata, so having `meta_attrs` is bogus. // Conceptually, unsized arguments must be copied around, which requires dynamically @@ -546,7 +549,8 @@ fn fn_abi_new_uncached<'tcx>( // FIXME(eddyb) replace this with something typed, like an `enum`. force_thin_self_ptr: bool, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { - let sig = cx.tcx.normalize_erasing_late_bound_regions(cx.param_env, sig); + let tcx = cx.tcx(); + let sig = tcx.normalize_erasing_late_bound_regions(cx.param_env, sig); let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic); @@ -576,7 +580,7 @@ fn fn_abi_new_uncached<'tcx>( }; let is_drop_in_place = - fn_def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::DropInPlace)); + fn_def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::DropInPlace)); let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, &'tcx FnAbiError<'tcx>> { let span = tracing::debug_span!("arg_of"); @@ -588,8 +592,7 @@ fn fn_abi_new_uncached<'tcx>( _ => bug!("argument to drop_in_place is not a raw ptr: {:?}", ty), }); - let layout = - cx.layout_of(ty).map_err(|err| &*cx.tcx.arena.alloc(FnAbiError::Layout(*err)))?; + let layout = cx.layout_of(ty).map_err(|err| &*tcx.arena.alloc(FnAbiError::Layout(*err)))?; let layout = if force_thin_self_ptr && arg_idx == Some(0) { // Don't pass the vtable, it's not an argument of the virtual fn. // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` @@ -638,7 +641,7 @@ fn fn_abi_new_uncached<'tcx>( fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi, fn_def_id)?; debug!("fn_abi_new_uncached = {:?}", fn_abi); fn_abi_sanity_check(cx, &fn_abi, sig.abi); - Ok(cx.tcx.arena.alloc(fn_abi)) + Ok(tcx.arena.alloc(fn_abi)) } #[tracing::instrument(level = "trace", skip(cx))] @@ -670,17 +673,18 @@ fn fn_abi_adjust_for_abi<'tcx>( return Ok(()); } + let tcx = cx.tcx(); + if abi == SpecAbi::Rust || abi == SpecAbi::RustCall || abi == SpecAbi::RustIntrinsic { // Look up the deduced parameter attributes for this function, if we have its def ID and // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes // as appropriate. - let deduced_param_attrs = if cx.tcx.sess.opts.optimize != OptLevel::No - && cx.tcx.sess.opts.incremental.is_none() - { - fn_def_id.map(|fn_def_id| cx.tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default() - } else { - &[] - }; + let deduced_param_attrs = + if tcx.sess.opts.optimize != OptLevel::No && tcx.sess.opts.incremental.is_none() { + fn_def_id.map(|fn_def_id| tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default() + } else { + &[] + }; let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, arg_idx: Option<usize>| { if arg.is_ignore() { @@ -689,7 +693,7 @@ fn fn_abi_adjust_for_abi<'tcx>( // Avoid returning floats in x87 registers on x86 as loading and storing from x87 // registers will quiet signalling NaNs. - if cx.tcx.sess.target.arch == "x86" + if tcx.sess.target.arch == "x86" && arg_idx.is_none() // Intrinsics themselves are not actual "real" functions, so theres no need to // change their ABIs. @@ -744,7 +748,7 @@ fn fn_abi_adjust_for_abi<'tcx>( // that's how we connect up to LLVM and it's unstable // anyway, we control all calls to it in libstd. Abi::Vector { .. } - if abi != SpecAbi::RustIntrinsic && cx.tcx.sess.target.simd_types_indirect => + if abi != SpecAbi::RustIntrinsic && tcx.sess.target.simd_types_indirect => { arg.make_indirect(); return; @@ -793,7 +797,7 @@ fn fn_abi_adjust_for_abi<'tcx>( } else { fn_abi .adjust_for_foreign_abi(cx, abi) - .map_err(|err| &*cx.tcx.arena.alloc(FnAbiError::AdjustForForeignAbi(err)))?; + .map_err(|err| &*tcx.arena.alloc(FnAbiError::AdjustForForeignAbi(err)))?; } Ok(()) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 2c2276ad40d..50b6d8a0c3f 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -9,7 +9,7 @@ use rustc_middle::bug; use rustc_middle::mir::{CoroutineLayout, CoroutineSavedLocal}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ - FloatExt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, + FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ @@ -63,14 +63,14 @@ fn layout_of<'tcx>( return tcx.layout_of(param_env.and(ty)); } - let cx = LayoutCx { tcx, param_env }; + let cx = LayoutCx::new(tcx, param_env); let layout = layout_of_uncached(&cx, ty)?; let layout = TyAndLayout { ty, layout }; // If we are running with `-Zprint-type-sizes`, maybe record layouts // for dumping later. - if cx.tcx.sess.opts.unstable_opts.print_type_sizes { + if cx.tcx().sess.opts.unstable_opts.print_type_sizes { record_layout_for_printing(&cx, layout); } @@ -80,7 +80,36 @@ fn layout_of<'tcx>( } fn error<'tcx>(cx: &LayoutCx<'tcx>, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> { - cx.tcx.arena.alloc(err) + cx.tcx().arena.alloc(err) +} + +fn map_error<'tcx>( + cx: &LayoutCx<'tcx>, + ty: Ty<'tcx>, + err: LayoutCalculatorError, +) -> &'tcx LayoutError<'tcx> { + let err = match err { + LayoutCalculatorError::SizeOverflow => { + // This is sometimes not a compile error in `check` builds. + LayoutError::SizeOverflow(ty) + } + LayoutCalculatorError::UnexpectedUnsized => { + // This is sometimes not a compile error if there are trivially false where + // clauses, but it is always a compiler error in the empty environment. + if cx.param_env.caller_bounds().is_empty() { + cx.tcx().dcx().delayed_bug(format!( + "encountered unexpected unsized field in layout of {ty:?}" + )); + } + LayoutError::Unknown(ty) + } + LayoutCalculatorError::EmptyUnion => { + // This is always a compile error. + cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}")); + LayoutError::Unknown(ty) + } + }; + error(cx, err) } fn univariant_uninterned<'tcx>( @@ -90,13 +119,12 @@ fn univariant_uninterned<'tcx>( repr: &ReprOptions, kind: StructKind, ) -> Result<LayoutS<FieldIdx, VariantIdx>, &'tcx LayoutError<'tcx>> { - let dl = cx.data_layout(); let pack = repr.pack; if pack.is_some() && repr.align.is_some() { - cx.tcx.dcx().bug("struct cannot be packed and aligned"); + cx.tcx().dcx().bug("struct cannot be packed and aligned"); } - cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty))) + cx.calc.univariant(fields, repr, kind).map_err(|err| map_error(cx, ty, err)) } fn layout_of_uncached<'tcx>( @@ -110,7 +138,7 @@ fn layout_of_uncached<'tcx>( return Err(error(cx, LayoutError::ReferencesError(guar))); } - let tcx = cx.tcx; + let tcx = cx.tcx(); let param_env = cx.param_env; let dl = cx.data_layout(); let scalar_unit = |value: Primitive| { @@ -188,7 +216,7 @@ fn layout_of_uncached<'tcx>( } // The never type. - ty::Never => tcx.mk_layout(cx.layout_of_never_type()), + ty::Never => tcx.mk_layout(cx.calc.layout_of_never_type()), // Potentially-wide pointers. ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { @@ -264,7 +292,7 @@ fn layout_of_uncached<'tcx>( }; // Effectively a (ptr, meta) tuple. - tcx.mk_layout(cx.scalar_pair(data_ptr, metadata)) + tcx.mk_layout(cx.calc.scalar_pair(data_ptr, metadata)) } ty::Dynamic(_, _, ty::DynStar) => { @@ -272,7 +300,7 @@ fn layout_of_uncached<'tcx>( data.valid_range_mut().start = 0; let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); vtable.valid_range_mut().start = 1; - tcx.mk_layout(cx.scalar_pair(data, vtable)) + tcx.mk_layout(cx.calc.scalar_pair(data, vtable)) } // Arrays and slices. @@ -531,7 +559,7 @@ fn layout_of_uncached<'tcx>( if def.is_union() { if def.repr().pack.is_some() && def.repr().align.is_some() { - cx.tcx.dcx().span_delayed_bug( + tcx.dcx().span_delayed_bug( tcx.def_span(def.did()), "union cannot be packed and aligned", ); @@ -539,8 +567,9 @@ fn layout_of_uncached<'tcx>( } return Ok(tcx.mk_layout( - cx.layout_of_union(&def.repr(), &variants) - .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?, + cx.calc + .layout_of_union(&def.repr(), &variants) + .map_err(|err| map_error(cx, ty, err))?, )); } @@ -557,7 +586,7 @@ fn layout_of_uncached<'tcx>( })?; if is_unsized { - cx.tcx.dcx().span_delayed_bug(tcx.def_span(def.did()), err_msg.to_owned()); + tcx.dcx().span_delayed_bug(tcx.def_span(def.did()), err_msg.to_owned()); Err(error(cx, LayoutError::Unknown(ty))) } else { Ok(()) @@ -600,19 +629,20 @@ fn layout_of_uncached<'tcx>( !tcx.type_of(last_field.did).instantiate_identity().is_sized(tcx, param_env) }); - let Some(layout) = cx.layout_of_struct_or_enum( - &def.repr(), - &variants, - def.is_enum(), - def.is_unsafe_cell(), - tcx.layout_scalar_valid_range(def.did()), - get_discriminant_type, - discriminants_iter(), - dont_niche_optimize_enum, - !maybe_unsized, - ) else { - return Err(error(cx, LayoutError::SizeOverflow(ty))); - }; + let layout = cx + .calc + .layout_of_struct_or_enum( + &def.repr(), + &variants, + def.is_enum(), + def.is_unsafe_cell(), + tcx.layout_scalar_valid_range(def.did()), + get_discriminant_type, + discriminants_iter(), + dont_niche_optimize_enum, + !maybe_unsized, + ) + .map_err(|err| map_error(cx, ty, err))?; // If the struct tail is sized and can be unsized, check that unsizing doesn't move the fields around. if cfg!(debug_assertions) @@ -623,7 +653,7 @@ fn layout_of_uncached<'tcx>( let tail_replacement = cx.layout_of(Ty::new_slice(tcx, tcx.types.u8)).unwrap(); *variants[FIRST_VARIANT].raw.last_mut().unwrap() = tail_replacement.layout; - let Some(unsized_layout) = cx.layout_of_struct_or_enum( + let Ok(unsized_layout) = cx.calc.layout_of_struct_or_enum( &def.repr(), &variants, def.is_enum(), @@ -812,7 +842,7 @@ fn coroutine_layout<'tcx>( args: GenericArgsRef<'tcx>, ) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> { use SavedLocalEligibility::*; - let tcx = cx.tcx; + let tcx = cx.tcx(); let instantiate_field = |ty: Ty<'tcx>| EarlyBinder::bind(ty).instantiate(tcx, args); let Some(info) = tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) else { @@ -832,7 +862,7 @@ fn coroutine_layout<'tcx>( value: Primitive::Int(discr_int, false), valid_range: WrappingRange { start: 0, end: max_discr }, }; - let tag_layout = cx.tcx.mk_layout(LayoutS::scalar(cx, tag)); + let tag_layout = tcx.mk_layout(LayoutS::scalar(cx, tag)); let promoted_layouts = ineligible_locals.iter().map(|local| { let field_ty = instantiate_field(info.field_tys[local].ty); @@ -1025,7 +1055,7 @@ fn record_layout_for_printing<'tcx>(cx: &LayoutCx<'tcx>, layout: TyAndLayout<'tc // (delay format until we actually need it) let record = |kind, packed, opt_discr_size, variants| { let type_desc = with_no_trimmed_paths!(format!("{}", layout.ty)); - cx.tcx.sess.code_stats.record_type_size( + cx.tcx().sess.code_stats.record_type_size( kind, type_desc, layout.align.abi, @@ -1148,8 +1178,8 @@ fn variant_info_for_coroutine<'tcx>( return (vec![], None); }; - let coroutine = cx.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap(); - let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); + let coroutine = cx.tcx().coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap(); + let upvar_names = cx.tcx().closure_saved_names_of_captured_variables(def_id); let mut upvars_size = Size::ZERO; let upvar_fields: Vec<_> = args diff --git a/compiler/rustc_ty_utils/src/layout_sanity_check.rs b/compiler/rustc_ty_utils/src/layout_sanity_check.rs index 38fbd7a9437..be0a7c5ee89 100644 --- a/compiler/rustc_ty_utils/src/layout_sanity_check.rs +++ b/compiler/rustc_ty_utils/src/layout_sanity_check.rs @@ -1,20 +1,22 @@ use std::assert_matches::assert_matches; use rustc_middle::bug; -use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; use rustc_target::abi::*; /// Enforce some basic invariants on layouts. pub(super) fn sanity_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { + let tcx = cx.tcx(); + // Type-level uninhabitedness should always imply ABI uninhabitedness. - if layout.ty.is_privately_uninhabited(cx.tcx, cx.param_env) { + if layout.ty.is_privately_uninhabited(tcx, cx.param_env) { assert!(layout.abi.is_uninhabited()); } if layout.size.bytes() % layout.align.abi.bytes() != 0 { bug!("size is not a multiple of align, in the following layout:\n{layout:#?}"); } - if layout.size.bytes() >= cx.tcx.data_layout.obj_size_bound() { + if layout.size.bytes() >= tcx.data_layout.obj_size_bound() { bug!("size is too large, in the following layout:\n{layout:#?}"); } |
