Auto merge of #72747 - Dylan-DPC:rollup-vvydkgl, r=Dylan-DPC

Rollup of 9 pull requests

Successful merges:

 - #72310 (Add Peekable::next_if)
 - #72383 (Suggest using std::mem::drop function instead of explicit destructor call)
 - #72398 (SocketAddr and friends now correctly pad its content)
 - #72465 (Warn about unused captured variables)
 - #72568 (Implement total_cmp for f32, f64)
 - #72572 (Add some regression tests)
 - #72591 (librustc_middle: Rename upvar_list to closure_captures)
 - #72701 (Fix grammar in liballoc raw_vec)
 - #72731 (Add missing empty line in E0619 explanation)

Failed merges:

r? @ghost
This commit is contained in:
bors 2020-05-29 19:50:22 +00:00
commit 4bd32c9804
42 changed files with 1275 additions and 155 deletions

View File

@ -315,7 +315,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
/// `used_capacity + needed_extra_capacity` elements. If it doesn't already,
/// will reallocate the minimum possible amount of memory necessary.
/// Generally this will be exactly the amount of memory necessary,
/// but in principle the allocator is free to give back more than
/// but in principle the allocator is free to give back more than what
/// we asked for.
///
/// If `used_capacity` exceeds `self.capacity()`, this may fail to actually allocate

View File

@ -1619,6 +1619,69 @@ impl<I: Iterator> Peekable<I> {
let iter = &mut self.iter;
self.peeked.get_or_insert_with(|| iter.next()).as_ref()
}
/// Consume the next value of this iterator if a condition is true.
///
/// If `func` returns `true` for the next value of this iterator, consume and return it.
/// Otherwise, return `None`.
///
/// # Examples
/// Consume a number if it's equal to 0.
/// ```
/// #![feature(peekable_next_if)]
/// let mut iter = (0..5).peekable();
/// // The first item of the iterator is 0; consume it.
/// assert_eq!(iter.next_if(|&x| x == 0), Some(0));
/// // The next item returned is now 1, so `consume` will return `false`.
/// assert_eq!(iter.next_if(|&x| x == 0), None);
/// // `next_if` saves the value of the next item if it was not equal to `expected`.
/// assert_eq!(iter.next(), Some(1));
/// ```
///
/// Consume any number less than 10.
/// ```
/// #![feature(peekable_next_if)]
/// let mut iter = (1..20).peekable();
/// // Consume all numbers less than 10
/// while iter.next_if(|&x| x < 10).is_some() {}
/// // The next value returned will be 10
/// assert_eq!(iter.next(), Some(10));
/// ```
#[unstable(feature = "peekable_next_if", issue = "72480")]
pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
match self.next() {
Some(matched) if func(&matched) => Some(matched),
other => {
// Since we called `self.next()`, we consumed `self.peeked`.
assert!(self.peeked.is_none());
self.peeked = Some(other);
None
}
}
}
/// Consume the next item if it is equal to `expected`.
///
/// # Example
/// Consume a number if it's equal to 0.
/// ```
/// #![feature(peekable_next_if)]
/// let mut iter = (0..5).peekable();
/// // The first item of the iterator is 0; consume it.
/// assert_eq!(iter.next_if_eq(&0), Some(0));
/// // The next item returned is now 1, so `consume` will return `false`.
/// assert_eq!(iter.next_if_eq(&0), None);
/// // `next_if_eq` saves the value of the next item if it was not equal to `expected`.
/// assert_eq!(iter.next(), Some(1));
/// ```
#[unstable(feature = "peekable_next_if", issue = "72480")]
pub fn next_if_eq<R>(&mut self, expected: &R) -> Option<I::Item>
where
R: ?Sized,
I::Item: PartialEq<R>,
{
self.next_if(|next| next == expected)
}
}
/// An iterator that rejects elements while `predicate` returns `true`.

View File

@ -810,4 +810,78 @@ impl f32 {
pub fn from_ne_bytes(bytes: [u8; 4]) -> Self {
Self::from_bits(u32::from_ne_bytes(bytes))
}
/// Returns an ordering between self and other values.
/// Unlike the standard partial comparison between floating point numbers,
/// this comparison always produces an ordering in accordance to
/// the totalOrder predicate as defined in IEEE 754 (2008 revision)
/// floating point standard. The values are ordered in following order:
/// - Negative quiet NaN
/// - Negative signaling NaN
/// - Negative infinity
/// - Negative numbers
/// - Negative subnormal numbers
/// - Negative zero
/// - Positive zero
/// - Positive subnormal numbers
/// - Positive numbers
/// - Positive infinity
/// - Positive signaling NaN
/// - Positive quiet NaN
///
/// # Example
/// ```
/// #![feature(total_cmp)]
/// struct GoodBoy {
/// name: String,
/// weight: f32,
/// }
///
/// let mut bois = vec![
/// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 },
/// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 },
/// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 },
/// GoodBoy { name: "Chonk".to_owned(), weight: f32::INFINITY },
/// GoodBoy { name: "Abs. Unit".to_owned(), weight: f32::NAN },
/// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 },
/// ];
///
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
/// # assert!(bois.into_iter().map(|b| b.weight)
/// # .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter())
/// # .all(|(a, b)| a.to_bits() == b.to_bits()))
/// ```
#[unstable(feature = "total_cmp", issue = "72599")]
#[inline]
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
let mut left = self.to_bits() as i32;
let mut right = other.to_bits() as i32;
// In case of negatives, flip all the bits except the sign
// to achieve a similar layout as two's complement integers
//
// Why does this work? IEEE 754 floats consist of three fields:
// Sign bit, exponent and mantissa. The set of exponent and mantissa
// fields as a whole have the property that their bitwise order is
// equal to the numeric magnitude where the magnitude is defined.
// The magnitude is not normally defined on NaN values, but
// IEEE 754 totalOrder defines the NaN values also to follow the
// bitwise order. This leads to order explained in the doc comment.
// However, the representation of magnitude is the same for negative
// and positive numbers only the sign bit is different.
// To easily compare the floats as signed integers, we need to
// flip the exponent and mantissa bits in case of negative numbers.
// We effectively convert the numbers to "two's complement" form.
//
// To do the flipping, we construct a mask and XOR against it.
// We branchlessly calculate an "all-ones except for the sign bit"
// mask from negative-signed values: right shifting sign-extends
// the integer, so we "fill" the mask with sign bits, and then
// convert to unsigned to push one more zero bit.
// On positive values, the mask is all zeros, so it's a no-op.
left ^= (((left >> 31) as u32) >> 1) as i32;
right ^= (((right >> 31) as u32) >> 1) as i32;
left.cmp(&right)
}
}

