Skip to content

Commit

Permalink
Auto merge of #128703 - compiler-errors:normalizing-tails, r=lcnr
Browse files Browse the repository at this point in the history
Miscellaneous improvements to struct tail normalization

1. Make checks for foreign tails more accurate by normalizing the struct tail. I didn't write a test for this one.
2. Normalize when computing struct tail for `offset_of` for slice/str. This fixes the new solver only.
3. Normalizing when computing tails for disaligned reference check. This fixes both solvers.

r? lcnr
  • Loading branch information
bors committed Aug 9, 2024
2 parents 97e7252 + b916431 commit 899eb03
Show file tree
Hide file tree
Showing 22 changed files with 245 additions and 46 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
return false;
}

let tail = tcx.struct_tail_erasing_lifetimes(ty, ParamEnv::reveal_all());
let tail = tcx.struct_tail_for_codegen(ty, ParamEnv::reveal_all());
match tail.kind() {
ty::Foreign(..) => false,
ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/unsize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub(crate) fn unsized_info<'tcx>(
old_info: Option<Value>,
) -> Value {
let (source, target) =
fx.tcx.struct_lockstep_tails_erasing_lifetimes(source, target, ParamEnv::reveal_all());
fx.tcx.struct_lockstep_tails_for_codegen(source, target, ParamEnv::reveal_all());
match (&source.kind(), &target.kind()) {
(&ty::Array(_, len), &ty::Slice(_)) => fx
.bcx
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_codegen_llvm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use rustc_data_structures::small_c_str::SmallCStr;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::ty::layout::{
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, LayoutError, LayoutOfHelpers,
TyAndLayout,
};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_sanitizers::{cfi, kcfi};
Expand Down Expand Up @@ -531,7 +532,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
#[instrument(level = "trace", skip(self))]
fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> {
if place.layout.is_unsized() {
let tail = self.tcx.struct_tail_with_normalize(place.layout.ty, |ty| ty, || {});
let tail = self.tcx.struct_tail_for_codegen(place.layout.ty, self.param_env());
if matches!(tail.kind(), ty::Foreign(..)) {
// Unsized locals and, at least conceptually, even unsized arguments must be copied
// around, which requires dynamically determining their size. Therefore, we cannot
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
pointee_ty: Ty<'tcx>,
) -> Option<FatPtrKind> {
let pointee_tail_ty = cx.tcx.struct_tail_erasing_lifetimes(pointee_ty, cx.param_env());
let pointee_tail_ty = cx.tcx.struct_tail_for_codegen(pointee_ty, cx.param_env());
let layout = cx.layout_of(pointee_tail_ty);
trace!(
"fat_pointer_kind: {:?} has layout {:?} (is_unsized? {})",
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
) -> Bx::Value {
let cx = bx.cx();
let (source, target) =
cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, bx.param_env());
cx.tcx().struct_lockstep_tails_for_codegen(source, target, bx.param_env());
match (source.kind(), target.kind()) {
(&ty::Array(_, len), &ty::Slice(_)) => {
cx.const_usize(len.eval_target_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/traits/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> {
return false;
}

let tail = self.tcx().struct_tail_erasing_lifetimes(ty, param_env);
let tail = self.tcx().struct_tail_for_codegen(ty, param_env);
match tail.kind() {
ty::Foreign(..) => false,
ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_const_eval/src/interpret/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,9 +678,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
} else {
// Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`.
// (For that reason we also cannot use `unpack_dyn_trait`.)
let receiver_tail = self
.tcx
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
let receiver_tail =
self.tcx.struct_tail_for_codegen(receiver_place.layout.ty, self.param_env);
let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else {
span_bug!(
self.cur_span(),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx> {
// A<Struct> -> A<Trait> conversion
let (src_pointee_ty, dest_pointee_ty) =
self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env);
self.tcx.struct_lockstep_tails_for_codegen(source_ty, cast_ty, self.param_env);

match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) {
(&ty::Array(_, length), &ty::Slice(_)) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
meta: MemPlaceMeta<M::Provenance>,
pointee: TyAndLayout<'tcx>,
) -> InterpResult<'tcx> {
let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env);
let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.param_env);
match tail.kind() {
ty::Dynamic(data, _, ty::Dyn) => {
let vtable = meta.unwrap_meta().to_pointer(self.ecx)?;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/util/alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ where
};

let ty = place.ty(local_decls, tcx).ty;
let unsized_tail = || tcx.struct_tail_with_normalize(ty, |ty| ty, || {});
let unsized_tail = || tcx.struct_tail_for_codegen(ty, param_env);
match tcx.layout_of(param_env.and(ty)) {
Ok(layout)
if layout.align.abi <= pack
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ fn check_item_type(
UnsizedHandling::Forbid => true,
UnsizedHandling::Allow => false,
UnsizedHandling::AllowIfForeignTail => {
let tail = tcx.struct_tail_erasing_lifetimes(item_ty, wfcx.param_env);
let tail = tcx.struct_tail_for_codegen(item_ty, wfcx.param_env);
!matches!(tail.kind(), ty::Foreign(_))
}
};
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
code: traits::ObligationCauseCode<'tcx>,
) {
if !ty.references_error() {
let tail =
self.tcx.struct_tail_with_normalize(ty, |ty| self.normalize(span, ty), || {});
let tail = self.tcx.struct_tail_with_normalize(
ty,
|ty| {
if self.next_trait_solver() {
self.try_structurally_resolve_type(span, ty)
} else {
self.normalize(span, ty)
}
},
|| {},
);
// Sized types have static alignment, and so do slices.
if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) {
// Nothing else is required here.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ where
metadata
}
} else {
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
match tcx.struct_tail_for_codegen(pointee, cx.param_env()).kind() {
ty::Slice(_) | ty::Str => tcx.types.usize,
ty::Dynamic(data, _, ty::Dyn) => mk_dyn_vtable(data.principal()),
_ => bug!("TyAndLayout::field({:?}): not applicable", this),
Expand Down Expand Up @@ -1348,7 +1348,7 @@ impl<'tcx> TyCtxt<'tcx> {
layout = layout.field(&cx, index);
if !layout.is_sized() {
// If it is not sized, then the tail must still have at least a known static alignment.
let tail = self.struct_tail_erasing_lifetimes(layout.ty, param_env);
let tail = self.struct_tail_for_codegen(layout.ty, param_env);
if !matches!(tail.kind(), ty::Slice(..)) {
bug!(
"offset of not-statically-aligned field (type {:?}) cannot be computed statically",
Expand Down
14 changes: 5 additions & 9 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Should only be called if `ty` has no inference variables and does not
/// need its lifetimes preserved (e.g. as part of codegen); otherwise
/// normalization attempt may cause compiler bugs.
pub fn struct_tail_erasing_lifetimes(
self,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Ty<'tcx> {
pub fn struct_tail_for_codegen(self, ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
let tcx = self;
tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty), || {})
}
Expand All @@ -203,7 +199,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// handle `<T as Trait>::Assoc` and `impl Trait`); pass the identity
/// function to indicate no normalization should take place.
///
/// See also `struct_tail_erasing_lifetimes`, which is suitable for use
/// See also `struct_tail_for_codegen`, which is suitable for use
/// during codegen.
pub fn struct_tail_with_normalize(
self,
Expand Down Expand Up @@ -272,13 +268,13 @@ impl<'tcx> TyCtxt<'tcx> {
/// Same as applying `struct_tail` on `source` and `target`, but only
/// keeps going as long as the two types are instances of the same
/// structure definitions.
/// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, Trait)`,
/// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, dyn Trait)`,
/// whereas struct_tail produces `T`, and `Trait`, respectively.
///
/// Should only be called if the types have no inference variables and do
/// not need their lifetimes preserved (e.g., as part of codegen); otherwise,
/// normalization attempt may cause compiler bugs.
pub fn struct_lockstep_tails_erasing_lifetimes(
pub fn struct_lockstep_tails_for_codegen(
self,
source: Ty<'tcx>,
target: Ty<'tcx>,
Expand All @@ -296,7 +292,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, Trait)`,
/// whereas struct_tail produces `T`, and `Trait`, respectively.
///
/// See also `struct_lockstep_tails_erasing_lifetimes`, which is suitable for use
/// See also `struct_lockstep_tails_for_codegen`, which is suitable for use
/// during codegen.
pub fn struct_lockstep_tails_with_normalize(
self,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1019,7 +1019,7 @@ fn find_vtable_types_for_unsizing<'tcx>(
if ty.is_sized(tcx.tcx, param_env) {
return false;
}
let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env);
let tail = tcx.struct_tail_for_codegen(ty, param_env);
match tail.kind() {
ty::Foreign(..) => false,
ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,
Expand All @@ -1029,7 +1029,7 @@ fn find_vtable_types_for_unsizing<'tcx>(
if type_has_metadata(inner_source) {
(inner_source, inner_target)
} else {
tcx.struct_lockstep_tails_erasing_lifetimes(inner_source, inner_target, param_env)
tcx.struct_lockstep_tails_for_codegen(inner_source, inner_target, param_env)
}
};

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ty_utils/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,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_with_normalize(arg.layout.ty, |ty| ty, || {});
let tail = cx.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
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ty_utils/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ fn layout_of_uncached<'tcx>(

metadata
} else {
let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
let unsized_part = tcx.struct_tail_for_codegen(pointee, param_env);

match unsized_part.kind() {
ty::Foreign(..) => {
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ fn check_rvalue<'tcx>(
// We cannot allow this for now.
return Err((span, "unsizing casts are only allowed for references right now".into()));
};
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
let unsized_ty = tcx.struct_tail_for_codegen(pointee_ty, tcx.param_env(def_id));
if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
check_operand(tcx, op, span, body, msrv)?;
// Casting/coercing things to slices is fine.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:28:13
--> $DIR/unaligned_references.rs:32:13
|
LL | &self.x;
| ^^^^^^^
Expand All @@ -9,7 +9,7 @@ LL | &self.x;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:40:24
--> $DIR/unaligned_references.rs:44:24
|
LL | println!("{:?}", &*foo.0);
| ^^^^^
Expand All @@ -19,7 +19,7 @@ LL | println!("{:?}", &*foo.0);
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:42:24
--> $DIR/unaligned_references.rs:46:24
|
LL | println!("{:?}", &*foo.0);
| ^^^^^
Expand All @@ -29,7 +29,7 @@ LL | println!("{:?}", &*foo.0);
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:47:24
--> $DIR/unaligned_references.rs:51:24
|
LL | println!("{:?}", &*foo.0);
| ^^^^^
Expand All @@ -39,7 +39,7 @@ LL | println!("{:?}", &*foo.0);
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:57:17
--> $DIR/unaligned_references.rs:81:17
|
LL | let _ = &good.ptr;
| ^^^^^^^^^
Expand All @@ -49,7 +49,7 @@ LL | let _ = &good.ptr;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:58:17
--> $DIR/unaligned_references.rs:82:17
|
LL | let _ = &good.data;
| ^^^^^^^^^^
Expand All @@ -59,7 +59,7 @@ LL | let _ = &good.data;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:60:17
--> $DIR/unaligned_references.rs:84:17
|
LL | let _ = &good.data as *const _;
| ^^^^^^^^^^
Expand All @@ -69,7 +69,7 @@ LL | let _ = &good.data as *const _;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:61:27
--> $DIR/unaligned_references.rs:85:27
|
LL | let _: *const _ = &good.data;
| ^^^^^^^^^^
Expand All @@ -79,7 +79,7 @@ LL | let _: *const _ = &good.data;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:63:17
--> $DIR/unaligned_references.rs:87:17
|
LL | let _ = good.data.clone();
| ^^^^^^^^^
Expand All @@ -89,7 +89,7 @@ LL | let _ = good.data.clone();
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:65:17
--> $DIR/unaligned_references.rs:89:17
|
LL | let _ = &good.data2[0];
| ^^^^^^^^^^^^^^
Expand All @@ -99,7 +99,7 @@ LL | let _ = &good.data2[0];
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:74:17
--> $DIR/unaligned_references.rs:98:17
|
LL | let _ = &packed2.x;
| ^^^^^^^^^^
Expand All @@ -109,7 +109,7 @@ LL | let _ = &packed2.x;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:113:20
--> $DIR/unaligned_references.rs:137:20
|
LL | let _ref = &m1.1.a;
| ^^^^^^^
Expand All @@ -119,7 +119,7 @@ LL | let _ref = &m1.1.a;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:116:20
--> $DIR/unaligned_references.rs:140:20
|
LL | let _ref = &m2.1.a;
| ^^^^^^^
Expand Down
Loading

0 comments on commit 899eb03

Please sign in to comment.