Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc_layout, rustc_abi: make sure the types are well-formed #115712

Merged
merged 3 commits into from
Sep 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 21 additions & 35 deletions compiler/rustc_passes/src/abi_test.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,31 @@
use rustc_ast::Attribute;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::layout::{FnAbiError, LayoutError};
use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym;
use rustc_target::abi::call::FnAbi;

use super::layout_test::ensure_wf;
use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedField};

pub fn test_abi(tcx: TyCtxt<'_>) {
if !tcx.features().rustc_attrs {
// if the `rustc_attrs` feature is not enabled, don't bother testing ABI
return;
}
for id in tcx.hir().items() {
for attr in tcx.get_attrs(id.owner_id, sym::rustc_abi) {
match tcx.def_kind(id.owner_id) {
DefKind::Fn => {
dump_abi_of_fn_item(tcx, id.owner_id.def_id.into(), attr);
for id in tcx.hir_crate_items(()).definitions() {
for attr in tcx.get_attrs(id, sym::rustc_abi) {
match tcx.def_kind(id) {
DefKind::Fn | DefKind::AssocFn => {
dump_abi_of_fn_item(tcx, id, attr);
}
DefKind::TyAlias { .. } => {
dump_abi_of_fn_type(tcx, id.owner_id.def_id.into(), attr);
dump_abi_of_fn_type(tcx, id, attr);
}
_ => {
tcx.sess.emit_err(AbiInvalidAttribute { span: tcx.def_span(id.owner_id) });
}
}
}
if matches!(tcx.def_kind(id.owner_id), DefKind::Impl { .. }) {
// To find associated functions we need to go into the child items here.
for &id in tcx.associated_item_def_ids(id.owner_id) {
for attr in tcx.get_attrs(id, sym::rustc_abi) {
match tcx.def_kind(id) {
DefKind::AssocFn => {
dump_abi_of_fn_item(tcx, id, attr);
}
_ => {
tcx.sess.emit_err(AbiInvalidAttribute { span: tcx.def_span(id) });
}
}
tcx.sess.emit_err(AbiInvalidAttribute { span: tcx.def_span(id) });
}
}
}
Expand All @@ -49,7 +35,7 @@ pub fn test_abi(tcx: TyCtxt<'_>) {
fn unwrap_fn_abi<'tcx>(
abi: Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>>,
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
item_def_id: LocalDefId,
) -> &'tcx FnAbi<'tcx, Ty<'tcx>> {
match abi {
Ok(abi) => abi,
Expand All @@ -71,10 +57,10 @@ fn unwrap_fn_abi<'tcx>(
}
}

fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
let param_env = tcx.param_env(item_def_id);
let args = GenericArgs::identity_for_item(tcx, item_def_id);
let instance = match Instance::resolve(tcx, param_env, item_def_id, args) {
let instance = match Instance::resolve(tcx, param_env, item_def_id.into(), args) {
Ok(Some(instance)) => instance,
Ok(None) => {
// Not sure what to do here, but `LayoutError::Unknown` seems reasonable?
Expand All @@ -99,7 +85,7 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
for meta_item in meta_items {
match meta_item.name_or_empty() {
sym::debug => {
let fn_name = tcx.item_name(item_def_id);
let fn_name = tcx.item_name(item_def_id.into());
tcx.sess.emit_err(AbiOf {
span: tcx.def_span(item_def_id),
fn_name,
Expand Down Expand Up @@ -128,9 +114,13 @@ fn test_abi_eq<'tcx>(abi1: &'tcx FnAbi<'tcx, Ty<'tcx>>, abi2: &'tcx FnAbi<'tcx,
&& abi1.args.iter().zip(abi2.args.iter()).all(|(arg1, arg2)| arg1.eq_abi(arg2))
}

fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
let param_env = tcx.param_env(item_def_id);
let ty = tcx.type_of(item_def_id).instantiate_identity();
let span = tcx.def_span(item_def_id);
if !ensure_wf(tcx, param_env, ty, item_def_id, span) {
return;
}
let meta_items = attr.meta_item_list().unwrap_or_default();
for meta_item in meta_items {
match meta_item.name_or_empty() {
Expand All @@ -147,12 +137,8 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
item_def_id,
);

let fn_name = tcx.item_name(item_def_id);
tcx.sess.emit_err(AbiOf {
span: tcx.def_span(item_def_id),
fn_name,
fn_abi: format!("{:#?}", abi),
});
let fn_name = tcx.item_name(item_def_id.into());
tcx.sess.emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) });
}
sym::assert_eq => {
let ty::Tuple(fields) = ty.kind() else {
Expand Down Expand Up @@ -196,7 +182,7 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {

if !test_abi_eq(abi1, abi2) {
tcx.sess.emit_err(AbiNe {
span: tcx.def_span(item_def_id),
span,
left: format!("{:#?}", abi1),
right: format!("{:#?}", abi2),
});
Expand Down
82 changes: 49 additions & 33 deletions compiler/rustc_passes/src/layout_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ use rustc_ast::Attribute;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::abi::{HasDataLayout, TargetDataLayout};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::{infer::TyCtxtInferExt, traits};

use crate::errors::{
LayoutAbi, LayoutAlign, LayoutHomogeneousAggregate, LayoutInvalidAttribute, LayoutOf,
Expand All @@ -18,31 +20,58 @@ pub fn test_layout(tcx: TyCtxt<'_>) {
// if the `rustc_attrs` feature is not enabled, don't bother testing layout
return;
}
for id in tcx.hir().items() {
for attr in tcx.get_attrs(id.owner_id, sym::rustc_layout) {
match tcx.def_kind(id.owner_id) {
for id in tcx.hir_crate_items(()).definitions() {
for attr in tcx.get_attrs(id, sym::rustc_layout) {
match tcx.def_kind(id) {
DefKind::TyAlias { .. } | DefKind::Enum | DefKind::Struct | DefKind::Union => {
dump_layout_of(tcx, id.owner_id.def_id, attr);
dump_layout_of(tcx, id, attr);
}
_ => {
tcx.sess.emit_err(LayoutInvalidAttribute { span: tcx.def_span(id.owner_id) });
}
}
}
if matches!(tcx.def_kind(id.owner_id), DefKind::Impl { .. }) {
// To find associated functions we need to go into the child items here.
for &id in tcx.associated_item_def_ids(id.owner_id) {
for _attr in tcx.get_attrs(id, sym::rustc_layout) {
tcx.sess.emit_err(LayoutInvalidAttribute { span: tcx.def_span(id) });
}
}
}
}
}

pub fn ensure_wf<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
ty: Ty<'tcx>,
def_id: LocalDefId,
span: Span,
) -> bool {
let pred = ty::ClauseKind::WellFormed(ty.into());
let obligation = traits::Obligation::new(
tcx,
traits::ObligationCause::new(
span,
def_id,
traits::ObligationCauseCode::WellFormed(Some(traits::WellFormedLoc::Ty(def_id))),
),
param_env,
pred,
);
let infcx = tcx.infer_ctxt().build();
let ocx = traits::ObligationCtxt::new(&infcx);
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(&errors);
false
} else {
// looks WF!
true
}
}

fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
let param_env = tcx.param_env(item_def_id);
let ty = tcx.type_of(item_def_id).instantiate_identity();
let span = tcx.def_span(item_def_id.to_def_id());
if !ensure_wf(tcx, param_env, ty, item_def_id, span) {
return;
}
match tcx.layout_of(param_env.and(ty)) {
Ok(ty_layout) => {
// Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
Expand All @@ -51,29 +80,24 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
for meta_item in meta_items {
match meta_item.name_or_empty() {
sym::abi => {
tcx.sess.emit_err(LayoutAbi {
span: tcx.def_span(item_def_id.to_def_id()),
abi: format!("{:?}", ty_layout.abi),
});
tcx.sess.emit_err(LayoutAbi { span, abi: format!("{:?}", ty_layout.abi) });
}

sym::align => {
tcx.sess.emit_err(LayoutAlign {
span: tcx.def_span(item_def_id.to_def_id()),
span,
align: format!("{:?}", ty_layout.align),
});
}

sym::size => {
tcx.sess.emit_err(LayoutSize {
span: tcx.def_span(item_def_id.to_def_id()),
size: format!("{:?}", ty_layout.size),
});
tcx.sess
.emit_err(LayoutSize { span, size: format!("{:?}", ty_layout.size) });
}

sym::homogeneous_aggregate => {
tcx.sess.emit_err(LayoutHomogeneousAggregate {
span: tcx.def_span(item_def_id.to_def_id()),
span,
homogeneous_aggregate: format!(
"{:?}",
ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env })
Expand All @@ -90,11 +114,7 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
)
);
let ty_layout = format!("{:#?}", *ty_layout);
tcx.sess.emit_err(LayoutOf {
span: tcx.def_span(item_def_id.to_def_id()),
normalized_ty,
ty_layout,
});
tcx.sess.emit_err(LayoutOf { span, normalized_ty, ty_layout });
}

name => {
Expand All @@ -105,11 +125,7 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
}

Err(layout_error) => {
tcx.sess.emit_fatal(Spanned {
node: layout_error.into_diagnostic(),

span: tcx.def_span(item_def_id.to_def_id()),
});
tcx.sess.emit_fatal(Spanned { node: layout_error.into_diagnostic(), span });
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions tests/ui/abi/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![feature(rustc_attrs, transparent_unions)]
#![allow(unused, improper_ctypes_definitions)]
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::num::NonZeroI32;
use std::ptr::NonNull;

Expand Down Expand Up @@ -37,9 +38,9 @@ enum ReprCEnum<T> {
Variant2(T),
}
#[repr(C)]
union ReprCUnion<T: Copy> {
union ReprCUnion<T> {
nothing: (),
something: T,
something: ManuallyDrop<T>,
}

macro_rules! test_abi_compatible {
Expand Down Expand Up @@ -82,9 +83,9 @@ struct Wrapper2<T>((), Zst, T);
#[repr(transparent)]
struct Wrapper3<T>(T, [u8; 0], PhantomData<u64>);
#[repr(transparent)]
union WrapperUnion<T: Copy> {
union WrapperUnion<T> {
nothing: (),
something: T,
something: ManuallyDrop<T>,
}

macro_rules! test_transparent {
Expand Down
3 changes: 3 additions & 0 deletions tests/ui/abi/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ type TestAbiNeFloat = (fn(f32), fn(u32)); //~ ERROR: ABIs are not compatible
// Sign matters on some targets (such as s390x), so let's make sure we never accept this.
#[rustc_abi(assert_eq)]
type TestAbiNeSign = (fn(i32), fn(u32)); //~ ERROR: ABIs are not compatible

#[rustc_abi(assert_eq)]
type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); //~ ERROR: cannot be known at compilation time
Loading