diff options
69 files changed, 867 insertions, 246 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index d76dfca7960..f7a83373e87 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -713,14 +713,21 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, val); }; - ptr_offset_from, (v ptr, v base) { + ptr_offset_from | ptr_offset_from_unsigned, (v ptr, v base) { let ty = substs.type_at(0); let isize_layout = fx.layout_of(fx.tcx.types.isize); let pointee_size: u64 = fx.layout_of(ty).size.bytes(); - let diff = fx.bcx.ins().isub(ptr, base); + let diff_bytes = fx.bcx.ins().isub(ptr, base); // FIXME this can be an exact division. - let val = CValue::by_val(fx.bcx.ins().sdiv_imm(diff, pointee_size as i64), isize_layout); + let diff = if intrinsic == sym::ptr_offset_from_unsigned { + // Because diff_bytes ULE isize::MAX, this would be fine as signed, + // but unsigned is slightly easier to codegen, so might as well. + fx.bcx.ins().udiv_imm(diff_bytes, pointee_size as i64) + } else { + fx.bcx.ins().sdiv_imm(diff_bytes, pointee_size as i64) + }; + let val = CValue::by_val(diff, isize_layout); ret.write_cvalue(fx, val); }; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cf9cf1b70aa..4407297c943 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -816,6 +816,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, ) -> Result<&'ll Value, ()> { // macros for error handling: + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) @@ -1144,6 +1145,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, args: &[OperandRef<'tcx, &'ll Value>], ) -> Result<&'ll Value, ()> { + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index f15c469ae57..6d6d3ae01f4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -555,21 +555,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - sym::ptr_offset_from => { + sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { let ty = substs.type_at(0); let pointee_size = bx.layout_of(ty).size; - // This is the same sequence that Clang emits for pointer subtraction. - // It can be neither `nsw` nor `nuw` because the input is treated as - // unsigned but then the output is treated as signed, so neither works. let a = args[0].immediate(); let b = args[1].immediate(); let a = bx.ptrtoint(a, bx.type_isize()); let b = bx.ptrtoint(b, bx.type_isize()); - let d = bx.sub(a, b); let pointee_size = bx.const_usize(pointee_size.bytes()); - // this is where the signed magic happens (notice the `s` in `exactsdiv`) - bx.exactsdiv(d, pointee_size) + if name == sym::ptr_offset_from { + // This is the same sequence that Clang emits for pointer subtraction. + // It can be neither `nsw` nor `nuw` because the input is treated as + // unsigned but then the output is treated as signed, so neither works. + let d = bx.sub(a, b); + // this is where the signed magic happens (notice the `s` in `exactsdiv`) + bx.exactsdiv(d, pointee_size) + } else { + // The `_unsigned` version knows the relative ordering of the pointers, + // so can use `sub nuw` and `udiv exact` instead of dealing in signed. + let d = bx.unchecked_usub(a, b); + bx.exactudiv(d, pointee_size) + } } _ => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 3cc237faf69..59ea40dc2f9 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -308,7 +308,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self); self.write_pointer(offset_ptr, dest)?; } - sym::ptr_offset_from => { + sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { let a = self.read_pointer(&args[0])?; let b = self.read_pointer(&args[1])?; @@ -330,8 +330,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Both are pointers. They must be into the same allocation. if a_alloc_id != b_alloc_id { throw_ub_format!( - "ptr_offset_from cannot compute offset of pointers into different \ - allocations.", + "{} cannot compute offset of pointers into different allocations.", + intrinsic_name, ); } // And they must both be valid for zero-sized accesses ("in-bounds or one past the end"). @@ -348,16 +348,39 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { CheckInAllocMsg::OffsetFromTest, )?; + if intrinsic_name == sym::ptr_offset_from_unsigned && a_offset < b_offset { + throw_ub_format!( + "{} cannot compute a negative offset, but {} < {}", + intrinsic_name, + a_offset.bytes(), + b_offset.bytes(), + ); + } + // Compute offset. let usize_layout = self.layout_of(self.tcx.types.usize)?; let isize_layout = self.layout_of(self.tcx.types.isize)?; - let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout); - let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout); - let (val, _overflowed, _ty) = + let ret_layout = if intrinsic_name == sym::ptr_offset_from { + isize_layout + } else { + usize_layout + }; + + // The subtraction is always done in `isize` to enforce + // the "no more than `isize::MAX` apart" requirement. + let a_offset = ImmTy::from_uint(a_offset.bytes(), isize_layout); + let b_offset = ImmTy::from_uint(b_offset.bytes(), isize_layout); + let (val, overflowed, _ty) = self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?; + if overflowed { + throw_ub_format!("Pointers were too far apart for {}", intrinsic_name); + } + let pointee_layout = self.layout_of(substs.type_at(0))?; - let val = ImmTy::from_scalar(val, isize_layout); - let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout); + // This re-interprets an isize at ret_layout, but we already checked + // that if ret_layout is usize, then the result must be non-negative. + let val = ImmTy::from_scalar(val, ret_layout); + let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout); self.exact_div(&val, &size, dest)?; } } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 8926676e815..d627ddb39d0 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -284,8 +284,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Abi::Scalar(s) if force => Some(s.primitive()), _ => None, }; - if let Some(_) = scalar_layout { - let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?; + if let Some(_s) = scalar_layout { + //FIXME(#96185): let size = s.size(self); + //FIXME(#96185): assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); + let size = mplace.layout.size; //FIXME(#96185): remove this line + let scalar = alloc.read_scalar(alloc_range(Size::ZERO, size))?; return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })); } let scalar_pair_layout = match mplace.layout.abi { @@ -302,7 +305,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. let (a_size, b_size) = (a.size(self), b.size(self)); let b_offset = a_size.align_to(b.align(self).abi); - assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields + assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?; let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?; return Ok(Some(ImmTy { @@ -394,28 +397,44 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Err(value) => value, }; - let field_layout = op.layout.field(self, field); - if field_layout.is_zst() { - let immediate = Scalar::ZST.into(); - return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }); - } - let offset = op.layout.fields.offset(field); - let immediate = match *base { + let field_layout = base.layout.field(self, field); + let offset = base.layout.fields.offset(field); + // This makes several assumptions about what layouts we will encounter; we match what + // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). + let field_val: Immediate<_> = match (*base, base.layout.abi) { + // the field contains no information + _ if field_layout.is_zst() => Scalar::ZST.into(), // the field covers the entire type - _ if offset.bytes() == 0 && field_layout.size == op.layout.size => *base, + _ if field_layout.size == base.layout.size => { + assert!(match (base.layout.abi, field_layout.abi) { + (Abi::Scalar(..), Abi::Scalar(..)) => true, + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, + _ => false, + }); + assert!(offset.bytes() == 0); + *base + } // extract fields from types with `ScalarPair` ABI - Immediate::ScalarPair(a, b) => { - let val = if offset.bytes() == 0 { a } else { b }; - Immediate::from(val) + (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { + assert!(matches!(field_layout.abi, Abi::Scalar(..))); + Immediate::from(if offset.bytes() == 0 { + debug_assert_eq!(field_layout.size, a.size(self)); + a_val + } else { + debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi)); + debug_assert_eq!(field_layout.size, b.size(self)); + b_val + }) } - Immediate::Scalar(val) => span_bug!( + _ => span_bug!( self.cur_span(), - "field access on non aggregate {:#?}, {:#?}", - val, - op.layout + "invalid field access on immediate {}, layout {:#?}", + base, + base.layout ), }; - Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }) + + Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout }) } pub fn operand_index( diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index e23c6f2a954..95d6f431391 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -16,7 +16,7 @@ use rustc_target::abi::{HasDataLayout, Size, VariantIdx, Variants}; use super::{ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, - Operand, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, + Operand, Pointer, Provenance, Scalar, ScalarMaybeUninit, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] @@ -700,24 +700,7 @@ where src: Immediate<M::PointerTag>, dest: &PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - if cfg!(debug_assertions) { - // This is a very common path, avoid some checks in release mode - assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); - match src { - Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(..))) => assert_eq!( - self.pointer_size(), - dest.layout.size, - "Size mismatch when writing pointer" - ), - Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Int(int))) => { - assert_eq!(int.size(), dest.layout.size, "Size mismatch when writing bits") - } - Immediate::Scalar(ScalarMaybeUninit::Uninit) => {} // uninit can have any size - Immediate::ScalarPair(_, _) => { - // FIXME: Can we check anything here? - } - } - } + assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, @@ -753,31 +736,27 @@ where dest: &MPlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { // Note that it is really important that the type here is the right one, and matches the - // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here + // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here // to handle padding properly, which is only correct if we never look at this data with the // wrong type. - // Invalid places are a thing: the return place of a diverging function let tcx = *self.tcx; let Some(mut alloc) = self.get_place_alloc_mut(dest)? else { // zero-sized access return Ok(()); }; - // FIXME: We should check that there are dest.layout.size many bytes available in - // memory. The code below is not sufficient, with enough padding it might not - // cover all the bytes! match value { Immediate::Scalar(scalar) => { - match dest.layout.abi { - Abi::Scalar(_) => {} // fine - _ => span_bug!( + let Abi::Scalar(s) = dest.layout.abi else { span_bug!( self.cur_span(), "write_immediate_to_mplace: invalid Scalar layout: {:#?}", dest.layout - ), - } - alloc.write_scalar(alloc_range(Size::ZERO, dest.layout.size), scalar) + ) + }; + let size = s.size(&tcx); + //FIXME(#96185): assert_eq!(dest.layout.size, size, "abi::Scalar size does not match layout size"); + alloc.write_scalar(alloc_range(Size::ZERO, size), scalar) } Immediate::ScalarPair(a_val, b_val) => { // We checked `ptr_align` above, so all fields will have the alignment they need. @@ -791,6 +770,7 @@ where }; let (a_size, b_size) = (a.size(&tcx), b.size(&tcx)); let b_offset = a_size.align_to(b.align(&tcx).abi); + assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, // but that does not work: We could be a newtype around a pair, then the diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 92e3ac04dc4..2dab9ff8986 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -645,17 +645,18 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // i.e. that we go over the `check_init` below. let size = scalar_layout.size(self.ecx); let is_full_range = match scalar_layout { - ScalarAbi::Initialized { valid_range, .. } => { + ScalarAbi::Initialized { .. } => { if M::enforce_number_validity(self.ecx) { false // not "full" since uninit is not accepted } else { - valid_range.is_full_for(size) + scalar_layout.is_always_valid(self.ecx) } } ScalarAbi::Union { .. } => true, }; if is_full_range { - // Nothing to check + // Nothing to check. Cruciall we don't even `read_scalar` until here, since that would + // fail for `Union` scalars! return Ok(()); } // We have something to check: it must at least be initialized. @@ -688,7 +689,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } else { return Ok(()); } - } else if scalar_layout.valid_range(self.ecx).is_full_for(size) { + } else if scalar_layout.is_always_valid(self.ecx) { // Easy. (This is reachable if `enforce_number_validity` is set.) return Ok(()); } else { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 48b5502c20f..9ea09f7d702 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -886,6 +886,8 @@ pub trait ResolverExpand { force: bool, ) -> Result<Lrc<SyntaxExtension>, Indeterminate>; + fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize); + fn check_unused_macros(&mut self); // Resolver interfaces for specific built-in macros. diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 050710097c3..ba0b35470b6 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -156,13 +156,13 @@ impl<'a> ParserAnyMacro<'a> { } struct MacroRulesMacroExpander { + node_id: NodeId, name: Ident, span: Span, transparency: Transparency, lhses: Vec<Vec<MatcherLoc>>, rhses: Vec<mbe::TokenTree>, valid: bool, - is_local: bool, } impl TTMacroExpander for MacroRulesMacroExpander { @@ -179,12 +179,12 @@ impl TTMacroExpander for MacroRulesMacroExpander { cx, sp, self.span, + self.node_id, self.name, self.transparency, input, &self.lhses, &self.rhses, - self.is_local, ) } } @@ -207,14 +207,17 @@ fn generic_extension<'cx, 'tt>( cx: &'cx mut ExtCtxt<'_>, sp: Span, def_span: Span, + node_id: NodeId, name: Ident, transparency: Transparency, arg: TokenStream, lhses: &'tt [Vec<MatcherLoc>], rhses: &'tt [mbe::TokenTree], - is_local: bool, ) -> Box<dyn MacResult + 'cx> { let sess = &cx.sess.parse_sess; + // Macros defined in the current crate have a real node id, + // whereas macros from an external crate have a dummy id. + let is_local = node_id != DUMMY_NODE_ID; if cx.trace_macros() { let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg)); @@ -296,6 +299,10 @@ fn generic_extension<'cx, 'tt>( let mut p = Parser::new(sess, tts, false, None); p.last_type_ascription = cx.current_expansion.prior_type_ascription; + if is_local { + cx.resolver.record_macro_rule_usage(node_id, i); + } + // Let the context choose how to interpret the result. // Weird, but useful for X-macros. return Box::new(ParserAnyMacro { @@ -372,7 +379,7 @@ pub fn compile_declarative_macro( features: &Features, def: &ast::Item, edition: Edition, -) -> SyntaxExtension { +) -> (SyntaxExtension, Vec<Span>) { debug!("compile_declarative_macro: {:?}", def); let mk_syn_ext = |expander| { SyntaxExtension::new( @@ -385,6 +392,7 @@ pub fn compile_declarative_macro( &def.attrs, ) }; + let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new()); let diag = &sess.parse_sess.span_diagnostic; let lhs_nm = Ident::new(sym::lhs, def.span); @@ -445,17 +453,17 @@ pub fn compile_declarative_macro( let s = parse_failure_msg(&token); let sp = token.span.substitute_dummy(def.span); sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } Error(sp, msg) => { sess.parse_sess .span_diagnostic .struct_span_err(sp.substitute_dummy(def.span), &msg) .emit(); - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } ErrorReported => { - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } }; @@ -530,6 +538,15 @@ pub fn compile_declarative_macro( None => {} } + // Compute the spans of the macro rules + // We only take the span of the lhs here, + // so that the spans of created warnings are smaller. + let rule_spans = if def.id != DUMMY_NODE_ID { + lhses.iter().map(|lhs| lhs.span()).collect::<Vec<_>>() + } else { + Vec::new() + }; + // Convert the lhses into `MatcherLoc` form, which is better for doing the // actual matching. Unless the matcher is invalid. let lhses = if valid { @@ -549,17 +566,16 @@ pub fn compile_declarative_macro( vec![] }; - mk_syn_ext(Box::new(MacroRulesMacroExpander { + let expander = Box::new(MacroRulesMacroExpander { name: def.ident, span: def.span, + node_id: def.id, transparency, lhses, rhses, valid, - // Macros defined in the current crate have a real node id, - // whereas macros from an external crate have a dummy id. - is_local: def.id != DUMMY_NODE_ID, - })) + }); + (mk_syn_ext(expander), rule_spans) } fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index a965587afb7..0a0f292fe7a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -303,6 +303,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { PATH_STATEMENTS, UNUSED_ATTRIBUTES, UNUSED_MACROS, + UNUSED_MACRO_RULES, UNUSED_ALLOCATION, UNUSED_DOC_COMMENTS, UNUSED_EXTERN_CRATES, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 565ec4e5ab9..7ebb1e85cdb 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -749,6 +749,10 @@ declare_lint! { declare_lint! { /// The `unused_macros` lint detects macros that were not used. /// + /// Note that this lint is distinct from the `unused_macro_rules` lint, + /// which checks for single rules that never match of an otherwise used + /// macro, and thus never expand. + /// /// ### Example /// /// ```rust @@ -776,6 +780,45 @@ declare_lint! { } declare_lint! { + /// The `unused_macro_rules` lint detects macro rules that were not used. + /// + /// Note that the lint is distinct from the `unused_macros` lint, which + /// fires if the entire macro is never called, while this lint fires for + /// single unused rules of the macro that is otherwise used. + /// `unused_macro_rules` fires only if `unused_macros` wouldn't fire. + /// + /// ### Example + /// + /// ```rust + /// macro_rules! unused_empty { + /// (hello) => { println!("Hello, world!") }; // This rule is unused + /// () => { println!("empty") }; // This rule is used + /// } + /// + /// fn main() { + /// unused_empty!(hello); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused macro rules may signal a mistake or unfinished code. Furthermore, + /// they slow down compilation. Right now, silencing the warning is not + /// supported on a single rule level, so you have to add an allow to the + /// entire macro definition. + /// + /// If you intended to export the macro to make it + /// available outside of the crate, use the [`macro_export` attribute]. + /// + /// [`macro_export` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope + pub UNUSED_MACRO_RULES, + Warn, + "detects macro rules that were not used" +} + +declare_lint! { /// The `warnings` lint allows you to change the level of other /// lints which produce warnings. /// @@ -3104,6 +3147,7 @@ declare_lint_pass! { OVERLAPPING_RANGE_ENDPOINTS, BINDINGS_WITH_VARIANT_NAME, UNUSED_MACROS, + UNUSED_MACRO_RULES, WARNINGS, UNUSED_FEATURES, STABLE_FEATURES, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 350bdfdadb2..ef4f1f5e84e 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -258,6 +258,7 @@ macro_rules! make_mir_visitor { // for best performance, we want to use an iterator rather // than a for-loop, to avoid calling `body::Body::invalidate` for // each basic block. + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut().iter_enumerated_mut()); () => (body.basic_blocks().iter_enumerated()); @@ -279,6 +280,7 @@ macro_rules! make_mir_visitor { self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); } + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! type_annotations { (mut) => (body.user_type_annotations.iter_enumerated_mut()); () => (body.user_type_annotations.iter_enumerated()); @@ -932,6 +934,7 @@ macro_rules! make_mir_visitor { body: &$($mutability)? Body<'tcx>, location: Location ) { + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut()); () => (body.basic_blocks()); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 629a550b775..0ef694a3c85 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1145,7 +1145,7 @@ rustc_queries! { query codegen_fulfill_obligation( key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) - ) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> { + ) -> Result<&'tcx ImplSource<'tcx, ()>, traits::CodegenObligationError> { cache_on_disk_if { true } desc { |tcx| "checking if `{}` fulfills its obligations", diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index ffde1294ec6..8c660e38a7f 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -963,3 +963,21 @@ pub enum MethodViolationCode { /// the method's receiver (`self` argument) can't be dispatched on UndispatchableReceiver, } + +/// These are the error cases for `codegen_fulfill_obligation`. +#[derive(Copy, Clone, Debug, Hash, HashStable, Encodable, Decodable)] +pub enum CodegenObligationError { + /// Ambiguity can happen when monomorphizing during trans + /// expands to some humongous type that never occurred + /// statically -- this humongous type can then overflow, + /// leading to an ambiguous result. So report this as an + /// overflow bug, since I believe this is the only case + /// where ambiguity can result. + Ambiguity, + /// This can trigger when we probe for the source of a `'static` lifetime requirement + /// on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound. + /// This can also trigger when we have a global bound that is not actually satisfied, + /// but was included during typeck due to the trivial_bounds feature. + Unimplemented, + FulfillmentError, +} diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index bd27c16c732..b2178ff5995 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -test = false doctest = false [dependencies] diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 783ff5a3f91..e68d6fdeea5 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -194,7 +194,7 @@ impl<'a> Resolver<'a> { } let ext = Lrc::new(match self.cstore().load_macro_untracked(def_id, &self.session) { - LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition), + LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition).0, LoadedMacro::ProcMacro(ext) => ext, }); @@ -1218,9 +1218,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Mark the given macro as unused unless its name starts with `_`. // Macro uses will remove items from this set, and the remaining // items will be reported as `unused_macros`. - fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) { + fn insert_unused_macro( + &mut self, + ident: Ident, + def_id: LocalDefId, + node_id: NodeId, + rule_spans: &[Span], + ) { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); + for (rule_i, rule_span) in rule_spans.iter().enumerate() { + self.r.unused_macro_rules.insert((def_id, rule_i), (ident, *rule_span)); + } } } @@ -1228,15 +1237,16 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let parent_scope = self.parent_scope; let expansion = parent_scope.expansion; let def_id = self.r.local_def_id(item.id); - let (ext, ident, span, macro_rules) = match &item.kind { + let (ext, ident, span, macro_rules, rule_spans) = match &item.kind { ItemKind::MacroDef(def) => { - let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition())); - (ext, item.ident, item.span, def.macro_rules) + let (ext, rule_spans) = self.r.compile_macro(item, self.r.session.edition()); + let ext = Lrc::new(ext); + (ext, item.ident, item.span, def.macro_rules, rule_spans) } ItemKind::Fn(..) => match self.proc_macro_stub(item) { Some((macro_kind, ident, span)) => { self.r.proc_macro_stubs.insert(def_id); - (self.r.dummy_ext(macro_kind), ident, span, false) + (self.r.dummy_ext(macro_kind), ident, span, false, Vec::new()) } None => return parent_scope.macro_rules, }, @@ -1264,7 +1274,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport)); } else { self.r.check_reserved_macro_name(ident, res); - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(ident, def_id, item.id, &rule_spans); } self.r.visibilities.insert(def_id, vis); let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding( @@ -1287,7 +1297,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _ => self.resolve_visibility(&item.vis), }; if vis != ty::Visibility::Public { - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(ident, def_id, item.id, &rule_spans); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); self.r.visibilities.insert(def_id, vis); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 95e608da012..268e2c9b69b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -35,6 +35,9 @@ use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, Vis use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet}; use crate::{Segment, UseError}; +#[cfg(test)] +mod tests; + type Res = def::Res<ast::NodeId>; /// A vector of spans and replacements, a message and applicability. @@ -2675,3 +2678,14 @@ fn is_span_suitable_for_use_injection(s: Span) -> bool { // import or other generated ones !s.from_expansion() } + +/// Convert the given number into the corresponding ordinal +crate fn ordinalize(v: usize) -> String { + let suffix = match ((11..=13).contains(&(v % 100)), v % 10) { + (false, 1) => "st", + (false, 2) => "nd", + (false, 3) => "rd", + _ => "th", + }; + format!("{v}{suffix}") +} diff --git a/compiler/rustc_resolve/src/diagnostics/tests.rs b/compiler/rustc_resolve/src/diagnostics/tests.rs new file mode 100644 index 00000000000..2aa6cc61e46 --- /dev/null +++ b/compiler/rustc_resolve/src/diagnostics/tests.rs @@ -0,0 +1,40 @@ +use super::ordinalize; + +#[test] +fn test_ordinalize() { + assert_eq!(ordinalize(1), "1st"); + assert_eq!(ordinalize(2), "2nd"); + assert_eq!(ordinalize(3), "3rd"); + assert_eq!(ordinalize(4), "4th"); + assert_eq!(ordinalize(5), "5th"); + // ... + assert_eq!(ordinalize(10), "10th"); + assert_eq!(ordinalize(11), "11th"); + assert_eq!(ordinalize(12), "12th"); + assert_eq!(ordinalize(13), "13th"); + assert_eq!(ordinalize(14), "14th"); + // ... + assert_eq!(ordinalize(20), "20th"); + assert_eq!(ordinalize(21), "21st"); + assert_eq!(ordinalize(22), "22nd"); + assert_eq!(ordinalize(23), "23rd"); + assert_eq!(ordinalize(24), "24th"); + // ... + assert_eq!(ordinalize(30), "30th"); + assert_eq!(ordinalize(31), "31st"); + assert_eq!(ordinalize(32), "32nd"); + assert_eq!(ordinalize(33), "33rd"); + assert_eq!(ordinalize(34), "34th"); + // ... + assert_eq!(ordinalize(7010), "7010th"); + assert_eq!(ordinalize(7011), "7011th"); + assert_eq!(ordinalize(7012), "7012th"); + assert_eq!(ordinalize(7013), "7013th"); + assert_eq!(ordinalize(7014), "7014th"); + // ... + assert_eq!(ordinalize(7020), "7020th"); + assert_eq!(ordinalize(7021), "7021st"); + assert_eq!(ordinalize(7022), "7022nd"); + assert_eq!(ordinalize(7023), "7023rd"); + assert_eq!(ordinalize(7024), "7024th"); +} diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 62485beac47..8d3c46c29a8 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -975,6 +975,7 @@ pub struct Resolver<'a> { local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>, ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>, unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>, + unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>, proc_macro_stubs: FxHashSet<LocalDefId>, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: @@ -1374,6 +1375,7 @@ impl<'a> Resolver<'a> { potentially_unused_imports: Vec::new(), struct_constructors: Default::default(), unused_macros: Default::default(), + unused_macro_rules: Default::default(), proc_macro_stubs: Default::default(), single_segment_macro_resolutions: Default::default(), multi_segment_macro_resolutions: Default::default(), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 19a9c1b99fc..2337f72f1e8 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -22,7 +22,8 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, LocalDefId}; use rustc_middle::middle::stability; use rustc_middle::ty::RegisteredTools; -use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS}; +use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE}; +use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -311,6 +312,11 @@ impl<'a> ResolverExpand for Resolver<'a> { Ok(ext) } + fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { + let did = self.local_def_id(id); + self.unused_macro_rules.remove(&(did, rule_i)); + } + fn check_unused_macros(&mut self) { for (_, &(node_id, ident)) in self.unused_macros.iter() { self.lint_buffer.buffer_lint( @@ -320,6 +326,23 @@ impl<'a> ResolverExpand for Resolver<'a> { &format!("unused macro definition: `{}`", ident.as_str()), ); } + for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() { + if self.unused_macros.contains_key(&def_id) { + // We already lint the entire macro as unused + continue; + } + let node_id = self.def_id_to_node_id[def_id]; + self.lint_buffer.buffer_lint( + UNUSED_MACRO_RULES, + node_id, + rule_span, + &format!( + "{} rule of macro `{}` is never used", + crate::diagnostics::ordinalize(arm_i + 1), + ident.as_str() + ), + ); + } } fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool { @@ -830,10 +853,15 @@ impl<'a> Resolver<'a> { } } - /// Compile the macro into a `SyntaxExtension` and possibly replace - /// its expander to a pre-defined one for built-in macros. - crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension { - let mut result = compile_declarative_macro( + /// Compile the macro into a `SyntaxExtension` and its rule spans. + /// + /// Possibly replace its expander to a pre-defined one for built-in macros. + crate fn compile_macro( + &mut self, + item: &ast::Item, + edition: Edition, + ) -> (SyntaxExtension, Vec<Span>) { + let (mut result, mut rule_spans) = compile_declarative_macro( &self.session, self.session.features_untracked(), item, @@ -849,6 +877,7 @@ impl<'a> Resolver<'a> { match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) { BuiltinMacroState::NotYetSeen(ext) => { result.kind = ext; + rule_spans = Vec::new(); if item.id != ast::DUMMY_NODE_ID { self.builtin_macro_kinds .insert(self.local_def_id(item.id), result.macro_kind()); @@ -871,6 +900,6 @@ impl<'a> Resolver<'a> { } } - result + (result, rule_spans) } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d6885bebc32..2cc6eb03585 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1079,6 +1079,7 @@ symbols! { ptr_null, ptr_null_mut, ptr_offset_from, + ptr_offset_from_unsigned, pub_macro_rules, pub_restricted, pure, diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index c76a6542ca1..6ca630b74cc 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -3,13 +3,12 @@ // seems likely that they should eventually be merged into more // general routines. -use crate::infer::{InferCtxt, TyCtxtInferExt}; +use crate::infer::TyCtxtInferExt; use crate::traits::{ FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, Unimplemented, }; -use rustc_errors::ErrorGuaranteed; -use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt}; /// Attempts to resolve an obligation to an `ImplSource`. The result is @@ -23,7 +22,7 @@ use rustc_middle::ty::{self, TyCtxt}; pub fn codegen_fulfill_obligation<'tcx>( tcx: TyCtxt<'tcx>, (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), -) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> { +) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { // Remove any references to regions; this helps improve caching. let trait_ref = tcx.erase_regions(trait_ref); // We expect the input to be fully normalized. @@ -40,37 +39,8 @@ pub fn codegen_fulfill_obligation<'tcx>( let selection = match selcx.select(&obligation) { Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongous type that never occurred - // statically -- this humongous type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - let reported = infcx.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!( - "encountered ambiguity selecting `{:?}` during codegen, presuming due to \ - overflow or prior type error", - trait_ref - ), - ); - return Err(reported); - } - Err(Unimplemented) => { - // This can trigger when we probe for the source of a `'static` lifetime requirement - // on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound. - // This can also trigger when we have a global bound that is not actually satisfied, - // but was included during typeck due to the trivial_bounds feature. - let guar = infcx.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!( - "Encountered error `Unimplemented` selecting `{:?}` during codegen", - trait_ref - ), - ); - return Err(guar); - } + Ok(None) => return Err(CodegenObligationError::Ambiguity), + Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented), Err(e) => { bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) } @@ -85,7 +55,17 @@ pub fn codegen_fulfill_obligation<'tcx>( let impl_source = selection.map(|predicate| { fulfill_cx.register_predicate_obligation(&infcx, predicate); }); - let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, impl_source); + + // In principle, we only need to do this so long as `impl_source` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + let errors = fulfill_cx.select_all_or_error(&infcx); + if !errors.is_empty() { + return Err(CodegenObligationError::FulfillmentError); + } + + let impl_source = infcx.resolve_vars_if_possible(impl_source); + let impl_source = infcx.tcx.erase_regions(impl_source); // Opaque types may have gotten their hidden types constrained, but we can ignore them safely // as they will get constrained elsewhere, too. @@ -95,42 +75,3 @@ pub fn codegen_fulfill_obligation<'tcx>( Ok(&*tcx.arena.alloc(impl_source)) }) } - -// # Global Cache - -/// Finishes processes any obligations that remain in the -/// fulfillment context, and then returns the result with all type -/// variables removed and regions erased. Because this is intended -/// for use outside of type inference, if any errors occur, -/// it will panic. It is used during normalization and other cases -/// where processing the obligations in `fulfill_cx` may cause -/// type inference variables that appear in `result` to be -/// unified, and hence we need to process those obligations to get -/// the complete picture of the type. -fn drain_fulfillment_cx_or_panic<'tcx, T>( - infcx: &InferCtxt<'_, 'tcx>, - fulfill_cx: &mut FulfillmentContext<'tcx>, - result: T, -) -> T -where - T: TypeFoldable<'tcx>, -{ - debug!("drain_fulfillment_cx_or_panic()"); - - // In principle, we only need to do this so long as `result` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - let errors = fulfill_cx.select_all_or_error(infcx); - if !errors.is_empty() { - infcx.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!( - "Encountered errors `{:?}` resolving bounds outside of type inference", - errors - ), - ); - } - - let result = infcx.resolve_vars_if_possible(result); - infcx.tcx.erase_regions(result) -} diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 802a59abe5f..143081d61fb 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -1,6 +1,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Binder, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::{sym, DUMMY_SP}; @@ -212,7 +213,22 @@ fn resolve_associated_item<'tcx>( let mut bound_vars_collector = BoundVarsCollector::new(); trait_ref.visit_with(&mut bound_vars_collector); let trait_binder = ty::Binder::bind_with_vars(trait_ref, bound_vars_collector.into_vars(tcx)); - let vtbl = tcx.codegen_fulfill_obligation((param_env, trait_binder))?; + let vtbl = match tcx.codegen_fulfill_obligation((param_env, trait_binder)) { + Ok(vtbl) => vtbl, + Err(CodegenObligationError::Ambiguity) => { + let reported = tcx.sess.delay_span_bug( + tcx.def_span(trait_item_id), + &format!( + "encountered ambiguity selecting `{:?}` during codegen, presuming due to \ + overflow or prior type error", + trait_binder + ), + ); + return Err(reported); + } + Err(CodegenObligationError::Unimplemented) => return Ok(None), + Err(CodegenObligationError::FulfillmentError) => return Ok(None), + }; // Now that we know which impl is being used, we can dispatch to // the actual function: diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 0dd8ee88ca2..b67185e5211 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -305,6 +305,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::ptr_offset_from => { (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize) } + sym::ptr_offset_from_unsigned => { + (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.usize) + } sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { (1, vec![param(0), param(0)], param(0)) } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index ecebc7ed9ac..fd21b367118 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -127,6 +127,7 @@ #![feature(pattern)] #![feature(ptr_internals)] #![feature(ptr_metadata)] +#![feature(ptr_sub_ptr)] #![feature(receiver_trait)] #![feature(set_ptr_value)] #![feature(slice_group_by)] diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 22c19243e7f..093b02113c3 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -56,6 +56,7 @@ macro_rules! vec { // `slice::into_vec` function which is only available with cfg(test) // NB see the slice::hack module in slice.rs for more information #[cfg(all(not(no_global_oom_handling), test))] +#[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! vec { () => ( $crate::vec::Vec::new() diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 5bdf36a63a2..199b3c9d029 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -1056,7 +1056,7 @@ where fn drop(&mut self) { // `T` is not a zero-sized type, and these are pointers into a slice's elements. unsafe { - let len = self.end.offset_from(self.start) as usize; + let len = self.end.sub_ptr(self.start); ptr::copy_nonoverlapping(self.start, self.dest, len); } } diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs index 1bff19d05c1..5cdee0bd4da 100644 --- a/library/alloc/src/vec/drain.rs +++ b/library/alloc/src/vec/drain.rs @@ -163,7 +163,7 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> { // it from the original vec but also avoid creating a &mut to the front since that could // invalidate raw pointers to it which some unsafe code might rely on. let vec_ptr = vec.as_mut().as_mut_ptr(); - let drop_offset = drop_ptr.offset_from(vec_ptr) as usize; + let drop_offset = drop_ptr.sub_ptr(vec_ptr); let to_drop = ptr::slice_from_raw_parts_mut(vec_ptr.add(drop_offset), drop_len); ptr::drop_in_place(to_drop); } diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 282af8cc33f..55dcb84ad16 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -250,7 +250,7 @@ where let sink = self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).unwrap(); // iteration succeeded, don't drop head - unsafe { ManuallyDrop::new(sink).dst.offset_from(dst_buf) as usize } + unsafe { ManuallyDrop::new(sink).dst.sub_ptr(dst_buf) } } } diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs index 354d25c2389..1b1ef9130fa 100644 --- a/library/alloc/src/vec/in_place_drop.rs +++ b/library/alloc/src/vec/in_place_drop.rs @@ -10,7 +10,7 @@ pub(super) struct InPlaceDrop<T> { impl<T> InPlaceDrop<T> { fn len(&self) -> usize { - unsafe { self.dst.offset_from(self.inner) as usize } + unsafe { self.dst.sub_ptr(self.inner) } } } diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index a7df6f59b59..9b84a1d9b4b 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -169,7 +169,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> { let exact = if mem::size_of::<T>() == 0 { self.end.addr().wrapping_sub(self.ptr.addr()) } else { - unsafe { self.end.offset_from(self.ptr) as usize } + unsafe { self.end.sub_ptr(self.ptr) } }; (exact, Some(exact)) } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index d1647c38859..88e4262922d 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1903,6 +1903,11 @@ extern "rust-intrinsic" { #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")] pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize; + /// See documentation of `<*const T>::sub_ptr` for details. + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")] + #[cfg(not(bootstrap))] + pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize; + /// See documentation of `<*const T>::guaranteed_eq` for details. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2385,3 +2390,11 @@ where { called_in_const.call_once(arg) } + +/// Bootstrap polyfill +#[cfg(bootstrap)] +pub const unsafe fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize { + // SAFETY: we have stricter preconditions than `ptr_offset_from`, so can + // call it, and its output has to be positive, so we can just cast. + unsafe { ptr_offset_from(ptr, base) as _ } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 1612aa582ad..d1936b6b566 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -126,6 +126,7 @@ #![feature(const_option)] #![feature(const_option_ext)] #![feature(const_pin)] +#![feature(const_ptr_sub_ptr)] #![feature(const_replace)] #![feature(const_ptr_as_ref)] #![feature(const_ptr_is_null)] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 45964c3a444..028adc796e5 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -611,6 +611,83 @@ impl<T: ?Sized> *const T { unsafe { intrinsics::ptr_offset_from(self, origin) } } + /// Calculates the distance between two pointers, *where it's known that + /// `self` is equal to or greater than `origin`*. The returned value is in + /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. + /// + /// This computes the same value that [`offset_from`](#method.offset_from) + /// would compute, but with the added precondition that that the offset is + /// guaranteed to be non-negative. This method is equivalent to + /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`, + /// but it provides slightly more information to the optimizer, which can + /// sometimes allow it to optimize slightly better with some backends. + /// + /// This method can be though of as recovering the `count` that was passed + /// to [`add`](#method.add) (or, with the parameters in the other order, + /// to [`sub`](#method.sub)). The following are all equivalent, assuming + /// that their safety preconditions are met: + /// ```rust + /// # #![feature(ptr_sub_ptr)] + /// # unsafe fn blah(ptr: *const i32, origin: *const i32, count: usize) -> bool { + /// ptr.sub_ptr(origin) == count + /// # && + /// origin.add(count) == ptr + /// # && + /// ptr.sub(count) == origin + /// # } + /// ``` + /// + /// # Safety + /// + /// - The distance between the pointers must be non-negative (`self >= origin`) + /// + /// - *All* the safety conditions of [`offset_from`](#method.offset_from) + /// apply to this method as well; see it for the full details. + /// + /// Importantly, despite the return type of this method being able to represent + /// a larger offset, it's still *not permitted* to pass pointers which differ + /// by more than `isize::MAX` *bytes*. As such, the result of this method will + /// always be less than or equal to `isize::MAX as usize`. + /// + /// # Panics + /// + /// This function panics if `T` is a Zero-Sized Type ("ZST"). + /// + /// # Examples + /// + /// ``` + /// #![feature(ptr_sub_ptr)] + /// + /// let a = [0; 5]; + /// let ptr1: *const i32 = &a[1]; + /// let ptr2: *const i32 = &a[3]; + /// unsafe { + /// assert_eq!(ptr2.sub_ptr(ptr1), 2); + /// assert_eq!(ptr1.add(2), ptr2); + /// assert_eq!(ptr2.sub(2), ptr1); + /// assert_eq!(ptr2.sub_ptr(ptr2), 0); + /// } + /// + /// // This would be incorrect, as the pointers are not correctly ordered: + /// // ptr1.offset_from(ptr2) + /// ``` + #[unstable(feature = "ptr_sub_ptr", issue = "95892")] + #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")] + #[inline] + pub const unsafe fn sub_ptr(self, origin: *const T) -> usize + where + T: Sized, + { + // SAFETY: The comparison has no side-effects, and the intrinsic + // does this check internally in the CTFE implementation. + unsafe { assert_unsafe_precondition!(self >= origin) }; + + let pointee_size = mem::size_of::<T>(); + assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); + // SAFETY: the caller must uphold the safety contract for `ptr_offset_from_unsigned`. + unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } + } + /// Returns whether two pointers are guaranteed to be equal. /// /// At runtime this function behaves like `self == other`. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ed80cdc9bf9..1a32dd62dfd 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -787,6 +787,78 @@ impl<T: ?Sized> *mut T { unsafe { (self as *const T).offset_from(origin) } } + /// Calculates the distance between two pointers, *where it's known that + /// `self` is equal to or greater than `origin`*. The returned value is in + /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. + /// + /// This computes the same value that [`offset_from`](#method.offset_from) + /// would compute, but with the added precondition that that the offset is + /// guaranteed to be non-negative. This method is equivalent to + /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`, + /// but it provides slightly more information to the optimizer, which can + /// sometimes allow it to optimize slightly better with some backends. + /// + /// This method can be though of as recovering the `count` that was passed + /// to [`add`](#method.add) (or, with the parameters in the other order, + /// to [`sub`](#method.sub)). The following are all equivalent, assuming + /// that their safety preconditions are met: + /// ```rust + /// # #![feature(ptr_sub_ptr)] + /// # unsafe fn blah(ptr: *mut i32, origin: *mut i32, count: usize) -> bool { + /// ptr.sub_ptr(origin) == count + /// # && + /// origin.add(count) == ptr + /// # && + /// ptr.sub(count) == origin + /// # } + /// ``` + /// + /// # Safety + /// + /// - The distance between the pointers must be non-negative (`self >= origin`) + /// + /// - *All* the safety conditions of [`offset_from`](#method.offset_from) + /// apply to this method as well; see it for the full details. + /// + /// Importantly, despite the return type of this method being able to represent + /// a larger offset, it's still *not permitted* to pass pointers which differ + /// by more than `isize::MAX` *bytes*. As such, the result of this method will + /// always be less than or equal to `isize::MAX as usize`. + /// + /// # Panics + /// + /// This function panics if `T` is a Zero-Sized Type ("ZST"). + /// + /// # Examples + /// + /// ``` + /// #![feature(ptr_sub_ptr)] + /// + /// let mut a = [0; 5]; + /// let p: *mut i32 = a.as_mut_ptr(); + /// unsafe { + /// let ptr1: *mut i32 = p.add(1); + /// let ptr2: *mut i32 = p.add(3); + /// + /// assert_eq!(ptr2.sub_ptr(ptr1), 2); + /// assert_eq!(ptr1.add(2), ptr2); + /// assert_eq!(ptr2.sub(2), ptr1); + /// assert_eq!(ptr2.sub_ptr(ptr2), 0); + /// } + /// + /// // This would be incorrect, as the pointers are not correctly ordered: + /// // ptr1.offset_from(ptr2) + #[unstable(feature = "ptr_sub_ptr", issue = "95892")] + #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")] + #[inline] + pub const unsafe fn sub_ptr(self, origin: *const T) -> usize + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `sub_ptr`. + unsafe { (self as *const T).sub_ptr(origin) } + } + /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 1b88573035d..6bc60b04b5c 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -215,7 +215,7 @@ pub const fn from_mut<T>(s: &mut T) -> &mut [T] { #[unstable(feature = "slice_from_ptr_range", issue = "89792")] pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] { // SAFETY: the caller must uphold the safety contract for `from_ptr_range`. - unsafe { from_raw_parts(range.start, range.end.offset_from(range.start) as usize) } + unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } } /// Performs the same functionality as [`from_ptr_range`], except that a @@ -265,5 +265,5 @@ pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] { #[unstable(feature = "slice_from_ptr_range", issue = "89792")] pub unsafe fn from_mut_ptr_range<'a, T>(range: Range<*mut T>) -> &'a mut [T] { // SAFETY: the caller must uphold the safety contract for `from_mut_ptr_range`. - unsafe { from_raw_parts_mut(range.start, range.end.offset_from(range.start) as usize) } + unsafe { from_raw_parts_mut(range.start, range.end.sub_ptr(range.start)) } } diff --git a/library/stdarch b/library/stdarch -Subproject d215afe9d1cf79c5edb0dfd3cdf4c50aca1f176 +Subproject 28335054b1f417175ab5005cf1d9cf793773793 diff --git a/src/test/codegen/intrinsics/offset_from.rs b/src/test/codegen/intrinsics/offset_from.rs new file mode 100644 index 00000000000..d0de4c8355d --- /dev/null +++ b/src/test/codegen/intrinsics/offset_from.rs @@ -0,0 +1,36 @@ +// compile-flags: -C opt-level=1 +// only-64bit (because we're using [ui]size) + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +//! Basic optimizations are enabled because otherwise `x86_64-gnu-nopt` had an alloca. +//! Uses a type with non-power-of-two size to avoid normalizations to shifts. + +use std::intrinsics::*; + +type RGB = [u8; 3]; + +// CHECK-LABEL: @offset_from_odd_size +#[no_mangle] +pub unsafe fn offset_from_odd_size(a: *const RGB, b: *const RGB) -> isize { + // CHECK: start + // CHECK-NEXT: ptrtoint + // CHECK-NEXT: ptrtoint + // CHECK-NEXT: sub i64 + // CHECK-NEXT: sdiv exact i64 %{{[0-9]+}}, 3 + // CHECK-NEXT: ret i64 + ptr_offset_from(a, b) +} + +// CHECK-LABEL: @offset_from_unsigned_odd_size +#[no_mangle] +pub unsafe fn offset_from_unsigned_odd_size(a: *const RGB, b: *const RGB) -> usize { + // CHECK: start + // CHECK-NEXT: ptrtoint + // CHECK-NEXT: ptrtoint + // CHECK-NEXT: sub nuw i64 + // CHECK-NEXT: udiv exact i64 %{{[0-9]+}}, 3 + // CHECK-NEXT: ret i64 + ptr_offset_from_unsigned(a, b) +} diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-85848.rs b/src/test/ui/const-generics/generic_const_exprs/issue-85848.rs index e86ffbf1075..3a7f4c6184c 100644 --- a/src/test/ui/const-generics/generic_const_exprs/issue-85848.rs +++ b/src/test/ui/const-generics/generic_const_exprs/issue-85848.rs @@ -24,6 +24,7 @@ fn writes_to_path<C>(cap: &C) { writes_to_specific_path(&cap); //~^ ERROR: the trait bound `(): _Contains<&C>` is not satisfied [E0277] //~| ERROR: unconstrained generic constant + //~| ERROR: mismatched types [E0308] } fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {} diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr index 27f5dce9fb2..d45dfde9a79 100644 --- a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr @@ -18,7 +18,7 @@ note: required because of the requirements on the impl of `Delegates<()>` for `& LL | impl<T, U> Delegates<U> for T where T: Contains<U, true> {} | ^^^^^^^^^^^^ ^ note: required by a bound in `writes_to_specific_path` - --> $DIR/issue-85848.rs:29:31 + --> $DIR/issue-85848.rs:30:31 | LL | fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {} | ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path` @@ -43,11 +43,21 @@ note: required because of the requirements on the impl of `Delegates<()>` for `& LL | impl<T, U> Delegates<U> for T where T: Contains<U, true> {} | ^^^^^^^^^^^^ ^ note: required by a bound in `writes_to_specific_path` - --> $DIR/issue-85848.rs:29:31 + --> $DIR/issue-85848.rs:30:31 | LL | fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {} | ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/issue-85848.rs:24:5 + | +LL | writes_to_specific_path(&cap); + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `true`, found `{ contains::<T, U>() }` + | + = note: expected type `true` + found type `{ contains::<T, U>() }` + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-86530.rs b/src/test/ui/const-generics/issues/issue-86530.rs index b024decd4e1..4a6ffd1f300 100644 --- a/src/test/ui/const-generics/issues/issue-86530.rs +++ b/src/test/ui/const-generics/issues/issue-86530.rs @@ -15,6 +15,7 @@ where fn unit_literals() { z(" "); //~^ ERROR: the trait bound `&str: X` is not satisfied + //~| ERROR: unconstrained generic constant } fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-86530.stderr b/src/test/ui/const-generics/issues/issue-86530.stderr index c63857b2314..c688f838dab 100644 --- a/src/test/ui/const-generics/issues/issue-86530.stderr +++ b/src/test/ui/const-generics/issues/issue-86530.stderr @@ -15,6 +15,22 @@ LL | where LL | T: X, | ^ required by this bound in `z` -error: aborting due to previous error +error: unconstrained generic constant + --> $DIR/issue-86530.rs:16:5 + | +LL | z(" "); + | ^ + | + = help: try adding a `where` bound using this expression: `where [(); T::Y]:` +note: required by a bound in `z` + --> $DIR/issue-86530.rs:11:10 + | +LL | fn z<T>(t: T) + | - required by a bound in this +... +LL | [(); T::Y]: , + | ^^^^ required by this bound in `z` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/consts/const-float-bits-conv.rs b/src/test/ui/consts/const-float-bits-conv.rs index 310db2174aa..fd5e42ef170 100644 --- a/src/test/ui/consts/const-float-bits-conv.rs +++ b/src/test/ui/consts/const-float-bits-conv.rs @@ -3,6 +3,7 @@ #![feature(const_float_bits_conv)] #![feature(const_float_classify)] +#![allow(unused_macro_rules)] // Don't promote const fn nop<T>(x: T) -> T { x } diff --git a/src/test/ui/consts/offset_from.rs b/src/test/ui/consts/offset_from.rs index 4c9b1c1571d..b53718316f3 100644 --- a/src/test/ui/consts/offset_from.rs +++ b/src/test/ui/consts/offset_from.rs @@ -1,6 +1,8 @@ // run-pass #![feature(const_ptr_offset_from)] +#![feature(const_ptr_sub_ptr)] +#![feature(ptr_sub_ptr)] struct Struct { field: (), @@ -43,9 +45,16 @@ pub const OFFSET_EQUAL_INTS: isize = { unsafe { ptr.offset_from(ptr) } }; +pub const OFFSET_UNSIGNED: usize = { + let a = ['a', 'b', 'c']; + let ptr = a.as_ptr(); + unsafe { ptr.add(2).sub_ptr(ptr) } +}; + fn main() { assert_eq!(OFFSET, 0); assert_eq!(OFFSET_2, 1); assert_eq!(OVERFLOW, -1); assert_eq!(OFFSET_EQUAL_INTS, 0); + assert_eq!(OFFSET_UNSIGNED, 2); } diff --git a/src/test/ui/consts/offset_from_ub.rs b/src/test/ui/consts/offset_from_ub.rs index 939c1e31f9a..f604f57e39d 100644 --- a/src/test/ui/consts/offset_from_ub.rs +++ b/src/test/ui/consts/offset_from_ub.rs @@ -1,7 +1,7 @@ #![feature(const_ptr_offset_from)] #![feature(core_intrinsics)] -use std::intrinsics::ptr_offset_from; +use std::intrinsics::{ptr_offset_from, ptr_offset_from_unsigned}; #[repr(C)] struct Struct { @@ -15,7 +15,7 @@ pub const DIFFERENT_ALLOC: usize = { let uninit2 = std::mem::MaybeUninit::<Struct>::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed - //~| cannot compute offset of pointers into different allocations. + //~| ptr_offset_from cannot compute offset of pointers into different allocations. offset as usize }; @@ -70,4 +70,21 @@ const OUT_OF_BOUNDS_SAME: isize = { //~| pointer at offset 10 is out-of-bounds }; +pub const DIFFERENT_ALLOC_UNSIGNED: usize = { + let uninit = std::mem::MaybeUninit::<Struct>::uninit(); + let base_ptr: *const Struct = &uninit as *const _ as *const Struct; + let uninit2 = std::mem::MaybeUninit::<Struct>::uninit(); + let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; + let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed + //~| ptr_offset_from_unsigned cannot compute offset of pointers into different allocations. + offset as usize +}; + +const WRONG_ORDER_UNSIGNED: usize = { + let a = ['a', 'b', 'c']; + let p = a.as_ptr(); + unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } //~ERROR evaluation of constant value failed + //~| cannot compute a negative offset, but 0 < 8 +}; + fn main() {} diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr index 293a2b47d30..4c98fd72cac 100644 --- a/src/test/ui/consts/offset_from_ub.stderr +++ b/src/test/ui/consts/offset_from_ub.stderr @@ -54,6 +54,18 @@ error[E0080]: evaluation of constant value failed LL | unsafe { ptr_offset_from(end_ptr, end_ptr) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc26 has size 4, so pointer at offset 10 is out-of-bounds -error: aborting due to 8 previous errors +error[E0080]: evaluation of constant value failed + --> $DIR/offset_from_ub.rs:78:27 + | +LL | let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute offset of pointers into different allocations. + +error[E0080]: evaluation of constant value failed + --> $DIR/offset_from_ub.rs:86:14 + | +LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute a negative offset, but 0 < 8 + +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/issues/issue-77919.rs b/src/test/ui/issues/issue-77919.rs index 966d76d148a..1d5d5930731 100644 --- a/src/test/ui/issues/issue-77919.rs +++ b/src/test/ui/issues/issue-77919.rs @@ -1,5 +1,6 @@ fn main() { [1; <Multiply<Five, Five>>::VAL]; + //~^ ERROR: constant expression depends on a generic parameter } trait TypeVal<T> { const VAL: T; diff --git a/src/test/ui/issues/issue-77919.stderr b/src/test/ui/issues/issue-77919.stderr index 97bd5ab36b6..c986e47fb55 100644 --- a/src/test/ui/issues/issue-77919.stderr +++ b/src/test/ui/issues/issue-77919.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `PhantomData` in this scope - --> $DIR/issue-77919.rs:9:9 + --> $DIR/issue-77919.rs:10:9 | LL | _n: PhantomData, | ^^^^^^^^^^^ not found in this scope @@ -10,7 +10,7 @@ LL | use std::marker::PhantomData; | error[E0412]: cannot find type `VAL` in this scope - --> $DIR/issue-77919.rs:11:63 + --> $DIR/issue-77919.rs:12:63 | LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} | - ^^^ not found in this scope @@ -18,7 +18,7 @@ LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} | help: you might be missing a type parameter: `, VAL` error[E0046]: not all trait items implemented, missing: `VAL` - --> $DIR/issue-77919.rs:11:1 + --> $DIR/issue-77919.rs:12:1 | LL | const VAL: T; | ------------- `VAL` from trait @@ -26,7 +26,15 @@ LL | const VAL: T; LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation -error: aborting due to 3 previous errors +error: constant expression depends on a generic parameter + --> $DIR/issue-77919.rs:2:9 + | +LL | [1; <Multiply<Five, Five>>::VAL]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0046, E0412. For more information about an error, try `rustc --explain E0046`. diff --git a/src/test/ui/lint/unused/unused-macro-rules-decl.rs b/src/test/ui/lint/unused/unused-macro-rules-decl.rs new file mode 100644 index 00000000000..537c84940fd --- /dev/null +++ b/src/test/ui/lint/unused/unused-macro-rules-decl.rs @@ -0,0 +1,49 @@ +#![feature(decl_macro)] +#![deny(unused_macro_rules)] +// To make sure we are not hitting this +#![deny(unused_macros)] + +// Most simple case +macro num { + (one) => { 1 }, + (two) => { 2 }, //~ ERROR: 2nd rule of macro + (three) => { 3 }, + (four) => { 4 }, //~ ERROR: 4th rule of macro +} +const _NUM: u8 = num!(one) + num!(three); + +// Check that allowing the lint works +#[allow(unused_macro_rules)] +macro num_allowed { + (one) => { 1 }, + (two) => { 2 }, + (three) => { 3 }, + (four) => { 4 }, +} +const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three); + +// Check that macro calls inside the macro trigger as usage +macro num_rec { + (one) => { 1 }, + (two) => { + num_rec!(one) + num_rec!(one) + }, + (three) => { //~ ERROR: 3rd rule of macro + num_rec!(one) + num_rec!(two) + }, + (four) => { + num_rec!(two) + num_rec!(two) + }, +} +const _NUM_RECURSIVE: u8 = num_rec!(four); + +// No error if the macro is public +pub macro num_public { + (one) => { 1 }, + (two) => { 2 }, + (three) => { 3 }, + (four) => { 4 }, +} +const _NUM_PUBLIC: u8 = num_public!(one) + num_public!(three); + +fn main() {} diff --git a/src/test/ui/lint/unused/unused-macro-rules-decl.stderr b/src/test/ui/lint/unused/unused-macro-rules-decl.stderr new file mode 100644 index 00000000000..4d9b22feda2 --- /dev/null +++ b/src/test/ui/lint/unused/unused-macro-rules-decl.stderr @@ -0,0 +1,26 @@ +error: 4th rule of macro `num` is never used + --> $DIR/unused-macro-rules-decl.rs:11:5 + | +LL | (four) => { 4 }, + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macro-rules-decl.rs:2:9 + | +LL | #![deny(unused_macro_rules)] + | ^^^^^^^^^^^^^^^^^^ + +error: 2nd rule of macro `num` is never used + --> $DIR/unused-macro-rules-decl.rs:9:5 + | +LL | (two) => { 2 }, + | ^^^^^ + +error: 3rd rule of macro `num_rec` is never used + --> $DIR/unused-macro-rules-decl.rs:31:5 + | +LL | (three) => { + | ^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/lint/unused/unused-macro-rules.rs b/src/test/ui/lint/unused/unused-macro-rules.rs index 1a714b8f0a0..eeaf4d1b0a9 100644 --- a/src/test/ui/lint/unused/unused-macro-rules.rs +++ b/src/test/ui/lint/unused/unused-macro-rules.rs @@ -1,29 +1,47 @@ +#![deny(unused_macro_rules)] +// To make sure we are not hitting this #![deny(unused_macros)] // Most simple case -macro_rules! unused { //~ ERROR: unused macro definition - () => {}; +macro_rules! num { + (one) => { 1 }; + (two) => { 2 }; //~ ERROR: 2nd rule of macro + (three) => { 3 }; + (four) => { 4 }; //~ ERROR: 4th rule of macro } +const _NUM: u8 = num!(one) + num!(three); -// Test macros created by macros -macro_rules! create_macro { - () => { - macro_rules! m { //~ ERROR: unused macro definition - () => {}; - } - }; +// Check that allowing the lint works +#[allow(unused_macro_rules)] +macro_rules! num_allowed { + (one) => { 1 }; + (two) => { 2 }; + (three) => { 3 }; + (four) => { 4 }; } -create_macro!(); +const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three); -#[allow(unused_macros)] -mod bar { - // Test that putting the #[deny] close to the macro's definition - // works. +// Check that macro calls inside the macro trigger as usage +macro_rules! num_rec { + (one) => { 1 }; + (two) => { + num_rec!(one) + num_rec!(one) + }; + (three) => { //~ ERROR: 3rd rule of macro + num_rec!(one) + num_rec!(two) + }; + (four) => { num_rec!(two) + num_rec!(two) }; +} +const _NUM_RECURSIVE: u8 = num_rec!(four); - #[deny(unused_macros)] - macro_rules! unused { //~ ERROR: unused macro definition - () => {}; - } +// No error if the macro is being exported +#[macro_export] +macro_rules! num_exported { + (one) => { 1 }; + (two) => { 2 }; + (three) => { 3 }; + (four) => { 4 }; } +const _NUM_EXPORTED: u8 = num_exported!(one) + num_exported!(three); fn main() {} diff --git a/src/test/ui/lint/unused/unused-macro-rules.stderr b/src/test/ui/lint/unused/unused-macro-rules.stderr index 59db35b4111..2b3098a5128 100644 --- a/src/test/ui/lint/unused/unused-macro-rules.stderr +++ b/src/test/ui/lint/unused/unused-macro-rules.stderr @@ -1,32 +1,26 @@ -error: unused macro definition: `unused` - --> $DIR/unused-macro-rules.rs:4:14 +error: 4th rule of macro `num` is never used + --> $DIR/unused-macro-rules.rs:10:5 | -LL | macro_rules! unused { - | ^^^^^^ +LL | (four) => { 4 }; + | ^^^^^^ | note: the lint level is defined here --> $DIR/unused-macro-rules.rs:1:9 | -LL | #![deny(unused_macros)] - | ^^^^^^^^^^^^^ +LL | #![deny(unused_macro_rules)] + | ^^^^^^^^^^^^^^^^^^ -error: unused macro definition: `m` - --> $DIR/unused-macro-rules.rs:11:22 +error: 2nd rule of macro `num` is never used + --> $DIR/unused-macro-rules.rs:8:5 | -LL | macro_rules! m { - | ^ +LL | (two) => { 2 }; + | ^^^^^ -error: unused macro definition: `unused` - --> $DIR/unused-macro-rules.rs:24:18 +error: 3rd rule of macro `num_rec` is never used + --> $DIR/unused-macro-rules.rs:30:5 | -LL | macro_rules! unused { - | ^^^^^^ - | -note: the lint level is defined here - --> $DIR/unused-macro-rules.rs:23:12 - | -LL | #[deny(unused_macros)] - | ^^^^^^^^^^^^^ +LL | (three) => { + | ^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/lint/unused/unused-macro.rs b/src/test/ui/lint/unused/unused-macros-decl.rs index 302b0727d77..21f6108b18a 100644 --- a/src/test/ui/lint/unused/unused-macro.rs +++ b/src/test/ui/lint/unused/unused-macros-decl.rs @@ -1,5 +1,7 @@ #![feature(decl_macro)] #![deny(unused_macros)] +// To make sure we are not hitting this +#![deny(unused_macro_rules)] // Most simple case macro unused { //~ ERROR: unused macro definition diff --git a/src/test/ui/lint/unused/unused-macro.stderr b/src/test/ui/lint/unused/unused-macros-decl.stderr index 1a73279ed6d..1f426b9d91a 100644 --- a/src/test/ui/lint/unused/unused-macro.stderr +++ b/src/test/ui/lint/unused/unused-macros-decl.stderr @@ -1,29 +1,29 @@ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:5:7 + --> $DIR/unused-macros-decl.rs:7:7 | LL | macro unused { | ^^^^^^ | note: the lint level is defined here - --> $DIR/unused-macro.rs:2:9 + --> $DIR/unused-macros-decl.rs:2:9 | LL | #![deny(unused_macros)] | ^^^^^^^^^^^^^ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:15:11 + --> $DIR/unused-macros-decl.rs:17:11 | LL | macro unused { | ^^^^^^ | note: the lint level is defined here - --> $DIR/unused-macro.rs:14:12 + --> $DIR/unused-macros-decl.rs:16:12 | LL | #[deny(unused_macros)] | ^^^^^^^^^^^^^ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:21:22 + --> $DIR/unused-macros-decl.rs:23:22 | LL | pub(crate) macro unused { | ^^^^^^ diff --git a/src/test/ui/lint/unused/unused-macros.rs b/src/test/ui/lint/unused/unused-macros.rs new file mode 100644 index 00000000000..70b50b2082b --- /dev/null +++ b/src/test/ui/lint/unused/unused-macros.rs @@ -0,0 +1,31 @@ +#![deny(unused_macros)] +// To make sure we are not hitting this +#![deny(unused_macro_rules)] + +// Most simple case +macro_rules! unused { //~ ERROR: unused macro definition + () => {}; +} + +// Test macros created by macros +macro_rules! create_macro { + () => { + macro_rules! m { //~ ERROR: unused macro definition + () => {}; + } + }; +} +create_macro!(); + +#[allow(unused_macros)] +mod bar { + // Test that putting the #[deny] close to the macro's definition + // works. + + #[deny(unused_macros)] + macro_rules! unused { //~ ERROR: unused macro definition + () => {}; + } +} + +fn main() {} diff --git a/src/test/ui/lint/unused/unused-macros.stderr b/src/test/ui/lint/unused/unused-macros.stderr new file mode 100644 index 00000000000..d0baf5becec --- /dev/null +++ b/src/test/ui/lint/unused/unused-macros.stderr @@ -0,0 +1,32 @@ +error: unused macro definition: `unused` + --> $DIR/unused-macros.rs:6:14 + | +LL | macro_rules! unused { + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macros.rs:1:9 + | +LL | #![deny(unused_macros)] + | ^^^^^^^^^^^^^ + +error: unused macro definition: `m` + --> $DIR/unused-macros.rs:13:22 + | +LL | macro_rules! m { + | ^ + +error: unused macro definition: `unused` + --> $DIR/unused-macros.rs:26:18 + | +LL | macro_rules! unused { + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macros.rs:25:12 + | +LL | #[deny(unused_macros)] + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/macros/issue-41803.rs b/src/test/ui/macros/issue-41803.rs index 19ab81d04d0..bccfdc61146 100644 --- a/src/test/ui/macros/issue-41803.rs +++ b/src/test/ui/macros/issue-41803.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + /// A compile-time map from identifiers to arbitrary (heterogeneous) expressions macro_rules! ident_map { ( $name:ident = { $($key:ident => $e:expr,)* } ) => { diff --git a/src/test/ui/macros/issue-52169.rs b/src/test/ui/macros/issue-52169.rs index 60be97f0aee..f178cd30cb4 100644 --- a/src/test/ui/macros/issue-52169.rs +++ b/src/test/ui/macros/issue-52169.rs @@ -1,5 +1,6 @@ // run-pass +#[allow(unused_macro_rules)] macro_rules! a { ($i:literal) => { "right" }; ($i:tt) => { "wrong" }; diff --git a/src/test/ui/macros/macro-first-set.rs b/src/test/ui/macros/macro-first-set.rs index f85376dabcb..eeb1ddd84ae 100644 --- a/src/test/ui/macros/macro-first-set.rs +++ b/src/test/ui/macros/macro-first-set.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_macro_rules)] //{{{ issue 40569 ============================================================== diff --git a/src/test/ui/macros/macro-literal.rs b/src/test/ui/macros/macro-literal.rs index e08d0a67b43..3c2e71f9c43 100644 --- a/src/test/ui/macros/macro-literal.rs +++ b/src/test/ui/macros/macro-literal.rs @@ -21,6 +21,7 @@ macro_rules! only_expr { }; } +#[allow(unused_macro_rules)] macro_rules! mtester_dbg { ($l:literal) => { &format!("macro caught literal: {:?}", $l) diff --git a/src/test/ui/macros/macro-pub-matcher.rs b/src/test/ui/macros/macro-pub-matcher.rs index c02e6794edb..174056d6cdf 100644 --- a/src/test/ui/macros/macro-pub-matcher.rs +++ b/src/test/ui/macros/macro-pub-matcher.rs @@ -1,5 +1,5 @@ // run-pass -#![allow(dead_code, unused_imports)] +#![allow(dead_code, unused_imports, unused_macro_rules)] #![feature(crate_visibility_modifier)] /** diff --git a/src/test/ui/macros/stmt_expr_attr_macro_parse.rs b/src/test/ui/macros/stmt_expr_attr_macro_parse.rs index 3ab50db0ea1..570191d2c90 100644 --- a/src/test/ui/macros/stmt_expr_attr_macro_parse.rs +++ b/src/test/ui/macros/stmt_expr_attr_macro_parse.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + macro_rules! m { ($e:expr) => { "expr includes attr" diff --git a/src/test/ui/macros/type-macros-hlist.rs b/src/test/ui/macros/type-macros-hlist.rs index 77d866cea9c..946b5bd5d93 100644 --- a/src/test/ui/macros/type-macros-hlist.rs +++ b/src/test/ui/macros/type-macros-hlist.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + use std::ops::*; #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] diff --git a/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs b/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs index 9d68a7bffde..76c07bbfd81 100644 --- a/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs +++ b/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs @@ -3,6 +3,7 @@ // Test that failing macro matchers will not cause pre-expansion errors // even though they use a feature that is pre-expansion gated. +#[allow(unused_macro_rules)] macro_rules! m { ($e:expr) => { 0 }; // This fails on the input below due to `, foo`. ($e:expr,) => { 1 }; // This also fails to match due to `foo`. diff --git a/src/test/ui/recursion/issue-83150.rs b/src/test/ui/recursion/issue-83150.rs index dc25004f89b..aa3f66b2e28 100644 --- a/src/test/ui/recursion/issue-83150.rs +++ b/src/test/ui/recursion/issue-83150.rs @@ -1,5 +1,5 @@ // build-fail - //~^ overflow evaluating +//~^ ERROR overflow evaluating the requirement fn main() { let mut iter = 0u8..1; diff --git a/src/test/ui/rust-2018/async-ident.fixed b/src/test/ui/rust-2018/async-ident.fixed index f4ae518c71d..e909c79070c 100644 --- a/src/test/ui/rust-2018/async-ident.fixed +++ b/src/test/ui/rust-2018/async-ident.fixed @@ -1,4 +1,4 @@ -#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)] +#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)] #![deny(keyword_idents)] // edition:2015 diff --git a/src/test/ui/rust-2018/async-ident.rs b/src/test/ui/rust-2018/async-ident.rs index 79c73dafac7..2bfbc3871d1 100644 --- a/src/test/ui/rust-2018/async-ident.rs +++ b/src/test/ui/rust-2018/async-ident.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)] +#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)] #![deny(keyword_idents)] // edition:2015 diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr index c8239897f3a..d930d486fde 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr @@ -30,7 +30,15 @@ LL | const VAL: T; LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation -error: aborting due to 3 previous errors +error: constant expression depends on a generic parameter + --> $DIR/ice-6252.rs:13:9 + | +LL | [1; <Multiply<Five, Five>>::VAL]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0046, E0412. For more information about an error, try `rustc --explain E0046`. diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs index 51ed1b5a61c..1ce02e48c05 100644 --- a/src/tools/error_index_generator/main.rs +++ b/src/tools/error_index_generator/main.rs @@ -280,10 +280,6 @@ fn main() { fn register_all() -> Vec<(&'static str, Option<&'static str>)> { let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new(); macro_rules! register_diagnostics { - ($($ecode:ident: $message:expr,)*) => ( - register_diagnostics!{$($ecode:$message,)* ;} - ); - ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => ( $( {long_codes.extend([ |
