diff options
81 files changed, 1124 insertions, 819 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 5c67d3b6431..3154f12a779 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -13,8 +13,7 @@ use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::lang_items::LangItem; use rustc_index::vec::Idx; -use rustc_middle::mir::AssertKind; -use rustc_middle::mir::{self, SwitchTargets}; +use rustc_middle::mir::{self, AssertKind, SwitchTargets}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitable}; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 9ea9fbe0e0e..6e5c840bdfd 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -35,21 +35,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { // All `#[rustc_do_not_const_check]` functions should be hooked here. let def_id = instance.def_id(); - if Some(def_id) == self.tcx.lang_items().const_eval_select() { - // redirect to const_eval_select_ct - if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() { - return Ok(Some( - ty::Instance::resolve( - *self.tcx, - ty::ParamEnv::reveal_all(), - const_eval_select, - instance.substs, - ) - .unwrap() - .unwrap(), - )); - } - } else if Some(def_id) == self.tcx.lang_items().panic_display() + if Some(def_id) == self.tcx.lang_items().panic_display() || Some(def_id) == self.tcx.lang_items().begin_panic_fn() { // &str or &&str diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index f19d07d98dc..bc1ea1c4c73 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -269,8 +269,6 @@ language_item_table! { DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); Oom, sym::oom, oom, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; - ConstEvalSelect, sym::const_eval_select, const_eval_select, Target::Fn, GenericRequirement::Exact(4); - ConstConstEvalSelect, sym::const_eval_select_ct,const_eval_select_ct, Target::Fn, GenericRequirement::Exact(4); Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 93707bb18ce..a803fca0d5b 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -18,7 +18,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; /// `ValTree` does not have this problem with representation, as it only contains integers or /// lists of (nested) `ValTree`. pub enum ValTree<'tcx> { - /// ZSTs, integers, `bool`, `char` are represented as scalars. + /// integers, `bool`, `char` are represented as scalars. /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values /// of these types have the same representation. Leaf(ScalarInt), @@ -27,8 +27,11 @@ pub enum ValTree<'tcx> { // dont use SliceOrStr for now /// The fields of any kind of aggregate. Structs, tuples and arrays are represented by /// listing their fields' values in order. + /// /// Enums are represented by storing their discriminant as a field, followed by all /// the fields of the variant. + /// + /// ZST types are represented as an empty slice. Branch(&'tcx [ValTree<'tcx>]), } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 26b60e4f339..2c587b76f02 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -263,6 +263,7 @@ fn layout_of<'tcx>( Ok(layout) } +#[derive(Clone, Copy)] pub struct LayoutCx<'tcx, C> { pub tcx: C, pub param_env: ty::ParamEnv<'tcx>, @@ -3063,6 +3064,93 @@ fn fn_abi_of_instance<'tcx>( ) } +// Handle safe Rust thin and fat pointers. +pub fn adjust_for_rust_scalar<'tcx>( + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + attrs: &mut ArgAttributes, + scalar: Scalar, + layout: TyAndLayout<'tcx>, + offset: Size, + is_return: bool, +) { + // Booleans are always a noundef i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.ext(ArgExtension::Zext); + attrs.set(ArgAttribute::NoUndef); + return; + } + + // Scalars which have invalid values cannot be undef. + if !scalar.is_always_valid(&cx) { + attrs.set(ArgAttribute::NoUndef); + } + + // Only pointer types handled below. + let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return }; + + if !valid_range.contains(0) { + attrs.set(ArgAttribute::NonNull); + } + + if let Some(pointee) = layout.pointee_info_at(&cx, offset) { + if let Some(kind) = pointee.safe { + attrs.pointee_align = Some(pointee.align); + + // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable + // for the entire duration of the function as they can be deallocated + // at any time. Same for shared mutable references. If LLVM had a + // way to say "dereferenceable on entry" we could use it here. + attrs.pointee_size = match kind { + PointerKind::UniqueBorrowed + | PointerKind::UniqueBorrowedPinned + | PointerKind::Frozen => pointee.size, + PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO, + }; + + // `Box`, `&T`, and `&mut T` cannot be undef. + // Note that this only applies to the value of the pointer itself; + // this attribute doesn't make it UB for the pointed-to data to be undef. + attrs.set(ArgAttribute::NoUndef); + + // The aliasing rules for `Box<T>` are still not decided, but currently we emit + // `noalias` for it. This can be turned off using an unstable flag. + // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 + let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias.unwrap_or(true); + + // `&mut` pointer parameters never alias other parameters, + // or mutable global data + // + // `&T` where `T` contains no `UnsafeCell<U>` is immutable, + // and can be marked as both `readonly` and `noalias`, as + // LLVM's definition of `noalias` is based solely on memory + // dependencies rather than pointer equality + // + // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute + // for UniqueBorrowed arguments, so that the codegen backend can decide whether + // or not to actually emit the attribute. It can also be controlled with the + // `-Zmutable-noalias` debugging option. + let no_alias = match kind { + PointerKind::SharedMutable + | PointerKind::UniqueBorrowed + | PointerKind::UniqueBorrowedPinned => false, + PointerKind::UniqueOwned => noalias_for_box, + PointerKind::Frozen => !is_return, + }; + if no_alias { + attrs.set(ArgAttribute::NoAlias); + } + + if kind == PointerKind::Frozen && !is_return { + attrs.set(ArgAttribute::ReadOnly); + } + + if kind == PointerKind::UniqueBorrowed && !is_return { + attrs.set(ArgAttribute::NoAliasMutRef); + } + } + } +} + impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // FIXME(eddyb) perhaps group the signature/type-containing (or all of them?) // arguments of this method, into a separate `struct`. @@ -3118,91 +3206,6 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { use SpecAbi::*; let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall); - // Handle safe Rust thin and fat pointers. - let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, - scalar: Scalar, - layout: TyAndLayout<'tcx>, - offset: Size, - is_return: bool| { - // Booleans are always a noundef i1 that needs to be zero-extended. - if scalar.is_bool() { - attrs.ext(ArgExtension::Zext); - attrs.set(ArgAttribute::NoUndef); - return; - } - - // Scalars which have invalid values cannot be undef. - if !scalar.is_always_valid(self) { - attrs.set(ArgAttribute::NoUndef); - } - - // Only pointer types handled below. - let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return }; - - if !valid_range.contains(0) { - attrs.set(ArgAttribute::NonNull); - } - - if let Some(pointee) = layout.pointee_info_at(self, offset) { - if let Some(kind) = pointee.safe { - attrs.pointee_align = Some(pointee.align); - - // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable - // for the entire duration of the function as they can be deallocated - // at any time. Same for shared mutable references. If LLVM had a - // way to say "dereferenceable on entry" we could use it here. - attrs.pointee_size = match kind { - PointerKind::UniqueBorrowed - | PointerKind::UniqueBorrowedPinned - | PointerKind::Frozen => pointee.size, - PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO, - }; - - // `Box`, `&T`, and `&mut T` cannot be undef. - // Note that this only applies to the value of the pointer itself; - // this attribute doesn't make it UB for the pointed-to data to be undef. - attrs.set(ArgAttribute::NoUndef); - - // The aliasing rules for `Box<T>` are still not decided, but currently we emit - // `noalias` for it. This can be turned off using an unstable flag. - // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 - let noalias_for_box = - self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true); - - // `&mut` pointer parameters never alias other parameters, - // or mutable global data - // - // `&T` where `T` contains no `UnsafeCell<U>` is immutable, - // and can be marked as both `readonly` and `noalias`, as - // LLVM's definition of `noalias` is based solely on memory - // dependencies rather than pointer equality - // - // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute - // for UniqueBorrowed arguments, so that the codegen backend can decide whether - // or not to actually emit the attribute. It can also be controlled with the - // `-Zmutable-noalias` debugging option. - let no_alias = match kind { - PointerKind::SharedMutable - | PointerKind::UniqueBorrowed - | PointerKind::UniqueBorrowedPinned => false, - PointerKind::UniqueOwned => noalias_for_box, - PointerKind::Frozen => !is_return, - }; - if no_alias { - attrs.set(ArgAttribute::NoAlias); - } - - if kind == PointerKind::Frozen && !is_return { - attrs.set(ArgAttribute::ReadOnly); - } - - if kind == PointerKind::UniqueBorrowed && !is_return { - attrs.set(ArgAttribute::NoAliasMutRef); - } - } - } - }; - let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> { let is_return = arg_idx.is_none(); @@ -3218,7 +3221,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let mut arg = ArgAbi::new(self, layout, |layout, scalar, offset| { let mut attrs = ArgAttributes::new(); - adjust_for_rust_scalar(&mut attrs, scalar, *layout, offset, is_return); + adjust_for_rust_scalar(*self, &mut attrs, scalar, *layout, offset, is_return); attrs }); diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 0b674b38f32..162f7d969b1 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -10,6 +10,7 @@ #![feature(trusted_step)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![feature(if_let_guard)] #![recursion_limit = "256"] #[macro_use] @@ -27,10 +28,13 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{ - traversal, AnalysisPhase, Body, ConstQualifs, MirPass, MirPhase, Promoted, RuntimePhase, + traversal, AnalysisPhase, Body, ConstQualifs, Constant, LocalDecl, MirPass, MirPhase, Operand, + Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, Statement, StatementKind, + TerminatorKind, }; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; +use rustc_span::sym; #[macro_use] mod pass_manager; @@ -140,6 +144,64 @@ pub fn provide(providers: &mut Providers) { }; } +fn remap_mir_for_const_eval_select<'tcx>( + tcx: TyCtxt<'tcx>, + mut body: Body<'tcx>, + context: hir::Constness, +) -> Body<'tcx> { + for bb in body.basic_blocks.as_mut().iter_mut() { + let terminator = bb.terminator.as_mut().expect("invalid terminator"); + match terminator.kind { + TerminatorKind::Call { + func: Operand::Constant(box Constant { ref literal, .. }), + ref mut args, + destination, + target, + cleanup, + fn_span, + .. + } if let ty::FnDef(def_id, _) = *literal.ty().kind() + && tcx.item_name(def_id) == sym::const_eval_select + && tcx.is_intrinsic(def_id) => + { + let [tupled_args, called_in_const, called_at_rt]: [_; 3] = std::mem::take(args).try_into().unwrap(); + let ty = tupled_args.ty(&body.local_decls, tcx); + let fields = ty.tuple_fields(); + let num_args = fields.len(); + let func = if context == hir::Constness::Const { called_in_const } else { called_at_rt }; + let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = match tupled_args { + Operand::Constant(_) => { + // there is no good way of extracting a tuple arg from a constant (const generic stuff) + // so we just create a temporary and deconstruct that. + let local = body.local_decls.push(LocalDecl::new(ty, fn_span)); + bb.statements.push(Statement { + source_info: SourceInfo::outermost(fn_span), + kind: StatementKind::Assign(Box::new((local.into(), Rvalue::Use(tupled_args.clone())))), + }); + (Operand::Move, local.into()) + } + Operand::Move(place) => (Operand::Move, place), + Operand::Copy(place) => (Operand::Copy, place), + }; + let place_elems = place.projection; + let arguments = (0..num_args).map(|x| { + let mut place_elems = place_elems.to_vec(); + place_elems.push(ProjectionElem::Field(x.into(), fields[x])); + let projection = tcx.intern_place_elems(&place_elems); + let place = Place { + local: place.local, + projection, + }; + method(place) + }).collect(); + terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, cleanup, from_hir_call: false, fn_span }; + } + _ => {} + } + } + body +} + fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let def_id = def_id.expect_local(); tcx.mir_keys(()).contains(&def_id) @@ -325,7 +387,9 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) - .body_const_context(def.did) .expect("mir_for_ctfe should not be used for runtime functions"); - let mut body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone(); + let body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone(); + + let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const); match context { // Do not const prop functions, either they get executed at runtime or exported to metadata, @@ -558,8 +622,9 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other), } debug!("about to call mir_drops_elaborated..."); - let mut body = + let body = tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did)).steal(); + let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst); debug!("body: {:#?}", body); run_optimization_passes(tcx, &mut body); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 4e37da0dedb..be74a9d11e3 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -112,12 +112,6 @@ //! method in operand position, we treat it as a neighbor of the current //! mono item. Calls are just a special case of that. //! -//! #### Closures -//! In a way, closures are a simple case. Since every closure object needs to be -//! constructed somewhere, we can reliably discover them by observing -//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also -//! true for closures inlined from other crates. -//! //! #### Drop glue //! Drop glue mono items are introduced by MIR drop-statements. The //! generated mono item will again have drop-glue item neighbors if the @@ -835,7 +829,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { mir::TerminatorKind::Call { ref func, .. } => { let callee_ty = func.ty(self.body, tcx); let callee_ty = self.monomorphize(callee_ty); - visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output); + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output) } mir::TerminatorKind::Drop { ref place, .. } | mir::TerminatorKind::DropAndReplace { ref place, .. } => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c501fba21c6..75b1dfc856a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -510,7 +510,6 @@ symbols! { const_deallocate, const_eval_limit, const_eval_select, - const_eval_select_ct, const_evaluatable_checked, const_extern_fn, const_fn, @@ -662,7 +661,6 @@ symbols! { emit_struct, emit_struct_field, enable, - enclosing_scope, encode, end, env, @@ -1065,6 +1063,7 @@ symbols! { panic_unwind, panicking, param_attrs, + parent_label, partial_cmp, partial_ord, passes, 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 8151d2b365d..34b877d3f72 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -349,7 +349,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { message, label, note, - enclosing_scope, + parent_label, append_const_msg, } = self.on_unimplemented_note(trait_ref, &obligation); let have_alt_message = message.is_some() || label.is_some(); @@ -530,7 +530,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // If it has a custom `#[rustc_on_unimplemented]` note, let's display it err.note(s.as_str()); } - if let Some(ref s) = enclosing_scope { + if let Some(ref s) = parent_label { let body = tcx .hir() .opt_local_def_id(obligation.cause.body_id) @@ -539,11 +539,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { hir_id: obligation.cause.body_id, }) }); - - let enclosing_scope_span = - tcx.hir().span_with_body(tcx.hir().local_def_id_to_hir_id(body)); - - err.span_label(enclosing_scope_span, s); + err.span_label(tcx.def_span(body), s); } self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index 3d8840e9e74..4a4f34b7680 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -22,7 +22,7 @@ pub struct OnUnimplementedDirective { pub message: Option<OnUnimplementedFormatString>, pub label: Option<OnUnimplementedFormatString>, pub note: Option<OnUnimplementedFormatString>, - pub enclosing_scope: Option<OnUnimplementedFormatString>, + pub parent_label: Option<OnUnimplementedFormatString>, pub append_const_msg: Option<Option<Symbol>>, } @@ -31,7 +31,7 @@ pub struct OnUnimplementedNote { pub message: Option<String>, pub label: Option<String>, pub note: Option<String>, - pub enclosing_scope: Option<String>, + pub parent_label: Option<String>, /// Append a message for `~const Trait` errors. `None` means not requested and /// should fallback to a generic message, `Some(None)` suggests using the default /// appended message, `Some(Some(s))` suggests use the `s` message instead of the @@ -74,7 +74,7 @@ impl<'tcx> OnUnimplementedDirective { let mut message = None; let mut label = None; let mut note = None; - let mut enclosing_scope = None; + let mut parent_label = None; let mut subcommands = vec![]; let mut append_const_msg = None; @@ -94,9 +94,9 @@ impl<'tcx> OnUnimplementedDirective { note = parse_value(note_)?; continue; } - } else if item.has_name(sym::enclosing_scope) && enclosing_scope.is_none() { - if let Some(enclosing_scope_) = item.value_str() { - enclosing_scope = parse_value(enclosing_scope_)?; + } else if item.has_name(sym::parent_label) && parent_label.is_none() { + if let Some(parent_label_) = item.value_str() { + parent_label = parse_value(parent_label_)?; continue; } } else if item.has_name(sym::on) @@ -135,7 +135,7 @@ impl<'tcx> OnUnimplementedDirective { message, label, note, - enclosing_scope, + parent_label, append_const_msg, }) } @@ -160,7 +160,7 @@ impl<'tcx> OnUnimplementedDirective { attr.span, )?), note: None, - enclosing_scope: None, + parent_label: None, append_const_msg: None, })) } else { @@ -181,7 +181,7 @@ impl<'tcx> OnUnimplementedDirective { let mut message = None; let mut label = None; let mut note = None; - let mut enclosing_scope = None; + let mut parent_label = None; let mut append_const_msg = None; info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); @@ -217,8 +217,8 @@ impl<'tcx> OnUnimplementedDirective { note = Some(note_.clone()); } - if let Some(ref enclosing_scope_) = command.enclosing_scope { - enclosing_scope = Some(enclosing_scope_.clone()); + if let Some(ref parent_label_) = command.parent_label { + parent_label = Some(parent_label_.clone()); } append_const_msg = command.append_const_msg; @@ -228,7 +228,7 @@ impl<'tcx> OnUnimplementedDirective { label: label.map(|l| l.format(tcx, trait_ref, &options_map)), message: message.map(|m| m.format(tcx, trait_ref, &options_map)), note: note.map(|n| n.format(tcx, trait_ref, &options_map)), - enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), + parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), append_const_msg, } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index ce0e59d0622..d1d27db9131 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -31,7 +31,7 @@ use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span}; +use rustc_span::{self, sym, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; @@ -224,6 +224,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let minimum_input_count = expected_input_tys.len(); let provided_arg_count = provided_args.len(); + let is_const_eval_select = matches!(fn_def_id, Some(def_id) if + self.tcx.def_kind(def_id) == hir::def::DefKind::Fn + && self.tcx.is_intrinsic(def_id) + && self.tcx.item_name(def_id) == sym::const_eval_select); + // We introduce a helper function to demand that a given argument satisfy a given input // This is more complicated than just checking type equality, as arguments could be coerced // This version writes those types back so further type checking uses the narrowed types @@ -259,6 +264,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Compatibility::Incompatible(coerce_error); } + // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that + // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed + // in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here. + // + // This check is here because there is currently no way to express a trait bound for `FnDef` types only. + if is_const_eval_select && (1..=2).contains(&idx) { + if let ty::FnDef(def_id, _) = checked_ty.kind() { + if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) { + self.tcx + .sess + .struct_span_err(provided_arg.span, "this argument must be a `const fn`") + .help("consult the documentation on `const_eval_select` for more information") + .emit(); + } + } else { + self.tcx + .sess + .struct_span_err(provided_arg.span, "this argument must be a function item") + .note(format!("expected a function item, found {checked_ty}")) + .help( + "consult the documentation on `const_eval_select` for more information", + ) + .emit(); + } + } + // 3. Check if the formal type is a supertype of the checked one // and register any such obligations for future type checks let supertype_error = self diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 952086e898f..0d9dbb5bc11 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -57,9 +57,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .is_ok() { - // Suppress this error, since we already emitted - // a deref suggestion in check_overloaded_binop - err.downgrade_to_delayed_bug(); + // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have + // emitted a better suggestion during error handling in check_overloaded_binop. + if self + .lookup_op_method( + lhs_ty, + Some(rhs_ty), + Some(rhs), + Op::Binary(op, IsAssign::Yes), + expected, + ) + .is_err() + { + err.downgrade_to_delayed_bug(); + } else { + // Otherwise, it's valid to suggest dereferencing the LHS here. + err.span_suggestion_verbose( + lhs.span.shrink_to_lo(), + "consider dereferencing the left-hand side of this operation", + "*", + Applicability::MaybeIncorrect, + ); + } } } }); diff --git a/library/alloc/src/collections/btree/dedup_sorted_iter.rs b/library/alloc/src/collections/btree/dedup_sorted_iter.rs index 60bf83b8387..17ee78045a9 100644 --- a/library/alloc/src/collections/btree/dedup_sorted_iter.rs +++ b/library/alloc/src/collections/btree/dedup_sorted_iter.rs @@ -3,7 +3,9 @@ use core::iter::Peekable; /// A iterator for deduping the key of a sorted iterator. /// When encountering the duplicated key, only the last key-value pair is yielded. /// -/// Used by [`BTreeMap::bulk_build_from_sorted_iter`]. +/// Used by [`BTreeMap::bulk_build_from_sorted_iter`][1]. +/// +/// [1]: crate::collections::BTreeMap::bulk_build_from_sorted_iter pub struct DedupSortedIter<K, V, I> where I: Iterator<Item = (K, V)>, diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index ad6d19bbc68..e5cf9033c86 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -134,6 +134,7 @@ #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] #![feature(receiver_trait)] +#![feature(saturating_int_impl)] #![feature(set_ptr_value)] #![feature(slice_from_ptr_range)] #![feature(slice_group_by)] diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index 92a32779b8e..2e025c8a4a5 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -1,3 +1,5 @@ +use core::num::{Saturating, Wrapping}; + use crate::boxed::Box; #[rustc_specialization_trait] @@ -144,3 +146,17 @@ impl_is_zero_option_of_nonzero!( NonZeroUsize, NonZeroIsize, ); + +unsafe impl<T: IsZero> IsZero for Wrapping<T> { + #[inline] + fn is_zero(&self) -> bool { + self.0.is_zero() + } +} + +unsafe impl<T: IsZero> IsZero for Saturating<T> { + #[inline] + fn is_zero(&self) -> bool { + self.0.is_zero() + } +} diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 2433139a592..b7a63b7c675 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -746,10 +746,19 @@ impl char { /// assert!(!'ä¸'.is_lowercase()); /// assert!(!' '.is_lowercase()); /// ``` + /// + /// In a const context: + /// + /// ``` + /// #![feature(const_unicode_case_lookup)] + /// const CAPITAL_DELTA_IS_LOWERCASE: bool = 'Δ'.is_lowercase(); + /// assert!(!CAPITAL_DELTA_IS_LOWERCASE); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")] #[inline] - pub fn is_lowercase(self) -> bool { + pub const fn is_lowercase(self) -> bool { match self { 'a'..='z' => true, c => c > '\x7f' && unicode::Lowercase(c), @@ -779,10 +788,19 @@ impl char { /// assert!(!'ä¸'.is_uppercase()); /// assert!(!' '.is_uppercase()); /// ``` + /// + /// In a const context: + /// + /// ``` + /// #![feature(const_unicode_case_lookup)] + /// const CAPITAL_DELTA_IS_UPPERCASE: bool = 'Δ'.is_uppercase(); + /// assert!(CAPITAL_DELTA_IS_UPPERCASE); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")] #[inline] - pub fn is_uppercase(self) -> bool { + pub const fn is_uppercase(self) -> bool { match self { 'A'..='Z' => true, c => c > '\x7f' && unicode::Uppercase(c), diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index d610f0a02f4..56f5824efd4 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -54,7 +54,9 @@ )] #![allow(missing_docs)] -use crate::marker::{Destruct, DiscriminantKind}; +#[cfg(bootstrap)] +use crate::marker::Destruct; +use crate::marker::DiscriminantKind; use crate::mem; // These imports are used for simplifying intra-doc links @@ -2085,6 +2087,65 @@ extern "rust-intrinsic" { /// `ptr` must point to a vtable. /// The intrinsic will return the alignment stored in that vtable. pub fn vtable_align(ptr: *const ()) -> usize; + + /// Selects which function to call depending on the context. + /// + /// If this function is evaluated at compile-time, then a call to this + /// intrinsic will be replaced with a call to `called_in_const`. It gets + /// replaced with a call to `called_at_rt` otherwise. + /// + /// # Type Requirements + /// + /// The two functions must be both function items. They cannot be function + /// pointers or closures. The first function must be a `const fn`. + /// + /// `arg` will be the tupled arguments that will be passed to either one of + /// the two functions, therefore, both functions must accept the same type of + /// arguments. Both functions must return RET. + /// + /// # Safety + /// + /// The two functions must behave observably equivalent. Safe code in other + /// crates may assume that calling a `const fn` at compile-time and at run-time + /// produces the same result. A function that produces a different result when + /// evaluated at run-time, or has any other observable side-effects, is + /// *unsound*. + /// + /// Here is an example of how this could cause a problem: + /// ```no_run + /// #![feature(const_eval_select)] + /// #![feature(core_intrinsics)] + /// use std::hint::unreachable_unchecked; + /// use std::intrinsics::const_eval_select; + /// + /// // Crate A + /// pub const fn inconsistent() -> i32 { + /// fn runtime() -> i32 { 1 } + /// const fn compiletime() -> i32 { 2 } + /// + /// unsafe { + // // âš This code violates the required equivalence of `compiletime` + /// // and `runtime`. + /// const_eval_select((), compiletime, runtime) + /// } + /// } + /// + /// // Crate B + /// const X: i32 = inconsistent(); + /// let x = inconsistent(); + /// if x != X { unsafe { unreachable_unchecked(); }} + /// ``` + /// + /// This code causes Undefined Behavior when being run, since the + /// `unreachable_unchecked` is actually being reached. The bug is in *crate A*, + /// which violates the principle that a `const fn` must behave the same at + /// compile-time and at run-time. The unsafe code in crate B is fine. + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_eval_select", issue = "none")] + pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET + where + G: FnOnce<ARG, Output = RET>, + F: FnOnce<ARG, Output = RET>; } // Some functions are defined here because they accidentally got made @@ -2095,6 +2156,11 @@ extern "rust-intrinsic" { /// Check that the preconditions of an unsafe function are followed, if debug_assertions are on, /// and only at runtime. /// +/// This macro should be called as `assert_unsafe_precondition!([Generics](name: Type) => Expression)` +/// where the names specified will be moved into the macro as captured variables, and defines an item +/// to call `const_eval_select` on. The tokens inside the square brackets are used to denote generics +/// for the function declaractions and can be omitted if there is no generics. +/// /// # Safety /// /// Invoking this macro is only sound if the following code is already UB when the passed @@ -2109,18 +2175,21 @@ extern "rust-intrinsic" { /// the occasional mistake, and this check should help them figure things out. #[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn macro_rules! assert_unsafe_precondition { - ($e:expr) => { + ($([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => { if cfg!(debug_assertions) { - // Use a closure so that we can capture arbitrary expressions from the invocation - let runtime = || { + // allow non_snake_case to allow capturing const generics + #[allow(non_snake_case)] + #[inline(always)] + fn runtime$(<$($tt)*>)?($($i:$ty),*) { if !$e { // abort instead of panicking to reduce impact on code size ::core::intrinsics::abort(); } - }; - const fn comptime() {} + } + #[allow(non_snake_case)] + const fn comptime$(<$($tt)*>)?($(_:$ty),*) {} - ::core::intrinsics::const_eval_select((), comptime, runtime); + ::core::intrinsics::const_eval_select(($($i,)*), comptime, runtime); } }; } @@ -2243,7 +2312,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us // SAFETY: the safety contract for `copy_nonoverlapping` must be // upheld by the caller. unsafe { - assert_unsafe_precondition!( + assert_unsafe_precondition!([T](src: *const T, dst: *mut T, count: usize) => is_aligned_and_not_null(src) && is_aligned_and_not_null(dst) && is_nonoverlapping(src, dst, count) @@ -2329,7 +2398,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)); + assert_unsafe_precondition!([T](src: *const T, dst: *mut T) => + is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)); copy(src, dst, count) } } @@ -2397,63 +2467,12 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) { // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(dst)); + assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst)); write_bytes(dst, val, count) } } -/// Selects which function to call depending on the context. -/// -/// If this function is evaluated at compile-time, then a call to this -/// intrinsic will be replaced with a call to `called_in_const`. It gets -/// replaced with a call to `called_at_rt` otherwise. -/// -/// # Type Requirements -/// -/// The two functions must be both function items. They cannot be function -/// pointers or closures. -/// -/// `arg` will be the arguments that will be passed to either one of the -/// two functions, therefore, both functions must accept the same type of -/// arguments. Both functions must return RET. -/// -/// # Safety -/// -/// The two functions must behave observably equivalent. Safe code in other -/// crates may assume that calling a `const fn` at compile-time and at run-time -/// produces the same result. A function that produces a different result when -/// evaluated at run-time, or has any other observable side-effects, is -/// *unsound*. -/// -/// Here is an example of how this could cause a problem: -/// ```no_run -/// #![feature(const_eval_select)] -/// #![feature(core_intrinsics)] -/// use std::hint::unreachable_unchecked; -/// use std::intrinsics::const_eval_select; -/// -/// // Crate A -/// pub const fn inconsistent() -> i32 { -/// fn runtime() -> i32 { 1 } -/// const fn compiletime() -> i32 { 2 } -/// -/// unsafe { -// // âš This code violates the required equivalence of `compiletime` -/// // and `runtime`. -/// const_eval_select((), compiletime, runtime) -/// } -/// } -/// -/// // Crate B -/// const X: i32 = inconsistent(); -/// let x = inconsistent(); -/// if x != X { unsafe { unreachable_unchecked(); }} -/// ``` -/// -/// This code causes Undefined Behavior when being run, since the -/// `unreachable_unchecked` is actually being reached. The bug is in *crate A*, -/// which violates the principle that a `const fn` must behave the same at -/// compile-time and at run-time. The unsafe code in crate B is fine. +#[cfg(bootstrap)] #[unstable( feature = "const_eval_select", issue = "none", @@ -2475,6 +2494,7 @@ where called_at_rt.call_once(arg) } +#[cfg(bootstrap)] #[unstable( feature = "const_eval_select", issue = "none", diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 9d857eb63da..5b1e2045fff 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -143,6 +143,7 @@ #![feature(const_type_id)] #![feature(const_type_name)] #![feature(const_default_impls)] +#![feature(const_unicode_case_lookup)] #![feature(const_unsafecell_get_mut)] #![feature(core_panic)] #![feature(duration_consts_float)] diff --git a/library/core/src/mem/valid_align.rs b/library/core/src/mem/valid_align.rs index b9ccc0b4c79..32b2afb72b0 100644 --- a/library/core/src/mem/valid_align.rs +++ b/library/core/src/mem/valid_align.rs @@ -28,7 +28,7 @@ impl ValidAlign { #[inline] pub(crate) const unsafe fn new_unchecked(align: usize) -> Self { // SAFETY: Precondition passed to the caller. - unsafe { assert_unsafe_precondition!(align.is_power_of_two()) }; + unsafe { assert_unsafe_precondition!((align: usize) => align.is_power_of_two()) }; // SAFETY: By precondition, this must be a power of two, and // our variants encompass all possible powers of two. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index da41ea53635..c83b928efc1 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1033,10 +1033,14 @@ impl f32 { } } } - // SAFETY: `u32` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - let rt_f32_to_u32 = |rt| unsafe { mem::transmute::<f32, u32>(rt) }; + + #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 + fn rt_f32_to_u32(x: f32) -> u32 { + // SAFETY: `u32` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(x) } + } // SAFETY: We use internal implementations that either always work or fail at compile time. unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) } } @@ -1121,10 +1125,14 @@ impl f32 { } } } - // SAFETY: `u32` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - let rt_u32_to_f32 = |rt| unsafe { mem::transmute::<u32, f32>(rt) }; + + #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 + fn rt_u32_to_f32(x: u32) -> f32 { + // SAFETY: `u32` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(x) } + } // SAFETY: We use internal implementations that either always work or fail at compile time. unsafe { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 631d559df5f..28d23f733de 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1026,10 +1026,14 @@ impl f64 { } } } - // SAFETY: `u64` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - let rt_f64_to_u64 = |rt| unsafe { mem::transmute::<f64, u64>(rt) }; + + #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 + fn rt_f64_to_u64(rt: f64) -> u64 { + // SAFETY: `u64` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute::<f64, u64>(rt) } + } // SAFETY: We use internal implementations that either always work or fail at compile time. unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) } } @@ -1119,10 +1123,14 @@ impl f64 { } } } - // SAFETY: `u64` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - let rt_u64_to_f64 = |rt| unsafe { mem::transmute::<u64, f64>(rt) }; + + #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 + fn rt_u64_to_f64(rt: u64) -> f64 { + // SAFETY: `u64` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute::<u64, f64>(rt) } + } // SAFETY: We use internal implementations that either always work or fail at compile time. unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) } } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 1cf306f2103..532a09736a7 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -56,7 +56,7 @@ macro_rules! nonzero_integers { pub const unsafe fn new_unchecked(n: $Int) -> Self { // SAFETY: this is guaranteed to be safe by the caller. unsafe { - core::intrinsics::assert_unsafe_precondition!(n != 0); + core::intrinsics::assert_unsafe_precondition!((n: $Int) => n != 0); Self(n) } } diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 02f7f62bfe2..10f04134409 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -222,7 +222,87 @@ pub trait Try: FromResidual { /// Every `Try` type needs to be recreatable from its own associated /// `Residual` type, but can also have additional `FromResidual` implementations /// to support interconversion with other `Try` types. -#[rustc_on_unimplemented( +#[cfg_attr(not(bootstrap), rustc_on_unimplemented( + on( + all( + from_desugaring = "QuestionMark", + _Self = "std::result::Result<T, E>", + R = "std::option::Option<std::convert::Infallible>" + ), + message = "the `?` operator can only be used on `Result`s, not `Option`s, \ + in {ItemContext} that returns `Result`", + label = "use `.ok_or(...)?` to provide an error compatible with `{Self}`", + parent_label = "this function returns a `Result`" + ), + on( + all( + from_desugaring = "QuestionMark", + _Self = "std::result::Result<T, E>", + ), + // There's a special error message in the trait selection code for + // `From` in `?`, so this is not shown for result-in-result errors, + // and thus it can be phrased more strongly than `ControlFlow`'s. + message = "the `?` operator can only be used on `Result`s \ + in {ItemContext} that returns `Result`", + label = "this `?` produces `{R}`, which is incompatible with `{Self}`", + parent_label = "this function returns a `Result`" + ), + on( + all( + from_desugaring = "QuestionMark", + _Self = "std::option::Option<T>", + R = "std::result::Result<T, E>", + ), + message = "the `?` operator can only be used on `Option`s, not `Result`s, \ + in {ItemContext} that returns `Option`", + label = "use `.ok()?` if you want to discard the `{R}` error information", + parent_label = "this function returns an `Option`" + ), + on( + all( + from_desugaring = "QuestionMark", + _Self = "std::option::Option<T>", + ), + // `Option`-in-`Option` always works, as there's only one possible + // residual, so this can also be phrased strongly. + message = "the `?` operator can only be used on `Option`s \ + in {ItemContext} that returns `Option`", + label = "this `?` produces `{R}`, which is incompatible with `{Self}`", + parent_label = "this function returns an `Option`" + ), + on( + all( + from_desugaring = "QuestionMark", + _Self = "std::ops::ControlFlow<B, C>", + R = "std::ops::ControlFlow<B, C>", + ), + message = "the `?` operator in {ItemContext} that returns `ControlFlow<B, _>` \ + can only be used on other `ControlFlow<B, _>`s (with the same Break type)", + label = "this `?` produces `{R}`, which is incompatible with `{Self}`", + parent_label = "this function returns a `ControlFlow`", + note = "unlike `Result`, there's no `From`-conversion performed for `ControlFlow`" + ), + on( + all( + from_desugaring = "QuestionMark", + _Self = "std::ops::ControlFlow<B, C>", + // `R` is not a `ControlFlow`, as that case was matched previously + ), + message = "the `?` operator can only be used on `ControlFlow`s \ + in {ItemContext} that returns `ControlFlow`", + label = "this `?` produces `{R}`, which is incompatible with `{Self}`", + parent_label = "this function returns a `ControlFlow`", + ), + on( + all(from_desugaring = "QuestionMark"), + message = "the `?` operator can only be used in {ItemContext} \ + that returns `Result` or `Option` \ + (or another type that implements `{FromResidual}`)", + label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", + parent_label = "this function should return `Result` or `Option` to accept `?`" + ), +))] +#[cfg_attr(bootstrap, rustc_on_unimplemented( on( all( from_desugaring = "QuestionMark", @@ -301,7 +381,7 @@ pub trait Try: FromResidual { label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", enclosing_scope = "this function should return `Result` or `Option` to accept `?`" ), -)] +))] #[rustc_diagnostic_item = "FromResidual"] #[unstable(feature = "try_trait_v2", issue = "84277")] pub trait FromResidual<R = <Self as Try>::Residual> { diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index f5c72d79755..80bff74f3e9 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -755,9 +755,12 @@ impl<T: ?Sized> *const T { where T: Sized, { + let this = self; // SAFETY: The comparison has no side-effects, and the intrinsic // does this check internally in the CTFE implementation. - unsafe { assert_unsafe_precondition!(self >= origin) }; + unsafe { + assert_unsafe_precondition!([T](this: *const T, origin: *const T) => this >= origin) + }; let pointee_size = mem::size_of::<T>(); assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 41a2685d361..e976abed774 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -886,7 +886,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) { // SAFETY: the caller must guarantee that `x` and `y` are // valid for writes and properly aligned. unsafe { - assert_unsafe_precondition!( + assert_unsafe_precondition!([T](x: *mut T, y: *mut T, count: usize) => is_aligned_and_not_null(x) && is_aligned_and_not_null(y) && is_nonoverlapping(x, y, count) @@ -983,7 +983,7 @@ pub const unsafe fn replace<T>(dst: *mut T, mut src: T) -> T { // and cannot overlap `src` since `dst` must point to a distinct // allocated object. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(dst)); + assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst)); mem::swap(&mut *dst, &mut src); // cannot overlap } src @@ -1470,7 +1470,7 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) { pub unsafe fn read_volatile<T>(src: *const T) -> T { // SAFETY: the caller must uphold the safety contract for `volatile_load`. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(src)); + assert_unsafe_precondition!([T](src: *const T) => is_aligned_and_not_null(src)); intrinsics::volatile_load(src) } } @@ -1541,7 +1541,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T { pub unsafe fn write_volatile<T>(dst: *mut T, src: T) { // SAFETY: the caller must uphold the safety contract for `volatile_store`. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(dst)); + assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst)); intrinsics::volatile_store(dst, src); } } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index fd7ecf3daf3..3403a5a86f7 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -48,10 +48,12 @@ const fn slice_start_index_len_fail(index: usize, len: usize) -> ! { } // FIXME const-hack +#[track_caller] fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! { panic!("range start index {index} out of range for slice of length {len}"); } +#[track_caller] const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! { panic!("slice start index is out of range for slice"); } @@ -69,10 +71,12 @@ const fn slice_end_index_len_fail(index: usize, len: usize) -> ! { } // FIXME const-hack +#[track_caller] fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! { panic!("range end index {index} out of range for slice of length {len}"); } +#[track_caller] const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! { panic!("slice end index is out of range for slice"); } @@ -88,10 +92,12 @@ const fn slice_index_order_fail(index: usize, end: usize) -> ! { } // FIXME const-hack +#[track_caller] fn slice_index_order_fail_rt(index: usize, end: usize) -> ! { panic!("slice index starts at {index} but ends at {end}"); } +#[track_caller] const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! { panic!("slice index start is larger than end"); } @@ -217,21 +223,23 @@ unsafe impl<T> const SliceIndex<[T]> for usize { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { + let this = self; // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. unsafe { - assert_unsafe_precondition!(self < slice.len()); + assert_unsafe_precondition!([T](this: usize, slice: *const [T]) => this < slice.len()); slice.as_ptr().add(self) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { + let this = self; // SAFETY: see comments for `get_unchecked` above. unsafe { - assert_unsafe_precondition!(self < slice.len()); + assert_unsafe_precondition!([T](this: usize, slice: *mut [T]) => this < slice.len()); slice.as_mut_ptr().add(self) } } @@ -276,22 +284,26 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + let this = ops::Range { start: self.start, end: self.end }; // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. unsafe { - assert_unsafe_precondition!(self.end >= self.start && self.end <= slice.len()); + assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *const [T]) => + this.end >= this.start && this.end <= slice.len()); ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + let this = ops::Range { start: self.start, end: self.end }; // SAFETY: see comments for `get_unchecked` above. unsafe { - assert_unsafe_precondition!(self.end >= self.start && self.end <= slice.len()); + assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *mut [T]) => + this.end >= this.start && this.end <= slice.len()); ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start) } } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 1958745b586..6a7150d2986 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -656,10 +656,11 @@ impl<T> [T] { #[unstable(feature = "slice_swap_unchecked", issue = "88539")] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { - let ptr = self.as_mut_ptr(); + let this = self; + let ptr = this.as_mut_ptr(); // SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()` unsafe { - assert_unsafe_precondition!(a < self.len() && b < self.len()); + assert_unsafe_precondition!([T](a: usize, b: usize, this: &mut [T]) => a < this.len() && b < this.len()); ptr::swap(ptr.add(a), ptr.add(b)); } } @@ -972,9 +973,10 @@ impl<T> [T] { #[inline] #[must_use] pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] { + let this = self; // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { - assert_unsafe_precondition!(N != 0 && self.len() % N == 0); + assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0); exact_div(self.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into @@ -1111,10 +1113,11 @@ impl<T> [T] { #[inline] #[must_use] pub unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] { + let this = &*self; // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { - assert_unsafe_precondition!(N != 0 && self.len() % N == 0); - exact_div(self.len(), N) + assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0); + exact_div(this.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. @@ -1687,7 +1690,7 @@ impl<T> [T] { // `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference // is fine. unsafe { - assert_unsafe_precondition!(mid <= len); + assert_unsafe_precondition!((mid: usize, len: usize) => mid <= len); (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) } } diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 107e71ab68b..f1e8bc79bf4 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -90,7 +90,7 @@ use crate::ptr; pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { - assert_unsafe_precondition!( + assert_unsafe_precondition!([T](data: *const T, len: usize) => is_aligned_and_not_null(data) && crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize ); @@ -134,7 +134,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { - assert_unsafe_precondition!( + assert_unsafe_precondition!([T](data: *mut T, len: usize) => is_aligned_and_not_null(data) && crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize ); diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 2120bf61d75..f673aa2a44b 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -91,10 +91,12 @@ const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { } } +#[track_caller] const fn slice_error_fail_ct(_: &str, _: usize, _: usize) -> ! { panic!("failed to slice string"); } +#[track_caller] fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! { const MAX_DISPLAY_LENGTH: usize = 256; let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH); diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index c1eff3a36e6..7301da2afec 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -1,7 +1,8 @@ ///! This file is generated by src/tools/unicode-table-generator; do not edit manually! +#[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")] #[inline(always)] -fn bitset_search< +const fn bitset_search< const N: usize, const CHUNK_SIZE: usize, const N1: usize, @@ -17,14 +18,18 @@ fn bitset_search< let bucket_idx = (needle / 64) as usize; let chunk_map_idx = bucket_idx / CHUNK_SIZE; let chunk_piece = bucket_idx % CHUNK_SIZE; - let chunk_idx = if let Some(&v) = chunk_idx_map.get(chunk_map_idx) { - v + // FIXME: const-hack: Revert to `slice::get` after `const_slice_index` + // feature stabilizes. + let chunk_idx = if chunk_map_idx < chunk_idx_map.len() { + chunk_idx_map[chunk_map_idx] } else { return false; }; let idx = bitset_chunk_idx[chunk_idx as usize][chunk_piece] as usize; - let word = if let Some(word) = bitset_canonical.get(idx) { - *word + // FIXME: const-hack: Revert to `slice::get` after `const_slice_index` + // feature stabilizes. + let word = if idx < bitset_canonical.len() { + bitset_canonical[idx] } else { let (real_idx, mapping) = bitset_canonicalized[idx - bitset_canonical.len()]; let mut word = bitset_canonical[real_idx as usize]; @@ -318,14 +323,14 @@ pub mod grapheme_extend { #[rustfmt::skip] pub mod lowercase { - static BITSET_CHUNKS_MAP: [u8; 123] = [ + const BITSET_CHUNKS_MAP: &'static [u8; 123] = &[ 14, 17, 0, 0, 9, 0, 0, 12, 13, 10, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 15, 0, 8, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 3, 0, 0, 7, ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 19] = [ + const BITSET_INDEX_CHUNKS: &'static [[u8; 16]; 19] = &[ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 55, 0], @@ -346,7 +351,7 @@ pub mod lowercase { [16, 49, 2, 20, 66, 9, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0], [63, 39, 54, 12, 73, 61, 18, 1, 6, 62, 71, 19, 68, 69, 3, 44], ]; - static BITSET_CANONICAL: [u64; 55] = [ + const BITSET_CANONICAL: &'static [u64; 55] = &[ 0b0000000000000000000000000000000000000000000000000000000000000000, 0b1111111111111111110000000000000000000000000011111111111111111111, 0b1010101010101010101010101010101010101010101010101010100000000010, @@ -403,13 +408,14 @@ pub mod lowercase { 0b1110011111111111111111111111111111111111111111110000000000000000, 0b1110101111000000000000000000000000001111111111111111111111111100, ]; - static BITSET_MAPPING: [(u8, u8); 20] = [ + const BITSET_MAPPING: &'static [(u8, u8); 20] = &[ (0, 64), (1, 188), (1, 183), (1, 176), (1, 109), (1, 124), (1, 126), (1, 66), (1, 70), (1, 77), (2, 146), (2, 144), (2, 83), (3, 12), (3, 6), (4, 156), (4, 78), (5, 187), (6, 132), (7, 93), ]; - pub fn lookup(c: char) -> bool { + #[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")] + pub const fn lookup(c: char) -> bool { super::bitset_search( c as u32, &BITSET_CHUNKS_MAP, @@ -454,14 +460,14 @@ pub mod n { #[rustfmt::skip] pub mod uppercase { - static BITSET_CHUNKS_MAP: [u8; 125] = [ + const BITSET_CHUNKS_MAP: &'static [u8; 125] = &[ 12, 15, 6, 6, 0, 6, 6, 2, 4, 11, 6, 16, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 14, 6, 10, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, 6, 6, 9, 6, 3, ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 17] = [ + const BITSET_INDEX_CHUNKS: &'static [[u8; 16]; 17] = &[ [43, 43, 5, 34, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 5, 1], [43, 43, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43], [43, 43, 39, 43, 43, 43, 43, 43, 17, 17, 62, 17, 42, 29, 24, 23], @@ -480,7 +486,7 @@ pub mod uppercase { [57, 19, 2, 18, 10, 47, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43], [57, 37, 17, 27, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43], ]; - static BITSET_CANONICAL: [u64; 43] = [ + const BITSET_CANONICAL: &'static [u64; 43] = &[ 0b0000011111111111111111111111111000000000000000000000000000000000, 0b0000000000111111111111111111111111111111111111111111111111111111, 0b0101010101010101010101010101010101010101010101010101010000000001, @@ -525,13 +531,14 @@ pub mod uppercase { 0b1111011111111111000000000000000000000000000000000000000000000000, 0b1111111100000000111111110000000000111111000000001111111100000000, ]; - static BITSET_MAPPING: [(u8, u8); 25] = [ + const BITSET_MAPPING: &'static [(u8, u8); 25] = &[ (0, 187), (0, 177), (0, 171), (0, 167), (0, 164), (0, 32), (0, 47), (0, 51), (0, 121), (0, 117), (0, 109), (1, 150), (1, 148), (1, 142), (1, 134), (1, 131), (1, 64), (2, 164), (2, 146), (2, 20), (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), ]; - pub fn lookup(c: char) -> bool { + #[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")] + pub const fn lookup(c: char) -> bool { super::bitset_search( c as u32, &BITSET_CHUNKS_MAP, diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index a05e0db3af7..61c1ff578b2 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -68,7 +68,10 @@ pub use alloc_crate::alloc::*; /// The default memory allocator provided by the operating system. /// /// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows, -/// plus related functions. +/// plus related functions. However, it is not valid to mix use of the backing +/// system allocator with `System`, as this implementation may include extra +/// work, such as to serve alignment requests greater than the alignment +/// provided directly by the backing system allocator. /// /// This type implements the `GlobalAlloc` trait and Rust programs by default /// work as if they had this definition: diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 28a2c99f7e5..c6c78dc3939 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -19,7 +19,7 @@ use crate::sys::fs as fs_imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; -/// A reference to an open file on the filesystem. +/// An object providing access to an open file on the filesystem. /// /// An instance of a `File` can be read and/or written depending on what options /// it was opened with. Files also implement [`Seek`] to alter the logical cursor diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 07239746258..dd4ff4952fd 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -8,7 +8,6 @@ use crate::io::prelude::*; use crate::cell::{Cell, RefCell}; use crate::fmt; use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines}; -use crate::pin::Pin; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard, OnceLock}; use crate::sys::stdio; @@ -526,7 +525,7 @@ pub struct Stdout { // FIXME: this should be LineWriter or BufWriter depending on the state of // stdout (tty or not). Note that if this is not line buffered it // should also flush-on-panic or some form of flush-on-abort. - inner: Pin<&'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>, + inner: &'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>, } /// A locked reference to the [`Stdout`] handle. @@ -603,24 +602,20 @@ static STDOUT: OnceLock<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = OnceLo #[stable(feature = "rust1", since = "1.0.0")] pub fn stdout() -> Stdout { Stdout { - inner: Pin::static_ref(&STDOUT).get_or_init_pin( - || unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) }, - |mutex| unsafe { mutex.init() }, - ), + inner: STDOUT + .get_or_init(|| ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))), } } pub fn cleanup() { - if let Some(instance) = STDOUT.get() { - // Flush the data and disable buffering during shutdown - // by replacing the line writer by one with zero - // buffering capacity. - // We use try_lock() instead of lock(), because someone - // might have leaked a StdoutLock, which would - // otherwise cause a deadlock here. - if let Some(lock) = Pin::static_ref(instance).try_lock() { - *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); - } + // Flush the data and disable buffering during shutdown + // by replacing the line writer by one with zero + // buffering capacity. + // We use try_lock() instead of lock(), because someone + // might have leaked a StdoutLock, which would + // otherwise cause a deadlock here. + if let Some(lock) = STDOUT.get().and_then(ReentrantMutex::try_lock) { + *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); } } @@ -761,7 +756,7 @@ impl fmt::Debug for StdoutLock<'_> { /// standard library or via raw Windows API calls, will fail. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { - inner: Pin<&'static ReentrantMutex<RefCell<StderrRaw>>>, + inner: &'static ReentrantMutex<RefCell<StderrRaw>>, } /// A locked reference to the [`Stderr`] handle. @@ -834,16 +829,12 @@ pub struct StderrLock<'a> { #[stable(feature = "rust1", since = "1.0.0")] pub fn stderr() -> Stderr { // Note that unlike `stdout()` we don't use `at_exit` here to register a - // destructor. Stderr is not buffered , so there's no need to run a + // destructor. Stderr is not buffered, so there's no need to run a // destructor for flushing the buffer - static INSTANCE: OnceLock<ReentrantMutex<RefCell<StderrRaw>>> = OnceLock::new(); + static INSTANCE: ReentrantMutex<RefCell<StderrRaw>> = + ReentrantMutex::new(RefCell::new(stderr_raw())); - Stderr { - inner: Pin::static_ref(&INSTANCE).get_or_init_pin( - || unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) }, - |mutex| unsafe { mutex.init() }, - ), - } + Stderr { inner: &INSTANCE } } impl Stderr { diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 813516040cd..37413ec62a7 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -3,7 +3,6 @@ use crate::fmt; use crate::marker::PhantomData; use crate::mem::MaybeUninit; use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::pin::Pin; use crate::sync::Once; /// A synchronization primitive which can be written to only once. @@ -223,60 +222,6 @@ impl<T> OnceLock<T> { Ok(unsafe { self.get_unchecked() }) } - /// Internal-only API that gets the contents of the cell, initializing it - /// in two steps with `f` and `g` if the cell was empty. - /// - /// `f` is called to construct the value, which is then moved into the cell - /// and given as a (pinned) mutable reference to `g` to finish - /// initialization. - /// - /// This allows `g` to inspect an manipulate the value after it has been - /// moved into its final place in the cell, but before the cell is - /// considered initialized. - /// - /// # Panics - /// - /// If `f` or `g` panics, the panic is propagated to the caller, and the - /// cell remains uninitialized. - /// - /// With the current implementation, if `g` panics, the value from `f` will - /// not be dropped. This should probably be fixed if this is ever used for - /// a type where this matters. - /// - /// It is an error to reentrantly initialize the cell from `f`. The exact - /// outcome is unspecified. Current implementation deadlocks, but this may - /// be changed to a panic in the future. - pub(crate) fn get_or_init_pin<F, G>(self: Pin<&Self>, f: F, g: G) -> Pin<&T> - where - F: FnOnce() -> T, - G: FnOnce(Pin<&mut T>), - { - if let Some(value) = self.get_ref().get() { - // SAFETY: The inner value was already initialized, and will not be - // moved anymore. - return unsafe { Pin::new_unchecked(value) }; - } - - let slot = &self.value; - - // Ignore poisoning from other threads - // If another thread panics, then we'll be able to run our closure - self.once.call_once_force(|_| { - let value = f(); - // SAFETY: We use the Once (self.once) to guarantee unique access - // to the UnsafeCell (slot). - let value: &mut T = unsafe { (&mut *slot.get()).write(value) }; - // SAFETY: The value has been written to its final place in - // self.value. We do not to move it anymore, which we promise here - // with a Pin<&mut T>. - g(unsafe { Pin::new_unchecked(value) }); - }); - - // SAFETY: The inner value has been initialized, and will not be moved - // anymore. - unsafe { Pin::new_unchecked(self.get_ref().get_unchecked()) } - } - /// Consumes the `OnceLock`, returning the wrapped value. Returns /// `None` if the cell was empty. /// diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index eb15a04ffcf..e0184eb5baf 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -175,9 +175,6 @@ impl Mutex { } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn lock(&self) { loop { let mut guard = self.inner.lock(); diff --git a/library/std/src/sys/itron/mutex.rs b/library/std/src/sys/itron/mutex.rs index 715e94c3b3d..085662e6d44 100644 --- a/library/std/src/sys/itron/mutex.rs +++ b/library/std/src/sys/itron/mutex.rs @@ -31,12 +31,6 @@ impl Mutex { Mutex { mtx: SpinIdOnceCell::new() } } - pub unsafe fn init(&mut self) { - // Initialize `self.mtx` eagerly - let id = new_mtx().unwrap_or_else(|e| fail(e, &"acre_mtx")); - unsafe { self.mtx.set_unchecked((id, ())) }; - } - /// Get the inner mutex's ID, which is lazily created. fn raw(&self) -> abi::ID { match self.mtx.get_or_try_init(|| new_mtx().map(|id| (id, ()))) { diff --git a/library/std/src/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs index 513cd77fd2a..aa747d56b0d 100644 --- a/library/std/src/sys/sgx/mutex.rs +++ b/library/std/src/sys/sgx/mutex.rs @@ -21,9 +21,6 @@ impl Mutex { } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn lock(&self) { let mut guard = self.inner.lock(); if *guard.lock_var() { diff --git a/library/std/src/sys/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/unix/locks/fuchsia_mutex.rs index dbb9829bb66..117611ce43f 100644 --- a/library/std/src/sys/unix/locks/fuchsia_mutex.rs +++ b/library/std/src/sys/unix/locks/fuchsia_mutex.rs @@ -86,9 +86,6 @@ impl Mutex { } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn try_lock(&self) -> bool { let thread_self = zx_thread_self(); self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok() diff --git a/library/std/src/sys/unix/locks/futex_mutex.rs b/library/std/src/sys/unix/locks/futex_mutex.rs index 1b5be46c605..33b13dad4d6 100644 --- a/library/std/src/sys/unix/locks/futex_mutex.rs +++ b/library/std/src/sys/unix/locks/futex_mutex.rs @@ -20,9 +20,6 @@ impl Mutex { } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn try_lock(&self) -> bool { self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok() } diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs index 98afee69ba6..5964935ddb5 100644 --- a/library/std/src/sys/unix/locks/pthread_mutex.rs +++ b/library/std/src/sys/unix/locks/pthread_mutex.rs @@ -52,7 +52,7 @@ impl Mutex { Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } } #[inline] - pub unsafe fn init(&mut self) { + unsafe fn init(&mut self) { // Issue #33770 // // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have diff --git a/library/std/src/sys/unsupported/locks/mutex.rs b/library/std/src/sys/unsupported/locks/mutex.rs index d7cb12e0cf9..2be0b34b985 100644 --- a/library/std/src/sys/unsupported/locks/mutex.rs +++ b/library/std/src/sys/unsupported/locks/mutex.rs @@ -17,9 +17,6 @@ impl Mutex { } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn lock(&self) { assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex"); } diff --git a/library/std/src/sys/windows/locks/mutex.rs b/library/std/src/sys/windows/locks/mutex.rs index f91e8f9f59a..91207f5f466 100644 --- a/library/std/src/sys/windows/locks/mutex.rs +++ b/library/std/src/sys/windows/locks/mutex.rs @@ -37,8 +37,6 @@ impl Mutex { pub const fn new() -> Mutex { Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) } } - #[inline] - pub unsafe fn init(&mut self) {} #[inline] pub unsafe fn lock(&self) { diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs index 8921af311d4..b448ae3a997 100644 --- a/library/std/src/sys_common/remutex.rs +++ b/library/std/src/sys_common/remutex.rs @@ -1,13 +1,11 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; +use super::mutex as sys; use crate::cell::UnsafeCell; -use crate::marker::PhantomPinned; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::pin::Pin; use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; -use crate::sys::locks as sys; /// A re-entrant mutual exclusion /// @@ -41,11 +39,10 @@ use crate::sys::locks as sys; /// synchronization is left to the mutex, making relaxed memory ordering for /// the `owner` field fine in all cases. pub struct ReentrantMutex<T> { - mutex: sys::Mutex, + mutex: sys::MovableMutex, owner: AtomicUsize, lock_count: UnsafeCell<u32>, data: T, - _pinned: PhantomPinned, } unsafe impl<T: Send> Send for ReentrantMutex<T> {} @@ -68,39 +65,22 @@ impl<T> RefUnwindSafe for ReentrantMutex<T> {} /// guarded data. #[must_use = "if unused the ReentrantMutex will immediately unlock"] pub struct ReentrantMutexGuard<'a, T: 'a> { - lock: Pin<&'a ReentrantMutex<T>>, + lock: &'a ReentrantMutex<T>, } impl<T> !Send for ReentrantMutexGuard<'_, T> {} impl<T> ReentrantMutex<T> { /// Creates a new reentrant mutex in an unlocked state. - /// - /// # Unsafety - /// - /// This function is unsafe because it is required that `init` is called - /// once this mutex is in its final resting place, and only then are the - /// lock/unlock methods safe. - pub const unsafe fn new(t: T) -> ReentrantMutex<T> { + pub const fn new(t: T) -> ReentrantMutex<T> { ReentrantMutex { - mutex: sys::Mutex::new(), + mutex: sys::MovableMutex::new(), owner: AtomicUsize::new(0), lock_count: UnsafeCell::new(0), data: t, - _pinned: PhantomPinned, } } - /// Initializes this mutex so it's ready for use. - /// - /// # Unsafety - /// - /// Unsafe to call more than once, and must be called after this will no - /// longer move in memory. - pub unsafe fn init(self: Pin<&mut Self>) { - self.get_unchecked_mut().mutex.init() - } - /// Acquires a mutex, blocking the current thread until it is able to do so. /// /// This function will block the caller until it is available to acquire the mutex. @@ -113,15 +93,14 @@ impl<T> ReentrantMutex<T> { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> { + pub fn lock(&self) -> ReentrantMutexGuard<'_, T> { let this_thread = current_thread_unique_ptr(); - // Safety: We only touch lock_count when we own the lock, - // and since self is pinned we can safely call the lock() on the mutex. + // Safety: We only touch lock_count when we own the lock. unsafe { if self.owner.load(Relaxed) == this_thread { self.increment_lock_count(); } else { - self.mutex.lock(); + self.mutex.raw_lock(); self.owner.store(this_thread, Relaxed); debug_assert_eq!(*self.lock_count.get(), 0); *self.lock_count.get() = 1; @@ -142,10 +121,9 @@ impl<T> ReentrantMutex<T> { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn try_lock(self: Pin<&Self>) -> Option<ReentrantMutexGuard<'_, T>> { + pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, T>> { let this_thread = current_thread_unique_ptr(); - // Safety: We only touch lock_count when we own the lock, - // and since self is pinned we can safely call the try_lock on the mutex. + // Safety: We only touch lock_count when we own the lock. unsafe { if self.owner.load(Relaxed) == this_thread { self.increment_lock_count(); @@ -179,12 +157,12 @@ impl<T> Deref for ReentrantMutexGuard<'_, T> { impl<T> Drop for ReentrantMutexGuard<'_, T> { #[inline] fn drop(&mut self) { - // Safety: We own the lock, and the lock is pinned. + // Safety: We own the lock. unsafe { *self.lock.lock_count.get() -= 1; if *self.lock.lock_count.get() == 0 { self.lock.owner.store(0, Relaxed); - self.lock.mutex.unlock(); + self.lock.mutex.raw_unlock(); } } } diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs index 64873b850d3..8e97ce11c34 100644 --- a/library/std/src/sys_common/remutex/tests.rs +++ b/library/std/src/sys_common/remutex/tests.rs @@ -1,18 +1,11 @@ -use crate::boxed::Box; use crate::cell::RefCell; -use crate::pin::Pin; use crate::sync::Arc; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::thread; #[test] fn smoke() { - let m = unsafe { - let mut m = Box::pin(ReentrantMutex::new(())); - m.as_mut().init(); - m - }; - let m = m.as_ref(); + let m = ReentrantMutex::new(()); { let a = m.lock(); { @@ -29,20 +22,15 @@ fn smoke() { #[test] fn is_mutex() { - let m = unsafe { - // FIXME: Simplify this if Arc gets an Arc::get_pin_mut. - let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0))); - Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init(); - Pin::new_unchecked(m) - }; + let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); let m2 = m.clone(); - let lock = m.as_ref().lock(); + let lock = m.lock(); let child = thread::spawn(move || { - let lock = m2.as_ref().lock(); + let lock = m2.lock(); assert_eq!(*lock.borrow(), 4950); }); for i in 0..100 { - let lock = m.as_ref().lock(); + let lock = m.lock(); *lock.borrow_mut() += i; } drop(lock); @@ -51,22 +39,17 @@ fn is_mutex() { #[test] fn trylock_works() { - let m = unsafe { - // FIXME: Simplify this if Arc gets an Arc::get_pin_mut. - let mut m = Arc::new(ReentrantMutex::new(())); - Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init(); - Pin::new_unchecked(m) - }; + let m = Arc::new(ReentrantMutex::new(())); let m2 = m.clone(); - let _lock = m.as_ref().try_lock(); - let _lock2 = m.as_ref().try_lock(); + let _lock = m.try_lock(); + let _lock2 = m.try_lock(); thread::spawn(move || { - let lock = m2.as_ref().try_lock(); + let lock = m2.try_lock(); assert!(lock.is_none()); }) .join() .unwrap(); - let _lock3 = m.as_ref().try_lock(); + let _lock3 = m.try_lock(); } pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>); diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index c6723d91c8b..52a7776153d 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -45,7 +45,8 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py test --stage 2 src/tools/tidy && \ python3 ../x.py test --stage 0 core alloc std test proc_macro && \ - python3 ../x.py doc --stage 0 library/test && \ + # Build both public and internal documentation. + RUSTDOCFLAGS="--document-private-items" python3 ../x.py doc --stage 0 library/test && \ /scripts/validate-toolstate.sh && \ /scripts/validate-error-codes.sh && \ reuse lint && \ diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ebf6c55ee35..be2227f47af 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1786,7 +1786,10 @@ pub(crate) fn clean_visibility(vis: ty::Visibility) -> Visibility { pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocContext<'tcx>) -> Item { let kind = match variant.ctor_kind { - CtorKind::Const => Variant::CLike, + CtorKind::Const => Variant::CLike(match variant.discr { + ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }), + ty::VariantDiscr::Relative(_) => None, + }), CtorKind::Fn => Variant::Tuple( variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(), ), @@ -1803,6 +1806,7 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont fn clean_variant_data<'tcx>( variant: &hir::VariantData<'tcx>, + disr_expr: &Option<hir::AnonConst>, cx: &mut DocContext<'tcx>, ) -> Variant { match variant { @@ -1813,7 +1817,10 @@ fn clean_variant_data<'tcx>( hir::VariantData::Tuple(..) => { Variant::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect()) } - hir::VariantData::Unit(..) => Variant::CLike, + hir::VariantData::Unit(..) => Variant::CLike(disr_expr.map(|disr| Discriminant { + expr: Some(disr.body), + value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(), + })), } } @@ -1967,7 +1974,7 @@ fn clean_maybe_renamed_item<'tcx>( } fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item { - let kind = VariantItem(clean_variant_data(&variant.data, cx)); + let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx)); let what_rustc_thinks = Item::from_hir_id_and_parts(variant.id, Some(variant.ident.name), kind, cx); // don't show `pub` for variants, which are always public diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2808b400a0b..d6bb7c6c4fc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2098,7 +2098,7 @@ impl Enum { #[derive(Clone, Debug)] pub(crate) enum Variant { - CLike, + CLike(Option<Discriminant>), Tuple(Vec<Item>), Struct(VariantStruct), } @@ -2107,11 +2107,31 @@ impl Variant { pub(crate) fn has_stripped_entries(&self) -> Option<bool> { match *self { Self::Struct(ref struct_) => Some(struct_.has_stripped_entries()), - Self::CLike | Self::Tuple(_) => None, + Self::CLike(..) | Self::Tuple(_) => None, } } } +#[derive(Clone, Debug)] +pub(crate) struct Discriminant { + // In the case of cross crate re-exports, we don't have the nessesary information + // to reconstruct the expression of the discriminant, only the value. + pub(super) expr: Option<BodyId>, + pub(super) value: DefId, +} + +impl Discriminant { + /// Will be `None` in the case of cross-crate reexports, and may be + /// simplified + pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> { + self.expr.map(|body| print_const_expr(tcx, body)) + } + /// Will always be a machine readable number, without underscores or suffixes. + pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String { + print_evaluated_const(tcx, self.value, false).unwrap() + } +} + /// Small wrapper around [`rustc_span::Span`] that adds helper methods /// and enforces calling [`rustc_span::Span::source_callsite()`]. #[derive(Copy, Clone, Debug)] @@ -2338,7 +2358,7 @@ impl ConstantKind { match *self { ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None, ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => { - print_evaluated_const(tcx, def_id) + print_evaluated_const(tcx, def_id, true) } } } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index ac9ab339616..a9d511ae11e 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -261,7 +261,11 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { } } -pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> { +pub(crate) fn print_evaluated_const( + tcx: TyCtxt<'_>, + def_id: DefId, + underscores_and_type: bool, +) -> Option<String> { tcx.const_eval_poly(def_id).ok().and_then(|val| { let ty = tcx.type_of(def_id); match (val, ty.kind()) { @@ -269,7 +273,7 @@ pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<St (ConstValue::Scalar(_), &ty::Adt(_, _)) => None, (ConstValue::Scalar(_), _) => { let const_ = mir::ConstantKind::from_value(val, ty); - Some(print_const_with_custom_print_scalar(tcx, const_)) + Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type)) } _ => None, } @@ -302,23 +306,35 @@ fn format_integer_with_underscore_sep(num: &str) -> String { .collect() } -fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: mir::ConstantKind<'_>) -> String { +fn print_const_with_custom_print_scalar( + tcx: TyCtxt<'_>, + ct: mir::ConstantKind<'_>, + underscores_and_type: bool, +) -> String { // Use a slightly different format for integer types which always shows the actual value. // For all other types, fallback to the original `pretty_print_const`. match (ct, ct.ty().kind()) { (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Uint(ui)) => { - format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str()) + if underscores_and_type { + format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str()) + } else { + int.to_string() + } } (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => { let ty = tcx.lift(ct.ty()).unwrap(); let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; let data = int.assert_bits(size); let sign_extended_data = size.sign_extend(data) as i128; - format!( - "{}{}", - format_integer_with_underscore_sep(&sign_extended_data.to_string()), - i.name_str() - ) + if underscores_and_type { + format!( + "{}{}", + format_integer_with_underscore_sep(&sign_extended_data.to_string()), + i.name_str() + ) + } else { + sign_extended_data.to_string() + } } _ => ct.to_string(), } diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 6b7e67e2ce3..ed702f5c4a9 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -46,7 +46,7 @@ pub(crate) trait DocFolder: Sized { let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); VariantItem(Variant::Tuple(fields)) } - Variant::CLike => VariantItem(Variant::CLike), + Variant::CLike(disr) => VariantItem(Variant::CLike(disr)), }, ExternCrateItem { src: _ } | ImportItem(_) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index d63d4c2d159..cfa4509428f 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1203,7 +1203,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: let name = v.name.unwrap(); match *v.kind { clean::VariantItem(ref var) => match var { - clean::Variant::CLike => write!(w, "{}", name), + // FIXME(#101337): Show discriminant + clean::Variant::CLike(..) => write!(w, "{}", name), clean::Variant::Tuple(ref s) => { write!(w, "{}(", name); print_tuple_struct_fields(w, cx, s); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index fddff771f1c..3502d97d470 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1883,10 +1883,6 @@ in storage.js plus the media query with (min-width: 701px) background-color: var(--sidebar-background-color); } - .source nav:not(.sidebar).sub { - margin-left: 32px; - } - .content { margin-left: 0px; } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 5c0c023c64e..4d009316730 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -662,7 +662,7 @@ impl FromWithTcx<clean::Variant> for Variant { fn from_tcx(variant: clean::Variant, tcx: TyCtxt<'_>) -> Self { use clean::Variant::*; match variant { - CLike => Variant::Plain, + CLike(disr) => Variant::Plain(disr.map(|disr| disr.into_tcx(tcx))), Tuple(fields) => Variant::Tuple( fields .into_iter() @@ -678,6 +678,18 @@ impl FromWithTcx<clean::Variant> for Variant { } } +impl FromWithTcx<clean::Discriminant> for Discriminant { + fn from_tcx(disr: clean::Discriminant, tcx: TyCtxt<'_>) -> Self { + Discriminant { + // expr is only none if going throught the inlineing path, which gets + // `rustc_middle` types, not `rustc_hir`, but because JSON never inlines + // the expr is always some. + expr: disr.expr(tcx).unwrap(), + value: disr.value(tcx), + } + } +} + impl FromWithTcx<clean::Import> for Import { fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self { use clean::ImportKind::*; diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index 0bb41977c97..c40274394f3 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -20,7 +20,7 @@ pub(crate) trait DocVisitor: Sized { VariantItem(i) => match i { Variant::Struct(j) => j.fields.iter().for_each(|x| self.visit_item(x)), Variant::Tuple(fields) => fields.iter().for_each(|x| self.visit_item(x)), - Variant::CLike => {} + Variant::CLike(_) => {} }, ExternCrateItem { src: _ } | ImportItem(_) diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 7dcad66b1f9..d25f68b3743 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 18; +pub const FORMAT_VERSION: u32 = 19; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -308,12 +308,29 @@ pub struct Enum { #[serde(rename_all = "snake_case")] #[serde(tag = "variant_kind", content = "variant_inner")] pub enum Variant { - Plain, + Plain(Option<Discriminant>), Tuple(Vec<Type>), Struct(Vec<Id>), } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Discriminant { + /// The expression that produced the discriminant. + /// + /// Unlike `value`, this preserves the original formatting (eg suffixes, + /// hexadecimal, and underscores), making it unsuitable to be machine + /// interpreted. + /// + /// In some cases, when the value is to complex, this may be `"{ _ }"`. + /// When this occurs is unstable, and may change without notice. + pub expr: String, + /// The numerical value of the discriminant. Stored as a string due to + /// JSON's poor support for large integers, and the fact that it would need + /// to store from [`i128::MIN`] to [`u128::MAX`]. + pub value: String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum StructType { Plain, diff --git a/src/test/rustdoc-gui/search-input.goml b/src/test/rustdoc-gui/search-input.goml deleted file mode 100644 index fd61c4f43d1..00000000000 --- a/src/test/rustdoc-gui/search-input.goml +++ /dev/null @@ -1,22 +0,0 @@ -// Ensures that the search input border color changes on focus. -goto: file://|DOC_PATH|/test_docs/index.html -local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"} -reload: - -assert-css: (".search-input", {"border-color": "rgb(224, 224, 224)"}) -click: ".search-input" -assert-css: (".search-input", {"border-color": "rgb(0, 141, 253)"}) - -local-storage: {"rustdoc-theme": "light"} -reload: - -assert-css: (".search-input", {"border-color": "rgb(224, 224, 224)"}) -click: ".search-input" -assert-css: (".search-input", {"border-color": "rgb(102, 175, 233)"}) - -local-storage: {"rustdoc-theme": "ayu"} -reload: - -assert-css: (".search-input", {"border-color": "rgb(92, 103, 115)"}) -click: ".search-input" -assert-css: (".search-input", {"border-color": "rgb(92, 103, 115)"}) diff --git a/src/test/rustdoc-json/enums/discriminant/basic.rs b/src/test/rustdoc-json/enums/discriminant/basic.rs new file mode 100644 index 00000000000..8c221615aa7 --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/basic.rs @@ -0,0 +1,12 @@ +#[repr(i8)] +pub enum Ordering { + // @is "$.index[*][?(@.name=='Less')].inner.variant_inner.expr" '"-1"' + // @is "$.index[*][?(@.name=='Less')].inner.variant_inner.value" '"-1"' + Less = -1, + // @is "$.index[*][?(@.name=='Equal')].inner.variant_inner.expr" '"0"' + // @is "$.index[*][?(@.name=='Equal')].inner.variant_inner.value" '"0"' + Equal = 0, + // @is "$.index[*][?(@.name=='Greater')].inner.variant_inner.expr" '"1"' + // @is "$.index[*][?(@.name=='Greater')].inner.variant_inner.value" '"1"' + Greater = 1, +} diff --git a/src/test/rustdoc-json/enums/discriminant/expr.rs b/src/test/rustdoc-json/enums/discriminant/expr.rs new file mode 100644 index 00000000000..235b0b47381 --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/expr.rs @@ -0,0 +1,39 @@ +pub enum Foo { + // @is "$.index[*][?(@.name=='Addition')].inner.variant_inner.value" '"0"' + // @is "$.index[*][?(@.name=='Addition')].inner.variant_inner.expr" '"{ _ }"' + Addition = 0 + 0, + // @is "$.index[*][?(@.name=='Bin')].inner.variant_inner.value" '"1"' + // @is "$.index[*][?(@.name=='Bin')].inner.variant_inner.expr" '"0b1"' + Bin = 0b1, + // @is "$.index[*][?(@.name=='Oct')].inner.variant_inner.value" '"2"' + // @is "$.index[*][?(@.name=='Oct')].inner.variant_inner.expr" '"0o2"' + Oct = 0o2, + // @is "$.index[*][?(@.name=='PubConst')].inner.variant_inner.value" '"3"' + // @is "$.index[*][?(@.name=='PubConst')].inner.variant_inner.expr" '"THREE"' + PubConst = THREE, + // @is "$.index[*][?(@.name=='Hex')].inner.variant_inner.value" '"4"' + // @is "$.index[*][?(@.name=='Hex')].inner.variant_inner.expr" '"0x4"' + Hex = 0x4, + // @is "$.index[*][?(@.name=='Cast')].inner.variant_inner.value" '"5"' + // @is "$.index[*][?(@.name=='Cast')].inner.variant_inner.expr" '"{ _ }"' + Cast = 5 as isize, + // @is "$.index[*][?(@.name=='PubCall')].inner.variant_inner.value" '"6"' + // @is "$.index[*][?(@.name=='PubCall')].inner.variant_inner.expr" '"{ _ }"' + PubCall = six(), + // @is "$.index[*][?(@.name=='PrivCall')].inner.variant_inner.value" '"7"' + // @is "$.index[*][?(@.name=='PrivCall')].inner.variant_inner.expr" '"{ _ }"' + PrivCall = seven(), + // @is "$.index[*][?(@.name=='PrivConst')].inner.variant_inner.value" '"8"' + // @is "$.index[*][?(@.name=='PrivConst')].inner.variant_inner.expr" '"EIGHT"' + PrivConst = EIGHT, +} + +pub const THREE: isize = 3; +const EIGHT: isize = 8; + +pub const fn six() -> isize { + 6 +} +const fn seven() -> isize { + 7 +} diff --git a/src/test/rustdoc-json/enums/discriminant/limits.rs b/src/test/rustdoc-json/enums/discriminant/limits.rs new file mode 100644 index 00000000000..8df73d78d23 --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/limits.rs @@ -0,0 +1,43 @@ +// ignore-tidy-linelength +#![feature(repr128)] +#![allow(incomplete_features)] + +#[repr(u64)] +pub enum U64 { + // @is "$.index[*][?(@.name=='U64Min')].inner.variant_inner.value" '"0"' + // @is "$.index[*][?(@.name=='U64Min')].inner.variant_inner.expr" '"u64::MIN"' + U64Min = u64::MIN, + // @is "$.index[*][?(@.name=='U64Max')].inner.variant_inner.value" '"18446744073709551615"' + // @is "$.index[*][?(@.name=='U64Max')].inner.variant_inner.expr" '"u64::MAX"' + U64Max = u64::MAX, +} + +#[repr(i64)] +pub enum I64 { + // @is "$.index[*][?(@.name=='I64Min')].inner.variant_inner.value" '"-9223372036854775808"' + // @is "$.index[*][?(@.name=='I64Min')].inner.variant_inner.expr" '"i64::MIN"' + I64Min = i64::MIN, + // @is "$.index[*][?(@.name=='I64Max')].inner.variant_inner.value" '"9223372036854775807"' + // @is "$.index[*][?(@.name=='I64Max')].inner.variant_inner.expr" '"i64::MAX"' + I64Max = i64::MAX, +} + +#[repr(u128)] +pub enum U128 { + // @is "$.index[*][?(@.name=='U128Min')].inner.variant_inner.value" '"0"' + // @is "$.index[*][?(@.name=='U128Min')].inner.variant_inner.expr" '"u128::MIN"' + U128Min = u128::MIN, + // @is "$.index[*][?(@.name=='U128Max')].inner.variant_inner.value" '"340282366920938463463374607431768211455"' + // @is "$.index[*][?(@.name=='U128Max')].inner.variant_inner.expr" '"u128::MAX"' + U128Max = u128::MAX, +} + +#[repr(i128)] +pub enum I128 { + // @is "$.index[*][?(@.name=='I128Min')].inner.variant_inner.value" '"-170141183460469231731687303715884105728"' + // @is "$.index[*][?(@.name=='I128Min')].inner.variant_inner.expr" '"i128::MIN"' + I128Min = i128::MIN, + // @is "$.index[*][?(@.name=='I128Max')].inner.variant_inner.value" '"170141183460469231731687303715884105727"' + // @is "$.index[*][?(@.name=='I128Max')].inner.variant_inner.expr" '"i128::MAX"' + I128Max = i128::MAX, +} diff --git a/src/test/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs b/src/test/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs new file mode 100644 index 00000000000..3417baa0760 --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs @@ -0,0 +1,15 @@ +#[repr(u32)] +pub enum Foo { + // @is "$.index[*][?(@.name=='Basic')].inner.variant_inner.value" '"0"' + // @is "$.index[*][?(@.name=='Basic')].inner.variant_inner.expr" '"0"' + Basic = 0, + // @is "$.index[*][?(@.name=='Suffix')].inner.variant_inner.value" '"10"' + // @is "$.index[*][?(@.name=='Suffix')].inner.variant_inner.expr" '"10u32"' + Suffix = 10u32, + // @is "$.index[*][?(@.name=='Underscore')].inner.variant_inner.value" '"100"' + // @is "$.index[*][?(@.name=='Underscore')].inner.variant_inner.expr" '"1_0_0"' + Underscore = 1_0_0, + // @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.variant_inner.value" '"1000"' + // @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.variant_inner.expr" '"1_0_0_0u32"' + SuffixUnderscore = 1_0_0_0u32, +} diff --git a/src/test/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs b/src/test/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs new file mode 100644 index 00000000000..6af944a2219 --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs @@ -0,0 +1,10 @@ +pub enum Foo { + // @is "$.index[*][?(@.name=='Has')].inner.variant_inner" '{"expr":"0", "value":"0"}' + Has = 0, + // @is "$.index[*][?(@.name=='Doesnt')].inner.variant_inner" null + Doesnt, + // @is "$.index[*][?(@.name=='AlsoDoesnt')].inner.variant_inner" null + AlsoDoesnt, + // @is "$.index[*][?(@.name=='AlsoHas')].inner.variant_inner" '{"expr":"44", "value":"44"}' + AlsoHas = 44, +} diff --git a/src/test/ui/consts/const-float-bits-reject-conv.stderr b/src/test/ui/consts/const-float-bits-reject-conv.stderr index d6e993a1010..01f2f489564 100644 --- a/src/test/ui/consts/const-float-bits-reject-conv.stderr +++ b/src/test/ui/consts/const-float-bits-reject-conv.stderr @@ -10,16 +10,6 @@ LL | panic!("const-eval error: cannot use f32::to_bits on a LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) } | -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL | - ::: $SRC_DIR/core/src/ops/function.rs:LL:COL - | -LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL - | - ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL - | -LL | called_in_const.call_once(arg) - | ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | ::: $DIR/const-float-bits-reject-conv.rs:27:30 | LL | const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA; @@ -39,16 +29,6 @@ LL | panic!("const-eval error: cannot use f32::to_bits on a LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) } | -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL | - ::: $SRC_DIR/core/src/ops/function.rs:LL:COL - | -LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL - | - ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL - | -LL | called_in_const.call_once(arg) - | ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | ::: $DIR/const-float-bits-reject-conv.rs:28:30 | LL | const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555; @@ -117,16 +97,6 @@ LL | panic!("const-eval error: cannot use f64::to_bits on a LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) } | -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL | - ::: $SRC_DIR/core/src/ops/function.rs:LL:COL - | -LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL - | - ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL - | -LL | called_in_const.call_once(arg) - | ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | ::: $DIR/const-float-bits-reject-conv.rs:54:30 | LL | const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; @@ -146,16 +116,6 @@ LL | panic!("const-eval error: cannot use f64::to_bits on a LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) } | -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL | - ::: $SRC_DIR/core/src/ops/function.rs:LL:COL - | -LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL - | - ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL - | -LL | called_in_const.call_once(arg) - | ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | ::: $DIR/const-float-bits-reject-conv.rs:55:30 | LL | const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; diff --git a/src/test/ui/intrinsics/const-eval-select-backtrace-std.rs b/src/test/ui/intrinsics/const-eval-select-backtrace-std.rs new file mode 100644 index 00000000000..29aefe07162 --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-backtrace-std.rs @@ -0,0 +1,6 @@ +// See issue #100696. +// run-fail +// check-run-results +fn main() { + &""[1..]; +} diff --git a/src/test/ui/intrinsics/const-eval-select-backtrace-std.run.stderr b/src/test/ui/intrinsics/const-eval-select-backtrace-std.run.stderr new file mode 100644 index 00000000000..e53e6034620 --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-backtrace-std.run.stderr @@ -0,0 +1,2 @@ +thread 'main' panicked at 'byte index 1 is out of bounds of ``', $DIR/const-eval-select-backtrace-std.rs:5:6 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/test/ui/intrinsics/const-eval-select-backtrace.rs b/src/test/ui/intrinsics/const-eval-select-backtrace.rs new file mode 100644 index 00000000000..99f0725200c --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-backtrace.rs @@ -0,0 +1,18 @@ +#![feature(core_intrinsics)] +// See issue #100696. +// run-fail +// check-run-results + +#[track_caller] +fn uhoh() { + panic!("Aaah!") +} + +const fn c() {} + +fn main() { + // safety: this is unsound and just used to test + unsafe { + std::intrinsics::const_eval_select((), c, uhoh); + } +} diff --git a/src/test/ui/intrinsics/const-eval-select-backtrace.run.stderr b/src/test/ui/intrinsics/const-eval-select-backtrace.run.stderr new file mode 100644 index 00000000000..2fd730ac7a6 --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-backtrace.run.stderr @@ -0,0 +1,2 @@ +thread 'main' panicked at 'Aaah!', $DIR/const-eval-select-backtrace.rs:16:9 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/test/ui/intrinsics/const-eval-select-bad.rs b/src/test/ui/intrinsics/const-eval-select-bad.rs index e5640f5ab53..fa14efad7b4 100644 --- a/src/test/ui/intrinsics/const-eval-select-bad.rs +++ b/src/test/ui/intrinsics/const-eval-select-bad.rs @@ -5,10 +5,13 @@ use std::intrinsics::const_eval_select; const fn not_fn_items() { const_eval_select((), || {}, || {}); - //~^ ERROR the trait bound + //~^ ERROR this argument must be a function item + //~| ERROR this argument must be a function item const_eval_select((), 42, 0xDEADBEEF); - //~^ ERROR the trait bound + //~^ ERROR expected a `FnOnce<()>` closure //~| ERROR expected a `FnOnce<()>` closure + //~| ERROR this argument must be a function item + //~| ERROR this argument must be a function item } const fn foo(n: i32) -> i32 { @@ -35,4 +38,9 @@ const fn args_ty_mismatch() { //~^ ERROR type mismatch } +const fn non_const_fn() { + const_eval_select((1,), bar, bar); + //~^ ERROR this argument must be a `const fn` +} + fn main() {} diff --git a/src/test/ui/intrinsics/const-eval-select-bad.stderr b/src/test/ui/intrinsics/const-eval-select-bad.stderr index 904e83624b3..3720528ad4e 100644 --- a/src/test/ui/intrinsics/const-eval-select-bad.stderr +++ b/src/test/ui/intrinsics/const-eval-select-bad.stderr @@ -1,42 +1,57 @@ -error[E0277]: the trait bound `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]: FnOnce<()>` is not satisfied +error: this argument must be a function item --> $DIR/const-eval-select-bad.rs:7:27 | LL | const_eval_select((), || {}, || {}); - | ----------------- ^^^^^ expected an `FnOnce<()>` closure, found `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` - | | - | required by a bound introduced by this call + | ^^^^^ | - = help: the trait `~const FnOnce<()>` is not implemented for closure `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` -note: the trait `FnOnce<()>` is implemented for `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`, but that implementation is not `const` - --> $DIR/const-eval-select-bad.rs:7:27 + = note: expected a function item, found [closure@$DIR/const-eval-select-bad.rs:7:27: 7:29] + = help: consult the documentation on `const_eval_select` for more information + +error: this argument must be a function item + --> $DIR/const-eval-select-bad.rs:7:34 | LL | const_eval_select((), || {}, || {}); - | ^^^^^ - = note: wrap the `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` in a closure with no arguments: `|| { /* code */ }` -note: required by a bound in `const_eval_select` - --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + | ^^^^^ | -LL | F: ~const FnOnce<ARG, Output = RET>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` + = note: expected a function item, found [closure@$DIR/const-eval-select-bad.rs:7:34: 7:36] + = help: consult the documentation on `const_eval_select` for more information -error[E0277]: the trait bound `{integer}: FnOnce<()>` is not satisfied - --> $DIR/const-eval-select-bad.rs:9:27 +error: this argument must be a function item + --> $DIR/const-eval-select-bad.rs:10:27 + | +LL | const_eval_select((), 42, 0xDEADBEEF); + | ^^ + | + = note: expected a function item, found {integer} + = help: consult the documentation on `const_eval_select` for more information + +error[E0277]: expected a `FnOnce<()>` closure, found `{integer}` + --> $DIR/const-eval-select-bad.rs:10:27 | LL | const_eval_select((), 42, 0xDEADBEEF); | ----------------- ^^ expected an `FnOnce<()>` closure, found `{integer}` | | | required by a bound introduced by this call | - = help: the trait `~const FnOnce<()>` is not implemented for `{integer}` + = help: the trait `FnOnce<()>` is not implemented for `{integer}` = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | F: ~const FnOnce<ARG, Output = RET>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | F: FnOnce<ARG, Output = RET>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` + +error: this argument must be a function item + --> $DIR/const-eval-select-bad.rs:10:31 + | +LL | const_eval_select((), 42, 0xDEADBEEF); + | ^^^^^^^^^^ + | + = note: expected a function item, found {integer} + = help: consult the documentation on `const_eval_select` for more information error[E0277]: expected a `FnOnce<()>` closure, found `{integer}` - --> $DIR/const-eval-select-bad.rs:9:31 + --> $DIR/const-eval-select-bad.rs:10:31 | LL | const_eval_select((), 42, 0xDEADBEEF); | ----------------- ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `{integer}` @@ -48,11 +63,11 @@ LL | const_eval_select((), 42, 0xDEADBEEF); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | G: FnOnce<ARG, Output = RET> + ~const Destruct, - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | G: FnOnce<ARG, Output = RET>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` error[E0271]: expected `fn(i32) -> bool {bar}` to be a fn item that returns `i32`, but it returns `bool` - --> $DIR/const-eval-select-bad.rs:29:34 + --> $DIR/const-eval-select-bad.rs:32:34 | LL | const_eval_select((1,), foo, bar); | ----------------- ^^^ expected `i32`, found `bool` @@ -62,11 +77,11 @@ LL | const_eval_select((1,), foo, bar); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | G: FnOnce<ARG, Output = RET> + ~const Destruct, - | ^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | G: FnOnce<ARG, Output = RET>, + | ^^^^^^^^^^^^ required by this bound in `const_eval_select` error[E0631]: type mismatch in function arguments - --> $DIR/const-eval-select-bad.rs:34:32 + --> $DIR/const-eval-select-bad.rs:37:32 | LL | const fn foo(n: i32) -> i32 { | --------------------------- found signature defined here @@ -81,10 +96,18 @@ LL | const_eval_select((true,), foo, baz); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | F: ~const FnOnce<ARG, Output = RET>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | F: FnOnce<ARG, Output = RET>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` + +error: this argument must be a `const fn` + --> $DIR/const-eval-select-bad.rs:42:29 + | +LL | const_eval_select((1,), bar, bar); + | ^^^ + | + = help: consult the documentation on `const_eval_select` for more information -error: aborting due to 5 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0271, E0277, E0631. For more information about an error, try `rustc --explain E0271`. diff --git a/src/test/ui/on-unimplemented/enclosing-scope.rs b/src/test/ui/on-unimplemented/enclosing-scope.rs deleted file mode 100644 index 881bff63f5f..00000000000 --- a/src/test/ui/on-unimplemented/enclosing-scope.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Test scope annotations from `enclosing_scope` parameter - -#![feature(rustc_attrs)] - -#[rustc_on_unimplemented(enclosing_scope="in this scope")] -trait Trait{} - -struct Foo; - -fn f<T: Trait>(x: T) {} - -fn main() { - let x = || { - f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied - let y = || { - f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied - }; - }; - - { - { - f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied - } - } - - f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied -} diff --git a/src/test/ui/on-unimplemented/enclosing-scope.stderr b/src/test/ui/on-unimplemented/enclosing-scope.stderr deleted file mode 100644 index 67759d02a16..00000000000 --- a/src/test/ui/on-unimplemented/enclosing-scope.stderr +++ /dev/null @@ -1,86 +0,0 @@ -error[E0277]: the trait bound `Foo: Trait` is not satisfied - --> $DIR/enclosing-scope.rs:14:11 - | -LL | let x = || { - | _____________- -LL | | f(Foo{}); - | | - ^^^^^ the trait `Trait` is not implemented for `Foo` - | | | - | | required by a bound introduced by this call -LL | | let y = || { -LL | | f(Foo{}); -LL | | }; -LL | | }; - | |_____- in this scope - | -note: required by a bound in `f` - --> $DIR/enclosing-scope.rs:10:9 - | -LL | fn f<T: Trait>(x: T) {} - | ^^^^^ required by this bound in `f` - -error[E0277]: the trait bound `Foo: Trait` is not satisfied - --> $DIR/enclosing-scope.rs:16:15 - | -LL | let y = || { - | _________________- -LL | | f(Foo{}); - | | - ^^^^^ the trait `Trait` is not implemented for `Foo` - | | | - | | required by a bound introduced by this call -LL | | }; - | |_________- in this scope - | -note: required by a bound in `f` - --> $DIR/enclosing-scope.rs:10:9 - | -LL | fn f<T: Trait>(x: T) {} - | ^^^^^ required by this bound in `f` - -error[E0277]: the trait bound `Foo: Trait` is not satisfied - --> $DIR/enclosing-scope.rs:22:15 - | -LL | / fn main() { -LL | | let x = || { -LL | | f(Foo{}); -LL | | let y = || { -... | -LL | | f(Foo{}); - | | - ^^^^^ the trait `Trait` is not implemented for `Foo` - | | | - | | required by a bound introduced by this call -... | -LL | | f(Foo{}); -LL | | } - | |_- in this scope - | -note: required by a bound in `f` - --> $DIR/enclosing-scope.rs:10:9 - | -LL | fn f<T: Trait>(x: T) {} - | ^^^^^ required by this bound in `f` - -error[E0277]: the trait bound `Foo: Trait` is not satisfied - --> $DIR/enclosing-scope.rs:26:7 - | -LL | / fn main() { -LL | | let x = || { -LL | | f(Foo{}); -LL | | let y = || { -... | -LL | | f(Foo{}); - | | - ^^^^^ the trait `Trait` is not implemented for `Foo` - | | | - | | required by a bound introduced by this call -LL | | } - | |_- in this scope - | -note: required by a bound in `f` - --> $DIR/enclosing-scope.rs:10:9 - | -LL | fn f<T: Trait>(x: T) {} - | ^^^^^ required by this bound in `f` - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/on-unimplemented/parent-label.rs b/src/test/ui/on-unimplemented/parent-label.rs new file mode 100644 index 00000000000..b65f6496831 --- /dev/null +++ b/src/test/ui/on-unimplemented/parent-label.rs @@ -0,0 +1,27 @@ +// Test scope annotations from `parent_label` parameter + +#![feature(rustc_attrs)] + +#[rustc_on_unimplemented(parent_label = "in this scope")] +trait Trait {} + +struct Foo; + +fn f<T: Trait>(x: T) {} + +fn main() { + let x = || { + f(Foo {}); //~ ERROR the trait bound `Foo: Trait` is not satisfied + let y = || { + f(Foo {}); //~ ERROR the trait bound `Foo: Trait` is not satisfied + }; + }; + + { + { + f(Foo {}); //~ ERROR the trait bound `Foo: Trait` is not satisfied + } + } + + f(Foo {}); //~ ERROR the trait bound `Foo: Trait` is not satisfied +} diff --git a/src/test/ui/on-unimplemented/parent-label.stderr b/src/test/ui/on-unimplemented/parent-label.stderr new file mode 100644 index 00000000000..8cd7412fd9d --- /dev/null +++ b/src/test/ui/on-unimplemented/parent-label.stderr @@ -0,0 +1,69 @@ +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/parent-label.rs:14:11 + | +LL | let x = || { + | -- in this scope +LL | f(Foo {}); + | - ^^^^^^ the trait `Trait` is not implemented for `Foo` + | | + | required by a bound introduced by this call + | +note: required by a bound in `f` + --> $DIR/parent-label.rs:10:9 + | +LL | fn f<T: Trait>(x: T) {} + | ^^^^^ required by this bound in `f` + +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/parent-label.rs:16:15 + | +LL | let y = || { + | -- in this scope +LL | f(Foo {}); + | - ^^^^^^ the trait `Trait` is not implemented for `Foo` + | | + | required by a bound introduced by this call + | +note: required by a bound in `f` + --> $DIR/parent-label.rs:10:9 + | +LL | fn f<T: Trait>(x: T) {} + | ^^^^^ required by this bound in `f` + +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/parent-label.rs:22:15 + | +LL | fn main() { + | --------- in this scope +... +LL | f(Foo {}); + | - ^^^^^^ the trait `Trait` is not implemented for `Foo` + | | + | required by a bound introduced by this call + | +note: required by a bound in `f` + --> $DIR/parent-label.rs:10:9 + | +LL | fn f<T: Trait>(x: T) {} + | ^^^^^ required by this bound in `f` + +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/parent-label.rs:26:7 + | +LL | fn main() { + | --------- in this scope +... +LL | f(Foo {}); + | - ^^^^^^ the trait `Trait` is not implemented for `Foo` + | | + | required by a bound introduced by this call + | +note: required by a bound in `f` + --> $DIR/parent-label.rs:10:9 + | +LL | fn f<T: Trait>(x: T) {} + | ^^^^^ required by this bound in `f` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index fce0cdfe0d5..bc06fde49e9 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -1493,17 +1493,11 @@ LL | if (let 0 = 0)? {} error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/disallowed-positions.rs:132:19 | -LL | / fn nested_within_if_expr() { -LL | | if &let 0 = 0 {} -LL | | -LL | | -... | -LL | | if (let 0 = 0)? {} - | | ^ cannot use the `?` operator in a function that returns `()` -... | -LL | | -LL | | } - | |_- this function should return `Result` or `Option` to accept `?` +LL | fn nested_within_if_expr() { + | -------------------------- this function should return `Result` or `Option` to accept `?` +... +LL | if (let 0 = 0)? {} + | ^ cannot use the `?` operator in a function that returns `()` | = help: the trait `FromResidual<_>` is not implemented for `()` @@ -1693,17 +1687,11 @@ LL | while (let 0 = 0)? {} error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/disallowed-positions.rs:224:22 | -LL | / fn nested_within_while_expr() { -LL | | while &let 0 = 0 {} -LL | | -LL | | -... | -LL | | while (let 0 = 0)? {} - | | ^ cannot use the `?` operator in a function that returns `()` -... | -LL | | -LL | | } - | |_- this function should return `Result` or `Option` to accept `?` +LL | fn nested_within_while_expr() { + | ----------------------------- this function should return `Result` or `Option` to accept `?` +... +LL | while (let 0 = 0)? {} + | ^ cannot use the `?` operator in a function that returns `()` | = help: the trait `FromResidual<_>` is not implemented for `()` @@ -1881,17 +1869,11 @@ LL | (let 0 = 0)?; error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/disallowed-positions.rs:325:16 | -LL | / fn outside_if_and_while_expr() { -LL | | &let 0 = 0; -LL | | -LL | | -... | -LL | | (let 0 = 0)?; - | | ^ cannot use the `?` operator in a function that returns `()` -... | -LL | | -LL | | } - | |_- this function should return `Result` or `Option` to accept `?` +LL | fn outside_if_and_while_expr() { + | ------------------------------ this function should return `Result` or `Option` to accept `?` +... +LL | (let 0 = 0)?; + | ^ cannot use the `?` operator in a function that returns `()` | = help: the trait `FromResidual<_>` is not implemented for `()` diff --git a/src/test/ui/try-trait/bad-interconversion.stderr b/src/test/ui/try-trait/bad-interconversion.stderr index 529f5f16c8f..27e6a603acd 100644 --- a/src/test/ui/try-trait/bad-interconversion.stderr +++ b/src/test/ui/try-trait/bad-interconversion.stderr @@ -22,13 +22,10 @@ LL | Ok(Err(123_i32)?) error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result` --> $DIR/bad-interconversion.rs:11:12 | -LL | / fn option_to_result() -> Result<u64, String> { -LL | | Some(3)?; - | | ^ use `.ok_or(...)?` to provide an error compatible with `Result<u64, String>` -LL | | -LL | | Ok(10) -LL | | } - | |_- this function returns a `Result` +LL | fn option_to_result() -> Result<u64, String> { + | -------------------------------------------- this function returns a `Result` +LL | Some(3)?; + | ^ use `.ok_or(...)?` to provide an error compatible with `Result<u64, String>` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u64, String>` = help: the following other types implement trait `FromResidual<R>`: @@ -38,12 +35,10 @@ LL | | } error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result` --> $DIR/bad-interconversion.rs:17:31 | -LL | / fn control_flow_to_result() -> Result<u64, String> { -LL | | Ok(ControlFlow::Break(123)?) - | | ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Result<u64, String>` -LL | | -LL | | } - | |_- this function returns a `Result` +LL | fn control_flow_to_result() -> Result<u64, String> { + | -------------------------------------------------- this function returns a `Result` +LL | Ok(ControlFlow::Break(123)?) + | ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Result<u64, String>` | = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Result<u64, String>` = help: the following other types implement trait `FromResidual<R>`: @@ -53,12 +48,10 @@ LL | | } error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` --> $DIR/bad-interconversion.rs:22:22 | -LL | / fn result_to_option() -> Option<u16> { -LL | | Some(Err("hello")?) - | | ^ use `.ok()?` if you want to discard the `Result<Infallible, &str>` error information -LL | | -LL | | } - | |_- this function returns an `Option` +LL | fn result_to_option() -> Option<u16> { + | ------------------------------------ this function returns an `Option` +LL | Some(Err("hello")?) + | ^ use `.ok()?` if you want to discard the `Result<Infallible, &str>` error information | = help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `Option<u16>` = help: the following other types implement trait `FromResidual<R>`: @@ -68,12 +61,10 @@ LL | | } error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option` --> $DIR/bad-interconversion.rs:27:33 | -LL | / fn control_flow_to_option() -> Option<u64> { -LL | | Some(ControlFlow::Break(123)?) - | | ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Option<u64>` -LL | | -LL | | } - | |_- this function returns an `Option` +LL | fn control_flow_to_option() -> Option<u64> { + | ------------------------------------------ this function returns an `Option` +LL | Some(ControlFlow::Break(123)?) + | ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Option<u64>` | = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Option<u64>` = help: the following other types implement trait `FromResidual<R>`: @@ -83,12 +74,10 @@ LL | | } error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow` --> $DIR/bad-interconversion.rs:32:39 | -LL | / fn result_to_control_flow() -> ControlFlow<String> { -LL | | ControlFlow::Continue(Err("hello")?) - | | ^ this `?` produces `Result<Infallible, &str>`, which is incompatible with `ControlFlow<String>` -LL | | -LL | | } - | |_- this function returns a `ControlFlow` +LL | fn result_to_control_flow() -> ControlFlow<String> { + | -------------------------------------------------- this function returns a `ControlFlow` +LL | ControlFlow::Continue(Err("hello")?) + | ^ this `?` produces `Result<Infallible, &str>`, which is incompatible with `ControlFlow<String>` | = help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `ControlFlow<String>` = help: the trait `FromResidual` is implemented for `ControlFlow<B, C>` @@ -96,13 +85,10 @@ LL | | } error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow` --> $DIR/bad-interconversion.rs:37:12 | -LL | / fn option_to_control_flow() -> ControlFlow<u64> { -LL | | Some(3)?; - | | ^ this `?` produces `Option<Infallible>`, which is incompatible with `ControlFlow<u64>` -LL | | -LL | | ControlFlow::Break(10) -LL | | } - | |_- this function returns a `ControlFlow` +LL | fn option_to_control_flow() -> ControlFlow<u64> { + | ----------------------------------------------- this function returns a `ControlFlow` +LL | Some(3)?; + | ^ this `?` produces `Option<Infallible>`, which is incompatible with `ControlFlow<u64>` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `ControlFlow<u64>` = help: the trait `FromResidual` is implemented for `ControlFlow<B, C>` @@ -110,13 +96,10 @@ LL | | } error[E0277]: the `?` operator in a function that returns `ControlFlow<B, _>` can only be used on other `ControlFlow<B, _>`s (with the same Break type) --> $DIR/bad-interconversion.rs:43:29 | -LL | / fn control_flow_to_control_flow() -> ControlFlow<i64> { -LL | | ControlFlow::Break(4_u8)?; - | | ^ this `?` produces `ControlFlow<u8, Infallible>`, which is incompatible with `ControlFlow<i64>` -LL | | -LL | | ControlFlow::Continue(()) -LL | | } - | |_- this function returns a `ControlFlow` +LL | fn control_flow_to_control_flow() -> ControlFlow<i64> { + | ----------------------------------------------------- this function returns a `ControlFlow` +LL | ControlFlow::Break(4_u8)?; + | ^ this `?` produces `ControlFlow<u8, Infallible>`, which is incompatible with `ControlFlow<i64>` | = help: the trait `FromResidual<ControlFlow<u8, Infallible>>` is not implemented for `ControlFlow<i64>` = note: unlike `Result`, there's no `From`-conversion performed for `ControlFlow` diff --git a/src/test/ui/try-trait/option-to-result.stderr b/src/test/ui/try-trait/option-to-result.stderr index ae5c3ad6282..fabc1ff2c76 100644 --- a/src/test/ui/try-trait/option-to-result.stderr +++ b/src/test/ui/try-trait/option-to-result.stderr @@ -1,13 +1,11 @@ error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result` --> $DIR/option-to-result.rs:5:6 | -LL | / fn test_result() -> Result<(),()> { -LL | | let a:Option<()> = Some(()); -LL | | a?; - | | ^ use `.ok_or(...)?` to provide an error compatible with `Result<(), ()>` -LL | | Ok(()) -LL | | } - | |_- this function returns a `Result` +LL | fn test_result() -> Result<(),()> { + | --------------------------------- this function returns a `Result` +LL | let a:Option<()> = Some(()); +LL | a?; + | ^ use `.ok_or(...)?` to provide an error compatible with `Result<(), ()>` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<(), ()>` = help: the following other types implement trait `FromResidual<R>`: @@ -17,13 +15,11 @@ LL | | } error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` --> $DIR/option-to-result.rs:11:6 | -LL | / fn test_option() -> Option<i32>{ -LL | | let a:Result<i32, i32> = Ok(5); -LL | | a?; - | | ^ use `.ok()?` if you want to discard the `Result<Infallible, i32>` error information -LL | | Some(5) -LL | | } - | |_- this function returns an `Option` +LL | fn test_option() -> Option<i32>{ + | ------------------------------- this function returns an `Option` +LL | let a:Result<i32, i32> = Ok(5); +LL | a?; + | ^ use `.ok()?` if you want to discard the `Result<Infallible, i32>` error information | = help: the trait `FromResidual<Result<Infallible, i32>>` is not implemented for `Option<i32>` = help: the following other types implement trait `FromResidual<R>`: diff --git a/src/test/ui/try-trait/try-on-option-diagnostics.stderr b/src/test/ui/try-trait/try-on-option-diagnostics.stderr index a6badd19038..9ee540c79fd 100644 --- a/src/test/ui/try-trait/try-on-option-diagnostics.stderr +++ b/src/test/ui/try-trait/try-on-option-diagnostics.stderr @@ -1,51 +1,44 @@ error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-diagnostics.rs:7:6 | -LL | / fn a_function() -> u32 { -LL | | let x: Option<u32> = None; -LL | | x?; - | | ^ cannot use the `?` operator in a function that returns `u32` -LL | | 22 -LL | | } - | |_- this function should return `Result` or `Option` to accept `?` +LL | fn a_function() -> u32 { + | ---------------------- this function should return `Result` or `Option` to accept `?` +LL | let x: Option<u32> = None; +LL | x?; + | ^ cannot use the `?` operator in a function that returns `u32` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32` error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-diagnostics.rs:14:10 | -LL | let a_closure = || { - | _____________________- -LL | | let x: Option<u32> = None; -LL | | x?; - | | ^ cannot use the `?` operator in a closure that returns `{integer}` -LL | | 22 -LL | | }; - | |_____- this function should return `Result` or `Option` to accept `?` +LL | let a_closure = || { + | -- this function should return `Result` or `Option` to accept `?` +LL | let x: Option<u32> = None; +LL | x?; + | ^ cannot use the `?` operator in a closure that returns `{integer}` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `{integer}` error[E0277]: the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-diagnostics.rs:26:14 | -LL | / fn a_method() { -LL | | let x: Option<u32> = None; -LL | | x?; - | | ^ cannot use the `?` operator in a method that returns `()` -LL | | } - | |_________- this function should return `Result` or `Option` to accept `?` +LL | fn a_method() { + | ------------- this function should return `Result` or `Option` to accept `?` +LL | let x: Option<u32> = None; +LL | x?; + | ^ cannot use the `?` operator in a method that returns `()` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `()` error[E0277]: the `?` operator can only be used in a trait method that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-diagnostics.rs:39:14 | -LL | / fn a_trait_method() { -LL | | let x: Option<u32> = None; -LL | | x?; - | | ^ cannot use the `?` operator in a trait method that returns `()` -LL | | } - | |_________- this function should return `Result` or `Option` to accept `?` +LL | fn a_trait_method() { + | ------------------- this function should return `Result` or `Option` to accept `?` +LL | let x: Option<u32> = None; +LL | x?; + | ^ cannot use the `?` operator in a trait method that returns `()` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `()` diff --git a/src/test/ui/try-trait/try-on-option.stderr b/src/test/ui/try-trait/try-on-option.stderr index ba85a7cada2..fad6a1fe823 100644 --- a/src/test/ui/try-trait/try-on-option.stderr +++ b/src/test/ui/try-trait/try-on-option.stderr @@ -1,13 +1,11 @@ error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result` --> $DIR/try-on-option.rs:5:6 | -LL | / fn foo() -> Result<u32, ()> { -LL | | let x: Option<u32> = None; -LL | | x?; - | | ^ use `.ok_or(...)?` to provide an error compatible with `Result<u32, ()>` -LL | | Ok(22) -LL | | } - | |_- this function returns a `Result` +LL | fn foo() -> Result<u32, ()> { + | --------------------------- this function returns a `Result` +LL | let x: Option<u32> = None; +LL | x?; + | ^ use `.ok_or(...)?` to provide an error compatible with `Result<u32, ()>` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u32, ()>` = help: the following other types implement trait `FromResidual<R>`: @@ -17,13 +15,11 @@ LL | | } error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option.rs:11:6 | -LL | / fn bar() -> u32 { -LL | | let x: Option<u32> = None; -LL | | x?; - | | ^ cannot use the `?` operator in a function that returns `u32` -LL | | 22 -LL | | } - | |_- this function should return `Result` or `Option` to accept `?` +LL | fn bar() -> u32 { + | --------------- this function should return `Result` or `Option` to accept `?` +LL | let x: Option<u32> = None; +LL | x?; + | ^ cannot use the `?` operator in a function that returns `u32` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32` diff --git a/src/test/ui/try-trait/try-operator-on-main.stderr b/src/test/ui/try-trait/try-operator-on-main.stderr index ad55f40b5b6..7cd38e0cf95 100644 --- a/src/test/ui/try-trait/try-operator-on-main.stderr +++ b/src/test/ui/try-trait/try-operator-on-main.stderr @@ -1,15 +1,11 @@ error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-operator-on-main.rs:7:31 | -LL | / fn main() { -LL | | // error for a `Try` type on a non-`Try` fn -LL | | std::fs::File::open("foo")?; - | | ^ cannot use the `?` operator in a function that returns `()` -LL | | -... | -LL | | try_trait_generic::<()>(); -LL | | } - | |_- this function should return `Result` or `Option` to accept `?` +LL | fn main() { + | --------- this function should return `Result` or `Option` to accept `?` +LL | // error for a `Try` type on a non-`Try` fn +LL | std::fs::File::open("foo")?; + | ^ cannot use the `?` operator in a function that returns `()` | = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()` @@ -24,17 +20,11 @@ LL | ()?; error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-operator-on-main.rs:10:7 | -LL | / fn main() { -LL | | // error for a `Try` type on a non-`Try` fn -LL | | std::fs::File::open("foo")?; -LL | | -LL | | // a non-`Try` type on a non-`Try` fn -LL | | ()?; - | | ^ cannot use the `?` operator in a function that returns `()` -... | -LL | | try_trait_generic::<()>(); -LL | | } - | |_- this function should return `Result` or `Option` to accept `?` +LL | fn main() { + | --------- this function should return `Result` or `Option` to accept `?` +... +LL | ()?; + | ^ cannot use the `?` operator in a function that returns `()` | = help: the trait `FromResidual<_>` is not implemented for `()` diff --git a/src/test/ui/typeck/assign-non-lval-needs-deref.rs b/src/test/ui/typeck/assign-non-lval-needs-deref.rs new file mode 100644 index 00000000000..c979d76b4f4 --- /dev/null +++ b/src/test/ui/typeck/assign-non-lval-needs-deref.rs @@ -0,0 +1,19 @@ +// issue #101376 + +use std::ops::AddAssign; +struct Foo; + +impl AddAssign<()> for Foo { + fn add_assign(&mut self, _: ()) {} +} + +impl AddAssign<()> for &mut Foo { + fn add_assign(&mut self, _: ()) {} +} + +fn main() { + (&mut Foo) += (); + //~^ ERROR invalid left-hand side of assignment + //~| NOTE cannot assign to this expression + //~| HELP consider dereferencing the left-hand side of this operation +} diff --git a/src/test/ui/typeck/assign-non-lval-needs-deref.stderr b/src/test/ui/typeck/assign-non-lval-needs-deref.stderr new file mode 100644 index 00000000000..ee83b145321 --- /dev/null +++ b/src/test/ui/typeck/assign-non-lval-needs-deref.stderr @@ -0,0 +1,16 @@ +error[E0067]: invalid left-hand side of assignment + --> $DIR/assign-non-lval-needs-deref.rs:15:16 + | +LL | (&mut Foo) += (); + | ---------- ^^ + | | + | cannot assign to this expression + | +help: consider dereferencing the left-hand side of this operation + | +LL | *(&mut Foo) += (); + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0067`. diff --git a/src/tools/unicode-table-generator/src/range_search.rs b/src/tools/unicode-table-generator/src/range_search.rs index 39b47ce703f..3a5b869f72f 100644 --- a/src/tools/unicode-table-generator/src/range_search.rs +++ b/src/tools/unicode-table-generator/src/range_search.rs @@ -1,5 +1,6 @@ +#[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")] #[inline(always)] -fn bitset_search< +const fn bitset_search< const N: usize, const CHUNK_SIZE: usize, const N1: usize, @@ -15,14 +16,18 @@ fn bitset_search< let bucket_idx = (needle / 64) as usize; let chunk_map_idx = bucket_idx / CHUNK_SIZE; let chunk_piece = bucket_idx % CHUNK_SIZE; - let chunk_idx = if let Some(&v) = chunk_idx_map.get(chunk_map_idx) { - v + // FIXME: const-hack: Revert to `slice::get` after `const_slice_index` + // feature stabilizes. + let chunk_idx = if chunk_map_idx < chunk_idx_map.len() { + chunk_idx_map[chunk_map_idx] } else { return false; }; let idx = bitset_chunk_idx[chunk_idx as usize][chunk_piece] as usize; - let word = if let Some(word) = bitset_canonical.get(idx) { - *word + // FIXME: const-hack: Revert to `slice::get` after `const_slice_index` + // feature stabilizes. + let word = if idx < bitset_canonical.len() { + bitset_canonical[idx] } else { let (real_idx, mapping) = bitset_canonicalized[idx - bitset_canonical.len()]; let mut word = bitset_canonical[real_idx as usize]; diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index 13eed7b1099..890ff986c2b 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -76,7 +76,7 @@ impl RawEmitter { writeln!( &mut self.file, - "static BITSET_CANONICAL: [u64; {}] = [{}];", + "const BITSET_CANONICAL: &'static [u64; {}] = &[{}];", canonicalized.canonical_words.len(), fmt_list(canonicalized.canonical_words.iter().map(|v| Bits(*v))), ) @@ -84,7 +84,7 @@ impl RawEmitter { self.bytes_used += 8 * canonicalized.canonical_words.len(); writeln!( &mut self.file, - "static BITSET_MAPPING: [(u8, u8); {}] = [{}];", + "const BITSET_MAPPING: &'static [(u8, u8); {}] = &[{}];", canonicalized.canonicalized_words.len(), fmt_list(&canonicalized.canonicalized_words), ) @@ -96,7 +96,12 @@ impl RawEmitter { self.blank_line(); - writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); + writeln!( + &mut self.file, + r#"#[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]"# + ) + .unwrap(); + writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap(); writeln!(&mut self.file, " super::bitset_search(",).unwrap(); writeln!(&mut self.file, " c as u32,").unwrap(); writeln!(&mut self.file, " &BITSET_CHUNKS_MAP,").unwrap(); @@ -130,7 +135,7 @@ impl RawEmitter { writeln!( &mut self.file, - "static BITSET_CHUNKS_MAP: [u8; {}] = [{}];", + "const BITSET_CHUNKS_MAP: &'static [u8; {}] = &[{}];", chunk_indices.len(), fmt_list(&chunk_indices), ) @@ -138,7 +143,7 @@ impl RawEmitter { self.bytes_used += chunk_indices.len(); writeln!( &mut self.file, - "static BITSET_INDEX_CHUNKS: [[u8; {}]; {}] = [{}];", + "const BITSET_INDEX_CHUNKS: &'static [[u8; {}]; {}] = &[{}];", chunk_length, chunks.len(), fmt_list(chunks.iter()), |