View File

@ -824,4 +824,78 @@ impl f64 {
pub fn from_ne_bytes(bytes: [u8; 8]) -> Self {
Self::from_bits(u64::from_ne_bytes(bytes))
}
/// Returns an ordering between self and other values.
/// Unlike the standard partial comparison between floating point numbers,
/// this comparison always produces an ordering in accordance to
/// the totalOrder predicate as defined in IEEE 754 (2008 revision)
/// floating point standard. The values are ordered in following order:
/// - Negative quiet NaN
/// - Negative signaling NaN
/// - Negative infinity
/// - Negative numbers
/// - Negative subnormal numbers
/// - Negative zero
/// - Positive zero
/// - Positive subnormal numbers
/// - Positive numbers
/// - Positive infinity
/// - Positive signaling NaN
/// - Positive quiet NaN
///
/// # Example
/// ```
/// #![feature(total_cmp)]
/// struct GoodBoy {
/// name: String,
/// weight: f64,
/// }
///
/// let mut bois = vec![
/// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 },
/// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 },
/// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 },
/// GoodBoy { name: "Chonk".to_owned(), weight: f64::INFINITY },
/// GoodBoy { name: "Abs. Unit".to_owned(), weight: f64::NAN },
/// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 },
/// ];
///
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
/// # assert!(bois.into_iter().map(|b| b.weight)
/// # .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter())
/// # .all(|(a, b)| a.to_bits() == b.to_bits()))
/// ```
#[unstable(feature = "total_cmp", issue = "72599")]
#[inline]
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
let mut left = self.to_bits() as i64;
let mut right = other.to_bits() as i64;
// In case of negatives, flip all the bits except the sign
// to achieve a similar layout as two's complement integers
//
// Why does this work? IEEE 754 floats consist of three fields:
// Sign bit, exponent and mantissa. The set of exponent and mantissa
// fields as a whole have the property that their bitwise order is
// equal to the numeric magnitude where the magnitude is defined.
// The magnitude is not normally defined on NaN values, but
// IEEE 754 totalOrder defines the NaN values also to follow the
// bitwise order. This leads to order explained in the doc comment.
// However, the representation of magnitude is the same for negative
// and positive numbers only the sign bit is different.
// To easily compare the floats as signed integers, we need to
// flip the exponent and mantissa bits in case of negative numbers.
// We effectively convert the numbers to "two's complement" form.
//
// To do the flipping, we construct a mask and XOR against it.
// We branchlessly calculate an "all-ones except for the sign bit"
// mask from negative-signed values: right shifting sign-extends
// the integer, so we "fill" the mask with sign bits, and then
// convert to unsigned to push one more zero bit.
// On positive values, the mask is all zeros, so it's a no-op.
left ^= (((left >> 63) as u64) >> 1) as i64;
right ^= (((right >> 63) as u64) >> 1) as i64;
left.cmp(&right)
}
}

View File

@ -813,6 +813,30 @@ fn test_iterator_peekable_rfold() {
assert_eq!(i, xs.len());
}
#[test]
fn test_iterator_peekable_next_if_eq() {
// first, try on references
let xs = vec!["Heart", "of", "Gold"];
let mut it = xs.into_iter().peekable();
// try before `peek()`
assert_eq!(it.next_if_eq(&"trillian"), None);
assert_eq!(it.next_if_eq(&"Heart"), Some("Heart"));
// try after peek()
assert_eq!(it.peek(), Some(&"of"));
assert_eq!(it.next_if_eq(&"of"), Some("of"));
assert_eq!(it.next_if_eq(&"zaphod"), None);
// make sure `next()` still behaves
assert_eq!(it.next(), Some("Gold"));
// make sure comparison works for owned values
let xs = vec![String::from("Ludicrous"), "speed".into()];
let mut it = xs.into_iter().peekable();
// make sure basic functionality works
assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into()));
assert_eq!(it.next_if_eq("speed"), Some("speed".into()));
assert_eq!(it.next_if_eq(""), None);
}
/// This is an iterator that follows the Iterator contract,
/// but it is not fused. After having returned None once, it will start
/// producing elements if .next() is called again.

View File

@ -43,6 +43,7 @@
#![feature(leading_trailing_ones)]
#![feature(const_forget)]
#![feature(option_unwrap_none)]
#![feature(peekable_next_if)]
extern crate test;

View File

@ -1,4 +1,5 @@
#### Note: this error code is no longer emitted by the compiler.
The type-checker needed to know the type of an expression, but that type had not
yet been inferred.

View File

@ -419,7 +419,7 @@ pub struct TypeckTables<'tcx> {
/// The upvarID contains the HIR node ID and it also contains the full path
/// leading to the member of the struct or tuple that is used instead of the
/// entire variable.
pub upvar_list: ty::UpvarListMap,
pub closure_captures: ty::UpvarListMap,
/// Stores the type, expression, span and optional scope span of all types
/// that are live across the yield of this generator (if a generator).
@ -447,7 +447,7 @@ impl<'tcx> TypeckTables<'tcx> {
used_trait_imports: Lrc::new(Default::default()),
tainted_by_errors: None,
concrete_opaque_types: Default::default(),
upvar_list: Default::default(),
closure_captures: Default::default(),
generator_interior_types: Default::default(),
}
}
@ -688,7 +688,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckTables<'tcx> {
ref used_trait_imports,
tainted_by_errors,
ref concrete_opaque_types,
ref upvar_list,
ref closure_captures,
ref generator_interior_types,
} = *self;
@ -721,7 +721,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckTables<'tcx> {
used_trait_imports.hash_stable(hcx, hasher);
tainted_by_errors.hash_stable(hcx, hasher);
concrete_opaque_types.hash_stable(hcx, hasher);
upvar_list.hash_stable(hcx, hasher);
closure_captures.hash_stable(hcx, hasher);
generator_interior_types.hash_stable(hcx, hasher);
})
}

View File

@ -142,7 +142,7 @@ fn do_mir_borrowck<'a, 'tcx>(
infcx.set_tainted_by_errors();
}
let upvars: Vec<_> = tables
.upvar_list
.closure_captures
.get(&def_id.to_def_id())
.into_iter()
.flat_map(|v| v.values())

View File

