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 8 pull requests #81385

Closed
wants to merge 21 commits into from
Closed
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
679f6f3
Add `unwrap_unchecked()` methods for `Option` and `Result`
ojeda Jan 10, 2021
76299b3
Add `SAFETY` annotations
ojeda Jan 10, 2021
def0e9b
Fix ICE with `ReadPointerAsBytes` validation error
camelid Jan 11, 2021
a7b7a43
Move test to `src/test/ui/consts/`
camelid Jan 11, 2021
e25959b
Make more traits of the From/Into family diagnostic items
flip1995 Jan 22, 2021
6d4e03a
codegen: assume constants cannot fail to evaluate
RalfJung Jan 24, 2021
57c72ab
libtest: Wait for test threads to exit after they report completion
andersk Jan 25, 2021
2be1993
Ignore test on 32-bit architectures
camelid Jan 25, 2021
26b4baf
Point to span of upvar making closure FnMut
sledgehammervampire Jan 18, 2021
088c89d
Account for generics when suggesting bound
estebank Jan 19, 2021
042facb
Fix some bugs reported by eslint
GuillaumeGomez Jan 23, 2021
0140dac
Link the reference about undefined behavior
ojeda Jan 25, 2021
01250fc
Add tracking issue
ojeda Jan 25, 2021
8bf2392
Rollup merge of #80876 - ojeda:option-result-unwrap_unchecked, r=m-ou-se
jonas-schievink Jan 25, 2021
3ca45fd
Rollup merge of #80900 - camelid:readpointerasbytes-ice, r=oli-obk
jonas-schievink Jan 25, 2021
0f3c5e8
Rollup merge of #81158 - 1000teslas:issue-80313-fix, r=Aaron1011
jonas-schievink Jan 25, 2021
f4fd715
Rollup merge of #81195 - estebank:suggest-bound-on-trait-with-params,…
jonas-schievink Jan 25, 2021
0248edb
Rollup merge of #81277 - flip1995:from_diag_items, r=matthewjasper
jonas-schievink Jan 25, 2021
21bdfcb
Rollup merge of #81299 - GuillaumeGomez:fix-eslint-detected-bugs, r=N…
jonas-schievink Jan 25, 2021
75563f1
Rollup merge of #81327 - RalfJung:codegen-no-const-fail, r=oli-obk
jonas-schievink Jan 25, 2021
e0414d5
Rollup merge of #81367 - andersk:join-test-threads, r=dtolnay
jonas-schievink Jan 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -188,8 +188,10 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(

fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut bx);

let mut all_consts_ok = true;
for const_ in &mir.required_consts {
if let Err(err) = fx.eval_mir_constant(const_) {
all_consts_ok = false;
match err {
// errored or at least linted
ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {}
@@ -199,6 +201,25 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
}
}
if !all_consts_ok {
// There is no delay_span_bug here, we just have to rely on a hard error actually having
// been raised.
bx.abort();
// `abort` does not terminate the block, so we still need to generate
// an `unreachable` terminator after it.
bx.unreachable();
// We also have to delete all the other basic blocks again...
for bb in mir.basic_blocks().indices() {
if bb != mir::START_BLOCK {
unsafe {
bx.delete_basic_block(fx.blocks[bb]);
}
}
}
// FIXME: Can we somehow avoid all this by evaluating the consts before creating the basic
// blocks? The tricky part is doing that without duplicating `fx.eval_mir_constant`.
return;
}

let memory_locals = analyze::non_ssa_locals(&fx);

25 changes: 4 additions & 21 deletions compiler/rustc_codegen_ssa/src/mir/operand.rs
Original file line number Diff line number Diff line change
@@ -6,9 +6,8 @@ use crate::glue;
use crate::traits::*;
use crate::MemFlags;

use rustc_errors::ErrorReported;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{ConstValue, ErrorHandled, Pointer, Scalar};
use rustc_middle::mir::interpret::{ConstValue, Pointer, Scalar};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::Ty;
use rustc_target::abi::{Abi, Align, LayoutOf, Size};
@@ -439,25 +438,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}

mir::Operand::Constant(ref constant) => {
self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|err| {
match err {
// errored or at least linted
ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {}
ErrorHandled::TooGeneric => {
bug!("codegen encountered polymorphic constant")
}
}
// Allow RalfJ to sleep soundly knowing that even refactorings that remove
// the above error (or silence it under some conditions) will not cause UB.
bx.abort();
// We still have to return an operand but it doesn't matter,
// this code is unreachable.
let ty = self.monomorphize(constant.literal.ty);
let layout = bx.cx().layout_of(ty);
bx.load_operand(PlaceRef::new_sized(
bx.cx().const_undef(bx.cx().type_ptr_to(bx.cx().backend_type(layout))),
layout,
))
// This cannot fail because we checked all required_consts in advance.
self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|_err| {
span_bug!(constant.span, "erroneous constant not captured by required_consts")
})
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use rustc_hir as hir;
use rustc_hir::Node;
use rustc_index::vec::Idx;
use rustc_middle::mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location};
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{
hir::place::PlaceBase,
mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location},
};
use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::Span;
@@ -241,6 +244,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
format!("mut {}", self.local_names[local].unwrap()),
Applicability::MachineApplicable,
);
let tcx = self.infcx.tcx;
if let ty::Closure(id, _) = the_place_err.ty(self.body, tcx).ty.kind() {
self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
}
}

