Skip to content

[beta] rollup #99300

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

Merged
merged 8 commits into from
Jul 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 27 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param_mode: ParamMode,
itctx: ImplTraitContext,
) -> hir::Ty<'hir> {
// Check whether we should interpret this as a bare trait object.
// This check mirrors the one in late resolution. We only introduce this special case in
// the rare occurence we need to lower `Fresh` anonymous lifetimes.
// The other cases when a qpath should be opportunistically made a trait object are handled
// by `ty_path`.
if qself.is_none()
&& let Some(partial_res) = self.resolver.get_partial_res(t.id)
&& partial_res.unresolved_segments() == 0
&& let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = partial_res.base_res()
{
let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| {
let bound = this.lower_poly_trait_ref(
&PolyTraitRef {
bound_generic_params: vec![],
trait_ref: TraitRef { path: path.clone(), ref_id: t.id },
span: t.span
},
itctx,
);
let bounds = this.arena.alloc_from_iter([bound]);
let lifetime_bound = this.elided_dyn_bound(t.span);
(bounds, lifetime_bound)
});
let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None);
return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() };
}

let id = self.lower_node_id(t.id);
let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx);
self.ty_path(id, t.span, qpath)
Expand Down
38 changes: 33 additions & 5 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// The idea then is to lower the `T: 'X` constraint into multiple
/// bounds -- e.g., if `'X` is the union of two free lifetimes,
/// `'1` and `'2`, then we would create `T: '1` and `T: '2`.
#[instrument(level = "debug", skip(self, infcx, propagated_outlives_requirements))]
fn try_promote_type_test(
&self,
infcx: &InferCtxt<'_, 'tcx>,
Expand All @@ -933,11 +934,41 @@ impl<'tcx> RegionInferenceContext<'tcx> {
return false;
};

debug!("subject = {:?}", subject);

let r_scc = self.constraint_sccs.scc(*lower_bound);

debug!(
"lower_bound = {:?} r_scc={:?} universe={:?}",
lower_bound, r_scc, self.scc_universes[r_scc]
);

// If the type test requires that `T: 'a` where `'a` is a
// placeholder from another universe, that effectively requires
// `T: 'static`, so we have to propagate that requirement.
//
// It doesn't matter *what* universe because the promoted `T` will
// always be in the root universe.
if let Some(p) = self.scc_values.placeholders_contained_in(r_scc).next() {
debug!("encountered placeholder in higher universe: {:?}, requiring 'static", p);
let static_r = self.universal_regions.fr_static;
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
subject,
outlived_free_region: static_r,
blame_span: locations.span(body),
category: ConstraintCategory::Boring,
});

// we can return here -- the code below might push add'l constraints
// but they would all be weaker than this one.
return true;
}

// For each region outlived by lower_bound find a non-local,
// universal region (it may be the same region) and add it to
// `ClosureOutlivesRequirement`.
let r_scc = self.constraint_sccs.scc(*lower_bound);
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
debug!("universal_region_outlived_by ur={:?}", ur);
// Check whether we can already prove that the "subject" outlives `ur`.
// If so, we don't have to propagate this requirement to our caller.
//
Expand All @@ -962,8 +993,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
continue;
}

debug!("try_promote_type_test: ur={:?}", ur);

let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur);
debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub);

Expand Down Expand Up @@ -1000,15 +1029,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// will use it's *external name*, which will be a `RegionKind`
/// variant that can be used in query responses such as
/// `ReEarlyBound`.
#[instrument(level = "debug", skip(self, infcx))]
fn try_promote_type_test_subject(
&self,
infcx: &InferCtxt<'_, 'tcx>,
ty: Ty<'tcx>,
) -> Option<ClosureOutlivesSubject<'tcx>> {
let tcx = infcx.tcx;

debug!("try_promote_type_test_subject(ty = {:?})", ty);