@ -227,7 +227,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let mut name = None;
if let Some(def_id) = def_id.as_local() {
let tables = self.ecx.tcx.typeck_tables_of(def_id);
if let Some(upvars) = tables.upvar_list.get(&def_id.to_def_id()) {
if let Some(upvars) = tables.closure_captures.get(&def_id.to_def_id()) {
// Sometimes the index is beyond the number of upvars (seen
// for a generator).
if let Some((&var_hir_id, _)) = upvars.get_index(field) {

View File

@ -790,11 +790,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let hir_tables = self.hir.tables();
// In analyze_closure() in upvar.rs we gathered a list of upvars used by a
// closure and we stored in a map called upvar_list in TypeckTables indexed
// indexed closure and we stored in a map called closure_captures in TypeckTables
// with the closure's DefId. Here, we run through that vec of UpvarIds for
// the given closure and use the necessary information to create upvar
// debuginfo and to fill `self.upvar_mutbls`.
if let Some(upvars) = hir_tables.upvar_list.get(&fn_def_id) {
if let Some(upvars) = hir_tables.closure_captures.get(&fn_def_id) {
let closure_env_arg = Local::new(1);
let mut closure_env_projs = vec![];
let mut closure_ty = self.local_decls[closure_env_arg].ty;

View File

@ -884,7 +884,7 @@ fn convert_var<'tcx>(
) -> ExprKind<'tcx> {
let upvar_index = cx
.tables()
.upvar_list
.closure_captures
.get(&cx.body_owner)
.and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i));

View File

@ -76,22 +76,10 @@
//! is not just used to generate a new value. For example, `x += 1` is
//! a read but not a use. This is used to generate better warnings.
//!
//! ## Special Variables
//! ## Special nodes and variables
//!
//! We generate various special variables for various, well, special purposes.
//! These are described in the `specials` struct:
//!
//! - `exit_ln`: a live node that is generated to represent every 'exit' from
//! the function, whether it be by explicit return, panic, or other means.
//!
//! - `fallthrough_ln`: a live node that represents a fallthrough
//!
//! - `clean_exit_var`: a synthetic variable that is only 'read' from the
//! fallthrough node. It is only live if the function could converge
//! via means other than an explicit `return` expression. That is, it is
//! only dead if the end of the function's block can never be reached.
//! It is the responsibility of typeck to ensure that there are no
//! `return` expressions in a function declared as diverging.
//! We generate various special nodes for various, well, special purposes.
//! These are described in the `Specials` struct.
use self::LiveNodeKind::*;
use self::VarKind::*;
@ -140,6 +128,7 @@ enum LiveNodeKind {
UpvarNode(Span),
ExprNode(Span),
VarDefNode(Span),
ClosureNode,
ExitNode,
}
@ -149,6 +138,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_string(s)),
ExprNode(s) => format!("Expr node [{}]", sm.span_to_string(s)),
VarDefNode(s) => format!("Var def node [{}]", sm.span_to_string(s)),
ClosureNode => "Closure node".to_owned(),
ExitNode => "Exit node".to_owned(),
}
}
@ -253,7 +243,7 @@ struct LocalInfo {
enum VarKind {
Param(HirId, Symbol),
Local(LocalInfo),
CleanExit,
Upvar(HirId, Symbol),
}
struct IrMaps<'tcx> {
@ -306,10 +296,9 @@ impl IrMaps<'tcx> {
self.num_vars += 1;
match vk {
Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) => {
Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) | Upvar(node_id, _) => {
self.variable_map.insert(node_id, v);
}
CleanExit => {}
}
debug!("{:?} is {:?}", v, vk);
@ -328,15 +317,14 @@ impl IrMaps<'tcx> {
fn variable_name(&self, var: Variable) -> String {
match self.var_kinds[var.get()] {
Local(LocalInfo { name, .. }) | Param(_, name) => name.to_string(),
CleanExit => "<clean-exit>".to_owned(),
Local(LocalInfo { name, .. }) | Param(_, name) | Upvar(_, name) => name.to_string(),
}
}
fn variable_is_shorthand(&self, var: Variable) -> bool {
match self.var_kinds[var.get()] {
Local(LocalInfo { is_shorthand, .. }) => is_shorthand,
Param(..) | CleanExit => false,
Param(..) | Upvar(..) => false,
}
}
@ -357,7 +345,7 @@ fn visit_fn<'tcx>(
sp: Span,
id: hir::HirId,
) {
debug!("visit_fn");
debug!("visit_fn {:?}", id);
// swap in a new set of IR maps for this function body:
let def_id = ir.tcx.hir().local_def_id(id);
@ -377,6 +365,14 @@ fn visit_fn<'tcx>(
let body = ir.tcx.hir().body(body_id);
if let Some(upvars) = ir.tcx.upvars_mentioned(def_id) {
for (&var_hir_id, _upvar) in upvars {
debug!("adding upvar {:?}", var_hir_id);
let var_name = ir.tcx.hir().name(var_hir_id);
fn_maps.add_variable(Upvar(var_hir_id, var_name));
}
}
for param in body.params {
let is_shorthand = match param.pat.kind {
rustc_hir::PatKind::Struct(..) => true,
@ -399,10 +395,12 @@ fn visit_fn<'tcx>(
// compute liveness
let mut lsets = Liveness::new(&mut fn_maps, def_id);
let entry_ln = lsets.compute(&body.value);
let entry_ln = lsets.compute(fk, &body, sp, id);
lsets.log_liveness(entry_ln, id);
// check for various error conditions
lsets.visit_body(body);
lsets.warn_about_unused_upvars(entry_ln);
lsets.warn_about_unused_args(body, entry_ln);
}
@ -462,11 +460,8 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
// live nodes required for uses or definitions of variables:
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res);
if let Res::Local(var_hir_id) = path.res {
let upvars = ir.tcx.upvars_mentioned(ir.body_owner);
if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) {
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
}
if let Res::Local(_var_hir_id) = path.res {
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
}
intravisit::walk_expr(ir, expr);
}
@ -482,16 +477,9 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
let mut call_caps = Vec::new();
let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id);
if let Some(upvars) = ir.tcx.upvars_mentioned(closure_def_id) {
let parent_upvars = ir.tcx.upvars_mentioned(ir.body_owner);
call_caps.extend(upvars.iter().filter_map(|(&var_id, upvar)| {
let has_parent =
parent_upvars.map_or(false, |upvars| upvars.contains_key(&var_id));
if !has_parent {
let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
Some(CaptureInfo { ln: upvar_ln, var_hid: var_id })
} else {
None
}
call_caps.extend(upvars.iter().map(|(&var_id, upvar)| {
let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
CaptureInfo { ln: upvar_ln, var_hid: var_id }
}));
}
ir.set_captures(expr.hir_id, call_caps);
@ -647,9 +635,13 @@ impl RWUTable {
#[derive(Copy, Clone)]
struct Specials {
/// A live node representing a point of execution before closure entry &
/// after closure exit. Used to calculate liveness of captured variables
/// through calls to the same closure. Used for Fn & FnMut closures only.
closure_ln: LiveNode,
/// A live node representing every 'exit' from the function, whether it be
/// by explicit return, panic, or other means.
exit_ln: LiveNode,
fallthrough_ln: LiveNode,
clean_exit_var: Variable,
}
const ACC_READ: u32 = 1;
@ -673,14 +665,9 @@ struct Liveness<'a, 'tcx> {
impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn new(ir: &'a mut IrMaps<'tcx>, def_id: LocalDefId) -> Liveness<'a, 'tcx> {
// Special nodes and variables:
// - exit_ln represents the end of the fn, either by return or panic
// - implicit_ret_var is a pseudo-variable that represents
// an implicit return
let specials = Specials {
closure_ln: ir.add_live_node(ClosureNode),
exit_ln: ir.add_live_node(ExitNode),
fallthrough_ln: ir.add_live_node(ExitNode),
clean_exit_var: ir.add_variable(CleanExit),
};
let tables = ir.tcx.typeck_tables_of(def_id);
@ -777,12 +764,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn write_vars<F>(&self, wr: &mut dyn Write, ln: LiveNode, mut test: F) -> io::Result<()>
where
F: FnMut(usize) -> LiveNode,
F: FnMut(usize) -> bool,
{
let node_base_idx = self.idx(ln, Variable(0));
for var_idx in 0..self.ir.num_vars {
let idx = node_base_idx + var_idx;
if test(idx).is_valid() {
if test(idx) {
write!(wr, " {:?}", Variable(var_idx as u32))?;
}
}
@ -795,14 +782,31 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
{
let wr = &mut wr as &mut dyn Write;
write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx));
self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_valid());
write!(wr, " writes");
self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx));
self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_valid());
write!(wr, " uses");
self.write_vars(wr, ln, |idx| self.rwu_table.get_used(idx));
write!(wr, " precedes {:?}]", self.successors[ln.get()]);
}
String::from_utf8(wr).unwrap()
}
fn log_liveness(&self, entry_ln: LiveNode, hir_id: hir::HirId) {
// hack to skip the loop unless debug! is enabled:
debug!(
"^^ liveness computation results for body {} (entry={:?})",
{
for ln_idx in 0..self.ir.num_live_nodes {
debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
}
hir_id
},
entry_ln
);
}
fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
self.successors[ln.get()] = succ_ln;
@ -903,30 +907,87 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.rwu_table.assign_unpacked(idx, rwu);
}
fn compute(&mut self, body: &hir::Expr<'_>) -> LiveNode {
debug!("compute: using id for body, {:?}", body);
fn compute(
&mut self,
fk: FnKind<'_>,
body: &hir::Body<'_>,
span: Span,
id: hir::HirId,
) -> LiveNode {
debug!("compute: using id for body, {:?}", body.value);
// the fallthrough exit is only for those cases where we do not
// explicitly return:
let s = self.s;
self.init_from_succ(s.fallthrough_ln, s.exit_ln);
self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
// # Liveness of captured variables
//
// When computing the liveness for captured variables we take into
// account how variable is captured (ByRef vs ByValue) and what is the
// closure kind (Generator / FnOnce vs Fn / FnMut).
//
// Variables captured by reference are assumed to be used on the exit
// from the closure.
//
// In FnOnce closures, variables captured by value are known to be dead
// on exit since it is impossible to call the closure again.
//
// In Fn / FnMut closures, variables captured by value are live on exit
// if they are live on the entry to the closure, since only the closure
// itself can access them on subsequent calls.
let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln);
// hack to skip the loop unless debug! is enabled:
debug!(
"^^ liveness computation results for body {} (entry={:?})",
{
for ln_idx in 0..self.ir.num_live_nodes {
debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
// Mark upvars captured by reference as used after closure exits.
for (&var_hir_id, upvar) in upvars.iter().rev() {
let upvar_id = ty::UpvarId {
var_path: ty::UpvarPath { hir_id: var_hir_id },
closure_expr_id: self.ir.body_owner.expect_local(),
};
match self.tables.upvar_capture(upvar_id) {
ty::UpvarCapture::ByRef(_) => {
let var = self.variable(var_hir_id, upvar.span);
self.acc(self.s.exit_ln, var, ACC_READ | ACC_USE);
}
ty::UpvarCapture::ByValue => {}
}
body.hir_id
},
entry_ln
);
}
}
entry_ln
let succ = self.propagate_through_expr(&body.value, self.s.exit_ln);
match fk {
FnKind::Method(..) | FnKind::ItemFn(..) => return succ,
FnKind::Closure(..) => {}
}
let ty = self.tables.node_type(id);
match ty.kind {
ty::Closure(_def_id, substs) => match substs.as_closure().kind() {
ty::ClosureKind::Fn => {}
ty::ClosureKind::FnMut => {}
ty::ClosureKind::FnOnce => return succ,
},
ty::Generator(..) => return succ,
_ => {
span_bug!(span, "type of closure expr {:?} is not a closure {:?}", id, ty,);
}
};
// Propagate through calls to the closure.
let mut first_merge = true;
loop {
self.init_from_succ(self.s.closure_ln, succ);
for param in body.params {
param.pat.each_binding(|_bm, hir_id, _x, ident| {
let var = self.variable(hir_id, ident.span);
self.define(self.s.closure_ln, var);
})
}
if !self.merge_from_succ(self.s.exit_ln, self.s.closure_ln, first_merge) {
break;
}
first_merge = false;
assert_eq!(succ, self.propagate_through_expr(&body.value, self.s.exit_ln));
}
succ
}
fn propagate_through_block(&mut self, blk: &hir::Block<'_>, succ: LiveNode) -> LiveNode {
@ -1363,14 +1424,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
acc: u32,
) -> LiveNode {
match path.res {
Res::Local(hid) => {
let upvars = self.ir.tcx.upvars_mentioned(self.ir.body_owner);
if !upvars.map_or(false, |upvars| upvars.contains_key(&hid)) {
self.access_var(hir_id, hid, succ, acc, path.span)
} else {
succ
}
}
Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span),
_ => succ,
}
}
@ -1529,16 +1583,13 @@ impl<'tcx> Liveness<'_, 'tcx> {
match expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
if let Res::Local(var_hid) = path.res {
let upvars = self.ir.tcx.upvars_mentioned(self.ir.body_owner);
if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hid)) {
// Assignment to an immutable variable or argument: only legal
// if there is no later assignment. If this local is actually
// mutable, then check for a reassignment to flag the mutability
// as being used.
let ln = self.live_node(expr.hir_id, expr.span);
let var = self.variable(var_hid, expr.span);
self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
}
// Assignment to an immutable variable or argument: only legal
// if there is no later assignment. If this local is actually
// mutable, then check for a reassignment to flag the mutability
// as being used.
let ln = self.live_node(expr.hir_id, expr.span);
let var = self.variable(var_hid, expr.span);
self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
}
}
_ => {
@ -1554,11 +1605,60 @@ impl<'tcx> Liveness<'_, 'tcx> {
if name.is_empty() || name.as_bytes()[0] == b'_' { None } else { Some(name) }
}
fn warn_about_unused_upvars(&self, entry_ln: LiveNode) {
let upvars = match self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
None => return,
Some(upvars) => upvars,
};
for (&var_hir_id, upvar) in upvars.iter() {
let var = self.variable(var_hir_id, upvar.span);
let upvar_id = ty::UpvarId {
var_path: ty::UpvarPath { hir_id: var_hir_id },
closure_expr_id: self.ir.body_owner.expect_local(),
};
match self.tables.upvar_capture(upvar_id) {
ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByRef(..) => continue,
};
if self.used_on_entry(entry_ln, var) {
if self.live_on_entry(entry_ln, var).is_none() {
if let Some(name) = self.should_warn(var) {
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS,
var_hir_id,
vec![upvar.span],
|lint| {
lint.build(&format!("value captured by `{}` is never read", name))
.help("did you mean to capture by reference instead?")
.emit();
},
);
}
}
} else {
if let Some(name) = self.should_warn(var) {
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES,
var_hir_id,
vec![upvar.span],
|lint| {
lint.build(&format!("unused variable: `{}`", name))
.help("did you mean to capture by reference instead?")
.emit();
},
);
}
}
}
}
fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
for p in body.params {
self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
if self.live_on_entry(ln, var).is_none() {
self.report_dead_assign(hir_id, spans, var, true);
self.report_unsed_assign(hir_id, spans, var, |name| {
format!("value passed to `{}` is never read", name)
});
}
});
}
@ -1672,35 +1772,30 @@ impl<'tcx> Liveness<'_, 'tcx> {
fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
if self.live_on_exit(ln, var).is_none() {
self.report_dead_assign(hir_id, spans, var, false);
self.report_unsed_assign(hir_id, spans, var, |name| {
format!("value assigned to `{}` is never read", name)
});
}
}
fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_param: bool) {
fn report_unsed_assign(
&self,
hir_id: HirId,
spans: Vec<Span>,
var: Variable,
message: impl Fn(&str) -> String,
) {
if let Some(name) = self.should_warn(var) {
if is_param {
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS,
hir_id,
spans,
|lint| {
lint.build(&format!("value passed to `{}` is never read", name))
.help("maybe it is overwritten before being read?")
.emit();
},
)
} else {
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS,
hir_id,
spans,
|lint| {
lint.build(&format!("value assigned to `{}` is never read", name))
.help("maybe it is overwritten before being read?")
.emit();
},
)
}
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS,
hir_id,
spans,
|lint| {
lint.build(&message(&name))
.help("maybe it is overwritten before being read?")
.emit();
},
)
}
}
}

