diff options
Diffstat (limited to 'compiler')
58 files changed, 821 insertions, 492 deletions
diff --git a/compiler/rustc_ast_ir/src/visit.rs b/compiler/rustc_ast_ir/src/visit.rs index dec9f7a47d0..f6d6bf3a3e3 100644 --- a/compiler/rustc_ast_ir/src/visit.rs +++ b/compiler/rustc_ast_ir/src/visit.rs @@ -14,7 +14,7 @@ impl VisitorResult for () { type Residual = !; #[cfg(not(feature = "nightly"))] - type Residual = core::ops::Infallible; + type Residual = core::convert::Infallible; fn output() -> Self {} fn from_residual(_: Self::Residual) -> Self {} diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 99e79f65fb4..eace5ce8208 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index b38a2915a43..9ae5c9b3cec 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_lexer = { path = "../rustc_lexer" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 714f46270f9..bafc62c7318 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start either = "1.5.0" -itertools = "0.11" +itertools = "0.12" polonius-engine = "0.13.0" rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8d38b86fa34..412d50f493e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2000,7 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } - &Rvalue::NullaryOp(NullOp::DebugAssertions, _) => {} + &Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {} Rvalue::ShallowInitBox(operand, ty) => { self.check_operand(operand, location); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 1ce920f3bdb..2415c2c90b2 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -779,7 +779,7 @@ fn codegen_stmt<'tcx>( NullOp::OffsetOf(fields) => { layout.offset_of_subfield(fx, fields.iter()).bytes() } - NullOp::DebugAssertions => { + NullOp::UbCheck(_) => { let val = fx.tcx.sess.opts.debug_assertions; let val = CValue::by_val( fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 3948a49ee2a..3fda59e8b52 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -9,7 +9,7 @@ test = false [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -itertools = "0.11" +itertools = "0.12" libc = "0.2" measureme = "11" object = { version = "0.32.0", default-features = false, features = ["std", "read"] } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 81ca42e1ad8..7851b9e8e03 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" ar_archive_writer = "0.1.5" bitflags = "2.4.1" cc = "1.0.90" -itertools = "0.11" +itertools = "0.12" jobserver = "0.1.28" pathdiff = "0.2.0" regex = "1.4" diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 9ae82d4845e..8c6b9faf39d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -685,7 +685,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes(); bx.cx().const_usize(val) } - mir::NullOp::DebugAssertions => { + mir::NullOp::UbCheck(_) => { + // In codegen, we want to check for language UB and library UB let val = bx.tcx().sess.opts.debug_assertions; bx.cx().const_bool(val) } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index d4c96f4573d..54bac70da38 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -258,10 +258,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = layout.offset_of_subfield(self, fields.iter()).bytes(); Scalar::from_target_usize(val, self) } - mir::NullOp::DebugAssertions => { - // The checks hidden behind this are always better done by the interpreter - // itself, because it knows the runtime state better. - Scalar::from_bool(false) + mir::NullOp::UbCheck(kind) => { + // We want to enable checks for library UB, because the interpreter doesn't + // know about those on its own. + // But we want to disable checks for language UB, because the interpreter + // has its own better checks for that. + let should_check = match kind { + mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions, + mir::UbKind::LanguageUb => false, + }; + Scalar::from_bool(should_check) } }; self.write_scalar(val, &dest)?; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ff1cb43db86..c568f9acfd3 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -17,8 +17,8 @@ use rustc_middle::mir::interpret::{ ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance, ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*, }; -use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{ Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange, @@ -783,7 +783,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } #[inline] - fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { + fn visit_box( + &mut self, + _box_ty: Ty<'tcx>, + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx> { self.check_safe_pointer(op, PointerKind::Box)?; Ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index b200ecbf73a..0e824f3f592 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -3,7 +3,7 @@ use rustc_index::IndexVec; use rustc_middle::mir::interpret::InterpResult; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_target::abi::FieldIdx; use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; @@ -47,10 +47,10 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { Ok(()) } /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. - /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the - /// pointee type is the actual `T`. + /// The type of `v` will be a raw pointer to `T`, but this is a field of `Box<T>` and the + /// pointee type is the actual `T`. `box_ty` provides the full type of the `Box` itself. #[inline(always)] - fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> { + fn visit_box(&mut self, _box_ty: Ty<'tcx>, _v: &Self::V) -> InterpResult<'tcx> { Ok(()) } @@ -144,7 +144,7 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { assert_eq!(nonnull_ptr.layout().fields.count(), 1); let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr // ... whose only field finally is a raw ptr we can dereference. - self.visit_box(&raw_ptr)?; + self.visit_box(ty, &raw_ptr)?; // The second `Box` field is the allocator, which we recursively check for validity // like in regular structs. diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 7ec78e7b9cf..53308cd7f90 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), _, ) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 74ba2f6039e..f26c8f8592d 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -1157,7 +1157,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::AddressOf(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::DebugAssertions, _) + | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _) | Rvalue::Discriminant(_) => {} } self.super_rvalue(rvalue, location); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index e06d1d245b2..2802f44cda7 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -676,6 +676,7 @@ fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dy metadata_loader, &mut v, &sess.opts.unstable_opts.ls, + sess.cfg_version, ) .unwrap(); safe_println!("{}", String::from_utf8(v).unwrap()); diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 648b569a217..04ca7f123d3 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 95c51cc0486..bf5838143d9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -127,7 +127,8 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::variant_count | sym::is_val_statically_known | sym::ptr_mask - | sym::debug_assertions + | sym::check_language_ub + | sym::check_library_ub | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -584,7 +585,7 @@ pub fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize) } - sym::debug_assertions => (0, 1, Vec::new(), tcx.types.bool), + sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool), sym::simd_eq | sym::simd_ne @@ -657,6 +658,10 @@ pub fn check_intrinsic_type( sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)), sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)), + sym::retag_box_to_raw => { + (2, 0, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_mut_ptr(tcx, param(0))) + } + other => { tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other }); return; diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 0a5fa37ed04..9e7f0776b60 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 1ff961a9089..c17af666eb9 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -19,7 +19,7 @@ use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::ArgKind; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use std::cmp; +use rustc_type_ir::ClosureKind; use std::iter; use std::ops::ControlFlow; @@ -437,10 +437,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Some(found_kind) = found_kind { - expected_kind = Some( - expected_kind - .map_or_else(|| found_kind, |current| cmp::min(current, found_kind)), - ); + // always use the closure kind that is more permissive. + match (expected_kind, found_kind) { + (None, _) => expected_kind = Some(found_kind), + (Some(ClosureKind::FnMut), ClosureKind::Fn) => { + expected_kind = Some(ClosureKind::Fn) + } + (Some(ClosureKind::FnOnce), ClosureKind::Fn | ClosureKind::FnMut) => { + expected_kind = Some(found_kind) + } + _ => {} + } } } } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0d50200133c..23bd2dac57e 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -101,10 +101,11 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( threads: usize, f: F, ) -> R { - use rustc_data_structures::{jobserver, sync::FromDyn}; + use rustc_data_structures::{defer, jobserver, sync::FromDyn}; use rustc_middle::ty::tls; use rustc_query_impl::QueryCtxt; - use rustc_query_system::query::{deadlock, QueryContext}; + use rustc_query_system::query::{break_query_cycles, QueryContext}; + use std::process; let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); @@ -128,7 +129,19 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( let query_map = FromDyn::from(tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs())); let registry = rayon_core::Registry::current(); - thread::spawn(move || deadlock(query_map.into_inner(), ®istry)); + thread::Builder::new() + .name("rustc query cycle handler".to_string()) + .spawn(move || { + let on_panic = defer(|| { + eprintln!("query cycle handler thread panicked, aborting process"); + // We need to abort here as we failed to resolve the deadlock, + // otherwise the compiler could just hang, + process::abort(); + }); + break_query_cycles(query_map.into_inner(), ®istry); + on_panic.disable(); + }) + .unwrap(); }); if let Some(size) = get_stack_size() { builder = builder.stack_size(size); diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 2271321b8bf..fa1133e7780 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -23,7 +23,6 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_type_ir = { path = "../rustc_type_ir" } -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" unicode-security = "0.1.0" # tidy-alphabetical-end diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index a4fd5a7c45f..7c4d92d3ce0 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -2,8 +2,6 @@ use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, Path, QPath, TyKind}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind}; -use smallvec::{smallvec, SmallVec}; - use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext}; @@ -85,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { if let Some(def_id) = oexpn.macro_def_id && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind && def_id.krate != LOCAL_CRATE - && std::env::var_os("CARGO").is_some() + && rustc_session::utils::was_invoked_from_cargo() { Some(NonLocalDefinitionsCargoUpdateNote { macro_kind: macro_kind.descr(), @@ -114,25 +112,25 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // is using local items and so we don't lint on it. // We also ignore anon-const in item by including the anon-const - // parent as well; and since it's quite uncommon, we use smallvec - // to avoid unnecessary heap allocations. - let local_parents: SmallVec<[DefId; 1]> = if parent_def_kind == DefKind::Const + // parent as well. + let parent_parent = if parent_def_kind == DefKind::Const && parent_opt_item_name == Some(kw::Underscore) { - smallvec![parent, cx.tcx.parent(parent)] + Some(cx.tcx.parent(parent)) } else { - smallvec![parent] + None }; let self_ty_has_local_parent = match impl_.self_ty.kind { TyKind::Path(QPath::Resolved(_, ty_path)) => { - path_has_local_parent(ty_path, cx, &*local_parents) + path_has_local_parent(ty_path, cx, parent, parent_parent) } TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { path_has_local_parent( principle_poly_trait_ref.trait_ref.path, cx, - &*local_parents, + parent, + parent_parent, ) } TyKind::TraitObject([], _, _) @@ -154,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { let of_trait_has_local_parent = impl_ .of_trait - .map(|of_trait| path_has_local_parent(of_trait.path, cx, &*local_parents)) + .map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent)) .unwrap_or(false); // If none of them have a local parent (LOGICAL NOR) this means that @@ -218,6 +216,16 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { /// std::convert::PartialEq<Foo<Bar>> /// ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` -fn path_has_local_parent(path: &Path<'_>, cx: &LateContext<'_>, local_parents: &[DefId]) -> bool { - path.res.opt_def_id().is_some_and(|did| local_parents.contains(&cx.tcx.parent(did))) +fn path_has_local_parent( + path: &Path<'_>, + cx: &LateContext<'_>, + impl_parent: DefId, + impl_parent_parent: Option<DefId>, +) -> bool { + path.res.opt_def_id().is_some_and(|did| { + did.is_local() && { + let res_parent = cx.tcx.parent(did); + res_parent == impl_parent || Some(res_parent) == impl_parent_parent + } + }) } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 94f8bbe2437..f2560b35aa2 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -67,6 +67,7 @@ declare_lint_pass! { MISSING_FRAGMENT_SPECIFIER, MUST_NOT_SUSPEND, NAMED_ARGUMENTS_USED_POSITIONALLY, + NON_CONTIGUOUS_RANGE_ENDPOINTS, NON_EXHAUSTIVE_OMITTED_PATTERNS, ORDER_DEPENDENT_TRAIT_OBJECTS, OVERLAPPING_RANGE_ENDPOINTS, @@ -813,6 +814,36 @@ declare_lint! { } declare_lint! { + /// The `non_contiguous_range_endpoints` lint detects likely off-by-one errors when using + /// exclusive [range patterns]. + /// + /// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns + /// + /// ### Example + /// + /// ```rust + /// # #![feature(exclusive_range_pattern)] + /// let x = 123u32; + /// match x { + /// 0..100 => { println!("small"); } + /// 101..1000 => { println!("large"); } + /// _ => { println!("larger"); } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is likely a mistake to have range patterns in a match expression that miss out a single + /// number. Check that the beginning and end values are what you expect, and keep in mind that + /// with `..=` the right bound is inclusive, and with `..` it is exclusive. + pub NON_CONTIGUOUS_RANGE_ENDPOINTS, + Warn, + "detects off-by-one errors with exclusive range patterns" +} + +declare_lint! { /// The `bindings_with_variant_name` lint detects pattern bindings with /// the same name as one of the matched variants. /// diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index dcccace12b0..10bbebb3871 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -569,31 +569,47 @@ impl<'a> CrateLocator<'a> { debug!("skipping empty file"); continue; } - let (hash, metadata) = - match get_metadata_section(self.target, flavor, &lib, self.metadata_loader) { - Ok(blob) => { - if let Some(h) = self.crate_matches(&blob, &lib) { - (h, blob) - } else { - info!("metadata mismatch"); - continue; - } - } - Err(MetadataError::LoadFailure(err)) => { - info!("no metadata found: {}", err); - // The file was present and created by the same compiler version, but we - // couldn't load it for some reason. Give a hard error instead of silently - // ignoring it, but only if we would have given an error anyway. - self.crate_rejections - .via_invalid - .push(CrateMismatch { path: lib, got: err }); - continue; - } - Err(err @ MetadataError::NotPresent(_)) => { - info!("no metadata found: {}", err); + let (hash, metadata) = match get_metadata_section( + self.target, + flavor, + &lib, + self.metadata_loader, + self.cfg_version, + ) { + Ok(blob) => { + if let Some(h) = self.crate_matches(&blob, &lib) { + (h, blob) + } else { + info!("metadata mismatch"); continue; } - }; + } + Err(MetadataError::VersionMismatch { expected_version, found_version }) => { + // The file was present and created by the same compiler version, but we + // couldn't load it for some reason. Give a hard error instead of silently + // ignoring it, but only if we would have given an error anyway. + info!( + "Rejecting via version: expected {} got {}", + expected_version, found_version + ); + self.crate_rejections + .via_version + .push(CrateMismatch { path: lib, got: found_version }); + continue; + } + Err(MetadataError::LoadFailure(err)) => { + info!("no metadata found: {}", err); + // The file was present and created by the same compiler version, but we + // couldn't load it for some reason. Give a hard error instead of silently + // ignoring it, but only if we would have given an error anyway. + self.crate_rejections.via_invalid.push(CrateMismatch { path: lib, got: err }); + continue; + } + Err(err @ MetadataError::NotPresent(_)) => { + info!("no metadata found: {}", err); + continue; + } + }; // If we see multiple hashes, emit an error about duplicate candidates. if slot.as_ref().is_some_and(|s| s.0 != hash) { if let Some(candidates) = err_data { @@ -648,16 +664,6 @@ impl<'a> CrateLocator<'a> { } fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option<Svh> { - let rustc_version = rustc_version(self.cfg_version); - let found_version = metadata.get_rustc_version(); - if found_version != rustc_version { - info!("Rejecting via version: expected {} got {}", rustc_version, found_version); - self.crate_rejections - .via_version - .push(CrateMismatch { path: libpath.to_path_buf(), got: found_version }); - return None; - } - let header = metadata.get_header(); if header.is_proc_macro_crate != self.is_proc_macro { info!( @@ -770,6 +776,7 @@ fn get_metadata_section<'p>( flavor: CrateFlavor, filename: &'p Path, loader: &dyn MetadataLoader, + cfg_version: &'static str, ) -> Result<MetadataBlob, MetadataError<'p>> { if !filename.exists() { return Err(MetadataError::NotPresent(filename)); @@ -847,13 +854,18 @@ fn get_metadata_section<'p>( } }; let blob = MetadataBlob(raw_bytes); - if blob.is_compatible() { - Ok(blob) - } else { - Err(MetadataError::LoadFailure(format!( + match blob.check_compatibility(cfg_version) { + Ok(()) => Ok(blob), + Err(None) => Err(MetadataError::LoadFailure(format!( "invalid metadata version found: {}", filename.display() - ))) + ))), + Err(Some(found_version)) => { + return Err(MetadataError::VersionMismatch { + expected_version: rustc_version(cfg_version), + found_version, + }); + } } } @@ -864,9 +876,10 @@ pub fn list_file_metadata( metadata_loader: &dyn MetadataLoader, out: &mut dyn Write, ls_kinds: &[String], + cfg_version: &'static str, ) -> IoResult<()> { let flavor = get_flavor_from_path(path); - match get_metadata_section(target, flavor, path, metadata_loader) { + match get_metadata_section(target, flavor, path, metadata_loader, cfg_version) { Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds), Err(msg) => write!(out, "{msg}\n"), } @@ -932,6 +945,8 @@ enum MetadataError<'a> { NotPresent(&'a Path), /// The file was present and invalid. LoadFailure(String), + /// The file was present, but compiled with a different rustc version. + VersionMismatch { expected_version: String, found_version: String }, } impl fmt::Display for MetadataError<'_> { @@ -941,6 +956,12 @@ impl fmt::Display for MetadataError<'_> { f.write_str(&format!("no such file: '{}'", filename.display())) } MetadataError::LoadFailure(msg) => f.write_str(msg), + MetadataError::VersionMismatch { expected_version, found_version } => { + f.write_str(&format!( + "rustc version mismatch. expected {}, found {}", + expected_version, found_version, + )) + } } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index da384007c22..0467cf2969f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -684,13 +684,25 @@ impl<'a, 'tcx, I: Idx, T> Decodable<DecodeContext<'a, 'tcx>> for LazyTable<I, T> implement_ty_decoder!(DecodeContext<'a, 'tcx>); impl MetadataBlob { - pub(crate) fn is_compatible(&self) -> bool { - self.blob().starts_with(METADATA_HEADER) - } + pub(crate) fn check_compatibility( + &self, + cfg_version: &'static str, + ) -> Result<(), Option<String>> { + if !self.blob().starts_with(METADATA_HEADER) { + if self.blob().starts_with(b"rust") { + return Err(Some("<unknown rustc version>".to_owned())); + } + return Err(None); + } - pub(crate) fn get_rustc_version(&self) -> String { - LazyValue::<String>::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap()) - .decode(self) + let found_version = + LazyValue::<String>::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap()) + .decode(self); + if rustc_version(cfg_version) != found_version { + return Err(Some(found_version)); + } + + Ok(()) } fn root_pos(&self) -> NonZero<usize> { diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 5a24a7ab0bd..96c58ef411b 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -derive_more = "0.99.17" either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e058302af31..18069547a7e 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -915,7 +915,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { NullOp::SizeOf => write!(fmt, "SizeOf({t})"), NullOp::AlignOf => write!(fmt, "AlignOf({t})"), NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), - NullOp::DebugAssertions => write!(fmt, "cfg!(debug_assertions)"), + NullOp::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"), } } ThreadLocalRef(did) => ty::tls::with(|tcx| { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index f188923f876..0ab797134c0 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1366,8 +1366,16 @@ pub enum NullOp<'tcx> { AlignOf, /// Returns the offset of a field OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), - /// cfg!(debug_assertions), but expanded in codegen - DebugAssertions, + /// Returns whether we want to check for library UB or language UB at monomorphization time. + /// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in + /// const-eval/Miri, because the interpreter has its own better checks for language UB. + UbCheck(UbKind), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] +pub enum UbKind { + LanguageUb, + LibraryUb, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 5bc151de659..0c29fe57d4f 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize } - Rvalue::NullaryOp(NullOp::DebugAssertions, _) => tcx.types.bool, + Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool, Rvalue::Aggregate(ref ak, ref ops) => match **ak { AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), AggregateKind::Tuple => { diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 9fc8d418f5b..814c3629b08 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -30,7 +30,7 @@ pub struct Instance<'tcx> { pub args: GenericArgsRef<'tcx>, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] pub enum InstanceDef<'tcx> { /// A user-defined callable item. diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 427c0f04bd1..39b5d3b6ea7 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2008,13 +2008,10 @@ impl<'tcx> Ty<'tcx> { // Single-argument Box is always global. (for "minicore" tests) return true; }; - if let Some(alloc_adt) = alloc.expect_ty().ty_adt_def() { + alloc.expect_ty().ty_adt_def().is_some_and(|alloc_adt| { let global_alloc = tcx.require_lang_item(LangItem::GlobalAlloc, None); alloc_adt.did() == global_alloc - } else { - // Allocator is not an ADT... - false - } + }) } _ => false, } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index d71f7121322..6618e4f5a00 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start either = "1" -itertools = "0.11" +itertools = "0.12" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index db48ecd702b..0f900e6a557 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -433,7 +433,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::DebugAssertions, + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_), _, ) => {} } diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 9cc083edb44..bd0a54ef363 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start either = "1" -itertools = "0.11" +itertools = "0.12" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index a080e2423d4..a3a2108787a 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -488,7 +488,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { NullOp::OffsetOf(fields) => { layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() } - NullOp::DebugAssertions => return None, + NullOp::UbCheck(_) => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::try_from_uint(val, usize_layout)?; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 27477769cef..4bca437ea6f 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -639,7 +639,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { NullOp::OffsetOf(fields) => { op_layout.offset_of_subfield(self, fields.iter()).bytes() } - NullOp::DebugAssertions => return None, + NullOp::UbCheck(_) => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index f317c025e96..1bab240ef50 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -20,13 +20,30 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::unreachable => { terminator.kind = TerminatorKind::Unreachable; } - sym::debug_assertions => { + sym::check_language_ub => { let target = target.unwrap(); block.statements.push(Statement { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::NullaryOp(NullOp::DebugAssertions, tcx.types.bool), + Rvalue::NullaryOp( + NullOp::UbCheck(UbKind::LanguageUb), + tcx.types.bool, + ), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; + } + sym::check_library_ub => { + let target = target.unwrap(); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::NullaryOp( + NullOp::UbCheck(UbKind::LibraryUb), + tcx.types.bool, + ), ))), }); terminator.kind = TerminatorKind::Goto { target }; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 2e11da4d585..9fe8c34a8bf 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::SizeOf => {} NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} - NullOp::DebugAssertions => {} + NullOp::UbCheck(_) => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 2f7ac7d3a12..de088b9364b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -667,7 +667,7 @@ impl<'a> Parser<'a> { { err.note("you may be trying to write a c-string literal"); err.note("c-string literals require Rust 2021 or later"); - HelpUseLatestEdition::new().add_to_diagnostic(&mut err); + err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new()); } // `pub` may be used for an item or `pub(crate)` diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 80e6c104bd4..6abfa08c530 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl index 827928f97d7..41a1d958f10 100644 --- a/compiler/rustc_pattern_analysis/messages.ftl +++ b/compiler/rustc_pattern_analysis/messages.ftl @@ -1,3 +1,11 @@ +pattern_analysis_excluside_range_missing_gap = multiple ranges are one apart + .label = this range doesn't match `{$gap}` because `..` is an exclusive range + .suggestion = use an inclusive range instead + +pattern_analysis_excluside_range_missing_max = exclusive range missing `{$max}` + .label = this range doesn't match `{$max}` because `..` is an exclusive range + .suggestion = use an inclusive range instead + pattern_analysis_non_exhaustive_omitted_pattern = some variants are not matched explicitly .help = ensure that all variants are matched explicitly by adding the suggested match arms .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 767227619b0..69e294e47a5 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -192,7 +192,7 @@ impl fmt::Display for RangeEnd { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum MaybeInfiniteInt { NegInfinity, - /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`. + /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite_{int,uint}`. #[non_exhaustive] Finite(u128), /// The integer after `u128::MAX`. We need it to represent `x..=u128::MAX` as an exclusive range. @@ -229,25 +229,22 @@ impl MaybeInfiniteInt { } /// Note: this will not turn a finite value into an infinite one or vice-versa. - pub fn minus_one(self) -> Self { + pub fn minus_one(self) -> Option<Self> { match self { - Finite(n) => match n.checked_sub(1) { - Some(m) => Finite(m), - None => panic!("Called `MaybeInfiniteInt::minus_one` on 0"), - }, - JustAfterMax => Finite(u128::MAX), - x => x, + Finite(n) => n.checked_sub(1).map(Finite), + JustAfterMax => Some(Finite(u128::MAX)), + x => Some(x), } } /// Note: this will not turn a finite value into an infinite one or vice-versa. - pub fn plus_one(self) -> Self { + pub fn plus_one(self) -> Option<Self> { match self { Finite(n) => match n.checked_add(1) { - Some(m) => Finite(m), - None => JustAfterMax, + Some(m) => Some(Finite(m)), + None => Some(JustAfterMax), }, - JustAfterMax => panic!("Called `MaybeInfiniteInt::plus_one` on u128::MAX+1"), - x => x, + JustAfterMax => None, + x => Some(x), } } } @@ -268,18 +265,24 @@ impl IntRange { pub fn is_singleton(&self) -> bool { // Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite // to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`. - self.lo.plus_one() == self.hi + self.lo.plus_one() == Some(self.hi) } + /// Construct a singleton range. + /// `x` must be a `Finite(_)` value. #[inline] pub fn from_singleton(x: MaybeInfiniteInt) -> IntRange { - IntRange { lo: x, hi: x.plus_one() } + // `unwrap()` is ok on a finite value + IntRange { lo: x, hi: x.plus_one().unwrap() } } + /// Construct a range with these boundaries. + /// `lo` must not be `PosInfinity` or `JustAfterMax`. `hi` must not be `NegInfinity`. + /// If `end` is `Included`, `hi` must also not be `JustAfterMax`. #[inline] pub fn from_range(lo: MaybeInfiniteInt, mut hi: MaybeInfiniteInt, end: RangeEnd) -> IntRange { if end == RangeEnd::Included { - hi = hi.plus_one(); + hi = hi.plus_one().unwrap(); } if lo >= hi { // This should have been caught earlier by E0030. diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index d0f56f0268d..e471b8abd73 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -77,6 +77,57 @@ impl<'tcx> AddToDiagnostic for Overlap<'tcx> { } #[derive(LintDiagnostic)] +#[diag(pattern_analysis_excluside_range_missing_max)] +pub struct ExclusiveRangeMissingMax<'tcx> { + #[label] + #[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")] + /// This is an exclusive range that looks like `lo..max` (i.e. doesn't match `max`). + pub first_range: Span, + /// Suggest `lo..=max` instead. + pub suggestion: String, + pub max: Pat<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(pattern_analysis_excluside_range_missing_gap)] +pub struct ExclusiveRangeMissingGap<'tcx> { + #[label] + #[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")] + /// This is an exclusive range that looks like `lo..gap` (i.e. doesn't match `gap`). + pub first_range: Span, + pub gap: Pat<'tcx>, + /// Suggest `lo..=gap` instead. + pub suggestion: String, + #[subdiagnostic] + /// All these ranges skipped over `gap` which we think is probably a mistake. + pub gap_with: Vec<GappedRange<'tcx>>, +} + +pub struct GappedRange<'tcx> { + pub span: Span, + pub gap: Pat<'tcx>, + pub first_range: Pat<'tcx>, +} + +impl<'tcx> AddToDiagnostic for GappedRange<'tcx> { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + _: F, + ) { + let GappedRange { span, gap, first_range } = self; + + // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` + // does not support `#[subdiagnostic(eager)]`... + let message = format!( + "this could appear to continue range `{first_range}`, but `{gap}` isn't matched by \ + either of them" + ); + diag.span_label(span, message); + } +} + +#[derive(LintDiagnostic)] #[diag(pattern_analysis_non_exhaustive_omitted_pattern)] #[help] #[note] diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 4b0955699fc..f632eaf7ea4 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -70,14 +70,8 @@ use rustc_middle::ty::Ty; use rustc_span::ErrorGuaranteed; use crate::constructor::{Constructor, ConstructorSet, IntRange}; -#[cfg(feature = "rustc")] -use crate::lints::lint_nonexhaustive_missing_variants; use crate::pat::DeconstructedPat; use crate::pat_column::PatternColumn; -#[cfg(feature = "rustc")] -use crate::rustc::RustcMatchCheckCtxt; -#[cfg(feature = "rustc")] -use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; pub trait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {} @@ -145,6 +139,18 @@ pub trait TypeCx: Sized + fmt::Debug { /// The maximum pattern complexity limit was reached. fn complexity_exceeded(&self) -> Result<(), Self::Error>; + + /// Lint that there is a gap `gap` between `pat` and all of `gapped_with` such that the gap is + /// not matched by another range. If `gapped_with` is empty, then `gap` is `T::MAX`. We only + /// detect singleton gaps. + /// The default implementation does nothing. + fn lint_non_contiguous_range_endpoints( + &self, + _pat: &DeconstructedPat<Self>, + _gap: IntRange, + _gapped_with: &[&DeconstructedPat<Self>], + ) { + } } /// The arm of a match expression. @@ -167,11 +173,14 @@ impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {} /// useful, and runs some lints. #[cfg(feature = "rustc")] pub fn analyze_match<'p, 'tcx>( - tycx: &RustcMatchCheckCtxt<'p, 'tcx>, + tycx: &rustc::RustcMatchCheckCtxt<'p, 'tcx>, arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, pattern_complexity_limit: Option<usize>, ) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { + use lints::lint_nonexhaustive_missing_variants; + use usefulness::{compute_match_usefulness, ValidityConstraint}; + let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee); let report = diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 5f5bfa7154a..0085f0ab656 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -8,7 +8,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{self, Const}; -use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; +use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef}; use rustc_session::lint; @@ -718,12 +718,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { let value = mir::Const::from_ty_const(c, cx.tcx); lo = PatRangeBoundary::Finite(value); } - let hi = if matches!(range.hi, Finite(0)) { + let hi = if let Some(hi) = range.hi.minus_one() { + hi + } else { // The range encodes `..ty::MIN`, so we can't convert it to an inclusive range. end = rustc_hir::RangeEnd::Excluded; range.hi - } else { - range.hi.minus_one() }; let hi = cx.hoist_pat_range_bdy(hi, ty); PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() })) @@ -900,6 +900,70 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { let span = self.whole_match_span.unwrap_or(self.scrut_span); Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit")) } + + fn lint_non_contiguous_range_endpoints( + &self, + pat: &crate::pat::DeconstructedPat<Self>, + gap: IntRange, + gapped_with: &[&crate::pat::DeconstructedPat<Self>], + ) { + let Some(&thir_pat) = pat.data() else { return }; + let thir::PatKind::Range(range) = &thir_pat.kind else { return }; + // Only lint when the left range is an exclusive range. + if range.end != rustc_hir::RangeEnd::Excluded { + return; + } + // `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with + // `gap+1`. + let suggested_range: thir::Pat<'_> = { + // Suggest `lo..=gap` instead. + let mut suggested_range = thir_pat.clone(); + let thir::PatKind::Range(range) = &mut suggested_range.kind else { unreachable!() }; + range.end = rustc_hir::RangeEnd::Included; + suggested_range + }; + let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty()); + if gapped_with.is_empty() { + // If `gapped_with` is empty, `gap == T::MAX`. + self.tcx.emit_node_span_lint( + lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS, + self.match_lint_level, + thir_pat.span, + errors::ExclusiveRangeMissingMax { + // Point at this range. + first_range: thir_pat.span, + // That's the gap that isn't covered. + max: gap_as_pat.clone(), + // Suggest `lo..=max` instead. + suggestion: suggested_range.to_string(), + }, + ); + } else { + self.tcx.emit_node_span_lint( + lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS, + self.match_lint_level, + thir_pat.span, + errors::ExclusiveRangeMissingGap { + // Point at this range. + first_range: thir_pat.span, + // That's the gap that isn't covered. + gap: gap_as_pat.clone(), + // Suggest `lo..=gap` instead. + suggestion: suggested_range.to_string(), + // All these ranges skipped over `gap` which we think is probably a + // mistake. + gap_with: gapped_with + .iter() + .map(|pat| errors::GappedRange { + span: pat.data().unwrap().span, + gap: gap_as_pat.clone(), + first_range: thir_pat.clone(), + }) + .collect(), + }, + ); + } + } } /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index c518844cc5e..a067bf1f0c2 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1489,7 +1489,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> { /// We can however get false negatives because exhaustiveness does not explore all cases. See the /// section on relevancy at the top of the file. fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( - mcx: &mut UsefulnessCtxt<'_, Cx>, + cx: &Cx, overlap_range: IntRange, matrix: &Matrix<'p, Cx>, specialized_matrix: &Matrix<'p, Cx>, @@ -1522,11 +1522,11 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( .map(|&(_, pat)| pat) .collect(); if !overlaps_with.is_empty() { - mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); + cx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); } } suffixes.push((child_row_id, pat)) - } else if this_range.hi == overlap.plus_one() { + } else if Some(this_range.hi) == overlap.plus_one() { // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any // ranges that look like `overlap..=hi`. if !suffixes.is_empty() { @@ -1538,7 +1538,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( .map(|&(_, pat)| pat) .collect(); if !overlaps_with.is_empty() { - mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); + cx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); } } prefixes.push((child_row_id, pat)) @@ -1546,6 +1546,33 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( } } +/// Collect ranges that have a singleton gap between them. +fn collect_non_contiguous_range_endpoints<'p, Cx: TypeCx>( + cx: &Cx, + gap_range: &IntRange, + matrix: &Matrix<'p, Cx>, +) { + let gap = gap_range.lo; + // Ranges that look like `lo..gap`. + let mut onebefore: SmallVec<[_; 1]> = Default::default(); + // Ranges that start on `gap+1` or singletons `gap+1`. + let mut oneafter: SmallVec<[_; 1]> = Default::default(); + // Look through the column for ranges near the gap. + for pat in matrix.heads() { + let PatOrWild::Pat(pat) = pat else { continue }; + let Constructor::IntRange(this_range) = pat.ctor() else { continue }; + if gap == this_range.hi { + onebefore.push(pat) + } else if gap.plus_one() == Some(this_range.lo) { + oneafter.push(pat) + } + } + + for pat_before in onebefore { + cx.lint_non_contiguous_range_endpoints(pat_before, *gap_range, oneafter.as_slice()); + } +} + /// The core of the algorithm. /// /// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks @@ -1626,13 +1653,24 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( && spec_matrix.rows.len() >= 2 && spec_matrix.rows.iter().any(|row| !row.intersects.is_empty()) { - collect_overlapping_range_endpoints(mcx, overlap_range, matrix, &spec_matrix); + collect_overlapping_range_endpoints(mcx.tycx, overlap_range, matrix, &spec_matrix); } } matrix.unspecialize(spec_matrix); } + // Detect singleton gaps between ranges. + if missing_ctors.iter().any(|c| matches!(c, Constructor::IntRange(..))) { + for missing in &missing_ctors { + if let Constructor::IntRange(gap) = missing { + if gap.is_singleton() { + collect_non_contiguous_range_endpoints(mcx.tycx, gap, matrix); + } + } + } + } + // Record usefulness in the patterns. for row in matrix.rows() { if row.useful { diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 1a54a229357..248a741af90 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -17,10 +17,9 @@ use std::num::NonZero; use { parking_lot::{Condvar, Mutex}, rustc_data_structures::fx::FxHashSet, - rustc_data_structures::{defer, jobserver}, + rustc_data_structures::jobserver, rustc_span::DUMMY_SP, std::iter, - std::process, std::sync::Arc, }; @@ -514,12 +513,7 @@ fn remove_cycle( /// There may be multiple cycles involved in a deadlock, so this searches /// all active queries for cycles before finally resuming all the waiters at once. #[cfg(parallel_compiler)] -pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { - let on_panic = defer(|| { - eprintln!("deadlock handler panicked, aborting process"); - process::abort(); - }); - +pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) { let mut wakelist = Vec::new(); let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect(); @@ -539,19 +533,17 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here // only considers the true dependency and won't detect a cycle. if !found_cycle { - if query_map.len() == 0 { - panic!("deadlock detected without any query!") - } else { - panic!("deadlock detected! current query map:\n{:#?}", query_map); - } + panic!( + "deadlock detected as we're unable to find a query cycle to break\n\ + current query map:\n{:#?}", + query_map + ); } // FIXME: Ensure this won't cause a deadlock before we return for waiter in wakelist.into_iter() { waiter.notify(registry); } - - on_panic.disable(); } #[inline(never)] diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 0aefe553a34..01b9d458f1e 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -3,7 +3,7 @@ pub use self::plumbing::*; mod job; #[cfg(parallel_compiler)] -pub use self::job::deadlock; +pub use self::job::break_query_cycles; pub use self::job::{ print_query_stack, report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, }; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 003a9a59200..c0876adf905 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -251,13 +251,19 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { type T = stable_mir::mir::NullOp; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { use rustc_middle::mir::NullOp::*; + use rustc_middle::mir::UbKind; match self { SizeOf => stable_mir::mir::NullOp::SizeOf, AlignOf => stable_mir::mir::NullOp::AlignOf, OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( indices.iter().map(|idx| idx.stable(tables)).collect(), ), - DebugAssertions => stable_mir::mir::NullOp::DebugAssertions, + UbCheck(UbKind::LanguageUb) => { + stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb) + } + UbCheck(UbKind::LibraryUb) => { + stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb) + } } } } diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 99de91a068a..98ed985738a 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start indexmap = { version = "2.0.0" } +itoa = "1.0" md5 = { package = "md-5", version = "0.10.0" } rustc_arena = { path = "../rustc_arena" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9dadd471247..708349e85ae 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -518,6 +518,8 @@ symbols! { cfi, cfi_encoding, char, + check_language_ub, + check_library_ub, client, clippy, clobber_abi, @@ -998,6 +1000,11 @@ symbols! { is_val_statically_known, isa_attribute, isize, + isize_legacy_const_max, + isize_legacy_const_min, + isize_legacy_fn_max_value, + isize_legacy_fn_min_value, + isize_legacy_mod, issue, issue_5723_bootstrap, issue_tracker_base_url, @@ -1450,6 +1457,7 @@ symbols! { residual, result, resume, + retag_box_to_raw, return_position_impl_trait_in_trait, return_type_notation, rhs, @@ -1907,6 +1915,11 @@ symbols! { used_with_arg, using, usize, + usize_legacy_const_max, + usize_legacy_const_min, + usize_legacy_fn_max_value, + usize_legacy_fn_min_value, + usize_legacy_mod, va_arg, va_copy, va_end, @@ -2327,13 +2340,15 @@ pub mod sym { /// /// The first few non-negative integers each have a static symbol and therefore /// are fast. - pub fn integer<N: TryInto<usize> + Copy + ToString>(n: N) -> Symbol { + pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol { if let Result::Ok(idx) = n.try_into() { if idx < 10 { return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32); } } - Symbol::intern(&n.to_string()) + let mut buffer = itoa::Buffer::new(); + let printed = buffer.format(n); + Symbol::intern(printed) } } diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 3a58d41fcd0..811eb4c9810 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -itertools = "0.11.0" +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index b09a803e856..f89daf033f6 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -2,7 +2,9 @@ use crate::infer::InferCtxt; use crate::traits; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, +}; use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_span::{Span, DUMMY_SP}; @@ -535,305 +537,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// Pushes all the predicates needed to validate that `ty` is WF into `out`. #[instrument(level = "debug", skip(self))] fn compute(&mut self, arg: GenericArg<'tcx>) { - let mut walker = arg.walk(); - let param_env = self.param_env; - let depth = self.recursion_depth; - while let Some(arg) = walker.next() { - debug!(?arg, ?self.out); - let ty = match arg.unpack() { - GenericArgKind::Type(ty) => ty, - - // No WF constraints for lifetimes being present, any outlives - // obligations are handled by the parent (e.g. `ty::Ref`). - GenericArgKind::Lifetime(_) => continue, - - GenericArgKind::Const(ct) => { - match ct.kind() { - ty::ConstKind::Unevaluated(uv) => { - if !ct.has_escaping_bound_vars() { - let obligations = self.nominal_obligations(uv.def, uv.args); - self.out.extend(obligations); - - let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstEvaluatable(ct), - )); - let cause = self.cause(traits::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - self.param_env, - predicate, - )); - } - } - ty::ConstKind::Infer(_) => { - let cause = self.cause(traits::WellFormed(None)); - - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::WellFormed(ct.into()), - )), - )); - } - ty::ConstKind::Expr(_) => { - // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the - // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary - // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` - // which means that the `DefId` would have been typeck'd elsewhere. However in - // the future we may allow directly lowering to `ConstKind::Expr` in which case - // we would not be proving bounds we should. - - let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstEvaluatable(ct), - )); - let cause = self.cause(traits::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - self.param_env, - predicate, - )); - } - - ty::ConstKind::Error(_) - | ty::ConstKind::Param(_) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(..) => { - // These variants are trivially WF, so nothing to do here. - } - ty::ConstKind::Value(..) => { - // FIXME: Enforce that values are structurally-matchable. - } - } - continue; - } - }; - - debug!("wf bounds for ty={:?} ty.kind={:#?}", ty, ty.kind()); - - match *ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Error(_) - | ty::Str - | ty::CoroutineWitness(..) - | ty::Never - | ty::Param(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Foreign(..) => { - // WfScalar, WfParameter, etc - } - - // Can only infer to `ty::Int(_) | ty::Uint(_)`. - ty::Infer(ty::IntVar(_)) => {} - - // Can only infer to `ty::Float(_)`. - ty::Infer(ty::FloatVar(_)) => {} - - ty::Slice(subty) => { - self.require_sized(subty, traits::SliceOrArrayElem); - } - - ty::Array(subty, _) => { - self.require_sized(subty, traits::SliceOrArrayElem); - // Note that we handle the len is implicitly checked while walking `arg`. - } - - ty::Tuple(tys) => { - if let Some((_last, rest)) = tys.split_last() { - for &elem in rest { - self.require_sized(elem, traits::TupleElem); - } - } - } - - ty::RawPtr(_) => { - // Simple cases that are WF if their type args are WF. - } - - ty::Alias(ty::Projection, data) => { - walker.skip_current_subtree(); // Subtree handled by compute_projection. - self.compute_projection(data); - } - ty::Alias(ty::Inherent, data) => { - walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection. - self.compute_inherent_projection(data); - } - - ty::Adt(def, args) => { - // WfNominalType - let obligations = self.nominal_obligations(def.did(), args); - self.out.extend(obligations); - } - - ty::FnDef(did, args) => { - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - ty::Ref(r, rty, _) => { - // WfReference - if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { - let cause = self.cause(traits::ReferenceOutlivesReferent(ty)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - depth, - param_env, - ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)), - )), - )); - } - } - - ty::Coroutine(did, args, ..) => { - // Walk ALL the types in the coroutine: this will - // include the upvar types as well as the yield - // type. Note that this is mildly distinct from - // the closure case, where we have to be careful - // about the signature of the closure. We don't - // have the problem of implied bounds here since - // coroutines don't take arguments. - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - ty::Closure(did, args) => { - // Only check the upvar types for WF, not the rest - // of the types within. This is needed because we - // capture the signature and it may not be WF - // without the implied bounds. Consider a closure - // like `|x: &'a T|` -- it may be that `T: 'a` is - // not known to hold in the creator's context (and - // indeed the closure may not be invoked by its - // creator, but rather turned to someone who *can* - // verify that). - // - // The special treatment of closures here really - // ought not to be necessary either; the problem - // is related to #25860 -- there is no way for us - // to express a fn type complete with the implied - // bounds that it is assuming. I think in reality - // the WF rules around fn are a bit messed up, and - // that is the rot problem: `fn(&'a T)` should - // probably always be WF, because it should be - // shorthand for something like `where(T: 'a) { - // fn(&'a T) }`, as discussed in #25860. - walker.skip_current_subtree(); // subtree handled below - // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(args.as_closure().tupled_upvars_ty().into()); - // Note that we cannot skip the generic types - // types. Normally, within the fn - // body where they are created, the generics will - // always be WF, and outside of that fn body we - // are not directly inspecting closure types - // anyway, except via auto trait matching (which - // only inspects the upvar types). - // But when a closure is part of a type-alias-impl-trait - // then the function that created the defining site may - // have had more bounds available than the type alias - // specifies. This may cause us to have a closure in the - // hidden type that is not actually well formed and - // can cause compiler crashes when the user abuses unsafe - // code to procure such a closure. - // See tests/ui/type-alias-impl-trait/wf_check_closures.rs - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - ty::CoroutineClosure(did, args) => { - // See the above comments. The same apply to coroutine-closures. - walker.skip_current_subtree(); - self.compute(args.as_coroutine_closure().tupled_upvars_ty().into()); - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - ty::FnPtr(_) => { - // let the loop iterate into the argument/return - // types appearing in the fn signature - } - - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - // All of the requirements on type parameters - // have already been checked for `impl Trait` in - // return position. We do need to check type-alias-impl-trait though. - if self.tcx().is_type_alias_impl_trait(def_id) { - let obligations = self.nominal_obligations(def_id, args); - self.out.extend(obligations); - } - } - - ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => { - let obligations = self.nominal_obligations(def_id, args); - self.out.extend(obligations); - } - - ty::Dynamic(data, r, _) => { - // WfObject - // - // Here, we defer WF checking due to higher-ranked - // regions. This is perhaps not ideal. - self.from_object_ty(ty, data, r); - - // FIXME(#27579) RFC also considers adding trait - // obligations that don't refer to Self and - // checking those - - let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; - - if !defer_to_coercion { - if let Some(principal) = data.principal_def_id() { - self.out.push(traits::Obligation::with_depth( - self.tcx(), - self.cause(traits::WellFormed(None)), - depth, - param_env, - ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)), - )); - } - } - } - - // Inference variables are the complicated case, since we don't - // know what type they are. We do two things: - // - // 1. Check if they have been resolved, and if so proceed with - // THAT type. - // 2. If not, we've at least simplified things (e.g., we went - // from `Vec<$0>: WF` to `$0: WF`), so we can - // register a pending obligation and keep - // moving. (Goal is that an "inductive hypothesis" - // is satisfied to ensure termination.) - // See also the comment on `fn obligations`, describing "livelock" - // prevention, which happens before this can be reached. - ty::Infer(_) => { - let cause = self.cause(traits::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - ty.into(), - ))), - )); - } - } - - debug!(?self.out); - } + arg.visit_with(self); + debug!(?self.out); } #[instrument(level = "debug", skip(self))] @@ -933,6 +638,302 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } +impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { + type Result = (); + + fn visit_ty(&mut self, t: <TyCtxt<'tcx> as ty::Interner>::Ty) -> Self::Result { + debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind()); + + match *t.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Error(_) + | ty::Str + | ty::CoroutineWitness(..) + | ty::Never + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Foreign(..) => { + // WfScalar, WfParameter, etc + } + + // Can only infer to `ty::Int(_) | ty::Uint(_)`. + ty::Infer(ty::IntVar(_)) => {} + + // Can only infer to `ty::Float(_)`. + ty::Infer(ty::FloatVar(_)) => {} + + ty::Slice(subty) => { + self.require_sized(subty, traits::SliceOrArrayElem); + } + + ty::Array(subty, _) => { + self.require_sized(subty, traits::SliceOrArrayElem); + // Note that we handle the len is implicitly checked while walking `arg`. + } + + ty::Tuple(tys) => { + if let Some((_last, rest)) = tys.split_last() { + for &elem in rest { + self.require_sized(elem, traits::TupleElem); + } + } + } + + ty::RawPtr(_) => { + // Simple cases that are WF if their type args are WF. + } + + ty::Alias(ty::Projection, data) => { + self.compute_projection(data); + return; // Subtree handled by compute_projection. + } + ty::Alias(ty::Inherent, data) => { + self.compute_inherent_projection(data); + return; // Subtree handled by compute_inherent_projection. + } + + ty::Adt(def, args) => { + // WfNominalType + let obligations = self.nominal_obligations(def.did(), args); + self.out.extend(obligations); + } + + ty::FnDef(did, args) => { + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + ty::Ref(r, rty, _) => { + // WfReference + if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { + let cause = self.cause(traits::ReferenceOutlivesReferent(t)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( + ty::OutlivesPredicate(rty, r), + ))), + )); + } + } + + ty::Coroutine(did, args, ..) => { + // Walk ALL the types in the coroutine: this will + // include the upvar types as well as the yield + // type. Note that this is mildly distinct from + // the closure case, where we have to be careful + // about the signature of the closure. We don't + // have the problem of implied bounds here since + // coroutines don't take arguments. + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + ty::Closure(did, args) => { + // Note that we cannot skip the generic types + // types. Normally, within the fn + // body where they are created, the generics will + // always be WF, and outside of that fn body we + // are not directly inspecting closure types + // anyway, except via auto trait matching (which + // only inspects the upvar types). + // But when a closure is part of a type-alias-impl-trait + // then the function that created the defining site may + // have had more bounds available than the type alias + // specifies. This may cause us to have a closure in the + // hidden type that is not actually well formed and + // can cause compiler crashes when the user abuses unsafe + // code to procure such a closure. + // See tests/ui/type-alias-impl-trait/wf_check_closures.rs + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + // Only check the upvar types for WF, not the rest + // of the types within. This is needed because we + // capture the signature and it may not be WF + // without the implied bounds. Consider a closure + // like `|x: &'a T|` -- it may be that `T: 'a` is + // not known to hold in the creator's context (and + // indeed the closure may not be invoked by its + // creator, but rather turned to someone who *can* + // verify that). + // + // The special treatment of closures here really + // ought not to be necessary either; the problem + // is related to #25860 -- there is no way for us + // to express a fn type complete with the implied + // bounds that it is assuming. I think in reality + // the WF rules around fn are a bit messed up, and + // that is the rot problem: `fn(&'a T)` should + // probably always be WF, because it should be + // shorthand for something like `where(T: 'a) { + // fn(&'a T) }`, as discussed in #25860. + let upvars = args.as_closure().tupled_upvars_ty(); + return upvars.visit_with(self); + } + + ty::CoroutineClosure(did, args) => { + // See the above comments. The same apply to coroutine-closures. + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + let upvars = args.as_coroutine_closure().tupled_upvars_ty(); + return upvars.visit_with(self); + } + + ty::FnPtr(_) => { + // Let the visitor iterate into the argument/return + // types appearing in the fn signature. + } + + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + // All of the requirements on type parameters + // have already been checked for `impl Trait` in + // return position. We do need to check type-alias-impl-trait though. + if self.tcx().is_type_alias_impl_trait(def_id) { + let obligations = self.nominal_obligations(def_id, args); + self.out.extend(obligations); + } + } + + ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => { + let obligations = self.nominal_obligations(def_id, args); + self.out.extend(obligations); + } + + ty::Dynamic(data, r, _) => { + // WfObject + // + // Here, we defer WF checking due to higher-ranked + // regions. This is perhaps not ideal. + self.from_object_ty(t, data, r); + + // FIXME(#27579) RFC also considers adding trait + // obligations that don't refer to Self and + // checking those + + let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; + + if !defer_to_coercion { + if let Some(principal) = data.principal_def_id() { + self.out.push(traits::Obligation::with_depth( + self.tcx(), + self.cause(traits::WellFormed(None)), + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)), + )); + } + } + } + + // Inference variables are the complicated case, since we don't + // know what type they are. We do two things: + // + // 1. Check if they have been resolved, and if so proceed with + // THAT type. + // 2. If not, we've at least simplified things (e.g., we went + // from `Vec<$0>: WF` to `$0: WF`), so we can + // register a pending obligation and keep + // moving. (Goal is that an "inductive hypothesis" + // is satisfied to ensure termination.) + // See also the comment on `fn obligations`, describing "livelock" + // prevention, which happens before this can be reached. + ty::Infer(_) => { + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + t.into(), + ))), + )); + } + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: <TyCtxt<'tcx> as ty::Interner>::Const) -> Self::Result { + match c.kind() { + ty::ConstKind::Unevaluated(uv) => { + if !c.has_escaping_bound_vars() { + let obligations = self.nominal_obligations(uv.def, uv.args); + self.out.extend(obligations); + + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(c), + )); + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + } + ty::ConstKind::Infer(_) => { + let cause = self.cause(traits::WellFormed(None)); + + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + c.into(), + ))), + )); + } + ty::ConstKind::Expr(_) => { + // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the + // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // which means that the `DefId` would have been typeck'd elsewhere. However in + // the future we may allow directly lowering to `ConstKind::Expr` in which case + // we would not be proving bounds we should. + + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(c), + )); + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + + ty::ConstKind::Error(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(..) => { + // These variants are trivially WF, so nothing to do here. + } + ty::ConstKind::Value(..) => { + // FIXME: Enforce that values are structurally-matchable. + } + } + + c.super_visit_with(self) + } + + fn visit_predicate(&mut self, _p: <TyCtxt<'tcx> as ty::Interner>::Predicate) -> Self::Result { + bug!("predicate should not be checked for well-formedness"); + } +} + /// Given an object type like `SomeTrait + Send`, computes the lifetime /// bounds that must hold on the elided self type. These are derived /// from the declarations of `SomeTrait`, `Send`, and friends -- if diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml index 3d0ad31747f..79939d62a51 100644 --- a/compiler/rustc_transmute/Cargo.toml +++ b/compiler/rustc_transmute/Cargo.toml @@ -29,5 +29,5 @@ rustc = [ [dev-dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" # tidy-alphabetical-end diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index 2a30bd5d539..01d5251bfa0 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 381681fb1f4..2816bcc888b 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -7,6 +7,7 @@ use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt}; use rustc_span::sym; use rustc_trait_selection::traits; +use rustc_type_ir::ClosureKind; use traits::{translate_args, Reveal}; use crate::errors::UnexpectedFnPtrAssociatedItem; @@ -296,23 +297,25 @@ fn resolve_associated_item<'tcx>( { match *rcvr_args.type_at(0).kind() { ty::CoroutineClosure(coroutine_closure_def_id, args) => { - // If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure, - // or `AsyncFnOnce` for a by-mut closure, then construct a new body that - // has the right return types. - // - // Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs - // to have its input and output types fixed (`&mut self` and returning - // `i16` coroutine kind). - if target_kind > args.as_coroutine_closure().kind() { - Some(Instance { - def: ty::InstanceDef::ConstructCoroutineInClosureShim { - coroutine_closure_def_id, - target_kind, - }, - args, - }) - } else { - Some(Instance::new(coroutine_closure_def_id, args)) + match (target_kind, args.as_coroutine_closure().kind()) { + (ClosureKind::FnOnce | ClosureKind::FnMut, ClosureKind::Fn) + | (ClosureKind::FnOnce, ClosureKind::FnMut) => { + // If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure, + // or `AsyncFnOnce` for a by-mut closure, then construct a new body that + // has the right return types. + // + // Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs + // to have its input and output types fixed (`&mut self` and returning + // `i16` coroutine kind). + Some(Instance { + def: ty::InstanceDef::ConstructCoroutineInClosureShim { + coroutine_closure_def_id, + target_kind, + }, + args, + }) + } + _ => Some(Instance::new(coroutine_closure_def_id, args)), } } ty::Closure(closure_def_id, args) => { diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 2ded1b956e5..c01baa58ae7 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -369,12 +369,9 @@ rustc_index::newtype_index! { /// /// You can get the environment type of a closure using /// `tcx.closure_env_ty()`. -#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum ClosureKind { - // Warning: Ordering is significant here! The ordering is chosen - // because the trait Fn is a subtrait of FnMut and so in turn, and - // hence we order it so that Fn < FnMut < FnOnce. Fn, FnMut, FnOnce, @@ -394,8 +391,15 @@ impl ClosureKind { /// Returns `true` if a type that impls this closure kind /// must also implement `other`. + #[rustfmt::skip] pub fn extends(self, other: ClosureKind) -> bool { - self <= other + use ClosureKind::*; + match (self, other) { + (Fn, Fn | FnMut | FnOnce) + | (FnMut, FnMut | FnOnce) + | (FnOnce, FnOnce) => true, + _ => false, + } } } diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index be727f024c6..ae8e71bb950 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -639,7 +639,7 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Ok(Ty::usize_ty()) } - Rvalue::NullaryOp(NullOp::DebugAssertions, _) => Ok(Ty::bool_ty()), + Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()), Rvalue::Aggregate(ak, ops) => match *ak { AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), AggregateKind::Tuple => Ok(Ty::new_tuple( @@ -1007,7 +1007,13 @@ pub enum NullOp { /// Returns the offset of a field. OffsetOf(Vec<(VariantIdx, FieldIdx)>), /// cfg!(debug_assertions), but at codegen time - DebugAssertions, + UbCheck(UbKind), +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum UbKind { + LanguageUb, + LibraryUb, } impl Operand { |
