diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f02c7b2d2e11a..74e194750fadd 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -166,6 +166,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( pointee_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { + // The debuginfo generated by this function is only valid if `ptr_type` is really just + // a (fat) pointer. Make sure it is not called for e.g. `Box`. + debug_assert_eq!( + cx.size_and_align_of(ptr_type), + cx.size_and_align_of(cx.tcx.mk_mut_ptr(pointee_type)) + ); + let pointee_type_di_node = type_di_node(cx, pointee_type); return_if_di_node_created_in_meantime!(cx, unique_type_id); @@ -212,7 +219,17 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), |cx, owner| { - let layout = cx.layout_of(ptr_type); + // FIXME: If this fat pointer is a `Box` then we don't want to use its + // type layout and instead use the layout of the raw pointer inside + // of it. + // The proper way to handle this is to not treat Box as a pointer + // at all and instead emit regular struct debuginfo for it. We just + // need to make sure that we don't break existing debuginfo consumers + // by doing that (at least not without a warning period). + let layout_type = + if ptr_type.is_box() { cx.tcx.mk_mut_ptr(pointee_type) } else { ptr_type }; + + let layout = cx.layout_of(layout_type); let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 30764f689c95c..faea2111d9210 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -42,7 +42,7 @@ pub struct PromoteTemps<'tcx> { impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { fn phase_change(&self) -> Option { - Some(MirPhase::ConstPromotion) + Some(MirPhase::ConstsPromoted) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index cf15fc4ddc3a5..deeca78b75d99 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -266,22 +266,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } - // The deaggregator currently does not deaggreagate arrays. - // So for now, we ignore them here. - Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {} - // All other aggregates must be gone after some phases. - Rvalue::Aggregate(box kind, _) => { - if self.mir_phase > MirPhase::DropLowering - && !matches!(kind, AggregateKind::Generator(..)) - { - // Generators persist until the state machine transformation, but all - // other aggregates must have been lowered. - self.fail( - location, - format!("{:?} have been lowered to field assignments", rvalue), - ) - } else if self.mir_phase > MirPhase::GeneratorLowering { - // No more aggregates after drop and generator lowering. + Rvalue::Aggregate(agg_kind, _) => { + let disallowed = match **agg_kind { + AggregateKind::Array(..) => false, + AggregateKind::Generator(..) => { + self.mir_phase >= MirPhase::GeneratorsLowered + } + _ => self.mir_phase >= MirPhase::Deaggregated, + }; + if disallowed { self.fail( location, format!("{:?} have been lowered to field assignments", rvalue), @@ -289,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::Ref(_, BorrowKind::Shallow, _) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", @@ -300,7 +293,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::AscribeUserType(..) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`AscribeUserType` should have been removed after drop lowering phase", @@ -308,7 +301,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::FakeRead(..) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`FakeRead` should have been removed after drop lowering phase", @@ -351,10 +344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty)) } } - StatementKind::SetDiscriminant { .. } - | StatementKind::StorageLive(..) + StatementKind::SetDiscriminant { .. } => { + if self.mir_phase < MirPhase::DropsLowered { + self.fail(location, "`SetDiscriminant` is not allowed until drop elaboration"); + } + } + StatementKind::Retag(_, _) => { + // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // DropsLowered`. However, this causes ICEs with generation of drop shims, which + // seem to fail to set their `MirPhase` correctly. + } + StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - | StatementKind::Retag(_, _) | StatementKind::Coverage(_) | StatementKind::Nop => {} } @@ -424,10 +425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::DropAndReplace { target, unwind, .. } => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, - "`DropAndReplace` is not permitted to exist after drop elaboration", + "`DropAndReplace` should have been removed during drop elaboration", ); } self.check_edge(location, *target, EdgeKind::Normal); @@ -494,7 +495,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Yield { resume, drop, .. } => { - if self.mir_phase > MirPhase::GeneratorLowering { + if self.mir_phase >= MirPhase::GeneratorsLowered { self.fail(location, "`Yield` should have been replaced by generator lowering"); } self.check_edge(location, *resume, EdgeKind::Normal); @@ -503,10 +504,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::FalseEdge { real_target, imaginary_target } => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`FalseEdge` should have been removed after drop elaboration", + ); + } self.check_edge(location, *real_target, EdgeKind::Normal); self.check_edge(location, *imaginary_target, EdgeKind::Normal); } TerminatorKind::FalseUnwind { real_target, unwind } => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`FalseUnwind` should have been removed after drop elaboration", + ); + } self.check_edge(location, *real_target, EdgeKind::Normal); if let Some(unwind) = unwind { self.check_edge(location, *unwind, EdgeKind::Unwind); @@ -520,12 +533,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.check_edge(location, *cleanup, EdgeKind::Unwind); } } + TerminatorKind::GeneratorDrop => { + if self.mir_phase >= MirPhase::GeneratorsLowered { + self.fail( + location, + "`GeneratorDrop` should have been replaced by generator lowering", + ); + } + } // Nothing to validate for these. TerminatorKind::Resume | TerminatorKind::Abort | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::GeneratorDrop => {} + | TerminatorKind::Unreachable => {} } self.super_terminator(terminator, location); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5697e73f73a70..2886d921c705d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -20,8 +20,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; -use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::mir::interpret::EvalToConstValueResult; +use rustc_middle::mir::interpret::{ErrorHandled, EvalToConstValueResult}; use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; @@ -71,7 +70,6 @@ mod sub; pub mod type_variable; mod undo_log; -use crate::infer::canonical::OriginalQueryValues; pub use rustc_middle::infer::unify_key; #[must_use] @@ -687,15 +685,28 @@ pub struct CombinedSnapshot<'a, 'tcx> { impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// calls `tcx.try_unify_abstract_consts` after /// canonicalizing the consts. + #[instrument(skip(self), level = "debug")] pub fn try_unify_abstract_consts( &self, a: ty::Unevaluated<'tcx, ()>, b: ty::Unevaluated<'tcx, ()>, + param_env: ty::ParamEnv<'tcx>, ) -> bool { - let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default()); - debug!("canonical consts: {:?}", &canonical.value); + // Reject any attempt to unify two unevaluated constants that contain inference + // variables, since inference variables in queries lead to ICEs. + if a.substs.has_infer_types_or_consts() + || b.substs.has_infer_types_or_consts() + || param_env.has_infer_types_or_consts() + { + debug!("a or b or param_env contain infer vars in its substs -> cannot unify"); + return false; + } + + let param_env_and = param_env.and((a, b)); + let erased = self.tcx.erase_regions(param_env_and); + debug!("after erase_regions: {:?}", erased); - self.tcx.try_unify_abstract_consts(canonical.value) + self.tcx.try_unify_abstract_consts(erased) } pub fn is_in_snapshot(&self) -> bool { @@ -1598,6 +1609,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// This handles inferences variables within both `param_env` and `substs` by /// performing the operation on their respective canonical forms. + #[instrument(skip(self), level = "debug")] pub fn const_eval_resolve( &self, param_env: ty::ParamEnv<'tcx>, @@ -1605,15 +1617,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span: Option, ) -> EvalToConstValueResult<'tcx> { let substs = self.resolve_vars_if_possible(unevaluated.substs); + debug!(?substs); // Postpone the evaluation of constants whose substs depend on inference // variables if substs.has_infer_types_or_consts() { + debug!("substs have infer types or consts: {:?}", substs); return Err(ErrorHandled::TooGeneric); } let param_env_erased = self.tcx.erase_regions(param_env); let substs_erased = self.tcx.erase_regions(substs); + debug!(?param_env_erased); + debug!(?substs_erased); let unevaluated = ty::Unevaluated { def: unevaluated.def, diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 8b0f92d23d75f..7e5989b4112cf 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,6 +1,7 @@ use super::{ErrorHandled, EvalToConstValueResult, GlobalId}; use crate::mir; +use crate::ty::fold::TypeFoldable; use crate::ty::subst::InternalSubsts; use crate::ty::{self, TyCtxt}; use rustc_hir::def_id::DefId; @@ -38,6 +39,16 @@ impl<'tcx> TyCtxt<'tcx> { ct: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { + // Cannot resolve `Unevaluated` constants that contain inference + // variables. We reject those here since `resolve_opt_const_arg` + // would fail otherwise. + // + // When trying to evaluate constants containing inference variables, + // use `Infcx::const_eval_resolve` instead. + if ct.substs.has_infer_types_or_consts() { + bug!("did not expect inference variables here"); + } + match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted: ct.promoted }; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a5468b3f4f202..ea71cff161612 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -127,14 +127,11 @@ pub trait MirPass<'tcx> { /// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the /// dialects forbid certain variants or values in certain phases. /// -/// Note: Each phase's validation checks all invariants of the *previous* phases' dialects. A phase -/// that changes the dialect documents what invariants must be upheld *after* that phase finishes. -/// /// Warning: ordering of variants is significant. #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(HashStable)] pub enum MirPhase { - Build = 0, + Built = 0, // FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query). // We used to have this for pre-miri MIR based const eval. Const = 1, @@ -142,17 +139,32 @@ pub enum MirPhase { /// by creating a new MIR body per promoted element. After this phase (and thus the termination /// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir` /// query. - ConstPromotion = 2, - /// After this phase - /// * the only `AggregateKind`s allowed are `Array` and `Generator`, - /// * `DropAndReplace` is gone for good - /// * `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` terminator - /// means that the auto-generated drop glue will be invoked. - DropLowering = 3, - /// After this phase, generators are explicit state machines (no more `Yield`). - /// `AggregateKind::Generator` is gone for good. - GeneratorLowering = 4, - Optimization = 5, + ConstsPromoted = 2, + /// Beginning with this phase, the following variants are disallowed: + /// * [`TerminatorKind::DropAndReplace`](terminator::TerminatorKind::DropAndReplace) + /// * [`TerminatorKind::FalseUnwind`](terminator::TerminatorKind::FalseUnwind) + /// * [`TerminatorKind::FalseEdge`](terminator::TerminatorKind::FalseEdge) + /// * [`StatementKind::FakeRead`] + /// * [`StatementKind::AscribeUserType`] + /// * [`Rvalue::Ref`] with `BorrowKind::Shallow` + /// + /// And the following variant is allowed: + /// * [`StatementKind::Retag`] + /// + /// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` + /// terminator means that the auto-generated drop glue will be invoked. + DropsLowered = 3, + /// Beginning with this phase, the following variant is disallowed: + /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array` + /// + /// And the following variant is allowed: + /// * [`StatementKind::SetDiscriminant`] + Deaggregated = 4, + /// Beginning with this phase, the following variants are disallowed: + /// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield) + /// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop) + GeneratorsLowered = 5, + Optimized = 6, } impl MirPhase { @@ -311,7 +323,7 @@ impl<'tcx> Body<'tcx> { ); let mut body = Body { - phase: MirPhase::Build, + phase: MirPhase::Built, source, basic_blocks, source_scopes, @@ -346,7 +358,7 @@ impl<'tcx> Body<'tcx> { /// crate. pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { let mut body = Body { - phase: MirPhase::Build, + phase: MirPhase::Built, source: MirSource::item(DefId::local(CRATE_DEF_INDEX)), basic_blocks, source_scopes: IndexVec::new(), @@ -1541,9 +1553,16 @@ impl Statement<'_> { } } +/// The various kinds of statements that can appear in MIR. +/// +/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which +/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, +/// causing an ICE if they are violated. #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum StatementKind<'tcx> { /// Write the RHS Rvalue to the LHS Place. + /// + /// The LHS place may not overlap with any memory accessed on the RHS. Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), /// This represents all the reading that a pattern match may do @@ -1761,6 +1780,19 @@ static_assert_size!(Place<'_>, 16); pub enum ProjectionElem { Deref, Field(Field, T), + /// Index into a slice/array. + /// + /// Note that this does not also dereference, and so it does not exactly correspond to slice + /// indexing in Rust. In other words, in the below Rust code: + /// + /// ```rust + /// let x = &[1, 2, 3, 4]; + /// let i = 2; + /// x[i]; + /// ``` + /// + /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same + /// thing is true of the `ConstantIndex` and `Subslice` projections below. Index(V), /// These indices are generated by slice patterns. Easiest to explain @@ -2223,6 +2255,11 @@ impl<'tcx> Operand<'tcx> { /// Rvalues #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] +/// The various kinds of rvalues that can appear in MIR. +/// +/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which +/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, +/// causing an ICE if they are violated. pub enum Rvalue<'tcx> { /// x (either a move or copy, depending on type of x) Use(Operand<'tcx>), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 87af90d059b54..cd6ff8254ad86 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -331,12 +331,12 @@ rustc_queries! { } } - query try_unify_abstract_consts(key: ( - ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()> - )) -> bool { + query try_unify_abstract_consts(key: + ty::ParamEnvAnd<'tcx, (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()> + )>) -> bool { desc { |tcx| "trying to unify the generic constants {} and {}", - tcx.def_path_str(key.0.def.did), tcx.def_path_str(key.1.def.did) + tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did) } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 3ed7836074b25..d852531bc6867 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -491,7 +491,7 @@ pub enum SelectionError<'tcx> { /// A given constant couldn't be evaluated. NotConstEvaluatable(NotConstEvaluatable), /// Exceeded the recursion depth during type projection. - Overflow, + Overflow(OverflowError), /// Signaling that an error has already been emitted, to avoid /// multiple errors being shown. ErrorReporting, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 56d42706f6778..5297825a92fcc 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -5,6 +5,7 @@ use self::EvaluationResult::*; use super::{SelectionError, SelectionResult}; +use rustc_errors::ErrorGuaranteed; use crate::ty; @@ -264,14 +265,26 @@ impl EvaluationResult { /// Indicates that trait evaluation caused overflow and in which pass. #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable)] pub enum OverflowError { + Error(ErrorGuaranteed), Canonical, ErrorReporting, } +impl From for OverflowError { + fn from(e: ErrorGuaranteed) -> OverflowError { + OverflowError::Error(e) + } +} + +TrivialTypeFoldableAndLiftImpls! { + OverflowError, +} + impl<'tcx> From for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { match overflow_error { - OverflowError::Canonical => SelectionError::Overflow, + OverflowError::Error(e) => SelectionError::Overflow(OverflowError::Error(e)), + OverflowError::Canonical => SelectionError::Overflow(OverflowError::Canonical), OverflowError::ErrorReporting => SelectionError::ErrorReporting, } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 81ee7942c4d34..5d6cbcf690706 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -585,7 +585,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if tcx.features().generic_const_exprs => { - tcx.try_unify_abstract_consts((au.shrink(), bu.shrink())) + tcx.try_unify_abstract_consts(relation.param_env().and((au.shrink(), bu.shrink()))) } // While this is slightly incorrect, it shouldn't matter for `min_const_generics` diff --git a/compiler/rustc_mir_transform/src/deaggregator.rs b/compiler/rustc_mir_transform/src/deaggregator.rs index 44753c5f631ca..616ba819982ec 100644 --- a/compiler/rustc_mir_transform/src/deaggregator.rs +++ b/compiler/rustc_mir_transform/src/deaggregator.rs @@ -6,6 +6,10 @@ use rustc_middle::ty::TyCtxt; pub struct Deaggregator; impl<'tcx> MirPass<'tcx> for Deaggregator { + fn phase_change(&self) -> Option { + Some(MirPhase::Deaggregated) + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let local_decls = &*local_decls; diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index a4b1d86ff610e..f78c7a084d89a 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -20,7 +20,7 @@ pub struct ElaborateDrops; impl<'tcx> MirPass<'tcx> for ElaborateDrops { fn phase_change(&self) -> Option { - Some(MirPhase::DropLowering) + Some(MirPhase::DropsLowered) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 8bc7f5e9ce2e0..ad96bf544cbd3 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1235,7 +1235,7 @@ fn create_cases<'tcx>( impl<'tcx> MirPass<'tcx> for StateTransform { fn phase_change(&self) -> Option { - Some(MirPhase::GeneratorLowering) + Some(MirPhase::GeneratorsLowered) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 45b8febf9f63c..2fca498a12502 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -342,7 +342,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) - pm::run_passes( tcx, &mut body, - &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimization)], + &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)], ); } } @@ -399,7 +399,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( } run_post_borrowck_cleanup_passes(tcx, &mut body); - assert!(body.phase == MirPhase::DropLowering); + assert!(body.phase == MirPhase::Deaggregated); tcx.alloc_steal_mir(body) } @@ -460,7 +460,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { ], ); - assert!(body.phase == MirPhase::GeneratorLowering); + assert!(body.phase == MirPhase::GeneratorsLowered); // The main optimizations that we do on MIR. pm::run_passes( @@ -497,7 +497,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &deduplicate_blocks::DeduplicateBlocks, // Some cleanup necessary at least for LLVM and potentially other codegen backends. &add_call_guards::CriticalCallEdges, - &marker::PhaseChange(MirPhase::Optimization), + &marker::PhaseChange(MirPhase::Optimized), // Dump the end result for testing and debugging purposes. &dump_mir::Marker("PreCodegen"), ], diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 8725eae870917..740a2168b41cc 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -114,7 +114,7 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn } } - if validate || body.phase == MirPhase::Optimization { + if validate || body.phase == MirPhase::Optimized { validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase)); } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 48ca321b73746..5cf362bfa7e98 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1347,6 +1347,10 @@ symbols! { store, str, str_alloc, + str_split_whitespace, + str_trim, + str_trim_end, + str_trim_start, stringify, stringify_macro, struct_field_attributes, diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 490c04e7be241..959b644becd9b 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -188,6 +188,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } } +#[instrument(skip(tcx), level = "debug")] fn satisfied_from_param_env<'tcx>( tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, @@ -197,14 +198,17 @@ fn satisfied_from_param_env<'tcx>( match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { if let Some(b_ct) = AbstractConst::new(tcx, uv)? { + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; + // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // predicate for `(N + 1) * 2` - let result = - walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) { + let result = walk_abstract_const(tcx, b_ct, |b_ct| { + match const_unify_ctxt.try_unify(ct, b_ct) { true => ControlFlow::BREAK, false => ControlFlow::CONTINUE, - }); + } + }); if let ControlFlow::Break(()) = result { debug!("is_const_evaluatable: abstract_const ~~> ok"); @@ -637,11 +641,13 @@ pub(super) fn thir_abstract_const<'tcx>( pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + param_env: ty::ParamEnv<'tcx>, ) -> bool { (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - return Ok(try_unify(tcx, a, b)); + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; + return Ok(const_unify_ctxt.try_unify(a, b)); } } @@ -689,88 +695,115 @@ where recurse(tcx, ct, &mut f) } -/// Tries to unify two abstract constants using structural equality. -pub(super) fn try_unify<'tcx>( +struct ConstUnifyCtxt<'tcx> { tcx: TyCtxt<'tcx>, - mut a: AbstractConst<'tcx>, - mut b: AbstractConst<'tcx>, -) -> bool { - // We substitute generics repeatedly to allow AbstractConsts to unify where a + param_env: ty::ParamEnv<'tcx>, +} + +impl<'tcx> ConstUnifyCtxt<'tcx> { + // Substitutes generics repeatedly to allow AbstractConsts to unify where a // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - while let Node::Leaf(a_ct) = a.root(tcx) { - match AbstractConst::from_const(tcx, a_ct) { - Ok(Some(a_act)) => a = a_act, - Ok(None) => break, - Err(_) => return true, - } - } - while let Node::Leaf(b_ct) = b.root(tcx) { - match AbstractConst::from_const(tcx, b_ct) { - Ok(Some(b_act)) => b = b_act, - Ok(None) => break, - Err(_) => return true, + #[inline] + #[instrument(skip(self), level = "debug")] + fn try_replace_substs_in_root( + &self, + mut abstr_const: AbstractConst<'tcx>, + ) -> Option> { + while let Node::Leaf(ct) = abstr_const.root(self.tcx) { + match AbstractConst::from_const(self.tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } } - } - match (a.root(tcx), b.root(tcx)) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - if a_ct.ty() != b_ct.ty() { - return false; - } + Some(abstr_const) + } - match (a_ct.val(), b_ct.val()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param + /// Tries to unify two abstract constants using structural equality. + #[instrument(skip(self), level = "debug")] + fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + let a = if let Some(a) = self.try_replace_substs_in_root(a) { + a + } else { + return true; + }; + + let b = if let Some(b) = self.try_replace_substs_in_root(b) { + b + } else { + return true; + }; + + let a_root = a.root(self.tcx); + let b_root = b.root(self.tcx); + debug!(?a_root, ?b_root); + + match (a_root, b_root) { + (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(self.tcx, self.param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(self.tcx, self.param_env); + debug!("b_ct evaluated: {:?}", b_ct); + + if a_ct.ty() != b_ct.ty() { + return false; } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - a_uv == b_uv + + match (a_ct.val(), b_ct.val()) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, + // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(generic_const_exprs): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } + // FIXME(generic_const_exprs): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, } - // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like - // this, for now we just return false here. - _ => false, } + (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { + self.try_unify(a.subtree(al), b.subtree(bl)) + && self.try_unify(a.subtree(ar), b.subtree(br)) + } + (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { + self.try_unify(a.subtree(av), b.subtree(bv)) + } + (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) + if a_args.len() == b_args.len() => + { + self.try_unify(a.subtree(a_f), b.subtree(b_f)) + && iter::zip(a_args, b_args) + .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) + } + (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) + if (a_ty == b_ty) && (a_kind == b_kind) => + { + self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) + } + // use this over `_ => false` to make adding variants to `Node` less error prone + (Node::Cast(..), _) + | (Node::FunctionCall(..), _) + | (Node::UnaryOp(..), _) + | (Node::Binop(..), _) + | (Node::Leaf(..), _) => false, } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - try_unify(tcx, a.subtree(al), b.subtree(bl)) - && try_unify(tcx, a.subtree(ar), b.subtree(br)) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - try_unify(tcx, a.subtree(av), b.subtree(bv)) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - try_unify(tcx, a.subtree(a_f), b.subtree(b_f)) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn))) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand)) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 229e108d5d640..5e220173caeea 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -22,6 +22,7 @@ use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::{ @@ -928,8 +929,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error"); return; } - - Overflow => { + // Already reported. + Overflow(OverflowError::Error(_)) => { + self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported"); + return; + } + Overflow(_) => { bug!("overflow should be handled before the `report_selection_error` path"); } SelectionError::ErrorReporting => { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 9ac8dc59a1d1e..1b8628344671d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -580,7 +580,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val(), c2.val()) { - if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { + if infcx.try_unify_abstract_consts( + a.shrink(), + b.shrink(), + obligation.param_env, + ) { return ProcessResult::Changed(vec![]); } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 3a6ca9b7624f0..88750f272c8d3 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -862,7 +862,10 @@ pub fn provide(providers: &mut ty::query::Providers) { ty::WithOptConstParam { did, const_param_did: Some(param_did) }, ) }, - try_unify_abstract_consts: const_evaluatable::try_unify_abstract_consts, + try_unify_abstract_consts: |tcx, param_env_and| { + let (param_env, (a, b)) = param_env_and.into_parts(); + const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) + }, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 390381752f95d..b61e68735712b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -27,6 +27,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; @@ -1139,7 +1140,9 @@ fn project<'cx, 'tcx>( if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) { // This should really be an immediate error, but some existing code // relies on being able to recover from this. - return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow)); + return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow( + OverflowError::Canonical, + ))); } if obligation.predicate.references_error() { diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 4874ba6f58cac..db45ee3fed7db 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -108,9 +108,11 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { ) } OverflowError::ErrorReporting => EvaluationResult::EvaluatedToErr, + OverflowError::Error(_) => EvaluationResult::EvaluatedToErr, }) } Err(OverflowError::ErrorReporting) => EvaluationResult::EvaluatedToErr, + Err(OverflowError::Error(_)) => EvaluationResult::EvaluatedToErr, } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c0a283d2edae4..3e7a2252318be 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -164,8 +164,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) } Ok(_) => Ok(None), - Err(OverflowError::Canonical) => Err(Overflow), + Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)), Err(OverflowError::ErrorReporting) => Err(ErrorReporting), + Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))), }) .flat_map(Result::transpose) .collect::, _>>()?; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 3a5bca492965e..6d232d86d8a4f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -25,7 +25,7 @@ use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::Diagnostic; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::LateBoundRegionConversionTime; @@ -316,11 +316,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { let candidate = match self.select_from_obligation(obligation) { - Err(SelectionError::Overflow) => { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { // In standard mode, overflow must have been caught and reported // earlier. assert!(self.query_mode == TraitQueryMode::Canonical); - return Err(SelectionError::Overflow); + return Err(SelectionError::Overflow(OverflowError::Canonical)); } Err(SelectionError::Ambiguous(_)) => { return Ok(None); @@ -335,9 +335,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; match self.confirm_candidate(obligation, candidate) { - Err(SelectionError::Overflow) => { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { assert!(self.query_mode == TraitQueryMode::Canonical); - Err(SelectionError::Overflow) + Err(SelectionError::Overflow(OverflowError::Canonical)) } Err(e) => Err(e), Ok(candidate) => { @@ -639,7 +639,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val(), c2.val()) { - if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { + if self.infcx.try_unify_abstract_consts( + a.shrink(), + b.shrink(), + obligation.param_env, + ) { return Ok(EvaluatedToOk); } } @@ -954,7 +958,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(c)) => self.evaluate_candidate(stack, &c), Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig), Ok(None) => Ok(EvaluatedToAmbig), - Err(Overflow) => Err(OverflowError::Canonical), + Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical), Err(ErrorReporting) => Err(OverflowError::ErrorReporting), Err(..) => Ok(EvaluatedToErr), } @@ -1113,7 +1117,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.query_mode { TraitQueryMode::Standard => { if self.infcx.is_tainted_by_errors() { - return Err(OverflowError::ErrorReporting); + return Err(OverflowError::Error( + ErrorGuaranteed::unchecked_claim_error_was_emitted(), + )); } self.infcx.report_overflow_error(error_obligation, true); } @@ -1349,7 +1355,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if self.can_use_global_caches(param_env) { - if let Err(Overflow) = candidate { + if let Err(Overflow(OverflowError::Canonical)) = candidate { // Don't cache overflow globally; we only produce this in certain modes. } else if !pred.needs_infer() { if !candidate.needs_infer() { diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index d38777bea5990..4ab94f39357d8 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -243,7 +243,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( ( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), - ) => tcx.try_unify_abstract_consts((a, b)), + ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))), ( ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index b1d36f2710737..c603420f0f862 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -904,6 +904,7 @@ impl str { #[must_use = "this returns the split string as an iterator, \ without modifying the original"] #[stable(feature = "split_whitespace", since = "1.1.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_split_whitespace")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace<'_> { SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } @@ -1846,6 +1847,7 @@ impl str { #[must_use = "this returns the trimmed string as a slice, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim")] pub fn trim(&self) -> &str { self.trim_matches(|c: char| c.is_whitespace()) } @@ -1884,6 +1886,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_start")] pub fn trim_start(&self) -> &str { self.trim_start_matches(|c: char| c.is_whitespace()) } @@ -1922,6 +1925,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_end")] pub fn trim_end(&self) -> &str { self.trim_end_matches(|c: char| c.is_whitespace()) } diff --git a/src/test/debuginfo/unsized.rs b/src/test/debuginfo/unsized.rs index 7ccc88ef940dc..7cb0002ca5125 100644 --- a/src/test/debuginfo/unsized.rs +++ b/src/test/debuginfo/unsized.rs @@ -16,13 +16,17 @@ // gdbg-check:$3 = {pointer = [...], vtable = [...]} // gdbr-check:$3 = &unsized::Foo {pointer: [...], vtable: [...]} +// gdb-command:print _box +// gdbg-check:$4 = {pointer = [...], vtable = [...]} +// gdbr-check:$4 = alloc::boxed::Box, alloc::alloc::Global> {pointer: [...], vtable: [...]} + // gdb-command:print tuple_slice -// gdbg-check:$4 = {data_ptr = [...], length = 2} -// gdbr-check:$4 = &(i32, i32, [i32]) {data_ptr: [...], length: 2} +// gdbg-check:$5 = {data_ptr = [...], length = 2} +// gdbr-check:$5 = &(i32, i32, [i32]) {data_ptr: [...], length: 2} // gdb-command:print tuple_dyn -// gdbg-check:$5 = {pointer = [...], vtable = [...]} -// gdbr-check:$5 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]} +// gdbg-check:$6 = {pointer = [...], vtable = [...]} +// gdbr-check:$6 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]} // === CDB TESTS =================================================================================== @@ -42,6 +46,12 @@ // cdb-check: [+0x000] pointer : 0x[...] [Type: unsized::Foo > *] // cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] +// cdb-command:dx _box +// cdb-check: +// cdb-check:_box [Type: alloc::boxed::Box >,alloc::alloc::Global>] +// cdb-check:[+0x000] pointer : 0x[...] [Type: unsized::Foo > *] +// cdb-check:[...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] + // cdb-command:dx tuple_slice // cdb-check:tuple_slice [Type: ref$ > >] // cdb-check: [+0x000] data_ptr : 0x[...] [Type: tuple$ > *] @@ -69,6 +79,7 @@ fn main() { let a: &Foo<[u8]> = &foo.value; let b: &Foo> = &foo; let c: &Foo = &Foo { value: 7i32 }; + let _box: Box> = Box::new(Foo { value: 8i32 }); // Also check unsized tuples let tuple_slice: &(i32, i32, [i32]) = &(0, 1, [2, 3]); diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs new file mode 100644 index 0000000000000..c59d62e576d9b --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs @@ -0,0 +1,26 @@ +// build-pass + +#![feature(generic_const_exprs)] +//~^ WARNING the feature `generic_const_exprs` is incomplete + +trait Generic { + const ASSOC: usize; +} + +impl Generic for u8 { + const ASSOC: usize = 17; +} +impl Generic for u16 { + const ASSOC: usize = 13; +} + + +fn uses_assoc_type() -> [u8; N + T::ASSOC] { + [0; N + T::ASSOC] +} + +fn only_generic_n() -> [u8; N + 13] { + uses_assoc_type::() +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr new file mode 100644 index 0000000000000..b5719b3fe1dc2 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr @@ -0,0 +1,11 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/eval-try-unify.rs:3:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #76560 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 68536348d3884..71c164ab0a5bf 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -2,114 +2,115 @@ #![allow(incomplete_features)] trait TensorDimension { - const DIM : usize; - const ISSCALAR : bool = Self::DIM == 0; - fn is_scalar(&self) -> bool {Self::ISSCALAR} + const DIM: usize; + //~^ ERROR cycle detected when resolving instance + // FIXME Given the current state of the compiler its expected that we cycle here, + // but the cycle is still wrong. + const ISSCALAR: bool = Self::DIM == 0; + fn is_scalar(&self) -> bool { + Self::ISSCALAR + } } -trait TensorSize : TensorDimension { - fn size(&self) -> [usize;Self::DIM]; - fn inbounds(&self,index : [usize;Self::DIM]) -> bool { - index.iter().zip(self.size().iter()).all(|(i,s)| i < s) +trait TensorSize: TensorDimension { + fn size(&self) -> [usize; Self::DIM]; + fn inbounds(&self, index: [usize; Self::DIM]) -> bool { + index.iter().zip(self.size().iter()).all(|(i, s)| i < s) } } - trait Broadcastable: TensorSize + Sized { type Element; - fn bget(&self, index:[usize;Self::DIM]) -> Option; - fn lazy_updim(&self, size : [usize;NEWDIM] ) -> - LazyUpdim - { - assert!(NEWDIM >= Self::DIM, - "Updimmed tensor cannot have fewer indices than the initial one."); - LazyUpdim {size,reference:&self} + fn bget(&self, index: [usize; Self::DIM]) -> Option; + fn lazy_updim( + &self, + size: [usize; NEWDIM], + ) -> LazyUpdim { + assert!( + NEWDIM >= Self::DIM, + "Updimmed tensor cannot have fewer indices than the initial one." + ); + LazyUpdim { size, reference: &self } } - fn bmap T>(&self,foo : F) -> BMap{ - BMap {reference:self,closure : foo} + fn bmap T>(&self, foo: F) -> BMap { + BMap { reference: self, closure: foo } } } - -struct LazyUpdim<'a,T : Broadcastable,const OLDDIM : usize, const DIM : usize> { - size : [usize;DIM], - reference : &'a T +struct LazyUpdim<'a, T: Broadcastable, const OLDDIM: usize, const DIM: usize> { + size: [usize; DIM], + reference: &'a T, } -impl<'a,T : Broadcastable,const DIM : usize> TensorDimension for LazyUpdim<'a,T,{T::DIM},DIM> { - const DIM : usize = DIM; +impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T, { T::DIM }, DIM> { + const DIM: usize = DIM; } -impl<'a,T : Broadcastable,const DIM : usize> TensorSize for LazyUpdim<'a,T,{T::DIM},DIM> { - fn size(&self) -> [usize;DIM] {self.size} - //~^ ERROR method not compatible with trait +impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> { + fn size(&self) -> [usize; DIM] { + self.size + } } -impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> -{ +impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> { type Element = T::Element; - fn bget(&self,index:[usize;DIM]) -> Option { - //~^ ERROR method not compatible with trait + fn bget(&self, index: [usize; DIM]) -> Option { assert!(DIM >= T::DIM); - if !self.inbounds(index) {return None} - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types + if !self.inbounds(index) { + return None; + } let size = self.size(); - //~^ ERROR unconstrained generic constant - let newindex : [usize;T::DIM] = Default::default(); - //~^ ERROR the trait bound `[usize; _]: Default` is not satisfied + let newindex: [usize; T::DIM] = Default::default(); self.reference.bget(newindex) } } -struct BMap<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , const DIM: usize> { - reference : &'a T, - closure : F +struct BMap<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> { + reference: &'a T, + closure: F, } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R, - const DIM: usize> TensorDimension for BMap<'a,R,T,F,DIM> { - - const DIM : usize = DIM; +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorDimension + for BMap<'a, R, T, F, DIM> +{ + const DIM: usize = DIM; } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , - const DIM: usize> TensorSize for BMap<'a,R,T,F,DIM> { - - fn size(&self) -> [usize;DIM] {self.reference.size()} - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types - //~| ERROR method not compatible with trait +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSize + for BMap<'a, R, T, F, DIM> +{ + fn size(&self) -> [usize; DIM] { + self.reference.size() + } } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , - const DIM: usize> Broadcastable for BMap<'a,R,T,F,DIM> { - +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcastable + for BMap<'a, R, T, F, DIM> +{ type Element = R; - fn bget(&self,index:[usize;DIM]) -> Option { - //~^ ERROR method not compatible with trait + fn bget(&self, index: [usize; DIM]) -> Option { self.reference.bget(index).map(&self.closure) - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types } } impl TensorDimension for Vec { - const DIM : usize = 1; + const DIM: usize = 1; } impl TensorSize for Vec { - fn size(&self) -> [usize;1] {[self.len()]} + fn size(&self) -> [usize; 1] { + [self.len()] + } } impl Broadcastable for Vec { type Element = T; - fn bget(& self,index : [usize;1]) -> Option { + fn bget(&self, index: [usize; 1]) -> Option { self.get(index[0]).cloned() } } fn main() { - let v = vec![1,2,3]; - let bv = v.lazy_updim([3,4]); - let bbv = bv.bmap(|x| x*x); + let v = vec![1, 2, 3]; + let bv = v.lazy_updim([3, 4]); + let bbv = bv.bmap(|x| x * x); - println!("The size of v is {:?}",bbv.bget([0,2]).expect("Out of bounds.")); + println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds.")); } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index a49f850717f8a..8705a39fa4bcd 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,130 +1,17 @@ -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:44:5 +error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` + --> $DIR/issue-83765.rs:5:5 | -LL | fn size(&self) -> [usize;DIM] {self.size} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` +LL | const DIM: usize; + | ^^^^^^^^^^^^^^^^^ | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:51:5 - | -LL | fn bget(&self,index:[usize;DIM]) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:78:5 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:88:5 - | -LL | fn bget(&self,index:[usize;DIM]) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:54:18 - | -LL | if !self.inbounds(index) {return None} - | ^^^^^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::inbounds` - --> $DIR/issue-83765.rs:12:38 - | -LL | fn inbounds(&self,index : [usize;Self::DIM]) -> bool { - | ^^^^^^^^^ required by this bound in `TensorSize::inbounds` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:54:27 - | -LL | if !self.inbounds(index) {return None} - | ^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:57:25 - | -LL | let size = self.size(); - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::size` - --> $DIR/issue-83765.rs:11:30 - | -LL | fn size(&self) -> [usize;Self::DIM]; - | ^^^^^^^^^ required by this bound in `TensorSize::size` - -error[E0277]: the trait bound `[usize; _]: Default` is not satisfied - --> $DIR/issue-83765.rs:59:41 - | -LL | let newindex : [usize;T::DIM] = Default::default(); - | ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]` - | -help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement - | -LL | impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> where [usize; _]: Default - | +++++++++++++++++++++++++ - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:78:51 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::size` - --> $DIR/issue-83765.rs:11:30 - | -LL | fn size(&self) -> [usize;Self::DIM]; - | ^^^^^^^^^ required by this bound in `TensorSize::size` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:78:36 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM` - | - = note: expected type `DIM` - found type `Self::DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:90:24 - | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `Broadcastable::bget` - --> $DIR/issue-83765.rs:20:33 - | -LL | fn bget(&self, index:[usize;Self::DIM]) -> Option; - | ^^^^^^^^^ required by this bound in `Broadcastable::bget` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:90:29 - | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^^ expected `Self::DIM`, found `DIM` +note: ...which requires checking if `TensorDimension` fulfills its obligations... + --> $DIR/issue-83765.rs:4:1 | - = note: expected type `Self::DIM` - found type `DIM` +LL | trait TensorDimension { + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle + = note: cycle used when normalizing ` as TensorDimension>::DIM` -error: aborting due to 12 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/typeck/issue-90319.rs b/src/test/ui/typeck/issue-90319.rs new file mode 100644 index 0000000000000..57e6ac7cf34f5 --- /dev/null +++ b/src/test/ui/typeck/issue-90319.rs @@ -0,0 +1,17 @@ +struct Wrapper(T); + +trait Trait { + fn method(&self) {} +} + +impl<'a, T> Trait for Wrapper<&'a T> where Wrapper: Trait {} + +fn get() -> T { + unimplemented!() +} + +fn main() { + let thing = get::();//~ERROR cannot find type `Thing` in this scope [E0412] + let wrapper = Wrapper(thing); + Trait::method(&wrapper); +} diff --git a/src/test/ui/typeck/issue-90319.stderr b/src/test/ui/typeck/issue-90319.stderr new file mode 100644 index 0000000000000..61549dd701e74 --- /dev/null +++ b/src/test/ui/typeck/issue-90319.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Thing` in this scope + --> $DIR/issue-90319.rs:14:23 + | +LL | let thing = get::(); + | ^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`.