View File

@ -21,11 +21,31 @@ use rustc_target::spec::abi;
/// Checks that it is legal to call methods of the trait corresponding
/// to `trait_id` (this only cares about the trait, not the specific
/// method that is called).
pub fn check_legal_trait_for_method_call(tcx: TyCtxt<'_>, span: Span, trait_id: DefId) {
pub fn check_legal_trait_for_method_call(
tcx: TyCtxt<'_>,
span: Span,
receiver: Option<Span>,
trait_id: DefId,
) {
if tcx.lang_items().drop_trait() == Some(trait_id) {
struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method")
.span_label(span, "explicit destructor calls not allowed")
.emit();
let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method");
err.span_label(span, "explicit destructor calls not allowed");
let snippet = receiver
.and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok())
.unwrap_or_default();
let suggestion =
if snippet.is_empty() { "drop".to_string() } else { format!("drop({})", snippet) };
err.span_suggestion(
span,
&format!("consider using `drop` function: `{}`", suggestion),
String::new(),
Applicability::Unspecified,
);
err.emit();
}
}

View File

@ -597,9 +597,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) {
// Disallow calls to the method `drop` defined in the `Drop` trait.
match pick.item.container {
ty::TraitContainer(trait_def_id) => {
callee::check_legal_trait_for_method_call(self.tcx, self.span, trait_def_id)
}
ty::TraitContainer(trait_def_id) => callee::check_legal_trait_for_method_call(
self.tcx,
self.span,
Some(self.self_expr.span),
trait_def_id,
),
ty::ImplContainer(..) => {}
}
}

