diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index d9ccba07a346a..753e0bf1645a3 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -33,6 +33,7 @@ use rustc_target::abi::{ use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel}; use smallvec::SmallVec; +use libc::c_uint; use std::cell::{Cell, RefCell}; use std::ffi::CStr; use std::str; @@ -347,6 +348,23 @@ pub unsafe fn create_module<'ll>( ); } + // Insert `llvm.ident` metadata. + // + // On the wasm targets it will get hooked up to the "producer" sections + // `processed-by` information. + let rustc_producer = + format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION")); + let name_metadata = llvm::LLVMMDStringInContext( + llcx, + rustc_producer.as_ptr().cast(), + rustc_producer.as_bytes().len() as c_uint, + ); + llvm::LLVMAddNamedMetadataOperand( + llmod, + cstr!("llvm.ident").as_ptr(), + llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), + ); + llmod } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index b6eb5ee183fa3..7db068f2cb14e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -907,21 +907,6 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( ); } - // Insert `llvm.ident` metadata on the wasm targets since that will - // get hooked up to the "producer" sections `processed-by` information. - if tcx.sess.target.is_like_wasm { - let name_metadata = llvm::LLVMMDStringInContext( - debug_context.llcontext, - rustc_producer.as_ptr().cast(), - rustc_producer.as_bytes().len() as c_uint, - ); - llvm::LLVMAddNamedMetadataOperand( - debug_context.llmod, - cstr!("llvm.ident").as_ptr(), - llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1), - ); - } - return unit_metadata; }; diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl index cc38d71b484a4..164d6d26d230d 100644 --- a/compiler/rustc_error_messages/locales/en-US/infer.ftl +++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl @@ -268,28 +268,28 @@ infer_but_calling_introduces = {$has_param_name -> [true] `{$param_name}` *[false] `fn` parameter } has {$lifetime_kind -> - [named] lifetime `{$lifetime}` - *[anon] an anonymous lifetime `'_` -} but calling `{assoc_item}` introduces an implicit `'static` lifetime requirement + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement .label1 = {$has_lifetime -> - [named] lifetime `{$lifetime}` - *[anon] an anonymous lifetime `'_` + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` } .label2 = ...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> - [named] `impl` of `{$impl_path}` - *[anon] inherent `impl` + [true] `impl` of `{$impl_path}` + *[false] inherent `impl` } infer_but_needs_to_satisfy = {$has_param_name -> [true] `{$param_name}` *[false] `fn` parameter } has {$has_lifetime -> - [named] lifetime `{$lifetime}` - *[anon] an anonymous lifetime `'_` + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` } but it needs to satisfy a `'static` lifetime requirement .influencer = this data with {$has_lifetime -> - [named] lifetime `{$lifetime}` - *[anon] an anonymous lifetime `'_` + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` }... .require = {$spans_empty -> *[true] ...is used and required to live as long as `'static` here @@ -302,8 +302,8 @@ infer_more_targeted = {$has_param_name -> [true] `{$param_name}` *[false] `fn` parameter } has {$has_lifetime -> - [named] lifetime `{$lifetime}` - *[anon] an anonymous lifetime `'_` + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` } but calling `{$ident}` introduces an implicit `'static` lifetime requirement infer_ril_introduced_here = `'static` requirement introduced here diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index c18a911b2fbcd..68cdc6d7711d4 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -207,7 +207,12 @@ impl IndexVec { &'a mut self, range: R, ) -> impl Iterator + 'a { - self.raw.drain(range).enumerate().map(|(n, t)| (I::new(n), t)) + let begin = match range.start_bound() { + std::ops::Bound::Included(i) => *i, + std::ops::Bound::Excluded(i) => i.checked_add(1).unwrap(), + std::ops::Bound::Unbounded => 0, + }; + self.raw.drain(range).enumerate().map(move |(n, t)| (I::new(begin + n), t)) } #[inline] diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 033a1842edb25..8bf3a160abbb4 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -927,6 +927,8 @@ pub struct ButNeedsToSatisfy { #[subdiagnostic] pub req_introduces_loc: Option, + pub has_param_name: bool, + pub param_name: String, pub spans_empty: bool, pub has_lifetime: bool, pub lifetime: String, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 49ad3ce50b8f5..6a463583dfb0f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -98,6 +98,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sp = var_origin.span(); let return_sp = sub_origin.span(); let param = self.find_param_with_region(*sup_r, *sub_r)?; + let simple_ident = param.param.pat.simple_ident(); let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; let (mention_influencer, influencer_point) = @@ -187,7 +188,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { req_introduces_loc: subdiag, has_lifetime: sup_r.has_name(), - lifetime: sup_r.to_string(), + lifetime: lifetime_name.clone(), + has_param_name: simple_ident.is_some(), + param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), spans_empty, bound, }; diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index e22ba9785e1fd..c54c66eab2799 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -479,7 +479,7 @@ where } ty::Alias(ty::Opaque, ty::AliasTy { def_id, ref substs, .. }) => { - // Skip lifetime paramters that are not captures. + // Skip lifetime parameters that are not captures. let variances = self.tcx.variances_of(*def_id); for (v, s) in std::iter::zip(variances, substs.iter()) { @@ -492,7 +492,7 @@ where ty::Alias(ty::Projection, proj) if self.tcx.def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder => { - // Skip lifetime paramters that are not captures. + // Skip lifetime parameters that are not captures. let variances = self.tcx.variances_of(proj.def_id); for (v, s) in std::iter::zip(variances, proj.substs.iter()) { diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index f728bff0e3b91..27211aabfcafd 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1360,6 +1360,11 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, if (WasmCustomSections) WasmCustomSections->eraseFromParent(); + // `llvm.ident` named metadata also gets duplicated. + auto *llvmIdent = (*MOrErr)->getNamedMetadata("llvm.ident"); + if (llvmIdent) + llvmIdent->eraseFromParent(); + return MOrErr; }; bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index 0bca02589bce1..9840b95feefde 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -147,6 +147,12 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ExprKind::AddressOf { mutability, arg } => Ok( Rvalue::AddressOf(*mutability, self.parse_place(*arg)?) ), + ExprKind::Binary { op, lhs, rhs } => Ok( + Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))) + ), + ExprKind::Unary { op, arg } => Ok( + Rvalue::UnaryOp(*op, self.parse_operand(*arg)?) + ), _ => self.parse_operand(expr_id).map(Rvalue::Use), ) } diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 2336fb53aec28..31c1bc9ecc062 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -1,7 +1,7 @@ //! Code shared by trait and projection goals for candidate assembly. use super::infcx_ext::InferCtxtExt; -use super::{CanonicalResponse, EvalCtxt, Goal, QueryResult}; +use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::elaborate_predicates; @@ -79,7 +79,7 @@ pub(super) enum CandidateSource { AliasBound(usize), } -pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { +pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { fn self_ty(self) -> Ty<'tcx>; fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; @@ -117,6 +117,22 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + + fn consider_builtin_pointer_sized_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + fn consider_builtin_fn_trait_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + kind: ty::ClosureKind, + ) -> QueryResult<'tcx>; + + fn consider_builtin_tuple_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -124,6 +140,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, G>, ) -> Vec> { + debug_assert_eq!(goal, self.infcx.resolve_vars_if_possible(goal)); + + // HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule, + // object bound, alias bound, etc. We are unable to determine this until we can at + // least structually resolve the type one layer. + if goal.predicate.self_ty().is_ty_var() { + return vec![Candidate { + source: CandidateSource::BuiltinImpl, + result: self + .make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) + .unwrap(), + }]; + } + let mut candidates = Vec::new(); self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates); @@ -169,6 +199,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok((_, certainty)) => certainty, Err(NoSolution) => return, }; + let normalized_ty = self.infcx.resolve_vars_if_possible(normalized_ty); // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. // This doesn't work as long as we use `CandidateSource` in winnowing. @@ -224,6 +255,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { || lang_items.clone_trait() == Some(trait_def_id) { G::consider_builtin_copy_clone_candidate(self, goal) + } else if lang_items.pointer_sized() == Some(trait_def_id) { + G::consider_builtin_pointer_sized_candidate(self, goal) + } else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) { + G::consider_builtin_fn_trait_candidates(self, goal, kind) + } else if lang_items.tuple_trait() == Some(trait_def_id) { + G::consider_builtin_tuple_candidate(self, goal) } else { Err(NoSolution) }; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index b086c0684d28d..a6240666ed43a 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -52,7 +52,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { .drain(..) .map(|obligation| FulfillmentError { obligation: obligation.clone(), - code: FulfillmentErrorCode::CodeSelectionError(SelectionError::Unimplemented), + code: FulfillmentErrorCode::CodeAmbiguity, root_obligation: obligation, }) .collect() @@ -75,7 +75,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { Err(NoSolution) => { errors.push(FulfillmentError { obligation: obligation.clone(), - code: FulfillmentErrorCode::CodeAmbiguity, + code: FulfillmentErrorCode::CodeSelectionError( + SelectionError::Unimplemented, + ), root_obligation: obligation, }); continue; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index ffc1c70e0cb81..e39fa05339286 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -2,6 +2,7 @@ use crate::traits::{specialization_graph, translate_substs}; use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; +use super::trait_goals::structural_traits; use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; @@ -11,9 +12,9 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor}; +use rustc_middle::ty::{ToPredicate, TypeVisitable}; use rustc_span::DUMMY_SP; use std::iter; use std::ops::ControlFlow; @@ -351,6 +352,46 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) -> QueryResult<'tcx> { bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal); } + + fn consider_builtin_pointer_sized_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`PointerSized` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_fn_trait_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + goal_kind: ty::ClosureKind, + ) -> QueryResult<'tcx> { + if let Some(tupled_inputs_and_output) = + structural_traits::extract_tupled_inputs_and_output_from_callable( + ecx.tcx(), + goal.predicate.self_ty(), + goal_kind, + )? + { + let pred = tupled_inputs_and_output + .map_bound(|(inputs, output)| ty::ProjectionPredicate { + projection_ty: ecx + .tcx() + .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]), + term: output.into(), + }) + .to_predicate(ecx.tcx()); + Self::consider_assumption(ecx, goal, pred) + } else { + ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) + } + } + + fn consider_builtin_tuple_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Tuple` does not have an associated type: {:?}", goal); + } } /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 1ebcfd03c14ea..9985d7181bb7d 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -4,16 +4,16 @@ use std::iter; use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; -use super::{EvalCtxt, Goal, QueryResult}; +use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::TraitPredicate; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{TraitPredicate, TypeVisitable}; use rustc_span::DUMMY_SP; -mod structural_traits; +pub mod structural_traits; impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn self_ty(self) -> Ty<'tcx> { @@ -127,6 +127,64 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { structural_traits::instantiate_constituent_tys_for_copy_clone_trait, ) } + + fn consider_builtin_pointer_sized_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.self_ty().has_non_region_infer() { + return ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)); + } + + let tcx = ecx.tcx(); + let self_ty = tcx.erase_regions(goal.predicate.self_ty()); + + if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty)) + && let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout + && layout.layout.size() == usize_layout.size() + && layout.layout.align().abi == usize_layout.align().abi + { + // FIXME: We could make this faster by making a no-constraints response + ecx.make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + + fn consider_builtin_fn_trait_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + goal_kind: ty::ClosureKind, + ) -> QueryResult<'tcx> { + if let Some(tupled_inputs_and_output) = + structural_traits::extract_tupled_inputs_and_output_from_callable( + ecx.tcx(), + goal.predicate.self_ty(), + goal_kind, + )? + { + let pred = tupled_inputs_and_output + .map_bound(|(inputs, _)| { + ecx.tcx() + .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) + }) + .to_predicate(ecx.tcx()); + Self::consider_assumption(ecx, goal, pred) + } else { + ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) + } + } + + fn consider_builtin_tuple_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if let ty::Tuple(..) = goal.predicate.self_ty().kind() { + ecx.make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } } impl<'tcx> EvalCtxt<'_, 'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs index bbc0c77253278..a11cd13cb0856 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs @@ -1,6 +1,6 @@ use rustc_hir::{Movability, Mutability}; use rustc_infer::{infer::InferCtxt, traits::query::NoSolution}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; // Calculates the constituent types of a type for `auto trait` purposes. // @@ -30,10 +30,7 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>( | ty::Foreign(..) | ty::Alias(ty::Projection, ..) | ty::Bound(..) - | ty::Infer(ty::TyVar(_)) => { - // FIXME: Do we need to mark anything as ambiguous here? Yeah? - Err(NoSolution) - } + | ty::Infer(ty::TyVar(_)) => Err(NoSolution), ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), @@ -101,9 +98,8 @@ pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>( | ty::Dynamic(..) | ty::Foreign(..) | ty::Alias(..) - | ty::Param(_) => Err(NoSolution), - - ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), + | ty::Param(_) + | ty::Infer(ty::TyVar(_)) => Err(NoSolution), ty::Placeholder(..) | ty::Bound(..) @@ -151,9 +147,8 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( | ty::Ref(_, _, Mutability::Mut) | ty::Adt(_, _) | ty::Alias(_, _) - | ty::Param(_) => Err(NoSolution), - - ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), + | ty::Param(_) + | ty::Infer(ty::TyVar(_)) => Err(NoSolution), ty::Placeholder(..) | ty::Bound(..) @@ -177,3 +172,52 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( } } } + +pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>( + tcx: TyCtxt<'tcx>, + self_ty: Ty<'tcx>, + goal_kind: ty::ClosureKind, +) -> Result, Ty<'tcx>)>>, NoSolution> { + match *self_ty.kind() { + ty::FnDef(def_id, substs) => Ok(Some( + tcx.bound_fn_sig(def_id) + .subst(tcx, substs) + .map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output())), + )), + ty::FnPtr(sig) => { + Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output())))) + } + ty::Closure(_, substs) => { + let closure_substs = substs.as_closure(); + match closure_substs.kind_ty().to_opt_closure_kind() { + Some(closure_kind) if closure_kind.extends(goal_kind) => {} + None => return Ok(None), + _ => return Err(NoSolution), + } + Ok(Some(closure_substs.sig().map_bound(|sig| (sig.inputs()[0], sig.output())))) + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::Dynamic(_, _, _) + | ty::Generator(_, _, _) + | ty::GeneratorWitness(_) + | ty::Never + | ty::Tuple(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Placeholder(_) + | ty::Bound(_, _) + | ty::Infer(_) + | ty::Error(_) => Err(NoSolution), + } +} diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index e9886fc571799..fecacc2bb6395 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -19,10 +19,12 @@ use core::cmp::Ordering::{self, Less}; use core::mem::{self, SizedTypeProperties}; #[cfg(not(no_global_oom_handling))] use core::ptr; +#[cfg(not(no_global_oom_handling))] +use core::slice::sort; use crate::alloc::Allocator; #[cfg(not(no_global_oom_handling))] -use crate::alloc::Global; +use crate::alloc::{self, Global}; #[cfg(not(no_global_oom_handling))] use crate::borrow::ToOwned; use crate::boxed::Box; @@ -206,7 +208,7 @@ impl [T] { where T: Ord, { - merge_sort(self, T::lt); + stable_sort(self, T::lt); } /// Sorts the slice with a comparator function. @@ -262,7 +264,7 @@ impl [T] { where F: FnMut(&T, &T) -> Ordering, { - merge_sort(self, |a, b| compare(a, b) == Less); + stable_sort(self, |a, b| compare(a, b) == Less); } /// Sorts the slice with a key extraction function. @@ -305,7 +307,7 @@ impl [T] { F: FnMut(&T) -> K, K: Ord, { - merge_sort(self, |a, b| f(a).lt(&f(b))); + stable_sort(self, |a, b| f(a).lt(&f(b))); } /// Sorts the slice with a key extraction function. @@ -812,324 +814,52 @@ impl ToOwned for [T] { // Sorting //////////////////////////////////////////////////////////////////////////////// -/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted. -/// -/// This is the integral subroutine of insertion sort. -#[cfg(not(no_global_oom_handling))] -fn insert_head(v: &mut [T], is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - if v.len() >= 2 && is_less(&v[1], &v[0]) { - unsafe { - // There are three ways to implement insertion here: - // - // 1. Swap adjacent elements until the first one gets to its final destination. - // However, this way we copy data around more than is necessary. If elements are big - // structures (costly to copy), this method will be slow. - // - // 2. Iterate until the right place for the first element is found. Then shift the - // elements succeeding it to make room for it and finally place it into the - // remaining hole. This is a good method. - // - // 3. Copy the first element into a temporary variable. Iterate until the right place - // for it is found. As we go along, copy every traversed element into the slot - // preceding it. Finally, copy data from the temporary variable into the remaining - // hole. This method is very good. Benchmarks demonstrated slightly better - // performance than with the 2nd method. - // - // All methods were benchmarked, and the 3rd showed best results. So we chose that one. - let tmp = mem::ManuallyDrop::new(ptr::read(&v[0])); - - // Intermediate state of the insertion process is always tracked by `hole`, which - // serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` in the end. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and - // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it - // initially held exactly once. - let mut hole = InsertionHole { src: &*tmp, dest: &mut v[1] }; - ptr::copy_nonoverlapping(&v[1], &mut v[0], 1); - - for i in 2..v.len() { - if !is_less(&v[i], &*tmp) { - break; - } - ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1); - hole.dest = &mut v[i]; - } - // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. - } - } - - // When dropped, copies from `src` into `dest`. - struct InsertionHole { - src: *const T, - dest: *mut T, - } - - impl Drop for InsertionHole { - fn drop(&mut self) { - unsafe { - ptr::copy_nonoverlapping(self.src, self.dest, 1); - } - } - } -} - -/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and -/// stores the result into `v[..]`. -/// -/// # Safety -/// -/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough -/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type. -#[cfg(not(no_global_oom_handling))] -unsafe fn merge(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - let v = v.as_mut_ptr(); - let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) }; - - // The merge process first copies the shorter run into `buf`. Then it traces the newly copied - // run and the longer run forwards (or backwards), comparing their next unconsumed elements and - // copying the lesser (or greater) one into `v`. - // - // As soon as the shorter run is fully consumed, the process is done. If the longer run gets - // consumed first, then we must copy whatever is left of the shorter run into the remaining - // hole in `v`. - // - // Intermediate state of the process is always tracked by `hole`, which serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` if the longer run gets consumed first. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and fill the - // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every - // object it initially held exactly once. - let mut hole; - - if mid <= len - mid { - // The left run is shorter. - unsafe { - ptr::copy_nonoverlapping(v, buf, mid); - hole = MergeHole { start: buf, end: buf.add(mid), dest: v }; - } - - // Initially, these pointers point to the beginnings of their arrays. - let left = &mut hole.start; - let mut right = v_mid; - let out = &mut hole.dest; - - while *left < hole.end && right < v_end { - // Consume the lesser side. - // If equal, prefer the left run to maintain stability. - unsafe { - let to_copy = if is_less(&*right, &**left) { - get_and_increment(&mut right) - } else { - get_and_increment(left) - }; - ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1); - } - } - } else { - // The right run is shorter. - unsafe { - ptr::copy_nonoverlapping(v_mid, buf, len - mid); - hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid }; - } - - // Initially, these pointers point past the ends of their arrays. - let left = &mut hole.dest; - let right = &mut hole.end; - let mut out = v_end; - - while v < *left && buf < *right { - // Consume the greater side. - // If equal, prefer the right run to maintain stability. - unsafe { - let to_copy = if is_less(&*right.sub(1), &*left.sub(1)) { - decrement_and_get(left) - } else { - decrement_and_get(right) - }; - ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1); - } - } - } - // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of - // it will now be copied into the hole in `v`. - - unsafe fn get_and_increment(ptr: &mut *mut T) -> *mut T { - let old = *ptr; - *ptr = unsafe { ptr.add(1) }; - old - } - - unsafe fn decrement_and_get(ptr: &mut *mut T) -> *mut T { - *ptr = unsafe { ptr.sub(1) }; - *ptr - } - - // When dropped, copies the range `start..end` into `dest..`. - struct MergeHole { - start: *mut T, - end: *mut T, - dest: *mut T, - } - - impl Drop for MergeHole { - fn drop(&mut self) { - // `T` is not a zero-sized type, and these are pointers into a slice's elements. - unsafe { - let len = self.end.sub_ptr(self.start); - ptr::copy_nonoverlapping(self.start, self.dest, len); - } - } - } -} - -/// This merge sort borrows some (but not all) ideas from TimSort, which is described in detail -/// [here](https://github.com/python/cpython/blob/main/Objects/listsort.txt). -/// -/// The algorithm identifies strictly descending and non-descending subsequences, which are called -/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed -/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are -/// satisfied: -/// -/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` -/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` -/// -/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case. +#[inline] #[cfg(not(no_global_oom_handling))] -fn merge_sort(v: &mut [T], mut is_less: F) +fn stable_sort(v: &mut [T], mut is_less: F) where F: FnMut(&T, &T) -> bool, { - // Slices of up to this length get sorted using insertion sort. - const MAX_INSERTION: usize = 20; - // Very short runs are extended using insertion sort to span at least this many elements. - const MIN_RUN: usize = 10; - - // Sorting has no meaningful behavior on zero-sized types. if T::IS_ZST { + // Sorting has no meaningful behavior on zero-sized types. Do nothing. return; } - let len = v.len(); - - // Short arrays get sorted in-place via insertion sort to avoid allocations. - if len <= MAX_INSERTION { - if len >= 2 { - for i in (0..len - 1).rev() { - insert_head(&mut v[i..], &mut is_less); - } - } - return; - } - - // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it - // shallow copies of the contents of `v` without risking the dtors running on copies if - // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run, - // which will always have length at most `len / 2`. - let mut buf = Vec::with_capacity(len / 2); + let elem_alloc_fn = |len: usize| -> *mut T { + // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len > + // v.len(). Alloc in general will only be used as 'shadow-region' to store temporary swap + // elements. + unsafe { alloc::alloc(alloc::Layout::array::(len).unwrap_unchecked()) as *mut T } + }; - // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a - // strange decision, but consider the fact that merges more often go in the opposite direction - // (forwards). According to benchmarks, merging forwards is slightly faster than merging - // backwards. To conclude, identifying runs by traversing backwards improves performance. - let mut runs = vec![]; - let mut end = len; - while end > 0 { - // Find the next natural run, and reverse it if it's strictly descending. - let mut start = end - 1; - if start > 0 { - start -= 1; - unsafe { - if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) { - while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) { - start -= 1; - } - v[start..end].reverse(); - } else { - while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) - { - start -= 1; - } - } - } - } - - // Insert some more elements into the run if it's too short. Insertion sort is faster than - // merge sort on short sequences, so this significantly improves performance. - while start > 0 && end - start < MIN_RUN { - start -= 1; - insert_head(&mut v[start..end], &mut is_less); + let elem_dealloc_fn = |buf_ptr: *mut T, len: usize| { + // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len > + // v.len(). The caller must ensure that buf_ptr was created by elem_alloc_fn with the same + // len. + unsafe { + alloc::dealloc(buf_ptr as *mut u8, alloc::Layout::array::(len).unwrap_unchecked()); } + }; - // Push this run onto the stack. - runs.push(Run { start, len: end - start }); - end = start; - - // Merge some pairs of adjacent runs to satisfy the invariants. - while let Some(r) = collapse(&runs) { - let left = runs[r + 1]; - let right = runs[r]; - unsafe { - merge( - &mut v[left.start..right.start + right.len], - left.len, - buf.as_mut_ptr(), - &mut is_less, - ); - } - runs[r] = Run { start: left.start, len: left.len + right.len }; - runs.remove(r + 1); + let run_alloc_fn = |len: usize| -> *mut sort::TimSortRun { + // SAFETY: Creating the layout is safe as long as merge_sort never calls this with an + // obscene length or 0. + unsafe { + alloc::alloc(alloc::Layout::array::(len).unwrap_unchecked()) + as *mut sort::TimSortRun } - } - - // Finally, exactly one run must remain in the stack. - debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len); + }; - // Examines the stack of runs and identifies the next pair of runs to merge. More specifically, - // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the - // algorithm should continue building a new run instead, `None` is returned. - // - // TimSort is infamous for its buggy implementations, as described here: - // http://envisage-project.eu/timsort-specification-and-verification/ - // - // The gist of the story is: we must enforce the invariants on the top four runs on the stack. - // Enforcing them on just top three is not sufficient to ensure that the invariants will still - // hold for *all* runs in the stack. - // - // This function correctly checks invariants for the top four runs. Additionally, if the top - // run starts at index 0, it will always demand a merge operation until the stack is fully - // collapsed, in order to complete the sort. - #[inline] - fn collapse(runs: &[Run]) -> Option { - let n = runs.len(); - if n >= 2 - && (runs[n - 1].start == 0 - || runs[n - 2].len <= runs[n - 1].len - || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len) - || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len)) - { - if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) } - } else { - None + let run_dealloc_fn = |buf_ptr: *mut sort::TimSortRun, len: usize| { + // SAFETY: The caller must ensure that buf_ptr was created by elem_alloc_fn with the same + // len. + unsafe { + alloc::dealloc( + buf_ptr as *mut u8, + alloc::Layout::array::(len).unwrap_unchecked(), + ); } - } + }; - #[derive(Clone, Copy)] - struct Run { - start: usize, - len: usize, - } + sort::merge_sort(v, &mut is_less, elem_alloc_fn, elem_dealloc_fn, run_alloc_fn, run_dealloc_fn); } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index c32caa144594b..d93a3a57ecd27 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -29,13 +29,19 @@ use crate::slice; /// Pure rust memchr implementation, taken from rust-memchr pub mod memchr; +#[unstable( + feature = "slice_internals", + issue = "none", + reason = "exposed from core to be reused in std;" +)] +pub mod sort; + mod ascii; mod cmp; mod index; mod iter; mod raw; mod rotate; -mod sort; mod specialize; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index 3ac01d1727513..2181f9a811855 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -5,6 +5,9 @@ //! //! Unstable sorting is compatible with core because it doesn't allocate memory, unlike our //! stable sorting implementation. +//! +//! In addition it also contains the core logic of the stable sort used by `slice::sort` based on +//! TimSort. use crate::cmp; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; @@ -905,6 +908,7 @@ fn partition_at_index_loop<'a, T, F>( } } +/// Reorder the slice such that the element at `index` is at its final sorted position. pub fn partition_at_index( v: &mut [T], index: usize, @@ -949,3 +953,513 @@ where let pivot = &mut pivot[0]; (left, pivot, right) } + +/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted. +/// +/// This is the integral subroutine of insertion sort. +fn insert_head(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + if v.len() >= 2 && is_less(&v[1], &v[0]) { + // SAFETY: Copy tmp back even if panic, and ensure unique observation. + unsafe { + // There are three ways to implement insertion here: + // + // 1. Swap adjacent elements until the first one gets to its final destination. + // However, this way we copy data around more than is necessary. If elements are big + // structures (costly to copy), this method will be slow. + // + // 2. Iterate until the right place for the first element is found. Then shift the + // elements succeeding it to make room for it and finally place it into the + // remaining hole. This is a good method. + // + // 3. Copy the first element into a temporary variable. Iterate until the right place + // for it is found. As we go along, copy every traversed element into the slot + // preceding it. Finally, copy data from the temporary variable into the remaining + // hole. This method is very good. Benchmarks demonstrated slightly better + // performance than with the 2nd method. + // + // All methods were benchmarked, and the 3rd showed best results. So we chose that one. + let tmp = mem::ManuallyDrop::new(ptr::read(&v[0])); + + // Intermediate state of the insertion process is always tracked by `hole`, which + // serves two purposes: + // 1. Protects integrity of `v` from panics in `is_less`. + // 2. Fills the remaining hole in `v` in the end. + // + // Panic safety: + // + // If `is_less` panics at any point during the process, `hole` will get dropped and + // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it + // initially held exactly once. + let mut hole = InsertionHole { src: &*tmp, dest: &mut v[1] }; + ptr::copy_nonoverlapping(&v[1], &mut v[0], 1); + + for i in 2..v.len() { + if !is_less(&v[i], &*tmp) { + break; + } + ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1); + hole.dest = &mut v[i]; + } + // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. + } + } + + // When dropped, copies from `src` into `dest`. + struct InsertionHole { + src: *const T, + dest: *mut T, + } + + impl Drop for InsertionHole { + fn drop(&mut self) { + // SAFETY: The caller must ensure that src and dest are correctly set. + unsafe { + ptr::copy_nonoverlapping(self.src, self.dest, 1); + } + } + } +} + +/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and +/// stores the result into `v[..]`. +/// +/// # Safety +/// +/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough +/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type. +unsafe fn merge(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + let v = v.as_mut_ptr(); + + // SAFETY: mid and len must be in-bounds of v. + let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) }; + + // The merge process first copies the shorter run into `buf`. Then it traces the newly copied + // run and the longer run forwards (or backwards), comparing their next unconsumed elements and + // copying the lesser (or greater) one into `v`. + // + // As soon as the shorter run is fully consumed, the process is done. If the longer run gets + // consumed first, then we must copy whatever is left of the shorter run into the remaining + // hole in `v`. + // + // Intermediate state of the process is always tracked by `hole`, which serves two purposes: + // 1. Protects integrity of `v` from panics in `is_less`. + // 2. Fills the remaining hole in `v` if the longer run gets consumed first. + // + // Panic safety: + // + // If `is_less` panics at any point during the process, `hole` will get dropped and fill the + // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every + // object it initially held exactly once. + let mut hole; + + if mid <= len - mid { + // The left run is shorter. + + // SAFETY: buf must have enough capacity for `v[..mid]`. + unsafe { + ptr::copy_nonoverlapping(v, buf, mid); + hole = MergeHole { start: buf, end: buf.add(mid), dest: v }; + } + + // Initially, these pointers point to the beginnings of their arrays. + let left = &mut hole.start; + let mut right = v_mid; + let out = &mut hole.dest; + + while *left < hole.end && right < v_end { + // Consume the lesser side. + // If equal, prefer the left run to maintain stability. + + // SAFETY: left and right must be valid and part of v same for out. + unsafe { + let to_copy = if is_less(&*right, &**left) { + get_and_increment(&mut right) + } else { + get_and_increment(left) + }; + ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1); + } + } + } else { + // The right run is shorter. + + // SAFETY: buf must have enough capacity for `v[mid..]`. + unsafe { + ptr::copy_nonoverlapping(v_mid, buf, len - mid); + hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid }; + } + + // Initially, these pointers point past the ends of their arrays. + let left = &mut hole.dest; + let right = &mut hole.end; + let mut out = v_end; + + while v < *left && buf < *right { + // Consume the greater side. + // If equal, prefer the right run to maintain stability. + + // SAFETY: left and right must be valid and part of v same for out. + unsafe { + let to_copy = if is_less(&*right.sub(1), &*left.sub(1)) { + decrement_and_get(left) + } else { + decrement_and_get(right) + }; + ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1); + } + } + } + // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of + // it will now be copied into the hole in `v`. + + unsafe fn get_and_increment(ptr: &mut *mut T) -> *mut T { + let old = *ptr; + + // SAFETY: ptr.add(1) must still be a valid pointer and part of `v`. + *ptr = unsafe { ptr.add(1) }; + old + } + + unsafe fn decrement_and_get(ptr: &mut *mut T) -> *mut T { + // SAFETY: ptr.sub(1) must still be a valid pointer and part of `v`. + *ptr = unsafe { ptr.sub(1) }; + *ptr + } + + // When dropped, copies the range `start..end` into `dest..`. + struct MergeHole { + start: *mut T, + end: *mut T, + dest: *mut T, + } + + impl Drop for MergeHole { + fn drop(&mut self) { + // SAFETY: `T` is not a zero-sized type, and these are pointers into a slice's elements. + unsafe { + let len = self.end.sub_ptr(self.start); + ptr::copy_nonoverlapping(self.start, self.dest, len); + } + } + } +} + +/// This merge sort borrows some (but not all) ideas from TimSort, which used to be described in +/// detail [here](https://github.com/python/cpython/blob/main/Objects/listsort.txt). However Python +/// has switched to a Powersort based implementation. +/// +/// The algorithm identifies strictly descending and non-descending subsequences, which are called +/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed +/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are +/// satisfied: +/// +/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` +/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` +/// +/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case. +pub fn merge_sort( + v: &mut [T], + is_less: &mut CmpF, + elem_alloc_fn: ElemAllocF, + elem_dealloc_fn: ElemDeallocF, + run_alloc_fn: RunAllocF, + run_dealloc_fn: RunDeallocF, +) where + CmpF: FnMut(&T, &T) -> bool, + ElemAllocF: Fn(usize) -> *mut T, + ElemDeallocF: Fn(*mut T, usize), + RunAllocF: Fn(usize) -> *mut TimSortRun, + RunDeallocF: Fn(*mut TimSortRun, usize), +{ + // Slices of up to this length get sorted using insertion sort. + const MAX_INSERTION: usize = 20; + // Very short runs are extended using insertion sort to span at least this many elements. + const MIN_RUN: usize = 10; + + // The caller should have already checked that. + debug_assert!(!T::IS_ZST); + + let len = v.len(); + + // Short arrays get sorted in-place via insertion sort to avoid allocations. + if len <= MAX_INSERTION { + if len >= 2 { + for i in (0..len - 1).rev() { + insert_head(&mut v[i..], is_less); + } + } + return; + } + + // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it + // shallow copies of the contents of `v` without risking the dtors running on copies if + // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run, + // which will always have length at most `len / 2`. + let buf = BufGuard::new(len / 2, elem_alloc_fn, elem_dealloc_fn); + let buf_ptr = buf.buf_ptr; + + let mut runs = RunVec::new(run_alloc_fn, run_dealloc_fn); + + // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a + // strange decision, but consider the fact that merges more often go in the opposite direction + // (forwards). According to benchmarks, merging forwards is slightly faster than merging + // backwards. To conclude, identifying runs by traversing backwards improves performance. + let mut end = len; + while end > 0 { + // Find the next natural run, and reverse it if it's strictly descending. + let mut start = end - 1; + if start > 0 { + start -= 1; + + // SAFETY: The v.get_unchecked must be fed with correct inbound indicies. + unsafe { + if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) { + while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) { + start -= 1; + } + v[start..end].reverse(); + } else { + while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) + { + start -= 1; + } + } + } + } + + // Insert some more elements into the run if it's too short. Insertion sort is faster than + // merge sort on short sequences, so this significantly improves performance. + while start > 0 && end - start < MIN_RUN { + start -= 1; + insert_head(&mut v[start..end], is_less); + } + + // Push this run onto the stack. + runs.push(TimSortRun { start, len: end - start }); + end = start; + + // Merge some pairs of adjacent runs to satisfy the invariants. + while let Some(r) = collapse(runs.as_slice()) { + let left = runs[r + 1]; + let right = runs[r]; + // SAFETY: `buf_ptr` must hold enough capacity for the shorter of the two sides, and + // neither side may be on length 0. + unsafe { + merge(&mut v[left.start..right.start + right.len], left.len, buf_ptr, is_less); + } + runs[r] = TimSortRun { start: left.start, len: left.len + right.len }; + runs.remove(r + 1); + } + } + + // Finally, exactly one run must remain in the stack. + debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len); + + // Examines the stack of runs and identifies the next pair of runs to merge. More specifically, + // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the + // algorithm should continue building a new run instead, `None` is returned. + // + // TimSort is infamous for its buggy implementations, as described here: + // http://envisage-project.eu/timsort-specification-and-verification/ + // + // The gist of the story is: we must enforce the invariants on the top four runs on the stack. + // Enforcing them on just top three is not sufficient to ensure that the invariants will still + // hold for *all* runs in the stack. + // + // This function correctly checks invariants for the top four runs. Additionally, if the top + // run starts at index 0, it will always demand a merge operation until the stack is fully + // collapsed, in order to complete the sort. + #[inline] + fn collapse(runs: &[TimSortRun]) -> Option { + let n = runs.len(); + if n >= 2 + && (runs[n - 1].start == 0 + || runs[n - 2].len <= runs[n - 1].len + || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len) + || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len)) + { + if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) } + } else { + None + } + } + + // Extremely basic versions of Vec. + // Their use is super limited and by having the code here, it allows reuse between the sort + // implementations. + struct BufGuard + where + ElemDeallocF: Fn(*mut T, usize), + { + buf_ptr: *mut T, + capacity: usize, + elem_dealloc_fn: ElemDeallocF, + } + + impl BufGuard + where + ElemDeallocF: Fn(*mut T, usize), + { + fn new( + len: usize, + elem_alloc_fn: ElemAllocF, + elem_dealloc_fn: ElemDeallocF, + ) -> Self + where + ElemAllocF: Fn(usize) -> *mut T, + { + Self { buf_ptr: elem_alloc_fn(len), capacity: len, elem_dealloc_fn } + } + } + + impl Drop for BufGuard + where + ElemDeallocF: Fn(*mut T, usize), + { + fn drop(&mut self) { + (self.elem_dealloc_fn)(self.buf_ptr, self.capacity); + } + } + + struct RunVec + where + RunAllocF: Fn(usize) -> *mut TimSortRun, + RunDeallocF: Fn(*mut TimSortRun, usize), + { + buf_ptr: *mut TimSortRun, + capacity: usize, + len: usize, + run_alloc_fn: RunAllocF, + run_dealloc_fn: RunDeallocF, + } + + impl RunVec + where + RunAllocF: Fn(usize) -> *mut TimSortRun, + RunDeallocF: Fn(*mut TimSortRun, usize), + { + fn new(run_alloc_fn: RunAllocF, run_dealloc_fn: RunDeallocF) -> Self { + // Most slices can be sorted with at most 16 runs in-flight. + const START_RUN_CAPACITY: usize = 16; + + Self { + buf_ptr: run_alloc_fn(START_RUN_CAPACITY), + capacity: START_RUN_CAPACITY, + len: 0, + run_alloc_fn, + run_dealloc_fn, + } + } + + fn push(&mut self, val: TimSortRun) { + if self.len == self.capacity { + let old_capacity = self.capacity; + let old_buf_ptr = self.buf_ptr; + + self.capacity = self.capacity * 2; + self.buf_ptr = (self.run_alloc_fn)(self.capacity); + + // SAFETY: buf_ptr new and old were correctly allocated and old_buf_ptr has + // old_capacity valid elements. + unsafe { + ptr::copy_nonoverlapping(old_buf_ptr, self.buf_ptr, old_capacity); + } + + (self.run_dealloc_fn)(old_buf_ptr, old_capacity); + } + + // SAFETY: The invariant was just checked. + unsafe { + self.buf_ptr.add(self.len).write(val); + } + self.len += 1; + } + + fn remove(&mut self, index: usize) { + if index >= self.len { + panic!("Index out of bounds"); + } + + // SAFETY: buf_ptr needs to be valid and len invariant upheld. + unsafe { + // the place we are taking from. + let ptr = self.buf_ptr.add(index); + + // Shift everything down to fill in that spot. + ptr::copy(ptr.add(1), ptr, self.len - index - 1); + } + self.len -= 1; + } + + fn as_slice(&self) -> &[TimSortRun] { + // SAFETY: Safe as long as buf_ptr is valid and len invariant was upheld. + unsafe { &*ptr::slice_from_raw_parts(self.buf_ptr, self.len) } + } + + fn len(&self) -> usize { + self.len + } + } + + impl core::ops::Index for RunVec + where + RunAllocF: Fn(usize) -> *mut TimSortRun, + RunDeallocF: Fn(*mut TimSortRun, usize), + { + type Output = TimSortRun; + + fn index(&self, index: usize) -> &Self::Output { + if index < self.len { + // SAFETY: buf_ptr and len invariant must be upheld. + unsafe { + return &*(self.buf_ptr.add(index)); + } + } + + panic!("Index out of bounds"); + } + } + + impl core::ops::IndexMut for RunVec + where + RunAllocF: Fn(usize) -> *mut TimSortRun, + RunDeallocF: Fn(*mut TimSortRun, usize), + { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + if index < self.len { + // SAFETY: buf_ptr and len invariant must be upheld. + unsafe { + return &mut *(self.buf_ptr.add(index)); + } + } + + panic!("Index out of bounds"); + } + } + + impl Drop for RunVec + where + RunAllocF: Fn(usize) -> *mut TimSortRun, + RunDeallocF: Fn(*mut TimSortRun, usize), + { + fn drop(&mut self) { + // As long as TimSortRun is Copy we don't need to drop them individually but just the + // whole allocation. + (self.run_dealloc_fn)(self.buf_ptr, self.capacity); + } + } +} + +/// Internal type used by merge_sort. +#[derive(Clone, Copy, Debug)] +pub struct TimSortRun { + len: usize, + start: usize, +} diff --git a/library/std/src/path.rs b/library/std/src/path.rs index c3593264e520b..2f53cf8393691 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2531,6 +2531,8 @@ impl Path { /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. /// + /// If `path` is absolute, it replaces the current path. + /// /// See [`PathBuf::push`] for more details on what it means to adjoin a path. /// /// # Examples @@ -2539,6 +2541,7 @@ impl Path { /// use std::path::{Path, PathBuf}; /// /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// assert_eq!(Path::new("/etc").join("/bin/sh"), PathBuf::from("/bin/sh")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use] diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index a08b8d89db67d..a93f60da2adf2 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -497,7 +497,7 @@ ul.block, .block li { padding-left: 24px; } -.sidebar a, .sidebar .current { +.sidebar a { color: var(--sidebar-link-color); } .sidebar .current, diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css index 7211ffb779568..4e9803fe2366d 100644 --- a/src/librustdoc/html/static/css/settings.css +++ b/src/librustdoc/html/static/css/settings.css @@ -3,11 +3,6 @@ position: relative; } -.setting-line .choices { - display: flex; - flex-wrap: wrap; -} - .setting-line .radio-line input, .setting-line .settings-toggle input { margin-right: 0.3em; @@ -38,7 +33,7 @@ margin-bottom: 0.1em; min-width: 3.8em; padding: 0.3em; - display: flex; + display: inline-flex; align-items: center; cursor: pointer; } diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 0b9a1b37e947e..17cba0f6ccd4e 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -60,7 +60,7 @@ fn main() { let handle = s.spawn(|| { let mut flag = false; - $p::check($($args),* , &mut flag); + $p::check($($args, )* &mut flag); if (flag) { bad.store(true, Ordering::Relaxed); } diff --git a/tests/codegen/llvm-ident.rs b/tests/codegen/llvm-ident.rs new file mode 100644 index 0000000000000..927f0d602ad70 --- /dev/null +++ b/tests/codegen/llvm-ident.rs @@ -0,0 +1,15 @@ +// Verifies that the `!llvm.ident` named metadata is emitted. +// +// revisions: NONE OPT DEBUG +// +// [OPT] compile-flags: -Copt-level=2 +// [DEBUG] compile-flags: -Cdebuginfo=2 + +// The named metadata should contain a single metadata node (see +// `LLVMRustPrepareThinLTOImport` for details). +// CHECK: !llvm.ident = !{![[ID:[0-9]+]]} + +// In addition, check that the metadata node has the expected content. +// CHECK: ![[ID]] = !{!"rustc version 1.{{.*}}"} + +fn main() {} diff --git a/tests/mir-opt/building/custom/operators.f.built.after.mir b/tests/mir-opt/building/custom/operators.f.built.after.mir new file mode 100644 index 0000000000000..a0c5f1b40dbb7 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.f.built.after.mir @@ -0,0 +1,26 @@ +// MIR for `f` after built + +fn f(_1: i32, _2: bool) -> i32 { + let mut _0: i32; // return place in scope 0 at $DIR/operators.rs:+0:30: +0:33 + + bb0: { + _1 = Neg(_1); // scope 0 at $DIR/operators.rs:+2:9: +2:15 + _2 = Not(_2); // scope 0 at $DIR/operators.rs:+3:9: +3:15 + _1 = Add(_1, _1); // scope 0 at $DIR/operators.rs:+4:9: +4:18 + _1 = Sub(_1, _1); // scope 0 at $DIR/operators.rs:+5:9: +5:18 + _1 = Mul(_1, _1); // scope 0 at $DIR/operators.rs:+6:9: +6:18 + _1 = Div(_1, _1); // scope 0 at $DIR/operators.rs:+7:9: +7:18 + _1 = Rem(_1, _1); // scope 0 at $DIR/operators.rs:+8:9: +8:18 + _1 = BitXor(_1, _1); // scope 0 at $DIR/operators.rs:+9:9: +9:18 + _1 = BitAnd(_1, _1); // scope 0 at $DIR/operators.rs:+10:9: +10:18 + _1 = Shl(_1, _1); // scope 0 at $DIR/operators.rs:+11:9: +11:19 + _1 = Shr(_1, _1); // scope 0 at $DIR/operators.rs:+12:9: +12:19 + _2 = Eq(_1, _1); // scope 0 at $DIR/operators.rs:+13:9: +13:19 + _2 = Lt(_1, _1); // scope 0 at $DIR/operators.rs:+14:9: +14:18 + _2 = Le(_1, _1); // scope 0 at $DIR/operators.rs:+15:9: +15:19 + _2 = Ge(_1, _1); // scope 0 at $DIR/operators.rs:+16:9: +16:19 + _2 = Gt(_1, _1); // scope 0 at $DIR/operators.rs:+17:9: +17:18 + _0 = _1; // scope 0 at $DIR/operators.rs:+18:9: +18:16 + return; // scope 0 at $DIR/operators.rs:+19:9: +19:17 + } +} diff --git a/tests/mir-opt/building/custom/operators.rs b/tests/mir-opt/building/custom/operators.rs new file mode 100644 index 0000000000000..51f80c66392a1 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.rs @@ -0,0 +1,28 @@ +// compile-flags: --crate-type=lib +#![feature(custom_mir, core_intrinsics, inline_const)] +use std::intrinsics::mir::*; + +// EMIT_MIR operators.f.built.after.mir +#[custom_mir(dialect = "built")] +pub fn f(a: i32, b: bool) -> i32 { + mir!({ + a = -a; + b = !b; + a = a + a; + a = a - a; + a = a * a; + a = a / a; + a = a % a; + a = a ^ a; + a = a & a; + a = a << a; + a = a >> a; + b = a == a; + b = a < a; + b = a <= a; + b = a >= a; + b = a > a; + RET = a; + Return() + }) +} diff --git a/tests/run-make/comment-section/Makefile b/tests/run-make/comment-section/Makefile new file mode 100644 index 0000000000000..322f2c0485bca --- /dev/null +++ b/tests/run-make/comment-section/Makefile @@ -0,0 +1,15 @@ +include ../../run-make-fulldeps/tools.mk + +# only-linux + +all: + echo 'fn main(){}' | $(RUSTC) - --emit=link,obj -Csave-temps + + # Check linked output has a `.comment` section with the expected content. + readelf -p '.comment' $(TMPDIR)/rust_out | $(CGREP) -F 'rustc version 1.' + + # Check all object files (including temporary outputs) have a `.comment` + # section with the expected content. + set -e; for f in $(TMPDIR)/*.o; do \ + readelf -p '.comment' $$f | $(CGREP) -F 'rustc version 1.'; \ + done diff --git a/tests/run-make/llvm-ident/Makefile b/tests/run-make/llvm-ident/Makefile new file mode 100644 index 0000000000000..635a8a594755a --- /dev/null +++ b/tests/run-make/llvm-ident/Makefile @@ -0,0 +1,19 @@ +include ../../run-make-fulldeps/tools.mk + +# only-linux + +all: + # `-Ccodegen-units=16 -Copt-level=2` is used here to trigger thin LTO + # across codegen units to test deduplication of the named metadata + # (see `LLVMRustPrepareThinLTOImport` for details). + echo 'fn main(){}' | $(RUSTC) - --emit=link,obj -Csave-temps -Ccodegen-units=16 -Copt-level=2 + + # `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR + # for temporary outputs. + "$(LLVM_BIN_DIR)"/llvm-dis $(TMPDIR)/*.bc + + # Check LLVM IR files (including temporary outputs) have `!llvm.ident` + # named metadata, reusing the related codegen test. + set -e; for f in $(TMPDIR)/*.ll; do \ + $(LLVM_FILECHECK) --input-file $$f ../../codegen/llvm-ident.rs; \ + done diff --git a/tests/ui/inference/issue-107090.rs b/tests/ui/inference/issue-107090.rs new file mode 100644 index 0000000000000..9426445656f23 --- /dev/null +++ b/tests/ui/inference/issue-107090.rs @@ -0,0 +1,31 @@ +use std::marker::PhantomData; +struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) +where + Foo<'short, 'out, T>: Convert<'a, 'b>; + //~^ ERROR mismatched types + //~^^ ERROR mismatched types + //~^^^ ERROR use of undeclared lifetime name + //~| ERROR use of undeclared lifetime name `'out` + +trait Convert<'a, 'b>: Sized { + fn cast(&'a self) -> &'b Self; +} +impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + //~^ ERROR use of undeclared lifetime name + //~^^ ERROR use of undeclared lifetime name `'out` + //~| ERROR cannot infer an appropriate lifetime for lifetime parameter + fn cast(&'long self) -> &'short Foo<'short, 'out, T> { + //~^ ERROR use of undeclared lifetime name + //~| ERROR cannot infer an appropriate lifetime for lifetime parameter + self + } +} + +fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { + //~^ ERROR use of undeclared lifetime name + //~^^ ERROR incompatible lifetime on type + //~| ERROR `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement + sadness.cast() +} + +fn main() {} diff --git a/tests/ui/inference/issue-107090.stderr b/tests/ui/inference/issue-107090.stderr new file mode 100644 index 0000000000000..33cb39014acfa --- /dev/null +++ b/tests/ui/inference/issue-107090.stderr @@ -0,0 +1,173 @@ +error[E0261]: use of undeclared lifetime name `'short` + --> $DIR/issue-107090.rs:4:9 + | +LL | Foo<'short, 'out, T>: Convert<'a, 'b>; + | ^^^^^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'short` lifetime + | +LL | for<'short> Foo<'short, 'out, T>: Convert<'a, 'b>; + | +++++++++++ +help: consider introducing lifetime `'short` here + | +LL | struct Foo<'short, 'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) + | +++++++ + +error[E0261]: use of undeclared lifetime name `'out` + --> $DIR/issue-107090.rs:4:17 + | +LL | Foo<'short, 'out, T>: Convert<'a, 'b>; + | ^^^^ undeclared lifetime + | +help: consider making the bound lifetime-generic with a new `'out` lifetime + | +LL | for<'out> Foo<'short, 'out, T>: Convert<'a, 'b>; + | +++++++++ +help: consider introducing lifetime `'out` here + | +LL | struct Foo<'out, 'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) + | +++++ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/issue-107090.rs:13:47 + | +LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'b` here: `'b,` + +error[E0261]: use of undeclared lifetime name `'out` + --> $DIR/issue-107090.rs:13:67 + | +LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + | - help: consider introducing lifetime `'out` here: `'out,` ^^^^ undeclared lifetime + +error[E0261]: use of undeclared lifetime name `'out` + --> $DIR/issue-107090.rs:17:49 + | +LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { + | ^^^^ undeclared lifetime + | +help: consider introducing lifetime `'out` here + | +LL | fn cast<'out>(&'long self) -> &'short Foo<'short, 'out, T> { + | ++++++ +help: consider introducing lifetime `'out` here + | +LL | impl<'out, 'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + | +++++ + +error[E0261]: use of undeclared lifetime name `'short` + --> $DIR/issue-107090.rs:24:68 + | +LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { + | - ^^^^^^ undeclared lifetime + | | + | help: consider introducing lifetime `'short` here: `'short,` + +error[E0308]: mismatched types + --> $DIR/issue-107090.rs:4:27 + | +LL | Foo<'short, 'out, T>: Convert<'a, 'b>; + | ^^^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected trait `Convert<'static, 'static>` + found trait `Convert<'a, 'b>` +note: the lifetime `'a` as defined here... + --> $DIR/issue-107090.rs:2:12 + | +LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) + | ^^ + = note: ...does not necessarily outlive the static lifetime + +error[E0308]: mismatched types + --> $DIR/issue-107090.rs:4:27 + | +LL | Foo<'short, 'out, T>: Convert<'a, 'b>; + | ^^^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected trait `Convert<'static, 'static>` + found trait `Convert<'a, 'b>` +note: the lifetime `'b` as defined here... + --> $DIR/issue-107090.rs:2:16 + | +LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) + | ^^ + = note: ...does not necessarily outlive the static lifetime + +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements + --> $DIR/issue-107090.rs:13:55 + | +LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'short` as defined here... + --> $DIR/issue-107090.rs:13:21 + | +LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + | ^^^^^^ + = note: ...but the lifetime must also be valid for the static lifetime... +note: ...so that the types are compatible + --> $DIR/issue-107090.rs:13:55 + | +LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + | ^^^^^^^^^^^^^^^^^^^^ + = note: expected `Convert<'short, 'static>` + found `Convert<'_, 'static>` + +error: incompatible lifetime on type + --> $DIR/issue-107090.rs:24:29 + | +LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { + | ^^^^^^^^^^^^^^^^^^ + | +note: because this has an unmet lifetime requirement + --> $DIR/issue-107090.rs:4:27 + | +LL | Foo<'short, 'out, T>: Convert<'a, 'b>; + | ^^^^^^^^^^^^^^^ introduces a `'static` lifetime requirement +note: the lifetime `'out` as defined here... + --> $DIR/issue-107090.rs:24:17 + | +LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { + | ^^^^ +note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` + --> $DIR/issue-107090.rs:13:1 + | +LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0759]: `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/issue-107090.rs:24:29 + | +LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { + | ^^^^^^^^^^^^^^^^^^ + | | + | this data with lifetime `'in_`... + | ...is used and required to live as long as `'static` here + +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements + --> $DIR/issue-107090.rs:17:13 + | +LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { + | ^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'short` as defined here... + --> $DIR/issue-107090.rs:13:21 + | +LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { + | ^^^^^^ + = note: ...but the lifetime must also be valid for the static lifetime... +note: ...so that the types are compatible + --> $DIR/issue-107090.rs:17:13 + | +LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { + | ^^^^^^^^^^^ + = note: expected `Convert<'short, 'static>` + found `Convert<'_, 'static>` + +error: aborting due to 12 previous errors + +Some errors have detailed explanations: E0261, E0308, E0495, E0759. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/traits/new-solver/fn-trait-closure.rs b/tests/ui/traits/new-solver/fn-trait-closure.rs new file mode 100644 index 0000000000000..c0ecf1c91fb38 --- /dev/null +++ b/tests/ui/traits/new-solver/fn-trait-closure.rs @@ -0,0 +1,15 @@ +// compile-flags: -Ztrait-solver=next +// known-bug: unknown +// failure-status: 101 +// dont-check-compiler-stderr + +// This test will fail until we fix `FulfillmentCtxt::relationships`. That's +// because we create a type variable for closure upvar types, which is not +// constrained until after we try to do fallback on diverging type variables. +// Thus, we will call that function, which is unimplemented. + +fn require_fn(_: impl Fn() -> i32) {} + +fn main() { + require_fn(|| -> i32 { 1i32 }); +} diff --git a/tests/ui/traits/new-solver/fn-trait.rs b/tests/ui/traits/new-solver/fn-trait.rs new file mode 100644 index 0000000000000..d566ead105c86 --- /dev/null +++ b/tests/ui/traits/new-solver/fn-trait.rs @@ -0,0 +1,13 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +fn require_fn(_: impl Fn() -> i32) {} + +fn f() -> i32 { + 1i32 +} + +fn main() { + require_fn(f); + require_fn(f as fn() -> i32); +} diff --git a/tests/ui/traits/new-solver/pointer-sized.rs b/tests/ui/traits/new-solver/pointer-sized.rs new file mode 100644 index 0000000000000..15681cd132ec6 --- /dev/null +++ b/tests/ui/traits/new-solver/pointer-sized.rs @@ -0,0 +1,12 @@ +#![feature(pointer_sized_trait)] + +use std::marker::PointerSized; + +fn require_pointer_sized(_: impl PointerSized) {} + +fn main() { + require_pointer_sized(1usize); + require_pointer_sized(1u16); + //~^ ERROR `u16` needs to be a pointer-sized type + require_pointer_sized(&1i16); +} diff --git a/tests/ui/traits/new-solver/pointer-sized.stderr b/tests/ui/traits/new-solver/pointer-sized.stderr new file mode 100644 index 0000000000000..b250b1331bbf9 --- /dev/null +++ b/tests/ui/traits/new-solver/pointer-sized.stderr @@ -0,0 +1,24 @@ +error[E0277]: `u16` needs to be a pointer-sized type + --> $DIR/pointer-sized.rs:9:27 + | +LL | require_pointer_sized(1u16); + | --------------------- ^^^^ the trait `PointerSized` is not implemented for `u16` + | | + | required by a bound introduced by this call + | + = note: the trait bound `u16: PointerSized` is not satisfied +note: required by a bound in `require_pointer_sized` + --> $DIR/pointer-sized.rs:5:34 + | +LL | fn require_pointer_sized(_: impl PointerSized) {} + | ^^^^^^^^^^^^ required by this bound in `require_pointer_sized` +help: consider borrowing here + | +LL | require_pointer_sized(&1u16); + | + +LL | require_pointer_sized(&mut 1u16); + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.