Skip to content
Open
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
2 changes: 2 additions & 0 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {

const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error

const SHOULD_RESPECT_CONST_BOUNDS_WHEN_RESOLVING_INSTANCES: bool = true;

#[inline(always)]
fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool {
matches!(ecx.machine.check_alignment, CheckAlignment::Error)
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
trace!("resolve: {:?}, {:#?}", def, args);
trace!("typing_env: {:#?}", self.typing_env);
trace!("args: {:#?}", args);
match ty::Instance::try_resolve(*self.tcx, self.typing_env, def, args) {

let resolve = if M::SHOULD_RESPECT_CONST_BOUNDS_WHEN_RESOLVING_INSTANCES
&& !self.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you
{
ty::Instance::try_resolve_for_ctfe
} else {
ty::Instance::try_resolve
};
match resolve(*self.tcx, self.typing_env, def, args) {
Ok(Some(instance)) => interp_ok(instance),
Ok(None) => throw_inval!(TooGeneric),

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_const_eval/src/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ pub trait Machine<'tcx>: Sized {
/// already been checked before.
const ALL_CONSTS_ARE_PRECHECKED: bool = true;

/// Should we resolve instances as if we are in a const context? This means specialized impls whose
/// `T: [const] Trait` bounds that are not satisfied will not be selected.
const SHOULD_RESPECT_CONST_BOUNDS_WHEN_RESOLVING_INSTANCES: bool = false;

/// Whether memory accesses should be alignment-checked.
fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool;

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4627,7 +4627,7 @@ impl fmt::Display for Safety {
}
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, StableHash)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, StableHash, Hash)]
#[derive(Default)]
pub enum Constness {
#[default]
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,13 @@ rustc_queries! {
desc { "computing candidate for `{}`", key.value }
}

query codegen_select_candidate_for_ctfe(
key: PseudoCanonicalInput<'tcx, ty::TraitRef<'tcx>>
) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
cache_on_disk
desc { "computing const candidate for `{}`", key.value }
}

/// Return all `impl` blocks in the current crate.
query all_local_trait_impls(_: ()) -> &'tcx rustc_data_structures::fx::FxIndexMap<DefId, Vec<LocalDefId>> {
desc { "finding local trait impls" }
Expand Down Expand Up @@ -2619,7 +2626,7 @@ rustc_queries! {
/// from `Ok(None)` to avoid misleading diagnostics when an error
/// has already been/will be emitted, for the original cause.
query resolve_instance_raw(
key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>)>
key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>, hir::Constness)>
) -> Result<Option<ty::Instance<'tcx>>, ErrorGuaranteed> {
desc { "resolving instance `{}`", ty::Instance::new_raw(key.value.0, key.value.1) }
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/query/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::hash::Hash;
use rustc_ast::tokenstream::TokenStream;
use rustc_data_structures::sso::SsoHashSet;
use rustc_data_structures::stable_hash::StableHash;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
use rustc_hir::hir_id::OwnerId;
use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol};
Expand Down Expand Up @@ -244,6 +245,12 @@ impl<'tcx> QueryKey for (DefId, GenericArgsRef<'tcx>) {
}
}

impl<'tcx> QueryKey for (DefId, GenericArgsRef<'tcx>, hir::Constness) {
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
self.0.default_span(tcx)
}
}

