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

Rollup of 5 pull requests #134716

Merged
merged 10 commits into from
Dec 24, 2024
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ const_eval_non_const_fmt_macro_call =
cannot call non-const formatting macro in {const_eval_const_context}s

const_eval_non_const_fn_call =
cannot call non-const fn `{$def_path_str}` in {const_eval_const_context}s
cannot call non-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s

const_eval_non_const_impl =
impl defined here, but it is not `const`
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
}
_ => ccx.dcx().create_err(errors::NonConstFnCall {
span,
def_descr: ccx.tcx.def_descr(callee),
def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
kind: ccx.const_kind(),
}),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ pub(crate) struct NonConstFnCall {
#[primary_span]
pub span: Span,
pub def_path_str: String,
pub def_descr: &'static str,
pub kind: ConstContext,
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0015.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn create_some() -> Option<u8> {
Some(1)
}

// error: cannot call non-const fn `create_some` in constants
// error: cannot call non-const function `create_some` in constants
const FOO: Option<u8> = create_some();
```

Expand Down
165 changes: 137 additions & 28 deletions compiler/rustc_trait_selection/src/traits/effects.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use rustc_hir as hir;
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt};
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes};
use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation};
use rustc_middle::span_bug;
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::{self, TypingMode};
use rustc_type_ir::elaborate::elaborate;
use rustc_type_ir::solve::NoSolution;
use thin_vec::ThinVec;
use thin_vec::{ThinVec, thin_vec};

use super::SelectionContext;
use super::normalize::normalize_with_depth_to;

pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>;

Expand Down Expand Up @@ -38,6 +40,12 @@ pub fn evaluate_host_effect_obligation<'tcx>(
Err(EvaluationFailure::NoSolution) => {}
}

match evaluate_host_effect_from_item_bounds(selcx, obligation) {
Ok(result) => return Ok(result),
Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
Err(EvaluationFailure::NoSolution) => {}
}

match evaluate_host_effect_from_selection_candiate(selcx, obligation) {
Ok(result) => return Ok(result),
Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
Expand All @@ -48,24 +56,45 @@ pub fn evaluate_host_effect_obligation<'tcx>(
}

fn match_candidate<'tcx>(
infcx: &InferCtxt<'tcx>,
selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &HostEffectObligation<'tcx>,
candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
candidate_is_unnormalized: bool,
more_nested: impl FnOnce(&mut SelectionContext<'_, 'tcx>, &mut ThinVec<PredicateObligation<'tcx>>),
) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> {
if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) {
return Err(NoSolution);
}

let candidate = infcx.instantiate_binder_with_fresh_vars(
let mut candidate = selcx.infcx.instantiate_binder_with_fresh_vars(
obligation.cause.span,
BoundRegionConversionTime::HigherRankedType,
candidate,
);

let mut nested = infcx
.at(&obligation.cause, obligation.param_env)
.eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)?
.into_obligations();
let mut nested = thin_vec![];

// Unlike param-env bounds, item bounds may not be normalized.
if candidate_is_unnormalized {
candidate = normalize_with_depth_to(
selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth,
candidate,
&mut nested,
);
}

nested.extend(
selcx
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)?
.into_obligations(),
);

more_nested(selcx, &mut nested);

for nested in &mut nested {
nested.set_depth_from_parent(obligation.recursion_depth);
Expand All @@ -82,36 +111,116 @@ fn evaluate_host_effect_from_bounds<'tcx>(
let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
let mut candidate = None;

for predicate in obligation.param_env.caller_bounds() {
let bound_predicate = predicate.kind();
if let ty::ClauseKind::HostEffect(data) = predicate.kind().skip_binder() {
let data = bound_predicate.rebind(data);
if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
continue;
for clause in obligation.param_env.caller_bounds() {
let bound_clause = clause.kind();
let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
continue;
};
let data = bound_clause.rebind(data);
if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
continue;
}

if !drcx
.args_may_unify(obligation.predicate.trait_ref.args, data.skip_binder().trait_ref.args)
{
continue;
}

let is_match =
infcx.probe(|_| match_candidate(selcx, obligation, data, false, |_, _| {}).is_ok());

if is_match {
if candidate.is_some() {
return Err(EvaluationFailure::Ambiguous);
} else {
candidate = Some(data);
}
}
}

if !drcx.args_may_unify(
obligation.predicate.trait_ref.args,
data.skip_binder().trait_ref.args,
if let Some(data) = candidate {
Ok(match_candidate(selcx, obligation, data, false, |_, _| {})
.expect("candidate matched before, so it should match again"))
} else {
Err(EvaluationFailure::NoSolution)
}
}

fn evaluate_host_effect_from_item_bounds<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &HostEffectObligation<'tcx>,
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
let infcx = selcx.infcx;
let tcx = infcx.tcx;
let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
let mut candidate = None;

let mut consider_ty = obligation.predicate.self_ty();
while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() {
if tcx.is_conditionally_const(alias_ty.def_id) {
for clause in elaborate(
tcx,
tcx.explicit_implied_const_bounds(alias_ty.def_id)
.iter_instantiated_copied(tcx, alias_ty.args)
.map(|(trait_ref, _)| {
trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness)
}),
) {
continue;
}
let bound_clause = clause.kind();
let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
unreachable!("should not elaborate non-HostEffect from HostEffect")
};
let data = bound_clause.rebind(data);
if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
continue;
}

let is_match = infcx.probe(|_| match_candidate(infcx, obligation, data).is_ok());
if !drcx.args_may_unify(
obligation.predicate.trait_ref.args,
data.skip_binder().trait_ref.args,
) {
continue;
}

if is_match {
if candidate.is_some() {
return Err(EvaluationFailure::Ambiguous);
} else {
candidate = Some(data);
let is_match = infcx
.probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());

if is_match {
if candidate.is_some() {
return Err(EvaluationFailure::Ambiguous);
} else {
candidate = Some((data, alias_ty));
}
}
}
}

if kind != ty::Projection {
break;
}

consider_ty = alias_ty.self_ty();
}

if let Some(data) = candidate {
Ok(match_candidate(infcx, obligation, data)
.expect("candidate matched before, so it should match again"))
if let Some((data, alias_ty)) = candidate {
Ok(match_candidate(selcx, obligation, data, true, |selcx, nested| {
// An alias bound only holds if we also check the const conditions
// of the alias, so we need to register those, too.
let const_conditions = normalize_with_depth_to(
selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth,
tcx.const_conditions(alias_ty.def_id).instantiate(tcx, alias_ty.args),
nested,
);
nested.extend(const_conditions.into_iter().map(|(trait_ref, _)| {
obligation
.with(tcx, trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness))
}));
})
.expect("candidate matched before, so it should match again"))
} else {
Err(EvaluationFailure::NoSolution)
}
Expand Down
14 changes: 12 additions & 2 deletions library/core/src/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,8 @@ impl dyn Any + Send {
///
/// # Safety
///
/// Same as the method on the type `dyn Any`.
/// The contained value must be of type `T`. Calling this method
/// with the incorrect type is *undefined behavior*.
#[unstable(feature = "downcast_unchecked", issue = "90850")]
#[inline]
pub unsafe fn downcast_ref_unchecked<T: Any>(&self) -> &T {
Expand Down Expand Up @@ -451,7 +452,8 @@ impl dyn Any + Send {
///
/// # Safety
///
/// Same as the method on the type `dyn Any`.
/// The contained value must be of type `T`. Calling this method
/// with the incorrect type is *undefined behavior*.
#[unstable(feature = "downcast_unchecked", issue = "90850")]
#[inline]
pub unsafe fn downcast_mut_unchecked<T: Any>(&mut self) -> &mut T {
Expand Down Expand Up @@ -552,6 +554,10 @@ impl dyn Any + Send + Sync {
/// assert_eq!(*x.downcast_ref_unchecked::<usize>(), 1);
/// }
/// ```
/// # Safety
///
/// The contained value must be of type `T`. Calling this method
/// with the incorrect type is *undefined behavior*.
#[unstable(feature = "downcast_unchecked", issue = "90850")]
#[inline]
pub unsafe fn downcast_ref_unchecked<T: Any>(&self) -> &T {
Expand All @@ -576,6 +582,10 @@ impl dyn Any + Send + Sync {
///
/// assert_eq!(*x.downcast_ref::<usize>().unwrap(), 2);
/// ```
/// # Safety
///
/// The contained value must be of type `T`. Calling this method
/// with the incorrect type is *undefined behavior*.
#[unstable(feature = "downcast_unchecked", issue = "90850")]
#[inline]
pub unsafe fn downcast_mut_unchecked<T: Any>(&mut self) -> &mut T {
Expand Down
14 changes: 7 additions & 7 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3795,15 +3795,15 @@ where
/// See [`const_eval_select()`] for the rules and requirements around that intrinsic.
pub(crate) macro const_eval_select {
(
@capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? :
@capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? :
if const
$(#[$compiletime_attr:meta])* $compiletime:block
else
$(#[$runtime_attr:meta])* $runtime:block
) => {
// Use the `noinline` arm, after adding explicit `inline` attributes
$crate::intrinsics::const_eval_select!(
@capture { $($arg : $ty = $val),* } $(-> $ret)? :
@capture$([$($binders)*])? { $($arg : $ty = $val),* } $(-> $ret)? :
#[noinline]
if const
#[inline] // prevent codegen on this function
Expand All @@ -3817,20 +3817,20 @@ pub(crate) macro const_eval_select {
},
// With a leading #[noinline], we don't add inline attributes
(
@capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? :
@capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? :
#[noinline]
if const
$(#[$compiletime_attr:meta])* $compiletime:block
else
$(#[$runtime_attr:meta])* $runtime:block
) => {{
$(#[$runtime_attr])*
fn runtime($($arg: $ty),*) $( -> $ret )? {
fn runtime$(<$($binders)*>)?($($arg: $ty),*) $( -> $ret )? {
$runtime
}

$(#[$compiletime_attr])*
const fn compiletime($($arg: $ty),*) $( -> $ret )? {
const fn compiletime$(<$($binders)*>)?($($arg: $ty),*) $( -> $ret )? {
// Don't warn if one of the arguments is unused.
$(let _ = $arg;)*

Expand All @@ -3842,14 +3842,14 @@ pub(crate) macro const_eval_select {
// We support leaving away the `val` expressions for *all* arguments
// (but not for *some* arguments, that's too tricky).
(
@capture { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? :
@capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? :
if const
$(#[$compiletime_attr:meta])* $compiletime:block
else
$(#[$runtime_attr:meta])* $runtime:block
) => {
$crate::intrinsics::const_eval_select!(
@capture { $($arg : $ty = $arg),* } $(-> $ret)? :
@capture$([$($binders)*])? { $($arg : $ty = $arg),* } $(-> $ret)? :
if const
$(#[$compiletime_attr])* $compiletime
else
Expand Down
Loading
Loading