Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollup of 5 pull requests #94751

Merged
merged 11 commits into from
Mar 9, 2022
Merged
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,6 @@ impl<'tcx> TraitRef<'tcx> {
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()]) }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{
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;
Expand All @@ -17,7 +17,7 @@ crate trait InferCtxtExt<'tcx> {
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> Option<DefId>;
) -> Option<(DefId, SubstsRef<'tcx>)>;

/*private*/
fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>;
Expand All @@ -34,7 +34,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> Option<DefId> {
) -> Option<(DefId, SubstsRef<'tcx>)> {
let tcx = self.tcx;
let param_env = obligation.param_env;
let trait_ref = tcx.erase_late_bound_regions(trait_ref);
Expand All @@ -50,28 +50,29 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
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),
impl_trait_ref.substs.types().skip(1),
)
.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]
} else {
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`
Expand Down Expand Up @@ -120,8 +121,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
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![(
Expand Down Expand Up @@ -176,15 +178,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
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,
};
let name = param.name;
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
Expand Down Expand Up @@ -229,9 +231,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}
});

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()
Expand Down
46 changes: 26 additions & 20 deletions compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -63,7 +63,7 @@ impl<'tcx> OnUnimplementedDirective {
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 {
Expand Down Expand Up @@ -135,7 +135,7 @@ impl<'tcx> OnUnimplementedDirective {
{
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 {
Expand Down Expand Up @@ -178,27 +178,23 @@ impl<'tcx> OnUnimplementedDirective {
}
}

pub fn of_item(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
impl_def_id: DefId,
) -> Result<Option<Self>, ErrorGuaranteed> {
let attrs = tcx.get_attrs(impl_def_id);
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, 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,
message: None,
subcommands: vec![],
label: Some(OnUnimplementedFormatString::try_parse(
tcx,
trait_def_id,
item_def_id,
value,
attr.span,
)?),
Expand All @@ -209,7 +205,7 @@ impl<'tcx> OnUnimplementedDirective {
} else {
return Err(ErrorGuaranteed);
};
debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result);
debug!("of_item({:?}) = {:?}", item_def_id, result);
result
}

Expand Down Expand Up @@ -280,23 +276,29 @@ impl<'tcx> OnUnimplementedDirective {
impl<'tcx> OnUnimplementedFormatString {
fn try_parse(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
item_def_id: DefId,
from: Symbol,
err_sp: Span,
) -> Result<Self, ErrorGuaranteed> {
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(());
Expand All @@ -307,7 +309,7 @@ impl<'tcx> OnUnimplementedFormatString {
// `{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
Expand All @@ -329,9 +331,13 @@ impl<'tcx> OnUnimplementedFormatString {
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);
Expand Down
9 changes: 4 additions & 5 deletions compiler/rustc_typeck/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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>(
Expand Down
123 changes: 123 additions & 0 deletions library/core/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,126 @@ pub fn spin_loop() {
pub const fn black_box<T>(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<Error> {
/// 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<T>(value: T) -> T {
value
}
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
Loading