From 4f50efd1329f430998231f018c2cf61bfe0db449 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sat, 5 Oct 2024 22:48:55 +0300 Subject: [PATCH 01/10] update "build/host" symlink comment Signed-off-by: onur-ozkan --- src/bootstrap/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 75659f46431af..bcd6b07f2a25e 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -470,7 +470,7 @@ impl Build { crate::core::metadata::build(&mut build); } - // Make a symbolic link so we can use a consistent directory in the documentation. + // Create symbolic link to use host sysroot from a consistent path (e.g., in the rust-analyzer config file). let build_triple = build.out.join(build.build); t!(fs::create_dir_all(&build_triple)); let host = build.out.join("host"); From 6371ef6e964a72ca3b4c7557312d8808e98f4ff3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 07:02:26 -0400 Subject: [PATCH 02/10] Evaluating place expr that is never read from does not diverge --- compiler/rustc_hir_typeck/src/expr.rs | 67 ++++++++++++++++++- compiler/rustc_hir_typeck/src/pat.rs | 7 ++ ...ocess_never.SimplifyLocals-final.after.mir | 5 +- tests/mir-opt/uninhabited_enum.rs | 2 - tests/ui/never_type/diverging-place-match.rs | 13 ++++ .../never_type/diverging-place-match.stderr | 20 ++++++ .../raw-ref-op/never-place-isnt-diverging.rs | 13 ++++ .../never-place-isnt-diverging.stderr | 20 ++++++ tests/ui/reachable/expr_assign.stderr | 9 +-- .../reachable/unwarned-match-on-never.stderr | 2 +- 10 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 tests/ui/never_type/diverging-place-match.rs create mode 100644 tests/ui/never_type/diverging-place-match.stderr create mode 100644 tests/ui/raw-ref-op/never-place-isnt-diverging.rs create mode 100644 tests/ui/raw-ref-op/never-place-isnt-diverging.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e3c7dded0ca6e..870a260c9fe09 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -238,8 +238,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), } - // Any expression that produces a value of type `!` must have diverged - if ty.is_never() { + // Any expression that produces a value of type `!` must have diverged, + // unless it's a place expression that isn't being read from, in which case + // diverging would be unsound since we may never actually read the `!`. + // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. + if ty.is_never() && self.expr_constitutes_read(expr) { self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); } @@ -257,6 +260,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } + pub(super) fn expr_constitutes_read(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { + // We only care about place exprs. Anything else returns an immediate + // which would constitute a read. We don't care about distinguishing + // "syntactic" place exprs since if the base of a field projection is + // not a place then it would've been UB to read from it anyways since + // that constitutes a read. + if !expr.is_syntactic_place_expr() { + return true; + } + + // If this expression has any adjustments applied after the place expression, + // they may constitute reads. + if !self.typeck_results.borrow().expr_adjustments(expr).is_empty() { + return true; + } + + fn pat_does_read(pat: &hir::Pat<'_>) -> bool { + let mut does_read = false; + pat.walk(|pat| { + if matches!( + pat.kind, + hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) + ) { + true + } else { + does_read = true; + // No need to continue. + false + } + }); + does_read + } + + match self.tcx.parent_hir_node(expr.hir_id) { + // Addr-of, field projections, and LHS of assignment don't constitute reads. + // Assignment does call `drop_in_place`, though, but its safety + // requirements are not the same. + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. }) => false, + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Assign(target, _, _) | hir::ExprKind::Field(target, _), + .. + }) if expr.hir_id == target.hir_id => false, + + // If we have a subpattern that performs a read, we want to consider this + // to diverge for compatibility to support something like `let x: () = *never_ptr;`. + hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Let(hir::LetExpr { init: target, pat, .. }), + .. + }) if expr.hir_id == target.hir_id && !pat_does_read(*pat) => false, + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Match(target, arms, _), .. }) + if expr.hir_id == target.hir_id + && !arms.iter().any(|arm| pat_does_read(arm.pat)) => + { + false + } + _ => true, + } + } + #[instrument(skip(self, expr), level = "debug")] fn check_expr_kind( &self, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 49c5a7d8a652d..824918d1fa222 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -27,6 +27,7 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use super::report_unexpected_variant_res; +use crate::diverges::Diverges; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, LoweredTy, errors}; @@ -276,6 +277,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + // All other patterns constitute a read, which causes us to diverge + // if the type is never. + if ty.is_never() && !matches!(pat.kind, PatKind::Wild | PatKind::Never | PatKind::Or(_)) { + self.diverges.set(self.diverges.get() | Diverges::always(pat.span)); + } + self.write_ty(pat.hir_id, ty); // (note_1): In most of the cases where (note_1) is referenced diff --git a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir index 240f409817d77..64fc81e298946 100644 --- a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir @@ -3,12 +3,11 @@ fn process_never(_1: *const !) -> () { debug input => _1; let mut _0: (); - let _2: &!; scope 1 { - debug _input => _2; + debug _input => _1; } bb0: { - unreachable; + return; } } diff --git a/tests/mir-opt/uninhabited_enum.rs b/tests/mir-opt/uninhabited_enum.rs index 859535852cf0d..9d845e34fbda8 100644 --- a/tests/mir-opt/uninhabited_enum.rs +++ b/tests/mir-opt/uninhabited_enum.rs @@ -13,8 +13,6 @@ pub fn process_never(input: *const !) { #[no_mangle] pub fn process_void(input: *const Void) { let _input = unsafe { &*input }; - // In the future, this should end with `unreachable`, but we currently only do - // unreachability analysis for `!`. } fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.rs b/tests/ui/never_type/diverging-place-match.rs new file mode 100644 index 0000000000000..7bc00773c0ba5 --- /dev/null +++ b/tests/ui/never_type/diverging-place-match.rs @@ -0,0 +1,13 @@ +#![feature(never_type)] + +fn make_up_a_value() -> T { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let _: ! = *x; + // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this + // is unsound since we act as if it diverges but it doesn't. + } +} + +fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.stderr b/tests/ui/never_type/diverging-place-match.stderr new file mode 100644 index 0000000000000..e86c634d59186 --- /dev/null +++ b/tests/ui/never_type/diverging-place-match.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:4:5 + | +LL | fn make_up_a_value() -> T { + | - expected this type parameter +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let _: ! = *x; +LL | | // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this +LL | | // is unsound since we act as if it diverges but it doesn't. +LL | | } + | |_____^ expected type parameter `T`, found `()` + | + = note: expected type parameter `T` + found unit type `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.rs b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs new file mode 100644 index 0000000000000..52f4158dbc988 --- /dev/null +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs @@ -0,0 +1,13 @@ +#![feature(never_type)] + +fn make_up_a_value() -> T { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + &raw const *x; + // Since `*x` is `!`, HIR typeck used to think that it diverges + // and allowed the block to coerce to any value, leading to UB. + } +} + +fn main() {} diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr new file mode 100644 index 0000000000000..9eba57dde8fa7 --- /dev/null +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/never-place-isnt-diverging.rs:4:5 + | +LL | fn make_up_a_value() -> T { + | - expected this type parameter +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | &raw const *x; +LL | | // Since `*x` is `!`, HIR typeck used to think that it diverges +LL | | // and allowed the block to coerce to any value, leading to UB. +LL | | } + | |_____^ expected type parameter `T`, found `()` + | + = note: expected type parameter `T` + found unit type `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/reachable/expr_assign.stderr b/tests/ui/reachable/expr_assign.stderr index c51156b3f40cf..cfbbe04db769d 100644 --- a/tests/ui/reachable/expr_assign.stderr +++ b/tests/ui/reachable/expr_assign.stderr @@ -14,12 +14,13 @@ LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ error: unreachable expression - --> $DIR/expr_assign.rs:20:14 + --> $DIR/expr_assign.rs:20:9 | LL | *p = return; - | -- ^^^^^^ unreachable expression - | | - | any code following this expression is unreachable + | ^^^^^------ + | | | + | | any code following this expression is unreachable + | unreachable expression error: unreachable expression --> $DIR/expr_assign.rs:26:15 diff --git a/tests/ui/reachable/unwarned-match-on-never.stderr b/tests/ui/reachable/unwarned-match-on-never.stderr index a296d2a055e09..c1ad3511b4e08 100644 --- a/tests/ui/reachable/unwarned-match-on-never.stderr +++ b/tests/ui/reachable/unwarned-match-on-never.stderr @@ -2,7 +2,7 @@ error: unreachable expression --> $DIR/unwarned-match-on-never.rs:10:5 | LL | match x {} - | - any code following this expression is unreachable + | ---------- any code following this expression is unreachable LL | // But matches in unreachable code are warned. LL | match x {} | ^^^^^^^^^^ unreachable expression From 5193c211ea594ad9918372868bdb5d4dfafaea87 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 07:21:52 -0400 Subject: [PATCH 03/10] Do not coerce places if they do not constitute reads --- compiler/rustc_hir_typeck/src/coercion.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index fb462eec1b9aa..6a1018883c98a 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -82,6 +82,7 @@ struct Coerce<'a, 'tcx> { /// See #47489 and #48598 /// See docs on the "AllowTwoPhase" type for a more detailed discussion allow_two_phase: AllowTwoPhase, + coerce_never: bool, } impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { @@ -125,8 +126,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fcx: &'f FnCtxt<'f, 'tcx>, cause: ObligationCause<'tcx>, allow_two_phase: AllowTwoPhase, + coerce_never: bool, ) -> Self { - Coerce { fcx, cause, allow_two_phase, use_lub: false } + Coerce { fcx, cause, allow_two_phase, use_lub: false, coerce_never } } fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { @@ -177,7 +179,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Coercing from `!` to any type is allowed: if a.is_never() { - return success(simple(Adjust::NeverToAny)(b), b, vec![]); + if self.coerce_never { + return success(simple(Adjust::NeverToAny)(b), b, vec![]); + } else { + return self.unify_and(a, b, identity); + } } // Coercing *from* an unresolved inference variable means that @@ -1038,7 +1044,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// The expressions *must not* have any preexisting adjustments. pub(crate) fn coerce( &self, - expr: &hir::Expr<'_>, + expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, mut target: Ty<'tcx>, allow_two_phase: AllowTwoPhase, @@ -1055,7 +1061,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable)); - let coerce = Coerce::new(self, cause, allow_two_phase); + let coerce = Coerce::new(self, cause, allow_two_phase, self.expr_constitutes_read(expr)); let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?; let (adjustments, _) = self.register_infer_ok_obligations(ok); @@ -1078,7 +1084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); // We don't ever need two-phase here since we throw out the result of the coercion - let coerce = Coerce::new(self, cause, AllowTwoPhase::No); + let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); self.probe(|_| { let Ok(ok) = coerce.coerce(source, target) else { return false; @@ -1095,7 +1101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option { let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); // We don't ever need two-phase here since we throw out the result of the coercion - let coerce = Coerce::new(self, cause, AllowTwoPhase::No); + let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); coerce .autoderef(DUMMY_SP, expr_ty) .find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps)) @@ -1252,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // probably aren't processing function arguments here and even if we were, // they're going to get autorefed again anyway and we can apply 2-phase borrows // at that time. - let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No); + let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true); coerce.use_lub = true; // First try to coerce the new expression to the type of the previous ones, From 73d49f8c697da34ee1372b51540bfb0f1ae0c712 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 07:49:38 -0400 Subject: [PATCH 04/10] Fix up tests --- compiler/rustc_hir_typeck/src/coercion.rs | 17 ++++- .../src/fn_ctxt/suggestions.rs | 6 +- .../miri/tests/pass/underscore_pattern.rs | 51 ++++++++++++- ...t_read.main.SimplifyLocals-final.after.mir | 49 ++++++++++++ tests/mir-opt/uninhabited_not_read.rs | 27 +++++++ tests/ui/never_type/diverging-place-match.rs | 36 ++++++++- .../never_type/diverging-place-match.stderr | 75 +++++++++++++++++-- 7 files changed, 245 insertions(+), 16 deletions(-) create mode 100644 tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir create mode 100644 tests/mir-opt/uninhabited_not_read.rs diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 6a1018883c98a..1705421c2f40e 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -82,6 +82,10 @@ struct Coerce<'a, 'tcx> { /// See #47489 and #48598 /// See docs on the "AllowTwoPhase" type for a more detailed discussion allow_two_phase: AllowTwoPhase, + /// Whether we allow `NeverToAny` coercions. This is unsound if we're + /// coercing a place expression without it counting as a read in the MIR. + /// This is a side-effect of HIR not really having a great distinction + /// between places and values. coerce_never: bool, } @@ -1083,7 +1087,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("coercion::can_with_predicates({:?} -> {:?})", source, target); let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); - // We don't ever need two-phase here since we throw out the result of the coercion + // We don't ever need two-phase here since we throw out the result of the coercion. + // We also just always set `coerce_never` to true, since this is a heuristic. let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); self.probe(|_| { let Ok(ok) = coerce.coerce(source, target) else { @@ -1096,11 +1101,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Given a type and a target type, this function will calculate and return - /// how many dereference steps needed to achieve `expr_ty <: target`. If + /// how many dereference steps needed to coerce `expr_ty` to `target`. If /// it's not possible, return `None`. - pub(crate) fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option { + pub(crate) fn deref_steps_for_suggestion( + &self, + expr_ty: Ty<'tcx>, + target: Ty<'tcx>, + ) -> Option { let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); - // We don't ever need two-phase here since we throw out the result of the coercion + // We don't ever need two-phase here since we throw out the result of the coercion. let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); coerce .autoderef(DUMMY_SP, expr_ty) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 435b7d0f39a37..1df4d32f3cbac 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2608,7 +2608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind - && let Some(1) = self.deref_steps(expected, checked_ty) + && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty) { // We have `*&T`, check if what was expected was `&T`. // If so, we may want to suggest removing a `*`. @@ -2738,7 +2738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => { - if let Some(steps) = self.deref_steps(ty_a, ty_b) + if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b) // Only suggest valid if dereferencing needed. && steps > 0 // The pointer type implements `Copy` trait so the suggestion is always valid. @@ -2782,7 +2782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } _ if sp == expr.span => { - if let Some(mut steps) = self.deref_steps(checked_ty, expected) { + if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) { let mut expr = expr.peel_blocks(); let mut prefix_span = expr.span.shrink_to_lo(); let mut remove = String::new(); diff --git a/src/tools/miri/tests/pass/underscore_pattern.rs b/src/tools/miri/tests/pass/underscore_pattern.rs index f0afe5589546e..f59bb9f5c82d5 100644 --- a/src/tools/miri/tests/pass/underscore_pattern.rs +++ b/src/tools/miri/tests/pass/underscore_pattern.rs @@ -1,5 +1,7 @@ // Various tests ensuring that underscore patterns really just construct the place, but don't check its contents. #![feature(strict_provenance)] +#![feature(never_type)] + use std::ptr; fn main() { @@ -9,6 +11,7 @@ fn main() { invalid_let(); dangling_let_type_annotation(); invalid_let_type_annotation(); + never(); } fn dangling_match() { @@ -34,6 +37,13 @@ fn invalid_match() { _ => {} } } + + unsafe { + let x: Uninit = Uninit { uninit: () }; + match x.value { + _ => {} + } + } } fn dangling_let() { @@ -41,6 +51,11 @@ fn dangling_let() { let ptr = ptr::without_provenance::(0x40); let _ = *ptr; } + + unsafe { + let ptr = ptr::without_provenance::(0x40); + let _ = *ptr; + } } fn invalid_let() { @@ -49,6 +64,12 @@ fn invalid_let() { let ptr = ptr::addr_of!(val).cast::(); let _ = *ptr; } + + unsafe { + let val = 3u8; + let ptr = ptr::addr_of!(val).cast::(); + let _ = *ptr; + } } // Adding a type annotation used to change how MIR is generated, make sure we cover both cases. @@ -57,6 +78,11 @@ fn dangling_let_type_annotation() { let ptr = ptr::without_provenance::(0x40); let _: bool = *ptr; } + + unsafe { + let ptr = ptr::without_provenance::(0x40); + let _: ! = *ptr; + } } fn invalid_let_type_annotation() { @@ -65,7 +91,28 @@ fn invalid_let_type_annotation() { let ptr = ptr::addr_of!(val).cast::(); let _: bool = *ptr; } + + unsafe { + let val = 3u8; + let ptr = ptr::addr_of!(val).cast::(); + let _: ! = *ptr; + } } -// FIXME: we should also test `!`, not just `bool` -- but that s currently buggy: -// https://github.com/rust-lang/rust/issues/117288 +// Regression test from . +fn never() { + unsafe { + let x = 3u8; + let x: *const ! = &x as *const u8 as *const _; + let _: ! = *x; + } + + // Without a type annotation, make sure we don't implicitly coerce `!` to `()` + // when we do the noop `*x` (as that would require a `!` *value*, creating + // which is UB). + unsafe { + let x = 3u8; + let x: *const ! = &x as *const u8 as *const _; + let _ = *x; + } +} diff --git a/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir new file mode 100644 index 0000000000000..6bf4be652bef3 --- /dev/null +++ b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir @@ -0,0 +1,49 @@ +// MIR for `main` after SimplifyLocals-final + +fn main() -> () { + let mut _0: (); + let _1: u8; + let mut _2: *const !; + let mut _3: *const u8; + let _4: u8; + let mut _5: *const !; + let mut _6: *const u8; + scope 1 { + debug x => _1; + scope 2 { + debug x => _2; + scope 3 { + } + } + } + scope 4 { + debug x => _4; + scope 5 { + debug x => _5; + scope 6 { + } + } + } + + bb0: { + StorageLive(_1); + _1 = const 3_u8; + StorageLive(_2); + StorageLive(_3); + _3 = &raw const _1; + _2 = move _3 as *const ! (PtrToPtr); + StorageDead(_3); + StorageDead(_2); + StorageDead(_1); + StorageLive(_4); + _4 = const 3_u8; + StorageLive(_5); + StorageLive(_6); + _6 = &raw const _4; + _5 = move _6 as *const ! (PtrToPtr); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + return; + } +} diff --git a/tests/mir-opt/uninhabited_not_read.rs b/tests/mir-opt/uninhabited_not_read.rs new file mode 100644 index 0000000000000..39ffdbdbb3359 --- /dev/null +++ b/tests/mir-opt/uninhabited_not_read.rs @@ -0,0 +1,27 @@ +// skip-filecheck + +//@ edition: 2021 +// In ed 2021 and below, we don't fallback `!` to `()`. +// This would introduce a `! -> ()` coercion which would +// be UB if we didn't disallow this explicitly. + +#![feature(never_type)] + +// EMIT_MIR uninhabited_not_read.main.SimplifyLocals-final.after.mir +fn main() { + // With a type annotation + unsafe { + let x = 3u8; + let x: *const ! = &x as *const u8 as *const _; + let _: ! = *x; + } + + // Without a type annotation, make sure we don't implicitly coerce `!` to `()` + // when we do the noop `*x`. + unsafe { + let x = 3u8; + let x: *const ! = &x as *const u8 as *const _; + let _ = *x; + } +} + diff --git a/tests/ui/never_type/diverging-place-match.rs b/tests/ui/never_type/diverging-place-match.rs index 7bc00773c0ba5..185e17c05a773 100644 --- a/tests/ui/never_type/diverging-place-match.rs +++ b/tests/ui/never_type/diverging-place-match.rs @@ -1,6 +1,6 @@ #![feature(never_type)] -fn make_up_a_value() -> T { +fn not_a_read() -> ! { unsafe { //~^ ERROR mismatched types let x: *const ! = 0 as _; @@ -10,4 +10,38 @@ fn make_up_a_value() -> T { } } +fn not_a_read_implicit() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let _ = *x; + } +} + +fn not_a_read_guide_coercion() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let _: () = *x; + //~^ ERROR mismatched types + } +} + +fn empty_match() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + match *x { _ => {} }; + } +} + +fn field_projection() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const (!, ()) = 0 as _; + let _ = (*x).0; + // ^ I think this is still UB, but because of the inbounds projection. + } +} + fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.stderr b/tests/ui/never_type/diverging-place-match.stderr index e86c634d59186..14b14f4270131 100644 --- a/tests/ui/never_type/diverging-place-match.stderr +++ b/tests/ui/never_type/diverging-place-match.stderr @@ -1,8 +1,6 @@ error[E0308]: mismatched types --> $DIR/diverging-place-match.rs:4:5 | -LL | fn make_up_a_value() -> T { - | - expected this type parameter LL | / unsafe { LL | | LL | | let x: *const ! = 0 as _; @@ -10,11 +8,76 @@ LL | | let _: ! = *x; LL | | // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this LL | | // is unsound since we act as if it diverges but it doesn't. LL | | } - | |_____^ expected type parameter `T`, found `()` + | |_____^ expected `!`, found `()` | - = note: expected type parameter `T` - found unit type `()` + = note: expected type `!` + found unit type `()` -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:14:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let _ = *x; +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:25:21 + | +LL | let _: () = *x; + | -- ^^ expected `()`, found `!` + | | + | expected due to this + | + = note: expected unit type `()` + found type `!` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:22:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let _: () = *x; +LL | | +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:31:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | match *x { _ => {} }; +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:39:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const (!, ()) = 0 as _; +LL | | let _ = (*x).0; +LL | | // ^ I think this is still UB, but because of the inbounds projection. +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0308`. From 515b93297ff6b96e6368070f3eb3f3a833112bc0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 07:53:49 -0400 Subject: [PATCH 05/10] Document things a bit more carefully, also account for coercion in check_expr_has_type_or_error --- compiler/rustc_hir_typeck/src/coercion.rs | 1 + compiler/rustc_hir_typeck/src/expr.rs | 16 +++++++++++++++- tests/mir-opt/uninhabited_not_read.rs | 1 - .../match/pattern-matching-should-fail.stderr | 6 ++---- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 1705421c2f40e..96bff04dbc7cf 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -186,6 +186,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { if self.coerce_never { return success(simple(Adjust::NeverToAny)(b), b, vec![]); } else { + // Otherwise the only coercion we can do is unification. return self.unify_and(a, b, identity); } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 870a260c9fe09..604bec95701d5 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -62,7 +62,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from ! to `expected`. - if ty.is_never() { + if ty.is_never() && self.expr_constitutes_read(expr) { if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { let reported = self.dcx().span_delayed_bug( expr.span, @@ -260,6 +260,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } + /// Whether this expression constitutes a read of value of the type that + /// it evaluates to. + /// + /// This is used to determine if we should consider the block to diverge + /// if the expression evaluates to `!`, and if we should insert a `NeverToAny` + /// coercion for values of type `!`. + /// + /// This function generally returns `false` if the expression is a place + /// expression and the *parent* expression is the scrutinee of a match or + /// the pointee of an `&` addr-of expression, since both of those parent + /// expressions take a *place* and not a value. + /// + /// This function is unfortunately a bit heuristical, though it is certainly + /// far sounder than the prior status quo: . pub(super) fn expr_constitutes_read(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { // We only care about place exprs. Anything else returns an immediate // which would constitute a read. We don't care about distinguishing diff --git a/tests/mir-opt/uninhabited_not_read.rs b/tests/mir-opt/uninhabited_not_read.rs index 39ffdbdbb3359..15769cdd75b3f 100644 --- a/tests/mir-opt/uninhabited_not_read.rs +++ b/tests/mir-opt/uninhabited_not_read.rs @@ -24,4 +24,3 @@ fn main() { let _ = *x; } } - diff --git a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr index f8ed792e3c675..e0d900a1eb564 100644 --- a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr @@ -7,14 +7,12 @@ LL | let c1 = || match x { }; | ^ `x` used here but it isn't initialized error[E0381]: used binding `x` isn't initialized - --> $DIR/pattern-matching-should-fail.rs:15:14 + --> $DIR/pattern-matching-should-fail.rs:15:23 | LL | let x: !; | - binding declared here but left uninitialized LL | let c2 = || match x { _ => () }; - | ^^ - borrow occurs due to use in closure - | | - | `x` used here but it isn't initialized + | ^ `x` used here but it isn't initialized error[E0381]: used binding `variant` isn't initialized --> $DIR/pattern-matching-should-fail.rs:27:13 From d2bd018dadc98442a15f52d962aaf3c5d102f034 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 09:42:44 -0400 Subject: [PATCH 06/10] Be more thorough in expr_constitutes_read --- compiler/rustc_hir_typeck/src/expr.rs | 181 ++++++++++++++++++++------ compiler/rustc_hir_typeck/src/pat.rs | 2 +- 2 files changed, 141 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 604bec95701d5..cef00cf8ce865 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1,3 +1,6 @@ +// ignore-tidy-filelength +// FIXME: we should move the field error reporting code somewhere else. + //! Type checking expressions. //! //! See [`rustc_hir_analysis::check`] for more context on type checking in general. @@ -284,54 +287,150 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } - // If this expression has any adjustments applied after the place expression, - // they may constitute reads. - if !self.typeck_results.borrow().expr_adjustments(expr).is_empty() { - return true; - } + let parent_node = self.tcx.parent_hir_node(expr.hir_id); + match parent_node { + hir::Node::Expr(parent_expr) => { + match parent_expr.kind { + // Addr-of, field projections, and LHS of assignment don't constitute reads. + // Assignment does call `drop_in_place`, though, but its safety + // requirements are not the same. + ExprKind::AddrOf(..) | hir::ExprKind::Field(..) => false, + ExprKind::Assign(lhs, _, _) => { + // Only the LHS does not constitute a read + expr.hir_id != lhs.hir_id + } - fn pat_does_read(pat: &hir::Pat<'_>) -> bool { - let mut does_read = false; - pat.walk(|pat| { - if matches!( - pat.kind, - hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) - ) { - true - } else { - does_read = true; - // No need to continue. - false - } - }); - does_read - } + // If we have a subpattern that performs a read, we want to consider this + // to diverge for compatibility to support something like `let x: () = *never_ptr;`. + ExprKind::Match(scrutinee, arms, _) => { + assert_eq!(scrutinee.hir_id, expr.hir_id); + arms.iter().any(|arm| self.pat_constitutes_read(arm.pat)) + } + ExprKind::Let(hir::LetExpr { init, pat, .. }) => { + assert_eq!(init.hir_id, expr.hir_id); + self.pat_constitutes_read(*pat) + } - match self.tcx.parent_hir_node(expr.hir_id) { - // Addr-of, field projections, and LHS of assignment don't constitute reads. - // Assignment does call `drop_in_place`, though, but its safety - // requirements are not the same. - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. }) => false, - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Assign(target, _, _) | hir::ExprKind::Field(target, _), - .. - }) if expr.hir_id == target.hir_id => false, + // Any expression child of these expressions constitute reads. + ExprKind::Array(_) + | ExprKind::Call(_, _) + | ExprKind::MethodCall(_, _, _, _) + | ExprKind::Tup(_) + | ExprKind::Binary(_, _, _) + | ExprKind::Unary(_, _) + | ExprKind::Cast(_, _) + | ExprKind::Type(_, _) + | ExprKind::DropTemps(_) + | ExprKind::If(_, _, _) + | ExprKind::Closure(_) + | ExprKind::Block(_, _) + | ExprKind::AssignOp(_, _, _) + | ExprKind::Index(_, _, _) + | ExprKind::Break(_, _) + | ExprKind::Ret(_) + | ExprKind::Become(_) + | ExprKind::InlineAsm(_) + | ExprKind::Struct(_, _, _) + | ExprKind::Repeat(_, _) + | ExprKind::Yield(_, _) => true, + + // These expressions have no (direct) sub-exprs. + ExprKind::ConstBlock(_) + | ExprKind::Loop(_, _, _, _) + | ExprKind::Lit(_) + | ExprKind::Path(_) + | ExprKind::Continue(_) + | ExprKind::OffsetOf(_, _) + | ExprKind::Err(_) => unreachable!("no sub-expr expected for {:?}", expr.kind), + } + } // If we have a subpattern that performs a read, we want to consider this // to diverge for compatibility to support something like `let x: () = *never_ptr;`. - hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Let(hir::LetExpr { init: target, pat, .. }), + hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) => { + assert_eq!(target.hir_id, expr.hir_id); + self.pat_constitutes_read(*pat) + } + + // These nodes (if they have a sub-expr) do constitute a read. + hir::Node::Block(_) + | hir::Node::Arm(_) + | hir::Node::ExprField(_) + | hir::Node::AnonConst(_) + | hir::Node::ConstBlock(_) + | hir::Node::ConstArg(_) + | hir::Node::Stmt(_) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..), .. - }) if expr.hir_id == target.hir_id && !pat_does_read(*pat) => false, - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Match(target, arms, _), .. }) - if expr.hir_id == target.hir_id - && !arms.iter().any(|arm| pat_does_read(arm.pat)) => - { - false + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(..), .. + }) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => true, + + // These nodes do not have direct sub-exprs. + hir::Node::Param(_) + | hir::Node::Item(_) + | hir::Node::ForeignItem(_) + | hir::Node::TraitItem(_) + | hir::Node::ImplItem(_) + | hir::Node::Variant(_) + | hir::Node::Field(_) + | hir::Node::PathSegment(_) + | hir::Node::Ty(_) + | hir::Node::AssocItemConstraint(_) + | hir::Node::TraitRef(_) + | hir::Node::Pat(_) + | hir::Node::PatField(_) + | hir::Node::LetStmt(_) + | hir::Node::Synthetic + | hir::Node::Err(_) + | hir::Node::Ctor(_) + | hir::Node::Lifetime(_) + | hir::Node::GenericParam(_) + | hir::Node::Crate(_) + | hir::Node::Infer(_) + | hir::Node::WhereBoundPredicate(_) + | hir::Node::ArrayLenInfer(_) + | hir::Node::PreciseCapturingNonLifetimeArg(_) + | hir::Node::OpaqueTy(_) => { + unreachable!("no sub-expr expected for {parent_node:?}") + } + } + } + + /// Whether this pattern constitutes a read of value of the scrutinee that + /// it is matching against. + /// + /// See above for the nuances of what happens when this returns true. + pub(super) fn pat_constitutes_read(&self, pat: &hir::Pat<'_>) -> bool { + let mut does_read = false; + pat.walk(|pat| { + match pat.kind { + hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) => { + // Recurse + true + } + hir::PatKind::Binding(_, _, _, _) + | hir::PatKind::Struct(_, _, _) + | hir::PatKind::TupleStruct(_, _, _) + | hir::PatKind::Path(_) + | hir::PatKind::Tuple(_, _) + | hir::PatKind::Box(_) + | hir::PatKind::Ref(_, _) + | hir::PatKind::Deref(_) + | hir::PatKind::Lit(_) + | hir::PatKind::Range(_, _, _) + | hir::PatKind::Slice(_, _, _) + | hir::PatKind::Err(_) => { + does_read = true; + // No need to continue. + false + } } - _ => true, - } + }); + does_read } #[instrument(skip(self, expr), level = "debug")] diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 824918d1fa222..c2e25d259c582 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -279,7 +279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // All other patterns constitute a read, which causes us to diverge // if the type is never. - if ty.is_never() && !matches!(pat.kind, PatKind::Wild | PatKind::Never | PatKind::Or(_)) { + if ty.is_never() && self.pat_constitutes_read(pat) { self.diverges.set(self.diverges.get() | Diverges::always(pat.span)); } From e8d5eb2a2b6cdddd6725501e1b54a33c278d9697 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 6 Sep 2024 10:19:07 -0400 Subject: [PATCH 07/10] Be far more strict about what we consider to be a read of never --- compiler/rustc_hir_typeck/src/coercion.rs | 10 ++- compiler/rustc_hir_typeck/src/expr.rs | 87 ++++++++++--------- compiler/rustc_hir_typeck/src/pat.rs | 7 -- ...ocess_never.SimplifyLocals-final.after.mir | 4 +- ...rocess_void.SimplifyLocals-final.after.mir | 2 +- tests/mir-opt/uninhabited_enum.rs | 7 +- tests/ui/never_type/diverging-place-match.rs | 27 ++++++ .../never_type/diverging-place-match.stderr | 61 ++++++++++++- .../raw-ref-op/never-place-isnt-diverging.rs | 9 ++ .../never-place-isnt-diverging.stderr | 16 +++- .../reachable/unwarned-match-on-never.stderr | 2 +- 11 files changed, 177 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 96bff04dbc7cf..642db3f36b586 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1066,7 +1066,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable)); - let coerce = Coerce::new(self, cause, allow_two_phase, self.expr_constitutes_read(expr)); + let coerce = Coerce::new( + self, + cause, + allow_two_phase, + self.expr_guaranteed_to_constitute_read_for_never(expr), + ); let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?; let (adjustments, _) = self.register_infer_ok_obligations(ok); @@ -1268,6 +1273,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // probably aren't processing function arguments here and even if we were, // they're going to get autorefed again anyway and we can apply 2-phase borrows // at that time. + // + // NOTE: we set `coerce_never` to `true` here because coercion LUBs only + // operate on values and not places, so a never coercion is valid. let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true); coerce.use_lub = true; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index cef00cf8ce865..2d71829537496 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from ! to `expected`. - if ty.is_never() && self.expr_constitutes_read(expr) { + if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) { if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { let reported = self.dcx().span_delayed_bug( expr.span, @@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // unless it's a place expression that isn't being read from, in which case // diverging would be unsound since we may never actually read the `!`. // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. - if ty.is_never() && self.expr_constitutes_read(expr) { + if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) { self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); } @@ -274,10 +274,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// expression and the *parent* expression is the scrutinee of a match or /// the pointee of an `&` addr-of expression, since both of those parent /// expressions take a *place* and not a value. - /// - /// This function is unfortunately a bit heuristical, though it is certainly - /// far sounder than the prior status quo: . - pub(super) fn expr_constitutes_read(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { + pub(super) fn expr_guaranteed_to_constitute_read_for_never( + &self, + expr: &'tcx hir::Expr<'tcx>, + ) -> bool { // We only care about place exprs. Anything else returns an immediate // which would constitute a read. We don't care about distinguishing // "syntactic" place exprs since if the base of a field projection is @@ -300,15 +300,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr.hir_id != lhs.hir_id } - // If we have a subpattern that performs a read, we want to consider this - // to diverge for compatibility to support something like `let x: () = *never_ptr;`. + // See note on `PatKind::Or` below for why this is `all`. ExprKind::Match(scrutinee, arms, _) => { assert_eq!(scrutinee.hir_id, expr.hir_id); - arms.iter().any(|arm| self.pat_constitutes_read(arm.pat)) + arms.iter() + .all(|arm| self.pat_guaranteed_to_constitute_read_for_never(arm.pat)) } ExprKind::Let(hir::LetExpr { init, pat, .. }) => { assert_eq!(init.hir_id, expr.hir_id); - self.pat_constitutes_read(*pat) + self.pat_guaranteed_to_constitute_read_for_never(*pat) } // Any expression child of these expressions constitute reads. @@ -349,7 +349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to diverge for compatibility to support something like `let x: () = *never_ptr;`. hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) => { assert_eq!(target.hir_id, expr.hir_id); - self.pat_constitutes_read(*pat) + self.pat_guaranteed_to_constitute_read_for_never(*pat) } // These nodes (if they have a sub-expr) do constitute a read. @@ -401,36 +401,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Whether this pattern constitutes a read of value of the scrutinee that - /// it is matching against. + /// it is matching against. This is used to determine whether we should + /// perform `NeverToAny` coercions. /// /// See above for the nuances of what happens when this returns true. - pub(super) fn pat_constitutes_read(&self, pat: &hir::Pat<'_>) -> bool { - let mut does_read = false; - pat.walk(|pat| { - match pat.kind { - hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) => { - // Recurse - true - } - hir::PatKind::Binding(_, _, _, _) - | hir::PatKind::Struct(_, _, _) - | hir::PatKind::TupleStruct(_, _, _) - | hir::PatKind::Path(_) - | hir::PatKind::Tuple(_, _) - | hir::PatKind::Box(_) - | hir::PatKind::Ref(_, _) - | hir::PatKind::Deref(_) - | hir::PatKind::Lit(_) - | hir::PatKind::Range(_, _, _) - | hir::PatKind::Slice(_, _, _) - | hir::PatKind::Err(_) => { - does_read = true; - // No need to continue. - false - } - } - }); - does_read + pub(super) fn pat_guaranteed_to_constitute_read_for_never(&self, pat: &hir::Pat<'_>) -> bool { + match pat.kind { + // Does not constitute a read. + hir::PatKind::Wild => false, + + // This is unnecessarily restrictive when the pattern that doesn't + // constitute a read is unreachable. + // + // For example `match *never_ptr { value => {}, _ => {} }` or + // `match *never_ptr { _ if false => {}, value => {} }`. + // + // It is however fine to be restrictive here; only returning `true` + // can lead to unsoundness. + hir::PatKind::Or(subpats) => { + subpats.iter().all(|pat| self.pat_guaranteed_to_constitute_read_for_never(pat)) + } + + // Does constitute a read, since it is equivalent to a discriminant read. + hir::PatKind::Never => true, + + // All of these constitute a read, or match on something that isn't `!`, + // which would require a `NeverToAny` coercion. + hir::PatKind::Binding(_, _, _, _) + | hir::PatKind::Struct(_, _, _) + | hir::PatKind::TupleStruct(_, _, _) + | hir::PatKind::Path(_) + | hir::PatKind::Tuple(_, _) + | hir::PatKind::Box(_) + | hir::PatKind::Ref(_, _) + | hir::PatKind::Deref(_) + | hir::PatKind::Lit(_) + | hir::PatKind::Range(_, _, _) + | hir::PatKind::Slice(_, _, _) + | hir::PatKind::Err(_) => true, + } } #[instrument(skip(self, expr), level = "debug")] diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index c2e25d259c582..49c5a7d8a652d 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -27,7 +27,6 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use super::report_unexpected_variant_res; -use crate::diverges::Diverges; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, LoweredTy, errors}; @@ -277,12 +276,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - // All other patterns constitute a read, which causes us to diverge - // if the type is never. - if ty.is_never() && self.pat_constitutes_read(pat) { - self.diverges.set(self.diverges.get() | Diverges::always(pat.span)); - } - self.write_ty(pat.hir_id, ty); // (note_1): In most of the cases where (note_1) is referenced diff --git a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir index 64fc81e298946..02e1f4be15e2e 100644 --- a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir @@ -4,10 +4,10 @@ fn process_never(_1: *const !) -> () { debug input => _1; let mut _0: (); scope 1 { - debug _input => _1; + debug _input => const (); } bb0: { - return; + unreachable; } } diff --git a/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir index 51514ba5e5d90..64711755f7394 100644 --- a/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir @@ -4,7 +4,7 @@ fn process_void(_1: *const Void) -> () { debug input => _1; let mut _0: (); scope 1 { - debug _input => _1; + debug _input => const ZeroSized: Void; } bb0: { diff --git a/tests/mir-opt/uninhabited_enum.rs b/tests/mir-opt/uninhabited_enum.rs index 9d845e34fbda8..90b5353f29143 100644 --- a/tests/mir-opt/uninhabited_enum.rs +++ b/tests/mir-opt/uninhabited_enum.rs @@ -1,18 +1,21 @@ // skip-filecheck #![feature(never_type)] +#[derive(Copy, Clone)] pub enum Void {} // EMIT_MIR uninhabited_enum.process_never.SimplifyLocals-final.after.mir #[no_mangle] pub fn process_never(input: *const !) { - let _input = unsafe { &*input }; + let _input = unsafe { *input }; } // EMIT_MIR uninhabited_enum.process_void.SimplifyLocals-final.after.mir #[no_mangle] pub fn process_void(input: *const Void) { - let _input = unsafe { &*input }; + let _input = unsafe { *input }; + // In the future, this should end with `unreachable`, but we currently only do + // unreachability analysis for `!`. } fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.rs b/tests/ui/never_type/diverging-place-match.rs index 185e17c05a773..b9bc29a218c4a 100644 --- a/tests/ui/never_type/diverging-place-match.rs +++ b/tests/ui/never_type/diverging-place-match.rs @@ -44,4 +44,31 @@ fn field_projection() -> ! { } } +fn covered_arm() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let (_ | 1i32) = *x; + //~^ ERROR mismatched types + } +} + +// FIXME: This *could* be considered a read of `!`, but we're not that sophisticated.. +fn uncovered_arm() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let (1i32 | _) = *x; + //~^ ERROR mismatched types + } +} + +fn coerce_ref_binding() -> ! { + unsafe { + let x: *const ! = 0 as _; + let ref _x: () = *x; + //~^ ERROR mismatched types + } +} + fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.stderr b/tests/ui/never_type/diverging-place-match.stderr index 14b14f4270131..74e1bfa0a6b64 100644 --- a/tests/ui/never_type/diverging-place-match.stderr +++ b/tests/ui/never_type/diverging-place-match.stderr @@ -78,6 +78,65 @@ LL | | } = note: expected type `!` found unit type `()` -error: aborting due to 6 previous errors +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:51:18 + | +LL | let (_ | 1i32) = *x; + | ^^^^ -- this expression has type `!` + | | + | expected `!`, found `i32` + | + = note: expected type `!` + found type `i32` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:48:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let (_ | 1i32) = *x; +LL | | +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:61:14 + | +LL | let (1i32 | _) = *x; + | ^^^^ -- this expression has type `!` + | | + | expected `!`, found `i32` + | + = note: expected type `!` + found type `i32` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:58:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let (1i32 | _) = *x; +LL | | +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:69:26 + | +LL | let ref _x: () = *x; + | ^^ expected `()`, found `!` + | + = note: expected unit type `()` + found type `!` + +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.rs b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs index 52f4158dbc988..80d441729f746 100644 --- a/tests/ui/raw-ref-op/never-place-isnt-diverging.rs +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs @@ -10,4 +10,13 @@ fn make_up_a_value() -> T { } } + +fn make_up_a_pointer() -> *const T { + unsafe { + let x: *const ! = 0 as _; + &raw const *x + //~^ ERROR mismatched types + } +} + fn main() {} diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr index 9eba57dde8fa7..af9e788982179 100644 --- a/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr @@ -15,6 +15,20 @@ LL | | } = note: expected type parameter `T` found unit type `()` -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/never-place-isnt-diverging.rs:17:9 + | +LL | fn make_up_a_pointer() -> *const T { + | - -------- expected `*const T` because of return type + | | + | expected this type parameter +... +LL | &raw const *x + | ^^^^^^^^^^^^^ expected `*const T`, found `*const !` + | + = note: expected raw pointer `*const T` + found raw pointer `*const !` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/reachable/unwarned-match-on-never.stderr b/tests/ui/reachable/unwarned-match-on-never.stderr index c1ad3511b4e08..a296d2a055e09 100644 --- a/tests/ui/reachable/unwarned-match-on-never.stderr +++ b/tests/ui/reachable/unwarned-match-on-never.stderr @@ -2,7 +2,7 @@ error: unreachable expression --> $DIR/unwarned-match-on-never.rs:10:5 | LL | match x {} - | ---------- any code following this expression is unreachable + | - any code following this expression is unreachable LL | // But matches in unreachable code are warned. LL | match x {} | ^^^^^^^^^^ unreachable expression From 7f5548fa8b22bccdadb3cbe7cf4d566a970eeb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 6 Oct 2024 01:44:59 +0000 Subject: [PATCH 08/10] On function and method calls in patterns, link to the book ``` error: expected a pattern, found an expression --> f889.rs:3:13 | 3 | let (x, y.drop()) = (1, 2); //~ ERROR | ^^^^^^^^ not a pattern | = note: arbitrary expressions are not allowed in patterns: error[E0532]: expected a pattern, found a function call --> f889.rs:2:13 | 2 | let (x, drop(y)) = (1, 2); //~ ERROR | ^^^^ not a tuple struct or tuple variant | = note: function calls are not allowed in patterns: ``` Fix #97200. --- compiler/rustc_parse/messages.ftl | 3 +- compiler/rustc_parse/src/errors.rs | 1 + .../rustc_resolve/src/late/diagnostics.rs | 13 +++ .../range_pat_interactions1.stderr | 3 +- .../range_pat_interactions2.stderr | 3 +- tests/ui/parser/bad-name.stderr | 4 +- tests/ui/parser/issues/issue-24197.stderr | 4 +- tests/ui/parser/issues/issue-24375.stderr | 3 +- tests/ui/parser/pat-lt-bracket-5.stderr | 4 +- tests/ui/parser/pat-lt-bracket-6.stderr | 4 +- tests/ui/parser/pat-ranges-3.stderr | 8 +- .../parser/recover/recover-pat-exprs.stderr | 103 ++++++++++++------ .../parser/recover/recover-pat-issues.stderr | 18 ++- .../ui/parser/recover/recover-pat-lets.stderr | 18 ++- .../parser/recover/recover-pat-ranges.stderr | 18 ++- .../recover/recover-pat-wildcards.stderr | 3 +- tests/ui/resolve/issue-10200.rs | 2 +- tests/ui/resolve/issue-10200.stderr | 4 +- ...s-in-pattern-with-ty-err-doesnt-ice.stderr | 4 +- 19 files changed, 156 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 5d1c300b45357..948199fd55c65 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -812,7 +812,8 @@ parse_unexpected_expr_in_pat = *[false] a pattern }, found an expression - .label = arbitrary expressions are not allowed in patterns + .label = not a pattern + .note = arbitrary expressions are not allowed in patterns: parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression into a `const` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 40502158469f0..dade39127515f 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2608,6 +2608,7 @@ pub(crate) struct ExpectedCommaAfterPatternField { #[derive(Diagnostic)] #[diag(parse_unexpected_expr_in_pat)] +#[note] pub(crate) struct UnexpectedExpressionInPattern { /// The unexpected expr's span. #[primary_span] diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 35d166e8b4ab3..fce5ec36c661b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -443,6 +443,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { self.suggest_bare_struct_literal(&mut err); self.suggest_changing_type_to_const_param(&mut err, res, source, span); + self.explain_functions_in_pattern(&mut err, res, source); if self.suggest_pattern_match_with_let(&mut err, source, span) { // Fallback label. @@ -1166,6 +1167,18 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } + fn explain_functions_in_pattern( + &mut self, + err: &mut Diag<'_>, + res: Option, + source: PathSource<'_>, + ) { + let PathSource::TupleStruct(_, _) = source else { return }; + let Some(Res::Def(DefKind::Fn, _)) = res else { return }; + err.primary_message("expected a pattern, found a function call"); + err.note("function calls are not allowed in patterns: "); + } + fn suggest_changing_type_to_const_param( &mut self, err: &mut Diag<'_>, diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr index 9831348de7569..e2916725fbd90 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr @@ -2,8 +2,9 @@ error: expected a pattern range bound, found an expression --> $DIR/range_pat_interactions1.rs:17:16 | LL | 0..5+1 => errors_only.push(x), - | ^^^ arbitrary expressions are not allowed in patterns + | ^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 5+1; diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr index 1b5e875cccb68..f54e07c3a6370 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr @@ -14,8 +14,9 @@ error: expected a pattern range bound, found an expression --> $DIR/range_pat_interactions2.rs:10:18 | LL | 0..=(5+1) => errors_only.push(x), - | ^^^ arbitrary expressions are not allowed in patterns + | ^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 5+1; diff --git a/tests/ui/parser/bad-name.stderr b/tests/ui/parser/bad-name.stderr index 3fc416dd5311f..5ca248380ee5b 100644 --- a/tests/ui/parser/bad-name.stderr +++ b/tests/ui/parser/bad-name.stderr @@ -8,7 +8,9 @@ error: expected a pattern, found an expression --> $DIR/bad-name.rs:2:7 | LL | let x.y::.z foo; - | ^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected one of `(`, `.`, `::`, `:`, `;`, `=`, `?`, `|`, or an operator, found `foo` --> $DIR/bad-name.rs:2:22 diff --git a/tests/ui/parser/issues/issue-24197.stderr b/tests/ui/parser/issues/issue-24197.stderr index 7ebbf4ac370d7..c92e165b23b65 100644 --- a/tests/ui/parser/issues/issue-24197.stderr +++ b/tests/ui/parser/issues/issue-24197.stderr @@ -2,7 +2,9 @@ error: expected a pattern, found an expression --> $DIR/issue-24197.rs:2:9 | LL | let buf[0] = 0; - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr index a25c277d78a5b..fef3fcde7b799 100644 --- a/tests/ui/parser/issues/issue-24375.stderr +++ b/tests/ui/parser/issues/issue-24375.stderr @@ -2,8 +2,9 @@ error: expected a pattern, found an expression --> $DIR/issue-24375.rs:6:9 | LL | tmp[0] => {} - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == tmp[0] => {} diff --git a/tests/ui/parser/pat-lt-bracket-5.stderr b/tests/ui/parser/pat-lt-bracket-5.stderr index 18cf2df028216..a2a972652d188 100644 --- a/tests/ui/parser/pat-lt-bracket-5.stderr +++ b/tests/ui/parser/pat-lt-bracket-5.stderr @@ -2,7 +2,9 @@ error: expected a pattern, found an expression --> $DIR/pat-lt-bracket-5.rs:2:9 | LL | let v[0] = v[1]; - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error[E0425]: cannot find value `v` in this scope --> $DIR/pat-lt-bracket-5.rs:2:16 diff --git a/tests/ui/parser/pat-lt-bracket-6.stderr b/tests/ui/parser/pat-lt-bracket-6.stderr index 892883c4aedca..14ae602fedfa1 100644 --- a/tests/ui/parser/pat-lt-bracket-6.stderr +++ b/tests/ui/parser/pat-lt-bracket-6.stderr @@ -2,7 +2,9 @@ error: expected a pattern, found an expression --> $DIR/pat-lt-bracket-6.rs:5:14 | LL | let Test(&desc[..]) = x; - | ^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error[E0308]: mismatched types --> $DIR/pat-lt-bracket-6.rs:10:30 diff --git a/tests/ui/parser/pat-ranges-3.stderr b/tests/ui/parser/pat-ranges-3.stderr index 5e1f35d1b6f9a..ef080368e19ca 100644 --- a/tests/ui/parser/pat-ranges-3.stderr +++ b/tests/ui/parser/pat-ranges-3.stderr @@ -2,13 +2,17 @@ error: expected a pattern range bound, found an expression --> $DIR/pat-ranges-3.rs:4:16 | LL | let 10 ..= 10 + 3 = 12; - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern range bound, found an expression --> $DIR/pat-ranges-3.rs:7:9 | LL | let 10 - 3 ..= 10 = 8; - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: aborting due to 2 previous errors diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr index 63956f35c079f..6cb3753de8d17 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.stderr +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -2,8 +2,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:5:9 | LL | x.y => (), - | ^^^ arbitrary expressions are not allowed in patterns + | ^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.y => (), @@ -24,8 +25,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:6:9 | LL | x.0 => (), - | ^^^ arbitrary expressions are not allowed in patterns + | ^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.0 => (), @@ -47,8 +49,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:7:9 | LL | x._0 => (), - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x._0 => (), @@ -71,8 +74,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:8:9 | LL | x.0.1 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.0.1 => (), @@ -95,8 +99,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:9:9 | LL | x.4.y.17.__z => (), - | ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.4.y.17.__z => (), @@ -149,8 +154,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:23:9 | LL | x[0] => (), - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x[0] => (), @@ -170,8 +176,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:24:9 | LL | x[..] => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x[..] => (), @@ -219,8 +226,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:37:9 | LL | x.f() => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.f() => (), @@ -240,8 +248,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:38:9 | LL | x._f() => (), - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x._f() => (), @@ -262,8 +271,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:39:9 | LL | x? => (), - | ^^ arbitrary expressions are not allowed in patterns + | ^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x? => (), @@ -285,8 +295,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:40:9 | LL | ().f() => (), - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == ().f() => (), @@ -309,8 +320,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:41:9 | LL | (0, x)?.f() => (), - | ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == (0, x)?.f() => (), @@ -333,8 +345,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:42:9 | LL | x.f().g() => (), - | ^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.f().g() => (), @@ -357,8 +370,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:43:9 | LL | 0.f()?.g()?? => (), - | ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == 0.f()?.g()?? => (), @@ -381,8 +395,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:50:9 | LL | x as usize => (), - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x as usize => (), @@ -402,8 +417,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:51:9 | LL | 0 as usize => (), - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == 0 as usize => (), @@ -424,8 +440,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:52:9 | LL | x.f().0.4 as f32 => (), - | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.f().0.4 as f32 => (), @@ -447,8 +464,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:59:9 | LL | 1 + 1 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == 1 + 1 => (), @@ -468,8 +486,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:60:9 | LL | (1 + 2) * 3 => (), - | ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == (1 + 2) * 3 => (), @@ -490,8 +509,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:63:9 | LL | x.0 > 2 => (), - | ^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == (x.0 > 2) => (), @@ -514,8 +534,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:64:9 | LL | x.0 == 2 => (), - | ^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == (x.0 == 2) => (), @@ -538,8 +559,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:69:13 | LL | (x, y.0 > 2) if x != 0 => (), - | ^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to the match arm guard | LL | (x, val) if x != 0 && val == (y.0 > 2) => (), @@ -559,8 +581,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:70:13 | LL | (x, y.0 > 2) if x != 0 || x != 1 => (), - | ^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to the match arm guard | LL | (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (), @@ -598,8 +621,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:81:9 | LL | u8::MAX.abs() => (), - | ^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == u8::MAX.abs() => (), @@ -619,8 +643,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:86:17 | LL | z @ w @ v.u() => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | z @ w @ val if val == v.u() => (), @@ -643,8 +668,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:88:9 | LL | y.ilog(3) => (), - | ^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == y.ilog(3) => (), @@ -667,8 +693,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:90:9 | LL | n + 1 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == n + 1 => (), @@ -691,8 +718,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:92:10 | LL | ("".f() + 14 * 8) => (), - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | (val) if val == "".f() + 14 * 8 => (), @@ -715,8 +743,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:95:9 | LL | f?() => (), - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == f?() => (), @@ -739,7 +768,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:101:9 | LL | let 1 + 1 = 2; - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected one of `)`, `,`, `@`, or `|`, found `*` --> $DIR/recover-pat-exprs.rs:104:28 @@ -754,19 +785,25 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:60:10 | LL | (1 + 2) * 3 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:75:5 | LL | 1 + 2 * PI.cos() => 2, - | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:83:9 | LL | x.sqrt() @ .. => (), - | ^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: aborting due to 45 previous errors diff --git a/tests/ui/parser/recover/recover-pat-issues.stderr b/tests/ui/parser/recover/recover-pat-issues.stderr index 596bff2139525..17cb7b4aead80 100644 --- a/tests/ui/parser/recover/recover-pat-issues.stderr +++ b/tests/ui/parser/recover/recover-pat-issues.stderr @@ -2,8 +2,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:6:13 | LL | Foo("hi".to_owned()) => true, - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | Foo(val) if val == "hi".to_owned() => true, @@ -23,8 +24,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:14:20 | LL | Bar { baz: "hi".to_owned() } => true, - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | Bar { baz } if baz == "hi".to_owned() => true, @@ -44,8 +46,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:25:11 | LL | &["foo".to_string()] => {} - | ^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | &[val] if val == "foo".to_string() => {} @@ -65,8 +68,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:36:17 | LL | if let Some(MAGIC.0 as usize) = None:: {} - | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = MAGIC.0 as usize; @@ -81,8 +85,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:41:13 | LL | if let (-1.some(4)) = (0, Some(4)) {} - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = -1.some(4); @@ -97,8 +102,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:44:13 | LL | if let (-1.Some(4)) = (0, Some(4)) {} - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = -1.Some(4); diff --git a/tests/ui/parser/recover/recover-pat-lets.stderr b/tests/ui/parser/recover/recover-pat-lets.stderr index e54586b092483..b481813b24621 100644 --- a/tests/ui/parser/recover/recover-pat-lets.stderr +++ b/tests/ui/parser/recover/recover-pat-lets.stderr @@ -2,26 +2,33 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:4:9 | LL | let x.expect("foo"); - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:7:9 | LL | let x.unwrap(): u32; - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:10:9 | LL | let x[0] = 1; - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:13:14 | LL | let Some(1 + 1) = x else { - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 1; @@ -36,8 +43,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:17:17 | LL | if let Some(1 + 1) = x { - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 1; diff --git a/tests/ui/parser/recover/recover-pat-ranges.stderr b/tests/ui/parser/recover/recover-pat-ranges.stderr index 088f83b0ccbac..0a9b54474689a 100644 --- a/tests/ui/parser/recover/recover-pat-ranges.stderr +++ b/tests/ui/parser/recover/recover-pat-ranges.stderr @@ -86,8 +86,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:11:12 | LL | ..=1 + 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 2; @@ -106,8 +107,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:15:10 | LL | (-4 + 0).. => (), - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = -4 + 0; @@ -126,8 +128,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:18:10 | LL | (1 + 4)...1 * 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 4; @@ -146,8 +149,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:18:19 | LL | (1 + 4)...1 * 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 * 2; @@ -166,8 +170,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:24:9 | LL | 0.x()..="y".z() => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 0.x(); @@ -186,8 +191,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:24:17 | LL | 0.x()..="y".z() => (), - | ^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "y".z(); diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr index 30307726a973b..8d4212ed389dd 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.stderr +++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr @@ -75,8 +75,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-wildcards.rs:55:14 | LL | 4..=(2 + _) => () - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 2 + _; diff --git a/tests/ui/resolve/issue-10200.rs b/tests/ui/resolve/issue-10200.rs index fe36a7e00bff3..d529536b95256 100644 --- a/tests/ui/resolve/issue-10200.rs +++ b/tests/ui/resolve/issue-10200.rs @@ -3,7 +3,7 @@ fn foo(_: usize) -> Foo { Foo(false) } fn main() { match Foo(true) { - foo(x) //~ ERROR expected tuple struct or tuple variant, found function `foo` + foo(x) //~ ERROR expected a pattern, found a function call => () } } diff --git a/tests/ui/resolve/issue-10200.stderr b/tests/ui/resolve/issue-10200.stderr index 7b218694b269e..172d016c6e6c4 100644 --- a/tests/ui/resolve/issue-10200.stderr +++ b/tests/ui/resolve/issue-10200.stderr @@ -1,4 +1,4 @@ -error[E0532]: expected tuple struct or tuple variant, found function `foo` +error[E0532]: expected a pattern, found a function call --> $DIR/issue-10200.rs:6:9 | LL | struct Foo(bool); @@ -6,6 +6,8 @@ LL | struct Foo(bool); ... LL | foo(x) | ^^^ help: a tuple struct with a similar name exists (notice the capitalization): `Foo` + | + = note: function calls are not allowed in patterns: error: aborting due to 1 previous error 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 fc431eb14127f..d40e98224355b 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 @@ -2,7 +2,9 @@ error: expected a pattern, found an expression --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:31 | LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; - | ^^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error[E0412]: cannot find type `T` in this scope --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:55 From 8b968764f15ea84b389b2af98297b9d30fb9952e Mon Sep 17 00:00:00 2001 From: Shriram Balaji Date: Thu, 12 Sep 2024 00:56:52 +0530 Subject: [PATCH 09/10] bootstrap: add `std_features` config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bootstrap: add std_features to config.example fix: use BTreeSet for std-features; add unit tests * fix formatting of string in front of std_features * rename `std_features` to `std-features` in config.toml fix: remove space before std-features in config.toml fix: remove explicit .into_iter conversion bootstrap: add details for rust.std-features in config.example.toml Co-authored-by: Onur Özkan fix: remove `Option` from `rust_std_features` fix: move default rust_std_features to config fix: make std_features CI rustc incompatible --- config.example.toml | 10 +++++++++ src/bootstrap/src/core/config/config.rs | 12 ++++++++++- src/bootstrap/src/core/config/tests.rs | 22 ++++++++++++++++++++ src/bootstrap/src/lib.rs | 27 ++++++++++++++----------- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/config.example.toml b/config.example.toml index 47ebb20d8fa82..4b591b949b36d 100644 --- a/config.example.toml +++ b/config.example.toml @@ -759,6 +759,16 @@ # Build compiler with the optimization enabled and -Zvalidate-mir, currently only for `std` #validate-mir-opts = 3 +# Configure `std` features used during bootstrap. +# Default features will be expanded in the following cases: +# - If `rust.llvm-libunwind` or `target.llvm-libunwind` is enabled: +# - "llvm-libunwind" will be added for in-tree LLVM builds. +# - "system-llvm-libunwind" will be added for system LLVM builds. +# - If `rust.backtrace` is enabled, "backtrace" will be added. +# - If `rust.profiler` or `target.profiler` is enabled, "profiler" will be added. +# - If building for a zkvm target, "compiler-builtins-mem" will be added. +#std-features = ["panic_unwind"] + # ============================================================================= # Options for specific targets # diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 07460b81412ac..7dc3b7b081aea 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -4,7 +4,7 @@ //! how the build runs. use std::cell::{Cell, RefCell}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::{self, Display}; use std::io::IsTerminal; use std::path::{Path, PathBuf, absolute}; @@ -287,6 +287,7 @@ pub struct Config { pub rust_profile_generate: Option, pub rust_lto: RustcLto, pub rust_validate_mir_opts: Option, + pub rust_std_features: BTreeSet, pub llvm_profile_use: Option, pub llvm_profile_generate: bool, pub llvm_libunwind_default: Option, @@ -1152,6 +1153,7 @@ define_config! { download_rustc: Option = "download-rustc", lto: Option = "lto", validate_mir_opts: Option = "validate-mir-opts", + std_features: Option> = "std-features", } } @@ -1645,6 +1647,7 @@ impl Config { let mut optimize = None; let mut omit_git_hash = None; let mut lld_enabled = None; + let mut std_features = None; let mut is_user_configured_rust_channel = false; @@ -1703,6 +1706,7 @@ impl Config { stack_protector, strip, lld_mode, + std_features: std_features_toml, } = rust; is_user_configured_rust_channel = channel.is_some(); @@ -1722,6 +1726,7 @@ impl Config { debuginfo_level_tools = debuginfo_level_tools_toml; debuginfo_level_tests = debuginfo_level_tests_toml; lld_enabled = lld_enabled_toml; + std_features = std_features_toml; optimize = optimize_toml; omit_git_hash = omit_git_hash_toml; @@ -2118,6 +2123,9 @@ impl Config { ); } + let default_std_features = BTreeSet::from([String::from("panic-unwind")]); + config.rust_std_features = std_features.unwrap_or(default_std_features); + let default = debug == Some(true); config.rust_debug_assertions = debug_assertions.unwrap_or(default); config.rust_debug_assertions_std = @@ -3016,6 +3024,7 @@ fn check_incompatible_options_for_ci_rustc( description, incremental, default_linker, + std_features, // Rest of the options can simply be ignored. debug: _, @@ -3077,6 +3086,7 @@ fn check_incompatible_options_for_ci_rustc( err!(current_rust_config.default_linker, default_linker); err!(current_rust_config.stack_protector, stack_protector); err!(current_rust_config.lto, lto); + err!(current_rust_config.std_features, std_features); warn!(current_rust_config.channel, channel); warn!(current_rust_config.description, description); diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index e38d4eac051d8..278becdcbc77d 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::env; use std::fs::{File, remove_file}; use std::io::Write; @@ -326,3 +327,24 @@ fn verbose_tests_default_value() { let config = Config::parse(Flags::parse(&["build".into(), "compiler".into(), "-v".into()])); assert_eq!(config.verbose_tests, true); } + +#[test] +fn parse_rust_std_features() { + let config = parse("rust.std-features = [\"panic-unwind\", \"backtrace\"]"); + let expected_features: BTreeSet = + ["panic-unwind", "backtrace"].into_iter().map(|s| s.to_string()).collect(); + assert_eq!(config.rust_std_features, expected_features); +} + +#[test] +fn parse_rust_std_features_empty() { + let config = parse("rust.std-features = []"); + let expected_features: BTreeSet = BTreeSet::new(); + assert_eq!(config.rust_std_features, expected_features); +} + +#[test] +#[should_panic] +fn parse_rust_std_features_invalid() { + parse("rust.std-features = \"backtrace\""); +} diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 75659f46431af..313b49346bc56 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -17,7 +17,7 @@ //! also check out the `src/bootstrap/README.md` file for more information. use std::cell::{Cell, RefCell}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Display; use std::fs::{self, File}; use std::path::{Path, PathBuf}; @@ -645,28 +645,31 @@ impl Build { &self.config.rust_info } - /// Gets the space-separated set of activated features for the standard - /// library. + /// Gets the space-separated set of activated features for the standard library. + /// This can be configured with the `std-features` key in config.toml. fn std_features(&self, target: TargetSelection) -> String { - let mut features = " panic-unwind".to_string(); + let mut features: BTreeSet<&str> = + self.config.rust_std_features.iter().map(|s| s.as_str()).collect(); match self.config.llvm_libunwind(target) { - LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"), - LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"), - LlvmLibunwind::No => {} - } + LlvmLibunwind::InTree => features.insert("llvm-libunwind"), + LlvmLibunwind::System => features.insert("system-llvm-libunwind"), + LlvmLibunwind::No => false, + }; + if self.config.backtrace { - features.push_str(" backtrace"); + features.insert("backtrace"); } if self.config.profiler_enabled(target) { - features.push_str(" profiler"); + features.insert("profiler"); } // Generate memcpy, etc. FIXME: Remove this once compiler-builtins // automatically detects this target. if target.contains("zkvm") { - features.push_str(" compiler-builtins-mem"); + features.insert("compiler-builtins-mem"); } - features + + features.into_iter().collect::>().join(" ") } /// Gets the space-separated set of activated features for the compiler. From 002afd1ae9c646d4120cc8e078bb329de8b3ecac Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sat, 5 Oct 2024 22:01:02 -0700 Subject: [PATCH 10/10] Fix typo in primitive_docs.rs --- library/core/src/primitive_docs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 962da6643ddb1..c25501f12001a 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1251,7 +1251,7 @@ mod prim_f16 {} /// - **Unchanged NaN propagation**: The quiet bit and payload are copied from any input operand /// that is a NaN. If the inputs and outputs do not have the same size (i.e., for `as` casts), the /// same rules as for "quieting NaN propagation" apply, with one caveat: if the output is smaller -/// than the input, droppig the low-order bits may result in a payload of 0; a payload of 0 is not +/// than the input, dropping the low-order bits may result in a payload of 0; a payload of 0 is not /// possible with a signaling NaN (the all-0 significand encodes an infinity) so unchanged NaN /// propagation cannot occur with some inputs. /// - **Target-specific NaN**: The quiet bit is set and the payload is picked from a target-specific