diff --git a/Cargo.lock b/Cargo.lock index a3c4d2493ff18..209b144658b48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,7 +310,7 @@ dependencies = [ "crypto-hash", "curl", "curl-sys", - "env_logger 0.7.1", + "env_logger 0.8.1", "filetime", "flate2", "fwdansi", @@ -626,9 +626,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455" +checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" dependencies = [ "cc", "rustc-std-workspace-core", @@ -1035,6 +1035,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" +dependencies = [ + "atty", + "humantime 2.0.1", + "log", + "regex", + "termcolor", +] + [[package]] name = "error_index_generator" version = "0.0.0" diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 5587e6ead1dbb..81f0cc6e862a0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1416,10 +1416,11 @@ fn generator_layout_and_saved_local_names( let state_arg = mir::Local::new(1); for var in &body.var_debug_info { - if var.place.local != state_arg { + let place = if let mir::VarDebugInfoContents::Place(p) = var.value { p } else { continue }; + if place.local != state_arg { continue; } - match var.place.projection[..] { + match place.projection[..] { [ // Deref of the `Pin<&mut Self>` state argument. mir::ProjectionElem::Field(..), diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 4943e279c7e05..61f831f25ab06 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -11,7 +11,7 @@ use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn eval_mir_constant_to_operand( - &mut self, + &self, bx: &mut Bx, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { @@ -21,7 +21,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } pub fn eval_mir_constant( - &mut self, + &self, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { match self.monomorphize(&constant.literal).val { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 26a646b029367..8ab081e8c3fff 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Span}; use rustc_target::abi::{LayoutOf, Size}; -use super::operand::OperandValue; +use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; @@ -111,6 +111,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + fn spill_operand_to_stack( + operand: &OperandRef<'tcx, Bx::Value>, + name: Option, + bx: &mut Bx, + ) -> PlaceRef<'tcx, Bx::Value> { + // "Spill" the value onto the stack, for debuginfo, + // without forcing non-debuginfo uses of the local + // to also load from the stack every single time. + // FIXME(#68817) use `llvm.dbg.value` instead, + // at least for the cases which LLVM handles correctly. + let spill_slot = PlaceRef::alloca(bx, operand.layout); + if let Some(name) = name { + bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill")); + } + operand.val.store(bx, spill_slot); + spill_slot + } + /// Apply debuginfo and/or name, after creating the `alloca` for a local, /// or initializing the local with an operand (whichever applies). pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { @@ -225,17 +243,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - // "Spill" the value onto the stack, for debuginfo, - // without forcing non-debuginfo uses of the local - // to also load from the stack every single time. - // FIXME(#68817) use `llvm.dbg.value` instead, - // at least for the cases which LLVM handles correctly. - let spill_slot = PlaceRef::alloca(bx, operand.layout); - if let Some(name) = name { - bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill")); - } - operand.val.store(bx, spill_slot); - spill_slot + Self::spill_operand_to_stack(operand, name, bx) } LocalRef::Place(place) => *place, @@ -310,6 +318,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`. pub fn compute_per_local_var_debug_info( &self, + bx: &mut Bx, ) -> Option>>> { let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full; @@ -324,22 +333,30 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { (None, var.source_info.span) }; + let (var_ty, var_kind) = match var.value { + mir::VarDebugInfoContents::Place(place) => { + let var_ty = self.monomorphized_place_ty(place.as_ref()); + let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg + && place.projection.is_empty() + && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE + { + let arg_index = place.local.index() - 1; + + // FIXME(eddyb) shouldn't `ArgumentVariable` indices be + // offset in closures to account for the hidden environment? + // Also, is this `+ 1` needed at all? + VariableKind::ArgumentVariable(arg_index + 1) + } else { + VariableKind::LocalVariable + }; + (var_ty, var_kind) + } + mir::VarDebugInfoContents::Const(c) => { + let ty = self.monomorphize(&c.literal.ty); + (ty, VariableKind::LocalVariable) + } + }; let dbg_var = scope.map(|scope| { - let place = var.place; - let var_ty = self.monomorphized_place_ty(place.as_ref()); - let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg - && place.projection.is_empty() - && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE - { - let arg_index = place.local.index() - 1; - - // FIXME(eddyb) shouldn't `ArgumentVariable` indices be - // offset in closures to account for the hidden environment? - // Also, is this `+ 1` needed at all? - VariableKind::ArgumentVariable(arg_index + 1) - } else { - VariableKind::LocalVariable - }; self.cx.create_dbg_var( self.debug_context.as_ref().unwrap(), var.name, @@ -350,12 +367,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) }); - per_local[var.place.local].push(PerLocalVarDebugInfo { - name: var.name, - source_info: var.source_info, - dbg_var, - projection: var.place.projection, - }); + match var.value { + mir::VarDebugInfoContents::Place(place) => { + per_local[place.local].push(PerLocalVarDebugInfo { + name: var.name, + source_info: var.source_info, + dbg_var, + projection: place.projection, + }); + } + mir::VarDebugInfoContents::Const(c) => { + if let (Some(scope), Some(dbg_var)) = (scope, dbg_var) { + if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) { + let base = Self::spill_operand_to_stack( + &operand, + Some(var.name.to_string()), + bx, + ); + + bx.dbg_var_addr(dbg_var, scope, base.llval, Size::ZERO, &[], span); + } + } + } + } } Some(per_local) } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index bff263567bf6b..295bfa7469c82 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -190,7 +190,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( caller_location: None, }; - fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(); + fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut bx); for const_ in &mir.required_consts { if let Err(err) = fx.eval_mir_constant(const_) { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 05bcf2ba0ce49..a6ec2281b0ab2 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1071,6 +1071,23 @@ impl<'tcx> LocalDecl<'tcx> { } } +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum VarDebugInfoContents<'tcx> { + /// NOTE(eddyb) There's an unenforced invariant that this `Place` is + /// based on a `Local`, not a `Static`, and contains no indexing. + Place(Place<'tcx>), + Const(Constant<'tcx>), +} + +impl<'tcx> Debug for VarDebugInfoContents<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + match self { + VarDebugInfoContents::Const(c) => write!(fmt, "{}", c), + VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p), + } + } +} + /// Debug information pertaining to a user variable. #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct VarDebugInfo<'tcx> { @@ -1082,9 +1099,7 @@ pub struct VarDebugInfo<'tcx> { pub source_info: SourceInfo, /// Where the data for this user variable is to be found. - /// NOTE(eddyb) There's an unenforced invariant that this `Place` is - /// based on a `Local`, not a `Static`, and contains no indexing. - pub place: Place<'tcx>, + pub value: VarDebugInfoContents<'tcx>, } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 58dd0bc00d204..91b4de45004d2 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -796,16 +796,20 @@ macro_rules! make_mir_visitor { let VarDebugInfo { name: _, source_info, - place, + value, } = var_debug_info; self.visit_source_info(source_info); let location = START_BLOCK.start_location(); - self.visit_place( - place, - PlaceContext::NonUse(NonUseContext::VarDebugInfo), - location, - ); + match value { + VarDebugInfoContents::Const(c) => self.visit_constant(c, location), + VarDebugInfoContents::Place(place) => + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ), + } } fn super_source_scope(&mut self, diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index de54c5582e049..3a6316b1ed352 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -11,7 +11,7 @@ use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, - PlaceRef, + PlaceRef, VarDebugInfoContents, }; use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; @@ -133,19 +133,21 @@ fn do_mir_borrowck<'a, 'tcx>( let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); for var_debug_info in &input_body.var_debug_info { - if let Some(local) = var_debug_info.place.as_local() { - if let Some(prev_name) = local_names[local] { - if var_debug_info.name != prev_name { - span_bug!( - var_debug_info.source_info.span, - "local {:?} has many names (`{}` vs `{}`)", - local, - prev_name, - var_debug_info.name - ); + if let VarDebugInfoContents::Place(place) = var_debug_info.value { + if let Some(local) = place.as_local() { + if let Some(prev_name) = local_names[local] { + if var_debug_info.name != prev_name { + span_bug!( + var_debug_info.source_info.span, + "local {:?} has many names (`{}` vs `{}`)", + local, + prev_name, + var_debug_info.name + ); + } } + local_names[local] = Some(var_debug_info.name); } - local_names[local] = Some(var_debug_info.name); } } diff --git a/compiler/rustc_mir/src/interpret/intern.rs b/compiler/rustc_mir/src/interpret/intern.rs index 945791eddc8f1..846ca18990022 100644 --- a/compiler/rustc_mir/src/interpret/intern.rs +++ b/compiler/rustc_mir/src/interpret/intern.rs @@ -33,8 +33,9 @@ struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> { /// A list of all encountered allocations. After type-based interning, we traverse this list to /// also intern allocations that are only referenced by a raw pointer or inside a union. leftover_allocations: &'rt mut FxHashSet, - /// The root kind of the value that we're looking at. This field is never mutated and only used - /// for sanity assertions that will ICE when `const_qualif` screws up. + /// The root kind of the value that we're looking at. This field is never mutated for a + /// particular allocation. It is primarily used to make as many allocations as possible + /// read-only so LLVM can place them in const memory. mode: InternMode, /// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect /// the intern mode of references we encounter. @@ -113,8 +114,8 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( // For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume // no interior mutability. let frozen = ty.map_or(true, |ty| ty.is_freeze(ecx.tcx, ecx.param_env)); - // For statics, allocation mutability is the combination of the place mutability and - // the type mutability. + // For statics, allocation mutability is the combination of place mutability and + // type mutability. // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere. let immutable = mutability == Mutability::Not && frozen; if immutable { @@ -188,8 +189,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir } } - // ZSTs do not need validation unless they're uninhabited - if mplace.layout.is_zst() && !mplace.layout.abi.is_uninhabited() { + // ZSTs cannot contain pointers, so we can skip them. + if mplace.layout.is_zst() { return Ok(()); } @@ -209,13 +210,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir if let ty::Dynamic(..) = tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { - // Validation will error (with a better message) on an invalid vtable pointer - // so we can safely not do anything if this is not a real pointer. if let Scalar::Ptr(vtable) = mplace.meta.unwrap_meta() { // Explicitly choose const mode here, since vtables are immutable, even // if the reference of the fat pointer is mutable. self.intern_shallow(vtable.alloc_id, InternMode::ConstInner, None); } else { + // Validation will error (with a better message) on an invalid vtable pointer. // Let validation show the error message, but make sure it *does* error. tcx.sess .delay_span_bug(tcx.span, "vtables pointers cannot be integer pointers"); @@ -224,7 +224,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir // Check if we have encountered this pointer+layout combination before. // Only recurse for allocation-backed pointers. if let Scalar::Ptr(ptr) = mplace.ptr { - // Compute the mode with which we intern this. + // Compute the mode with which we intern this. Our goal here is to make as many + // statics as we can immutable so they can be placed in const memory by LLVM. let ref_mode = match self.mode { InternMode::Static(mutbl) => { // In statics, merge outer mutability with reference mutability and @@ -243,8 +244,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir } Mutability::Not => { // A shared reference, things become immutable. - // We do *not* consier `freeze` here -- that is done more precisely - // when traversing the referenced data (by tracking `UnsafeCell`). + // We do *not* consider `freeze` here: `intern_shallow` considers + // `freeze` for the actual mutability of this allocation; the intern + // mode for references contained in this allocation is tracked more + // precisely when traversing the referenced data (by tracking + // `UnsafeCell`). This makes sure that `&(&i32, &Cell)` still + // has the left inner reference interned into a read-only + // allocation. InternMode::Static(Mutability::Not) } Mutability::Mut => { diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 2b83e1c8134ef..c38f25564e8dd 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -775,17 +775,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ); } ty::Array(tys, ..) | ty::Slice(tys) - if { - // This optimization applies for types that can hold arbitrary bytes (such as - // integer and floating point types) or for structs or tuples with no fields. - // FIXME(wesleywiser) This logic could be extended further to arbitrary structs - // or tuples made up of integer/floating point types or inhabited ZSTs with no - // padding. - match tys.kind() { - ty::Int(..) | ty::Uint(..) | ty::Float(..) => true, - _ => false, - } - } => + // This optimization applies for types that can hold arbitrary bytes (such as + // integer and floating point types) or for structs or tuples with no fields. + // FIXME(wesleywiser) This logic could be extended further to arbitrary structs + // or tuples made up of integer/floating point types or inhabited ZSTs with no + // padding. + if matches!(tys.kind(), ty::Int(..) | ty::Uint(..) | ty::Float(..)) + => { // Optimized handling for arrays of integer/float type. @@ -853,7 +849,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // of an array and not all of them, because there's only a single value of a specific // ZST type, so either validation fails for all elements or none. ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(tys)?.is_zst() => { - // Validate just the first element + // Validate just the first element (if any). self.walk_aggregate(op, fields.take(1))? } _ => { diff --git a/compiler/rustc_mir/src/transform/const_debuginfo.rs b/compiler/rustc_mir/src/transform/const_debuginfo.rs new file mode 100644 index 0000000000000..719075f94823c --- /dev/null +++ b/compiler/rustc_mir/src/transform/const_debuginfo.rs @@ -0,0 +1,98 @@ +//! Finds locals which are assigned once to a const and unused except for debuginfo and converts +//! their debuginfo to use the const directly, allowing the local to be removed. + +use rustc_middle::{ + mir::{ + visit::{PlaceContext, Visitor}, + Body, Constant, Local, Location, Operand, Rvalue, StatementKind, VarDebugInfoContents, + }, + ty::TyCtxt, +}; + +use crate::transform::MirPass; +use rustc_index::{bit_set::BitSet, vec::IndexVec}; + +pub struct ConstDebugInfo; + +impl<'tcx> MirPass<'tcx> for ConstDebugInfo { + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + trace!("running ConstDebugInfo on {:?}", body.source); + + for (local, constant) in find_optimization_oportunities(body) { + for debuginfo in &mut body.var_debug_info { + if let VarDebugInfoContents::Place(p) = debuginfo.value { + if p.local == local && p.projection.is_empty() { + trace!( + "changing debug info for {:?} from place {:?} to constant {:?}", + debuginfo.name, + p, + constant + ); + debuginfo.value = VarDebugInfoContents::Const(constant); + } + } + } + } + } +} + +struct LocalUseVisitor { + local_mutating_uses: IndexVec, + local_assignment_locations: IndexVec>, +} + +fn find_optimization_oportunities<'tcx>(body: &Body<'tcx>) -> Vec<(Local, Constant<'tcx>)> { + let mut visitor = LocalUseVisitor { + local_mutating_uses: IndexVec::from_elem(0, &body.local_decls), + local_assignment_locations: IndexVec::from_elem(None, &body.local_decls), + }; + + visitor.visit_body(body); + + let mut locals_to_debuginfo = BitSet::new_empty(body.local_decls.len()); + for debuginfo in &body.var_debug_info { + if let VarDebugInfoContents::Place(p) = debuginfo.value { + if let Some(l) = p.as_local() { + locals_to_debuginfo.insert(l); + } + } + } + + let mut eligable_locals = Vec::new(); + for (local, mutating_uses) in visitor.local_mutating_uses.drain_enumerated(..) { + if mutating_uses != 1 || !locals_to_debuginfo.contains(local) { + continue; + } + + if let Some(location) = visitor.local_assignment_locations[local] { + let bb = &body[location.block]; + + // The value is assigned as the result of a call, not a constant + if bb.statements.len() == location.statement_index { + continue; + } + + if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(box c)))) = + &bb.statements[location.statement_index].kind + { + if let Some(local) = p.as_local() { + eligable_locals.push((local, *c)); + } + } + } + } + + eligable_locals +} + +impl<'tcx> Visitor<'tcx> for LocalUseVisitor { + fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) { + if context.is_mutating_use() { + self.local_mutating_uses[*local] += 1; + + if context.is_place_assignment() { + self.local_assignment_locations[*local] = Some(location); + } + } + } +} diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index 20b8c90a9dcad..51f2485a09d2f 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -21,6 +21,7 @@ pub mod check_consts; pub mod check_packed_ref; pub mod check_unsafety; pub mod cleanup_post_borrowck; +pub mod const_debuginfo; pub mod const_prop; pub mod deaggregator; pub mod dest_prop; @@ -404,6 +405,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::new("final"), &nrvo::RenameReturnPlace, + &const_debuginfo::ConstDebugInfo, &simplify::SimplifyLocals, &multiple_return_terminators::MultipleReturnTerminators, ]; diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs index 27bb1def726e1..bea95bf43d21e 100644 --- a/compiler/rustc_mir/src/transform/simplify_try.rs +++ b/compiler/rustc_mir/src/transform/simplify_try.rs @@ -246,14 +246,19 @@ fn get_arm_identity_info<'a, 'tcx>( tmp_assigned_vars.insert(*r); } - let dbg_info_to_adjust: Vec<_> = - debug_info - .iter() - .enumerate() - .filter_map(|(i, var_info)| { - if tmp_assigned_vars.contains(var_info.place.local) { Some(i) } else { None } - }) - .collect(); + let dbg_info_to_adjust: Vec<_> = debug_info + .iter() + .enumerate() + .filter_map(|(i, var_info)| { + if let VarDebugInfoContents::Place(p) = var_info.value { + if tmp_assigned_vars.contains(p.local) { + return Some(i); + } + } + + None + }) + .collect(); Some(ArmIdentityInfo { local_temp_0: local_tmp_s0, @@ -340,9 +345,11 @@ fn optimization_applies<'tcx>( // Check that debug info only points to full Locals and not projections. for dbg_idx in &opt_info.dbg_info_to_adjust { let dbg_info = &var_debug_info[*dbg_idx]; - if !dbg_info.place.projection.is_empty() { - trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, dbg_info.place); - return false; + if let VarDebugInfoContents::Place(p) = dbg_info.value { + if !p.projection.is_empty() { + trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, p); + return false; + } } } @@ -423,9 +430,15 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { // Fix the debug info to point to the right local for dbg_index in opt_info.dbg_info_to_adjust { let dbg_info = &mut debug_info[dbg_index]; - assert!(dbg_info.place.projection.is_empty()); - dbg_info.place.local = opt_info.local_0; - dbg_info.place.projection = opt_info.dbg_projection; + assert!( + matches!(dbg_info.value, VarDebugInfoContents::Place(_)), + "value was not a Place" + ); + if let VarDebugInfoContents::Place(p) = &mut dbg_info.value { + assert!(p.projection.is_empty()); + p.local = opt_info.local_0; + p.projection = opt_info.dbg_projection; + } } trace!("block is now {:?}", bb.statements); diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index e04f07774ed0b..4b2d68aea9e12 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -218,7 +218,7 @@ fn write_graph_label<'tcx, W: Write>( w, r#"debug {} => {};
"#, var_debug_info.name, - escape(&var_debug_info.place) + escape(&var_debug_info.value), )?; } diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index bd7c25bf25043..d6458443cb3cf 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -494,7 +494,7 @@ fn write_scope_tree( let indented_debug_info = format!( "{0:1$}debug {2} => {3:?};", - INDENT, indent, var_debug_info.name, var_debug_info.place, + INDENT, indent, var_debug_info.name, var_debug_info.value, ); writeln!( diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index b7bd67fea0679..0737d5f2cb737 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1992,7 +1992,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - place: for_arm_body.into(), + value: VarDebugInfoContents::Place(for_arm_body.into()), }); let locals = if has_guard.0 { let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { @@ -2011,7 +2011,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - place: ref_for_guard.into(), + value: VarDebugInfoContents::Place(ref_for_guard.into()), }); LocalsForNode::ForGuard { ref_for_guard, for_arm_body } } else { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 899fc647493a0..498b4053fccc7 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -796,7 +796,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name: ident.name, source_info, - place: arg_local.into(), + value: VarDebugInfoContents::Place(arg_local.into()), }); } } @@ -860,10 +860,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: SourceInfo::outermost(tcx_hir.span(var_id)), - place: Place { + value: VarDebugInfoContents::Place(Place { local: closure_env_arg, projection: tcx.intern_place_elems(&projs), - }, + }), }); mutability diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 04de9a7a58dda..2dceb6c90ba8d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -284,10 +284,9 @@ use super::{FieldPat, Pat, PatKind, PatRange}; use rustc_arena::TypedArena; use rustc_attr::{SignedInt, UnsignedInt}; -use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::interpret::{truncate, ConstValue}; use rustc_middle::mir::Field; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; @@ -296,108 +295,21 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Integer, Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; -use std::borrow::Cow; use std::cmp::{self, max, min, Ordering}; -use std::convert::TryInto; use std::fmt; use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; -crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { - LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat) +crate fn expand_pattern<'tcx>(pat: Pat<'tcx>) -> Pat<'tcx> { + LiteralExpander.fold_pattern(&pat) } -struct LiteralExpander<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} +struct LiteralExpander; -impl<'tcx> LiteralExpander<'tcx> { - /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice. - /// - /// `crty` and `rty` can differ because you can use array constants in the presence of slice - /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert - /// the array to a slice in that case. - fn fold_const_value_deref( - &mut self, - val: ConstValue<'tcx>, - // the pattern's pointee type - rty: Ty<'tcx>, - // the constant's pointee type - crty: Ty<'tcx>, - ) -> ConstValue<'tcx> { - debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); - match (val, &crty.kind(), &rty.kind()) { - // the easy case, deref a reference - (ConstValue::Scalar(p), x, y) if x == y => { - match p { - Scalar::Ptr(p) => { - let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory(); - ConstValue::ByRef { alloc, offset: p.offset } - } - Scalar::Raw { .. } => { - let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap(); - if layout.is_zst() { - // Deref of a reference to a ZST is a nop. - ConstValue::Scalar(Scalar::zst()) - } else { - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty); - } - } - } - } - // unsize array to slice if pattern is array but match value or other patterns are slice - (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { - assert_eq!(t, u); - ConstValue::Slice { - data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), - start: p.offset.bytes().try_into().unwrap(), - end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), - } - } - // fat pointers stay the same - (ConstValue::Slice { .. }, _, _) - | (_, ty::Slice(_), ty::Slice(_)) - | (_, ty::Str, ty::Str) => val, - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used - _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), - } - } -} - -impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> { +impl<'tcx> PatternFolder<'tcx> for LiteralExpander { fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> { debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind); match (pat.ty.kind(), &*pat.kind) { - (&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } }) - if const_ty.is_ref() => - { - let crty = - if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() }; - if let ty::ConstKind::Value(val) = val { - Pat { - ty: pat.ty, - span: pat.span, - kind: box PatKind::Deref { - subpattern: Pat { - ty: rty, - span: pat.span, - kind: box PatKind::Constant { - value: Const::from_value( - self.tcx, - self.fold_const_value_deref(*val, rty, crty), - rty, - ), - }, - }, - }, - } - } else { - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty) - } - } - (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self), (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self), _ => pat.super_fold_with(self), @@ -482,9 +394,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { cx: &mut MatchCheckCtxt<'p, 'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, + is_my_head_ctor: bool, ) -> Option> { - let new_fields = - specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?; + let new_fields = specialize_one_pattern( + cx, + self.head(), + constructor, + ctor_wild_subpatterns, + is_my_head_ctor, + )?; Some(new_fields.push_on_patstack(&self.0[1..])) } } @@ -662,6 +580,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { cx, constructor, ctor_wild_subpatterns, + false, ) }) .collect() @@ -687,7 +606,9 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { SpecializationCache::Incompatible => self .patterns .iter() - .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) + .filter_map(|r| { + r.specialize_constructor(cx, constructor, ctor_wild_subpatterns, false) + }) .collect(), } } @@ -707,6 +628,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// +++++++++++++++++++++++++++++ /// + _ + [_, _, tail @ ..] + /// +++++++++++++++++++++++++++++ +/// ``` impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -908,42 +830,30 @@ enum Constructor<'tcx> { Single, /// Enum variants. Variant(DefId), - /// Literal values. - ConstantValue(&'tcx ty::Const<'tcx>), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange<'tcx>), /// Ranges of floating-point literal values (`2.0..=5.2`). FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), + /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. + Str(&'tcx ty::Const<'tcx>), /// Array and slice patterns. Slice(Slice), + /// Constants that must not be matched structurally. They are treated as black + /// boxes for the purposes of exhaustiveness: we must not inspect them, and they + /// don't count towards making a match exhaustive. + Opaque, /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. NonExhaustive, } impl<'tcx> Constructor<'tcx> { - fn is_slice(&self) -> bool { - match self { - Slice(_) => true, - _ => false, - } - } - - fn variant_index_for_adt<'a>( - &self, - cx: &MatchCheckCtxt<'a, 'tcx>, - adt: &'tcx ty::AdtDef, - ) -> VariantIdx { + fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx { match *self { Variant(id) => adt.variant_index_with_id(id), Single => { assert!(!adt.is_enum()); VariantIdx::new(0) } - ConstantValue(c) => cx - .tcx - .destructure_const(cx.param_env.and(c)) - .variant - .expect("destructed const of adt without variant id"), _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } @@ -957,7 +867,7 @@ impl<'tcx> Constructor<'tcx> { match self { // Those constructors can only match themselves. - Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { + Single | Variant(_) | Str(..) | FloatRange(..) => { if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } } &Slice(slice) => { @@ -965,8 +875,6 @@ impl<'tcx> Constructor<'tcx> { .iter() .filter_map(|c: &Constructor<'_>| match c { Slice(slice) => Some(*slice), - // FIXME(oli-obk): implement `deref` for `ConstValue` - ConstantValue(..) => None, _ => bug!("bad slice pattern constructor {:?}", c), }) .map(Slice::value_kind); @@ -1030,6 +938,7 @@ impl<'tcx> Constructor<'tcx> { } // This constructor is never covered by anything else NonExhaustive => vec![NonExhaustive], + Opaque => bug!("unexpected opaque ctor {:?} found in all_ctors", self), } } @@ -1069,7 +978,7 @@ impl<'tcx> Constructor<'tcx> { PatKind::Variant { adt_def: adt, substs, - variant_index: self.variant_index_for_adt(cx, adt), + variant_index: self.variant_index_for_adt(adt), subpatterns, } } else { @@ -1108,10 +1017,11 @@ impl<'tcx> Constructor<'tcx> { PatKind::Slice { prefix, slice: Some(wild), suffix } } }, - &ConstantValue(value) => PatKind::Constant { value }, + &Str(value) => PatKind::Constant { value }, &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), IntRange(range) => return range.to_pat(cx.tcx), NonExhaustive => PatKind::Wild, + Opaque => bug!("we should not try to apply an opaque constructor {:?}", self), }; Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } @@ -1186,12 +1096,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { Fields::Slice(std::slice::from_ref(pat)) } - /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't - /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`. - fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self { - Fields::Slice(pats) - } - /// Convenience; internal use. fn wildcards_from_tys( cx: &MatchCheckCtxt<'p, 'tcx>, @@ -1221,7 +1125,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { // Use T as the sub pattern type of Box. Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) } else { - let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; + let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; // Whether we must not match the fields of this variant exhaustively. let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local(); @@ -1269,7 +1173,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, - ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(), + Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque => Fields::empty(), }; debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); ret @@ -1581,9 +1485,7 @@ fn all_constructors<'a, 'tcx>( ) }; match *pcx.ty.kind() { - ty::Bool => { - [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() - } + ty::Bool => vec![make_range(0, 1)], ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { let len = len.eval_usize(cx.tcx, cx.param_env); if len != 0 && cx.is_uninhabited(sub_ty) { @@ -1696,7 +1598,7 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn is_integral(ty: Ty<'_>) -> bool { match ty.kind() { - ty::Char | ty::Int(_) | ty::Uint(_) => true, + ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool => true, _ => false, } } @@ -1718,6 +1620,7 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { match *ty.kind() { + ty::Bool => Some((Size::from_bytes(1), 0)), ty::Char => Some((Size::from_bytes(4), 0)), ty::Int(ity) => { let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); @@ -2206,7 +2109,7 @@ fn is_useful_specialized<'p, 'tcx>( // We cache the result of `Fields::wildcards` because it is used a lot. let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty); let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); - v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) + v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns, true) .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)) .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns)) .unwrap_or(NotUseful) @@ -2231,18 +2134,13 @@ fn pat_constructor<'tcx>( if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { Some(IntRange(int_range)) } else { - match (value.val, &value.ty.kind()) { - (_, ty::Array(_, n)) => { - let len = n.eval_usize(tcx, param_env); - Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) })) - } - (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => { - let len = (end - start) as u64; - Some(Slice(Slice { array_len: None, kind: FixedLen(len) })) - } - // FIXME(oli-obk): implement `deref` for `ConstValue` - // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... } - _ => Some(ConstantValue(value)), + match value.ty.kind() { + ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), + ty::Ref(_, t, _) if t.is_str() => Some(Str(value)), + // All constants that can be structurally matched have already been expanded + // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are + // opaque. + _ => Some(Opaque), } } } @@ -2278,75 +2176,6 @@ fn pat_constructor<'tcx>( } } -// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, -// meaning all other types will compare unequal and thus equal patterns often do not cause the -// second pattern to lint about unreachable match arms. -fn slice_pat_covered_by_const<'tcx>( - tcx: TyCtxt<'tcx>, - _span: Span, - const_val: &'tcx ty::Const<'tcx>, - prefix: &[Pat<'tcx>], - slice: &Option>, - suffix: &[Pat<'tcx>], - param_env: ty::ParamEnv<'tcx>, -) -> Result { - let const_val_val = if let ty::ConstKind::Value(val) = const_val.val { - val - } else { - bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ) - }; - - let data: &[u8] = match (const_val_val, &const_val.ty.kind()) { - (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => { - assert_eq!(*t, tcx.types.u8); - let n = n.eval_usize(tcx, param_env); - let ptr = Pointer::new(AllocId(0), offset); - alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap() - } - (ConstValue::Slice { data, start, end }, ty::Slice(t)) => { - assert_eq!(*t, tcx.types.u8); - let ptr = Pointer::new(AllocId(0), Size::from_bytes(start)); - data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap() - } - // FIXME(oli-obk): create a way to extract fat pointers from ByRef - (_, ty::Slice(_)) => return Ok(false), - _ => bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ), - }; - - let pat_len = prefix.len() + suffix.len(); - if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) { - return Ok(false); - } - - for (ch, pat) in data[..prefix.len()] - .iter() - .zip(prefix) - .chain(data[data.len() - suffix.len()..].iter().zip(suffix)) - { - if let box PatKind::Constant { value } = pat.kind { - let b = value.eval_bits(tcx, param_env, pat.ty); - assert_eq!(b as u8 as u128, b); - if b as u8 != *ch { - return Ok(false); - } - } - } - - Ok(true) -} - /// For exhaustive integer matching, some constructors are grouped within other constructors /// (namely integer typed values are grouped within ranges). However, when specialising these /// constructors, we want to be specialising for the underlying constructors (the integers), not @@ -2644,35 +2473,6 @@ fn lint_overlapping_patterns<'tcx>( } } -fn constructor_covered_by_range<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctor: &Constructor<'tcx>, - pat: &Pat<'tcx>, -) -> Option<()> { - if let Single = ctor { - return Some(()); - } - - let (pat_from, pat_to, pat_end, ty) = match *pat.kind { - PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), - PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), - _ => bug!("`constructor_covered_by_range` called with {:?}", pat), - }; - let (ctor_from, ctor_to, ctor_end) = match *ctor { - ConstantValue(value) => (value, value, RangeEnd::Included), - FloatRange(from, to, ctor_end) => (from, to, ctor_end), - _ => bug!("`constructor_covered_by_range` called with {:?}", ctor), - }; - trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty); - - let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?; - let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?; - let intersects = (from == Ordering::Greater || from == Ordering::Equal) - && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal)); - if intersects { Some(()) } else { None } -} - /// This is the main specialization step. It expands the pattern /// into `arity` patterns based on the constructor. For most patterns, the step is trivial, /// for instance tuple patterns are flattened and box patterns expand into their inner pattern. @@ -2689,15 +2489,43 @@ fn specialize_one_pattern<'p, 'tcx>( pat: &'p Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, + is_its_own_ctor: bool, // Whether `ctor` is known to be derived from `pat` ) -> Option> { if let NonExhaustive = constructor { - // Only a wildcard pattern can match the special extra constructor + // Only a wildcard pattern can match the special extra constructor. if !pat.is_wildcard() { return None; } return Some(Fields::empty()); } + if let Opaque = constructor { + // Only a wildcard pattern can match an opaque constant, unless we're specializing the + // value against its own constructor. That happens when we call + // `v.specialize_constructor(ctor)` with `ctor` obtained from `pat_constructor(v.head())`. + // For example, in the following match, when we are dealing with the third branch, we will + // specialize with an `Opaque` ctor. We want to ignore the second branch because opaque + // constants should not be inspected, but we don't want to ignore the current (third) + // branch, as that would cause us to always conclude that such a branch is unreachable. + // ```rust + // #[derive(PartialEq)] + // struct Foo(i32); + // impl Eq for Foo {} + // const FOO: Foo = Foo(42); + // + // match (Foo(0), true) { + // (_, true) => {} + // (FOO, true) => {} + // (FOO, false) => {} + // } + // ``` + if is_its_own_ctor || pat.is_wildcard() { + return Some(Fields::empty()); + } else { + return None; + } + } + let result = match *pat.kind { PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` @@ -2717,93 +2545,52 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), - PatKind::Constant { value } if constructor.is_slice() => { - // We extract an `Option` for the pointer because slices of zero - // elements don't necessarily point to memory, they are usually - // just integers. The only time they should be pointing to memory - // is when they are subslices of nonzero slices. - let (alloc, offset, n, ty) = match value.ty.kind() { - ty::Array(t, n) => { - let n = n.eval_usize(cx.tcx, cx.param_env); - // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, - // the result would be exactly what we early return here. - if n == 0 { - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - return Some(Fields::empty()); - } - match value.val { - ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { - (Cow::Borrowed(alloc), offset, n, t) - } - _ => span_bug!(pat.span, "array pattern is {:?}", value,), + PatKind::Constant { .. } | PatKind::Range { .. } => { + match constructor { + IntRange(ctor) => { + let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; + ctor.intersection(cx.tcx, &pat)?; + // Constructor splitting should ensure that all intersections we encounter + // are actually inclusions. + assert!(ctor.is_subrange(&pat)); + } + FloatRange(ctor_from, ctor_to, ctor_end) => { + let (pat_from, pat_to, pat_end, ty) = match *pat.kind { + PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), + PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), + _ => unreachable!(), // This is ensured by the branch we're in + }; + let to = compare_const_vals(cx.tcx, ctor_to, pat_to, cx.param_env, ty)?; + let from = compare_const_vals(cx.tcx, ctor_from, pat_from, cx.param_env, ty)?; + let intersects = (from == Ordering::Greater || from == Ordering::Equal) + && (to == Ordering::Less + || (pat_end == *ctor_end && to == Ordering::Equal)); + if !intersects { + return None; } } - ty::Slice(t) => { - match value.val { - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { - let offset = Size::from_bytes(start); - let n = (end - start) as u64; - (Cow::Borrowed(data), offset, n, t) - } - ty::ConstKind::Value(ConstValue::ByRef { .. }) => { - // FIXME(oli-obk): implement `deref` for `ConstValue` - return None; - } + Str(ctor_value) => { + let pat_value = match *pat.kind { + PatKind::Constant { value } => value, _ => span_bug!( pat.span, - "slice pattern constant must be scalar pair but is {:?}", - value, + "unexpected range pattern {:?} for constant value ctor", + pat ), + }; + + // FIXME: there's probably a more direct way of comparing for equality + if compare_const_vals(cx.tcx, ctor_value, pat_value, cx.param_env, pat.ty)? + != Ordering::Equal + { + return None; } } - _ => span_bug!( - pat.span, - "unexpected const-val {:?} with ctor {:?}", - value, - constructor, - ), - }; - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - - // Convert a constant slice/array pattern to a list of patterns. - let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; - let ptr = Pointer::new(AllocId(0), offset); - let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| { - let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; - let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; - let scalar = scalar.check_init().ok()?; - let value = ty::Const::from_scalar(cx.tcx, scalar, ty); - let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; - Some(pattern) - })); - // Ensure none of the dereferences failed. - if pats.len() as u64 != n { - return None; - } - Some(Fields::from_slice_unfiltered(pats)) - } - - PatKind::Constant { .. } | PatKind::Range { .. } => { - // If the constructor is a: - // - Single value: add a row if the pattern contains the constructor. - // - Range: add a row if the constructor intersects the pattern. - if let IntRange(ctor) = constructor { - let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; - ctor.intersection(cx.tcx, &pat)?; - // Constructor splitting should ensure that all intersections we encounter - // are actually inclusions. - assert!(ctor.is_subrange(&pat)); - } else { - // Fallback for non-ranges and ranges that involve - // floating-point numbers, which are not conveniently handled - // by `IntRange`. For these cases, the constructor may not be a - // range so intersection actually devolves into being covered - // by the pattern. - constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?; + _ => { + // If we reach here, we must be trying to inspect an opaque constant. Thus we skip + // the row. + return None; + } } Some(Fields::empty()) } @@ -2826,21 +2613,6 @@ fn specialize_one_pattern<'p, 'tcx>( let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) } - ConstantValue(cv) => { - match slice_pat_covered_by_const( - cx.tcx, - pat.span, - cv, - prefix, - slice, - suffix, - cx.param_env, - ) { - Ok(true) => Some(Fields::empty()), - Ok(false) => None, - Err(ErrorReported) => None, - } - } _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), }, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 69de7c7e2ee0b..30b700a1d4f63 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -137,7 +137,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; - let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern)); + let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern)); if !patcx.errors.is_empty() { *have_errors = true; patcx.report_inlining_errors(pat.span); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a203b3a142863..6370f8c375b2a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -387,14 +387,16 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this // optimization for now. ty::Str => PatKind::Constant { value: cv }, - ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. - // FIXME: clean this up, likely by permitting array patterns when matching on slices - ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, + // The typechecker has a special case for byte string literals, by treating them + // as slices. This means we turn `&[T; N]` constants into slice patterns, which + // has no negative effects on pattern matching, even if we're actually matching on + // arrays. + ty::Array(..) | // Cannot merge this with the catch all branch below, because the `const_deref` - // changes the type from slice to array, and slice patterns behave differently from - // array patterns. + // changes the type from slice to array, we need to keep the original type in the + // pattern. ty::Slice(..) => { let old = self.behind_reference.replace(true); let array = tcx.deref_const(self.param_env.and(cv)); diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index b05ddb3b46452..25e187243416d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -158,6 +158,13 @@ crate enum PatKind<'tcx> { subpattern: Pat<'tcx>, }, + /// One of the following: + /// * `&str`, which will be handled as a string pattern and thus exhaustiveness + /// checking will detect if you use the same string twice in different patterns. + /// * integer, bool, char or float, which will be handled by exhaustivenes to cover exactly + /// its own value, similar to `&str`, but these values are much simpler. + /// * Opaque constants, that must not be matched structurally. So anything that does not derive + /// `PartialEq` and `Eq`. Constant { value: &'tcx ty::Const<'tcx>, }, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 0963c6d6cd7ea..cc84bf14a33a5 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1082,7 +1082,9 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { // a string piece. for (arg, piece) in fmt.iter().zip(args.pieces.iter()) { formatter.buf.write_str(*piece)?; - run(&mut formatter, arg, &args.args)?; + // SAFETY: arg and args.args come from the same Arguments, + // which guarantees the indexes are always within bounds. + unsafe { run(&mut formatter, arg, &args.args) }?; idx += 1; } } @@ -1096,25 +1098,37 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { Ok(()) } -fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result { +unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result { fmt.fill = arg.format.fill; fmt.align = arg.format.align; fmt.flags = arg.format.flags; - fmt.width = getcount(args, &arg.format.width); - fmt.precision = getcount(args, &arg.format.precision); + // SAFETY: arg and args come from the same Arguments, + // which guarantees the indexes are always within bounds. + unsafe { + fmt.width = getcount(args, &arg.format.width); + fmt.precision = getcount(args, &arg.format.precision); + } // Extract the correct argument - let value = args[arg.position]; + debug_assert!(arg.position < args.len()); + // SAFETY: arg and args come from the same Arguments, + // which guarantees its index is always within bounds. + let value = unsafe { args.get_unchecked(arg.position) }; // Then actually do some printing (value.formatter)(value.value, fmt) } -fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option { +unsafe fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option { match *cnt { rt::v1::Count::Is(n) => Some(n), rt::v1::Count::Implied => None, - rt::v1::Count::Param(i) => args[i].as_usize(), + rt::v1::Count::Param(i) => { + debug_assert!(i < args.len()); + // SAFETY: cnt and args come from the same Arguments, + // which guarantees this index is always within bounds. + unsafe { args.get_unchecked(i).as_usize() } + } } } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 30e7a7f3c3b10..3da0ebdd4982a 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -259,6 +259,7 @@ #![feature(exhaustive_patterns)] #![feature(extend_one)] #![feature(external_doc)] +#![feature(fmt_as_str)] #![feature(fn_traits)] #![feature(format_args_nl)] #![feature(gen_future)] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8dceb12de87b8..221ae809e23a2 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -478,10 +478,26 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { } } + struct StrPanicPayload(&'static str); + + unsafe impl BoxMeUp for StrPanicPayload { + fn take_box(&mut self) -> *mut (dyn Any + Send) { + Box::into_raw(Box::new(self.0)) + } + + fn get(&mut self) -> &(dyn Any + Send) { + &self.0 + } + } + let loc = info.location().unwrap(); // The current implementation always returns Some let msg = info.message().unwrap(); // The current implementation always returns Some crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + if let Some(msg) = msg.as_str() { + rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc); + } else { + rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + } }) } diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 7d3b4b13503a0..8fe0d2584f7a9 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -893,10 +893,15 @@ def update_submodules(self): ).decode(default_encoding).splitlines()] filtered_submodules = [] submodules_names = [] + llvm_checked_out = os.path.exists(os.path.join(self.rust_root, "src/llvm-project/.git")) for module in submodules: if module.endswith("llvm-project"): + # Don't sync the llvm-project submodule either if an external LLVM + # was provided, or if we are downloading LLVM. Also, if the + # submodule has been initialized already, sync it anyways so that + # it doesn't mess up contributor pull requests. if self.get_toml('llvm-config') or self.downloading_llvm(): - if self.get_toml('lld') != 'true': + if self.get_toml('lld') != 'true' and not llvm_checked_out: continue check = self.check_submodule(module, slow_submodules) filtered_submodules.append((module, check)) diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index d50e4cf52697a..e750c2963dddc 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -129,7 +129,8 @@ pub fn find(build: &mut Build) { set_compiler(&mut cfg, Language::CPlusPlus, target, config, build); true } else { - false + // Use an auto-detected compiler (or one configured via `CXX_target_triple` env vars). + cfg.try_get_compiler().is_ok() }; // for VxWorks, record CXX compiler which will be used in lib.rs:linker() diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 33e85dc5e2a11..6c2c05ac7197e 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1202,7 +1202,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. - if !builder.config.dry_run && suite == "run-make-fulldeps" { + if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { cmd.arg("--cc") .arg(builder.cc(target)) .arg("--cxx") diff --git a/src/test/codegen/lifetime_start_end.rs b/src/test/codegen/lifetime_start_end.rs index 9df46bb3dd1b5..2296c224f8685 100644 --- a/src/test/codegen/lifetime_start_end.rs +++ b/src/test/codegen/lifetime_start_end.rs @@ -8,7 +8,7 @@ pub fn test() { let a = 0; &a; // keep variable in an alloca -// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %_1 to i8* // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_a]]) { @@ -31,12 +31,12 @@ pub fn test() { let c = 1; &c; // keep variable in an alloca -// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %c to i8* +// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %_7 to i8* // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_c]]) -// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %c to i8* +// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %_7 to i8* // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_c]]) -// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %_1 to i8* // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_a]]) } diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 918e72582d697..2c37b2e78ffae 100644 --- a/src/test/incremental/hashes/let_expressions.rs +++ b/src/test/incremental/hashes/let_expressions.rs @@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_nodes,typeck,optimized_mir")] + except="hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_slot() { let _x: u64 = 0; @@ -166,7 +166,7 @@ pub fn change_mutability_of_binding_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_nodes,typeck,optimized_mir")] + except="hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_binding_in_pattern() { let (mut _a, _b) = (99u8, 'q'); diff --git a/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff new file mode 100644 index 0000000000000..47c3239b8bf25 --- /dev/null +++ b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff @@ -0,0 +1,115 @@ +- // MIR for `main` before ConstDebugInfo ++ // MIR for `main` after ConstDebugInfo + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_debuginfo.rs:8:11: 8:11 + let _1: u8; // in scope 0 at $DIR/const_debuginfo.rs:9:9: 9:10 + let mut _5: u8; // in scope 0 at $DIR/const_debuginfo.rs:12:15: 12:20 + let mut _6: u8; // in scope 0 at $DIR/const_debuginfo.rs:12:15: 12:16 + let mut _7: u8; // in scope 0 at $DIR/const_debuginfo.rs:12:19: 12:20 + let mut _8: u8; // in scope 0 at $DIR/const_debuginfo.rs:12:23: 12:24 + let mut _14: u32; // in scope 0 at $DIR/const_debuginfo.rs:21:13: 21:16 + let mut _15: u32; // in scope 0 at $DIR/const_debuginfo.rs:21:19: 21:22 + scope 1 { +- debug x => _1; // in scope 1 at $DIR/const_debuginfo.rs:9:9: 9:10 ++ debug x => const 1_u8; // in scope 1 at $DIR/const_debuginfo.rs:9:9: 9:10 + let _2: u8; // in scope 1 at $DIR/const_debuginfo.rs:10:9: 10:10 + scope 2 { +- debug y => _2; // in scope 2 at $DIR/const_debuginfo.rs:10:9: 10:10 ++ debug y => const 2_u8; // in scope 2 at $DIR/const_debuginfo.rs:10:9: 10:10 + let _3: u8; // in scope 2 at $DIR/const_debuginfo.rs:11:9: 11:10 + scope 3 { +- debug z => _3; // in scope 3 at $DIR/const_debuginfo.rs:11:9: 11:10 ++ debug z => const 3_u8; // in scope 3 at $DIR/const_debuginfo.rs:11:9: 11:10 + let _4: u8; // in scope 3 at $DIR/const_debuginfo.rs:12:9: 12:12 + scope 4 { +- debug sum => _4; // in scope 4 at $DIR/const_debuginfo.rs:12:9: 12:12 ++ debug sum => const 6_u8; // in scope 4 at $DIR/const_debuginfo.rs:12:9: 12:12 + let _9: &str; // in scope 4 at $DIR/const_debuginfo.rs:14:9: 14:10 + scope 5 { +- debug s => _9; // in scope 5 at $DIR/const_debuginfo.rs:14:9: 14:10 ++ debug s => const "hello, world!"; // in scope 5 at $DIR/const_debuginfo.rs:14:9: 14:10 + let _10: (bool, bool, u32); // in scope 5 at $DIR/const_debuginfo.rs:16:9: 16:10 + scope 6 { + debug f => _10; // in scope 6 at $DIR/const_debuginfo.rs:16:9: 16:10 + let _11: std::option::Option; // in scope 6 at $DIR/const_debuginfo.rs:18:9: 18:10 + scope 7 { + debug o => _11; // in scope 7 at $DIR/const_debuginfo.rs:18:9: 18:10 + let _12: Point; // in scope 7 at $DIR/const_debuginfo.rs:20:9: 20:10 + scope 8 { + debug p => _12; // in scope 8 at $DIR/const_debuginfo.rs:20:9: 20:10 + let _13: u32; // in scope 8 at $DIR/const_debuginfo.rs:21:9: 21:10 + scope 9 { +- debug a => _13; // in scope 9 at $DIR/const_debuginfo.rs:21:9: 21:10 ++ debug a => const 64_u32; // in scope 9 at $DIR/const_debuginfo.rs:21:9: 21:10 + } + } + } + } + } + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_debuginfo.rs:9:9: 9:10 + _1 = const 1_u8; // scope 0 at $DIR/const_debuginfo.rs:9:13: 9:16 + StorageLive(_2); // scope 1 at $DIR/const_debuginfo.rs:10:9: 10:10 + _2 = const 2_u8; // scope 1 at $DIR/const_debuginfo.rs:10:13: 10:16 + StorageLive(_3); // scope 2 at $DIR/const_debuginfo.rs:11:9: 11:10 + _3 = const 3_u8; // scope 2 at $DIR/const_debuginfo.rs:11:13: 11:16 + StorageLive(_4); // scope 3 at $DIR/const_debuginfo.rs:12:9: 12:12 + StorageLive(_5); // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:20 + StorageLive(_6); // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:16 + _6 = const 1_u8; // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:16 + StorageLive(_7); // scope 3 at $DIR/const_debuginfo.rs:12:19: 12:20 + _7 = const 2_u8; // scope 3 at $DIR/const_debuginfo.rs:12:19: 12:20 + _5 = const 3_u8; // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:20 + StorageDead(_7); // scope 3 at $DIR/const_debuginfo.rs:12:19: 12:20 + StorageDead(_6); // scope 3 at $DIR/const_debuginfo.rs:12:19: 12:20 + StorageLive(_8); // scope 3 at $DIR/const_debuginfo.rs:12:23: 12:24 + _8 = const 3_u8; // scope 3 at $DIR/const_debuginfo.rs:12:23: 12:24 + _4 = const 6_u8; // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:24 + StorageDead(_8); // scope 3 at $DIR/const_debuginfo.rs:12:23: 12:24 + StorageDead(_5); // scope 3 at $DIR/const_debuginfo.rs:12:23: 12:24 + StorageLive(_9); // scope 4 at $DIR/const_debuginfo.rs:14:9: 14:10 + _9 = const "hello, world!"; // scope 4 at $DIR/const_debuginfo.rs:14:13: 14:28 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [8191], len: Size { raw: 13 } }, size: Size { raw: 13 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 13 }) + // mir::Constant + // + span: $DIR/const_debuginfo.rs:14:13: 14:28 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [8191], len: Size { raw: 13 } }, size: Size { raw: 13 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 13 }) } + StorageLive(_10); // scope 5 at $DIR/const_debuginfo.rs:16:9: 16:10 + (_10.0: bool) = const true; // scope 5 at $DIR/const_debuginfo.rs:16:13: 16:34 + (_10.1: bool) = const false; // scope 5 at $DIR/const_debuginfo.rs:16:13: 16:34 + (_10.2: u32) = const 123_u32; // scope 5 at $DIR/const_debuginfo.rs:16:13: 16:34 + StorageLive(_11); // scope 6 at $DIR/const_debuginfo.rs:18:9: 18:10 + ((_11 as Some).0: u16) = const 99_u16; // scope 6 at $DIR/const_debuginfo.rs:18:13: 18:24 + discriminant(_11) = 1; // scope 6 at $DIR/const_debuginfo.rs:18:13: 18:24 + StorageLive(_12); // scope 7 at $DIR/const_debuginfo.rs:20:9: 20:10 + (_12.0: u32) = const 32_u32; // scope 7 at $DIR/const_debuginfo.rs:20:13: 20:35 + (_12.1: u32) = const 32_u32; // scope 7 at $DIR/const_debuginfo.rs:20:13: 20:35 + StorageLive(_13); // scope 8 at $DIR/const_debuginfo.rs:21:9: 21:10 + StorageLive(_14); // scope 8 at $DIR/const_debuginfo.rs:21:13: 21:16 + _14 = const 32_u32; // scope 8 at $DIR/const_debuginfo.rs:21:13: 21:16 + StorageLive(_15); // scope 8 at $DIR/const_debuginfo.rs:21:19: 21:22 + _15 = const 32_u32; // scope 8 at $DIR/const_debuginfo.rs:21:19: 21:22 + _13 = const 64_u32; // scope 8 at $DIR/const_debuginfo.rs:21:13: 21:22 + StorageDead(_15); // scope 8 at $DIR/const_debuginfo.rs:21:21: 21:22 + StorageDead(_14); // scope 8 at $DIR/const_debuginfo.rs:21:21: 21:22 + _0 = const (); // scope 0 at $DIR/const_debuginfo.rs:8:11: 22:2 + StorageDead(_13); // scope 8 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_12); // scope 7 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_11); // scope 6 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_10); // scope 5 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_9); // scope 4 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_4); // scope 3 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_3); // scope 2 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_2); // scope 1 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_1); // scope 0 at $DIR/const_debuginfo.rs:22:1: 22:2 + return; // scope 0 at $DIR/const_debuginfo.rs:22:2: 22:2 + } + } + diff --git a/src/test/mir-opt/const_debuginfo.rs b/src/test/mir-opt/const_debuginfo.rs new file mode 100644 index 0000000000000..a66d66c60c7ad --- /dev/null +++ b/src/test/mir-opt/const_debuginfo.rs @@ -0,0 +1,24 @@ +// compile-flags: -C overflow-checks=no + +struct Point { + x: u32, + y: u32, +} + +fn main() { + let x = 1u8; + let y = 2u8; + let z = 3u8; + let sum = x + y + z; + + let s = "hello, world!"; + + let f = (true, false, 123u32); + + let o = Some(99u16); + + let p = Point { x: 32, y: 32 }; + let a = p.x + p.y; +} + +// EMIT_MIR const_debuginfo.main.ConstDebugInfo.diff diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.32bit.mir b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.32bit.mir index a78a6341c299d..e4fbba3abfea1 100644 --- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.32bit.mir +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.32bit.mir @@ -2,30 +2,18 @@ fn main() -> () { let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 scope 1 { - debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + debug x => const 4_i32; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 scope 2 { - debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + debug y => const 3_i32; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 scope 3 { - debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + debug z => const 42_u32; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 } } } bb0: { - StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - _2 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - _3 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 - StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 } } diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.64bit.mir b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.64bit.mir index a78a6341c299d..e4fbba3abfea1 100644 --- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.64bit.mir +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.64bit.mir @@ -2,30 +2,18 @@ fn main() -> () { let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 scope 1 { - debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + debug x => const 4_i32; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 scope 2 { - debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + debug y => const 3_i32; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 scope 3 { - debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + debug z => const 42_u32; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 } } } bb0: { - StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - _2 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - _3 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 - StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 } } diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.32bit.mir b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.32bit.mir index 523ecb5ec1a3d..a75569964a9c8 100644 --- a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.32bit.mir +++ b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.32bit.mir @@ -2,20 +2,16 @@ fn change_loop_body() -> () { let mut _0: (); // return place in scope 0 at $DIR/while_let_loops.rs:5:27: 5:27 - let mut _1: i32; // in scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 - let mut _2: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 + let mut _1: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 scope 1 { - debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 + debug _x => const 0_i32; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 } bb0: { - StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 - _1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:6:18: 6:19 - StorageLive(_2); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 - discriminant(_2) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + StorageLive(_1); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + discriminant(_1) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 _0 = const (); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 - StorageDead(_2); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 - StorageDead(_1); // scope 0 at $DIR/while_let_loops.rs:11:1: 11:2 + StorageDead(_1); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 return; // scope 0 at $DIR/while_let_loops.rs:11:2: 11:2 } } diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.64bit.mir b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.64bit.mir index 523ecb5ec1a3d..a75569964a9c8 100644 --- a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.64bit.mir +++ b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.64bit.mir @@ -2,20 +2,16 @@ fn change_loop_body() -> () { let mut _0: (); // return place in scope 0 at $DIR/while_let_loops.rs:5:27: 5:27 - let mut _1: i32; // in scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 - let mut _2: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 + let mut _1: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 scope 1 { - debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 + debug _x => const 0_i32; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 } bb0: { - StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 - _1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:6:18: 6:19 - StorageLive(_2); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 - discriminant(_2) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + StorageLive(_1); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + discriminant(_1) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 _0 = const (); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 - StorageDead(_2); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 - StorageDead(_1); // scope 0 at $DIR/while_let_loops.rs:11:1: 11:2 + StorageDead(_1); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 return; // scope 0 at $DIR/while_let_loops.rs:11:2: 11:2 } } diff --git a/src/test/run-make-fulldeps/fmt-write-bloat/Makefile b/src/test/run-make-fulldeps/fmt-write-bloat/Makefile new file mode 100644 index 0000000000000..811ec4b31934d --- /dev/null +++ b/src/test/run-make-fulldeps/fmt-write-bloat/Makefile @@ -0,0 +1,7 @@ +-include ../tools.mk + +NM=nm + +all: main.rs + $(RUSTC) $< -O + $(NM) $(call RUN_BINFILE,main) | $(CGREP) -v panicking panic_fmt panic_bounds_check pad_integral Display Debug diff --git a/src/test/run-make-fulldeps/fmt-write-bloat/main.rs b/src/test/run-make-fulldeps/fmt-write-bloat/main.rs new file mode 100644 index 0000000000000..e86c48014c3aa --- /dev/null +++ b/src/test/run-make-fulldeps/fmt-write-bloat/main.rs @@ -0,0 +1,32 @@ +#![feature(lang_items)] +#![feature(start)] +#![no_std] + +use core::fmt; +use core::fmt::Write; + +#[link(name = "c")] +extern "C" {} + +struct Dummy; + +impl fmt::Write for Dummy { + #[inline(never)] + fn write_str(&mut self, _: &str) -> fmt::Result { + Ok(()) + } +} + +#[start] +fn main(_: isize, _: *const *const u8) -> isize { + let _ = writeln!(Dummy, "Hello World"); + 0 +} + +#[lang = "eh_personality"] +fn eh_personality() {} + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/src/test/run-make-fulldeps/issue-36710/Makefile b/src/test/run-make/issue-36710/Makefile similarity index 57% rename from src/test/run-make-fulldeps/issue-36710/Makefile rename to src/test/run-make/issue-36710/Makefile index 4f93d97636e60..b7bf366c918e2 100644 --- a/src/test/run-make-fulldeps/issue-36710/Makefile +++ b/src/test/run-make/issue-36710/Makefile @@ -1,6 +1,6 @@ --include ../tools.mk +include ../../run-make-fulldeps/tools.mk -# ignore-musl +# ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64) all: foo $(call RUN,foo) diff --git a/src/test/run-make-fulldeps/issue-36710/foo.cpp b/src/test/run-make/issue-36710/foo.cpp similarity index 100% rename from src/test/run-make-fulldeps/issue-36710/foo.cpp rename to src/test/run-make/issue-36710/foo.cpp diff --git a/src/test/run-make-fulldeps/issue-36710/foo.rs b/src/test/run-make/issue-36710/foo.rs similarity index 100% rename from src/test/run-make-fulldeps/issue-36710/foo.rs rename to src/test/run-make/issue-36710/foo.rs diff --git a/src/test/ui/const-generics/issue-73298.rs b/src/test/ui/const-generics/issue-73298.rs new file mode 100644 index 0000000000000..4ad4108c90581 --- /dev/null +++ b/src/test/ui/const-generics/issue-73298.rs @@ -0,0 +1,24 @@ +// build-pass +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +use std::convert::AsMut; +use std::default::Default; + +trait Foo: Sized { + type Baz: Default + AsMut<[u8]>; + fn bar() { + Self::Baz::default().as_mut(); + } +} + +impl Foo for () { + type Baz = [u8; 1 * 1]; +} + +fn main() { + <() as Foo>::bar(); +} diff --git a/src/test/ui/issues/issue-44333.rs b/src/test/ui/consts/const_in_pattern/issue-44333.rs similarity index 100% rename from src/test/ui/issues/issue-44333.rs rename to src/test/ui/consts/const_in_pattern/issue-44333.rs diff --git a/src/test/ui/issues/issue-44333.stderr b/src/test/ui/consts/const_in_pattern/issue-44333.stderr similarity index 100% rename from src/test/ui/issues/issue-44333.stderr rename to src/test/ui/consts/const_in_pattern/issue-44333.stderr diff --git a/src/test/ui/consts/const_in_pattern/issue-53708.rs b/src/test/ui/consts/const_in_pattern/issue-53708.rs new file mode 100644 index 0000000000000..355ba63790f3b --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-53708.rs @@ -0,0 +1,11 @@ +// check-pass +// https://github.com/rust-lang/rust/issues/53708 +#[derive(PartialEq, Eq)] +struct S; + +fn main() { + const C: &S = &S; + match C { + C => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/issue-78057.rs b/src/test/ui/consts/const_in_pattern/issue-78057.rs new file mode 100644 index 0000000000000..69cf8404da18e --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-78057.rs @@ -0,0 +1,17 @@ +#![deny(unreachable_patterns)] + +#[derive(PartialEq)] +struct Opaque(i32); + +impl Eq for Opaque {} + +const FOO: Opaque = Opaque(42); + +fn main() { + match FOO { + FOO => {}, + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} + //~^ ERROR unreachable pattern + } +} diff --git a/src/test/ui/consts/const_in_pattern/issue-78057.stderr b/src/test/ui/consts/const_in_pattern/issue-78057.stderr new file mode 100644 index 0000000000000..0d49d0e96c854 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-78057.stderr @@ -0,0 +1,20 @@ +error: to use a constant of type `Opaque` in a pattern, `Opaque` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/issue-78057.rs:12:9 + | +LL | FOO => {}, + | ^^^ + +error: unreachable pattern + --> $DIR/issue-78057.rs:14:9 + | +LL | _ => {} + | ^ + | +note: the lint level is defined here + --> $DIR/issue-78057.rs:1:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index 24474cabf1e37..4a91198ab9f6f 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -53,8 +53,8 @@ enum LR_NonZero { fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { let err = panic::catch_unwind(op).err(); assert_eq!( - err.as_ref().and_then(|a| a.downcast_ref::()).map(|s| &**s), - Some(msg) + err.as_ref().and_then(|a| a.downcast_ref::<&str>()), + Some(&msg) ); } diff --git a/src/test/ui/pattern/usefulness/consts-opaque.rs b/src/test/ui/pattern/usefulness/consts-opaque.rs new file mode 100644 index 0000000000000..f87f96e34fccd --- /dev/null +++ b/src/test/ui/pattern/usefulness/consts-opaque.rs @@ -0,0 +1,114 @@ +// This file tests the exhaustiveness algorithm on opaque constants. Most of the examples give +// unnecessary warnings because const_to_pat.rs converts a constant pattern to a wildcard when the +// constant is not allowed as a pattern. This is an edge case so we may not care to fix it. +// See also https://github.com/rust-lang/rust/issues/78057 + +#![deny(unreachable_patterns)] + +#[derive(PartialEq)] +struct Foo(i32); +impl Eq for Foo {} +const FOO: Foo = Foo(42); +const FOO_REF: &Foo = &Foo(42); +const FOO_REF_REF: &&Foo = &&Foo(42); + +#[derive(PartialEq)] +struct Bar; +impl Eq for Bar {} +const BAR: Bar = Bar; + +#[derive(PartialEq)] +enum Baz { + Baz1, + Baz2 +} +impl Eq for Baz {} +const BAZ: Baz = Baz::Baz1; + +type Quux = fn(usize, usize) -> usize; +fn quux(a: usize, b: usize) -> usize { a + b } +const QUUX: Quux = quux; + +fn main() { + match FOO { + FOO => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match FOO_REF { + FOO_REF => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Foo(_) => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071) + match FOO_REF_REF { + FOO_REF_REF => {} + //~^ WARNING must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARNING this was previously accepted by the compiler but is being phased out + Foo(_) => {} + } + + match BAR { + Bar => {} + BAR => {} // should not be emitting unreachable warning + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAR { + BAR => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Bar => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAR { + BAR => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + BAR => {} // should not be emitting unreachable warning + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR unreachable pattern + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match BAZ { + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Baz::Baz1 => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAZ { + Baz::Baz1 => {} + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} + //~^ ERROR unreachable pattern + } + + match BAZ { + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Baz::Baz2 => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match QUUX { + QUUX => {} + QUUX => {} + _ => {} + } +} diff --git a/src/test/ui/pattern/usefulness/consts-opaque.stderr b/src/test/ui/pattern/usefulness/consts-opaque.stderr new file mode 100644 index 0000000000000..f10166d5a3580 --- /dev/null +++ b/src/test/ui/pattern/usefulness/consts-opaque.stderr @@ -0,0 +1,158 @@ +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:34:9 + | +LL | FOO => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:36:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + | +note: the lint level is defined here + --> $DIR/consts-opaque.rs:6:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:41:9 + | +LL | FOO_REF => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:43:9 + | +LL | Foo(_) => {} // should not be emitting unreachable warning + | ^^^^^^ + +warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:49:9 + | +LL | FOO_REF_REF => {} + | ^^^^^^^^^^^ + | + = note: `#[warn(indirect_structural_match)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:57:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:57:9 + | +LL | Bar => {} + | --- matches any value +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ unreachable pattern + +error: unreachable pattern + --> $DIR/consts-opaque.rs:60:9 + | +LL | Bar => {} + | --- matches any value +... +LL | _ => {} + | ^ unreachable pattern + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:65:9 + | +LL | BAR => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:67:9 + | +LL | Bar => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:69:9 + | +LL | Bar => {} // should not be emitting unreachable warning + | --- matches any value +LL | +LL | _ => {} + | ^ unreachable pattern + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:74:9 + | +LL | BAR => {} + | ^^^ + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:76:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:76:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:79:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:84:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:86:9 + | +LL | Baz::Baz1 => {} // should not be emitting unreachable warning + | ^^^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:88:9 + | +LL | _ => {} + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:94:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:96:9 + | +LL | _ => {} + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:101:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:103:9 + | +LL | Baz::Baz2 => {} // should not be emitting unreachable warning + | ^^^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:105:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + +error: aborting due to 22 previous errors; 1 warning emitted + diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index ffc8433403fd5..7968f9713ff22 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -7,11 +7,11 @@ LL | match buf { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `&[u8; 4]` -error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered +error[E0004]: non-exhaustive patterns: `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered --> $DIR/match-byte-array-patterns-2.rs:10:11 | LL | match buf { - | ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered + | ^^^ patterns `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `&[u8]` diff --git a/src/tools/cargo b/src/tools/cargo index 79b397d72c557..dd83ae55c871d 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 79b397d72c557eb6444a2ba0dc00a211a226a35a +Subproject commit dd83ae55c871d94f060524656abab62ec40b4c40