impl<'tcx> QueryKey for ty::TraitRef<'tcx> {
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
tcx.def_span(self.def_id)
Expand Down
42 changes: 36 additions & 6 deletions compiler/rustc_middle/src/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,15 +493,16 @@ impl<'tcx> Instance<'tcx> {
/// couldn't complete due to errors elsewhere - this is distinct
/// from `Ok(None)` to avoid misleading diagnostics when an error
/// has already been/will be emitted, for the original cause
#[instrument(level = "debug", skip(tcx), ret)]
pub fn try_resolve(
fn try_resolve_inner(
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
def_id: DefId,
args: GenericArgsRef<'tcx>,
mut constness: hir::Constness,
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
let def_kind = tcx.def_kind(def_id);
assert_matches!(
tcx.def_kind(def_id),
def_kind,
DefKind::Fn
| DefKind::AssocFn
| DefKind::Const { .. }
Expand All @@ -517,6 +518,10 @@ impl<'tcx> Instance<'tcx> {
`try_normalize_erasing_regions`."
);

if !def_kind.is_assoc() {
constness = hir::Constness::NotConst;
}

// Rust code can easily create exponentially-long types using only a
// polynomial recursion depth. Even with the default recursion
// depth, you can easily get cases that take >2^60 steps to run,
Expand All @@ -529,11 +534,36 @@ impl<'tcx> Instance<'tcx> {
return Ok(None);
}

let input = tcx.erase_and_anonymize_regions(typing_env.as_query_input((def_id, args)));

// All regions in the result of this query are erased, so it's
// fine to erase all of the input regions.
tcx.resolve_instance_raw(
tcx.erase_and_anonymize_regions(typing_env.as_query_input((def_id, args))),
)
tcx.resolve_instance_raw(ty::PseudoCanonicalInput {
typing_env: input.typing_env,
value: (input.value.0, input.value.1, constness),
})
}

/// See `try_resolve_inner`.
#[instrument(level = "debug", skip(tcx), ret)]
pub fn try_resolve(
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
Self::try_resolve_inner(tcx, typing_env, def_id, args, hir::Constness::NotConst)
}

/// See `try_resolve_inner`.
#[instrument(level = "debug", skip(tcx), ret)]
pub fn try_resolve_for_ctfe(
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
Self::try_resolve_inner(tcx, typing_env, def_id, args, hir::Constness::Const)
}

pub fn expect_resolve(
Expand Down
15 changes: 14 additions & 1 deletion compiler/rustc_trait_selection/src/solve/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_infer::traits::{
Selection, SelectionError, SelectionResult, TraitObligation,
};
use rustc_macros::extension;
use rustc_middle::{bug, span_bug};
use rustc_middle::{bug, span_bug, ty};
use rustc_span::Span;
use thin_vec::thin_vec;

Expand All @@ -30,6 +30,19 @@ impl<'tcx> InferCtxt<'tcx> {
.break_value()
.unwrap()
}
fn select_host_effect_predicate_in_new_trait_solver(
&self,
obligation: &Obligation<'tcx, ty::HostEffectPredicate<'tcx>>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
assert!(self.next_trait_solver());

self.visit_proof_tree(
Goal::new(self.tcx, obligation.param_env, ty::Binder::dummy(obligation.predicate)),
&mut Select { span: obligation.cause.span },
)
.break_value()
.unwrap()
}
}

struct Select {
Expand Down
50 changes: 43 additions & 7 deletions compiler/rustc_traits/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_middle::bug;
use rustc_middle::traits::CodegenObligationError;
use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt, TypeVisitableExt};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::solve::InferCtxtSelectExt as _;
use rustc_trait_selection::traits::{
ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext,
SelectionError,
Expand All @@ -21,28 +22,49 @@ use tracing::debug;
/// obligations *could be* resolved if we wanted to.
///
/// This also expects that `trait_ref` is fully normalized.
pub(crate) fn codegen_select_candidate<'tcx>(
///
/// When `use_const` is set, we use the new trait solver to prove
/// `HostEffectPredicate` with `constness` set to `Const`.
pub(crate) fn codegen_select_candidate_inner<'tcx>(
tcx: TyCtxt<'tcx>,
key: PseudoCanonicalInput<'tcx, ty::TraitRef<'tcx>>,
typing_env: ty::TypingEnv<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
use_const: bool,
) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
let PseudoCanonicalInput { typing_env, value: trait_ref } = key;
// We expect the input to be fully normalized.
tcx.debug_assert_fully_normalized(typing_env, trait_ref);

// Do the initial selection for the obligation. This yields the
// shallow result we are looking for -- that is, what specific impl.
let (infcx, param_env) = tcx.infer_ctxt().ignoring_regions().build_with_typing_env(typing_env);
let mut infcx_builder = tcx.infer_ctxt().ignoring_regions();
if use_const {
// next trait solver is used unconditionally here, because only const code that use trait methods
// are effected (nightly code)
infcx_builder = infcx_builder.with_next_trait_solver(true);
}
let (infcx, param_env) = infcx_builder.build_with_typing_env(typing_env);
let mut selcx = SelectionContext::new(&infcx);

let obligation_cause = ObligationCause::dummy();
let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref);

let selection = match selcx.select(&obligation) {
let selection = if use_const {
let host_predicate =
ty::HostEffectPredicate { trait_ref, constness: ty::BoundConstness::Const };
let obligation = Obligation::new(tcx, obligation_cause, param_env, host_predicate);
infcx.select_host_effect_predicate_in_new_trait_solver(&obligation)
} else {
let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref);
selcx.select(&obligation)
};

let selection = match selection {
Ok(Some(selection)) => selection,
Ok(None) => return Err(CodegenObligationError::Ambiguity),
Err(SelectionError::Unimplemented) => return Err(CodegenObligationError::Unimplemented),
Err(e) => {
bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
bug!(
"Encountered error `{e:?}` selecting `{trait_ref:?}` during codegen (use_const: {use_const})"
)
}
};

Expand Down Expand Up @@ -91,3 +113,17 @@ pub(crate) fn codegen_select_candidate<'tcx>(

Ok(&*tcx.arena.alloc(impl_source))
}

pub(crate) fn codegen_select_candidate<'tcx>(
tcx: TyCtxt<'tcx>,
key: PseudoCanonicalInput<'tcx, ty::TraitRef<'tcx>>,
) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
codegen_select_candidate_inner(tcx, key.typing_env, key.value, false)
}