// Also suggest adding mut for upvars
@@ -271,6 +278,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
);
}
}

let tcx = self.infcx.tcx;
if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
{
if let ty::Closure(id, _) = ty.kind() {
self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
}
}
}

// complete hack to approximate old AST-borrowck
@@ -463,6 +478,45 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
err.buffer(&mut self.errors_buffer);
}

// point to span of upvar making closure call require mutable borrow
fn show_mutating_upvar(
&self,
tcx: TyCtxt<'_>,
id: &hir::def_id::DefId,
the_place_err: PlaceRef<'tcx>,
err: &mut DiagnosticBuilder<'_>,
) {
let id = id.expect_local();
let tables = tcx.typeck(id);
let hir_id = tcx.hir().local_def_id_to_hir_id(id);
let (span, place) = &tables.closure_kind_origins()[hir_id];
let reason = if let PlaceBase::Upvar(upvar_id) = place.base {
let upvar = ty::place_to_string_for_capture(tcx, place);
match tables.upvar_capture(upvar_id) {
ty::UpvarCapture::ByRef(ty::UpvarBorrow {
kind: ty::BorrowKind::MutBorrow,
..
}) => {
format!("mutable borrow of `{}`", upvar)
}
ty::UpvarCapture::ByValue(_) => {
format!("possible mutation of `{}`", upvar)
}
_ => bug!("upvar `{}` borrowed, but not mutably", upvar),
}
} else {
bug!("not an upvar")
};
err.span_label(
*span,
format!(
"calling `{}` requires mutable binding due to {}",
self.describe_place(the_place_err).unwrap(),
reason
),
);
}

/// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) {
err.span_label(sp, format!("cannot {}", act));
4 changes: 4 additions & 0 deletions compiler/rustc_mir/src/interpret/operand.rs
Original file line number Diff line number Diff line change
@@ -511,6 +511,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Constant(ref constant) => {
let val =
self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal);
// This can still fail:
// * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
// checked yet.
// * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
self.const_to_op(val, layout)?
}
};
6 changes: 5 additions & 1 deletion compiler/rustc_mir/src/interpret/validity.rs
Original file line number Diff line number Diff line change
@@ -515,7 +515,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
Ok(true)
}
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
let value = self.ecx.read_scalar(value)?;
let value = try_validation!(
self.ecx.read_scalar(value),
self.path,
err_unsup!(ReadPointerAsBytes) => { "read of part of a pointer" },
);
// NOTE: Keep this in sync with the array optimization for int/float
// types below!
if self.ctfe_mode.is_some() {
3 changes: 3 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -622,6 +622,7 @@ symbols! {
intel,
into_iter,
into_result,
into_trait,
intra_doc_pointers,
intrinsics,
irrefutable_let_patterns,
@@ -1159,6 +1160,8 @@ symbols! {
truncf32,
truncf64,
try_blocks,
try_from_trait,
try_into_trait,
try_trait,
tt,
tuple,
Original file line number Diff line number Diff line change
@@ -286,21 +286,32 @@ fn suggest_restriction(
);
} else {
// Trivial case: `T` needs an extra bound: `T: Bound`.
let (sp, suggestion) = match super_traits {
None => predicate_constraint(
let (sp, suggestion) = match (
generics
.params
.iter()
.filter(
|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: Some(_), ..}),
)
.next(),
super_traits,
) {
(_, None) => predicate_constraint(
generics,
trait_ref.without_const().to_predicate(tcx).to_string(),
),
Some((ident, bounds)) => match bounds {
[.., bound] => (
bound.span().shrink_to_hi(),
format!(" + {}", trait_ref.print_only_trait_path().to_string()),
),
[] => (
ident.span.shrink_to_hi(),
format!(": {}", trait_ref.print_only_trait_path().to_string()),
),
},
(None, Some((ident, []))) => (
ident.span.shrink_to_hi(),
format!(": {}", trait_ref.print_only_trait_path().to_string()),
),
(_, Some((_, [.., bounds]))) => (
bounds.span().shrink_to_hi(),
format!(" + {}", trait_ref.print_only_trait_path().to_string()),
),
(Some(_), Some((_, []))) => (
generics.span.shrink_to_hi(),
format!(": {}", trait_ref.print_only_trait_path().to_string()),
),
};

err.span_suggestion_verbose(
3 changes: 3 additions & 0 deletions library/core/src/convert/mod.rs
Original file line number Diff line number Diff line change
@@ -267,6 +267,7 @@ pub trait AsMut<T: ?Sized> {
///
/// [`String`]: ../../std/string/struct.String.html
/// [`Vec`]: ../../std/vec/struct.Vec.html
#[rustc_diagnostic_item = "into_trait"]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Into<T>: Sized {
/// Performs the conversion.
@@ -382,6 +383,7 @@ pub trait From<T>: Sized {
///
/// This suffers the same restrictions and reasoning as implementing
/// [`Into`], see there for details.
#[rustc_diagnostic_item = "try_into_trait"]
#[stable(feature = "try_from", since = "1.34.0")]
pub trait TryInto<T>: Sized {
/// The type returned in the event of a conversion error.
@@ -462,6 +464,7 @@ pub trait TryInto<T>: Sized {
///
/// [`try_from`]: TryFrom::try_from
/// [`!`]: ../../std/primitive.never.html
#[rustc_diagnostic_item = "try_from_trait"]
#[stable(feature = "try_from", since = "1.34.0")]
pub trait TryFrom<T>: Sized {
/// The type returned in the event of a conversion error.
34 changes: 34 additions & 0 deletions library/core/src/option.rs
Original file line number Diff line number Diff line change
@@ -428,6 +428,40 @@ impl<T> Option<T> {
}
}

/// Returns the contained [`Some`] value, consuming the `self` value,
/// without checking that the value is not [`None`].
///
/// # Safety
///
/// Calling this method on [`None`] is *[undefined behavior]*.
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
///
/// # Examples
///
/// ```
/// #![feature(option_result_unwrap_unchecked)]
/// let x = Some("air");
/// assert_eq!(unsafe { x.unwrap_unchecked() }, "air");
/// ```
///
/// ```no_run
/// #![feature(option_result_unwrap_unchecked)]
/// let x: Option<&str> = None;
/// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); // Undefined behavior!
/// ```
#[inline]
#[track_caller]
#[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")]
pub unsafe fn unwrap_unchecked(self) -> T {
debug_assert!(self.is_some());
match self {
Some(val) => val,
// SAFETY: the safety contract must be upheld by the caller.
None => unsafe { hint::unreachable_unchecked() },
}
}

/////////////////////////////////////////////////////////////////////////
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
70 changes: 69 additions & 1 deletion library/core/src/result.rs
Original file line number Diff line number Diff line change
@@ -229,7 +229,7 @@

use crate::iter::{self, FromIterator, FusedIterator, TrustedLen};
use crate::ops::{self, Deref, DerefMut};
use crate::{convert, fmt};
use crate::{convert, fmt, hint};

/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).
///
@@ -821,6 +821,74 @@ impl<T, E> Result<T, E> {
Err(e) => op(e),
}
}

/// Returns the contained [`Ok`] value, consuming the `self` value,
/// without checking that the value is not an [`Err`].
///
/// # Safety
///
/// Calling this method on an [`Err`] is *[undefined behavior]*.
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
///
/// # Examples
///
/// ```
/// #![feature(option_result_unwrap_unchecked)]
/// let x: Result<u32, &str> = Ok(2);
/// assert_eq!(unsafe { x.unwrap_unchecked() }, 2);
/// ```
///
/// ```no_run
/// #![feature(option_result_unwrap_unchecked)]
/// let x: Result<u32, &str> = Err("emergency failure");
/// unsafe { x.unwrap_unchecked(); } // Undefined behavior!
/// ```
#[inline]
#[track_caller]
#[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")]
pub unsafe fn unwrap_unchecked(self) -> T {
debug_assert!(self.is_ok());
match self {
Ok(t) => t,
// SAFETY: the safety contract must be upheld by the caller.
Err(_) => unsafe { hint::unreachable_unchecked() },
}
}

/// Returns the contained [`Err`] value, consuming the `self` value,
/// without checking that the value is not an [`Ok`].
///
/// # Safety
///
/// Calling this method on an [`Ok`] is *[undefined behavior]*.
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
///
/// # Examples
///
/// ```no_run
/// #![feature(option_result_unwrap_unchecked)]
/// let x: Result<u32, &str> = Ok(2);
/// unsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
/// ```
///
/// ```
/// #![feature(option_result_unwrap_unchecked)]
/// let x: Result<u32, &str> = Err("emergency failure");
/// assert_eq!(unsafe { x.unwrap_err_unchecked() }, "emergency failure");
/// ```
#[inline]
#[track_caller]
#[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")]
pub unsafe fn unwrap_err_unchecked(self) -> E {
debug_assert!(self.is_err());
match self {
// SAFETY: the safety contract must be upheld by the caller.
Ok(_) => unsafe { hint::unreachable_unchecked() },
Err(e) => e,
}
}
}

impl<T: Copy, E> Result<&T, E> {
Loading