Skip to content

Commit

Permalink
redact coerced type away
Browse files Browse the repository at this point in the history
  • Loading branch information
dingxiangfei2009 committed Feb 11, 2025
1 parent c182ce9 commit 8e48d60
Show file tree
Hide file tree
Showing 10 changed files with 416 additions and 69 deletions.
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_cranelift/example/mini_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ pub trait Unsize<T: ?Sized> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}

#[lang = "coerce_pointee_validated"]
pub trait CoercePointeeValidated {
/* compiler built-in */
}

impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
Expand Down
51 changes: 37 additions & 14 deletions compiler/rustc_codegen_gcc/example/mini_core.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
#![feature(
no_core, lang_items, intrinsics, unboxed_closures, extern_types,
decl_macro, rustc_attrs, transparent_unions, auto_traits, freeze_impls,
no_core,
lang_items,
intrinsics,
unboxed_closures,
extern_types,
decl_macro,
rustc_attrs,
transparent_unions,
auto_traits,
freeze_impls,
thread_local
)]
#![no_core]
Expand All @@ -26,6 +34,11 @@ pub trait Unsize<T: ?Sized> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}

#[lang = "coerce_pointee_validated"]
pub trait CoercePointeeValidated {
/* compiler built-in */
}

impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
Expand All @@ -35,13 +48,13 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
pub trait DispatchFromDyn<T> {}

// &T -> &U
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
// &mut T -> &mut U
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
// *const T -> *const U
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
// *mut T -> *mut U
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U, ()>> for Box<T, ()> {}

#[lang = "legacy_receiver"]
Expand Down Expand Up @@ -289,7 +302,6 @@ impl PartialEq for u32 {
}
}


impl PartialEq for u64 {
fn eq(&self, other: &u64) -> bool {
(*self) == (*other)
Expand Down Expand Up @@ -476,7 +488,11 @@ fn panic_in_cleanup() -> ! {
#[track_caller]
fn panic_bounds_check(index: usize, len: usize) -> ! {
unsafe {
libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
libc::printf(
"index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8,
len,
index,
);
intrinsics::abort();
}
}
Expand Down Expand Up @@ -504,8 +520,7 @@ pub trait Deref {
fn deref(&self) -> &Self::Target;
}

pub trait Allocator {
}
pub trait Allocator {}

impl Allocator for () {}

Expand Down Expand Up @@ -699,19 +714,27 @@ pub struct VaList<'a>(&'a mut VaListImpl);

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro stringify($($t:tt)*) { /* compiler built-in */ }
pub macro stringify($($t:tt)*) {
/* compiler built-in */
}

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro file() { /* compiler built-in */ }
pub macro file() {
/* compiler built-in */
}

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro line() { /* compiler built-in */ }
pub macro line() {
/* compiler built-in */
}

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro cfg() { /* compiler built-in */ }
pub macro cfg() {
/* compiler built-in */
}

pub static A_STATIC: u8 = 42;

Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ hir_analysis_cmse_output_stack_spill =
.note1 = functions with the `"{$abi_name}"` ABI must pass their result via the available return registers
.note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
hir_analysis_coerce_pointee_multiple_targets = `derive(CoercePointee)` only admits exactly one data field, {$diag_trait ->
[DispatchFromDyn] to which `dyn` methods shall be dispatched
*[CoerceUnsized] on which unsize coercion shall be performed
}
hir_analysis_coerce_pointee_no_field = `CoercePointee` can only be derived on `struct`s with at least one field
hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden
Expand Down
139 changes: 108 additions & 31 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//! Check properties that are required by built-in traits and set
//! up data structures required by type-checking/codegen.
mod diagnostics;

use std::assert_matches::assert_matches;
use std::collections::BTreeMap;

use diagnostics::{extract_coerce_pointee_data, redact_fulfillment_err_for_coerce_pointee};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
Expand All @@ -12,6 +15,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::{self, RegionResolutionError, TyCtxtInferExt};
use rustc_infer::traits::Obligation;
use rustc_middle::bug;
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{
Expand All @@ -24,7 +28,7 @@ use rustc_trait_selection::traits::misc::{
type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy,
};
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt};
use tracing::debug;
use tracing::{debug, instrument};

use crate::errors;

Expand Down Expand Up @@ -187,10 +191,10 @@ fn visit_implementation_of_const_param_ty(
}
}

#[instrument(level = "debug", skip(checker), fields(impl_did = ?checker.impl_def_id))]
fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
let tcx = checker.tcx;
let impl_did = checker.impl_def_id;
debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);

// Just compute this for the side-effects, in particular reporting
// errors; other parts of the code may demand it for the info of
Expand All @@ -199,11 +203,11 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E
tcx.at(span).ensure_ok().coerce_unsized_info(impl_did)
}

#[instrument(level = "debug", skip(checker), fields(impl_did = ?checker.impl_def_id))]
fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
let tcx = checker.tcx;
let impl_did = checker.impl_def_id;
let trait_ref = checker.impl_header.trait_ref.instantiate_identity();
debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);

let span = tcx.def_span(impl_did);

Expand Down Expand Up @@ -307,29 +311,45 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
.collect::<Vec<_>>();

