Fix transmute goal
This commit is contained in:
parent
38bbcc001e
commit
9453d2cfeb
@ -295,6 +295,37 @@ fn structurally_normalize_ty(
|
|||||||
Ok(ty)
|
Ok(ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Normalize a const for when it is structurally matched on, or more likely
|
||||||
|
/// when it needs `.try_to_*` called on it (e.g. to turn it into a usize).
|
||||||
|
///
|
||||||
|
/// This function is necessary in nearly all cases before matching on a const.
|
||||||
|
/// Not doing so is likely to be incomplete and therefore unsound during
|
||||||
|
/// coherence.
|
||||||
|
#[instrument(level = "trace", skip(self, param_env), ret)]
|
||||||
|
fn structurally_normalize_const(
|
||||||
|
&mut self,
|
||||||
|
param_env: I::ParamEnv,
|
||||||
|
ct: I::Const,
|
||||||
|
) -> Result<I::Const, NoSolution> {
|
||||||
|
if let ty::ConstKind::Unevaluated(..) = ct.kind() {
|
||||||
|
let normalized_ct = self.next_const_infer();
|
||||||
|
let alias_relate_goal = Goal::new(
|
||||||
|
self.cx(),
|
||||||
|
param_env,
|
||||||
|
ty::PredicateKind::AliasRelate(
|
||||||
|
ct.into(),
|
||||||
|
normalized_ct.into(),
|
||||||
|
ty::AliasRelationDirection::Equate,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
self.add_goal(GoalSource::Misc, alias_relate_goal);
|
||||||
|
self.try_evaluate_added_goals()?;
|
||||||
|
Ok(self.resolve_vars_if_possible(normalized_ct))
|
||||||
|
} else {
|
||||||
|
Ok(ct)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn response_no_constraints_raw<I: Interner>(
|
fn response_no_constraints_raw<I: Interner>(
|
||||||
|
@ -627,11 +627,16 @@ fn consider_builtin_transmute_candidate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
||||||
|
let assume = ecx.structurally_normalize_const(
|
||||||
|
goal.param_env,
|
||||||
|
goal.predicate.trait_ref.args.const_at(2),
|
||||||
|
)?;
|
||||||
|
|
||||||
let certainty = ecx.is_transmutable(
|
let certainty = ecx.is_transmutable(
|
||||||
goal.param_env,
|
goal.param_env,
|
||||||
goal.predicate.trait_ref.args.type_at(0),
|
goal.predicate.trait_ref.args.type_at(0),
|
||||||
goal.predicate.trait_ref.args.type_at(1),
|
goal.predicate.trait_ref.args.type_at(1),
|
||||||
goal.predicate.trait_ref.args.const_at(2),
|
assume,
|
||||||
)?;
|
)?;
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
|
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
|
||||||
})
|
})
|
||||||
|
@ -2247,124 +2247,143 @@ fn get_safe_transmute_error_and_reason(
|
|||||||
span: Span,
|
span: Span,
|
||||||
) -> GetSafeTransmuteErrorAndReason {
|
) -> GetSafeTransmuteErrorAndReason {
|
||||||
use rustc_transmute::Answer;
|
use rustc_transmute::Answer;
|
||||||
|
self.probe(|_| {
|
||||||
|
// We don't assemble a transmutability candidate for types that are generic
|
||||||
|
// and we should have ambiguity for types that still have non-region infer.
|
||||||
|
if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() {
|
||||||
|
return GetSafeTransmuteErrorAndReason::Default;
|
||||||
|
}
|
||||||
|
|
||||||
// We don't assemble a transmutability candidate for types that are generic
|
// Erase regions because layout code doesn't particularly care about regions.
|
||||||
// and we should have ambiguity for types that still have non-region infer.
|
let trait_ref =
|
||||||
if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() {
|
self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
|
||||||
return GetSafeTransmuteErrorAndReason::Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Erase regions because layout code doesn't particularly care about regions.
|
let src_and_dst = rustc_transmute::Types {
|
||||||
let trait_ref =
|
dst: trait_ref.args.type_at(0),
|
||||||
self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
|
src: trait_ref.args.type_at(1),
|
||||||
|
};
|
||||||
|
|
||||||
let src_and_dst = rustc_transmute::Types {
|
let ocx = ObligationCtxt::new(self);
|
||||||
dst: trait_ref.args.type_at(0),
|
let Ok(assume) = ocx.structurally_normalize_const(
|
||||||
src: trait_ref.args.type_at(1),
|
&obligation.cause,
|
||||||
};
|
obligation.param_env,
|
||||||
let Some(assume) = rustc_transmute::Assume::from_const(
|
trait_ref.args.const_at(2),
|
||||||
self.infcx.tcx,
|
) else {
|
||||||
obligation.param_env,
|
self.dcx().span_delayed_bug(
|
||||||
trait_ref.args.const_at(2),
|
span,
|
||||||
) else {
|
"Unable to construct rustc_transmute::Assume where it was previously possible",
|
||||||
self.dcx().span_delayed_bug(
|
);
|
||||||
span,
|
return GetSafeTransmuteErrorAndReason::Silent;
|
||||||
"Unable to construct rustc_transmute::Assume where it was previously possible",
|
};
|
||||||
);
|
|
||||||
return GetSafeTransmuteErrorAndReason::Silent;
|
|
||||||
};
|
|
||||||
|
|
||||||
let dst = trait_ref.args.type_at(0);
|
let Some(assume) =
|
||||||
let src = trait_ref.args.type_at(1);
|
rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume)
|
||||||
|
else {
|
||||||
|
self.dcx().span_delayed_bug(
|
||||||
|
span,
|
||||||
|
"Unable to construct rustc_transmute::Assume where it was previously possible",
|
||||||
|
);
|
||||||
|
return GetSafeTransmuteErrorAndReason::Silent;
|
||||||
|
};
|
||||||
|
|
||||||
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
|
let dst = trait_ref.args.type_at(0);
|
||||||
|
let src = trait_ref.args.type_at(1);
|
||||||
|
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
|
||||||
|
|
||||||
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
|
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
|
||||||
obligation.cause,
|
obligation.cause,
|
||||||
src_and_dst,
|
src_and_dst,
|
||||||
assume,
|
assume,
|
||||||
) {
|
) {
|
||||||
Answer::No(reason) => {
|
Answer::No(reason) => {
|
||||||
let safe_transmute_explanation = match reason {
|
let safe_transmute_explanation = match reason {
|
||||||
rustc_transmute::Reason::SrcIsNotYetSupported => {
|
rustc_transmute::Reason::SrcIsNotYetSupported => {
|
||||||
format!("analyzing the transmutability of `{src}` is not yet supported")
|
format!("analyzing the transmutability of `{src}` is not yet supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
rustc_transmute::Reason::DstIsNotYetSupported => {
|
rustc_transmute::Reason::DstIsNotYetSupported => {
|
||||||
format!("analyzing the transmutability of `{dst}` is not yet supported")
|
format!("analyzing the transmutability of `{dst}` is not yet supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
rustc_transmute::Reason::DstIsBitIncompatible => {
|
rustc_transmute::Reason::DstIsBitIncompatible => {
|
||||||
format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`")
|
format!(
|
||||||
}
|
"at least one value of `{src}` isn't a bit-valid value of `{dst}`"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
rustc_transmute::Reason::DstUninhabited => {
|
rustc_transmute::Reason::DstUninhabited => {
|
||||||
format!("`{dst}` is uninhabited")
|
format!("`{dst}` is uninhabited")
|
||||||
}
|
}
|
||||||
|
|
||||||
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
|
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
|
||||||
format!("`{dst}` may carry safety invariants")
|
format!("`{dst}` may carry safety invariants")
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::DstIsTooBig => {
|
||||||
|
format!("the size of `{src}` is smaller than the size of `{dst}`")
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::DstRefIsTooBig { src, dst } => {
|
||||||
|
let src_size = src.size;
|
||||||
|
let dst_size = dst.size;
|
||||||
|
format!(
|
||||||
|
"the referent size of `{src}` ({src_size} bytes) \
|
||||||
|
is smaller than that of `{dst}` ({dst_size} bytes)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::SrcSizeOverflow => {
|
||||||
|
format!(
|
||||||
|
"values of the type `{src}` are too big for the target architecture"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::DstSizeOverflow => {
|
||||||
|
format!(
|
||||||
|
"values of the type `{dst}` are too big for the target architecture"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::DstHasStricterAlignment {
|
||||||
|
src_min_align,
|
||||||
|
dst_min_align,
|
||||||
|
} => {
|
||||||
|
format!(
|
||||||
|
"the minimum alignment of `{src}` ({src_min_align}) should \
|
||||||
|
be greater than that of `{dst}` ({dst_min_align})"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::DstIsMoreUnique => {
|
||||||
|
format!(
|
||||||
|
"`{src}` is a shared reference, but `{dst}` is a unique reference"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Already reported by rustc
|
||||||
|
rustc_transmute::Reason::TypeError => {
|
||||||
|
return GetSafeTransmuteErrorAndReason::Silent;
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::SrcLayoutUnknown => {
|
||||||
|
format!("`{src}` has an unknown layout")
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::DstLayoutUnknown => {
|
||||||
|
format!("`{dst}` has an unknown layout")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
GetSafeTransmuteErrorAndReason::Error {
|
||||||
|
err_msg,
|
||||||
|
safe_transmute_explanation: Some(safe_transmute_explanation),
|
||||||
}
|
}
|
||||||
rustc_transmute::Reason::DstIsTooBig => {
|
|
||||||
format!("the size of `{src}` is smaller than the size of `{dst}`")
|
|
||||||
}
|
|
||||||
rustc_transmute::Reason::DstRefIsTooBig { src, dst } => {
|
|
||||||
let src_size = src.size;
|
|
||||||
let dst_size = dst.size;
|
|
||||||
format!(
|
|
||||||
"the referent size of `{src}` ({src_size} bytes) is smaller than that of `{dst}` ({dst_size} bytes)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
rustc_transmute::Reason::SrcSizeOverflow => {
|
|
||||||
format!(
|
|
||||||
"values of the type `{src}` are too big for the target architecture"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
rustc_transmute::Reason::DstSizeOverflow => {
|
|
||||||
format!(
|
|
||||||
"values of the type `{dst}` are too big for the target architecture"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
rustc_transmute::Reason::DstHasStricterAlignment {
|
|
||||||
src_min_align,
|
|
||||||
dst_min_align,
|
|
||||||
} => {
|
|
||||||
format!(
|
|
||||||
"the minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
rustc_transmute::Reason::DstIsMoreUnique => {
|
|
||||||
format!("`{src}` is a shared reference, but `{dst}` is a unique reference")
|
|
||||||
}
|
|
||||||
// Already reported by rustc
|
|
||||||
rustc_transmute::Reason::TypeError => {
|
|
||||||
return GetSafeTransmuteErrorAndReason::Silent;
|
|
||||||
}
|
|
||||||
rustc_transmute::Reason::SrcLayoutUnknown => {
|
|
||||||
format!("`{src}` has an unknown layout")
|
|
||||||
}
|
|
||||||
rustc_transmute::Reason::DstLayoutUnknown => {
|
|
||||||
format!("`{dst}` has an unknown layout")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
GetSafeTransmuteErrorAndReason::Error {
|
|
||||||
err_msg,
|
|
||||||
safe_transmute_explanation: Some(safe_transmute_explanation),
|
|
||||||
}
|
}
|
||||||
|
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
|
||||||
|
Answer::Yes => span_bug!(
|
||||||
|
span,
|
||||||
|
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
|
||||||
|
),
|
||||||
|
// Reached when a different obligation (namely `Freeze`) causes the
|
||||||
|
// transmutability analysis to fail. In this case, silence the
|
||||||
|
// transmutability error message in favor of that more specific
|
||||||
|
// error.
|
||||||
|
Answer::If(_) => GetSafeTransmuteErrorAndReason::Error {
|
||||||
|
err_msg,
|
||||||
|
safe_transmute_explanation: None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
|
})
|
||||||
Answer::Yes => span_bug!(
|
|
||||||
span,
|
|
||||||
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
|
|
||||||
),
|
|
||||||
// Reached when a different obligation (namely `Freeze`) causes the
|
|
||||||
// transmutability analysis to fail. In this case, silence the
|
|
||||||
// transmutability error message in favor of that more specific
|
|
||||||
// error.
|
|
||||||
Answer::If(_) => {
|
|
||||||
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For effects predicates such as `<u32 as Add>::Effects: Compat<host>`, pretend that the
|
/// For effects predicates such as `<u32 as Add>::Effects: Compat<host>`, pretend that the
|
||||||
|
@ -329,4 +329,15 @@ pub fn structurally_normalize(
|
|||||||
.at(cause, param_env)
|
.at(cause, param_env)
|
||||||
.structurally_normalize(value, &mut **self.engine.borrow_mut())
|
.structurally_normalize(value, &mut **self.engine.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn structurally_normalize_const(
|
||||||
|
&self,
|
||||||
|
cause: &ObligationCause<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
value: ty::Const<'tcx>,
|
||||||
|
) -> Result<ty::Const<'tcx>, Vec<E>> {
|
||||||
|
self.infcx
|
||||||
|
.at(cause, param_env)
|
||||||
|
.structurally_normalize_const(value, &mut **self.engine.borrow_mut())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -405,11 +405,14 @@ fn flatten_answer_tree<'tcx>(
|
|||||||
|
|
||||||
let predicate = obligation.predicate.skip_binder();
|
let predicate = obligation.predicate.skip_binder();
|
||||||
|
|
||||||
let Some(assume) = rustc_transmute::Assume::from_const(
|
let mut assume = predicate.trait_ref.args.const_at(2);
|
||||||
self.infcx.tcx,
|
// FIXME(min_generic_const_exprs): We should shallowly normalize this.
|
||||||
obligation.param_env,
|
if self.tcx().features().generic_const_exprs {
|
||||||
predicate.trait_ref.args.const_at(2),
|
assume = assume.normalize_internal(self.tcx(), obligation.param_env);
|
||||||
) else {
|
}
|
||||||
|
let Some(assume) =
|
||||||
|
rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume)
|
||||||
|
else {
|
||||||
return Err(Unimplemented);
|
return Err(Unimplemented);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,6 +82,8 @@ fn structurally_normalize_const<E: 'tcx>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(self.infcx.resolve_vars_if_possible(new_infer_ct))
|
Ok(self.infcx.resolve_vars_if_possible(new_infer_ct))
|
||||||
|
} else if self.infcx.tcx.features().generic_const_exprs {
|
||||||
|
Ok(ct.normalize_internal(self.infcx.tcx, self.param_env))
|
||||||
} else {
|
} else {
|
||||||
Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx))
|
Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx))
|
||||||
}
|
}
|
||||||
|
@ -134,12 +134,7 @@ pub fn from_const<'tcx>(
|
|||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
let Some((cv, ty)) = c.try_to_valtree() else {
|
let Some((cv, ty)) = c.try_to_valtree() else {
|
||||||
return Some(Self {
|
return None;
|
||||||
alignment: true,
|
|
||||||
lifetimes: true,
|
|
||||||
safety: true,
|
|
||||||
validity: true,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let adt_def = ty.ty_adt_def()?;
|
let adt_def = ty.ty_adt_def()?;
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
#![feature(transmutability)]
|
||||||
|
#![feature(generic_const_exprs)]
|
||||||
|
//~^ WARN the feature `generic_const_exprs` is incomplete
|
||||||
|
|
||||||
|
use std::mem::{Assume, TransmuteFrom};
|
||||||
|
|
||||||
|
pub fn is_transmutable<const ASSUME_ALIGNMENT: bool>()
|
||||||
|
where
|
||||||
|
(): TransmuteFrom<(), { Assume::SAFETY }>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo<const N: usize>() {
|
||||||
|
is_transmutable::<{}>();
|
||||||
|
//~^ ERROR the trait bound `(): TransmuteFrom<(), { Assume::SAFETY }>` is not satisfied
|
||||||
|
//~| ERROR mismatched types
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,34 @@
|
|||||||
|
warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/dont-assume-err-is-yes-issue-126377.rs:2:12
|
||||||
|
|
|
||||||
|
LL | #![feature(generic_const_exprs)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/dont-assume-err-is-yes-issue-126377.rs:14:23
|
||||||
|
|
|
||||||
|
LL | is_transmutable::<{}>();
|
||||||
|
| ^^ expected `bool`, found `()`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `(): TransmuteFrom<(), { Assume::SAFETY }>` is not satisfied
|
||||||
|
--> $DIR/dont-assume-err-is-yes-issue-126377.rs:14:23
|
||||||
|
|
|
||||||
|
LL | is_transmutable::<{}>();
|
||||||
|
| ^^ the trait `TransmuteFrom<(), { Assume::SAFETY }>` is not implemented for `()`
|
||||||
|
|
|
||||||
|
note: required by a bound in `is_transmutable`
|
||||||
|
--> $DIR/dont-assume-err-is-yes-issue-126377.rs:9:9
|
||||||
|
|
|
||||||
|
LL | pub fn is_transmutable<const ASSUME_ALIGNMENT: bool>()
|
||||||
|
| --------------- required by a bound in this function
|
||||||
|
LL | where
|
||||||
|
LL | (): TransmuteFrom<(), { Assume::SAFETY }>,
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors; 1 warning emitted
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0277, E0308.
|
||||||
|
For more information about an error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user