mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-02 07:22:42 +00:00
Auto merge of #99299 - Mark-Simulacrum:stable-next, r=Mark-Simulacrum
[stable] 1.62.1 release This bundles: * Windows: Fallback for overlapped I/O #98950 * don't succeed evaluate_obligation query if new opaque types were registered #98614 * Mitigate MMIO stale data vulnerability #98126 * Return a FxIndexSet in is_late_bound query. #99219 Also bumps the version number to 1.62.1 and includes a short release notes section for the release. r? `@Mark-Simulacrum`
This commit is contained in:
commit
e092d0b6b4
18
RELEASES.md
18
RELEASES.md
@ -1,3 +1,21 @@
|
||||
Version 1.62.1 (2022-07-19)
|
||||
==========================
|
||||
|
||||
Rust 1.62.1 addresses a few recent regressions in the compiler and standard
|
||||
library, and also mitigates a CPU vulnerability on Intel SGX.
|
||||
|
||||
* [The compiler fixed unsound function coercions involving `impl Trait` return types.][98608]
|
||||
* [The compiler fixed an incremental compilation bug with `async fn` lifetimes.][98890]
|
||||
* [Windows added a fallback for overlapped I/O in synchronous reads and writes.][98950]
|
||||
* [The `x86_64-fortanix-unknown-sgx` target added a mitigation for the
|
||||
MMIO stale data vulnerability][98126], advisory [INTEL-SA-00615].
|
||||
|
||||
[98608]: https://github.com/rust-lang/rust/issues/98608
|
||||
[98890]: https://github.com/rust-lang/rust/issues/98890
|
||||
[98950]: https://github.com/rust-lang/rust/pull/98950
|
||||
[98126]: https://github.com/rust-lang/rust/pull/98126
|
||||
[INTEL-SA-00615]: https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00615.html
|
||||
|
||||
Version 1.62.0 (2022-06-30)
|
||||
==========================
|
||||
|
||||
|
@ -929,6 +929,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
.region_constraints_added_in_snapshot(&snapshot.undo_snapshot)
|
||||
}
|
||||
|
||||
pub fn opaque_types_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'a, 'tcx>) -> bool {
|
||||
self.inner.borrow().undo_log.opaque_types_in_snapshot(&snapshot.undo_snapshot)
|
||||
}
|
||||
|
||||
pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) {
|
||||
self.inner.borrow_mut().unwrap_region_constraints().add_given(sub, sup);
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
}
|
||||
let (a, b) = if a_is_expected { (a, b) } else { (b, a) };
|
||||
let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() {
|
||||
ty::Opaque(def_id, substs) => {
|
||||
ty::Opaque(def_id, substs) if def_id.is_local() => {
|
||||
let origin = if self.defining_use_anchor.is_some() {
|
||||
// Check that this is `impl Trait` type is
|
||||
// declared by `parent_def_id` -- i.e., one whose
|
||||
|
@ -185,6 +185,10 @@ impl<'tcx> InferCtxtUndoLogs<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn opaque_types_in_snapshot(&self, s: &Snapshot<'tcx>) -> bool {
|
||||
self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..)))
|
||||
}
|
||||
|
||||
pub(crate) fn region_constraints(
|
||||
&self,
|
||||
) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
|
||||
|
@ -203,7 +203,7 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
|
||||
Some(&ProjectionCacheEntry::NormalizedTy { ref ty, complete: _ }) => {
|
||||
info!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty);
|
||||
let mut ty = ty.clone();
|
||||
if result == EvaluationResult::EvaluatedToOk {
|
||||
if result.must_apply_considering_regions() {
|
||||
ty.obligations = vec![];
|
||||
}
|
||||
map.insert(key, ProjectionCacheEntry::NormalizedTy { ty, complete: Some(result) });
|
||||
|
@ -85,6 +85,7 @@ macro_rules! arena_types {
|
||||
[] attribute: rustc_ast::Attribute,
|
||||
[] name_set: rustc_data_structures::fx::FxHashSet<rustc_span::symbol::Symbol>,
|
||||
[] hir_id_set: rustc_hir::HirIdSet,
|
||||
[] late_bound_lifetimes: rustc_data_structures::fx::FxIndexSet<rustc_hir::def_id::LocalDefId>,
|
||||
|
||||
// Interned types
|
||||
[] tys: rustc_data_structures::intern::WithStableHash<rustc_middle::ty::TyS<'tcx>>,
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::ty;
|
||||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::ItemLocalId;
|
||||
use rustc_macros::HashStable;
|
||||
@ -64,7 +64,7 @@ pub struct ResolveLifetimes {
|
||||
/// Set of lifetime def ids that are late-bound; a region can
|
||||
/// be late-bound if (a) it does NOT appear in a where-clause and
|
||||
/// (b) it DOES appear in the arguments.
|
||||
pub late_bound: FxHashMap<LocalDefId, FxHashSet<LocalDefId>>,
|
||||
pub late_bound: FxHashMap<LocalDefId, FxIndexSet<LocalDefId>>,
|
||||
|
||||
pub late_bound_vars: FxHashMap<LocalDefId, FxHashMap<ItemLocalId, Vec<ty::BoundVariableKind>>>,
|
||||
}
|
||||
|
@ -1537,7 +1537,7 @@ rustc_queries! {
|
||||
Option<&'tcx FxHashMap<ItemLocalId, Region>> {
|
||||
desc { "looking up a named region" }
|
||||
}
|
||||
query is_late_bound_map(_: LocalDefId) -> Option<(LocalDefId, &'tcx FxHashSet<LocalDefId>)> {
|
||||
query is_late_bound_map(_: LocalDefId) -> Option<(LocalDefId, &'tcx FxIndexSet<LocalDefId>)> {
|
||||
desc { "testing if a region is late bound" }
|
||||
}
|
||||
/// For a given item (like a struct), gets the default lifetimes to be used
|
||||
|
@ -176,6 +176,10 @@ pub enum EvaluationResult {
|
||||
EvaluatedToOk,
|
||||
/// Evaluation successful, but there were unevaluated region obligations.
|
||||
EvaluatedToOkModuloRegions,
|
||||
/// Evaluation successful, but need to rerun because opaque types got
|
||||
/// hidden types assigned without it being known whether the opaque types
|
||||
/// are within their defining scope
|
||||
EvaluatedToOkModuloOpaqueTypes,
|
||||
/// Evaluation is known to be ambiguous -- it *might* hold for some
|
||||
/// assignment of inference variables, but it might not.
|
||||
///
|
||||
@ -252,9 +256,11 @@ impl EvaluationResult {
|
||||
|
||||
pub fn may_apply(self) -> bool {
|
||||
match self {
|
||||
EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToUnknown => {
|
||||
true
|
||||
}
|
||||
EvaluatedToOkModuloOpaqueTypes
|
||||
| EvaluatedToOk
|
||||
| EvaluatedToOkModuloRegions
|
||||
| EvaluatedToAmbig
|
||||
| EvaluatedToUnknown => true,
|
||||
|
||||
EvaluatedToErr | EvaluatedToRecur => false,
|
||||
}
|
||||
@ -264,7 +270,11 @@ impl EvaluationResult {
|
||||
match self {
|
||||
EvaluatedToUnknown | EvaluatedToRecur => true,
|
||||
|
||||
EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToErr => false,
|
||||
EvaluatedToOkModuloOpaqueTypes
|
||||
| EvaluatedToOk
|
||||
| EvaluatedToOkModuloRegions
|
||||
| EvaluatedToAmbig
|
||||
| EvaluatedToErr => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1065,6 +1065,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
|
||||
Lift
|
||||
)]
|
||||
pub struct OpaqueTypeKey<'tcx> {
|
||||
// FIXME(oli-obk): make this a LocalDefId
|
||||
pub def_id: DefId,
|
||||
pub substs: SubstsRef<'tcx>,
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, T
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::steal::Steal;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
|
||||
use rustc_ast::walk_list;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::{struct_span_err, Applicability, Diagnostic};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
@ -482,6 +482,11 @@ fn convert_named_region_map(tcx: TyCtxt<'_>, named_region_map: NamedRegionMap) -
|
||||
let def_id = tcx.hir().local_def_id(hir_id);
|
||||
map.insert(def_id);
|
||||
}
|
||||
for (_, late_bound) in &mut rl.late_bound {
|
||||
late_bound.sort_by(|&a, &b| {
|
||||
tcx.def_path_hash(a.to_def_id()).cmp(&tcx.def_path_hash(b.to_def_id()))
|
||||
});
|
||||
}
|
||||
for (hir_id, v) in named_region_map.late_bound_vars {
|
||||
let map = rl.late_bound_vars.entry(hir_id.owner).or_default();
|
||||
map.insert(hir_id.local_id, v);
|
||||
@ -540,7 +545,7 @@ fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> LocalDefId {
|
||||
fn is_late_bound_map<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) -> Option<(LocalDefId, &'tcx FxHashSet<LocalDefId>)> {
|
||||
) -> Option<(LocalDefId, &'tcx FxIndexSet<LocalDefId>)> {
|
||||
match tcx.def_kind(def_id) {
|
||||
DefKind::AnonConst | DefKind::InlineConst => {
|
||||
let mut def_id = tcx.local_parent(def_id);
|
||||
|
@ -761,6 +761,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
Ok(
|
||||
EvaluationResult::EvaluatedToOk
|
||||
| EvaluationResult::EvaluatedToOkModuloRegions
|
||||
| EvaluationResult::EvaluatedToOkModuloOpaqueTypes
|
||||
| EvaluationResult::EvaluatedToAmbig,
|
||||
) => {}
|
||||
_ => return false,
|
||||
|
@ -388,6 +388,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
Err(_) => return Ok(EvaluatedToErr),
|
||||
}
|
||||
|
||||
if self.infcx.opaque_types_added_in_snapshot(snapshot) {
|
||||
return Ok(result.max(EvaluatedToOkModuloOpaqueTypes));
|
||||
}
|
||||
|
||||
match self.infcx.region_constraints_added_in_snapshot(snapshot) {
|
||||
None => Ok(result),
|
||||
Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)),
|
||||
|
@ -61,14 +61,6 @@ bitflags! {
|
||||
| TypeFlags::HAS_CT_INFER.bits
|
||||
| TypeFlags::HAS_TY_PLACEHOLDER.bits
|
||||
| TypeFlags::HAS_CT_PLACEHOLDER.bits
|
||||
// The `evaluate_obligation` query does not return further
|
||||
// obligations. If it evaluates an obligation with an opaque
|
||||
// type, that opaque type may get compared to another type,
|
||||
// constraining it. We would lose this information.
|
||||
// FIXME: differentiate between crate-local opaque types
|
||||
// and opaque types from other crates, as only opaque types
|
||||
// from the local crate can possibly be a local name
|
||||
| TypeFlags::HAS_TY_OPAQUE.bits
|
||||
// We consider 'freshened' types and constants
|
||||
// to depend on a particular fn.
|
||||
// The freshening process throws away information,
|
||||
|
@ -1,13 +1,16 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use crate::arch::asm;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::cmp;
|
||||
use crate::convert::TryInto;
|
||||
use crate::mem;
|
||||
use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut};
|
||||
use crate::ptr::{self, NonNull};
|
||||
use crate::slice;
|
||||
use crate::slice::SliceIndex;
|
||||
|
||||
use super::super::mem::is_user_range;
|
||||
use super::super::mem::{is_enclave_range, is_user_range};
|
||||
use fortanix_sgx_abi::*;
|
||||
|
||||
/// A type that can be safely read from or written to userspace.
|
||||
@ -210,7 +213,9 @@ where
|
||||
unsafe {
|
||||
// Mustn't call alloc with size 0.
|
||||
let ptr = if size > 0 {
|
||||
rtunwrap!(Ok, super::alloc(size, T::align_of())) as _
|
||||
// `copy_to_userspace` is more efficient when data is 8-byte aligned
|
||||
let alignment = cmp::max(T::align_of(), 8);
|
||||
rtunwrap!(Ok, super::alloc(size, alignment)) as _
|
||||
} else {
|
||||
T::align_of() as _ // dangling pointer ok for size 0
|
||||
};
|
||||
@ -225,13 +230,9 @@ where
|
||||
/// Copies `val` into freshly allocated space in user memory.
|
||||
pub fn new_from_enclave(val: &T) -> Self {
|
||||
unsafe {
|
||||
let ret = Self::new_uninit_bytes(mem::size_of_val(val));
|
||||
ptr::copy(
|
||||
val as *const T as *const u8,
|
||||
ret.0.as_ptr() as *mut u8,
|
||||
mem::size_of_val(val),
|
||||
);
|
||||
ret
|
||||
let mut user = Self::new_uninit_bytes(mem::size_of_val(val));
|
||||
user.copy_from_enclave(val);
|
||||
user
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,6 +305,105 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies `len` bytes of data from enclave pointer `src` to userspace `dst`
|
||||
///
|
||||
/// This function mitigates stale data vulnerabilities by ensuring all writes to untrusted memory are either:
|
||||
/// - preceded by the VERW instruction and followed by the MFENCE; LFENCE instruction sequence
|
||||
/// - or are in multiples of 8 bytes, aligned to an 8-byte boundary
|
||||
///
|
||||
/// # Panics
|
||||
/// This function panics if:
|
||||
///
|
||||
/// * The `src` pointer is null
|
||||
/// * The `dst` pointer is null
|
||||
/// * The `src` memory range is not in enclave memory
|
||||
/// * The `dst` memory range is not in user memory
|
||||
///
|
||||
/// # References
|
||||
/// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00615.html
|
||||
/// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/processor-mmio-stale-data-vulnerabilities.html#inpage-nav-3-2-2
|
||||
pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
|
||||
unsafe fn copy_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
|
||||
unsafe {
|
||||
let mut seg_sel: u16 = 0;
|
||||
for off in 0..len {
|
||||
asm!("
|
||||
mov %ds, ({seg_sel})
|
||||
verw ({seg_sel})
|
||||
movb {val}, ({dst})
|
||||
mfence
|
||||
lfence
|
||||
",
|
||||
val = in(reg_byte) *src.offset(off as isize),
|
||||
dst = in(reg) dst.offset(off as isize),
|
||||
seg_sel = in(reg) &mut seg_sel,
|
||||
options(nostack, att_syntax)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn copy_aligned_quadwords_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
|
||||
unsafe {
|
||||
asm!(
|
||||
"rep movsq (%rsi), (%rdi)",
|
||||
inout("rcx") len / 8 => _,
|
||||
inout("rdi") dst => _,
|
||||
inout("rsi") src => _,
|
||||
options(att_syntax, nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
}
|
||||
assert!(!src.is_null());
|
||||
assert!(!dst.is_null());
|
||||
assert!(is_enclave_range(src, len));
|
||||
assert!(is_user_range(dst, len));
|
||||
assert!(len < isize::MAX as usize);
|
||||
assert!(!(src as usize).overflowing_add(len).1);
|
||||
assert!(!(dst as usize).overflowing_add(len).1);
|
||||
|
||||
if len < 8 {
|
||||
// Can't align on 8 byte boundary: copy safely byte per byte
|
||||
unsafe {
|
||||
copy_bytewise_to_userspace(src, dst, len);
|
||||
}
|
||||
} else if len % 8 == 0 && dst as usize % 8 == 0 {
|
||||
// Copying 8-byte aligned quadwords: copy quad word per quad word
|
||||
unsafe {
|
||||
copy_aligned_quadwords_to_userspace(src, dst, len);
|
||||
}
|
||||
} else {
|
||||
// Split copies into three parts:
|
||||
// +--------+
|
||||
// | small0 | Chunk smaller than 8 bytes
|
||||
// +--------+
|
||||
// | big | Chunk 8-byte aligned, and size a multiple of 8 bytes
|
||||
// +--------+
|
||||
// | small1 | Chunk smaller than 8 bytes
|
||||
// +--------+
|
||||
|
||||
unsafe {
|
||||
// Copy small0
|
||||
let small0_size = (8 - dst as usize % 8) as u8;
|
||||
let small0_src = src;
|
||||
let small0_dst = dst;
|
||||
copy_bytewise_to_userspace(small0_src as _, small0_dst, small0_size as _);
|
||||
|
||||
// Copy big
|
||||
let small1_size = ((len - small0_size as usize) % 8) as u8;
|
||||
let big_size = len - small0_size as usize - small1_size as usize;
|
||||
let big_src = src.offset(small0_size as _);
|
||||
let big_dst = dst.offset(small0_size as _);
|
||||
copy_aligned_quadwords_to_userspace(big_src as _, big_dst, big_size);
|
||||
|
||||
// Copy small1
|
||||
let small1_src = src.offset(big_size as isize + small0_size as isize);
|
||||
let small1_dst = dst.offset(big_size as isize + small0_size as isize);
|
||||
copy_bytewise_to_userspace(small1_src, small1_dst, small1_size as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
impl<T: ?Sized> UserRef<T>
|
||||
where
|
||||
@ -352,7 +452,7 @@ where
|
||||
pub fn copy_from_enclave(&mut self, val: &T) {
|
||||
unsafe {
|
||||
assert_eq!(mem::size_of_val(val), mem::size_of_val(&*self.0.get()));
|
||||
ptr::copy(
|
||||
copy_to_userspace(
|
||||
val as *const T as *const u8,
|
||||
self.0.get() as *mut T as *mut u8,
|
||||
mem::size_of_val(val),
|
||||
|
@ -6,6 +6,8 @@ use crate::time::{Duration, Instant};
|
||||
pub(crate) mod alloc;
|
||||
#[macro_use]
|
||||
pub(crate) mod raw;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use self::raw::*;
|
||||
|
||||
|
30
library/std/src/sys/sgx/abi/usercalls/tests.rs
Normal file
30
library/std/src/sys/sgx/abi/usercalls/tests.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use super::alloc::copy_to_userspace;
|
||||
use super::alloc::User;
|
||||
|
||||
#[test]
|
||||
fn test_copy_function() {
|
||||
let mut src = [0u8; 100];
|
||||
let mut dst = User::<[u8]>::uninitialized(100);
|
||||
|
||||
for i in 0..src.len() {
|
||||
src[i] = i as _;
|
||||
}
|
||||
|
||||
for size in 0..48 {
|
||||
// For all possible alignment
|
||||
for offset in 0..8 {
|
||||
// overwrite complete dst
|
||||
dst.copy_from_enclave(&[0u8; 100]);
|
||||
|
||||
// Copy src[0..size] to dst + offset
|
||||
unsafe { copy_to_userspace(src.as_ptr(), dst.as_mut_ptr().offset(offset), size) };
|
||||
|
||||
// Verify copy
|
||||
for byte in 0..size {
|
||||
unsafe {
|
||||
assert_eq!(*dst.as_ptr().offset(offset + byte as isize), src[byte as usize]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -325,7 +325,9 @@ union IO_STATUS_BLOCK_union {
|
||||
}
|
||||
impl Default for IO_STATUS_BLOCK_union {
|
||||
fn default() -> Self {
|
||||
Self { Pointer: ptr::null_mut() }
|
||||
let mut this = Self { Pointer: ptr::null_mut() };
|
||||
this.Status = STATUS_PENDING;
|
||||
this
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
@ -334,6 +336,16 @@ pub struct IO_STATUS_BLOCK {
|
||||
u: IO_STATUS_BLOCK_union,
|
||||
pub Information: usize,
|
||||
}
|
||||
impl IO_STATUS_BLOCK {
|
||||
pub fn status(&self) -> NTSTATUS {
|
||||
// SAFETY: If `self.u.Status` was set then this is obviously safe.
|
||||
// If `self.u.Pointer` was set then this is the equivalent to converting
|
||||
// the pointer to an integer, which is also safe.
|
||||
// Currently the only safe way to construct `IO_STATUS_BLOCK` outside of
|
||||
// this module is to call the `default` method, which sets the `Status`.
|
||||
unsafe { self.u.Status }
|
||||
}
|
||||
}
|
||||
|
||||
pub type LPOVERLAPPED_COMPLETION_ROUTINE = unsafe extern "system" fn(
|
||||
dwErrorCode: DWORD,
|
||||
|
@ -1,5 +1,8 @@
|
||||
#![unstable(issue = "none", feature = "windows_handle")]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::cmp;
|
||||
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf};
|
||||
use crate::mem;
|
||||
@ -248,14 +251,18 @@ impl Handle {
|
||||
offset.map(|n| n as _).as_ref(),
|
||||
None,
|
||||
);
|
||||
|
||||
let status = if status == c::STATUS_PENDING {
|
||||
c::WaitForSingleObject(self.as_raw_handle(), c::INFINITE);
|
||||
io_status.status()
|
||||
} else {
|
||||
status
|
||||
};
|
||||
match status {
|
||||
// If the operation has not completed then abort the process.
|
||||
// Doing otherwise means that the buffer and stack may be written to
|
||||
// after this function returns.
|
||||
c::STATUS_PENDING => {
|
||||
eprintln!("I/O error: operation failed to complete synchronously");
|
||||
crate::process::abort();
|
||||
}
|
||||
c::STATUS_PENDING => rtabort!("I/O error: operation failed to complete synchronously"),
|
||||
|
||||
// Return `Ok(0)` when there's nothing more to read.
|
||||
c::STATUS_END_OF_FILE => Ok(0),
|
||||
@ -294,13 +301,17 @@ impl Handle {
|
||||
None,
|
||||
)
|
||||
};
|
||||
let status = if status == c::STATUS_PENDING {
|
||||
unsafe { c::WaitForSingleObject(self.as_raw_handle(), c::INFINITE) };
|
||||
io_status.status()
|
||||
} else {
|
||||
status
|
||||
};
|
||||
match status {
|
||||
// If the operation has not completed then abort the process.
|
||||
// Doing otherwise means that the buffer may be read and the stack
|
||||
// written to after this function returns.
|
||||
c::STATUS_PENDING => {
|
||||
rtabort!("I/O error: operation failed to complete synchronously");
|
||||
}
|
||||
c::STATUS_PENDING => rtabort!("I/O error: operation failed to complete synchronously"),
|
||||
|
||||
// Success!
|
||||
status if c::nt_success(status) => Ok(io_status.Information),
|
||||
|
22
library/std/src/sys/windows/handle/tests.rs
Normal file
22
library/std/src/sys/windows/handle/tests.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use crate::sys::pipe::{anon_pipe, Pipes};
|
||||
use crate::{thread, time};
|
||||
|
||||
/// Test the synchronous fallback for overlapped I/O.
|
||||
#[test]
|
||||
fn overlapped_handle_fallback() {
|
||||
// Create some pipes. `ours` will be asynchronous.
|
||||
let Pipes { ours, theirs } = anon_pipe(true, false).unwrap();
|
||||
|
||||
let async_readable = ours.into_handle();
|
||||
let sync_writeable = theirs.into_handle();
|
||||
|
||||
thread::scope(|_| {
|
||||
thread::sleep(time::Duration::from_millis(100));
|
||||
sync_writeable.write(b"hello world!").unwrap();
|
||||
});
|
||||
|
||||
// The pipe buffer starts empty so reading won't complete synchronously unless
|
||||
// our fallback path works.
|
||||
let mut buffer = [0u8; 1024];
|
||||
async_readable.read(&mut buffer).unwrap();
|
||||
}
|
19
src/test/incremental/async-lifetimes.rs
Normal file
19
src/test/incremental/async-lifetimes.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// revisions: rpass1 rpass2
|
||||
// edition:2021
|
||||
|
||||
// See https://github.com/rust-lang/rust/issues/98890
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
async fn f(&self, _: &&()) -> &() {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rpass2)]
|
||||
enum Bar {}
|
||||
|
||||
fn main() {}
|
81
src/test/ui-fulldeps/issue-81357-unsound-file-methods.rs
Normal file
81
src/test/ui-fulldeps/issue-81357-unsound-file-methods.rs
Normal file
@ -0,0 +1,81 @@
|
||||
// run-fail
|
||||
// only-windows
|
||||
|
||||
fn main() {
|
||||
use std::fs;
|
||||
use std::io::prelude::*;
|
||||
use std::os::windows::prelude::*;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
const FILE_FLAG_OVERLAPPED: u32 = 0x40000000;
|
||||
|
||||
fn create_pipe_server(path: &str) -> fs::File {
|
||||
let mut path0 = path.as_bytes().to_owned();
|
||||
path0.push(0);
|
||||
extern "system" {
|
||||
fn CreateNamedPipeA(
|
||||
lpName: *const u8,
|
||||
dwOpenMode: u32,
|
||||
dwPipeMode: u32,
|
||||
nMaxInstances: u32,
|
||||
nOutBufferSize: u32,
|
||||
nInBufferSize: u32,
|
||||
nDefaultTimeOut: u32,
|
||||
lpSecurityAttributes: *mut u8,
|
||||
) -> RawHandle;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let h = CreateNamedPipeA(path0.as_ptr(), 3, 0, 1, 0, 0, 0, ptr::null_mut());
|
||||
assert_ne!(h as isize, -1);
|
||||
fs::File::from_raw_handle(h)
|
||||
}
|
||||
}
|
||||
|
||||
let path = "\\\\.\\pipe\\repro";
|
||||
let mut server = create_pipe_server(path);
|
||||
|
||||
let client = Arc::new(
|
||||
fs::OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).read(true).open(path).unwrap(),
|
||||
);
|
||||
|
||||
let spawn_read = |is_first: bool| {
|
||||
thread::spawn({
|
||||
let f = client.clone();
|
||||
move || {
|
||||
let mut buf = [0xcc; 1];
|
||||
let mut f = f.as_ref();
|
||||
f.read(&mut buf).unwrap();
|
||||
if is_first {
|
||||
assert_ne!(buf[0], 0xcc);
|
||||
} else {
|
||||
let b = buf[0]; // capture buf[0]
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
|
||||
// Check the buffer hasn't been written to after read.
|
||||
dbg!(buf[0], b);
|
||||
assert_eq!(buf[0], b);
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let t1 = spawn_read(true);
|
||||
thread::sleep(Duration::from_millis(20));
|
||||
let t2 = spawn_read(false);
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
let _ = server.write(b"x");
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
let _ = server.write(b"y");
|
||||
|
||||
// This is run fail because we need to test for the `abort`.
|
||||
// That failing to run is the success case.
|
||||
if t1.join().is_err() || t2.join().is_err() {
|
||||
return;
|
||||
} else {
|
||||
panic!("success");
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ fn main() {
|
||||
// return type, which can't depend on the obligation.
|
||||
fn cycle1() -> impl Clone {
|
||||
//~^ ERROR cycle detected
|
||||
//~| ERROR cycle detected
|
||||
send(cycle2().clone());
|
||||
|
||||
Rc::new(Cell::new(5))
|
||||
|
@ -30,45 +30,47 @@ note: ...which requires building MIR for `cycle1`...
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires type-checking `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
--> $DIR/auto-trait-leak.rs:14:5
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | send(cycle2().clone());
|
||||
| ^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `impl core::clone::Clone: core::marker::Send`...
|
||||
note: ...which requires computing type of `cycle2::{opaque#0}`...
|
||||
--> $DIR/auto-trait-leak.rs:20:16
|
||||
--> $DIR/auto-trait-leak.rs:19:16
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^
|
||||
note: ...which requires borrow-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires processing `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires processing MIR for `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires unsafety-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building MIR for `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires type-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
--> $DIR/auto-trait-leak.rs:20:5
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | send(cycle1().clone());
|
||||
| ^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `impl core::clone::Clone: core::marker::Send`...
|
||||
= note: ...which again requires computing type of `cycle1::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in top-level module
|
||||
--> $DIR/auto-trait-leak.rs:1:1
|
||||
@ -82,90 +84,6 @@ LL | | Rc::new(String::from("foo"))
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error[E0391]: cycle detected when computing type of `cycle1::{opaque#0}`
|
||||
--> $DIR/auto-trait-leak.rs:12:16
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires borrow-checking `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires processing `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires processing MIR for `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires unsafety-checking `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building MIR for `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires type-checking `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires computing type of `cycle2::{opaque#0}`...
|
||||
--> $DIR/auto-trait-leak.rs:20:16
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^
|
||||
note: ...which requires borrow-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires processing `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires processing MIR for `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires unsafety-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building MIR for `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires type-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: ...which again requires computing type of `cycle1::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in top-level module
|
||||
--> $DIR/auto-trait-leak.rs:1:1
|
||||
|
|
||||
LL | / use std::cell::Cell;
|
||||
LL | | use std::rc::Rc;
|
||||
LL | |
|
||||
LL | | fn send<T: Send>(_: T) {}
|
||||
... |
|
||||
LL | | Rc::new(String::from("foo"))
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0391`.
|
||||
|
@ -6,7 +6,6 @@
|
||||
mod m {
|
||||
type Foo = impl std::fmt::Debug;
|
||||
//~^ ERROR: cycle detected when computing type of `m::Foo::{opaque#0}` [E0391]
|
||||
//~| ERROR: cycle detected when computing type of `m::Foo::{opaque#0}` [E0391]
|
||||
|
||||
pub fn foo() -> Foo {
|
||||
22_u32
|
||||
|
@ -5,10 +5,11 @@ LL | type Foo = impl std::fmt::Debug;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires type-checking `m::bar`...
|
||||
--> $DIR/auto-trait-leakage3.rs:15:5
|
||||
--> $DIR/auto-trait-leakage3.rs:15:9
|
||||
|
|
||||
LL | pub fn bar() {
|
||||
| ^^^^^^^^^^^^
|
||||
LL | is_send(foo());
|
||||
| ^^^^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `m::Foo: core::marker::Send`...
|
||||
= note: ...which again requires computing type of `m::Foo::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in module `m`
|
||||
--> $DIR/auto-trait-leakage3.rs:6:1
|
||||
@ -16,24 +17,6 @@ note: cycle used when checking item types in module `m`
|
||||
LL | mod m {
|
||||
| ^^^^^
|
||||
|
||||
error[E0391]: cycle detected when computing type of `m::Foo::{opaque#0}`
|
||||
--> $DIR/auto-trait-leakage3.rs:7:16
|
||||
|
|
||||
LL | type Foo = impl std::fmt::Debug;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires type-checking `m::bar`...
|
||||
--> $DIR/auto-trait-leakage3.rs:15:5
|
||||
|
|
||||
LL | pub fn bar() {
|
||||
| ^^^^^^^^^^^^
|
||||
= note: ...which again requires computing type of `m::Foo::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in module `m`
|
||||
--> $DIR/auto-trait-leakage3.rs:6:1
|
||||
|
|
||||
LL | mod m {
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0391`.
|
||||
|
@ -4,7 +4,6 @@
|
||||
mod m {
|
||||
type Foo = impl std::fmt::Debug;
|
||||
//~^ ERROR cycle detected
|
||||
//~| ERROR cycle detected
|
||||
|
||||
// Cycle: error today, but it'd be nice if it eventually worked
|
||||
|
||||
|
@ -5,10 +5,11 @@ LL | type Foo = impl std::fmt::Debug;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires type-checking `m::bar`...
|
||||
--> $DIR/inference-cycle.rs:15:5
|
||||
--> $DIR/inference-cycle.rs:15:9
|
||||
|
|
||||
LL | pub fn bar() {
|
||||
| ^^^^^^^^^^^^
|
||||
LL | is_send(foo()); // Today: error
|
||||
| ^^^^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `m::Foo: core::marker::Send`...
|
||||
= note: ...which again requires computing type of `m::Foo::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in module `m`
|
||||
--> $DIR/inference-cycle.rs:4:1
|
||||
@ -16,24 +17,6 @@ note: cycle used when checking item types in module `m`
|
||||
LL | mod m {
|
||||
| ^^^^^
|
||||
|
||||
error[E0391]: cycle detected when computing type of `m::Foo::{opaque#0}`
|
||||
--> $DIR/inference-cycle.rs:5:16
|
||||
|
|
||||
LL | type Foo = impl std::fmt::Debug;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires type-checking `m::bar`...
|
||||
--> $DIR/inference-cycle.rs:15:5
|
||||
|
|
||||
LL | pub fn bar() {
|
||||
| ^^^^^^^^^^^^
|
||||
= note: ...which again requires computing type of `m::Foo::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in module `m`
|
||||
--> $DIR/inference-cycle.rs:4:1
|
||||
|
|
||||
LL | mod m {
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0391`.
|
||||
|
13
src/test/ui/type-alias-impl-trait/issue-98604.rs
Normal file
13
src/test/ui/type-alias-impl-trait/issue-98604.rs
Normal file
@ -0,0 +1,13 @@
|
||||
// edition:2018
|
||||
|
||||
type AsyncFnPtr = Box<
|
||||
dyn Fn() -> std::pin::Pin<Box<dyn std::future::Future<Output = ()>>>,
|
||||
>;
|
||||
|
||||
async fn test() {}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
fn main() {
|
||||
Box::new(test) as AsyncFnPtr;
|
||||
//~^ ERROR type mismatch
|
||||
}
|
18
src/test/ui/type-alias-impl-trait/issue-98604.stderr
Normal file
18
src/test/ui/type-alias-impl-trait/issue-98604.stderr
Normal file
@ -0,0 +1,18 @@
|
||||
error[E0271]: type mismatch resolving `<fn() -> impl Future<Output = ()> {test} as FnOnce<()>>::Output == Pin<Box<(dyn Future<Output = ()> + 'static)>>`
|
||||
--> $DIR/issue-98604.rs:11:5
|
||||
|
|
||||
LL | Box::new(test) as AsyncFnPtr;
|
||||
| ^^^^^^^^^^^^^^ expected struct `Pin`, found opaque type
|
||||
|
|
||||
note: while checking the return type of the `async fn`
|
||||
--> $DIR/issue-98604.rs:7:17
|
||||
|
|
||||
LL | async fn test() {}
|
||||
| ^ checked the `Output` of this `async fn`, found opaque type
|
||||
= note: expected struct `Pin<Box<(dyn Future<Output = ()> + 'static)>>`
|
||||
found opaque type `impl Future<Output = ()>`
|
||||
= note: required for the cast to the object type `dyn Fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>>`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0271`.
|
9
src/test/ui/type-alias-impl-trait/issue-98608.rs
Normal file
9
src/test/ui/type-alias-impl-trait/issue-98608.rs
Normal file
@ -0,0 +1,9 @@
|
||||
fn hi() -> impl Sized { std::ptr::null::<u8>() }
|
||||
|
||||
fn main() {
|
||||
let b: Box<dyn Fn() -> Box<u8>> = Box::new(hi);
|
||||
//~^ ERROR type mismatch resolving `<fn() -> impl Sized {hi} as FnOnce<()>>::Output == Box<u8>`
|
||||
let boxed = b();
|
||||
let null = *boxed;
|
||||
println!("{null:?}");
|
||||
}
|
16
src/test/ui/type-alias-impl-trait/issue-98608.stderr
Normal file
16
src/test/ui/type-alias-impl-trait/issue-98608.stderr
Normal file
@ -0,0 +1,16 @@
|
||||
error[E0271]: type mismatch resolving `<fn() -> impl Sized {hi} as FnOnce<()>>::Output == Box<u8>`
|
||||
--> $DIR/issue-98608.rs:4:39
|
||||
|
|
||||
LL | fn hi() -> impl Sized { std::ptr::null::<u8>() }
|
||||
| ---------- the found opaque type
|
||||
...
|
||||
LL | let b: Box<dyn Fn() -> Box<u8>> = Box::new(hi);
|
||||
| ^^^^^^^^^^^^ expected struct `Box`, found opaque type
|
||||
|
|
||||
= note: expected struct `Box<u8>`
|
||||
found opaque type `impl Sized`
|
||||
= note: required for the cast to the object type `dyn Fn() -> Box<u8>`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0271`.
|
@ -4,7 +4,6 @@ use std::fmt::Debug;
|
||||
|
||||
type Foo = impl Debug;
|
||||
//~^ ERROR cycle detected
|
||||
//~| ERROR cycle detected
|
||||
|
||||
fn is_send<T: Send>() { }
|
||||
|
||||
|
@ -5,10 +5,11 @@ LL | type Foo = impl Debug;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires type-checking `not_good`...
|
||||
--> $DIR/reveal_local.rs:11:1
|
||||
--> $DIR/reveal_local.rs:13:5
|
||||
|
|
||||
LL | fn not_good() {
|
||||
| ^^^^^^^^^^^^^
|
||||
LL | is_send::<Foo>();
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `Foo: core::marker::Send`...
|
||||
= note: ...which again requires computing type of `Foo::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in top-level module
|
||||
--> $DIR/reveal_local.rs:1:1
|
||||
@ -22,30 +23,6 @@ LL | |
|
||||
LL | | fn main() {}
|
||||
| |____________^
|
||||
|
||||
error[E0391]: cycle detected when computing type of `Foo::{opaque#0}`
|
||||
--> $DIR/reveal_local.rs:5:12
|
||||
|
|
||||
LL | type Foo = impl Debug;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires type-checking `not_gooder`...
|
||||
--> $DIR/reveal_local.rs:17:1
|
||||
|
|
||||
LL | fn not_gooder() {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
= note: ...which again requires computing type of `Foo::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in top-level module
|
||||
--> $DIR/reveal_local.rs:1:1
|
||||
|
|
||||
LL | / #![feature(type_alias_impl_trait)]
|
||||
LL | |
|
||||
LL | | use std::fmt::Debug;
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | fn main() {}
|
||||
| |____________^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0391`.
|
||||
|
@ -1 +1 @@
|
||||
1.62.0
|
||||
1.62.1
|
||||
|
Loading…
Reference in New Issue
Block a user