Auto merge of #99300 - Mark-Simulacrum:beta-next, r=Mark-Simulacrum
[beta] rollup * Fix sized check ICE in asm check #99124 * Windows: Fallback for overlapped I/O #98950 * promote placeholder bounds to 'static obligations #98713 * Create fresh lifetime parameters for bare fn trait too #98637 r? `@Mark-Simulacrum`
This commit is contained in:
commit
efd358333a
17 changed files with 377 additions and 35 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
@ -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.
|
||||
//
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
@ -1000,6 +1029,7 @@ 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>,
|
||||
|
|
@ -1007,8 +1037,6 @@ impl<'tcx> RegionInferenceContext<'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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
@ -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() {
|
||||
|
|
@ -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),
|
||||
|
|
@ -191,6 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
ty::Infer(_) => unreachable!(),
|
||||
_ => None,
|
||||
};
|
||||
let Some(asm_ty) = asm_ty else {
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
21
src/test/ui/asm/issue-99122-2.rs
Normal file
21
src/test/ui/asm/issue-99122-2.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// check-pass
|
||||
// needs-asm-support
|
||||
// only-x86_64
|
||||
|
||||
// This demonstrates why we need to erase regions before sized check in intrinsicck
|
||||
|
||||
struct NoCopy;
|
||||
|
||||
struct Wrap<'a, T, Tail: ?Sized>(&'a T, Tail);
|
||||
|
||||
pub unsafe fn test() {
|
||||
let i = NoCopy;
|
||||
let j = Wrap(&i, ());
|
||||
let pointer = &j as *const _;
|
||||
core::arch::asm!(
|
||||
"nop",
|
||||
in("eax") pointer,
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
13
src/test/ui/asm/issue-99122.rs
Normal file
13
src/test/ui/asm/issue-99122.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// needs-asm-support
|
||||
// only-x86_64
|
||||
|
||||
pub unsafe fn test() {
|
||||
let pointer = 1u32 as *const _;
|
||||
//~^ ERROR cannot cast to a pointer of an unknown kind
|
||||
core::arch::asm!(
|
||||
"nop",
|
||||
in("eax") pointer,
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
11
src/test/ui/asm/issue-99122.stderr
Normal file
11
src/test/ui/asm/issue-99122.stderr
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
error[E0641]: cannot cast to a pointer of an unknown kind
|
||||
--> $DIR/issue-99122.rs:5:27
|
||||
|
|
||||
LL | let pointer = 1u32 as *const _;
|
||||
| ^^^^^^^^ needs more type information
|
||||
|
|
||||
= note: the type information given here is insufficient to check whether the pointer cast is valid
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0641`.
|
||||
|
|
@ -22,6 +22,7 @@ fn foo<T>() {
|
|||
//~| ERROR `T` does not live long enough
|
||||
//~| ERROR `T` does not live long enough
|
||||
//~| ERROR `T` does not live long enough
|
||||
//~| ERROR `T` may not live long enough
|
||||
//
|
||||
// FIXME: This error is bogus, but it arises because we try to validate
|
||||
// that `<() as Foo<T>>::Type<'a>` is valid, which requires proving
|
||||
|
|
|
|||
|
|
@ -34,6 +34,17 @@ error: `T` does not live long enough
|
|||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^
|
||||
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/issue-91139.rs:16:58
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound...
|
||||
|
|
||||
LL | fn foo<T: 'static>() {
|
||||
| +++++++++
|
||||
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:58
|
||||
|
|
||||
|
|
@ -46,5 +57,6 @@ error: `T` does not live long enough
|
|||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
||||
|
|
|
|||
24
src/test/ui/lifetimes/bare-trait-object-borrowck.rs
Normal file
24
src/test/ui/lifetimes/bare-trait-object-borrowck.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#![allow(bare_trait_objects)]
|
||||
// check-pass
|
||||
pub struct FormatWith<'a, I, F> {
|
||||
sep: &'a str,
|
||||
/// FormatWith uses interior mutability because Display::fmt takes &self.
|
||||
inner: RefCell<Option<(I, F)>>,
|
||||
}
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
|
||||
struct Layout;
|
||||
|
||||
pub fn new_format<'a, I, F>(iter: I, separator: &'a str, f: F) -> FormatWith<'a, I, F>
|
||||
where
|
||||
I: Iterator,
|
||||
F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result,
|
||||
{
|
||||
FormatWith { sep: separator, inner: RefCell::new(Some((iter, f))) }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = new_format(0..32, " | ", |i, f| f(&format_args!("0x{:x}", i)));
|
||||
}
|
||||
25
src/test/ui/lifetimes/bare-trait-object.rs
Normal file
25
src/test/ui/lifetimes/bare-trait-object.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Verify that lifetime resolution correctly accounts for `Fn` bare trait objects.
|
||||
// check-pass
|
||||
#![allow(bare_trait_objects)]
|
||||
|
||||
// This should work as: fn next_u32(fill_buf: &mut dyn FnMut(&mut [u8]))
|
||||
fn next_u32(fill_buf: &mut FnMut(&mut [u8])) {
|
||||
let mut buf: [u8; 4] = [0; 4];
|
||||
fill_buf(&mut buf);
|
||||
}
|
||||
|
||||
fn explicit(fill_buf: &mut dyn FnMut(&mut [u8])) {
|
||||
let mut buf: [u8; 4] = [0; 4];
|
||||
fill_buf(&mut buf);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _: fn(&mut FnMut(&mut [u8])) = next_u32;
|
||||
let _: &dyn Fn(&mut FnMut(&mut [u8])) = &next_u32;
|
||||
let _: fn(&mut FnMut(&mut [u8])) = explicit;
|
||||
let _: &dyn Fn(&mut FnMut(&mut [u8])) = &explicit;
|
||||
let _: fn(&mut dyn FnMut(&mut [u8])) = next_u32;
|
||||
let _: &dyn Fn(&mut dyn FnMut(&mut [u8])) = &next_u32;
|
||||
let _: fn(&mut dyn FnMut(&mut [u8])) = explicit;
|
||||
let _: &dyn Fn(&mut dyn FnMut(&mut [u8])) = &explicit;
|
||||
}
|
||||
21
src/test/ui/nll/issue-98693.rs
Normal file
21
src/test/ui/nll/issue-98693.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Regression test for #98693.
|
||||
//
|
||||
// The closure encounters an obligation that `T` must outlive `!U1`,
|
||||
// a placeholder from universe U1. We were ignoring this placeholder
|
||||
// when promoting the constraint to the enclosing function, and
|
||||
// thus incorrectly judging the closure to be safe.
|
||||
|
||||
fn assert_static<T>()
|
||||
where
|
||||
for<'a> T: 'a,
|
||||
{
|
||||
}
|
||||
|
||||
fn test<T>() {
|
||||
|| {
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
assert_static::<T>();
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
17
src/test/ui/nll/issue-98693.stderr
Normal file
17
src/test/ui/nll/issue-98693.stderr
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/issue-98693.rs:15:5
|
||||
|
|
||||
LL | / || {
|
||||
LL | |
|
||||
LL | | assert_static::<T>();
|
||||
LL | | };
|
||||
| |_____^ ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound...
|
||||
|
|
||||
LL | fn test<T: 'static>() {
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue