diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index fb456d80e465f..835c17da70acd 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -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) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7dc6d292a94af..ad9b16c1265d8 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -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), diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index debcc2ac338b5..b1855286beb6f 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -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; diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 0569c1b986d1a..9621eef1cd45a 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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] diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index d62e4ec941d21..5ddffc579f0bb 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -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> { desc { "finding local trait impls" } @@ -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>, ErrorGuaranteed> { desc { "resolving instance `{}`", ty::Instance::new_raw(key.value.0, key.value.1) } } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 569c30a6067a9..2c74abe6ca0f0 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -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}; @@ -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) diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index b81e0b6f3471a..67ee51782f748 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -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>, ErrorGuaranteed> { + let def_kind = tcx.def_kind(def_id); assert_matches!( - tcx.def_kind(def_id), + def_kind, DefKind::Fn | DefKind::AssocFn | DefKind::Const { .. } @@ -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, @@ -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>, 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>, ErrorGuaranteed> { + Self::try_resolve_inner(tcx, typing_env, def_id, args, hir::Constness::Const) } pub fn expect_resolve( diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index b413b8b5ed9c7..fd26406993cbb 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -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; @@ -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 { diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 9d3284e609260..8c0ccea9ebfc1 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -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, @@ -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})" + ) } }; @@ -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) +} diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index 32d8c3f58e08a..231d8c00f4caa 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -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; } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index f5ff24af85b91..5b72bebd7aea8 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -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; @@ -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>, 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); @@ -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() { @@ -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>, 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); } @@ -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: {:?}, {:?}, {:?}, {:?}", diff --git a/tests/ui/consts/qualif-indirect-mutation-fail.rs b/tests/ui/consts/qualif-indirect-mutation-fail.rs index b70fca7b86430..0f59a86b7cc01 100644 --- a/tests/ui/consts/qualif-indirect-mutation-fail.rs +++ b/tests/ui/consts/qualif-indirect-mutation-fail.rs @@ -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 ` as Drop>::drop` +}; // Mutable borrow of a type with drop impl. pub const A2: () = { @@ -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 ` as Drop>::drop` +}; // Shared borrow of a type that might be !Freeze and Drop. pub const fn g1() { diff --git a/tests/ui/consts/qualif-indirect-mutation-fail.stderr b/tests/ui/consts/qualif-indirect-mutation-fail.stderr index 102bbe03b2efd..5c88ed1732edb 100644 --- a/tests/ui/consts/qualif-indirect-mutation-fail.stderr +++ b/tests/ui/consts/qualif-indirect-mutation-fail.stderr @@ -7,19 +7,6 @@ LL | let mut x = None; LL | }; | - value is dropped here -error[E0080]: calling non-const function ` as Drop>::drop` - --> $DIR/qualif-indirect-mutation-fail.rs:18:1 - | -LL | }; - | ^ evaluation of `A1` failed inside this call - | -note: inside `std::ptr::drop_glue::> - shim(Some(Option))` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_glue:: - shim(Some(String))` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_glue::> - shim(Some(Vec))` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL - error[E0493]: destructor of `Option` cannot be evaluated at compile-time --> $DIR/qualif-indirect-mutation-fail.rs:28:9 | @@ -28,19 +15,6 @@ LL | let _z = x; LL | }; | - value is dropped here -error[E0080]: calling non-const function ` as Drop>::drop` - --> $DIR/qualif-indirect-mutation-fail.rs:29:1 - | -LL | }; - | ^ evaluation of `A2` failed inside this call - | -note: inside `std::ptr::drop_glue::> - shim(Some(Option))` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_glue:: - shim(Some(String))` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_glue::> - shim(Some(Vec))` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL - error[E0493]: destructor of `(u32, Option)` cannot be evaluated at compile-time --> $DIR/qualif-indirect-mutation-fail.rs:6:9 | @@ -103,7 +77,6 @@ LL | let x: Option = None; LL | } | - value is dropped here -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0080, E0493. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0493`. diff --git a/tests/ui/traits/const-traits/specialization/dont-run-runtime-code.rs b/tests/ui/traits/const-traits/specialization/dont-run-runtime-code.rs new file mode 100644 index 0000000000000..180200d582f83 --- /dev/null +++ b/tests/ui/traits/const-traits/specialization/dont-run-runtime-code.rs @@ -0,0 +1,43 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +#![feature(min_specialization, const_trait_impl)] + +struct Dummy; + +const trait DummyTrait { + fn dummy_fn() -> u32; +} +impl DummyTrait for Wrap { + fn dummy_fn() -> u32 { + println!("wut"); + 0 + } +} + +const trait Trait { + fn trait_fn() -> u32; +} +impl const Trait for T where T: DummyTrait { + default fn trait_fn() -> u32 { + 42 + } +} + +struct Wrap(T); + +impl const Trait for Wrap where Self: [const] DummyTrait { + fn trait_fn() -> u32 { + >::dummy_fn() + } +} + +const fn indirect() -> u32 { + T::trait_fn() +} + +const A: u32 = indirect::>(); + +const B: () = { assert!(A == 42); }; + +fn main() {}