View File

@ -5433,7 +5433,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
match container {
ty::TraitContainer(trait_did) => {
callee::check_legal_trait_for_method_call(tcx, span, trait_did)
callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
}
ty::ImplContainer(impl_def_id) => {
if segments.len() == 1 {

View File

@ -112,7 +112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
let mut upvar_list: FxIndexMap<hir::HirId, ty::UpvarId> =
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> =
FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default());
for (&var_hir_id, _) in upvars.iter() {
let upvar_id = ty::UpvarId {
@ -122,7 +122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("seed upvar_id {:?}", upvar_id);
// Adding the upvar Id to the list of Upvars, which will be added
// to the map for the closure at the end of the for loop.
upvar_list.insert(var_hir_id, upvar_id);
closure_captures.insert(var_hir_id, upvar_id);
let capture_kind = match capture_clause {
hir::CaptureBy::Value => ty::UpvarCapture::ByValue,
@ -140,8 +140,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Add the vector of upvars to the map keyed with the closure id.
// This gives us an easier access to them without having to call
// tcx.upvars again..
if !upvar_list.is_empty() {
self.tables.borrow_mut().upvar_list.insert(closure_def_id, upvar_list);
if !closure_captures.is_empty() {
self.tables.borrow_mut().closure_captures.insert(closure_def_id, closure_captures);
}
}

View File

@ -74,8 +74,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);
wbcx.tables.used_trait_imports = used_trait_imports;
wbcx.tables.upvar_list =
mem::replace(&mut self.tables.borrow_mut().upvar_list, Default::default());
wbcx.tables.closure_captures =
mem::replace(&mut self.tables.borrow_mut().closure_captures, Default::default());
if self.is_tainted_by_errors() {
// FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted.

View File

@ -1531,4 +1531,147 @@ mod tests {
fn test_clamp_max_is_nan() {
let _ = 1.0f32.clamp(3.0, NAN);
}
#[test]
fn test_total_cmp() {
use core::cmp::Ordering;
fn quiet_bit_mask() -> u32 {
1 << (f32::MANTISSA_DIGITS - 2)
}
fn min_subnorm() -> f32 {
f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0)
}
fn max_subnorm() -> f32 {
f32::MIN_POSITIVE - min_subnorm()
}
fn q_nan() -> f32 {
f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask())
}
fn s_nan() -> f32 {
f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42)
}
assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY));
assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX));
assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5));
assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0));
assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5));
assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5));
assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE));
assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0));
assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0));
assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE));
assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5));
assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0));
assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5));
assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5));
assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX));
assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY));
assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX));
assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5));
assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5));
assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0));
assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5));
assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0));
assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm()));
assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE));
assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5));
assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0));
assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5));
assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5));
assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX));
assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY));
assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan()));
assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan()));
assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY));
assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX));
assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5));
assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5));
assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0));
assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5));
assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE));
assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0));
assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm()));
assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE));
assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5));
assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0));
assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5));
assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5));
assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX));
assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY));
assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
}
}

