From cfb04795a1183ca59a4d63060f3443accb9e59bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Tue, 14 May 2024 16:16:33 +0200 Subject: [PATCH 01/21] Fix `read_exact` and `read_buf_exact` for `&[u8]` and `io:Cursor` --- library/std/src/io/cursor.rs | 27 +++++++++++++++++---------- library/std/src/io/impls.rs | 6 ++++++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 37492e9efab74..e6c53acacaee9 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -328,7 +328,7 @@ where fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let prev_written = cursor.written(); - Read::read_buf(&mut self.fill_buf()?, cursor.reborrow())?; + Read::read_buf(&mut self.remaining_slice(), cursor.reborrow())?; self.pos += (cursor.written() - prev_written) as u64; @@ -352,17 +352,24 @@ where } fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let n = buf.len(); - Read::read_exact(&mut self.remaining_slice(), buf)?; - self.pos += n as u64; - Ok(()) + let result = Read::read_exact(&mut self.remaining_slice(), buf); + + match result { + Ok(_) => self.pos += buf.len() as u64, + // The only posible error condition is EOF + Err(_) => self.pos = self.inner.as_ref().len() as u64, + } + + result } - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - let n = cursor.capacity(); - Read::read_buf_exact(&mut self.remaining_slice(), cursor)?; - self.pos += n as u64; - Ok(()) + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let prev_written = cursor.written(); + + let result = Read::read_buf_exact(&mut self.remaining_slice(), cursor.reborrow()); + self.pos += (cursor.written() - prev_written) as u64; + + result } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 46f04c7cd3957..a8a2e9413e11c 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -287,6 +287,9 @@ impl Read for &[u8] { #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if buf.len() > self.len() { + // `read_exact` makes no promise about the content of `buf` if it + // fails so don't bother about that. + *self = &self[self.len()..]; return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(buf.len()); @@ -307,6 +310,9 @@ impl Read for &[u8] { #[inline] fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { if cursor.capacity() > self.len() { + // Append everything we can to the cursor. + cursor.append(*self); + *self = &self[self.len()..]; return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(cursor.capacity()); From b092b5d02b9e2aea72d5993d36c1cbe60e4e9b6b Mon Sep 17 00:00:00 2001 From: surechen Date: Tue, 23 Apr 2024 15:31:54 +0800 Subject: [PATCH 02/21] Note for E0599 if shadowed bindings has the method. implement #123558 --- compiler/rustc_hir_typeck/src/expr.rs | 1 + .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 1 + .../rustc_hir_typeck/src/method/suggest.rs | 213 +++++++++++++++++- .../rustc_confusables_std_cases.stderr | 8 + .../deriving-with-repr-packed-2.stderr | 8 + ...ount-for-shadowed-bindings-issue-123558.rs | 7 + ...-for-shadowed-bindings-issue-123558.stderr | 17 ++ 7 files changed, 249 insertions(+), 6 deletions(-) create mode 100644 tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.rs create mode 100644 tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index fb7d3f40093b8..fade943c5ae3d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1346,6 +1346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if segment.ident.name != kw::Empty { if let Some(err) = self.report_method_error( span, + Some(rcvr), rcvr_t, segment.ident, SelfSource::MethodCall(rcvr), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6e8ef04445215..758df83d3ebc9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -834,6 +834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if item_name.name != kw::Empty { if let Some(e) = self.report_method_error( span, + None, ty.normalized, item_name, SelfSource::QPath(qself), diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index db510d44392cb..54af8354c4c72 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -7,6 +7,7 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem}; use crate::Expectation; use crate::FnCtxt; use core::ops::ControlFlow; +use hir::Expr; use rustc_ast::ast::Mutability; use rustc_attr::parse_confusables; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -19,7 +20,6 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::PatKind::Binding; use rustc_hir::PathSegment; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::{self, RegionVariableOrigin}; @@ -46,7 +46,7 @@ use std::borrow::Cow; use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{self, Visitor}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -188,6 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn report_method_error( &self, span: Span, + rcvr_opt: Option<&'tcx hir::Expr<'tcx>>, rcvr_ty: Ty<'tcx>, item_name: Ident, source: SelfSource<'tcx>, @@ -212,6 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { MethodError::NoMatch(mut no_match_data) => { return self.report_no_match_method_error( span, + rcvr_opt, rcvr_ty, item_name, source, @@ -356,9 +358,197 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err } + pub fn suggest_use_shadowed_binding_with_method( + &self, + rcvr_opt: Option<&'tcx hir::Expr<'tcx>>, + method_name: Ident, + ty_str_reported: &str, + err: &mut Diag<'_>, + ) { + #[derive(Debug)] + struct LetStmt { + ty_hir_id_opt: Option, + binding_id: hir::HirId, + span: Span, + init_hir_id: hir::HirId, + } + + // Used for finding suggest binding. + // ```rust + // earlier binding for suggesting: + // let y = vec![1, 2]; + // now binding: + // if let Some(y) = x { + // y.push(y); + // } + // ``` + struct LetVisitor<'a, 'tcx> { + // Error binding which don't have `method_name`. + binding_name: Symbol, + binding_id: hir::HirId, + // Used for check if the suggest binding has `method_name`. + fcx: &'a FnCtxt<'a, 'tcx>, + call_expr: &'tcx Expr<'tcx>, + method_name: Ident, + // Suggest the binding which is shallowed. + sugg_let: Option, + } + + impl<'a, 'tcx> LetVisitor<'a, 'tcx> { + // Check scope of binding. + fn is_sub_scope(&self, sub_id: hir::ItemLocalId, super_id: hir::ItemLocalId) -> bool { + let scope_tree = self.fcx.tcx.region_scope_tree(self.fcx.body_id); + if let Some(sub_var_scope) = scope_tree.var_scope(sub_id) + && let Some(super_var_scope) = scope_tree.var_scope(super_id) + && scope_tree.is_subscope_of(sub_var_scope, super_var_scope) + { + return true; + } + false + } + + // Check if an earlier shadowed binding make `the receiver` of a MethodCall has the method. + // If it does, record the earlier binding for subsequent notes. + fn check_and_add_sugg_binding(&mut self, binding: LetStmt) -> bool { + if !self.is_sub_scope(self.binding_id.local_id, binding.binding_id.local_id) { + return false; + } + + // Get the earlier shadowed binding'ty and use it to check the method. + if let Some(ty_hir_id) = binding.ty_hir_id_opt + && let Some(tyck_ty) = self.fcx.node_ty_opt(ty_hir_id) + { + if self + .fcx + .lookup_probe_for_diagnostic( + self.method_name, + tyck_ty, + self.call_expr, + ProbeScope::TraitsInScope, + None, + ) + .is_ok() + { + self.sugg_let = Some(binding); + return true; + } else { + return false; + } + } + + // If the shadowed binding has an an itializer expression, + // use the initializer expression'ty to try to find the method again. + // For example like: `let mut x = Vec::new();`, + // `Vec::new()` is the itializer expression. + if let Some(self_ty) = self.fcx.node_ty_opt(binding.init_hir_id) + && self + .fcx + .lookup_probe_for_diagnostic( + self.method_name, + self_ty, + self.call_expr, + ProbeScope::TraitsInScope, + None, + ) + .is_ok() + { + self.sugg_let = Some(binding); + return true; + } + return false; + } + } + + impl<'v> Visitor<'v> for LetVisitor<'_, '_> { + type Result = ControlFlow<()>; + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { + if let hir::StmtKind::Let(&hir::LetStmt { pat, ty, init, .. }) = ex.kind + && let hir::PatKind::Binding(_, binding_id, binding_name, ..) = pat.kind + && let Some(init) = init + && binding_name.name == self.binding_name + && binding_id != self.binding_id + { + if self.check_and_add_sugg_binding(LetStmt { + ty_hir_id_opt: if let Some(ty) = ty { Some(ty.hir_id) } else { None }, + binding_id: binding_id, + span: pat.span, + init_hir_id: init.hir_id, + }) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + } else { + hir::intravisit::walk_stmt(self, ex) + } + } + + // Used for find the error binding. + // When the visitor reaches this point, all the shadowed bindings + // have been found, so the visitor ends. + fn visit_pat(&mut self, p: &'v hir::Pat<'v>) -> Self::Result { + match p.kind { + hir::PatKind::Binding(_, binding_id, binding_name, _) => { + if binding_name.name == self.binding_name && binding_id == self.binding_id { + return ControlFlow::Break(()); + } + } + _ => { + intravisit::walk_pat(self, p); + } + } + ControlFlow::Continue(()) + } + } + + if let Some(rcvr) = rcvr_opt + && let hir::ExprKind::Path(QPath::Resolved(_, path)) = rcvr.kind + && let hir::def::Res::Local(recv_id) = path.res + && let Some(segment) = path.segments.first() + { + let map = self.infcx.tcx.hir(); + let body_id = self.tcx.hir().body_owned_by(self.body_id); + let body = map.body(body_id); + + if let Node::Expr(call_expr) = self.tcx.parent_hir_node(rcvr.hir_id) { + let mut let_visitor = LetVisitor { + fcx: self, + call_expr, + binding_name: segment.ident.name, + binding_id: recv_id, + method_name, + sugg_let: None, + }; + let_visitor.visit_body(body); + if let Some(sugg_let) = let_visitor.sugg_let + && let Some(self_ty) = self.node_ty_opt(sugg_let.init_hir_id) + { + let _sm = self.infcx.tcx.sess.source_map(); + let rcvr_name = segment.ident.name; + let mut span = MultiSpan::from_span(sugg_let.span); + span.push_span_label(sugg_let.span, + format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here")); + span.push_span_label( + self.tcx.hir().span(recv_id), + format!( + "earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`" + ), + ); + err.span_note( + span, + format!( + "there's an earlier shadowed binding `{rcvr_name}` of type `{self_ty}` \ + that has method `{method_name}` available" + ), + ); + } + } + } + } + pub fn report_no_match_method_error( &self, mut span: Span, + rcvr_opt: Option<&'tcx hir::Expr<'tcx>>, rcvr_ty: Ty<'tcx>, item_name: Ident, source: SelfSource<'tcx>, @@ -451,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_missing_writer(rcvr_ty, rcvr_expr) } else { - tcx.dcx().create_err(NoAssociatedItem { + let mut err = tcx.dcx().create_err(NoAssociatedItem { span, item_kind, item_name, @@ -461,9 +651,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { rcvr_ty.prefix_string(self.tcx) }, - ty_str: ty_str_reported, + ty_str: ty_str_reported.clone(), trait_missing_method, - }) + }); + + if is_method { + self.suggest_use_shadowed_binding_with_method( + rcvr_opt, + item_name, + &ty_str_reported, + &mut err, + ); + } + + err }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); @@ -2240,7 +2441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { type Result = ControlFlow>>; fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind - && let Binding(_, _, ident, ..) = pat.kind + && let hir::PatKind::Binding(_, _, ident, ..) = pat.kind && ident.name == self.ident_name { ControlFlow::Break(init) diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index 45d571f435cbd..f4b6947ccd94a 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -26,6 +26,14 @@ error[E0599]: no method named `push` found for struct `VecDeque` in the current LL | x.push(1); | ^^^^ method not found in `VecDeque<_>` | +note: there's an earlier shadowed binding `x` of type `Vec<_>` that has method `push` available + --> $DIR/rustc_confusables_std_cases.rs:8:9 + | +LL | let mut x = Vec::new(); + | ^^^^^ `x` of type `Vec<_>` that has method `push` defined earlier here +... +LL | let mut x = VecDeque::new(); + | ----- earlier `x` shadowed here with type `VecDeque` help: you might have meant to use `push_back` | LL | x.push_back(1); diff --git a/tests/ui/derives/deriving-with-repr-packed-2.stderr b/tests/ui/derives/deriving-with-repr-packed-2.stderr index 96f51a4e7a2b4..b62c67d9a9da1 100644 --- a/tests/ui/derives/deriving-with-repr-packed-2.stderr +++ b/tests/ui/derives/deriving-with-repr-packed-2.stderr @@ -10,6 +10,14 @@ LL | struct NonCopy; LL | _ = x.clone(); | ^^^^^ method cannot be called on `Foo` due to unsatisfied trait bounds | +note: there's an earlier shadowed binding `x` of type `Foo` that has method `clone` available + --> $DIR/deriving-with-repr-packed-2.rs:13:9 + | +LL | let x: Foo = Foo(1, 2, 3); + | ^ `x` of type `Foo` that has method `clone` defined earlier here +... +LL | let x: Foo = Foo(NonCopy, NonCopy, NonCopy); + | - earlier `x` shadowed here with type `Foo` note: the following trait bounds were not satisfied: `NonCopy: Clone` `NonCopy: Copy` diff --git a/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.rs b/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.rs new file mode 100644 index 0000000000000..a08bbf1fdbec5 --- /dev/null +++ b/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.rs @@ -0,0 +1,7 @@ +fn main() { + let x = Some(3); + let y = vec![1, 2]; + if let Some(y) = x { + y.push(y); //~ ERROR E0599 + } +} diff --git a/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.stderr b/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.stderr new file mode 100644 index 0000000000000..aecad201c7b28 --- /dev/null +++ b/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.stderr @@ -0,0 +1,17 @@ +error[E0599]: no method named `push` found for type `{integer}` in the current scope + --> $DIR/account-for-shadowed-bindings-issue-123558.rs:5:11 + | +LL | y.push(y); + | ^^^^ method not found in `{integer}` + | +note: there's an earlier shadowed binding `y` of type `Vec<{integer}>` that has method `push` available + --> $DIR/account-for-shadowed-bindings-issue-123558.rs:3:9 + | +LL | let y = vec![1, 2]; + | ^ `y` of type `Vec<{integer}>` that has method `push` defined earlier here +LL | if let Some(y) = x { + | - earlier `y` shadowed here with type `{integer}` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. From a197ff325989ed200fc4730d35501b9e4064316b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Mon, 20 May 2024 17:00:11 +0200 Subject: [PATCH 03/21] Address review comments --- library/std/src/io/cursor.rs | 2 +- library/std/src/io/tests.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index e6c53acacaee9..a1a8b2a3505c7 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -356,7 +356,7 @@ where match result { Ok(_) => self.pos += buf.len() as u64, - // The only posible error condition is EOF + // The only possible error condition is EOF, so place the cursor at "EOF" Err(_) => self.pos = self.inner.as_ref().len() as u64, } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 090a091b09a13..a2c1c430863ab 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -653,6 +653,38 @@ fn test_take_wrong_length() { let _ = reader.read(&mut buffer[..]); } +#[test] +fn slice_read_exact_eof() { + let slice = &b"123456"[..]; + + let mut r = slice; + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + +#[test] +fn cursor_read_exact_eof() { + let slice = Cursor::new(b"123456"); + + let mut r = slice.clone(); + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + #[bench] fn bench_take_read(b: &mut test::Bencher) { b.iter(|| { From 8d3bc559048e47de32e32a562fe5bbea0b38f4e6 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Wed, 17 Apr 2024 18:47:28 -0400 Subject: [PATCH 04/21] Fix up a few more tests --- .../c-link-to-rust-va-list-fn/checkrust.rs | 5 +---- tests/run-make/link-path-order/main.rs | 6 ++---- tests/ui/env-null-vars.rs | 14 ++++---------- tests/ui/runtime/out-of-stack.rs | 7 +------ tests/ui/sanitizer/thread.rs | 1 - 5 files changed, 8 insertions(+), 25 deletions(-) diff --git a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs index 5830ef033d389..e518579b906a9 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs @@ -1,10 +1,7 @@ #![crate_type = "staticlib"] #![feature(c_variadic)] -#![feature(rustc_private)] -extern crate libc; - -use libc::{c_char, c_double, c_int, c_long, c_longlong}; +use std::ffi::{c_char, c_double, c_int, c_long, c_longlong}; use std::ffi::VaList; use std::ffi::{CString, CStr}; diff --git a/tests/run-make/link-path-order/main.rs b/tests/run-make/link-path-order/main.rs index 8024e343d19a7..20a517dcda9b2 100644 --- a/tests/run-make/link-path-order/main.rs +++ b/tests/run-make/link-path-order/main.rs @@ -1,10 +1,8 @@ -#![feature(rustc_private)] - -extern crate libc; +use std::ffi::c_int; #[link(name = "foo", kind = "static")] extern "C" { - fn should_return_one() -> libc::c_int; + fn should_return_one() -> c_int; } fn main() { diff --git a/tests/ui/env-null-vars.rs b/tests/ui/env-null-vars.rs index bb86fd353c4e9..24d783553d126 100644 --- a/tests/ui/env-null-vars.rs +++ b/tests/ui/env-null-vars.rs @@ -1,21 +1,15 @@ +// Ensure that env::vars() does not panic if environ is null. +// Regression test for rust-lang/rust#53200 //@ run-pass -#![allow(unused_imports)] - -//@ ignore-windows - -// issue-53200 - #![feature(rustc_private)] -extern crate libc; - -use std::env; // FIXME: more platforms? #[cfg(target_os = "linux")] fn main() { + extern crate libc; unsafe { libc::clearenv(); } - assert_eq!(env::vars().count(), 0); + assert_eq!(std::env::vars().count(), 0); } #[cfg(not(target_os = "linux"))] diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index ab2b50b293ce5..a62398df8b80b 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -8,21 +8,16 @@ //@ ignore-fuchsia must translate zircon signal to SIGABRT, FIXME (#58590) //@ ignore-nto no stack overflow handler used (no alternate stack available) -#![feature(core_intrinsics)] #![feature(rustc_private)] #[cfg(unix)] extern crate libc; use std::env; +use std::hint::black_box; use std::process::Command; use std::thread; -// Inlining to avoid llvm turning the recursive functions into tail calls, -// which doesn't consume stack. -#[inline(always)] -pub fn black_box(dummy: T) { std::intrinsics::black_box(dummy); } - fn silent_recurse() { let buf = [0u8; 1000]; black_box(buf); diff --git a/tests/ui/sanitizer/thread.rs b/tests/ui/sanitizer/thread.rs index 9d9ad6ee51867..566774d6b1d64 100644 --- a/tests/ui/sanitizer/thread.rs +++ b/tests/ui/sanitizer/thread.rs @@ -20,7 +20,6 @@ //@ error-pattern: Location is heap block of size 4 //@ error-pattern: allocated by main thread -#![feature(raw_ref_op)] #![feature(rustc_private)] extern crate libc; From e0632d7cec42a68be84ab85d3c69b0fb6fa45f02 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 18 Apr 2024 16:50:06 -0400 Subject: [PATCH 05/21] Add only-unix to sigpipe tests --- tests/ui/runtime/on-broken-pipe/error.rs | 1 + tests/ui/runtime/on-broken-pipe/inherit.rs | 1 + tests/ui/runtime/on-broken-pipe/kill.rs | 1 + tests/ui/runtime/on-broken-pipe/not-used.rs | 1 + tests/ui/runtime/on-broken-pipe/with-rustc_main.rs | 1 + 5 files changed, 5 insertions(+) diff --git a/tests/ui/runtime/on-broken-pipe/error.rs b/tests/ui/runtime/on-broken-pipe/error.rs index ab2036c2f415e..0a020873df081 100644 --- a/tests/ui/runtime/on-broken-pipe/error.rs +++ b/tests/ui/runtime/on-broken-pipe/error.rs @@ -1,6 +1,7 @@ //@ run-pass //@ aux-build:sigpipe-utils.rs //@ compile-flags: -Zon-broken-pipe=error +//@ only-unix because SIGPIPE is a unix thing fn main() { extern crate sigpipe_utils; diff --git a/tests/ui/runtime/on-broken-pipe/inherit.rs b/tests/ui/runtime/on-broken-pipe/inherit.rs index 64909b7352815..f3c8140eaaef1 100644 --- a/tests/ui/runtime/on-broken-pipe/inherit.rs +++ b/tests/ui/runtime/on-broken-pipe/inherit.rs @@ -4,6 +4,7 @@ //@ aux-bin: assert-inherit-sig_ign.rs //@ run-pass //@ compile-flags: -Zon-broken-pipe=kill +//@ only-unix because SIGPIPE is a unix thing #![feature(rustc_private)] diff --git a/tests/ui/runtime/on-broken-pipe/kill.rs b/tests/ui/runtime/on-broken-pipe/kill.rs index 5dace6f1c6ffc..748e53ffcdbee 100644 --- a/tests/ui/runtime/on-broken-pipe/kill.rs +++ b/tests/ui/runtime/on-broken-pipe/kill.rs @@ -1,6 +1,7 @@ //@ run-pass //@ aux-build:sigpipe-utils.rs //@ compile-flags: -Zon-broken-pipe=kill +//@ only-unix because SIGPIPE is a unix thing fn main() { extern crate sigpipe_utils; diff --git a/tests/ui/runtime/on-broken-pipe/not-used.rs b/tests/ui/runtime/on-broken-pipe/not-used.rs index e31236f2b3df8..22a26047874fc 100644 --- a/tests/ui/runtime/on-broken-pipe/not-used.rs +++ b/tests/ui/runtime/on-broken-pipe/not-used.rs @@ -1,5 +1,6 @@ //@ run-pass //@ aux-build:sigpipe-utils.rs +//@ only-unix because SIGPIPE is a unix thing fn main() { extern crate sigpipe_utils; diff --git a/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs b/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs index c1731200038b4..c40590ad87f47 100644 --- a/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs +++ b/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs @@ -1,6 +1,7 @@ //@ run-pass //@ aux-build:sigpipe-utils.rs //@ compile-flags: -Zon-broken-pipe=kill +//@ only-unix because SIGPIPE is a unix thing #![feature(rustc_attrs)] From 18b0a07d4912f4bdd7f74c03ca3a61f93609b323 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 18 Apr 2024 17:03:17 -0400 Subject: [PATCH 06/21] Handle a few more simple tests --- tests/ui/error-codes/E0259.rs | 4 ++-- tests/ui/error-codes/E0259.stderr | 4 ++-- tests/ui/imports/issue-37887.rs | 4 ++-- tests/ui/imports/issue-37887.stderr | 16 ++++++++-------- tests/ui/lint/unnecessary-extern-crate.rs | 14 +++++++------- tests/ui/lint/unnecessary-extern-crate.stderr | 12 ++++++------ tests/ui/meta/no_std-extern-libc.rs | 1 + tests/ui/process/no-stdio.rs | 8 +++++--- tests/ui/wait-forked-but-failed-child.rs | 3 +-- 9 files changed, 34 insertions(+), 32 deletions(-) diff --git a/tests/ui/error-codes/E0259.rs b/tests/ui/error-codes/E0259.rs index e7e94d58635c1..78e56deccb4c3 100644 --- a/tests/ui/error-codes/E0259.rs +++ b/tests/ui/error-codes/E0259.rs @@ -1,8 +1,8 @@ -#![feature(rustc_private)] +#![feature(test)] extern crate alloc; -extern crate libc as alloc; +extern crate test as alloc; //~^ ERROR E0259 fn main() {} diff --git a/tests/ui/error-codes/E0259.stderr b/tests/ui/error-codes/E0259.stderr index 6e086268fc6d1..975d1a161a014 100644 --- a/tests/ui/error-codes/E0259.stderr +++ b/tests/ui/error-codes/E0259.stderr @@ -4,13 +4,13 @@ error[E0259]: the name `alloc` is defined multiple times LL | extern crate alloc; | ------------------- previous import of the extern crate `alloc` here LL | -LL | extern crate libc as alloc; +LL | extern crate test as alloc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `alloc` reimported here | = note: `alloc` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate libc as other_alloc; +LL | extern crate test as other_alloc; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-37887.rs b/tests/ui/imports/issue-37887.rs index 58f0c6b651ad6..919f46d34c6d9 100644 --- a/tests/ui/imports/issue-37887.rs +++ b/tests/ui/imports/issue-37887.rs @@ -1,4 +1,4 @@ fn main() { - extern crate libc; //~ ERROR use of unstable - use libc::*; //~ ERROR unresolved import + extern crate test; //~ ERROR use of unstable + use test::*; //~ ERROR unresolved import } diff --git a/tests/ui/imports/issue-37887.stderr b/tests/ui/imports/issue-37887.stderr index 6117fd21af188..e7792ac0d159b 100644 --- a/tests/ui/imports/issue-37887.stderr +++ b/tests/ui/imports/issue-37887.stderr @@ -1,19 +1,19 @@ -error[E0432]: unresolved import `libc` +error[E0432]: unresolved import `test` --> $DIR/issue-37887.rs:3:9 | -LL | use libc::*; - | ^^^^ maybe a missing crate `libc`? +LL | use test::*; + | ^^^^ maybe a missing crate `test`? | - = help: consider adding `extern crate libc` to use the `libc` crate + = help: consider adding `extern crate test` to use the `test` crate -error[E0658]: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? +error[E0658]: use of unstable library feature 'test' --> $DIR/issue-37887.rs:2:5 | -LL | extern crate libc; +LL | extern crate test; | ^^^^^^^^^^^^^^^^^^ | - = note: see issue #27812 for more information - = help: add `#![feature(rustc_private)]` to the crate attributes to enable + = note: see issue #50297 for more information + = help: add `#![feature(test)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 2 previous errors diff --git a/tests/ui/lint/unnecessary-extern-crate.rs b/tests/ui/lint/unnecessary-extern-crate.rs index 6ca3b96758f15..7f97a4c469ed8 100644 --- a/tests/ui/lint/unnecessary-extern-crate.rs +++ b/tests/ui/lint/unnecessary-extern-crate.rs @@ -1,12 +1,12 @@ //@ edition:2018 #![deny(unused_extern_crates)] -#![feature(test, rustc_private)] +#![feature(test)] -extern crate libc; +extern crate core; //~^ ERROR unused extern crate //~| HELP remove -extern crate libc as x; +extern crate core as x; //~^ ERROR unused extern crate //~| HELP remove @@ -28,11 +28,11 @@ mod foo { pub(super) extern crate alloc as d; - extern crate libc; + extern crate core; //~^ ERROR unused extern crate //~| HELP remove - extern crate libc as x; + extern crate core as x; //~^ ERROR unused extern crate //~| HELP remove @@ -41,11 +41,11 @@ mod foo { pub extern crate test as y; mod bar { - extern crate libc; + extern crate core; //~^ ERROR unused extern crate //~| HELP remove - extern crate libc as x; + extern crate core as x; //~^ ERROR unused extern crate //~| HELP remove diff --git a/tests/ui/lint/unnecessary-extern-crate.stderr b/tests/ui/lint/unnecessary-extern-crate.stderr index 14ba9d052f425..1fa4aa9c9a9cf 100644 --- a/tests/ui/lint/unnecessary-extern-crate.stderr +++ b/tests/ui/lint/unnecessary-extern-crate.stderr @@ -1,7 +1,7 @@ error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:6:1 | -LL | extern crate libc; +LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: remove it | note: the lint level is defined here @@ -13,31 +13,31 @@ LL | #![deny(unused_extern_crates)] error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:9:1 | -LL | extern crate libc as x; +LL | extern crate core as x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:31:5 | -LL | extern crate libc; +LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: remove it error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:35:5 | -LL | extern crate libc as x; +LL | extern crate core as x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:44:9 | -LL | extern crate libc; +LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: remove it error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:48:9 | -LL | extern crate libc as x; +LL | extern crate core as x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it error: aborting due to 6 previous errors diff --git a/tests/ui/meta/no_std-extern-libc.rs b/tests/ui/meta/no_std-extern-libc.rs index 919caf9428f71..8b89e14382d14 100644 --- a/tests/ui/meta/no_std-extern-libc.rs +++ b/tests/ui/meta/no_std-extern-libc.rs @@ -1,5 +1,6 @@ // Test that `download-rustc` doesn't put duplicate copies of libc in the sysroot. //@ check-pass +//@ ignore-windows doesn't necessarily have the libc crate #![crate_type = "lib"] #![no_std] #![feature(rustc_private)] diff --git a/tests/ui/process/no-stdio.rs b/tests/ui/process/no-stdio.rs index 05b1e52b799e9..8eebf6dbc7d4e 100644 --- a/tests/ui/process/no-stdio.rs +++ b/tests/ui/process/no-stdio.rs @@ -5,11 +5,13 @@ #![feature(rustc_private)] +#[cfg(unix)] extern crate libc; -use std::process::{Command, Stdio}; use std::env; +use std::ffi::c_int; use std::io::{self, Read, Write}; +use std::process::{Command, Stdio}; #[cfg(unix)] unsafe fn without_stdio R>(f: F) -> R { @@ -36,14 +38,14 @@ unsafe fn without_stdio R>(f: F) -> R { } #[cfg(unix)] -fn assert_fd_is_valid(fd: libc::c_int) { +fn assert_fd_is_valid(fd: c_int) { if unsafe { libc::fcntl(fd, libc::F_GETFD) == -1 } { panic!("file descriptor {} is not valid: {}", fd, io::Error::last_os_error()); } } #[cfg(windows)] -fn assert_fd_is_valid(_fd: libc::c_int) {} +fn assert_fd_is_valid(_fd: c_int) {} #[cfg(windows)] unsafe fn without_stdio R>(f: F) -> R { diff --git a/tests/ui/wait-forked-but-failed-child.rs b/tests/ui/wait-forked-but-failed-child.rs index 3d052cc193c55..dd6a7fa0e6505 100644 --- a/tests/ui/wait-forked-but-failed-child.rs +++ b/tests/ui/wait-forked-but-failed-child.rs @@ -7,8 +7,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::process::Command; // The output from "ps -A -o pid,ppid,args" should look like this: @@ -28,6 +26,7 @@ use std::process::Command; #[cfg(unix)] fn find_zombies() { + extern crate libc; let my_pid = unsafe { libc::getpid() }; // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html From 281178de4242af70ef6e4f2803ae8573c2778433 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 18 Apr 2024 17:30:07 -0400 Subject: [PATCH 07/21] Add a Windows version of foreign2.rs --- tests/ui/foreign/foreign2.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/ui/foreign/foreign2.rs b/tests/ui/foreign/foreign2.rs index eb24df35033c5..765f2833f3ce2 100644 --- a/tests/ui/foreign/foreign2.rs +++ b/tests/ui/foreign/foreign2.rs @@ -1,9 +1,8 @@ //@ run-pass -#![allow(dead_code)] //@ pretty-expanded FIXME #23616 -#![feature(rustc_private)] -extern crate libc; +#![allow(dead_code)] +#![feature(rustc_private)] mod bar { extern "C" {} @@ -13,7 +12,9 @@ mod zed { extern "C" {} } +#[cfg(not(windows))] mod mlibc { + extern crate libc; use libc::{c_int, c_void, size_t, ssize_t}; extern "C" { @@ -21,6 +22,27 @@ mod mlibc { } } +#[cfg(windows)] +mod mlibc { + #![allow(non_snake_case)] + + use std::ffi::c_void; + + pub type BOOL = i32; + pub type HANDLE = *mut c_void; + + #[link(name = "ntdll")] + extern "system" { + pub fn WriteFile( + hfile: HANDLE, + lpbuffer: *const u8, + nnumberofbytestowrite: u32, + lpnumberofbyteswritten: *mut u32, + lpoverlapped: *mut c_void, + ) -> BOOL; + } +} + mod baz { extern "C" {} } From 70147cd3ab30a0f1c6677db0581a17a4bcf5f85d Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 18 Apr 2024 17:39:35 -0400 Subject: [PATCH 08/21] Patch up foreign-fn-linkname.rs --- tests/ui/foreign/foreign-fn-linkname.rs | 10 ++-------- tests/ui/foreign/foreign2.rs | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/ui/foreign/foreign-fn-linkname.rs b/tests/ui/foreign/foreign-fn-linkname.rs index 47edf6fc7bba1..7ced34e73fa76 100644 --- a/tests/ui/foreign/foreign-fn-linkname.rs +++ b/tests/ui/foreign/foreign-fn-linkname.rs @@ -1,20 +1,14 @@ //@ run-pass //@ ignore-sgx no libc -// Ensure no false positive on "unused extern crate" lint -#![deny(unused_extern_crates)] - -#![feature(rustc_private)] - -extern crate libc; use std::ffi::CString; mod mlibc { - use libc::{c_char, size_t}; + use std::ffi::c_char; extern "C" { #[link_name = "strlen"] - pub fn my_strlen(str: *const c_char) -> size_t; + pub fn my_strlen(str: *const c_char) -> usize; } } diff --git a/tests/ui/foreign/foreign2.rs b/tests/ui/foreign/foreign2.rs index 765f2833f3ce2..178a04255ccee 100644 --- a/tests/ui/foreign/foreign2.rs +++ b/tests/ui/foreign/foreign2.rs @@ -15,7 +15,7 @@ mod zed { #[cfg(not(windows))] mod mlibc { extern crate libc; - use libc::{c_int, c_void, size_t, ssize_t}; + use self::libc::{c_int, c_void, size_t, ssize_t}; extern "C" { pub fn write(fd: c_int, buf: *const c_void, count: size_t) -> ssize_t; From 7a906e1af71684138702a85fe114def61f21379d Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 18 Apr 2024 17:49:07 -0400 Subject: [PATCH 09/21] Port stdout-during-shutdown --- ...down.rs => stdout-during-shutdown-unix.rs} | 1 + ...=> stdout-during-shutdown-unix.run.stdout} | 0 .../runtime/stdout-during-shutdown-windows.rs | 20 +++++++++++++++++++ .../stdout-during-shutdown-windows.run.stdout | 1 + 4 files changed, 22 insertions(+) rename tests/ui/runtime/{stdout-during-shutdown.rs => stdout-during-shutdown-unix.rs} (97%) rename tests/ui/runtime/{stdout-during-shutdown.run.stdout => stdout-during-shutdown-unix.run.stdout} (100%) create mode 100644 tests/ui/runtime/stdout-during-shutdown-windows.rs create mode 100644 tests/ui/runtime/stdout-during-shutdown-windows.run.stdout diff --git a/tests/ui/runtime/stdout-during-shutdown.rs b/tests/ui/runtime/stdout-during-shutdown-unix.rs similarity index 97% rename from tests/ui/runtime/stdout-during-shutdown.rs rename to tests/ui/runtime/stdout-during-shutdown-unix.rs index 8549f5d8eb6d9..8e0f1d371ae59 100644 --- a/tests/ui/runtime/stdout-during-shutdown.rs +++ b/tests/ui/runtime/stdout-during-shutdown-unix.rs @@ -1,6 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-emscripten +//@ only-unix // Emscripten doesn't flush its own stdout buffers on exit, which would fail // this test. So this test is disabled on this platform. diff --git a/tests/ui/runtime/stdout-during-shutdown.run.stdout b/tests/ui/runtime/stdout-during-shutdown-unix.run.stdout similarity index 100% rename from tests/ui/runtime/stdout-during-shutdown.run.stdout rename to tests/ui/runtime/stdout-during-shutdown-unix.run.stdout diff --git a/tests/ui/runtime/stdout-during-shutdown-windows.rs b/tests/ui/runtime/stdout-during-shutdown-windows.rs new file mode 100644 index 0000000000000..4644965b154cc --- /dev/null +++ b/tests/ui/runtime/stdout-during-shutdown-windows.rs @@ -0,0 +1,20 @@ +//@ run-pass +//@ check-run-results +//@ only-windows + +struct Bye; + +impl Drop for Bye { + fn drop(&mut self) { + print!(", world!"); + } +} + +fn main() { + thread_local!{ + static BYE: Bye = Bye; + } + BYE.with(|_| { + print!("hello"); + }); +} diff --git a/tests/ui/runtime/stdout-during-shutdown-windows.run.stdout b/tests/ui/runtime/stdout-during-shutdown-windows.run.stdout new file mode 100644 index 0000000000000..30f51a3fba527 --- /dev/null +++ b/tests/ui/runtime/stdout-during-shutdown-windows.run.stdout @@ -0,0 +1 @@ +hello, world! \ No newline at end of file From f45a7a291c4d32f3d8c2c9c67138e229677ddb38 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 18 Apr 2024 18:08:47 -0400 Subject: [PATCH 10/21] Fix feature-gates/rustc-private.rs --- tests/ui/feature-gates/rustc-private.rs | 2 +- tests/ui/feature-gates/rustc-private.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/feature-gates/rustc-private.rs b/tests/ui/feature-gates/rustc-private.rs index 7b8944bb0a0b8..aa44f790c8ae3 100644 --- a/tests/ui/feature-gates/rustc-private.rs +++ b/tests/ui/feature-gates/rustc-private.rs @@ -1,5 +1,5 @@ // gate-test-rustc_private -extern crate libc; //~ ERROR use of unstable library feature 'rustc_private' +extern crate cfg_if; //~ ERROR use of unstable library feature 'rustc_private' fn main() {} diff --git a/tests/ui/feature-gates/rustc-private.stderr b/tests/ui/feature-gates/rustc-private.stderr index 03397cba763d2..96cc98619fd77 100644 --- a/tests/ui/feature-gates/rustc-private.stderr +++ b/tests/ui/feature-gates/rustc-private.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? --> $DIR/rustc-private.rs:3:1 | -LL | extern crate libc; - | ^^^^^^^^^^^^^^^^^^ +LL | extern crate cfg_if; + | ^^^^^^^^^^^^^^^^^^^^ | = note: see issue #27812 for more information = help: add `#![feature(rustc_private)]` to the crate attributes to enable From 30379f9bcc8cc31610784024c7a1f1d4fb98f4c5 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 19 Apr 2024 18:36:54 -0400 Subject: [PATCH 11/21] Update tests/incremental/foreign.rs --- tests/incremental/foreign.rs | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/tests/incremental/foreign.rs b/tests/incremental/foreign.rs index cb040fe1296d8..1af203e9b77b4 100644 --- a/tests/incremental/foreign.rs +++ b/tests/incremental/foreign.rs @@ -1,38 +1,21 @@ // Test what happens we save incremental compilation state that makes // use of foreign items. This used to ICE (#34991). -//@ ignore-sgx no libc - //@ revisions: rpass1 -#![feature(rustc_private)] - -extern crate libc; - use std::ffi::CString; mod mlibc { - use libc::{c_char, c_long, c_longlong}; - extern "C" { - pub fn atol(x: *const c_char) -> c_long; - pub fn atoll(x: *const c_char) -> c_longlong; + // strlen is provided either by an external library or compiler-builtins as a fallback + pub fn strlen(x: *const std::ffi::c_char) -> usize; } } -fn atol(s: String) -> isize { - let c = CString::new(s).unwrap(); - unsafe { mlibc::atol(c.as_ptr()) as isize } -} - -fn atoll(s: String) -> i64 { +fn strlen(s: String) -> usize { let c = CString::new(s).unwrap(); - unsafe { mlibc::atoll(c.as_ptr()) as i64 } + unsafe { mlibc::strlen(c.as_ptr()) } } pub fn main() { - assert_eq!(atol("1024".to_string()) * 10, atol("10240".to_string())); - assert_eq!( - (atoll("11111111111111111".to_string()) * 10), - atoll("111111111111111110".to_string()) - ); + assert_eq!(strlen("1024".to_string()), strlen("2048".to_string())); } From aa31281f2d8cb2b31e0396ad2dd7e3e8976ae25d Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Wed, 17 Apr 2024 23:11:39 -0400 Subject: [PATCH 12/21] Remove Windows dependency on libc --- library/std/Cargo.toml | 3 --- library/std/src/lib.rs | 1 + library/std/src/os/raw/tests.rs | 2 ++ 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 52729ba1f8456..a77d438e1f172 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -33,9 +33,6 @@ addr2line = { version = "0.21.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } -[target.'cfg(all(windows, target_env = "msvc"))'.dependencies] -libc = { version = "0.2.153", default-features = false } - [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 27b46b462044a..949c543a26479 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -435,6 +435,7 @@ extern crate alloc as alloc_crate; // so include it here even if it's unused. #[doc(masked)] #[allow(unused_extern_crates)] +#[cfg(not(all(windows, target_env = "msvc")))] extern crate libc; // We always need an unwinder currently for backtraces diff --git a/library/std/src/os/raw/tests.rs b/library/std/src/os/raw/tests.rs index e7bb7d7e73e80..f41a22e1bcce4 100644 --- a/library/std/src/os/raw/tests.rs +++ b/library/std/src/os/raw/tests.rs @@ -1,3 +1,5 @@ +#![cfg(not(all(windows, target_env = "msvc")))] + use crate::any::TypeId; macro_rules! ok { From 39ef149a295a45a21e06f0a764a3e9b6f5d210a8 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 14 May 2024 13:59:28 -0400 Subject: [PATCH 13/21] Undo accidental change to tests/ui/sanitizer/thread.rs --- tests/ui/sanitizer/thread.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/sanitizer/thread.rs b/tests/ui/sanitizer/thread.rs index 566774d6b1d64..9d9ad6ee51867 100644 --- a/tests/ui/sanitizer/thread.rs +++ b/tests/ui/sanitizer/thread.rs @@ -20,6 +20,7 @@ //@ error-pattern: Location is heap block of size 4 //@ error-pattern: allocated by main thread +#![feature(raw_ref_op)] #![feature(rustc_private)] extern crate libc; From ba8fba96fbf112a45613592e9ba43e955cc4a0da Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 20 May 2024 13:00:45 -0400 Subject: [PATCH 14/21] Update books --- src/doc/book | 2 +- src/doc/embedded-book | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- src/doc/rustc-dev-guide | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/book b/src/doc/book index bebcf527e6775..5e9051f71638a 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit bebcf527e67755a989a1739b7cfaa8f0e6b30040 +Subproject commit 5e9051f71638aa941cd5dda465e25c61cde9594f diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 17842ebb050f6..dd962bb82865a 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 17842ebb050f62e40a4618edeb8e8ee86e758707 +Subproject commit dd962bb82865a5284f2404e5234f1e3222b9c022 diff --git a/src/doc/reference b/src/doc/reference index 51817951d0d21..e356977fceaa8 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 51817951d0d213a0011f82b62aae02c3b3f2472e +Subproject commit e356977fceaa8591c762312d8d446769166d4b3e diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 229ad13b64d91..20482893d1a50 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 229ad13b64d919b12e548d560f06d88963b25cd3 +Subproject commit 20482893d1a502df72f76762c97aed88854cdf81 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 2d1947ff34d50..b6d4a4940bab8 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 2d1947ff34d50ca46dfe242ad75531a4c429bb52 +Subproject commit b6d4a4940bab85cc91eec70cc2e3096dd48da62d From c170bf99273ce194b7d4cf71abb946fe47d58f82 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 20 May 2024 19:18:58 +0200 Subject: [PATCH 15/21] switch to the default implementation of `write_vectored` --- library/std/src/sys/pal/hermit/net.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 23ac71cb9f29e..84ae311a74165 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -225,17 +225,11 @@ impl Socket { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut size: isize = 0; - - for i in bufs.iter() { - size += cvt(unsafe { netc::write(self.0.as_raw_fd(), i.as_ptr(), i.len()) })?; - } - - Ok(size.try_into().unwrap()) + crate::io::default_write_vectored(|b| self.write(b), bufs) } pub fn is_write_vectored(&self) -> bool { - true + false } pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { From 7b1527ff5f7adb2de57a9a925e578ffd3b25ed23 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 15 May 2024 20:18:22 +0200 Subject: [PATCH 16/21] hir pretty: fix block indent --- compiler/rustc_hir_pretty/src/lib.rs | 2 +- tests/pretty/issue-4264.pp | 46 +++++++++---------- tests/ui/match/issue-82392.stdout | 14 +++--- ...s-in-pattern-with-ty-err-doesnt-ice.stderr | 4 +- .../type-alias-impl-trait/issue-60662.stdout | 4 +- tests/ui/unpretty/bad-literal.stdout | 4 +- tests/ui/unpretty/box.stdout | 8 ++-- .../ui/unpretty/flattened-format-args.stdout | 14 +++--- tests/ui/unpretty/let-else-hir.stdout | 10 ++-- 9 files changed, 53 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index a47e6af0bf201..0c2a4bf103f57 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1454,7 +1454,7 @@ impl<'a> State<'a> { self.word_space(":"); } // containing cbox, will be closed by print-block at `}` - self.cbox(INDENT_UNIT); + self.cbox(0); // head-box, will be closed by print-block after `{` self.ibox(0); self.print_block(blk); diff --git a/tests/pretty/issue-4264.pp b/tests/pretty/issue-4264.pp index af64260d02000..4d43db5716e3c 100644 --- a/tests/pretty/issue-4264.pp +++ b/tests/pretty/issue-4264.pp @@ -11,15 +11,15 @@ fn foo(_: [i32; (3 as usize)]) ({ } as ()) fn bar() ({ - const FOO: usize = ((5 as usize) - (4 as usize) as usize); - let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]); + const FOO: usize = ((5 as usize) - (4 as usize) as usize); + let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]); - let _: [(); (1 as usize)] = ([(() as ())] as [(); 1]); + let _: [(); (1 as usize)] = ([(() as ())] as [(); 1]); - let _ = - (((&([(1 as i32), (2 as i32), (3 as i32)] as [i32; 3]) as - &[i32; 3]) as *const _ as *const [i32; 3]) as - *const [i32; (3 as usize)] as *const [i32; 3]); + let _ = + (((&([(1 as i32), (2 as i32), (3 as i32)] as [i32; 3]) as &[i32; 3]) + as *const _ as *const [i32; 3]) as *const [i32; (3 as usize)] + as *const [i32; 3]); @@ -29,17 +29,17 @@ - ({ - let res = - ((::alloc::fmt::format as - for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const - as - fn(&[&'static str]) -> Arguments<'_> {Arguments::<'_>::new_const})((&([("test" - as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>)) - as String); - (res as String) - } as String); - } as ()) + ({ + let res = + ((::alloc::fmt::format as + for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const + as + fn(&[&'static str]) -> Arguments<'_> {Arguments::<'_>::new_const})((&([("test" + as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>)) + as String); + (res as String) + } as String); +} as ()) type Foo = [i32; (3 as usize)]; struct Bar { x: [i32; (3 as usize)], @@ -48,9 +48,9 @@ enum Baz { BazVariant([i32; (5 as usize)]), } fn id(x: T) -> T ({ (x as T) } as T) fn use_id() ({ - let _ = - ((id::<[i32; (3 as usize)]> as - fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32), - (2 as i32), (3 as i32)] as [i32; 3])) as [i32; 3]); - } as ()) + let _ = + ((id::<[i32; (3 as usize)]> as + fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32), + (2 as i32), (3 as i32)] as [i32; 3])) as [i32; 3]); +} as ()) fn main() ({ } as ()) diff --git a/tests/ui/match/issue-82392.stdout b/tests/ui/match/issue-82392.stdout index 8a23d906757d7..8949611ac12f4 100644 --- a/tests/ui/match/issue-82392.stdout +++ b/tests/ui/match/issue-82392.stdout @@ -7,10 +7,10 @@ extern crate std; //@ check-pass fn main() ({ - (if (true as bool) - ({ } as - ()) else if (let Some(a) = - ((Some as - fn(i32) -> Option {Option::::Some})((3 as i32)) as - Option) as bool) ({ } as ()) as ()) - } as ()) + (if (true as bool) + ({ } as + ()) else if (let Some(a) = + ((Some as + fn(i32) -> Option {Option::::Some})((3 as i32)) as + Option) as bool) ({ } as ()) as ()) + } as ()) diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr index 8df0613695be3..99f8dbd9a6c87 100644 --- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr @@ -25,8 +25,8 @@ LL + let str::as_bytes; | error[E0533]: expected unit struct, unit variant or constant, found associated function `str<{ - fn str() { let (/*ERROR*/); } - }, T>::as_bytes` + fn str() { let (/*ERROR*/); } + }, T>::as_bytes` --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:9 | LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; diff --git a/tests/ui/type-alias-impl-trait/issue-60662.stdout b/tests/ui/type-alias-impl-trait/issue-60662.stdout index e643dba124537..b541cbeb2279a 100644 --- a/tests/ui/type-alias-impl-trait/issue-60662.stdout +++ b/tests/ui/type-alias-impl-trait/issue-60662.stdout @@ -10,5 +10,5 @@ extern crate std; trait Animal { } fn main() { - type ServeFut = /*impl Trait*/; - } + type ServeFut = /*impl Trait*/; +} diff --git a/tests/ui/unpretty/bad-literal.stdout b/tests/ui/unpretty/bad-literal.stdout index 07ecb99dccc54..c5272711d6e93 100644 --- a/tests/ui/unpretty/bad-literal.stdout +++ b/tests/ui/unpretty/bad-literal.stdout @@ -7,5 +7,5 @@ extern crate std; // In #100948 this caused an ICE with -Zunpretty=hir. fn main() { - ; - } + ; +} diff --git a/tests/ui/unpretty/box.stdout b/tests/ui/unpretty/box.stdout index 0fd51edea241d..e3b9b9ac20715 100644 --- a/tests/ui/unpretty/box.stdout +++ b/tests/ui/unpretty/box.stdout @@ -8,7 +8,7 @@ use ::std::prelude::rust_2015::*; extern crate std; fn main() { - let _ = - #[rustc_box] - Box::new(1); - } + let _ = + #[rustc_box] + Box::new(1); +} diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout index 275fa104e6676..2de1cdd96b5b7 100644 --- a/tests/ui/unpretty/flattened-format-args.stdout +++ b/tests/ui/unpretty/flattened-format-args.stdout @@ -6,10 +6,10 @@ extern crate std; //@ check-pass fn main() { - let x = 1; - // Should flatten to println!("a 123 b {x} xyz\n"): - { - ::std::io::_print(format_arguments::new_v1(&["a 123 b ", - " xyz\n"], &[format_argument::new_display(&x)])); - }; - } + let x = 1; + // Should flatten to println!("a 123 b {x} xyz\n"): + { + ::std::io::_print(format_arguments::new_v1(&["a 123 b ", " xyz\n"], + &[format_argument::new_display(&x)])); + }; +} diff --git a/tests/ui/unpretty/let-else-hir.stdout b/tests/ui/unpretty/let-else-hir.stdout index ed55f293876ec..a2ffa5de5673c 100644 --- a/tests/ui/unpretty/let-else-hir.stdout +++ b/tests/ui/unpretty/let-else-hir.stdout @@ -9,10 +9,10 @@ extern crate std; fn foo(x: Option) { - let Some(_) = x else - { + let Some(_) = x else + { - { ::std::rt::begin_panic("explicit panic") } - }; - } + { ::std::rt::begin_panic("explicit panic") } + }; +} fn main() { } From d39dc0ab2340547ba24a5153a5076a4554521479 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 20 May 2024 21:44:04 +0200 Subject: [PATCH 17/21] switch also the default implementation for read_vectored --- library/std/src/sys/pal/hermit/net.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 84ae311a74165..00dbca86a4bae 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -175,23 +175,12 @@ impl Socket { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut size: isize = 0; - - for i in bufs.iter_mut() { - let ret: isize = - cvt(unsafe { netc::read(self.0.as_raw_fd(), i.as_mut_ptr(), i.len()) })?; - - if ret != 0 { - size += ret; - } - } - - Ok(size.try_into().unwrap()) + crate::io::default_read_vectored(|b| self.read(b), bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - true + false } fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { From 82df0c3540dd6325ee4401ffd9f4ad8f13120a6a Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 13 May 2024 15:08:25 +0000 Subject: [PATCH 18/21] move fixpoint step into subfunction --- .../src/solve/search_graph.rs | 129 +++++++++++------- 1 file changed, 76 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 5a5df439a7888..87e4fd9ae7368 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -391,60 +391,10 @@ impl<'tcx> SearchGraph> { // `with_anon_task` closure. let ((final_entry, result), dep_node) = tcx.dep_graph.with_anon_task(tcx, dep_kinds::TraitSelect, || { - // When we encounter a coinductive cycle, we have to fetch the - // result of that cycle while we are still computing it. Because - // of this we continuously recompute the cycle until the result - // of the previous iteration is equal to the final result, at which - // point we are done. for _ in 0..FIXPOINT_STEP_LIMIT { - let result = prove_goal(self, inspect); - let stack_entry = self.pop_stack(); - debug_assert_eq!(stack_entry.input, input); - - // If the current goal is not the root of a cycle, we are done. - if stack_entry.has_been_used.is_empty() { - return (stack_entry, result); - } - - // If it is a cycle head, we have to keep trying to prove it until - // we reach a fixpoint. We need to do so for all cycle heads, - // not only for the root. - // - // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs - // for an example. - - // Start by clearing all provisional cache entries which depend on this - // the current goal. - Self::clear_dependent_provisional_results( - &mut self.provisional_cache, - self.stack.next_index(), - ); - - // Check whether we reached a fixpoint, either because the final result - // is equal to the provisional result of the previous iteration, or because - // this was only the root of either coinductive or inductive cycles, and the - // final result is equal to the initial response for that case. - let reached_fixpoint = if let Some(r) = stack_entry.provisional_result { - r == result - } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE { - Self::response_no_constraints(tcx, input, Certainty::Yes) == result - } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE { - Self::response_no_constraints(tcx, input, Certainty::overflow(false)) - == result - } else { - false - }; - - // If we did not reach a fixpoint, update the provisional result and reevaluate. - if reached_fixpoint { - return (stack_entry, result); - } else { - let depth = self.stack.push(StackEntry { - has_been_used: HasBeenUsed::empty(), - provisional_result: Some(result), - ..stack_entry - }); - debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); + match self.fixpoint_step_in_task(tcx, input, inspect, &mut prove_goal) { + StepResult::Done(final_entry, result) => return (final_entry, result), + StepResult::HasChanged => {} } } @@ -496,6 +446,79 @@ impl<'tcx> SearchGraph> { result } +} + +enum StepResult<'tcx> { + Done(StackEntry<'tcx>, QueryResult<'tcx>), + HasChanged, +} + +impl<'tcx> SearchGraph<'tcx> { + /// When we encounter a coinductive cycle, we have to fetch the + /// result of that cycle while we are still computing it. Because + /// of this we continuously recompute the cycle until the result + /// of the previous iteration is equal to the final result, at which + /// point we are done. + fn fixpoint_step_in_task( + &mut self, + tcx: TyCtxt<'tcx>, + input: CanonicalInput<'tcx>, + inspect: &mut ProofTreeBuilder>, + prove_goal: &mut F, + ) -> StepResult<'tcx> + where + F: FnMut(&mut Self, &mut ProofTreeBuilder>) -> QueryResult<'tcx>, + { + let result = prove_goal(self, inspect); + let stack_entry = self.pop_stack(); + debug_assert_eq!(stack_entry.input, input); + + // If the current goal is not the root of a cycle, we are done. + if stack_entry.has_been_used.is_empty() { + return StepResult::Done(stack_entry, result); + } + + // If it is a cycle head, we have to keep trying to prove it until + // we reach a fixpoint. We need to do so for all cycle heads, + // not only for the root. + // + // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs + // for an example. + + // Start by clearing all provisional cache entries which depend on this + // the current goal. + Self::clear_dependent_provisional_results( + &mut self.provisional_cache, + self.stack.next_index(), + ); + + // Check whether we reached a fixpoint, either because the final result + // is equal to the provisional result of the previous iteration, or because + // this was only the root of either coinductive or inductive cycles, and the + // final result is equal to the initial response for that case. + let reached_fixpoint = if let Some(r) = stack_entry.provisional_result { + r == result + } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE { + Self::response_no_constraints(tcx, input, Certainty::Yes) == result + } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE { + Self::response_no_constraints(tcx, input, Certainty::overflow(false)) == result + } else { + false + }; + + // If we did not reach a fixpoint, update the provisional result and reevaluate. + if reached_fixpoint { + StepResult::Done(stack_entry, result) + } else { + let depth = self.stack.push(StackEntry { + has_been_used: HasBeenUsed::empty(), + provisional_result: Some(result), + ..stack_entry + }); + debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); + StepResult::HasChanged + } + } fn response_no_constraints( tcx: TyCtxt<'tcx>, From ee0f20bb97ee834d79f69506a959ba9a78f16683 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 13 May 2024 15:50:59 +0000 Subject: [PATCH 19/21] move global cache lookup into fn --- .../rustc_middle/src/traits/solve/cache.rs | 34 ++++---- .../src/solve/search_graph.rs | 86 ++++++++++--------- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_middle/src/traits/solve/cache.rs b/compiler/rustc_middle/src/traits/solve/cache.rs index 03ce7cf98cf7e..2ff4ade21d087 100644 --- a/compiler/rustc_middle/src/traits/solve/cache.rs +++ b/compiler/rustc_middle/src/traits/solve/cache.rs @@ -14,11 +14,11 @@ pub struct EvaluationCache<'tcx> { map: Lock, CacheEntry<'tcx>>>, } -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct CacheData<'tcx> { pub result: QueryResult<'tcx>, pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep>]>, - pub reached_depth: usize, + pub additional_depth: usize, pub encountered_overflow: bool, } @@ -29,7 +29,7 @@ impl<'tcx> EvaluationCache<'tcx> { tcx: TyCtxt<'tcx>, key: CanonicalInput<'tcx>, proof_tree: Option<&'tcx [inspect::GoalEvaluationStep>]>, - reached_depth: usize, + additional_depth: usize, encountered_overflow: bool, cycle_participants: FxHashSet>, dep_node: DepNodeIndex, @@ -40,17 +40,17 @@ impl<'tcx> EvaluationCache<'tcx> { let data = WithDepNode::new(dep_node, QueryData { result, proof_tree }); entry.cycle_participants.extend(cycle_participants); if encountered_overflow { - entry.with_overflow.insert(reached_depth, data); + entry.with_overflow.insert(additional_depth, data); } else { - entry.success = Some(Success { data, reached_depth }); + entry.success = Some(Success { data, additional_depth }); } if cfg!(debug_assertions) { drop(map); - if Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) - != self.get(tcx, key, |_| false, Limit(reached_depth)) - { - bug!("unable to retrieve inserted element from cache: {key:?}"); + let expected = CacheData { result, proof_tree, additional_depth, encountered_overflow }; + let actual = self.get(tcx, key, [], Limit(additional_depth)); + if !actual.as_ref().is_some_and(|actual| expected == *actual) { + bug!("failed to lookup inserted element for {key:?}: {expected:?} != {actual:?}"); } } } @@ -63,23 +63,25 @@ impl<'tcx> EvaluationCache<'tcx> { &self, tcx: TyCtxt<'tcx>, key: CanonicalInput<'tcx>, - cycle_participant_in_stack: impl FnOnce(&FxHashSet>) -> bool, + stack_entries: impl IntoIterator>, available_depth: Limit, ) -> Option> { let map = self.map.borrow(); let entry = map.get(&key)?; - if cycle_participant_in_stack(&entry.cycle_participants) { - return None; + for stack_entry in stack_entries { + if entry.cycle_participants.contains(&stack_entry) { + return None; + } } if let Some(ref success) = entry.success { - if available_depth.value_within_limit(success.reached_depth) { + if available_depth.value_within_limit(success.additional_depth) { let QueryData { result, proof_tree } = success.data.get(tcx); return Some(CacheData { result, proof_tree, - reached_depth: success.reached_depth, + additional_depth: success.additional_depth, encountered_overflow: false, }); } @@ -90,7 +92,7 @@ impl<'tcx> EvaluationCache<'tcx> { CacheData { result, proof_tree, - reached_depth: available_depth.0, + additional_depth: available_depth.0, encountered_overflow: true, } }) @@ -99,7 +101,7 @@ impl<'tcx> EvaluationCache<'tcx> { struct Success<'tcx> { data: WithDepNode>, - reached_depth: usize, + additional_depth: usize, } #[derive(Clone, Copy)] diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 87e4fd9ae7368..6cc674dcfed1c 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -134,16 +134,6 @@ impl SearchGraph { self.mode } - /// Update the stack and reached depths on cache hits. - #[instrument(level = "trace", skip(self))] - fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) { - let reached_depth = self.stack.next_index().plus(additional_depth); - if let Some(last) = self.stack.raw.last_mut() { - last.reached_depth = last.reached_depth.max(reached_depth); - last.encountered_overflow |= encountered_overflow; - } - } - /// Pops the highest goal from the stack, lazily updating the /// the next goal in the stack. /// @@ -276,37 +266,7 @@ impl<'tcx> SearchGraph> { return Self::response_no_constraints(tcx, input, Certainty::overflow(true)); }; - // Try to fetch the goal from the global cache. - 'global: { - let Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) = - self.global_cache(tcx).get( - tcx, - input, - |cycle_participants| { - self.stack.iter().any(|entry| cycle_participants.contains(&entry.input)) - }, - available_depth, - ) - else { - break 'global; - }; - - // If we're building a proof tree and the current cache entry does not - // contain a proof tree, we do not use the entry but instead recompute - // the goal. We simply overwrite the existing entry once we're done, - // caching the proof tree. - if !inspect.is_noop() { - if let Some(revisions) = proof_tree { - inspect.goal_evaluation_kind( - inspect::WipCanonicalGoalEvaluationKind::Interned { revisions }, - ); - } else { - break 'global; - } - } - - self.on_cache_hit(reached_depth, encountered_overflow); - debug!("global cache hit"); + if let Some(result) = self.lookup_global_cache(tcx, input, available_depth, inspect) { return result; } @@ -388,7 +348,10 @@ impl<'tcx> SearchGraph> { // This is for global caching, so we properly track query dependencies. // Everything that affects the `result` should be performed within this - // `with_anon_task` closure. + // `with_anon_task` closure. If computing this goal depends on something + // not tracked by the cache key and from outside of this anon task, it + // must not be added to the global cache. Notably, this is the case for + // trait solver cycles participants. let ((final_entry, result), dep_node) = tcx.dep_graph.with_anon_task(tcx, dep_kinds::TraitSelect, || { for _ in 0..FIXPOINT_STEP_LIMIT { @@ -446,6 +409,45 @@ impl<'tcx> SearchGraph> { result } + + /// Try to fetch a previously computed result from the global cache, + /// making sure to only do so if it would match the result of reevaluating + /// this goal. + fn lookup_global_cache( + &mut self, + tcx: TyCtxt<'tcx>, + input: CanonicalInput<'tcx>, + available_depth: Limit, + inspect: &mut ProofTreeBuilder>, + ) -> Option> { + let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self + .global_cache(tcx) + .get(tcx, input, self.stack.iter().map(|e| e.input), available_depth)?; + + // If we're building a proof tree and the current cache entry does not + // contain a proof tree, we do not use the entry but instead recompute + // the goal. We simply overwrite the existing entry once we're done, + // caching the proof tree. + if !inspect.is_noop() { + if let Some(revisions) = proof_tree { + let kind = inspect::WipCanonicalGoalEvaluationKind::Interned { revisions }; + inspect.goal_evaluation_kind(kind); + } else { + return None; + } + } + + // Update the reached depth of the current goal to make sure + // its state is the same regardless of whether we've used the + // global cache or not. + let reached_depth = self.stack.next_index().plus(additional_depth); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(reached_depth); + last.encountered_overflow |= encountered_overflow; + } + + Some(result) + } } enum StepResult<'tcx> { From f99c9ffd88cd6d1954c444e86fe98b3e05333edd Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 20 May 2024 03:12:05 +0000 Subject: [PATCH 20/21] track cycle participants per entry --- .../src/solve/search_graph.rs | 178 +++++++++++++----- .../incompleteness-unstable-result.rs | 2 + ...ncompleteness-unstable-result.with.stderr} | 6 +- ...ompleteness-unstable-result.without.stderr | 26 +++ 4 files changed, 159 insertions(+), 53 deletions(-) rename tests/ui/traits/next-solver/cycles/coinduction/{incompleteness-unstable-result.stderr => incompleteness-unstable-result.with.stderr} (87%) create mode 100644 tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 6cc674dcfed1c..bcd210f789bfe 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -47,20 +47,39 @@ struct StackEntry { /// Whether this entry is a non-root cycle participant. /// /// We must not move the result of non-root cycle participants to the - /// global cache. See [SearchGraph::cycle_participants] for more details. - /// We store the highest stack depth of a head of a cycle this goal is involved - /// in. This necessary to soundly cache its provisional result. + /// global cache. We store the highest stack depth of a head of a cycle + /// this goal is involved in. This necessary to soundly cache its + /// provisional result. non_root_cycle_participant: Option, encountered_overflow: bool, has_been_used: HasBeenUsed, + + /// We put only the root goal of a coinductive cycle into the global cache. + /// + /// If we were to use that result when later trying to prove another cycle + /// participant, we can end up with unstable query results. + /// + /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for + /// an example of where this is needed. + /// + /// There can be multiple roots on the same stack, so we need to track + /// cycle participants per root: + /// ```plain + /// A :- B + /// B :- A, C + /// C :- D + /// D :- C + /// ``` + cycle_participants: FxHashSet>, /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. provisional_result: Option>, } /// The provisional result for a goal which is not on the stack. +#[derive(Debug)] struct DetachedEntry { /// The head of the smallest non-trivial cycle involving this entry. /// @@ -110,24 +129,11 @@ pub(super) struct SearchGraph { /// An element is *deeper* in the stack if its index is *lower*. stack: IndexVec>, provisional_cache: FxHashMap, ProvisionalCacheEntry>, - /// We put only the root goal of a coinductive cycle into the global cache. - /// - /// If we were to use that result when later trying to prove another cycle - /// participant, we can end up with unstable query results. - /// - /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for - /// an example of where this is needed. - cycle_participants: FxHashSet>, } impl SearchGraph { pub(super) fn new(mode: SolverMode) -> SearchGraph { - Self { - mode, - stack: Default::default(), - provisional_cache: Default::default(), - cycle_participants: Default::default(), - } + Self { mode, stack: Default::default(), provisional_cache: Default::default() } } pub(super) fn solver_mode(&self) -> SolverMode { @@ -149,13 +155,7 @@ impl SearchGraph { } pub(super) fn is_empty(&self) -> bool { - if self.stack.is_empty() { - debug_assert!(self.provisional_cache.is_empty()); - debug_assert!(self.cycle_participants.is_empty()); - true - } else { - false - } + self.stack.is_empty() } /// Returns the remaining depth allowed for nested goals. @@ -205,15 +205,26 @@ impl SearchGraph { // their result does not get moved to the global cache. fn tag_cycle_participants( stack: &mut IndexVec>, - cycle_participants: &mut FxHashSet>, usage_kind: HasBeenUsed, head: StackDepth, ) { stack[head].has_been_used |= usage_kind; debug_assert!(!stack[head].has_been_used.is_empty()); - for entry in &mut stack.raw[head.index() + 1..] { + + // The current root of these cycles. Note that this may not be the final + // root in case a later goal depends on a goal higher up the stack. + let mut current_root = head; + while let Some(parent) = stack[current_root].non_root_cycle_participant { + current_root = parent; + debug_assert!(!stack[current_root].has_been_used.is_empty()); + } + + let (stack, cycle_participants) = stack.raw.split_at_mut(head.index() + 1); + let current_cycle_root = &mut stack[current_root.as_usize()]; + for entry in cycle_participants { entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head)); - cycle_participants.insert(entry.input); + current_cycle_root.cycle_participants.insert(entry.input); + current_cycle_root.cycle_participants.extend(mem::take(&mut entry.cycle_participants)); } } @@ -256,6 +267,7 @@ impl<'tcx> SearchGraph> { &mut ProofTreeBuilder>, ) -> QueryResult>, ) -> QueryResult> { + self.check_invariants(); // Check for overflow. let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { if let Some(last) = self.stack.raw.last_mut() { @@ -292,12 +304,7 @@ impl<'tcx> SearchGraph> { // already set correctly while computing the cache entry. inspect .goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::ProvisionalCacheHit); - Self::tag_cycle_participants( - &mut self.stack, - &mut self.cycle_participants, - HasBeenUsed::empty(), - entry.head, - ); + Self::tag_cycle_participants(&mut self.stack, HasBeenUsed::empty(), entry.head); return entry.result; } else if let Some(stack_depth) = cache_entry.stack_depth { debug!("encountered cycle with depth {stack_depth:?}"); @@ -314,12 +321,7 @@ impl<'tcx> SearchGraph> { } else { HasBeenUsed::INDUCTIVE_CYCLE }; - Self::tag_cycle_participants( - &mut self.stack, - &mut self.cycle_participants, - usage_kind, - stack_depth, - ); + Self::tag_cycle_participants(&mut self.stack, usage_kind, stack_depth); // Return the provisional result or, if we're in the first iteration, // start with no constraints. @@ -340,6 +342,7 @@ impl<'tcx> SearchGraph> { non_root_cycle_participant: None, encountered_overflow: false, has_been_used: HasBeenUsed::empty(), + cycle_participants: Default::default(), provisional_result: None, }; assert_eq!(self.stack.push(entry), depth); @@ -386,14 +389,13 @@ impl<'tcx> SearchGraph> { } else { self.provisional_cache.remove(&input); let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); - let cycle_participants = mem::take(&mut self.cycle_participants); // When encountering a cycle, both inductive and coinductive, we only // move the root into the global cache. We also store all other cycle // participants involved. // // We must not use the global cache entry of a root goal if a cycle // participant is on the stack. This is necessary to prevent unstable - // results. See the comment of `SearchGraph::cycle_participants` for + // results. See the comment of `StackEntry::cycle_participants` for // more details. self.global_cache(tcx).insert( tcx, @@ -401,12 +403,14 @@ impl<'tcx> SearchGraph> { proof_tree, reached_depth, final_entry.encountered_overflow, - cycle_participants, + final_entry.cycle_participants, dep_node, result, ) } + self.check_invariants(); + result } @@ -416,10 +420,10 @@ impl<'tcx> SearchGraph> { fn lookup_global_cache( &mut self, tcx: TyCtxt<'tcx>, - input: CanonicalInput<'tcx>, + input: CanonicalInput>, available_depth: Limit, inspect: &mut ProofTreeBuilder>, - ) -> Option> { + ) -> Option>> { let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self .global_cache(tcx) .get(tcx, input, self.stack.iter().map(|e| e.input), available_depth)?; @@ -450,12 +454,12 @@ impl<'tcx> SearchGraph> { } } -enum StepResult<'tcx> { - Done(StackEntry<'tcx>, QueryResult<'tcx>), +enum StepResult { + Done(StackEntry, QueryResult), HasChanged, } -impl<'tcx> SearchGraph<'tcx> { +impl<'tcx> SearchGraph> { /// When we encounter a coinductive cycle, we have to fetch the /// result of that cycle while we are still computing it. Because /// of this we continuously recompute the cycle until the result @@ -464,12 +468,12 @@ impl<'tcx> SearchGraph<'tcx> { fn fixpoint_step_in_task( &mut self, tcx: TyCtxt<'tcx>, - input: CanonicalInput<'tcx>, + input: CanonicalInput>, inspect: &mut ProofTreeBuilder>, prove_goal: &mut F, - ) -> StepResult<'tcx> + ) -> StepResult> where - F: FnMut(&mut Self, &mut ProofTreeBuilder>) -> QueryResult<'tcx>, + F: FnMut(&mut Self, &mut ProofTreeBuilder>) -> QueryResult>, { let result = prove_goal(self, inspect); let stack_entry = self.pop_stack(); @@ -530,3 +534,77 @@ impl<'tcx> SearchGraph<'tcx> { Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty)) } } + +impl SearchGraph { + #[allow(rustc::potential_query_instability)] + fn check_invariants(&self) { + if !cfg!(debug_assertions) { + return; + } + + let SearchGraph { mode: _, stack, provisional_cache } = self; + if stack.is_empty() { + assert!(provisional_cache.is_empty()); + } + + for (depth, entry) in stack.iter_enumerated() { + let StackEntry { + input, + available_depth: _, + reached_depth: _, + non_root_cycle_participant, + encountered_overflow: _, + has_been_used, + ref cycle_participants, + provisional_result, + } = *entry; + let cache_entry = provisional_cache.get(&entry.input).unwrap(); + assert_eq!(cache_entry.stack_depth, Some(depth)); + if let Some(head) = non_root_cycle_participant { + assert!(head < depth); + assert!(cycle_participants.is_empty()); + assert_ne!(stack[head].has_been_used, HasBeenUsed::empty()); + + let mut current_root = head; + while let Some(parent) = stack[current_root].non_root_cycle_participant { + current_root = parent; + } + assert!(stack[current_root].cycle_participants.contains(&input)); + } + + if !cycle_participants.is_empty() { + assert!(provisional_result.is_some() || !has_been_used.is_empty()); + for entry in stack.iter().take(depth.as_usize()) { + assert_eq!(cycle_participants.get(&entry.input), None); + } + } + } + + for (&input, entry) in &self.provisional_cache { + let ProvisionalCacheEntry { stack_depth, with_coinductive_stack, with_inductive_stack } = + entry; + assert!( + stack_depth.is_some() + || with_coinductive_stack.is_some() + || with_inductive_stack.is_some() + ); + + if let &Some(stack_depth) = stack_depth { + assert_eq!(stack[stack_depth].input, input); + } + + let check_detached = |detached_entry: &DetachedEntry| { + let DetachedEntry { head, result: _ } = *detached_entry; + assert_ne!(stack[head].has_been_used, HasBeenUsed::empty()); + }; + + if let Some(with_coinductive_stack) = with_coinductive_stack { + check_detached(with_coinductive_stack); + } + + if let Some(with_inductive_stack) = with_inductive_stack { + check_detached(with_inductive_stack); + } + } + } +} diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs index 7eea81ce03c66..920f8add50795 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs +++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs @@ -1,3 +1,4 @@ +//@ revisions: with without //@ compile-flags: -Znext-solver #![feature(rustc_attrs)] @@ -56,6 +57,7 @@ where X: IncompleteGuidance, X: IncompleteGuidance, { + #[cfg(with)] impls_trait::, _, _, _>(); // entering the cycle from `B` works // entering the cycle from `A` fails, but would work if we were to use the cache diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr similarity index 87% rename from tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr rename to tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr index ffa3f29e4bd6f..a81229e5e355c 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr @@ -1,12 +1,12 @@ error[E0277]: the trait bound `A: Trait<_, _, _>` is not satisfied - --> $DIR/incompleteness-unstable-result.rs:63:19 + --> $DIR/incompleteness-unstable-result.rs:65:19 | LL | impls_trait::, _, _, _>(); | ^^^^ the trait `Trait<_, _, _>` is not implemented for `A`, which is required by `A: Trait<_, _, _>` | = help: the trait `Trait` is implemented for `A` note: required for `A` to implement `Trait<_, _, _>` - --> $DIR/incompleteness-unstable-result.rs:32:50 + --> $DIR/incompleteness-unstable-result.rs:33:50 | LL | impl Trait for A | ^^^^^^^^^^^^^^ ^^^^ @@ -16,7 +16,7 @@ LL | A: Trait, = note: 8 redundant requirements hidden = note: required for `A` to implement `Trait<_, _, _>` note: required by a bound in `impls_trait` - --> $DIR/incompleteness-unstable-result.rs:51:28 + --> $DIR/incompleteness-unstable-result.rs:52:28 | LL | fn impls_trait, U: ?Sized, V: ?Sized, D: ?Sized>() {} | ^^^^^^^^^^^^^^ required by this bound in `impls_trait` diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr new file mode 100644 index 0000000000000..a81229e5e355c --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr @@ -0,0 +1,26 @@ +error[E0277]: the trait bound `A: Trait<_, _, _>` is not satisfied + --> $DIR/incompleteness-unstable-result.rs:65:19 + | +LL | impls_trait::, _, _, _>(); + | ^^^^ the trait `Trait<_, _, _>` is not implemented for `A`, which is required by `A: Trait<_, _, _>` + | + = help: the trait `Trait` is implemented for `A` +note: required for `A` to implement `Trait<_, _, _>` + --> $DIR/incompleteness-unstable-result.rs:33:50 + | +LL | impl Trait for A + | ^^^^^^^^^^^^^^ ^^^^ +... +LL | A: Trait, + | -------------- unsatisfied trait bound introduced here + = note: 8 redundant requirements hidden + = note: required for `A` to implement `Trait<_, _, _>` +note: required by a bound in `impls_trait` + --> $DIR/incompleteness-unstable-result.rs:52:28 + | +LL | fn impls_trait, U: ?Sized, V: ?Sized, D: ?Sized>() {} + | ^^^^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From fa1b7f2d7865e8fd0165899f8ac2a01aca121e39 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Mon, 20 May 2024 23:17:11 +0200 Subject: [PATCH 21/21] Remove some `Path::to_str` from `rustc_codegen_llvm` Unnecessary panic paths when there's a better option. --- .../rustc_codegen_llvm/src/back/archive.rs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index d4a3e39cef728..c304c0cbd3bd5 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -200,21 +200,20 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { _ => panic!("unsupported arch {}", sess.target.arch), }; let mut dlltool_cmd = std::process::Command::new(&dlltool); - dlltool_cmd.args([ - "-d", - def_file_path.to_str().unwrap(), - "-D", - lib_name, - "-l", - output_path.to_str().unwrap(), - "-m", - dlltool_target_arch, - "-f", - dlltool_target_bitness, - "--no-leading-underscore", - "--temp-prefix", - temp_prefix.to_str().unwrap(), - ]); + dlltool_cmd + .arg("-d") + .arg(def_file_path) + .arg("-D") + .arg(lib_name) + .arg("-l") + .arg(&output_path) + .arg("-m") + .arg(dlltool_target_arch) + .arg("-f") + .arg(dlltool_target_bitness) + .arg("--no-leading-underscore") + .arg("--temp-prefix") + .arg(temp_prefix); match dlltool_cmd.output() { Err(e) => {