From e9ddb8f8fb6fb42d533deb9b092e34c046b45b66 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 6 Mar 2022 18:38:42 -0800 Subject: [PATCH 1/6] use impl substs in on_unimplemented --- compiler/rustc_middle/src/ty/sty.rs | 1 - .../error_reporting/on_unimplemented.rs | 24 ++++++------ src/test/ui/consts/issue-94675.rs | 16 ++++++++ src/test/ui/consts/issue-94675.stderr | 38 +++++++++++++++++++ src/test/ui/issues/issue-87707.rs | 1 + src/test/ui/issues/issue-87707.run.stderr | 4 +- 6 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/consts/issue-94675.rs create mode 100644 src/test/ui/consts/issue-94675.stderr diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a1e906140e0..8b0e7794f92 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -977,7 +977,6 @@ pub fn from_method( substs: SubstsRef<'tcx>, ) -> ty::TraitRef<'tcx> { let defs = tcx.generics_of(trait_id); - ty::TraitRef { def_id: trait_id, substs: tcx.intern_substs(&substs[..defs.params.len()]) } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index a277f74f7a4..d05f62ea29e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -4,7 +4,7 @@ use crate::infer::InferCtxt; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind}; use rustc_span::symbol::sym; use std::iter; @@ -17,7 +17,7 @@ fn impl_similar_to( &self, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, - ) -> Option; + ) -> Option<(DefId, SubstsRef<'tcx>)>; /*private*/ fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>; @@ -34,7 +34,7 @@ fn impl_similar_to( &self, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, - ) -> Option { + ) -> Option<(DefId, SubstsRef<'tcx>)> { let tcx = self.tcx; let param_env = obligation.param_env; let trait_ref = tcx.erase_late_bound_regions(trait_ref); @@ -50,7 +50,7 @@ fn impl_similar_to( let impl_self_ty = impl_trait_ref.self_ty(); if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { - self_match_impls.push(def_id); + self_match_impls.push((def_id, impl_substs)); if iter::zip( trait_ref.substs.types().skip(1), @@ -58,12 +58,12 @@ fn impl_similar_to( ) .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some()) { - fuzzy_match_impls.push(def_id); + fuzzy_match_impls.push((def_id, impl_substs)); } } }); - let impl_def_id = if self_match_impls.len() == 1 { + let impl_def_id_and_substs = if self_match_impls.len() == 1 { self_match_impls[0] } else if fuzzy_match_impls.len() == 1 { fuzzy_match_impls[0] @@ -71,7 +71,8 @@ fn impl_similar_to( return None; }; - tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) + tcx.has_attr(impl_def_id_and_substs.0, sym::rustc_on_unimplemented) + .then_some(impl_def_id_and_substs) } /// Used to set on_unimplemented's `ItemContext` @@ -120,8 +121,9 @@ fn on_unimplemented_note( trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, ) -> OnUnimplementedNote { - let def_id = - self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); + let (def_id, substs) = self + .impl_similar_to(trait_ref, obligation) + .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().substs)); let trait_ref = trait_ref.skip_binder(); let mut flags = vec![( @@ -176,7 +178,7 @@ fn on_unimplemented_note( for param in generics.params.iter() { let value = match param.kind { GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - trait_ref.substs[param.index as usize].to_string() + substs[param.index as usize].to_string() } GenericParamDefKind::Lifetime => continue, }; @@ -184,7 +186,7 @@ fn on_unimplemented_note( flags.push((name, Some(value))); if let GenericParamDefKind::Type { .. } = param.kind { - let param_ty = trait_ref.substs[param.index as usize].expect_ty(); + let param_ty = substs[param.index as usize].expect_ty(); if let Some(def) = param_ty.ty_adt_def() { // We also want to be able to select the parameter's // original signature with no type arguments resolved diff --git a/src/test/ui/consts/issue-94675.rs b/src/test/ui/consts/issue-94675.rs new file mode 100644 index 00000000000..0604aab3bcd --- /dev/null +++ b/src/test/ui/consts/issue-94675.rs @@ -0,0 +1,16 @@ +#![feature(const_trait_impl, const_mut_refs)] + +struct Foo<'a> { + bar: &'a mut Vec, +} + +impl<'a> Foo<'a> { + const fn spam(&mut self, baz: &mut Vec) { + self.bar[0] = baz.len(); + //~^ ERROR cannot call non-const fn `Vec::::len` in constant functions + //~| ERROR the trait bound `Vec: ~const IndexMut` is not satisfied + //~| ERROR cannot call non-const operator in constant functions + } +} + +fn main() {} diff --git a/src/test/ui/consts/issue-94675.stderr b/src/test/ui/consts/issue-94675.stderr new file mode 100644 index 00000000000..6665e42835b --- /dev/null +++ b/src/test/ui/consts/issue-94675.stderr @@ -0,0 +1,38 @@ +error[E0015]: cannot call non-const fn `Vec::::len` in constant functions + --> $DIR/issue-94675.rs:9:27 + | +LL | self.bar[0] = baz.len(); + | ^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0277]: the trait bound `Vec: ~const IndexMut` is not satisfied + --> $DIR/issue-94675.rs:9:9 + | +LL | self.bar[0] = baz.len(); + | ^^^^^^^^^^^ vector indices are of type `usize` or ranges of `usize` + | + = help: the trait `~const IndexMut` is not implemented for `Vec` +note: the trait `IndexMut` is implemented for `Vec`, but that implementation is not `const` + --> $DIR/issue-94675.rs:9:9 + | +LL | self.bar[0] = baz.len(); + | ^^^^^^^^^^^ + +error[E0015]: cannot call non-const operator in constant functions + --> $DIR/issue-94675.rs:9:9 + | +LL | self.bar[0] = baz.len(); + | ^^^^^^^^^^^ + | +note: impl defined here, but it is not `const` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | +LL | impl, A: Allocator> IndexMut for Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0015, E0277. +For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/issues/issue-87707.rs b/src/test/ui/issues/issue-87707.rs index d2e9343f86c..26e9e2c8f91 100644 --- a/src/test/ui/issues/issue-87707.rs +++ b/src/test/ui/issues/issue-87707.rs @@ -1,6 +1,7 @@ // test for #87707 // edition:2018 // run-fail +// exec-env:RUST_BACKTRACE=0 // check-run-results use std::sync::Once; diff --git a/src/test/ui/issues/issue-87707.run.stderr b/src/test/ui/issues/issue-87707.run.stderr index 8f82ccc0c2a..e6c9ea0eb53 100644 --- a/src/test/ui/issues/issue-87707.run.stderr +++ b/src/test/ui/issues/issue-87707.run.stderr @@ -1,3 +1,3 @@ -thread 'main' panicked at 'Here Once instance is poisoned.', $DIR/issue-87707.rs:12:24 +thread 'main' panicked at 'Here Once instance is poisoned.', $DIR/issue-87707.rs:13:24 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -thread 'main' panicked at 'Once instance has previously been poisoned', $DIR/issue-87707.rs:14:7 +thread 'main' panicked at 'Once instance has previously been poisoned', $DIR/issue-87707.rs:15:7 From b726bfb56902eaa743f7164ce502afbdd232562c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 6 Mar 2022 19:51:30 -0800 Subject: [PATCH 2/6] allow referencing impl substs from rustc_on_unimplemented --- .../error_reporting/on_unimplemented.rs | 4 +- .../src/traits/on_unimplemented.rs | 46 +++++++++++-------- compiler/rustc_typeck/src/check/check.rs | 9 ++-- src/test/ui/on-unimplemented/impl-substs.rs | 15 ++++++ .../ui/on-unimplemented/impl-substs.stderr | 13 ++++++ 5 files changed, 59 insertions(+), 28 deletions(-) create mode 100644 src/test/ui/on-unimplemented/impl-substs.rs create mode 100644 src/test/ui/on-unimplemented/impl-substs.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index d05f62ea29e..6dfbdace8e2 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -231,9 +231,7 @@ fn on_unimplemented_note( } }); - if let Ok(Some(command)) = - OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) - { + if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) { command.evaluate(self.tcx, trait_ref, &flags) } else { OnUnimplementedNote::default() diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index 9752ff45323..2f697c1fa27 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -54,7 +54,7 @@ fn parse_error( impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, - trait_def_id: DefId, + item_def_id: DefId, items: &[NestedMetaItem], span: Span, is_root: bool, @@ -63,7 +63,7 @@ fn parse( let mut item_iter = items.iter(); let parse_value = |value_str| { - OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some) + OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some) }; let condition = if is_root { @@ -135,7 +135,7 @@ fn parse( { if let Some(items) = item.meta_item_list() { if let Ok(subcommand) = - Self::parse(tcx, trait_def_id, &items, item.span(), false) + Self::parse(tcx, item_def_id, &items, item.span(), false) { subcommands.push(subcommand); } else { @@ -178,19 +178,15 @@ fn parse( } } - pub fn of_item( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - impl_def_id: DefId, - ) -> Result, ErrorGuaranteed> { - let attrs = tcx.get_attrs(impl_def_id); + pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { + let attrs = tcx.get_attrs(item_def_id); let Some(attr) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) else { return Ok(None); }; let result = if let Some(items) = attr.meta_item_list() { - Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some) + Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some) } else if let Some(value) = attr.value_str() { Ok(Some(OnUnimplementedDirective { condition: None, @@ -198,7 +194,7 @@ pub fn of_item( subcommands: vec![], label: Some(OnUnimplementedFormatString::try_parse( tcx, - trait_def_id, + item_def_id, value, attr.span, )?), @@ -209,7 +205,7 @@ pub fn of_item( } else { return Err(ErrorGuaranteed); }; - debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result); + debug!("of_item({:?}) = {:?}", item_def_id, result); result } @@ -280,23 +276,29 @@ pub fn evaluate( impl<'tcx> OnUnimplementedFormatString { fn try_parse( tcx: TyCtxt<'tcx>, - trait_def_id: DefId, + item_def_id: DefId, from: Symbol, err_sp: Span, ) -> Result { let result = OnUnimplementedFormatString(from); - result.verify(tcx, trait_def_id, err_sp)?; + result.verify(tcx, item_def_id, err_sp)?; Ok(result) } fn verify( &self, tcx: TyCtxt<'tcx>, - trait_def_id: DefId, + item_def_id: DefId, span: Span, ) -> Result<(), ErrorGuaranteed> { - let name = tcx.item_name(trait_def_id); - let generics = tcx.generics_of(trait_def_id); + let trait_def_id = if tcx.is_trait(item_def_id) { + item_def_id + } else { + tcx.trait_id_of_impl(item_def_id) + .expect("expected `on_unimplemented` to correspond to a trait") + }; + let trait_name = tcx.item_name(trait_def_id); + let generics = tcx.generics_of(item_def_id); let s = self.0.as_str(); let parser = Parser::new(s, None, None, false, ParseMode::Format); let mut result = Ok(()); @@ -307,7 +309,7 @@ fn verify( // `{Self}` is allowed Position::ArgumentNamed(s, _) if s == kw::SelfUpper => (), // `{ThisTraitsName}` is allowed - Position::ArgumentNamed(s, _) if s == name => (), + Position::ArgumentNamed(s, _) if s == trait_name => (), // `{from_method}` is allowed Position::ArgumentNamed(s, _) if s == sym::from_method => (), // `{from_desugaring}` is allowed @@ -329,9 +331,13 @@ fn verify( tcx.sess, span, E0230, - "there is no parameter `{}` on trait `{}`", + "there is no parameter `{}` on {}", s, - name + if trait_def_id == item_def_id { + format!("trait `{}`", trait_name) + } else { + "impl".to_string() + } ) .emit(); result = Err(ErrorGuaranteed); diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 765b752691f..7c3594175b8 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -742,12 +742,11 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { impl_trait_ref, &impl_.items, ); - let trait_def_id = impl_trait_ref.def_id; - check_on_unimplemented(tcx, trait_def_id, it); + check_on_unimplemented(tcx, it); } } hir::ItemKind::Trait(_, _, _, _, ref items) => { - check_on_unimplemented(tcx, it.def_id.to_def_id(), it); + check_on_unimplemented(tcx, it); for item in items.iter() { let item = tcx.hir().trait_item(item.id); @@ -857,9 +856,9 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { } } -pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item<'_>) { +pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { // an error would be reported if this fails. - let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item.def_id.to_def_id()); + let _ = traits::OnUnimplementedDirective::of_item(tcx, item.def_id.to_def_id()); } pub(super) fn check_specialization_validity<'tcx>( diff --git a/src/test/ui/on-unimplemented/impl-substs.rs b/src/test/ui/on-unimplemented/impl-substs.rs new file mode 100644 index 00000000000..fe9c50ec3d4 --- /dev/null +++ b/src/test/ui/on-unimplemented/impl-substs.rs @@ -0,0 +1,15 @@ +#![feature(rustc_attrs)] + +trait Foo { + fn foo(self); +} + +#[rustc_on_unimplemented = "an impl did not match: {A} {B} {C}"] +impl Foo for (A, B, C) { + fn foo(self) {} +} + +fn main() { + Foo::::foo((1i32, 1i32, 1i32)); + //~^ ERROR the trait bound `(i32, i32, i32): Foo` is not satisfied +} diff --git a/src/test/ui/on-unimplemented/impl-substs.stderr b/src/test/ui/on-unimplemented/impl-substs.stderr new file mode 100644 index 00000000000..db66ab0bfae --- /dev/null +++ b/src/test/ui/on-unimplemented/impl-substs.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `(i32, i32, i32): Foo` is not satisfied + --> $DIR/impl-substs.rs:13:23 + | +LL | Foo::::foo((1i32, 1i32, 1i32)); + | ----------------- ^^^^^^^^^^^^^^^^^^ an impl did not match: usize _ _ + | | + | required by a bound introduced by this call + | + = help: the trait `Foo` is not implemented for `(i32, i32, i32)` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 24ec0f223d4c942139d9238c9b683cc73a4f0e92 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 7 Mar 2022 22:35:17 +0000 Subject: [PATCH 3/6] Enable `close_read_wakes_up` on Windows --- library/std/src/net/tcp/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index c2061c13512..959fe6943f6 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -508,7 +508,6 @@ fn close_readwrite_smoke() { } #[test] -#[cfg(unix)] // test doesn't work on Windows, see #31657 fn close_read_wakes_up() { each_ip(&mut |addr| { let a = t!(TcpListener::bind(&addr)); From ef3e33bd16b1392f8f238c1224cd58d5fb95b555 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 7 Mar 2022 18:51:53 -0800 Subject: [PATCH 4/6] unix: Avoid name conversions in `remove_dir_all_recursive` Each recursive call was creating an `OsString` for a `&Path`, only for it to be turned into a `CString` right away. Instead we can directly pass `.name_cstr()`, saving two allocations each time. --- library/std/src/sys/unix/fs.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 03684608f75..4a48be5cda6 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1604,17 +1604,15 @@ fn is_dir(ent: &DirEntry) -> Option { } } - fn remove_dir_all_recursive(parent_fd: Option, p: &Path) -> io::Result<()> { - let pcstr = cstr(p)?; - + fn remove_dir_all_recursive(parent_fd: Option, path: &CStr) -> io::Result<()> { // try opening as directory - let fd = match openat_nofollow_dironly(parent_fd, &pcstr) { + let fd = match openat_nofollow_dironly(parent_fd, &path) { Err(err) if err.raw_os_error() == Some(libc::ENOTDIR) => { // not a directory - don't traverse further return match parent_fd { // unlink... Some(parent_fd) => { - cvt(unsafe { unlinkat(parent_fd, pcstr.as_ptr(), 0) }).map(drop) + cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop) } // ...unless this was supposed to be the deletion root directory None => Err(err), @@ -1627,26 +1625,27 @@ fn remove_dir_all_recursive(parent_fd: Option, p: &Path) -> io::Result<() let (dir, fd) = fdreaddir(fd)?; for child in dir { let child = child?; + let child_name = child.name_cstr(); match is_dir(&child) { Some(true) => { - remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?; + remove_dir_all_recursive(Some(fd), child_name)?; } Some(false) => { - cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) })?; + cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?; } None => { // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed // if the process has the appropriate privileges. This however can causing orphaned // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing // into it first instead of trying to unlink() it. - remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?; + remove_dir_all_recursive(Some(fd), child_name)?; } } } // unlink the directory after removing its contents cvt(unsafe { - unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), pcstr.as_ptr(), libc::AT_REMOVEDIR) + unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR) })?; Ok(()) } @@ -1659,7 +1658,7 @@ fn remove_dir_all_modern(p: &Path) -> io::Result<()> { if attr.file_type().is_symlink() { crate::fs::remove_file(p) } else { - remove_dir_all_recursive(None, p) + remove_dir_all_recursive(None, &cstr(p)?) } } From d8e75bc1b7b538593f7ef3d55eedeb2f00fec474 Mon Sep 17 00:00:00 2001 From: Sandeep Bansal Date: Mon, 7 Mar 2022 23:41:52 -0800 Subject: [PATCH 5/6] Reverted atomic-mut-ptr feature removal causing compilation break --- library/std/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5b02e711aab..c35389d44f9 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -231,6 +231,7 @@ #![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(async_iterator)] +#![feature(atomic_mut_ptr)] #![feature(bench_black_box)] #![feature(box_syntax)] #![feature(c_unwind)] From b2473e988fdf304f87099102019cca8041a64887 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 7 Mar 2022 16:37:59 -0800 Subject: [PATCH 6/6] Add core::hint::must_use --- library/core/src/hint.rs | 123 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 330c43d2948..58ea109c735 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -173,3 +173,126 @@ pub fn spin_loop() { pub const fn black_box(dummy: T) -> T { crate::intrinsics::black_box(dummy) } + +/// An identity function that causes an `unused_must_use` warning to be +/// triggered if the given value is not used (returned, stored in a variable, +/// etc) by the caller. +/// +/// This is primarily intended for use in macro-generated code, in which a +/// [`#[must_use]` attribute][must_use] either on a type or a function would not +/// be convenient. +/// +/// [must_use]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute +/// +/// # Example +/// +/// ``` +/// #![feature(hint_must_use)] +/// +/// use core::fmt; +/// +/// pub struct Error(/* ... */); +/// +/// #[macro_export] +/// macro_rules! make_error { +/// ($($args:expr),*) => { +/// core::hint::must_use({ +/// let error = $crate::make_error(core::format_args!($($args),*)); +/// error +/// }) +/// }; +/// } +/// +/// // Implementation detail of make_error! macro. +/// #[doc(hidden)] +/// pub fn make_error(args: fmt::Arguments<'_>) -> Error { +/// Error(/* ... */) +/// } +/// +/// fn demo() -> Option { +/// if true { +/// // Oops, meant to write `return Some(make_error!("..."));` +/// Some(make_error!("...")); +/// } +/// None +/// } +/// # +/// # // Make rustdoc not wrap the whole snippet in fn main, so that $crate::make_error works +/// # fn main() {} +/// ``` +/// +/// In the above example, we'd like an `unused_must_use` lint to apply to the +/// value created by `make_error!`. However, neither `#[must_use]` on a struct +/// nor `#[must_use]` on a function is appropriate here, so the macro expands +/// using `core::hint::must_use` instead. +/// +/// - We wouldn't want `#[must_use]` on the `struct Error` because that would +/// make the following unproblematic code trigger a warning: +/// +/// ``` +/// # struct Error; +/// # +/// fn f(arg: &str) -> Result<(), Error> +/// # { Ok(()) } +/// +/// #[test] +/// fn t() { +/// // Assert that `f` returns error if passed an empty string. +/// // A value of type `Error` is unused here but that's not a problem. +/// f("").unwrap_err(); +/// } +/// ``` +/// +/// - Using `#[must_use]` on `fn make_error` can't help because the return value +/// *is* used, as the right-hand side of a `let` statement. The `let` +/// statement looks useless but is in fact necessary for ensuring that +/// temporaries within the `format_args` expansion are not kept alive past the +/// creation of the `Error`, as keeping them alive past that point can cause +/// autotrait issues in async code: +/// +/// ``` +/// # #![feature(hint_must_use)] +/// # +/// # struct Error; +/// # +/// # macro_rules! make_error { +/// # ($($args:expr),*) => { +/// # core::hint::must_use({ +/// # // If `let` isn't used, then `f()` produces a non-Send future. +/// # let error = make_error(core::format_args!($($args),*)); +/// # error +/// # }) +/// # }; +/// # } +/// # +/// # fn make_error(args: core::fmt::Arguments<'_>) -> Error { +/// # Error +/// # } +/// # +/// async fn f() { +/// // Using `let` inside the make_error expansion causes temporaries like +/// // `unsync()` to drop at the semicolon of that `let` statement, which +/// // is prior to the await point. They would otherwise stay around until +/// // the semicolon on *this* statement, which is after the await point, +/// // and the enclosing Future would not implement Send. +/// log(make_error!("look: {:p}", unsync())).await; +/// } +/// +/// async fn log(error: Error) {/* ... */} +/// +/// // Returns something without a Sync impl. +/// fn unsync() -> *const () { +/// 0 as *const () +/// } +/// # +/// # fn test() { +/// # fn assert_send(_: impl Send) {} +/// # assert_send(f()); +/// # } +/// ``` +#[unstable(feature = "hint_must_use", issue = "94745")] +#[rustc_const_unstable(feature = "hint_must_use", issue = "94745")] +#[must_use] // <-- :) +pub const fn must_use(value: T) -> T { + value +}