View File

@ -1554,4 +1554,147 @@ mod tests {
fn test_clamp_max_is_nan() {
let _ = 1.0f64.clamp(3.0, NAN);
}
#[test]
fn test_total_cmp() {
use core::cmp::Ordering;
fn quiet_bit_mask() -> u64 {
1 << (f64::MANTISSA_DIGITS - 2)
}
fn min_subnorm() -> f64 {
f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0)
}
fn max_subnorm() -> f64 {
f64::MIN_POSITIVE - min_subnorm()
}
fn q_nan() -> f64 {
f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask())
}
fn s_nan() -> f64 {
f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42)
}
assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY));
assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX));
assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5));
assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0));
assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5));
assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5));
assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE));
assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0));
assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0));
assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE));
assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5));
assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0));
assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5));
assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5));
assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX));
assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY));
assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX));
assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5));
assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5));
assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0));
assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5));
assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0));
assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm()));
assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE));
assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5));
assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0));
assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5));
assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5));
assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX));
assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY));
assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan()));
assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan()));
assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY));
assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX));
assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5));
assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5));
assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0));
assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5));
assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE));
assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0));
assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm()));
assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE));
assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5));
assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0));
assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5));
assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5));
assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX));
assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY));
assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY));
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY));
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
}
}

View File

@ -311,6 +311,7 @@
#![feature(test)]
#![feature(thread_local)]
#![feature(toowned_clone_into)]
#![feature(total_cmp)]
#![feature(trace_macros)]
#![feature(track_caller)]
#![feature(try_reserve)]

View File