if coerced_fields.is_empty() {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle {
span,
trait_name: "DispatchFromDyn",
note: true,
}));
if extract_coerce_pointee_data(tcx, def_a.did()).is_some() {
res = Err(tcx.dcx().span_delayed_bug(
span,
"a specialised message for CoercePointee is expected",
));
} else {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle {
span,
trait_name: "DispatchFromDyn",
note: true,
}));
}
} else if coerced_fields.len() > 1 {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti {
span,
coercions_note: true,
number: coerced_fields.len(),
coercions: coerced_fields
.iter()
.map(|field| {
format!(
"`{}` (`{}` to `{}`)",
field.name,
field.ty(tcx, args_a),
field.ty(tcx, args_b),
)
})
.collect::<Vec<_>>()
.join(", "),
}));
if extract_coerce_pointee_data(tcx, def_a.did()).is_some() {
let spans =
coerced_fields.iter().map(|field| tcx.def_span(field.did)).collect();
res = Err(tcx.dcx().emit_err(errors::CoercePointeeMultipleTargets {
spans,
diag_trait: "DispatchFromDyn",
}));
} else {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti {
span,
coercions_note: true,
number: coerced_fields.len(),
coercions: coerced_fields
.iter()
.map(|field| {
format!(
"`{}` (`{}` to `{}`)",
field.name,
field.ty(tcx, args_a),
field.ty(tcx, args_b),
)
})
.collect::<Vec<_>>()
.join(", "),
}));
}
} else {
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
for field in coerced_fields {
Expand All @@ -344,8 +364,26 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
),
));
}
let errors = ocx.select_all_or_error();
let mut errors = ocx.select_all_or_error();
if !errors.is_empty() {
if let Some((pointee_idx, _)) = extract_coerce_pointee_data(tcx, def_a.did()) {
let target_pointee = args_b.type_at(pointee_idx);
let source_pointee = args_a.type_at(pointee_idx);
let new_pointee =
diagnostics::unsized_type_for_coerce_pointee(tcx, source_pointee);

errors = errors
.into_iter()
.map(|err| {
redact_fulfillment_err_for_coerce_pointee(
tcx,
err,
target_pointee,
new_pointee,
)
})
.collect();
}
res = Err(infcx.err_ctxt().report_fulfillment_errors(errors));
}

Expand All @@ -360,27 +398,29 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
}
}

#[instrument(level = "debug", skip(tcx))]
pub(crate) fn coerce_unsized_info<'tcx>(
tcx: TyCtxt<'tcx>,
impl_did: LocalDefId,
) -> Result<CoerceUnsizedInfo, ErrorGuaranteed> {
debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
let span = tcx.def_span(impl_did);

let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));

let unsize_trait = tcx.require_lang_item(LangItem::Unsize, Some(span));

let source = tcx.type_of(impl_did).instantiate_identity();
let self_ty = source;
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity();
assert_eq!(trait_ref.def_id, coerce_unsized_trait);
let target = trait_ref.args.type_at(1);
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
let coerced_ty = target;
debug!("{:?} -> {:?} (bound)", source, target);

let param_env = tcx.param_env(impl_did);
assert!(!source.has_escaping_bound_vars());

debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
debug!("{:?} -> {:?} (free)", source, target);

let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let cause = ObligationCause::misc(span, impl_did);
Expand Down Expand Up @@ -509,12 +549,28 @@ pub(crate) fn coerce_unsized_info<'tcx>(
.collect::<Vec<_>>();

if diff_fields.is_empty() {
if extract_coerce_pointee_data(tcx, def_a.did()).is_some() {
return Err(tcx.dcx().span_delayed_bug(
span,
"a specialised message for CoercePointee is expected",
));
}
return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField {
span,
trait_name: "CoerceUnsized",
note: true,
}));
} else if diff_fields.len() > 1 {
if extract_coerce_pointee_data(tcx, def_a.did()).is_some() {
let spans = diff_fields
.iter()
.map(|&(idx, _, _)| tcx.def_span(fields[idx].did))
.collect();
return Err(tcx.dcx().emit_err(errors::CoercePointeeMultipleTargets {
spans,
diag_trait: "CoerceUnsized",
}));
}
let item = tcx.hir().expect_item(impl_did);
let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind {
t.path.span
Expand Down Expand Up @@ -547,17 +603,38 @@ pub(crate) fn coerce_unsized_info<'tcx>(
};

// Register an obligation for `A: Trait<B>`.
let coerce_pointee_data = if let ty::Adt(def, _) = self_ty.kind() {
extract_coerce_pointee_data(tcx, def.did())
} else {
None
};
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let cause = traits::ObligationCause::misc(span, impl_did);
let cause = traits::ObligationCause::misc(
span,
coerce_pointee_data.map_or(impl_did, |(_, impl_did)| impl_did.expect_local()),
);
let obligation = Obligation::new(
tcx,
cause,
param_env,
ty::TraitRef::new(tcx, trait_def_id, [source, target]),
);
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error();
let mut errors = ocx.select_all_or_error();
if !errors.is_empty() {
if let Some((pointee, _)) = coerce_pointee_data {
let ty::Adt(_def, args) = coerced_ty.kind() else { bug!() };
let target_pointee = args.type_at(pointee);
let ty::Adt(_def, args) = self_ty.kind() else { bug!() };
let source_pointee = args.type_at(pointee);
let new_pointee = diagnostics::unsized_type_for_coerce_pointee(tcx, source_pointee);
errors = errors
.into_iter()
.map(|err| {
redact_fulfillment_err_for_coerce_pointee(tcx, err, target_pointee, new_pointee)
})
.collect();
}
infcx.err_ctxt().report_fulfillment_errors(errors);
}

Expand Down
Loading

0 comments on commit 8e48d60

Please sign in to comment.