pub(crate) fn codegen_select_candidate_for_ctfe<'tcx>(
tcx: TyCtxt<'tcx>,
key: PseudoCanonicalInput<'tcx, ty::TraitRef<'tcx>>,
) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
codegen_select_candidate_inner(tcx, key.typing_env, key.value, true)
}
1 change: 1 addition & 0 deletions compiler/rustc_traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ pub fn provide(p: &mut Providers) {
normalize_erasing_regions::provide(p);
type_op::provide(p);
p.codegen_select_candidate = codegen::codegen_select_candidate;
p.codegen_select_candidate_for_ctfe = codegen::codegen_select_candidate_for_ctfe;
p.coroutine_hidden_types = coroutine_witnesses::coroutine_hidden_types;
}
23 changes: 15 additions & 8 deletions compiler/rustc_ty_utils/src/instance.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_errors::ErrorGuaranteed;
use rustc_hir::LangItem;
use rustc_hir::def_id::DefId;
use rustc_hir::{Constness, LangItem};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::bug;
use rustc_middle::query::Providers;
Expand All @@ -18,9 +18,9 @@ use crate::errors::UnexpectedFnPtrAssociatedItem;

fn resolve_instance_raw<'tcx>(
tcx: TyCtxt<'tcx>,
key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>)>,
key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>, Constness)>,
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
let PseudoCanonicalInput { typing_env, value: (def_id, args) } = key;
let PseudoCanonicalInput { typing_env, value: (def_id, args, constness) } = key;

let result = if let Some(trait_def_id) = tcx.trait_of_assoc(def_id) {
debug!(" => associated item, attempting to find impl in typing_env {:#?}", typing_env);
Expand All @@ -30,6 +30,7 @@ fn resolve_instance_raw<'tcx>(
typing_env,
trait_def_id,
tcx.normalize_erasing_regions(typing_env, Unnormalized::new_wip(args)),
constness,
)
} else {
let def = if tcx.intrinsic(def_id).is_some() {
Expand Down Expand Up @@ -101,20 +102,26 @@ fn resolve_instance_raw<'tcx>(
result
}

#[tracing::instrument(level = "debug", skip(tcx))]
fn resolve_associated_item<'tcx>(
tcx: TyCtxt<'tcx>,
trait_item_id: DefId,
typing_env: ty::TypingEnv<'tcx>,
trait_id: DefId,
rcvr_args: GenericArgsRef<'tcx>,
constness: Constness,
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
debug!(?trait_item_id, ?typing_env, ?trait_id, ?rcvr_args, "resolve_associated_item");

let trait_ref = ty::TraitRef::from_assoc(tcx, trait_id, rcvr_args);

let input = typing_env.as_query_input(trait_ref);
let vtbl = match tcx.codegen_select_candidate(input) {
Ok(vtbl) => vtbl,
let candidate = if constness == Constness::Const {
tcx.codegen_select_candidate_for_ctfe(input)
} else {
tcx.codegen_select_candidate(input)
};

let impl_src = match candidate {
Ok(impl_src) => impl_src,
Err(CodegenObligationError::Ambiguity | CodegenObligationError::Unimplemented) => {
return Ok(None);
}
Expand All @@ -123,7 +130,7 @@ fn resolve_associated_item<'tcx>(

// Now that we know which impl is being used, we can dispatch to
// the actual function:
Ok(match vtbl {
Ok(match impl_src {
traits::ImplSource::UserDefined(impl_data) => {
debug!(
"resolving ImplSource::UserDefined: {:?}, {:?}, {:?}, {:?}",
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/consts/qualif-indirect-mutation-fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub const A1: () = {
let b = &mut y;
std::mem::swap(a, b);
std::mem::forget(y);
}; //~ ERROR calling non-const function `<Vec<u8> as Drop>::drop`
};

// Mutable borrow of a type with drop impl.
pub const A2: () = {
Expand All @@ -26,7 +26,7 @@ pub const A2: () = {
std::mem::swap(a, b);
std::mem::forget(y);
let _z = x; //~ ERROR destructor of
}; //~ ERROR calling non-const function `<Vec<u8> as Drop>::drop`
};

// Shared borrow of a type that might be !Freeze and Drop.
pub const fn g1<T>() {
Expand Down
Loading
Loading