@ -2,7 +2,7 @@ use crate::cmp::Ordering;
use crate::convert::TryInto;
use crate::fmt;
use crate::hash;
use crate::io;
use crate::io::{self, Write};
use crate::iter;
use crate::mem;
use crate::net::{htons, ntohs, IpAddr, Ipv4Addr, Ipv6Addr};
@ -600,7 +600,26 @@ impl fmt::Display for SocketAddr {
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for SocketAddrV4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.ip(), self.port())
// Fast path: if there's no alignment stuff, write to the output buffer
// directly
if f.precision().is_none() && f.width().is_none() {
write!(f, "{}:{}", self.ip(), self.port())
} else {
const IPV4_SOCKET_BUF_LEN: usize = (3 * 4) // the segments
+ 3 // the separators
+ 1 + 5; // the port
let mut buf = [0; IPV4_SOCKET_BUF_LEN];
let mut buf_slice = &mut buf[..];
// Unwrap is fine because writing to a sufficiently-sized
// buffer is infallible
write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();
// This unsafe is OK because we know what is being written to the buffer
let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
f.pad(buf)
}
}
}
@ -614,7 +633,28 @@ impl fmt::Debug for SocketAddrV4 {
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for SocketAddrV6 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}]:{}", self.ip(), self.port())
// Fast path: if there's no alignment stuff, write to the output
// buffer directly
if f.precision().is_none() && f.width().is_none() {
write!(f, "[{}]:{}", self.ip(), self.port())
} else {
const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address
+ 7 // The colon separators
+ 2 // The brackets
+ 1 + 5; // The port
let mut buf = [0; IPV6_SOCKET_BUF_LEN];
let mut buf_slice = &mut buf[..];
// Unwrap is fine because writing to a sufficiently-sized
// buffer is infallible
write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap();
let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();
// This unsafe is OK because we know what is being written to the buffer
let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
f.pad(buf)
}
}
}
@ -1168,6 +1208,28 @@ mod tests {
assert!(v6.is_ipv6());
}
#[test]
fn socket_v4_to_str() {
let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080);
assert_eq!(format!("{}", socket), "192.168.0.1:8080");
assert_eq!(format!("{:<20}", socket), "192.168.0.1:8080 ");
assert_eq!(format!("{:>20}", socket), " 192.168.0.1:8080");
assert_eq!(format!("{:^20}", socket), " 192.168.0.1:8080 ");
assert_eq!(format!("{:.10}", socket), "192.168.0.");
}
#[test]
fn socket_v6_to_str() {
let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap();
assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53");
assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53 ");
assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1]:53");
assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1]:53 ");
assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::");
}
#[test]
fn compare() {
let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap();

View File

@ -8,6 +8,6 @@ fn foo(mut f: Box<dyn FnMut()>) {
fn main() {
let mut y = true;
foo(Box::new(move || y = false) as Box<_>);
foo(Box::new(move || y = !y) as Box<_>);
//~^ ERROR cannot assign to `y`, as it is not declared as mutable
}

View File

@ -8,6 +8,6 @@ fn foo(mut f: Box<dyn FnMut()>) {
fn main() {
let y = true;
foo(Box::new(move || y = false) as Box<_>);
foo(Box::new(move || y = !y) as Box<_>);
//~^ ERROR cannot assign to `y`, as it is not declared as mutable
}

View File

@ -3,8 +3,8 @@ error[E0594]: cannot assign to `y`, as it is not declared as mutable
|
LL | let y = true;
| - help: consider changing this to be mutable: `mut y`
LL | foo(Box::new(move || y = false) as Box<_>);
| ^^^^^^^^^ cannot assign
LL | foo(Box::new(move || y = !y) as Box<_>);
| ^^^^^^ cannot assign
error: aborting due to previous error

View File

@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
--> $DIR/E0040.rs:13:7
|
LL | x.drop();
| ^^^^ explicit destructor calls not allowed
| ^^^^
| |
| explicit destructor calls not allowed
| help: consider using `drop` function: `drop(x)`
error: aborting due to previous error

View File

@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
--> $DIR/explicit-call-to-dtor.rs:13:7
|
LL | x.drop();
| ^^^^ explicit destructor calls not allowed
| ^^^^
| |
| explicit destructor calls not allowed
| help: consider using `drop` function: `drop(x)`
error: aborting due to previous error

View File

@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
--> $DIR/explicit-call-to-supertrait-dtor.rs:17:14
|
LL | self.drop();
| ^^^^ explicit destructor calls not allowed
| ^^^^
| |
| explicit destructor calls not allowed
| help: consider using `drop` function: `drop(self)`
error: aborting due to previous error

View File

@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
--> $DIR/illegal-ufcs-drop.rs:8:5
|
LL | Drop::drop(&mut Foo)
| ^^^^^^^^^^ explicit destructor calls not allowed
| ^^^^^^^^^^
| |
| explicit destructor calls not allowed
| help: consider using `drop` function: `drop`
error: aborting due to previous error

View File

@ -0,0 +1,26 @@
// Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-629426939
// check-pass
#![crate_type = "lib"]
use std::marker::PhantomData;
pub struct S<'a>
{
pub m1: PhantomData<&'a u8>,
pub m2: [u8; S::size()],
}
impl<'a> S<'a>
{
pub const fn size() -> usize { 1 }
pub fn new() -> Self
{
Self
{
m1: PhantomData,
m2: [0; Self::size()],
}
}
}

View File

@ -0,0 +1,13 @@
// check-pass
pub struct A<'a>(&'a ());
impl<'a> A<'a> {
const N: usize = 68;
pub fn foo(&self) {
let _b = [0; Self::N];
}
}
fn main() {}

View File

@ -1,5 +1,4 @@
// run-pass
#![forbid(warnings)]
// We shouldn't need to rebind a moved upvar as mut if it's already
// marked as mut
@ -7,4 +6,6 @@
pub fn main() {
let mut x = 1;
let _thunk = Box::new(move|| { x = 2; });
//~^ WARN value assigned to `x` is never read
//~| WARN unused variable: `x`
}

View File

@ -0,0 +1,20 @@
warning: value assigned to `x` is never read
--> $DIR/issue-11958.rs:8:36
|
LL | let _thunk = Box::new(move|| { x = 2; });
| ^
|
= note: `#[warn(unused_assignments)]` on by default
= help: maybe it is overwritten before being read?
warning: unused variable: `x`
--> $DIR/issue-11958.rs:8:36
|
LL | let _thunk = Box::new(move|| { x = 2; });
| ^
|
= note: `#[warn(unused_variables)]` on by default
= help: did you mean to capture by reference instead?
warning: 2 warnings emitted

View File

@ -0,0 +1,108 @@
// edition:2018
// check-pass
#![warn(unused)]
#![allow(unreachable_code)]
pub fn unintentional_copy_one() {
let mut last = None;
let mut f = move |s| {
last = Some(s); //~ WARN value assigned to `last` is never read
//~| WARN unused variable: `last`
};
f("a");
f("b");
f("c");
dbg!(last.unwrap());
}
pub fn unintentional_copy_two() {
let mut sum = 0;
(1..10).for_each(move |x| {
sum += x; //~ WARN unused variable: `sum`
});
dbg!(sum);
}
pub fn f() {
let mut c = 0;
// Captured by value, but variable is dead on entry.
move || {
c = 1; //~ WARN value captured by `c` is never read
println!("{}", c);
};
let _ = async move {
c = 1; //~ WARN value captured by `c` is never read
println!("{}", c);
};
// Read and written to, but never actually used.
move || {
c += 1; //~ WARN unused variable: `c`
};
let _ = async move {
c += 1; //~ WARN value assigned to `c` is never read
//~| WARN unused variable: `c`
};
move || {
println!("{}", c);
// Value is read by closure itself on later invocations.
c += 1;
};
let b = Box::new(42);
move || {
println!("{}", c);
// Never read because this is FnOnce closure.
c += 1; //~ WARN value assigned to `c` is never read
drop(b);
};
let _ = async move {
println!("{}", c);
// Never read because this is a generator.
c += 1; //~ WARN value assigned to `c` is never read
};
}
pub fn nested() {
let mut d = None;
let mut e = None;
|| {
|| {
d = Some("d1"); //~ WARN value assigned to `d` is never read
d = Some("d2");
};
move || {
e = Some("e1"); //~ WARN value assigned to `e` is never read
//~| WARN unused variable: `e`
e = Some("e2"); //~ WARN value assigned to `e` is never read
};
};
}
pub fn g<T: Default>(mut v: T) {
|r| {
if r {
v = T::default(); //~ WARN value assigned to `v` is never read
} else {
drop(v);
}
};
}
pub fn h<T: Copy + Default + std::fmt::Debug>() {
let mut z = T::default();
move |b| {
loop {
if b {
z = T::default(); //~ WARN value assigned to `z` is never read
//~| WARN unused variable: `z`
} else {
return;
}
}
dbg!(z);
};
}
fn main() {}

View File

@ -0,0 +1,150 @@
warning: value assigned to `last` is never read
--> $DIR/liveness-upvars.rs:9:9
|
LL | last = Some(s);
| ^^^^
|
note: the lint level is defined here
--> $DIR/liveness-upvars.rs:3:9
|
LL | #![warn(unused)]
| ^^^^^^
= note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]`
= help: maybe it is overwritten before being read?
warning: unused variable: `last`
--> $DIR/liveness-upvars.rs:9:9
|
LL | last = Some(s);
| ^^^^
|
note: the lint level is defined here
--> $DIR/liveness-upvars.rs:3:9
|
LL | #![warn(unused)]
| ^^^^^^
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
= help: did you mean to capture by reference instead?
warning: unused variable: `sum`
--> $DIR/liveness-upvars.rs:21:9
|
LL | sum += x;
| ^^^
|
= help: did you mean to capture by reference instead?
warning: value captured by `c` is never read
--> $DIR/liveness-upvars.rs:31:9
|
LL | c = 1;
| ^
|
= help: did you mean to capture by reference instead?
warning: value captured by `c` is never read
--> $DIR/liveness-upvars.rs:35:9
|
LL | c = 1;
| ^
|
= help: did you mean to capture by reference instead?
warning: unused variable: `c`
--> $DIR/liveness-upvars.rs:41:9
|
LL | c += 1;
| ^
|
= help: did you mean to capture by reference instead?
warning: value assigned to `c` is never read
--> $DIR/liveness-upvars.rs:44:9
|
LL | c += 1;
| ^
|
= help: maybe it is overwritten before being read?
warning: unused variable: `c`
--> $DIR/liveness-upvars.rs:44:9
|
LL | c += 1;
| ^
|
= help: did you mean to capture by reference instead?
warning: value assigned to `c` is never read
--> $DIR/liveness-upvars.rs:57:9
|
LL | c += 1;
| ^
|
= help: maybe it is overwritten before being read?
warning: value assigned to `c` is never read
--> $DIR/liveness-upvars.rs:63:9
|
LL | c += 1;
| ^
|
= help: maybe it is overwritten before being read?
warning: value assigned to `d` is never read
--> $DIR/liveness-upvars.rs:72:13
|
LL | d = Some("d1");
| ^
|
= help: maybe it is overwritten before being read?
warning: value assigned to `e` is never read
--> $DIR/liveness-upvars.rs:76:13
|
LL | e = Some("e1");
| ^
|
= help: maybe it is overwritten before being read?
warning: value assigned to `e` is never read
--> $DIR/liveness-upvars.rs:78:13
|
LL | e = Some("e2");
| ^
|
= help: maybe it is overwritten before being read?
warning: unused variable: `e`
--> $DIR/liveness-upvars.rs:76:13
|
LL | e = Some("e1");
| ^
|
= help: did you mean to capture by reference instead?
warning: value assigned to `v` is never read
--> $DIR/liveness-upvars.rs:86:13
|
LL | v = T::default();
| ^
|
= help: maybe it is overwritten before being read?
warning: value assigned to `z` is never read
--> $DIR/liveness-upvars.rs:98:17
|
LL | z = T::default();
| ^
|
= help: maybe it is overwritten before being read?
warning: unused variable: `z`
--> $DIR/liveness-upvars.rs:98:17
|
LL | z = T::default();
| ^
|
= help: did you mean to capture by reference instead?
warning: 17 warnings emitted

View File

@ -0,0 +1,9 @@
#![feature(impl_trait_in_bindings)]
#![allow(incomplete_features)]
fn main() {
const C: impl Copy = 0;
match C {
C | _ => {} //~ ERROR: opaque types cannot be used in patterns
}
}

View File

@ -0,0 +1,8 @@
error: opaque types cannot be used in patterns
--> $DIR/issue-71042-opaquely-typed-constant-used-in-pattern.rs:7:9
|
LL | C | _ => {}
| ^
error: aborting due to previous error

View File

@ -0,0 +1,23 @@
// check-pass
#![feature(type_alias_impl_trait)]
pub type Successors<'a> = impl Iterator<Item = &'a ()>;
pub fn f<'a>() -> Successors<'a> {
None.into_iter()
}
pub trait Tr {
type Item;
}
impl<'a> Tr for &'a () {
type Item = Successors<'a>;
}
pub fn kazusa<'a>() -> <&'a () as Tr>::Item {
None.into_iter()
}
fn main() {}

View File

@ -1,5 +1,4 @@
// run-pass
#![allow(unused_variables)]
// Test that we mutate a counter on the stack only when we expect to.
fn call<F>(f: F) where F : FnOnce() {
@ -13,7 +12,7 @@ fn main() {
call(|| {
// Move `y`, but do not move `counter`, even though it is read
// by value (note that it is also mutated).
for item in y {
for item in y { //~ WARN unused variable: `item`
let v = counter;
counter += v;
}
@ -22,7 +21,8 @@ fn main() {
call(move || {
// this mutates a moved copy, and hence doesn't affect original
counter += 1;
counter += 1; //~ WARN value assigned to `counter` is never read
//~| WARN unused variable: `counter`
});
assert_eq!(counter, 88);
}

View File

@ -0,0 +1,27 @@
warning: unused variable: `item`
--> $DIR/unboxed-closures-counter-not-moved.rs:15:13
|
LL | for item in y {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_item`
|
= note: `#[warn(unused_variables)]` on by default
warning: value assigned to `counter` is never read
--> $DIR/unboxed-closures-counter-not-moved.rs:24:9
|
LL | counter += 1;
| ^^^^^^^
|
= note: `#[warn(unused_assignments)]` on by default
= help: maybe it is overwritten before being read?
warning: unused variable: `counter`
--> $DIR/unboxed-closures-counter-not-moved.rs:24:9
|
LL | counter += 1;
| ^^^^^^^
|
= help: did you mean to capture by reference instead?
warning: 3 warnings emitted

View File

@ -13,11 +13,11 @@ fn set(x: &mut usize) { *x = 42; }
fn main() {
{
let mut x = 0_usize;
move || x += 1;
move || x += 1; //~ WARN unused variable: `x`
}
{
let mut x = 0_usize;
move || x += 1;
move || x += 1; //~ WARN unused variable: `x`
}
{
let mut x = 0_usize;

View File

@ -0,0 +1,19 @@
warning: unused variable: `x`
--> $DIR/unboxed-closures-move-mutable.rs:16:17
|
LL | move || x += 1;
| ^
|
= note: `#[warn(unused_variables)]` on by default
= help: did you mean to capture by reference instead?
warning: unused variable: `x`
--> $DIR/unboxed-closures-move-mutable.rs:20:17
|
LL | move || x += 1;
| ^
|
= help: did you mean to capture by reference instead?
warning: 2 warnings emitted