From 4be041a2cd9f134ee42579beb14840b9abebcb75 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 15 May 2024 10:00:42 -0500 Subject: [PATCH 01/21] Also apply `warn(for_loops_over_fallibles)` to &T and &mut T, not just T = Result/Option. --- compiler/rustc_lint/src/for_loops_over_fallibles.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index a6876d8aae793..d766393db29a5 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -52,7 +52,15 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { let ty = cx.typeck_results().expr_ty(arg); - let &ty::Adt(adt, args) = ty.kind() else { return }; + // let &ty::Adt(adt, args) = ty.kind() else { return }; + let (adt, args, _) = match ty.kind() { + &ty::Adt(adt, args) => (adt, args, None), + &ty::Ref(_, ty, mutability) => match ty.kind() { + &ty::Adt(adt, args) => (adt, args, Some(mutability)), + _ => return, + }, + _ => return, + }; let (article, ty, var) = match adt.did() { did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"), From 7d7eb973d0118a52209e5ee7adafc29b341957f0 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 15 May 2024 10:01:06 -0500 Subject: [PATCH 02/21] Fix new for_loops_over_fallibles hits in compiler. --- compiler/rustc_builtin_macros/src/deriving/default.rs | 4 ++-- compiler/rustc_resolve/src/late.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index bf92ddb33701c..577523a1d5a32 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -3,7 +3,7 @@ use crate::deriving::generic::*; use crate::errors; use core::ops::ControlFlow; use rustc_ast as ast; -use rustc_ast::visit::walk_list; +use rustc_ast::visit::visit_opt; use rustc_ast::{attr, EnumDef, VariantData}; use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; use rustc_span::symbol::Ident; @@ -224,7 +224,7 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, ' self.visit_ident(v.ident); self.visit_vis(&v.vis); self.visit_variant_data(&v.data); - walk_list!(self, visit_anon_const, &v.disr_expr); + visit_opt!(self, visit_anon_const, &v.disr_expr); for attr in &v.attrs { rustc_ast::visit::walk_attribute(self, attr); } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 322f2922f9253..796e3e38aebfb 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -12,7 +12,7 @@ use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult}; use crate::{ResolutionError, Resolver, Segment, UseError}; use rustc_ast::ptr::P; -use rustc_ast::visit::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; +use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::{codes::*, Applicability, DiagArgValue, IntoDiagArg, StashKey}; @@ -3286,7 +3286,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn resolve_local(&mut self, local: &'ast Local) { debug!("resolving local ({:?})", local); // Resolve the type. - walk_list!(self, visit_ty, &local.ty); + visit_opt!(self, visit_ty, &local.ty); // Resolve the initializer. if let Some((init, els)) = local.kind.init_else_opt() { @@ -3485,8 +3485,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn resolve_arm(&mut self, arm: &'ast Arm) { self.with_rib(ValueNS, RibKind::Normal, |this| { this.resolve_pattern_top(&arm.pat, PatternSource::Match); - walk_list!(this, visit_expr, &arm.guard); - walk_list!(this, visit_expr, &arm.body); + visit_opt!(this, visit_expr, &arm.guard); + visit_opt!(this, visit_expr, &arm.body); }); } From 77f288c18d638f3cfda91810e0c4fb6a6c7e6963 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 15 May 2024 10:59:13 -0500 Subject: [PATCH 03/21] Include reference in lint diagnostic --- compiler/rustc_lint/messages.ftl | 2 +- compiler/rustc_lint/src/for_loops_over_fallibles.rs | 10 +++++++--- compiler/rustc_lint/src/lints.rs | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5180fce2eb378..8c9ad5b6c6b7a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -215,7 +215,7 @@ lint_expectation = this lint expectation is unfulfilled .rationale = {$rationale} lint_for_loops_over_fallibles = - for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement + for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent .remove_next = to iterate over `{$recv_snip}` remove the call to `next` .use_while_let = to check pattern in a loop use `while let` diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index d766393db29a5..12a790bb0faf5 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -52,8 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { let ty = cx.typeck_results().expr_ty(arg); - // let &ty::Adt(adt, args) = ty.kind() else { return }; - let (adt, args, _) = match ty.kind() { + let (adt, args, ref_mutability) = match ty.kind() { &ty::Adt(adt, args) => (adt, args, None), &ty::Ref(_, ty, mutability) => match ty.kind() { &ty::Adt(adt, args) => (adt, args, Some(mutability)), @@ -68,6 +67,11 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { _ => return, }; + let ref_prefix = match ref_mutability { + None => "", + Some(ref_mutability) => ref_mutability.ref_prefix_str(), + }; + let sub = if let Some(recv) = extract_iterator_next_call(cx, arg) && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span) { @@ -93,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { cx.emit_span_lint( FOR_LOOPS_OVER_FALLIBLES, arg.span, - ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion }, + ForLoopsOverFalliblesDiag { article, ref_prefix, ty, sub, question_mark, suggestion }, ); } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 7efa5245baa20..382532b17880a 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -613,6 +613,7 @@ pub enum PtrNullChecksDiag<'a> { #[diag(lint_for_loops_over_fallibles)] pub struct ForLoopsOverFalliblesDiag<'a> { pub article: &'static str, + pub ref_prefix: &'static str, pub ty: &'static str, #[subdiagnostic] pub sub: ForLoopsOverFalliblesLoopSub<'a>, From ea549fd1761c8f1fbc99df2ad7d3c1ac7d8f7824 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 15 May 2024 11:53:40 -0500 Subject: [PATCH 04/21] Add tests for 'Also apply `warn(for_loops_over_fallibles)` to &T and &mut T, not just T = Result/Option.' --- tests/ui/lint/for_loop_over_fallibles.rs | 22 +++++++ tests/ui/lint/for_loop_over_fallibles.stderr | 62 +++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/tests/ui/lint/for_loop_over_fallibles.rs b/tests/ui/lint/for_loop_over_fallibles.rs index 52c3b8f2aae5f..76049453126b2 100644 --- a/tests/ui/lint/for_loop_over_fallibles.rs +++ b/tests/ui/lint/for_loop_over_fallibles.rs @@ -41,3 +41,25 @@ fn _returns_result() -> Result<(), ()> { Ok(()) } + +fn _by_ref() { + // Shared refs + for _ in &Some(1) {} + //~^ WARN for loop over an `&Option`. This is more readably written as an `if let` statement + //~| HELP to check pattern in a loop use `while let` + //~| HELP consider using `if let` to clear intent + for _ in &Ok::<_, ()>(1) {} + //~^ WARN for loop over a `&Result`. This is more readably written as an `if let` statement + //~| HELP to check pattern in a loop use `while let` + //~| HELP consider using `if let` to clear intent + + // Mutable refs + for _ in &mut Some(1) {} + //~^ WARN for loop over an `&mut Option`. This is more readably written as an `if let` statement + //~| HELP to check pattern in a loop use `while let` + //~| HELP consider using `if let` to clear intent + for _ in &mut Ok::<_, ()>(1) {} + //~^ WARN for loop over a `&mut Result`. This is more readably written as an `if let` statement + //~| HELP to check pattern in a loop use `while let` + //~| HELP consider using `if let` to clear intent +} diff --git a/tests/ui/lint/for_loop_over_fallibles.stderr b/tests/ui/lint/for_loop_over_fallibles.stderr index 96efdf85c4901..00134e468ba65 100644 --- a/tests/ui/lint/for_loop_over_fallibles.stderr +++ b/tests/ui/lint/for_loop_over_fallibles.stderr @@ -97,5 +97,65 @@ help: consider using `if let` to clear intent LL | if let Ok(_) = Ok::<_, ()>([0; 0]) {} | ~~~~~~~~~~ ~~~ -warning: 6 warnings emitted +warning: for loop over an `&Option`. This is more readably written as an `if let` statement + --> $DIR/for_loop_over_fallibles.rs:47:14 + | +LL | for _ in &Some(1) {} + | ^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +LL | while let Some(_) = &Some(1) {} + | ~~~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +LL | if let Some(_) = &Some(1) {} + | ~~~~~~~~~~~~ ~~~ + +warning: for loop over a `&Result`. This is more readably written as an `if let` statement + --> $DIR/for_loop_over_fallibles.rs:51:14 + | +LL | for _ in &Ok::<_, ()>(1) {} + | ^^^^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +LL | while let Ok(_) = &Ok::<_, ()>(1) {} + | ~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +LL | if let Ok(_) = &Ok::<_, ()>(1) {} + | ~~~~~~~~~~ ~~~ + +warning: for loop over an `&mut Option`. This is more readably written as an `if let` statement + --> $DIR/for_loop_over_fallibles.rs:57:14 + | +LL | for _ in &mut Some(1) {} + | ^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +LL | while let Some(_) = &mut Some(1) {} + | ~~~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +LL | if let Some(_) = &mut Some(1) {} + | ~~~~~~~~~~~~ ~~~ + +warning: for loop over a `&mut Result`. This is more readably written as an `if let` statement + --> $DIR/for_loop_over_fallibles.rs:61:14 + | +LL | for _ in &mut Ok::<_, ()>(1) {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +LL | while let Ok(_) = &mut Ok::<_, ()>(1) {} + | ~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +LL | if let Ok(_) = &mut Ok::<_, ()>(1) {} + | ~~~~~~~~~~ ~~~ + +warning: 10 warnings emitted From 0e467596ffa85c68504b94b951a0c32fefb3c889 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 15 May 2024 12:17:25 -0500 Subject: [PATCH 05/21] Fix more new for_loops_over_fallibles hits in compiler. --- compiler/rustc_hir_analysis/src/check/region.rs | 4 ++-- compiler/rustc_mir_build/src/build/matches/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 4540310937d02..e51f95eed021b 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -6,7 +6,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html -use rustc_ast::visit::walk_list; +use rustc_ast::visit::visit_opt; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -168,7 +168,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement), } } - walk_list!(visitor, visit_expr, &blk.expr); + visit_opt!(visitor, visit_expr, &blk.expr); } visitor.cx = prev_cx; diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 3fc719394bf9e..d948354bc4959 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -925,7 +925,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for subpattern in prefix.iter() { self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); } - for subpattern in slice { + if let Some(subpattern) = slice { self.visit_primary_bindings( subpattern, pattern_user_ty.clone().subslice(from, to), From 66573b70a9b1807337f1980538d524e1dfd2aba1 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 15 May 2024 12:45:52 -0500 Subject: [PATCH 06/21] Use 'a' article for &Option. --- compiler/rustc_lint/src/for_loops_over_fallibles.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index 12a790bb0faf5..b05f5e7638b4e 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -62,6 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { }; let (article, ty, var) = match adt.did() { + did if cx.tcx.is_diagnostic_item(sym::Option, did) && ref_mutability.is_some() => ("a", "Option", "Some"), did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"), did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"), _ => return, From 6b818ddac61e9771e3b97f17d8ab21cf9f7bc2de Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 15 May 2024 13:15:59 -0500 Subject: [PATCH 07/21] Fix article in test --- tests/ui/lint/for_loop_over_fallibles.rs | 4 ++-- tests/ui/lint/for_loop_over_fallibles.stderr | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/lint/for_loop_over_fallibles.rs b/tests/ui/lint/for_loop_over_fallibles.rs index 76049453126b2..89ac20c352178 100644 --- a/tests/ui/lint/for_loop_over_fallibles.rs +++ b/tests/ui/lint/for_loop_over_fallibles.rs @@ -45,7 +45,7 @@ fn _returns_result() -> Result<(), ()> { fn _by_ref() { // Shared refs for _ in &Some(1) {} - //~^ WARN for loop over an `&Option`. This is more readably written as an `if let` statement + //~^ WARN for loop over a `&Option`. This is more readably written as an `if let` statement //~| HELP to check pattern in a loop use `while let` //~| HELP consider using `if let` to clear intent for _ in &Ok::<_, ()>(1) {} @@ -55,7 +55,7 @@ fn _by_ref() { // Mutable refs for _ in &mut Some(1) {} - //~^ WARN for loop over an `&mut Option`. This is more readably written as an `if let` statement + //~^ WARN for loop over a `&mut Option`. This is more readably written as an `if let` statement //~| HELP to check pattern in a loop use `while let` //~| HELP consider using `if let` to clear intent for _ in &mut Ok::<_, ()>(1) {} diff --git a/tests/ui/lint/for_loop_over_fallibles.stderr b/tests/ui/lint/for_loop_over_fallibles.stderr index 00134e468ba65..f695de082572a 100644 --- a/tests/ui/lint/for_loop_over_fallibles.stderr +++ b/tests/ui/lint/for_loop_over_fallibles.stderr @@ -97,7 +97,7 @@ help: consider using `if let` to clear intent LL | if let Ok(_) = Ok::<_, ()>([0; 0]) {} | ~~~~~~~~~~ ~~~ -warning: for loop over an `&Option`. This is more readably written as an `if let` statement +warning: for loop over a `&Option`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:47:14 | LL | for _ in &Some(1) {} @@ -127,7 +127,7 @@ help: consider using `if let` to clear intent LL | if let Ok(_) = &Ok::<_, ()>(1) {} | ~~~~~~~~~~ ~~~ -warning: for loop over an `&mut Option`. This is more readably written as an `if let` statement +warning: for loop over a `&mut Option`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:57:14 | LL | for _ in &mut Some(1) {} From 376a8c0ae5ad6cb8d0c5d6631874bdd3c950436f Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 15 May 2024 13:51:16 -0500 Subject: [PATCH 08/21] Allow for_loops_over_fallibles in test that tests &mut Result as IntoIterator. --- library/core/tests/result.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs index a47ef7aa1ebc6..00a6fd75b4f46 100644 --- a/library/core/tests/result.rs +++ b/library/core/tests/result.rs @@ -170,6 +170,7 @@ pub fn test_iter() { } #[test] +#[allow(for_loops_over_fallibles)] pub fn test_iter_mut() { let mut ok: Result = Ok(100); for loc in ok.iter_mut() { From a59d071e9caa140ddaff4a9e1b747dba0d95f0cd Mon Sep 17 00:00:00 2001 From: zachs18 <8355914+zachs18@users.noreply.github.com> Date: Thu, 16 May 2024 05:51:29 +0000 Subject: [PATCH 09/21] Fix `for_loops_over_fallibles` hits in compiletest/src/json.rs --- src/tools/compiletest/src/json.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 10726b9842080..29e8809e5bd66 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -282,7 +282,7 @@ fn push_expected_errors( // Add notes for the backtrace for span in primary_spans { - for frame in &span.expansion { + if let Some(frame) = &span.expansion { push_backtrace(expected_errors, frame, file_name); } } @@ -315,7 +315,7 @@ fn push_backtrace( }); } - for previous_expansion in &expansion.span.expansion { + if let Some(previous_expansion) = &expansion.span.expansion { push_backtrace(expected_errors, previous_expansion, file_name); } } From 3a21fb5cec07052cb664b6a051e233cd4776283a Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 21 May 2024 18:55:12 -0700 Subject: [PATCH 10/21] Wrap Context.ext in AssertUnwindSafe --- library/core/src/task/wake.rs | 11 +++-- tests/ui/async-await/async-is-unwindsafe.rs | 1 - .../ui/async-await/async-is-unwindsafe.stderr | 43 +++---------------- .../context-is-sorta-unwindsafe.rs | 14 ++++++ 4 files changed, 27 insertions(+), 42 deletions(-) create mode 100644 tests/ui/async-await/context-is-sorta-unwindsafe.rs diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 7691721b91e21..3d21b09fa8a02 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -5,6 +5,7 @@ use crate::mem::transmute; use crate::any::Any; use crate::fmt; use crate::marker::PhantomData; +use crate::panic::AssertUnwindSafe; use crate::ptr; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] @@ -236,7 +237,7 @@ enum ExtData<'a> { pub struct Context<'a> { waker: &'a Waker, local_waker: &'a LocalWaker, - ext: ExtData<'a>, + ext: AssertUnwindSafe>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -279,7 +280,9 @@ impl<'a> Context<'a> { #[unstable(feature = "context_ext", issue = "123392")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn ext(&mut self) -> &mut dyn Any { - match &mut self.ext { + // FIXME: this field makes Context extra-weird about unwind safety + // can we justify AssertUnwindSafe if we stabilize this? do we care? + match &mut *self.ext { ExtData::Some(data) => *data, ExtData::None(unit) => unit, } @@ -353,7 +356,7 @@ impl<'a> ContextBuilder<'a> { #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "context_ext", issue = "123392")] pub const fn from(cx: &'a mut Context<'_>) -> Self { - let ext = match &mut cx.ext { + let ext = match &mut *cx.ext { ExtData::Some(ext) => ExtData::Some(*ext), ExtData::None(()) => ExtData::None(()), }; @@ -396,7 +399,7 @@ impl<'a> ContextBuilder<'a> { #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self; - Context { waker, local_waker, ext, _marker, _marker2 } + Context { waker, local_waker, ext: AssertUnwindSafe(ext), _marker, _marker2 } } } diff --git a/tests/ui/async-await/async-is-unwindsafe.rs b/tests/ui/async-await/async-is-unwindsafe.rs index d0202f72f0086..53009b6e7410f 100644 --- a/tests/ui/async-await/async-is-unwindsafe.rs +++ b/tests/ui/async-await/async-is-unwindsafe.rs @@ -11,7 +11,6 @@ fn main() { is_unwindsafe(async { //~^ ERROR the type `&mut Context<'_>` may not be safely transferred across an unwind boundary - //~| ERROR the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary use std::ptr::null; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; let waker = unsafe { diff --git a/tests/ui/async-await/async-is-unwindsafe.stderr b/tests/ui/async-await/async-is-unwindsafe.stderr index 6bb06df9f39e9..5d87fc747682a 100644 --- a/tests/ui/async-await/async-is-unwindsafe.stderr +++ b/tests/ui/async-await/async-is-unwindsafe.stderr @@ -6,18 +6,19 @@ LL | is_unwindsafe(async { | |_____| | || LL | || -LL | || LL | || use std::ptr::null; +LL | || use std::task::{Context, RawWaker, RawWakerVTable, Waker}; ... || LL | || drop(cx_ref); LL | || }); | ||_____-^ `&mut Context<'_>` may not be safely transferred across an unwind boundary | |_____| - | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}` + | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}` | - = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe` + = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}: UnwindSafe` + = note: `UnwindSafe` is implemented for `&Context<'_>`, but not for `&mut Context<'_>` note: future does not implement `UnwindSafe` as this value is used across an await - --> $DIR/async-is-unwindsafe.rs:26:18 + --> $DIR/async-is-unwindsafe.rs:25:18 | LL | let cx_ref = &mut cx; | ------ has type `&mut Context<'_>` which does not implement `UnwindSafe` @@ -30,38 +31,6 @@ note: required by a bound in `is_unwindsafe` LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {} | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe` -error[E0277]: the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary - --> $DIR/async-is-unwindsafe.rs:12:5 - | -LL | is_unwindsafe(async { - | _____^_____________- - | |_____| - | || -LL | || -LL | || -LL | || use std::ptr::null; -... || -LL | || drop(cx_ref); -LL | || }); - | ||_____-^ `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary - | |_____| - | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}` - | - = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut (dyn Any + 'static)`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe` -note: future does not implement `UnwindSafe` as this value is used across an await - --> $DIR/async-is-unwindsafe.rs:26:18 - | -LL | let mut cx = Context::from_waker(&waker); - | ------ has type `Context<'_>` which does not implement `UnwindSafe` -... -LL | async {}.await; // this needs an inner await point - | ^^^^^ await occurs here, with `mut cx` maybe used later -note: required by a bound in `is_unwindsafe` - --> $DIR/async-is-unwindsafe.rs:3:26 - | -LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {} - | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/context-is-sorta-unwindsafe.rs b/tests/ui/async-await/context-is-sorta-unwindsafe.rs new file mode 100644 index 0000000000000..278476ea2379c --- /dev/null +++ b/tests/ui/async-await/context-is-sorta-unwindsafe.rs @@ -0,0 +1,14 @@ +//@ run-pass +// Tests against a regression surfaced by crater in https://github.com/rust-lang/rust/issues/125193 +// Unwind Safety is not a very coherent concept, but we'd prefer no regressions until we kibosh it +// and this is an unstable feature anyways sooo... + +use std::panic::UnwindSafe; +use std::task::Context; + +fn unwind_safe() {} + +fn main() { + unwind_safe::>(); // test UnwindSafe + unwind_safe::<&Context<'_>>(); // test RefUnwindSafe +} From 8fde7e3b64109f5ebfadef4a015e2cb055bed08c Mon Sep 17 00:00:00 2001 From: surechen Date: Mon, 8 Apr 2024 16:19:09 +0800 Subject: [PATCH 11/21] For OutsideLoop we should not suggest add 'block label in if block, or we wiil get another err: block label not supported here. fixes #123261 --- compiler/rustc_passes/src/errors.rs | 4 +- compiler/rustc_passes/src/loops.rs | 172 +++++++++++++++--- .../loop-if-else-break-issue-123261.fixed | 31 ++++ .../loops/loop-if-else-break-issue-123261.rs | 31 ++++ .../loop-if-else-break-issue-123261.stderr | 59 ++++++ .../break-in-unlabeled-block-in-macro.stderr | 38 ++-- 6 files changed, 290 insertions(+), 45 deletions(-) create mode 100644 tests/ui/loops/loop-if-else-break-issue-123261.fixed create mode 100644 tests/ui/loops/loop-if-else-break-issue-123261.rs create mode 100644 tests/ui/loops/loop-if-else-break-issue-123261.stderr diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index b8586e7e974ad..a55187170937e 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1103,7 +1103,7 @@ pub struct BreakInsideCoroutine<'a> { pub struct OutsideLoop<'a> { #[primary_span] #[label] - pub span: Span, + pub spans: Vec, pub name: &'a str, pub is_break: bool, #[subdiagnostic] @@ -1115,7 +1115,7 @@ pub struct OutsideLoopSuggestion { #[suggestion_part(code = "'block: ")] pub block_span: Span, #[suggestion_part(code = " 'block")] - pub break_span: Span, + pub break_spans: Vec, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 3b20112eab7b9..2587a18b8c897 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; +use std::fmt; use Context::*; use rustc_hir as hir; @@ -25,22 +27,55 @@ enum Context { Closure(Span), Coroutine { coroutine_span: Span, kind: hir::CoroutineDesugaring, source: hir::CoroutineSource }, UnlabeledBlock(Span), + UnlabeledIfBlock(Span), LabeledBlock, Constant, } -#[derive(Copy, Clone)] +#[derive(Clone)] +struct BlockInfo { + name: String, + spans: Vec, + suggs: Vec, +} + +#[derive(PartialEq)] +enum BreakContextKind { + Break, + Continue, +} + +impl fmt::Display for BreakContextKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BreakContextKind::Break => "break", + BreakContextKind::Continue => "continue", + } + .fmt(f) + } +} + +#[derive(Clone)] struct CheckLoopVisitor<'a, 'tcx> { sess: &'a Session, tcx: TyCtxt<'tcx>, - cx: Context, + // Keep track of a stack of contexts, so that suggestions + // are not made for contexts where it would be incorrect, + // such as adding a label for an `if`. + // e.g. `if 'foo: {}` would be incorrect. + cx_stack: Vec, + block_breaks: BTreeMap, } fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { - tcx.hir().visit_item_likes_in_module( - module_def_id, - &mut CheckLoopVisitor { sess: tcx.sess, tcx, cx: Normal }, - ); + let mut check = CheckLoopVisitor { + sess: tcx.sess, + tcx, + cx_stack: vec![Normal], + block_breaks: Default::default(), + }; + tcx.hir().visit_item_likes_in_module(module_def_id, &mut check); + check.report_outside_loop_error(); } pub(crate) fn provide(providers: &mut Providers) { @@ -83,6 +118,45 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { match e.kind { + hir::ExprKind::If(cond, then, else_opt) => { + self.visit_expr(cond); + + let get_block = |ck_loop: &CheckLoopVisitor<'a, 'hir>, + expr: &hir::Expr<'hir>| + -> Option<&hir::Block<'hir>> { + if let hir::ExprKind::Block(b, None) = expr.kind + && matches!( + ck_loop.cx_stack.last(), + Some(&Normal) + | Some(&Constant) + | Some(&UnlabeledBlock(_)) + | Some(&UnlabeledIfBlock(_)) + ) + { + Some(b) + } else { + None + } + }; + + if let Some(b) = get_block(self, then) { + self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| { + v.visit_block(b) + }); + } else { + self.visit_expr(then); + } + + if let Some(else_expr) = else_opt { + if let Some(b) = get_block(self, else_expr) { + self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| { + v.visit_block(b) + }); + } else { + self.visit_expr(else_expr); + } + } + } hir::ExprKind::Loop(ref b, _, source, _) => { self.with_context(Loop(source), |v| v.visit_block(b)); } @@ -101,11 +175,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { hir::ExprKind::Block(ref b, Some(_label)) => { self.with_context(LabeledBlock, |v| v.visit_block(b)); } - hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => { + hir::ExprKind::Block(ref b, None) if matches!(self.cx_stack.last(), Some(&Fn)) => { self.with_context(Normal, |v| v.visit_block(b)); } hir::ExprKind::Block(ref b, None) - if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) => + if matches!( + self.cx_stack.last(), + Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_)) + ) => { self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b)); } @@ -178,7 +255,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { Some(label) => sp_lo.with_hi(label.ident.span.hi()), None => sp_lo.shrink_to_lo(), }; - self.require_break_cx("break", e.span, label_sp); + self.require_break_cx( + BreakContextKind::Break, + e.span, + label_sp, + self.cx_stack.len() - 1, + ); } hir::ExprKind::Continue(destination) => { self.require_label_in_labeled_block(e.span, &destination, "continue"); @@ -200,7 +282,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { } Err(_) => {} } - self.require_break_cx("continue", e.span, e.span) + self.require_break_cx( + BreakContextKind::Continue, + e.span, + e.span, + self.cx_stack.len() - 1, + ) } _ => intravisit::walk_expr(self, e), } @@ -212,18 +299,26 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { where F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>), { - let old_cx = self.cx; - self.cx = cx; + self.cx_stack.push(cx); f(self); - self.cx = old_cx; + self.cx_stack.pop(); } - fn require_break_cx(&self, name: &str, span: Span, break_span: Span) { - let is_break = name == "break"; - match self.cx { + fn require_break_cx( + &mut self, + br_cx_kind: BreakContextKind, + span: Span, + break_span: Span, + cx_pos: usize, + ) { + match self.cx_stack[cx_pos] { LabeledBlock | Loop(_) => {} Closure(closure_span) => { - self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name }); + self.sess.dcx().emit_err(BreakInsideClosure { + span, + closure_span, + name: &br_cx_kind.to_string(), + }); } Coroutine { coroutine_span, kind, source } => { let kind = match kind { @@ -239,17 +334,32 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { self.sess.dcx().emit_err(BreakInsideCoroutine { span, coroutine_span, - name, + name: &br_cx_kind.to_string(), kind, source, }); } - UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => { - let suggestion = Some(OutsideLoopSuggestion { block_span, break_span }); - self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion }); + UnlabeledBlock(block_span) + if br_cx_kind == BreakContextKind::Break && block_span.eq_ctxt(break_span) => + { + let block = self.block_breaks.entry(block_span).or_insert_with(|| BlockInfo { + name: br_cx_kind.to_string(), + spans: vec![], + suggs: vec![], + }); + block.spans.push(span); + block.suggs.push(break_span); + } + UnlabeledIfBlock(_) if br_cx_kind == BreakContextKind::Break => { + self.require_break_cx(br_cx_kind, span, break_span, cx_pos - 1); } - Normal | Constant | Fn | UnlabeledBlock(_) => { - self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion: None }); + Normal | Constant | Fn | UnlabeledBlock(_) | UnlabeledIfBlock(_) => { + self.sess.dcx().emit_err(OutsideLoop { + spans: vec![span], + name: &br_cx_kind.to_string(), + is_break: br_cx_kind == BreakContextKind::Break, + suggestion: None, + }); } } } @@ -261,7 +371,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { cf_type: &str, ) -> bool { if !span.is_desugaring(DesugaringKind::QuestionMark) - && self.cx == LabeledBlock + && self.cx_stack.last() == Some(&LabeledBlock) && label.label.is_none() { self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type }); @@ -269,4 +379,18 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { } false } + + fn report_outside_loop_error(&mut self) { + for (s, block) in &self.block_breaks { + self.sess.dcx().emit_err(OutsideLoop { + spans: block.spans.clone(), + name: &block.name, + is_break: true, + suggestion: Some(OutsideLoopSuggestion { + block_span: *s, + break_spans: block.suggs.clone(), + }), + }); + } + } } diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.fixed b/tests/ui/loops/loop-if-else-break-issue-123261.fixed new file mode 100644 index 0000000000000..f9e88c18ad02c --- /dev/null +++ b/tests/ui/loops/loop-if-else-break-issue-123261.fixed @@ -0,0 +1,31 @@ +//@ run-rustfix + +#![allow(unused)] + +fn main() { + let n = 1; + let m = 2; + let x = 'block: { + if n == 0 { + break 'block 1; //~ ERROR [E0268] + } else { + break 'block 2; + } + }; + + let y = 'block: { + if n == 0 { + break 'block 1; //~ ERROR [E0268] + } + break 'block 0; + }; + + let z = 'block: { + if n == 0 { + if m > 1 { + break 'block 3; //~ ERROR [E0268] + } + } + break 'block 1; + }; +} diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.rs b/tests/ui/loops/loop-if-else-break-issue-123261.rs new file mode 100644 index 0000000000000..a1f9dabe891fd --- /dev/null +++ b/tests/ui/loops/loop-if-else-break-issue-123261.rs @@ -0,0 +1,31 @@ +//@ run-rustfix + +#![allow(unused)] + +fn main() { + let n = 1; + let m = 2; + let x = { + if n == 0 { + break 1; //~ ERROR [E0268] + } else { + break 2; + } + }; + + let y = { + if n == 0 { + break 1; //~ ERROR [E0268] + } + break 0; + }; + + let z = { + if n == 0 { + if m > 1 { + break 3; //~ ERROR [E0268] + } + } + break 1; + }; +} diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.stderr b/tests/ui/loops/loop-if-else-break-issue-123261.stderr new file mode 100644 index 0000000000000..7bd192fc00b0d --- /dev/null +++ b/tests/ui/loops/loop-if-else-break-issue-123261.stderr @@ -0,0 +1,59 @@ +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/loop-if-else-break-issue-123261.rs:10:13 + | +LL | break 1; + | ^^^^^^^ cannot `break` outside of a loop or labeled block +LL | } else { +LL | break 2; + | ^^^^^^^ cannot `break` outside of a loop or labeled block + | +help: consider labeling this block to be able to break within it + | +LL ~ let x = 'block: { +LL | if n == 0 { +LL ~ break 'block 1; +LL | } else { +LL ~ break 'block 2; + | + +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/loop-if-else-break-issue-123261.rs:18:13 + | +LL | break 1; + | ^^^^^^^ cannot `break` outside of a loop or labeled block +LL | } +LL | break 0; + | ^^^^^^^ cannot `break` outside of a loop or labeled block + | +help: consider labeling this block to be able to break within it + | +LL ~ let y = 'block: { +LL | if n == 0 { +LL ~ break 'block 1; +LL | } +LL ~ break 'block 0; + | + +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/loop-if-else-break-issue-123261.rs:26:17 + | +LL | break 3; + | ^^^^^^^ cannot `break` outside of a loop or labeled block +... +LL | break 1; + | ^^^^^^^ cannot `break` outside of a loop or labeled block + | +help: consider labeling this block to be able to break within it + | +LL ~ let z = 'block: { +LL | if n == 0 { +LL | if m > 1 { +LL ~ break 'block 3; +LL | } +LL | } +LL ~ break 'block 1; + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0268`. diff --git a/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr b/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr index 9407e8ac0029d..2f46cb36750be 100644 --- a/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr +++ b/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr @@ -21,16 +21,21 @@ LL | foo!(()); = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0268]: `break` outside of a loop or labeled block - --> $DIR/break-in-unlabeled-block-in-macro.rs:27:19 - | -LL | foo!(stmt break ()); - | ^^^^^^^^ cannot `break` outside of a loop or labeled block + --> $DIR/break-in-unlabeled-block-in-macro.rs:33:17 | -help: consider labeling this block to be able to break within it +LL | foo!(=> break ()); + | ^^^^^^^^ cannot `break` outside of a loop or labeled block + +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/break-in-unlabeled-block-in-macro.rs:38:17 | -LL ~ 'block: { -LL ~ foo!(stmt break 'block ()); +LL | break () + | ^^^^^^^^ cannot `break` outside of a loop or labeled block +... +LL | bar!() + | ------ in this macro invocation | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0268]: `break` outside of a loop or labeled block --> $DIR/break-in-unlabeled-block-in-macro.rs:12:11 @@ -48,21 +53,16 @@ LL | 'block: { break 'block $e; } | +++++++ ++++++ error[E0268]: `break` outside of a loop or labeled block - --> $DIR/break-in-unlabeled-block-in-macro.rs:33:17 + --> $DIR/break-in-unlabeled-block-in-macro.rs:27:19 | -LL | foo!(=> break ()); - | ^^^^^^^^ cannot `break` outside of a loop or labeled block - -error[E0268]: `break` outside of a loop or labeled block - --> $DIR/break-in-unlabeled-block-in-macro.rs:38:17 +LL | foo!(stmt break ()); + | ^^^^^^^^ cannot `break` outside of a loop or labeled block | -LL | break () - | ^^^^^^^^ cannot `break` outside of a loop or labeled block -... -LL | bar!() - | ------ in this macro invocation +help: consider labeling this block to be able to break within it + | +LL ~ 'block: { +LL ~ foo!(stmt break 'block ()); | - = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors From 5da41f59dafc7356dedba9c1e0fbd1cc1aa55491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 22 May 2024 16:37:12 +0000 Subject: [PATCH 12/21] self-contained linker: retry without -fuse-ld=lld on older GCCs --- compiler/rustc_codegen_ssa/src/back/link.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 37b8f81ad9465..c973bda1ef14f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -799,6 +799,27 @@ fn link_natively( continue; } + // Check if linking failed with an error message that indicates the driver didn't recognize + // the `-fuse-ld=lld` option. If so, re-perform the link step without it. This avoids having + // to spawn multiple instances on the happy path to do version checking, and ensures things + // keep working on the tier 1 baseline of GLIBC 2.17+. That is generally understood as GCCs + // circa RHEL/CentOS 7, 4.5 or so, whereas lld support was added in GCC 9. + if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, Lld::Yes)) + && unknown_arg_regex.is_match(&out) + && out.contains("-fuse-ld=lld") + && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld") + { + info!("linker output: {:?}", out); + warn!("The linker driver does not support `-fuse-ld=lld`. Retrying without it."); + for arg in cmd.take_args() { + if arg.to_string_lossy() != "-fuse-ld=lld" { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + // Detect '-static-pie' used with an older version of gcc or clang not supporting it. // Fallback from '-static-pie' to '-static' in that case. if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) From c8844dfdc00fbfe4b5e7839635214b744410312f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 May 2024 08:45:54 +1000 Subject: [PATCH 13/21] Clarify the meaning of the span within `mbe::TokenTree::MetaVar`. --- compiler/rustc_expand/src/mbe.rs | 3 ++- compiler/rustc_expand/src/mbe/quoted.rs | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index a805c4fcf7b97..32d12c4fd0cc9 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -73,7 +73,8 @@ enum TokenTree { Delimited(DelimSpan, DelimSpacing, Delimited), /// A kleene-style repetition sequence, e.g. `$($e:expr)*` (RHS) or `$($e),*` (LHS). Sequence(DelimSpan, SequenceRepetition), - /// e.g., `$var`. + /// e.g., `$var`. The span covers the leading dollar and the ident. (The span within the ident + /// only covers the ident, e.g. `var`.) MetaVar(Span, Ident), /// e.g., `$var:expr`. Only appears on the LHS. MetaVarDecl(Span, Ident /* name to bind */, Option), diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index d3ea48e2e2a8e..2e37fd70df9a7 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -176,7 +176,7 @@ fn parse_tree<'a>( // Depending on what `tree` is, we could be parsing different parts of a macro match tree { // `tree` is a `$` token. Look at the next token in `trees` - &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => { + &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span: dollar_span }, _) => { // FIXME: Handle `Invisible`-delimited groups in a more systematic way // during parsing. let mut next = outer_trees.next(); @@ -209,7 +209,7 @@ fn parse_tree<'a>( err.emit(); // Returns early the same read `$` to avoid spanning // unrelated diagnostics that could be performed afterwards - return TokenTree::token(token::Dollar, span); + return TokenTree::token(token::Dollar, dollar_span); } Ok(elem) => { maybe_emit_macro_metavar_expr_feature( @@ -251,7 +251,7 @@ fn parse_tree<'a>( // special metavariable that names the crate of the invocation. Some(tokenstream::TokenTree::Token(token, _)) if token.is_ident() => { let (ident, is_raw) = token.ident().unwrap(); - let span = ident.span.with_lo(span.lo()); + let span = ident.span.with_lo(dollar_span.lo()); if ident.name == kw::Crate && matches!(is_raw, IdentIsRaw::No) { TokenTree::token(token::Ident(kw::DollarCrate, is_raw), span) } else { @@ -260,16 +260,19 @@ fn parse_tree<'a>( } // `tree` is followed by another `$`. This is an escaped `$`. - Some(&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => { + Some(&tokenstream::TokenTree::Token( + Token { kind: token::Dollar, span: dollar_span2 }, + _, + )) => { if parsing_patterns { span_dollar_dollar_or_metavar_in_the_lhs_err( sess, - &Token { kind: token::Dollar, span }, + &Token { kind: token::Dollar, span: dollar_span2 }, ); } else { - maybe_emit_macro_metavar_expr_feature(features, sess, span); + maybe_emit_macro_metavar_expr_feature(features, sess, dollar_span2); } - TokenTree::token(token::Dollar, span) + TokenTree::token(token::Dollar, dollar_span2) } // `tree` is followed by some other token. This is an error. @@ -281,7 +284,7 @@ fn parse_tree<'a>( } // There are no more tokens. Just return the `$` we already have. - None => TokenTree::token(token::Dollar, span), + None => TokenTree::token(token::Dollar, dollar_span), } } From 3fc8f8998c45fbc211b98b5bae04cc87f8eb1619 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 May 2024 09:20:49 +1000 Subject: [PATCH 14/21] Clarify `parse` a little. - Name the colon span as `colon_span` to distinguish it from the other `span` local variable. - Just use basic pattern matching, which is easier to read than `map_or`. --- compiler/rustc_expand/src/mbe/quoted.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 2e37fd70df9a7..8ad7cb15c92a9 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -62,7 +62,10 @@ pub(super) fn parse( match tree { TokenTree::MetaVar(start_sp, ident) if parsing_patterns => { let span = match trees.next() { - Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { + Some(&tokenstream::TokenTree::Token( + Token { kind: token::Colon, span: colon_span }, + _, + )) => { match trees.next() { Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { Some((fragment, _)) => { @@ -126,10 +129,12 @@ pub(super) fn parse( } _ => token.span, }, - tree => tree.map_or(span, tokenstream::TokenTree::span), + Some(tree) => tree.span(), + None => colon_span, } } - tree => tree.map_or(start_sp, tokenstream::TokenTree::span), + Some(tree) => tree.span(), + None => start_sp, }; result.push(TokenTree::MetaVarDecl(span, ident, None)); From b6de7821982caf99f58e2925ae3cb78673b31e24 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 17 May 2024 09:54:53 +1000 Subject: [PATCH 15/21] Clarify a comment. --- compiler/rustc_expand/src/proc_macro_server.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1f3547c841a90..ec7e4416b9130 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -309,10 +309,10 @@ impl ToInternal> use rustc_ast::token::*; // The code below is conservative, using `token_alone`/`Spacing::Alone` - // in most places. When the resulting code is pretty-printed by - // `print_tts` it ends up with spaces between most tokens, which is - // safe but ugly. It's hard in general to do better when working at the - // token level. + // in most places. It's hard in general to do better when working at + // the token level. When the resulting code is pretty-printed by + // `print_tts` the `space_between` function helps avoid a lot of + // unnecessary whitespace, so the results aren't too bad. let (tree, rustc) = self; match tree { TokenTree::Punct(Punct { ch, joint, span }) => { From c679a5510222cb861a2a23088d1365707d1e0d6f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 17 May 2024 09:49:09 +1000 Subject: [PATCH 16/21] Convert some `token_joint_hidden` calls to `token_joint`. This has no noticeable effect, but it makes these cases follow the guidelines in the comments on `Spacing`, which say that `Joint` should be used "for each token that (a) should be pretty-printed without a space after it, and (b) is followed by a punctuation token". These two tokens are both followed by a comma, which is a punctuation token. --- compiler/rustc_builtin_macros/src/assert/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 085ea3458bbfa..7814422611439 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -153,7 +153,7 @@ impl<'cx, 'a> Context<'cx, 'a> { fn build_panic(&self, expr_str: &str, panic_path: Path) -> P { let escaped_expr_str = escape_to_fmt(expr_str); let initial = [ - TokenTree::token_joint_hidden( + TokenTree::token_joint( token::Literal(token::Lit { kind: token::LitKind::Str, symbol: Symbol::intern(&if self.fmt_string.is_empty() { @@ -172,7 +172,7 @@ impl<'cx, 'a> Context<'cx, 'a> { ]; let captures = self.capture_decls.iter().flat_map(|cap| { [ - TokenTree::token_joint_hidden( + TokenTree::token_joint( token::Ident(cap.ident.name, IdentIsRaw::No), cap.ident.span, ), From a1b6d46e040176e63954ba3ba5bb18cd4ed3691a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 17 May 2024 08:23:24 +1000 Subject: [PATCH 17/21] Use `JointHidden` in a couple of suitable places. This has no notable effect, but it's appropriate because the relevant tokens are followed by delimiters. --- compiler/rustc_ast/src/tokenstream.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index fb666550e9302..3d46415507def 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -661,11 +661,11 @@ impl TokenStream { if attr_style == AttrStyle::Inner { vec![ TokenTree::token_joint(token::Pound, span), - TokenTree::token_alone(token::Not, span), + TokenTree::token_joint_hidden(token::Not, span), body, ] } else { - vec![TokenTree::token_alone(token::Pound, span), body] + vec![TokenTree::token_joint_hidden(token::Pound, span), body] } } } From 4d513cb4bf7d8c3151594096e97cd848f5fcab77 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 May 2024 12:54:38 +1000 Subject: [PATCH 18/21] Add some comments. --- compiler/rustc_ast_pretty/src/pprust/state.rs | 40 ++++++++++++++----- compiler/rustc_expand/src/mbe.rs | 2 + compiler/rustc_expand/src/mbe/transcribe.rs | 15 +++++++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 545b98a9135af..f02fe4cf0a728 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -681,22 +681,40 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } } + // The easiest way to implement token stream pretty printing would be to + // print each token followed by a single space. But that would produce ugly + // output, so we go to some effort to do better. + // + // First, we track whether each token that appears in source code is + // followed by a space, with `Spacing`, and reproduce that in the output. + // This works well in a lot of cases. E.g. `stringify!(x + y)` produces + // "x + y" and `stringify!(x+y)` produces "x+y". + // + // But this doesn't work for code produced by proc macros (which have no + // original source text representation) nor for code produced by decl + // macros (which are tricky because the whitespace after tokens appearing + // in macro rules isn't always what you want in the produced output). For + // these we mostly use `Spacing::Alone`, which is the conservative choice. + // + // So we have a backup mechanism for when `Spacing::Alone` occurs between a + // pair of tokens: we check if that pair of tokens can obviously go + // together without a space between them. E.g. token `x` followed by token + // `,` is better printed as `x,` than `x ,`. (Even if the original source + // code was `x ,`.) + // + // Finally, we must be careful about changing the output. Token pretty + // printing is used by `stringify!` and `impl Display for + // proc_macro::TokenStream`, and some programs rely on the output having a + // particular form, even though they shouldn't. In particular, some proc + // macros do `format!({stream})` on a token stream and then "parse" the + // output with simple string matching that can't handle whitespace changes. + // E.g. we have seen cases where a proc macro can handle `a :: b` but not + // `a::b`. See #117433 for some examples. fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) { let mut iter = tts.trees().peekable(); while let Some(tt) = iter.next() { let spacing = self.print_tt(tt, convert_dollar_crate); if let Some(next) = iter.peek() { - // Should we print a space after `tt`? There are two guiding - // factors. - // - `spacing` is the more important and accurate one. Most - // tokens have good spacing information, and - // `Joint`/`JointHidden` get used a lot. - // - `space_between` is the backup. Code produced by proc - // macros has worse spacing information, with no - // `JointHidden` usage and too much `Alone` usage, which - // would result in over-spaced output such as - // `( x () , y . z )`. `space_between` avoids some of the - // excess whitespace. if spacing == Spacing::Alone && space_between(tt, next) { self.space(); } diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index 32d12c4fd0cc9..08d4a03945489 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -68,6 +68,8 @@ pub(crate) enum KleeneOp { /// `MetaVarExpr` are "first-class" token trees. Useful for parsing macros. #[derive(Debug, PartialEq, Encodable, Decodable)] enum TokenTree { + /// A token. Unlike `tokenstream::TokenTree::Token` this lacks a `Spacing`. + /// See the comments about `Spacing` in the `transcribe` function. Token(Token), /// A delimited sequence, e.g. `($e:expr)` (RHS) or `{ $e }` (LHS). Delimited(DelimSpan, DelimSpacing, Delimited), diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 3901b82eb52ec..8a084dcb4fe3a 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -253,8 +253,23 @@ pub(super) fn transcribe<'a>( mbe::TokenTree::MetaVar(mut sp, mut original_ident) => { // Find the matched nonterminal from the macro invocation, and use it to replace // the meta-var. + // + // We use `Spacing::Alone` everywhere here, because that's the conservative choice + // and spacing of declarative macros is tricky. E.g. in this macro: + // ``` + // macro_rules! idents { + // ($($a:ident,)*) => { stringify!($($a)*) } + // } + // ``` + // `$a` has no whitespace after it and will be marked `JointHidden`. If you then + // call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So + // if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up + // producing "xyz", which is bad because it effectively merges tokens. + // `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid + // some of the unnecessary whitespace. let ident = MacroRulesNormalizedIdent::new(original_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { + // njn: explain the use of alone here let tt = match cur_matched { MatchedSingle(ParseNtResult::Tt(tt)) => { // `tt`s are emitted into the output stream directly as "raw tokens", From 07b7cd62c70ef6ec111e9a7182cb83ec0209460e Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 17 Mar 2024 15:46:40 -0700 Subject: [PATCH 19/21] Add some tests for public-private dependencies. --- .../auxiliary/diamond_priv_dep.rs | 9 +++ .../pub-priv-dep/auxiliary/diamond_pub_dep.rs | 9 +++ .../pub-priv-dep/auxiliary/indirect1.rs | 4 ++ .../pub-priv-dep/auxiliary/indirect2.rs | 4 ++ tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs | 22 ++++++ .../pub-priv-dep/auxiliary/priv_dep.rs | 9 +++ .../pub-priv-dep/auxiliary/reexport.rs | 5 ++ .../privacy/pub-priv-dep/auxiliary/shared.rs | 1 + tests/ui/privacy/pub-priv-dep/diamond_deps.rs | 48 +++++++++++++ .../privacy/pub-priv-dep/diamond_deps.stderr | 14 ++++ tests/ui/privacy/pub-priv-dep/pub-priv1.rs | 67 ++++++++++++++++++- .../ui/privacy/pub-priv-dep/pub-priv1.stderr | 62 +++++++++++++++-- .../pub-priv-dep/reexport_from_priv.rs | 15 +++++ .../pub-priv-dep/shared_both_private.rs | 32 +++++++++ .../pub-priv-dep/shared_direct_private.rs | 39 +++++++++++ .../privacy/pub-priv-dep/shared_indirect.rs | 29 ++++++++ 16 files changed, 360 insertions(+), 9 deletions(-) create mode 100644 tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs create mode 100644 tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs create mode 100644 tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs create mode 100644 tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs create mode 100644 tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs create mode 100644 tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs create mode 100644 tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs create mode 100644 tests/ui/privacy/pub-priv-dep/diamond_deps.rs create mode 100644 tests/ui/privacy/pub-priv-dep/diamond_deps.stderr create mode 100644 tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs create mode 100644 tests/ui/privacy/pub-priv-dep/shared_both_private.rs create mode 100644 tests/ui/privacy/pub-priv-dep/shared_direct_private.rs create mode 100644 tests/ui/privacy/pub-priv-dep/shared_indirect.rs diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs new file mode 100644 index 0000000000000..ed76815a6ac8e --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs @@ -0,0 +1,9 @@ +//@ aux-crate:shared=shared.rs + +extern crate shared; + +pub use shared::Shared; + +pub struct SharedInType { + pub f: Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs new file mode 100644 index 0000000000000..ed76815a6ac8e --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs @@ -0,0 +1,9 @@ +//@ aux-crate:shared=shared.rs + +extern crate shared; + +pub use shared::Shared; + +pub struct SharedInType { + pub f: Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs new file mode 100644 index 0000000000000..0e4a73c7fc011 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs @@ -0,0 +1,4 @@ +//@ aux-crate:priv:indirect2=indirect2.rs +//@ compile-flags: -Zunstable-options + +extern crate indirect2; diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs new file mode 100644 index 0000000000000..5f6b289eb1414 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs @@ -0,0 +1,4 @@ +//@ aux-crate:shared=shared.rs + +// This is public. +extern crate shared; diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs new file mode 100644 index 0000000000000..9e2aa898afe8d --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs @@ -0,0 +1,22 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub fn fn_like(input: TokenStream) -> TokenStream { + "".parse().unwrap() +} + +#[proc_macro_derive(PmDerive)] +pub fn pm_derive(item: TokenStream) -> TokenStream { + "".parse().unwrap() +} + +#[proc_macro_attribute] +pub fn pm_attr(attr: TokenStream, item: TokenStream) -> TokenStream { + "".parse().unwrap() +} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs index e7afeb84fb4f4..4eeecdc056972 100644 --- a/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs @@ -1,2 +1,11 @@ pub struct OtherType; pub trait OtherTrait {} + +#[macro_export] +macro_rules! m { + () => {}; +} + +pub enum E { + V1 +} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs new file mode 100644 index 0000000000000..0655e3ae2cfdb --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs @@ -0,0 +1,5 @@ +//@ aux-crate:shared=shared.rs + +extern crate shared; + +pub use shared::Shared; diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs new file mode 100644 index 0000000000000..efc4daa7befb8 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs @@ -0,0 +1 @@ +pub struct Shared; diff --git a/tests/ui/privacy/pub-priv-dep/diamond_deps.rs b/tests/ui/privacy/pub-priv-dep/diamond_deps.rs new file mode 100644 index 0000000000000..0e1f6f36bc8cc --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/diamond_deps.rs @@ -0,0 +1,48 @@ +//@ aux-crate:priv:diamond_priv_dep=diamond_priv_dep.rs +//@ aux-crate:diamond_pub_dep=diamond_pub_dep.rs +//@ compile-flags: -Zunstable-options + +// A diamond dependency: +// +// diamond_reepxort +// /\ +// (public) / \ (PRIVATE) +// / \ +// diamond_pub_dep diamond_priv_dep +// \ / +// (public) \ / (public) +// \/ +// shared +// +// Where the pub and private crates reexport something from the shared crate. +// +// Checks the behavior when the same shared item appears in the public API, +// depending on whether it comes from the public side or the private side. +// +// NOTE: compiletest does not support deduplicating shared dependencies. +// However, it should work well enough for this test, the only downside is +// that diamond_shared gets built twice. + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate diamond_priv_dep; +extern crate diamond_pub_dep; + +// FIXME: This should trigger. +pub fn leaks_priv() -> diamond_priv_dep::Shared { + diamond_priv_dep::Shared +} + +pub fn leaks_pub() -> diamond_pub_dep::Shared { + diamond_pub_dep::Shared +} + +pub struct PrivInStruct { + pub f: diamond_priv_dep::SharedInType +//~^ ERROR type `diamond_priv_dep::SharedInType` from private dependency 'diamond_priv_dep' in public interface +} + +pub struct PubInStruct { + pub f: diamond_pub_dep::SharedInType +} diff --git a/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr b/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr new file mode 100644 index 0000000000000..8a6d35a747b63 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr @@ -0,0 +1,14 @@ +error: type `diamond_priv_dep::SharedInType` from private dependency 'diamond_priv_dep' in public interface + --> $DIR/diamond_deps.rs:42:5 + | +LL | pub f: diamond_priv_dep::SharedInType + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/diamond_deps.rs:27:9 + | +LL | #![deny(exported_private_dependencies)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs index f26dbb47ba5e9..112eaf528be27 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs @@ -1,12 +1,20 @@ //@ aux-crate:priv:priv_dep=priv_dep.rs //@ aux-build:pub_dep.rs +//@ aux-crate:priv:pm=pm.rs //@ compile-flags: -Zunstable-options + +// Basic behavior check of exported_private_dependencies from either a public +// dependency or a private one. + #![deny(exported_private_dependencies)] // This crate is a private dependency -extern crate priv_dep; +// FIXME: This should trigger. +pub extern crate priv_dep; // This crate is a public dependency extern crate pub_dep; +// This crate is a private dependency +extern crate pm; use priv_dep::{OtherTrait, OtherType}; use pub_dep::PubType; @@ -25,7 +33,10 @@ pub struct PublicType { } impl PublicType { - pub fn pub_fn(param: OtherType) {} + pub fn pub_fn_param(param: OtherType) {} + //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + + pub fn pub_fn_return() -> OtherType { OtherType } //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface fn priv_fn(param: OtherType) {} @@ -36,9 +47,61 @@ pub trait MyPubTrait { } //~^^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface +pub trait WithSuperTrait: OtherTrait {} +//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface + +pub trait PubLocalTraitWithAssoc { + type X; +} + +pub struct PrivateAssoc; +impl PubLocalTraitWithAssoc for PrivateAssoc { + type X = OtherType; +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface +} + +pub fn in_bounds(x: T) { unimplemented!() } +//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface + +pub fn private_in_generic() -> std::num::Saturating { unimplemented!() } +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + +pub static STATIC: OtherType = OtherType; +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + +pub const CONST: OtherType = OtherType; +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + +pub type Alias = OtherType; +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + +pub struct PublicWithPrivateImpl; + +// FIXME: This should trigger. +// See https://github.com/rust-lang/rust/issues/71043 +impl OtherTrait for PublicWithPrivateImpl {} + +pub trait PubTraitOnPrivate {} + +// FIXME: This should trigger. +// See https://github.com/rust-lang/rust/issues/71043 +impl PubTraitOnPrivate for OtherType {} + pub struct AllowedPrivType { #[allow(exported_private_dependencies)] pub allowed: OtherType, } +// FIXME: This should trigger. +pub use priv_dep::m; +// FIXME: This should trigger. +pub use pm::fn_like; +// FIXME: This should trigger. +pub use pm::PmDerive; +// FIXME: This should trigger. +pub use pm::pm_attr; + +// FIXME: This should trigger. +pub use priv_dep::E::V1; + fn main() {} diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr index e62a440d8f568..53d461a5774a0 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr @@ -1,26 +1,74 @@ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:21:5 + --> $DIR/pub-priv1.rs:29:5 | LL | pub field: OtherType, | ^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/pub-priv1.rs:4:9 + --> $DIR/pub-priv1.rs:9:9 | LL | #![deny(exported_private_dependencies)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:28:5 + --> $DIR/pub-priv1.rs:36:5 | -LL | pub fn pub_fn(param: OtherType) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub fn pub_fn_param(param: OtherType) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:39:5 + | +LL | pub fn pub_fn_return() -> OtherType { OtherType } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: trait `OtherTrait` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:35:5 + --> $DIR/pub-priv1.rs:46:5 | LL | type Foo: OtherTrait; | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:50:1 + | +LL | pub trait WithSuperTrait: OtherTrait {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:59:5 + | +LL | type X = OtherType; + | ^^^^^^ + +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:63:1 + | +LL | pub fn in_bounds(x: T) { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:66:1 + | +LL | pub fn private_in_generic() -> std::num::Saturating { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:69:1 + | +LL | pub static STATIC: OtherType = OtherType; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:72:1 + | +LL | pub const CONST: OtherType = OtherType; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:75:1 + | +LL | pub type Alias = OtherType; + | ^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs b/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs new file mode 100644 index 0000000000000..3c6e9825e7288 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs @@ -0,0 +1,15 @@ +//@ aux-crate:priv:reexport=reexport.rs +//@ compile-flags: -Zunstable-options +//@ check-pass + +// Checks the behavior of a reexported item from a private dependency. + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate reexport; + +// FIXME: This should trigger. +pub fn leaks_priv() -> reexport::Shared { + reexport::Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/shared_both_private.rs b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs new file mode 100644 index 0000000000000..20a4b85c01e8d --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs @@ -0,0 +1,32 @@ +//@ aux-crate:priv:shared=shared.rs +//@ aux-crate:reexport=reexport.rs +//@ compile-flags: -Zunstable-options +//@ check-pass + +// A shared dependency, where a private dependency reexports a public dependency. +// +// shared_both_private +// /\ +// (PRIVATE) / | (PRIVATE) +// / | +// reexport | +// \ | +// (public) \ / +// \/ +// shared + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate shared; +extern crate reexport; + +// FIXME: This should trigger. +pub fn leaks_priv() -> shared::Shared { + shared::Shared +} + +// FIXME: This should trigger. +pub fn leaks_priv_reexport() -> reexport::Shared { + reexport::Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs b/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs new file mode 100644 index 0000000000000..b329a7acb5834 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs @@ -0,0 +1,39 @@ +//@ aux-crate:priv:shared=shared.rs +//@ aux-crate:reexport=reexport.rs +//@ compile-flags: -Zunstable-options +//@ check-pass + +// A shared dependency, where the public side reexports the same item as a +// direct private dependency. +// +// shared_direct_private +// /\ +// (public) / | (PRIVATE) +// / | +// reexport | +// \ | +// (public) \ / +// \/ +// shared +// + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate shared; +extern crate reexport; + +// FIXME: Should this trigger? +// +// One could make an argument that I said I want "reexport" to be public, and +// since "reexport" says "shared_direct_private" is public, then it should +// transitively be public for me. However, as written, this is explicitly +// referring to a dependency that is marked "private", which I think is +// confusing. +pub fn leaks_priv() -> shared::Shared { + shared::Shared +} + +pub fn leaks_pub() -> reexport::Shared { + reexport::Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/shared_indirect.rs b/tests/ui/privacy/pub-priv-dep/shared_indirect.rs new file mode 100644 index 0000000000000..34b624b4a1a40 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/shared_indirect.rs @@ -0,0 +1,29 @@ +//@ aux-crate:priv:shared=shared.rs +//@ aux-crate:priv:indirect1=indirect1.rs +//@ compile-flags: -Zunstable-options +//@ check-pass + +// A shared dependency, where it is only indirectly public. +// +// shared_indirect +// /\ +// (PRIVATE) / | (PRIVATE) +// / | +// indirect1 | | +// (PRIVATE) | | +// indirect2 | | +// \ | +// (public) \ / +// \/ +// shared + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate shared; +extern crate indirect1; + +// FIXME: This should trigger. +pub fn leaks_priv() -> shared::Shared { + shared::Shared +} From 5f4424bfaf6fc9c66df6e19a3f5e96b0fc9a1441 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 May 2024 08:59:02 +1000 Subject: [PATCH 20/21] Handle `ReVar` in `note_and_explain_region`. PR #124918 made this path abort. The added test, from fuzzing, identified that it is reachable. --- .../rustc_infer/src/infer/error_reporting/mod.rs | 5 ++++- tests/ui/inference/note-and-explain-ReVar-124973.rs | 8 ++++++++ .../inference/note-and-explain-ReVar-124973.stderr | 13 +++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/ui/inference/note-and-explain-ReVar-124973.rs create mode 100644 tests/ui/inference/note-and-explain-ReVar-124973.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 46a7e7b239965..e0894ed31bfc3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -173,7 +173,10 @@ pub(super) fn note_and_explain_region<'tcx>( ty::ReError(_) => return, - ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => { + // FIXME(#125431): `ReVar` shouldn't reach here. + ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span), + + ty::ReBound(..) | ty::ReErased => { bug!("unexpected region for note_and_explain_region: {:?}", region); } }; diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.rs b/tests/ui/inference/note-and-explain-ReVar-124973.rs new file mode 100644 index 0000000000000..f1e2464563699 --- /dev/null +++ b/tests/ui/inference/note-and-explain-ReVar-124973.rs @@ -0,0 +1,8 @@ +//@ edition:2018 + +#![feature(c_variadic)] + +async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} +//~^ ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds + +fn main() {} diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.stderr b/tests/ui/inference/note-and-explain-ReVar-124973.stderr new file mode 100644 index 0000000000000..574f6508e4c78 --- /dev/null +++ b/tests/ui/inference/note-and-explain-ReVar-124973.stderr @@ -0,0 +1,13 @@ +error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds + --> $DIR/note-and-explain-ReVar-124973.rs:5:73 + | +LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} + | ----------------------------------------------------------------------- ^^ + | | + | opaque type defined here + | + = note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. From ddb81ce68047d6383a789c8da514e443faea8349 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Fri, 17 May 2024 15:05:36 -0400 Subject: [PATCH 21/21] rewrite and rename issue-46239 --- src/tools/run-make-support/src/rustc.rs | 6 ++++++ .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/codegen/noalias-freeze.rs | 21 +++++++++++++++++++ tests/run-make/issue-46239/Makefile | 6 ------ tests/run-make/issue-46239/main.rs | 8 ------- 5 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 tests/codegen/noalias-freeze.rs delete mode 100644 tests/run-make/issue-46239/Makefile delete mode 100644 tests/run-make/issue-46239/main.rs diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index d034826a830b4..f581204d5f1cb 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -64,6 +64,12 @@ impl Rustc { self } + /// Specify a specific optimization level. + pub fn opt_level(&mut self, option: &str) -> &mut Self { + self.cmd.arg(format!("-Copt-level={option}")); + self + } + /// Specify type(s) of output files to generate. pub fn emit(&mut self, kinds: &str) -> &mut Self { self.cmd.arg(format!("--emit={kinds}")); diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 1a3d6f8d81360..379d07ed22ded 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -112,7 +112,6 @@ run-make/issue-37839/Makefile run-make/issue-37893/Makefile run-make/issue-38237/Makefile run-make/issue-40535/Makefile -run-make/issue-46239/Makefile run-make/issue-47384/Makefile run-make/issue-47551/Makefile run-make/issue-51671/Makefile diff --git a/tests/codegen/noalias-freeze.rs b/tests/codegen/noalias-freeze.rs new file mode 100644 index 0000000000000..8086f3afbbc87 --- /dev/null +++ b/tests/codegen/noalias-freeze.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Copt-level=1 + +// References returned by a Frozen pointer type +// could be marked as "noalias", which caused miscompilation errors. +// This test runs the most minimal possible code that can reproduce this bug, +// and checks that noalias does not appear. +// See https://github.com/rust-lang/rust/issues/46239 + +#![crate_type = "lib"] + +fn project(x: &(T,)) -> &T { &x.0 } + +fn dummy() {} + +// CHECK-LABEL: @foo( +// CHECK-NOT: noalias +#[no_mangle] +pub fn foo() { + let f = (dummy as fn(),); + (*project(&f))(); +} diff --git a/tests/run-make/issue-46239/Makefile b/tests/run-make/issue-46239/Makefile deleted file mode 100644 index 0006ced25156a..0000000000000 --- a/tests/run-make/issue-46239/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) main.rs -C opt-level=1 - $(call RUN,main) diff --git a/tests/run-make/issue-46239/main.rs b/tests/run-make/issue-46239/main.rs deleted file mode 100644 index b7df5cf4d8128..0000000000000 --- a/tests/run-make/issue-46239/main.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn project(x: &(T,)) -> &T { &x.0 } - -fn dummy() {} - -fn main() { - let f = (dummy as fn(),); - (*project(&f))(); -}