let ty = tcx.fold_regions(ty, &mut false, |r, _depth| {
let region_vid = self.to_region_vid(r);

Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,30 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
TyKind::Path(ref qself, ref path) => {
self.diagnostic_metadata.current_type_path = Some(ty);
self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type);

// Check whether we should interpret this as a bare trait object.
if qself.is_none()
&& let Some(partial_res) = self.r.partial_res_map.get(&ty.id)
&& partial_res.unresolved_segments() == 0
&& let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = partial_res.base_res()
{
// This path is actually a bare trait object. In case of a bare `Fn`-trait
// object with anonymous lifetimes, we need this rib to correctly place the
// synthetic lifetimes.
let span = ty.span.shrink_to_lo().to(path.span.shrink_to_lo());
self.with_generic_param_rib(
&[],
NormalRibKind,
LifetimeRibKind::Generics {
binder: ty.id,
kind: LifetimeBinderKind::PolyTrait,
span,
},
|this| this.visit_path(&path, ty.id),
);
self.diagnostic_metadata.current_type_path = prev_ty;
return;
}
}
TyKind::ImplicitSelf => {
let self_ty = Ident::with_dummy_span(kw::SelfUpper);
Expand Down
34 changes: 13 additions & 21 deletions compiler/rustc_typeck/src/check/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_index::vec::Idx;
use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
use rustc_middle::ty::{self, Article, FloatTy, InferTy, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
use rustc_session::lint;
use rustc_span::{Span, Symbol, DUMMY_SP};
use rustc_target::abi::{Pointer, VariantIdx};
Expand Down Expand Up @@ -99,8 +99,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}

// FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
// Type still may have region variables, but `Sized` does not depend
// on those, so just erase them before querying.
if self.tcx.erase_regions(ty).is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
return true;
}
if let ty::Foreign(..) = ty.kind() {
Expand Down Expand Up @@ -128,30 +131,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
64 => InlineAsmType::I64,
_ => unreachable!(),
};

// Expect types to be fully resolved, no const or type variables.
if ty.has_infer_types_or_consts() {
assert!(self.is_tainted_by_errors());
return None;
}

let asm_ty = match *ty.kind() {
// `!` is allowed for input but not for output (issue #87802)
ty::Never if is_input => return None,
ty::Error(_) => return None,
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
// Somewhat of a hack: fallback in the presence of errors does not actually
// fall back to i32, but to ty::Error. For integer inference variables this
// means that they don't get any fallback and stay as `{integer}`.
// Since compilation can't succeed anyway, it's fine to use this to avoid printing
// "cannot use value of type `{integer}`", even though that would absolutely
// work due due i32 fallback if the current function had no other errors.
ty::Infer(InferTy::IntVar(_)) => {
assert!(self.is_tainted_by_errors());
Some(InlineAsmType::I32)
}
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
ty::Infer(InferTy::FloatVar(_)) => {
assert!(self.is_tainted_by_errors());
Some(InlineAsmType::F32)
}
ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
ty::FnPtr(_) => Some(asm_ty_isize),
Expand Down Expand Up @@ -191,6 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => None,
}
}
ty::Infer(_) => unreachable!(),
_ => None,
};
let Some(asm_ty) = asm_ty else {
Expand All @@ -204,11 +201,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
};

if ty.has_infer_types_or_consts() {
assert!(self.is_tainted_by_errors());
return None;
}

// Check that the type implements Copy. The only case where this can
// possibly fail is for SIMD types which don't #[derive(Copy)].
if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, DUMMY_SP) {
Expand Down
14 changes: 13 additions & 1 deletion library/std/src/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,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)]
Expand All @@ -335,6 +337,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,
Expand Down
25 changes: 18 additions & 7 deletions library/std/src/sys/windows/handle.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down
22 changes: 22 additions & 0 deletions library/std/src/sys/windows/handle/tests.rs
Original file line number Diff line number Diff line change
@@ -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();
}
Loading